diff mbox series

[net-next,3/4] gen_stats: Add instead Set the value in __gnet_stats_copy_queue().

Message ID 20211007175000.2334713-4-bigeasy@linutronix.de (mailing list archive)
State Deferred
Delegated to: Netdev Maintainers
Headers show
Series mqprio fixup and simplify code. | expand

Checks

Context Check Description
netdev/cover_letter success Series has a cover letter
netdev/fixes_present success Fixes tag not required for -next series
netdev/patch_count success Link
netdev/tree_selection success Guessed tree name to be net-next
netdev/subject_prefix warning Target tree name not specified in the subject
netdev/cc_maintainers success CCed 4 of 4 maintainers
netdev/source_inline success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/module_param success Was 0 now: 0
netdev/build_32bit success Errors and warnings before: 7 this patch: 7
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/verify_fixes success No Fixes tag
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 20 lines checked
netdev/build_allmodconfig_warn success Errors and warnings before: 7 this patch: 7
netdev/header_inline success No static functions without inline keyword in header files

Commit Message

Sebastian Andrzej Siewior Oct. 7, 2021, 5:49 p.m. UTC
Based on review there are five users of __gnet_stats_copy_queue as of
today:
- qdisc_qstats_qlen_backlog(), gnet_stats_copy_queue(),
  memsets() bstats to zero, single invocation.

- mq_dump(), mqprio_dump(), mqprio_dump_class_stats(),
  memsets() bstats to zero, multiple invocation but does not use the
  function due to !qdisc_is_percpu_stats().

It will probably simplify in percpu stats case if the value would be
added and not just stored.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 net/core/gen_stats.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

Comments

Jakub Kicinski Oct. 8, 2021, 11:38 p.m. UTC | #1
On Thu,  7 Oct 2021 19:49:59 +0200 Sebastian Andrzej Siewior wrote:
> --- a/net/core/gen_stats.c
> +++ b/net/core/gen_stats.c
> @@ -312,14 +312,14 @@ void __gnet_stats_copy_queue(struct gnet_stats_queue *qstats,
>  	if (cpu) {
>  		__gnet_stats_copy_queue_cpu(qstats, cpu);
>  	} else {
> -		qstats->qlen = q->qlen;
> -		qstats->backlog = q->backlog;
> -		qstats->drops = q->drops;
> -		qstats->requeues = q->requeues;
> -		qstats->overlimits = q->overlimits;
> +		qstats->qlen += q->qlen;
> +		qstats->backlog += q->backlog;
> +		qstats->drops += q->drops;
> +		qstats->requeues += q->requeues;
> +		qstats->overlimits += q->overlimits;
>  	}
>  
> -	qstats->qlen = qlen;
> +	qstats->qlen += qlen;

Looks like qlen is going to be added twice for the non-per-cpu case?
Sebastian Andrzej Siewior Oct. 13, 2021, 4 p.m. UTC | #2
On 2021-10-08 16:38:51 [-0700], Jakub Kicinski wrote:
> On Thu,  7 Oct 2021 19:49:59 +0200 Sebastian Andrzej Siewior wrote:
> > --- a/net/core/gen_stats.c
> > +++ b/net/core/gen_stats.c
> > @@ -312,14 +312,14 @@ void __gnet_stats_copy_queue(struct gnet_stats_queue *qstats,
> >  	if (cpu) {
> >  		__gnet_stats_copy_queue_cpu(qstats, cpu);
> >  	} else {
> > -		qstats->qlen = q->qlen;
> > -		qstats->backlog = q->backlog;
> > -		qstats->drops = q->drops;
> > -		qstats->requeues = q->requeues;
> > -		qstats->overlimits = q->overlimits;
> > +		qstats->qlen += q->qlen;
> > +		qstats->backlog += q->backlog;
> > +		qstats->drops += q->drops;
> > +		qstats->requeues += q->requeues;
> > +		qstats->overlimits += q->overlimits;
> >  	}
> >  
> > -	qstats->qlen = qlen;
> > +	qstats->qlen += qlen;
> 
> Looks like qlen is going to be added twice for the non-per-cpu case?

Hmmm. Let me dive into unknown territory…

Yes, it does. Also in the pcpu-case it does not look very straight what
goes on:
	qlen = qdisc_qlen_sum(qdisc); /* sum of per-CPU cpu_qstat.qlen */
	__gnet_stats_copy_queue() /* sch.qstats.qlen = qlen */
	sch->q.qlen     += qlen;

sch->qstats.qlen is qdisc_qlen_sum() of the qdisc from last for loop.
sch->q.qlen contains qdisc_qlen_sum() of all qdiscs from the for loop. I
doubt this is intended.
My guess is that a sum (like in the !pcpu case is intended).

We have
- qdisc.q.qlen qdisc_skb_head::qlen
- qdisc.qstats.qlen aka gnet_stats_queue::qlen.

qdisc_skb_head::qlen is incremented if skbs are added to the qdisc which
are about to be sent. Usually the skb is added to the qdisc_skb_head but
some scheduling classes have their own queues (say sch_sfq) and probably
increment this field just to keep things like qdisc_is_empty() working.

gnet_stats_queue::qlen is the number of skbs either in Qdisc::skb_bad_txq or
Qdisc::gso_skb.

qdisc_update_stats_at_enqueue() increments in percpu case the per-CPU
gnet_stats_queue::qlen but in the !percpu case it increments
qdisc_skb_head::qlen. But there is the qstats member which is also if
type gnet_stats_queue. For backlog, the gnet_stats_queue struct is
always used, either per-CPU or the global one. Not here. Not sure if it
on purpose or not. The same true for qdisc_enqueue_skb_bad_txq() and
dev_requeue_skb() plus their counterpart so it consistent.

This brings me to __gnet_stats_copy_queue(). In the percpu case,
caller's gnet_stats_queue::qlen is set to 0 multiple times. And then
caller's qlen argument is assigned to the qlen member. In !percpu
case it copies gnet_stats_queue::qlen. But I don't see an
increment/decrement of that field here so it has to be zero. Also at the
end it sets the field to caller's qlen. So…

This probably works because callers of __gnet_stats_copy_queue() invoke
qdisc_qlen_sum() which returns the sum of:
  qstats::qlen (0) + 
     per-CPU qstats::qlen || !per-CPU qdisc_skb_head::qlen

So in the end the caller figures out qlen is and passes it as a
argument. The copy process of qlen ist decoy.

But then there is gnet_stats_copy_queue().
sch_fq_codel modifies qdisc_skb_head::qlen on fq_codel_enqueue()/
fq_codel_dequeue(). But fq_codel_dump_class_stats() returns statistics
for a specific flow so it counts the number of skb which is less than
the value qdisc_skb_head.qlen if multiple flows are configured.

So I think I could remove the qlen argument from
__gnet_stats_copy_queue() and just copy what is there. And make a real
copy, not just summing all qlen into qdisc_skb_head.qlen and leaving
gnet_stats_queue.qlen with the last value.

Sebastian
diff mbox series

Patch

diff --git a/net/core/gen_stats.c b/net/core/gen_stats.c
index e12e544a7ab0f..76dbae98c83fd 100644
--- a/net/core/gen_stats.c
+++ b/net/core/gen_stats.c
@@ -312,14 +312,14 @@  void __gnet_stats_copy_queue(struct gnet_stats_queue *qstats,
 	if (cpu) {
 		__gnet_stats_copy_queue_cpu(qstats, cpu);
 	} else {
-		qstats->qlen = q->qlen;
-		qstats->backlog = q->backlog;
-		qstats->drops = q->drops;
-		qstats->requeues = q->requeues;
-		qstats->overlimits = q->overlimits;
+		qstats->qlen += q->qlen;
+		qstats->backlog += q->backlog;
+		qstats->drops += q->drops;
+		qstats->requeues += q->requeues;
+		qstats->overlimits += q->overlimits;
 	}
 
-	qstats->qlen = qlen;
+	qstats->qlen += qlen;
 }
 EXPORT_SYMBOL(__gnet_stats_copy_queue);