mlx5: support VLAN filtering
authorAdrien Mazarguil <adrien.mazarguil@6wind.com>
Fri, 30 Oct 2015 18:52:40 +0000 (19:52 +0100)
committerThomas Monjalon <thomas.monjalon@6wind.com>
Fri, 30 Oct 2015 21:39:30 +0000 (22:39 +0100)
All MAC RX flows must be updated with VLAN information when configuring a
VLAN filter.

Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Signed-off-by: Nelio Laranjeiro <nelio.laranjeiro@6wind.com>
drivers/net/mlx5/Makefile
drivers/net/mlx5/mlx5.c
drivers/net/mlx5/mlx5.h
drivers/net/mlx5/mlx5_defs.h
drivers/net/mlx5/mlx5_mac.c
drivers/net/mlx5/mlx5_rxtx.h
drivers/net/mlx5/mlx5_vlan.c [new file with mode: 0644]

index 4d25d9c..8b1e32b 100644 (file)
@@ -49,6 +49,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_trigger.c
 SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_ethdev.c
 SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_mac.c
 SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_rxmode.c
+SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_vlan.c
 SRCS-$(CONFIG_RTE_LIBRTE_MLX5_PMD) += mlx5_stats.c
 
 # Dependencies.
index c454e93..8f75f76 100644 (file)
@@ -141,6 +141,7 @@ static const struct eth_dev_ops mlx5_dev_ops = {
        .stats_get = mlx5_stats_get,
        .stats_reset = mlx5_stats_reset,
        .dev_infos_get = mlx5_dev_infos_get,
+       .vlan_filter_set = mlx5_vlan_filter_set,
        .rx_queue_setup = mlx5_rx_queue_setup,
        .tx_queue_setup = mlx5_tx_queue_setup,
        .rx_queue_release = mlx5_rx_queue_release,
index c6c3d3f..3a1e7a6 100644 (file)
@@ -90,6 +90,8 @@ struct priv {
         */
        struct ether_addr mac[MLX5_MAX_MAC_ADDRESSES];
        BITFIELD_DECLARE(mac_configured, uint32_t, MLX5_MAX_MAC_ADDRESSES);
+       uint16_t vlan_filter[MLX5_MAX_VLAN_IDS]; /* VLAN filters table. */
+       unsigned int vlan_filter_n; /* Number of configured VLAN filters. */
        /* Device properties. */
        uint16_t mtu; /* Configured MTU. */
        uint8_t port; /* Physical port number. */
@@ -198,6 +200,10 @@ void mlx5_allmulticast_disable(struct rte_eth_dev *);
 void mlx5_stats_get(struct rte_eth_dev *, struct rte_eth_stats *);
 void mlx5_stats_reset(struct rte_eth_dev *);
 
+/* mlx5_vlan.c */
+
+int mlx5_vlan_filter_set(struct rte_eth_dev *, uint16_t, int);
+
 /* mlx5_trigger.c */
 
 int mlx5_dev_start(struct rte_eth_dev *);
index d3a0d0e..369f8b6 100644 (file)
@@ -40,6 +40,9 @@
 /* Maximum number of simultaneous MAC addresses. */
 #define MLX5_MAX_MAC_ADDRESSES 128
 
+/* Maximum number of simultaneous VLAN filters. */
+#define MLX5_MAX_VLAN_IDS 128
+
 /* Request send completion once in every 64 sends, might be less. */
 #define MLX5_PMD_TX_PER_COMP_REQ 64
 
index 262d7c6..95afccf 100644 (file)
@@ -97,9 +97,12 @@ priv_get_mac(struct priv *priv, uint8_t (*mac)[ETHER_ADDR_LEN])
  *   Pointer to RX queue structure.
  * @param mac_index
  *   MAC address index.
+ * @param vlan_index
+ *   VLAN index to use.
  */
 static void
-rxq_del_mac_flow(struct rxq *rxq, unsigned int mac_index)
+rxq_del_mac_flow(struct rxq *rxq, unsigned int mac_index,
+                unsigned int vlan_index)
 {
 #ifndef NDEBUG
        const uint8_t (*mac)[ETHER_ADDR_LEN] =
@@ -108,14 +111,17 @@ rxq_del_mac_flow(struct rxq *rxq, unsigned int mac_index)
 #endif
 
        assert(mac_index < RTE_DIM(rxq->mac_flow));
-       if (rxq->mac_flow[mac_index] == NULL)
+       assert(vlan_index < RTE_DIM(rxq->mac_flow[mac_index]));
+       if (rxq->mac_flow[mac_index][vlan_index] == NULL)
                return;
-       DEBUG("%p: removing MAC address %02x:%02x:%02x:%02x:%02x:%02x index %u",
+       DEBUG("%p: removing MAC address %02x:%02x:%02x:%02x:%02x:%02x index %u"
+             " VLAN index %u",
              (void *)rxq,
              (*mac)[0], (*mac)[1], (*mac)[2], (*mac)[3], (*mac)[4], (*mac)[5],
-             mac_index);
-       claim_zero(ibv_destroy_flow(rxq->mac_flow[mac_index]));
-       rxq->mac_flow[mac_index] = NULL;
+             mac_index,
+             vlan_index);
+       claim_zero(ibv_destroy_flow(rxq->mac_flow[mac_index][vlan_index]));
+       rxq->mac_flow[mac_index][vlan_index] = NULL;
 }
 
 /**
@@ -129,8 +135,11 @@ rxq_del_mac_flow(struct rxq *rxq, unsigned int mac_index)
 static void
 rxq_mac_addr_del(struct rxq *rxq, unsigned int mac_index)
 {
+       unsigned int i;
+
        assert(mac_index < RTE_DIM(rxq->mac_flow));
-       rxq_del_mac_flow(rxq, mac_index);
+       for (i = 0; (i != RTE_DIM(rxq->mac_flow[mac_index])); ++i)
+               rxq_del_mac_flow(rxq, mac_index, i);
 }
 
 /**
@@ -208,12 +217,15 @@ end:
  *   Pointer to RX queue structure.
  * @param mac_index
  *   MAC address index to register.
+ * @param vlan_index
+ *   VLAN index to use.
  *
  * @return
  *   0 on success, errno value on failure.
  */
 static int
-rxq_add_mac_flow(struct rxq *rxq, unsigned int mac_index)
+rxq_add_mac_flow(struct rxq *rxq, unsigned int mac_index,
+                unsigned int vlan_index)
 {
        struct ibv_flow *flow;
        struct priv *priv = rxq->priv;
@@ -226,9 +238,12 @@ rxq_add_mac_flow(struct rxq *rxq, unsigned int mac_index)
        } data;
        struct ibv_flow_attr *attr = &data.attr;
        struct ibv_flow_spec_eth *spec = &data.spec;
+       unsigned int vlan_enabled = !!priv->vlan_filter_n;
+       unsigned int vlan_id = priv->vlan_filter[vlan_index];
 
        assert(mac_index < RTE_DIM(rxq->mac_flow));
-       if (rxq->mac_flow[mac_index] != NULL)
+       assert(vlan_index < RTE_DIM(rxq->mac_flow[mac_index]));
+       if (rxq->mac_flow[mac_index][vlan_index] != NULL)
                return 0;
        /*
         * No padding must be inserted by the compiler between attr and spec.
@@ -249,15 +264,21 @@ rxq_add_mac_flow(struct rxq *rxq, unsigned int mac_index)
                                (*mac)[0], (*mac)[1], (*mac)[2],
                                (*mac)[3], (*mac)[4], (*mac)[5]
                        },
+                       .vlan_tag = (vlan_enabled ? htons(vlan_id) : 0),
                },
                .mask = {
                        .dst_mac = "\xff\xff\xff\xff\xff\xff",
+                       .vlan_tag = (vlan_enabled ? htons(0xfff) : 0),
                },
        };
-       DEBUG("%p: adding MAC address %02x:%02x:%02x:%02x:%02x:%02x index %u",
+       DEBUG("%p: adding MAC address %02x:%02x:%02x:%02x:%02x:%02x index %u"
+             " VLAN index %u filtering %s, ID %u",
              (void *)rxq,
              (*mac)[0], (*mac)[1], (*mac)[2], (*mac)[3], (*mac)[4], (*mac)[5],
-             mac_index);
+             mac_index,
+             vlan_index,
+             (vlan_enabled ? "enabled" : "disabled"),
+             vlan_id);
        /* Create related flow. */
        errno = 0;
        flow = ibv_create_flow(rxq->qp, attr);
@@ -270,7 +291,7 @@ rxq_add_mac_flow(struct rxq *rxq, unsigned int mac_index)
                        return errno;
                return EINVAL;
        }
-       rxq->mac_flow[mac_index] = flow;
+       rxq->mac_flow[mac_index][vlan_index] = flow;
        return 0;
 }
 
@@ -288,12 +309,23 @@ rxq_add_mac_flow(struct rxq *rxq, unsigned int mac_index)
 static int
 rxq_mac_addr_add(struct rxq *rxq, unsigned int mac_index)
 {
+       struct priv *priv = rxq->priv;
+       unsigned int i = 0;
        int ret;
 
        assert(mac_index < RTE_DIM(rxq->mac_flow));
-       ret = rxq_add_mac_flow(rxq, mac_index);
-       if (ret)
-               return ret;
+       assert(RTE_DIM(rxq->mac_flow[mac_index]) ==
+              RTE_DIM(priv->vlan_filter));
+       /* Add a MAC address for each VLAN filter, or at least once. */
+       do {
+               ret = rxq_add_mac_flow(rxq, mac_index, i);
+               if (ret) {
+                       /* Failure, rollback. */
+                       while (i != 0)
+                               rxq_del_mac_flow(rxq, mac_index, --i);
+                       return ret;
+               }
+       } while (++i < priv->vlan_filter_n);
        return 0;
 }
 
index 020acf0..521aee0 100644 (file)
@@ -104,8 +104,8 @@ struct rxq {
        struct ibv_qp *qp; /* Queue Pair. */
        struct ibv_exp_qp_burst_family *if_qp; /* QP burst interface. */
        struct ibv_exp_cq_family *if_cq; /* CQ interface. */
-       /* MAC flow steering rules. */
-       struct ibv_flow *mac_flow[MLX5_MAX_MAC_ADDRESSES];
+       /* MAC flow steering rules, one per VLAN ID. */
+       struct ibv_flow *mac_flow[MLX5_MAX_MAC_ADDRESSES][MLX5_MAX_VLAN_IDS];
        struct ibv_flow *promisc_flow; /* Promiscuous flow. */
        struct ibv_flow *allmulti_flow; /* Multicast flow. */
        unsigned int port_id; /* Port ID for incoming packets. */
diff --git a/drivers/net/mlx5/mlx5_vlan.c b/drivers/net/mlx5/mlx5_vlan.c
new file mode 100644 (file)
index 0000000..ca80571
--- /dev/null
@@ -0,0 +1,156 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright 2015 6WIND S.A.
+ *   Copyright 2015 Mellanox.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of 6WIND S.A. nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stddef.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdint.h>
+
+/* DPDK headers don't like -pedantic. */
+#ifdef PEDANTIC
+#pragma GCC diagnostic ignored "-pedantic"
+#endif
+#include <rte_ethdev.h>
+#include <rte_common.h>
+#ifdef PEDANTIC
+#pragma GCC diagnostic error "-pedantic"
+#endif
+
+#include "mlx5_utils.h"
+#include "mlx5.h"
+
+/**
+ * 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, errno value on failure.
+ */
+static int
+vlan_filter_set(struct rte_eth_dev *dev, uint16_t vlan_id, int on)
+{
+       struct priv *priv = dev->data->dev_private;
+       unsigned int i;
+       unsigned int r;
+       struct rxq *rxq;
+
+       DEBUG("%p: %s VLAN filter ID %" PRIu16,
+             (void *)dev, (on ? "enable" : "disable"), vlan_id);
+       assert(priv->vlan_filter_n <= RTE_DIM(priv->vlan_filter));
+       for (i = 0; (i != priv->vlan_filter_n); ++i)
+               if (priv->vlan_filter[i] == vlan_id)
+                       break;
+       /* Check if there's room for another VLAN filter. */
+       if (i == RTE_DIM(priv->vlan_filter))
+               return ENOMEM;
+       if (i < priv->vlan_filter_n) {
+               assert(priv->vlan_filter_n != 0);
+               /* Enabling an existing VLAN filter has no effect. */
+               if (on)
+                       return 0;
+               /* Remove VLAN filter from list. */
+               --priv->vlan_filter_n;
+               memmove(&priv->vlan_filter[i],
+                       &priv->vlan_filter[i + 1],
+                       priv->vlan_filter_n - i);
+               priv->vlan_filter[priv->vlan_filter_n] = 0;
+       } else {
+               assert(i == priv->vlan_filter_n);
+               /* Disabling an unknown VLAN filter has no effect. */
+               if (!on)
+                       return 0;
+               /* Add new VLAN filter. */
+               priv->vlan_filter[priv->vlan_filter_n] = vlan_id;
+               ++priv->vlan_filter_n;
+       }
+       if (!priv->started)
+               return 0;
+       /* Rehash MAC flows in all RX queues. */
+       if (priv->rss) {
+               rxq = &priv->rxq_parent;
+               r = 1;
+       } else {
+               rxq = (*priv->rxqs)[0];
+               r = priv->rxqs_n;
+       }
+       for (i = 0; (i < r); rxq = (*priv->rxqs)[++i]) {
+               int ret;
+
+               if (rxq == NULL)
+                       continue;
+               rxq_mac_addrs_del(rxq);
+               ret = rxq_mac_addrs_add(rxq);
+               if (!ret)
+                       continue;
+               /* Rollback. */
+               while (i != 0) {
+                       rxq = (*priv->rxqs)[--i];
+                       if (rxq != NULL)
+                               rxq_mac_addrs_del(rxq);
+               }
+               return ret;
+       }
+       return 0;
+}
+
+/**
+ * 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 on failure.
+ */
+int
+mlx5_vlan_filter_set(struct rte_eth_dev *dev, uint16_t vlan_id, int on)
+{
+       struct priv *priv = dev->data->dev_private;
+       int ret;
+
+       priv_lock(priv);
+       ret = vlan_filter_set(dev, vlan_id, on);
+       priv_unlock(priv);
+       assert(ret >= 0);
+       return -ret;
+}