task_work: add helper for more targeted task_work canceling
authorJens Axboe <axboe@kernel.dk>
Fri, 2 Apr 2021 01:53:29 +0000 (19:53 -0600)
committerJens Axboe <axboe@kernel.dk>
Mon, 12 Apr 2021 01:30:25 +0000 (19:30 -0600)
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: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
include/linux/task_work.h
kernel/task_work.c

index 0d848a1e9e62f18840ebd33a4e355b50d1526629..5b8a93f288bb4d924dcead4cb79b3754e0fecad4 100644 (file)
@@ -22,6 +22,8 @@ enum task_work_notify_mode {
 int task_work_add(struct task_struct *task, struct callback_head *twork,
                        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);
 void task_work_run(void);
 
index 9cde961875c0accb59680b72e09be7e5ea3bf8ff..e9316198c64bf5acf8fd1d32b88bf4cc3276492f 100644 (file)
@@ -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
- * @func: identifies the work to remove
- *
- * Find the last queued pending work with ->func == @func and remove
- * it from queue.
+ * @match: match function to call
  *
  * RETURNS:
  * The found work or NULL if not found.
  */
 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 *work;
@@ -86,7 +85,7 @@ task_work_cancel(struct task_struct *task, task_work_func_t func)
         */
        raw_spin_lock_irqsave(&task->pi_lock, flags);
        while ((work = READ_ONCE(*pprev))) {
-               if (work->func != func)
+               if (!match(work, data))
                        pprev = &work->next;
                else if (cmpxchg(pprev, work, work->next) == work)
                        break;
@@ -96,6 +95,28 @@ task_work_cancel(struct task_struct *task, task_work_func_t func)
        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()
  *