[RFC PATCH 1/6] task_work: Provide means to check if a work is queued

Frederic Weisbecker posted 6 patches 1 year, 5 months ago
There is a newer version of this series
[RFC PATCH 1/6] task_work: Provide means to check if a work is queued
Posted by Frederic Weisbecker 1 year, 5 months ago
Some task work users implement their own ways to know if a callback is
already queued on the current task while fiddling with the callback
head internals.

Provide instead a consolidated API to serve this very purpose.

Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
---
 include/linux/task_work.h | 12 ++++++++++++
 kernel/task_work.c        |  1 +
 2 files changed, 13 insertions(+)

diff --git a/include/linux/task_work.h b/include/linux/task_work.h
index 795ef5a68429..f2eae971b73a 100644
--- a/include/linux/task_work.h
+++ b/include/linux/task_work.h
@@ -5,12 +5,15 @@
 #include <linux/list.h>
 #include <linux/sched.h>
 
+#define TASK_WORK_DEQUEUED	((void *) -1UL)
+
 typedef void (*task_work_func_t)(struct callback_head *);
 
 static inline void
 init_task_work(struct callback_head *twork, task_work_func_t func)
 {
 	twork->func = func;
+	twork->next = TASK_WORK_DEQUEUED;
 }
 
 enum task_work_notify_mode {
@@ -25,6 +28,15 @@ static inline bool task_work_pending(struct task_struct *task)
 	return READ_ONCE(task->task_works);
 }
 
+/*
+ * Check if a work is queued. Beware: this is inherently racy if the work can
+ * be queued elsewhere than the current task.
+ */
+static inline bool task_work_queued(struct callback_head *twork)
+{
+	return twork->next != TASK_WORK_DEQUEUED;
+}
+
 int task_work_add(struct task_struct *task, struct callback_head *twork,
 			enum task_work_notify_mode mode);
 
diff --git a/kernel/task_work.c b/kernel/task_work.c
index 95a7e1b7f1da..6e3bee0b7011 100644
--- a/kernel/task_work.c
+++ b/kernel/task_work.c
@@ -177,6 +177,7 @@ void task_work_run(void)
 
 		do {
 			next = work->next;
+			work->next = TASK_WORK_DEQUEUED;
 			work->func(work);
 			work = next;
 			cond_resched();
-- 
2.45.2
Re: [RFC PATCH 1/6] task_work: Provide means to check if a work is queued
Posted by Valentin Schneider 1 year, 5 months ago
On 25/06/24 15:52, Frederic Weisbecker wrote:
> Some task work users implement their own ways to know if a callback is
> already queued on the current task while fiddling with the callback
> head internals.
>
> Provide instead a consolidated API to serve this very purpose.
>

I'm potentially going to add yet another one of these to sched/fair.c, so
yes please!
Re: [RFC PATCH 1/6] task_work: Provide means to check if a work is queued
Posted by Oleg Nesterov 1 year, 5 months ago
On 06/25, Frederic Weisbecker wrote:
>
> --- a/kernel/task_work.c
> +++ b/kernel/task_work.c
> @@ -177,6 +177,7 @@ void task_work_run(void)
>
>  		do {
>  			next = work->next;
> +			work->next = TASK_WORK_DEQUEUED;

OK, but then the additional change below makes sense too?

Oleg.
---

--- x/kernel/task_work.c
+++ x/kernel/task_work.c
@@ -106,8 +106,10 @@ task_work_cancel_match(struct task_struc
 		if (!match(work, data)) {
 			pprev = &work->next;
 			work = READ_ONCE(*pprev);
-		} else if (try_cmpxchg(pprev, &work, work->next))
+		} else if (try_cmpxchg(pprev, &work, work->next)) {
+			work->next = TASK_WORK_DEQUEUED;
 			break;
+		}
 	}
 	raw_spin_unlock_irqrestore(&task->pi_lock, flags);
Re: [RFC PATCH 1/6] task_work: Provide means to check if a work is queued
Posted by Frederic Weisbecker 1 year, 5 months ago
Le Tue, Jun 25, 2024 at 04:15:39PM +0200, Oleg Nesterov a écrit :
> On 06/25, Frederic Weisbecker wrote:
> >
> > --- a/kernel/task_work.c
> > +++ b/kernel/task_work.c
> > @@ -177,6 +177,7 @@ void task_work_run(void)
> >
> >  		do {
> >  			next = work->next;
> > +			work->next = TASK_WORK_DEQUEUED;
> 
> OK, but then the additional change below makes sense too?
> 
> Oleg.
> ---
> 
> --- x/kernel/task_work.c
> +++ x/kernel/task_work.c
> @@ -106,8 +106,10 @@ task_work_cancel_match(struct task_struc
>  		if (!match(work, data)) {
>  			pprev = &work->next;
>  			work = READ_ONCE(*pprev);
> -		} else if (try_cmpxchg(pprev, &work, work->next))
> +		} else if (try_cmpxchg(pprev, &work, work->next)) {
> +			work->next = TASK_WORK_DEQUEUED;
>  			break;
> +		}
>  	}
>  	raw_spin_unlock_irqrestore(&task->pi_lock, flags);
>  
> 

Yes it does!

Thanks.
Re: [RFC PATCH 1/6] task_work: Provide means to check if a work is queued
Posted by Oleg Nesterov 1 year, 5 months ago
And probably task_work_add() should do the same when it returns -ESRCH.

On 06/25, Oleg Nesterov wrote:
>
> On 06/25, Frederic Weisbecker wrote:
> >
> > --- a/kernel/task_work.c
> > +++ b/kernel/task_work.c
> > @@ -177,6 +177,7 @@ void task_work_run(void)
> >
> >  		do {
> >  			next = work->next;
> > +			work->next = TASK_WORK_DEQUEUED;
>
> OK, but then the additional change below makes sense too?
>
> Oleg.
> ---
>
> --- x/kernel/task_work.c
> +++ x/kernel/task_work.c
> @@ -106,8 +106,10 @@ task_work_cancel_match(struct task_struc
>  		if (!match(work, data)) {
>  			pprev = &work->next;
>  			work = READ_ONCE(*pprev);
> -		} else if (try_cmpxchg(pprev, &work, work->next))
> +		} else if (try_cmpxchg(pprev, &work, work->next)) {
> +			work->next = TASK_WORK_DEQUEUED;
>  			break;
> +		}
>  	}
>  	raw_spin_unlock_irqrestore(&task->pi_lock, flags);
>
Re: [RFC PATCH 1/6] task_work: Provide means to check if a work is queued
Posted by Frederic Weisbecker 1 year, 5 months ago
Le Tue, Jun 25, 2024 at 05:16:25PM +0200, Oleg Nesterov a écrit :
> And probably task_work_add() should do the same when it returns -ESRCH.

Good point!

Thanks.