Commit c7aab1a7 authored by Jens Axboe's avatar Jens Axboe

task_work: add helper for more targeted task_work canceling

The only exported helper we have right now is task_work_cancel(), which
cancels any task_work from a given task where func matches the queued
work item. This is a bit too coarse for some use cases. Add a
task_work_cancel_match() that allows to more specifically target
individual work items outside of purely the callback function used.

task_work_cancel() can be trivially implemented on top of that, hence do
so.
Reviewed-by: default avatarOleg Nesterov <oleg@redhat.com>
Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent b2e720ac
...@@ -22,6 +22,8 @@ enum task_work_notify_mode { ...@@ -22,6 +22,8 @@ enum task_work_notify_mode {
int task_work_add(struct task_struct *task, struct callback_head *twork, int task_work_add(struct task_struct *task, struct callback_head *twork,
enum task_work_notify_mode mode); enum task_work_notify_mode mode);
struct callback_head *task_work_cancel_match(struct task_struct *task,
bool (*match)(struct callback_head *, void *data), void *data);
struct callback_head *task_work_cancel(struct task_struct *, task_work_func_t); struct callback_head *task_work_cancel(struct task_struct *, task_work_func_t);
void task_work_run(void); void task_work_run(void);
......
...@@ -59,18 +59,17 @@ int task_work_add(struct task_struct *task, struct callback_head *work, ...@@ -59,18 +59,17 @@ int task_work_add(struct task_struct *task, struct callback_head *work,
} }
/** /**
* task_work_cancel - cancel a pending work added by task_work_add() * task_work_cancel_match - cancel a pending work added by task_work_add()
* @task: the task which should execute the work * @task: the task which should execute the work
* @func: identifies the work to remove * @match: match function to call
*
* Find the last queued pending work with ->func == @func and remove
* it from queue.
* *
* RETURNS: * RETURNS:
* The found work or NULL if not found. * The found work or NULL if not found.
*/ */
struct callback_head * struct callback_head *
task_work_cancel(struct task_struct *task, task_work_func_t func) task_work_cancel_match(struct task_struct *task,
bool (*match)(struct callback_head *, void *data),
void *data)
{ {
struct callback_head **pprev = &task->task_works; struct callback_head **pprev = &task->task_works;
struct callback_head *work; struct callback_head *work;
...@@ -86,7 +85,7 @@ task_work_cancel(struct task_struct *task, task_work_func_t func) ...@@ -86,7 +85,7 @@ task_work_cancel(struct task_struct *task, task_work_func_t func)
*/ */
raw_spin_lock_irqsave(&task->pi_lock, flags); raw_spin_lock_irqsave(&task->pi_lock, flags);
while ((work = READ_ONCE(*pprev))) { while ((work = READ_ONCE(*pprev))) {
if (work->func != func) if (!match(work, data))
pprev = &work->next; pprev = &work->next;
else if (cmpxchg(pprev, work, work->next) == work) else if (cmpxchg(pprev, work, work->next) == work)
break; break;
...@@ -96,6 +95,28 @@ task_work_cancel(struct task_struct *task, task_work_func_t func) ...@@ -96,6 +95,28 @@ task_work_cancel(struct task_struct *task, task_work_func_t func)
return work; return work;
} }
static bool task_work_func_match(struct callback_head *cb, void *data)
{
return cb->func == data;
}
/**
* task_work_cancel - cancel a pending work added by task_work_add()
* @task: the task which should execute the work
* @func: identifies the work to remove
*
* Find the last queued pending work with ->func == @func and remove
* it from queue.
*
* RETURNS:
* The found work or NULL if not found.
*/
struct callback_head *
task_work_cancel(struct task_struct *task, task_work_func_t func)
{
return task_work_cancel_match(task, task_work_func_match, func);
}
/** /**
* task_work_run - execute the works added by task_work_add() * task_work_run - execute the works added by task_work_add()
* *
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment