diff options
| -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: |
