vxlan: Add hardware FDB learning
authorPetr Machata <petrm@mellanox.com>
Wed, 21 Nov 2018 08:02:39 +0000 (08:02 +0000)
committerDavid S. Miller <davem@davemloft.net>
Thu, 22 Nov 2018 01:10:31 +0000 (17:10 -0800)
In order to allow devices to signal learning events to VXLAN, introduce
two new switchdev messages: SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE and
SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE.

Listen to these notifications in the vxlan driver. The FDB entries
learned this way have an NTF_EXT_LEARNED flag, and only entries marked
as such can be unlearned by the _DEL_ event. They are also immediately
marked as offloaded. This is the same behavior that the bridge driver
observes.

Signed-off-by: Petr Machata <petrm@mellanox.com>
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/vxlan.c
include/net/switchdev.h

index b50705a50686a68e808a5dc45b391a2c5996b8ca..03ba1b56ba6d6dcc7e8485e462328b021ae68032 100644 (file)
@@ -3923,18 +3923,89 @@ out:
        spin_unlock_bh(&vxlan->hash_lock);
 }
 
+static int
+vxlan_fdb_external_learn_add(struct net_device *dev,
+                            struct switchdev_notifier_vxlan_fdb_info *fdb_info)
+{
+       struct vxlan_dev *vxlan = netdev_priv(dev);
+       int err;
+
+       spin_lock_bh(&vxlan->hash_lock);
+       err = vxlan_fdb_update(vxlan, fdb_info->eth_addr, &fdb_info->remote_ip,
+                              NUD_REACHABLE,
+                              NLM_F_CREATE | NLM_F_REPLACE,
+                              fdb_info->remote_port,
+                              fdb_info->vni,
+                              fdb_info->remote_vni,
+                              fdb_info->remote_ifindex,
+                              NTF_USE | NTF_SELF | NTF_EXT_LEARNED,
+                              false);
+       spin_unlock_bh(&vxlan->hash_lock);
+
+       return err;
+}
+
+static int
+vxlan_fdb_external_learn_del(struct net_device *dev,
+                            struct switchdev_notifier_vxlan_fdb_info *fdb_info)
+{
+       struct vxlan_dev *vxlan = netdev_priv(dev);
+       struct vxlan_fdb *f;
+       int err = 0;
+
+       spin_lock_bh(&vxlan->hash_lock);
+
+       f = vxlan_find_mac(vxlan, fdb_info->eth_addr, fdb_info->vni);
+       if (!f)
+               err = -ENOENT;
+       else if (f->flags & NTF_EXT_LEARNED)
+               err = __vxlan_fdb_delete(vxlan, fdb_info->eth_addr,
+                                        fdb_info->remote_ip,
+                                        fdb_info->remote_port,
+                                        fdb_info->vni,
+                                        fdb_info->remote_vni,
+                                        fdb_info->remote_ifindex,
+                                        false);
+
+       spin_unlock_bh(&vxlan->hash_lock);
+
+       return err;
+}
+
 static int vxlan_switchdev_event(struct notifier_block *unused,
                                 unsigned long event, void *ptr)
 {
        struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+       struct switchdev_notifier_vxlan_fdb_info *fdb_info;
+       int err = 0;
 
        switch (event) {
        case SWITCHDEV_VXLAN_FDB_OFFLOADED:
                vxlan_fdb_offloaded_set(dev, ptr);
                break;
+       case SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE:
+               fdb_info = ptr;
+               err = vxlan_fdb_external_learn_add(dev, fdb_info);
+               if (err) {
+                       err = notifier_from_errno(err);
+                       break;
+               }
+               fdb_info->offloaded = true;
+               vxlan_fdb_offloaded_set(dev, fdb_info);
+               break;
+       case SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE:
+               fdb_info = ptr;
+               err = vxlan_fdb_external_learn_del(dev, fdb_info);
+               if (err) {
+                       err = notifier_from_errno(err);
+                       break;
+               }
+               fdb_info->offloaded = false;
+               vxlan_fdb_offloaded_set(dev, fdb_info);
+               break;
        }
 
-       return 0;
+       return err;
 }
 
 static struct notifier_block vxlan_switchdev_notifier_block __read_mostly = {
index 881ecb1555bf1a40cd873ba21f16065112afade5..7b371e7c4bc676a0d346b69909f5a5833b898a0b 100644 (file)
@@ -146,6 +146,8 @@ enum switchdev_notifier_type {
        SWITCHDEV_FDB_DEL_TO_DEVICE,
        SWITCHDEV_FDB_OFFLOADED,
 
+       SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE,
+       SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE,
        SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE,
        SWITCHDEV_VXLAN_FDB_DEL_TO_DEVICE,
        SWITCHDEV_VXLAN_FDB_OFFLOADED,