Implement promiscuous and all multicast through internal flow rules
automatically generated according to the configured mode.

Signed-off-by: Adrien Mazarguil <adrien.mazarg...@6wind.com>
Acked-by: Nelio Laranjeiro <nelio.laranje...@6wind.com>
---
 doc/guides/nics/features/mlx4.ini |  2 +
 drivers/net/mlx4/mlx4.c           |  4 ++
 drivers/net/mlx4/mlx4.h           |  4 ++
 drivers/net/mlx4/mlx4_ethdev.c    | 95 ++++++++++++++++++++++++++++++++++
 drivers/net/mlx4/mlx4_flow.c      | 63 +++++++++++++++++++---
 5 files changed, 162 insertions(+), 6 deletions(-)

diff --git a/doc/guides/nics/features/mlx4.ini 
b/doc/guides/nics/features/mlx4.ini
index 9e3ba34..6f8c82a 100644
--- a/doc/guides/nics/features/mlx4.ini
+++ b/doc/guides/nics/features/mlx4.ini
@@ -12,6 +12,8 @@ Rx interrupt         = Y
 Queue start/stop     = Y
 MTU update           = Y
 Jumbo frame          = Y
+Promiscuous mode     = Y
+Allmulticast mode    = Y
 Unicast MAC filter   = Y
 Multicast MAC filter = Y
 SR-IOV               = Y
diff --git a/drivers/net/mlx4/mlx4.c b/drivers/net/mlx4/mlx4.c
index e25e958..f02508a 100644
--- a/drivers/net/mlx4/mlx4.c
+++ b/drivers/net/mlx4/mlx4.c
@@ -221,6 +221,10 @@ static const struct eth_dev_ops mlx4_dev_ops = {
        .dev_set_link_up = mlx4_dev_set_link_up,
        .dev_close = mlx4_dev_close,
        .link_update = mlx4_link_update,
+       .promiscuous_enable = mlx4_promiscuous_enable,
+       .promiscuous_disable = mlx4_promiscuous_disable,
+       .allmulticast_enable = mlx4_allmulticast_enable,
+       .allmulticast_disable = mlx4_allmulticast_disable,
        .mac_addr_remove = mlx4_mac_addr_remove,
        .mac_addr_add = mlx4_mac_addr_add,
        .mac_addr_set = mlx4_mac_addr_set,
diff --git a/drivers/net/mlx4/mlx4.h b/drivers/net/mlx4/mlx4.h
index cc403ea..a27399a 100644
--- a/drivers/net/mlx4/mlx4.h
+++ b/drivers/net/mlx4/mlx4.h
@@ -124,6 +124,10 @@ int mlx4_mtu_get(struct priv *priv, uint16_t *mtu);
 int mlx4_mtu_set(struct rte_eth_dev *dev, uint16_t mtu);
 int mlx4_dev_set_link_down(struct rte_eth_dev *dev);
 int mlx4_dev_set_link_up(struct rte_eth_dev *dev);
+void mlx4_promiscuous_enable(struct rte_eth_dev *dev);
+void mlx4_promiscuous_disable(struct rte_eth_dev *dev);
+void mlx4_allmulticast_enable(struct rte_eth_dev *dev);
+void mlx4_allmulticast_disable(struct rte_eth_dev *dev);
 void mlx4_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index);
 int mlx4_mac_addr_add(struct rte_eth_dev *dev, struct ether_addr *mac_addr,
                      uint32_t index, uint32_t vmdq);
