Re: Uclamp races due to non-atomic static_branch_likely updates

From: Steven Rostedt

Date: Wed Oct 21 2026 - 09:25:33 EST


On Wed, 21 Oct 2026 13:38:06 +0200
Peter Zijlstra <peterz@xxxxxxxxxxxxx> wrote:

> On Tue, Oct 20, 2026 at 03:29:33PM -0400, Steven Rostedt wrote:
> > On Tue, 20 Oct 2026 11:53:47 -0700
> > John Stultz <jstultz@xxxxxxxxxx> wrote:
> >
> > > Maybe should the jump_label_update() logic be using something like
> > > stop_machine? Or should there still be a conditional on key->enabled
> > > in the unlikely case to make the behavior consistent? Or is there just
> > > something about the uclamp usage that is off?
> >
> > There's never been any guarantee that all call sites would update
> > atomically. The first user of jump labels was tracepoints and I really do
> > not want stop machine involved with them.
> >
> > Perhaps we could add an api like static_key_enable_atomic() and
> > static_key_disable_atomic() that would go through a stop_machine() and
> > update everything that way for those locations that need an atomic view of
> > the updated jump labels.
>
> Argh, no, we do not want people to 'accidentally' add stop_machine()
> usage, which we then have to spend tons of time on removing again.

OK, sure. I was a bit skeptical of that approach too.

>
> And yeah, its always been the case that jump_labels (and static_branch)
> updates are non-atomic. Most of them are init time, and don't care.
>
> Now, I'm sure we've ran into this particular problem before, and it
> always requires care.
>
> Also note that a simple global boolean has the exact same problem.
> Adding runtime conditionals isn't going to fix this.

Another idea for "atomic static branch" is to have two static branches at
each location. One that switches to a "check this variable and do a normal
test and branch" and "we are now in sync and can use the original static
branch".

Something like:

if (static_branch_unlikely(&foo_use_variable.key)) {
return static_key_enabled(&foo->key);
} else {
if (static_branch_unlikely(&foo->key))
return true;
else
return false;
}

And then in between updates of foo, the foo_use_variable gets set making
all the branches use the key, which will be updated "atomically" (maybe
some memory barriers needs to be added). After the updates of all the
static keys are complete, the foo_use_variable gets unset.

The non-atomic portion of "foo_use_variable" is OK because the foo->key
will match whatever the static_branch version it has.

The end result avoids the test and branch but will have two nop/jmp pairs.

-- Steve