diff options
author | Ingo Molnar <mingo@kernel.org> | 2013-01-24 12:48:41 +0100 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2013-01-24 12:48:41 +0100 |
commit | 786133f6e8ff94aaa78cd6b7844d04c227098327 (patch) | |
tree | be5922c90ec74267bf068987e4de14a6436d04de /kernel | |
parent | befddb21c845f8fb49e637997891ef97c6a869dc (diff) | |
parent | 6147a9d8070e1c9d16d57eb53a14942b95b28dc4 (diff) |
Merge branch 'core/irq_work' of git://git.kernel.org/pub/scm/linux/kernel/git/frederic/linux-dynticks into irq/core
irq_work fixes and cleanups, in preparation for full dyntics support.
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/irq_work.c | 21 |
1 files changed, 15 insertions, 6 deletions
diff --git a/kernel/irq_work.c b/kernel/irq_work.c index 1588e3b2871..64eddd59ed8 100644 --- a/kernel/irq_work.c +++ b/kernel/irq_work.c @@ -34,15 +34,21 @@ static DEFINE_PER_CPU(struct llist_head, irq_work_list); */ static bool irq_work_claim(struct irq_work *work) { - unsigned long flags, nflags; + unsigned long flags, oflags, nflags; + /* + * Start with our best wish as a premise but only trust any + * flag value after cmpxchg() result. + */ + flags = work->flags & ~IRQ_WORK_PENDING; for (;;) { - flags = work->flags; - if (flags & IRQ_WORK_PENDING) - return false; nflags = flags | IRQ_WORK_FLAGS; - if (cmpxchg(&work->flags, flags, nflags) == flags) + oflags = cmpxchg(&work->flags, flags, nflags); + if (oflags == flags) break; + if (oflags & IRQ_WORK_PENDING) + return false; + flags = oflags; cpu_relax(); } @@ -119,8 +125,11 @@ void irq_work_run(void) /* * Clear the PENDING bit, after this point the @work * can be re-used. + * Make it immediately visible so that other CPUs trying + * to claim that work don't rely on us to handle their data + * while we are in the middle of the func. */ - work->flags = IRQ_WORK_BUSY; + xchg(&work->flags, IRQ_WORK_BUSY); work->func(work); /* * Clear the BUSY bit and return to the free state if |