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

Merge branch 'mv88e6xxx-switchdev-fdb'

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 4933d85c 87820510
......@@ -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");
......
......@@ -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");
......
......@@ -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;
......@@ -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);
}
......@@ -1125,9 +1125,16 @@ 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;
......@@ -1135,6 +1142,7 @@ int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask)
if (!ret)
ret = _mv88e6xxx_update_bridge_config(ds, newfid);
unlock:
mutex_unlock(&ps->smi_mutex);
return ret;
......@@ -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;
......@@ -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;
......@@ -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;
return _mv88e6xxx_atu_cmd(ds, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
}
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_cmd(ds, fid, GLOBAL_ATU_OP_LOAD_DB);
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;
}
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, 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,
const unsigned char *addr, u16 vid)
int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, u16 vid,
const u8 addr[ETH_ALEN])
{
int state = is_multicast_ether_addr(addr) ?
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);
if (next.state == GLOBAL_ATU_DATA_STATE_UNUSED)
continue;
} while (next.trunk || (next.portv_trunkid & BIT(port)) == 0);
*is_static = state == (is_multicast_ether_addr(addr) ?
*is_static = next.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;
mutex_lock(&ps->smi_mutex);
ret = __mv88e6xxx_port_getnext(ds, port, addr, is_static);
unlock:
mutex_unlock(&ps->smi_mutex);
return ret;
......@@ -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;
......@@ -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;
......@@ -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);
......@@ -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);
......
......@@ -11,6 +11,8 @@
#ifndef __MV88E6XXX_H
#define __MV88E6XXX_H
#include <linux/if_vlan.h>
#ifndef UINT64_MAX
#define UINT64_MAX (u64)(~((u64)0))
#endif
......@@ -169,6 +171,7 @@
#define GLOBAL_MAC_01 0x01
#define GLOBAL_MAC_23 0x02
#define GLOBAL_MAC_45 0x03
#define GLOBAL_ATU_FID 0x01 /* 6097 6165 6351 6352 */
#define GLOBAL_CONTROL 0x04
#define GLOBAL_CONTROL_SW_RESET BIT(15)
#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_DATA 0x0c
#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_SHIFT 4
#define GLOBAL_ATU_DATA_STATE_MASK 0x0f
......@@ -309,6 +314,14 @@
#define GLOBAL2_QOS_WEIGHT 0x1c
#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 {
/* When using multi-chip addressing, this mutex protects
* access to the indirect access registers. (In single-chip
......@@ -347,9 +360,9 @@ struct mv88e6xxx_priv_state {
/* hw bridging */
u32 fid_mask;
u8 fid[DSA_MAX_PORTS];
u16 bridge_mask[DSA_MAX_PORTS];
DECLARE_BITMAP(fid_bitmap, VLAN_N_VID); /* FIDs 1 to 4095 available */
u16 fid[DSA_MAX_PORTS]; /* per (non-bridged) port FID */
u16 bridge_mask[DSA_MAX_PORTS]; /* br groups (indexed by FID) */
unsigned long port_state_update_mask;
u8 port_state[DSA_MAX_PORTS];
......@@ -409,15 +422,15 @@ int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask);
int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask);
int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state);
int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid);
int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid);
int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port,
unsigned char *addr, bool *is_static);
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 reg, int val);
int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, u16 vid,
const u8 addr[ETH_ALEN]);
int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, u16 vid,
const u8 addr[ETH_ALEN]);
int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port, u16 *vid,
u8 addr[ETH_ALEN], bool *is_static);
extern struct dsa_switch_driver mv88e6131_switch_driver;
extern struct dsa_switch_driver mv88e6123_61_65_switch_driver;
......
......@@ -4543,7 +4543,7 @@ static int rocker_port_fdb_dump(const struct rocker_port *rocker_port,
hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, found, entry) {
if (found->key.pport != rocker_port->pport)
continue;
fdb->addr = found->key.addr;
ether_addr_copy(fdb->addr, found->key.addr);
fdb->vid = rocker_port_vlan_to_vid(rocker_port,
found->key.vlan_id);
err = obj->cb(rocker_port->dev, obj);
......
......@@ -296,12 +296,16 @@ struct dsa_switch_driver {
u32 br_port_mask);
int (*port_stp_update)(struct dsa_switch *ds, int port,
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,
const unsigned char *addr, u16 vid);
int (*fdb_getnext)(struct dsa_switch *ds, int port,
unsigned char *addr, bool *is_static);
/*
* Forwarding database
*/
int (*port_fdb_add)(struct dsa_switch *ds, int port, u16 vid,
const u8 addr[ETH_ALEN]);
int (*port_fdb_del)(struct dsa_switch *ds, int port, u16 vid,
const u8 addr[ETH_ALEN]);
int (*port_fdb_getnext)(struct dsa_switch *ds, int port, u16 *vid,
u8 addr[ETH_ALEN], bool *is_static);
};
void register_switch_driver(struct dsa_switch_driver *type);
......
......@@ -70,8 +70,9 @@ struct switchdev_obj {
u32 tb_id;
} ipv4_fib;
struct switchdev_obj_fdb { /* PORT_FDB */
const unsigned char *addr;
u8 addr[ETH_ALEN];
u16 vid;
bool is_static;
} fdb;
} u;
};
......
......@@ -136,11 +136,11 @@ static void fdb_del_external_learn(struct net_bridge_fdb_entry *f)
struct switchdev_obj obj = {
.id = SWITCHDEV_OBJ_PORT_FDB,
.u.fdb = {
.addr = f->addr.addr,
.vid = f->vlan_id,
},
};
ether_addr_copy(obj.u.fdb.addr, f->addr.addr);
switchdev_port_obj_del(f->dst->dev, &obj);
}
......
......@@ -19,6 +19,7 @@
#include <net/switchdev.h>
#include <linux/if_bridge.h>
#include <linux/netpoll.h>
#include <linux/if_vlan.h>
#include "dsa_priv.h"
/* slave mii_bus handling ***************************************************/
......@@ -200,105 +201,6 @@ static int dsa_slave_set_mac_address(struct net_device *dev, void *a)
return 0;
}
static int dsa_slave_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid, u16 nlm_flags)
{
struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_switch *ds = p->parent;
int ret = -EOPNOTSUPP;
if (ds->drv->fdb_add)
ret = ds->drv->fdb_add(ds, p->port, addr, vid);
return ret;
}
static int dsa_slave_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid)
{
struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_switch *ds = p->parent;
int ret = -EOPNOTSUPP;
if (ds->drv->fdb_del)
ret = ds->drv->fdb_del(ds, p->port, addr, vid);
return ret;
}
static int dsa_slave_fill_info(struct net_device *dev, struct sk_buff *skb,
const unsigned char *addr, u16 vid,
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_switch *ds = p->parent;
unsigned char addr[ETH_ALEN] = { 0 };
int ret;
if (!ds->drv->fdb_getnext)
return -EOPNOTSUPP;
for (; ; idx++) {
bool is_static;
ret = ds->drv->fdb_getnext(ds, p->port, addr, &is_static);
if (ret < 0)
break;
if (idx < cb->args[0])
continue;
ret = dsa_slave_fill_info(dev, skb, addr, 0,
is_static,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
RTM_NEWNEIGH, NLM_F_MULTI);
if (ret < 0)
break;
}
return idx;
}
static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct dsa_slave_priv *p = netdev_priv(dev);
......@@ -364,6 +266,115 @@ static int dsa_slave_port_attr_set(struct net_device *dev,
return ret;
}
static int dsa_slave_port_fdb_add(struct net_device *dev,
struct switchdev_obj *obj)
{
struct switchdev_obj_fdb *fdb = &obj->u.fdb;
struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_switch *ds = p->parent;
int err;
if (obj->trans == SWITCHDEV_TRANS_PREPARE)
err = ds->drv->port_fdb_add ? 0 : -EOPNOTSUPP;
else if (obj->trans == SWITCHDEV_TRANS_COMMIT)
err = ds->drv->port_fdb_add(ds, p->port, fdb->vid, fdb->addr);
else
err = -EOPNOTSUPP;
return err;
}
static int dsa_slave_port_fdb_del(struct net_device *dev,
struct switchdev_obj *obj)
{
struct switchdev_obj_fdb *fdb = &obj->u.fdb;
struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_switch *ds = p->parent;
if (!ds->drv->port_fdb_del)
return -EOPNOTSUPP;
return ds->drv->port_fdb_del(ds, p->port, fdb->vid, fdb->addr);
}
static int dsa_slave_port_fdb_dump(struct net_device *dev,
struct switchdev_obj *obj)
{
struct switchdev_obj_fdb *fdb = &obj->u.fdb;
struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_switch *ds = p->parent;
int err;
if (!ds->drv->port_fdb_getnext)
return -EOPNOTSUPP;
memset(fdb, 0, sizeof(*fdb));
for (;;) {
err = ds->drv->port_fdb_getnext(ds, p->port, &fdb->vid,
fdb->addr, &fdb->is_static);
if (err)
break;
err = obj->cb(dev, obj);
if (err)
break;
}
return err == -ENOENT ? 0 : err;
}
static int dsa_slave_port_obj_add(struct net_device *dev,
struct switchdev_obj *obj)
{
int err;
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,
struct net_device *br)
{
......@@ -765,9 +776,9 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
.ndo_change_rx_flags = dsa_slave_change_rx_flags,
.ndo_set_rx_mode = dsa_slave_set_rx_mode,
.ndo_set_mac_address = dsa_slave_set_mac_address,
.ndo_fdb_add = dsa_slave_fdb_add,
.ndo_fdb_del = dsa_slave_fdb_del,
.ndo_fdb_dump = dsa_slave_fdb_dump,
.ndo_fdb_add = switchdev_port_fdb_add,
.ndo_fdb_del = switchdev_port_fdb_del,
.ndo_fdb_dump = switchdev_port_fdb_dump,
.ndo_do_ioctl = dsa_slave_ioctl,
.ndo_get_iflink = dsa_slave_get_iflink,
#ifdef CONFIG_NET_POLL_CONTROLLER
......@@ -780,6 +791,9 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
static const struct switchdev_ops dsa_slave_switchdev_ops = {
.switchdev_port_attr_get = dsa_slave_port_attr_get,
.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)
......
......@@ -15,6 +15,7 @@
#include <linux/mutex.h>
#include <linux/notifier.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/if_bridge.h>
#include <net/ip_fib.h>
#include <net/switchdev.h>
......@@ -742,11 +743,11 @@ int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct switchdev_obj obj = {
.id = SWITCHDEV_OBJ_PORT_FDB,
.u.fdb = {
.addr = addr,
.vid = vid,
},
};
ether_addr_copy(obj.u.fdb.addr, addr);
return switchdev_port_obj_add(dev, &obj);
}
EXPORT_SYMBOL_GPL(switchdev_port_fdb_add);
......@@ -769,11 +770,11 @@ int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
struct switchdev_obj obj = {
.id = SWITCHDEV_OBJ_PORT_FDB,
.u.fdb = {
.addr = addr,
.vid = vid,
},
};
ether_addr_copy(obj.u.fdb.addr, addr);
return switchdev_port_obj_del(dev, &obj);
}
EXPORT_SYMBOL_GPL(switchdev_port_fdb_del);
......@@ -810,7 +811,7 @@ static int switchdev_port_fdb_dump_cb(struct net_device *dev,
ndm->ndm_flags = NTF_SELF;
ndm->ndm_type = 0;
ndm->ndm_ifindex = dev->ifindex;
ndm->ndm_state = NUD_REACHABLE;
ndm->ndm_state = obj->u.fdb.is_static ? NUD_NOARP : NUD_REACHABLE;
if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, obj->u.fdb.addr))
goto nla_put_failure;
......
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