Merge tag 'mmc-v3.18-1' of git://git.linaro.org/people/ulf.hansson/mmc
[cascardo/linux.git] / net / ipv6 / anycast.c
index 9a38684..f5e319a 100644 (file)
 
 static int ipv6_dev_ac_dec(struct net_device *dev, const struct in6_addr *addr);
 
-/* Big ac list lock for all the sockets */
-static DEFINE_SPINLOCK(ipv6_sk_ac_lock);
-
-
 /*
  *     socket join an anycast group
  */
@@ -78,7 +74,6 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
        pac->acl_addr = *addr;
 
        rtnl_lock();
-       rcu_read_lock();
        if (ifindex == 0) {
                struct rt6_info *rt;
 
@@ -91,11 +86,11 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
                        goto error;
                } else {
                        /* router, no matching interface: just pick one */
-                       dev = dev_get_by_flags_rcu(net, IFF_UP,
-                                                  IFF_UP | IFF_LOOPBACK);
+                       dev = __dev_get_by_flags(net, IFF_UP,
+                                                IFF_UP | IFF_LOOPBACK);
                }
        } else
-               dev = dev_get_by_index_rcu(net, ifindex);
+               dev = __dev_get_by_index(net, ifindex);
 
        if (dev == NULL) {
                err = -ENODEV;
@@ -127,17 +122,14 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
                        goto error;
        }
 
-       err = ipv6_dev_ac_inc(dev, addr);
+       err = __ipv6_dev_ac_inc(idev, addr);
        if (!err) {
-               spin_lock_bh(&ipv6_sk_ac_lock);
                pac->acl_next = np->ipv6_ac_list;
                np->ipv6_ac_list = pac;
-               spin_unlock_bh(&ipv6_sk_ac_lock);
                pac = NULL;
        }
 
 error:
-       rcu_read_unlock();
        rtnl_unlock();
        if (pac)
                sock_kfree_s(sk, pac, sizeof(*pac));
@@ -154,7 +146,7 @@ int ipv6_sock_ac_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
        struct ipv6_ac_socklist *pac, *prev_pac;
        struct net *net = sock_net(sk);
 
-       spin_lock_bh(&ipv6_sk_ac_lock);
+       rtnl_lock();
        prev_pac = NULL;
        for (pac = np->ipv6_ac_list; pac; pac = pac->acl_next) {
                if ((ifindex == 0 || pac->acl_ifindex == ifindex) &&
@@ -163,7 +155,7 @@ int ipv6_sock_ac_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
                prev_pac = pac;
        }
        if (!pac) {
-               spin_unlock_bh(&ipv6_sk_ac_lock);
+               rtnl_unlock();
                return -ENOENT;
        }
        if (prev_pac)
@@ -171,14 +163,9 @@ int ipv6_sock_ac_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
        else
                np->ipv6_ac_list = pac->acl_next;
 
-       spin_unlock_bh(&ipv6_sk_ac_lock);
-
-       rtnl_lock();
-       rcu_read_lock();
-       dev = dev_get_by_index_rcu(net, pac->acl_ifindex);
+       dev = __dev_get_by_index(net, pac->acl_ifindex);
        if (dev)
                ipv6_dev_ac_dec(dev, &pac->acl_addr);
-       rcu_read_unlock();
        rtnl_unlock();
 
        sock_kfree_s(sk, pac, sizeof(*pac));
@@ -196,19 +183,16 @@ void ipv6_sock_ac_close(struct sock *sk)
        if (!np->ipv6_ac_list)
                return;
 
-       spin_lock_bh(&ipv6_sk_ac_lock);
+       rtnl_lock();
        pac = np->ipv6_ac_list;
        np->ipv6_ac_list = NULL;
-       spin_unlock_bh(&ipv6_sk_ac_lock);
 
        prev_index = 0;
-       rtnl_lock();
-       rcu_read_lock();
        while (pac) {
                struct ipv6_ac_socklist *next = pac->acl_next;
 
                if (pac->acl_ifindex != prev_index) {
-                       dev = dev_get_by_index_rcu(net, pac->acl_ifindex);
+                       dev = __dev_get_by_index(net, pac->acl_ifindex);
                        prev_index = pac->acl_ifindex;
                }
                if (dev)
@@ -216,10 +200,14 @@ void ipv6_sock_ac_close(struct sock *sk)
                sock_kfree_s(sk, pac, sizeof(*pac));
                pac = next;
        }
-       rcu_read_unlock();
        rtnl_unlock();
 }
 
+static void aca_get(struct ifacaddr6 *aca)
+{
+       atomic_inc(&aca->aca_refcnt);
+}
+
 static void aca_put(struct ifacaddr6 *ac)
 {
        if (atomic_dec_and_test(&ac->aca_refcnt)) {
@@ -229,23 +217,40 @@ static void aca_put(struct ifacaddr6 *ac)
        }
 }
 
+static struct ifacaddr6 *aca_alloc(struct rt6_info *rt,
+                                  const struct in6_addr *addr)
+{
+       struct inet6_dev *idev = rt->rt6i_idev;
+       struct ifacaddr6 *aca;
+
+       aca = kzalloc(sizeof(*aca), GFP_ATOMIC);
+       if (aca == NULL)
+               return NULL;
+
+       aca->aca_addr = *addr;
+       in6_dev_hold(idev);
+       aca->aca_idev = idev;
+       aca->aca_rt = rt;
+       aca->aca_users = 1;
+       /* aca_tstamp should be updated upon changes */
+       aca->aca_cstamp = aca->aca_tstamp = jiffies;
+       atomic_set(&aca->aca_refcnt, 1);
+       spin_lock_init(&aca->aca_lock);
+
+       return aca;
+}
+
 /*
  *     device anycast group inc (add if not found)
  */
-int ipv6_dev_ac_inc(struct net_device *dev, const struct in6_addr *addr)
+int __ipv6_dev_ac_inc(struct inet6_dev *idev, const struct in6_addr *addr)
 {
        struct ifacaddr6 *aca;
-       struct inet6_dev *idev;
        struct rt6_info *rt;
        int err;
 
        ASSERT_RTNL();
 
-       idev = in6_dev_get(dev);
-
-       if (idev == NULL)
-               return -EINVAL;
-
        write_lock_bh(&idev->lock);
        if (idev->dead) {
                err = -ENODEV;
@@ -260,46 +265,35 @@ int ipv6_dev_ac_inc(struct net_device *dev, const struct in6_addr *addr)
                }
        }
 
-       /*
-        *      not found: create a new one.
-        */
-
-       aca = kzalloc(sizeof(struct ifacaddr6), GFP_ATOMIC);
-
-       if (aca == NULL) {
-               err = -ENOMEM;
-               goto out;
-       }
-
        rt = addrconf_dst_alloc(idev, addr, true);
        if (IS_ERR(rt)) {
-               kfree(aca);
                err = PTR_ERR(rt);
                goto out;
        }
-
-       aca->aca_addr = *addr;
-       aca->aca_idev = idev;
-       aca->aca_rt = rt;
-       aca->aca_users = 1;
-       /* aca_tstamp should be updated upon changes */
-       aca->aca_cstamp = aca->aca_tstamp = jiffies;
-       atomic_set(&aca->aca_refcnt, 2);
-       spin_lock_init(&aca->aca_lock);
+       aca = aca_alloc(rt, addr);
+       if (aca == NULL) {
+               ip6_rt_put(rt);
+               err = -ENOMEM;
+               goto out;
+       }
 
        aca->aca_next = idev->ac_list;
        idev->ac_list = aca;
+
+       /* Hold this for addrconf_join_solict() below before we unlock,
+        * it is already exposed via idev->ac_list.
+        */
+       aca_get(aca);
        write_unlock_bh(&idev->lock);
 
        ip6_ins_rt(rt);
 
-       addrconf_join_solict(dev, &aca->aca_addr);
+       addrconf_join_solict(idev->dev, &aca->aca_addr);
 
        aca_put(aca);
        return 0;
 out:
        write_unlock_bh(&idev->lock);
-       in6_dev_put(idev);
        return err;
 }
 
@@ -341,7 +335,7 @@ int __ipv6_dev_ac_dec(struct inet6_dev *idev, const struct in6_addr *addr)
        return 0;
 }
 
-/* called with rcu_read_lock() */
+/* called with rtnl_lock() */
 static int ipv6_dev_ac_dec(struct net_device *dev, const struct in6_addr *addr)
 {
        struct inet6_dev *idev = __in6_dev_get(dev);