Patch "net/sched: Fix mirred deadlock on device recursion" has been added to the 6.8-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    net/sched: Fix mirred deadlock on device recursion

to the 6.8-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     net-sched-fix-mirred-deadlock-on-device-recursion.patch
and it can be found in the queue-6.8 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit 46de49778bf2b0149b4f742f92c597ff726c2a84
Author: Eric Dumazet <edumazet@xxxxxxxxxx>
Date:   Mon Apr 15 18:07:28 2024 -0300

    net/sched: Fix mirred deadlock on device recursion
    
    [ Upstream commit 0f022d32c3eca477fbf79a205243a6123ed0fe11 ]
    
    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 #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 <renmingshuai@xxxxxxxxxx>
    Closes: https://lore.kernel.org/netdev/20240314111713.5979-1-renmingshuai@xxxxxxxxxx/
    Fixes: 3bcb846ca4cf ("net: get rid of spin_trylock() in net_tx_action()")
    Fixes: e578d9c02587 ("net: sched: use counter to break reclassify loops")
    Signed-off-by: Eric Dumazet <edumazet@xxxxxxxxxx>
    Reviewed-by: Victor Nogueira <victor@xxxxxxxxxxxx>
    Reviewed-by: Pedro Tammela <pctammela@xxxxxxxxxxxx>
    Tested-by: Jamal Hadi Salim <jhs@xxxxxxxxxxxx>
    Acked-by: Jamal Hadi Salim <jhs@xxxxxxxxxxxx>
    Link: https://lore.kernel.org/r/20240415210728.36949-1-victor@xxxxxxxxxxxx
    Signed-off-by: Jakub Kicinski <kuba@xxxxxxxxxx>
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index cefe0c4bdae34..41ca14e81d55f 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -117,6 +117,7 @@ struct Qdisc {
 	struct qdisc_skb_head	q;
 	struct gnet_stats_basic_sync bstats;
 	struct gnet_stats_queue	qstats;
+	int                     owner;
 	unsigned long		state;
 	unsigned long		state2; /* must be written under qdisc spinlock */
 	struct Qdisc            *next_sched;
diff --git a/net/core/dev.c b/net/core/dev.c
index c9b8412f1c9d3..c365aa06f886f 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3791,6 +3791,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_reason(skb, SKB_DROP_REASON_TC_RECLASSIFY_LOOP);
+		return NET_XMIT_DROP;
+	}
 	/*
 	 * Heuristic to force contended enqueues to serialize on a
 	 * separate lock before trying to get qdisc main lock.
@@ -3830,7 +3834,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 = dev_qdisc_enqueue(skb, q, &to_free, txq);
+		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 9b3e9262040b6..a498b5d7c5d60 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -973,6 +973,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;
 	netdev_hold(dev, &sch->dev_tracker, GFP_KERNEL);
 	refcount_set(&sch->refcnt, 1);
 




[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux