mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	workqueue: add missing wmb() in clear_work_data()
Any operation which clears PENDING should be preceded by a wmb to guarantee that the next PENDING owner sees all the changes made before PENDING release. There are only two places where PENDING is cleared - set_work_cpu_and_clear_pending() and clear_work_data(). The caller of the former already does smp_wmb() but the latter doesn't have any. Move the wmb above set_work_cpu_and_clear_pending() into it and add one to clear_work_data(). There hasn't been any report related to this issue, and, given how clear_work_data() is used, it is extremely unlikely to have caused any actual problems on any architecture. Signed-off-by: Tejun Heo <tj@kernel.org> Cc: Oleg Nesterov <oleg@redhat.com>
This commit is contained in:
		
							parent
							
								
									1265057fa0
								
							
						
					
					
						commit
						23657bb192
					
				
					 1 changed files with 12 additions and 7 deletions
				
			
		| 
						 | 
					@ -570,11 +570,19 @@ static void set_work_cwq(struct work_struct *work,
 | 
				
			||||||
static void set_work_cpu_and_clear_pending(struct work_struct *work,
 | 
					static void set_work_cpu_and_clear_pending(struct work_struct *work,
 | 
				
			||||||
					   unsigned int cpu)
 | 
										   unsigned int cpu)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * The following wmb is paired with the implied mb in
 | 
				
			||||||
 | 
						 * test_and_set_bit(PENDING) and ensures all updates to @work made
 | 
				
			||||||
 | 
						 * here are visible to and precede any updates by the next PENDING
 | 
				
			||||||
 | 
						 * owner.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						smp_wmb();
 | 
				
			||||||
	set_work_data(work, (unsigned long)cpu << WORK_OFFQ_CPU_SHIFT, 0);
 | 
						set_work_data(work, (unsigned long)cpu << WORK_OFFQ_CPU_SHIFT, 0);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void clear_work_data(struct work_struct *work)
 | 
					static void clear_work_data(struct work_struct *work)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						smp_wmb();	/* see set_work_cpu_and_clear_pending() */
 | 
				
			||||||
	set_work_data(work, WORK_STRUCT_NO_CPU, 0);
 | 
						set_work_data(work, WORK_STRUCT_NO_CPU, 0);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2182,14 +2190,11 @@ __acquires(&gcwq->lock)
 | 
				
			||||||
		wake_up_worker(pool);
 | 
							wake_up_worker(pool);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Record the last CPU and clear PENDING.  The following wmb is
 | 
						 * Record the last CPU and clear PENDING which should be the last
 | 
				
			||||||
	 * paired with the implied mb in test_and_set_bit(PENDING) and
 | 
						 * update to @work.  Also, do this inside @gcwq->lock so that
 | 
				
			||||||
	 * ensures all updates to @work made here are visible to and
 | 
						 * PENDING and queued state changes happen together while IRQ is
 | 
				
			||||||
	 * precede any updates by the next PENDING owner.  Also, clear
 | 
						 * disabled.
 | 
				
			||||||
	 * PENDING inside @gcwq->lock so that PENDING and queued state
 | 
					 | 
				
			||||||
	 * changes happen together while IRQ is disabled.
 | 
					 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	smp_wmb();
 | 
					 | 
				
			||||||
	set_work_cpu_and_clear_pending(work, gcwq->cpu);
 | 
						set_work_cpu_and_clear_pending(work, gcwq->cpu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spin_unlock_irq(&gcwq->lock);
 | 
						spin_unlock_irq(&gcwq->lock);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue