diff options
Diffstat (limited to 'net/dsa')
| -rw-r--r-- | net/dsa/Kconfig | 8 | ||||
| -rw-r--r-- | net/dsa/Makefile | 1 | ||||
| -rw-r--r-- | net/dsa/dsa.c | 2 | ||||
| -rw-r--r-- | net/dsa/port.c | 72 | ||||
| -rw-r--r-- | net/dsa/tag_8021q.c | 84 | ||||
| -rw-r--r-- | net/dsa/tag_8021q.h | 7 | ||||
| -rw-r--r-- | net/dsa/tag_ocelot_8021q.c | 2 | ||||
| -rw-r--r-- | net/dsa/tag_sja1105.c | 72 | ||||
| -rw-r--r-- | net/dsa/tag_vsc73xx_8021q.c | 68 | ||||
| -rw-r--r-- | net/dsa/user.c | 99 | ||||
| -rw-r--r-- | net/dsa/user.h | 2 | 
11 files changed, 257 insertions, 160 deletions
| diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index 8e698bea99a3..2dfe9063613f 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -129,7 +129,7 @@ config NET_DSA_TAG_RTL4_A  	tristate "Tag driver for Realtek 4 byte protocol A tags"  	help  	  Say Y or M if you want to enable support for tagging frames for the -	  Realtek switches with 4 byte protocol A tags, sich as found in +	  Realtek switches with 4 byte protocol A tags, such as found in  	  the Realtek RTL8366RB.  config NET_DSA_TAG_RTL8_4 @@ -166,6 +166,12 @@ config NET_DSA_TAG_TRAILER  	  Say Y or M if you want to enable support for tagging frames at  	  with a trailed. e.g. Marvell 88E6060. +config NET_DSA_TAG_VSC73XX_8021Q +	tristate "Tag driver for Microchip/Vitesse VSC73xx family of switches, using VLAN" +	help +	  Say Y or M if you want to enable support for tagging frames with a +	  custom VLAN-based header. +  config NET_DSA_TAG_XRS700X  	tristate "Tag driver for XRS700x switches"  	help diff --git a/net/dsa/Makefile b/net/dsa/Makefile index 8a1894a42552..555c07cfeb71 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_NET_DSA_TAG_RTL8_4) += tag_rtl8_4.o  obj-$(CONFIG_NET_DSA_TAG_RZN1_A5PSW) += tag_rzn1_a5psw.o  obj-$(CONFIG_NET_DSA_TAG_SJA1105) += tag_sja1105.o  obj-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o +obj-$(CONFIG_NET_DSA_TAG_VSC73XX_8021Q) += tag_vsc73xx_8021q.o  obj-$(CONFIG_NET_DSA_TAG_XRS700X) += tag_xrs700x.o  # for tracing framework to find trace.h diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 12521a7d4048..668c729946ea 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -1507,9 +1507,7 @@ static int dsa_switch_probe(struct dsa_switch *ds)  	if (ds->phylink_mac_ops) {  		if (ds->ops->phylink_mac_select_pcs || -		    ds->ops->phylink_mac_prepare ||  		    ds->ops->phylink_mac_config || -		    ds->ops->phylink_mac_finish ||  		    ds->ops->phylink_mac_link_down ||  		    ds->ops->phylink_mac_link_up)  			return -EINVAL; diff --git a/net/dsa/port.c b/net/dsa/port.c index 9a249d4ac3a5..25258b33e59e 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -1467,10 +1467,34 @@ int dsa_port_change_conduit(struct dsa_port *dp, struct net_device *conduit,  	 */  	dsa_user_unsync_ha(dev); +	/* If live-changing, we also need to uninstall the user device address +	 * from the port FDB and the conduit interface. +	 */ +	if (dev->flags & IFF_UP) +		dsa_user_host_uc_uninstall(dev); +  	err = dsa_port_assign_conduit(dp, conduit, extack, true);  	if (err)  		goto rewind_old_addrs; +	/* If the port doesn't have its own MAC address and relies on the DSA +	 * conduit's one, inherit it again from the new DSA conduit. +	 */ +	if (is_zero_ether_addr(dp->mac)) +		eth_hw_addr_inherit(dev, conduit); + +	/* If live-changing, we need to install the user device address to the +	 * port FDB and the conduit interface. +	 */ +	if (dev->flags & IFF_UP) { +		err = dsa_user_host_uc_install(dev, dev->dev_addr); +		if (err) { +			NL_SET_ERR_MSG_MOD(extack, +					   "Failed to install host UC address"); +			goto rewind_addr_inherit; +		} +	} +  	dsa_user_sync_ha(dev);  	if (vlan_filtering) { @@ -1500,10 +1524,26 @@ rewind_new_vlan:  rewind_new_addrs:  	dsa_user_unsync_ha(dev); +	if (dev->flags & IFF_UP) +		dsa_user_host_uc_uninstall(dev); + +rewind_addr_inherit: +	if (is_zero_ether_addr(dp->mac)) +		eth_hw_addr_inherit(dev, old_conduit); +  	dsa_port_assign_conduit(dp, old_conduit, NULL, false);  /* Restore the objects on the old CPU port */  rewind_old_addrs: +	if (dev->flags & IFF_UP) { +		tmp = dsa_user_host_uc_install(dev, dev->dev_addr); +		if (tmp) { +			dev_err(ds->dev, +				"port %d failed to restore host UC address: %pe\n", +				dp->index, ERR_PTR(tmp)); +		} +	} +  	dsa_user_sync_ha(dev);  	if (vlan_filtering) { @@ -1549,21 +1589,6 @@ dsa_port_phylink_mac_select_pcs(struct phylink_config *config,  	return pcs;  } -static int dsa_port_phylink_mac_prepare(struct phylink_config *config, -					unsigned int mode, -					phy_interface_t interface) -{ -	struct dsa_port *dp = dsa_phylink_to_port(config); -	struct dsa_switch *ds = dp->ds; -	int err = 0; - -	if (ds->ops->phylink_mac_prepare) -		err = ds->ops->phylink_mac_prepare(ds, dp->index, mode, -						   interface); - -	return err; -} -  static void dsa_port_phylink_mac_config(struct phylink_config *config,  					unsigned int mode,  					const struct phylink_link_state *state) @@ -1577,21 +1602,6 @@ static void dsa_port_phylink_mac_config(struct phylink_config *config,  	ds->ops->phylink_mac_config(ds, dp->index, mode, state);  } -static int dsa_port_phylink_mac_finish(struct phylink_config *config, -				       unsigned int mode, -				       phy_interface_t interface) -{ -	struct dsa_port *dp = dsa_phylink_to_port(config); -	struct dsa_switch *ds = dp->ds; -	int err = 0; - -	if (ds->ops->phylink_mac_finish) -		err = ds->ops->phylink_mac_finish(ds, dp->index, mode, -						  interface); - -	return err; -} -  static void dsa_port_phylink_mac_link_down(struct phylink_config *config,  					   unsigned int mode,  					   phy_interface_t interface) @@ -1624,9 +1634,7 @@ static void dsa_port_phylink_mac_link_up(struct phylink_config *config,  static const struct phylink_mac_ops dsa_port_phylink_mac_ops = {  	.mac_select_pcs = dsa_port_phylink_mac_select_pcs, -	.mac_prepare = dsa_port_phylink_mac_prepare,  	.mac_config = dsa_port_phylink_mac_config, -	.mac_finish = dsa_port_phylink_mac_finish,  	.mac_link_down = dsa_port_phylink_mac_link_down,  	.mac_link_up = dsa_port_phylink_mac_link_up,  }; diff --git a/net/dsa/tag_8021q.c b/net/dsa/tag_8021q.c index 71b26ae6db39..3ee53e28ec2e 100644 --- a/net/dsa/tag_8021q.c +++ b/net/dsa/tag_8021q.c @@ -286,7 +286,8 @@ int dsa_switch_tag_8021q_vlan_del(struct dsa_switch *ds,   * be used for VLAN-unaware bridging.   */  int dsa_tag_8021q_bridge_join(struct dsa_switch *ds, int port, -			      struct dsa_bridge bridge) +			      struct dsa_bridge bridge, bool *tx_fwd_offload, +			      struct netlink_ext_ack *extack)  {  	struct dsa_port *dp = dsa_to_port(ds, port);  	u16 standalone_vid, bridge_vid; @@ -304,6 +305,8 @@ int dsa_tag_8021q_bridge_join(struct dsa_switch *ds, int port,  	dsa_port_tag_8021q_vlan_del(dp, standalone_vid, false); +	*tx_fwd_offload = true; +  	return 0;  }  EXPORT_SYMBOL_GPL(dsa_tag_8021q_bridge_join); @@ -468,8 +471,8 @@ struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,  }  EXPORT_SYMBOL_GPL(dsa_8021q_xmit); -struct net_device *dsa_tag_8021q_find_port_by_vbid(struct net_device *conduit, -						   int vbid) +static struct net_device * +dsa_tag_8021q_find_port_by_vbid(struct net_device *conduit, int vbid)  {  	struct dsa_port *cpu_dp = conduit->dsa_ptr;  	struct dsa_switch_tree *dst = cpu_dp->dst; @@ -495,30 +498,91 @@ struct net_device *dsa_tag_8021q_find_port_by_vbid(struct net_device *conduit,  	return NULL;  } -EXPORT_SYMBOL_GPL(dsa_tag_8021q_find_port_by_vbid); +struct net_device *dsa_tag_8021q_find_user(struct net_device *conduit, +					   int source_port, int switch_id, +					   int vid, int vbid) +{ +	/* Always prefer precise source port information, if available */ +	if (source_port != -1 && switch_id != -1) +		return dsa_conduit_find_user(conduit, switch_id, source_port); +	else if (vbid >= 1) +		return dsa_tag_8021q_find_port_by_vbid(conduit, vbid); + +	return dsa_find_designated_bridge_port_by_vid(conduit, vid); +} +EXPORT_SYMBOL_GPL(dsa_tag_8021q_find_user); + +/** + * dsa_8021q_rcv - Decode source information from tag_8021q header + * @skb: RX socket buffer + * @source_port: pointer to storage for precise source port information. + *	If this is known already from outside tag_8021q, the pre-initialized + *	value is preserved. If not known, pass -1. + * @switch_id: similar to source_port. + * @vbid: pointer to storage for imprecise bridge ID. Must be pre-initialized + *	with -1. If a positive value is returned, the source_port and switch_id + *	are invalid. + * @vid: pointer to storage for original VID, in case tag_8021q decoding failed. + * + * If the packet has a tag_8021q header, decode it and set @source_port, + * @switch_id and @vbid, and strip the header. Otherwise set @vid and keep the + * header in the hwaccel area of the packet. + */  void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id, -		   int *vbid) +		   int *vbid, int *vid)  { -	u16 vid, tci; +	int tmp_source_port, tmp_switch_id, tmp_vbid; +	__be16 vlan_proto; +	u16 tmp_vid, tci;  	if (skb_vlan_tag_present(skb)) { +		vlan_proto = skb->vlan_proto;  		tci = skb_vlan_tag_get(skb);  		__vlan_hwaccel_clear_tag(skb);  	} else { +		struct vlan_ethhdr *hdr = vlan_eth_hdr(skb); + +		vlan_proto = hdr->h_vlan_proto;  		skb_push_rcsum(skb, ETH_HLEN);  		__skb_vlan_pop(skb, &tci);  		skb_pull_rcsum(skb, ETH_HLEN);  	} -	vid = tci & VLAN_VID_MASK; +	tmp_vid = tci & VLAN_VID_MASK; +	if (!vid_is_dsa_8021q(tmp_vid)) { +		/* Not a tag_8021q frame, so return the VID to the +		 * caller for further processing, and put the tag back +		 */ +		if (vid) +			*vid = tmp_vid; + +		__vlan_hwaccel_put_tag(skb, vlan_proto, tci); + +		return; +	} -	*source_port = dsa_8021q_rx_source_port(vid); -	*switch_id = dsa_8021q_rx_switch_id(vid); +	tmp_source_port = dsa_8021q_rx_source_port(tmp_vid); +	tmp_switch_id = dsa_8021q_rx_switch_id(tmp_vid); +	tmp_vbid = dsa_tag_8021q_rx_vbid(tmp_vid); + +	/* Precise source port information is unknown when receiving from a +	 * VLAN-unaware bridging domain, and tmp_source_port and tmp_switch_id +	 * are zeroes in this case. +	 * +	 * Preserve the source information from hardware-specific mechanisms, +	 * if available. This allows us to not overwrite a valid source port +	 * and switch ID with less precise values. +	 */ +	if (tmp_vbid == 0 && *source_port == -1) +		*source_port = tmp_source_port; +	if (tmp_vbid == 0 && *switch_id == -1) +		*switch_id = tmp_switch_id;  	if (vbid) -		*vbid = dsa_tag_8021q_rx_vbid(vid); +		*vbid = tmp_vbid;  	skb->priority = (tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; +	return;  }  EXPORT_SYMBOL_GPL(dsa_8021q_rcv); diff --git a/net/dsa/tag_8021q.h b/net/dsa/tag_8021q.h index 41f7167ac520..27b8906f99ec 100644 --- a/net/dsa/tag_8021q.h +++ b/net/dsa/tag_8021q.h @@ -14,10 +14,11 @@ struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,  			       u16 tpid, u16 tci);  void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id, -		   int *vbid); +		   int *vbid, int *vid); -struct net_device *dsa_tag_8021q_find_port_by_vbid(struct net_device *conduit, -						   int vbid); +struct net_device *dsa_tag_8021q_find_user(struct net_device *conduit, +					   int source_port, int switch_id, +					   int vid, int vbid);  int dsa_switch_tag_8021q_vlan_add(struct dsa_switch *ds,  				  struct dsa_notifier_tag_8021q_vlan_info *info); diff --git a/net/dsa/tag_ocelot_8021q.c b/net/dsa/tag_ocelot_8021q.c index b059381310fe..8e8b1bef6af6 100644 --- a/net/dsa/tag_ocelot_8021q.c +++ b/net/dsa/tag_ocelot_8021q.c @@ -81,7 +81,7 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb,  {  	int src_port, switch_id; -	dsa_8021q_rcv(skb, &src_port, &switch_id, NULL); +	dsa_8021q_rcv(skb, &src_port, &switch_id, NULL, NULL);  	skb->dev = dsa_conduit_find_user(netdev, switch_id, src_port);  	if (!skb->dev) diff --git a/net/dsa/tag_sja1105.c b/net/dsa/tag_sja1105.c index 1aba1d05c27a..3e902af7eea6 100644 --- a/net/dsa/tag_sja1105.c +++ b/net/dsa/tag_sja1105.c @@ -472,37 +472,14 @@ static bool sja1110_skb_has_inband_control_extension(const struct sk_buff *skb)  	return ntohs(eth_hdr(skb)->h_proto) == ETH_P_SJA1110;  } -/* If the VLAN in the packet is a tag_8021q one, set @source_port and - * @switch_id and strip the header. Otherwise set @vid and keep it in the - * packet. - */ -static void sja1105_vlan_rcv(struct sk_buff *skb, int *source_port, -			     int *switch_id, int *vbid, u16 *vid) -{ -	struct vlan_ethhdr *hdr = vlan_eth_hdr(skb); -	u16 vlan_tci; - -	if (skb_vlan_tag_present(skb)) -		vlan_tci = skb_vlan_tag_get(skb); -	else -		vlan_tci = ntohs(hdr->h_vlan_TCI); - -	if (vid_is_dsa_8021q(vlan_tci & VLAN_VID_MASK)) -		return dsa_8021q_rcv(skb, source_port, switch_id, vbid); - -	/* Try our best with imprecise RX */ -	*vid = vlan_tci & VLAN_VID_MASK; -} -  static struct sk_buff *sja1105_rcv(struct sk_buff *skb,  				   struct net_device *netdev)  { -	int source_port = -1, switch_id = -1, vbid = -1; +	int source_port = -1, switch_id = -1, vbid = -1, vid = -1;  	struct sja1105_meta meta = {0};  	struct ethhdr *hdr;  	bool is_link_local;  	bool is_meta; -	u16 vid;  	hdr = eth_hdr(skb);  	is_link_local = sja1105_is_link_local(skb); @@ -524,37 +501,16 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,  	/* Normal data plane traffic and link-local frames are tagged with  	 * a tag_8021q VLAN which we have to strip  	 */ -	if (sja1105_skb_has_tag_8021q(skb)) { -		int tmp_source_port = -1, tmp_switch_id = -1; - -		sja1105_vlan_rcv(skb, &tmp_source_port, &tmp_switch_id, &vbid, -				 &vid); -		/* Preserve the source information from the INCL_SRCPT option, -		 * if available. This allows us to not overwrite a valid source -		 * port and switch ID with zeroes when receiving link-local -		 * frames from a VLAN-unaware bridged port (non-zero vbid) or a -		 * VLAN-aware bridged port (non-zero vid). Furthermore, the -		 * tag_8021q source port information is only of trust when the -		 * vbid is 0 (precise port). Otherwise, tmp_source_port and -		 * tmp_switch_id will be zeroes. -		 */ -		if (vbid == 0 && source_port == -1) -			source_port = tmp_source_port; -		if (vbid == 0 && switch_id == -1) -			switch_id = tmp_switch_id; -	} else if (source_port == -1 && switch_id == -1) { +	if (sja1105_skb_has_tag_8021q(skb)) +		dsa_8021q_rcv(skb, &source_port, &switch_id, &vbid, &vid); +	else if (source_port == -1 && switch_id == -1)  		/* Packets with no source information have no chance of  		 * getting accepted, drop them straight away.  		 */  		return NULL; -	} -	if (source_port != -1 && switch_id != -1) -		skb->dev = dsa_conduit_find_user(netdev, switch_id, source_port); -	else if (vbid >= 1) -		skb->dev = dsa_tag_8021q_find_port_by_vbid(netdev, vbid); -	else -		skb->dev = dsa_find_designated_bridge_port_by_vid(netdev, vid); +	skb->dev = dsa_tag_8021q_find_user(netdev, source_port, switch_id, +					   vid, vbid);  	if (!skb->dev) {  		netdev_warn(netdev, "Couldn't decode source port\n");  		return NULL; @@ -677,9 +633,8 @@ static struct sk_buff *sja1110_rcv_inband_control_extension(struct sk_buff *skb,  static struct sk_buff *sja1110_rcv(struct sk_buff *skb,  				   struct net_device *netdev)  { -	int source_port = -1, switch_id = -1, vbid = -1; +	int source_port = -1, switch_id = -1, vbid = -1, vid = -1;  	bool host_only = false; -	u16 vid = 0;  	if (sja1110_skb_has_inband_control_extension(skb)) {  		skb = sja1110_rcv_inband_control_extension(skb, &source_port, @@ -691,14 +646,11 @@ static struct sk_buff *sja1110_rcv(struct sk_buff *skb,  	/* Packets with in-band control extensions might still have RX VLANs */  	if (likely(sja1105_skb_has_tag_8021q(skb))) -		sja1105_vlan_rcv(skb, &source_port, &switch_id, &vbid, &vid); - -	if (vbid >= 1) -		skb->dev = dsa_tag_8021q_find_port_by_vbid(netdev, vbid); -	else if (source_port == -1 || switch_id == -1) -		skb->dev = dsa_find_designated_bridge_port_by_vid(netdev, vid); -	else -		skb->dev = dsa_conduit_find_user(netdev, switch_id, source_port); +		dsa_8021q_rcv(skb, &source_port, &switch_id, &vbid, &vid); + +	skb->dev = dsa_tag_8021q_find_user(netdev, source_port, switch_id, +					   vid, vbid); +  	if (!skb->dev) {  		netdev_warn(netdev, "Couldn't decode source port\n");  		return NULL; diff --git a/net/dsa/tag_vsc73xx_8021q.c b/net/dsa/tag_vsc73xx_8021q.c new file mode 100644 index 000000000000..af121a9aff7f --- /dev/null +++ b/net/dsa/tag_vsc73xx_8021q.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* Copyright (C) 2024 Pawel Dembicki <[email protected]> + */ +#include <linux/dsa/8021q.h> + +#include "tag.h" +#include "tag_8021q.h" + +#define VSC73XX_8021Q_NAME "vsc73xx-8021q" + +static struct sk_buff * +vsc73xx_xmit(struct sk_buff *skb, struct net_device *netdev) +{ +	struct dsa_port *dp = dsa_user_to_port(netdev); +	u16 queue_mapping = skb_get_queue_mapping(skb); +	u16 tx_vid = dsa_tag_8021q_standalone_vid(dp); +	u8 pcp; + +	if (skb->offload_fwd_mark) { +		unsigned int bridge_num = dsa_port_bridge_num_get(dp); +		struct net_device *br = dsa_port_bridge_dev_get(dp); + +		if (br_vlan_enabled(br)) +			return skb; + +		tx_vid = dsa_tag_8021q_bridge_vid(bridge_num); +	} + +	pcp = netdev_txq_to_tc(netdev, queue_mapping); + +	return dsa_8021q_xmit(skb, netdev, ETH_P_8021Q, +			      ((pcp << VLAN_PRIO_SHIFT) | tx_vid)); +} + +static struct sk_buff * +vsc73xx_rcv(struct sk_buff *skb, struct net_device *netdev) +{ +	int src_port = -1, switch_id = -1, vbid = -1, vid = -1; + +	dsa_8021q_rcv(skb, &src_port, &switch_id, &vbid, &vid); + +	skb->dev = dsa_tag_8021q_find_user(netdev, src_port, switch_id, +					   vid, vbid); +	if (!skb->dev) { +		dev_warn_ratelimited(&netdev->dev, +				     "Couldn't decode source port\n"); +		return NULL; +	} + +	dsa_default_offload_fwd_mark(skb); + +	return skb; +} + +static const struct dsa_device_ops vsc73xx_8021q_netdev_ops = { +	.name			= VSC73XX_8021Q_NAME, +	.proto			= DSA_TAG_PROTO_VSC73XX_8021Q, +	.xmit			= vsc73xx_xmit, +	.rcv			= vsc73xx_rcv, +	.needed_headroom	= VLAN_HLEN, +	.promisc_on_conduit	= true, +}; + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("DSA tag driver for VSC73XX family of switches, using VLAN"); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_VSC73XX_8021Q, VSC73XX_8021Q_NAME); + +module_dsa_tag_driver(vsc73xx_8021q_netdev_ops); diff --git a/net/dsa/user.c b/net/dsa/user.c index 867c5fe9a4da..f5adfa1d978a 100644 --- a/net/dsa/user.c +++ b/net/dsa/user.c @@ -355,60 +355,82 @@ static int dsa_user_get_iflink(const struct net_device *dev)  	return READ_ONCE(dsa_user_to_conduit(dev)->ifindex);  } -static int dsa_user_open(struct net_device *dev) +int dsa_user_host_uc_install(struct net_device *dev, const u8 *addr)  {  	struct net_device *conduit = dsa_user_to_conduit(dev);  	struct dsa_port *dp = dsa_user_to_port(dev);  	struct dsa_switch *ds = dp->ds;  	int err; -	err = dev_open(conduit, NULL); -	if (err < 0) { -		netdev_err(dev, "failed to open conduit %s\n", conduit->name); -		goto out; -	} -  	if (dsa_switch_supports_uc_filtering(ds)) { -		err = dsa_port_standalone_host_fdb_add(dp, dev->dev_addr, 0); +		err = dsa_port_standalone_host_fdb_add(dp, addr, 0);  		if (err)  			goto out;  	} -	if (!ether_addr_equal(dev->dev_addr, conduit->dev_addr)) { -		err = dev_uc_add(conduit, dev->dev_addr); +	if (!ether_addr_equal(addr, conduit->dev_addr)) { +		err = dev_uc_add(conduit, addr);  		if (err < 0)  			goto del_host_addr;  	} -	err = dsa_port_enable_rt(dp, dev->phydev); -	if (err) -		goto del_unicast; -  	return 0; -del_unicast: -	if (!ether_addr_equal(dev->dev_addr, conduit->dev_addr)) -		dev_uc_del(conduit, dev->dev_addr);  del_host_addr:  	if (dsa_switch_supports_uc_filtering(ds)) -		dsa_port_standalone_host_fdb_del(dp, dev->dev_addr, 0); +		dsa_port_standalone_host_fdb_del(dp, addr, 0);  out:  	return err;  } -static int dsa_user_close(struct net_device *dev) +void dsa_user_host_uc_uninstall(struct net_device *dev)  {  	struct net_device *conduit = dsa_user_to_conduit(dev);  	struct dsa_port *dp = dsa_user_to_port(dev);  	struct dsa_switch *ds = dp->ds; -	dsa_port_disable_rt(dp); -  	if (!ether_addr_equal(dev->dev_addr, conduit->dev_addr))  		dev_uc_del(conduit, dev->dev_addr);  	if (dsa_switch_supports_uc_filtering(ds))  		dsa_port_standalone_host_fdb_del(dp, dev->dev_addr, 0); +} + +static int dsa_user_open(struct net_device *dev) +{ +	struct net_device *conduit = dsa_user_to_conduit(dev); +	struct dsa_port *dp = dsa_user_to_port(dev); +	int err; + +	err = dev_open(conduit, NULL); +	if (err < 0) { +		netdev_err(dev, "failed to open conduit %s\n", conduit->name); +		goto out; +	} + +	err = dsa_user_host_uc_install(dev, dev->dev_addr); +	if (err) +		goto out; + +	err = dsa_port_enable_rt(dp, dev->phydev); +	if (err) +		goto out_del_host_uc; + +	return 0; + +out_del_host_uc: +	dsa_user_host_uc_uninstall(dev); +out: +	return err; +} + +static int dsa_user_close(struct net_device *dev) +{ +	struct dsa_port *dp = dsa_user_to_port(dev); + +	dsa_port_disable_rt(dp); + +	dsa_user_host_uc_uninstall(dev);  	return 0;  } @@ -448,7 +470,6 @@ static void dsa_user_set_rx_mode(struct net_device *dev)  static int dsa_user_set_mac_address(struct net_device *dev, void *a)  { -	struct net_device *conduit = dsa_user_to_conduit(dev);  	struct dsa_port *dp = dsa_user_to_port(dev);  	struct dsa_switch *ds = dp->ds;  	struct sockaddr *addr = a; @@ -470,34 +491,16 @@ static int dsa_user_set_mac_address(struct net_device *dev, void *a)  	if (!(dev->flags & IFF_UP))  		goto out_change_dev_addr; -	if (dsa_switch_supports_uc_filtering(ds)) { -		err = dsa_port_standalone_host_fdb_add(dp, addr->sa_data, 0); -		if (err) -			return err; -	} - -	if (!ether_addr_equal(addr->sa_data, conduit->dev_addr)) { -		err = dev_uc_add(conduit, addr->sa_data); -		if (err < 0) -			goto del_unicast; -	} +	err = dsa_user_host_uc_install(dev, addr->sa_data); +	if (err) +		return err; -	if (!ether_addr_equal(dev->dev_addr, conduit->dev_addr)) -		dev_uc_del(conduit, dev->dev_addr); - -	if (dsa_switch_supports_uc_filtering(ds)) -		dsa_port_standalone_host_fdb_del(dp, dev->dev_addr, 0); +	dsa_user_host_uc_uninstall(dev);  out_change_dev_addr:  	eth_hw_addr_set(dev, addr->sa_data);  	return 0; - -del_unicast: -	if (dsa_switch_supports_uc_filtering(ds)) -		dsa_port_standalone_host_fdb_del(dp, addr->sa_data, 0); - -	return err;  }  struct dsa_user_dump_ctx { @@ -1726,7 +1729,7 @@ static int dsa_user_set_rxnfc(struct net_device *dev,  }  static int dsa_user_get_ts_info(struct net_device *dev, -				struct ethtool_ts_info *ts) +				struct kernel_ethtool_ts_info *ts)  {  	struct dsa_user_priv *p = netdev_priv(dev);  	struct dsa_switch *ds = p->dp->ds; @@ -2879,12 +2882,6 @@ int dsa_user_change_conduit(struct net_device *dev, struct net_device *conduit,  			    ERR_PTR(err));  	} -	/* If the port doesn't have its own MAC address and relies on the DSA -	 * conduit's one, inherit it again from the new DSA conduit. -	 */ -	if (is_zero_ether_addr(dp->mac)) -		eth_hw_addr_inherit(dev, conduit); -  	return 0;  out_revert_conduit_link: diff --git a/net/dsa/user.h b/net/dsa/user.h index 996069130bea..016884bead3c 100644 --- a/net/dsa/user.h +++ b/net/dsa/user.h @@ -42,6 +42,8 @@ int dsa_user_suspend(struct net_device *user_dev);  int dsa_user_resume(struct net_device *user_dev);  int dsa_user_register_notifier(void);  void dsa_user_unregister_notifier(void); +int dsa_user_host_uc_install(struct net_device *dev, const u8 *addr); +void dsa_user_host_uc_uninstall(struct net_device *dev);  void dsa_user_sync_ha(struct net_device *dev);  void dsa_user_unsync_ha(struct net_device *dev);  void dsa_user_setup_tagger(struct net_device *user); |