Skip to content

Commit

Permalink
[NET]: Fix race between poll_napi() and net_rx_action()
Browse files Browse the repository at this point in the history
netpoll_poll_lock() synchronizes the ->poll() invocation
code paths, but once we have the lock we have to make
sure that NAPI_STATE_SCHED is still set.  Otherwise we
get:

	cpu 0			cpu 1

	net_rx_action()		poll_napi()
	netpoll_poll_lock()	... spin on ->poll_lock
	->poll()
	  netif_rx_complete
	netpoll_poll_unlock()	acquire ->poll_lock()
				->poll()
				 netif_rx_complete()
				 CRASH

Based upon a bug report from Tina Yang.

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Oct 30, 2007
1 parent b0a713e commit 0a7606c
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 10 deletions.
10 changes: 9 additions & 1 deletion net/core/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -2172,7 +2172,15 @@ static void net_rx_action(struct softirq_action *h)

weight = n->weight;

work = n->poll(n, weight);
/* This NAPI_STATE_SCHED test is for avoiding a race
* with netpoll's poll_napi(). Only the entity which
* obtains the lock and sees NAPI_STATE_SCHED set will
* actually make the ->poll() call. Therefore we avoid
* accidently calling ->poll() when NAPI is not scheduled.
*/
work = 0;
if (test_bit(NAPI_STATE_SCHED, &n->state))
work = n->poll(n, weight);

WARN_ON_ONCE(work > weight);

Expand Down
37 changes: 28 additions & 9 deletions net/core/netpoll.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,24 +116,43 @@ static __sum16 checksum_udp(struct sk_buff *skb, struct udphdr *uh,
* network adapter, forcing superfluous retries and possibly timeouts.
* Thus, we set our budget to greater than 1.
*/
static int poll_one_napi(struct netpoll_info *npinfo,
struct napi_struct *napi, int budget)
{
int work;

/* net_rx_action's ->poll() invocations and our's are
* synchronized by this test which is only made while
* holding the napi->poll_lock.
*/
if (!test_bit(NAPI_STATE_SCHED, &napi->state))
return budget;

npinfo->rx_flags |= NETPOLL_RX_DROP;
atomic_inc(&trapped);

work = napi->poll(napi, budget);

atomic_dec(&trapped);
npinfo->rx_flags &= ~NETPOLL_RX_DROP;

return budget - work;
}

static void poll_napi(struct netpoll *np)
{
struct netpoll_info *npinfo = np->dev->npinfo;
struct napi_struct *napi;
int budget = 16;

list_for_each_entry(napi, &np->dev->napi_list, dev_list) {
if (test_bit(NAPI_STATE_SCHED, &napi->state) &&
napi->poll_owner != smp_processor_id() &&
if (napi->poll_owner != smp_processor_id() &&
spin_trylock(&napi->poll_lock)) {
npinfo->rx_flags |= NETPOLL_RX_DROP;
atomic_inc(&trapped);

napi->poll(napi, budget);

atomic_dec(&trapped);
npinfo->rx_flags &= ~NETPOLL_RX_DROP;
budget = poll_one_napi(npinfo, napi, budget);
spin_unlock(&napi->poll_lock);

if (!budget)
break;
}
}
}
Expand Down

0 comments on commit 0a7606c

Please sign in to comment.