summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJulian Anastasov <ja@ssi.bg>2015-07-09 02:59:09 -0400
committerDavid S. Miller <davem@davemloft.net>2015-07-10 21:16:36 -0400
commite9e4dd3267d0c5234c5c0f47440456b10875dec9 (patch)
tree8595d91b2a51565721b14ec23ac20835ace9852a /net
parenta7d35f9d73e9ffa74a02304b817e579eec632f67 (diff)
net: do not process device backlog during unregistration
commit 381c759d9916 ("ipv4: Avoid crashing in ip_error") fixes a problem where processed packet comes from device with destroyed inetdev (dev->ip_ptr). This is not expected because inetdev_destroy is called in NETDEV_UNREGISTER phase and packets should not be processed after dev_close_many() and synchronize_net(). Above fix is still required because inetdev_destroy can be called for other reasons. But it shows the real problem: backlog can keep packets for long time and they do not hold reference to device. Such packets are then delivered to upper levels at the same time when device is unregistered. Calling flush_backlog after NETDEV_UNREGISTER_FINAL still accounts all packets from backlog but before that some packets continue to be delivered to upper levels long after the synchronize_net call which is supposed to wait the last ones. Also, as Eric pointed out, processed packets, mostly from other devices, can continue to add new packets to backlog. Fix the problem by moving flush_backlog early, after the device driver is stopped and before the synchronize_net() call. Then use netif_running check to make sure we do not add more packets to backlog. We have to do it in enqueue_to_backlog context when the local IRQ is disabled. As result, after the flush_backlog and synchronize_net sequence all packets should be accounted. Thanks to Eric W. Biederman for the test script and his valuable feedback! Reported-by: Vittorio Gambaletta <linuxbugs@vittgam.net> Fixes: 6e583ce5242f ("net: eliminate refcounting in backlog queue") Cc: Eric W. Biederman <ebiederm@xmission.com> Cc: Stephen Hemminger <stephen@networkplumber.org> Signed-off-by: Julian Anastasov <ja@ssi.bg> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/core/dev.c6
1 files changed, 4 insertions, 2 deletions
diff --git a/net/core/dev.c b/net/core/dev.c
index 1e57efda055b..6dd126a69aa0 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3448,6 +3448,8 @@ static int enqueue_to_backlog(struct sk_buff *skb, int cpu,
3448 local_irq_save(flags); 3448 local_irq_save(flags);
3449 3449
3450 rps_lock(sd); 3450 rps_lock(sd);
3451 if (!netif_running(skb->dev))
3452 goto drop;
3451 qlen = skb_queue_len(&sd->input_pkt_queue); 3453 qlen = skb_queue_len(&sd->input_pkt_queue);
3452 if (qlen <= netdev_max_backlog && !skb_flow_limit(skb, qlen)) { 3454 if (qlen <= netdev_max_backlog && !skb_flow_limit(skb, qlen)) {
3453 if (qlen) { 3455 if (qlen) {
@@ -3469,6 +3471,7 @@ enqueue:
3469 goto enqueue; 3471 goto enqueue;
3470 } 3472 }
3471 3473
3474drop:
3472 sd->dropped++; 3475 sd->dropped++;
3473 rps_unlock(sd); 3476 rps_unlock(sd);
3474 3477
@@ -6135,6 +6138,7 @@ static void rollback_registered_many(struct list_head *head)
6135 unlist_netdevice(dev); 6138 unlist_netdevice(dev);
6136 6139
6137 dev->reg_state = NETREG_UNREGISTERING; 6140 dev->reg_state = NETREG_UNREGISTERING;
6141 on_each_cpu(flush_backlog, dev, 1);
6138 } 6142 }
6139 6143
6140 synchronize_net(); 6144 synchronize_net();
@@ -6770,8 +6774,6 @@ void netdev_run_todo(void)
6770 6774
6771 dev->reg_state = NETREG_UNREGISTERED; 6775 dev->reg_state = NETREG_UNREGISTERED;
6772 6776
6773 on_each_cpu(flush_backlog, dev, 1);
6774
6775 netdev_wait_allrefs(dev); 6777 netdev_wait_allrefs(dev);
6776 6778
6777 /* paranoia */ 6779 /* paranoia */