diff options
Diffstat (limited to 'net/dsa')
| -rw-r--r-- | net/dsa/Kconfig | 2 | ||||
| -rw-r--r-- | net/dsa/dsa2.c | 19 | ||||
| -rw-r--r-- | net/dsa/dsa_priv.h | 25 | ||||
| -rw-r--r-- | net/dsa/slave.c | 59 | ||||
| -rw-r--r-- | net/dsa/switch.c | 15 | ||||
| -rw-r--r-- | net/dsa/tag_mtk.c | 19 | ||||
| -rw-r--r-- | net/dsa/tag_rtl4_a.c | 12 | 
7 files changed, 100 insertions, 51 deletions
| diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index 3589224c8da9..58b8fc82cd3c 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -118,6 +118,8 @@ config NET_DSA_TAG_OCELOT  config NET_DSA_TAG_OCELOT_8021Q  	tristate "Tag driver for Ocelot family of switches, using VLAN" +	depends on MSCC_OCELOT_SWITCH_LIB || \ +	          (MSCC_OCELOT_SWITCH_LIB=n && COMPILE_TEST)  	select NET_DSA_TAG_8021Q  	help  	  Say Y or M if you want to enable support for tagging frames with a diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 4d4956ed303b..3c3e56a1f34d 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -795,8 +795,14 @@ static int dsa_tree_setup_switches(struct dsa_switch_tree *dst)  	list_for_each_entry(dp, &dst->ports, list) {  		err = dsa_port_setup(dp); -		if (err) +		if (err) { +			dsa_port_devlink_teardown(dp); +			dp->type = DSA_PORT_TYPE_UNUSED; +			err = dsa_port_devlink_setup(dp); +			if (err) +				goto teardown;  			continue; +		}  	}  	return 0; @@ -1066,6 +1072,7 @@ static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master)  {  	struct dsa_switch *ds = dp->ds;  	struct dsa_switch_tree *dst = ds->dst; +	const struct dsa_device_ops *tag_ops;  	enum dsa_tag_protocol tag_protocol;  	tag_protocol = dsa_get_tag_protocol(dp, master); @@ -1080,14 +1087,16 @@ static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master)  		 * nothing to do here.  		 */  	} else { -		dst->tag_ops = dsa_tag_driver_get(tag_protocol); -		if (IS_ERR(dst->tag_ops)) { -			if (PTR_ERR(dst->tag_ops) == -ENOPROTOOPT) +		tag_ops = dsa_tag_driver_get(tag_protocol); +		if (IS_ERR(tag_ops)) { +			if (PTR_ERR(tag_ops) == -ENOPROTOOPT)  				return -EPROBE_DEFER;  			dev_warn(ds->dev, "No tagger for this switch\n");  			dp->master = NULL; -			return PTR_ERR(dst->tag_ops); +			return PTR_ERR(tag_ops);  		} + +		dst->tag_ops = tag_ops;  	}  	dp->master = master; diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 2eeaa42f2e08..9d4b0e9b1aa1 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -230,8 +230,8 @@ int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr);  void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr);  extern const struct phylink_mac_ops dsa_port_phylink_mac_ops; -static inline bool dsa_port_offloads_netdev(struct dsa_port *dp, -					    struct net_device *dev) +static inline bool dsa_port_offloads_bridge_port(struct dsa_port *dp, +						 struct net_device *dev)  {  	/* Switchdev offloading can be configured on: */ @@ -241,12 +241,6 @@ static inline bool dsa_port_offloads_netdev(struct dsa_port *dp,  		 */  		return true; -	if (dp->bridge_dev == dev) -		/* DSA ports connected to a bridge, and event was emitted -		 * for the bridge. -		 */ -		return true; -  	if (dp->lag_dev == dev)  		/* DSA ports connected to a bridge via a LAG */  		return true; @@ -254,14 +248,23 @@ static inline bool dsa_port_offloads_netdev(struct dsa_port *dp,  	return false;  } +static inline bool dsa_port_offloads_bridge(struct dsa_port *dp, +					    struct net_device *bridge_dev) +{ +	/* DSA ports connected to a bridge, and event was emitted +	 * for the bridge. +	 */ +	return dp->bridge_dev == bridge_dev; +} +  /* Returns true if any port of this tree offloads the given net_device */ -static inline bool dsa_tree_offloads_netdev(struct dsa_switch_tree *dst, -					    struct net_device *dev) +static inline bool dsa_tree_offloads_bridge_port(struct dsa_switch_tree *dst, +						 struct net_device *dev)  {  	struct dsa_port *dp;  	list_for_each_entry(dp, &dst->ports, list) -		if (dsa_port_offloads_netdev(dp, dev)) +		if (dsa_port_offloads_bridge_port(dp, dev))  			return true;  	return false; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 491e3761b5f4..992fcab4b552 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -278,28 +278,43 @@ static int dsa_slave_port_attr_set(struct net_device *dev,  	struct dsa_port *dp = dsa_slave_to_port(dev);  	int ret; -	if (!dsa_port_offloads_netdev(dp, attr->orig_dev)) -		return -EOPNOTSUPP; -  	switch (attr->id) {  	case SWITCHDEV_ATTR_ID_PORT_STP_STATE: +		if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev)) +			return -EOPNOTSUPP; +  		ret = dsa_port_set_state(dp, attr->u.stp_state);  		break;  	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: +		if (!dsa_port_offloads_bridge(dp, attr->orig_dev)) +			return -EOPNOTSUPP; +  		ret = dsa_port_vlan_filtering(dp, attr->u.vlan_filtering,  					      extack);  		break;  	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: +		if (!dsa_port_offloads_bridge(dp, attr->orig_dev)) +			return -EOPNOTSUPP; +  		ret = dsa_port_ageing_time(dp, attr->u.ageing_time);  		break;  	case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: +		if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev)) +			return -EOPNOTSUPP; +  		ret = dsa_port_pre_bridge_flags(dp, attr->u.brport_flags,  						extack);  		break;  	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: +		if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev)) +			return -EOPNOTSUPP; +  		ret = dsa_port_bridge_flags(dp, attr->u.brport_flags, extack);  		break;  	case SWITCHDEV_ATTR_ID_BRIDGE_MROUTER: +		if (!dsa_port_offloads_bridge(dp, attr->orig_dev)) +			return -EOPNOTSUPP; +  		ret = dsa_port_mrouter(dp->cpu_dp, attr->u.mrouter, extack);  		break;  	default: @@ -341,9 +356,6 @@ static int dsa_slave_vlan_add(struct net_device *dev,  	struct switchdev_obj_port_vlan vlan;  	int err; -	if (!dsa_port_offloads_netdev(dp, obj->orig_dev)) -		return -EOPNOTSUPP; -  	if (dsa_port_skip_vlan_configuration(dp)) {  		NL_SET_ERR_MSG_MOD(extack, "skipping configuration of VLAN");  		return 0; @@ -391,27 +403,36 @@ static int dsa_slave_port_obj_add(struct net_device *dev,  	switch (obj->id) {  	case SWITCHDEV_OBJ_ID_PORT_MDB: -		if (!dsa_port_offloads_netdev(dp, obj->orig_dev)) +		if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev))  			return -EOPNOTSUPP; +  		err = dsa_port_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj));  		break;  	case SWITCHDEV_OBJ_ID_HOST_MDB: +		if (!dsa_port_offloads_bridge(dp, obj->orig_dev)) +			return -EOPNOTSUPP; +  		/* DSA can directly translate this to a normal MDB add,  		 * but on the CPU port.  		 */  		err = dsa_port_mdb_add(dp->cpu_dp, SWITCHDEV_OBJ_PORT_MDB(obj));  		break;  	case SWITCHDEV_OBJ_ID_PORT_VLAN: +		if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev)) +			return -EOPNOTSUPP; +  		err = dsa_slave_vlan_add(dev, obj, extack);  		break;  	case SWITCHDEV_OBJ_ID_MRP: -		if (!dsa_port_offloads_netdev(dp, obj->orig_dev)) +		if (!dsa_port_offloads_bridge(dp, obj->orig_dev))  			return -EOPNOTSUPP; +  		err = dsa_port_mrp_add(dp, SWITCHDEV_OBJ_MRP(obj));  		break;  	case SWITCHDEV_OBJ_ID_RING_ROLE_MRP: -		if (!dsa_port_offloads_netdev(dp, obj->orig_dev)) +		if (!dsa_port_offloads_bridge(dp, obj->orig_dev))  			return -EOPNOTSUPP; +  		err = dsa_port_mrp_add_ring_role(dp,  						 SWITCHDEV_OBJ_RING_ROLE_MRP(obj));  		break; @@ -431,9 +452,6 @@ static int dsa_slave_vlan_del(struct net_device *dev,  	struct switchdev_obj_port_vlan *vlan;  	int err; -	if (!dsa_port_offloads_netdev(dp, obj->orig_dev)) -		return -EOPNOTSUPP; -  	if (dsa_port_skip_vlan_configuration(dp))  		return 0; @@ -459,27 +477,36 @@ static int dsa_slave_port_obj_del(struct net_device *dev,  	switch (obj->id) {  	case SWITCHDEV_OBJ_ID_PORT_MDB: -		if (!dsa_port_offloads_netdev(dp, obj->orig_dev)) +		if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev))  			return -EOPNOTSUPP; +  		err = dsa_port_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj));  		break;  	case SWITCHDEV_OBJ_ID_HOST_MDB: +		if (!dsa_port_offloads_bridge(dp, obj->orig_dev)) +			return -EOPNOTSUPP; +  		/* DSA can directly translate this to a normal MDB add,  		 * but on the CPU port.  		 */  		err = dsa_port_mdb_del(dp->cpu_dp, SWITCHDEV_OBJ_PORT_MDB(obj));  		break;  	case SWITCHDEV_OBJ_ID_PORT_VLAN: +		if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev)) +			return -EOPNOTSUPP; +  		err = dsa_slave_vlan_del(dev, obj);  		break;  	case SWITCHDEV_OBJ_ID_MRP: -		if (!dsa_port_offloads_netdev(dp, obj->orig_dev)) +		if (!dsa_port_offloads_bridge(dp, obj->orig_dev))  			return -EOPNOTSUPP; +  		err = dsa_port_mrp_del(dp, SWITCHDEV_OBJ_MRP(obj));  		break;  	case SWITCHDEV_OBJ_ID_RING_ROLE_MRP: -		if (!dsa_port_offloads_netdev(dp, obj->orig_dev)) +		if (!dsa_port_offloads_bridge(dp, obj->orig_dev))  			return -EOPNOTSUPP; +  		err = dsa_port_mrp_del_ring_role(dp,  						 SWITCHDEV_OBJ_RING_ROLE_MRP(obj));  		break; @@ -2298,7 +2325,7 @@ static int dsa_slave_switchdev_event(struct notifier_block *unused,  			 * other ports bridged with the LAG should be able to  			 * autonomously forward towards it.  			 */ -			if (dsa_tree_offloads_netdev(dp->ds->dst, dev)) +			if (dsa_tree_offloads_bridge_port(dp->ds->dst, dev))  				return NOTIFY_DONE;  		} diff --git a/net/dsa/switch.c b/net/dsa/switch.c index 4b5da89dc27a..32963276452f 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -107,7 +107,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; +	int err, port;  	if (dst->index == info->tree_index && ds->index == info->sw_index &&  	    ds->ops->port_bridge_join) @@ -124,13 +124,16 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds,  	 * it. That is a good thing, because that lets us handle it and also  	 * handle the case where the switch's vlan_filtering setting is global  	 * (not per port). When that happens, the correct moment to trigger the -	 * vlan_filtering callback is only when the last port left this bridge. +	 * vlan_filtering callback is only when the last port leaves the last +	 * VLAN-aware bridge.  	 */  	if (unset_vlan_filtering && ds->vlan_filtering_is_global) { -		for (i = 0; i < ds->num_ports; i++) { -			if (i == info->port) -				continue; -			if (dsa_to_port(ds, i)->bridge_dev == info->br) { +		for (port = 0; port < ds->num_ports; port++) { +			struct net_device *bridge_dev; + +			bridge_dev = dsa_to_port(ds, port)->bridge_dev; + +			if (bridge_dev && br_vlan_enabled(bridge_dev)) {  				unset_vlan_filtering = false;  				break;  			} diff --git a/net/dsa/tag_mtk.c b/net/dsa/tag_mtk.c index 38dcdded74c0..59748487664f 100644 --- a/net/dsa/tag_mtk.c +++ b/net/dsa/tag_mtk.c @@ -13,6 +13,7 @@  #define MTK_HDR_LEN		4  #define MTK_HDR_XMIT_UNTAGGED		0  #define MTK_HDR_XMIT_TAGGED_TPID_8100	1 +#define MTK_HDR_XMIT_TAGGED_TPID_88A8	2  #define MTK_HDR_RECV_SOURCE_PORT_MASK	GENMASK(2, 0)  #define MTK_HDR_XMIT_DP_BIT_MASK	GENMASK(5, 0)  #define MTK_HDR_XMIT_SA_DIS		BIT(6) @@ -21,8 +22,8 @@ static struct sk_buff *mtk_tag_xmit(struct sk_buff *skb,  				    struct net_device *dev)  {  	struct dsa_port *dp = dsa_slave_to_port(dev); +	u8 xmit_tpid;  	u8 *mtk_tag; -	bool is_vlan_skb = true;  	unsigned char *dest = eth_hdr(skb)->h_dest;  	bool is_multicast_skb = is_multicast_ether_addr(dest) &&  				!is_broadcast_ether_addr(dest); @@ -33,10 +34,17 @@ static struct sk_buff *mtk_tag_xmit(struct sk_buff *skb,  	 * the both special and VLAN tag at the same time and then look up VLAN  	 * table with VID.  	 */ -	if (!skb_vlan_tagged(skb)) { +	switch (skb->protocol) { +	case htons(ETH_P_8021Q): +		xmit_tpid = MTK_HDR_XMIT_TAGGED_TPID_8100; +		break; +	case htons(ETH_P_8021AD): +		xmit_tpid = MTK_HDR_XMIT_TAGGED_TPID_88A8; +		break; +	default: +		xmit_tpid = MTK_HDR_XMIT_UNTAGGED;  		skb_push(skb, MTK_HDR_LEN);  		memmove(skb->data, skb->data + MTK_HDR_LEN, 2 * ETH_ALEN); -		is_vlan_skb = false;  	}  	mtk_tag = skb->data + 2 * ETH_ALEN; @@ -44,8 +52,7 @@ static struct sk_buff *mtk_tag_xmit(struct sk_buff *skb,  	/* Mark tag attribute on special tag insertion to notify hardware  	 * whether that's a combined special tag with 802.1Q header.  	 */ -	mtk_tag[0] = is_vlan_skb ? MTK_HDR_XMIT_TAGGED_TPID_8100 : -		     MTK_HDR_XMIT_UNTAGGED; +	mtk_tag[0] = xmit_tpid;  	mtk_tag[1] = (1 << dp->index) & MTK_HDR_XMIT_DP_BIT_MASK;  	/* Disable SA learning for multicast frames */ @@ -53,7 +60,7 @@ static struct sk_buff *mtk_tag_xmit(struct sk_buff *skb,  		mtk_tag[1] |= MTK_HDR_XMIT_SA_DIS;  	/* Tag control information is kept for 802.1Q */ -	if (!is_vlan_skb) { +	if (xmit_tpid == MTK_HDR_XMIT_UNTAGGED) {  		mtk_tag[2] = 0;  		mtk_tag[3] = 0;  	} diff --git a/net/dsa/tag_rtl4_a.c b/net/dsa/tag_rtl4_a.c index c17d39b4a1a0..e9176475bac8 100644 --- a/net/dsa/tag_rtl4_a.c +++ b/net/dsa/tag_rtl4_a.c @@ -35,14 +35,12 @@ static struct sk_buff *rtl4a_tag_xmit(struct sk_buff *skb,  				      struct net_device *dev)  {  	struct dsa_port *dp = dsa_slave_to_port(dev); +	__be16 *p;  	u8 *tag; -	u16 *p;  	u16 out;  	/* Pad out to at least 60 bytes */ -	if (unlikely(eth_skb_pad(skb))) -		return NULL; -	if (skb_cow_head(skb, RTL4_A_HDR_LEN) < 0) +	if (unlikely(__skb_put_padto(skb, ETH_ZLEN, false)))  		return NULL;  	netdev_dbg(dev, "add realtek tag to package to port %d\n", @@ -53,13 +51,13 @@ static struct sk_buff *rtl4a_tag_xmit(struct sk_buff *skb,  	tag = skb->data + 2 * ETH_ALEN;  	/* Set Ethertype */ -	p = (u16 *)tag; +	p = (__be16 *)tag;  	*p = htons(RTL4_A_ETHERTYPE);  	out = (RTL4_A_PROTOCOL_RTL8366RB << 12) | (2 << 8); -	/* The lower bits is the port numer */ +	/* The lower bits is the port number */  	out |= (u8)dp->index; -	p = (u16 *)(tag + 2); +	p = (__be16 *)(tag + 2);  	*p = htons(out);  	return skb; |