From 129b7532a0edc3f381a6c5f3bb1a155e743d141b Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Sun, 22 May 2022 00:37:38 +0300 Subject: [PATCH 1/6] net: dsa: fix missing adjustment of host broadcast flooding PGID_BC is configured statically by ocelot_init() to flood towards the CPU port module, and dynamically by ocelot_port_set_bcast_flood() towards all user ports. When the tagging protocol changes, the intention is to turn off flooding towards the old pipe towards the host, and to turn it on towards the new pipe. Due to a recent change which removed the adjustment of PGID_BC from felix_set_host_flood(), 3 things happen. - when we change from NPI to tag_8021q mode: in this mode, the CPU port module is accessed via registers, and used to read PTP packets with timestamps. We fail to disable broadcast flooding towards the CPU port module, and to enable broadcast flooding towards the physical port that serves as a DSA tag_8021q CPU port. - from tag_8021q to NPI mode: in this mode, the CPU port module is redirected to a physical port. We fail to disable broadcast flooding towards the physical tag_8021q CPU port, and to enable it towards the CPU port module at ocelot->num_phys_ports. - when the ports are put in promiscuous mode, we also fail to update PGID_BC towards the host pipe of the current protocol. First issue means that felix_check_xtr_pkt() has to do extra work, because it will not see only PTP packets, but also broadcasts. It needs to dequeue these packets just to drop them. Third issue is inconsequential, since PGID_BC is allocated from the nonreserved multicast PGID space, and these PGIDs are conveniently initialized to 0x7f (i.e. flood towards all ports except the CPU port module). Broadcasts reach the NPI port via ocelot_init(), and reach the tag_8021q CPU port via the hardware defaults. Second issue is also inconsequential, because we fail both at disabling and at enabling broadcast flooding on a port, so the defaults mentioned above are preserved, and they are fine except for the performance impact. Fixes: 7a29d220f4c0 ("net: dsa: felix: reimplement tagging protocol change with function pointers") Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/ocelot/felix.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index d38258a39d07..78c32b7de185 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -534,6 +534,9 @@ static void felix_set_host_flood(struct dsa_switch *ds, unsigned long mask, ocelot_rmw_rix(ocelot, val, mask, ANA_PGID_PGID, PGID_MC); ocelot_rmw_rix(ocelot, val, mask, ANA_PGID_PGID, PGID_MCIPV4); ocelot_rmw_rix(ocelot, val, mask, ANA_PGID_PGID, PGID_MCIPV6); + + val = bc ? mask : 0; + ocelot_rmw_rix(ocelot, val, mask, ANA_PGID_PGID, PGID_BC); } static void From 61be79ba2d90162b0b213384f35eb8db7eb183b8 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Sun, 22 May 2022 00:37:39 +0300 Subject: [PATCH 2/6] net: dsa: felix: move the updating of PGID_CPU to the ocelot lib PGID_CPU must be updated every time a port is configured or unconfigured as a tag_8021q CPU port. The ocelot switch lib already has a hook for that operation, so move the updating of PGID_CPU to those hooks. These bits are pretty specific to DSA, so normally I would keep them out of the common switch lib, but when tag_8021q is in use, this has implications upon the forwarding mask determined by ocelot_apply_bridge_fwd_mask() and called extensively by the switch lib. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/ocelot/felix.c | 7 ------- drivers/net/ethernet/mscc/ocelot.c | 31 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 78c32b7de185..1299f6a8ac5b 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -253,9 +253,6 @@ static void felix_8021q_cpu_port_init(struct ocelot *ocelot, int port) ocelot_port_set_dsa_8021q_cpu(ocelot, port); - /* Overwrite PGID_CPU with the non-tagging port */ - ocelot_write_rix(ocelot, BIT(port), ANA_PGID_PGID, PGID_CPU); - ocelot_apply_bridge_fwd_mask(ocelot, true); mutex_unlock(&ocelot->fwd_domain_lock); @@ -267,10 +264,6 @@ static void felix_8021q_cpu_port_deinit(struct ocelot *ocelot, int port) ocelot_port_unset_dsa_8021q_cpu(ocelot, port); - /* Restore PGID_CPU */ - ocelot_write_rix(ocelot, BIT(ocelot->num_phys_ports), ANA_PGID_PGID, - PGID_CPU); - ocelot_apply_bridge_fwd_mask(ocelot, true); mutex_unlock(&ocelot->fwd_domain_lock); diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index e0d1d5b59981..ac9faf1923c5 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -2165,6 +2165,33 @@ void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining) } EXPORT_SYMBOL(ocelot_apply_bridge_fwd_mask); +/* Update PGID_CPU which is the destination port mask used for whitelisting + * unicast addresses filtered towards the host. In the normal and NPI modes, + * this points to the analyzer entry for the CPU port module, while in DSA + * tag_8021q mode, it is a bit mask of all active CPU ports. + * PGID_SRC will take care of forwarding a packet from one user port to + * no more than a single CPU port. + */ +static void ocelot_update_pgid_cpu(struct ocelot *ocelot) +{ + int pgid_cpu = 0; + int port; + + for (port = 0; port < ocelot->num_phys_ports; port++) { + struct ocelot_port *ocelot_port = ocelot->ports[port]; + + if (!ocelot_port || !ocelot_port->is_dsa_8021q_cpu) + continue; + + pgid_cpu |= BIT(port); + } + + if (!pgid_cpu) + pgid_cpu = BIT(ocelot->num_phys_ports); + + ocelot_write_rix(ocelot, pgid_cpu, ANA_PGID_PGID, PGID_CPU); +} + void ocelot_port_set_dsa_8021q_cpu(struct ocelot *ocelot, int port) { u16 vid; @@ -2173,6 +2200,8 @@ void ocelot_port_set_dsa_8021q_cpu(struct ocelot *ocelot, int port) for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++) ocelot_vlan_member_add(ocelot, port, vid, true); + + ocelot_update_pgid_cpu(ocelot); } EXPORT_SYMBOL_GPL(ocelot_port_set_dsa_8021q_cpu); @@ -2184,6 +2213,8 @@ void ocelot_port_unset_dsa_8021q_cpu(struct ocelot *ocelot, int port) for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++) ocelot_vlan_member_del(ocelot, port, vid); + + ocelot_update_pgid_cpu(ocelot); } EXPORT_SYMBOL_GPL(ocelot_port_unset_dsa_8021q_cpu); From a72e23dd679c632bcf6ab47a3a1a58c1e777713a Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Sun, 22 May 2022 00:37:40 +0300 Subject: [PATCH 3/6] net: dsa: felix: update bridge fwd mask from ocelot lib when changing tag_8021q CPU Add more logic to ocelot_port_{,un}set_dsa_8021q_cpu() from the ocelot switch lib by encapsulating the ocelot_apply_bridge_fwd_mask() call that felix used to have. This is necessary because the CPU port change procedure will also need to do this, and it's good to reduce code duplication by having an entry point in the ocelot switch lib that does all that is needed. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/ocelot/felix.c | 4 ---- drivers/net/ethernet/mscc/ocelot.c | 7 +++++-- include/soc/mscc/ocelot.h | 1 - 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 1299f6a8ac5b..b60d6e7295e1 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -253,8 +253,6 @@ static void felix_8021q_cpu_port_init(struct ocelot *ocelot, int port) ocelot_port_set_dsa_8021q_cpu(ocelot, port); - ocelot_apply_bridge_fwd_mask(ocelot, true); - mutex_unlock(&ocelot->fwd_domain_lock); } @@ -264,8 +262,6 @@ static void felix_8021q_cpu_port_deinit(struct ocelot *ocelot, int port) ocelot_port_unset_dsa_8021q_cpu(ocelot, port); - ocelot_apply_bridge_fwd_mask(ocelot, true); - mutex_unlock(&ocelot->fwd_domain_lock); } diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index ac9faf1923c5..4011a7968be5 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -2094,7 +2094,7 @@ u32 ocelot_get_dsa_8021q_cpu_mask(struct ocelot *ocelot) } EXPORT_SYMBOL_GPL(ocelot_get_dsa_8021q_cpu_mask); -void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining) +static void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining) { unsigned long cpu_fwd_mask; int port; @@ -2163,7 +2163,6 @@ void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining) if (!joining && ocelot->ops->cut_through_fwd) ocelot->ops->cut_through_fwd(ocelot); } -EXPORT_SYMBOL(ocelot_apply_bridge_fwd_mask); /* Update PGID_CPU which is the destination port mask used for whitelisting * unicast addresses filtered towards the host. In the normal and NPI modes, @@ -2202,6 +2201,8 @@ void ocelot_port_set_dsa_8021q_cpu(struct ocelot *ocelot, int port) ocelot_vlan_member_add(ocelot, port, vid, true); ocelot_update_pgid_cpu(ocelot); + + ocelot_apply_bridge_fwd_mask(ocelot, true); } EXPORT_SYMBOL_GPL(ocelot_port_set_dsa_8021q_cpu); @@ -2215,6 +2216,8 @@ void ocelot_port_unset_dsa_8021q_cpu(struct ocelot *ocelot, int port) ocelot_vlan_member_del(ocelot, port, vid); ocelot_update_pgid_cpu(ocelot); + + ocelot_apply_bridge_fwd_mask(ocelot, true); } EXPORT_SYMBOL_GPL(ocelot_port_unset_dsa_8021q_cpu); diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index 3b8c5a54fb00..2c90a24ca064 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -880,7 +880,6 @@ int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, bool enabled, void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state); u32 ocelot_get_dsa_8021q_cpu_mask(struct ocelot *ocelot); u32 ocelot_get_bridge_fwd_mask(struct ocelot *ocelot, int src_port); -void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining); int ocelot_port_pre_bridge_flags(struct ocelot *ocelot, int port, struct switchdev_brport_flags val); void ocelot_port_bridge_flags(struct ocelot *ocelot, int port, From 8c166acb60f8284470db673b6dccd43e6fedfae2 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Sun, 22 May 2022 00:37:41 +0300 Subject: [PATCH 4/6] net: dsa: felix: directly call ocelot_port_{set,unset}_dsa_8021q_cpu Absorb the final details of calling ocelot_port_{,un}set_dsa_8021q_cpu(), i.e. the need to lock &ocelot->fwd_domain_lock, into the callee, to simplify the caller and permit easier code reuse later. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/ocelot/felix.c | 36 ++++++++---------------------- drivers/net/ethernet/mscc/ocelot.c | 8 +++++++ 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index b60d6e7295e1..033f7d5cc03d 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -240,31 +240,6 @@ static int felix_tag_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid) return 0; } -/* Alternatively to using the NPI functionality, that same hardware MAC - * connected internally to the enetc or fman DSA master can be configured to - * use the software-defined tag_8021q frame format. As far as the hardware is - * concerned, it thinks it is a "dumb switch" - the queues of the CPU port - * module are now disconnected from it, but can still be accessed through - * register-based MMIO. - */ -static void felix_8021q_cpu_port_init(struct ocelot *ocelot, int port) -{ - mutex_lock(&ocelot->fwd_domain_lock); - - ocelot_port_set_dsa_8021q_cpu(ocelot, port); - - mutex_unlock(&ocelot->fwd_domain_lock); -} - -static void felix_8021q_cpu_port_deinit(struct ocelot *ocelot, int port) -{ - mutex_lock(&ocelot->fwd_domain_lock); - - ocelot_port_unset_dsa_8021q_cpu(ocelot, port); - - mutex_unlock(&ocelot->fwd_domain_lock); -} - static int felix_trap_get_cpu_port(struct dsa_switch *ds, const struct ocelot_vcap_filter *trap) { @@ -423,6 +398,13 @@ static unsigned long felix_tag_npi_get_host_fwd_mask(struct dsa_switch *ds) return BIT(ocelot->num_phys_ports); } +/* Alternatively to using the NPI functionality, that same hardware MAC + * connected internally to the enetc or fman DSA master can be configured to + * use the software-defined tag_8021q frame format. As far as the hardware is + * concerned, it thinks it is a "dumb switch" - the queues of the CPU port + * module are now disconnected from it, but can still be accessed through + * register-based MMIO. + */ static const struct felix_tag_proto_ops felix_tag_npi_proto_ops = { .setup = felix_tag_npi_setup, .teardown = felix_tag_npi_teardown, @@ -440,7 +422,7 @@ static int felix_tag_8021q_setup(struct dsa_switch *ds) return err; dsa_switch_for_each_cpu_port(cpu_dp, ds) { - felix_8021q_cpu_port_init(ocelot, cpu_dp->index); + ocelot_port_set_dsa_8021q_cpu(ocelot, cpu_dp->index); /* TODO we could support multiple CPU ports in tag_8021q mode */ break; @@ -490,7 +472,7 @@ static void felix_tag_8021q_teardown(struct dsa_switch *ds) } dsa_switch_for_each_cpu_port(cpu_dp, ds) { - felix_8021q_cpu_port_deinit(ocelot, cpu_dp->index); + ocelot_port_unset_dsa_8021q_cpu(ocelot, cpu_dp->index); /* TODO we could support multiple CPU ports in tag_8021q mode */ break; diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 4011a7968be5..d208d57f4894 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -2195,6 +2195,8 @@ void ocelot_port_set_dsa_8021q_cpu(struct ocelot *ocelot, int port) { u16 vid; + mutex_lock(&ocelot->fwd_domain_lock); + ocelot->ports[port]->is_dsa_8021q_cpu = true; for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++) @@ -2203,6 +2205,8 @@ void ocelot_port_set_dsa_8021q_cpu(struct ocelot *ocelot, int port) ocelot_update_pgid_cpu(ocelot); ocelot_apply_bridge_fwd_mask(ocelot, true); + + mutex_unlock(&ocelot->fwd_domain_lock); } EXPORT_SYMBOL_GPL(ocelot_port_set_dsa_8021q_cpu); @@ -2210,6 +2214,8 @@ void ocelot_port_unset_dsa_8021q_cpu(struct ocelot *ocelot, int port) { u16 vid; + mutex_lock(&ocelot->fwd_domain_lock); + ocelot->ports[port]->is_dsa_8021q_cpu = false; for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++) @@ -2218,6 +2224,8 @@ void ocelot_port_unset_dsa_8021q_cpu(struct ocelot *ocelot, int port) ocelot_update_pgid_cpu(ocelot); ocelot_apply_bridge_fwd_mask(ocelot, true); + + mutex_unlock(&ocelot->fwd_domain_lock); } EXPORT_SYMBOL_GPL(ocelot_port_unset_dsa_8021q_cpu); From c295f9831f1db12330d6a28a1cb2bd2562535e37 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Sun, 22 May 2022 00:37:42 +0300 Subject: [PATCH 5/6] net: mscc: ocelot: switch from {,un}set to {,un}assign for tag_8021q CPU ports There is a desire for the felix driver to gain support for multiple tag_8021q CPU ports, but the current model prevents it. This is because ocelot_apply_bridge_fwd_mask() only takes into consideration whether a port is a tag_8021q CPU port, but not whose CPU port it is. We need a model where we can have a direct affinity between an ocelot port and a tag_8021q CPU port. This serves as the basis for multiple CPU ports. Declare a "dsa_8021q_cpu" backpointer in struct ocelot_port which encodes that affinity. Repurpose the "ocelot_set_dsa_8021q_cpu" API to "ocelot_assign_dsa_8021q_cpu" to express the change of paradigm. Note that this change makes the first practical use of the new ocelot_port->index field in ocelot_port_unassign_dsa_8021q_cpu(), where we need to remove the old tag_8021q CPU port from the reserved VLAN range. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/ocelot/felix.c | 27 ++---- drivers/net/dsa/ocelot/felix_vsc9959.c | 3 +- drivers/net/ethernet/mscc/ocelot.c | 122 +++++++++++++++---------- include/soc/mscc/ocelot.h | 10 +- 4 files changed, 93 insertions(+), 69 deletions(-) diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 033f7d5cc03d..01d8a731851e 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -414,21 +414,18 @@ static const struct felix_tag_proto_ops felix_tag_npi_proto_ops = { static int felix_tag_8021q_setup(struct dsa_switch *ds) { struct ocelot *ocelot = ds->priv; - struct dsa_port *dp, *cpu_dp; + struct dsa_port *dp; int err; err = dsa_tag_8021q_register(ds, htons(ETH_P_8021AD)); if (err) return err; - dsa_switch_for_each_cpu_port(cpu_dp, ds) { - ocelot_port_set_dsa_8021q_cpu(ocelot, cpu_dp->index); + dsa_switch_for_each_user_port(dp, ds) + ocelot_port_assign_dsa_8021q_cpu(ocelot, dp->index, + dp->cpu_dp->index); - /* TODO we could support multiple CPU ports in tag_8021q mode */ - break; - } - - dsa_switch_for_each_available_port(dp, ds) { + dsa_switch_for_each_available_port(dp, ds) /* This overwrites ocelot_init(): * Do not forward BPDU frames to the CPU port module, * for 2 reasons: @@ -442,7 +439,6 @@ static int felix_tag_8021q_setup(struct dsa_switch *ds) ocelot_write_gix(ocelot, ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0), ANA_PORT_CPU_FWD_BPDU_CFG, dp->index); - } /* The ownership of the CPU port module's queues might have just been * transferred to the tag_8021q tagger from the NPI-based tagger. @@ -459,9 +455,9 @@ static int felix_tag_8021q_setup(struct dsa_switch *ds) static void felix_tag_8021q_teardown(struct dsa_switch *ds) { struct ocelot *ocelot = ds->priv; - struct dsa_port *dp, *cpu_dp; + struct dsa_port *dp; - dsa_switch_for_each_available_port(dp, ds) { + dsa_switch_for_each_available_port(dp, ds) /* Restore the logic from ocelot_init: * do not forward BPDU frames to the front ports. */ @@ -469,14 +465,9 @@ static void felix_tag_8021q_teardown(struct dsa_switch *ds) ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0xffff), ANA_PORT_CPU_FWD_BPDU_CFG, dp->index); - } - dsa_switch_for_each_cpu_port(cpu_dp, ds) { - ocelot_port_unset_dsa_8021q_cpu(ocelot, cpu_dp->index); - - /* TODO we could support multiple CPU ports in tag_8021q mode */ - break; - } + dsa_switch_for_each_user_port(dp, ds) + ocelot_port_unassign_dsa_8021q_cpu(ocelot, dp->index); dsa_tag_8021q_unregister(ds); } diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index 98caca4317d7..570d0204b7be 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -2162,7 +2162,8 @@ static void vsc9959_cut_through_fwd(struct ocelot *ocelot) if (ocelot->npi >= 0) mask |= BIT(ocelot->npi); else - mask |= ocelot_get_dsa_8021q_cpu_mask(ocelot); + mask |= ocelot_port_assigned_dsa_8021q_cpu_mask(ocelot, + port); } /* Calculate the minimum link speed, among the ports that are diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index d208d57f4894..8da7e25a47c9 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -2046,6 +2046,37 @@ static int ocelot_bond_get_id(struct ocelot *ocelot, struct net_device *bond) return __ffs(bond_mask); } +static u32 ocelot_dsa_8021q_cpu_assigned_ports(struct ocelot *ocelot, + struct ocelot_port *cpu) +{ + u32 mask = 0; + int port; + + for (port = 0; port < ocelot->num_phys_ports; port++) { + struct ocelot_port *ocelot_port = ocelot->ports[port]; + + if (!ocelot_port) + continue; + + if (ocelot_port->dsa_8021q_cpu == cpu) + mask |= BIT(port); + } + + return mask; +} + +u32 ocelot_port_assigned_dsa_8021q_cpu_mask(struct ocelot *ocelot, int port) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + struct ocelot_port *cpu_port = ocelot_port->dsa_8021q_cpu; + + if (!cpu_port) + return 0; + + return BIT(cpu_port->index); +} +EXPORT_SYMBOL_GPL(ocelot_port_assigned_dsa_8021q_cpu_mask); + u32 ocelot_get_bridge_fwd_mask(struct ocelot *ocelot, int src_port) { struct ocelot_port *ocelot_port = ocelot->ports[src_port]; @@ -2075,28 +2106,8 @@ u32 ocelot_get_bridge_fwd_mask(struct ocelot *ocelot, int src_port) } EXPORT_SYMBOL_GPL(ocelot_get_bridge_fwd_mask); -u32 ocelot_get_dsa_8021q_cpu_mask(struct ocelot *ocelot) -{ - u32 mask = 0; - int port; - - for (port = 0; port < ocelot->num_phys_ports; port++) { - struct ocelot_port *ocelot_port = ocelot->ports[port]; - - if (!ocelot_port) - continue; - - if (ocelot_port->is_dsa_8021q_cpu) - mask |= BIT(port); - } - - return mask; -} -EXPORT_SYMBOL_GPL(ocelot_get_dsa_8021q_cpu_mask); - static void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining) { - unsigned long cpu_fwd_mask; int port; lockdep_assert_held(&ocelot->fwd_domain_lock); @@ -2108,15 +2119,6 @@ static void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining) if (joining && ocelot->ops->cut_through_fwd) ocelot->ops->cut_through_fwd(ocelot); - /* If a DSA tag_8021q CPU exists, it needs to be included in the - * regular forwarding path of the front ports regardless of whether - * those are bridged or standalone. - * If DSA tag_8021q is not used, this returns 0, which is fine because - * the hardware-based CPU port module can be a destination for packets - * even if it isn't part of PGID_SRC. - */ - cpu_fwd_mask = ocelot_get_dsa_8021q_cpu_mask(ocelot); - /* Apply FWD mask. The loop is needed to add/remove the current port as * a source for the other ports. */ @@ -2129,17 +2131,19 @@ static void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining) mask = 0; } else if (ocelot_port->is_dsa_8021q_cpu) { /* The DSA tag_8021q CPU ports need to be able to - * forward packets to all other ports except for - * themselves + * forward packets to all ports assigned to them. */ - mask = GENMASK(ocelot->num_phys_ports - 1, 0); - mask &= ~cpu_fwd_mask; + mask = ocelot_dsa_8021q_cpu_assigned_ports(ocelot, + ocelot_port); } else if (ocelot_port->bridge) { struct net_device *bond = ocelot_port->bond; mask = ocelot_get_bridge_fwd_mask(ocelot, port); - mask |= cpu_fwd_mask; mask &= ~BIT(port); + + mask |= ocelot_port_assigned_dsa_8021q_cpu_mask(ocelot, + port); + if (bond) mask &= ~ocelot_get_bond_mask(ocelot, bond); } else { @@ -2147,7 +2151,8 @@ static void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining) * ports (if those exist), or to the hardware CPU port * module otherwise. */ - mask = cpu_fwd_mask; + mask = ocelot_port_assigned_dsa_8021q_cpu_mask(ocelot, + port); } ocelot_write_rix(ocelot, mask, ANA_PGID_PGID, PGID_SRC + port); @@ -2191,43 +2196,66 @@ static void ocelot_update_pgid_cpu(struct ocelot *ocelot) ocelot_write_rix(ocelot, pgid_cpu, ANA_PGID_PGID, PGID_CPU); } -void ocelot_port_set_dsa_8021q_cpu(struct ocelot *ocelot, int port) +void ocelot_port_assign_dsa_8021q_cpu(struct ocelot *ocelot, int port, + int cpu) { + struct ocelot_port *cpu_port = ocelot->ports[cpu]; u16 vid; mutex_lock(&ocelot->fwd_domain_lock); - ocelot->ports[port]->is_dsa_8021q_cpu = true; + ocelot->ports[port]->dsa_8021q_cpu = cpu_port; - for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++) - ocelot_vlan_member_add(ocelot, port, vid, true); + if (!cpu_port->is_dsa_8021q_cpu) { + cpu_port->is_dsa_8021q_cpu = true; - ocelot_update_pgid_cpu(ocelot); + for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++) + ocelot_vlan_member_add(ocelot, cpu, vid, true); + + ocelot_update_pgid_cpu(ocelot); + } ocelot_apply_bridge_fwd_mask(ocelot, true); mutex_unlock(&ocelot->fwd_domain_lock); } -EXPORT_SYMBOL_GPL(ocelot_port_set_dsa_8021q_cpu); +EXPORT_SYMBOL_GPL(ocelot_port_assign_dsa_8021q_cpu); -void ocelot_port_unset_dsa_8021q_cpu(struct ocelot *ocelot, int port) +void ocelot_port_unassign_dsa_8021q_cpu(struct ocelot *ocelot, int port) { + struct ocelot_port *cpu_port = ocelot->ports[port]->dsa_8021q_cpu; + bool keep = false; u16 vid; + int p; mutex_lock(&ocelot->fwd_domain_lock); - ocelot->ports[port]->is_dsa_8021q_cpu = false; + ocelot->ports[port]->dsa_8021q_cpu = NULL; - for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++) - ocelot_vlan_member_del(ocelot, port, vid); + for (p = 0; p < ocelot->num_phys_ports; p++) { + if (!ocelot->ports[p]) + continue; - ocelot_update_pgid_cpu(ocelot); + if (ocelot->ports[p]->dsa_8021q_cpu == cpu_port) { + keep = true; + break; + } + } + + if (!keep) { + cpu_port->is_dsa_8021q_cpu = false; + + for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++) + ocelot_vlan_member_del(ocelot, cpu_port->index, vid); + + ocelot_update_pgid_cpu(ocelot); + } ocelot_apply_bridge_fwd_mask(ocelot, true); mutex_unlock(&ocelot->fwd_domain_lock); } -EXPORT_SYMBOL_GPL(ocelot_port_unset_dsa_8021q_cpu); +EXPORT_SYMBOL_GPL(ocelot_port_unassign_dsa_8021q_cpu); void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state) { diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index 2c90a24ca064..5f88385a7748 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -654,6 +654,8 @@ struct ocelot_mirror { int to; }; +struct ocelot_port; + struct ocelot_port { struct ocelot *ocelot; @@ -662,6 +664,8 @@ struct ocelot_port { struct net_device *bond; struct net_device *bridge; + struct ocelot_port *dsa_8021q_cpu; + /* VLAN that untagged frames are classified to, on ingress */ const struct ocelot_bridge_vlan *pvid_vlan; @@ -865,8 +869,9 @@ void ocelot_deinit(struct ocelot *ocelot); void ocelot_init_port(struct ocelot *ocelot, int port); void ocelot_deinit_port(struct ocelot *ocelot, int port); -void ocelot_port_set_dsa_8021q_cpu(struct ocelot *ocelot, int port); -void ocelot_port_unset_dsa_8021q_cpu(struct ocelot *ocelot, int port); +void ocelot_port_assign_dsa_8021q_cpu(struct ocelot *ocelot, int port, int cpu); +void ocelot_port_unassign_dsa_8021q_cpu(struct ocelot *ocelot, int port); +u32 ocelot_port_assigned_dsa_8021q_cpu_mask(struct ocelot *ocelot, int port); /* DSA callbacks */ void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data); @@ -878,7 +883,6 @@ void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs); int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, bool enabled, struct netlink_ext_ack *extack); void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state); -u32 ocelot_get_dsa_8021q_cpu_mask(struct ocelot *ocelot); u32 ocelot_get_bridge_fwd_mask(struct ocelot *ocelot, int src_port); int ocelot_port_pre_bridge_flags(struct ocelot *ocelot, int port, struct switchdev_brport_flags val); From a4e044dc4c5b1e03f4195224c47108dc44122917 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Sun, 22 May 2022 00:37:43 +0300 Subject: [PATCH 6/6] net: dsa: felix: tag_8021q preparation for multiple CPU ports Update the VCAP filters to support multiple tag_8021q CPU ports. TX works using a filter for VLAN ID on the ingress of the CPU port, with a redirect and a VLAN pop action. This can be updated trivially by amending the ingress port mask of this rule to match on all tag_8021q CPU ports. RX works using a filter for ingress port on the egress of the CPU port, with a VLAN push action. Here we need to replicate these filters for each tag_8021q CPU port, and let them all have the same action. This means that the OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN() cookie needs to encode a unique value for every {user port, CPU port} pair it's given. Do this by encoding the CPU port in the upper 16 bits of the cookie, and the user port in the lower 16 bits. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/ocelot/felix.c | 104 ++++++++++++++++++++------------- include/soc/mscc/ocelot_vcap.h | 2 +- 2 files changed, 65 insertions(+), 41 deletions(-) diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 01d8a731851e..3e07dc39007a 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -45,24 +45,26 @@ static struct net_device *felix_classify_db(struct dsa_db db) /* Set up VCAP ES0 rules for pushing a tag_8021q VLAN towards the CPU such that * the tagger can perform RX source port identification. */ -static int felix_tag_8021q_vlan_add_rx(struct felix *felix, int port, u16 vid) +static int felix_tag_8021q_vlan_add_rx(struct dsa_switch *ds, int port, + int upstream, u16 vid) { struct ocelot_vcap_filter *outer_tagging_rule; - struct ocelot *ocelot = &felix->ocelot; - struct dsa_switch *ds = felix->ds; - int key_length, upstream, err; + struct ocelot *ocelot = ds->priv; + unsigned long cookie; + int key_length, err; key_length = ocelot->vcap[VCAP_ES0].keys[VCAP_ES0_IGR_PORT].length; - upstream = dsa_upstream_port(ds, port); outer_tagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL); if (!outer_tagging_rule) return -ENOMEM; + cookie = OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port, upstream); + outer_tagging_rule->key_type = OCELOT_VCAP_KEY_ANY; outer_tagging_rule->prio = 1; - outer_tagging_rule->id.cookie = OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port); + outer_tagging_rule->id.cookie = cookie; outer_tagging_rule->id.tc_offload = false; outer_tagging_rule->block_id = VCAP_ES0; outer_tagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; @@ -83,16 +85,19 @@ static int felix_tag_8021q_vlan_add_rx(struct felix *felix, int port, u16 vid) return err; } -static int felix_tag_8021q_vlan_del_rx(struct felix *felix, int port, u16 vid) +static int felix_tag_8021q_vlan_del_rx(struct dsa_switch *ds, int port, + int upstream, u16 vid) { struct ocelot_vcap_filter *outer_tagging_rule; struct ocelot_vcap_block *block_vcap_es0; - struct ocelot *ocelot = &felix->ocelot; + struct ocelot *ocelot = ds->priv; + unsigned long cookie; block_vcap_es0 = &ocelot->block[VCAP_ES0]; + cookie = OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port, upstream); outer_tagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_es0, - port, false); + cookie, false); if (!outer_tagging_rule) return -ENOENT; @@ -102,12 +107,14 @@ static int felix_tag_8021q_vlan_del_rx(struct felix *felix, int port, u16 vid) /* Set up VCAP IS1 rules for stripping the tag_8021q VLAN on TX and VCAP IS2 * rules for steering those tagged packets towards the correct destination port */ -static int felix_tag_8021q_vlan_add_tx(struct felix *felix, int port, u16 vid) +static int felix_tag_8021q_vlan_add_tx(struct dsa_switch *ds, int port, + u16 vid) { struct ocelot_vcap_filter *untagging_rule, *redirect_rule; - struct ocelot *ocelot = &felix->ocelot; - struct dsa_switch *ds = felix->ds; - int upstream, err; + unsigned long cpu_ports = dsa_cpu_ports(ds); + struct ocelot *ocelot = ds->priv; + unsigned long cookie; + int err; untagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL); if (!untagging_rule) @@ -119,14 +126,14 @@ static int felix_tag_8021q_vlan_add_tx(struct felix *felix, int port, u16 vid) return -ENOMEM; } - upstream = dsa_upstream_port(ds, port); + cookie = OCELOT_VCAP_IS1_TAG_8021Q_TXVLAN(ocelot, port); untagging_rule->key_type = OCELOT_VCAP_KEY_ANY; - untagging_rule->ingress_port_mask = BIT(upstream); + untagging_rule->ingress_port_mask = cpu_ports; untagging_rule->vlan.vid.value = vid; untagging_rule->vlan.vid.mask = VLAN_VID_MASK; untagging_rule->prio = 1; - untagging_rule->id.cookie = OCELOT_VCAP_IS1_TAG_8021Q_TXVLAN(ocelot, port); + untagging_rule->id.cookie = cookie; untagging_rule->id.tc_offload = false; untagging_rule->block_id = VCAP_IS1; untagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; @@ -143,11 +150,13 @@ static int felix_tag_8021q_vlan_add_tx(struct felix *felix, int port, u16 vid) return err; } + cookie = OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port); + redirect_rule->key_type = OCELOT_VCAP_KEY_ANY; - redirect_rule->ingress_port_mask = BIT(upstream); + redirect_rule->ingress_port_mask = cpu_ports; redirect_rule->pag = port; redirect_rule->prio = 1; - redirect_rule->id.cookie = OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port); + redirect_rule->id.cookie = cookie; redirect_rule->id.tc_offload = false; redirect_rule->block_id = VCAP_IS2; redirect_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; @@ -165,19 +174,21 @@ static int felix_tag_8021q_vlan_add_tx(struct felix *felix, int port, u16 vid) return 0; } -static int felix_tag_8021q_vlan_del_tx(struct felix *felix, int port, u16 vid) +static int felix_tag_8021q_vlan_del_tx(struct dsa_switch *ds, int port, u16 vid) { struct ocelot_vcap_filter *untagging_rule, *redirect_rule; struct ocelot_vcap_block *block_vcap_is1; struct ocelot_vcap_block *block_vcap_is2; - struct ocelot *ocelot = &felix->ocelot; + struct ocelot *ocelot = ds->priv; + unsigned long cookie; int err; block_vcap_is1 = &ocelot->block[VCAP_IS1]; block_vcap_is2 = &ocelot->block[VCAP_IS2]; + cookie = OCELOT_VCAP_IS1_TAG_8021Q_TXVLAN(ocelot, port); untagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is1, - port, false); + cookie, false); if (!untagging_rule) return -ENOENT; @@ -185,8 +196,9 @@ static int felix_tag_8021q_vlan_del_tx(struct felix *felix, int port, u16 vid) if (err) return err; + cookie = OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port); redirect_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, - port, false); + cookie, false); if (!redirect_rule) return -ENOENT; @@ -196,7 +208,7 @@ static int felix_tag_8021q_vlan_del_tx(struct felix *felix, int port, u16 vid) static int felix_tag_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid, u16 flags) { - struct ocelot *ocelot = ds->priv; + struct dsa_port *cpu_dp; int err; /* tag_8021q.c assumes we are implementing this via port VLAN @@ -206,38 +218,50 @@ static int felix_tag_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid, if (!dsa_is_user_port(ds, port)) return 0; - err = felix_tag_8021q_vlan_add_rx(ocelot_to_felix(ocelot), port, vid); - if (err) - return err; - - err = felix_tag_8021q_vlan_add_tx(ocelot_to_felix(ocelot), port, vid); - if (err) { - felix_tag_8021q_vlan_del_rx(ocelot_to_felix(ocelot), port, vid); - return err; + dsa_switch_for_each_cpu_port(cpu_dp, ds) { + err = felix_tag_8021q_vlan_add_rx(ds, port, cpu_dp->index, vid); + if (err) + return err; } + err = felix_tag_8021q_vlan_add_tx(ds, port, vid); + if (err) + goto add_tx_failed; + return 0; + +add_tx_failed: + dsa_switch_for_each_cpu_port(cpu_dp, ds) + felix_tag_8021q_vlan_del_rx(ds, port, cpu_dp->index, vid); + + return err; } static int felix_tag_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid) { - struct ocelot *ocelot = ds->priv; + struct dsa_port *cpu_dp; int err; if (!dsa_is_user_port(ds, port)) return 0; - err = felix_tag_8021q_vlan_del_rx(ocelot_to_felix(ocelot), port, vid); - if (err) - return err; - - err = felix_tag_8021q_vlan_del_tx(ocelot_to_felix(ocelot), port, vid); - if (err) { - felix_tag_8021q_vlan_add_rx(ocelot_to_felix(ocelot), port, vid); - return err; + dsa_switch_for_each_cpu_port(cpu_dp, ds) { + err = felix_tag_8021q_vlan_del_rx(ds, port, cpu_dp->index, vid); + if (err) + return err; } + err = felix_tag_8021q_vlan_del_tx(ds, port, vid); + if (err) + goto del_tx_failed; + return 0; + +del_tx_failed: + dsa_switch_for_each_cpu_port(cpu_dp, ds) + felix_tag_8021q_vlan_add_rx(ds, port, cpu_dp->index, vid); + + return err; } static int felix_trap_get_cpu_port(struct dsa_switch *ds, diff --git a/include/soc/mscc/ocelot_vcap.h b/include/soc/mscc/ocelot_vcap.h index de26c992f821..c601a4598b0d 100644 --- a/include/soc/mscc/ocelot_vcap.h +++ b/include/soc/mscc/ocelot_vcap.h @@ -11,7 +11,7 @@ /* Cookie definitions for private VCAP filters installed by the driver. * Must be unique per VCAP block. */ -#define OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port) (port) +#define OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port, upstream) ((upstream) << 16 | (port)) #define OCELOT_VCAP_IS1_TAG_8021Q_TXVLAN(ocelot, port) (port) #define OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port) (port) #define OCELOT_VCAP_IS2_MRP_REDIRECT(ocelot, port) ((ocelot)->num_phys_ports + (port))