Skip to content

Commit

Permalink
Merge branch 'mv88e6xxx-switchdev-fdb'
Browse files Browse the repository at this point in the history
Vivien Didelot says:

====================
net: dsa: mv88e6xxx: support switchdev FDB objects

This patchset refactors the DSA and mv88e6xxx code to use the switchdev FDB
objects.

The first two patches add minor but necessary changes to switchdev, the third
one implements the switchdev glue in DSA for FDB routines, and the remaining
ones refactor the FDB access functions in the mv88e6xxx code.

Below is an usage example (ports 0-2 belongs to br0, ports 3-4 belongs to br1):

    # bridge fdb add 3c:97:0e:11:30:6e dev swp2
    # bridge fdb add 3c:97:0e:11:40:78 dev swp3
    # bridge fdb add 3c:97:0e:11:50:86 dev swp4
    # bridge fdb del 3c:97:0e:11:40:78 dev swp3
    # bridge fdb
    01:00:5e:00:00:01 dev eth0 self permanent
    01:00:5e:00:00:01 dev eth1 self permanent
    00:50:d2:10:78:15 dev swp0 master br0 permanent
    3c:97:0e:11:30:6e dev swp2 self static
    00:50:d2:10:78:15 dev swp3 master br1 permanent
    3c:97:0e:11:50:86 dev swp4 self static
    # cat /sys/kernel/debug/dsa0/atu
    # DB   T/P  Vec State Addr
    # 001  Port 004   e   3c:97:0e:11:30:6e
    # 004  Port 010   e   3c:97:0e:11:50:86

For the 88E6xxx switches, FIDs 1 to num_ports will be reserved for non-bridged
ports and bridge groups, and the remaining will be later used by VLANs.

This change is necessary to welcome the support for hardware VLANs (which will
follow soon).

Changes in v2:

 - remove ndo_bridge_{get,set,del}link from switchdev/DSA glue code

 - use ether_addr_copy instead of memcpy for MAC addresses

 - constify MAC address in port_fdb_{add,del}

 - split the mv88e6xxx code refactoring into several patches
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Aug 10, 2015
2 parents 4933d85 + 8782051 commit f1d5ca4
Show file tree
Hide file tree
Showing 10 changed files with 317 additions and 197 deletions.
6 changes: 3 additions & 3 deletions drivers/net/dsa/mv88e6171.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,9 @@ struct dsa_switch_driver mv88e6171_switch_driver = {
.port_join_bridge = mv88e6xxx_join_bridge,
.port_leave_bridge = mv88e6xxx_leave_bridge,
.port_stp_update = mv88e6xxx_port_stp_update,
.fdb_add = mv88e6xxx_port_fdb_add,
.fdb_del = mv88e6xxx_port_fdb_del,
.fdb_getnext = mv88e6xxx_port_fdb_getnext,
.port_fdb_add = mv88e6xxx_port_fdb_add,
.port_fdb_del = mv88e6xxx_port_fdb_del,
.port_fdb_getnext = mv88e6xxx_port_fdb_getnext,
};

MODULE_ALIAS("platform:mv88e6171");
Expand Down
6 changes: 3 additions & 3 deletions drivers/net/dsa/mv88e6352.c
Original file line number Diff line number Diff line change
Expand Up @@ -341,9 +341,9 @@ struct dsa_switch_driver mv88e6352_switch_driver = {
.port_join_bridge = mv88e6xxx_join_bridge,
.port_leave_bridge = mv88e6xxx_leave_bridge,
.port_stp_update = mv88e6xxx_port_stp_update,
.fdb_add = mv88e6xxx_port_fdb_add,
.fdb_del = mv88e6xxx_port_fdb_del,
.fdb_getnext = mv88e6xxx_port_fdb_getnext,
.port_fdb_add = mv88e6xxx_port_fdb_add,
.port_fdb_del = mv88e6xxx_port_fdb_del,
.port_fdb_getnext = mv88e6xxx_port_fdb_getnext,
};

MODULE_ALIAS("platform:mv88e6172");
Expand Down
223 changes: 155 additions & 68 deletions drivers/net/dsa/mv88e6xxx.c
Original file line number Diff line number Diff line change
Expand Up @@ -964,7 +964,7 @@ static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, int fid, u16 cmd)
{
int ret;

ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x01, fid);
ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID, fid);
if (ret < 0)
return ret;

Expand Down Expand Up @@ -1091,7 +1091,7 @@ int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask)
ps->bridge_mask[fid] = br_port_mask;

if (fid != ps->fid[port]) {
ps->fid_mask |= 1 << ps->fid[port];
clear_bit(ps->fid[port], ps->fid_bitmap);
ps->fid[port] = fid;
ret = _mv88e6xxx_update_bridge_config(ds, fid);
}
Expand Down Expand Up @@ -1125,16 +1125,24 @@ int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask)

mutex_lock(&ps->smi_mutex);

