diff options
author | Konstantin Khlebnikov <khlebnikov@yandex-team.ru> | 2017-08-15 16:39:59 +0300 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-08-16 10:55:34 -0700 |
commit | 959466588aa7f84ccf79ae36a1d89542eaf9aaec (patch) | |
tree | 92eb64dfd551fdcf0b9e09b113fe59bc3f178557 /net/sched/sch_api.c | |
parent | 869cec99b4f7c4b97e3e912529cf8505ed1e6c25 (diff) | |
download | linux-959466588aa7f84ccf79ae36a1d89542eaf9aaec.tar.bz2 |
net_sched: call qlen_notify only if child qdisc is empty
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>
Diffstat (limited to 'net/sched/sch_api.c')
-rw-r--r-- | net/sched/sch_api.c | 10 |
1 files changed, 9 insertions, 1 deletions
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 361377fbd780..0fea0c50b763 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -749,6 +749,7 @@ void qdisc_tree_reduce_backlog(struct Qdisc *sch, unsigned int n, 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 Qdisc *sch, unsigned int n, 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 Qdisc *sch, unsigned int n, 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); |