Commit 39bb5e62 authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

net: skb_fclone_busy() needs to detect orphaned skb

Some drivers are unable to perform TX completions in a bound time.
They instead call skb_orphan()

Problem is skb_fclone_busy() has to detect this case, otherwise
we block TCP retransmits and can freeze unlucky tcp sessions on
mostly idle hosts.
Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Fixes: 1f3279ae ("tcp: avoid retransmits of TCP packets hanging in host queues")
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 14051f04
...@@ -799,15 +799,19 @@ struct sk_buff_fclones { ...@@ -799,15 +799,19 @@ struct sk_buff_fclones {
* @skb: buffer * @skb: buffer
* *
* Returns true is skb is a fast clone, and its clone is not freed. * Returns true is skb is a fast clone, and its clone is not freed.
* Some drivers call skb_orphan() in their ndo_start_xmit(),
* so we also check that this didnt happen.
*/ */
static inline bool skb_fclone_busy(const struct sk_buff *skb) static inline bool skb_fclone_busy(const struct sock *sk,
const struct sk_buff *skb)
{ {
const struct sk_buff_fclones *fclones; const struct sk_buff_fclones *fclones;
fclones = container_of(skb, struct sk_buff_fclones, skb1); fclones = container_of(skb, struct sk_buff_fclones, skb1);
return skb->fclone == SKB_FCLONE_ORIG && return skb->fclone == SKB_FCLONE_ORIG &&
fclones->skb2.fclone == SKB_FCLONE_CLONE; fclones->skb2.fclone == SKB_FCLONE_CLONE &&
fclones->skb2.sk == sk;
} }
static inline struct sk_buff *alloc_skb_fclone(unsigned int size, static inline struct sk_buff *alloc_skb_fclone(unsigned int size,
......
...@@ -2126,7 +2126,7 @@ bool tcp_schedule_loss_probe(struct sock *sk) ...@@ -2126,7 +2126,7 @@ bool tcp_schedule_loss_probe(struct sock *sk)
static bool skb_still_in_host_queue(const struct sock *sk, static bool skb_still_in_host_queue(const struct sock *sk,
const struct sk_buff *skb) const struct sk_buff *skb)
{ {
if (unlikely(skb_fclone_busy(skb))) { if (unlikely(skb_fclone_busy(sk, skb))) {
NET_INC_STATS_BH(sock_net(sk), NET_INC_STATS_BH(sock_net(sk),
LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES); LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES);
return true; return true;
......
...@@ -1962,7 +1962,7 @@ static int xdst_queue_output(struct sock *sk, struct sk_buff *skb) ...@@ -1962,7 +1962,7 @@ static int xdst_queue_output(struct sock *sk, struct sk_buff *skb)
struct xfrm_policy *pol = xdst->pols[0]; struct xfrm_policy *pol = xdst->pols[0];
struct xfrm_policy_queue *pq = &pol->polq; struct xfrm_policy_queue *pq = &pol->polq;
if (unlikely(skb_fclone_busy(skb))) { if (unlikely(skb_fclone_busy(sk, skb))) {
kfree_skb(skb); kfree_skb(skb);
return 0; return 0;
} }
......
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