From: Peter Zijlstra <[email protected]>
To: Oleg Nesterov <[email protected]>
Cc: "Jens Axboe" <[email protected]>,
"Carter Li 李通洲" <[email protected]>,
"Pavel Begunkov" <[email protected]>,
io-uring <[email protected]>
Subject: Re: [PATCH] task_work_run: don't take ->pi_lock unconditionally
Date: Thu, 20 Feb 2020 18:49:32 +0100 [thread overview]
Message-ID: <[email protected]> (raw)
In-Reply-To: <[email protected]>
On Thu, Feb 20, 2020 at 06:22:02PM +0100, Oleg Nesterov wrote:
> On 02/20, Peter Zijlstra wrote:
> > + work = READ_ONCE(task->task_works);
> > + if (!work) {
> > + if (!(task->flags & PF_EXITING))
> > + return;
> > +
> > + /*
> > + * work->func() can do task_work_add(), do not set
> > + * work_exited unless the list is empty.
> > + */
> > + if (try_cmpxchg(&task->task_works, &work, &work_exited))
> > + return;
> > + }
> > +
> > + work = xchg(&task->task_works, NULL);
> >
> > - if (!work)
> > - break;
>
> You can't remove the "if (!work)" check, cancel_task_work() can remove
> a single callback between READ_ONCE() and xchg().
Argh!
> Oleg.
>
> --- a/kernel/task_work.c
> +++ b/kernel/task_work.c
> @@ -27,14 +27,11 @@ static struct callback_head work_exited; /* all we need is ->next == NULL */
> int
> task_work_add(struct task_struct *task, struct callback_head *work, bool notify)
> {
> - struct callback_head *head;
> -
> + work->next = READ_ONCE(task->task_works);
> do {
> - head = READ_ONCE(task->task_works);
> - if (unlikely(head == &work_exited))
> + if (unlikely(work->next == &work_exited))
> return -ESRCH;
> - work->next = head;
> - } while (cmpxchg(&task->task_works, head, work) != head);
> + } while (!try_cmpxchg(&task->task_works, &work->next, work));
>
> if (notify)
> set_notify_resume(task);
Cute, that should work.
> @@ -68,10 +65,10 @@ task_work_cancel(struct task_struct *task, task_work_func_t func)
> * we raced with task_work_run(), *pprev == NULL/exited.
> */
> raw_spin_lock_irqsave(&task->pi_lock, flags);
> + for (work = READ_ONCE(*pprev); work; ) {
> if (work->func != func)
> pprev = &work->next;
But didn't you loose the READ_ONCE() of *pprev in this branch?
> + else if (try_cmpxchg(pprev, &work, work->next))
> break;
> }
> raw_spin_unlock_irqrestore(&task->pi_lock, flags);
> @@ -97,16 +94,16 @@ void task_work_run(void)
> * work->func() can do task_work_add(), do not set
> * work_exited unless the list is empty.
> */
> + work = READ_ONCE(task->task_works);
> do {
> head = NULL;
> if (!work) {
> if (task->flags & PF_EXITING)
> head = &work_exited;
> else
> break;
> }
> + } while (!try_cmpxchg(&task->task_works, &work, head));
>
> if (!work)
> break;
But given that, as you say, cancel() could have gone and stole our head,
should we not try and install &work_exiting when PF_EXITING in that
case?
That is; should we not do continue in that case, instead of break.
---
--- a/kernel/task_work.c
+++ b/kernel/task_work.c
@@ -27,14 +27,13 @@ static struct callback_head work_exited;
int
task_work_add(struct task_struct *task, struct callback_head *work, bool notify)
{
- struct callback_head *head;
+ struct callback_head *head = READ_ONCE(tsk->task_works);
do {
- head = READ_ONCE(task->task_works);
if (unlikely(head == &work_exited))
return -ESRCH;
work->next = head;
- } while (cmpxchg(&task->task_works, head, work) != head);
+ } while (!try_cmpxchg(&task->task_works, &head, work))
if (notify)
set_notify_resume(task);
@@ -69,9 +68,12 @@ task_work_cancel(struct task_struct *tas
*/
raw_spin_lock_irqsave(&task->pi_lock, flags);
while ((work = READ_ONCE(*pprev))) {
- if (work->func != func)
+ if (work->func != func) {
pprev = &work->next;
- else if (cmpxchg(pprev, work, work->next) == work)
+ continue;
+ }
+
+ if (try_cmpxchg(pprev, &work, work->next))
break;
}
raw_spin_unlock_irqrestore(&task->pi_lock, flags);
@@ -90,26 +92,26 @@ task_work_cancel(struct task_struct *tas
void task_work_run(void)
{
struct task_struct *task = current;
- struct callback_head *work, *head, *next;
+ struct callback_head *work, *next;
for (;;) {
- /*
- * work->func() can do task_work_add(), do not set
- * work_exited unless the list is empty.
- */
- do {
- head = NULL;
- work = READ_ONCE(task->task_works);
- if (!work) {
- if (task->flags & PF_EXITING)
- head = &work_exited;
- else
- break;
- }
- } while (cmpxchg(&task->task_works, work, head) != work);
+ work = READ_ONCE(task->task_works);
+ if (!work) {
+ if (!(task->flags & PF_EXITING))
+ return;
+
+ /*
+ * work->func() can do task_work_add(), do not set
+ * work_exited unless the list is empty.
+ */
+ if (try_cmpxchg(&task->task_works, &work, &work_exited))
+ return;
+ }
+
+ work = xchg(&task->task_works, NULL);
+ if (!work)
+ continue;
- if (!work)
- break;
/*
* Synchronize with task_work_cancel(). It can not remove
* the first entry == work, cmpxchg(task_works) must fail.
next prev parent reply other threads:[~2020-02-20 17:49 UTC|newest]
Thread overview: 59+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-02-12 16:31 [ISSUE] The time cost of IOSQE_IO_LINK Carter Li 李通洲
2020-02-12 17:11 ` Jens Axboe
2020-02-12 17:22 ` Jens Axboe
2020-02-12 17:29 ` Jens Axboe
2020-02-13 0:33 ` Carter Li 李通洲
2020-02-13 15:08 ` Pavel Begunkov
2020-02-13 15:14 ` Jens Axboe
2020-02-13 15:51 ` Carter Li 李通洲
2020-02-14 1:25 ` Carter Li 李通洲
2020-02-14 2:45 ` Jens Axboe
2020-02-14 5:03 ` Jens Axboe
2020-02-14 15:32 ` Peter Zijlstra
2020-02-14 15:47 ` Jens Axboe
2020-02-14 16:18 ` Jens Axboe
2020-02-14 17:52 ` Jens Axboe
2020-02-14 20:44 ` Jens Axboe
2020-02-15 0:16 ` Carter Li 李通洲
2020-02-15 1:10 ` Jens Axboe
2020-02-15 1:25 ` Carter Li 李通洲
2020-02-15 1:27 ` Jens Axboe
2020-02-15 6:01 ` Jens Axboe
2020-02-15 6:32 ` Carter Li 李通洲
2020-02-15 15:11 ` Jens Axboe
2020-02-16 19:06 ` Pavel Begunkov
2020-02-16 22:23 ` Jens Axboe
2020-02-17 10:30 ` Pavel Begunkov
2020-02-17 19:30 ` Jens Axboe
2020-02-16 23:06 ` Jens Axboe
2020-02-16 23:07 ` Jens Axboe
2020-02-17 12:09 ` Peter Zijlstra
2020-02-17 16:12 ` Jens Axboe
2020-02-17 17:16 ` Jens Axboe
2020-02-17 17:46 ` Peter Zijlstra
2020-02-17 18:16 ` Jens Axboe
2020-02-18 13:13 ` Peter Zijlstra
2020-02-18 14:27 ` [PATCH] asm-generic/atomic: Add try_cmpxchg() fallbacks Peter Zijlstra
2020-02-18 14:40 ` Peter Zijlstra
2020-02-20 10:30 ` Will Deacon
2020-02-20 10:37 ` Peter Zijlstra
2020-02-20 10:39 ` Will Deacon
2020-02-18 14:56 ` [ISSUE] The time cost of IOSQE_IO_LINK Oleg Nesterov
2020-02-18 15:07 ` Oleg Nesterov
2020-02-18 15:38 ` Peter Zijlstra
2020-02-18 16:33 ` Jens Axboe
2020-02-18 15:07 ` Peter Zijlstra
2020-02-18 15:50 ` [PATCH] task_work_run: don't take ->pi_lock unconditionally Oleg Nesterov
2020-02-20 16:39 ` Peter Zijlstra
2020-02-20 17:22 ` Oleg Nesterov
2020-02-20 17:49 ` Peter Zijlstra [this message]
2020-02-21 14:52 ` Oleg Nesterov
2020-02-24 18:47 ` Jens Axboe
2020-02-28 19:17 ` Jens Axboe
2020-02-28 19:25 ` Peter Zijlstra
2020-02-28 19:28 ` Jens Axboe
2020-02-28 20:06 ` Peter Zijlstra
2020-02-28 20:15 ` Jens Axboe
2020-02-18 16:46 ` [ISSUE] The time cost of IOSQE_IO_LINK Jens Axboe
2020-02-18 16:52 ` Jens Axboe
2020-02-18 13:13 ` Peter Zijlstra
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20200220174932.GB18400@hirez.programming.kicks-ass.net \
[email protected] \
[email protected] \
[email protected] \
[email protected] \
[email protected] \
[email protected] \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox