diff --git a/drivers/net/dsa/mv88e6171.c b/drivers/net/dsa/mv88e6171.c index 1c7808495a9d..735f04cd83ee 100644 --- a/drivers/net/dsa/mv88e6171.c +++ b/drivers/net/dsa/mv88e6171.c @@ -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"); diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c index af210efecc55..191fb2531efe 100644 --- a/drivers/net/dsa/mv88e6352.c +++ b/drivers/net/dsa/mv88e6352.c @@ -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"); diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index 109452056eff..9c6781de533b 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -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; - ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_LOAD_DB); - - return ret; + return _mv88e6xxx_atu_cmd(ds, entry->fid, GLOBAL_ATU_OP_LOAD_DB); } -int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) +static int _mv88e6xxx_atu_getnext(struct dsa_switch *ds, u16 fid, + const u8 addr[ETH_ALEN], + struct mv88e6xxx_atu_entry *entry) { - int 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); + struct mv88e6xxx_atu_entry next = { 0 }; int ret; - mutex_lock(&ps->smi_mutex); - ret = __mv88e6xxx_port_fdb_cmd(ds, port, 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) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; - - mutex_lock(&ps->smi_mutex); - ret = __mv88e6xxx_port_fdb_cmd(ds, port, addr, - GLOBAL_ATU_DATA_STATE_UNUSED); - mutex_unlock(&ps->smi_mutex); - - return ret; -} - -static int __mv88e6xxx_port_getnext(struct dsa_switch *ds, int port, - unsigned char *addr, bool *is_static) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - u8 fid = ps->fid[port]; - int ret, state; + next.fid = fid; ret = _mv88e6xxx_atu_wait(ds); if (ret < 0) return ret; - ret = __mv88e6xxx_write_addr(ds, addr); + ret = _mv88e6xxx_atu_mac_write(ds, addr); if (ret < 0) return ret; - do { - 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); + ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_GET_NEXT_DB); if (ret < 0) return ret; - *is_static = state == (is_multicast_ether_addr(addr) ? - GLOBAL_ATU_DATA_STATE_MC_STATIC : - GLOBAL_ATU_DATA_STATE_UC_STATIC); + 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; } -/* get next entry for port */ -int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port, - unsigned char *addr, bool *is_static) +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, 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; int ret; mutex_lock(&ps->smi_mutex); - ret = __mv88e6xxx_port_getnext(ds, port, addr, is_static); + 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, 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_load(ds, port, vid, addr, state); + mutex_unlock(&ps->smi_mutex); + + return ret; +} + +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); + struct mv88e6xxx_atu_entry next; + u16 fid; + int ret; + + mutex_lock(&ps->smi_mutex); + + 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); 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); diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h index 78e37226a37d..a8c727d846dd 100644 --- a/drivers/net/dsa/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx.h @@ -11,6 +11,8 @@ #ifndef __MV88E6XXX_H #define __MV88E6XXX_H +#include + #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; diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c index b77e0e7307d4..80bb25c5a644 100644 --- a/drivers/net/ethernet/rocker/rocker.c +++ b/drivers/net/ethernet/rocker/rocker.c @@ -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); diff --git a/include/net/dsa.h b/include/net/dsa.h index fbca63ba8f73..091d35f77180 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -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); diff --git a/include/net/switchdev.h b/include/net/switchdev.h index 89da8934519b..0e296b82aef3 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -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; }; diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 9e9875da0a4f..5656b44bf3de 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -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); } diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 0010c690cc67..1dbdeaab2bb4 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "dsa_priv.h" /* slave mii_bus handling ***************************************************/ @@ -200,105 +201,6 @@ out: 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) diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 33bafa2e703e..e9d1cacc4060 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -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;