|
@@ -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
|