Commit f637941b authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 00ce3a15 fc1273af
...@@ -1370,6 +1370,11 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port) ...@@ -1370,6 +1370,11 @@ 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;
} }
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); INIT_LIST_HEAD(&mlxsw_sp_port->vports_list);
mlxsw_sp_port->pcpu_stats = mlxsw_sp_port->pcpu_stats =
...@@ -1472,6 +1477,8 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port) ...@@ -1472,6 +1477,8 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port)
err_dev_addr_init: err_dev_addr_init:
free_percpu(mlxsw_sp_port->pcpu_stats); free_percpu(mlxsw_sp_port->pcpu_stats);
err_alloc_stats: err_alloc_stats:
kfree(mlxsw_sp_port->untagged_vlans);
err_port_untagged_vlans_alloc:
kfree(mlxsw_sp_port->active_vlans); kfree(mlxsw_sp_port->active_vlans);
err_port_active_vlans_alloc: err_port_active_vlans_alloc:
free_netdev(dev); free_netdev(dev);
...@@ -1505,6 +1512,7 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) ...@@ -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_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->untagged_vlans);
kfree(mlxsw_sp_port->active_vlans); kfree(mlxsw_sp_port->active_vlans);
free_netdev(mlxsw_sp_port->dev); free_netdev(mlxsw_sp_port->dev);
} }
......
...@@ -144,6 +144,7 @@ struct mlxsw_sp_port { ...@@ -144,6 +144,7 @@ struct mlxsw_sp_port {
} vport; } vport;
/* 802.1Q bridge VLANs */ /* 802.1Q bridge VLANs */
unsigned long *active_vlans; unsigned long *active_vlans;
unsigned long *untagged_vlans;
/* VLAN interfaces */ /* VLAN interfaces */
struct list_head vports_list; 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, ...@@ -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); 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, static int mlxsw_sp_port_attr_set(struct net_device *dev,
const struct switchdev_attr *attr, const struct switchdev_attr *attr,
struct switchdev_trans *trans) struct switchdev_trans *trans)
...@@ -323,6 +339,11 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev, ...@@ -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, err = mlxsw_sp_port_attr_br_ageing_set(mlxsw_sp_port, trans,
attr->u.ageing_time); attr->u.ageing_time);
break; 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: default:
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
break; break;
...@@ -505,8 +526,13 @@ static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -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 */ /* 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); 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 */ /* STP state change must be done after we set active VLANs */
err = mlxsw_sp_port_stp_state_set(mlxsw_sp_port, 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, ...@@ -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, const struct switchdev_obj_port_vlan *vlan,
struct switchdev_trans *trans) struct switchdev_trans *trans)
{ {
bool untagged_flag = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
bool pvid_flag = vlan->flags & BRIDGE_VLAN_INFO_PVID; bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
if (switchdev_trans_ph_prepare(trans)) if (switchdev_trans_ph_prepare(trans))
return 0; return 0;
return __mlxsw_sp_port_vlans_add(mlxsw_sp_port, return __mlxsw_sp_port_vlans_add(mlxsw_sp_port,
vlan->vid_begin, vlan->vid_end, 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) 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, ...@@ -933,6 +959,8 @@ static int mlxsw_sp_port_vlan_dump(struct mlxsw_sp_port *mlxsw_sp_port,
vlan->flags = 0; vlan->flags = 0;
if (vid == mlxsw_sp_port->pvid) if (vid == mlxsw_sp_port->pvid)
vlan->flags |= BRIDGE_VLAN_INFO_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_begin = vid;
vlan->vid_end = vid; vlan->vid_end = vid;
err = cb(&vlan->obj); err = cb(&vlan->obj);
...@@ -1201,7 +1229,8 @@ int mlxsw_sp_port_vlan_init(struct mlxsw_sp_port *mlxsw_sp_port) ...@@ -1201,7 +1229,8 @@ int mlxsw_sp_port_vlan_init(struct mlxsw_sp_port *mlxsw_sp_port)
* with VID 1. * with VID 1.
*/ */
mlxsw_sp_port->pvid = 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) { if (err) {
netdev_err(dev, "Unable to init VLANs\n"); netdev_err(dev, "Unable to init VLANs\n");
return err; return err;
......
...@@ -47,6 +47,7 @@ enum switchdev_attr_id { ...@@ -47,6 +47,7 @@ enum switchdev_attr_id {
SWITCHDEV_ATTR_ID_PORT_STP_STATE, SWITCHDEV_ATTR_ID_PORT_STP_STATE,
SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS, SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS,
SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME, SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME,
SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING,
}; };
struct switchdev_attr { struct switchdev_attr {
...@@ -58,6 +59,7 @@ struct switchdev_attr { ...@@ -58,6 +59,7 @@ struct switchdev_attr {
u8 stp_state; /* PORT_STP_STATE */ u8 stp_state; /* PORT_STP_STATE */
unsigned long brport_flags; /* PORT_BRIDGE_FLAGS */ unsigned long brport_flags; /* PORT_BRIDGE_FLAGS */
u32 ageing_time; /* BRIDGE_AGEING_TIME */ u32 ageing_time; /* BRIDGE_AGEING_TIME */
bool vlan_filtering; /* BRIDGE_VLAN_FILTERING */
} u; } u;
}; };
......
...@@ -511,8 +511,11 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) ...@@ -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)) if (br_fdb_insert(br, p, dev->dev_addr, 0))
netdev_err(dev, "failed insert local address bridge forwarding table\n"); 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"); netdev_err(dev, "failed to initialize vlan filtering on this port\n");
goto err6;
}
spin_lock_bh(&br->lock); spin_lock_bh(&br->lock);
changed_addr = br_stp_recalculate_bridge_id(br); changed_addr = br_stp_recalculate_bridge_id(br);
...@@ -533,6 +536,12 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) ...@@ -533,6 +536,12 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
return 0; 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: err5:
dev->priv_flags &= ~IFF_BRIDGE_PORT; dev->priv_flags &= ~IFF_BRIDGE_PORT;
netdev_rx_handler_unregister(dev); netdev_rx_handler_unregister(dev);
......
...@@ -626,9 +626,21 @@ void br_recalculate_fwd_mask(struct net_bridge *br) ...@@ -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) 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) if (br->vlan_enabled == val)
return 0; return 0;
err = switchdev_port_attr_set(br->dev, &attr);
if (err && err != -EOPNOTSUPP)
return err;
br->vlan_enabled = val; br->vlan_enabled = val;
br_manage_promisc(br); br_manage_promisc(br);
recalculate_group_addr(br); recalculate_group_addr(br);
...@@ -639,13 +651,15 @@ int __br_vlan_filter_toggle(struct net_bridge *br, unsigned long val) ...@@ -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 br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
{ {
int err;
if (!rtnl_trylock()) if (!rtnl_trylock())
return restart_syscall(); return restart_syscall();
__br_vlan_filter_toggle(br, val); err = __br_vlan_filter_toggle(br, val);
rtnl_unlock(); rtnl_unlock();
return 0; return err;
} }
int __br_vlan_set_proto(struct net_bridge *br, __be16 proto) int __br_vlan_set_proto(struct net_bridge *br, __be16 proto)
...@@ -893,6 +907,12 @@ int br_vlan_init(struct net_bridge *br) ...@@ -893,6 +907,12 @@ int br_vlan_init(struct net_bridge *br)
int nbp_vlan_init(struct net_bridge_port *p) 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; struct net_bridge_vlan_group *vg;
int ret = -ENOMEM; int ret = -ENOMEM;
...@@ -900,6 +920,10 @@ int nbp_vlan_init(struct net_bridge_port *p) ...@@ -900,6 +920,10 @@ int nbp_vlan_init(struct net_bridge_port *p)
if (!vg) if (!vg)
goto out; 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); ret = rhashtable_init(&vg->vlan_hash, &br_vlan_rht_params);
if (ret) if (ret)
goto err_rhtbl; goto err_rhtbl;
...@@ -919,6 +943,7 @@ int nbp_vlan_init(struct net_bridge_port *p) ...@@ -919,6 +943,7 @@ int nbp_vlan_init(struct net_bridge_port *p)
RCU_INIT_POINTER(p->vlgrp, NULL); RCU_INIT_POINTER(p->vlgrp, NULL);
synchronize_rcu(); synchronize_rcu();
rhashtable_destroy(&vg->vlan_hash); rhashtable_destroy(&vg->vlan_hash);
err_vlan_enabled:
err_rhtbl: err_rhtbl:
kfree(vg); kfree(vg);
......
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