Skip to content

Commit 0f022d3

Browse files
Eric Dumazetkuba-moo
Eric Dumazet
authored andcommitted
net/sched: Fix mirred deadlock on device recursion
When the mirred action is used on a classful egress qdisc and a packet is mirrored or redirected to self we hit a qdisc lock deadlock. See trace below. [..... other info removed for brevity....] [ 82.890906] [ 82.890906] ============================================ [ 82.890906] WARNING: possible recursive locking detected [ 82.890906] 6.8.0-05205-g77fadd89fe2d-dirty torvalds#213 Tainted: G W [ 82.890906] -------------------------------------------- [ 82.890906] ping/418 is trying to acquire lock: [ 82.890906] ffff888006994110 (&sch->q.lock){+.-.}-{3:3}, at: __dev_queue_xmit+0x1778/0x3550 [ 82.890906] [ 82.890906] but task is already holding lock: [ 82.890906] ffff888006994110 (&sch->q.lock){+.-.}-{3:3}, at: __dev_queue_xmit+0x1778/0x3550 [ 82.890906] [ 82.890906] other info that might help us debug this: [ 82.890906] Possible unsafe locking scenario: [ 82.890906] [ 82.890906] CPU0 [ 82.890906] ---- [ 82.890906] lock(&sch->q.lock); [ 82.890906] lock(&sch->q.lock); [ 82.890906] [ 82.890906] *** DEADLOCK *** [ 82.890906] [..... other info removed for brevity....] Example setup (eth0->eth0) to recreate tc qdisc add dev eth0 root handle 1: htb default 30 tc filter add dev eth0 handle 1: protocol ip prio 2 matchall \ action mirred egress redirect dev eth0 Another example(eth0->eth1->eth0) to recreate tc qdisc add dev eth0 root handle 1: htb default 30 tc filter add dev eth0 handle 1: protocol ip prio 2 matchall \ action mirred egress redirect dev eth1 tc qdisc add dev eth1 root handle 1: htb default 30 tc filter add dev eth1 handle 1: protocol ip prio 2 matchall \ action mirred egress redirect dev eth0 We fix this by adding an owner field (CPU id) to struct Qdisc set after root qdisc is entered. When the softirq enters it a second time, if the qdisc owner is the same CPU, the packet is dropped to break the loop. Reported-by: Mingshuai Ren <[email protected]> Closes: https://lore.kernel.org/netdev/[email protected]/ Fixes: 3bcb846 ("net: get rid of spin_trylock() in net_tx_action()") Fixes: e578d9c ("net: sched: use counter to break reclassify loops") Signed-off-by: Eric Dumazet <[email protected]> Reviewed-by: Victor Nogueira <[email protected]> Reviewed-by: Pedro Tammela <[email protected]> Tested-by: Jamal Hadi Salim <[email protected]> Acked-by: Jamal Hadi Salim <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 8378138 commit 0f022d3

File tree

3 files changed

+8
-0
lines changed

3 files changed

+8
-0
lines changed

include/net/sch_generic.h

+1
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ struct Qdisc {
117117
struct qdisc_skb_head q;
118118
struct gnet_stats_basic_sync bstats;
119119
struct gnet_stats_queue qstats;
120+
int owner;
120121
unsigned long state;
121122
unsigned long state2; /* must be written under qdisc spinlock */
122123
struct Qdisc *next_sched;

net/core/dev.c

+6
Original file line numberDiff line numberDiff line change
@@ -3775,6 +3775,10 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q,
37753775
return rc;
37763776
}
37773777

3778+
if (unlikely(READ_ONCE(q->owner) == smp_processor_id())) {
3779+
kfree_skb_reason(skb, SKB_DROP_REASON_TC_RECLASSIFY_LOOP);
3780+
return NET_XMIT_DROP;
3781+
}
37783782
/*
37793783
* Heuristic to force contended enqueues to serialize on a
37803784
* separate lock before trying to get qdisc main lock.
@@ -3814,7 +3818,9 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q,
38143818
qdisc_run_end(q);
38153819
rc = NET_XMIT_SUCCESS;
38163820
} else {
3821+
WRITE_ONCE(q->owner, smp_processor_id());
38173822
rc = dev_qdisc_enqueue(skb, q, &to_free, txq);
3823+
WRITE_ONCE(q->owner, -1);
38183824
if (qdisc_run_begin(q)) {
38193825
if (unlikely(contended)) {
38203826
spin_unlock(&q->busylock);

net/sched/sch_generic.c

+1
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,7 @@ struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue,
974974
sch->enqueue = ops->enqueue;
975975
sch->dequeue = ops->dequeue;
976976
sch->dev_queue = dev_queue;
977+
sch->owner = -1;
977978
netdev_hold(dev, &sch->dev_tracker, GFP_KERNEL);
978979
refcount_set(&sch->refcnt, 1);
979980

0 commit comments

Comments
 (0)