[NETPOLL]: fix initialization/NAPI race
authorMatt Mackall <mpm@selenic.com>
Fri, 12 Aug 2005 02:27:43 +0000 (19:27 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 12 Aug 2005 02:27:43 +0000 (19:27 -0700)
This fixes a race during initialization with the NAPI softirq
processing by using an RCU approach.

This race was discovered when refill_skbs() was added to
the setup code.

Signed-off-by: Matt Mackall <mpm@selenic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/netpoll.h
net/core/dev.c
net/core/netpoll.c

index be68d94b03d5e66e4e11d6af2a17d15509c7311d..5ade54a78dbbb3a3618d10d5e3d21d5d67a5b2fe 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <linux/netdevice.h>
 #include <linux/interrupt.h>
+#include <linux/rcupdate.h>
 #include <linux/list.h>
 
 struct netpoll;
@@ -61,25 +62,31 @@ static inline int netpoll_rx(struct sk_buff *skb)
        return ret;
 }
 
-static inline void netpoll_poll_lock(struct net_device *dev)
+static inline void *netpoll_poll_lock(struct net_device *dev)
 {
+       rcu_read_lock(); /* deal with race on ->npinfo */
        if (dev->npinfo) {
                spin_lock(&dev->npinfo->poll_lock);
                dev->npinfo->poll_owner = smp_processor_id();
+               return dev->npinfo;
        }
+       return NULL;
 }
 
-static inline void netpoll_poll_unlock(struct net_device *dev)
+static inline void netpoll_poll_unlock(void *have)
 {
-       if (dev->npinfo) {
-               dev->npinfo->poll_owner = -1;
-               spin_unlock(&dev->npinfo->poll_lock);
+       struct netpoll_info *npi = have;
+
+       if (npi) {
+               npi->poll_owner = -1;
+               spin_unlock(&npi->poll_lock);
        }
+       rcu_read_unlock();
 }
 
 #else
 #define netpoll_rx(a) 0
-#define netpoll_poll_lock(a)
+#define netpoll_poll_lock(a) 0
 #define netpoll_poll_unlock(a)
 #endif
 
index 52a3bf7ae177a803b5b77190ec1f34e8070175e5..faf59b02c4bf082179ff67477ab47719e885cafa 100644 (file)
@@ -1696,7 +1696,8 @@ static void net_rx_action(struct softirq_action *h)
        struct softnet_data *queue = &__get_cpu_var(softnet_data);
        unsigned long start_time = jiffies;
        int budget = netdev_budget;
-       
+       void *have;
+
        local_irq_disable();
 
        while (!list_empty(&queue->poll_list)) {
@@ -1709,10 +1710,10 @@ static void net_rx_action(struct softirq_action *h)
 
                dev = list_entry(queue->poll_list.next,
                                 struct net_device, poll_list);
-               netpoll_poll_lock(dev);
+               have = netpoll_poll_lock(dev);
 
                if (dev->quota <= 0 || dev->poll(dev, &budget)) {
-                       netpoll_poll_unlock(dev);
+                       netpoll_poll_unlock(have);
                        local_irq_disable();
                        list_del(&dev->poll_list);
                        list_add_tail(&dev->poll_list, &queue->poll_list);
@@ -1721,7 +1722,7 @@ static void net_rx_action(struct softirq_action *h)
                        else
                                dev->quota = dev->weight;
                } else {
-                       netpoll_poll_unlock(dev);
+                       netpoll_poll_unlock(have);
                        dev_put(dev);
                        local_irq_disable();
                }
index c02a08da6d42a3f59afee93de979e81e090d633f..996787bca17f311d8fde57c257aabeb55e10dfe8 100644 (file)
@@ -732,6 +732,9 @@ int netpoll_setup(struct netpoll *np)
        /* last thing to do is link it to the net device structure */
        ndev->npinfo = npinfo;
 
+       /* avoid racing with NAPI reading npinfo */
+       synchronize_rcu();
+
        return 0;
 
  release: