[IPV4]: Replace __in_dev_get with __in_dev_get_rcu/rtnl
Herbert Xu [Mon, 3 Oct 2005 21:35:55 +0000 (14:35 -0700)]
The following patch renames __in_dev_get() to __in_dev_get_rtnl() and
introduces __in_dev_get_rcu() to cover the second case.

1) RCU with refcnt should use in_dev_get().
2) RCU without refcnt should use __in_dev_get_rcu().
3) All others must hold RTNL and use __in_dev_get_rtnl().

There is one exception in net/ipv4/route.c which is in fact a pre-existing
race condition.  I've marked it as such so that we remember to fix it.

This patch is based on suggestions and prior work by Suzanne Wood and
Paul McKenney.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>

24 files changed:
drivers/net/bonding/bond_main.c
drivers/net/wan/sdlamain.c
drivers/net/wan/syncppp.c
drivers/net/wireless/strip.c
drivers/parisc/led.c
drivers/s390/net/qeth_main.c
include/linux/inetdevice.h
net/atm/clip.c
net/core/netpoll.c
net/core/pktgen.c
net/econet/af_econet.c
net/ipv4/arp.c
net/ipv4/devinet.c
net/ipv4/fib_frontend.c
net/ipv4/fib_semantics.c
net/ipv4/igmp.c
net/ipv4/ip_gre.c
net/ipv4/ipmr.c
net/ipv4/netfilter/ip_conntrack_netbios_ns.c
net/ipv4/netfilter/ipt_REDIRECT.c
net/ipv4/route.c
net/ipv6/addrconf.c
net/irda/irlan/irlan_eth.c
net/sctp/protocol.c

index 6d00c3d..bf81cd4 100644 (file)
@@ -2776,7 +2776,7 @@ static u32 bond_glean_dev_ip(struct net_device *dev)
                return 0;
 
        rcu_read_lock();
-       idev = __in_dev_get(dev);
+       idev = __in_dev_get_rcu(dev);
        if (!idev)
                goto out;
 
index 74e151a..7a8b22a 100644 (file)
@@ -57,6 +57,7 @@
 #include <linux/ioport.h>      /* request_region(), release_region() */
 #include <linux/wanrouter.h>   /* WAN router definitions */
 #include <linux/wanpipe.h>     /* WANPIPE common user API definitions */
+#include <linux/rcupdate.h>
 
 #include <linux/in.h>
 #include <asm/io.h>            /* phys_to_virt() */
@@ -1268,37 +1269,41 @@ unsigned long get_ip_address(struct net_device *dev, int option)
        
        struct in_ifaddr *ifaddr;
        struct in_device *in_dev;
+       unsigned long addr = 0;
 
-       if ((in_dev = __in_dev_get(dev)) == NULL){
-               return 0;
+       rcu_read_lock();
+       if ((in_dev = __in_dev_get_rcu(dev)) == NULL){
+               goto out;
        }
 
        if ((ifaddr = in_dev->ifa_list)== NULL ){
-               return 0;
+               goto out;
        }
        
        switch (option){
 
        case WAN_LOCAL_IP:
-               return ifaddr->ifa_local;
+               addr = ifaddr->ifa_local;
                break;
        
        case WAN_POINTOPOINT_IP:
-               return ifaddr->ifa_address;
+               addr = ifaddr->ifa_address;
                break;  
 
        case WAN_NETMASK_IP:
-               return ifaddr->ifa_mask;
+               addr = ifaddr->ifa_mask;
                break;
 
        case WAN_BROADCAST_IP:
-               return ifaddr->ifa_broadcast;
+               addr = ifaddr->ifa_broadcast;
                break;
        default:
-               return 0;
+               break;
        }
 
-       return 0;
+out:
+       rcu_read_unlock();
+       return addr;
 }      
 
 void add_gateway(sdla_t *card, struct net_device *dev)
