Skip to content

Commit 7cef293

Browse files
vladimirolteandavem330
authored andcommitted
net: dsa: sja1105: fix multicast forwarding working only for last added mdb entry
The commit cited in Fixes: did 2 things: it refactored the read-back polling from sja1105_dynamic_config_read() into a new function, sja1105_dynamic_config_wait_complete(), and it called that from sja1105_dynamic_config_write() too. What is problematic is the refactoring. The refactored code from sja1105_dynamic_config_poll_valid() works like the previous one, but the problem is that it uses another packed_buf[] SPI buffer, and there was code at the end of sja1105_dynamic_config_read() which was relying on the read-back packed_buf[]: /* Don't dereference possibly NULL pointer - maybe caller * only wanted to see whether the entry existed or not. */ if (entry) ops->entry_packing(packed_buf, entry, UNPACK); After the change, the packed_buf[] that this code sees is no longer the entry read back from hardware, but the original entry that the caller passed to the sja1105_dynamic_config_read(), packed into this buffer. This difference is the most notable with the SJA1105_SEARCH uses from sja1105pqrs_fdb_add() - used for both fdb and mdb. There, we have logic added by commit 728db84 ("net: dsa: sja1105: ignore the FDB entry for unknown multicast when adding a new address") to figure out whether the address we're trying to add matches on any existing hardware entry, with the exception of the catch-all multicast address. That logic was broken, because with sja1105_dynamic_config_read() not working properly, it doesn't return us the entry read back from hardware, but the entry that we passed to it. And, since for multicast, a match will always exist, it will tell us that any mdb entry already exists at index=0 L2 Address Lookup table. It is index=0 because the caller doesn't know the index - it wants to find it out, and sja1105_dynamic_config_read() does: if (index < 0) { // SJA1105_SEARCH /* Avoid copying a signed negative number to an u64 */ cmd.index = 0; // <- this cmd.search = true; } else { cmd.index = index; cmd.search = false; } So, to the caller of sja1105_dynamic_config_read(), the returned info looks entirely legit, and it will add all mdb entries to FDB index 0. There, they will always overwrite each other (not to mention, potentially they can also overwrite a pre-existing bridge fdb entry), and the user-visible impact will be that only the last mdb entry will be forwarded as it should. The others won't (will be flooded or dropped, depending on the egress flood settings). Fixing is a bit more complicated, and involves either passing the same packed_buf[] to sja1105_dynamic_config_wait_complete(), or moving all the extra processing on the packed_buf[] to sja1105_dynamic_config_wait_complete(). I've opted for the latter, because it makes sja1105_dynamic_config_wait_complete() a bit more self-contained. Fixes: df40591 ("net: dsa: sja1105: wait for dynamic config command completion on writes too") Reported-by: Yanan Yang <[email protected]> Signed-off-by: Vladimir Oltean <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent c956798 commit 7cef293

File tree

1 file changed

+37
-43
lines changed

1 file changed

+37
-43
lines changed

drivers/net/dsa/sja1105/sja1105_dynamic_config.c

