Merge branch 'mlxsw-vlan_filtering-offload'
Jiri Pirko says: ==================== mlxsw: add offload support for vlan_filtering option Elad says: This patch adds SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING port attribute. When a bridge is offloaded to hardware, the hardware can learn if the bridge is .1Q bridge (VLAN-aware) or not VLAN aware bridge. In order to toggle the mode a user can use sysfs: $ echo 1 > /sys/devices/virtual/net/br0/bridge/vlan_filtering or via iproute2: $ ip link set dev br0 type bridge vlan_filtering 1 --- v1->v2: small fix in patch #1 ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
f637941b14
@ -1370,6 +1370,11 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port)
|
||||
err = -ENOMEM;
|
||||
goto err_port_active_vlans_alloc;
|
||||
}
|
||||
mlxsw_sp_port->untagged_vlans = kzalloc(bytes, GFP_KERNEL);
|
||||
if (!mlxsw_sp_port->untagged_vlans) {
|
||||
err = -ENOMEM;
|
||||
goto err_port_untagged_vlans_alloc;
|
||||
}
|
||||
INIT_LIST_HEAD(&mlxsw_sp_port->vports_list);
|
||||
|
||||
mlxsw_sp_port->pcpu_stats =
|
||||
@ -1472,6 +1477,8 @@ err_port_module_check:
|
||||
err_dev_addr_init:
|
||||
free_percpu(mlxsw_sp_port->pcpu_stats);
|
||||
err_alloc_stats:
|
||||
kfree(mlxsw_sp_port->untagged_vlans);
|
||||
err_port_untagged_vlans_alloc:
|
||||
kfree(mlxsw_sp_port->active_vlans);
|
||||
err_port_active_vlans_alloc:
|
||||
free_netdev(dev);
|
||||
@ -1505,6 +1512,7 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port)
|
||||
mlxsw_sp_port_vports_fini(mlxsw_sp_port);
|
||||
mlxsw_sp_port_switchdev_fini(mlxsw_sp_port);
|
||||
free_percpu(mlxsw_sp_port->pcpu_stats);
|
||||
kfree(mlxsw_sp_port->untagged_vlans);
|
||||
kfree(mlxsw_sp_port->active_vlans);
|
||||
free_netdev(mlxsw_sp_port->dev);
|
||||
}
|
||||
|
@ -144,6 +144,7 @@ struct mlxsw_sp_port {
|
||||
} vport;
|
||||
/* 802.1Q bridge VLANs */
|
||||
unsigned long *active_vlans;
|
||||
unsigned long *untagged_vlans;
|
||||
/* VLAN interfaces */
|
||||
struct list_head vports_list;
|
||||
};
|
||||
|
@ -299,6 +299,22 @@ static int mlxsw_sp_port_attr_br_ageing_set(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
return mlxsw_sp_ageing_set(mlxsw_sp, ageing_time);
|
||||
}
|
||||
|
||||
static int mlxsw_sp_port_attr_br_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
struct switchdev_trans *trans,
|
||||
struct net_device *orig_dev,
|
||||
bool vlan_enabled)
|
||||
{
|
||||
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
||||
|
||||
/* SWITCHDEV_TRANS_PREPARE phase */
|
||||
if ((!vlan_enabled) && (mlxsw_sp->master_bridge.dev == orig_dev)) {
|
||||
netdev_err(mlxsw_sp_port->dev, "Bridge must be vlan-aware\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlxsw_sp_port_attr_set(struct net_device *dev,
|
||||
const struct switchdev_attr *attr,
|
||||
struct switchdev_trans *trans)
|
||||
@ -323,6 +339,11 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev,
|
||||
err = mlxsw_sp_port_attr_br_ageing_set(mlxsw_sp_port, trans,
|
||||
attr->u.ageing_time);
|
||||
break;
|
||||
case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
|
||||
err = mlxsw_sp_port_attr_br_vlan_set(mlxsw_sp_port, trans,
|
||||
attr->orig_dev,
|
||||
attr->u.vlan_filtering);
|
||||
break;
|
||||
default:
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
@ -505,8 +526,13 @@ static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
}
|
||||
|
||||
/* Changing activity bits only if HW operation succeded */
|
||||
for (vid = vid_begin; vid <= vid_end; vid++)
|
||||
for (vid = vid_begin; vid <= vid_end; vid++) {
|
||||
set_bit(vid, mlxsw_sp_port->active_vlans);
|
||||
if (flag_untagged)
|
||||
set_bit(vid, mlxsw_sp_port->untagged_vlans);
|
||||
else
|
||||
clear_bit(vid, mlxsw_sp_port->untagged_vlans);
|
||||
}
|
||||
|
||||
/* STP state change must be done after we set active VLANs */
|
||||
err = mlxsw_sp_port_stp_state_set(mlxsw_sp_port,
|
||||
@ -545,15 +571,15 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
const struct switchdev_obj_port_vlan *vlan,
|
||||
struct switchdev_trans *trans)
|
||||
{
|
||||
bool untagged_flag = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
|
||||
bool pvid_flag = vlan->flags & BRIDGE_VLAN_INFO_PVID;
|
||||
bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
|
||||
bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
|
||||
|
||||
if (switchdev_trans_ph_prepare(trans))
|
||||
return 0;
|
||||
|
||||
return __mlxsw_sp_port_vlans_add(mlxsw_sp_port,
|
||||
vlan->vid_begin, vlan->vid_end,
|
||||
untagged_flag, pvid_flag);
|
||||
flag_untagged, flag_pvid);
|
||||
}
|
||||
|
||||
static enum mlxsw_reg_sfd_rec_policy mlxsw_sp_sfd_rec_policy(bool dynamic)
|
||||
@ -933,6 +959,8 @@ static int mlxsw_sp_port_vlan_dump(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
vlan->flags = 0;
|
||||
if (vid == mlxsw_sp_port->pvid)
|
||||
vlan->flags |= BRIDGE_VLAN_INFO_PVID;
|
||||
if (test_bit(vid, mlxsw_sp_port->untagged_vlans))
|
||||
vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
|
||||
vlan->vid_begin = vid;
|
||||
vlan->vid_end = vid;
|
||||
err = cb(&vlan->obj);
|
||||
@ -1201,7 +1229,8 @@ int mlxsw_sp_port_vlan_init(struct mlxsw_sp_port *mlxsw_sp_port)
|
||||
* with VID 1.
|
||||
*/
|
||||
mlxsw_sp_port->pvid = 1;
|
||||
err = __mlxsw_sp_port_vlans_del(mlxsw_sp_port, 0, VLAN_N_VID, true);
|
||||
err = __mlxsw_sp_port_vlans_del(mlxsw_sp_port, 0, VLAN_N_VID - 1,
|
||||
true);
|
||||
if (err) {
|
||||
netdev_err(dev, "Unable to init VLANs\n");
|
||||
return err;
|
||||
|
@ -47,6 +47,7 @@ enum switchdev_attr_id {
|
||||
SWITCHDEV_ATTR_ID_PORT_STP_STATE,
|
||||
SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS,
|
||||
SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME,
|
||||
SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING,
|
||||
};
|
||||
|
||||
struct switchdev_attr {
|
||||
@ -58,6 +59,7 @@ struct switchdev_attr {
|
||||
u8 stp_state; /* PORT_STP_STATE */
|
||||
unsigned long brport_flags; /* PORT_BRIDGE_FLAGS */
|
||||
u32 ageing_time; /* BRIDGE_AGEING_TIME */
|
||||
bool vlan_filtering; /* BRIDGE_VLAN_FILTERING */
|
||||
} u;
|
||||
};
|
||||
|
||||
|
@ -511,8 +511,11 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
|
||||
if (br_fdb_insert(br, p, dev->dev_addr, 0))
|
||||
netdev_err(dev, "failed insert local address bridge forwarding table\n");
|
||||
|
||||
if (nbp_vlan_init(p))
|
||||
err = nbp_vlan_init(p);
|
||||
if (err) {
|
||||
netdev_err(dev, "failed to initialize vlan filtering on this port\n");
|
||||
goto err6;
|
||||
}
|
||||
|
||||
spin_lock_bh(&br->lock);
|
||||
changed_addr = br_stp_recalculate_bridge_id(br);
|
||||
@ -533,6 +536,12 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
|
||||
|
||||
return 0;
|
||||
|
||||
err6:
|
||||
list_del_rcu(&p->list);
|
||||
br_fdb_delete_by_port(br, p, 0, 1);
|
||||
nbp_update_port_count(br);
|
||||
netdev_upper_dev_unlink(dev, br->dev);
|
||||
|
||||
err5:
|
||||
dev->priv_flags &= ~IFF_BRIDGE_PORT;
|
||||
netdev_rx_handler_unregister(dev);
|
||||
|
@ -626,9 +626,21 @@ void br_recalculate_fwd_mask(struct net_bridge *br)
|
||||
|
||||
int __br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
|
||||
{
|
||||
struct switchdev_attr attr = {
|
||||
.orig_dev = br->dev,
|
||||
.id = SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING,
|
||||
.flags = SWITCHDEV_F_SKIP_EOPNOTSUPP,
|
||||
.u.vlan_filtering = val,
|
||||
};
|
||||
int err;
|
||||
|
||||
if (br->vlan_enabled == val)
|
||||
return 0;
|
||||
|
||||
err = switchdev_port_attr_set(br->dev, &attr);
|
||||
if (err && err != -EOPNOTSUPP)
|
||||
return err;
|
||||
|
||||
br->vlan_enabled = val;
|
||||
br_manage_promisc(br);
|
||||
recalculate_group_addr(br);
|
||||
@ -639,13 +651,15 @@ int __br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
|
||||
|
||||
int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!rtnl_trylock())
|
||||
return restart_syscall();
|
||||
|
||||
__br_vlan_filter_toggle(br, val);
|
||||
err = __br_vlan_filter_toggle(br, val);
|
||||
rtnl_unlock();
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
int __br_vlan_set_proto(struct net_bridge *br, __be16 proto)
|
||||
@ -893,6 +907,12 @@ err_rhtbl:
|
||||
|
||||
int nbp_vlan_init(struct net_bridge_port *p)
|
||||
{
|
||||
struct switchdev_attr attr = {
|
||||
.orig_dev = p->br->dev,
|
||||
.id = SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING,
|
||||
.flags = SWITCHDEV_F_SKIP_EOPNOTSUPP,
|
||||
.u.vlan_filtering = p->br->vlan_enabled,
|
||||
};
|
||||
struct net_bridge_vlan_group *vg;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
@ -900,6 +920,10 @@ int nbp_vlan_init(struct net_bridge_port *p)
|
||||
if (!vg)
|
||||
goto out;
|
||||
|
||||
ret = switchdev_port_attr_set(p->dev, &attr);
|
||||
if (ret && ret != -EOPNOTSUPP)
|
||||
goto err_vlan_enabled;
|
||||
|
||||
ret = rhashtable_init(&vg->vlan_hash, &br_vlan_rht_params);
|
||||
if (ret)
|
||||
goto err_rhtbl;
|
||||
@ -919,6 +943,7 @@ err_vlan_add:
|
||||
RCU_INIT_POINTER(p->vlgrp, NULL);
|
||||
synchronize_rcu();
|
||||
rhashtable_destroy(&vg->vlan_hash);
|
||||
err_vlan_enabled:
|
||||
err_rhtbl:
|
||||
kfree(vg);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user