diff options
author | David S. Miller <davem@davemloft.net> | 2022-05-23 10:40:10 +0100 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2022-05-23 10:40:10 +0100 |
commit | 682a8c633fc32933a73fd0379c62d37d1346055d (patch) | |
tree | 41b0ac849a21733f9e405d2623c0f77db876ea5b | |
parent | 1e39b27bd9d12d615973c3aacefb936466512cae (diff) | |
parent | a4e044dc4c5b1e03f4195224c47108dc44122917 (diff) | |
download | linux-682a8c633fc32933a73fd0379c62d37d1346055d.tar.bz2 |
Merge branch 'dsa-multi-cpu-port-part-two'
Vladimir Oltean says:
====================
DSA changes for multiple CPU ports (part 2)
As explained in part 1:
https://patchwork.kernel.org/project/netdevbpf/cover/20220511095020.562461-1-vladimir.oltean@nxp.com/
I am trying to enable the second internal port pair from the NXP LS1028A
Felix switch for DSA-tagged traffic via "ocelot-8021q". This series
represents part 2 (of an unknown number) of that effort.
This series deals only with a minor bug fix (first patch) and with code
reorganization in the Felix DSA driver and in the Ocelot switch library.
Hopefully this will lay the ground for a clean introduction of new UAPI
for changing the DSA master of a user port in part 3.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/dsa/ocelot/felix.c | 173 | ||||
-rw-r--r-- | drivers/net/dsa/ocelot/felix_vsc9959.c | 3 | ||||
-rw-r--r-- | drivers/net/ethernet/mscc/ocelot.c | 162 | ||||
-rw-r--r-- | include/soc/mscc/ocelot.h | 11 | ||||
-rw-r--r-- | include/soc/mscc/ocelot_vcap.h | 2 |
5 files changed, 207 insertions, 144 deletions
diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index d38258a39d07..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,74 +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; } - 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); - - /* 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); -} - -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); + err = felix_tag_8021q_vlan_del_tx(ds, port, vid); + if (err) + goto del_tx_failed; - /* Restore PGID_CPU */ - ocelot_write_rix(ocelot, BIT(ocelot->num_phys_ports), ANA_PGID_PGID, - PGID_CPU); + return 0; - ocelot_apply_bridge_fwd_mask(ocelot, true); +del_tx_failed: + dsa_switch_for_each_cpu_port(cpu_dp, ds) + felix_tag_8021q_vlan_add_rx(ds, port, cpu_dp->index, vid); - mutex_unlock(&ocelot->fwd_domain_lock); + return err; } static int felix_trap_get_cpu_port(struct dsa_switch *ds, @@ -434,6 +422,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, @@ -443,21 +438,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) { - felix_8021q_cpu_port_init(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_assign_dsa_8021q_cpu(ocelot, dp->index, + dp->cpu_dp->index); - 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: @@ -471,7 +463,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. @@ -488,9 +479,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. */ @@ -498,14 +489,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) { - felix_8021q_cpu_port_deinit(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); } @@ -534,6 +520,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 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 e0d1d5b59981..8da7e25a47c9 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -2046,57 +2046,68 @@ static int ocelot_bond_get_id(struct ocelot *ocelot, struct net_device *bond) return __ffs(bond_mask); } -u32 ocelot_get_bridge_fwd_mask(struct ocelot *ocelot, int src_port) +static u32 ocelot_dsa_8021q_cpu_assigned_ports(struct ocelot *ocelot, + struct ocelot_port *cpu) { - struct ocelot_port *ocelot_port = ocelot->ports[src_port]; - const struct net_device *bridge; u32 mask = 0; int port; - if (!ocelot_port || ocelot_port->stp_state != BR_STATE_FORWARDING) - return 0; - - bridge = ocelot_port->bridge; - if (!bridge) - return 0; - for (port = 0; port < ocelot->num_phys_ports; port++) { - ocelot_port = ocelot->ports[port]; + struct ocelot_port *ocelot_port = ocelot->ports[port]; if (!ocelot_port) continue; - if (ocelot_port->stp_state == BR_STATE_FORWARDING && - ocelot_port->bridge == bridge) + if (ocelot_port->dsa_8021q_cpu == cpu) mask |= BIT(port); } return mask; } -EXPORT_SYMBOL_GPL(ocelot_get_bridge_fwd_mask); -u32 ocelot_get_dsa_8021q_cpu_mask(struct ocelot *ocelot) +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]; + const struct net_device *bridge; u32 mask = 0; int port; + if (!ocelot_port || ocelot_port->stp_state != BR_STATE_FORWARDING) + return 0; + + bridge = ocelot_port->bridge; + if (!bridge) + return 0; + for (port = 0; port < ocelot->num_phys_ports; port++) { - struct ocelot_port *ocelot_port = ocelot->ports[port]; + ocelot_port = ocelot->ports[port]; if (!ocelot_port) continue; - if (ocelot_port->is_dsa_8021q_cpu) + if (ocelot_port->stp_state == BR_STATE_FORWARDING && + ocelot_port->bridge == bridge) mask |= BIT(port); } return mask; } -EXPORT_SYMBOL_GPL(ocelot_get_dsa_8021q_cpu_mask); +EXPORT_SYMBOL_GPL(ocelot_get_bridge_fwd_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; lockdep_assert_held(&ocelot->fwd_domain_lock); @@ -2108,15 +2119,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); - /* 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 @@ 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 @@ 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); @@ -2163,29 +2168,94 @@ 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); -void ocelot_port_set_dsa_8021q_cpu(struct ocelot *ocelot, int port) +/* 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_assign_dsa_8021q_cpu(struct ocelot *ocelot, int port, + int cpu) { + struct ocelot_port *cpu_port = ocelot->ports[cpu]; u16 vid; - ocelot->ports[port]->is_dsa_8021q_cpu = true; + mutex_lock(&ocelot->fwd_domain_lock); + + ocelot->ports[port]->dsa_8021q_cpu = cpu_port; + + if (!cpu_port->is_dsa_8021q_cpu) { + cpu_port->is_dsa_8021q_cpu = true; - for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++) - ocelot_vlan_member_add(ocelot, port, vid, true); + 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]->dsa_8021q_cpu = NULL; - ocelot->ports[port]->is_dsa_8021q_cpu = false; + for (p = 0; p < ocelot->num_phys_ports; p++) { + if (!ocelot->ports[p]) + continue; - for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++) - ocelot_vlan_member_del(ocelot, port, vid); + 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 3b8c5a54fb00..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,9 +883,7 @@ 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); -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, 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)) |