Commit 9540d977 authored by Wei Yongjun's avatar Wei Yongjun Committed by David S. Miller

net: sched: fix skb leak in dev_requeue_skb()

When dev_requeue_skb() is called with bulked skb list, only the
first skb of the list will be requeued to qdisc layer, and leak
the others without free them.

TCP is broken due to skb leak since no free skb will be considered
as still in the host queue and never be retransmitted. This happend
when dev_requeue_skb() called from qdisc_restart().
  qdisc_restart
  |-- dequeue_skb
  |-- sch_direct_xmit()
      |-- dev_requeue_skb() <-- skb may bluked

Fix dev_requeue_skb() to requeue the full bluked list. Also change
to use __skb_queue_tail() in __dev_requeue_skb() to avoid skb out
of order.

Fixes: a53851e2 ("net: sched: explicit locking in gso_cpu fallback")
Signed-off-by: default avatarWei Yongjun <weiyongjun1@huawei.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent a9add194
...@@ -112,10 +112,16 @@ static inline void qdisc_enqueue_skb_bad_txq(struct Qdisc *q, ...@@ -112,10 +112,16 @@ static inline void qdisc_enqueue_skb_bad_txq(struct Qdisc *q,
static inline int __dev_requeue_skb(struct sk_buff *skb, struct Qdisc *q) static inline int __dev_requeue_skb(struct sk_buff *skb, struct Qdisc *q)
{ {
__skb_queue_head(&q->gso_skb, skb); while (skb) {
q->qstats.requeues++; struct sk_buff *next = skb->next;
qdisc_qstats_backlog_inc(q, skb);
q->q.qlen++; /* it's still part of the queue */ __skb_queue_tail(&q->gso_skb, skb);
q->qstats.requeues++;
qdisc_qstats_backlog_inc(q, skb);
q->q.qlen++; /* it's still part of the queue */
skb = next;
}
__netif_schedule(q); __netif_schedule(q);
return 0; return 0;
...@@ -126,12 +132,19 @@ static inline int dev_requeue_skb_locked(struct sk_buff *skb, struct Qdisc *q) ...@@ -126,12 +132,19 @@ static inline int dev_requeue_skb_locked(struct sk_buff *skb, struct Qdisc *q)
spinlock_t *lock = qdisc_lock(q); spinlock_t *lock = qdisc_lock(q);
spin_lock(lock); spin_lock(lock);
__skb_queue_tail(&q->gso_skb, skb); while (skb) {
struct sk_buff *next = skb->next;
__skb_queue_tail(&q->gso_skb, skb);
qdisc_qstats_cpu_requeues_inc(q);
qdisc_qstats_cpu_backlog_inc(q, skb);
qdisc_qstats_cpu_qlen_inc(q);
skb = next;
}
spin_unlock(lock); spin_unlock(lock);
qdisc_qstats_cpu_requeues_inc(q);
qdisc_qstats_cpu_backlog_inc(q, skb);
qdisc_qstats_cpu_qlen_inc(q);
__netif_schedule(q); __netif_schedule(q);
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