pci: introduce library and driver
[dpdk.git] / drivers / net / mlx4 / mlx4_ethdev.c
index 5f1dba2..b0acd12 100644 (file)
 #pragma GCC diagnostic error "-Wpedantic"
 #endif
 
+#include <rte_bus_pci.h>
 #include <rte_errno.h>
 #include <rte_ethdev.h>
 #include <rte_ether.h>
+#include <rte_flow.h>
 #include <rte_pci.h>
 
 #include "mlx4.h"
+#include "mlx4_flow.h"
 #include "mlx4_rxtx.h"
 #include "mlx4_utils.h"
 
@@ -465,20 +468,16 @@ mlx4_set_flags(struct priv *priv, unsigned int keep, unsigned int flags)
 static int
 mlx4_dev_set_link(struct priv *priv, int up)
 {
-       struct rte_eth_dev *dev = priv->dev;
        int err;
 
        if (up) {
                err = mlx4_set_flags(priv, ~IFF_UP, IFF_UP);
                if (err)
                        return err;
-               dev->rx_pkt_burst = mlx4_rx_burst;
        } else {
                err = mlx4_set_flags(priv, ~IFF_UP, ~IFF_UP);
                if (err)
                        return err;
-               dev->rx_pkt_burst = mlx4_rx_burst_removed;
-               dev->tx_pkt_burst = mlx4_tx_burst_removed;
        }
        return 0;
 }
@@ -517,6 +516,225 @@ mlx4_dev_set_link_up(struct rte_eth_dev *dev)
        return mlx4_dev_set_link(priv, 1);
 }
 
+/**
+ * Supported Rx mode toggles.
+ *
+ * Even and odd values respectively stand for off and on.
+ */
+enum rxmode_toggle {
+       RXMODE_TOGGLE_PROMISC_OFF,
+       RXMODE_TOGGLE_PROMISC_ON,
+       RXMODE_TOGGLE_ALLMULTI_OFF,
+       RXMODE_TOGGLE_ALLMULTI_ON,
+};
+
+/**
+ * Helper function to toggle promiscuous and all multicast modes.
+ *
+ * @param dev
+ *   Pointer to Ethernet device structure.
+ * @param toggle
+ *   Toggle to set.
+ */
+static void
+mlx4_rxmode_toggle(struct rte_eth_dev *dev, enum rxmode_toggle toggle)
+{
+       struct priv *priv = dev->data->dev_private;
+       const char *mode;
+       struct rte_flow_error error;
+
+       switch (toggle) {
+       case RXMODE_TOGGLE_PROMISC_OFF:
+       case RXMODE_TOGGLE_PROMISC_ON:
+               mode = "promiscuous";
+               dev->data->promiscuous = toggle & 1;
+               break;
+       case RXMODE_TOGGLE_ALLMULTI_OFF:
+       case RXMODE_TOGGLE_ALLMULTI_ON:
+               mode = "all multicast";
+               dev->data->all_multicast = toggle & 1;
+               break;
+       }
+       if (!mlx4_flow_sync(priv, &error))
+               return;
+       ERROR("cannot toggle %s mode (code %d, \"%s\"),"
+             " flow error type %d, cause %p, message: %s",
+             mode, rte_errno, strerror(rte_errno), error.type, error.cause,
+             error.message ? error.message : "(unspecified)");
+}
+
+/**
+ * DPDK callback to enable promiscuous mode.
+ *
+ * @param dev
+ *   Pointer to Ethernet device structure.
+ */
+void
+mlx4_promiscuous_enable(struct rte_eth_dev *dev)
+{
+       mlx4_rxmode_toggle(dev, RXMODE_TOGGLE_PROMISC_ON);
+}
+
+/**
+ * DPDK callback to disable promiscuous mode.
+ *
+ * @param dev
+ *   Pointer to Ethernet device structure.
+ */
+void
+mlx4_promiscuous_disable(struct rte_eth_dev *dev)
+{
+       mlx4_rxmode_toggle(dev, RXMODE_TOGGLE_PROMISC_OFF);
+}
+
+/**
+ * DPDK callback to enable all multicast mode.
+ *
+ * @param dev
+ *   Pointer to Ethernet device structure.
+ */
+void
+mlx4_allmulticast_enable(struct rte_eth_dev *dev)
+{
+       mlx4_rxmode_toggle(dev, RXMODE_TOGGLE_ALLMULTI_ON);
+}
+
+/**
+ * DPDK callback to disable all multicast mode.
+ *
+ * @param dev
+ *   Pointer to Ethernet device structure.
+ */
+void
+mlx4_allmulticast_disable(struct rte_eth_dev *dev)
+{
+       mlx4_rxmode_toggle(dev, RXMODE_TOGGLE_ALLMULTI_OFF);
+}
+
+/**
+ * DPDK callback to remove a MAC address.
+ *
+ * @param dev
+ *   Pointer to Ethernet device structure.
+ * @param index
+ *   MAC address index.
+ */
+void
+mlx4_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index)
+{
+       struct priv *priv = dev->data->dev_private;
+       struct rte_flow_error error;
+
+       if (index >= RTE_DIM(priv->mac)) {
+               rte_errno = EINVAL;
+               return;
+       }
+       memset(&priv->mac[index], 0, sizeof(priv->mac[index]));
+       if (!mlx4_flow_sync(priv, &error))
+               return;
+       ERROR("failed to synchronize flow rules after removing MAC address"
+             " at index %d (code %d, \"%s\"),"
+             " flow error type %d, cause %p, message: %s",
+             index, rte_errno, strerror(rte_errno), error.type, error.cause,
+             error.message ? error.message : "(unspecified)");
+}
+
+/**
+ * DPDK callback to add a MAC address.
+ *
+ * @param dev
+ *   Pointer to Ethernet device structure.
+ * @param mac_addr
+ *   MAC address to register.
+ * @param index
+ *   MAC address index.
+ * @param vmdq
+ *   VMDq pool index to associate address with (ignored).
+ *
+ * @return
+ *   0 on success, negative errno value otherwise and rte_errno is set.
+ */
+int
+mlx4_mac_addr_add(struct rte_eth_dev *dev, struct ether_addr *mac_addr,
+                 uint32_t index, uint32_t vmdq)
+{
+       struct priv *priv = dev->data->dev_private;
+       struct rte_flow_error error;
+       int ret;
+
+       (void)vmdq;
+       if (index >= RTE_DIM(priv->mac)) {
+               rte_errno = EINVAL;
+               return -rte_errno;
+       }
+       memcpy(&priv->mac[index], mac_addr, sizeof(priv->mac[index]));
+       ret = mlx4_flow_sync(priv, &error);
+       if (!ret)
+               return 0;
+       ERROR("failed to synchronize flow rules after adding MAC address"
+             " at index %d (code %d, \"%s\"),"
+             " flow error type %d, cause %p, message: %s",
+             index, rte_errno, strerror(rte_errno), error.type, error.cause,
+             error.message ? error.message : "(unspecified)");
+       return ret;
+}
+
+/**
+ * DPDK callback to configure a VLAN filter.
+ *
+ * @param dev
+ *   Pointer to Ethernet device structure.
+ * @param vlan_id
+ *   VLAN ID to filter.
+ * @param on
+ *   Toggle filter.
+ *
+ * @return
+ *   0 on success, negative errno value otherwise and rte_errno is set.
+ */
+int
+mlx4_vlan_filter_set(struct rte_eth_dev *dev, uint16_t vlan_id, int on)
+{
+       struct priv *priv = dev->data->dev_private;
+       struct rte_flow_error error;
+       unsigned int vidx = vlan_id / 64;
+       unsigned int vbit = vlan_id % 64;
+       uint64_t *v;
+       int ret;
+
+       if (vidx >= RTE_DIM(dev->data->vlan_filter_conf.ids)) {
+               rte_errno = EINVAL;
+               return -rte_errno;
+       }
+       v = &dev->data->vlan_filter_conf.ids[vidx];
+       *v &= ~(UINT64_C(1) << vbit);
+       *v |= (uint64_t)!!on << vbit;
+       ret = mlx4_flow_sync(priv, &error);
+       if (!ret)
+               return 0;
+       ERROR("failed to synchronize flow rules after %s VLAN filter on ID %u"
+             " (code %d, \"%s\"), "
+             " flow error type %d, cause %p, message: %s",
+             on ? "enabling" : "disabling", vlan_id,
+             rte_errno, strerror(rte_errno), error.type, error.cause,
+             error.message ? error.message : "(unspecified)");
+       return ret;
+}
+
+/**
+ * DPDK callback to set the primary MAC address.
+ *
+ * @param dev
+ *   Pointer to Ethernet device structure.
+ * @param mac_addr
+ *   MAC address to register.
+ */
+void
+mlx4_mac_addr_set(struct rte_eth_dev *dev, struct ether_addr *mac_addr)
+{
+       mlx4_mac_addr_add(dev, mac_addr, 0, 0);
+}
+
 /**
  * DPDK callback to get information about the device.
  *
@@ -533,8 +751,6 @@ mlx4_dev_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *info)
        char ifname[IF_NAMESIZE];
 
        info->pci_dev = RTE_ETH_DEV_TO_PCI(dev);
-       if (priv == NULL)
-               return;
        /* FIXME: we should ask the device for these values. */
        info->min_rx_bufsize = 32;
        info->max_rx_pktlen = 65536;
@@ -549,12 +765,22 @@ mlx4_dev_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *info)
                max = 65535;
        info->max_rx_queues = max;
        info->max_tx_queues = max;
-       /* Last array entry is reserved for broadcast. */
-       info->max_mac_addrs = 1;
+       info->max_mac_addrs = RTE_DIM(priv->mac);
        info->rx_offload_capa = 0;
        info->tx_offload_capa = 0;
+       if (priv->hw_csum) {
+               info->tx_offload_capa |= (DEV_TX_OFFLOAD_IPV4_CKSUM |
+                                         DEV_TX_OFFLOAD_UDP_CKSUM |
+                                         DEV_TX_OFFLOAD_TCP_CKSUM);
+               info->rx_offload_capa |= (DEV_RX_OFFLOAD_IPV4_CKSUM |
+                                         DEV_RX_OFFLOAD_UDP_CKSUM |
+                                         DEV_RX_OFFLOAD_TCP_CKSUM);
+       }
+       if (priv->hw_csum_l2tun)
+               info->tx_offload_capa |= DEV_TX_OFFLOAD_OUTER_IPV4_CKSUM;
        if (mlx4_get_ifname(priv, &ifname) == 0)
                info->if_index = if_nametoindex(ifname);
+       info->hash_key_size = MLX4_RSS_HASH_KEY_SIZE;
        info->speed_capa =
                        ETH_LINK_SPEED_1G |
                        ETH_LINK_SPEED_10G |
@@ -571,20 +797,17 @@ mlx4_dev_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *info)
  * @param[out] stats
  *   Stats structure output buffer.
  */
-void
+int
 mlx4_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
 {
-       struct priv *priv = dev->data->dev_private;
        struct rte_eth_stats tmp;
        unsigned int i;
        unsigned int idx;
 
-       if (priv == NULL)
-               return;
        memset(&tmp, 0, sizeof(tmp));
        /* Add software counters. */
-       for (i = 0; (i != priv->rxqs_n); ++i) {
-               struct rxq *rxq = (*priv->rxqs)[i];
+       for (i = 0; i != dev->data->nb_rx_queues; ++i) {
+               struct rxq *rxq = dev->data->rx_queues[i];
 
                if (rxq == NULL)
                        continue;
@@ -600,8 +823,8 @@ mlx4_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
                tmp.ierrors += rxq->stats.idropped;
                tmp.rx_nombuf += rxq->stats.rx_nombuf;
        }
-       for (i = 0; (i != priv->txqs_n); ++i) {
-               struct txq *txq = (*priv->txqs)[i];
+       for (i = 0; i != dev->data->nb_tx_queues; ++i) {
+               struct txq *txq = dev->data->tx_queues[i];
 
                if (txq == NULL)
                        continue;
@@ -616,6 +839,7 @@ mlx4_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
                tmp.oerrors += txq->stats.odropped;
        }
        *stats = tmp;
+       return 0;
 }
 
 /**
@@ -627,25 +851,23 @@ mlx4_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
 void
 mlx4_stats_reset(struct rte_eth_dev *dev)
 {
-       struct priv *priv = dev->data->dev_private;
        unsigned int i;
-       unsigned int idx;
 
-       if (priv == NULL)
-               return;
-       for (i = 0; (i != priv->rxqs_n); ++i) {
-               if ((*priv->rxqs)[i] == NULL)
-                       continue;
-               idx = (*priv->rxqs)[i]->stats.idx;
-               (*priv->rxqs)[i]->stats =
-                       (struct mlx4_rxq_stats){ .idx = idx };
+       for (i = 0; i != dev->data->nb_rx_queues; ++i) {
+               struct rxq *rxq = dev->data->rx_queues[i];
+
+               if (rxq)
+                       rxq->stats = (struct mlx4_rxq_stats){
+                               .idx = rxq->stats.idx,
+                       };
        }
-       for (i = 0; (i != priv->txqs_n); ++i) {
-               if ((*priv->txqs)[i] == NULL)
-                       continue;
-               idx = (*priv->txqs)[i]->stats.idx;
-               (*priv->txqs)[i]->stats =
-                       (struct mlx4_txq_stats){ .idx = idx };
+       for (i = 0; i != dev->data->nb_tx_queues; ++i) {
+               struct txq *txq = dev->data->tx_queues[i];
+
+               if (txq)
+                       txq->stats = (struct mlx4_txq_stats){
+                               .idx = txq->stats.idx,
+                       };
        }
 }