Commit 70f50965 authored by Cong Wang's avatar Cong Wang Committed by David S. Miller
Browse files

net_sched: avoid resetting active qdisc for multiple times



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: default avatarVáclav Zindulka <vaclav.zindulka@tlapnet.cz>
Tested-by: default avatarVáclav Zindulka <vaclav.zindulka@tlapnet.cz>
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Cc: Jiri Pirko <jiri@resnulli.us>
Signed-off-by: default avatarCong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f5a7833e
Loading
Loading
Loading
Loading
+23 −14
Original line number Diff line number Diff line
@@ -1128,31 +1128,40 @@ void dev_activate(struct net_device *dev)
}
EXPORT_SYMBOL(dev_activate);

static void dev_deactivate_queue(struct net_device *dev,
				 struct netdev_queue *dev_queue,
				 void *_qdisc_default)
static void qdisc_deactivate(struct Qdisc *qdisc)
{
	struct Qdisc *qdisc_default = _qdisc_default;
	struct Qdisc *qdisc;

	qdisc = rtnl_dereference(dev_queue->qdisc);
	if (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));

		if (!(qdisc->flags & TCQ_F_BUILTIN))
	set_bit(__QDISC_STATE_DEACTIVATED, &qdisc->state);

		rcu_assign_pointer(dev_queue->qdisc, qdisc_default);
	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)
{
	struct Qdisc *qdisc_default = _qdisc_default;
	struct Qdisc *qdisc;

	qdisc = rtnl_dereference(dev_queue->qdisc);
	if (qdisc) {
		qdisc_deactivate(qdisc);
		rcu_assign_pointer(dev_queue->qdisc, qdisc_default);
	}
}

static bool some_qdisc_is_busy(struct net_device *dev)