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

net: dsa: ocelot: use simple HSR offload helpers

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 <vladimir.oltean@nxp.com>
Link: https://patch.msgid.link/20251130131657.65080-8-vladimir.oltean@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Vladimir Oltean and committed by
Jakub Kicinski
4b65d445 42e63b13

+69 -1
+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,