Lines changed: 37 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,18 +1175,15 @@ const struct sja1105_dynamic_table_ops sja1110_dyn_ops[BLK_IDX_MAX_DYN] = {
11751175

11761176
static int
11771177
sja1105_dynamic_config_poll_valid(struct sja1105_private *priv,
1178-
struct sja1105_dyn_cmd *cmd,
1179-
const struct sja1105_dynamic_table_ops *ops)
1178+
const struct sja1105_dynamic_table_ops *ops,
1179+
void *entry, bool check_valident,
1180+
bool check_errors)
11801181
{
11811182
u8 packed_buf[SJA1105_MAX_DYN_CMD_SIZE] = {};
1183+
struct sja1105_dyn_cmd cmd = {};
11821184
int rc;
11831185

1184-
/* We don't _need_ to read the full entry, just the command area which
1185-
* is a fixed SJA1105_SIZE_DYN_CMD. But our cmd_packing() API expects a
1186-
* buffer that contains the full entry too. Additionally, our API
1187-
* doesn't really know how many bytes into the buffer does the command
1188-
* area really begin. So just read back the whole entry.
1189-
*/
1186+
/* Read back the whole entry + command structure. */
11901187
rc = sja1105_xfer_buf(priv, SPI_READ, ops->addr, packed_buf,
11911188
ops->packed_size);
11921189
if (rc)
@@ -1195,11 +1192,25 @@ sja1105_dynamic_config_poll_valid(struct sja1105_private *priv,
11951192
/* Unpack the command structure, and return it to the caller in case it
11961193
* needs to perform further checks on it (VALIDENT).
11971194
*/
1198-
memset(cmd, 0, sizeof(*cmd));
1199-
ops->cmd_packing(packed_buf, cmd, UNPACK);
1195+
ops->cmd_packing(packed_buf, &cmd, UNPACK);
12001196

12011197
/* Hardware hasn't cleared VALID => still working on it */
1202-
return cmd->valid ? -EAGAIN : 0;
1198+
if (cmd.valid)
1199+
return -EAGAIN;
1200+
1201+
if (check_valident && !cmd.valident && !(ops->access & OP_VALID_ANYWAY))
1202+
return -ENOENT;
1203+
1204+
if (check_errors && cmd.errors)
1205+
return -EINVAL;
1206+
1207+
/* Don't dereference possibly NULL pointer - maybe caller
1208+
* only wanted to see whether the entry existed or not.
1209+
*/
1210+
if (entry)
1211+
ops->entry_packing(packed_buf, entry, UNPACK);
1212+
1213+
return 0;
12031214
}
12041215

12051216
/* Poll the dynamic config entry's control area until the hardware has
@@ -1208,16 +1219,18 @@ sja1105_dynamic_config_poll_valid(struct sja1105_private *priv,
12081219
*/
12091220
static int
12101221
sja1105_dynamic_config_wait_complete(struct sja1105_private *priv,
1211-
struct sja1105_dyn_cmd *cmd,
1212-
const struct sja1105_dynamic_table_ops *ops)
1222+
const struct sja1105_dynamic_table_ops *ops,
1223+
void *entry, bool check_valident,
1224+
bool check_errors)
12131225
{
12141226
int err, rc;
12151227

12161228
err = read_poll_timeout(sja1105_dynamic_config_poll_valid,
12171229
rc, rc != -EAGAIN,
12181230
SJA1105_DYNAMIC_CONFIG_SLEEP_US,
12191231
SJA1105_DYNAMIC_CONFIG_TIMEOUT_US,
1220-
false, priv, cmd, ops);
1232+
false, priv, ops, entry, check_valident,
1233+
check_errors);
12211234
return err < 0 ? err : rc;
12221235
}
12231236

@@ -1287,25 +1300,14 @@ int sja1105_dynamic_config_read(struct sja1105_private *priv,
12871300
mutex_lock(&priv->dynamic_config_lock);
12881301
rc = sja1105_xfer_buf(priv, SPI_WRITE, ops->addr, packed_buf,
12891302
ops->packed_size);
1290-
if (rc < 0) {
1291-
mutex_unlock(&priv->dynamic_config_lock);
1292-
return rc;
1293-
}
1294-
1295-
rc = sja1105_dynamic_config_wait_complete(priv, &cmd, ops);
1296-
mutex_unlock(&priv->dynamic_config_lock);
12971303
if (rc < 0)
1298-
return rc;
1304+
goto out;
12991305

1300-
if (!cmd.valident && !(ops->access & OP_VALID_ANYWAY))
1301-
return -ENOENT;
1306+
rc = sja1105_dynamic_config_wait_complete(priv, ops, entry, true, false);
1307+
out:
1308+
mutex_unlock(&priv->dynamic_config_lock);
13021309

1303-
/* Don't dereference possibly NULL pointer - maybe caller
1304-
* only wanted to see whether the entry existed or not.
1305-
*/
1306-
if (entry)
1307-
ops->entry_packing(packed_buf, entry, UNPACK);
1308-
return 0;
1310+
return rc;
13091311
}
13101312

13111313
int sja1105_dynamic_config_write(struct sja1105_private *priv,
@@ -1357,22 +1359,14 @@ int sja1105_dynamic_config_write(struct sja1105_private *priv,
13571359
mutex_lock(&priv->dynamic_config_lock);
13581360
rc = sja1105_xfer_buf(priv, SPI_WRITE, ops->addr, packed_buf,
13591361
ops->packed_size);
1360-
if (rc < 0) {
1361-
mutex_unlock(&priv->dynamic_config_lock);
1362-
return rc;
1363-
}
1364-
1365-
rc = sja1105_dynamic_config_wait_complete(priv, &cmd, ops);
1366-
mutex_unlock(&priv->dynamic_config_lock);
13671362
if (rc < 0)
1368-
return rc;
1363+
goto out;
13691364

1370-
cmd = (struct sja1105_dyn_cmd) {0};
1371-
ops->cmd_packing(packed_buf, &cmd, UNPACK);
1372-
if (cmd.errors)
1373-
return -EINVAL;
1365+
rc = sja1105_dynamic_config_wait_complete(priv, ops, NULL, false, true);
1366+
out:
1367+
mutex_unlock(&priv->dynamic_config_lock);
13741368

1375-
return 0;
1369+
return rc;
13761370
}
13771371

13781372
static u8 sja1105_crc8_add(u8 crc, u8 byte, u8 poly)

0 commit comments

Comments
 (0)