mlxsw: spectrum: Prevent duplicate mirrors
authorPetr Machata <petrm@mellanox.com>
Fri, 9 Mar 2018 13:33:53 +0000 (15:33 +0200)
committerDavid S. Miller <davem@davemloft.net>
Fri, 9 Mar 2018 18:02:14 +0000 (13:02 -0500)
The Spectrum ASIC doesn't support mirroring more than once from a single
binding point (which is a port-direction pair). Therefore detect that a
second binding of a given binding point is attempted.

To that end, extend struct mlxsw_sp_span_inspected_port to track whether
a given binding point is bound or not. Extend
mlxsw_sp_span_entry_port_find() to look for ports based on the full
unique key: port number, direction, and boundness.

Besides fixing the overt bug where configured mirrors are not offloaded,
this also fixes a more subtle bug: mlxsw_sp_span_inspected_port_del()
just defers to mlxsw_sp_span_entry_bound_port_find(), and that used to
find the first port with the right number (disregarding the type). Thus
by adding and removing egress and ingress mirrors in the right order,
one could trick the system into believing it has no egress mirrors when
in fact it did have some. That then caused that
mlxsw_sp_span_port_mtu_update() didn't update mirroring buffer when MTU
was changed.

Fixes: 763b4b70afcd ("mlxsw: spectrum: Add support in matchall mirror TC offloading")
Signed-off-by: Petr Machata <petrm@mellanox.com>
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/mellanox/mlxsw/spectrum.c
drivers/net/ethernet/mellanox/mlxsw/spectrum.h

index c7e941aecc2a906861a9ab8ef19ad7071a60f1e6..bf400c75fcc8bc82253fe5237a476655ce323bf4 100644 (file)
@@ -655,13 +655,17 @@ static int mlxsw_sp_span_port_mtu_update(struct mlxsw_sp_port *port, u16 mtu)
 }
 
 static struct mlxsw_sp_span_inspected_port *
-mlxsw_sp_span_entry_bound_port_find(struct mlxsw_sp_port *port,
-                                   struct mlxsw_sp_span_entry *span_entry)
+mlxsw_sp_span_entry_bound_port_find(struct mlxsw_sp_span_entry *span_entry,
+                                   enum mlxsw_sp_span_type type,
+                                   struct mlxsw_sp_port *port,
+                                   bool bind)
 {
        struct mlxsw_sp_span_inspected_port *p;
 
        list_for_each_entry(p, &span_entry->bound_ports_list, list)
-               if (port->local_port == p->local_port)
+               if (type == p->type &&
+                   port->local_port == p->local_port &&
+                   bind == p->bound)
                        return p;
        return NULL;
 }
@@ -691,8 +695,22 @@ mlxsw_sp_span_inspected_port_add(struct mlxsw_sp_port *port,
        struct mlxsw_sp_span_inspected_port *inspected_port;
        struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
        char sbib_pl[MLXSW_REG_SBIB_LEN];
+       int i;
        int err;
 
+       /* A given (source port, direction) can only be bound to one analyzer,
+        * so if a binding is requested, check for conflicts.
+        */
+       if (bind)
+               for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
+                       struct mlxsw_sp_span_entry *curr =
+                               &mlxsw_sp->span.entries[i];
+
+                       if (mlxsw_sp_span_entry_bound_port_find(curr, type,
+                                                               port, bind))
+                               return -EEXIST;
+               }
+
        /* if it is an egress SPAN, bind a shared buffer to it */
        if (type == MLXSW_SP_SPAN_EGRESS) {
                u32 buffsize = mlxsw_sp_span_mtu_to_buffsize(mlxsw_sp,
@@ -720,6 +738,7 @@ mlxsw_sp_span_inspected_port_add(struct mlxsw_sp_port *port,
        }
        inspected_port->local_port = port->local_port;
        inspected_port->type = type;
+       inspected_port->bound = bind;
        list_add_tail(&inspected_port->list, &span_entry->bound_ports_list);
 
        return 0;
@@ -746,7 +765,8 @@ mlxsw_sp_span_inspected_port_del(struct mlxsw_sp_port *port,
        struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
        char sbib_pl[MLXSW_REG_SBIB_LEN];
 
-       inspected_port = mlxsw_sp_span_entry_bound_port_find(port, span_entry);
+       inspected_port = mlxsw_sp_span_entry_bound_port_find(span_entry, type,
+                                                            port, bind);
        if (!inspected_port)
                return;
 
index 386861773476963a5d4c5052766ec556d991b208..92064db2ae442136141b01e64832b1487acbe43d 100644 (file)
@@ -120,6 +120,9 @@ struct mlxsw_sp_span_inspected_port {
        struct list_head list;
        enum mlxsw_sp_span_type type;
        u8 local_port;
+
+       /* Whether this is a directly bound mirror (port-to-port) or an ACL. */
+       bool bound;
 };
 
 struct mlxsw_sp_span_entry {