Commit 9e17f8a4 authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

net: make skb_set_owner_w() more robust

skb_set_owner_w() is called from various places that assume
skb->sk always point to a full blown socket (as it changes
sk->sk_wmem_alloc)

We'd like to attach skb to request sockets, and in the future
to timewait sockets as well. For these kind of pseudo sockets,
we need to take a traditional refcount and use sock_edemux()
as the destructor.

It is now time to un-inline skb_set_owner_w(), being too big.

Fixes: ca6fb065 ("tcp: attach SYNACK messages to request sockets instead of listener")
Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Bisected-by: default avatarHaiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent eca1e006
...@@ -1951,6 +1951,8 @@ static inline void skb_set_hash_from_sk(struct sk_buff *skb, struct sock *sk) ...@@ -1951,6 +1951,8 @@ static inline void skb_set_hash_from_sk(struct sk_buff *skb, struct sock *sk)
} }
} }
void skb_set_owner_w(struct sk_buff *skb, struct sock *sk);
/* /*
* Queue a received datagram if it will fit. Stream and sequenced * Queue a received datagram if it will fit. Stream and sequenced
* protocols can't normally use this as they need to fit buffers in * protocols can't normally use this as they need to fit buffers in
...@@ -1959,21 +1961,6 @@ static inline void skb_set_hash_from_sk(struct sk_buff *skb, struct sock *sk) ...@@ -1959,21 +1961,6 @@ static inline void skb_set_hash_from_sk(struct sk_buff *skb, struct sock *sk)
* Inlined as it's very short and called for pretty much every * Inlined as it's very short and called for pretty much every
* packet ever received. * packet ever received.
*/ */
static inline void skb_set_owner_w(struct sk_buff *skb, struct sock *sk)
{
skb_orphan(skb);
skb->sk = sk;
skb->destructor = sock_wfree;
skb_set_hash_from_sk(skb, sk);
/*
* We used to take a refcount on sk, but following operation
* is enough to guarantee sk_free() wont free this sock until
* all in-flight packets are completed
*/
atomic_add(skb->truesize, &sk->sk_wmem_alloc);
}
static inline void skb_set_owner_r(struct sk_buff *skb, struct sock *sk) static inline void skb_set_owner_r(struct sk_buff *skb, struct sock *sk)
{ {
skb_orphan(skb); skb_orphan(skb);
......
...@@ -1656,6 +1656,28 @@ void sock_wfree(struct sk_buff *skb) ...@@ -1656,6 +1656,28 @@ void sock_wfree(struct sk_buff *skb)
} }
EXPORT_SYMBOL(sock_wfree); EXPORT_SYMBOL(sock_wfree);
void skb_set_owner_w(struct sk_buff *skb, struct sock *sk)
{
skb_orphan(skb);
skb->sk = sk;
#ifdef CONFIG_INET
if (unlikely(!sk_fullsock(sk))) {
skb->destructor = sock_edemux;
sock_hold(sk);
return;
}
#endif
skb->destructor = sock_wfree;
skb_set_hash_from_sk(skb, sk);
/*
* We used to take a refcount on sk, but following operation
* is enough to guarantee sk_free() wont free this sock until
* all in-flight packets are completed
*/
atomic_add(skb->truesize, &sk->sk_wmem_alloc);
}
EXPORT_SYMBOL(skb_set_owner_w);
void skb_orphan_partial(struct sk_buff *skb) void skb_orphan_partial(struct sk_buff *skb)
{ {
/* TCP stack sets skb->ooo_okay based on sk_wmem_alloc, /* TCP stack sets skb->ooo_okay based on sk_wmem_alloc,
......
...@@ -2963,9 +2963,7 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst, ...@@ -2963,9 +2963,7 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst,
skb_reserve(skb, MAX_TCP_HEADER); skb_reserve(skb, MAX_TCP_HEADER);
if (attach_req) { if (attach_req) {
skb->destructor = sock_edemux; skb_set_owner_w(skb, req_to_sk(req));
sock_hold(req_to_sk(req));
skb->sk = req_to_sk(req);
} else { } else {
/* sk is a const pointer, because we want to express multiple /* sk is a const pointer, because we want to express multiple
* cpu might call us concurrently. * cpu might call us concurrently.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment