aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorNeil Horman <nhorman@tuxdriver.com>2006-06-26 03:04:27 -0400
committerDavid S. Miller <davem@davemloft.net>2006-06-26 03:04:27 -0400
commit068c6e98bc7ec4419299b38cd40be26ebf4bdeda (patch)
tree4312d3449036c35d5fc8fd4f60f447200928e817 /net
parent8834807b43200b1658b49d3b779e74a4f77e4ffb (diff)
[NET] netpoll: break recursive loop in netpoll rx path
The netpoll system currently has a rx to tx path via: netpoll_rx __netpoll_rx arp_reply netpoll_send_skb dev->hard_start_tx This rx->tx loop places network drivers at risk of inadvertently causing a deadlock or BUG halt by recursively trying to acquire a spinlock that is used in both their rx and tx paths (this problem was origionally reported to me in the 3c59x driver, which shares a spinlock between the boomerang_interrupt and boomerang_start_xmit routines). This patch breaks this loop, by queueing arp frames, so that they can be responded to after all receive operations have been completed. Tested by myself and the reported with successful results. Specifically it was tested with netdump. Heres the BZ with details: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=194055 Signed-off-by: Neil Horman <nhorman@tuxdriver.com> Acked-by: Matt Mackall <mpm@selenic.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/core/netpoll.c26
1 files changed, 24 insertions, 2 deletions
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index 377d1e7257b5..471da451cd48 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -54,6 +54,7 @@ static atomic_t trapped;
54 sizeof(struct iphdr) + sizeof(struct ethhdr)) 54 sizeof(struct iphdr) + sizeof(struct ethhdr))
55 55
56static void zap_completion_queue(void); 56static void zap_completion_queue(void);
57static void arp_reply(struct sk_buff *skb);
57 58
58static void queue_process(void *p) 59static void queue_process(void *p)
59{ 60{
@@ -153,6 +154,22 @@ static void poll_napi(struct netpoll *np)
153 } 154 }
154} 155}
155 156
157static void service_arp_queue(struct netpoll_info *npi)
158{
159 struct sk_buff *skb;
160
161 if (unlikely(!npi))
162 return;
163
164 skb = skb_dequeue(&npi->arp_tx);
165
166 while (skb != NULL) {
167 arp_reply(skb);
168 skb = skb_dequeue(&npi->arp_tx);
169 }
170 return;
171}
172
156void netpoll_poll(struct netpoll *np) 173void netpoll_poll(struct netpoll *np)
157{ 174{
158 if(!np->dev || !netif_running(np->dev) || !np->dev->poll_controller) 175 if(!np->dev || !netif_running(np->dev) || !np->dev->poll_controller)
@@ -163,6 +180,8 @@ void netpoll_poll(struct netpoll *np)
163 if (np->dev->poll) 180 if (np->dev->poll)
164 poll_napi(np); 181 poll_napi(np);
165 182
183 service_arp_queue(np->dev->npinfo);
184
166 zap_completion_queue(); 185 zap_completion_queue();
167} 186}
168 187
@@ -442,7 +461,9 @@ int __netpoll_rx(struct sk_buff *skb)
442 int proto, len, ulen; 461 int proto, len, ulen;
443 struct iphdr *iph; 462 struct iphdr *iph;
444 struct udphdr *uh; 463 struct udphdr *uh;
445 struct netpoll *np = skb->dev->npinfo->rx_np; 464 struct netpoll_info *npi = skb->dev->npinfo;
465 struct netpoll *np = npi->rx_np;
466
446 467
447 if (!np) 468 if (!np)
448 goto out; 469 goto out;
@@ -452,7 +473,7 @@ int __netpoll_rx(struct sk_buff *skb)
452 /* check if netpoll clients need ARP */ 473 /* check if netpoll clients need ARP */
453 if (skb->protocol == __constant_htons(ETH_P_ARP) && 474 if (skb->protocol == __constant_htons(ETH_P_ARP) &&
454 atomic_read(&trapped)) { 475 atomic_read(&trapped)) {
455 arp_reply(skb); 476 skb_queue_tail(&npi->arp_tx, skb);
456 return 1; 477 return 1;
457 } 478 }
458 479
@@ -647,6 +668,7 @@ int netpoll_setup(struct netpoll *np)
647 npinfo->poll_owner = -1; 668 npinfo->poll_owner = -1;
648 npinfo->tries = MAX_RETRIES; 669 npinfo->tries = MAX_RETRIES;
649 spin_lock_init(&npinfo->rx_lock); 670 spin_lock_init(&npinfo->rx_lock);
671 skb_queue_head_init(&npinfo->arp_tx);
650 } else 672 } else
651 npinfo = ndev->npinfo; 673 npinfo = ndev->npinfo;
652 674