diff options
| -rw-r--r-- | Documentation/networking/dsa/dsa.rst | 17 | ||||
| -rw-r--r-- | drivers/net/dsa/hirschmann/hellcreek.c | 2 | ||||
| -rw-r--r-- | drivers/net/dsa/lantiq/lantiq_gswip_common.c | 2 | ||||
| -rw-r--r-- | drivers/net/dsa/mt7530.c | 5 | ||||
| -rw-r--r-- | drivers/net/dsa/mt7530.h | 1 | ||||
| -rw-r--r-- | drivers/net/dsa/mv88e6060.c | 2 | ||||
| -rw-r--r-- | drivers/net/dsa/ocelot/felix.c | 70 | ||||
| -rw-r--r-- | drivers/net/dsa/realtek/rtl8365mb.c | 2 | ||||
| -rw-r--r-- | drivers/net/dsa/realtek/rtl8366rb.c | 2 | ||||
| -rw-r--r-- | drivers/net/dsa/rzn1_a5psw.c | 2 | ||||
| -rw-r--r-- | drivers/net/dsa/xrs700x/xrs700x.c | 11 | ||||
| -rw-r--r-- | drivers/net/dsa/yt921x.c | 3 | ||||
| -rw-r--r-- | include/linux/if_hsr.h | 9 | ||||
| -rw-r--r-- | include/net/dsa.h | 9 | ||||
| -rw-r--r-- | net/dsa/dsa.c | 65 | ||||
| -rw-r--r-- | net/dsa/port.c | 3 | ||||
| -rw-r--r-- | net/hsr/hsr_device.c | 20 | ||||
| -rw-r--r-- | net/hsr/hsr_slave.c | 7 |
18 files changed, 220 insertions, 12 deletions
diff --git a/Documentation/networking/dsa/dsa.rst b/Documentation/networking/dsa/dsa.rst index 7b2e69cd7ef0..5c79740a533b 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 should declare the +corresponding netdev features as indicated by the documentation at ``Documentation/networking/netdev-features.rst``. Additionally, the following methods must be implemented: @@ -1120,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 ==== 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) 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) diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 548b85befbf4..b9423389c2ef 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, @@ -3290,8 +3290,9 @@ 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, }; -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 */ 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) 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, 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 = { 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) 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; diff --git a/drivers/net/dsa/yt921x.c b/drivers/net/dsa/yt921x.c index 12e1bd5a1061..1c511f5dc6ab 100644 --- a/drivers/net/dsa/yt921x.c +++ b/drivers/net/dsa/yt921x.c @@ -2874,6 +2874,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, 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/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 <linux/device.h> #include <linux/err.h> +#include <linux/if_hsr.h> #include <linux/list.h> #include <linux/module.h> #include <linux/netdevice.h> @@ -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, }; 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) { 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; } |
