Skip to content

Commit

Permalink
[NETPOLL]: fix initialization/NAPI race
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Matt Mackall authored and David S. Miller committed Aug 12, 2005
1 parent 2652076 commit 53fb95d
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 10 deletions.
19 changes: 13 additions & 6 deletions include/linux/netpoll.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <linux/netdevice.h>
#include <linux/interrupt.h>
#include <linux/rcupdate.h>
#include <linux/list.h>

struct netpoll;
Expand Down Expand Up @@ -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

Expand Down
9 changes: 5 additions & 4 deletions net/core/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -1696,7 +1696,8 @@ 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();

while (!list_empty(&queue->poll_list)) {
Expand All @@ -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);
Expand All @@ -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();
}
Expand Down
3 changes: 3 additions & 0 deletions net/core/netpoll.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down

0 comments on commit 53fb95d

Please sign in to comment.