]> git.baikalelectronics.ru Git - kernel.git/commitdiff
mqprio: Correct stats in mqprio_dump_class_stats().
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>
Thu, 7 Oct 2021 17:49:57 +0000 (19:49 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 20 Oct 2021 09:40:17 +0000 (11:40 +0200)
commit 14132690860e4d06aa3e1c4d7d8e9866ba7756dd upstream.

Introduction of lockless subqueues broke the class statistics.
Before the change stats were accumulated in `bstats' and `qstats'
on the stack which was then copied to struct gnet_dump.

After the change the `bstats' and `qstats' are initialized to 0
and never updated, yet still fed to gnet_dump. The code updates
the global qdisc->cpu_bstats and qdisc->cpu_qstats instead,
clobbering them. Most likely a copy-paste error from the code in
mqprio_dump().

__gnet_stats_copy_basic() and __gnet_stats_copy_queue() accumulate
the values for per-CPU case but for global stats they overwrite
the value, so only stats from the last loop iteration / tc end up
in sch->[bq]stats.

Use the on-stack [bq]stats variables again and add the stats manually
in the global case.

Fixes: d094bccebae09 ("net: sched: add support for TCQ_F_NOLOCK subqueues to sch_mqprio")
Cc: John Fastabend <john.fastabend@gmail.com>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
https://lore.kernel.org/all/20211007175000.2334713-2-bigeasy@linutronix.de/
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
net/sched/sch_mqprio.c

index 8766ab5b8788042df03b0b8cdb9e35734ef1a8a7..5eb3b1b7ae5e7886789c010548e43bcff6cf4172 100644 (file)
@@ -529,22 +529,28 @@ static int mqprio_dump_class_stats(struct Qdisc *sch, unsigned long cl,
                for (i = tc.offset; i < tc.offset + tc.count; i++) {
                        struct netdev_queue *q = netdev_get_tx_queue(dev, i);
                        struct Qdisc *qdisc = rtnl_dereference(q->qdisc);
-                       struct gnet_stats_basic_cpu __percpu *cpu_bstats = NULL;
-                       struct gnet_stats_queue __percpu *cpu_qstats = NULL;
 
                        spin_lock_bh(qdisc_lock(qdisc));
+
                        if (qdisc_is_percpu_stats(qdisc)) {
-                               cpu_bstats = qdisc->cpu_bstats;
-                               cpu_qstats = qdisc->cpu_qstats;
+                               qlen = qdisc_qlen_sum(qdisc);
+
+                               __gnet_stats_copy_basic(NULL, &bstats,
+                                                       qdisc->cpu_bstats,
+                                                       &qdisc->bstats);
+                               __gnet_stats_copy_queue(&qstats,
+                                                       qdisc->cpu_qstats,
+                                                       &qdisc->qstats,
+                                                       qlen);
+                       } else {
+                               qlen            += qdisc->q.qlen;
+                               bstats.bytes    += qdisc->bstats.bytes;
+                               bstats.packets  += qdisc->bstats.packets;
+                               qstats.backlog  += qdisc->qstats.backlog;
+                               qstats.drops    += qdisc->qstats.drops;
+                               qstats.requeues += qdisc->qstats.requeues;
+                               qstats.overlimits += qdisc->qstats.overlimits;
                        }
-
-                       qlen = qdisc_qlen_sum(qdisc);
-                       __gnet_stats_copy_basic(NULL, &sch->bstats,
-                                               cpu_bstats, &qdisc->bstats);
-                       __gnet_stats_copy_queue(&sch->qstats,
-                                               cpu_qstats,
-                                               &qdisc->qstats,
-                                               qlen);
                        spin_unlock_bh(qdisc_lock(qdisc));
                }