net_sched: avoid resetting active qdisc for multiple times
authorCong Wang <xiyou.wangcong@gmail.com>
Wed, 27 May 2020 04:35:26 +0000 (21:35 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 27 May 2020 22:05:49 +0000 (15:05 -0700)
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 <vaclav.zindulka@tlapnet.cz>
Tested-by: Václav Zindulka <vaclav.zindulka@tlapnet.cz>
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Cc: Jiri Pirko <jiri@resnulli.us>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/sched/sch_generic.c

index a4271e47f220b38c4ef7bb01058ef486538a1992..d13e27467470d1abcd5c99870197d45ee4f7889b 100644 (file)
@@ -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);
        }
 }