aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/irq_work.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/irq_work.c')
-rw-r--r--kernel/irq_work.c21
1 files changed, 15 insertions, 6 deletions
diff --git a/kernel/irq_work.c b/kernel/irq_work.c
index 1588e3b2871b..64eddd59ed83 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);
34 */ 34 */
35static bool irq_work_claim(struct irq_work *work) 35static bool irq_work_claim(struct irq_work *work)
36{ 36{
37 unsigned long flags, nflags; 37 unsigned long flags, oflags, nflags;
38 38
39 /*
40 * Start with our best wish as a premise but only trust any
41 * flag value after cmpxchg() result.
42 */
43 flags = work->flags & ~IRQ_WORK_PENDING;
39 for (;;) { 44 for (;;) {
40 flags = work->flags;
41 if (flags & IRQ_WORK_PENDING)
42 return false;
43 nflags = flags | IRQ_WORK_FLAGS; 45 nflags = flags | IRQ_WORK_FLAGS;
44 if (cmpxchg(&work->flags, flags, nflags) == flags) 46 oflags = cmpxchg(&work->flags, flags, nflags);
47 if (oflags == flags)
45 break; 48 break;
49 if (oflags & IRQ_WORK_PENDING)
50 return false;
51 flags = oflags;
46 cpu_relax(); 52 cpu_relax();
47 } 53 }
48 54
@@ -119,8 +125,11 @@ void irq_work_run(void)
119 /* 125 /*
120 * Clear the PENDING bit, after this point the @work 126 * Clear the PENDING bit, after this point the @work
121 * can be re-used. 127 * can be re-used.
128 * Make it immediately visible so that other CPUs trying
129 * to claim that work don't rely on us to handle their data
130 * while we are in the middle of the func.
122 */ 131 */
123 work->flags = IRQ_WORK_BUSY; 132 xchg(&work->flags, IRQ_WORK_BUSY);
124 work->func(work); 133 work->func(work);
125 /* 134 /*
126 * Clear the BUSY bit and return to the free state if 135 * Clear the BUSY bit and return to the free state if