Commit cb675afcddbb ("net: dsa: mt7530: introduce separate MDIO driver") exported mt7530_switch_ops for use from mt7530-mmio.c. Later in the patch set, mt7530-mmio.c used mt7530_probe_common() to access the mt7530_switch_ops still from mt7530.c - see commit 110c18bfed41 ("net: dsa: mt7530: introduce driver for MT7988 built-in switch"). This proves that exporting mt7530_switch_ops was unnecessary, so unexport it back. Cc: "Chester A. Unal" Cc: Daniel Golle Cc: DENG Qingfang Cc: Sean Wang Signed-off-by: Vladimir Oltean --- drivers/net/dsa/mt7530.c | 3 +-- drivers/net/dsa/mt7530.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 548b85befbf4..1acb57002014 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -3254,7 +3254,7 @@ static int mt7988_setup(struct dsa_switch *ds) return mt7531_setup_common(ds); } -const struct dsa_switch_ops mt7530_switch_ops = { +static const struct dsa_switch_ops mt7530_switch_ops = { .get_tag_protocol = mtk_get_tag_protocol, .setup = mt753x_setup, .preferred_default_local_cpu_port = mt753x_preferred_default_local_cpu_port, @@ -3291,7 +3291,6 @@ const struct dsa_switch_ops mt7530_switch_ops = { .conduit_state_change = mt753x_conduit_state_change, .port_setup_tc = mt753x_setup_tc, }; -EXPORT_SYMBOL_GPL(mt7530_switch_ops); static const struct phylink_mac_ops mt753x_phylink_mac_ops = { .mac_select_pcs = mt753x_phylink_mac_select_pcs, diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h index 7e47cd9af256..3e0090bed298 100644 --- a/drivers/net/dsa/mt7530.h +++ b/drivers/net/dsa/mt7530.h @@ -939,7 +939,6 @@ static inline void INIT_MT7530_DUMMY_POLL(struct mt7530_dummy_poll *p, int mt7530_probe_common(struct mt7530_priv *priv); void mt7530_remove_common(struct mt7530_priv *priv); -extern const struct dsa_switch_ops mt7530_switch_ops; extern const struct mt753x_info mt753x_table[]; #endif /* __MT7530_H */ -- 2.34.1 From: Xiaoliang Yang Since the introduction of HSR_PT_INTERLINK in commit 5055cccfc2d1 ("net: hsr: Provide RedBox support (HSR-SAN)"), we see that different port types require different settings for hardware offload, which was not the case before when we only had HSR_PT_SLAVE_A and HSR_PT_SLAVE_B. But there is currently no way to know which port is which type, so create the hsr_get_port_type() API function and export it. When hsr_get_port_type() is called from the device driver, the port can must be found in the HSR port list. An important use case is for this function to work from offloading drivers' NETDEV_CHANGEUPPER handler, which is triggered by hsr_portdev_setup() -> netdev_master_upper_dev_link(). Therefore, we need to move the addition of the hsr_port to the HSR port list prior to calling hsr_portdev_setup(). This makes the error restoration path also more similar to hsr_del_port(), where kfree_rcu(port) is already used. Cc: Sebastian Andrzej Siewior Cc: Lukasz Majewski Signed-off-by: Xiaoliang Yang Signed-off-by: Vladimir Oltean --- include/linux/if_hsr.h | 9 +++++++++ net/hsr/hsr_device.c | 20 ++++++++++++++++++++ net/hsr/hsr_slave.c | 7 ++++--- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/include/linux/if_hsr.h b/include/linux/if_hsr.h index d7941fd88032..f4cf2dd36d19 100644 --- a/include/linux/if_hsr.h +++ b/include/linux/if_hsr.h @@ -43,6 +43,8 @@ extern bool is_hsr_master(struct net_device *dev); extern int hsr_get_version(struct net_device *dev, enum hsr_version *ver); struct net_device *hsr_get_port_ndev(struct net_device *ndev, enum hsr_port_type pt); +int hsr_get_port_type(struct net_device *hsr_dev, struct net_device *dev, + enum hsr_port_type *type); #else static inline bool is_hsr_master(struct net_device *dev) { @@ -59,6 +61,13 @@ static inline struct net_device *hsr_get_port_ndev(struct net_device *ndev, { return ERR_PTR(-EINVAL); } + +static inline int hsr_get_port_type(struct net_device *hsr_dev, + struct net_device *dev, + enum hsr_port_type *type) +{ + return -EINVAL; +} #endif /* CONFIG_HSR */ #endif /*_LINUX_IF_HSR_H_*/ diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c index 492cbc78ab75..d1bfc49b5f01 100644 --- a/net/hsr/hsr_device.c +++ b/net/hsr/hsr_device.c @@ -690,6 +690,26 @@ struct net_device *hsr_get_port_ndev(struct net_device *ndev, } EXPORT_SYMBOL(hsr_get_port_ndev); +int hsr_get_port_type(struct net_device *hsr_dev, struct net_device *dev, + enum hsr_port_type *type) +{ + struct hsr_priv *hsr = netdev_priv(hsr_dev); + struct hsr_port *port; + + rcu_read_lock(); + hsr_for_each_port(hsr, port) { + if (port->dev == dev) { + *type = port->type; + rcu_read_unlock(); + return 0; + } + } + rcu_read_unlock(); + + return -EINVAL; +} +EXPORT_SYMBOL(hsr_get_port_type); + /* Default multicast address for HSR Supervision frames */ static const unsigned char def_multicast_addr[ETH_ALEN] __aligned(2) = { 0x01, 0x15, 0x4e, 0x00, 0x01, 0x00 diff --git a/net/hsr/hsr_slave.c b/net/hsr/hsr_slave.c index 8177ac6c2d26..afe06ba00ea4 100644 --- a/net/hsr/hsr_slave.c +++ b/net/hsr/hsr_slave.c @@ -207,14 +207,14 @@ int hsr_add_port(struct hsr_priv *hsr, struct net_device *dev, port->type = type; ether_addr_copy(port->original_macaddress, dev->dev_addr); + list_add_tail_rcu(&port->port_list, &hsr->ports); + if (type != HSR_PT_MASTER) { res = hsr_portdev_setup(hsr, dev, port, extack); if (res) goto fail_dev_setup; } - list_add_tail_rcu(&port->port_list, &hsr->ports); - master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); netdev_update_features(master->dev); dev_set_mtu(master->dev, hsr_get_max_mtu(hsr)); @@ -222,7 +222,8 @@ int hsr_add_port(struct hsr_priv *hsr, struct net_device *dev, return 0; fail_dev_setup: - kfree(port); + list_del_rcu(&port->port_list); + kfree_rcu(port, rcu); return res; } -- 2.34.1 This mirrors what we do in dsa_port_lag_leave() and dsa_port_bridge_leave(): when ds->ops->port_hsr_join() returns -EOPNOTSUPP, we fall back to a software implementation where dp->hsr_dev is NULL, and the unoffloaded port is no longer bothered with calls from the HSR layer. This helps, for example, with interlink ports which current DSA drivers don't know how to offload. We have to check only in port_hsr_join() for the port type, then in port_hsr_leave() we are sure we're dealing only with known port types. Signed-off-by: Vladimir Oltean --- net/dsa/port.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/dsa/port.c b/net/dsa/port.c index 082573ae6864..ca3a7f52229b 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -1909,6 +1909,9 @@ void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr) struct dsa_switch *ds = dp->ds; int err; + if (!dp->hsr_dev) + return; + dp->hsr_dev = NULL; if (ds->ops->port_hsr_leave) { -- 2.34.1 As discussed here: https://lore.kernel.org/netdev/20240620090210.drop6jwh7e5qw556@skbuf/ the fact is that the xrs700x.c driver only supports offloading HSR_PT_SLAVE_A and HSR_PT_SLAVE_B (which were the only port types at the time the offload was written, _for this driver_). Up until now, the API did not explicitly tell offloading drivers what port has what role. So xrs700x can get confused and think that it can support a configuration which it actually can't. There was a table in the attached link which gave an example: $ ip link add name hsr0 type hsr slave1 swp0 slave2 swp1 \ interlink swp2 supervision 45 version 1 HSR_PT_SLAVE_A HSR_PT_SLAVE_B HSR_PT_INTERLINK ---------------------------------------------------------------- user space 0 1 2 requests ---------------------------------------------------------------- XRS700X driver 1 2 - understands The switch would act as if the ring ports were swp1 and swp2. Now that we have explicit hsr_get_port_type() API, let's use that to work around the unintended semantical changes of the offloading API brought by the introduction of interlink ports in HSR. Fixes: 5055cccfc2d1 ("net: hsr: Provide RedBox support (HSR-SAN)") Cc: George McCollister Cc: Lukasz Majewski Signed-off-by: Vladimir Oltean --- drivers/net/dsa/xrs700x/xrs700x.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/net/dsa/xrs700x/xrs700x.c b/drivers/net/dsa/xrs700x/xrs700x.c index 4dbcc49a9e52..0a05f4156ef4 100644 --- a/drivers/net/dsa/xrs700x/xrs700x.c +++ b/drivers/net/dsa/xrs700x/xrs700x.c @@ -566,6 +566,7 @@ static int xrs700x_hsr_join(struct dsa_switch *ds, int port, struct xrs700x *priv = ds->priv; struct net_device *user; int ret, i, hsr_pair[2]; + enum hsr_port_type type; enum hsr_version ver; bool fwd = false; @@ -589,6 +590,16 @@ static int xrs700x_hsr_join(struct dsa_switch *ds, int port, return -EOPNOTSUPP; } + ret = hsr_get_port_type(hsr, dsa_to_port(ds, port)->user, &type); + if (ret) + return ret; + + if (type != HSR_PT_SLAVE_A && type != HSR_PT_SLAVE_B) { + NL_SET_ERR_MSG_MOD(extack, + "Only HSR slave ports can be offloaded"); + return -EOPNOTSUPP; + } + dsa_hsr_foreach_port(dp, ds, hsr) { if (dp->index != port) { partner = dp; -- 2.34.1 It turns out that HSR offloads are so fine-grained that many DSA switches can do a small part even though they weren't specifically designed for the protocols supported by that driver (HSR and PRP). Specifically NETIF_F_HW_HSR_DUP - it is simple packet duplication on transmit, towards all (aka 2) ports members of the HSR device. For many DSA switches, we know how to duplicate a packet, even though we never typically use that feature. The transmit port mask from the tagging protocol can have multiple bits set, and the switch should send the packet once to every port with a bit set from that mask. Nonetheless, not all tagging protocols are like this, and sometimes the port is a single numeric value rather than a bit mask. For that reason, and also because switches can sometimes change tagging protocols for different ones, we need to make HSR offload helpers opt-in. For devices that can do nothing else HSR-specific, we introduce dsa_port_simple_hsr_join() and dsa_port_simple_hsr_leave(). These functions monitor when two user ports of the same switch are part of the same HSR device, and when that condition is true, they toggle the NETIF_F_HW_HSR_DUP feature flag of both net devices. Normally only dsa_port_simple_hsr_join() and dsa_port_simple_hsr_leave() are needed. The dsa_port_simple_hsr_validate() helper is just to see what kind of configuration could be offloadable using the generic helpers. This is used by switch drivers which are not currently using the right tagging protocol to offload this HSR ring, but could in principle offload it after changing the tagger. Suggested-by: David Yang Cc: "Alvin Šipraga" Cc: Chester A. Unal" Cc: "Clément Léger" Cc: Daniel Golle Cc: David Yang Cc: DENG Qingfang Cc: Florian Fainelli Cc: George McCollister Cc: Hauke Mehrtens Cc: Jonas Gorski Cc: Kurt Kanzenbach Cc: Linus Walleij Cc: linux-renesas-soc@vger.kernel.org Cc: Sean Wang Cc: UNGLinuxDriver@microchip.com Cc: Woojung Huh Signed-off-by: Vladimir Oltean --- include/net/dsa.h | 9 +++++++ net/dsa/dsa.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/include/net/dsa.h b/include/net/dsa.h index e40cdc12f7f3..cced1a866757 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -1322,6 +1322,15 @@ bool dsa_mdb_present_in_other_db(struct dsa_switch *ds, int port, const struct switchdev_obj_port_mdb *mdb, struct dsa_db db); +int dsa_port_simple_hsr_validate(struct dsa_switch *ds, int port, + struct net_device *hsr, + struct netlink_ext_ack *extack); +int dsa_port_simple_hsr_join(struct dsa_switch *ds, int port, + struct net_device *hsr, + struct netlink_ext_ack *extack); +int dsa_port_simple_hsr_leave(struct dsa_switch *ds, int port, + struct net_device *hsr); + /* Keep inline for faster access in hot path */ static inline bool netdev_uses_dsa(const struct net_device *dev) { diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 5b01a0e43ebe..a20efabe778f 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -1766,6 +1767,70 @@ bool dsa_mdb_present_in_other_db(struct dsa_switch *ds, int port, } EXPORT_SYMBOL_GPL(dsa_mdb_present_in_other_db); +/* Helpers for switches without specific HSR offloads, but which can implement + * NETIF_F_HW_HSR_DUP because their tagger uses dsa_xmit_port_mask() + */ +int dsa_port_simple_hsr_validate(struct dsa_switch *ds, int port, + struct net_device *hsr, + struct netlink_ext_ack *extack) +{ + enum hsr_port_type type; + int err; + + err = hsr_get_port_type(hsr, dsa_to_port(ds, port)->user, &type); + if (err) + return err; + + if (type != HSR_PT_SLAVE_A && type != HSR_PT_SLAVE_B) { + NL_SET_ERR_MSG_MOD(extack, + "Only HSR slave ports can be offloaded"); + return -EOPNOTSUPP; + } + + return 0; +} +EXPORT_SYMBOL_GPL(dsa_port_simple_hsr_validate); + +int dsa_port_simple_hsr_join(struct dsa_switch *ds, int port, + struct net_device *hsr, + struct netlink_ext_ack *extack) +{ + struct dsa_port *dp = dsa_to_port(ds, port), *other_dp; + int err; + + err = dsa_port_simple_hsr_validate(ds, port, hsr, extack); + if (err) + return err; + + dsa_hsr_foreach_port(other_dp, ds, hsr) { + if (other_dp != dp) { + dp->user->features |= NETIF_F_HW_HSR_DUP; + other_dp->user->features |= NETIF_F_HW_HSR_DUP; + break; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(dsa_port_simple_hsr_join); + +int dsa_port_simple_hsr_leave(struct dsa_switch *ds, int port, + struct net_device *hsr) +{ + struct dsa_port *dp = dsa_to_port(ds, port), *other_dp; + + dsa_hsr_foreach_port(other_dp, ds, hsr) { + if (other_dp != dp) { + dp->user->features &= ~NETIF_F_HW_HSR_DUP; + other_dp->user->features &= ~NETIF_F_HW_HSR_DUP; + break; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(dsa_port_simple_hsr_leave); + static const struct dsa_stubs __dsa_stubs = { .conduit_hwtstamp_validate = __dsa_conduit_hwtstamp_validate, }; -- 2.34.1 Accelerate TX packet duplication with HSR rings. Cc: David Yang Signed-off-by: Vladimir Oltean --- drivers/net/dsa/yt921x.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/dsa/yt921x.c b/drivers/net/dsa/yt921x.c index ebfd34f72314..51089615244d 100644 --- a/drivers/net/dsa/yt921x.c +++ b/drivers/net/dsa/yt921x.c @@ -2763,6 +2763,9 @@ static const struct dsa_switch_ops yt921x_dsa_switch_ops = { /* mtu */ .port_change_mtu = yt921x_dsa_port_change_mtu, .port_max_mtu = yt921x_dsa_port_max_mtu, + /* hsr */ + .port_hsr_leave = dsa_port_simple_hsr_leave, + .port_hsr_join = dsa_port_simple_hsr_join, /* mirror */ .port_mirror_del = yt921x_dsa_port_mirror_del, .port_mirror_add = yt921x_dsa_port_mirror_add, -- 2.34.1 Accelerate TX packet duplication with HSR rings. This is only possible with the NPI-based "ocelot" tagging protocol, not with "ocelot-8021q", because the latter does not use dsa_xmit_port_mask(). This has 2 implications: - Depending on tagging protocol, we should set (or not set) the offload feature flags. Switching tagging protocols is done with ports down, by design. Additional calls to dsa_port_simple_hsr_join() can be put in the ds->ops->change_tag_protocol() path, as I had originally tried, but this would not work: dsa_user_setup_tagger() would later clear the feature flag that we just set. So the additional call to dsa_port_simple_hsr_join() should sit in the ds->ops->port_enable() call. - When joining a HSR ring and we are currently using "ocelot-8021q", there are cases when we should return -EOPNOTSUPP (pessimistic) and cases when we shouldn't (optimistic). In the pessimistic case, it is a configuration that the port won't support even with the right tagging protocol. Distinguishing between these 2 cases matters because if we just return -EOPNOTSUPP regardless, we lose the dp->hsr_dev pointer and can no longer replay the offload later for the optimistic case, from felix_port_enable(). Signed-off-by: Vladimir Oltean --- drivers/net/dsa/ocelot/felix.c | 70 +++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 20ab558fde24..9e5ede932b42 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -1233,6 +1233,7 @@ static int felix_port_enable(struct dsa_switch *ds, int port, { struct dsa_port *dp = dsa_to_port(ds, port); struct ocelot *ocelot = ds->priv; + struct felix *felix = ocelot_to_felix(ocelot); if (!dsa_port_is_user(dp)) return 0; @@ -1246,7 +1247,25 @@ static int felix_port_enable(struct dsa_switch *ds, int port, } } - return 0; + if (!dp->hsr_dev || felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q) + return 0; + + return dsa_port_simple_hsr_join(ds, port, dp->hsr_dev, NULL); +} + +static void felix_port_disable(struct dsa_switch *ds, int port) +{ + struct dsa_port *dp = dsa_to_port(ds, port); + struct ocelot *ocelot = ds->priv; + struct felix *felix = ocelot_to_felix(ocelot); + + if (!dsa_port_is_user(dp)) + return; + + if (!dp->hsr_dev || felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q) + return; + + dsa_port_simple_hsr_leave(ds, port, dp->hsr_dev); } static void felix_port_qos_map_init(struct ocelot *ocelot, int port) @@ -2232,6 +2251,52 @@ static void felix_get_mm_stats(struct dsa_switch *ds, int port, ocelot_port_get_mm_stats(ocelot, port, stats); } +/* Depending on port type, we may be able to support the offload later (with + * the "ocelot"/"seville" tagging protocols), or never. + * If we return 0, the dp->hsr_dev reference is kept for later; if we return + * -EOPNOTSUPP, it is cleared (which helps to not bother + * dsa_port_simple_hsr_leave() with an offload that didn't pass validation). + */ +static int felix_port_hsr_join(struct dsa_switch *ds, int port, + struct net_device *hsr, + struct netlink_ext_ack *extack) +{ + struct ocelot *ocelot = ds->priv; + struct felix *felix = ocelot_to_felix(ocelot); + + if (felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q) { + int err; + + err = dsa_port_simple_hsr_validate(ds, port, hsr, extack); + if (err) + return err; + + NL_SET_ERR_MSG_MOD(extack, + "Offloading not supported with \"ocelot-8021q\""); + return 0; + } + + if (!(dsa_to_port(ds, port)->user->flags & IFF_UP)) + return 0; + + return dsa_port_simple_hsr_join(ds, port, hsr, extack); +} + +static int felix_port_hsr_leave(struct dsa_switch *ds, int port, + struct net_device *hsr) +{ + struct ocelot *ocelot = ds->priv; + struct felix *felix = ocelot_to_felix(ocelot); + + if (felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q) + return 0; + + if (!(dsa_to_port(ds, port)->user->flags & IFF_UP)) + return 0; + + return dsa_port_simple_hsr_leave(ds, port, hsr); +} + static const struct phylink_mac_ops felix_phylink_mac_ops = { .mac_select_pcs = felix_phylink_mac_select_pcs, .mac_config = felix_phylink_mac_config, @@ -2262,6 +2327,7 @@ static const struct dsa_switch_ops felix_switch_ops = { .get_ts_info = felix_get_ts_info, .phylink_get_caps = felix_phylink_get_caps, .port_enable = felix_port_enable, + .port_disable = felix_port_disable, .port_fast_age = felix_port_fast_age, .port_fdb_dump = felix_fdb_dump, .port_fdb_add = felix_fdb_add, @@ -2318,6 +2384,8 @@ static const struct dsa_switch_ops felix_switch_ops = { .port_del_dscp_prio = felix_port_del_dscp_prio, .port_set_host_flood = felix_port_set_host_flood, .port_change_conduit = felix_port_change_conduit, + .port_hsr_join = felix_port_hsr_join, + .port_hsr_leave = felix_port_hsr_leave, }; int felix_register_switch(struct device *dev, resource_size_t switch_base, -- 2.34.1 All known Realtek protocols: "rtl4a", "rtl8_4" and "rtl8_4t" use dsa_xmit_port_mask(), so they are compatible with accelerating TX packet duplication for HSR rings. Enable that feature by setting the port_hsr_join() and port_hsr_leave() operations to the simple helpers provided by DSA. Cc: Linus Walleij Cc: "Alvin Šipraga" Signed-off-by: Vladimir Oltean --- drivers/net/dsa/realtek/rtl8365mb.c | 2 ++ drivers/net/dsa/realtek/rtl8366rb.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/net/dsa/realtek/rtl8365mb.c b/drivers/net/dsa/realtek/rtl8365mb.c index 964a56ee16cc..c575e164368c 100644 --- a/drivers/net/dsa/realtek/rtl8365mb.c +++ b/drivers/net/dsa/realtek/rtl8365mb.c @@ -2134,6 +2134,8 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops = { .get_stats64 = rtl8365mb_get_stats64, .port_change_mtu = rtl8365mb_port_change_mtu, .port_max_mtu = rtl8365mb_port_max_mtu, + .port_hsr_join = dsa_port_simple_hsr_join, + .port_hsr_leave = dsa_port_simple_hsr_leave, }; static const struct realtek_ops rtl8365mb_ops = { diff --git a/drivers/net/dsa/realtek/rtl8366rb.c b/drivers/net/dsa/realtek/rtl8366rb.c index 8bdb52b5fdcb..d96ae72b0a5c 100644 --- a/drivers/net/dsa/realtek/rtl8366rb.c +++ b/drivers/net/dsa/realtek/rtl8366rb.c @@ -1815,6 +1815,8 @@ static const struct dsa_switch_ops rtl8366rb_switch_ops = { .port_fast_age = rtl8366rb_port_fast_age, .port_change_mtu = rtl8366rb_change_mtu, .port_max_mtu = rtl8366rb_max_mtu, + .port_hsr_join = dsa_port_simple_hsr_join, + .port_hsr_leave = dsa_port_simple_hsr_leave, }; static const struct realtek_ops rtl8366rb_ops = { -- 2.34.1 Both the "gswip" and "gsw1xx" protocols use dsa_xmit_port_mask(), so they are compatible with accelerating TX packet duplication for HSR rings. Enable that feature by setting the port_hsr_join() and port_hsr_leave() operations to the simple helpers provided by DSA. Cc: Hauke Mehrtens Cc: Daniel Golle Signed-off-by: Vladimir Oltean --- drivers/net/dsa/lantiq/lantiq_gswip_common.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/dsa/lantiq/lantiq_gswip_common.c b/drivers/net/dsa/lantiq/lantiq_gswip_common.c index 122ccea4057b..9da39edf8f57 100644 --- a/drivers/net/dsa/lantiq/lantiq_gswip_common.c +++ b/drivers/net/dsa/lantiq/lantiq_gswip_common.c @@ -1652,6 +1652,8 @@ static const struct dsa_switch_ops gswip_switch_ops = { .get_sset_count = gswip_get_sset_count, .set_mac_eee = gswip_set_mac_eee, .support_eee = gswip_support_eee, + .port_hsr_join = dsa_port_simple_hsr_join, + .port_hsr_leave = dsa_port_simple_hsr_leave, }; void gswip_disable_switch(struct gswip_priv *priv) -- 2.34.1 The "trailer" tagging protocol uses dsa_xmit_port_mask(), which means we can offload HSR packet duplication on transmit. Enable that feature. Signed-off-by: Vladimir Oltean --- drivers/net/dsa/mv88e6060.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/dsa/mv88e6060.c b/drivers/net/dsa/mv88e6060.c index 294312b58e4f..9c8ac14cd4f5 100644 --- a/drivers/net/dsa/mv88e6060.c +++ b/drivers/net/dsa/mv88e6060.c @@ -297,6 +297,8 @@ static const struct dsa_switch_ops mv88e6060_switch_ops = { .phy_read = mv88e6060_phy_read, .phy_write = mv88e6060_phy_write, .phylink_get_caps = mv88e6060_phylink_get_caps, + .port_hsr_join = dsa_port_simple_hsr_join, + .port_hsr_leave = dsa_port_simple_hsr_leave, }; static int mv88e6060_probe(struct mdio_device *mdiodev) -- 2.34.1 The "hellcreek" tagging protocol uses dsa_xmit_port_mask(), which means we can offload HSR packet duplication on transmit. Enable that feature. Cc: Kurt Kanzenbach Signed-off-by: Vladimir Oltean --- drivers/net/dsa/hirschmann/hellcreek.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c index e0b4758ca583..dd5f263ab984 100644 --- a/drivers/net/dsa/hirschmann/hellcreek.c +++ b/drivers/net/dsa/hirschmann/hellcreek.c @@ -1926,6 +1926,8 @@ static const struct dsa_switch_ops hellcreek_ds_ops = { .port_vlan_filtering = hellcreek_vlan_filtering, .setup = hellcreek_setup, .teardown = hellcreek_teardown, + .port_hsr_join = dsa_port_simple_hsr_join, + .port_hsr_leave = dsa_port_simple_hsr_leave, }; static int hellcreek_probe(struct platform_device *pdev) -- 2.34.1 The "mtk" tagging protocol uses dsa_xmit_port_mask(), which means we can offload HSR packet duplication on transmit. Enable that feature. Cc: "Chester A. Unal" Cc: Daniel Golle Cc: DENG Qingfang Cc: Sean Wang Signed-off-by: Vladimir Oltean --- drivers/net/dsa/mt7530.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 1acb57002014..b9423389c2ef 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -3290,6 +3290,8 @@ static const struct dsa_switch_ops mt7530_switch_ops = { .set_mac_eee = mt753x_set_mac_eee, .conduit_state_change = mt753x_conduit_state_change, .port_setup_tc = mt753x_setup_tc, + .port_hsr_join = dsa_port_simple_hsr_join, + .port_hsr_leave = dsa_port_simple_hsr_leave, }; static const struct phylink_mac_ops mt753x_phylink_mac_ops = { -- 2.34.1 The "a5psw" tagging protocol uses dsa_xmit_port_mask(), which means we can offload HSR packet duplication on transmit. Enable that feature. Cc: "Clément Léger" Cc: linux-renesas-soc@vger.kernel.org Signed-off-by: Vladimir Oltean --- drivers/net/dsa/rzn1_a5psw.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/dsa/rzn1_a5psw.c b/drivers/net/dsa/rzn1_a5psw.c index 1635255f58e4..4d857e3be10b 100644 --- a/drivers/net/dsa/rzn1_a5psw.c +++ b/drivers/net/dsa/rzn1_a5psw.c @@ -1035,6 +1035,8 @@ static const struct dsa_switch_ops a5psw_switch_ops = { .port_fdb_add = a5psw_port_fdb_add, .port_fdb_del = a5psw_port_fdb_del, .port_fdb_dump = a5psw_port_fdb_dump, + .port_hsr_join = dsa_port_simple_hsr_join, + .port_hsr_leave = dsa_port_simple_hsr_leave, }; static int a5psw_mdio_wait_busy(struct a5psw *a5psw) -- 2.34.1 Since commit 5055cccfc2d1 ("net: hsr: Provide RedBox support (HSR-SAN)"), RedBox is available (including for offload in DSA). Update the DSA documentation that states it isn't. Signed-off-by: Vladimir Oltean --- Documentation/networking/dsa/dsa.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Documentation/networking/dsa/dsa.rst b/Documentation/networking/dsa/dsa.rst index 7b2e69cd7ef0..89dc15bcf271 100644 --- a/Documentation/networking/dsa/dsa.rst +++ b/Documentation/networking/dsa/dsa.rst @@ -1104,12 +1104,11 @@ health of the network and for discovery of other nodes. In Linux, both HSR and PRP are implemented in the hsr driver, which instantiates a virtual, stackable network interface with two member ports. The driver only implements the basic roles of DANH (Doubly Attached Node -implementing HSR) and DANP (Doubly Attached Node implementing PRP); the roles -of RedBox and QuadBox are not implemented (therefore, bridging a hsr network -interface with a physical switch port does not produce the expected result). +implementing HSR), DANP (Doubly Attached Node implementing PRP) and RedBox +(allows non-HSR devices to connect to the ring via Interlink ports). -A driver which is able of offloading certain functions of a DANP or DANH should -declare the corresponding netdev features as indicated by the documentation at +A driver which is able of offloading certain functions of should declare the +corresponding netdev features as indicated by the documentation at ``Documentation/networking/netdev-features.rst``. Additionally, the following methods must be implemented: -- 2.34.1 Keep the documentation up to date. Signed-off-by: Vladimir Oltean --- Documentation/networking/dsa/dsa.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/networking/dsa/dsa.rst b/Documentation/networking/dsa/dsa.rst index 89dc15bcf271..378f089f84b5 100644 --- a/Documentation/networking/dsa/dsa.rst +++ b/Documentation/networking/dsa/dsa.rst @@ -1119,6 +1119,14 @@ methods must be implemented: - ``port_hsr_leave``: function invoked when a given switch port leaves a DANP/DANH and returns to normal operation as a standalone port. +Note that the ``NETIF_F_HW_HSR_DUP`` feature relies on transmission towards +multiple ports, which is generally available whenever the tagging protocol uses +the ``dsa_xmit_port_mask()`` helper function. If the helper is used, the HSR +offload feature should also be set. The ``dsa_port_simple_hsr_join()`` and +``dsa_port_simple_hsr_leave()`` methods can be used as generic implementations +of ``port_hsr_join`` and ``port_hsr_leave``, if this is the only supported +offload feature. + TODO ==== -- 2.34.1