Commit 7f71eb46 authored by Ido Schimmel's avatar Ido Schimmel Committed by David S. Miller

mlxsw: spectrum: Split vFID range in two

Up until now we used a 1:1 mapping - based on VID - to map a VLAN
interface to a vFID. However, a different scheme is needed in order to
support bridges between VLAN interfaces, as all the member interfaces -
which can have different VIDs - need to share the same vFID.

Solve that by splitting the vFID range in two:
 1. Non-bridged VLAN interfaces
 2. Bridged VLAN interfaces

When a VLAN interface is created, assign it the next available vFID in
the first range, unless one already exists for that VID or number of
vFIDs in the range was exceeded. When interface is removed, free the
vFID, unless other interfaces are mapped to it.

To accomplish the above:
 1. Store the VID to vFID mapping in a new struct (mlxsw_sp_vfid), which
    has a global context and holds a reference count.
 2. Create a vPort (dummy in case of bridge SELF invocation) on top of
    of the physical port and hold a reference to the associated vFID.

	     vfid                    vfid
	+-------------+	        +-------------+
	| vfid        |         | vfid        |
	| vid         +---> ... | vid         |
	| nr_vports   |         | nr_vports   |
	+------+------+         +------+------+
				       |
	       +-----------------------+-------+
	       |			       |
	     vport			     vport
	+-------------+         	+-------------+
	| ...	      |         	| ...	      |
	| *vfid	      +---> ... 	| *vfid	      +---> ...
	| ...	      |         	| ...	      |
	+------+------+         	+------+------+
	       |                               |
	     port			     port
	+-------------+         	+-------------+
	| ...         |         	| ...         |
	| vports_list |         	| vports_list |
	| ...         |         	| ...         |
	+-------------+         	+-------------+
	     swXpY			     swXpZ

Next patches in the series will add the missing infrastructure for the
second range and transfer vPorts between the two ranges according to the
received notifications.
Signed-off-by: default avatarIdo Schimmel <idosch@mellanox.com>
Signed-off-by: default avatarJiri Pirko <jiri@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent bd40e9d6
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/list.h>
#include <net/switchdev.h> #include <net/switchdev.h>
#include <generated/utsrelease.h> #include <generated/utsrelease.h>
...@@ -186,33 +187,6 @@ static int mlxsw_sp_port_oper_status_get(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -186,33 +187,6 @@ static int mlxsw_sp_port_oper_status_get(struct mlxsw_sp_port *mlxsw_sp_port,
return 0; return 0;
} }
static int mlxsw_sp_vfid_create(struct mlxsw_sp *mlxsw_sp, u16 vfid)
{
char sfmr_pl[MLXSW_REG_SFMR_LEN];
int err;
mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_CREATE_FID,
MLXSW_SP_VFID_BASE + vfid, 0);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl);
if (err)
return err;
set_bit(vfid, mlxsw_sp->active_vfids);
return 0;
}
static void mlxsw_sp_vfid_destroy(struct mlxsw_sp *mlxsw_sp, u16 vfid)
{
char sfmr_pl[MLXSW_REG_SFMR_LEN];
clear_bit(vfid, mlxsw_sp->active_vfids);
mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_DESTROY_FID,
MLXSW_SP_VFID_BASE + vfid, 0);
mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl);
}
static int mlxsw_sp_port_dev_addr_set(struct mlxsw_sp_port *mlxsw_sp_port, static int mlxsw_sp_port_dev_addr_set(struct mlxsw_sp_port *mlxsw_sp_port,
unsigned char *addr) unsigned char *addr)
{ {
...@@ -549,12 +523,130 @@ static int mlxsw_sp_port_vlan_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port) ...@@ -549,12 +523,130 @@ static int mlxsw_sp_port_vlan_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port)
return 0; return 0;
} }
static struct mlxsw_sp_vfid *
mlxsw_sp_vfid_find(const struct mlxsw_sp *mlxsw_sp, u16 vid)
{
struct mlxsw_sp_vfid *vfid;
list_for_each_entry(vfid, &mlxsw_sp->port_vfids.list, list) {
if (vfid->vid == vid)
return vfid;
}
return NULL;
}
static u16 mlxsw_sp_avail_vfid_get(const struct mlxsw_sp *mlxsw_sp)
{
return find_first_zero_bit(mlxsw_sp->port_vfids.mapped,
MLXSW_SP_VFID_PORT_MAX);
}
static int __mlxsw_sp_vfid_create(struct mlxsw_sp *mlxsw_sp, u16 vfid)
{
u16 fid = mlxsw_sp_vfid_to_fid(vfid);
char sfmr_pl[MLXSW_REG_SFMR_LEN];
mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_CREATE_FID, fid, 0);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl);
}
static void __mlxsw_sp_vfid_destroy(struct mlxsw_sp *mlxsw_sp, u16 vfid)
{
u16 fid = mlxsw_sp_vfid_to_fid(vfid);
char sfmr_pl[MLXSW_REG_SFMR_LEN];
mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_DESTROY_FID, fid, 0);
mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl);
}
static struct mlxsw_sp_vfid *mlxsw_sp_vfid_create(struct mlxsw_sp *mlxsw_sp,
u16 vid)
{
struct device *dev = mlxsw_sp->bus_info->dev;
struct mlxsw_sp_vfid *vfid;
u16 n_vfid;
int err;
n_vfid = mlxsw_sp_avail_vfid_get(mlxsw_sp);
if (n_vfid == MLXSW_SP_VFID_PORT_MAX) {
dev_err(dev, "No available vFIDs\n");
return ERR_PTR(-ERANGE);
}
err = __mlxsw_sp_vfid_create(mlxsw_sp, n_vfid);
if (err) {
dev_err(dev, "Failed to create vFID=%d\n", n_vfid);
return ERR_PTR(err);
}
vfid = kzalloc(sizeof(*vfid), GFP_KERNEL);
if (!vfid)
goto err_allocate_vfid;
vfid->vfid = n_vfid;
vfid->vid = vid;
list_add(&vfid->list, &mlxsw_sp->port_vfids.list);
set_bit(n_vfid, mlxsw_sp->port_vfids.mapped);
return vfid;
err_allocate_vfid:
__mlxsw_sp_vfid_destroy(mlxsw_sp, n_vfid);
return ERR_PTR(-ENOMEM);
}
static void mlxsw_sp_vfid_destroy(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_vfid *vfid)
{
clear_bit(vfid->vfid, mlxsw_sp->port_vfids.mapped);
list_del(&vfid->list);
__mlxsw_sp_vfid_destroy(mlxsw_sp, vfid->vfid);
kfree(vfid);
}
static struct mlxsw_sp_port *
mlxsw_sp_port_vport_create(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_vfid *vfid)
{
struct mlxsw_sp_port *mlxsw_sp_vport;
mlxsw_sp_vport = kzalloc(sizeof(*mlxsw_sp_vport), GFP_KERNEL);
if (!mlxsw_sp_vport)
return NULL;
/* dev will be set correctly after the VLAN device is linked
* with the real device. In case of bridge SELF invocation, dev
* will remain as is.
*/
mlxsw_sp_vport->dev = mlxsw_sp_port->dev;
mlxsw_sp_vport->mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
mlxsw_sp_vport->local_port = mlxsw_sp_port->local_port;
mlxsw_sp_vport->stp_state = BR_STATE_FORWARDING;
mlxsw_sp_vport->vport.vfid = vfid;
mlxsw_sp_vport->vport.vid = vfid->vid;
list_add(&mlxsw_sp_vport->vport.list, &mlxsw_sp_port->vports_list);
return mlxsw_sp_vport;
}
static void mlxsw_sp_port_vport_destroy(struct mlxsw_sp_port *mlxsw_sp_vport)
{
list_del(&mlxsw_sp_vport->vport.list);
kfree(mlxsw_sp_vport);
}
int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto, int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto,
u16 vid) u16 vid)
{ {
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
char *sftr_pl; struct mlxsw_sp_port *mlxsw_sp_vport;
struct mlxsw_sp_vfid *vfid;
int err; int err;
/* VLAN 0 is added to HW filter when device goes up, but it is /* VLAN 0 is added to HW filter when device goes up, but it is
...@@ -563,100 +655,104 @@ int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto, ...@@ -563,100 +655,104 @@ int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto,
if (!vid) if (!vid)
return 0; return 0;
if (test_bit(vid, mlxsw_sp_port->active_vfids)) { if (mlxsw_sp_port_vport_find(mlxsw_sp_port, vid)) {
netdev_warn(dev, "VID=%d already configured\n", vid); netdev_warn(dev, "VID=%d already configured\n", vid);
return 0; return 0;
} }
if (!test_bit(vid, mlxsw_sp->active_vfids)) { vfid = mlxsw_sp_vfid_find(mlxsw_sp, vid);
err = mlxsw_sp_vfid_create(mlxsw_sp, vid); if (!vfid) {
if (err) { vfid = mlxsw_sp_vfid_create(mlxsw_sp, vid);
netdev_err(dev, "Failed to create vFID=%d\n", if (IS_ERR(vfid)) {
MLXSW_SP_VFID_BASE + vid); netdev_err(dev, "Failed to create vFID for VID=%d\n",
return err; vid);
return PTR_ERR(vfid);
}
} }
sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL); mlxsw_sp_vport = mlxsw_sp_port_vport_create(mlxsw_sp_port, vfid);
if (!sftr_pl) { if (!mlxsw_sp_vport) {
netdev_err(dev, "Failed to create vPort for VID=%d\n", vid);
err = -ENOMEM; err = -ENOMEM;
goto err_flood_table_alloc; goto err_port_vport_create;
} }
mlxsw_reg_sftr_pack(sftr_pl, 0, vid,
MLXSW_REG_SFGC_TABLE_TYPE_FID, 0, if (!vfid->nr_vports) {
MLXSW_PORT_CPU_PORT, true); err = mlxsw_sp_vport_flood_set(mlxsw_sp_vport, vfid->vfid,
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl); true);
kfree(sftr_pl);
if (err) { if (err) {
netdev_err(dev, "Failed to configure flood table\n"); netdev_err(dev, "Failed to setup flooding for vFID=%d\n",
goto err_flood_table_config; vfid->vfid);
goto err_vport_flood_set;
} }
} }
/* In case we fail in the following steps, we intentionally do not
* destroy the associated vFID.
*/
/* When adding the first VLAN interface on a bridged port we need to /* When adding the first VLAN interface on a bridged port we need to
* transition all the active 802.1Q bridge VLANs to use explicit * 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. * {Port, VID} to FID mappings and set the port's mode to Virtual mode.
*/ */
if (!mlxsw_sp_port->nr_vfids) { if (list_is_singular(&mlxsw_sp_port->vports_list)) {
err = mlxsw_sp_port_vp_mode_trans(mlxsw_sp_port); err = mlxsw_sp_port_vp_mode_trans(mlxsw_sp_port);
if (err) { if (err) {
netdev_err(dev, "Failed to set to Virtual mode\n"); netdev_err(dev, "Failed to set to Virtual mode\n");
return err; goto err_port_vp_mode_trans;
} }
} }
err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport,
MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, MLXSW_REG_SVFA_MT_PORT_VID_TO_FID,
true, MLXSW_SP_VFID_BASE + vid, vid); true,
mlxsw_sp_vfid_to_fid(vfid->vfid),
vid);
if (err) { if (err) {
netdev_err(dev, "Failed to map {Port, VID=%d} to vFID=%d\n", netdev_err(dev, "Failed to map {Port, VID=%d} to vFID=%d\n",
vid, MLXSW_SP_VFID_BASE + vid); vid, vfid->vfid);
goto err_port_vid_to_fid_set; goto err_port_vid_to_fid_set;
} }
err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false); err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, false);
if (err) { if (err) {
netdev_err(dev, "Failed to disable learning for VID=%d\n", vid); netdev_err(dev, "Failed to disable learning for VID=%d\n", vid);
goto err_port_vid_learning_set; goto err_port_vid_learning_set;
} }
err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, true, false); err = mlxsw_sp_port_vlan_set(mlxsw_sp_vport, vid, vid, true, false);
if (err) { if (err) {
netdev_err(dev, "Failed to set VLAN membership for VID=%d\n", netdev_err(dev, "Failed to set VLAN membership for VID=%d\n",
vid); vid);
goto err_port_add_vid; goto err_port_add_vid;
} }
err = mlxsw_sp_port_stp_state_set(mlxsw_sp_port, vid, err = mlxsw_sp_port_stp_state_set(mlxsw_sp_vport, vid,
MLXSW_REG_SPMS_STATE_FORWARDING); MLXSW_REG_SPMS_STATE_FORWARDING);
if (err) { if (err) {
netdev_err(dev, "Failed to set STP state for VID=%d\n", vid); netdev_err(dev, "Failed to set STP state for VID=%d\n", vid);
goto err_port_stp_state_set; goto err_port_stp_state_set;
} }
mlxsw_sp_port->nr_vfids++; vfid->nr_vports++;
set_bit(vid, mlxsw_sp_port->active_vfids);
return 0; return 0;
err_flood_table_config:
err_flood_table_alloc:
mlxsw_sp_vfid_destroy(mlxsw_sp, vid);
return err;
err_port_stp_state_set: err_port_stp_state_set:
mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false); mlxsw_sp_port_vlan_set(mlxsw_sp_vport, vid, vid, false, false);
err_port_add_vid: err_port_add_vid:
mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true); mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, true);
err_port_vid_learning_set: err_port_vid_learning_set:
mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport,
MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, false, MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, false,
MLXSW_SP_VFID_BASE + vid, vid); mlxsw_sp_vfid_to_fid(vfid->vfid), vid);
err_port_vid_to_fid_set: err_port_vid_to_fid_set:
if (list_is_singular(&mlxsw_sp_port->vports_list))
mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port); mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port);
err_port_vp_mode_trans:
if (!vfid->nr_vports)
mlxsw_sp_vport_flood_set(mlxsw_sp_vport, vfid->vfid, false);
err_vport_flood_set:
mlxsw_sp_port_vport_destroy(mlxsw_sp_vport);
err_port_vport_create:
if (!vfid->nr_vports)
mlxsw_sp_vfid_destroy(mlxsw_sp, vfid);
return err; return err;
} }
...@@ -664,6 +760,8 @@ int mlxsw_sp_port_kill_vid(struct net_device *dev, ...@@ -664,6 +760,8 @@ int mlxsw_sp_port_kill_vid(struct net_device *dev,
__be16 __always_unused proto, u16 vid) __be16 __always_unused proto, u16 vid)
{ {
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
struct mlxsw_sp_port *mlxsw_sp_vport;
struct mlxsw_sp_vfid *vfid;
int err; int err;
/* VLAN 0 is removed from HW filter when device goes down, but /* VLAN 0 is removed from HW filter when device goes down, but
...@@ -672,38 +770,42 @@ int mlxsw_sp_port_kill_vid(struct net_device *dev, ...@@ -672,38 +770,42 @@ int mlxsw_sp_port_kill_vid(struct net_device *dev,
if (!vid) if (!vid)
return 0; return 0;
if (!test_bit(vid, mlxsw_sp_port->active_vfids)) { mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid);
if (!mlxsw_sp_vport) {
netdev_warn(dev, "VID=%d does not exist\n", vid); netdev_warn(dev, "VID=%d does not exist\n", vid);
return 0; return 0;
} }
err = mlxsw_sp_port_stp_state_set(mlxsw_sp_port, vid, vfid = mlxsw_sp_vport->vport.vfid;
err = mlxsw_sp_port_stp_state_set(mlxsw_sp_vport, vid,
MLXSW_REG_SPMS_STATE_DISCARDING); MLXSW_REG_SPMS_STATE_DISCARDING);
if (err) { if (err) {
netdev_err(dev, "Failed to set STP state for VID=%d\n", vid); netdev_err(dev, "Failed to set STP state for VID=%d\n", vid);
return err; return err;
} }
err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false); err = mlxsw_sp_port_vlan_set(mlxsw_sp_vport, vid, vid, false, false);
if (err) { if (err) {
netdev_err(dev, "Failed to set VLAN membership for VID=%d\n", netdev_err(dev, "Failed to set VLAN membership for VID=%d\n",
vid); vid);
return err; return err;
} }
err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true); err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, true);
if (err) { if (err) {
netdev_err(dev, "Failed to enable learning for VID=%d\n", vid); netdev_err(dev, "Failed to enable learning for VID=%d\n", vid);
return err; return err;
} }
err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport,
MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, MLXSW_REG_SVFA_MT_PORT_VID_TO_FID,
false, MLXSW_SP_VFID_BASE + vid, false,
mlxsw_sp_vfid_to_fid(vfid->vfid),
vid); vid);
if (err) { if (err) {
netdev_err(dev, "Failed to invalidate {Port, VID=%d} to vFID=%d mapping\n", netdev_err(dev, "Failed to invalidate {Port, VID=%d} to vFID=%d mapping\n",
vid, MLXSW_SP_VFID_BASE + vid); vid, vfid->vfid);
return err; return err;
} }
...@@ -711,7 +813,7 @@ int mlxsw_sp_port_kill_vid(struct net_device *dev, ...@@ -711,7 +813,7 @@ int mlxsw_sp_port_kill_vid(struct net_device *dev,
* transition all active 802.1Q bridge VLANs to use VID to FID * transition all active 802.1Q bridge VLANs to use VID to FID
* mappings and set port's mode to VLAN mode. * mappings and set port's mode to VLAN mode.
*/ */
if (mlxsw_sp_port->nr_vfids == 1) { if (list_is_singular(&mlxsw_sp_port->vports_list)) {
err = mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port); err = mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port);
if (err) { if (err) {
netdev_err(dev, "Failed to set to VLAN mode\n"); netdev_err(dev, "Failed to set to VLAN mode\n");
...@@ -719,8 +821,12 @@ int mlxsw_sp_port_kill_vid(struct net_device *dev, ...@@ -719,8 +821,12 @@ int mlxsw_sp_port_kill_vid(struct net_device *dev,
} }
} }
mlxsw_sp_port->nr_vfids--; vfid->nr_vports--;
clear_bit(vid, mlxsw_sp_port->active_vfids); mlxsw_sp_port_vport_destroy(mlxsw_sp_vport);
/* Destroy the vFID if no vPorts are assigned to it anymore. */
if (!vfid->nr_vports)
mlxsw_sp_vfid_destroy(mlxsw_sp_port->mlxsw_sp, vfid);
return 0; return 0;
} }
...@@ -1265,6 +1371,7 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port) ...@@ -1265,6 +1371,7 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port)
err = -ENOMEM; err = -ENOMEM;
goto err_port_active_vlans_alloc; goto err_port_active_vlans_alloc;
} }
INIT_LIST_HEAD(&mlxsw_sp_port->vports_list);
mlxsw_sp_port->pcpu_stats = mlxsw_sp_port->pcpu_stats =
netdev_alloc_pcpu_stats(struct mlxsw_sp_port_pcpu_stats); netdev_alloc_pcpu_stats(struct mlxsw_sp_port_pcpu_stats);
...@@ -1372,12 +1479,21 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port) ...@@ -1372,12 +1479,21 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port)
return err; return err;
} }
static void mlxsw_sp_vfids_fini(struct mlxsw_sp *mlxsw_sp) static void mlxsw_sp_port_vports_fini(struct mlxsw_sp_port *mlxsw_sp_port)
{ {
u16 vfid; struct net_device *dev = mlxsw_sp_port->dev;
struct mlxsw_sp_port *mlxsw_sp_vport, *tmp;
for_each_set_bit(vfid, mlxsw_sp->active_vfids, VLAN_N_VID) list_for_each_entry_safe(mlxsw_sp_vport, tmp,
mlxsw_sp_vfid_destroy(mlxsw_sp, vfid); &mlxsw_sp_port->vports_list, vport.list) {
u16 vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport);
/* vPorts created for VLAN devices should already be gone
* by now, since we unregistered the port netdev.
*/
WARN_ON(is_vlan_dev(mlxsw_sp_vport->dev));
mlxsw_sp_port_kill_vid(dev, 0, vid);
}
} }
static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port)
...@@ -1386,8 +1502,8 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) ...@@ -1386,8 +1502,8 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port)
if (!mlxsw_sp_port) if (!mlxsw_sp_port)
return; return;
mlxsw_sp_port_kill_vid(mlxsw_sp_port->dev, 0, 1);
unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */ unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */
mlxsw_sp_port_vports_fini(mlxsw_sp_port);
mlxsw_sp_port_switchdev_fini(mlxsw_sp_port); mlxsw_sp_port_switchdev_fini(mlxsw_sp_port);
free_percpu(mlxsw_sp_port->pcpu_stats); free_percpu(mlxsw_sp_port->pcpu_stats);
kfree(mlxsw_sp_port->active_vlans); kfree(mlxsw_sp_port->active_vlans);
...@@ -1746,6 +1862,7 @@ static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core, ...@@ -1746,6 +1862,7 @@ static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core,
mlxsw_sp->core = mlxsw_core; mlxsw_sp->core = mlxsw_core;
mlxsw_sp->bus_info = mlxsw_bus_info; mlxsw_sp->bus_info = mlxsw_bus_info;
INIT_LIST_HEAD(&mlxsw_sp->port_vfids.list);
err = mlxsw_sp_base_mac_get(mlxsw_sp); err = mlxsw_sp_base_mac_get(mlxsw_sp);
if (err) { if (err) {
...@@ -1756,7 +1873,7 @@ static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core, ...@@ -1756,7 +1873,7 @@ static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core,
err = mlxsw_sp_ports_create(mlxsw_sp); err = mlxsw_sp_ports_create(mlxsw_sp);
if (err) { if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to create ports\n"); dev_err(mlxsw_sp->bus_info->dev, "Failed to create ports\n");
goto err_ports_create; return err;
} }
err = mlxsw_sp_event_register(mlxsw_sp, MLXSW_TRAP_ID_PUDE); err = mlxsw_sp_event_register(mlxsw_sp, MLXSW_TRAP_ID_PUDE);
...@@ -1806,8 +1923,6 @@ static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core, ...@@ -1806,8 +1923,6 @@ static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core,
mlxsw_sp_event_unregister(mlxsw_sp, MLXSW_TRAP_ID_PUDE); mlxsw_sp_event_unregister(mlxsw_sp, MLXSW_TRAP_ID_PUDE);
err_event_register: err_event_register:
mlxsw_sp_ports_remove(mlxsw_sp); mlxsw_sp_ports_remove(mlxsw_sp);
err_ports_create:
mlxsw_sp_vfids_fini(mlxsw_sp);
return err; return err;
} }
...@@ -1819,7 +1934,6 @@ static void mlxsw_sp_fini(void *priv) ...@@ -1819,7 +1934,6 @@ static void mlxsw_sp_fini(void *priv)
mlxsw_sp_traps_fini(mlxsw_sp); mlxsw_sp_traps_fini(mlxsw_sp);
mlxsw_sp_event_unregister(mlxsw_sp, MLXSW_TRAP_ID_PUDE); mlxsw_sp_event_unregister(mlxsw_sp, MLXSW_TRAP_ID_PUDE);
mlxsw_sp_ports_remove(mlxsw_sp); mlxsw_sp_ports_remove(mlxsw_sp);
mlxsw_sp_vfids_fini(mlxsw_sp);
} }
static struct mlxsw_config_profile mlxsw_sp_config_profile = { static struct mlxsw_config_profile mlxsw_sp_config_profile = {
......
...@@ -41,11 +41,16 @@ ...@@ -41,11 +41,16 @@
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include <linux/list.h>
#include <net/switchdev.h> #include <net/switchdev.h>
#include "core.h" #include "core.h"
#define MLXSW_SP_VFID_BASE VLAN_N_VID #define MLXSW_SP_VFID_BASE VLAN_N_VID
#define MLXSW_SP_VFID_PORT_MAX 512 /* Non-bridged VLAN interfaces */
#define MLXSW_SP_VFID_BR_MAX 8192 /* Bridged VLAN interfaces */
#define MLXSW_SP_VFID_MAX (MLXSW_SP_VFID_PORT_MAX + MLXSW_SP_VFID_BR_MAX)
#define MLXSW_SP_LAG_MAX 64 #define MLXSW_SP_LAG_MAX 64
#define MLXSW_SP_PORT_PER_LAG_MAX 16 #define MLXSW_SP_PORT_PER_LAG_MAX 16
...@@ -56,8 +61,23 @@ struct mlxsw_sp_upper { ...@@ -56,8 +61,23 @@ struct mlxsw_sp_upper {
unsigned int ref_count; unsigned int ref_count;
}; };
struct mlxsw_sp_vfid {
struct list_head list;
u16 nr_vports;
u16 vfid; /* Starting at 0 */
u16 vid;
};
static inline u16 mlxsw_sp_vfid_to_fid(u16 vfid)
{
return MLXSW_SP_VFID_BASE + vfid;
}
struct mlxsw_sp { struct mlxsw_sp {
unsigned long active_vfids[BITS_TO_LONGS(VLAN_N_VID)]; struct {
struct list_head list;
unsigned long mapped[BITS_TO_LONGS(MLXSW_SP_VFID_PORT_MAX)];
} port_vfids;
unsigned long active_fids[BITS_TO_LONGS(VLAN_N_VID)]; unsigned long active_fids[BITS_TO_LONGS(VLAN_N_VID)];
struct mlxsw_sp_port **ports; struct mlxsw_sp_port **ports;
struct mlxsw_core *core; struct mlxsw_core *core;
...@@ -102,11 +122,15 @@ struct mlxsw_sp_port { ...@@ -102,11 +122,15 @@ struct mlxsw_sp_port {
lagged:1; lagged:1;
u16 pvid; u16 pvid;
u16 lag_id; u16 lag_id;
struct {
struct list_head list;
struct mlxsw_sp_vfid *vfid;
u16 vid;
} vport;
/* 802.1Q bridge VLANs */ /* 802.1Q bridge VLANs */
unsigned long *active_vlans; unsigned long *active_vlans;
/* VLAN interfaces */ /* VLAN interfaces */
unsigned long active_vfids[BITS_TO_LONGS(VLAN_N_VID)]; struct list_head vports_list;
u16 nr_vfids;
}; };
static inline struct mlxsw_sp_port * static inline struct mlxsw_sp_port *
...@@ -121,6 +145,38 @@ mlxsw_sp_port_lagged_get(struct mlxsw_sp *mlxsw_sp, u16 lag_id, u8 port_index) ...@@ -121,6 +145,38 @@ mlxsw_sp_port_lagged_get(struct mlxsw_sp *mlxsw_sp, u16 lag_id, u8 port_index)
return mlxsw_sp_port && mlxsw_sp_port->lagged ? mlxsw_sp_port : NULL; return mlxsw_sp_port && mlxsw_sp_port->lagged ? mlxsw_sp_port : NULL;
} }
static inline bool
mlxsw_sp_port_is_vport(const struct mlxsw_sp_port *mlxsw_sp_port)
{
return mlxsw_sp_port->vport.vfid;
}
static inline u16
mlxsw_sp_vport_vid_get(const struct mlxsw_sp_port *mlxsw_sp_vport)
{
return mlxsw_sp_vport->vport.vid;
}
static inline u16
mlxsw_sp_vport_vfid_get(const struct mlxsw_sp_port *mlxsw_sp_vport)
{
return mlxsw_sp_vport->vport.vfid->vfid;
}
static inline struct mlxsw_sp_port *
mlxsw_sp_port_vport_find(const struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
{
struct mlxsw_sp_port *mlxsw_sp_vport;
list_for_each_entry(mlxsw_sp_vport, &mlxsw_sp_port->vports_list,
vport.list) {
if (mlxsw_sp_vport_vid_get(mlxsw_sp_vport) == vid)
return mlxsw_sp_vport;
}
return NULL;
}
enum mlxsw_sp_flood_table { enum mlxsw_sp_flood_table {
MLXSW_SP_FLOOD_TABLE_UC, MLXSW_SP_FLOOD_TABLE_UC,
MLXSW_SP_FLOOD_TABLE_BM, MLXSW_SP_FLOOD_TABLE_BM,
...@@ -143,5 +199,7 @@ int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto, ...@@ -143,5 +199,7 @@ int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto,
u16 vid); u16 vid);
int mlxsw_sp_port_kill_vid(struct net_device *dev, int mlxsw_sp_port_kill_vid(struct net_device *dev,
__be16 __always_unused proto, u16 vid); __be16 __always_unused proto, u16 vid);
int mlxsw_sp_vport_flood_set(struct mlxsw_sp_port *mlxsw_sp_vport, u16 vfid,
bool set);
#endif #endif
...@@ -129,17 +129,25 @@ static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -129,17 +129,25 @@ static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
bool only_uc) bool only_uc)
{ {
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
u16 local_port = mlxsw_sp_port->local_port;
enum mlxsw_flood_table_type table_type;
u16 range = fid_end - fid_begin + 1; u16 range = fid_end - fid_begin + 1;
char *sftr_pl; char *sftr_pl;
int err; int err;
if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID;
local_port = MLXSW_PORT_CPU_PORT;
} else {
table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST;
}
sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL); sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL);
if (!sftr_pl) if (!sftr_pl)
return -ENOMEM; return -ENOMEM;
mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_UC, fid_begin, mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_UC, fid_begin,
MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST, range, table_type, range, local_port, set);
mlxsw_sp_port->local_port, set);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);
if (err) if (err)
goto buffer_out; goto buffer_out;
...@@ -151,8 +159,7 @@ static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -151,8 +159,7 @@ static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
goto buffer_out; goto buffer_out;
mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BM, fid_begin, mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BM, fid_begin,
MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST, range, table_type, range, local_port, set);
mlxsw_sp_port->local_port, set);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);
buffer_out: buffer_out:
...@@ -185,6 +192,15 @@ static int mlxsw_sp_port_uc_flood_set(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -185,6 +192,15 @@ static int mlxsw_sp_port_uc_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
return err; return err;
} }
int mlxsw_sp_vport_flood_set(struct mlxsw_sp_port *mlxsw_sp_vport, u16 vfid,
bool set)
{
/* In case of vFIDs, index into the flooding table is relative to
* the start of the vFIDs range.
*/
return __mlxsw_sp_port_flood_set(mlxsw_sp_vport, vfid, vfid, set, true);
}
static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port, static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
struct switchdev_trans *trans, struct switchdev_trans *trans,
unsigned long brport_flags) unsigned long brport_flags)
...@@ -304,7 +320,7 @@ static int mlxsw_sp_port_fid_map(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid) ...@@ -304,7 +320,7 @@ static int mlxsw_sp_port_fid_map(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid)
{ {
enum mlxsw_reg_svfa_mt mt; enum mlxsw_reg_svfa_mt mt;
if (mlxsw_sp_port->nr_vfids) if (!list_empty(&mlxsw_sp_port->vports_list))
mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID; mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID;
else else
mt = MLXSW_REG_SVFA_MT_VID_TO_FID; mt = MLXSW_REG_SVFA_MT_VID_TO_FID;
...@@ -316,7 +332,7 @@ static int mlxsw_sp_port_fid_unmap(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid) ...@@ -316,7 +332,7 @@ static int mlxsw_sp_port_fid_unmap(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid)
{ {
enum mlxsw_reg_svfa_mt mt; enum mlxsw_reg_svfa_mt mt;
if (!mlxsw_sp_port->nr_vfids) if (list_empty(&mlxsw_sp_port->vports_list))
return 0; return 0;
mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID; mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment