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>
This commit is contained in:
Vladimir Oltean 2025-11-30 15:16:49 +02:00 committed by Jakub Kicinski
parent 42e63b1373
commit 4b65d44555
1 changed files with 69 additions and 1 deletions

View File

@ -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 dsa_port *dp = dsa_to_port(ds, port);
struct ocelot *ocelot = ds->priv; struct ocelot *ocelot = ds->priv;
struct felix *felix = ocelot_to_felix(ocelot);
if (!dsa_port_is_user(dp)) if (!dsa_port_is_user(dp))
return 0; return 0;
@ -1246,7 +1247,25 @@ static int felix_port_enable(struct dsa_switch *ds, int port,
} }
} }
if (!dp->hsr_dev || felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q)
return 0; 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) 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); 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 = { static const struct phylink_mac_ops felix_phylink_mac_ops = {
.mac_select_pcs = felix_phylink_mac_select_pcs, .mac_select_pcs = felix_phylink_mac_select_pcs,
.mac_config = felix_phylink_mac_config, .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, .get_ts_info = felix_get_ts_info,
.phylink_get_caps = felix_phylink_get_caps, .phylink_get_caps = felix_phylink_get_caps,
.port_enable = felix_port_enable, .port_enable = felix_port_enable,
.port_disable = felix_port_disable,
.port_fast_age = felix_port_fast_age, .port_fast_age = felix_port_fast_age,
.port_fdb_dump = felix_fdb_dump, .port_fdb_dump = felix_fdb_dump,
.port_fdb_add = felix_fdb_add, .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_del_dscp_prio = felix_port_del_dscp_prio,
.port_set_host_flood = felix_port_set_host_flood, .port_set_host_flood = felix_port_set_host_flood,
.port_change_conduit = felix_port_change_conduit, .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, int felix_register_switch(struct device *dev, resource_size_t switch_base,