| From 4eeaa3b4c45df748ad6a26081dbbafc7b40fd095 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Sun, 21 Jun 2020 14:45:59 +0300 |
| Subject: net: mscc: ocelot: fix encoding destination ports into multicast IPv4 |
| address |
| |
| From: Vladimir Oltean <vladimir.oltean@nxp.com> |
| |
| [ Upstream commit 0897ecf7532577bda3dbcb043ce046a96948889d ] |
| |
| The ocelot hardware designers have made some hacks to support multicast |
| IPv4 and IPv6 addresses. Normally, the MAC table matches on MAC |
| addresses and the destination ports are selected through the DEST_IDX |
| field of the respective MAC table entry. The DEST_IDX points to a Port |
| Group ID (PGID) which contains the bit mask of ports that frames should |
| be forwarded to. But there aren't a lot of PGIDs (only 80 or so) and |
| there are clearly many more IP multicast addresses than that, so it |
| doesn't scale to use this PGID mechanism, so something else was done. |
| Since the first portion of the MAC address is known, the hack they did |
| was to use a single PGID for _flooding_ unknown IPv4 multicast |
| (PGID_MCIPV4 == 62), but for known IP multicast, embed the destination |
| ports into the first 3 bytes of the MAC address recorded in the MAC |
| table. |
| |
| The VSC7514 datasheet explains it like this: |
| |
| 3.9.1.5 IPv4 Multicast Entries |
| |
| MAC table entries with the ENTRY_TYPE = 2 settings are interpreted |
| as IPv4 multicast entries. |
| IPv4 multicasts entries match IPv4 frames, which are classified to |
| the specified VID, and which have DMAC = 0x01005Exxxxxx, where |
| xxxxxx is the lower 24 bits of the MAC address in the entry. |
| Instead of a lookup in the destination mask table (PGID), the |
| destination set is programmed as part of the entry MAC address. This |
| is shown in the following table. |
| |
| Table 78: IPv4 Multicast Destination Mask |
| |
| Destination Ports Record Bit Field |
| --------------------------------------------- |
| Ports 10-0 MAC[34-24] |
| |
| Example: All IPv4 multicast frames in VLAN 12 with MAC 01005E112233 are |
| to be forwarded to ports 3, 8, and 9. This is done by inserting the |
| following entry in the MAC table entry: |
| VALID = 1 |
| VID = 12 |
| MAC = 0x000308112233 |
| ENTRY_TYPE = 2 |
| DEST_IDX = 0 |
| |
| But this procedure is not at all what's going on in the driver. In fact, |
| the code that embeds the ports into the MAC address looks like it hasn't |
| actually been tested. This patch applies the procedure described in the |
| datasheet. |
| |
| Since there are many other fixes to be made around multicast forwarding |
| until it works properly, there is no real reason for this patch to be |
| backported to stable trees, or considered a real fix of something that |
| should have worked. |
| |
| Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/net/ethernet/mscc/ocelot.c | 16 ++++++++-------- |
| 1 file changed, 8 insertions(+), 8 deletions(-) |
| |
| diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c |
| index 76dbf9ac8ad50..1eaefc0ff87e6 100644 |
| --- a/drivers/net/ethernet/mscc/ocelot.c |
| +++ b/drivers/net/ethernet/mscc/ocelot.c |
| @@ -1599,14 +1599,14 @@ static int ocelot_port_obj_add_mdb(struct net_device *dev, |
| addr[0] = 0; |
| |
| if (!new) { |
| - addr[2] = mc->ports << 0; |
| - addr[1] = mc->ports << 8; |
| + addr[1] = mc->ports >> 8; |
| + addr[2] = mc->ports & 0xff; |
| ocelot_mact_forget(ocelot, addr, vid); |
| } |
| |
| mc->ports |= BIT(port); |
| - addr[2] = mc->ports << 0; |
| - addr[1] = mc->ports << 8; |
| + addr[1] = mc->ports >> 8; |
| + addr[2] = mc->ports & 0xff; |
| |
| return ocelot_mact_learn(ocelot, 0, addr, vid, ENTRYTYPE_MACv4); |
| } |
| @@ -1630,9 +1630,9 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev, |
| return -ENOENT; |
| |
| memcpy(addr, mc->addr, ETH_ALEN); |
| - addr[2] = mc->ports << 0; |
| - addr[1] = mc->ports << 8; |
| addr[0] = 0; |
| + addr[1] = mc->ports >> 8; |
| + addr[2] = mc->ports & 0xff; |
| ocelot_mact_forget(ocelot, addr, vid); |
| |
| mc->ports &= ~BIT(port); |
| @@ -1642,8 +1642,8 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev, |
| return 0; |
| } |
| |
| - addr[2] = mc->ports << 0; |
| - addr[1] = mc->ports << 8; |
| + addr[1] = mc->ports >> 8; |
| + addr[2] = mc->ports & 0xff; |
| |
| return ocelot_mact_learn(ocelot, 0, addr, vid, ENTRYTYPE_MACv4); |
| } |
| -- |
| 2.25.1 |
| |