| From e17f49e28a1686f3d21ee3e0f62a802f97bac386 Mon Sep 17 00:00:00 2001 |
| From: Michael S. Tsirkin <mst@redhat.com> |
| Date: Tue, 13 Apr 2010 04:59:44 +0000 |
| Subject: tun: orphan an skb on tx |
| |
| |
| From: Michael S. Tsirkin <mst@redhat.com> |
| |
| [ Upstream commit 0110d6f22f392f976e84ab49da1b42f85b64a3c5 ] |
| |
| The following situation was observed in the field: |
| tap1 sends packets, tap2 does not consume them, as a result |
| tap1 can not be closed. This happens because |
| tun/tap devices can hang on to skbs undefinitely. |
| |
| As noted by Herbert, possible solutions include a timeout followed by a |
| copy/change of ownership of the skb, or always copying/changing |
| ownership if we're going into a hostile device. |
| |
| This patch implements the second approach. |
| |
| Note: one issue still remaining is that since skbs |
| keep reference to tun socket and tun socket has a |
| reference to tun device, we won't flush backlog, |
| instead simply waiting for all skbs to get transmitted. |
| At least this is not user-triggerable, and |
| this was not reported in practice, my assumption is |
| other devices besides tap complete an skb |
| within finite time after it has been queued. |
| |
| A possible solution for the second issue |
| would not to have socket reference the device, |
| instead, implement dev->destructor for tun, and |
| wait for all skbs to complete there, but this |
| needs some thought, probably too risky for 2.6.34. |
| |
| Signed-off-by: Michael S. Tsirkin <mst@redhat.com> |
| Tested-by: Yan Vugenfirer <yvugenfi@redhat.com> |
| Acked-by: Herbert Xu <herbert@gondor.apana.org.au> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| --- |
| drivers/net/tun.c | 4 ++++ |
| 1 file changed, 4 insertions(+) |
| |
| --- a/drivers/net/tun.c |
| +++ b/drivers/net/tun.c |
| @@ -380,6 +380,10 @@ static netdev_tx_t tun_net_xmit(struct s |
| } |
| } |
| |
| + /* Orphan the skb - required as we might hang on to it |
| + * for indefinite time. */ |
| + skb_orphan(skb); |
| + |
| /* Enqueue packet */ |
| skb_queue_tail(&tun->socket.sk->sk_receive_queue, skb); |
| dev->trans_start = jiffies; |