Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

workqueue: Allow cancel_work_sync() and disable_work() from atomic contexts on BH work items

Now that work_grab_pending() can always grab the PENDING bit without
sleeping, the only thing that prevents allowing cancel_work_sync() of a BH
work item from an atomic context is the flushing of the in-flight instance.

When we're flushing a BH work item for cancel_work_sync(), we know that the
work item is not queued and must be executing in a BH context, which means
that it's safe to busy-wait for its completion from a non-hardirq atomic
context.

This patch updates __flush_work() so that it busy-waits when flushing a BH
work item for cancel_work_sync(). might_sleep() is pushed from
start_flush_work() to its callers - when operating on a BH work item,
__cancel_work_sync() now enforces !in_hardirq() instead of might_sleep().

This allows cancel_work_sync() and disable_work() to be called from
non-hardirq atomic contexts on BH work items.

v3: In __flush_work(), test WORK_OFFQ_BH to tell whether a work item being
canceled can be busy waited instead of making start_flush_work() return
the pool. (Lai)

v2: Lai pointed out that __flush_work() was accessing pool->flags outside
the RCU critical section protecting the pool pointer. Fix it by testing
and remembering the result inside the RCU critical section.

Signed-off-by: Tejun Heo <tj@kernel.org>
Reviewed-by: Lai Jiangshan <jiangshanlai@gmail.com>

+55 -19
+55 -19
kernel/workqueue.c
··· 4105 4105 struct pool_workqueue *pwq; 4106 4106 struct workqueue_struct *wq; 4107 4107 4108 - might_sleep(); 4109 - 4110 4108 rcu_read_lock(); 4111 4109 pool = get_work_pool(work); 4112 4110 if (!pool) { ··· 4156 4158 static bool __flush_work(struct work_struct *work, bool from_cancel) 4157 4159 { 4158 4160 struct wq_barrier barr; 4161 + unsigned long data; 4159 4162 4160 4163 if (WARN_ON(!wq_online)) 4161 4164 return false; ··· 4164 4165 if (WARN_ON(!work->func)) 4165 4166 return false; 4166 4167 4167 - if (start_flush_work(work, &barr, from_cancel)) { 4168 - wait_for_completion(&barr.done); 4169 - destroy_work_on_stack(&barr.work); 4170 - return true; 4171 - } else { 4168 + if (!start_flush_work(work, &barr, from_cancel)) 4172 4169 return false; 4170 + 4171 + /* 4172 + * start_flush_work() returned %true. If @from_cancel is set, we know 4173 + * that @work must have been executing during start_flush_work() and 4174 + * can't currently be queued. Its data must contain OFFQ bits. If @work 4175 + * was queued on a BH workqueue, we also know that it was running in the 4176 + * BH context and thus can be busy-waited. 4177 + */ 4178 + data = *work_data_bits(work); 4179 + if (from_cancel && 4180 + !WARN_ON_ONCE(data & WORK_STRUCT_PWQ) && (data & WORK_OFFQ_BH)) { 4181 + /* 4182 + * On RT, prevent a live lock when %current preempted soft 4183 + * interrupt processing or prevents ksoftirqd from running by 4184 + * keeping flipping BH. If the BH work item runs on a different 4185 + * CPU then this has no effect other than doing the BH 4186 + * disable/enable dance for nothing. This is copied from 4187 + * kernel/softirq.c::tasklet_unlock_spin_wait(). 4188 + */ 4189 + while (!try_wait_for_completion(&barr.done)) { 4190 + if (IS_ENABLED(CONFIG_PREEMPT_RT)) { 4191 + local_bh_disable(); 4192 + local_bh_enable(); 4193 + } else { 4194 + cpu_relax(); 4195 + } 4196 + } 4197 + } else { 4198 + wait_for_completion(&barr.done); 4173 4199 } 4200 + 4201 + destroy_work_on_stack(&barr.work); 4202 + return true; 4174 4203 } 4175 4204 4176 4205 /** ··· 4214 4187 */ 4215 4188 bool flush_work(struct work_struct *work) 4216 4189 { 4190 + might_sleep(); 4217 4191 return __flush_work(work, false); 4218 4192 } 4219 4193 EXPORT_SYMBOL_GPL(flush_work); ··· 4304 4276 4305 4277 ret = __cancel_work(work, cflags | WORK_CANCEL_DISABLE); 4306 4278 4279 + if (*work_data_bits(work) & WORK_OFFQ_BH) 4280 + WARN_ON_ONCE(in_hardirq()); 4281 + else 4282 + might_sleep(); 4283 + 4307 4284 /* 4308 4285 * Skip __flush_work() during early boot when we know that @work isn't 4309 4286 * executing. This allows canceling during early boot. ··· 4335 4302 * cancel_work_sync - cancel a work and wait for it to finish 4336 4303 * @work: the work to cancel 4337 4304 * 4338 - * Cancel @work and wait for its execution to finish. This function 4339 - * can be used even if the work re-queues itself or migrates to 4340 - * another workqueue. On return from this function, @work is 4341 - * guaranteed to be not pending or executing on any CPU. 4305 + * Cancel @work and wait for its execution to finish. This function can be used 4306 + * even if the work re-queues itself or migrates to another workqueue. On return 4307 + * from this function, @work is guaranteed to be not pending or executing on any 4308 + * CPU as long as there aren't racing enqueues. 4342 4309 * 4343 - * cancel_work_sync(&delayed_work->work) must not be used for 4344 - * delayed_work's. Use cancel_delayed_work_sync() instead. 4310 + * cancel_work_sync(&delayed_work->work) must not be used for delayed_work's. 4311 + * Use cancel_delayed_work_sync() instead. 4345 4312 * 4346 - * The caller must ensure that the workqueue on which @work was last 4347 - * queued can't be destroyed before this function returns. 4313 + * Must be called from a sleepable context if @work was last queued on a non-BH 4314 + * workqueue. Can also be called from non-hardirq atomic contexts including BH 4315 + * if @work was last queued on a BH workqueue. 4348 4316 * 4349 - * Return: 4350 - * %true if @work was pending, %false otherwise. 4317 + * Returns %true if @work was pending, %false otherwise. 4351 4318 */ 4352 4319 bool cancel_work_sync(struct work_struct *work) 4353 4320 { ··· 4417 4384 * Similar to disable_work() but also wait for @work to finish if currently 4418 4385 * executing. 4419 4386 * 4420 - * Must be called from a sleepable context. Returns %true if @work was pending, 4421 - * %false otherwise. 4387 + * Must be called from a sleepable context if @work was last queued on a non-BH 4388 + * workqueue. Can also be called from non-hardirq atomic contexts including BH 4389 + * if @work was last queued on a BH workqueue. 4390 + * 4391 + * Returns %true if @work was pending, %false otherwise. 4422 4392 */ 4423 4393 bool disable_work_sync(struct work_struct *work) 4424 4394 {