net: Fix sock_wfree() race
Commit 2b85a34e91
(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>
This commit is contained in:
parent
b7058842c9
commit
d99927f4d9
1 changed files with 12 additions and 7 deletions
|
@ -1228,17 +1228,22 @@ void __init sk_init(void)
|
||||||
void sock_wfree(struct sk_buff *skb)
|
void sock_wfree(struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct sock *sk = skb->sk;
|
struct sock *sk = skb->sk;
|
||||||
int res;
|
unsigned int len = skb->truesize;
|
||||||
|
|
||||||
/* In case it might be waiting for more memory. */
|
if (!sock_flag(sk, SOCK_USE_WRITE_QUEUE)) {
|
||||||
res = atomic_sub_return(skb->truesize, &sk->sk_wmem_alloc);
|
/*
|
||||||
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);
|
sk->sk_write_space(sk);
|
||||||
|
len = 1;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* if sk_wmem_alloc reached 0, we are last user and should
|
* if sk_wmem_alloc reaches 0, we must finish what sk_free()
|
||||||
* free this sock, as sk_free() call could not do it.
|
* could not do because of in-flight packets
|
||||||
*/
|
*/
|
||||||
if (res == 0)
|
if (atomic_sub_and_test(len, &sk->sk_wmem_alloc))
|
||||||
__sk_free(sk);
|
__sk_free(sk);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sock_wfree);
|
EXPORT_SYMBOL(sock_wfree);
|
||||||
|
|
Loading…
Reference in a new issue