forked from Minki/linux
Merge branch 'sja1105-fdb-fixes'
Vladimir Oltean says: ==================== FDB fixes for NXP SJA1105 I have some upcoming patches that make heavy use of statically installed FDB entries, and when testing them on SJA1105P/Q/R/S and SJA1110, it became clear that these switches do not behave reliably at all. - On SJA1110, a static FDB entry cannot be installed at all - On SJA1105P/Q/R/S, it is very picky about the inner/outer VLAN type - Dynamically learned entries will make us not install static ones, or even if we do, they might not take effect Patch 5/6 has a conflict with net-next (sorry), the commit message of that patch describes how to deal with it. Thanks. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
cebb5103f0
@ -304,6 +304,15 @@ sja1105pqrs_common_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
|
|||||||
hostcmd = SJA1105_HOSTCMD_INVALIDATE;
|
hostcmd = SJA1105_HOSTCMD_INVALIDATE;
|
||||||
}
|
}
|
||||||
sja1105_packing(p, &hostcmd, 25, 23, size, op);
|
sja1105_packing(p, &hostcmd, 25, 23, size, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sja1105pqrs_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
|
||||||
|
enum packing_op op)
|
||||||
|
{
|
||||||
|
int entry_size = SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY;
|
||||||
|
|
||||||
|
sja1105pqrs_common_l2_lookup_cmd_packing(buf, cmd, op, entry_size);
|
||||||
|
|
||||||
/* Hack - The hardware takes the 'index' field within
|
/* Hack - The hardware takes the 'index' field within
|
||||||
* struct sja1105_l2_lookup_entry as the index on which this command
|
* struct sja1105_l2_lookup_entry as the index on which this command
|
||||||
@ -313,26 +322,18 @@ sja1105pqrs_common_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
|
|||||||
* such that our API doesn't need to ask for a full-blown entry
|
* such that our API doesn't need to ask for a full-blown entry
|
||||||
* structure when e.g. a delete is requested.
|
* structure when e.g. a delete is requested.
|
||||||
*/
|
*/
|
||||||
sja1105_packing(buf, &cmd->index, 15, 6,
|
sja1105_packing(buf, &cmd->index, 15, 6, entry_size, op);
|
||||||
SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY, op);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sja1105pqrs_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
|
|
||||||
enum packing_op op)
|
|
||||||
{
|
|
||||||
int size = SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY;
|
|
||||||
|
|
||||||
return sja1105pqrs_common_l2_lookup_cmd_packing(buf, cmd, op, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sja1110_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
|
sja1110_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
|
||||||
enum packing_op op)
|
enum packing_op op)
|
||||||
{
|
{
|
||||||
int size = SJA1110_SIZE_L2_LOOKUP_ENTRY;
|
int entry_size = SJA1110_SIZE_L2_LOOKUP_ENTRY;
|
||||||
|
|
||||||
return sja1105pqrs_common_l2_lookup_cmd_packing(buf, cmd, op, size);
|
sja1105pqrs_common_l2_lookup_cmd_packing(buf, cmd, op, entry_size);
|
||||||
|
|
||||||
|
sja1105_packing(buf, &cmd->index, 10, 1, entry_size, op);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The switch is so retarded that it makes our command/entry abstraction
|
/* The switch is so retarded that it makes our command/entry abstraction
|
||||||
|
@ -1318,10 +1318,11 @@ static int sja1105et_is_fdb_entry_in_bin(struct sja1105_private *priv, int bin,
|
|||||||
int sja1105et_fdb_add(struct dsa_switch *ds, int port,
|
int sja1105et_fdb_add(struct dsa_switch *ds, int port,
|
||||||
const unsigned char *addr, u16 vid)
|
const unsigned char *addr, u16 vid)
|
||||||
{
|
{
|
||||||
struct sja1105_l2_lookup_entry l2_lookup = {0};
|
struct sja1105_l2_lookup_entry l2_lookup = {0}, tmp;
|
||||||
struct sja1105_private *priv = ds->priv;
|
struct sja1105_private *priv = ds->priv;
|
||||||
struct device *dev = ds->dev;
|
struct device *dev = ds->dev;
|
||||||
int last_unused = -1;
|
int last_unused = -1;
|
||||||
|
int start, end, i;
|
||||||
int bin, way, rc;
|
int bin, way, rc;
|
||||||
|
|
||||||
bin = sja1105et_fdb_hash(priv, addr, vid);
|
bin = sja1105et_fdb_hash(priv, addr, vid);
|
||||||
@ -1333,7 +1334,7 @@ int sja1105et_fdb_add(struct dsa_switch *ds, int port,
|
|||||||
* mask? If yes, we need to do nothing. If not, we need
|
* mask? If yes, we need to do nothing. If not, we need
|
||||||
* to rewrite the entry by adding this port to it.
|
* to rewrite the entry by adding this port to it.
|
||||||
*/
|
*/
|
||||||
if (l2_lookup.destports & BIT(port))
|
if ((l2_lookup.destports & BIT(port)) && l2_lookup.lockeds)
|
||||||
return 0;
|
return 0;
|
||||||
l2_lookup.destports |= BIT(port);
|
l2_lookup.destports |= BIT(port);
|
||||||
} else {
|
} else {
|
||||||
@ -1364,6 +1365,7 @@ int sja1105et_fdb_add(struct dsa_switch *ds, int port,
|
|||||||
index, NULL, false);
|
index, NULL, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
l2_lookup.lockeds = true;
|
||||||
l2_lookup.index = sja1105et_fdb_index(bin, way);
|
l2_lookup.index = sja1105et_fdb_index(bin, way);
|
||||||
|
|
||||||
rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP,
|
rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP,
|
||||||
@ -1372,6 +1374,29 @@ int sja1105et_fdb_add(struct dsa_switch *ds, int port,
|
|||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
|
/* Invalidate a dynamically learned entry if that exists */
|
||||||
|
start = sja1105et_fdb_index(bin, 0);
|
||||||
|
end = sja1105et_fdb_index(bin, way);
|
||||||
|
|
||||||
|
for (i = start; i < end; i++) {
|
||||||
|
rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP,
|
||||||
|
i, &tmp);
|
||||||
|
if (rc == -ENOENT)
|
||||||
|
continue;
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
if (tmp.macaddr != ether_addr_to_u64(addr) || tmp.vlanid != vid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP,
|
||||||
|
i, NULL, false);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return sja1105_static_fdb_change(priv, port, &l2_lookup, true);
|
return sja1105_static_fdb_change(priv, port, &l2_lookup, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1413,32 +1438,30 @@ int sja1105et_fdb_del(struct dsa_switch *ds, int port,
|
|||||||
int sja1105pqrs_fdb_add(struct dsa_switch *ds, int port,
|
int sja1105pqrs_fdb_add(struct dsa_switch *ds, int port,
|
||||||
const unsigned char *addr, u16 vid)
|
const unsigned char *addr, u16 vid)
|
||||||
{
|
{
|
||||||
struct sja1105_l2_lookup_entry l2_lookup = {0};
|
struct sja1105_l2_lookup_entry l2_lookup = {0}, tmp;
|
||||||
struct sja1105_private *priv = ds->priv;
|
struct sja1105_private *priv = ds->priv;
|
||||||
int rc, i;
|
int rc, i;
|
||||||
|
|
||||||
/* Search for an existing entry in the FDB table */
|
/* Search for an existing entry in the FDB table */
|
||||||
l2_lookup.macaddr = ether_addr_to_u64(addr);
|
l2_lookup.macaddr = ether_addr_to_u64(addr);
|
||||||
l2_lookup.vlanid = vid;
|
l2_lookup.vlanid = vid;
|
||||||
l2_lookup.iotag = SJA1105_S_TAG;
|
|
||||||
l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0);
|
l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0);
|
||||||
if (priv->vlan_state != SJA1105_VLAN_UNAWARE) {
|
l2_lookup.mask_vlanid = VLAN_VID_MASK;
|
||||||
l2_lookup.mask_vlanid = VLAN_VID_MASK;
|
|
||||||
l2_lookup.mask_iotag = BIT(0);
|
|
||||||
} else {
|
|
||||||
l2_lookup.mask_vlanid = 0;
|
|
||||||
l2_lookup.mask_iotag = 0;
|
|
||||||
}
|
|
||||||
l2_lookup.destports = BIT(port);
|
l2_lookup.destports = BIT(port);
|
||||||
|
|
||||||
|
tmp = l2_lookup;
|
||||||
|
|
||||||
rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP,
|
rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP,
|
||||||
SJA1105_SEARCH, &l2_lookup);
|
SJA1105_SEARCH, &tmp);
|
||||||
if (rc == 0) {
|
if (rc == 0 && tmp.index != SJA1105_MAX_L2_LOOKUP_COUNT - 1) {
|
||||||
/* Found and this port is already in the entry's
|
/* Found a static entry and this port is already in the entry's
|
||||||
* port mask => job done
|
* port mask => job done
|
||||||
*/
|
*/
|
||||||
if (l2_lookup.destports & BIT(port))
|
if ((tmp.destports & BIT(port)) && tmp.lockeds)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
l2_lookup = tmp;
|
||||||
|
|
||||||
/* l2_lookup.index is populated by the switch in case it
|
/* l2_lookup.index is populated by the switch in case it
|
||||||
* found something.
|
* found something.
|
||||||
*/
|
*/
|
||||||
@ -1460,16 +1483,46 @@ int sja1105pqrs_fdb_add(struct dsa_switch *ds, int port,
|
|||||||
dev_err(ds->dev, "FDB is full, cannot add entry.\n");
|
dev_err(ds->dev, "FDB is full, cannot add entry.\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
l2_lookup.lockeds = true;
|
|
||||||
l2_lookup.index = i;
|
l2_lookup.index = i;
|
||||||
|
|
||||||
skip_finding_an_index:
|
skip_finding_an_index:
|
||||||
|
l2_lookup.lockeds = true;
|
||||||
|
|
||||||
rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP,
|
rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP,
|
||||||
l2_lookup.index, &l2_lookup,
|
l2_lookup.index, &l2_lookup,
|
||||||
true);
|
true);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
|
/* The switch learns dynamic entries and looks up the FDB left to
|
||||||
|
* right. It is possible that our addition was concurrent with the
|
||||||
|
* dynamic learning of the same address, so now that the static entry
|
||||||
|
* has been installed, we are certain that address learning for this
|
||||||
|
* particular address has been turned off, so the dynamic entry either
|
||||||
|
* is in the FDB at an index smaller than the static one, or isn't (it
|
||||||
|
* can also be at a larger index, but in that case it is inactive
|
||||||
|
* because the static FDB entry will match first, and the dynamic one
|
||||||
|
* will eventually age out). Search for a dynamically learned address
|
||||||
|
* prior to our static one and invalidate it.
|
||||||
|
*/
|
||||||
|
tmp = l2_lookup;
|
||||||
|
|
||||||
|
rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP,
|
||||||
|
SJA1105_SEARCH, &tmp);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(ds->dev,
|
||||||
|
"port %d failed to read back entry for %pM vid %d: %pe\n",
|
||||||
|
port, addr, vid, ERR_PTR(rc));
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmp.index < l2_lookup.index) {
|
||||||
|
rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP,
|
||||||
|
tmp.index, NULL, false);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
return sja1105_static_fdb_change(priv, port, &l2_lookup, true);
|
return sja1105_static_fdb_change(priv, port, &l2_lookup, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1483,15 +1536,8 @@ int sja1105pqrs_fdb_del(struct dsa_switch *ds, int port,
|
|||||||
|
|
||||||
l2_lookup.macaddr = ether_addr_to_u64(addr);
|
l2_lookup.macaddr = ether_addr_to_u64(addr);
|
||||||
l2_lookup.vlanid = vid;
|
l2_lookup.vlanid = vid;
|
||||||
l2_lookup.iotag = SJA1105_S_TAG;
|
|
||||||
l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0);
|
l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0);
|
||||||
if (priv->vlan_state != SJA1105_VLAN_UNAWARE) {
|
l2_lookup.mask_vlanid = VLAN_VID_MASK;
|
||||||
l2_lookup.mask_vlanid = VLAN_VID_MASK;
|
|
||||||
l2_lookup.mask_iotag = BIT(0);
|
|
||||||
} else {
|
|
||||||
l2_lookup.mask_vlanid = 0;
|
|
||||||
l2_lookup.mask_iotag = 0;
|
|
||||||
}
|
|
||||||
l2_lookup.destports = BIT(port);
|
l2_lookup.destports = BIT(port);
|
||||||
|
|
||||||
rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP,
|
rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP,
|
||||||
|
Loading…
Reference in New Issue
Block a user