Skip to content

Commit 4d94235

Browse files
vladimirolteandavem330
authored andcommitted
net: dsa: sja1105: offload bridge port flags to device
The chip can configure unicast flooding, broadcast flooding and learning. Learning is per port, while flooding is per {ingress, egress} port pair and we need to configure the same value for all possible ingress ports towards the requested one. While multicast flooding is not officially supported, we can hack it by using a feature of the second generation (P/Q/R/S) devices, which is that FDB entries are maskable, and multicast addresses always have an odd first octet. So by putting a match-all for 00:01:00:00:00:00 addr and 00:01:00:00:00:00 mask at the end of the FDB, we make sure that it is always checked last, and does not take precedence in front of any other MDB. So it behaves effectively as an unknown multicast entry. For the first generation switches, this feature is not available, so unknown multicast will always be treated the same as unknown unicast. So the only thing we can do is request the user to offload the settings for these 2 flags in tandem, i.e. ip link set swp2 type bridge_slave flood off Error: sja1105: This chip cannot configure multicast flooding independently of unicast. ip link set swp2 type bridge_slave flood off mcast_flood off ip link set swp2 type bridge_slave mcast_flood on Error: sja1105: This chip cannot configure multicast flooding independently of unicast. Signed-off-by: Vladimir Oltean <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 421741e commit 4d94235

File tree

3 files changed

+219
-11
lines changed

3 files changed

+219
-11
lines changed

drivers/net/dsa/sja1105/sja1105.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ struct sja1105_info {
9494
* pop it when it's equal to TPID2.
9595
*/
9696
u16 qinq_tpid;
97+
bool can_limit_mcast_flood;
9798
int (*reset_cmd)(struct dsa_switch *ds);
9899
int (*setup_rgmii_delay)(const void *ctx, int port);
99100
/* Prototypes from include/net/dsa.h */
@@ -204,6 +205,7 @@ struct sja1105_private {
204205
bool rgmii_rx_delay[SJA1105_NUM_PORTS];
205206
bool rgmii_tx_delay[SJA1105_NUM_PORTS];
206207
bool best_effort_vlan_filtering;
208+
unsigned long learn_ena;
207209
const struct sja1105_info *info;
208210
struct gpio_desc *reset_gpio;
209211
struct spi_device *spidev;

drivers/net/dsa/sja1105/sja1105_main.c

Lines changed: 211 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include "sja1105_sgmii.h"
2626
#include "sja1105_tas.h"
2727

28+
#define SJA1105_UNKNOWN_MULTICAST 0x010000000000ull
29+
2830
static const struct dsa_switch_ops sja1105_switch_ops;
2931

3032
static void sja1105_hw_reset(struct gpio_desc *gpio, unsigned int pulse_len,
@@ -42,15 +44,10 @@ static void
4244
sja1105_port_allow_traffic(struct sja1105_l2_forwarding_entry *l2_fwd,
4345
int from, int to, bool allow)
4446
{
45-
if (allow) {
46-
l2_fwd[from].bc_domain |= BIT(to);
47+
if (allow)
4748
l2_fwd[from].reach_port |= BIT(to);
48-
l2_fwd[from].fl_domain |= BIT(to);
49-
} else {
50-
l2_fwd[from].bc_domain &= ~BIT(to);
49+
else
5150
l2_fwd[from].reach_port &= ~BIT(to);
52-
l2_fwd[from].fl_domain &= ~BIT(to);
53-
}
5451
}
5552

5653
/* Structure used to temporarily transport device tree
@@ -220,17 +217,43 @@ static int sja1105_init_mii_settings(struct sja1105_private *priv,
220217

221218
static int sja1105_init_static_fdb(struct sja1105_private *priv)
222219
{
220+
struct sja1105_l2_lookup_entry *l2_lookup;
223221
struct sja1105_table *table;
222+
int port;
224223

225224
table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP];
226225

227-
/* We only populate the FDB table through dynamic
228-
* L2 Address Lookup entries
226+
/* We only populate the FDB table through dynamic L2 Address Lookup
227+
* entries, except for a special entry at the end which is a catch-all
228+
* for unknown multicast and will be used to control flooding domain.
229229
*/
230230
if (table->entry_count) {
231231
kfree(table->entries);
232232
table->entry_count = 0;
233233
}
234+
235+
if (!priv->info->can_limit_mcast_flood)
236+
return 0;
237+
238+
table->entries = kcalloc(1, table->ops->unpacked_entry_size,
239+
GFP_KERNEL);
240+
if (!table->entries)
241+
return -ENOMEM;
242+
243+
table->entry_count = 1;
244+
l2_lookup = table->entries;
245+
246+
/* All L2 multicast addresses have an odd first octet */
247+
l2_lookup[0].macaddr = SJA1105_UNKNOWN_MULTICAST;
248+
l2_lookup[0].mask_macaddr = SJA1105_UNKNOWN_MULTICAST;
249+
l2_lookup[0].lockeds = true;
250+
l2_lookup[0].index = SJA1105_MAX_L2_LOOKUP_COUNT - 1;
251+
252+
/* Flood multicast to every port by default */
253+
for (port = 0; port < priv->ds->num_ports; port++)
254+
if (!dsa_is_unused_port(priv->ds, port))
255+
l2_lookup[0].destports |= BIT(port);
256+
234257
return 0;
235258
}
236259

@@ -390,6 +413,12 @@ static int sja1105_init_l2_forwarding(struct sja1105_private *priv)
390413

391414
sja1105_port_allow_traffic(l2fwd, i, upstream, true);
392415
sja1105_port_allow_traffic(l2fwd, upstream, i, true);
416+
417+
l2fwd[i].bc_domain = BIT(upstream);
418+
l2fwd[i].fl_domain = BIT(upstream);
419+
420+
l2fwd[upstream].bc_domain |= BIT(i);
421+
l2fwd[upstream].fl_domain |= BIT(i);
393422
}
394423
/* Next 8 entries define VLAN PCP mapping from ingress to egress.
395424
* Create a one-to-one mapping.
@@ -1514,6 +1543,12 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port,
15141543
*/
15151544
if (!(l2_lookup.destports & BIT(port)))
15161545
continue;
1546+
1547+
/* We need to hide the FDB entry for unknown multicast */
1548+
if (l2_lookup.macaddr == SJA1105_UNKNOWN_MULTICAST &&
1549+
l2_lookup.mask_macaddr == SJA1105_UNKNOWN_MULTICAST)
1550+
continue;
1551+
15171552
u64_to_ether_addr(l2_lookup.macaddr, macaddr);
15181553

15191554
/* We need to hide the dsa_8021q VLANs from the user. */
@@ -1605,12 +1640,12 @@ static void sja1105_bridge_stp_state_set(struct dsa_switch *ds, int port,
16051640
case BR_STATE_LEARNING:
16061641
mac[port].ingress = true;
16071642
mac[port].egress = false;
1608-
mac[port].dyn_learn = true;
1643+
mac[port].dyn_learn = !!(priv->learn_ena & BIT(port));
16091644
break;
16101645
case BR_STATE_FORWARDING:
16111646
mac[port].ingress = true;
16121647
mac[port].egress = true;
1613-
mac[port].dyn_learn = true;
1648+
mac[port].dyn_learn = !!(priv->learn_ena & BIT(port));
16141649
break;
16151650
default:
16161651
dev_err(ds->dev, "invalid STP state: %d\n", state);
@@ -3239,6 +3274,169 @@ static void sja1105_port_policer_del(struct dsa_switch *ds, int port)
32393274
sja1105_static_config_reload(priv, SJA1105_BEST_EFFORT_POLICING);
32403275
}
32413276

3277+
static int sja1105_port_set_learning(struct sja1105_private *priv, int port,
3278+
bool enabled)
3279+
{
3280+
struct sja1105_mac_config_entry *mac;
3281+
int rc;
3282+
3283+
mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries;
3284+
3285+
mac[port].dyn_learn = !!(priv->learn_ena & BIT(port));
3286+
3287+
rc = sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port,
3288+
&mac[port], true);
3289+
if (rc)
3290+
return rc;
3291+
3292+
if (enabled)
3293+
priv->learn_ena |= BIT(port);
3294+
else
3295+
priv->learn_ena &= ~BIT(port);
3296+
3297+
return 0;
3298+
}
3299+
3300+
/* Common function for unicast and broadcast flood configuration.
3301+
* Flooding is configured between each {ingress, egress} port pair, and since
3302+
* the bridge's semantics are those of "egress flooding", it means we must
3303+
* enable flooding towards this port from all ingress ports that are in the
3304+
* same bridge. In practice, we just enable flooding from all possible ingress
3305+
* ports regardless of whether they're in the same bridge or not, since the
3306+
* reach_port configuration will not allow flooded frames to leak across
3307+
* bridging domains anyway.
3308+
*/
3309+
static int sja1105_port_ucast_bcast_flood(struct sja1105_private *priv, int to,
3310+
struct switchdev_brport_flags flags)
3311+
{
3312+
struct sja1105_l2_forwarding_entry *l2_fwd;
3313+
int from, rc;
3314+
3315+
l2_fwd = priv->static_config.tables[BLK_IDX_L2_FORWARDING].entries;
3316+
3317+
for (from = 0; from < priv->ds->num_ports; from++) {
3318+
if (dsa_is_unused_port(priv->ds, from))
3319+
continue;
3320+
if (from == to)
3321+
continue;
3322+
3323+
/* Unicast */
3324+
if (flags.mask & BR_FLOOD) {
3325+
if (flags.val & BR_FLOOD)
3326+
l2_fwd[from].fl_domain |= BIT(to);
3327+
else
3328+
l2_fwd[from].fl_domain &= ~BIT(to);
3329+
}
3330+
/* Broadcast */
3331+
if (flags.mask & BR_BCAST_FLOOD) {
3332+
if (flags.val & BR_BCAST_FLOOD)
3333+
l2_fwd[from].bc_domain |= BIT(to);
3334+
else
3335+
l2_fwd[from].bc_domain &= ~BIT(to);
3336+
}
3337+
3338+
rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_FORWARDING,
3339+
from, &l2_fwd[from], true);
3340+
if (rc < 0)
3341+
return rc;
3342+
}
3343+
3344+
return 0;
3345+
}
3346+
3347+
static int sja1105_port_mcast_flood(struct sja1105_private *priv, int to,
3348+
struct switchdev_brport_flags flags,
3349+
struct netlink_ext_ack *extack)
3350+
{
3351+
struct sja1105_l2_lookup_entry *l2_lookup;
3352+
struct sja1105_table *table;
3353+
int match;
3354+
3355+
table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP];
3356+
l2_lookup = table->entries;
3357+
3358+
for (match = 0; match < table->entry_count; match++)
3359+
if (l2_lookup[match].macaddr == SJA1105_UNKNOWN_MULTICAST &&
3360+
l2_lookup[match].mask_macaddr == SJA1105_UNKNOWN_MULTICAST)
3361+
break;
3362+
3363+
if (match == table->entry_count) {
3364+
NL_SET_ERR_MSG_MOD(extack,
3365+
"Could not find FDB entry for unknown multicast");
3366+
return -ENOSPC;
3367+
}
3368+
3369+
if (flags.val & BR_MCAST_FLOOD)
3370+
l2_lookup[match].destports |= BIT(to);
3371+
else
3372+
l2_lookup[match].destports &= ~BIT(to);
3373+
3374+
return sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP,
3375+
l2_lookup[match].index,
3376+
&l2_lookup[match],
3377+
true);
3378+
}
3379+
3380+
static int sja1105_port_pre_bridge_flags(struct dsa_switch *ds, int port,
3381+
struct switchdev_brport_flags flags,
3382+
struct netlink_ext_ack *extack)
3383+
{
3384+
struct sja1105_private *priv = ds->priv;
3385+
3386+
if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD |
3387+
BR_BCAST_FLOOD))
3388+
return -EINVAL;
3389+
3390+
if (flags.mask & (BR_FLOOD | BR_MCAST_FLOOD) &&
3391+
!priv->info->can_limit_mcast_flood) {
3392+
bool multicast = !!(flags.val & BR_MCAST_FLOOD);
3393+
bool unicast = !!(flags.val & BR_FLOOD);
3394+
3395+
if (unicast != multicast) {
3396+
NL_SET_ERR_MSG_MOD(extack,
3397+
"This chip cannot configure multicast flooding independently of unicast");
3398+
return -EINVAL;
3399+
}
3400+
}
3401+
3402+
return 0;
3403+
}
3404+
3405+
static int sja1105_port_bridge_flags(struct dsa_switch *ds, int port,
3406+
struct switchdev_brport_flags flags,
3407+
struct netlink_ext_ack *extack)
3408+
{
3409+
struct sja1105_private *priv = ds->priv;
3410+
int rc;
3411+
3412+
if (flags.mask & BR_LEARNING) {
3413+
bool learn_ena = !!(flags.val & BR_LEARNING);
3414+
3415+
rc = sja1105_port_set_learning(priv, port, learn_ena);
3416+
if (rc)
3417+
return rc;
3418+
}
3419+
3420+
if (flags.mask & (BR_FLOOD | BR_BCAST_FLOOD)) {
3421+
rc = sja1105_port_ucast_bcast_flood(priv, port, flags);
3422+
if (rc)
3423+
return rc;
3424+
}
3425+
3426+
/* For chips that can't offload BR_MCAST_FLOOD independently, there
3427+
* is nothing to do here, we ensured the configuration is in sync by
3428+
* offloading BR_FLOOD.
3429+
*/
3430+
if (flags.mask & BR_MCAST_FLOOD && priv->info->can_limit_mcast_flood) {
3431+
rc = sja1105_port_mcast_flood(priv, port, flags,
3432+
extack);
3433+
if (rc)
3434+
return rc;
3435+
}
3436+
3437+
return 0;
3438+
}
3439+
32423440
static const struct dsa_switch_ops sja1105_switch_ops = {
32433441
.get_tag_protocol = sja1105_get_tag_protocol,
32443442
.setup = sja1105_setup,
@@ -3262,6 +3460,8 @@ static const struct dsa_switch_ops sja1105_switch_ops = {
32623460
.port_fdb_del = sja1105_fdb_del,
32633461
.port_bridge_join = sja1105_bridge_join,
32643462
.port_bridge_leave = sja1105_bridge_leave,
3463+
.port_pre_bridge_flags = sja1105_port_pre_bridge_flags,
3464+
.port_bridge_flags = sja1105_port_bridge_flags,
32653465
.port_stp_state_set = sja1105_bridge_stp_state_set,
32663466
.port_vlan_filtering = sja1105_vlan_filtering,
32673467
.port_vlan_add = sja1105_vlan_add,

drivers/net/dsa/sja1105/sja1105_spi.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,7 @@ const struct sja1105_info sja1105e_info = {
512512
.static_ops = sja1105e_table_ops,
513513
.dyn_ops = sja1105et_dyn_ops,
514514
.qinq_tpid = ETH_P_8021Q,
515+
.can_limit_mcast_flood = false,
515516
.ptp_ts_bits = 24,
516517
.ptpegr_ts_bytes = 4,
517518
.num_cbs_shapers = SJA1105ET_MAX_CBS_COUNT,
@@ -529,6 +530,7 @@ const struct sja1105_info sja1105t_info = {
529530
.static_ops = sja1105t_table_ops,
530531
.dyn_ops = sja1105et_dyn_ops,
531532
.qinq_tpid = ETH_P_8021Q,
533+
.can_limit_mcast_flood = false,
532534
.ptp_ts_bits = 24,
533535
.ptpegr_ts_bytes = 4,
534536
.num_cbs_shapers = SJA1105ET_MAX_CBS_COUNT,
@@ -546,6 +548,7 @@ const struct sja1105_info sja1105p_info = {
546548
.static_ops = sja1105p_table_ops,
547549
.dyn_ops = sja1105pqrs_dyn_ops,
548550
.qinq_tpid = ETH_P_8021AD,
551+
.can_limit_mcast_flood = true,
549552
.ptp_ts_bits = 32,
550553
.ptpegr_ts_bytes = 8,
551554
.num_cbs_shapers = SJA1105PQRS_MAX_CBS_COUNT,
@@ -564,6 +567,7 @@ const struct sja1105_info sja1105q_info = {
564567
.static_ops = sja1105q_table_ops,
565568
.dyn_ops = sja1105pqrs_dyn_ops,
566569
.qinq_tpid = ETH_P_8021AD,
570+
.can_limit_mcast_flood = true,
567571
.ptp_ts_bits = 32,
568572
.ptpegr_ts_bytes = 8,
569573
.num_cbs_shapers = SJA1105PQRS_MAX_CBS_COUNT,
@@ -582,6 +586,7 @@ const struct sja1105_info sja1105r_info = {
582586
.static_ops = sja1105r_table_ops,
583587
.dyn_ops = sja1105pqrs_dyn_ops,
584588
.qinq_tpid = ETH_P_8021AD,
589+
.can_limit_mcast_flood = true,
585590
.ptp_ts_bits = 32,
586591
.ptpegr_ts_bytes = 8,
587592
.num_cbs_shapers = SJA1105PQRS_MAX_CBS_COUNT,
@@ -601,6 +606,7 @@ const struct sja1105_info sja1105s_info = {
601606
.dyn_ops = sja1105pqrs_dyn_ops,
602607
.regs = &sja1105pqrs_regs,
603608
.qinq_tpid = ETH_P_8021AD,
609+
.can_limit_mcast_flood = true,
604610
.ptp_ts_bits = 32,
605611
.ptpegr_ts_bytes = 8,
606612
.num_cbs_shapers = SJA1105PQRS_MAX_CBS_COUNT,

0 commit comments

Comments
 (0)