diff options
Diffstat (limited to 'net/dsa/switch.c')
| -rw-r--r-- | net/dsa/switch.c | 194 | 
1 files changed, 193 insertions, 1 deletions
| diff --git a/net/dsa/switch.c b/net/dsa/switch.c index ca6e26e514f0..97e2e9c8cf3f 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -12,7 +12,47 @@  #include <linux/netdevice.h>  #include <linux/notifier.h> -#include <net/dsa.h> +#include <net/switchdev.h> + +#include "dsa_priv.h" + +static unsigned int dsa_switch_fastest_ageing_time(struct dsa_switch *ds, +						   unsigned int ageing_time) +{ +	int i; + +	for (i = 0; i < ds->num_ports; ++i) { +		struct dsa_port *dp = &ds->ports[i]; + +		if (dp->ageing_time && dp->ageing_time < ageing_time) +			ageing_time = dp->ageing_time; +	} + +	return ageing_time; +} + +static int dsa_switch_ageing_time(struct dsa_switch *ds, +				  struct dsa_notifier_ageing_time_info *info) +{ +	unsigned int ageing_time = info->ageing_time; +	struct switchdev_trans *trans = info->trans; + +	if (switchdev_trans_ph_prepare(trans)) { +		if (ds->ageing_time_min && ageing_time < ds->ageing_time_min) +			return -ERANGE; +		if (ds->ageing_time_max && ageing_time > ds->ageing_time_max) +			return -ERANGE; +		return 0; +	} + +	/* Program the fastest ageing time in case of multiple bridges */ +	ageing_time = dsa_switch_fastest_ageing_time(ds, ageing_time); + +	if (ds->ops->set_ageing_time) +		return ds->ops->set_ageing_time(ds, ageing_time); + +	return 0; +}  static int dsa_switch_bridge_join(struct dsa_switch *ds,  				  struct dsa_notifier_bridge_info *info) @@ -40,6 +80,137 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds,  	return 0;  } +static int dsa_switch_fdb_add(struct dsa_switch *ds, +			      struct dsa_notifier_fdb_info *info) +{ +	const struct switchdev_obj_port_fdb *fdb = info->fdb; +	struct switchdev_trans *trans = info->trans; + +	/* Do not care yet about other switch chips of the fabric */ +	if (ds->index != info->sw_index) +		return 0; + +	if (switchdev_trans_ph_prepare(trans)) { +		if (!ds->ops->port_fdb_prepare || !ds->ops->port_fdb_add) +			return -EOPNOTSUPP; + +		return ds->ops->port_fdb_prepare(ds, info->port, fdb, trans); +	} + +	ds->ops->port_fdb_add(ds, info->port, fdb, trans); + +	return 0; +} + +static int dsa_switch_fdb_del(struct dsa_switch *ds, +			      struct dsa_notifier_fdb_info *info) +{ +	const struct switchdev_obj_port_fdb *fdb = info->fdb; + +	/* Do not care yet about other switch chips of the fabric */ +	if (ds->index != info->sw_index) +		return 0; + +	if (!ds->ops->port_fdb_del) +		return -EOPNOTSUPP; + +	return ds->ops->port_fdb_del(ds, info->port, fdb); +} + +static int dsa_switch_mdb_add(struct dsa_switch *ds, +			      struct dsa_notifier_mdb_info *info) +{ +	const struct switchdev_obj_port_mdb *mdb = info->mdb; +	struct switchdev_trans *trans = info->trans; +	DECLARE_BITMAP(group, ds->num_ports); +	int port, err; + +	/* Build a mask of Multicast group members */ +	bitmap_zero(group, ds->num_ports); +	if (ds->index == info->sw_index) +		set_bit(info->port, group); +	for (port = 0; port < ds->num_ports; port++) +		if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) +			set_bit(port, group); + +	if (switchdev_trans_ph_prepare(trans)) { +		if (!ds->ops->port_mdb_prepare || !ds->ops->port_mdb_add) +			return -EOPNOTSUPP; + +		for_each_set_bit(port, group, ds->num_ports) { +			err = ds->ops->port_mdb_prepare(ds, port, mdb, trans); +			if (err) +				return err; +		} +	} + +	for_each_set_bit(port, group, ds->num_ports) +		ds->ops->port_mdb_add(ds, port, mdb, trans); + +	return 0; +} + +static int dsa_switch_mdb_del(struct dsa_switch *ds, +			      struct dsa_notifier_mdb_info *info) +{ +	const struct switchdev_obj_port_mdb *mdb = info->mdb; + +	if (!ds->ops->port_mdb_del) +		return -EOPNOTSUPP; + +	if (ds->index == info->sw_index) +		return ds->ops->port_mdb_del(ds, info->port, mdb); + +	return 0; +} + +static int dsa_switch_vlan_add(struct dsa_switch *ds, +			       struct dsa_notifier_vlan_info *info) +{ +	const struct switchdev_obj_port_vlan *vlan = info->vlan; +	struct switchdev_trans *trans = info->trans; +	DECLARE_BITMAP(members, ds->num_ports); +	int port, err; + +	/* Build a mask of VLAN members */ +	bitmap_zero(members, ds->num_ports); +	if (ds->index == info->sw_index) +		set_bit(info->port, members); +	for (port = 0; port < ds->num_ports; port++) +		if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) +			set_bit(port, members); + +	if (switchdev_trans_ph_prepare(trans)) { +		if (!ds->ops->port_vlan_prepare || !ds->ops->port_vlan_add) +			return -EOPNOTSUPP; + +		for_each_set_bit(port, members, ds->num_ports) { +			err = ds->ops->port_vlan_prepare(ds, port, vlan, trans); +			if (err) +				return err; +		} +	} + +	for_each_set_bit(port, members, ds->num_ports) +		ds->ops->port_vlan_add(ds, port, vlan, trans); + +	return 0; +} + +static int dsa_switch_vlan_del(struct dsa_switch *ds, +			       struct dsa_notifier_vlan_info *info) +{ +	const struct switchdev_obj_port_vlan *vlan = info->vlan; + +	if (!ds->ops->port_vlan_del) +		return -EOPNOTSUPP; + +	if (ds->index == info->sw_index) +		return ds->ops->port_vlan_del(ds, info->port, vlan); + +	return 0; +} +  static int dsa_switch_event(struct notifier_block *nb,  			    unsigned long event, void *info)  { @@ -47,12 +218,33 @@ static int dsa_switch_event(struct notifier_block *nb,  	int err;  	switch (event) { +	case DSA_NOTIFIER_AGEING_TIME: +		err = dsa_switch_ageing_time(ds, info); +		break;  	case DSA_NOTIFIER_BRIDGE_JOIN:  		err = dsa_switch_bridge_join(ds, info);  		break;  	case DSA_NOTIFIER_BRIDGE_LEAVE:  		err = dsa_switch_bridge_leave(ds, info);  		break; +	case DSA_NOTIFIER_FDB_ADD: +		err = dsa_switch_fdb_add(ds, info); +		break; +	case DSA_NOTIFIER_FDB_DEL: +		err = dsa_switch_fdb_del(ds, info); +		break; +	case DSA_NOTIFIER_MDB_ADD: +		err = dsa_switch_mdb_add(ds, info); +		break; +	case DSA_NOTIFIER_MDB_DEL: +		err = dsa_switch_mdb_del(ds, info); +		break; +	case DSA_NOTIFIER_VLAN_ADD: +		err = dsa_switch_vlan_add(ds, info); +		break; +	case DSA_NOTIFIER_VLAN_DEL: +		err = dsa_switch_vlan_del(ds, info); +		break;  	default:  		err = -EOPNOTSUPP;  		break; |