mlxsw: spectrum: Set port's mode according to FID mappings
authorIdo Schimmel <idosch@mellanox.com>
Fri, 26 May 2017 06:37:25 +0000 (08:37 +0200)
committerDavid S. Miller <davem@davemloft.net>
Fri, 26 May 2017 19:18:45 +0000 (15:18 -0400)
We currently transition the port to "Virtual mode" upon the creation of
its first VLAN upper, as we need to classify incoming packets to a FID
using {Port, VID} and not only the VID.

However, it's more appropriate to transition the port to this mode when
the {Port, VID} are actually mapped to a FID. Either during the
enslavement of the VLAN upper to a VLAN-unaware bridge or the
configuration of a router port.

Do this change now in preparation for the introduction of the FID core,
where this operation will be encapsulated.

To prevent regressions, this patch also explicitly configures an OVS
slave to "Virtual mode". Otherwise, a packet that didn't hit an ACL rule
could be classified to an existing FID based on a global VID-to-FID
mapping, thus not incurring a FID mis-classification, which would
otherwise trap the packet to the CPU to be processed by the OVS daemon.

Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@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
drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c

index 9594e9d..5d67336 100644 (file)
@@ -1398,7 +1398,7 @@ int mlxsw_sp_port_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid_begin,
        return 0;
 }
 
-static int mlxsw_sp_port_vp_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port)
+int mlxsw_sp_port_vp_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port)
 {
        enum mlxsw_reg_svfa_mt mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID;
        u16 vid, last_visited_vid;
@@ -1428,7 +1428,7 @@ err_port_vid_to_fid_set:
        return err;
 }
 
-static int mlxsw_sp_port_vlan_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port)
+int mlxsw_sp_port_vlan_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port)
 {
        enum mlxsw_reg_svfa_mt mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID;
        u16 vid;
@@ -1501,16 +1501,6 @@ static int mlxsw_sp_port_add_vid(struct net_device *dev,
        if (!mlxsw_sp_vport)
                return -ENOMEM;
 
-       /* When adding the first VLAN interface on a bridged port we need to
-        * transition all the active 802.1Q bridge VLANs to use explicit
-        * {Port, VID} to FID mappings and set the port's mode to Virtual mode.
-        */
-       if (list_is_singular(&mlxsw_sp_port->vports_list)) {
-               err = mlxsw_sp_port_vp_mode_trans(mlxsw_sp_port);
-               if (err)
-                       goto err_port_vp_mode_trans;
-       }
-
        err = mlxsw_sp_port_vlan_set(mlxsw_sp_vport, vid, vid, true, untagged);
        if (err)
                goto err_port_add_vid;
@@ -1518,9 +1508,6 @@ static int mlxsw_sp_port_add_vid(struct net_device *dev,
        return 0;
 
 err_port_add_vid:
-       if (list_is_singular(&mlxsw_sp_port->vports_list))
-               mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port);
-err_port_vp_mode_trans:
        mlxsw_sp_port_vport_destroy(mlxsw_sp_vport);
        return err;
 }
@@ -1551,13 +1538,6 @@ static int mlxsw_sp_port_kill_vid(struct net_device *dev,
        if (f && !WARN_ON(!f->leave))
                f->leave(mlxsw_sp_vport);
 
-       /* When removing the last VLAN interface on a bridged port we need to
-        * transition all active 802.1Q bridge VLANs to use VID to FID
-        * mappings and set port's mode to VLAN mode.
-        */
-       if (list_is_singular(&mlxsw_sp_port->vports_list))
-               mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port);
-
        mlxsw_sp_port_vport_destroy(mlxsw_sp_vport);
 
        return 0;
@@ -4382,9 +4362,12 @@ static int mlxsw_sp_port_ovs_join(struct mlxsw_sp_port *mlxsw_sp_port)
 {
        int err;
 
-       err = mlxsw_sp_port_stp_set(mlxsw_sp_port, true);
+       err = mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, true);
        if (err)
                return err;
+       err = mlxsw_sp_port_stp_set(mlxsw_sp_port, true);
+       if (err)
+               goto err_port_stp_set;
        err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, 2, VLAN_N_VID - 1,
                                     true, false);
        if (err)
@@ -4393,6 +4376,8 @@ static int mlxsw_sp_port_ovs_join(struct mlxsw_sp_port *mlxsw_sp_port)
 
 err_port_vlan_set:
        mlxsw_sp_port_stp_set(mlxsw_sp_port, false);
+err_port_stp_set:
+       mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, false);
        return err;
 }
 
@@ -4401,6 +4386,7 @@ static void mlxsw_sp_port_ovs_leave(struct mlxsw_sp_port *mlxsw_sp_port)
        mlxsw_sp_port_vlan_set(mlxsw_sp_port, 2, VLAN_N_VID - 1,
                               false, false);
        mlxsw_sp_port_stp_set(mlxsw_sp_port, false);
+       mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, false);
 }
 
 static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev,
@@ -4695,6 +4681,7 @@ static int mlxsw_sp_vport_fid_map(struct mlxsw_sp_port *mlxsw_sp_vport, u16 fid,
 static int mlxsw_sp_vport_vfid_join(struct mlxsw_sp_port *mlxsw_sp_vport,
                                    struct net_device *br_dev)
 {
+       struct mlxsw_sp_port *mlxsw_sp_port;
        struct mlxsw_sp_fid *f;
        int err;
 
@@ -4713,6 +4700,13 @@ static int mlxsw_sp_vport_vfid_join(struct mlxsw_sp_port *mlxsw_sp_vport,
        if (err)
                goto err_vport_fid_map;
 
+       mlxsw_sp_port = mlxsw_sp_vport_port(mlxsw_sp_vport);
+       if (mlxsw_sp_port->nr_port_vid_map++ == 0) {
+               err = mlxsw_sp_port_vp_mode_trans(mlxsw_sp_port);
+               if (err)
+                       goto err_port_vp_mode_trans;
+       }
+
        mlxsw_sp_vport_fid_set(mlxsw_sp_vport, f);
        f->ref_count++;
 
@@ -4720,6 +4714,9 @@ static int mlxsw_sp_vport_vfid_join(struct mlxsw_sp_port *mlxsw_sp_vport,
 
        return 0;
 
+err_port_vp_mode_trans:
+       mlxsw_sp_port->nr_port_vid_map--;
+       mlxsw_sp_vport_fid_map(mlxsw_sp_vport, f->fid, false);
 err_vport_fid_map:
        mlxsw_sp_vport_flood_set(mlxsw_sp_vport, f->fid, false);
 err_vport_flood_set:
@@ -4731,17 +4728,25 @@ err_vport_flood_set:
 static void mlxsw_sp_vport_vfid_leave(struct mlxsw_sp_port *mlxsw_sp_vport)
 {
        struct mlxsw_sp_fid *f = mlxsw_sp_vport_fid_get(mlxsw_sp_vport);
+       struct mlxsw_sp_port *mlxsw_sp_port;
 
        netdev_dbg(mlxsw_sp_vport->dev, "Left FID=%d\n", f->fid);
 
+       mlxsw_sp_vport_fid_set(mlxsw_sp_vport, NULL);
+       f->ref_count--;
+
+       mlxsw_sp_port = mlxsw_sp_vport_port(mlxsw_sp_vport);
+       if (mlxsw_sp_port->nr_port_vid_map == 1)
+               mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port);
+       mlxsw_sp_port->nr_port_vid_map--;
+
        mlxsw_sp_vport_fid_map(mlxsw_sp_vport, f->fid, false);
 
        mlxsw_sp_vport_flood_set(mlxsw_sp_vport, f->fid, false);
 
        mlxsw_sp_port_fdb_flush(mlxsw_sp_vport, f->fid);
 
-       mlxsw_sp_vport_fid_set(mlxsw_sp_vport, NULL);
-       if (--f->ref_count == 0)
+       if (f->ref_count == 0)
                mlxsw_sp_vfid_destroy(mlxsw_sp_vport->mlxsw_sp, f);
 }
 
index 7caf175..277a432 100644 (file)
@@ -253,6 +253,7 @@ struct mlxsw_sp_port {
                struct delayed_work update_dw;
        } hw_stats;
        struct mlxsw_sp_port_sample *sample;
+       unsigned int nr_port_vid_map;  /* {Port, VID} => FID mappings */
 };
 
 bool mlxsw_sp_port_dev_check(const struct net_device *dev);
@@ -343,6 +344,14 @@ mlxsw_sp_port_vport_find_by_fid(const struct mlxsw_sp_port *mlxsw_sp_port,
        return NULL;
 }
 
+static inline struct mlxsw_sp_port *
+mlxsw_sp_vport_port(const struct mlxsw_sp_port *mlxsw_sp_vport)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp;
+
+       return mlxsw_sp->ports[mlxsw_sp_vport->local_port];
+}
+
 static inline struct mlxsw_sp_fid *mlxsw_sp_fid_find(struct mlxsw_sp *mlxsw_sp,
                                                     u16 fid)
 {
@@ -446,6 +455,8 @@ int mlxsw_sp_port_vid_stp_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid,
 int mlxsw_sp_port_vid_learning_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid,
                                   bool learn_enable);
 int mlxsw_sp_port_pvid_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid);
+int mlxsw_sp_port_vp_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port);
+int mlxsw_sp_port_vlan_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port);
 
 #ifdef CONFIG_MLXSW_SPECTRUM_DCB
 
index 8165b11..cb5e86a 100644 (file)
@@ -3111,6 +3111,7 @@ static int mlxsw_sp_vport_rif_sp_join(struct mlxsw_sp_port *mlxsw_sp_vport,
 {
        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp;
        u16 vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport);
+       struct mlxsw_sp_port *mlxsw_sp_port;
        struct mlxsw_sp_rif *rif;
        int err;
 
@@ -3130,6 +3131,13 @@ static int mlxsw_sp_vport_rif_sp_join(struct mlxsw_sp_port *mlxsw_sp_vport,
        if (err)
                goto err_port_vid_stp_set;
 
+       mlxsw_sp_port = mlxsw_sp_vport_port(mlxsw_sp_vport);
+       if (mlxsw_sp_port->nr_port_vid_map++ == 0) {
+               err = mlxsw_sp_port_vp_mode_trans(mlxsw_sp_port);
+               if (err)
+                       goto err_port_vp_mode_trans;
+       }
+
        mlxsw_sp_vport_fid_set(mlxsw_sp_vport, rif->f);
        rif->f->ref_count++;
 
@@ -3137,6 +3145,9 @@ static int mlxsw_sp_vport_rif_sp_join(struct mlxsw_sp_port *mlxsw_sp_vport,
 
        return 0;
 
+err_port_vp_mode_trans:
+       mlxsw_sp_port->nr_port_vid_map--;
+       mlxsw_sp_port_vid_stp_set(mlxsw_sp_vport, vid, BR_STATE_BLOCKING);
 err_port_vid_stp_set:
        mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, true);
 err_port_vid_learning_set:
@@ -3149,13 +3160,21 @@ static void mlxsw_sp_vport_rif_sp_leave(struct mlxsw_sp_port *mlxsw_sp_vport)
 {
        struct mlxsw_sp_fid *f = mlxsw_sp_vport_fid_get(mlxsw_sp_vport);
        u16 vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport);
+       struct mlxsw_sp_port *mlxsw_sp_port;
 
        netdev_dbg(mlxsw_sp_vport->dev, "Left FID=%d\n", f->fid);
 
+       f->ref_count--;
+       mlxsw_sp_vport_fid_set(mlxsw_sp_vport, NULL);
+
+       mlxsw_sp_port = mlxsw_sp_vport_port(mlxsw_sp_vport);
+       if (mlxsw_sp_port->nr_port_vid_map == 1)
+               mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port);
+       mlxsw_sp_port->nr_port_vid_map--;
        mlxsw_sp_port_vid_stp_set(mlxsw_sp_vport, vid, BR_STATE_BLOCKING);
        mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, true);
-       mlxsw_sp_vport_fid_set(mlxsw_sp_vport, NULL);
-       if (--f->ref_count == 0)
+
+       if (f->ref_count == 0)
                mlxsw_sp_vport_rif_sp_destroy(mlxsw_sp_vport, f->rif);
 }
 
index ea0f4a5..0d173be 100644 (file)
@@ -617,7 +617,7 @@ static int mlxsw_sp_port_fid_map(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid,
        /* If port doesn't have vPorts, then it can use the global
         * VID-to-FID mapping.
         */
-       if (list_empty(&mlxsw_sp_port->vports_list))
+       if (mlxsw_sp_port->nr_port_vid_map == 0)
                return 0;
 
        return mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, mt, valid, fid, fid);