From: Tejun Heo <tj@kernel.org>
To: Oleg Nesterov <oleg@redhat.com>,
Linus Torvalds <torvalds@linux-foundation.org>,
Andrew Morton <akpm@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>,
Thomas Gleixner <tglx@linutronix.de>, Chris Mason <clm@fb.com>,
linux-kernel@vger.kernel.org, kernel-team@fb.com,
Li Zefan <lizefan@huawei.com>,
Johannes Weiner <hannes@cmpxchg.org>,
cgroups@vger.kernel.org
Subject: [PATCH 2/2] kthread, cgroup: close race window where new kthreads can be migrated to non-root cgroups
Date: Wed, 15 Mar 2017 19:19:20 -0400 [thread overview]
Message-ID: <20170315231920.GB13656@htj.duckdns.org> (raw)
In-Reply-To: <20170315231827.GA13656@htj.duckdns.org>
Creation of a kthread goes through a couple interlocked stages between
the kthread itself and its creator. Once the new kthread starts
running, it initializes itself and wakes up the creator. The creator
then can further configure the kthread and then let it start doing its
job by waking it up.
In this configuration-by-creator stage, the creator is the only one
that can wake it up but the kthread is visible to userland. When
altering the kthread's attributes from userland is allowed, this is
fine; however, for cases where CPU affinity is critical,
kthread_bind() is used to first disable affinity changes from userland
and then set the affinity. This also prevents the kthread from being
migrated into non-root cgroups as that can affect the CPU affinity and
many other things.
Unfortunately, the cgroup side of protection is racy. While the
PF_NO_SETAFFINITY flag prevents further migrations, userland can win
the race before the creator sets the flag with kthread_bind() and put
the kthread in a non-root cgroup, which can lead to all sorts of
problems including incorrect CPU affinity and starvation.
This bug got triggered by userland which periodically tries to migrate
all processes in the root cpuset cgroup to a non-root one. Per-cpu
workqueue workers got caught while being created and ended up with
incorrected CPU affinity breaking concurrency management and sometimes
stalling workqueue execution.
This patch introduces KTHREAD_INITIALIZED which is set after the
kthread finishes initialization. cgroup core closes the race window
by testing kthread_initialized() and rejecting migration accordingly.
It'd be better to wait for the initialization instead of failing but I
couldn't think of a way of implementing that without adding either a
new PF flag, or sleeping and retrying from waiting side. Even if
userland depends on changing cgroup membership of a kthread, it either
has to be synchronized with kthread_create() or periodically repeat,
so it's unlikely that this would break anything.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Reported-and-debugged-by: Chris Mason <clm@fb.com>
Cc: stable@vger.kernel.org # v4.3+ (we can't close the race < v4.3)
---
include/linux/kthread.h | 1 +
kernel/cgroup/cgroup.c | 10 ++++++----
kernel/kthread.c | 21 ++++++++++++++++++++-
3 files changed, 27 insertions(+), 5 deletions(-)
--- a/include/linux/kthread.h
+++ b/include/linux/kthread.h
@@ -49,6 +49,7 @@ struct task_struct *kthread_create_on_cp
})
void free_kthread_struct(struct task_struct *k);
+bool kthread_initialized(struct task_struct *k);
void kthread_bind(struct task_struct *k, unsigned int cpu);
void kthread_bind_mask(struct task_struct *k, const struct cpumask *mask);
int kthread_stop(struct task_struct *k);
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -2425,11 +2425,13 @@ ssize_t __cgroup_procs_write(struct kern
tsk = tsk->group_leader;
/*
- * Workqueue threads may acquire PF_NO_SETAFFINITY and become
- * trapped in a cpuset, or RT worker may be born in a cgroup
- * with no rt_runtime allocated. Just say no.
+ * kthreads may acquire PF_NO_SETAFFINITY during initialization.
+ * If userland migrates such kthread to a non-root cgroup, it can
+ * become trapped in a cpuset, or RT kthread may be born in a
+ * cgroup with no rt_runtime allocated. Just say no.
*/
- if (tsk == kthreadd_task || (tsk->flags & PF_NO_SETAFFINITY)) {
+ if (tsk == kthreadd_task || (tsk->flags & PF_NO_SETAFFINITY) ||
+ ((tsk->flags & PF_KTHREAD) && !kthread_initialized(tsk))) {
ret = -EINVAL;
goto out_unlock_rcu;
}
--- a/kernel/kthread.c
+++ b/kernel/kthread.c
@@ -50,6 +50,7 @@ struct kthread {
enum KTHREAD_BITS {
KTHREAD_IS_PER_CPU = 0,
+ KTHREAD_INITIALIZED,
KTHREAD_SHOULD_STOP,
KTHREAD_SHOULD_PARK,
KTHREAD_IS_PARKED,
@@ -57,7 +58,7 @@ enum KTHREAD_BITS {
static inline void set_kthread_struct(void *kthread)
{
- /* paired with smp_read_data_barrier_depends() in to_kthread() */
+ /* paired with smp_read_barrier_depends() in to_kthread() */
smp_wmb();
/*
@@ -95,6 +96,23 @@ void free_kthread_struct(struct task_str
}
/**
+ * kthread_initialized - has the kthread finished initialization?
+ * @k: thread created by kthread_create().
+ *
+ * Test whether @k, which must be a kthread, finished initialization and is
+ * ready to execute the threadfn. The kthread owner finishes
+ * initialization by waking up the new kthread for the first time. If this
+ * function returns %false, the kthread owner could still be configuring
+ * the kthread.
+ */
+bool kthread_initialized(struct task_struct *k)
+{
+ struct kthread *kthread = to_kthread(k);
+
+ return kthread && test_bit(KTHREAD_INITIALIZED, &kthread->flags);
+}
+
+/**
* kthread_should_stop - should this kthread return now?
*
* When someone calls kthread_stop() on your kthread, it will be woken
@@ -238,6 +256,7 @@ static int kthread(void *_create)
create->result = current;
complete(done);
schedule();
+ set_bit(KTHREAD_INITIALIZED, &self->flags);
ret = -EINTR;
if (!test_bit(KTHREAD_SHOULD_STOP, &self->flags)) {
next prev parent reply other threads:[~2017-03-15 23:19 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-03-15 23:18 [PATCH 1/2] kthread: add barriers to set_kthread_struct() and to_kthread() Tejun Heo
2017-03-15 23:19 ` Tejun Heo [this message]
2017-03-16 15:02 ` [PATCH 2/2] kthread, cgroup: close race window where new kthreads can be migrated to non-root cgroups Oleg Nesterov
2017-03-16 15:39 ` Oleg Nesterov
2017-03-16 16:07 ` Tejun Heo
2017-03-16 16:31 ` Oleg Nesterov
2017-03-16 17:41 ` Tejun Heo
2017-03-16 16:05 ` Tejun Heo
2017-03-16 16:17 ` Oleg Nesterov
2017-03-16 17:03 ` Tejun Heo
2017-03-16 20:54 ` [PATCH v2] cgroup, kthread: " Tejun Heo
2017-03-17 13:50 ` Oleg Nesterov
2017-03-17 14:44 ` Tejun Heo
2017-03-16 14:54 ` [PATCH 1/2] kthread: add barriers to set_kthread_struct() and to_kthread() Oleg Nesterov
2017-03-16 15:33 ` Tejun Heo
2017-03-16 15:38 ` Tejun Heo
2017-03-16 15:46 ` Oleg Nesterov
2017-03-16 15:55 ` Peter Zijlstra
2017-03-16 16:09 ` Tejun Heo
2017-03-16 16:14 ` 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=20170315231920.GB13656@htj.duckdns.org \
--to=tj@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=cgroups@vger.kernel.org \
--cc=clm@fb.com \
--cc=hannes@cmpxchg.org \
--cc=kernel-team@fb.com \
--cc=linux-kernel@vger.kernel.org \
--cc=lizefan@huawei.com \
--cc=oleg@redhat.com \
--cc=peterz@infradead.org \
--cc=tglx@linutronix.de \
--cc=torvalds@linux-foundation.org \
/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
all inboxes | Powered by JetHome®