diff options
author | Jason Wang <jasowang@redhat.com> | 2016-05-19 01:36:51 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-05-20 19:28:37 -0400 |
commit | addf8fc4acb1cf79492ac64966f07178793cb3d7 (patch) | |
tree | c0e712a1e95d2413004aeffd14ac45627b287c92 /drivers/net/tun.c | |
parent | 7fd3c56d60e9919be6b1ef70524eeb28f140653b (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.c | 6 |
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); |