mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	workqueue: remove WQ_SINGLE_CPU and use WQ_UNBOUND instead
WQ_SINGLE_CPU combined with @max_active of 1 is used to achieve full ordering among works queued to a workqueue. The same can be achieved using WQ_UNBOUND as unbound workqueues always use the gcwq for WORK_CPU_UNBOUND. As @max_active is always one and benefits from cpu locality isn't accessible anyway, serving them with unbound workqueues should be fine. Drop WQ_SINGLE_CPU support and use WQ_UNBOUND instead. Note that most single thread workqueue users will be converted to use multithread or non-reentrant instead and only the ones which require strict ordering will keep using WQ_UNBOUND + @max_active of 1. Signed-off-by: Tejun Heo <tj@kernel.org>
This commit is contained in:
		
							parent
							
								
									f34217977d
								
							
						
					
					
						commit
						c7fc77f78f
					
				
					 2 changed files with 21 additions and 86 deletions
				
			
		| 
						 | 
					@ -233,12 +233,11 @@ static inline unsigned int work_static(struct work_struct *work) { return 0; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum {
 | 
					enum {
 | 
				
			||||||
	WQ_NON_REENTRANT	= 1 << 0, /* guarantee non-reentrance */
 | 
						WQ_NON_REENTRANT	= 1 << 0, /* guarantee non-reentrance */
 | 
				
			||||||
	WQ_SINGLE_CPU		= 1 << 1, /* only single cpu at a time */
 | 
						WQ_UNBOUND		= 1 << 1, /* not bound to any cpu */
 | 
				
			||||||
	WQ_FREEZEABLE		= 1 << 2, /* freeze during suspend */
 | 
						WQ_FREEZEABLE		= 1 << 2, /* freeze during suspend */
 | 
				
			||||||
	WQ_RESCUER		= 1 << 3, /* has an rescue worker */
 | 
						WQ_RESCUER		= 1 << 3, /* has an rescue worker */
 | 
				
			||||||
	WQ_HIGHPRI		= 1 << 4, /* high priority */
 | 
						WQ_HIGHPRI		= 1 << 4, /* high priority */
 | 
				
			||||||
	WQ_CPU_INTENSIVE	= 1 << 5, /* cpu instensive workqueue */
 | 
						WQ_CPU_INTENSIVE	= 1 << 5, /* cpu instensive workqueue */
 | 
				
			||||||
	WQ_UNBOUND		= 1 << 6, /* not bound to any cpu */
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	WQ_MAX_ACTIVE		= 512,	  /* I like 512, better ideas? */
 | 
						WQ_MAX_ACTIVE		= 512,	  /* I like 512, better ideas? */
 | 
				
			||||||
	WQ_MAX_UNBOUND_PER_CPU	= 4,	  /* 4 * #cpus for unbound wq */
 | 
						WQ_MAX_UNBOUND_PER_CPU	= 4,	  /* 4 * #cpus for unbound wq */
 | 
				
			||||||
| 
						 | 
					@ -300,9 +299,9 @@ __alloc_workqueue_key(const char *name, unsigned int flags, int max_active,
 | 
				
			||||||
#define create_workqueue(name)					\
 | 
					#define create_workqueue(name)					\
 | 
				
			||||||
	alloc_workqueue((name), WQ_RESCUER, 1)
 | 
						alloc_workqueue((name), WQ_RESCUER, 1)
 | 
				
			||||||
#define create_freezeable_workqueue(name)			\
 | 
					#define create_freezeable_workqueue(name)			\
 | 
				
			||||||
	alloc_workqueue((name), WQ_FREEZEABLE | WQ_SINGLE_CPU | WQ_RESCUER, 1)
 | 
						alloc_workqueue((name), WQ_FREEZEABLE | WQ_UNBOUND | WQ_RESCUER, 1)
 | 
				
			||||||
#define create_singlethread_workqueue(name)			\
 | 
					#define create_singlethread_workqueue(name)			\
 | 
				
			||||||
	alloc_workqueue((name), WQ_SINGLE_CPU | WQ_RESCUER, 1)
 | 
						alloc_workqueue((name), WQ_UNBOUND | WQ_RESCUER, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern void destroy_workqueue(struct workqueue_struct *wq);
 | 
					extern void destroy_workqueue(struct workqueue_struct *wq);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -206,8 +206,6 @@ struct workqueue_struct {
 | 
				
			||||||
	struct list_head	flusher_queue;	/* F: flush waiters */
 | 
						struct list_head	flusher_queue;	/* F: flush waiters */
 | 
				
			||||||
	struct list_head	flusher_overflow; /* F: flush overflow list */
 | 
						struct list_head	flusher_overflow; /* F: flush overflow list */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	unsigned long		single_cpu;	/* cpu for single cpu wq */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cpumask_var_t		mayday_mask;	/* cpus requesting rescue */
 | 
						cpumask_var_t		mayday_mask;	/* cpus requesting rescue */
 | 
				
			||||||
	struct worker		*rescuer;	/* I: rescue worker */
 | 
						struct worker		*rescuer;	/* I: rescue worker */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -889,34 +887,6 @@ static void insert_work(struct cpu_workqueue_struct *cwq,
 | 
				
			||||||
		wake_up_worker(gcwq);
 | 
							wake_up_worker(gcwq);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * cwq_unbind_single_cpu - unbind cwq from single cpu workqueue processing
 | 
					 | 
				
			||||||
 * @cwq: cwq to unbind
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Try to unbind @cwq from single cpu workqueue processing.  If
 | 
					 | 
				
			||||||
 * @cwq->wq is frozen, unbind is delayed till the workqueue is thawed.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * CONTEXT:
 | 
					 | 
				
			||||||
 * spin_lock_irq(gcwq->lock).
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static void cwq_unbind_single_cpu(struct cpu_workqueue_struct *cwq)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct workqueue_struct *wq = cwq->wq;
 | 
					 | 
				
			||||||
	struct global_cwq *gcwq = cwq->gcwq;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	BUG_ON(wq->single_cpu != gcwq->cpu);
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
	 * Unbind from workqueue if @cwq is not frozen.  If frozen,
 | 
					 | 
				
			||||||
	 * thaw_workqueues() will either restart processing on this
 | 
					 | 
				
			||||||
	 * cpu or unbind if empty.  This keeps works queued while
 | 
					 | 
				
			||||||
	 * frozen fully ordered and flushable.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	if (likely(!(gcwq->flags & GCWQ_FREEZING))) {
 | 
					 | 
				
			||||||
		smp_wmb();	/* paired with cmpxchg() in __queue_work() */
 | 
					 | 
				
			||||||
		wq->single_cpu = WORK_CPU_NONE;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void __queue_work(unsigned int cpu, struct workqueue_struct *wq,
 | 
					static void __queue_work(unsigned int cpu, struct workqueue_struct *wq,
 | 
				
			||||||
			 struct work_struct *work)
 | 
								 struct work_struct *work)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -924,20 +894,16 @@ static void __queue_work(unsigned int cpu, struct workqueue_struct *wq,
 | 
				
			||||||
	struct cpu_workqueue_struct *cwq;
 | 
						struct cpu_workqueue_struct *cwq;
 | 
				
			||||||
	struct list_head *worklist;
 | 
						struct list_head *worklist;
 | 
				
			||||||
	unsigned long flags;
 | 
						unsigned long flags;
 | 
				
			||||||
	bool arbitrate;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	debug_work_activate(work);
 | 
						debug_work_activate(work);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* determine gcwq to use */
 | 
				
			||||||
 | 
						if (!(wq->flags & WQ_UNBOUND)) {
 | 
				
			||||||
 | 
							struct global_cwq *last_gcwq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (unlikely(cpu == WORK_CPU_UNBOUND))
 | 
							if (unlikely(cpu == WORK_CPU_UNBOUND))
 | 
				
			||||||
			cpu = raw_smp_processor_id();
 | 
								cpu = raw_smp_processor_id();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
	 * Determine gcwq to use.  SINGLE_CPU is inherently
 | 
					 | 
				
			||||||
	 * NON_REENTRANT, so test it first.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	if (!(wq->flags & (WQ_SINGLE_CPU | WQ_UNBOUND))) {
 | 
					 | 
				
			||||||
		struct global_cwq *last_gcwq;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * It's multi cpu.  If @wq is non-reentrant and @work
 | 
							 * It's multi cpu.  If @wq is non-reentrant and @work
 | 
				
			||||||
		 * was previously on a different cpu, it might still
 | 
							 * was previously on a different cpu, it might still
 | 
				
			||||||
| 
						 | 
					@ -962,38 +928,6 @@ static void __queue_work(unsigned int cpu, struct workqueue_struct *wq,
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else
 | 
							} else
 | 
				
			||||||
			spin_lock_irqsave(&gcwq->lock, flags);
 | 
								spin_lock_irqsave(&gcwq->lock, flags);
 | 
				
			||||||
	} else if (!(wq->flags & WQ_UNBOUND)) {
 | 
					 | 
				
			||||||
		unsigned int req_cpu = cpu;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/*
 | 
					 | 
				
			||||||
		 * It's a bit more complex for single cpu workqueues.
 | 
					 | 
				
			||||||
		 * We first need to determine which cpu is going to be
 | 
					 | 
				
			||||||
		 * used.  If no cpu is currently serving this
 | 
					 | 
				
			||||||
		 * workqueue, arbitrate using atomic accesses to
 | 
					 | 
				
			||||||
		 * wq->single_cpu; otherwise, use the current one.
 | 
					 | 
				
			||||||
		 */
 | 
					 | 
				
			||||||
	retry:
 | 
					 | 
				
			||||||
		cpu = wq->single_cpu;
 | 
					 | 
				
			||||||
		arbitrate = cpu == WORK_CPU_NONE;
 | 
					 | 
				
			||||||
		if (arbitrate)
 | 
					 | 
				
			||||||
			cpu = req_cpu;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		gcwq = get_gcwq(cpu);
 | 
					 | 
				
			||||||
		spin_lock_irqsave(&gcwq->lock, flags);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/*
 | 
					 | 
				
			||||||
		 * The following cmpxchg() is a full barrier paired
 | 
					 | 
				
			||||||
		 * with smp_wmb() in cwq_unbind_single_cpu() and
 | 
					 | 
				
			||||||
		 * guarantees that all changes to wq->st_* fields are
 | 
					 | 
				
			||||||
		 * visible on the new cpu after this point.
 | 
					 | 
				
			||||||
		 */
 | 
					 | 
				
			||||||
		if (arbitrate)
 | 
					 | 
				
			||||||
			cmpxchg(&wq->single_cpu, WORK_CPU_NONE, cpu);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (unlikely(wq->single_cpu != cpu)) {
 | 
					 | 
				
			||||||
			spin_unlock_irqrestore(&gcwq->lock, flags);
 | 
					 | 
				
			||||||
			goto retry;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		gcwq = get_gcwq(WORK_CPU_UNBOUND);
 | 
							gcwq = get_gcwq(WORK_CPU_UNBOUND);
 | 
				
			||||||
		spin_lock_irqsave(&gcwq->lock, flags);
 | 
							spin_lock_irqsave(&gcwq->lock, flags);
 | 
				
			||||||
| 
						 | 
					@ -1105,19 +1039,30 @@ int queue_delayed_work_on(int cpu, struct workqueue_struct *wq,
 | 
				
			||||||
	struct work_struct *work = &dwork->work;
 | 
						struct work_struct *work = &dwork->work;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) {
 | 
						if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) {
 | 
				
			||||||
		struct global_cwq *gcwq = get_work_gcwq(work);
 | 
							unsigned int lcpu;
 | 
				
			||||||
		unsigned int lcpu = gcwq ? gcwq->cpu : raw_smp_processor_id();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		BUG_ON(timer_pending(timer));
 | 
							BUG_ON(timer_pending(timer));
 | 
				
			||||||
		BUG_ON(!list_empty(&work->entry));
 | 
							BUG_ON(!list_empty(&work->entry));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		timer_stats_timer_set_start_info(&dwork->timer);
 | 
							timer_stats_timer_set_start_info(&dwork->timer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * This stores cwq for the moment, for the timer_fn.
 | 
							 * This stores cwq for the moment, for the timer_fn.
 | 
				
			||||||
		 * Note that the work's gcwq is preserved to allow
 | 
							 * Note that the work's gcwq is preserved to allow
 | 
				
			||||||
		 * reentrance detection for delayed works.
 | 
							 * reentrance detection for delayed works.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
 | 
							if (!(wq->flags & WQ_UNBOUND)) {
 | 
				
			||||||
 | 
								struct global_cwq *gcwq = get_work_gcwq(work);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (gcwq && gcwq->cpu != WORK_CPU_UNBOUND)
 | 
				
			||||||
 | 
									lcpu = gcwq->cpu;
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									lcpu = raw_smp_processor_id();
 | 
				
			||||||
 | 
							} else
 | 
				
			||||||
 | 
								lcpu = WORK_CPU_UNBOUND;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		set_work_cwq(work, get_cwq(lcpu, wq), 0);
 | 
							set_work_cwq(work, get_cwq(lcpu, wq), 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		timer->expires = jiffies + delay;
 | 
							timer->expires = jiffies + delay;
 | 
				
			||||||
		timer->data = (unsigned long)dwork;
 | 
							timer->data = (unsigned long)dwork;
 | 
				
			||||||
		timer->function = delayed_work_timer_fn;
 | 
							timer->function = delayed_work_timer_fn;
 | 
				
			||||||
| 
						 | 
					@ -1696,9 +1641,6 @@ static void cwq_dec_nr_in_flight(struct cpu_workqueue_struct *cwq, int color)
 | 
				
			||||||
		/* one down, submit a delayed one */
 | 
							/* one down, submit a delayed one */
 | 
				
			||||||
		if (cwq->nr_active < cwq->max_active)
 | 
							if (cwq->nr_active < cwq->max_active)
 | 
				
			||||||
			cwq_activate_first_delayed(cwq);
 | 
								cwq_activate_first_delayed(cwq);
 | 
				
			||||||
	} else if (!cwq->nr_active && cwq->wq->flags & WQ_SINGLE_CPU) {
 | 
					 | 
				
			||||||
		/* this was the last work, unbind from single cpu */
 | 
					 | 
				
			||||||
		cwq_unbind_single_cpu(cwq);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* is flush in progress and are we at the flushing tip? */
 | 
						/* is flush in progress and are we at the flushing tip? */
 | 
				
			||||||
| 
						 | 
					@ -2751,7 +2693,6 @@ struct workqueue_struct *__alloc_workqueue_key(const char *name,
 | 
				
			||||||
	atomic_set(&wq->nr_cwqs_to_flush, 0);
 | 
						atomic_set(&wq->nr_cwqs_to_flush, 0);
 | 
				
			||||||
	INIT_LIST_HEAD(&wq->flusher_queue);
 | 
						INIT_LIST_HEAD(&wq->flusher_queue);
 | 
				
			||||||
	INIT_LIST_HEAD(&wq->flusher_overflow);
 | 
						INIT_LIST_HEAD(&wq->flusher_overflow);
 | 
				
			||||||
	wq->single_cpu = WORK_CPU_NONE;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wq->name = name;
 | 
						wq->name = name;
 | 
				
			||||||
	lockdep_init_map(&wq->lockdep_map, lock_name, key, 0);
 | 
						lockdep_init_map(&wq->lockdep_map, lock_name, key, 0);
 | 
				
			||||||
| 
						 | 
					@ -3513,11 +3454,6 @@ void thaw_workqueues(void)
 | 
				
			||||||
			while (!list_empty(&cwq->delayed_works) &&
 | 
								while (!list_empty(&cwq->delayed_works) &&
 | 
				
			||||||
			       cwq->nr_active < cwq->max_active)
 | 
								       cwq->nr_active < cwq->max_active)
 | 
				
			||||||
				cwq_activate_first_delayed(cwq);
 | 
									cwq_activate_first_delayed(cwq);
 | 
				
			||||||
 | 
					 | 
				
			||||||
			/* perform delayed unbind from single cpu if empty */
 | 
					 | 
				
			||||||
			if (wq->single_cpu == gcwq->cpu &&
 | 
					 | 
				
			||||||
			    !cwq->nr_active && list_empty(&cwq->delayed_works))
 | 
					 | 
				
			||||||
				cwq_unbind_single_cpu(cwq);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		wake_up_worker(gcwq);
 | 
							wake_up_worker(gcwq);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue