forked from Minki/linux
Merge branch 'mv88e6xxx-switchdev-fdb'
Vivien Didelot says: ==================== net: dsa: mv88e6xxx: support switchdev FDB objects This patchset refactors the FDB management in the mv88e6xxx code and adds the glue in DSA to use the switchdev FDB objects. 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 v3: - reorder commits to improve bisectability and minimize diffs - add an ndm_state member in switchdev_fdb_obj instead of an is_static boolean - drop the need to convert unsigned char *addr to u8 addr[ETH_ALEN] (it is casted to char pointer anyway) 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>
This commit is contained in:
commit
adc6310ca4
@ -116,9 +116,9 @@ struct dsa_switch_driver mv88e6171_switch_driver = {
|
|||||||
.port_join_bridge = mv88e6xxx_join_bridge,
|
.port_join_bridge = mv88e6xxx_join_bridge,
|
||||||
.port_leave_bridge = mv88e6xxx_leave_bridge,
|
.port_leave_bridge = mv88e6xxx_leave_bridge,
|
||||||
.port_stp_update = mv88e6xxx_port_stp_update,
|
.port_stp_update = mv88e6xxx_port_stp_update,
|
||||||
.fdb_add = mv88e6xxx_port_fdb_add,
|
.port_fdb_add = mv88e6xxx_port_fdb_add,
|
||||||
.fdb_del = mv88e6xxx_port_fdb_del,
|
.port_fdb_del = mv88e6xxx_port_fdb_del,
|
||||||
.fdb_getnext = mv88e6xxx_port_fdb_getnext,
|
.port_fdb_getnext = mv88e6xxx_port_fdb_getnext,
|
||||||
};
|
};
|
||||||
|
|
||||||
MODULE_ALIAS("platform:mv88e6171");
|
MODULE_ALIAS("platform:mv88e6171");
|
||||||
|
@ -343,9 +343,9 @@ struct dsa_switch_driver mv88e6352_switch_driver = {
|
|||||||
.port_join_bridge = mv88e6xxx_join_bridge,
|
.port_join_bridge = mv88e6xxx_join_bridge,
|
||||||
.port_leave_bridge = mv88e6xxx_leave_bridge,
|
.port_leave_bridge = mv88e6xxx_leave_bridge,
|
||||||
.port_stp_update = mv88e6xxx_port_stp_update,
|
.port_stp_update = mv88e6xxx_port_stp_update,
|
||||||
.fdb_add = mv88e6xxx_port_fdb_add,
|
.port_fdb_add = mv88e6xxx_port_fdb_add,
|
||||||
.fdb_del = mv88e6xxx_port_fdb_del,
|
.port_fdb_del = mv88e6xxx_port_fdb_del,
|
||||||
.fdb_getnext = mv88e6xxx_port_fdb_getnext,
|
.port_fdb_getnext = mv88e6xxx_port_fdb_getnext,
|
||||||
};
|
};
|
||||||
|
|
||||||
MODULE_ALIAS("platform:mv88e6172");
|
MODULE_ALIAS("platform:mv88e6172");
|
||||||
|
@ -964,7 +964,7 @@ static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, int fid, u16 cmd)
|
|||||||
{
|
{
|
||||||
int ret;
|
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)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -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;
|
ps->bridge_mask[fid] = br_port_mask;
|
||||||
|
|
||||||
if (fid != ps->fid[port]) {
|
if (fid != ps->fid[port]) {
|
||||||
ps->fid_mask |= 1 << ps->fid[port];
|
clear_bit(ps->fid[port], ps->fid_bitmap);
|
||||||
ps->fid[port] = fid;
|
ps->fid[port] = fid;
|
||||||
ret = _mv88e6xxx_update_bridge_config(ds, fid);
|
ret = _mv88e6xxx_update_bridge_config(ds, fid);
|
||||||
}
|
}
|
||||||
@ -1125,9 +1125,16 @@ int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask)
|
|||||||
|
|
||||||
mutex_lock(&ps->smi_mutex);
|
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[port] = newfid;
|
||||||
ps->fid_mask &= ~(1 << newfid);
|
set_bit(newfid, ps->fid_bitmap);
|
||||||
ps->bridge_mask[fid] &= ~(1 << port);
|
ps->bridge_mask[fid] &= ~(1 << port);
|
||||||
ps->bridge_mask[newfid] = 1 << port;
|
ps->bridge_mask[newfid] = 1 << port;
|
||||||
|
|
||||||
@ -1135,6 +1142,7 @@ int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask)
|
|||||||
if (!ret)
|
if (!ret)
|
||||||
ret = _mv88e6xxx_update_bridge_config(ds, newfid);
|
ret = _mv88e6xxx_update_bridge_config(ds, newfid);
|
||||||
|
|
||||||
|
unlock:
|
||||||
mutex_unlock(&ps->smi_mutex);
|
mutex_unlock(&ps->smi_mutex);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -1174,8 +1182,8 @@ int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __mv88e6xxx_write_addr(struct dsa_switch *ds,
|
static int _mv88e6xxx_atu_mac_write(struct dsa_switch *ds,
|
||||||
const unsigned char *addr)
|
const unsigned char *addr)
|
||||||
{
|
{
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
@ -1190,7 +1198,7 @@ static int __mv88e6xxx_write_addr(struct dsa_switch *ds,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __mv88e6xxx_read_addr(struct dsa_switch *ds, unsigned char *addr)
|
static int _mv88e6xxx_atu_mac_read(struct dsa_switch *ds, unsigned char *addr)
|
||||||
{
|
{
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
@ -1206,29 +1214,74 @@ static int __mv88e6xxx_read_addr(struct dsa_switch *ds, unsigned char *addr)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __mv88e6xxx_port_fdb_cmd(struct dsa_switch *ds, int port,
|
static int _mv88e6xxx_atu_load(struct dsa_switch *ds,
|
||||||
const unsigned char *addr, int state)
|
struct mv88e6xxx_atu_entry *entry)
|
||||||
{
|
{
|
||||||
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
u16 reg = 0;
|
||||||
u8 fid = ps->fid[port];
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = _mv88e6xxx_atu_wait(ds);
|
ret = _mv88e6xxx_atu_wait(ds);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = __mv88e6xxx_write_addr(ds, addr);
|
ret = _mv88e6xxx_atu_mac_write(ds, entry->mac);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_DATA,
|
if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
|
||||||
(0x10 << port) | state);
|
unsigned int mask, shift;
|
||||||
if (ret)
|
|
||||||
|
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;
|
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_port_vid_to_fid(struct dsa_switch *ds, int port, u16 vid)
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
const unsigned char *addr, u16 vid,
|
||||||
|
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,
|
int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
|
||||||
@ -1241,7 +1294,7 @@ int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
mutex_lock(&ps->smi_mutex);
|
mutex_lock(&ps->smi_mutex);
|
||||||
ret = __mv88e6xxx_port_fdb_cmd(ds, port, addr, state);
|
ret = _mv88e6xxx_port_fdb_load(ds, port, addr, vid, state);
|
||||||
mutex_unlock(&ps->smi_mutex);
|
mutex_unlock(&ps->smi_mutex);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -1254,61 +1307,99 @@ int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
mutex_lock(&ps->smi_mutex);
|
mutex_lock(&ps->smi_mutex);
|
||||||
ret = __mv88e6xxx_port_fdb_cmd(ds, port, addr,
|
ret = _mv88e6xxx_port_fdb_load(ds, port, addr, vid,
|
||||||
GLOBAL_ATU_DATA_STATE_UNUSED);
|
GLOBAL_ATU_DATA_STATE_UNUSED);
|
||||||
mutex_unlock(&ps->smi_mutex);
|
mutex_unlock(&ps->smi_mutex);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __mv88e6xxx_port_getnext(struct dsa_switch *ds, int port,
|
static int _mv88e6xxx_atu_getnext(struct dsa_switch *ds, u16 fid,
|
||||||
unsigned char *addr, bool *is_static)
|
const unsigned char *addr,
|
||||||
|
struct mv88e6xxx_atu_entry *entry)
|
||||||
{
|
{
|
||||||
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
struct mv88e6xxx_atu_entry next = { 0 };
|
||||||
u8 fid = ps->fid[port];
|
int ret;
|
||||||
int ret, state;
|
|
||||||
|
next.fid = fid;
|
||||||
|
|
||||||
ret = _mv88e6xxx_atu_wait(ds);
|
ret = _mv88e6xxx_atu_wait(ds);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = __mv88e6xxx_write_addr(ds, addr);
|
ret = _mv88e6xxx_atu_mac_write(ds, addr);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
do {
|
ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
|
||||||
ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_ATU_DATA);
|
|
||||||
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)));
|
|
||||||
|
|
||||||
ret = __mv88e6xxx_read_addr(ds, addr);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
*is_static = state == (is_multicast_ether_addr(addr) ?
|
ret = _mv88e6xxx_atu_mac_read(ds, next.mac);
|
||||||
GLOBAL_ATU_DATA_STATE_MC_STATIC :
|
if (ret < 0)
|
||||||
GLOBAL_ATU_DATA_STATE_UC_STATIC);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get next entry for port */
|
/* get next entry for port */
|
||||||
int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port,
|
int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port,
|
||||||
unsigned char *addr, bool *is_static)
|
unsigned char *addr, u16 *vid, bool *is_static)
|
||||||
{
|
{
|
||||||
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
||||||
|
struct mv88e6xxx_atu_entry next;
|
||||||
|
u16 fid;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
mutex_lock(&ps->smi_mutex);
|
mutex_lock(&ps->smi_mutex);
|
||||||
ret = __mv88e6xxx_port_getnext(ds, port, addr, is_static);
|
|
||||||
|
ret = _mv88e6xxx_port_vid_to_fid(ds, port, *vid);
|
||||||
|
if (ret < 0)
|
||||||
|
goto unlock;
|
||||||
|
fid = ret;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (is_broadcast_ether_addr(addr)) {
|
||||||
|
ret = -ENOENT;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = _mv88e6xxx_atu_getnext(ds, fid, addr, &next);
|
||||||
|
if (ret < 0)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
ether_addr_copy(addr, next.mac);
|
||||||
|
|
||||||
|
if (next.state == GLOBAL_ATU_DATA_STATE_UNUSED)
|
||||||
|
continue;
|
||||||
|
} while (next.trunk || (next.portv_trunkid & BIT(port)) == 0);
|
||||||
|
|
||||||
|
*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);
|
mutex_unlock(&ps->smi_mutex);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -1552,9 +1643,9 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
|
|||||||
* ports, and allow each of the 'real' ports to only talk to
|
* ports, and allow each of the 'real' ports to only talk to
|
||||||
* the upstream port.
|
* the upstream port.
|
||||||
*/
|
*/
|
||||||
fid = __ffs(ps->fid_mask);
|
fid = port + 1;
|
||||||
ps->fid[port] = fid;
|
ps->fid[port] = fid;
|
||||||
ps->fid_mask &= ~(1 << fid);
|
set_bit(fid, ps->fid_bitmap);
|
||||||
|
|
||||||
if (!dsa_is_cpu_port(ds, port))
|
if (!dsa_is_cpu_port(ds, port))
|
||||||
ps->bridge_mask[fid] = 1 << port;
|
ps->bridge_mask[fid] = 1 << port;
|
||||||
@ -1651,7 +1742,7 @@ static int mv88e6xxx_atu_show_db(struct seq_file *s, struct dsa_switch *ds,
|
|||||||
unsigned char addr[6];
|
unsigned char addr[6];
|
||||||
int ret, data, state;
|
int ret, data, state;
|
||||||
|
|
||||||
ret = __mv88e6xxx_write_addr(ds, bcast);
|
ret = _mv88e6xxx_atu_mac_write(ds, bcast);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -1666,7 +1757,7 @@ static int mv88e6xxx_atu_show_db(struct seq_file *s, struct dsa_switch *ds,
|
|||||||
state = data & GLOBAL_ATU_DATA_STATE_MASK;
|
state = data & GLOBAL_ATU_DATA_STATE_MASK;
|
||||||
if (state == GLOBAL_ATU_DATA_STATE_UNUSED)
|
if (state == GLOBAL_ATU_DATA_STATE_UNUSED)
|
||||||
break;
|
break;
|
||||||
ret = __mv88e6xxx_read_addr(ds, addr);
|
ret = _mv88e6xxx_atu_mac_read(ds, addr);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
mv88e6xxx_atu_show_entry(s, dbnum, addr, data);
|
mv88e6xxx_atu_show_entry(s, dbnum, addr, data);
|
||||||
@ -1853,8 +1944,6 @@ int mv88e6xxx_setup_common(struct dsa_switch *ds)
|
|||||||
|
|
||||||
ps->id = REG_READ(REG_PORT(0), PORT_SWITCH_ID) & 0xfff0;
|
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);
|
INIT_WORK(&ps->bridge_work, mv88e6xxx_bridge_work);
|
||||||
|
|
||||||
name = kasprintf(GFP_KERNEL, "dsa%d", ds->index);
|
name = kasprintf(GFP_KERNEL, "dsa%d", ds->index);
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
#ifndef __MV88E6XXX_H
|
#ifndef __MV88E6XXX_H
|
||||||
#define __MV88E6XXX_H
|
#define __MV88E6XXX_H
|
||||||
|
|
||||||
|
#include <linux/if_vlan.h>
|
||||||
|
|
||||||
#ifndef UINT64_MAX
|
#ifndef UINT64_MAX
|
||||||
#define UINT64_MAX (u64)(~((u64)0))
|
#define UINT64_MAX (u64)(~((u64)0))
|
||||||
#endif
|
#endif
|
||||||
@ -169,6 +171,7 @@
|
|||||||
#define GLOBAL_MAC_01 0x01
|
#define GLOBAL_MAC_01 0x01
|
||||||
#define GLOBAL_MAC_23 0x02
|
#define GLOBAL_MAC_23 0x02
|
||||||
#define GLOBAL_MAC_45 0x03
|
#define GLOBAL_MAC_45 0x03
|
||||||
|
#define GLOBAL_ATU_FID 0x01 /* 6097 6165 6351 6352 */
|
||||||
#define GLOBAL_CONTROL 0x04
|
#define GLOBAL_CONTROL 0x04
|
||||||
#define GLOBAL_CONTROL_SW_RESET BIT(15)
|
#define GLOBAL_CONTROL_SW_RESET BIT(15)
|
||||||
#define GLOBAL_CONTROL_PPU_ENABLE BIT(14)
|
#define GLOBAL_CONTROL_PPU_ENABLE BIT(14)
|
||||||
@ -203,6 +206,8 @@
|
|||||||
#define GLOBAL_ATU_OP_GET_CLR_VIOLATION ((7 << 12) | GLOBAL_ATU_OP_BUSY)
|
#define GLOBAL_ATU_OP_GET_CLR_VIOLATION ((7 << 12) | GLOBAL_ATU_OP_BUSY)
|
||||||
#define GLOBAL_ATU_DATA 0x0c
|
#define GLOBAL_ATU_DATA 0x0c
|
||||||
#define GLOBAL_ATU_DATA_TRUNK BIT(15)
|
#define GLOBAL_ATU_DATA_TRUNK BIT(15)
|
||||||
|
#define GLOBAL_ATU_DATA_TRUNK_ID_MASK 0x00f0
|
||||||
|
#define GLOBAL_ATU_DATA_TRUNK_ID_SHIFT 4
|
||||||
#define GLOBAL_ATU_DATA_PORT_VECTOR_MASK 0x3ff0
|
#define GLOBAL_ATU_DATA_PORT_VECTOR_MASK 0x3ff0
|
||||||
#define GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT 4
|
#define GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT 4
|
||||||
#define GLOBAL_ATU_DATA_STATE_MASK 0x0f
|
#define GLOBAL_ATU_DATA_STATE_MASK 0x0f
|
||||||
@ -313,6 +318,14 @@
|
|||||||
#define GLOBAL2_QOS_WEIGHT 0x1c
|
#define GLOBAL2_QOS_WEIGHT 0x1c
|
||||||
#define GLOBAL2_MISC 0x1d
|
#define GLOBAL2_MISC 0x1d
|
||||||
|
|
||||||
|
struct mv88e6xxx_atu_entry {
|
||||||
|
u16 fid;
|
||||||
|
u8 state;
|
||||||
|
bool trunk;
|
||||||
|
u16 portv_trunkid;
|
||||||
|
u8 mac[ETH_ALEN];
|
||||||
|
};
|
||||||
|
|
||||||
struct mv88e6xxx_priv_state {
|
struct mv88e6xxx_priv_state {
|
||||||
/* When using multi-chip addressing, this mutex protects
|
/* When using multi-chip addressing, this mutex protects
|
||||||
* access to the indirect access registers. (In single-chip
|
* access to the indirect access registers. (In single-chip
|
||||||
@ -351,9 +364,9 @@ struct mv88e6xxx_priv_state {
|
|||||||
|
|
||||||
/* hw bridging */
|
/* hw bridging */
|
||||||
|
|
||||||
u32 fid_mask;
|
DECLARE_BITMAP(fid_bitmap, VLAN_N_VID); /* FIDs 1 to 4095 available */
|
||||||
u8 fid[DSA_MAX_PORTS];
|
u16 fid[DSA_MAX_PORTS]; /* per (non-bridged) port FID */
|
||||||
u16 bridge_mask[DSA_MAX_PORTS];
|
u16 bridge_mask[DSA_MAX_PORTS]; /* br groups (indexed by FID) */
|
||||||
|
|
||||||
unsigned long port_state_update_mask;
|
unsigned long port_state_update_mask;
|
||||||
u8 port_state[DSA_MAX_PORTS];
|
u8 port_state[DSA_MAX_PORTS];
|
||||||
@ -418,7 +431,7 @@ int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
|
|||||||
int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
|
int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
|
||||||
const unsigned char *addr, u16 vid);
|
const unsigned char *addr, u16 vid);
|
||||||
int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port,
|
int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port,
|
||||||
unsigned char *addr, bool *is_static);
|
unsigned char *addr, u16 *vid, bool *is_static);
|
||||||
int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg);
|
int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg);
|
||||||
int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page,
|
int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page,
|
||||||
int reg, int val);
|
int reg, int val);
|
||||||
|
@ -4544,6 +4544,7 @@ static int rocker_port_fdb_dump(const struct rocker_port *rocker_port,
|
|||||||
if (found->key.pport != rocker_port->pport)
|
if (found->key.pport != rocker_port->pport)
|
||||||
continue;
|
continue;
|
||||||
fdb->addr = found->key.addr;
|
fdb->addr = found->key.addr;
|
||||||
|
fdb->ndm_state = NUD_REACHABLE;
|
||||||
fdb->vid = rocker_port_vlan_to_vid(rocker_port,
|
fdb->vid = rocker_port_vlan_to_vid(rocker_port,
|
||||||
found->key.vlan_id);
|
found->key.vlan_id);
|
||||||
err = obj->cb(rocker_port->dev, obj);
|
err = obj->cb(rocker_port->dev, obj);
|
||||||
|
@ -296,12 +296,17 @@ struct dsa_switch_driver {
|
|||||||
u32 br_port_mask);
|
u32 br_port_mask);
|
||||||
int (*port_stp_update)(struct dsa_switch *ds, int port,
|
int (*port_stp_update)(struct dsa_switch *ds, int port,
|
||||||
u8 state);
|
u8 state);
|
||||||
int (*fdb_add)(struct dsa_switch *ds, int port,
|
|
||||||
const unsigned char *addr, u16 vid);
|
/*
|
||||||
int (*fdb_del)(struct dsa_switch *ds, int port,
|
* Forwarding database
|
||||||
const unsigned char *addr, u16 vid);
|
*/
|
||||||
int (*fdb_getnext)(struct dsa_switch *ds, int port,
|
int (*port_fdb_add)(struct dsa_switch *ds, int port,
|
||||||
unsigned char *addr, bool *is_static);
|
const unsigned char *addr, u16 vid);
|
||||||
|
int (*port_fdb_del)(struct dsa_switch *ds, int port,
|
||||||
|
const unsigned char *addr, u16 vid);
|
||||||
|
int (*port_fdb_getnext)(struct dsa_switch *ds, int port,
|
||||||
|
unsigned char *addr, u16 *vid,
|
||||||
|
bool *is_static);
|
||||||
};
|
};
|
||||||
|
|
||||||
void register_switch_driver(struct dsa_switch_driver *type);
|
void register_switch_driver(struct dsa_switch_driver *type);
|
||||||
|
@ -72,6 +72,7 @@ struct switchdev_obj {
|
|||||||
struct switchdev_obj_fdb { /* PORT_FDB */
|
struct switchdev_obj_fdb { /* PORT_FDB */
|
||||||
const unsigned char *addr;
|
const unsigned char *addr;
|
||||||
u16 vid;
|
u16 vid;
|
||||||
|
u16 ndm_state;
|
||||||
} fdb;
|
} fdb;
|
||||||
} u;
|
} u;
|
||||||
};
|
};
|
||||||
|
150
net/dsa/slave.c
150
net/dsa/slave.c
@ -200,103 +200,66 @@ out:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dsa_slave_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
|
static int dsa_slave_port_fdb_add(struct net_device *dev,
|
||||||
struct net_device *dev,
|
struct switchdev_obj *obj)
|
||||||
const unsigned char *addr, u16 vid, u16 nlm_flags)
|
|
||||||
{
|
{
|
||||||
|
struct switchdev_obj_fdb *fdb = &obj->u.fdb;
|
||||||
struct dsa_slave_priv *p = netdev_priv(dev);
|
struct dsa_slave_priv *p = netdev_priv(dev);
|
||||||
struct dsa_switch *ds = p->parent;
|
struct dsa_switch *ds = p->parent;
|
||||||
int ret = -EOPNOTSUPP;
|
int ret = -EOPNOTSUPP;
|
||||||
|
|
||||||
if (ds->drv->fdb_add)
|
if (obj->trans == SWITCHDEV_TRANS_PREPARE)
|
||||||
ret = ds->drv->fdb_add(ds, p->port, addr, vid);
|
ret = ds->drv->port_fdb_add ? 0 : -EOPNOTSUPP;
|
||||||
|
else if (obj->trans == SWITCHDEV_TRANS_COMMIT)
|
||||||
|
ret = ds->drv->port_fdb_add(ds, p->port, fdb->addr, fdb->vid);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dsa_slave_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
|
static int dsa_slave_port_fdb_del(struct net_device *dev,
|
||||||
struct net_device *dev,
|
struct switchdev_obj *obj)
|
||||||
const unsigned char *addr, u16 vid)
|
|
||||||
{
|
{
|
||||||
|
struct switchdev_obj_fdb *fdb = &obj->u.fdb;
|
||||||
struct dsa_slave_priv *p = netdev_priv(dev);
|
struct dsa_slave_priv *p = netdev_priv(dev);
|
||||||
struct dsa_switch *ds = p->parent;
|
struct dsa_switch *ds = p->parent;
|
||||||
int ret = -EOPNOTSUPP;
|
int ret = -EOPNOTSUPP;
|
||||||
|
|
||||||
if (ds->drv->fdb_del)
|
if (ds->drv->port_fdb_del)
|
||||||
ret = ds->drv->fdb_del(ds, p->port, addr, vid);
|
ret = ds->drv->port_fdb_del(ds, p->port, fdb->addr, fdb->vid);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dsa_slave_fill_info(struct net_device *dev, struct sk_buff *skb,
|
static int dsa_slave_port_fdb_dump(struct net_device *dev,
|
||||||
const unsigned char *addr, u16 vid,
|
struct switchdev_obj *obj)
|
||||||
bool is_static,
|
|
||||||
u32 portid, u32 seq, int type,
|
|
||||||
unsigned int flags)
|
|
||||||
{
|
|
||||||
struct nlmsghdr *nlh;
|
|
||||||
struct ndmsg *ndm;
|
|
||||||
|
|
||||||
nlh = nlmsg_put(skb, portid, seq, type, sizeof(*ndm), flags);
|
|
||||||
if (!nlh)
|
|
||||||
return -EMSGSIZE;
|
|
||||||
|
|
||||||
ndm = nlmsg_data(nlh);
|
|
||||||
ndm->ndm_family = AF_BRIDGE;
|
|
||||||
ndm->ndm_pad1 = 0;
|
|
||||||
ndm->ndm_pad2 = 0;
|
|
||||||
ndm->ndm_flags = NTF_EXT_LEARNED;
|
|
||||||
ndm->ndm_type = 0;
|
|
||||||
ndm->ndm_ifindex = dev->ifindex;
|
|
||||||
ndm->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
|
|
||||||
|
|
||||||
if (nla_put(skb, NDA_LLADDR, ETH_ALEN, addr))
|
|
||||||
goto nla_put_failure;
|
|
||||||
|
|
||||||
if (vid && nla_put_u16(skb, NDA_VLAN, vid))
|
|
||||||
goto nla_put_failure;
|
|
||||||
|
|
||||||
nlmsg_end(skb, nlh);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
nla_put_failure:
|
|
||||||
nlmsg_cancel(skb, nlh);
|
|
||||||
return -EMSGSIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dump information about entries, in response to GETNEIGH */
|
|
||||||
static int dsa_slave_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
|
|
||||||
struct net_device *dev,
|
|
||||||
struct net_device *filter_dev, int idx)
|
|
||||||
{
|
{
|
||||||
struct dsa_slave_priv *p = netdev_priv(dev);
|
struct dsa_slave_priv *p = netdev_priv(dev);
|
||||||
struct dsa_switch *ds = p->parent;
|
struct dsa_switch *ds = p->parent;
|
||||||
unsigned char addr[ETH_ALEN] = { 0 };
|
unsigned char addr[ETH_ALEN] = { 0 };
|
||||||
|
u16 vid = 0;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!ds->drv->fdb_getnext)
|
if (!ds->drv->port_fdb_getnext)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
for (; ; idx++) {
|
for (;;) {
|
||||||
bool is_static;
|
bool is_static;
|
||||||
|
|
||||||
ret = ds->drv->fdb_getnext(ds, p->port, addr, &is_static);
|
ret = ds->drv->port_fdb_getnext(ds, p->port, addr, &vid,
|
||||||
|
&is_static);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (idx < cb->args[0])
|
obj->u.fdb.addr = addr;
|
||||||
continue;
|
obj->u.fdb.vid = vid;
|
||||||
|
obj->u.fdb.ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
|
||||||
|
|
||||||
ret = dsa_slave_fill_info(dev, skb, addr, 0,
|
ret = obj->cb(dev, obj);
|
||||||
is_static,
|
|
||||||
NETLINK_CB(cb->skb).portid,
|
|
||||||
cb->nlh->nlmsg_seq,
|
|
||||||
RTM_NEWNEIGH, NLM_F_MULTI);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return idx;
|
return ret == -ENOENT ? 0 : ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||||
@ -364,6 +327,62 @@ static int dsa_slave_port_attr_set(struct net_device *dev,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int dsa_slave_port_obj_add(struct net_device *dev,
|
||||||
|
struct switchdev_obj *obj)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* For the prepare phase, ensure the full set of changes is feasable in
|
||||||
|
* one go in order to signal a failure properly. If an operation is not
|
||||||
|
* supported, return -EOPNOTSUPP.
|
||||||
|
*/
|
||||||
|
|
||||||
|
switch (obj->id) {
|
||||||
|
case SWITCHDEV_OBJ_PORT_FDB:
|
||||||
|
err = dsa_slave_port_fdb_add(dev, obj);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
err = -EOPNOTSUPP;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dsa_slave_port_obj_del(struct net_device *dev,
|
||||||
|
struct switchdev_obj *obj)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
switch (obj->id) {
|
||||||
|
case SWITCHDEV_OBJ_PORT_FDB:
|
||||||
|
err = dsa_slave_port_fdb_del(dev, obj);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
err = -EOPNOTSUPP;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dsa_slave_port_obj_dump(struct net_device *dev,
|
||||||
|
struct switchdev_obj *obj)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
switch (obj->id) {
|
||||||
|
case SWITCHDEV_OBJ_PORT_FDB:
|
||||||
|
err = dsa_slave_port_fdb_dump(dev, obj);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
err = -EOPNOTSUPP;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int dsa_slave_bridge_port_join(struct net_device *dev,
|
static int dsa_slave_bridge_port_join(struct net_device *dev,
|
||||||
struct net_device *br)
|
struct net_device *br)
|
||||||
{
|
{
|
||||||
@ -765,9 +784,9 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
|
|||||||
.ndo_change_rx_flags = dsa_slave_change_rx_flags,
|
.ndo_change_rx_flags = dsa_slave_change_rx_flags,
|
||||||
.ndo_set_rx_mode = dsa_slave_set_rx_mode,
|
.ndo_set_rx_mode = dsa_slave_set_rx_mode,
|
||||||
.ndo_set_mac_address = dsa_slave_set_mac_address,
|
.ndo_set_mac_address = dsa_slave_set_mac_address,
|
||||||
.ndo_fdb_add = dsa_slave_fdb_add,
|
.ndo_fdb_add = switchdev_port_fdb_add,
|
||||||
.ndo_fdb_del = dsa_slave_fdb_del,
|
.ndo_fdb_del = switchdev_port_fdb_del,
|
||||||
.ndo_fdb_dump = dsa_slave_fdb_dump,
|
.ndo_fdb_dump = switchdev_port_fdb_dump,
|
||||||
.ndo_do_ioctl = dsa_slave_ioctl,
|
.ndo_do_ioctl = dsa_slave_ioctl,
|
||||||
.ndo_get_iflink = dsa_slave_get_iflink,
|
.ndo_get_iflink = dsa_slave_get_iflink,
|
||||||
#ifdef CONFIG_NET_POLL_CONTROLLER
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||||
@ -780,6 +799,9 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
|
|||||||
static const struct switchdev_ops dsa_slave_switchdev_ops = {
|
static const struct switchdev_ops dsa_slave_switchdev_ops = {
|
||||||
.switchdev_port_attr_get = dsa_slave_port_attr_get,
|
.switchdev_port_attr_get = dsa_slave_port_attr_get,
|
||||||
.switchdev_port_attr_set = dsa_slave_port_attr_set,
|
.switchdev_port_attr_set = dsa_slave_port_attr_set,
|
||||||
|
.switchdev_port_obj_add = dsa_slave_port_obj_add,
|
||||||
|
.switchdev_port_obj_del = dsa_slave_port_obj_del,
|
||||||
|
.switchdev_port_obj_dump = dsa_slave_port_obj_dump,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void dsa_slave_adjust_link(struct net_device *dev)
|
static void dsa_slave_adjust_link(struct net_device *dev)
|
||||||
|
@ -810,7 +810,7 @@ static int switchdev_port_fdb_dump_cb(struct net_device *dev,
|
|||||||
ndm->ndm_flags = NTF_SELF;
|
ndm->ndm_flags = NTF_SELF;
|
||||||
ndm->ndm_type = 0;
|
ndm->ndm_type = 0;
|
||||||
ndm->ndm_ifindex = dev->ifindex;
|
ndm->ndm_ifindex = dev->ifindex;
|
||||||
ndm->ndm_state = NUD_REACHABLE;
|
ndm->ndm_state = obj->u.fdb.ndm_state;
|
||||||
|
|
||||||
if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, obj->u.fdb.addr))
|
if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, obj->u.fdb.addr))
|
||||||
goto nla_put_failure;
|
goto nla_put_failure;
|
||||||
|
Loading…
Reference in New Issue
Block a user