diff --git a/drivers/net/mlx4/mlx4_ethdev.c b/drivers/net/mlx4/mlx4_ethdev.c
index 7721f13..01fb195 100644
--- a/drivers/net/mlx4/mlx4_ethdev.c
+++ b/drivers/net/mlx4/mlx4_ethdev.c
@@ -520,6 +520,101 @@ mlx4_dev_set_link_up(struct rte_eth_dev *dev)
 }
 
 /**
+ * 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
diff --git a/drivers/net/mlx4/mlx4_flow.c b/drivers/net/mlx4/mlx4_flow.c
index 2ff1c69..2d826b4 100644
--- a/drivers/net/mlx4/mlx4_flow.c
+++ b/drivers/net/mlx4/mlx4_flow.c
@@ -1047,6 +1047,14 @@ mlx4_flow_internal_next_vlan(struct priv *priv, uint16_t 
vlan)
 /**
  * Generate internal flow rules.
  *
+ * Various flow rules are created depending on the mode the device is in:
+ *
+ * 1. Promiscuous: port MAC + catch-all (VLAN filtering is ignored).
+ * 2. All multicast: port MAC/VLAN + catch-all multicast.
+ * 3. Otherwise: port MAC/VLAN + broadcast MAC/VLAN.
+ *
+ * About MAC flow rules:
+ *
  * - MAC flow rules are generated from @p dev->data->mac_addrs
  *   (@p priv->mac array).
  * - An additional flow rule for Ethernet broadcasts is also generated.
@@ -1072,6 +1080,9 @@ mlx4_flow_internal(struct priv *priv, struct 
rte_flow_error *error)
        const struct rte_flow_item_eth eth_mask = {
                .dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
        };
+       const struct rte_flow_item_eth eth_allmulti = {
+               .dst.addr_bytes = "\x01\x00\x00\x00\x00\x00",
+       };
        struct rte_flow_item_vlan vlan_spec;
        const struct rte_flow_item_vlan vlan_mask = {
                .tci = RTE_BE16(0x0fff),
@@ -1106,9 +1117,13 @@ mlx4_flow_internal(struct priv *priv, struct 
rte_flow_error *error)
        };
        struct ether_addr *rule_mac = &eth_spec.dst;
        rte_be16_t *rule_vlan =
-               priv->dev->data->dev_conf.rxmode.hw_vlan_filter ?
+               priv->dev->data->dev_conf.rxmode.hw_vlan_filter &&
+               !priv->dev->data->promiscuous ?
                &vlan_spec.tci :
                NULL;
+       int broadcast =
+               !priv->dev->data->promiscuous &&
+               !priv->dev->data->all_multicast;
        uint16_t vlan = 0;
        struct rte_flow *flow;
        unsigned int i;
@@ -1132,7 +1147,7 @@ mlx4_flow_internal(struct priv *priv, struct 
rte_flow_error *error)
                        rule_vlan = NULL;
                }
        }
-       for (i = 0; i != RTE_DIM(priv->mac) + 1; ++i) {
+       for (i = 0; i != RTE_DIM(priv->mac) + broadcast; ++i) {
                const struct ether_addr *mac;
 
                /* Broadcasts are handled by an extra iteration. */
@@ -1178,23 +1193,59 @@ mlx4_flow_internal(struct priv *priv, struct 
rte_flow_error *error)
                                                actions, error);
                        if (!flow) {
                                err = -rte_errno;
-                               break;
+                               goto error;
                        }
                }
                flow->select = 1;
                flow->mac = 1;
        }
-       if (!err && rule_vlan) {
+       if (rule_vlan) {
                vlan = mlx4_flow_internal_next_vlan(priv, vlan + 1);
                if (vlan < 4096)
                        goto next_vlan;
        }
-       /* Clear selection and clean up stale MAC flow rules. */
+       /* Take care of promiscuous and all multicast flow rules. */
+       if (!broadcast) {
+               for (flow = LIST_FIRST(&priv->flows);
+                    flow && flow->internal;
+                    flow = LIST_NEXT(flow, next)) {
+                       if (priv->dev->data->promiscuous) {
+                               if (flow->promisc)
+                                       break;
+                       } else {
+                               assert(priv->dev->data->all_multicast);
+                               if (flow->allmulti)
+                                       break;
+                       }
+               }
+               if (!flow || !flow->internal) {
+                       /* Not found, create a new flow rule. */
+                       if (priv->dev->data->promiscuous) {
+                               pattern[1].spec = NULL;
+                               pattern[1].mask = NULL;
+                       } else {
+                               assert(priv->dev->data->all_multicast);
+                               pattern[1].spec = &eth_allmulti;
+                               pattern[1].mask = &eth_allmulti;
+                       }
+                       pattern[2] = pattern[3];
+                       flow = mlx4_flow_create(priv->dev, &attr, pattern,
+                                               actions, error);
+                       if (!flow) {
+                               err = -rte_errno;
+                               goto error;
+                       }
+               }
+               assert(flow->promisc || flow->allmulti);
+               flow->select = 1;
+       }
+error:
+       /* Clear selection and clean up stale internal flow rules. */
        flow = LIST_FIRST(&priv->flows);
        while (flow && flow->internal) {
                struct rte_flow *next = LIST_NEXT(flow, next);
 
-               if (flow->mac && !flow->select)
+               if (!flow->select)
                        claim_zero(mlx4_flow_destroy(priv->dev, flow, error));
                else
                        flow->select = 0;
-- 
2.1.4

Reply via email to