switchdev: add support for fdb add/del/dump via switchdev_port_obj ops.
authorSamudrala, Sridhar <sridhar.samudrala@intel.com>
Thu, 14 May 2015 04:55:43 +0000 (21:55 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 18 May 2015 02:49:09 +0000 (22:49 -0400)
- 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>
drivers/net/bonding/bond_main.c
drivers/net/ethernet/rocker/rocker.c
drivers/net/team/team.c
include/net/switchdev.h
net/switchdev/switchdev.c

index ef26e014705006aada58f153ba08651e44a808b3..2268438f3f638545bec32c0a317d0cc3eb1c2bc6 100644 (file)
@@ -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,
 };
 
index 3e13dcccd73d79136c3108d56fc2c2236aa68582..0f5e962d691c79ab0c439b953790fe4968448569 100644 (file)
@@ -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)
 {
@@ -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,
 };
 
@@ -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)
 {
@@ -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;
@@ -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)
 {
@@ -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;
@@ -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,
 };
 
 /********************
index 1ec035a53c3d3aa5927568e1fe06e0f275bdba89..daa054b3ff03ebf58f32c8bc7b1b012240895176 100644 (file)
@@ -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,
 };
 
index ea5b1c230d3d56374eaf713884bfd2546c10bbde..437f8fe75705baf56df2886748d8e49a2e00ffd5 100644 (file)
@@ -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;
@@ -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;
 };
 
@@ -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,
@@ -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 {
@@ -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,
@@ -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
 
@@ -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;
@@ -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_ */
index 0409f9b5bdbca771394203f7dabcb44dc3b82977..d4c8cf8282409a64cc0823cd994a1d2fe6d9c537 100644 (file)
@@ -296,6 +296,36 @@ int switchdev_port_obj_del(struct net_device *dev, struct switchdev_obj *obj)
 }
 EXPORT_SYMBOL_GPL(switchdev_port_obj_del);
 
+/**
+ *     switchdev_port_obj_dump - Dump port objects
+ *
+ *     @dev: port device
+ *     @obj: object to dump
+ */
+int switchdev_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj)
+{
+       const struct switchdev_ops *ops = dev->switchdev_ops;
+       struct net_device *lower_dev;
+       struct list_head *iter;
+       int err = -EOPNOTSUPP;
+
+       if (ops && ops->switchdev_port_obj_dump)
+               return ops->switchdev_port_obj_dump(dev, obj);
+
+       /* Switch device port(s) may be stacked under
+        * bond/team/vlan dev, so recurse down to dump objects on
+        * first port at bottom of stack.
+        */
+
+       netdev_for_each_lower_dev(dev, lower_dev, iter) {
+               err = switchdev_port_obj_dump(lower_dev, obj);
+               break;
+       }
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(switchdev_port_obj_dump);
+
 static DEFINE_MUTEX(switchdev_mutex);
 static RAW_NOTIFIER_HEAD(switchdev_notif_chain);
 
@@ -566,6 +596,151 @@ int switchdev_port_bridge_dellink(struct net_device *dev,
 }
 EXPORT_SYMBOL_GPL(switchdev_port_bridge_dellink);
 
+/**
+ *     switchdev_port_fdb_add - Add FDB (MAC/VLAN) entry to port
+ *
+ *     @ndmsg: netlink hdr
+ *     @nlattr: netlink attributes
+ *     @dev: port device
+ *     @addr: MAC address to add
+ *     @vid: VLAN to add
+ *
+ *     Add FDB entry to switch device.
+ */
+int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
+                          struct net_device *dev, const unsigned char *addr,
+                          u16 vid, u16 nlm_flags)
+{
+       struct switchdev_obj obj = {
+               .id = SWITCHDEV_OBJ_PORT_FDB,
+               .u.fdb = {
+                       .addr = addr,
+                       .vid = vid,
+               },
+       };
+
+       return switchdev_port_obj_add(dev, &obj);
+}
+EXPORT_SYMBOL_GPL(switchdev_port_fdb_add);
+
+/**
+ *     switchdev_port_fdb_del - Delete FDB (MAC/VLAN) entry from port
+ *
+ *     @ndmsg: netlink hdr
+ *     @nlattr: netlink attributes
+ *     @dev: port device
+ *     @addr: MAC address to delete
+ *     @vid: VLAN to delete
+ *
+ *     Delete FDB entry from switch device.
+ */
+int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
+                          struct net_device *dev, const unsigned char *addr,
+                          u16 vid)
+{
+       struct switchdev_obj obj = {
+               .id = SWITCHDEV_OBJ_PORT_FDB,
+               .u.fdb = {
+                       .addr = addr,
+                       .vid = vid,
+               },
+       };
+
+       return switchdev_port_obj_del(dev, &obj);
+}
+EXPORT_SYMBOL_GPL(switchdev_port_fdb_del);
+
+struct switchdev_fdb_dump {
+       struct switchdev_obj obj;
+       struct sk_buff *skb;
+       struct netlink_callback *cb;
+       struct net_device *filter_dev;
+       int idx;
+};
+
+static int switchdev_port_fdb_dump_cb(struct net_device *dev,
+                                     struct switchdev_obj *obj)
+{
+       struct switchdev_fdb_dump *dump =
+               container_of(obj, struct switchdev_fdb_dump, obj);
+       u32 portid = NETLINK_CB(dump->cb->skb).portid;
+       u32 seq = dump->cb->nlh->nlmsg_seq;
+       struct nlmsghdr *nlh;
+       struct ndmsg *ndm;
+       struct net_device *master = netdev_master_upper_dev_get(dev);
+
+       if (dump->idx < dump->cb->args[0])
+               goto skip;
+
+       if (master && dump->filter_dev != master)
+               goto skip;
+
+       nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH,
+                       sizeof(*ndm), NLM_F_MULTI);
+       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 = dev->ifindex;
+       ndm->ndm_state   = NUD_REACHABLE;
+
+       if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, obj->u.fdb.addr))
+               goto nla_put_failure;
+
+       if (obj->u.fdb.vid && nla_put_u16(dump->skb, NDA_VLAN, obj->u.fdb.vid))
+               goto nla_put_failure;
+
+       nlmsg_end(dump->skb, nlh);
+
+skip:
+       dump->idx++;
+       return 0;
+
+nla_put_failure:
+       nlmsg_cancel(dump->skb, nlh);
+       return -EMSGSIZE;
+}
+
+/**
+ *     switchdev_port_fdb_dump - Dump port FDB (MAC/VLAN) entries
+ *
+ *     @skb: netlink skb
+ *     @cb: netlink callback
+ *     @dev: port device
+ *     @filter_dev: filter device
+ *     @idx:
+ *
+ *     Delete FDB entry from switch device.
+ */
+int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
+                           struct net_device *dev,
+                           struct net_device *filter_dev, int idx)
+{
+       struct switchdev_fdb_dump dump = {
+               .obj = {
+                       .id = SWITCHDEV_OBJ_PORT_FDB,
+                       .cb = switchdev_port_fdb_dump_cb,
+               },
+               .skb = skb,
+               .cb = cb,
+               .filter_dev = filter_dev,
+               .idx = idx,
+       };
+       int err;
+
+       err = switchdev_port_obj_dump(dev, &dump.obj);
+       if (err)
+               return err;
+
+       return dump.idx;
+}
+EXPORT_SYMBOL_GPL(switchdev_port_fdb_dump);
+
 static struct net_device *switchdev_get_lowest_dev(struct net_device *dev)
 {
        const struct switchdev_ops *ops = dev->switchdev_ops;