Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 53fb95d3 authored by Matt Mackall's avatar Matt Mackall Committed by David S. Miller
Browse files

[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: default avatarMatt Mackall <mpm@selenic.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 26520765
Loading
Loading
Loading
Loading
+13 −6
Original line number Diff line number Diff line
@@ -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

+5 −4
Original line number Diff line number Diff line
@@ -1696,6 +1696,7 @@ 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();

@@ -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();
		}
+3 −0
Original line number Diff line number Diff line
@@ -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: