anycast: Some RCU conversions
authorEric Dumazet <eric.dumazet@gmail.com>
Mon, 7 Jun 2010 11:42:13 +0000 (11:42 +0000)
committerDavid S. Miller <davem@davemloft.net>
Tue, 8 Jun 2010 05:49:25 +0000 (22:49 -0700)
- dev_get_by_flags() changed to dev_get_by_flags_rcu()

- ipv6_sock_ac_join() dont touch dev & idev refcounts
- ipv6_sock_ac_drop() dont touch dev & idev refcounts
- ipv6_sock_ac_close() dont touch dev & idev refcounts
- ipv6_dev_ac_dec() dount touch idev refcount
- ipv6_chk_acast_addr() dont touch idev refcount

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
CC: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/netdevice.h
net/core/dev.c
net/ipv6/anycast.c

index 5156b806924c01565cb7aefc8152fc91460aba5e..c319f28d699df8d75af3c00597512760f62891ab 100644 (file)
@@ -1271,8 +1271,8 @@ extern void               dev_add_pack(struct packet_type *pt);
 extern void            dev_remove_pack(struct packet_type *pt);
 extern void            __dev_remove_pack(struct packet_type *pt);
 
-extern struct net_device       *dev_get_by_flags(struct net *net, unsigned short flags,
-                                                 unsigned short mask);
+extern struct net_device       *dev_get_by_flags_rcu(struct net *net, unsigned short flags,
+                                                     unsigned short mask);
 extern struct net_device       *dev_get_by_name(struct net *net, const char *name);
 extern struct net_device       *dev_get_by_name_rcu(struct net *net, const char *name);
 extern struct net_device       *__dev_get_by_name(struct net *net, const char *name);
index c8d127718ff191a78c80acf81eec26d95c791cda..6f330cee79a6720bf2ca0ce7c4261dd7651ef9d2 100644 (file)
@@ -803,35 +803,31 @@ struct net_device *dev_getfirstbyhwtype(struct net *net, unsigned short type)
 EXPORT_SYMBOL(dev_getfirstbyhwtype);
 
 /**
- *     dev_get_by_flags - find any device with given flags
+ *     dev_get_by_flags_rcu - find any device with given flags
  *     @net: the applicable net namespace
  *     @if_flags: IFF_* values
  *     @mask: bitmask of bits in if_flags to check
  *
  *     Search for any interface with the given flags. Returns NULL if a device
- *     is not found or a pointer to the device. The device returned has
- *     had a reference added and the pointer is safe until the user calls
- *     dev_put to indicate they have finished with it.
+ *     is not found or a pointer to the device. Must be called inside
+ *     rcu_read_lock(), and result refcount is unchanged.
  */
 
-struct net_device *dev_get_by_flags(struct net *net, unsigned short if_flags,
+struct net_device *dev_get_by_flags_rcu(struct net *net, unsigned short if_flags,
                                    unsigned short mask)
 {
        struct net_device *dev, *ret;
 
        ret = NULL;
-       rcu_read_lock();
        for_each_netdev_rcu(net, dev) {
                if (((dev->flags ^ if_flags) & mask) == 0) {
-                       dev_hold(dev);
                        ret = dev;
                        break;
                }
        }
-       rcu_read_unlock();
        return ret;
 }
-EXPORT_SYMBOL(dev_get_by_flags);
+EXPORT_SYMBOL(dev_get_by_flags_rcu);
 
 /**
  *     dev_valid_name - check if name is okay for network device
index b5b07054508a5154315c99f0da67b903e9ce46ee..f058fbd808c8b8370de696ff1567a4fe3f0293a6 100644 (file)
@@ -77,41 +77,40 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr)
        pac->acl_next = NULL;
        ipv6_addr_copy(&pac->acl_addr, addr);
 
+       rcu_read_lock();
        if (ifindex == 0) {
                struct rt6_info *rt;
 
                rt = rt6_lookup(net, addr, NULL, 0, 0);
                if (rt) {
                        dev = rt->rt6i_dev;
-                       dev_hold(dev);
                        dst_release(&rt->u.dst);
                } else if (ishost) {
                        err = -EADDRNOTAVAIL;
-                       goto out_free_pac;
+                       goto error;
                } else {
                        /* router, no matching interface: just pick one */
-
-                       dev = dev_get_by_flags(net, IFF_UP, IFF_UP|IFF_LOOPBACK);
+                       dev = dev_get_by_flags_rcu(net, IFF_UP,
+                                                  IFF_UP | IFF_LOOPBACK);
                }
        } else
-               dev = dev_get_by_index(net, ifindex);
+               dev = dev_get_by_index_rcu(net, ifindex);
 
        if (dev == NULL) {
                err = -ENODEV;
-               goto out_free_pac;
+               goto error;
        }
 
-       idev = in6_dev_get(dev);
+       idev = __in6_dev_get(dev);
        if (!idev) {
                if (ifindex)
                        err = -ENODEV;
                else
                        err = -EADDRNOTAVAIL;
-               goto out_dev_put;
+               goto error;
        }
        /* reset ishost, now that we have a specific device */
        ishost = !idev->cnf.forwarding;
-       in6_dev_put(idev);
 
        pac->acl_ifindex = dev->ifindex;
 
@@ -124,26 +123,22 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr)
                if (ishost)
                        err = -EADDRNOTAVAIL;
                if (err)
-                       goto out_dev_put;
+                       goto error;
        }
 
        err = ipv6_dev_ac_inc(dev, addr);
-       if (err)
-               goto out_dev_put;
-
-       write_lock_bh(&ipv6_sk_ac_lock);
-       pac->acl_next = np->ipv6_ac_list;
-       np->ipv6_ac_list = pac;
-       write_unlock_bh(&ipv6_sk_ac_lock);
-
-       dev_put(dev);
-
-       return 0;
+       if (!err) {
+               write_lock_bh(&ipv6_sk_ac_lock);
+               pac->acl_next = np->ipv6_ac_list;
+               np->ipv6_ac_list = pac;
+               write_unlock_bh(&ipv6_sk_ac_lock);
+               pac = NULL;
+       }
 
-out_dev_put:
-       dev_put(dev);
-out_free_pac:
-       sock_kfree_s(sk, pac, sizeof(*pac));
+error:
+       rcu_read_unlock();
+       if (pac)
+               sock_kfree_s(sk, pac, sizeof(*pac));
        return err;
 }
 
@@ -176,11 +171,12 @@ int ipv6_sock_ac_drop(struct sock *sk, int ifindex, struct in6_addr *addr)
 
        write_unlock_bh(&ipv6_sk_ac_lock);
 
-       dev = dev_get_by_index(net, pac->acl_ifindex);
-       if (dev) {
+       rcu_read_lock();
+       dev = dev_get_by_index_rcu(net, pac->acl_ifindex);
+       if (dev)
                ipv6_dev_ac_dec(dev, &pac->acl_addr);
-               dev_put(dev);
-       }
+       rcu_read_unlock();
+
        sock_kfree_s(sk, pac, sizeof(*pac));
        return 0;
 }
@@ -199,13 +195,12 @@ void ipv6_sock_ac_close(struct sock *sk)
        write_unlock_bh(&ipv6_sk_ac_lock);
 
        prev_index = 0;
+       rcu_read_lock();
        while (pac) {
                struct ipv6_ac_socklist *next = pac->acl_next;
 
                if (pac->acl_ifindex != prev_index) {
-                       if (dev)
-                               dev_put(dev);
-                       dev = dev_get_by_index(net, pac->acl_ifindex);
+                       dev = dev_get_by_index_rcu(net, pac->acl_ifindex);
                        prev_index = pac->acl_ifindex;
                }
                if (dev)
@@ -213,8 +208,7 @@ void ipv6_sock_ac_close(struct sock *sk)
                sock_kfree_s(sk, pac, sizeof(*pac));
                pac = next;
        }
-       if (dev)
-               dev_put(dev);
+       rcu_read_unlock();
 }
 
 #if 0
@@ -363,33 +357,32 @@ int __ipv6_dev_ac_dec(struct inet6_dev *idev, struct in6_addr *addr)
        return 0;
 }
 
+/* called with rcu_read_lock() */
 static int ipv6_dev_ac_dec(struct net_device *dev, struct in6_addr *addr)
 {
-       int ret;
-       struct inet6_dev *idev = in6_dev_get(dev);
+       struct inet6_dev *idev = __in6_dev_get(dev);
+
        if (idev == NULL)
                return -ENODEV;
-       ret = __ipv6_dev_ac_dec(idev, addr);
-       in6_dev_put(idev);
-       return ret;
+       return __ipv6_dev_ac_dec(idev, addr);
 }
 
 /*
  *     check if the interface has this anycast address
+ *     called with rcu_read_lock()
  */
 static int ipv6_chk_acast_dev(struct net_device *dev, struct in6_addr *addr)
 {
        struct inet6_dev *idev;
        struct ifacaddr6 *aca;
 
-       idev = in6_dev_get(dev);
+       idev = __in6_dev_get(dev);
        if (idev) {
                read_lock_bh(&idev->lock);
                for (aca = idev->ac_list; aca; aca = aca->aca_next)
                        if (ipv6_addr_equal(&aca->aca_addr, addr))
                                break;
                read_unlock_bh(&idev->lock);
-               in6_dev_put(idev);
                return aca != NULL;
        }
        return 0;
@@ -403,14 +396,15 @@ int ipv6_chk_acast_addr(struct net *net, struct net_device *dev,
 {
        int found = 0;
 
-       if (dev)
-               return ipv6_chk_acast_dev(dev, addr);
        rcu_read_lock();
-       for_each_netdev_rcu(net, dev)
-               if (ipv6_chk_acast_dev(dev, addr)) {
-                       found = 1;
-                       break;
-               }
+       if (dev)
+               found = ipv6_chk_acast_dev(dev, addr);
+       else
+               for_each_netdev_rcu(net, dev)
+                       if (ipv6_chk_acast_dev(dev, addr)) {
+                               found = 1;
+                               break;
+                       }
        rcu_read_unlock();
        return found;
 }