Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'dsa-simple-hsr-offload'

Vladimir Oltean says:

====================
DSA simple HSR offload

Provide a "simple" form of HSR offload for 8 DSA drivers (just the
NETIF_F_HW_HSR_DUP feature) based on the fact that their taggers use the
dsa_xmit_port_mask() function. This is in patches 6-13/15.

The helpers per se are introduced in patch 5/15, and documented in patch
15/15. Patch 14/15 is another small (and related) documentation update.

For HSR interlink ports the offloading rules are not quite so clear, and
for now we completely reject the offload. We can revise that once we see
a full offload implementation and understand what is needed.

To reject the offload, we need to know the port type, and patch 2/15
helps with that.

xrs700x is another driver which should have rejected offload based on
port type (patch 4/15). This is a bug fix submitted through net-next due
to the extra API required to fix it. If necessary, it could also be
picked up separately for backporting.

There is also patch 3/15, which makes the HSR offload like the others
supported by DSA: if we fall back to the software implementation, don't
call port_hsr_leave(), because by definition there won't be anything to
do.

A slightly unrelated change is patch 1/15, but I noticed this along the
way, and if I were to submit it separately, it would conflict with this
work (it would appear in patch 12/15's context).

Most of the driver additions are trivial. By far the most complex was
ocelot (which I could test). Microchip ksz (which I cannot test, and did
not patch) would also have some complexity. Essentially, ksz_hsr_join()
could fall back to a partial offload through the simple helpers, if the
full offload is not possible. But keeping track of which offload kind
was used is necessary later in ksz_hsr_leave(). This is left as homework
for interested developers.

With this patch set, one can observe a 50% reduction in transmitted
traffic over HSR interfaces.
====================

Link: https://patch.msgid.link/20251130131657.65080-1-vladimir.oltean@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+220 -12
+12 -5
Documentation/networking/dsa/dsa.rst
··· 1104 1104 In Linux, both HSR and PRP are implemented in the hsr driver, which 1105 1105 instantiates a virtual, stackable network interface with two member ports. 1106 1106 The driver only implements the basic roles of DANH (Doubly Attached Node 1107 - implementing HSR) and DANP (Doubly Attached Node implementing PRP); the roles 1108 - of RedBox and QuadBox are not implemented (therefore, bridging a hsr network 1109 - interface with a physical switch port does not produce the expected result). 1107 + implementing HSR), DANP (Doubly Attached Node implementing PRP) and RedBox 1108 + (allows non-HSR devices to connect to the ring via Interlink ports). 1110 1109 1111 - A driver which is able of offloading certain functions of a DANP or DANH should 1112 - declare the corresponding netdev features as indicated by the documentation at 1110 + A driver which is able of offloading certain functions should declare the 1111 + corresponding netdev features as indicated by the documentation at 1113 1112 ``Documentation/networking/netdev-features.rst``. Additionally, the following 1114 1113 methods must be implemented: 1115 1114 ··· 1118 1119 sent to the CPU. 1119 1120 - ``port_hsr_leave``: function invoked when a given switch port leaves a 1120 1121 DANP/DANH and returns to normal operation as a standalone port. 1122 + 1123 + Note that the ``NETIF_F_HW_HSR_DUP`` feature relies on transmission towards 1124 + multiple ports, which is generally available whenever the tagging protocol uses 1125 + the ``dsa_xmit_port_mask()`` helper function. If the helper is used, the HSR 1126 + offload feature should also be set. The ``dsa_port_simple_hsr_join()`` and 1127 + ``dsa_port_simple_hsr_leave()`` methods can be used as generic implementations 1128 + of ``port_hsr_join`` and ``port_hsr_leave``, if this is the only supported 1129 + offload feature. 1121 1130 1122 1131 TODO 1123 1132 ====
+2
drivers/net/dsa/hirschmann/hellcreek.c
··· 1926 1926 .port_vlan_filtering = hellcreek_vlan_filtering, 1927 1927 .setup = hellcreek_setup, 1928 1928 .teardown = hellcreek_teardown, 1929 + .port_hsr_join = dsa_port_simple_hsr_join, 1930 + .port_hsr_leave = dsa_port_simple_hsr_leave, 1929 1931 }; 1930 1932 1931 1933 static int hellcreek_probe(struct platform_device *pdev)
+2
drivers/net/dsa/lantiq/lantiq_gswip_common.c
··· 1652 1652 .get_sset_count = gswip_get_sset_count, 1653 1653 .set_mac_eee = gswip_set_mac_eee, 1654 1654 .support_eee = gswip_support_eee, 1655 + .port_hsr_join = dsa_port_simple_hsr_join, 1656 + .port_hsr_leave = dsa_port_simple_hsr_leave, 1655 1657 }; 1656 1658 1657 1659 void gswip_disable_switch(struct gswip_priv *priv)
+3 -2
drivers/net/dsa/mt7530.c
··· 3254 3254 return mt7531_setup_common(ds); 3255 3255 } 3256 3256 3257 - const struct dsa_switch_ops mt7530_switch_ops = { 3257 + static const struct dsa_switch_ops mt7530_switch_ops = { 3258 3258 .get_tag_protocol = mtk_get_tag_protocol, 3259 3259 .setup = mt753x_setup, 3260 3260 .preferred_default_local_cpu_port = mt753x_preferred_default_local_cpu_port, ··· 3290 3290 .set_mac_eee = mt753x_set_mac_eee, 3291 3291 .conduit_state_change = mt753x_conduit_state_change, 3292 3292 .port_setup_tc = mt753x_setup_tc, 3293 + .port_hsr_join = dsa_port_simple_hsr_join, 3294 + .port_hsr_leave = dsa_port_simple_hsr_leave, 3293 3295 }; 3294 - EXPORT_SYMBOL_GPL(mt7530_switch_ops); 3295 3296 3296 3297 static const struct phylink_mac_ops mt753x_phylink_mac_ops = { 3297 3298 .mac_select_pcs = mt753x_phylink_mac_select_pcs,
-1
drivers/net/dsa/mt7530.h
··· 939 939 int mt7530_probe_common(struct mt7530_priv *priv); 940 940 void mt7530_remove_common(struct mt7530_priv *priv); 941 941 942 - extern const struct dsa_switch_ops mt7530_switch_ops; 943 942 extern const struct mt753x_info mt753x_table[]; 944 943 945 944 #endif /* __MT7530_H */
+2
drivers/net/dsa/mv88e6060.c
··· 297 297 .phy_read = mv88e6060_phy_read, 298 298 .phy_write = mv88e6060_phy_write, 299 299 .phylink_get_caps = mv88e6060_phylink_get_caps, 300 + .port_hsr_join = dsa_port_simple_hsr_join, 301 + .port_hsr_leave = dsa_port_simple_hsr_leave, 300 302 }; 301 303 302 304 static int mv88e6060_probe(struct mdio_device *mdiodev)
+69 -1
drivers/net/dsa/ocelot/felix.c
··· 1233 1233 { 1234 1234 struct dsa_port *dp = dsa_to_port(ds, port); 1235 1235 struct ocelot *ocelot = ds->priv; 1236 + struct felix *felix = ocelot_to_felix(ocelot); 1236 1237 1237 1238 if (!dsa_port_is_user(dp)) 1238 1239 return 0; ··· 1247 1246 } 1248 1247 } 1249 1248 1250 - return 0; 1249 + if (!dp->hsr_dev || felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q) 1250 + return 0; 1251 + 1252 + return dsa_port_simple_hsr_join(ds, port, dp->hsr_dev, NULL); 1253 + } 1254 + 1255 + static void felix_port_disable(struct dsa_switch *ds, int port) 1256 + { 1257 + struct dsa_port *dp = dsa_to_port(ds, port); 1258 + struct ocelot *ocelot = ds->priv; 1259 + struct felix *felix = ocelot_to_felix(ocelot); 1260 + 1261 + if (!dsa_port_is_user(dp)) 1262 + return; 1263 + 1264 + if (!dp->hsr_dev || felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q) 1265 + return; 1266 + 1267 + dsa_port_simple_hsr_leave(ds, port, dp->hsr_dev); 1251 1268 } 1252 1269 1253 1270 static void felix_port_qos_map_init(struct ocelot *ocelot, int port) ··· 2251 2232 ocelot_port_get_mm_stats(ocelot, port, stats); 2252 2233 } 2253 2234 2235 + /* Depending on port type, we may be able to support the offload later (with 2236 + * the "ocelot"/"seville" tagging protocols), or never. 2237 + * If we return 0, the dp->hsr_dev reference is kept for later; if we return 2238 + * -EOPNOTSUPP, it is cleared (which helps to not bother 2239 + * dsa_port_simple_hsr_leave() with an offload that didn't pass validation). 2240 + */ 2241 + static int felix_port_hsr_join(struct dsa_switch *ds, int port, 2242 + struct net_device *hsr, 2243 + struct netlink_ext_ack *extack) 2244 + { 2245 + struct ocelot *ocelot = ds->priv; 2246 + struct felix *felix = ocelot_to_felix(ocelot); 2247 + 2248 + if (felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q) { 2249 + int err; 2250 + 2251 + err = dsa_port_simple_hsr_validate(ds, port, hsr, extack); 2252 + if (err) 2253 + return err; 2254 + 2255 + NL_SET_ERR_MSG_MOD(extack, 2256 + "Offloading not supported with \"ocelot-8021q\""); 2257 + return 0; 2258 + } 2259 + 2260 + if (!(dsa_to_port(ds, port)->user->flags & IFF_UP)) 2261 + return 0; 2262 + 2263 + return dsa_port_simple_hsr_join(ds, port, hsr, extack); 2264 + } 2265 + 2266 + static int felix_port_hsr_leave(struct dsa_switch *ds, int port, 2267 + struct net_device *hsr) 2268 + { 2269 + struct ocelot *ocelot = ds->priv; 2270 + struct felix *felix = ocelot_to_felix(ocelot); 2271 + 2272 + if (felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q) 2273 + return 0; 2274 + 2275 + if (!(dsa_to_port(ds, port)->user->flags & IFF_UP)) 2276 + return 0; 2277 + 2278 + return dsa_port_simple_hsr_leave(ds, port, hsr); 2279 + } 2280 + 2254 2281 static const struct phylink_mac_ops felix_phylink_mac_ops = { 2255 2282 .mac_select_pcs = felix_phylink_mac_select_pcs, 2256 2283 .mac_config = felix_phylink_mac_config, ··· 2327 2262 .get_ts_info = felix_get_ts_info, 2328 2263 .phylink_get_caps = felix_phylink_get_caps, 2329 2264 .port_enable = felix_port_enable, 2265 + .port_disable = felix_port_disable, 2330 2266 .port_fast_age = felix_port_fast_age, 2331 2267 .port_fdb_dump = felix_fdb_dump, 2332 2268 .port_fdb_add = felix_fdb_add, ··· 2384 2318 .port_del_dscp_prio = felix_port_del_dscp_prio, 2385 2319 .port_set_host_flood = felix_port_set_host_flood, 2386 2320 .port_change_conduit = felix_port_change_conduit, 2321 + .port_hsr_join = felix_port_hsr_join, 2322 + .port_hsr_leave = felix_port_hsr_leave, 2387 2323 }; 2388 2324 2389 2325 int felix_register_switch(struct device *dev, resource_size_t switch_base,
+2
drivers/net/dsa/realtek/rtl8365mb.c
··· 2134 2134 .get_stats64 = rtl8365mb_get_stats64, 2135 2135 .port_change_mtu = rtl8365mb_port_change_mtu, 2136 2136 .port_max_mtu = rtl8365mb_port_max_mtu, 2137 + .port_hsr_join = dsa_port_simple_hsr_join, 2138 + .port_hsr_leave = dsa_port_simple_hsr_leave, 2137 2139 }; 2138 2140 2139 2141 static const struct realtek_ops rtl8365mb_ops = {
+2
drivers/net/dsa/realtek/rtl8366rb.c
··· 1815 1815 .port_fast_age = rtl8366rb_port_fast_age, 1816 1816 .port_change_mtu = rtl8366rb_change_mtu, 1817 1817 .port_max_mtu = rtl8366rb_max_mtu, 1818 + .port_hsr_join = dsa_port_simple_hsr_join, 1819 + .port_hsr_leave = dsa_port_simple_hsr_leave, 1818 1820 }; 1819 1821 1820 1822 static const struct realtek_ops rtl8366rb_ops = {
+2
drivers/net/dsa/rzn1_a5psw.c
··· 1035 1035 .port_fdb_add = a5psw_port_fdb_add, 1036 1036 .port_fdb_del = a5psw_port_fdb_del, 1037 1037 .port_fdb_dump = a5psw_port_fdb_dump, 1038 + .port_hsr_join = dsa_port_simple_hsr_join, 1039 + .port_hsr_leave = dsa_port_simple_hsr_leave, 1038 1040 }; 1039 1041 1040 1042 static int a5psw_mdio_wait_busy(struct a5psw *a5psw)
+11
drivers/net/dsa/xrs700x/xrs700x.c
··· 566 566 struct xrs700x *priv = ds->priv; 567 567 struct net_device *user; 568 568 int ret, i, hsr_pair[2]; 569 + enum hsr_port_type type; 569 570 enum hsr_version ver; 570 571 bool fwd = false; 571 572 ··· 587 586 } else { 588 587 NL_SET_ERR_MSG_MOD(extack, 589 588 "Only HSR v1 and PRP v1 can be offloaded"); 589 + return -EOPNOTSUPP; 590 + } 591 + 592 + ret = hsr_get_port_type(hsr, dsa_to_port(ds, port)->user, &type); 593 + if (ret) 594 + return ret; 595 + 596 + if (type != HSR_PT_SLAVE_A && type != HSR_PT_SLAVE_B) { 597 + NL_SET_ERR_MSG_MOD(extack, 598 + "Only HSR slave ports can be offloaded"); 590 599 return -EOPNOTSUPP; 591 600 } 592 601
+3
drivers/net/dsa/yt921x.c
··· 2874 2874 /* mtu */ 2875 2875 .port_change_mtu = yt921x_dsa_port_change_mtu, 2876 2876 .port_max_mtu = yt921x_dsa_port_max_mtu, 2877 + /* hsr */ 2878 + .port_hsr_leave = dsa_port_simple_hsr_leave, 2879 + .port_hsr_join = dsa_port_simple_hsr_join, 2877 2880 /* mirror */ 2878 2881 .port_mirror_del = yt921x_dsa_port_mirror_del, 2879 2882 .port_mirror_add = yt921x_dsa_port_mirror_add,
+9
include/linux/if_hsr.h
··· 43 43 extern int hsr_get_version(struct net_device *dev, enum hsr_version *ver); 44 44 struct net_device *hsr_get_port_ndev(struct net_device *ndev, 45 45 enum hsr_port_type pt); 46 + int hsr_get_port_type(struct net_device *hsr_dev, struct net_device *dev, 47 + enum hsr_port_type *type); 46 48 #else 47 49 static inline bool is_hsr_master(struct net_device *dev) 48 50 { ··· 60 58 enum hsr_port_type pt) 61 59 { 62 60 return ERR_PTR(-EINVAL); 61 + } 62 + 63 + static inline int hsr_get_port_type(struct net_device *hsr_dev, 64 + struct net_device *dev, 65 + enum hsr_port_type *type) 66 + { 67 + return -EINVAL; 63 68 } 64 69 #endif /* CONFIG_HSR */ 65 70
+9
include/net/dsa.h
··· 1322 1322 const struct switchdev_obj_port_mdb *mdb, 1323 1323 struct dsa_db db); 1324 1324 1325 + int dsa_port_simple_hsr_validate(struct dsa_switch *ds, int port, 1326 + struct net_device *hsr, 1327 + struct netlink_ext_ack *extack); 1328 + int dsa_port_simple_hsr_join(struct dsa_switch *ds, int port, 1329 + struct net_device *hsr, 1330 + struct netlink_ext_ack *extack); 1331 + int dsa_port_simple_hsr_leave(struct dsa_switch *ds, int port, 1332 + struct net_device *hsr); 1333 + 1325 1334 /* Keep inline for faster access in hot path */ 1326 1335 static inline bool netdev_uses_dsa(const struct net_device *dev) 1327 1336 {
+65
net/dsa/dsa.c
··· 9 9 10 10 #include <linux/device.h> 11 11 #include <linux/err.h> 12 + #include <linux/if_hsr.h> 12 13 #include <linux/list.h> 13 14 #include <linux/module.h> 14 15 #include <linux/netdevice.h> ··· 1766 1765 return false; 1767 1766 } 1768 1767 EXPORT_SYMBOL_GPL(dsa_mdb_present_in_other_db); 1768 + 1769 + /* Helpers for switches without specific HSR offloads, but which can implement 1770 + * NETIF_F_HW_HSR_DUP because their tagger uses dsa_xmit_port_mask() 1771 + */ 1772 + int dsa_port_simple_hsr_validate(struct dsa_switch *ds, int port, 1773 + struct net_device *hsr, 1774 + struct netlink_ext_ack *extack) 1775 + { 1776 + enum hsr_port_type type; 1777 + int err; 1778 + 1779 + err = hsr_get_port_type(hsr, dsa_to_port(ds, port)->user, &type); 1780 + if (err) 1781 + return err; 1782 + 1783 + if (type != HSR_PT_SLAVE_A && type != HSR_PT_SLAVE_B) { 1784 + NL_SET_ERR_MSG_MOD(extack, 1785 + "Only HSR slave ports can be offloaded"); 1786 + return -EOPNOTSUPP; 1787 + } 1788 + 1789 + return 0; 1790 + } 1791 + EXPORT_SYMBOL_GPL(dsa_port_simple_hsr_validate); 1792 + 1793 + int dsa_port_simple_hsr_join(struct dsa_switch *ds, int port, 1794 + struct net_device *hsr, 1795 + struct netlink_ext_ack *extack) 1796 + { 1797 + struct dsa_port *dp = dsa_to_port(ds, port), *other_dp; 1798 + int err; 1799 + 1800 + err = dsa_port_simple_hsr_validate(ds, port, hsr, extack); 1801 + if (err) 1802 + return err; 1803 + 1804 + dsa_hsr_foreach_port(other_dp, ds, hsr) { 1805 + if (other_dp != dp) { 1806 + dp->user->features |= NETIF_F_HW_HSR_DUP; 1807 + other_dp->user->features |= NETIF_F_HW_HSR_DUP; 1808 + break; 1809 + } 1810 + } 1811 + 1812 + return 0; 1813 + } 1814 + EXPORT_SYMBOL_GPL(dsa_port_simple_hsr_join); 1815 + 1816 + int dsa_port_simple_hsr_leave(struct dsa_switch *ds, int port, 1817 + struct net_device *hsr) 1818 + { 1819 + struct dsa_port *dp = dsa_to_port(ds, port), *other_dp; 1820 + 1821 + dsa_hsr_foreach_port(other_dp, ds, hsr) { 1822 + if (other_dp != dp) { 1823 + dp->user->features &= ~NETIF_F_HW_HSR_DUP; 1824 + other_dp->user->features &= ~NETIF_F_HW_HSR_DUP; 1825 + break; 1826 + } 1827 + } 1828 + 1829 + return 0; 1830 + } 1831 + EXPORT_SYMBOL_GPL(dsa_port_simple_hsr_leave); 1769 1832 1770 1833 static const struct dsa_stubs __dsa_stubs = { 1771 1834 .conduit_hwtstamp_validate = __dsa_conduit_hwtstamp_validate,
+3
net/dsa/port.c
··· 1909 1909 struct dsa_switch *ds = dp->ds; 1910 1910 int err; 1911 1911 1912 + if (!dp->hsr_dev) 1913 + return; 1914 + 1912 1915 dp->hsr_dev = NULL; 1913 1916 1914 1917 if (ds->ops->port_hsr_leave) {
+20
net/hsr/hsr_device.c
··· 690 690 } 691 691 EXPORT_SYMBOL(hsr_get_port_ndev); 692 692 693 + int hsr_get_port_type(struct net_device *hsr_dev, struct net_device *dev, 694 + enum hsr_port_type *type) 695 + { 696 + struct hsr_priv *hsr = netdev_priv(hsr_dev); 697 + struct hsr_port *port; 698 + 699 + rcu_read_lock(); 700 + hsr_for_each_port(hsr, port) { 701 + if (port->dev == dev) { 702 + *type = port->type; 703 + rcu_read_unlock(); 704 + return 0; 705 + } 706 + } 707 + rcu_read_unlock(); 708 + 709 + return -EINVAL; 710 + } 711 + EXPORT_SYMBOL(hsr_get_port_type); 712 + 693 713 /* Default multicast address for HSR Supervision frames */ 694 714 static const unsigned char def_multicast_addr[ETH_ALEN] __aligned(2) = { 695 715 0x01, 0x15, 0x4e, 0x00, 0x01, 0x00
+4 -3
net/hsr/hsr_slave.c
··· 207 207 port->type = type; 208 208 ether_addr_copy(port->original_macaddress, dev->dev_addr); 209 209 210 + list_add_tail_rcu(&port->port_list, &hsr->ports); 211 + 210 212 if (type != HSR_PT_MASTER) { 211 213 res = hsr_portdev_setup(hsr, dev, port, extack); 212 214 if (res) 213 215 goto fail_dev_setup; 214 216 } 215 - 216 - list_add_tail_rcu(&port->port_list, &hsr->ports); 217 217 218 218 master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); 219 219 netdev_update_features(master->dev); ··· 222 222 return 0; 223 223 224 224 fail_dev_setup: 225 - kfree(port); 225 + list_del_rcu(&port->port_list); 226 + kfree_rcu(port, rcu); 226 227 return res; 227 228 } 228 229