aboutsummaryrefslogtreecommitdiff
path: root/net/core/netpoll.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-12 13:31:22 -0700
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-12 13:31:22 -0700
commite1bd2ac5a6b7a8b625e40c9e9f8b6dea4cf22f85 (patch)
tree9366e9fb481da2c7195ca3f2bafeffebbf001363 /net/core/netpoll.c
parent0b9062f6b57a87f22309c6b920a51aaa66ce2a13 (diff)
parent15028aad00ddf241581fbe74a02ec89cbb28d35d (diff)
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
* 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6: (183 commits) [TG3]: Update version to 3.78. [TG3]: Add missing NVRAM strapping. [TG3]: Enable auto MDI. [TG3]: Fix the polarity bit. [TG3]: Fix irq_sync race condition. [NET_SCHED]: ematch: module autoloading [TCP]: tcp probe wraparound handling and other changes [RTNETLINK]: rtnl_link: allow specifying initial device address [RTNETLINK]: rtnl_link API simplification [VLAN]: Fix MAC address handling [ETH]: Validate address in eth_mac_addr [NET]: Fix races in net_rx_action vs netpoll. [AF_UNIX]: Rewrite garbage collector, fixes race. [NETFILTER]: {ip, nf}_conntrack_sctp: fix remotely triggerable NULL ptr dereference (CVE-2007-2876) [NET]: Make all initialized struct seq_operations const. [UDP]: Fix length check. [IPV6]: Remove unneeded pointer idev from addrconf_cleanup(). [DECNET]: Another unnecessary net/tcp.h inclusion in net/dn.h [IPV6]: Make IPV6_{RECV,2292}RTHDR boolean options. [IPV6]: Do not send RH0 anymore. ... Fixed up trivial conflict in Documentation/feature-removal-schedule.txt manually. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'net/core/netpoll.c')
-rw-r--r--net/core/netpoll.c18
1 files changed, 14 insertions, 4 deletions
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index a0efdd7a6b3..d1264e9a50a 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -66,8 +66,9 @@ static void queue_process(struct work_struct *work)
local_irq_save(flags);
netif_tx_lock(dev);
- if (netif_queue_stopped(dev) ||
- dev->hard_start_xmit(skb, dev) != NETDEV_TX_OK) {
+ if ((netif_queue_stopped(dev) ||
+ netif_subqueue_stopped(dev, skb->queue_mapping)) ||
+ dev->hard_start_xmit(skb, dev) != NETDEV_TX_OK) {
skb_queue_head(&npinfo->txq, skb);
netif_tx_unlock(dev);
local_irq_restore(flags);
@@ -123,6 +124,13 @@ static void poll_napi(struct netpoll *np)
if (test_bit(__LINK_STATE_RX_SCHED, &np->dev->state) &&
npinfo->poll_owner != smp_processor_id() &&
spin_trylock(&npinfo->poll_lock)) {
+ /* When calling dev->poll from poll_napi, we may end up in
+ * netif_rx_complete. However, only the CPU to which the
+ * device was queued is allowed to remove it from poll_list.
+ * Setting POLL_LIST_FROZEN tells netif_rx_complete
+ * to leave the NAPI state alone.
+ */
+ set_bit(__LINK_STATE_POLL_LIST_FROZEN, &np->dev->state);
npinfo->rx_flags |= NETPOLL_RX_DROP;
atomic_inc(&trapped);
@@ -130,6 +138,7 @@ static void poll_napi(struct netpoll *np)
atomic_dec(&trapped);
npinfo->rx_flags &= ~NETPOLL_RX_DROP;
+ clear_bit(__LINK_STATE_POLL_LIST_FROZEN, &np->dev->state);
spin_unlock(&npinfo->poll_lock);
}
}
@@ -254,7 +263,8 @@ static void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb)
for (tries = jiffies_to_usecs(1)/USEC_PER_POLL;
tries > 0; --tries) {
if (netif_tx_trylock(dev)) {
- if (!netif_queue_stopped(dev))
+ if (!netif_queue_stopped(dev) &&
+ !netif_subqueue_stopped(dev, skb->queue_mapping))
status = dev->hard_start_xmit(skb, dev);
netif_tx_unlock(dev);
@@ -781,7 +791,6 @@ void netpoll_cleanup(struct netpoll *np)
spin_unlock_irqrestore(&npinfo->rx_lock, flags);
}
- np->dev->npinfo = NULL;
if (atomic_dec_and_test(&npinfo->refcnt)) {
skb_queue_purge(&npinfo->arp_tx);
skb_queue_purge(&npinfo->txq);
@@ -794,6 +803,7 @@ void netpoll_cleanup(struct netpoll *np)
kfree_skb(skb);
}
kfree(npinfo);
+ np->dev->npinfo = NULL;
}
}