Except for sch_mq and sch_mqprio, each dev queue points to the
same root qdisc, so when we reset the dev queues with
netdev_for_each_tx_queue() we end up resetting the same instance
of the root qdisc for multiple times.

Avoid this by checking the __QDISC_STATE_DEACTIVATED bit in
each iteration, so for sch_mq/sch_mqprio, we still reset all
of them like before, for the rest, we only reset it once.

Reported-by: Václav Zindulka <[email protected]>
Tested-by: Václav Zindulka <[email protected]>
Cc: Jamal Hadi Salim <[email protected]>
Cc: Jiri Pirko <[email protected]>
Signed-off-by: Cong Wang <[email protected]>
---
 net/sched/sch_generic.c | 37 +++++++++++++++++++++++--------------
 1 file changed, 23 insertions(+), 14 deletions(-)

diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index a4271e47f220..d13e27467470 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -1128,6 +1128,28 @@ void dev_activate(struct net_device *dev)
 }
 EXPORT_SYMBOL(dev_activate);
 
+static void qdisc_deactivate(struct Qdisc *qdisc)
+{
+       bool nolock = qdisc->flags & TCQ_F_NOLOCK;
+
+       if (qdisc->flags & TCQ_F_BUILTIN)
+               return;
+       if (test_bit(__QDISC_STATE_DEACTIVATED, &qdisc->state))
+               return;
+
+       if (nolock)
+               spin_lock_bh(&qdisc->seqlock);
+       spin_lock_bh(qdisc_lock(qdisc));
+
+       set_bit(__QDISC_STATE_DEACTIVATED, &qdisc->state);
+
+       qdisc_reset(qdisc);
+
+       spin_unlock_bh(qdisc_lock(qdisc));
+       if (nolock)
+               spin_unlock_bh(&qdisc->seqlock);
+}
+
 static void dev_deactivate_queue(struct net_device *dev,
                                 struct netdev_queue *dev_queue,
                                 void *_qdisc_default)
@@ -1137,21 +1159,8 @@ static void dev_deactivate_queue(struct net_device *dev,
 
        qdisc = rtnl_dereference(dev_queue->qdisc);
        if (qdisc) {
-               bool nolock = qdisc->flags & TCQ_F_NOLOCK;
-
-               if (nolock)
-                       spin_lock_bh(&qdisc->seqlock);
-               spin_lock_bh(qdisc_lock(qdisc));
-
-               if (!(qdisc->flags & TCQ_F_BUILTIN))
-                       set_bit(__QDISC_STATE_DEACTIVATED, &qdisc->state);
-
+               qdisc_deactivate(qdisc);
                rcu_assign_pointer(dev_queue->qdisc, qdisc_default);
-               qdisc_reset(qdisc);
-
-               spin_unlock_bh(qdisc_lock(qdisc));
-               if (nolock)
-                       spin_unlock_bh(&qdisc->seqlock);
        }
 }
 
-- 
2.26.2

Reply via email to