diff options
Diffstat (limited to 'kernel/irq_work.c')
| -rw-r--r-- | kernel/irq_work.c | 16 | 
1 files changed, 11 insertions, 5 deletions
diff --git a/kernel/irq_work.c b/kernel/irq_work.c index 57be1a6cd8d..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();  	}  |