Skip to content

Commit 68bb466

Browse files
committed
Merge branch 'l2-multicast-forwarding-for-ocelot-switch'
Vladimir Oltean says: ==================== L2 multicast forwarding for Ocelot switch This series enables the mscc_ocelot switch to forward raw L2 (non-IP) mdb entries as configured by the bridge driver after this patch: https://patchwork.ozlabs.org/project/netdev/patch/20201028233831.610076-1-vladimir.oltean@nxp.com/ ==================== Link: https://lore.kernel.org/r/20201029022738.722794-1-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2 parents 0e761ac + e5d1f89 commit 68bb466

File tree

3 files changed

+125
-54
lines changed

3 files changed

+125
-54
lines changed

drivers/net/ethernet/mscc/ocelot.c

Lines changed: 101 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -958,52 +958,88 @@ static enum macaccess_entry_type ocelot_classify_mdb(const unsigned char *addr)
958958
return ENTRYTYPE_MACv4;
959959
if (addr[0] == 0x33 && addr[1] == 0x33)
960960
return ENTRYTYPE_MACv6;
961-
return ENTRYTYPE_NORMAL;
961+
return ENTRYTYPE_LOCKED;
962962
}
963963

964-
static int ocelot_mdb_get_pgid(struct ocelot *ocelot,
965-
enum macaccess_entry_type entry_type)
964+
static struct ocelot_pgid *ocelot_pgid_alloc(struct ocelot *ocelot, int index,
965+
unsigned long ports)
966966
{
967-
int pgid;
967+
struct ocelot_pgid *pgid;
968+
969+
pgid = kzalloc(sizeof(*pgid), GFP_KERNEL);
970+
if (!pgid)
971+
return ERR_PTR(-ENOMEM);
972+
973+
pgid->ports = ports;
974+
pgid->index = index;
975+
refcount_set(&pgid->refcount, 1);
976+
list_add_tail(&pgid->list, &ocelot->pgids);
977+
978+
return pgid;
979+
}
980+
981+
static void ocelot_pgid_free(struct ocelot *ocelot, struct ocelot_pgid *pgid)
982+
{
983+
if (!refcount_dec_and_test(&pgid->refcount))
984+
return;
985+
986+
list_del(&pgid->list);
987+
kfree(pgid);
988+
}
989+
990+
static struct ocelot_pgid *ocelot_mdb_get_pgid(struct ocelot *ocelot,
991+
const struct ocelot_multicast *mc)
992+
{
993+
struct ocelot_pgid *pgid;
994+
int index;
968995

969996
/* According to VSC7514 datasheet 3.9.1.5 IPv4 Multicast Entries and
970997
* 3.9.1.6 IPv6 Multicast Entries, "Instead of a lookup in the
971998
* destination mask table (PGID), the destination set is programmed as
972999
* part of the entry MAC address.", and the DEST_IDX is set to 0.
9731000
*/
974-
if (entry_type == ENTRYTYPE_MACv4 ||
975-
entry_type == ENTRYTYPE_MACv6)
976-
return 0;
1001+
if (mc->entry_type == ENTRYTYPE_MACv4 ||
1002+
mc->entry_type == ENTRYTYPE_MACv6)
1003+
return ocelot_pgid_alloc(ocelot, 0, mc->ports);
9771004

978-
for_each_nonreserved_multicast_dest_pgid(ocelot, pgid) {
979-
struct ocelot_multicast *mc;
1005+
list_for_each_entry(pgid, &ocelot->pgids, list) {
1006+
/* When searching for a nonreserved multicast PGID, ignore the
1007+
* dummy PGID of zero that we have for MACv4/MACv6 entries
1008+
*/
1009+
if (pgid->index && pgid->ports == mc->ports) {
1010+
refcount_inc(&pgid->refcount);
1011+
return pgid;
1012+
}
1013+
}
1014+
1015+
/* Search for a free index in the nonreserved multicast PGID area */
1016+
for_each_nonreserved_multicast_dest_pgid(ocelot, index) {
9801017
bool used = false;
9811018

982-
list_for_each_entry(mc, &ocelot->multicast, list) {
983-
if (mc->pgid == pgid) {
1019+
list_for_each_entry(pgid, &ocelot->pgids, list) {
1020+
if (pgid->index == index) {
9841021
used = true;
9851022
break;
9861023
}
9871024
}
9881025

9891026
if (!used)
990-
return pgid;
1027+
return ocelot_pgid_alloc(ocelot, index, mc->ports);
9911028
}
9921029

993-
return -1;
1030+
return ERR_PTR(-ENOSPC);
9941031
}
9951032

9961033
static void ocelot_encode_ports_to_mdb(unsigned char *addr,
997-
struct ocelot_multicast *mc,
998-
enum macaccess_entry_type entry_type)
1034+
struct ocelot_multicast *mc)
9991035
{
1000-
memcpy(addr, mc->addr, ETH_ALEN);
1036+
ether_addr_copy(addr, mc->addr);
10011037

1002-
if (entry_type == ENTRYTYPE_MACv4) {
1038+
if (mc->entry_type == ENTRYTYPE_MACv4) {
10031039
addr[0] = 0;
10041040
addr[1] = mc->ports >> 8;
10051041
addr[2] = mc->ports & 0xff;
1006-
} else if (entry_type == ENTRYTYPE_MACv6) {
1042+
} else if (mc->entry_type == ENTRYTYPE_MACv6) {
10071043
addr[0] = mc->ports >> 8;
10081044
addr[1] = mc->ports & 0xff;
10091045
}
@@ -1013,62 +1049,69 @@ int ocelot_port_mdb_add(struct ocelot *ocelot, int port,
10131049
const struct switchdev_obj_port_mdb *mdb)
10141050
{
10151051
struct ocelot_port *ocelot_port = ocelot->ports[port];
1016-
enum macaccess_entry_type entry_type;
10171052
unsigned char addr[ETH_ALEN];
10181053
struct ocelot_multicast *mc;
1054+
struct ocelot_pgid *pgid;
10191055
u16 vid = mdb->vid;
1020-
bool new = false;
10211056

10221057
if (port == ocelot->npi)
10231058
port = ocelot->num_phys_ports;
10241059

10251060
if (!vid)
10261061
vid = ocelot_port->pvid;
10271062

1028-
entry_type = ocelot_classify_mdb(mdb->addr);
1029-
10301063
mc = ocelot_multicast_get(ocelot, mdb->addr, vid);
10311064
if (!mc) {
1032-
int pgid = ocelot_mdb_get_pgid(ocelot, entry_type);
1033-
1034-
if (pgid < 0) {
1035-
dev_err(ocelot->dev,
1036-
"No more PGIDs available for mdb %pM vid %d\n",
1037-
mdb->addr, vid);
1038-
return -ENOSPC;
1039-
}
1040-
1065+
/* New entry */
10411066
mc = devm_kzalloc(ocelot->dev, sizeof(*mc), GFP_KERNEL);
10421067
if (!mc)
10431068
return -ENOMEM;
10441069

1045-
memcpy(mc->addr, mdb->addr, ETH_ALEN);
1070+
mc->entry_type = ocelot_classify_mdb(mdb->addr);
1071+
ether_addr_copy(mc->addr, mdb->addr);
10461072
mc->vid = vid;
1047-
mc->pgid = pgid;
10481073

10491074
list_add_tail(&mc->list, &ocelot->multicast);
1050-
new = true;
1051-
}
1052-
1053-
if (!new) {
1054-
ocelot_encode_ports_to_mdb(addr, mc, entry_type);
1075+
} else {
1076+
/* Existing entry. Clean up the current port mask from
1077+
* hardware now, because we'll be modifying it.
1078+
*/
1079+
ocelot_pgid_free(ocelot, mc->pgid);
1080+
ocelot_encode_ports_to_mdb(addr, mc);
10551081
ocelot_mact_forget(ocelot, addr, vid);
10561082
}
10571083

10581084
mc->ports |= BIT(port);
1059-
ocelot_encode_ports_to_mdb(addr, mc, entry_type);
10601085

1061-
return ocelot_mact_learn(ocelot, mc->pgid, addr, vid, entry_type);
1086+
pgid = ocelot_mdb_get_pgid(ocelot, mc);
1087+
if (IS_ERR(pgid)) {
1088+
dev_err(ocelot->dev,
1089+
"Cannot allocate PGID for mdb %pM vid %d\n",
1090+
mc->addr, mc->vid);
1091+
devm_kfree(ocelot->dev, mc);
1092+
return PTR_ERR(pgid);
1093+
}
1094+
mc->pgid = pgid;
1095+
1096+
ocelot_encode_ports_to_mdb(addr, mc);
1097+
1098+
if (mc->entry_type != ENTRYTYPE_MACv4 &&
1099+
mc->entry_type != ENTRYTYPE_MACv6)
1100+
ocelot_write_rix(ocelot, pgid->ports, ANA_PGID_PGID,
1101+
pgid->index);
1102+
1103+
return ocelot_mact_learn(ocelot, pgid->index, addr, vid,
1104+
mc->entry_type);
10621105
}
10631106
EXPORT_SYMBOL(ocelot_port_mdb_add);
10641107

10651108
int ocelot_port_mdb_del(struct ocelot *ocelot, int port,
10661109
const struct switchdev_obj_port_mdb *mdb)
10671110
{
10681111
struct ocelot_port *ocelot_port = ocelot->ports[port];
1069-
enum macaccess_entry_type entry_type;
10701112
unsigned char addr[ETH_ALEN];
10711113
struct ocelot_multicast *mc;
1114+
struct ocelot_pgid *pgid;
10721115
u16 vid = mdb->vid;
10731116

10741117
if (port == ocelot->npi)
@@ -1081,21 +1124,32 @@ int ocelot_port_mdb_del(struct ocelot *ocelot, int port,
10811124
if (!mc)
10821125
return -ENOENT;
10831126

1084-
entry_type = ocelot_classify_mdb(mdb->addr);
1085-
1086-
ocelot_encode_ports_to_mdb(addr, mc, entry_type);
1127+
ocelot_encode_ports_to_mdb(addr, mc);
10871128
ocelot_mact_forget(ocelot, addr, vid);
10881129

1130+
ocelot_pgid_free(ocelot, mc->pgid);
10891131
mc->ports &= ~BIT(port);
10901132
if (!mc->ports) {
10911133
list_del(&mc->list);
10921134
devm_kfree(ocelot->dev, mc);
10931135
return 0;
10941136
}
10951137

1096-
ocelot_encode_ports_to_mdb(addr, mc, entry_type);
1138+
/* We have a PGID with fewer ports now */
1139+
pgid = ocelot_mdb_get_pgid(ocelot, mc);
1140+
if (IS_ERR(pgid))
1141+
return PTR_ERR(pgid);
1142+
mc->pgid = pgid;
1143+
1144+
ocelot_encode_ports_to_mdb(addr, mc);
1145+
1146+
if (mc->entry_type != ENTRYTYPE_MACv4 &&
1147+
mc->entry_type != ENTRYTYPE_MACv6)
1148+
ocelot_write_rix(ocelot, pgid->ports, ANA_PGID_PGID,
1149+
pgid->index);
10971150

1098-
return ocelot_mact_learn(ocelot, mc->pgid, addr, vid, entry_type);
1151+
return ocelot_mact_learn(ocelot, pgid->index, addr, vid,
1152+
mc->entry_type);
10991153
}
11001154
EXPORT_SYMBOL(ocelot_port_mdb_del);
11011155

@@ -1453,6 +1507,7 @@ int ocelot_init(struct ocelot *ocelot)
14531507
return -ENOMEM;
14541508

14551509
INIT_LIST_HEAD(&ocelot->multicast);
1510+
INIT_LIST_HEAD(&ocelot->pgids);
14561511
ocelot_mact_init(ocelot);
14571512
ocelot_vlan_init(ocelot);
14581513
ocelot_vcap_init(ocelot);

drivers/net/ethernet/mscc/ocelot.h

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,6 @@ struct frame_info {
4141
u32 timestamp; /* rew_val */
4242
};
4343

44-
struct ocelot_multicast {
45-
struct list_head list;
46-
unsigned char addr[ETH_ALEN];
47-
u16 vid;
48-
u16 ports;
49-
int pgid;
50-
};
51-
5244
struct ocelot_port_tc {
5345
bool block_shared;
5446
unsigned long offload_cnt;
@@ -87,6 +79,29 @@ enum macaccess_entry_type {
8779
ENTRYTYPE_MACv6,
8880
};
8981

82+
/* A (PGID) port mask structure, encoding the 2^ocelot->num_phys_ports
83+
* possibilities of egress port masks for L2 multicast traffic.
84+
* For a switch with 9 user ports, there are 512 possible port masks, but the
85+
* hardware only has 46 individual PGIDs that it can forward multicast traffic
86+
* to. So we need a structure that maps the limited PGID indices to the port
87+
* destinations requested by the user for L2 multicast.
88+
*/
89+
struct ocelot_pgid {
90+
unsigned long ports;
91+
int index;
92+
refcount_t refcount;
93+
struct list_head list;
94+
};
95+
96+
struct ocelot_multicast {
97+
struct list_head list;
98+
enum macaccess_entry_type entry_type;
99+
unsigned char addr[ETH_ALEN];
100+
u16 vid;
101+
u16 ports;
102+
struct ocelot_pgid *pgid;
103+
};
104+
90105
int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid,
91106
bool is_static, void *data);
92107
int ocelot_mact_learn(struct ocelot *ocelot, int port,

include/soc/mscc/ocelot.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,7 @@ struct ocelot {
632632
u32 *lags;
633633

634634
struct list_head multicast;
635+
struct list_head pgids;
635636

636637
struct list_head dummy_rules;
637638
struct ocelot_vcap_block block[3];

0 commit comments

Comments
 (0)