newfid = __ffs(ps->fid_mask);
newfid = find_next_zero_bit(ps->fid_bitmap, VLAN_N_VID, 1);
if (unlikely(newfid > ps->num_ports)) {
netdev_err(ds->ports[port], "all first %d FIDs are used\n",
ps->num_ports);
ret = -ENOSPC;
goto unlock;
}

ps->fid[port] = newfid;
ps->fid_mask &= ~(1 << newfid);
set_bit(newfid, ps->fid_bitmap);
ps->bridge_mask[fid] &= ~(1 << port);
ps->bridge_mask[newfid] = 1 << port;

ret = _mv88e6xxx_update_bridge_config(ds, fid);
if (!ret)
ret = _mv88e6xxx_update_bridge_config(ds, newfid);

unlock:
mutex_unlock(&ps->smi_mutex);

return ret;
Expand Down Expand Up @@ -1174,8 +1182,8 @@ int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state)
return 0;
}

static int __mv88e6xxx_write_addr(struct dsa_switch *ds,
const unsigned char *addr)
static int _mv88e6xxx_atu_mac_write(struct dsa_switch *ds,
const u8 addr[ETH_ALEN])
{
int i, ret;

Expand All @@ -1190,7 +1198,7 @@ static int __mv88e6xxx_write_addr(struct dsa_switch *ds,
return 0;
}

static int __mv88e6xxx_read_addr(struct dsa_switch *ds, unsigned char *addr)
static int _mv88e6xxx_atu_mac_read(struct dsa_switch *ds, u8 addr[ETH_ALEN])
{
int i, ret;

Expand All @@ -1206,109 +1214,190 @@ static int __mv88e6xxx_read_addr(struct dsa_switch *ds, unsigned char *addr)
return 0;
}

static int __mv88e6xxx_port_fdb_cmd(struct dsa_switch *ds, int port,
const unsigned char *addr, int state)
static int _mv88e6xxx_atu_load(struct dsa_switch *ds,
struct mv88e6xxx_atu_entry *entry)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
u8 fid = ps->fid[port];
u16 reg = 0;
int ret;

ret = _mv88e6xxx_atu_wait(ds);
if (ret < 0)
return ret;

ret = __mv88e6xxx_write_addr(ds, addr);
ret = _mv88e6xxx_atu_mac_write(ds, entry->mac);
if (ret < 0)
return ret;

ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_DATA,
(0x10 << port) | state);
if (ret)
if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
unsigned int mask, shift;

if (entry->trunk) {
reg |= GLOBAL_ATU_DATA_TRUNK;
mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
} else {
mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
}

reg |= (entry->portv_trunkid << shift) & mask;
}

reg |= entry->state & GLOBAL_ATU_DATA_STATE_MASK;

ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_DATA, reg);
if (ret < 0)
return ret;

ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_LOAD_DB);
return _mv88e6xxx_atu_cmd(ds, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
}

return ret;
static int _mv88e6xxx_atu_getnext(struct dsa_switch *ds, u16 fid,
const u8 addr[ETH_ALEN],
struct mv88e6xxx_atu_entry *entry)
{
struct mv88e6xxx_atu_entry next = { 0 };
int ret;

next.fid = fid;

ret = _mv88e6xxx_atu_wait(ds);
if (ret < 0)
return ret;

ret = _mv88e6xxx_atu_mac_write(ds, addr);
if (ret < 0)
return ret;

ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
if (ret < 0)
return ret;

ret = _mv88e6xxx_atu_mac_read(ds, next.mac);
if (ret < 0)
return ret;

ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_ATU_DATA);
if (ret < 0)
return ret;

next.state = ret & GLOBAL_ATU_DATA_STATE_MASK;
if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) {
unsigned int mask, shift;

if (ret & GLOBAL_ATU_DATA_TRUNK) {
next.trunk = true;
mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
} else {
next.trunk = false;
mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
}

next.portv_trunkid = (ret & mask) >> shift;
}

*entry = next;
return 0;
}

int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid)
static int _mv88e6xxx_port_vid_to_fid(struct dsa_switch *ds, int port, u16 vid)
{
int state = is_multicast_ether_addr(addr) ?
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);

if (vid == 0)
return ps->fid[port];

return -ENOENT;
}

static int _mv88e6xxx_port_fdb_load(struct dsa_switch *ds, int port, u16 vid,
const u8 addr[ETH_ALEN], u8 state)
{
struct mv88e6xxx_atu_entry entry = { 0 };
int ret;

ret = _mv88e6xxx_port_vid_to_fid(ds, port, vid);
if (ret < 0)
return ret;

entry.fid = ret;
entry.state = state;
ether_addr_copy(entry.mac, addr);
if (state != GLOBAL_ATU_DATA_STATE_UNUSED) {
entry.trunk = false;
entry.portv_trunkid = BIT(port);
}

return _mv88e6xxx_atu_load(ds, &entry);
}

int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, u16 vid,
const u8 addr[ETH_ALEN])
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
u8 state = is_multicast_ether_addr(addr) ?
GLOBAL_ATU_DATA_STATE_MC_STATIC :
GLOBAL_ATU_DATA_STATE_UC_STATIC;
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;

mutex_lock(&ps->smi_mutex);
ret = __mv88e6xxx_port_fdb_cmd(ds, port, addr, state);
ret = _mv88e6xxx_port_fdb_load(ds, port, vid, addr, state);
mutex_unlock(&ps->smi_mutex);

return ret;
}

int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid)
int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, u16 vid,
const u8 addr[ETH_ALEN])
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
u8 state = GLOBAL_ATU_DATA_STATE_UNUSED;
int ret;

mutex_lock(&ps->smi_mutex);
ret = __mv88e6xxx_port_fdb_cmd(ds, port, addr,
GLOBAL_ATU_DATA_STATE_UNUSED);
ret = _mv88e6xxx_port_fdb_load(ds, port, vid, addr, state);
mutex_unlock(&ps->smi_mutex);

return ret;
}

static int __mv88e6xxx_port_getnext(struct dsa_switch *ds, int port,
unsigned char *addr, bool *is_static)
int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port, u16 *vid,
u8 addr[ETH_ALEN], bool *is_static)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
u8 fid = ps->fid[port];
int ret, state;
struct mv88e6xxx_atu_entry next;
u16 fid;
int ret;

ret = _mv88e6xxx_atu_wait(ds);
if (ret < 0)
return ret;
mutex_lock(&ps->smi_mutex);

ret = __mv88e6xxx_write_addr(ds, addr);
ret = _mv88e6xxx_port_vid_to_fid(ds, port, *vid);
if (ret < 0)
return ret;
goto unlock;
fid = ret;

do {
ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
if (ret < 0)
return ret;
if (is_broadcast_ether_addr(addr)) {
ret = -ENOENT;
goto unlock;
}

ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_ATU_DATA);
ret = _mv88e6xxx_atu_getnext(ds, fid, addr, &next);
if (ret < 0)
return ret;
state = ret & GLOBAL_ATU_DATA_STATE_MASK;
if (state == GLOBAL_ATU_DATA_STATE_UNUSED)
return -ENOENT;
} while (!(((ret >> 4) & 0xff) & (1 << port)));
goto unlock;

ret = __mv88e6xxx_read_addr(ds, addr);
if (ret < 0)
return ret;
ether_addr_copy(addr, next.mac);

*is_static = state == (is_multicast_ether_addr(addr) ?
GLOBAL_ATU_DATA_STATE_MC_STATIC :
GLOBAL_ATU_DATA_STATE_UC_STATIC);

return 0;
}

/* get next entry for port */
int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port,
unsigned char *addr, bool *is_static)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
if (next.state == GLOBAL_ATU_DATA_STATE_UNUSED)
continue;
} while (next.trunk || (next.portv_trunkid & BIT(port)) == 0);

mutex_lock(&ps->smi_mutex);
ret = __mv88e6xxx_port_getnext(ds, port, addr, is_static);
*is_static = next.state == (is_multicast_ether_addr(addr) ?
GLOBAL_ATU_DATA_STATE_MC_STATIC :
GLOBAL_ATU_DATA_STATE_UC_STATIC);
unlock:
mutex_unlock(&ps->smi_mutex);

return ret;
Expand Down Expand Up @@ -1552,9 +1641,9 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
* ports, and allow each of the 'real' ports to only talk to
* the upstream port.
*/
fid = __ffs(ps->fid_mask);
fid = port + 1;
ps->fid[port] = fid;
ps->fid_mask &= ~(1 << fid);
set_bit(fid, ps->fid_bitmap);

if (!dsa_is_cpu_port(ds, port))
ps->bridge_mask[fid] = 1 << port;
Expand Down Expand Up @@ -1651,7 +1740,7 @@ static int mv88e6xxx_atu_show_db(struct seq_file *s, struct dsa_switch *ds,
unsigned char addr[6];
int ret, data, state;

ret = __mv88e6xxx_write_addr(ds, bcast);
ret = _mv88e6xxx_atu_mac_write(ds, bcast);
if (ret < 0)
return ret;

Expand All @@ -1666,7 +1755,7 @@ static int mv88e6xxx_atu_show_db(struct seq_file *s, struct dsa_switch *ds,
state = data & GLOBAL_ATU_DATA_STATE_MASK;
if (state == GLOBAL_ATU_DATA_STATE_UNUSED)
break;
ret = __mv88e6xxx_read_addr(ds, addr);
ret = _mv88e6xxx_atu_mac_read(ds, addr);
if (ret < 0)
return ret;
mv88e6xxx_atu_show_entry(s, dbnum, addr, data);
Expand Down Expand Up @@ -1853,8 +1942,6 @@ int mv88e6xxx_setup_common(struct dsa_switch *ds)

ps->id = REG_READ(REG_PORT(0), PORT_SWITCH_ID) & 0xfff0;

ps->fid_mask = (1 << DSA_MAX_PORTS) - 1;

INIT_WORK(&ps->bridge_work, mv88e6xxx_bridge_work);

name = kasprintf(GFP_KERNEL, "dsa%d", ds->index);
Expand Down
Loading

0 comments on commit f1d5ca4

Please sign in to comment.