diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index e357c7a0a77be19261238e08d10117ec0e6bd7b5..901b69e45fab0f3ba3080b2397a717c66ded9e70 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -104,6 +104,7 @@ struct Qdisc { struct gnet_stats_basic_packed bstats; seqcount_t running; struct gnet_stats_queue qstats; + int owner; unsigned long state; struct Qdisc *next_sched; struct sk_buff_head skb_bad_txq; diff --git a/net/core/dev.c b/net/core/dev.c index 674889f29b884540b6aa411293e6d7f76506775d..477969e86b71d7cbd819121b9ab5daa6ed3a9544 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3813,6 +3813,10 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, return rc; } + if (unlikely(READ_ONCE(q->owner) == smp_processor_id())) { + kfree_skb(skb); + return NET_XMIT_DROP; + } /* * Heuristic to force contended enqueues to serialize on a * separate lock before trying to get qdisc main lock. @@ -3848,7 +3852,9 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, qdisc_run_end(q); rc = NET_XMIT_SUCCESS; } else { + WRITE_ONCE(q->owner, smp_processor_id()); rc = q->enqueue(skb, q, &to_free) & NET_XMIT_MASK; + WRITE_ONCE(q->owner, -1); if (qdisc_run_begin(q)) { if (unlikely(contended)) { spin_unlock(&q->busylock); diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index bb921c05720e8187a32f22018476ffb7230ce216..da6cd1050ecf405aab6b7a91eb3c1ac8de7199d2 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -901,6 +901,7 @@ struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue, sch->enqueue = ops->enqueue; sch->dequeue = ops->dequeue; sch->dev_queue = dev_queue; + sch->owner = -1; sch->empty = true; dev_hold(dev); refcount_set(&sch->refcnt, 1);