[PATCH 3/3] timer: Reduce unnecessary sighand lock contention

From: George Spelvin
Date: Wed Aug 26 2015 - 15:33:18 EST


And some more comments on the series...

> @@ -626,6 +628,7 @@ struct task_cputime_atomic {
> struct thread_group_cputimer {
> struct task_cputime_atomic cputime_atomic;
> int running;
>+ int checking_timer;
> };

Why not make both running and checking_timer actual booleans, so the
pair is only 16 bits rather than 64?

I suppose since sum_exec_runtime in struct task_cputime_atomic is 64 bits,
alignment means there's no actual improvement. It's just my personal
preference (Linus disagrees with me!) for the bool type for documentation.

(Compile-tested patch appended.)


But when it comes to really understanding this code, I'm struggling.
It seems that this changes the return value of of fastpath_timer_check.
I'm tryng to wrap my head around exactly why that is safe.

Any time I see things like READ_ONCE and WRITE_ONCE, I wonder if there
need to be memory barriers as well. (Because x86 has strong default
memory ordering, testing there doesn't prove the code right on other
platforms.)

For the writes, the surrounding spinlock does the job.

But on the read side (fastpath_timer_check), I'm not sure
what the rules should be.

Or is it basically okay if this is massively racey, since process-wide
CPU timers are inherently sloppy. A race will just cause an expiration
check to be missed, but it will be retried soon anyway.


Other half-baked thoughts that went through my head:

If the problem is that you have contention for read access to
sig->cputimer.cputime, can that be changed to a seqlock?

Another possibility is to use a trylock before testing
checking_timer:

if (sig->cputimer.running) {
struct task_cputime group_sample;

- raw_spin_lock(&sig->cputimer.lock);
+ if (!raw_spin_trylock(&sig->cputimer.lock)) {
+ if (READ_ONCE(sig->cputimer.checking_timer))
+ return false; /* Lock holder's doing it for us */
+ raw_spin_lock(&sig->cputimer.lock);
+ }
group_sample = sig->cputimer.cputime;
raw_spin_unlock(&sig->cputimer.lock);

if (task_cputime_expired(&group_sample, &sig->cputime_expires))
return true;
}
return false;
}




----8<---- This is the patch mentioned above ----8<----