index b56a7b5..a6d3b55 100644 (file)
@@ -769,7 +769,7 @@ static void sppp_cisco_input (struct sppp *sp, struct sk_buff *skb)
                u32 addr = 0, mask = ~0; /* FIXME: is the mask correct? */
 #ifdef CONFIG_INET
                rcu_read_lock();
-               if ((in_dev = __in_dev_get(dev)) != NULL)
+               if ((in_dev = __in_dev_get_rcu(dev)) != NULL)
                {
                        for (ifa=in_dev->ifa_list; ifa != NULL;
                                ifa=ifa->ifa_next) {
index 4b0acae..7bc7fc8 100644 (file)
@@ -1352,7 +1352,7 @@ static unsigned char *strip_make_packet(unsigned char *buffer,
                struct in_device *in_dev;
 
                rcu_read_lock();
-               in_dev = __in_dev_get(strip_info->dev);
+               in_dev = __in_dev_get_rcu(strip_info->dev);
                if (in_dev == NULL) {
                        rcu_read_unlock();
                        return NULL;
@@ -1508,7 +1508,7 @@ static void strip_send(struct strip *strip_info, struct sk_buff *skb)
 
                brd = addr = 0;
                rcu_read_lock();
-               in_dev = __in_dev_get(strip_info->dev);
+               in_dev = __in_dev_get_rcu(strip_info->dev);
                if (in_dev) {
                        if (in_dev->ifa_list) {
                                brd = in_dev->ifa_list->ifa_broadcast;
index e90fb72..2869022 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/proc_fs.h>
 #include <linux/ctype.h>
 #include <linux/blkdev.h>
+#include <linux/rcupdate.h>
 #include <asm/io.h>
 #include <asm/processor.h>
 #include <asm/hardware.h>
@@ -358,9 +359,10 @@ static __inline__ int led_get_net_activity(void)
        /* we are running as tasklet, so locking dev_base 
         * for reading should be OK */
        read_lock(&dev_base_lock);
+       rcu_read_lock();
        for (dev = dev_base; dev; dev = dev->next) {
            struct net_device_stats *stats;
-           struct in_device *in_dev = __in_dev_get(dev);
+           struct in_device *in_dev = __in_dev_get_rcu(dev);
            if (!in_dev || !in_dev->ifa_list)
                continue;
            if (LOOPBACK(in_dev->ifa_list->ifa_local))
@@ -371,6 +373,7 @@ static __inline__ int led_get_net_activity(void)
            rx_total += stats->rx_packets;
            tx_total += stats->tx_packets;
        }
+       rcu_read_unlock();
        read_unlock(&dev_base_lock);
 
        retval = 0;
index 86582cf..71de834 100644 (file)
@@ -5200,7 +5200,7 @@ qeth_free_vlan_addresses4(struct qeth_card *card, unsigned short vid)
        if (!card->vlangrp)
                return;
        rcu_read_lock();
-       in_dev = __in_dev_get(card->vlangrp->vlan_devices[vid]);
+       in_dev = __in_dev_get_rcu(card->vlangrp->vlan_devices[vid]);
        if (!in_dev)
                goto out;
        for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
@@ -7725,7 +7725,7 @@ qeth_arp_constructor(struct neighbour *neigh)
                goto out;
 
        rcu_read_lock();
-       in_dev = rcu_dereference(__in_dev_get(dev));
+       in_dev = __in_dev_get_rcu(dev);
        if (in_dev == NULL) {
                rcu_read_unlock();
                return -EINVAL;
index 7e1e15f..fd7af86 100644 (file)
@@ -142,13 +142,21 @@ static __inline__ int bad_mask(u32 mask, u32 addr)
 
 #define endfor_ifa(in_dev) }
 
+static inline struct in_device *__in_dev_get_rcu(const struct net_device *dev)
+{
+       struct in_device *in_dev = dev->ip_ptr;
+       if (in_dev)
+               in_dev = rcu_dereference(in_dev);
+       return in_dev;
+}
+
 static __inline__ struct in_device *
 in_dev_get(const struct net_device *dev)
 {
        struct in_device *in_dev;
 
        rcu_read_lock();
-       in_dev = dev->ip_ptr;
+       in_dev = __in_dev_get_rcu(dev);
        if (in_dev)
                atomic_inc(&in_dev->refcnt);
        rcu_read_unlock();
@@ -156,7 +164,7 @@ in_dev_get(const struct net_device *dev)
 }
 
 static __inline__ struct in_device *
-__in_dev_get(const struct net_device *dev)
+__in_dev_get_rtnl(const struct net_device *dev)
 {
        return (struct in_device*)dev->ip_ptr;
 }
index 28dab55..4f54c9a 100644 (file)
@@ -310,7 +310,7 @@ static int clip_constructor(struct neighbour *neigh)
        if (neigh->type != RTN_UNICAST) return -EINVAL;
 
        rcu_read_lock();
-       in_dev = rcu_dereference(__in_dev_get(dev));
+       in_dev = __in_dev_get_rcu(dev);
        if (!in_dev) {
                rcu_read_unlock();
                return -EINVAL;
index 5265dfd..802fe11 100644 (file)
@@ -703,7 +703,7 @@ int netpoll_setup(struct netpoll *np)
 
        if (!np->local_ip) {
                rcu_read_lock();
-               in_dev = __in_dev_get(ndev);
+               in_dev = __in_dev_get_rcu(ndev);
 
                if (!in_dev || !in_dev->ifa_list) {
                        rcu_read_unlock();
index b7f2d65..44de070 100644 (file)
@@ -1667,7 +1667,7 @@ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)
                        struct in_device *in_dev; 
 
                        rcu_read_lock();
-                       in_dev = __in_dev_get(pkt_dev->odev);
+                       in_dev = __in_dev_get_rcu(pkt_dev->odev);
                        if (in_dev) {
                                if (in_dev->ifa_list) {
                                        pkt_dev->saddr_min = in_dev->ifa_list->ifa_address;
index 4a62093..34fdac5 100644 (file)
@@ -406,7 +406,7 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,
                unsigned long network = 0;
 
                rcu_read_lock();
-               idev = __in_dev_get(dev);
+               idev = __in_dev_get_rcu(dev);
                if (idev) {
                        if (idev->ifa_list)
                                network = ntohl(idev->ifa_list->ifa_address) & 
index ec0e368..b425748 100644 (file)
@@ -241,7 +241,7 @@ static int arp_constructor(struct neighbour *neigh)
        neigh->type = inet_addr_type(addr);
 
        rcu_read_lock();
-       in_dev = rcu_dereference(__in_dev_get(dev));
+       in_dev = __in_dev_get_rcu(dev);
        if (in_dev == NULL) {
                rcu_read_unlock();
                return -EINVAL;
@@ -989,8 +989,8 @@ static int arp_req_set(struct arpreq *r, struct net_device * dev)
                        ipv4_devconf.proxy_arp = 1;
                        return 0;
                }
-               if (__in_dev_get(dev)) {
-                       __in_dev_get(dev)->cnf.proxy_arp = 1;
+               if (__in_dev_get_rtnl(dev)) {
+                       __in_dev_get_rtnl(dev)->cnf.proxy_arp = 1;
                        return 0;
                }
                return -ENXIO;
@@ -1095,8 +1095,8 @@ static int arp_req_delete(struct arpreq *r, struct net_device * dev)
                                ipv4_devconf.proxy_arp = 0;
                                return 0;
                        }
-                       if (__in_dev_get(dev)) {
-                               __in_dev_get(dev)->cnf.proxy_arp = 0;
+                       if (__in_dev_get_rtnl(dev)) {
+                               __in_dev_get_rtnl(dev)->cnf.proxy_arp = 0;
                                return 0;
                        }
                        return -ENXIO;
index ba2895a..74f2207 100644 (file)
@@ -351,7 +351,7 @@ static int inet_insert_ifa(struct in_ifaddr *ifa)
 
 static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
 {
-       struct in_device *in_dev = __in_dev_get(dev);
+       struct in_device *in_dev = __in_dev_get_rtnl(dev);
 
        ASSERT_RTNL();
 
@@ -449,7 +449,7 @@ static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg
                goto out;
 
        rc = -ENOBUFS;
-       if ((in_dev = __in_dev_get(dev)) == NULL) {
+       if ((in_dev = __in_dev_get_rtnl(dev)) == NULL) {
                in_dev = inetdev_init(dev);
                if (!in_dev)
                        goto out;
@@ -584,7 +584,7 @@ int devinet_ioctl(unsigned int cmd, void __user *arg)
        if (colon)
                *colon = ':';
 
-       if ((in_dev = __in_dev_get(dev)) != NULL) {
+       if ((in_dev = __in_dev_get_rtnl(dev)) != NULL) {
                if (tryaddrmatch) {
                        /* Matthias Andree */
                        /* compare label and address (4.4BSD style) */
@@ -748,7 +748,7 @@ rarok:
 
 static int inet_gifconf(struct net_device *dev, char __user *buf, int len)
 {
-       struct in_device *in_dev = __in_dev_get(dev);
+       struct in_device *in_dev = __in_dev_get_rtnl(dev);
        struct in_ifaddr *ifa;
        struct ifreq ifr;
        int done = 0;
@@ -791,7 +791,7 @@ u32 inet_select_addr(const struct net_device *dev, u32 dst, int scope)
        struct in_device *in_dev;
 
        rcu_read_lock();
-       in_dev = __in_dev_get(dev);
+       in_dev = __in_dev_get_rcu(dev);
        if (!in_dev)
                goto no_in_dev;
 
@@ -818,7 +818,7 @@ no_in_dev:
        read_lock(&dev_base_lock);
        rcu_read_lock();
        for (dev = dev_base; dev; dev = dev->next) {
-               if ((in_dev = __in_dev_get(dev)) == NULL)
+               if ((in_dev = __in_dev_get_rcu(dev)) == NULL)
                        continue;
 
                for_primary_ifa(in_dev) {
@@ -887,7 +887,7 @@ u32 inet_confirm_addr(const struct net_device *dev, u32 dst, u32 local, int scop
 
        if (dev) {
                rcu_read_lock();
-               if ((in_dev = __in_dev_get(dev)))
+               if ((in_dev = __in_dev_get_rcu(dev)))
                        addr = confirm_addr_indev(in_dev, dst, local, scope);
                rcu_read_unlock();
 
@@ -897,7 +897,7 @@ u32 inet_confirm_addr(const struct net_device *dev, u32 dst, u32 local, int scop
        read_lock(&dev_base_lock);
        rcu_read_lock();
        for (dev = dev_base; dev; dev = dev->next) {
-               if ((in_dev = __in_dev_get(dev))) {
+               if ((in_dev = __in_dev_get_rcu(dev))) {
                        addr = confirm_addr_indev(in_dev, dst, local, scope);
                        if (addr)
                                break;
@@ -957,7 +957,7 @@ static int inetdev_event(struct notifier_block *this, unsigned long event,
                         void *ptr)
 {
        struct net_device *dev = ptr;
-       struct in_device *in_dev = __in_dev_get(dev);
+       struct in_device *in_dev = __in_dev_get_rtnl(dev);
 
        ASSERT_RTNL();
 
@@ -1078,7 +1078,7 @@ static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
                if (idx > s_idx)
                        s_ip_idx = 0;
                rcu_read_lock();
-               if ((in_dev = __in_dev_get(dev)) == NULL) {
+               if ((in_dev = __in_dev_get_rcu(dev)) == NULL) {
                        rcu_read_unlock();
                        continue;
                }
@@ -1149,7 +1149,7 @@ void inet_forward_change(void)
        for (dev = dev_base; dev; dev = dev->next) {
                struct in_device *in_dev;
                rcu_read_lock();
-               in_dev = __in_dev_get(dev);
+               in_dev = __in_dev_get_rcu(dev);
                if (in_dev)
                        in_dev->cnf.forwarding = on;
                rcu_read_unlock();
index 4e1379f..e61bc71 100644 (file)
@@ -173,7 +173,7 @@ int fib_validate_source(u32 src, u32 dst, u8 tos, int oif,
 
        no_addr = rpf = 0;
        rcu_read_lock();
-       in_dev = __in_dev_get(dev);
+       in_dev = __in_dev_get_rcu(dev);
        if (in_dev) {
                no_addr = in_dev->ifa_list == NULL;
                rpf = IN_DEV_RPFILTER(in_dev);
@@ -607,7 +607,7 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event,
 static int fib_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
        struct net_device *dev = ptr;
-       struct in_device *in_dev = __in_dev_get(dev);
+       struct in_device *in_dev = __in_dev_get_rtnl(dev);
 
        if (event == NETDEV_UNREGISTER) {
                fib_disable_ip(dev, 2);
index d41219e..186f20c 100644 (file)
@@ -1087,7 +1087,7 @@ fib_convert_rtentry(int cmd, struct nlmsghdr *nl, struct rtmsg *rtm,
                rta->rta_oif = &dev->ifindex;
                if (colon) {
                        struct in_ifaddr *ifa;
-                       struct in_device *in_dev = __in_dev_get(dev);
+                       struct in_device *in_dev = __in_dev_get_rtnl(dev);
                        if (!in_dev)
                                return -ENODEV;
                        *colon = ':';
@@ -1268,7 +1268,7 @@ int fib_sync_up(struct net_device *dev)
                        }
                        if (nh->nh_dev == NULL || !(nh->nh_dev->flags&IFF_UP))
                                continue;
-                       if (nh->nh_dev != dev || __in_dev_get(dev) == NULL)
+                       if (nh->nh_dev != dev || !__in_dev_get_rtnl(dev))
                                continue;
                        alive++;
                        spin_lock_bh(&fib_multipath_lock);
index 70c44e4..8b6d393 100644 (file)
@@ -1323,7 +1323,7 @@ static struct in_device * ip_mc_find_dev(struct ip_mreqn *imr)
        }
        if (dev) {
                imr->imr_ifindex = dev->ifindex;
-               idev = __in_dev_get(dev);
+               idev = __in_dev_get_rtnl(dev);
        }
        return idev;
 }
index f0d5740..896ce3f 100644 (file)
@@ -1104,10 +1104,10 @@ static int ipgre_open(struct net_device *dev)
                        return -EADDRNOTAVAIL;
                dev = rt->u.dst.dev;
                ip_rt_put(rt);
-               if (__in_dev_get(dev) == NULL)
+               if (__in_dev_get_rtnl(dev) == NULL)
                        return -EADDRNOTAVAIL;
                t->mlink = dev->ifindex;
-               ip_mc_inc_group(__in_dev_get(dev), t->parms.iph.daddr);
+               ip_mc_inc_group(__in_dev_get_rtnl(dev), t->parms.iph.daddr);
        }
        return 0;
 }
index 9dbf590..302b7eb 100644 (file)
@@ -149,7 +149,7 @@ struct net_device *ipmr_new_tunnel(struct vifctl *v)
                if (err == 0 && (dev = __dev_get_by_name(p.name)) != NULL) {
                        dev->flags |= IFF_MULTICAST;
 
-                       in_dev = __in_dev_get(dev);
+                       in_dev = __in_dev_get_rtnl(dev);
                        if (in_dev == NULL && (in_dev = inetdev_init(dev)) == NULL)
                                goto failure;
                        in_dev->cnf.rp_filter = 0;
@@ -278,7 +278,7 @@ static int vif_delete(int vifi)
 
        dev_set_allmulti(dev, -1);
 
-       if ((in_dev = __in_dev_get(dev)) != NULL) {
+       if ((in_dev = __in_dev_get_rtnl(dev)) != NULL) {
                in_dev->cnf.mc_forwarding--;
                ip_rt_multicast_event(in_dev);
        }
@@ -421,7 +421,7 @@ static int vif_add(struct vifctl *vifc, int mrtsock)
                return -EINVAL;
        }
 
-       if ((in_dev = __in_dev_get(dev)) == NULL)
+       if ((in_dev = __in_dev_get_rtnl(dev)) == NULL)
                return -EADDRNOTAVAIL;
        in_dev->cnf.mc_forwarding++;
        dev_set_allmulti(dev, +1);
index 577bac2..186646e 100644 (file)
@@ -58,7 +58,7 @@ static int help(struct sk_buff **pskb,
                goto out;
 
        rcu_read_lock();
-       in_dev = __in_dev_get(rt->u.dst.dev);
+       in_dev = __in_dev_get_rcu(rt->u.dst.dev);
        if (in_dev != NULL) {
                for_primary_ifa(in_dev) {
                        if (ifa->ifa_broadcast == iph->daddr) {
index 715cb61..5245bfd 100644 (file)
@@ -93,7 +93,7 @@ redirect_target(struct sk_buff **pskb,
                newdst = 0;
                
                rcu_read_lock();
-               indev = __in_dev_get((*pskb)->dev);
+               indev = __in_dev_get_rcu((*pskb)->dev);
                if (indev && (ifa = indev->ifa_list))
                        newdst = ifa->ifa_local;
                rcu_read_unlock();
index 8549f26..381dd6a 100644 (file)
@@ -2128,7 +2128,7 @@ int ip_route_input(struct sk_buff *skb, u32 daddr, u32 saddr,
                struct in_device *in_dev;
 
                rcu_read_lock();
-               if ((in_dev = __in_dev_get(dev)) != NULL) {
+               if ((in_dev = __in_dev_get_rcu(dev)) != NULL) {
                        int our = ip_check_mc(in_dev, daddr, saddr,
                                skb->nh.iph->protocol);
                        if (our
@@ -2443,7 +2443,9 @@ static int ip_route_output_slow(struct rtable **rp, const struct flowi *oldflp)
                err = -ENODEV;
                if (dev_out == NULL)
                        goto out;
-               if (__in_dev_get(dev_out) == NULL) {
+
+               /* RACE: Check return value of inet_select_addr instead. */
+               if (__in_dev_get_rtnl(dev_out) == NULL) {
                        dev_put(dev_out);
                        goto out;       /* Wrong error code */
                }
index 4e509e5..a970b47 100644 (file)
@@ -1806,7 +1806,7 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
        }
 
         for (dev = dev_base; dev != NULL; dev = dev->next) {
-               struct in_device * in_dev = __in_dev_get(dev);
+               struct in_device * in_dev = __in_dev_get_rtnl(dev);
                if (in_dev && (dev->flags & IFF_UP)) {
                        struct in_ifaddr * ifa;
 
index 071cd2c..953e255 100644 (file)
@@ -310,7 +310,7 @@ void irlan_eth_send_gratuitous_arp(struct net_device *dev)
 #ifdef CONFIG_INET
        IRDA_DEBUG(4, "IrLAN: Sending gratuitous ARP\n");
        rcu_read_lock();
-       in_dev = __in_dev_get(dev);
+       in_dev = __in_dev_get_rcu(dev);
        if (in_dev == NULL)
                goto out;
        if (in_dev->ifa_list)
index e7025be..f01d1c9 100644 (file)
@@ -147,7 +147,7 @@ static void sctp_v4_copy_addrlist(struct list_head *addrlist,
        struct sctp_sockaddr_entry *addr;
 
        rcu_read_lock();
-       if ((in_dev = __in_dev_get(dev)) == NULL) {
+       if ((in_dev = __in_dev_get_rcu(dev)) == NULL) {
                rcu_read_unlock();
                return;
        }