diff options
author | Matt Mackall <mpm@selenic.com> | 2005-08-11 22:27:43 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2005-08-11 22:27:43 -0400 |
commit | 53fb95d3c14290fd6ee808b221e35493f096246f (patch) | |
tree | 146c31c79bbc66d8fde3afa5ae37a04b63041eba | |
parent | 2652076507b662fc88ba16c27b59c7bdd9ccd956 (diff) |
[NETPOLL]: fix initialization/NAPI race
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>
-rw-r--r-- | include/linux/netpoll.h | 19 | ||||
-rw-r--r-- | net/core/dev.c | 9 | ||||
-rw-r--r-- | net/core/netpoll.c | 3 |
3 files changed, 21 insertions, 10 deletions
diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index be68d94b03d5..5ade54a78dbb 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h | |||
@@ -9,6 +9,7 @@ | |||
9 | 9 | ||
10 | #include <linux/netdevice.h> | 10 | #include <linux/netdevice.h> |
11 | #include <linux/interrupt.h> | 11 | #include <linux/interrupt.h> |
12 | #include <linux/rcupdate.h> | ||
12 | #include <linux/list.h> | 13 | #include <linux/list.h> |
13 | 14 | ||
14 | struct netpoll; | 15 | struct netpoll; |
@@ -61,25 +62,31 @@ static inline int netpoll_rx(struct sk_buff *skb) | |||
61 | return ret; | 62 | return ret; |
62 | } | 63 | } |
63 | 64 | ||
64 | static inline void netpoll_poll_lock(struct net_device *dev) | 65 | static inline void *netpoll_poll_lock(struct net_device *dev) |
65 | { | 66 | { |
67 | rcu_read_lock(); /* deal with race on ->npinfo */ | ||
66 | if (dev->npinfo) { | 68 | if (dev->npinfo) { |
67 | spin_lock(&dev->npinfo->poll_lock); | 69 | spin_lock(&dev->npinfo->poll_lock); |
68 | dev->npinfo->poll_owner = smp_processor_id(); | 70 | dev->npinfo->poll_owner = smp_processor_id(); |
71 | return dev->npinfo; | ||
69 | } | 72 | } |
73 | return NULL; | ||
70 | } | 74 | } |
71 | 75 | ||
72 | static inline void netpoll_poll_unlock(struct net_device *dev) | 76 | static inline void netpoll_poll_unlock(void *have) |
73 | { | 77 | { |
74 | if (dev->npinfo) { | 78 | struct netpoll_info *npi = have; |
75 | dev->npinfo->poll_owner = -1; | 79 | |
76 | spin_unlock(&dev->npinfo->poll_lock); | 80 | if (npi) { |
81 | npi->poll_owner = -1; | ||
82 | spin_unlock(&npi->poll_lock); | ||
77 | } | 83 | } |
84 | rcu_read_unlock(); | ||
78 | } | 85 | } |
79 | 86 | ||
80 | #else | 87 | #else |
81 | #define netpoll_rx(a) 0 | 88 | #define netpoll_rx(a) 0 |
82 | #define netpoll_poll_lock(a) | 89 | #define netpoll_poll_lock(a) 0 |
83 | #define netpoll_poll_unlock(a) | 90 | #define netpoll_poll_unlock(a) |
84 | #endif | 91 | #endif |
85 | 92 | ||
diff --git a/net/core/dev.c b/net/core/dev.c index 52a3bf7ae177..faf59b02c4bf 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -1696,7 +1696,8 @@ static void net_rx_action(struct softirq_action *h) | |||
1696 | struct softnet_data *queue = &__get_cpu_var(softnet_data); | 1696 | struct softnet_data *queue = &__get_cpu_var(softnet_data); |
1697 | unsigned long start_time = jiffies; | 1697 | unsigned long start_time = jiffies; |
1698 | int budget = netdev_budget; | 1698 | int budget = netdev_budget; |
1699 | 1699 | void *have; | |
1700 | |||
1700 | local_irq_disable(); | 1701 | local_irq_disable(); |
1701 | 1702 | ||
1702 | while (!list_empty(&queue->poll_list)) { | 1703 | while (!list_empty(&queue->poll_list)) { |
@@ -1709,10 +1710,10 @@ static void net_rx_action(struct softirq_action *h) | |||
1709 | 1710 | ||
1710 | dev = list_entry(queue->poll_list.next, | 1711 | dev = list_entry(queue->poll_list.next, |
1711 | struct net_device, poll_list); | 1712 | struct net_device, poll_list); |
1712 | netpoll_poll_lock(dev); | 1713 | have = netpoll_poll_lock(dev); |
1713 | 1714 | ||
1714 | if (dev->quota <= 0 || dev->poll(dev, &budget)) { | 1715 | if (dev->quota <= 0 || dev->poll(dev, &budget)) { |
1715 | netpoll_poll_unlock(dev); | 1716 | netpoll_poll_unlock(have); |
1716 | local_irq_disable(); | 1717 | local_irq_disable(); |
1717 | list_del(&dev->poll_list); | 1718 | list_del(&dev->poll_list); |
1718 | list_add_tail(&dev->poll_list, &queue->poll_list); | 1719 | list_add_tail(&dev->poll_list, &queue->poll_list); |
@@ -1721,7 +1722,7 @@ static void net_rx_action(struct softirq_action *h) | |||
1721 | else | 1722 | else |
1722 | dev->quota = dev->weight; | 1723 | dev->quota = dev->weight; |
1723 | } else { | 1724 | } else { |
1724 | netpoll_poll_unlock(dev); | 1725 | netpoll_poll_unlock(have); |
1725 | dev_put(dev); | 1726 | dev_put(dev); |
1726 | local_irq_disable(); | 1727 | local_irq_disable(); |
1727 | } | 1728 | } |
diff --git a/net/core/netpoll.c b/net/core/netpoll.c index c02a08da6d42..996787bca17f 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c | |||
@@ -732,6 +732,9 @@ int netpoll_setup(struct netpoll *np) | |||
732 | /* last thing to do is link it to the net device structure */ | 732 | /* last thing to do is link it to the net device structure */ |
733 | ndev->npinfo = npinfo; | 733 | ndev->npinfo = npinfo; |
734 | 734 | ||
735 | /* avoid racing with NAPI reading npinfo */ | ||
736 | synchronize_rcu(); | ||
737 | |||
735 | return 0; | 738 | return 0; |
736 | 739 | ||
737 | release: | 740 | release: |