diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2009-09-24 10:49:24 +0000 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-10-12 12:40:27 -0700 |
commit | 657453424a3c382035983f9a47306fafea730f6d (patch) | |
tree | 559423ec2c3f5860cd08a414549a7d1e0d8fa337 | |
parent | e3d38b579fe7fc60974e45e43034eab774c5a592 (diff) | |
download | lwn-657453424a3c382035983f9a47306fafea730f6d.tar.gz lwn-657453424a3c382035983f9a47306fafea730f6d.zip |
net: Fix sock_wfree() race
[ Upstream commit d99927f4d93f36553699573b279e0ff98ad7dea6 ]
Commit 2b85a34e911bf483c27cfdd124aeb1605145dc80
(net: No more expensive sock_hold()/sock_put() on each tx)
opens a window in sock_wfree() where another cpu
might free the socket we are working on.
A fix is to call sk->sk_write_space(sk) while still
holding a reference on sk.
Reported-by: Jike Song <albcamus@gmail.com>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | net/core/sock.c | 19 |
1 files changed, 12 insertions, 7 deletions
diff --git a/net/core/sock.c b/net/core/sock.c index 76334228ed1c..dd120d8cc8e6 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1218,17 +1218,22 @@ void __init sk_init(void) void sock_wfree(struct sk_buff *skb) { struct sock *sk = skb->sk; - int res; + unsigned int len = skb->truesize; - /* In case it might be waiting for more memory. */ - res = atomic_sub_return(skb->truesize, &sk->sk_wmem_alloc); - if (!sock_flag(sk, SOCK_USE_WRITE_QUEUE)) + if (!sock_flag(sk, SOCK_USE_WRITE_QUEUE)) { + /* + * Keep a reference on sk_wmem_alloc, this will be released + * after sk_write_space() call + */ + atomic_sub(len - 1, &sk->sk_wmem_alloc); sk->sk_write_space(sk); + len = 1; + } /* - * if sk_wmem_alloc reached 0, we are last user and should - * free this sock, as sk_free() call could not do it. + * if sk_wmem_alloc reaches 0, we must finish what sk_free() + * could not do because of in-flight packets */ - if (res == 0) + if (atomic_sub_and_test(len, &sk->sk_wmem_alloc)) __sk_free(sk); } EXPORT_SYMBOL(sock_wfree); |