[dpdk-dev] [PATCH 15/16] mlx4: fix support for multiple VLAN filters

Adrien Mazarguil adrien.mazarguil at 6wind.com
Sat Jun 6 01:15:19 CEST 2015


From: Olga Shern <olgas at mellanox.com>

This commit fixes the "Multiple RX VLAN filters can be configured, but only
the first one works" bug. Since a single flow specification cannot contain
several VLAN definitions, the flows table is extended with MLX4_MAX_VLAN_IDS
possible specifications per configured MAC address.

Signed-off-by: Olga Shern <olgas at mellanox.com>
Signed-off-by: Or Ami <ora at mellanox.com>
Signed-off-by: Adrien Mazarguil <adrien.mazarguil at 6wind.com>
---
 drivers/net/mlx4/mlx4.c | 174 ++++++++++++++++++++++++++++++++----------------
 1 file changed, 115 insertions(+), 59 deletions(-)

diff --git a/drivers/net/mlx4/mlx4.c b/drivers/net/mlx4/mlx4.c
index 4c0294a..4c4f693 100644
--- a/drivers/net/mlx4/mlx4.c
+++ b/drivers/net/mlx4/mlx4.c
@@ -33,8 +33,6 @@
 
 /*
  * Known limitations:
- * - Multiple RX VLAN filters can be configured, but only the first one
- *   works properly.
  * - RSS hash key and options cannot be modified.
  * - Hardware counters aren't implemented.
  */
@@ -227,11 +225,10 @@ struct rxq {
 	/* Faster callbacks that bypass Verbs. */
 	drv_exp_poll_cq_func ibv_exp_poll_cq;
 	/*
-	 * There is exactly one flow configured per MAC address. Each flow
-	 * may contain several specifications, one per configured VLAN ID.
+	 * Each VLAN ID requires a separate flow steering rule.
 	 */
 	BITFIELD_DECLARE(mac_configured, uint32_t, MLX4_MAX_MAC_ADDRESSES);
-	struct mlx_flow *mac_flow[MLX4_MAX_MAC_ADDRESSES];
+	struct mlx_flow *mac_flow[MLX4_MAX_MAC_ADDRESSES][MLX4_MAX_VLAN_IDS];
 	struct mlx_flow *promisc_flow; /* Promiscuous flow. */
 	struct mlx_flow *allmulti_flow; /* Multicast flow. */
 	unsigned int port_id; /* Port ID for incoming packets. */
@@ -1880,15 +1877,17 @@ rxq_free_elts(struct rxq *rxq)
 }
 
 /**
- * Unregister a MAC address from a RX queue.
+ * Delete flow steering rule.
  *
  * @param rxq
  *   Pointer to RX queue structure.
  * @param mac_index
  *   MAC address index.
+ * @param vlan_index
+ *   VLAN index.
  */
 static void
