On Thu, Feb 13, 2025 at 01:30:33PM -0500, Joel Fernandes wrote:
> +rcu for archives
>
> Hi,
>
> Following up about our gpwrap discussions, I did some tracing of rdp->gpwrap
> with just boot testing.
>
> I actually never see it set because rdp->gp_seq and rnp->gp_seq are usually
> very close.
>
> Example trace:
>
> # rnp->gp_seq starts with -1200 on boot and then right around the wrap:
>
> 178.096329: rcu_gpnum_ovf: CPU: 2, rnp->gp_seq: -3 (0xfffffffffffffffd),
> rdp->gp_seq: -3 (0xfffffffffffffffd), wrap before: 0
> 178.096330: rcu_gpnum_ovf: CPU: 2, rnp->gp_seq: -3 (0xfffffffffffffffd),
> rdp->gp_seq: -3 (0xfffffffffffffffd), wrap after: 0
> 178.100327: rcu_gpnum_ovf: CPU: 2, rnp->gp_seq: 1 (0x1), rdp->gp_seq: 1
> (0x1), wrap before: 0
> 178.100328: rcu_gpnum_ovf: CPU: 2, rnp->gp_seq: 1 (0x1), rdp->gp_seq: 1
> (0x1), wrap after: 0
>
> The wrap "before" and "after" are the value of gpwrap before and after
> calling rcu_gpnum_ovf().
>
> Closer look at the _ovf() shows it should be only set if rdp->gp_seq and
> rnp->gp_seq are quite a distance apart (say if the CPU was idle for too long
> and many GPs have passed. This can happen both with/without wrap. So imminent
> wrapping seems not necessary for ->gpwrap to be even set AFAICS.
>
> I think the problem is ULONG_CMP_LT is not really "<" so even after wrap, it
> can false. i.e if rdp->gpseq + ULONG/4 wraps, it could still return false.
Exactly, and all of that is in fact the intended behavior.
> >From a testing POV, the example shows it is not set even when around when a
> wrap actually happened. So may be, we are not testing gpwrap much, or at
> all, with rcutorture?
True enough, and that would be well worth fixing.
One simple (and maybe too simple) fix is to add a Kconfig option to
kernel/rcu/Kconfig.debug that makes rcu_gpnum_ovf() use something like
(32 << RCU_SEQ_CTR_SHIFT). This, or some similar value, should cause
rcutorture's stuttering and/or shuffling to trigger setting of ->gpwrap
within a few minutes.
And I would not be surprised if doing this located bugs.
> But before that, I am feeling it is not behaving correctly. I'd expect it to
> be set even if rnp and rdp values are close but we are actually about to
> wrap. So most likely I am missing something.
If I understand correctly, you would like ->gpwrap to be set
unconditionally at various points in the cycle, for example, at zero,
ULONG_MAX/4, ULONG_MAX/2, and 3*ULONG_MAX/4. But this could easily
have the effect of discarding each CPU's quiescent states that showed
up before the first call to rcu_watching_snap_recheck(), even if that
CPU was active. Our users might not consider this to be a friendly act.
In contrast, the current setup only discards quiescent states for CPUs
that just came back from idle prior to call to rcu_watching_snap_save().
In addition, use of the aforementioned Kconfig option has the benefit of
making any ->gpwrap-related bugs appear during testing, not in production.
On the same grounds, one could argue that absence of this Kconfig option
should also cause gp_seq to zero rather than the current;
.gp_seq = (0UL - 300UL) << RCU_SEQ_CTR_SHIFT,
One argument against is that production 32-bit systems can wrap ->gp_seq
in a relatively short time. Maybe moreso for ->expedited_sequence, so
perhaps leave ->gp_seq and modify ->expedited_sequence. Maybe similar
decisions for others as well. ;-)
Thanx, Paul