Skip to content
This repository has been archived by the owner on Sep 24, 2020. It is now read-only.

Commit

Permalink
switchdev: add support for fdb add/del/dump via switchdev_port_obj ops.
Browse files Browse the repository at this point in the history
- introduce port fdb obj and generic switchdev_port_fdb_add/del/dump()
- use switchdev_port_fdb_add/del/dump in rocker/team/bonding ndo ops.
- add support for fdb obj in switchdev_port_obj_add/del/dump()
- switch rocker to implement fdb ops via switchdev_ops

v3: updated to sync with named union changes.

Signed-off-by: Sridhar Samudrala <sridhar.samudrala@intel.com>
Signed-off-by: Scott Feldman <sfeldma@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
ssamudrala authored and davem330 committed May 18, 2015
1 parent 5d48ef3 commit 45d4122
Show file tree
Hide file tree
Showing 5 changed files with 312 additions and 107 deletions.
3 changes: 3 additions & 0 deletions drivers/net/bonding/bond_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -4039,6 +4039,9 @@ static const struct net_device_ops bond_netdev_ops = {
.ndo_bridge_setlink = switchdev_port_bridge_setlink,
.ndo_bridge_getlink = switchdev_port_bridge_getlink,
.ndo_bridge_dellink = switchdev_port_bridge_dellink,
.ndo_fdb_add = switchdev_port_fdb_add,
.ndo_fdb_del = switchdev_port_fdb_del,
.ndo_fdb_dump = switchdev_port_fdb_dump,
.ndo_features_check = passthru_features_check,
};

Expand Down
188 changes: 81 additions & 107 deletions drivers/net/ethernet/rocker/rocker.c
Original file line number Diff line number Diff line change
Expand Up @@ -4193,110 +4193,6 @@ static int rocker_port_vlan_rx_kill_vid(struct net_device *dev,
ROCKER_OP_FLAG_REMOVE, vid);
}

static int rocker_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid,
u16 nlm_flags)
{
struct rocker_port *rocker_port = netdev_priv(dev);
__be16 vlan_id = rocker_port_vid_to_vlan(rocker_port, vid, NULL);
int flags = 0;

if (!rocker_port_is_bridged(rocker_port))
return -EINVAL;

return rocker_port_fdb(rocker_port, SWITCHDEV_TRANS_NONE,
addr, vlan_id, flags);
}

static int rocker_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid)
{
struct rocker_port *rocker_port = netdev_priv(dev);
__be16 vlan_id = rocker_port_vid_to_vlan(rocker_port, vid, NULL);
int flags = ROCKER_OP_FLAG_REMOVE;

if (!rocker_port_is_bridged(rocker_port))
return -EINVAL;

return rocker_port_fdb(rocker_port, SWITCHDEV_TRANS_NONE,
addr, vlan_id, flags);
}

static int rocker_fdb_fill_info(struct sk_buff *skb,
struct rocker_port *rocker_port,
const unsigned char *addr, u16 vid,
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_SELF;
ndm->ndm_type = 0;
ndm->ndm_ifindex = rocker_port->dev->ifindex;
ndm->ndm_state = 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;
}

static int rocker_port_fdb_dump(struct sk_buff *skb,
struct netlink_callback *cb,
struct net_device *dev,
struct net_device *filter_dev,
int idx)
{
struct rocker_port *rocker_port = netdev_priv(dev);
struct rocker *rocker = rocker_port->rocker;
struct rocker_fdb_tbl_entry *found;
struct hlist_node *tmp;
int bkt;
unsigned long lock_flags;
const unsigned char *addr;
u16 vid;
int err;

spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags);
hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, found, entry) {
if (found->key.pport != rocker_port->pport)
continue;
if (idx < cb->args[0])
goto skip;
addr = found->key.addr;
vid = rocker_port_vlan_to_vid(rocker_port, found->key.vlan_id);
err = rocker_fdb_fill_info(skb, rocker_port, addr, vid,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
RTM_NEWNEIGH, NLM_F_MULTI);
if (err < 0)
break;
skip:
++idx;
}
spin_unlock_irqrestore(&rocker->fdb_tbl_lock, lock_flags);
return idx;
}

static int rocker_port_get_phys_port_name(struct net_device *dev,
char *buf, size_t len)
{
Expand All @@ -4320,12 +4216,12 @@ static const struct net_device_ops rocker_port_netdev_ops = {
.ndo_set_mac_address = rocker_port_set_mac_address,
.ndo_vlan_rx_add_vid = rocker_port_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = rocker_port_vlan_rx_kill_vid,
.ndo_fdb_add = rocker_port_fdb_add,
.ndo_fdb_del = rocker_port_fdb_del,
.ndo_fdb_dump = rocker_port_fdb_dump,
.ndo_bridge_getlink = switchdev_port_bridge_getlink,
.ndo_bridge_setlink = switchdev_port_bridge_setlink,
.ndo_bridge_dellink = switchdev_port_bridge_dellink,
.ndo_fdb_add = switchdev_port_fdb_add,
.ndo_fdb_del = switchdev_port_fdb_del,
.ndo_fdb_dump = switchdev_port_fdb_dump,
.ndo_get_phys_port_name = rocker_port_get_phys_port_name,
};

Expand Down Expand Up @@ -4447,6 +4343,19 @@ static int rocker_port_vlans_add(struct rocker_port *rocker_port,
return 0;
}

static int rocker_port_fdb_add(struct rocker_port *rocker_port,
enum switchdev_trans trans,
struct switchdev_obj_fdb *fdb)
{
__be16 vlan_id = rocker_port_vid_to_vlan(rocker_port, fdb->vid, NULL);
int flags = 0;

if (!rocker_port_is_bridged(rocker_port))
return -EINVAL;

return rocker_port_fdb(rocker_port, trans, fdb->addr, vlan_id, flags);
}

static int rocker_port_obj_add(struct net_device *dev,
struct switchdev_obj *obj)
{
Expand Down Expand Up @@ -4476,6 +4385,9 @@ static int rocker_port_obj_add(struct net_device *dev,
htonl(fib4->dst), fib4->dst_len,
fib4->fi, fib4->tb_id, 0);
break;
case SWITCHDEV_OBJ_PORT_FDB:
err = rocker_port_fdb_add(rocker_port, obj->trans, &obj->u.fdb);
break;
default:
err = -EOPNOTSUPP;
break;
Expand Down Expand Up @@ -4513,6 +4425,19 @@ static int rocker_port_vlans_del(struct rocker_port *rocker_port,
return 0;
}

static int rocker_port_fdb_del(struct rocker_port *rocker_port,
enum switchdev_trans trans,
struct switchdev_obj_fdb *fdb)
{
__be16 vlan_id = rocker_port_vid_to_vlan(rocker_port, fdb->vid, NULL);
int flags = ROCKER_OP_FLAG_REMOVE;

if (!rocker_port_is_bridged(rocker_port))
return -EINVAL;

return rocker_port_fdb(rocker_port, trans, fdb->addr, vlan_id, flags);
}

static int rocker_port_obj_del(struct net_device *dev,
struct switchdev_obj *obj)
{
Expand All @@ -4531,6 +4456,54 @@ static int rocker_port_obj_del(struct net_device *dev,
fib4->fi, fib4->tb_id,
ROCKER_OP_FLAG_REMOVE);
break;
case SWITCHDEV_OBJ_PORT_FDB:
err = rocker_port_fdb_del(rocker_port, obj->trans, &obj->u.fdb);
break;
default:
err = -EOPNOTSUPP;
break;
}

return err;
}

static int rocker_port_fdb_dump(struct rocker_port *rocker_port,
struct switchdev_obj *obj)
{
struct rocker *rocker = rocker_port->rocker;
struct switchdev_obj_fdb *fdb = &obj->u.fdb;
struct rocker_fdb_tbl_entry *found;
struct hlist_node *tmp;
unsigned long lock_flags;
int bkt;
int err = 0;

spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags);
hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, found, entry) {
if (found->key.pport != rocker_port->pport)
continue;
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);
if (err)
break;
}
spin_unlock_irqrestore(&rocker->fdb_tbl_lock, lock_flags);

return err;
}

static int rocker_port_obj_dump(struct net_device *dev,
struct switchdev_obj *obj)
{
struct rocker_port *rocker_port = netdev_priv(dev);
int err = 0;

switch (obj->id) {
case SWITCHDEV_OBJ_PORT_FDB:
err = rocker_port_fdb_dump(rocker_port, obj);
break;
default:
err = -EOPNOTSUPP;
break;
Expand All @@ -4544,6 +4517,7 @@ static const struct switchdev_ops rocker_port_switchdev_ops = {
.switchdev_port_attr_set = rocker_port_attr_set,
.switchdev_port_obj_add = rocker_port_obj_add,
.switchdev_port_obj_del = rocker_port_obj_del,
.switchdev_port_obj_dump = rocker_port_obj_dump,
};

/********************
Expand Down
3 changes: 3 additions & 0 deletions drivers/net/team/team.c
Original file line number Diff line number Diff line change
Expand Up @@ -1980,6 +1980,9 @@ static const struct net_device_ops team_netdev_ops = {
.ndo_bridge_setlink = switchdev_port_bridge_setlink,
.ndo_bridge_getlink = switchdev_port_bridge_getlink,
.ndo_bridge_dellink = switchdev_port_bridge_dellink,
.ndo_fdb_add = switchdev_port_fdb_add,
.ndo_fdb_del = switchdev_port_fdb_del,
.ndo_fdb_dump = switchdev_port_fdb_dump,
.ndo_features_check = passthru_features_check,
};

Expand Down
50 changes: 50 additions & 0 deletions include/net/switchdev.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,13 @@ enum switchdev_obj_id {
SWITCHDEV_OBJ_UNDEFINED,
SWITCHDEV_OBJ_PORT_VLAN,
SWITCHDEV_OBJ_IPV4_FIB,
SWITCHDEV_OBJ_PORT_FDB,
};

struct switchdev_obj {
enum switchdev_obj_id id;
enum switchdev_trans trans;
int (*cb)(struct net_device *dev, struct switchdev_obj *obj);
union {
struct switchdev_obj_vlan { /* PORT_VLAN */
u16 flags;
Expand All @@ -67,6 +69,10 @@ struct switchdev_obj {
u32 nlflags;
u32 tb_id;
} ipv4_fib;
struct switchdev_obj_fdb { /* PORT_FDB */
const unsigned char *addr;
u16 vid;
} fdb;
} u;
};

Expand All @@ -80,6 +86,8 @@ struct switchdev_obj {
* @switchdev_port_obj_add: Add an object to port (see switchdev_obj).
*
* @switchdev_port_obj_del: Delete an object from port (see switchdev_obj).
*
* @switchdev_port_obj_dump: Dump port objects (see switchdev_obj).
*/
struct switchdev_ops {
int (*switchdev_port_attr_get)(struct net_device *dev,
Expand All @@ -90,6 +98,8 @@ struct switchdev_ops {
struct switchdev_obj *obj);
int (*switchdev_port_obj_del)(struct net_device *dev,
struct switchdev_obj *obj);
int (*switchdev_port_obj_dump)(struct net_device *dev,
struct switchdev_obj *obj);
};

enum switchdev_notifier_type {
Expand Down Expand Up @@ -121,6 +131,7 @@ int switchdev_port_attr_set(struct net_device *dev,
struct switchdev_attr *attr);
int switchdev_port_obj_add(struct net_device *dev, struct switchdev_obj *obj);
int switchdev_port_obj_del(struct net_device *dev, struct switchdev_obj *obj);
int switchdev_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj);
int register_switchdev_notifier(struct notifier_block *nb);
int unregister_switchdev_notifier(struct notifier_block *nb);
int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
Expand All @@ -137,6 +148,15 @@ int switchdev_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi,
int switchdev_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi,
u8 tos, u8 type, u32 tb_id);
void switchdev_fib_ipv4_abort(struct fib_info *fi);
int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, const unsigned char *addr,
u16 vid, u16 nlm_flags);
int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, const unsigned char *addr,
u16 vid);
int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
struct net_device *dev,
struct net_device *filter_dev, int idx);

#else

Expand Down Expand Up @@ -164,6 +184,12 @@ static inline int switchdev_port_obj_del(struct net_device *dev,
return -EOPNOTSUPP;
}

static inline int switchdev_port_obj_dump(struct net_device *dev,
struct switchdev_obj *obj)
{
return -EOPNOTSUPP;
}

static inline int register_switchdev_notifier(struct notifier_block *nb)
{
return 0;
Expand Down Expand Up @@ -221,6 +247,30 @@ static inline void switchdev_fib_ipv4_abort(struct fib_info *fi)
{
}

static inline int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr,
u16 vid, u16 nlm_flags)
{
return -EOPNOTSUPP;
}

static inline int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid)
{
return -EOPNOTSUPP;
}

static inline int switchdev_port_fdb_dump(struct sk_buff *skb,
struct netlink_callback *cb,
struct net_device *dev,
struct net_device *filter_dev,
int idx)
{
return -EOPNOTSUPP;
}

#endif

#endif /* _LINUX_SWITCHDEV_H_ */
Loading

0 comments on commit 45d4122

Please sign in to comment.