Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

authored by

Matt Mackall and committed by
David S. Miller
53fb95d3 26520765

+21 -10
+13 -6
include/linux/netpoll.h
··· 9 9 10 10 #include <linux/netdevice.h> 11 11 #include <linux/interrupt.h> 12 + #include <linux/rcupdate.h> 12 13 #include <linux/list.h> 13 14 14 15 struct netpoll; ··· 62 61 return ret; 63 62 } 64 63 65 - static inline void netpoll_poll_lock(struct net_device *dev) 64 + static inline void *netpoll_poll_lock(struct net_device *dev) 66 65 { 66 + rcu_read_lock(); /* deal with race on ->npinfo */ 67 67 if (dev->npinfo) { 68 68 spin_lock(&dev->npinfo->poll_lock); 69 69 dev->npinfo->poll_owner = smp_processor_id(); 70 + return dev->npinfo; 70 71 } 72 + return NULL; 71 73 } 72 74 73 - static inline void netpoll_poll_unlock(struct net_device *dev) 75 + static inline void netpoll_poll_unlock(void *have) 74 76 { 75 - if (dev->npinfo) { 76 - dev->npinfo->poll_owner = -1; 77 - spin_unlock(&dev->npinfo->poll_lock); 77 + struct netpoll_info *npi = have; 78 + 79 + if (npi) { 80 + npi->poll_owner = -1; 81 + spin_unlock(&npi->poll_lock); 78 82 } 83 + rcu_read_unlock(); 79 84 } 80 85 81 86 #else 82 87 #define netpoll_rx(a) 0 83 - #define netpoll_poll_lock(a) 88 + #define netpoll_poll_lock(a) 0 84 89 #define netpoll_poll_unlock(a) 85 90 #endif 86 91
+5 -4
net/core/dev.c
··· 1696 1696 struct softnet_data *queue = &__get_cpu_var(softnet_data); 1697 1697 unsigned long start_time = jiffies; 1698 1698 int budget = netdev_budget; 1699 - 1699 + void *have; 1700 + 1700 1701 local_irq_disable(); 1701 1702 1702 1703 while (!list_empty(&queue->poll_list)) { ··· 1710 1709 1711 1710 dev = list_entry(queue->poll_list.next, 1712 1711 struct net_device, poll_list); 1713 - netpoll_poll_lock(dev); 1712 + have = netpoll_poll_lock(dev); 1714 1713 1715 1714 if (dev->quota <= 0 || dev->poll(dev, &budget)) { 1716 - netpoll_poll_unlock(dev); 1715 + netpoll_poll_unlock(have); 1717 1716 local_irq_disable(); 1718 1717 list_del(&dev->poll_list); 1719 1718 list_add_tail(&dev->poll_list, &queue->poll_list); ··· 1722 1721 else 1723 1722 dev->quota = dev->weight; 1724 1723 } else { 1725 - netpoll_poll_unlock(dev); 1724 + netpoll_poll_unlock(have); 1726 1725 dev_put(dev); 1727 1726 local_irq_disable(); 1728 1727 }
+3
net/core/netpoll.c
··· 732 732 /* last thing to do is link it to the net device structure */ 733 733 ndev->npinfo = npinfo; 734 734 735 + /* avoid racing with NAPI reading npinfo */ 736 + synchronize_rcu(); 737 + 735 738 return 0; 736 739 737 740 release: