Commit f7571cde authored by David S. Miller's avatar David S. Miller

Merge branch 'net-dsa-mv88e6xxx-avoid-some-redundant-VTU-operations'

Vivien Didelot says:

====================
net: dsa: mv88e6xxx: avoid some redundant VTU operations

The mv88e6xxx driver currently uses a mv88e6xxx_vtu_get wrapper to get a
single entry and uses a boolean to eventually initialize a fresh one.

However the fresh entry is only needed in one place and mv88e6xxx_vtu_getnext
is simple enough to call it directly. Doing so makes the code easier to read,
especially for the return code expected by switchdev to honor software VLANs.

In addition to not loading the VTU again when an entry is already correctly
programmed, this also allows to avoid programming the broadcast entries
again when updating a port's membership, from e.g. tagged to untagged.

This patch series removes the mv88e6xxx_vtu_get wrapper in favor of direct
calls to mv88e6xxx_vtu_getnext, and also renames the _mv88e6xxx_port_vlan_add
and _mv88e6xxx_port_vlan_del helpers using an old underscore prefix convention.

In case the port's membership is already correctly programmed in hardware,
the following debug message may be printed:

    [  745.989884] mv88e6085 2188000.ethernet-1:00: p4: already a member of VLAN 42
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents a8e600e2 b1ac6fb4
......@@ -1361,9 +1361,7 @@ static int mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip,
static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid)
{
DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
struct mv88e6xxx_vtu_entry vlan = {
.vid = chip->info->max_vid,
};
struct mv88e6xxx_vtu_entry vlan;
int i, err;
bitmap_zero(fid_bitmap, MV88E6XXX_N_FID);
......@@ -1378,6 +1376,9 @@ static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid)
}
/* Set every FID bit used by the VLAN entries */
vlan.vid = chip->info->max_vid;
vlan.valid = false;
do {
err = mv88e6xxx_vtu_getnext(chip, &vlan);
if (err)
......@@ -1400,50 +1401,11 @@ static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid)
return mv88e6xxx_g1_atu_flush(chip, *fid, true);
}
static int mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid,
struct mv88e6xxx_vtu_entry *entry, bool new)
{
int err;
if (!vid)
return -EOPNOTSUPP;
entry->vid = vid - 1;
entry->valid = false;
err = mv88e6xxx_vtu_getnext(chip, entry);
if (err)
return err;
if (entry->vid == vid && entry->valid)
return 0;
if (new) {
int i;
/* Initialize a fresh VLAN entry */
memset(entry, 0, sizeof(*entry));
entry->vid = vid;
/* Exclude all ports */
for (i = 0; i < mv88e6xxx_num_ports(chip); ++i)
entry->member[i] =
MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER;
return mv88e6xxx_atu_new(chip, &entry->fid);
}
/* switchdev expects -EOPNOTSUPP to honor software VLANs */
return -EOPNOTSUPP;
}
static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
u16 vid_begin, u16 vid_end)
{
struct mv88e6xxx_chip *chip = ds->priv;
struct mv88e6xxx_vtu_entry vlan = {
.vid = vid_begin - 1,
};
struct mv88e6xxx_vtu_entry vlan;
int i, err;
/* DSA and CPU ports have to be members of multiple vlans */
......@@ -1453,12 +1415,13 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
if (!vid_begin)
return -EOPNOTSUPP;
mv88e6xxx_reg_lock(chip);
vlan.vid = vid_begin - 1;
vlan.valid = false;
do {
err = mv88e6xxx_vtu_getnext(chip, &vlan);
if (err)
goto unlock;
return err;
if (!vlan.valid)
break;
......@@ -1487,15 +1450,11 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
dev_err(ds->dev, "p%d: hw VLAN %d already used by port %d in %s\n",
port, vlan.vid, i,
netdev_name(dsa_to_port(ds, i)->bridge_dev));
err = -EOPNOTSUPP;
goto unlock;
return -EOPNOTSUPP;
}
} while (vlan.vid < vid_end);
unlock:
mv88e6xxx_reg_unlock(chip);
return err;
return 0;
}
static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
......@@ -1529,38 +1488,51 @@ mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
/* If the requested port doesn't belong to the same bridge as the VLAN
* members, do not support it (yet) and fallback to software VLAN.
*/
mv88e6xxx_reg_lock(chip);
err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin,
vlan->vid_end);
if (err)
return err;
mv88e6xxx_reg_unlock(chip);
/* We don't need any dynamic resource from the kernel (yet),
* so skip the prepare phase.
*/
return 0;
return err;
}
static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
const unsigned char *addr, u16 vid,
u8 state)
{
struct mv88e6xxx_vtu_entry vlan;
struct mv88e6xxx_atu_entry entry;
struct mv88e6xxx_vtu_entry vlan;
u16 fid;
int err;
/* Null VLAN ID corresponds to the port private database */
if (vid == 0)
err = mv88e6xxx_port_get_fid(chip, port, &vlan.fid);
else
err = mv88e6xxx_vtu_get(chip, vid, &vlan, false);
if (vid == 0) {
err = mv88e6xxx_port_get_fid(chip, port, &fid);
if (err)
return err;
} else {
vlan.vid = vid - 1;
vlan.valid = false;
err = mv88e6xxx_vtu_getnext(chip, &vlan);
if (err)
return err;
/* switchdev expects -EOPNOTSUPP to honor software VLANs */
if (vlan.vid != vid || !vlan.valid)
return -EOPNOTSUPP;
fid = vlan.fid;
}
entry.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED;
ether_addr_copy(entry.mac, addr);
eth_addr_dec(entry.mac);
err = mv88e6xxx_g1_atu_getnext(chip, vlan.fid, &entry);
err = mv88e6xxx_g1_atu_getnext(chip, fid, &entry);
if (err)
return err;
......@@ -1581,7 +1553,7 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
entry.state = state;
}
return mv88e6xxx_g1_atu_loadpurge(chip, vlan.fid, &entry);
return mv88e6xxx_g1_atu_loadpurge(chip, fid, &entry);
}
static int mv88e6xxx_port_add_broadcast(struct mv88e6xxx_chip *chip, int port,
......@@ -1607,26 +1579,58 @@ static int mv88e6xxx_broadcast_setup(struct mv88e6xxx_chip *chip, u16 vid)
return 0;
}
static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port,
static int mv88e6xxx_port_vlan_join(struct mv88e6xxx_chip *chip, int port,
u16 vid, u8 member)
{
const u8 non_member = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER;
struct mv88e6xxx_vtu_entry vlan;
int err;
int i, err;
if (!vid)
return -EOPNOTSUPP;
vlan.vid = vid - 1;
vlan.valid = false;
err = mv88e6xxx_vtu_get(chip, vid, &vlan, true);
err = mv88e6xxx_vtu_getnext(chip, &vlan);
if (err)
return err;
if (vlan.valid && vlan.member[port] == member)
return 0;
if (vlan.vid != vid || !vlan.valid) {
memset(&vlan, 0, sizeof(vlan));
err = mv88e6xxx_atu_new(chip, &vlan.fid);
if (err)
return err;
for (i = 0; i < mv88e6xxx_num_ports(chip); ++i)
if (i == port)
vlan.member[i] = member;
else
vlan.member[i] = non_member;
vlan.vid = vid;
vlan.valid = true;
err = mv88e6xxx_vtu_loadpurge(chip, &vlan);
if (err)
return err;
err = mv88e6xxx_broadcast_setup(chip, vlan.vid);
if (err)
return err;
} else if (vlan.member[port] != member) {
vlan.member[port] = member;
err = mv88e6xxx_vtu_loadpurge(chip, &vlan);
if (err)
return err;
} else {
dev_info(chip->dev, "p%d: already a member of VLAN %d\n",
port, vid);
}
return mv88e6xxx_broadcast_setup(chip, vid);
return 0;
}
static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
......@@ -1651,7 +1655,7 @@ static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
mv88e6xxx_reg_lock(chip);
for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
if (_mv88e6xxx_port_vlan_add(chip, port, vid, member))
if (mv88e6xxx_port_vlan_join(chip, port, vid, member))
dev_err(ds->dev, "p%d: failed to add VLAN %d%c\n", port,
vid, untagged ? 'u' : 't');
......@@ -1662,18 +1666,27 @@ static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
mv88e6xxx_reg_unlock(chip);
}
static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_chip *chip,
static int mv88e6xxx_port_vlan_leave(struct mv88e6xxx_chip *chip,
int port, u16 vid)
{
struct mv88e6xxx_vtu_entry vlan;
int i, err;
err = mv88e6xxx_vtu_get(chip, vid, &vlan, false);
if (!vid)
return -EOPNOTSUPP;
vlan.vid = vid - 1;
vlan.valid = false;
err = mv88e6xxx_vtu_getnext(chip, &vlan);
if (err)
return err;
/* Tell switchdev if this VLAN is handled in software */
if (vlan.member[port] == MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER)
/* If the VLAN doesn't exist in hardware or the port isn't a member,
* tell switchdev that this VLAN is likely handled in software.
*/
if (vlan.vid != vid || !vlan.valid ||
vlan.member[port] == MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER)
return -EOPNOTSUPP;
vlan.member[port] = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER;
......@@ -1712,7 +1725,7 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
goto unlock;
for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
err = _mv88e6xxx_port_vlan_del(chip, port, vid);
err = mv88e6xxx_port_vlan_leave(chip, port, vid);
if (err)
goto unlock;
......@@ -1795,9 +1808,7 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip,
static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
dsa_fdb_dump_cb_t *cb, void *data)
{
struct mv88e6xxx_vtu_entry vlan = {
.vid = chip->info->max_vid,
};
struct mv88e6xxx_vtu_entry vlan;
u16 fid;
int err;
......@@ -1811,6 +1822,9 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
return err;
/* Dump VLANs' Filtering Information Databases */
vlan.vid = chip->info->max_vid;
vlan.valid = false;
do {
err = mv88e6xxx_vtu_getnext(chip, &vlan);
if (err)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment