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 <paweldembicki@gmail.com> + */ +#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); |