summaryrefslogtreecommitdiffstats
path: root/drivers/net/tun.c
diff options
context:
space:
mode:
authorJason Wang <jasowang@redhat.com>2016-05-19 01:36:51 -0400
committerDavid S. Miller <davem@davemloft.net>2016-05-20 19:28:37 -0400
commitaddf8fc4acb1cf79492ac64966f07178793cb3d7 (patch)
treec0e712a1e95d2413004aeffd14ac45627b287c92 /drivers/net/tun.c
parent7fd3c56d60e9919be6b1ef70524eeb28f140653b (diff)
tuntap: correctly wake up process during uninit
We used to check dev->reg_state against NETREG_REGISTERED after each time we are woke up. But after commit 9e641bdcfa4e ("net-tun: restructure tun_do_read for better sleep/wakeup efficiency"), it uses skb_recv_datagram() which does not check dev->reg_state. This will result if we delete a tun/tap device after a process is blocked in the reading. The device will wait for the reference count which was held by that process for ever. Fixes this by using RCV_SHUTDOWN which will be checked during sk_recv_datagram() before trying to wake up the process during uninit. Fixes: 9e641bdcfa4e ("net-tun: restructure tun_do_read for better sleep/wakeup efficiency") Cc: Eric Dumazet <edumazet@google.com> Cc: Xi Wang <xii@google.com> Cc: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Jason Wang <jasowang@redhat.com> Acked-by: Eric Dumazet <edumazet@google.com> Acked-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r--drivers/net/tun.c6
1 files changed, 3 insertions, 3 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 425e983bab93..e16487cc6a9a 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -580,11 +580,13 @@ static void tun_detach_all(struct net_device *dev)
580 for (i = 0; i < n; i++) { 580 for (i = 0; i < n; i++) {
581 tfile = rtnl_dereference(tun->tfiles[i]); 581 tfile = rtnl_dereference(tun->tfiles[i]);
582 BUG_ON(!tfile); 582 BUG_ON(!tfile);
583 tfile->socket.sk->sk_shutdown = RCV_SHUTDOWN;
583 tfile->socket.sk->sk_data_ready(tfile->socket.sk); 584 tfile->socket.sk->sk_data_ready(tfile->socket.sk);
584 RCU_INIT_POINTER(tfile->tun, NULL); 585 RCU_INIT_POINTER(tfile->tun, NULL);
585 --tun->numqueues; 586 --tun->numqueues;
586 } 587 }
587 list_for_each_entry(tfile, &tun->disabled, next) { 588 list_for_each_entry(tfile, &tun->disabled, next) {
589 tfile->socket.sk->sk_shutdown = RCV_SHUTDOWN;
588 tfile->socket.sk->sk_data_ready(tfile->socket.sk); 590 tfile->socket.sk->sk_data_ready(tfile->socket.sk);
589 RCU_INIT_POINTER(tfile->tun, NULL); 591 RCU_INIT_POINTER(tfile->tun, NULL);
590 } 592 }
@@ -641,6 +643,7 @@ static int tun_attach(struct tun_struct *tun, struct file *file, bool skip_filte
641 goto out; 643 goto out;
642 } 644 }
643 tfile->queue_index = tun->numqueues; 645 tfile->queue_index = tun->numqueues;
646 tfile->socket.sk->sk_shutdown &= ~RCV_SHUTDOWN;
644 rcu_assign_pointer(tfile->tun, tun); 647 rcu_assign_pointer(tfile->tun, tun);
645 rcu_assign_pointer(tun->tfiles[tun->numqueues], tfile); 648 rcu_assign_pointer(tun->tfiles[tun->numqueues], tfile);
646 tun->numqueues++; 649 tun->numqueues++;
@@ -1491,9 +1494,6 @@ static ssize_t tun_do_read(struct tun_struct *tun, struct tun_file *tfile,
1491 if (!iov_iter_count(to)) 1494 if (!iov_iter_count(to))
1492 return 0; 1495 return 0;
1493 1496
1494 if (tun->dev->reg_state != NETREG_REGISTERED)
1495 return -EIO;
1496
1497 /* Read frames from queue */ 1497 /* Read frames from queue */
1498 skb = __skb_recv_datagram(tfile->socket.sk, noblock ? MSG_DONTWAIT : 0, 1498 skb = __skb_recv_datagram(tfile->socket.sk, noblock ? MSG_DONTWAIT : 0,
1499 &peeked, &off, &err); 1499 &peeked, &off, &err);