The qca8xxx switch supports 2 way to write reg values, a slow way using mdio and a fast way by sending specially crafted mgmt packet to read/write reg.
The fast way can support up to 32 bytes of data as eth packet are used to send/receive.
This correctly works for almost the entire regmap of the switch but with the use of some kernel selftests for dsa drivers it was found a funny and interesting hw defect/limitation.
For some specific reg, bulk write won't work and will result in writing only part of the requested regs resulting in half data written. This was especially hard to track and discover due to the total strangeness of the problem and also by the specific regs where this occurs.
This occurs in the specific regs of the ATU table, where multiple entry needs to be written to compose the entire entry. It was discovered that with a bulk write of 12 bytes on QCA8K_REG_ATU_DATA0 only QCA8K_REG_ATU_DATA0 and QCA8K_REG_ATU_DATA2 were written, but QCA8K_REG_ATU_DATA1 was always zero. Tcpdump was used to make sure the specially crafted packet was correct and this was confirmed.
The problem was hard to track as the lack of QCA8K_REG_ATU_DATA1 resulted in an entry somehow possible as the first bytes of the mac address are set in QCA8K_REG_ATU_DATA0 and the entry type is set in QCA8K_REG_ATU_DATA2.
Funlly enough writing QCA8K_REG_ATU_DATA1 results in the same problem with QCA8K_REG_ATU_DATA2 empty and QCA8K_REG_ATU_DATA1 and QCA8K_REG_ATU_FUNC correctly written. A speculation on the problem might be that there are some kind of indirection internally when accessing these regs and they can't be accessed all together, due to the fact that it's really a table mapped somewhere in the switch SRAM.
Even more funny is the fact that every other reg was tested with all kind of combination and they are not affected by this problem. Read operation was also tested and always worked so it's not affected by this problem.
The problem is not present if we limit writing a single reg at times.
To handle this hardware defect, enable use_single_write so that bulk api can correctly split the write in multiple different operation effectively reverting to a non-bulk write.
Cc: Mark Brown broonie@kernel.org Fixes: c766e077d927 ("net: dsa: qca8k: convert to regmap read/write API") Signed-off-by: Christian Marangi ansuelsmth@gmail.com Cc: stable@vger.kernel.org --- drivers/net/dsa/qca/qca8k-8xxx.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c index dee7b6579916..ae088a4df794 100644 --- a/drivers/net/dsa/qca/qca8k-8xxx.c +++ b/drivers/net/dsa/qca/qca8k-8xxx.c @@ -576,8 +576,11 @@ static struct regmap_config qca8k_regmap_config = { .rd_table = &qca8k_readable_table, .disable_locking = true, /* Locking is handled by qca8k read/write */ .cache_type = REGCACHE_NONE, /* Explicitly disable CACHE */ - .max_raw_read = 32, /* mgmt eth can read/write up to 8 registers at time */ - .max_raw_write = 32, + .max_raw_read = 32, /* mgmt eth can read up to 8 registers at time */ + /* ATU regs suffer from a bug where some data are not correctly + * written. Disable bulk write to correctly write ATU entry. + */ + .use_single_write = true, };
static int
On inserting a mdb entry, fdb_search_and_insert is used to add a port to the qca8k target entry in the FDB db.
A FDB entry can't be modified so it needs to be removed and insert again with the new values.
To detect if an entry already exist, the SEARCH operation is used and we check the aging of the entry. If the entry is not 0, the entry exist and we proceed to delete it.
Current code have 2 main problem: - The condition to check if the FDB entry exist is wrong and should be the opposite. - When a FDB entry doesn't exist, aging was never actually set to the STATIC value resulting in allocating an invalid entry.
Fix both problem by adding aging support to the function, calling the function with STATIC as aging by default and finally by correct the condition to check if the entry actually exist.
Fixes: ba8f870dfa63 ("net: dsa: qca8k: add support for mdb_add/del") Signed-off-by: Christian Marangi ansuelsmth@gmail.com Cc: stable@vger.kernel.org --- drivers/net/dsa/qca/qca8k-common.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/drivers/net/dsa/qca/qca8k-common.c b/drivers/net/dsa/qca/qca8k-common.c index 8c2dc0e48ff4..4909603d07c8 100644 --- a/drivers/net/dsa/qca/qca8k-common.c +++ b/drivers/net/dsa/qca/qca8k-common.c @@ -244,7 +244,7 @@ void qca8k_fdb_flush(struct qca8k_priv *priv) }
static int qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask, - const u8 *mac, u16 vid) + const u8 *mac, u16 vid, u8 aging) { struct qca8k_fdb fdb = { 0 }; int ret; @@ -261,10 +261,12 @@ static int qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask, goto exit;
/* Rule exist. Delete first */ - if (!fdb.aging) { + if (fdb.aging) { ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); if (ret) goto exit; + } else { + fdb.aging = aging; }
/* Add port to fdb portmask */ @@ -810,7 +812,8 @@ int qca8k_port_mdb_add(struct dsa_switch *ds, int port, const u8 *addr = mdb->addr; u16 vid = mdb->vid;
- return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid); + return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid, + QCA8K_ATU_STATUS_STATIC); }
int qca8k_port_mdb_del(struct dsa_switch *ds, int port,
On deleting an MDB entry for a port, fdb_search_and_del is used. An FDB entry can't be modified so it needs to be deleted and readded again with the new portmap (and the port deleted as requested)
We use the SEARCH operator to search the entry to edit by vid and mac address and then we check the aging if we actually found an entry.
Currently the code suffer from a bug where the searched fdb entry is never read again with the found values (if found) resulting in the code always returning -EINVAL as aging was always 0.
Fix this by correctly read the fdb entry after it was searched.
Fixes: ba8f870dfa63 ("net: dsa: qca8k: add support for mdb_add/del") Signed-off-by: Christian Marangi ansuelsmth@gmail.com Cc: stable@vger.kernel.org --- drivers/net/dsa/qca/qca8k-common.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/drivers/net/dsa/qca/qca8k-common.c b/drivers/net/dsa/qca/qca8k-common.c index 4909603d07c8..b644c05337c5 100644 --- a/drivers/net/dsa/qca/qca8k-common.c +++ b/drivers/net/dsa/qca/qca8k-common.c @@ -293,6 +293,10 @@ static int qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask, if (ret < 0) goto exit;
+ ret = qca8k_fdb_read(priv, &fdb); + if (ret < 0) + goto exit; + /* Rule doesn't exist. Why delete? */ if (!fdb.aging) { ret = -EINVAL;
The qca8k switch doesn't support using 0 as VID and require a default VID to be always set. MDB add/del function doesn't currently handle this and are currently setting the default VID.
Fix this by correctly handling this corner case and internally use the default VID for VID 0 case.
Fixes: ba8f870dfa63 ("net: dsa: qca8k: add support for mdb_add/del") Signed-off-by: Christian Marangi ansuelsmth@gmail.com Cc: stable@vger.kernel.org --- drivers/net/dsa/qca/qca8k-common.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/drivers/net/dsa/qca/qca8k-common.c b/drivers/net/dsa/qca/qca8k-common.c index b644c05337c5..13b8452ce5b2 100644 --- a/drivers/net/dsa/qca/qca8k-common.c +++ b/drivers/net/dsa/qca/qca8k-common.c @@ -816,6 +816,9 @@ int qca8k_port_mdb_add(struct dsa_switch *ds, int port, const u8 *addr = mdb->addr; u16 vid = mdb->vid;
+ if (!vid) + vid = QCA8K_PORT_VID_DEF; + return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid, QCA8K_ATU_STATUS_STATIC); } @@ -828,6 +831,9 @@ int qca8k_port_mdb_del(struct dsa_switch *ds, int port, const u8 *addr = mdb->addr; u16 vid = mdb->vid;
+ if (!vid) + vid = QCA8K_PORT_VID_DEF; + return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid); }
Hello:
This series was applied to netdev/net.git (main) by David S. Miller davem@davemloft.net:
On Mon, 24 Jul 2023 05:25:28 +0200 you wrote:
The qca8xxx switch supports 2 way to write reg values, a slow way using mdio and a fast way by sending specially crafted mgmt packet to read/write reg.
The fast way can support up to 32 bytes of data as eth packet are used to send/receive.
[...]
Here is the summary with links: - [net,1/4] net: dsa: qca8k: enable use_single_write for qca8xxx https://git.kernel.org/netdev/net/c/2c39dd025da4 - [net,2/4] net: dsa: qca8k: fix search_and_insert wrong handling of new rule https://git.kernel.org/netdev/net/c/80248d416089 - [net,3/4] net: dsa: qca8k: fix broken search_and_del https://git.kernel.org/netdev/net/c/ae70dcb9d9ec - [net,4/4] net: dsa: qca8k: fix mdb add/del case with 0 VID https://git.kernel.org/netdev/net/c/dfd739f182b0
You are awesome, thank you!
linux-stable-mirror@lists.linaro.org