-rxq_mac_addr_del(struct rxq *rxq, unsigned int mac_index)
+rxq_del_flow(struct rxq *rxq, unsigned int mac_index, unsigned int vlan_index)
 {
 #ifndef NDEBUG
 	struct priv *priv = rxq->priv;
@@ -1896,20 +1895,43 @@ rxq_mac_addr_del(struct rxq *rxq, unsigned int mac_index)
 		(const uint8_t (*)[ETHER_ADDR_LEN])
 		priv->mac[mac_index].addr_bytes;
 #endif
+	assert(rxq->mac_flow[mac_index][vlan_index] != NULL);
+	DEBUG("%p: removing MAC address %02x:%02x:%02x:%02x:%02x:%02x index %u"
+	      " (VLAN ID %" PRIu16 ")",
+	      (void *)rxq,
+	      (*mac)[0], (*mac)[1], (*mac)[2], (*mac)[3], (*mac)[4], (*mac)[5],
+	      mac_index, priv->vlan_filter[vlan_index].id);
+	claim_zero(mlx_destroy_flow(rxq->mac_flow[mac_index][vlan_index]));
+	rxq->mac_flow[mac_index][vlan_index] = NULL;
+}
+
+/**
+ * Unregister a MAC address from a RX queue.
+ *
+ * @param rxq
+ *   Pointer to RX queue structure.
+ * @param mac_index
+ *   MAC address index.
+ */
+static void
+rxq_mac_addr_del(struct rxq *rxq, unsigned int mac_index)
+{
+	struct priv *priv = rxq->priv;
+	unsigned int i;
+	unsigned int vlans = 0;
 
 	assert(mac_index < elemof(priv->mac));
-	if (!BITFIELD_ISSET(rxq->mac_configured, mac_index)) {
-		assert(rxq->mac_flow[mac_index] == NULL);
+	if (!BITFIELD_ISSET(rxq->mac_configured, mac_index))
 		return;
+	for (i = 0; (i != elemof(priv->vlan_filter)); ++i) {
+		if (!priv->vlan_filter[i].enabled)
+			continue;
+		rxq_del_flow(rxq, mac_index, i);
+		vlans++;
+	}
+	if (!vlans) {
+		rxq_del_flow(rxq, mac_index, 0);
 	}
-	DEBUG("%p: removing MAC address %02x:%02x:%02x:%02x:%02x:%02x"
-	      " index %u",
-	      (void *)rxq,
-	      (*mac)[0], (*mac)[1], (*mac)[2], (*mac)[3], (*mac)[4], (*mac)[5],
-	      mac_index);
-	assert(rxq->mac_flow[mac_index] != NULL);
-	claim_zero(mlx_destroy_flow(rxq->mac_flow[mac_index]));
-	rxq->mac_flow[mac_index] = NULL;
 	BITFIELD_RESET(rxq->mac_configured, mac_index);
 }
 
@@ -1933,47 +1955,37 @@ static int rxq_promiscuous_enable(struct rxq *);
 static void rxq_promiscuous_disable(struct rxq *);
 
 /**
- * Register a MAC address in a RX queue.
+ * Add single flow steering rule.
  *
  * @param rxq
  *   Pointer to RX queue structure.
  * @param mac_index
  *   MAC address index to register.
+ * @param vlan_index
+ *   VLAN index. Use -1 for a flow without VLAN.
  *
  * @return
  *   0 on success, errno value on failure.
  */
 static int
-rxq_mac_addr_add(struct rxq *rxq, unsigned int mac_index)
+rxq_add_flow(struct rxq *rxq, unsigned int mac_index, unsigned int vlan_index)
 {
+	struct mlx_flow *flow;
 	struct priv *priv = rxq->priv;
 	const uint8_t (*mac)[ETHER_ADDR_LEN] =
-		(const uint8_t (*)[ETHER_ADDR_LEN])
-		priv->mac[mac_index].addr_bytes;
-	unsigned int vlans = 0;
-	unsigned int specs = 0;
-	unsigned int i, j;
-	struct mlx_flow *flow;
-
-	assert(mac_index < elemof(priv->mac));
-	if (BITFIELD_ISSET(rxq->mac_configured, mac_index))
-		rxq_mac_addr_del(rxq, mac_index);
-	/* Number of configured VLANs. */
-	for (i = 0; (i != elemof(priv->vlan_filter)); ++i)
-		if (priv->vlan_filter[i].enabled)
-			++vlans;
-	specs = (vlans ? vlans : 1);
+			(const uint8_t (*)[ETHER_ADDR_LEN])
+			priv->mac[mac_index].addr_bytes;
 
 	/* Allocate flow specification on the stack. */
-	struct mlx_flow_attr data
-		[1 +
-		 (sizeof(struct mlx_flow_spec_eth[specs]) /
-		  sizeof(struct mlx_flow_attr)) +
-		 !!(sizeof(struct mlx_flow_spec_eth[specs]) %
-		    sizeof(struct mlx_flow_attr))];
-	struct mlx_flow_attr *attr = (void *)&data[0];
-	struct mlx_flow_spec_eth *spec = (void *)&data[1];
+	struct __attribute__((packed)) {
+		struct mlx_flow_attr attr;
+		struct mlx_flow_spec_eth spec;
+	} data;
+	struct mlx_flow_attr *attr = &data.attr;
+	struct mlx_flow_spec_eth *spec = &data.spec;
 
+	assert(mac_index < elemof(priv->mac));
+	assert((vlan_index < elemof(priv->vlan_filter)) || (vlan_index == -1u));
 	/*
 	 * No padding must be inserted by the compiler between attr and spec.
 	 * This layout is expected by libibverbs.
@@ -1981,7 +1993,7 @@ rxq_mac_addr_add(struct rxq *rxq, unsigned int mac_index)
 	assert(((uint8_t *)attr + sizeof(*attr)) == (uint8_t *)spec);
 	*attr = (struct mlx_flow_attr) {
 		.type = MLX_FLOW_ATTR_NORMAL,
-		.num_of_specs = specs,
+		.num_of_specs = 1,
 		.port = priv->port,
 		.flags = 0
 	};
@@ -1992,29 +2004,23 @@ rxq_mac_addr_add(struct rxq *rxq, unsigned int mac_index)
 			.dst_mac = {
 				(*mac)[0], (*mac)[1], (*mac)[2],
 				(*mac)[3], (*mac)[4], (*mac)[5]
-			}
+			},
+			.vlan_tag = ((vlan_index != -1u) ?
+				     htons(priv->vlan_filter[vlan_index].id) :
+				     0),
 		},
 		.mask = {
 			.dst_mac = "\xff\xff\xff\xff\xff\xff",
-			.vlan_tag = (vlans ? htons(0xfff) : 0)
+			.vlan_tag = ((vlan_index != -1u) ? htons(0xfff) : 0),
 		}
 	};
-	/* Fill VLAN specifications. */
-	for (i = 0, j = 0; (i != elemof(priv->vlan_filter)); ++i) {
-		if (!priv->vlan_filter[i].enabled)
-			continue;
-		assert(j != vlans);
-		if (j)
-			spec[j] = spec[0];
-		spec[j].val.vlan_tag = htons(priv->vlan_filter[i].id);
-		++j;
-	}
 	DEBUG("%p: adding MAC address %02x:%02x:%02x:%02x:%02x:%02x index %u"
-	      " (%u VLAN(s) configured)",
+	      " (VLAN %s %" PRIu16 ")",
 	      (void *)rxq,
 	      (*mac)[0], (*mac)[1], (*mac)[2], (*mac)[3], (*mac)[4], (*mac)[5],
 	      mac_index,
-	      vlans);
+	      ((vlan_index != -1u) ? "ID" : "index"),
+	      ((vlan_index != -1u) ? priv->vlan_filter[vlan_index].id : -1u));
 	/* Create related flow. */
 	errno = 0;
 	flow = mlx_create_flow(rxq->qp, attr);
@@ -2027,8 +2033,58 @@ rxq_mac_addr_add(struct rxq *rxq, unsigned int mac_index)
 			return errno;
 		return EINVAL;
 	}
-	assert(rxq->mac_flow[mac_index] == NULL);
-	rxq->mac_flow[mac_index] = flow;
+	if (vlan_index == -1u)
+		vlan_index = 0;
+	assert(rxq->mac_flow[mac_index][vlan_index] == NULL);
+	rxq->mac_flow[mac_index][vlan_index] = flow;
+	return 0;
+}
+
+/**
+ * Register a MAC address in a RX queue.
+ *
+ * @param rxq
+ *   Pointer to RX queue structure.
+ * @param mac_index
+ *   MAC address index to register.
+ *
+ * @return
+ *   0 on success, errno value on failure.
+ */
+static int
+rxq_mac_addr_add(struct rxq *rxq, unsigned int mac_index)
+{
+	struct priv *priv = rxq->priv;
+	unsigned int i;
+	unsigned int vlans = 0;
+	int ret;
+
+	assert(mac_index < elemof(priv->mac));
+	if (BITFIELD_ISSET(rxq->mac_configured, mac_index))
+		rxq_mac_addr_del(rxq, mac_index);
+	/* Fill VLAN specifications. */
+	for (i = 0; (i != elemof(priv->vlan_filter)); ++i) {
+		if (!priv->vlan_filter[i].enabled)
+			continue;
+		/* Create related flow. */
+		ret = rxq_add_flow(rxq, mac_index, i);
+		if (!ret) {
+			vlans++;
+			continue;
+		}
+		/* Failure, rollback. */
+		while (i != 0)
+			if (priv->vlan_filter[--i].enabled)
+				rxq_del_flow(rxq, mac_index, i);
+		assert(ret > 0);
+		return ret;
+	}
+	/* In case there is no VLAN filter. */
+	if (!vlans) {
+		ret = rxq_add_flow(rxq, mac_index, -1);
+		if (ret)
+			return ret;
+	}
 	BITFIELD_SET(rxq->mac_configured, mac_index);
 	return 0;
 }
-- 
2.1.0



More information about the dev mailing list