Commit 682a8c63 authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 1e39b27b a4e044dc
...@@ -45,24 +45,26 @@ static struct net_device *felix_classify_db(struct dsa_db db) ...@@ -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 /* Set up VCAP ES0 rules for pushing a tag_8021q VLAN towards the CPU such that
* the tagger can perform RX source port identification. * 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_vcap_filter *outer_tagging_rule;
struct ocelot *ocelot = &felix->ocelot; struct ocelot *ocelot = ds->priv;
struct dsa_switch *ds = felix->ds; unsigned long cookie;
int key_length, upstream, err; int key_length, err;
key_length = ocelot->vcap[VCAP_ES0].keys[VCAP_ES0_IGR_PORT].length; 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), outer_tagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter),
GFP_KERNEL); GFP_KERNEL);
if (!outer_tagging_rule) if (!outer_tagging_rule)
return -ENOMEM; return -ENOMEM;
cookie = OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port, upstream);
outer_tagging_rule->key_type = OCELOT_VCAP_KEY_ANY; outer_tagging_rule->key_type = OCELOT_VCAP_KEY_ANY;
outer_tagging_rule->prio = 1; 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->id.tc_offload = false;
outer_tagging_rule->block_id = VCAP_ES0; outer_tagging_rule->block_id = VCAP_ES0;
outer_tagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; 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) ...@@ -83,16 +85,19 @@ static int felix_tag_8021q_vlan_add_rx(struct felix *felix, int port, u16 vid)
return err; 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_filter *outer_tagging_rule;
struct ocelot_vcap_block *block_vcap_es0; 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]; 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, outer_tagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_es0,
port, false); cookie, false);
if (!outer_tagging_rule) if (!outer_tagging_rule)
return -ENOENT; return -ENOENT;
...@@ -102,12 +107,14 @@ static int felix_tag_8021q_vlan_del_rx(struct felix *felix, int port, u16 vid) ...@@ -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 /* 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 * 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_vcap_filter *untagging_rule, *redirect_rule;
struct ocelot *ocelot = &felix->ocelot; unsigned long cpu_ports = dsa_cpu_ports(ds);
struct dsa_switch *ds = felix->ds; struct ocelot *ocelot = ds->priv;
int upstream, err; unsigned long cookie;
int err;
untagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL); untagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL);
if (!untagging_rule) if (!untagging_rule)
...@@ -119,14 +126,14 @@ static int felix_tag_8021q_vlan_add_tx(struct felix *felix, int port, u16 vid) ...@@ -119,14 +126,14 @@ static int felix_tag_8021q_vlan_add_tx(struct felix *felix, int port, u16 vid)
return -ENOMEM; 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->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.value = vid;
untagging_rule->vlan.vid.mask = VLAN_VID_MASK; untagging_rule->vlan.vid.mask = VLAN_VID_MASK;
untagging_rule->prio = 1; 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->id.tc_offload = false;
untagging_rule->block_id = VCAP_IS1; untagging_rule->block_id = VCAP_IS1;
untagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; 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) ...@@ -143,11 +150,13 @@ static int felix_tag_8021q_vlan_add_tx(struct felix *felix, int port, u16 vid)
return err; return err;
} }
cookie = OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port);
redirect_rule->key_type = OCELOT_VCAP_KEY_ANY; 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->pag = port;
redirect_rule->prio = 1; 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->id.tc_offload = false;
redirect_rule->block_id = VCAP_IS2; redirect_rule->block_id = VCAP_IS2;
redirect_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; 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) ...@@ -165,19 +174,21 @@ static int felix_tag_8021q_vlan_add_tx(struct felix *felix, int port, u16 vid)
return 0; 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_filter *untagging_rule, *redirect_rule;
struct ocelot_vcap_block *block_vcap_is1; struct ocelot_vcap_block *block_vcap_is1;
struct ocelot_vcap_block *block_vcap_is2; struct ocelot_vcap_block *block_vcap_is2;
struct ocelot *ocelot = &felix->ocelot; struct ocelot *ocelot = ds->priv;
unsigned long cookie;
int err; int err;
block_vcap_is1 = &ocelot->block[VCAP_IS1]; block_vcap_is1 = &ocelot->block[VCAP_IS1];
block_vcap_is2 = &ocelot->block[VCAP_IS2]; 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, untagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is1,
port, false); cookie, false);
if (!untagging_rule) if (!untagging_rule)
return -ENOENT; return -ENOENT;
...@@ -185,8 +196,9 @@ static int felix_tag_8021q_vlan_del_tx(struct felix *felix, int port, u16 vid) ...@@ -185,8 +196,9 @@ static int felix_tag_8021q_vlan_del_tx(struct felix *felix, int port, u16 vid)
if (err) if (err)
return err; return err;
cookie = OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port);
redirect_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, redirect_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is2,
port, false); cookie, false);
if (!redirect_rule) if (!redirect_rule)
return -ENOENT; return -ENOENT;
...@@ -196,7 +208,7 @@ static int felix_tag_8021q_vlan_del_tx(struct felix *felix, int port, u16 vid) ...@@ -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, static int felix_tag_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid,
u16 flags) u16 flags)
{ {
struct ocelot *ocelot = ds->priv; struct dsa_port *cpu_dp;
int err; int err;
/* tag_8021q.c assumes we are implementing this via port VLAN /* 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, ...@@ -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)) if (!dsa_is_user_port(ds, port))
return 0; return 0;
err = felix_tag_8021q_vlan_add_rx(ocelot_to_felix(ocelot), port, vid); dsa_switch_for_each_cpu_port(cpu_dp, ds) {
if (err) err = felix_tag_8021q_vlan_add_rx(ds, port, cpu_dp->index, vid);
return err; 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;
} }
err = felix_tag_8021q_vlan_add_tx(ds, port, vid);
if (err)
goto add_tx_failed;
return 0; 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) 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; int err;
if (!dsa_is_user_port(ds, port)) if (!dsa_is_user_port(ds, port))
return 0; return 0;
err = felix_tag_8021q_vlan_del_rx(ocelot_to_felix(ocelot), port, vid); dsa_switch_for_each_cpu_port(cpu_dp, ds) {
if (err) err = felix_tag_8021q_vlan_del_rx(ds, port, cpu_dp->index, vid);
return err; 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;
} }
return 0; err = felix_tag_8021q_vlan_del_tx(ds, port, vid);
} if (err)
goto del_tx_failed;
/* 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);
/* Restore PGID_CPU */ return 0;
ocelot_write_rix(ocelot, BIT(ocelot->num_phys_ports), ANA_PGID_PGID,
PGID_CPU);
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, 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) ...@@ -434,6 +422,13 @@ static unsigned long felix_tag_npi_get_host_fwd_mask(struct dsa_switch *ds)
return BIT(ocelot->num_phys_ports); 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 = { static const struct felix_tag_proto_ops felix_tag_npi_proto_ops = {
.setup = felix_tag_npi_setup, .setup = felix_tag_npi_setup,
.teardown = felix_tag_npi_teardown, .teardown = felix_tag_npi_teardown,
...@@ -443,21 +438,18 @@ static const struct felix_tag_proto_ops felix_tag_npi_proto_ops = { ...@@ -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) static int felix_tag_8021q_setup(struct dsa_switch *ds)
{ {
struct ocelot *ocelot = ds->priv; struct ocelot *ocelot = ds->priv;
struct dsa_port *dp, *cpu_dp; struct dsa_port *dp;
int err; int err;
err = dsa_tag_8021q_register(ds, htons(ETH_P_8021AD)); err = dsa_tag_8021q_register(ds, htons(ETH_P_8021AD));
if (err) if (err)
return err; return err;
dsa_switch_for_each_cpu_port(cpu_dp, ds) { dsa_switch_for_each_user_port(dp, ds)
felix_8021q_cpu_port_init(ocelot, cpu_dp->index); 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(): /* This overwrites ocelot_init():
* Do not forward BPDU frames to the CPU port module, * Do not forward BPDU frames to the CPU port module,
* for 2 reasons: * for 2 reasons:
...@@ -471,7 +463,6 @@ static int felix_tag_8021q_setup(struct dsa_switch *ds) ...@@ -471,7 +463,6 @@ static int felix_tag_8021q_setup(struct dsa_switch *ds)
ocelot_write_gix(ocelot, ocelot_write_gix(ocelot,
ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0), ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0),
ANA_PORT_CPU_FWD_BPDU_CFG, dp->index); ANA_PORT_CPU_FWD_BPDU_CFG, dp->index);
}
/* The ownership of the CPU port module's queues might have just been /* The ownership of the CPU port module's queues might have just been
* transferred to the tag_8021q tagger from the NPI-based tagger. * 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) ...@@ -488,9 +479,9 @@ static int felix_tag_8021q_setup(struct dsa_switch *ds)
static void felix_tag_8021q_teardown(struct dsa_switch *ds) static void felix_tag_8021q_teardown(struct dsa_switch *ds)
{ {
struct ocelot *ocelot = ds->priv; 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: /* Restore the logic from ocelot_init:
* do not forward BPDU frames to the front ports. * do not forward BPDU frames to the front ports.
*/ */
...@@ -498,14 +489,9 @@ static void felix_tag_8021q_teardown(struct dsa_switch *ds) ...@@ -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_BPDU_REDIR_ENA(0xffff),
ANA_PORT_CPU_FWD_BPDU_CFG, ANA_PORT_CPU_FWD_BPDU_CFG,
dp->index); dp->index);
}
dsa_switch_for_each_cpu_port(cpu_dp, ds) { dsa_switch_for_each_user_port(dp, ds)
felix_8021q_cpu_port_deinit(ocelot, cpu_dp->index); ocelot_port_unassign_dsa_8021q_cpu(ocelot, dp->index);
/* TODO we could support multiple CPU ports in tag_8021q mode */
break;
}
dsa_tag_8021q_unregister(ds); dsa_tag_8021q_unregister(ds);
} }
...@@ -534,6 +520,9 @@ static void felix_set_host_flood(struct dsa_switch *ds, unsigned long mask, ...@@ -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_MC);
ocelot_rmw_rix(ocelot, val, mask, ANA_PGID_PGID, PGID_MCIPV4); ocelot_rmw_rix(ocelot, val, mask, ANA_PGID_PGID, PGID_MCIPV4);
ocelot_rmw_rix(ocelot, val, mask, ANA_PGID_PGID, PGID_MCIPV6); 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 static void
......
...@@ -2162,7 +2162,8 @@ static void vsc9959_cut_through_fwd(struct ocelot *ocelot) ...@@ -2162,7 +2162,8 @@ static void vsc9959_cut_through_fwd(struct ocelot *ocelot)
if (ocelot->npi >= 0) if (ocelot->npi >= 0)
mask |= BIT(ocelot->npi); mask |= BIT(ocelot->npi);
else 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 /* Calculate the minimum link speed, among the ports that are
......
...@@ -2046,57 +2046,68 @@ static int ocelot_bond_get_id(struct ocelot *ocelot, struct net_device *bond) ...@@ -2046,57 +2046,68 @@ static int ocelot_bond_get_id(struct ocelot *ocelot, struct net_device *bond)
return __ffs(bond_mask); 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; u32 mask = 0;
int port; 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++) { 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) if (!ocelot_port)
continue; continue;
if (ocelot_port->stp_state == BR_STATE_FORWARDING && if (ocelot_port->dsa_8021q_cpu == cpu)
ocelot_port->bridge == bridge)
mask |= BIT(port); mask |= BIT(port);
} }
return mask; 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; u32 mask = 0;
int port; 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++) { 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) if (!ocelot_port)
continue; continue;
if (ocelot_port->is_dsa_8021q_cpu) if (ocelot_port->stp_state == BR_STATE_FORWARDING &&
ocelot_port->bridge == bridge)
mask |= BIT(port); mask |= BIT(port);
} }
return mask; 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; int port;
lockdep_assert_held(&ocelot->fwd_domain_lock); lockdep_assert_held(&ocelot->fwd_domain_lock);
...@@ -2108,15 +2119,6 @@ void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining) ...@@ -2108,15 +2119,6 @@ void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining)
if (joining && ocelot->ops->cut_through_fwd) if (joining && ocelot->ops->cut_through_fwd)
ocelot->ops->cut_through_fwd(ocelot); 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 /* Apply FWD mask. The loop is needed to add/remove the current port as
* a source for the other ports. * a source for the other ports.
*/ */
...@@ -2129,17 +2131,19 @@ void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining) ...@@ -2129,17 +2131,19 @@ void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining)
mask = 0; mask = 0;
} else if (ocelot_port->is_dsa_8021q_cpu) { } else if (ocelot_port->is_dsa_8021q_cpu) {
/* The DSA tag_8021q CPU ports need to be able to /* The DSA tag_8021q CPU ports need to be able to
* forward packets to all other ports except for * forward packets to all ports assigned to them.
* themselves
*/ */
mask = GENMASK(ocelot->num_phys_ports - 1, 0); mask = ocelot_dsa_8021q_cpu_assigned_ports(ocelot,
mask &= ~cpu_fwd_mask; ocelot_port);
} else if (ocelot_port->bridge) { } else if (ocelot_port->bridge) {
struct net_device *bond = ocelot_port->bond; struct net_device *bond = ocelot_port->bond;
mask = ocelot_get_bridge_fwd_mask(ocelot, port); mask = ocelot_get_bridge_fwd_mask(ocelot, port);
mask |= cpu_fwd_mask;
mask &= ~BIT(port); mask &= ~BIT(port);
mask |= ocelot_port_assigned_dsa_8021q_cpu_mask(ocelot,
port);
if (bond) if (bond)
mask &= ~ocelot_get_bond_mask(ocelot, bond); mask &= ~ocelot_get_bond_mask(ocelot, bond);
} else { } else {
...@@ -2147,7 +2151,8 @@ void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining) ...@@ -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 * ports (if those exist), or to the hardware CPU port
* module otherwise. * 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); 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) ...@@ -2163,29 +2168,94 @@ void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining)
if (!joining && ocelot->ops->cut_through_fwd) if (!joining && ocelot->ops->cut_through_fwd)
ocelot->ops->cut_through_fwd(ocelot); 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; 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++) for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++)
ocelot_vlan_member_add(ocelot, port, vid, true); 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; 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++) if (ocelot->ports[p]->dsa_8021q_cpu == cpu_port) {
ocelot_vlan_member_del(ocelot, port, vid); 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) void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state)
{ {
......
...@@ -654,6 +654,8 @@ struct ocelot_mirror { ...@@ -654,6 +654,8 @@ struct ocelot_mirror {
int to; int to;
}; };
struct ocelot_port;
struct ocelot_port { struct ocelot_port {
struct ocelot *ocelot; struct ocelot *ocelot;
...@@ -662,6 +664,8 @@ struct ocelot_port { ...@@ -662,6 +664,8 @@ struct ocelot_port {
struct net_device *bond; struct net_device *bond;
struct net_device *bridge; struct net_device *bridge;
struct ocelot_port *dsa_8021q_cpu;
/* VLAN that untagged frames are classified to, on ingress */ /* VLAN that untagged frames are classified to, on ingress */
const struct ocelot_bridge_vlan *pvid_vlan; const struct ocelot_bridge_vlan *pvid_vlan;
...@@ -865,8 +869,9 @@ void ocelot_deinit(struct ocelot *ocelot); ...@@ -865,8 +869,9 @@ void ocelot_deinit(struct ocelot *ocelot);
void ocelot_init_port(struct ocelot *ocelot, int port); void ocelot_init_port(struct ocelot *ocelot, int port);
void ocelot_deinit_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_assign_dsa_8021q_cpu(struct ocelot *ocelot, int port, int 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);
u32 ocelot_port_assigned_dsa_8021q_cpu_mask(struct ocelot *ocelot, int port);
/* DSA callbacks */ /* DSA callbacks */
void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data); 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); ...@@ -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, int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, bool enabled,
struct netlink_ext_ack *extack); struct netlink_ext_ack *extack);
void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state); 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); 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, int ocelot_port_pre_bridge_flags(struct ocelot *ocelot, int port,
struct switchdev_brport_flags val); struct switchdev_brport_flags val);
void ocelot_port_bridge_flags(struct ocelot *ocelot, int port, void ocelot_port_bridge_flags(struct ocelot *ocelot, int port,
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
/* Cookie definitions for private VCAP filters installed by the driver. /* Cookie definitions for private VCAP filters installed by the driver.
* Must be unique per VCAP block. * 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_IS1_TAG_8021Q_TXVLAN(ocelot, port) (port)
#define OCELOT_VCAP_IS2_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)) #define OCELOT_VCAP_IS2_MRP_REDIRECT(ocelot, port) ((ocelot)->num_phys_ports + (port))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment