Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[linux-2.6-block.git] / drivers / net / vxlan.c
index b4283f52a09d135b32bc5bf4b193cfae63ded7ee..3d9bcc957f7da15b58aad5d9cb6acdb9803f4553 100644 (file)
@@ -468,14 +468,19 @@ static u32 eth_vni_hash(const unsigned char *addr, __be32 vni)
        return jhash_2words(key, vni, vxlan_salt) & (FDB_HASH_SIZE - 1);
 }
 
+static u32 fdb_head_index(struct vxlan_dev *vxlan, const u8 *mac, __be32 vni)
+{
+       if (vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA)
+               return eth_vni_hash(mac, vni);
+       else
+               return eth_hash(mac);
+}
+
 /* Hash chain to use given mac address */
 static inline struct hlist_head *vxlan_fdb_head(struct vxlan_dev *vxlan,
                                                const u8 *mac, __be32 vni)
 {
-       if (vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA)
-               return &vxlan->fdb_head[eth_vni_hash(mac, vni)];
-       else
-               return &vxlan->fdb_head[eth_hash(mac)];
+       return &vxlan->fdb_head[fdb_head_index(vxlan, mac, vni)];
 }
 
 /* Look up Ethernet address in forwarding table */
@@ -590,8 +595,8 @@ int vxlan_fdb_replay(const struct net_device *dev, __be32 vni,
                return -EINVAL;
        vxlan = netdev_priv(dev);
 
-       spin_lock_bh(&vxlan->hash_lock);
        for (h = 0; h < FDB_HASH_SIZE; ++h) {
+               spin_lock_bh(&vxlan->hash_lock[h]);
                hlist_for_each_entry(f, &vxlan->fdb_head[h], hlist) {
                        if (f->vni == vni) {
                                list_for_each_entry(rdst, &f->remotes, list) {
@@ -599,14 +604,16 @@ int vxlan_fdb_replay(const struct net_device *dev, __be32 vni,
                                                                  f, rdst,
                                                                  extack);
                                        if (rc)
-                                               goto out;
+                                               goto unlock;
                                }
                        }
                }
+               spin_unlock_bh(&vxlan->hash_lock[h]);
        }
+       return 0;
 
-out:
-       spin_unlock_bh(&vxlan->hash_lock);
+unlock:
+       spin_unlock_bh(&vxlan->hash_lock[h]);
        return rc;
 }
 EXPORT_SYMBOL_GPL(vxlan_fdb_replay);
@@ -622,14 +629,15 @@ void vxlan_fdb_clear_offload(const struct net_device *dev, __be32 vni)
                return;
        vxlan = netdev_priv(dev);
 
-       spin_lock_bh(&vxlan->hash_lock);
        for (h = 0; h < FDB_HASH_SIZE; ++h) {
+               spin_lock_bh(&vxlan->hash_lock[h]);
                hlist_for_each_entry(f, &vxlan->fdb_head[h], hlist)
                        if (f->vni == vni)
                                list_for_each_entry(rdst, &f->remotes, list)
                                        rdst->offloaded = false;
+               spin_unlock_bh(&vxlan->hash_lock[h]);
        }
-       spin_unlock_bh(&vxlan->hash_lock);
+
 }
 EXPORT_SYMBOL_GPL(vxlan_fdb_clear_offload);
 
@@ -1116,6 +1124,7 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
        __be16 port;
        __be32 src_vni, vni;
        u32 ifindex;
+       u32 hash_index;
        int err;
 
        if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_REACHABLE))) {
@@ -1134,12 +1143,13 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
        if (vxlan->default_dst.remote_ip.sa.sa_family != ip.sa.sa_family)
                return -EAFNOSUPPORT;
 
-       spin_lock_bh(&vxlan->hash_lock);
+       hash_index = fdb_head_index(vxlan, addr, src_vni);
+       spin_lock_bh(&vxlan->hash_lock[hash_index]);
        err = vxlan_fdb_update(vxlan, addr, &ip, ndm->ndm_state, flags,
                               port, src_vni, vni, ifindex,
                               ndm->ndm_flags | NTF_VXLAN_ADDED_BY_USER,
                               true, extack);
-       spin_unlock_bh(&vxlan->hash_lock);
+       spin_unlock_bh(&vxlan->hash_lock[hash_index]);
 
        return err;
 }
@@ -1187,16 +1197,18 @@ static int vxlan_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
        __be32 src_vni, vni;
        __be16 port;
        u32 ifindex;
+       u32 hash_index;
        int err;
 
        err = vxlan_fdb_parse(tb, vxlan, &ip, &port, &src_vni, &vni, &ifindex);
        if (err)
                return err;
 
-       spin_lock_bh(&vxlan->hash_lock);
+       hash_index = fdb_head_index(vxlan, addr, src_vni);
+       spin_lock_bh(&vxlan->hash_lock[hash_index]);
        err = __vxlan_fdb_delete(vxlan, addr, ip, port, src_vni, vni, ifindex,
                                 true);
-       spin_unlock_bh(&vxlan->hash_lock);
+       spin_unlock_bh(&vxlan->hash_lock[hash_index]);
 
        return err;
 }
@@ -1308,8 +1320,10 @@ static bool vxlan_snoop(struct net_device *dev,
                f->updated = jiffies;
                vxlan_fdb_notify(vxlan, f, rdst, RTM_NEWNEIGH, true, NULL);
        } else {
+               u32 hash_index = fdb_head_index(vxlan, src_mac, vni);
+
                /* learned new entry */
-               spin_lock(&vxlan->hash_lock);
+               spin_lock(&vxlan->hash_lock[hash_index]);
 
                /* close off race between vxlan_flush and incoming packets */
                if (netif_running(dev))
@@ -1320,7 +1334,7 @@ static bool vxlan_snoop(struct net_device *dev,
                                         vni,
                                         vxlan->default_dst.remote_vni,
                                         ifindex, NTF_SELF, true, NULL);
-               spin_unlock(&vxlan->hash_lock);
+               spin_unlock(&vxlan->hash_lock[hash_index]);
        }
 
        return false;
@@ -2230,7 +2244,7 @@ static struct rtable *vxlan_get_route(struct vxlan_dev *vxlan, struct net_device
        fl4.fl4_sport = sport;
 
        rt = ip_route_output_key(vxlan->net, &fl4);
-       if (likely(!IS_ERR(rt))) {
+       if (!IS_ERR(rt)) {
                if (rt->dst.dev == dev) {
                        netdev_dbg(dev, "circular route to %pI4\n", &daddr);
                        ip_rt_put(rt);
@@ -2710,7 +2724,7 @@ static void vxlan_cleanup(struct timer_list *t)
        for (h = 0; h < FDB_HASH_SIZE; ++h) {
                struct hlist_node *p, *n;
 
-               spin_lock(&vxlan->hash_lock);
+               spin_lock(&vxlan->hash_lock[h]);
                hlist_for_each_safe(p, n, &vxlan->fdb_head[h]) {
                        struct vxlan_fdb *f
                                = container_of(p, struct vxlan_fdb, hlist);
@@ -2732,7 +2746,7 @@ static void vxlan_cleanup(struct timer_list *t)
                        } else if (time_before(timeout, next_timer))
                                next_timer = timeout;
                }
-               spin_unlock(&vxlan->hash_lock);
+               spin_unlock(&vxlan->hash_lock[h]);
        }
 
        mod_timer(&vxlan->age_timer, next_timer);
@@ -2775,12 +2789,13 @@ static int vxlan_init(struct net_device *dev)
 static void vxlan_fdb_delete_default(struct vxlan_dev *vxlan, __be32 vni)
 {
        struct vxlan_fdb *f;
+       u32 hash_index = fdb_head_index(vxlan, all_zeros_mac, vni);
 
-       spin_lock_bh(&vxlan->hash_lock);
+       spin_lock_bh(&vxlan->hash_lock[hash_index]);
        f = __vxlan_find_mac(vxlan, all_zeros_mac, vni);
        if (f)
                vxlan_fdb_destroy(vxlan, f, true, true);
-       spin_unlock_bh(&vxlan->hash_lock);
+       spin_unlock_bh(&vxlan->hash_lock[hash_index]);
 }
 
 static void vxlan_uninit(struct net_device *dev)
@@ -2825,9 +2840,10 @@ static void vxlan_flush(struct vxlan_dev *vxlan, bool do_all)
 {
        unsigned int h;
 
-       spin_lock_bh(&vxlan->hash_lock);
        for (h = 0; h < FDB_HASH_SIZE; ++h) {
                struct hlist_node *p, *n;
+
+               spin_lock_bh(&vxlan->hash_lock[h]);
                hlist_for_each_safe(p, n, &vxlan->fdb_head[h]) {
                        struct vxlan_fdb *f
                                = container_of(p, struct vxlan_fdb, hlist);
@@ -2837,8 +2853,8 @@ static void vxlan_flush(struct vxlan_dev *vxlan, bool do_all)
                        if (!is_zero_ether_addr(f->eth_addr))
                                vxlan_fdb_destroy(vxlan, f, true, true);
                }
+               spin_unlock_bh(&vxlan->hash_lock[h]);
        }
-       spin_unlock_bh(&vxlan->hash_lock);
 }
 
 /* Cleanup timer and forwarding table on shutdown */
@@ -3022,7 +3038,6 @@ static void vxlan_setup(struct net_device *dev)
        dev->max_mtu = ETH_MAX_MTU;
 
        INIT_LIST_HEAD(&vxlan->next);
-       spin_lock_init(&vxlan->hash_lock);
 
        timer_setup(&vxlan->age_timer, vxlan_cleanup, TIMER_DEFERRABLE);
 
@@ -3030,8 +3045,10 @@ static void vxlan_setup(struct net_device *dev)
 
        gro_cells_init(&vxlan->gro_cells, dev);
 
-       for (h = 0; h < FDB_HASH_SIZE; ++h)
+       for (h = 0; h < FDB_HASH_SIZE; ++h) {
+               spin_lock_init(&vxlan->hash_lock[h]);
                INIT_HLIST_HEAD(&vxlan->fdb_head[h]);
+       }
 }
 
 static void vxlan_ether_setup(struct net_device *dev)
@@ -3931,7 +3948,9 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
 
        /* handle default dst entry */
        if (!vxlan_addr_equal(&conf.remote_ip, &dst->remote_ip)) {
-               spin_lock_bh(&vxlan->hash_lock);
+               u32 hash_index = fdb_head_index(vxlan, all_zeros_mac, conf.vni);
+
+               spin_lock_bh(&vxlan->hash_lock[hash_index]);
                if (!vxlan_addr_any(&conf.remote_ip)) {
                        err = vxlan_fdb_update(vxlan, all_zeros_mac,
                                               &conf.remote_ip,
@@ -3942,7 +3961,7 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
                                               conf.remote_ifindex,
                                               NTF_SELF, true, extack);
                        if (err) {
-                               spin_unlock_bh(&vxlan->hash_lock);
+                               spin_unlock_bh(&vxlan->hash_lock[hash_index]);
                                return err;
                        }
                }
@@ -3954,7 +3973,7 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
                                           dst->remote_vni,
                                           dst->remote_ifindex,
                                           true);
-               spin_unlock_bh(&vxlan->hash_lock);
+               spin_unlock_bh(&vxlan->hash_lock[hash_index]);
        }
 
        if (conf.age_interval != vxlan->cfg.age_interval)
@@ -4209,8 +4228,11 @@ vxlan_fdb_offloaded_set(struct net_device *dev,
        struct vxlan_dev *vxlan = netdev_priv(dev);
        struct vxlan_rdst *rdst;
        struct vxlan_fdb *f;
+       u32 hash_index;
+
+       hash_index = fdb_head_index(vxlan, fdb_info->eth_addr, fdb_info->vni);
 
-       spin_lock_bh(&vxlan->hash_lock);
+       spin_lock_bh(&vxlan->hash_lock[hash_index]);
 
        f = vxlan_find_mac(vxlan, fdb_info->eth_addr, fdb_info->vni);
        if (!f)
@@ -4226,7 +4248,7 @@ vxlan_fdb_offloaded_set(struct net_device *dev,
        rdst->offloaded = fdb_info->offloaded;
 
 out:
-       spin_unlock_bh(&vxlan->hash_lock);
+       spin_unlock_bh(&vxlan->hash_lock[hash_index]);
 }
 
 static int
@@ -4235,11 +4257,13 @@ vxlan_fdb_external_learn_add(struct net_device *dev,
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
        struct netlink_ext_ack *extack;
+       u32 hash_index;
        int err;
 
+       hash_index = fdb_head_index(vxlan, fdb_info->eth_addr, fdb_info->vni);
        extack = switchdev_notifier_info_to_extack(&fdb_info->info);
 
-       spin_lock_bh(&vxlan->hash_lock);
+       spin_lock_bh(&vxlan->hash_lock[hash_index]);
        err = vxlan_fdb_update(vxlan, fdb_info->eth_addr, &fdb_info->remote_ip,
                               NUD_REACHABLE,
                               NLM_F_CREATE | NLM_F_REPLACE,
@@ -4249,7 +4273,7 @@ vxlan_fdb_external_learn_add(struct net_device *dev,
                               fdb_info->remote_ifindex,
                               NTF_USE | NTF_SELF | NTF_EXT_LEARNED,
                               false, extack);
-       spin_unlock_bh(&vxlan->hash_lock);
+       spin_unlock_bh(&vxlan->hash_lock[hash_index]);
 
        return err;
 }
@@ -4260,9 +4284,11 @@ vxlan_fdb_external_learn_del(struct net_device *dev,
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
        struct vxlan_fdb *f;
+       u32 hash_index;
        int err = 0;
 
-       spin_lock_bh(&vxlan->hash_lock);
+       hash_index = fdb_head_index(vxlan, fdb_info->eth_addr, fdb_info->vni);
+       spin_lock_bh(&vxlan->hash_lock[hash_index]);
 
        f = vxlan_find_mac(vxlan, fdb_info->eth_addr, fdb_info->vni);
        if (!f)
@@ -4276,7 +4302,7 @@ vxlan_fdb_external_learn_del(struct net_device *dev,
                                         fdb_info->remote_ifindex,
                                         false);
 
-       spin_unlock_bh(&vxlan->hash_lock);
+       spin_unlock_bh(&vxlan->hash_lock[hash_index]);
 
        return err;
 }