diff options
Diffstat (limited to 'net/dsa/switch.c')
| -rw-r--r-- | net/dsa/switch.c | 322 | 
1 files changed, 255 insertions, 67 deletions
diff --git a/net/dsa/switch.c b/net/dsa/switch.c index 3fb362b6874e..4b5da89dc27a 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -33,15 +33,12 @@ 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; -	} + +	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;  	/* Program the fastest ageing time in case of multiple bridges */  	ageing_time = dsa_switch_fastest_ageing_time(ds, ageing_time); @@ -109,6 +106,7 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds,  {  	bool unset_vlan_filtering = br_vlan_enabled(info->br);  	struct dsa_switch_tree *dst = ds->dst; +	struct netlink_ext_ack extack = {0};  	int err, i;  	if (dst->index == info->tree_index && ds->index == info->sw_index && @@ -139,17 +137,11 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds,  		}  	}  	if (unset_vlan_filtering) { -		struct switchdev_trans trans; - -		trans.ph_prepare = true;  		err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port), -					      false, &trans); -		if (err && err != EOPNOTSUPP) -			return err; - -		trans.ph_prepare = false; -		err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port), -					      false, &trans); +					      false, &extack); +		if (extack._msg) +			dev_err(ds->dev, "port %d: %s\n", info->port, +				extack._msg);  		if (err && err != EOPNOTSUPP)  			return err;  	} @@ -178,6 +170,65 @@ static int dsa_switch_fdb_del(struct dsa_switch *ds,  	return ds->ops->port_fdb_del(ds, port, info->addr, info->vid);  } +static int dsa_switch_hsr_join(struct dsa_switch *ds, +			       struct dsa_notifier_hsr_info *info) +{ +	if (ds->index == info->sw_index && ds->ops->port_hsr_join) +		return ds->ops->port_hsr_join(ds, info->port, info->hsr); + +	return -EOPNOTSUPP; +} + +static int dsa_switch_hsr_leave(struct dsa_switch *ds, +				struct dsa_notifier_hsr_info *info) +{ +	if (ds->index == info->sw_index && ds->ops->port_hsr_leave) +		return ds->ops->port_hsr_leave(ds, info->port, info->hsr); + +	return -EOPNOTSUPP; +} + +static int dsa_switch_lag_change(struct dsa_switch *ds, +				 struct dsa_notifier_lag_info *info) +{ +	if (ds->index == info->sw_index && ds->ops->port_lag_change) +		return ds->ops->port_lag_change(ds, info->port); + +	if (ds->index != info->sw_index && ds->ops->crosschip_lag_change) +		return ds->ops->crosschip_lag_change(ds, info->sw_index, +						     info->port); + +	return 0; +} + +static int dsa_switch_lag_join(struct dsa_switch *ds, +			       struct dsa_notifier_lag_info *info) +{ +	if (ds->index == info->sw_index && ds->ops->port_lag_join) +		return ds->ops->port_lag_join(ds, info->port, info->lag, +					      info->info); + +	if (ds->index != info->sw_index && ds->ops->crosschip_lag_join) +		return ds->ops->crosschip_lag_join(ds, info->sw_index, +						   info->port, info->lag, +						   info->info); + +	return 0; +} + +static int dsa_switch_lag_leave(struct dsa_switch *ds, +				struct dsa_notifier_lag_info *info) +{ +	if (ds->index == info->sw_index && ds->ops->port_lag_leave) +		return ds->ops->port_lag_leave(ds, info->port, info->lag); + +	if (ds->index != info->sw_index && ds->ops->crosschip_lag_leave) +		return ds->ops->crosschip_lag_leave(ds, info->sw_index, +						    info->port, info->lag); + +	return 0; +} +  static bool dsa_switch_mdb_match(struct dsa_switch *ds, int port,  				 struct dsa_notifier_mdb_info *info)  { @@ -190,41 +241,24 @@ static bool dsa_switch_mdb_match(struct dsa_switch *ds, int port,  	return false;  } -static int dsa_switch_mdb_prepare(struct dsa_switch *ds, -				  struct dsa_notifier_mdb_info *info) +static int dsa_switch_mdb_add(struct dsa_switch *ds, +			      struct dsa_notifier_mdb_info *info)  { -	int port, err; +	int err = 0; +	int port; -	if (!ds->ops->port_mdb_prepare || !ds->ops->port_mdb_add) +	if (!ds->ops->port_mdb_add)  		return -EOPNOTSUPP;  	for (port = 0; port < ds->num_ports; port++) {  		if (dsa_switch_mdb_match(ds, port, info)) { -			err = ds->ops->port_mdb_prepare(ds, port, info->mdb); +			err = ds->ops->port_mdb_add(ds, port, info->mdb);  			if (err) -				return err; +				break;  		}  	} -	return 0; -} - -static int dsa_switch_mdb_add(struct dsa_switch *ds, -			      struct dsa_notifier_mdb_info *info) -{ -	int port; - -	if (switchdev_trans_ph_prepare(info->trans)) -		return dsa_switch_mdb_prepare(ds, info); - -	if (!ds->ops->port_mdb_add) -		return 0; - -	for (port = 0; port < ds->num_ports; port++) -		if (dsa_switch_mdb_match(ds, port, info)) -			ds->ops->port_mdb_add(ds, port, info->mdb); - -	return 0; +	return err;  }  static int dsa_switch_mdb_del(struct dsa_switch *ds, @@ -251,17 +285,18 @@ static bool dsa_switch_vlan_match(struct dsa_switch *ds, int port,  	return false;  } -static int dsa_switch_vlan_prepare(struct dsa_switch *ds, -				   struct dsa_notifier_vlan_info *info) +static int dsa_switch_vlan_add(struct dsa_switch *ds, +			       struct dsa_notifier_vlan_info *info)  {  	int port, err; -	if (!ds->ops->port_vlan_prepare || !ds->ops->port_vlan_add) +	if (!ds->ops->port_vlan_add)  		return -EOPNOTSUPP;  	for (port = 0; port < ds->num_ports; port++) {  		if (dsa_switch_vlan_match(ds, port, info)) { -			err = ds->ops->port_vlan_prepare(ds, port, info->vlan); +			err = ds->ops->port_vlan_add(ds, port, info->vlan, +						     info->extack);  			if (err)  				return err;  		} @@ -270,36 +305,163 @@ static int dsa_switch_vlan_prepare(struct dsa_switch *ds,  	return 0;  } -static int dsa_switch_vlan_add(struct dsa_switch *ds, +static int dsa_switch_vlan_del(struct dsa_switch *ds,  			       struct dsa_notifier_vlan_info *info)  { +	if (!ds->ops->port_vlan_del) +		return -EOPNOTSUPP; + +	if (ds->index == info->sw_index) +		return ds->ops->port_vlan_del(ds, info->port, info->vlan); + +	/* Do not deprogram the DSA links as they may be used as conduit +	 * for other VLAN members in the fabric. +	 */ +	return 0; +} + +static bool dsa_switch_tag_proto_match(struct dsa_switch *ds, int port, +				       struct dsa_notifier_tag_proto_info *info) +{ +	if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) +		return true; + +	return false; +} + +static int dsa_switch_change_tag_proto(struct dsa_switch *ds, +				       struct dsa_notifier_tag_proto_info *info) +{ +	const struct dsa_device_ops *tag_ops = info->tag_ops; +	int port, err; + +	if (!ds->ops->change_tag_protocol) +		return -EOPNOTSUPP; + +	ASSERT_RTNL(); + +	for (port = 0; port < ds->num_ports; port++) { +		if (dsa_switch_tag_proto_match(ds, port, info)) { +			err = ds->ops->change_tag_protocol(ds, port, +							   tag_ops->proto); +			if (err) +				return err; + +			if (dsa_is_cpu_port(ds, port)) +				dsa_port_set_tag_protocol(dsa_to_port(ds, port), +							  tag_ops); +		} +	} + +	/* Now that changing the tag protocol can no longer fail, let's update +	 * the remaining bits which are "duplicated for faster access", and the +	 * bits that depend on the tagger, such as the MTU. +	 */ +	for (port = 0; port < ds->num_ports; port++) { +		if (dsa_is_user_port(ds, port)) { +			struct net_device *slave; + +			slave = dsa_to_port(ds, port)->slave; +			dsa_slave_setup_tagger(slave); + +			/* rtnl_mutex is held in dsa_tree_change_tag_proto */ +			dsa_slave_change_mtu(slave, slave->mtu); +		} +	} + +	return 0; +} + +static bool dsa_switch_mrp_match(struct dsa_switch *ds, int port, +				 struct dsa_notifier_mrp_info *info) +{ +	if (ds->index == info->sw_index && port == info->port) +		return true; + +	if (dsa_is_dsa_port(ds, port)) +		return true; + +	return false; +} + +static int dsa_switch_mrp_add(struct dsa_switch *ds, +			      struct dsa_notifier_mrp_info *info) +{ +	int err = 0;  	int port; -	if (switchdev_trans_ph_prepare(info->trans)) -		return dsa_switch_vlan_prepare(ds, info); +	if (!ds->ops->port_mrp_add) +		return -EOPNOTSUPP; -	if (!ds->ops->port_vlan_add) -		return 0; +	for (port = 0; port < ds->num_ports; port++) { +		if (dsa_switch_mrp_match(ds, port, info)) { +			err = ds->ops->port_mrp_add(ds, port, info->mrp); +			if (err) +				break; +		} +	} + +	return err; +} -	for (port = 0; port < ds->num_ports; port++) -		if (dsa_switch_vlan_match(ds, port, info)) -			ds->ops->port_vlan_add(ds, port, info->vlan); +static int dsa_switch_mrp_del(struct dsa_switch *ds, +			      struct dsa_notifier_mrp_info *info) +{ +	if (!ds->ops->port_mrp_del) +		return -EOPNOTSUPP; + +	if (ds->index == info->sw_index) +		return ds->ops->port_mrp_del(ds, info->port, info->mrp);  	return 0;  } -static int dsa_switch_vlan_del(struct dsa_switch *ds, -			       struct dsa_notifier_vlan_info *info) +static bool +dsa_switch_mrp_ring_role_match(struct dsa_switch *ds, int port, +			       struct dsa_notifier_mrp_ring_role_info *info)  { -	if (!ds->ops->port_vlan_del) +	if (ds->index == info->sw_index && port == info->port) +		return true; + +	if (dsa_is_dsa_port(ds, port)) +		return true; + +	return false; +} + +static int +dsa_switch_mrp_add_ring_role(struct dsa_switch *ds, +			     struct dsa_notifier_mrp_ring_role_info *info) +{ +	int err = 0; +	int port; + +	if (!ds->ops->port_mrp_add) +		return -EOPNOTSUPP; + +	for (port = 0; port < ds->num_ports; port++) { +		if (dsa_switch_mrp_ring_role_match(ds, port, info)) { +			err = ds->ops->port_mrp_add_ring_role(ds, port, +							      info->mrp); +			if (err) +				break; +		} +	} + +	return err; +} + +static int +dsa_switch_mrp_del_ring_role(struct dsa_switch *ds, +			     struct dsa_notifier_mrp_ring_role_info *info) +{ +	if (!ds->ops->port_mrp_del)  		return -EOPNOTSUPP;  	if (ds->index == info->sw_index) -		return ds->ops->port_vlan_del(ds, info->port, info->vlan); +		return ds->ops->port_mrp_del_ring_role(ds, info->port, +						       info->mrp); -	/* Do not deprogram the DSA links as they may be used as conduit -	 * for other VLAN members in the fabric. -	 */  	return 0;  } @@ -325,6 +487,21 @@ static int dsa_switch_event(struct notifier_block *nb,  	case DSA_NOTIFIER_FDB_DEL:  		err = dsa_switch_fdb_del(ds, info);  		break; +	case DSA_NOTIFIER_HSR_JOIN: +		err = dsa_switch_hsr_join(ds, info); +		break; +	case DSA_NOTIFIER_HSR_LEAVE: +		err = dsa_switch_hsr_leave(ds, info); +		break; +	case DSA_NOTIFIER_LAG_CHANGE: +		err = dsa_switch_lag_change(ds, info); +		break; +	case DSA_NOTIFIER_LAG_JOIN: +		err = dsa_switch_lag_join(ds, info); +		break; +	case DSA_NOTIFIER_LAG_LEAVE: +		err = dsa_switch_lag_leave(ds, info); +		break;  	case DSA_NOTIFIER_MDB_ADD:  		err = dsa_switch_mdb_add(ds, info);  		break; @@ -340,15 +517,26 @@ static int dsa_switch_event(struct notifier_block *nb,  	case DSA_NOTIFIER_MTU:  		err = dsa_switch_mtu(ds, info);  		break; +	case DSA_NOTIFIER_TAG_PROTO: +		err = dsa_switch_change_tag_proto(ds, info); +		break; +	case DSA_NOTIFIER_MRP_ADD: +		err = dsa_switch_mrp_add(ds, info); +		break; +	case DSA_NOTIFIER_MRP_DEL: +		err = dsa_switch_mrp_del(ds, info); +		break; +	case DSA_NOTIFIER_MRP_ADD_RING_ROLE: +		err = dsa_switch_mrp_add_ring_role(ds, info); +		break; +	case DSA_NOTIFIER_MRP_DEL_RING_ROLE: +		err = dsa_switch_mrp_del_ring_role(ds, info); +		break;  	default:  		err = -EOPNOTSUPP;  		break;  	} -	/* Non-switchdev operations cannot be rolled back. If a DSA driver -	 * returns an error during the chained call, switch chips may be in an -	 * inconsistent state. -	 */  	if (err)  		dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n",  			event, err);  |