Skip to content

Commit 2526987

Browse files
Frederic WeisbeckerIngo Molnar
authored andcommitted
irq_work: Fix irq_work_claim() memory ordering
When irq_work_claim() finds IRQ_WORK_PENDING flag already set, we just return and don't raise a new IPI. We expect the destination to see and handle our latest updades thanks to the pairing atomic_xchg() in irq_work_run_list(). But cmpxchg() doesn't guarantee a full memory barrier upon failure. So it's possible that the destination misses our latest updates. So use atomic_fetch_or() instead that is unconditionally fully ordered and also performs exactly what we want here and simplify the code. Signed-off-by: Frederic Weisbecker <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Paul E . McKenney <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Thomas Gleixner <[email protected]> Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent 153bedb commit 2526987

File tree

1 file changed

+7
-15
lines changed

1 file changed

+7
-15
lines changed

kernel/irq_work.c

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,16 @@ static DEFINE_PER_CPU(struct llist_head, lazy_list);
2929
*/
3030
static bool irq_work_claim(struct irq_work *work)
3131
{
32-
int flags, oflags, nflags;
32+
int oflags;
3333

34+
oflags = atomic_fetch_or(IRQ_WORK_CLAIMED, &work->flags);
3435
/*
35-
* Start with our best wish as a premise but only trust any
36-
* flag value after cmpxchg() result.
36+
* If the work is already pending, no need to raise the IPI.
37+
* The pairing atomic_xchg() in irq_work_run() makes sure
38+
* everything we did before is visible.
3739
*/
38-
flags = atomic_read(&work->flags) & ~IRQ_WORK_PENDING;
39-
for (;;) {
40-
nflags = flags | IRQ_WORK_CLAIMED;
41-
oflags = atomic_cmpxchg(&work->flags, flags, nflags);
42-
if (oflags == flags)
43-
break;
44-
if (oflags & IRQ_WORK_PENDING)
45-
return false;
46-
flags = oflags;
47-
cpu_relax();
48-
}
49-
40+
if (oflags & IRQ_WORK_PENDING)
41+
return false;
5042
return true;
5143
}
5244

0 commit comments

Comments
 (0)