Blob Blame History Raw
From: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
Date: Tue, 15 Aug 2017 16:39:59 +0300
Subject: net_sched: call qlen_notify only if child qdisc is empty
Patch-mainline: v4.14-rc1
Git-commit: 959466588aa7f84ccf79ae36a1d89542eaf9aaec
References: bsc#1109837

This callback is used for deactivating class in parent qdisc.
This is cheaper to test queue length right here.

Also this allows to catch draining screwed backlog and prevent
second deactivation of already inactive parent class which will
crash kernel for sure. Kernel with print warning at destruction
of child qdisc where no packets but backlog is not zero.

Signed-off-by: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
Signed-off-by: David S. Miller <davem@davemloft.net>
Acked-by: Thomas Bogendoerfer <tbogendoerfer@suse.de>
---
 net/sched/sch_api.c  |   10 +++++++++-
 net/sched/sch_cbq.c  |    3 +--
 net/sched/sch_drr.c  |    3 +--
 net/sched/sch_hfsc.c |    6 ++----
 net/sched/sch_htb.c  |    3 +--
 net/sched/sch_qfq.c  |    3 +--
 6 files changed, 15 insertions(+), 13 deletions(-)

--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -749,6 +749,7 @@ void qdisc_tree_reduce_backlog(struct Qd
 	const struct Qdisc_class_ops *cops;
 	unsigned long cl;
 	u32 parentid;
+	bool notify;
 	int drops;
 
 	if (n == 0 && len == 0)
@@ -761,6 +762,13 @@ void qdisc_tree_reduce_backlog(struct Qd
 
 		if (sch->flags & TCQ_F_NOPARENT)
 			break;
+		/* Notify parent qdisc only if child qdisc becomes empty.
+		 *
+		 * If child was empty even before update then backlog
+		 * counter is screwed and we skip notification because
+		 * parent class is already passive.
+		 */
+		notify = !sch->q.qlen && !WARN_ON_ONCE(!n);
 		/* TODO: perform the search on a per txq basis */
 		sch = qdisc_lookup(qdisc_dev(sch), TC_H_MAJ(parentid));
 		if (sch == NULL) {
@@ -768,7 +776,7 @@ void qdisc_tree_reduce_backlog(struct Qd
 			break;
 		}
 		cops = sch->ops->cl_ops;
-		if (cops->qlen_notify) {
+		if (notify && cops->qlen_notify) {
 			cl = cops->get(sch, parentid);
 			cops->qlen_notify(sch, cl);
 			cops->put(sch, cl);
--- a/net/sched/sch_cbq.c
+++ b/net/sched/sch_cbq.c
@@ -1389,8 +1389,7 @@ static void cbq_qlen_notify(struct Qdisc
 {
 	struct cbq_class *cl = (struct cbq_class *)arg;
 
-	if (cl->q->q.qlen == 0)
-		cbq_deactivate_class(cl);
+	cbq_deactivate_class(cl);
 }
 
 static unsigned long cbq_get(struct Qdisc *sch, u32 classid)
--- a/net/sched/sch_drr.c
+++ b/net/sched/sch_drr.c
@@ -246,8 +246,7 @@ static void drr_qlen_notify(struct Qdisc
 {
 	struct drr_class *cl = (struct drr_class *)arg;
 
-	if (cl->qdisc->q.qlen == 0)
-		list_del(&cl->alist);
+	list_del(&cl->alist);
 }
 
 static int drr_dump_class(struct Qdisc *sch, unsigned long arg,
--- a/net/sched/sch_hfsc.c
+++ b/net/sched/sch_hfsc.c
@@ -1221,10 +1221,8 @@ hfsc_qlen_notify(struct Qdisc *sch, unsi
 {
 	struct hfsc_class *cl = (struct hfsc_class *)arg;
 
-	if (cl->qdisc->q.qlen == 0) {
-		update_vf(cl, 0, 0);
-		set_passive(cl);
-	}
+	update_vf(cl, 0, 0);
+	set_passive(cl);
 }
 
 static unsigned long
--- a/net/sched/sch_htb.c
+++ b/net/sched/sch_htb.c
@@ -1187,8 +1187,7 @@ static void htb_qlen_notify(struct Qdisc
 {
 	struct htb_class *cl = (struct htb_class *)arg;
 
-	if (cl->un.leaf.q->q.qlen == 0)
-		htb_deactivate(qdisc_priv(sch), cl);
+	htb_deactivate(qdisc_priv(sch), cl);
 }
 
 static unsigned long htb_get(struct Qdisc *sch, u32 classid)
--- a/net/sched/sch_qfq.c
+++ b/net/sched/sch_qfq.c
@@ -1428,8 +1428,7 @@ static void qfq_qlen_notify(struct Qdisc
 	struct qfq_sched *q = qdisc_priv(sch);
 	struct qfq_class *cl = (struct qfq_class *)arg;
 
-	if (cl->qdisc->q.qlen == 0)
-		qfq_deactivate_class(q, cl);
+	qfq_deactivate_class(q, cl);
 }
 
 static int qfq_init_qdisc(struct Qdisc *sch, struct nlattr *opt)