]> git.baikalelectronics.ru Git - kernel.git/commitdiff
kthread: Ensure struct kthread is present for all kthreads
authorEric W. Biederman <ebiederm@xmission.com>
Thu, 2 Dec 2021 15:56:14 +0000 (09:56 -0600)
committerEric W. Biederman <ebiederm@xmission.com>
Mon, 13 Dec 2021 18:04:45 +0000 (12:04 -0600)
Today the rules are a bit iffy and arbitrary about which kernel
threads have struct kthread present.  Both idle threads and thread
started with create_kthread want struct kthread present so that is
effectively all kernel threads.  Make the rule that if PF_KTHREAD
and the task is running then struct kthread is present.

This will allow the kernel thread code to using tsk->exit_code
with different semantics from ordinary processes.

To make ensure that struct kthread is present for all
kernel threads move it's allocation into copy_process.

Add a deallocation of struct kthread in exec for processes
that were kernel threads.

Move the allocation of struct kthread for the initial thread
earlier so that it is not repeated for each additional idle
thread.

Move the initialization of struct kthread into set_kthread_struct
so that the structure is always and reliably initailized.

Clear set_child_tid in free_kthread_struct to ensure the kthread
struct is reliably freed during exec.  The function
free_kthread_struct does not need to clear vfork_done during exec as
exec_mm_release called from exec_mmap has already cleared vfork_done.

Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
fs/exec.c
include/linux/kthread.h
kernel/fork.c
kernel/kthread.c
kernel/sched/core.c

index 537d92c41105bcec0c07d7da2eb5a9bef40787e7..59cac7c18178d62653cc623c3d8b411701f414a6 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1307,6 +1307,8 @@ int begin_new_exec(struct linux_binprm * bprm)
         */
        force_uaccess_begin();
 
+       if (me->flags & PF_KTHREAD)
+               free_kthread_struct(me);
        me->flags &= ~(PF_RANDOMIZE | PF_FORKNOEXEC | PF_KTHREAD |
                                        PF_NOFREEZE | PF_NO_SETAFFINITY);
        flush_thread();
index d86a7e3b9a52100d5f28a2816b664d0f80b58677..4f3433afb54b1d88f9779e020a6f80d860d731a4 100644 (file)
@@ -33,7 +33,7 @@ struct task_struct *kthread_create_on_cpu(int (*threadfn)(void *data),
                                          unsigned int cpu,
                                          const char *namefmt);
 
-void set_kthread_struct(struct task_struct *p);
+bool set_kthread_struct(struct task_struct *p);
 
 void kthread_set_per_cpu(struct task_struct *k, int cpu);
 bool kthread_is_per_cpu(struct task_struct *k);
index 3244cc56b697d7e3a5b3a87d1ed580fb64db1954..04fa3e5d97af1298b4d0acacf8eab562f0a768a4 100644 (file)
@@ -2118,6 +2118,10 @@ static __latent_entropy struct task_struct *copy_process(
        p->io_context = NULL;
        audit_set_context(p, NULL);
        cgroup_fork(p);
+       if (p->flags & PF_KTHREAD) {
+               if (!set_kthread_struct(p))
+                       goto bad_fork_cleanup_threadgroup_lock;
+       }
 #ifdef CONFIG_NUMA
        p->mempolicy = mpol_dup(p->mempolicy);
        if (IS_ERR(p->mempolicy)) {
index 4388d6694a7fd7cdbfe8146d45fd399d12681631..8e5f44bed02739d4334443ce482ea399742d6d85 100644 (file)
@@ -93,20 +93,27 @@ static inline struct kthread *__to_kthread(struct task_struct *p)
        return kthread;
 }
 
-void set_kthread_struct(struct task_struct *p)
+bool set_kthread_struct(struct task_struct *p)
 {
        struct kthread *kthread;
 
-       if (__to_kthread(p))
-               return;
+       if (WARN_ON_ONCE(to_kthread(p)))
+               return false;
 
        kthread = kzalloc(sizeof(*kthread), GFP_KERNEL);
+       if (!kthread)
+               return false;
+
+       init_completion(&kthread->exited);
+       init_completion(&kthread->parked);
+       p->vfork_done = &kthread->exited;
+
        /*
         * We abuse ->set_child_tid to avoid the new member and because it
-        * can't be wrongly copied by copy_process(). We also rely on fact
-        * that the caller can't exec, so PF_KTHREAD can't be cleared.
+        * can't be wrongly copied by copy_process().
         */
        p->set_child_tid = (__force void __user *)kthread;
+       return true;
 }
 
 void free_kthread_struct(struct task_struct *k)
@@ -114,13 +121,13 @@ void free_kthread_struct(struct task_struct *k)
        struct kthread *kthread;
 
        /*
-        * Can be NULL if this kthread was created by kernel_thread()
-        * or if kmalloc() in kthread() failed.
+        * Can be NULL if kmalloc() in set_kthread_struct() failed.
         */
        kthread = to_kthread(k);
 #ifdef CONFIG_BLK_CGROUP
        WARN_ON_ONCE(kthread && kthread->blkcg_css);
 #endif
+       k->set_child_tid = (__force void __user *)NULL;
        kfree(kthread);
 }
 
@@ -315,7 +322,6 @@ static int kthread(void *_create)
        struct kthread *self;
        int ret;
 
-       set_kthread_struct(current);
        self = to_kthread(current);
 
        /* If user was SIGKILLed, I release the structure. */
@@ -325,17 +331,8 @@ static int kthread(void *_create)
                kthread_exit(-EINTR);
        }
 
-       if (!self) {
-               create->result = ERR_PTR(-ENOMEM);
-               complete(done);
-               kthread_exit(-ENOMEM);
-       }
-
        self->threadfn = threadfn;
        self->data = data;
-       init_completion(&self->exited);
-       init_completion(&self->parked);
-       current->vfork_done = &self->exited;
 
        /*
         * The new thread inherited kthreadd's priority and CPU mask. Reset
index 3c9b0fda64ac08b00723227bad6203279d8a9b1b..0404a8c572a1ebd1bec80168e50dd4403180ec36 100644 (file)
@@ -8599,14 +8599,6 @@ void __init init_idle(struct task_struct *idle, int cpu)
 
        __sched_fork(0, idle);
 
-       /*
-        * The idle task doesn't need the kthread struct to function, but it
-        * is dressed up as a per-CPU kthread and thus needs to play the part
-        * if we want to avoid special-casing it in code that deals with per-CPU
-        * kthreads.
-        */
-       set_kthread_struct(idle);
-
        raw_spin_lock_irqsave(&idle->pi_lock, flags);
        raw_spin_rq_lock(rq);
 
@@ -9427,6 +9419,14 @@ void __init sched_init(void)
        mmgrab(&init_mm);
        enter_lazy_tlb(&init_mm, current);
 
+       /*
+        * The idle task doesn't need the kthread struct to function, but it
+        * is dressed up as a per-CPU kthread and thus needs to play the part
+        * if we want to avoid special-casing it in code that deals with per-CPU
+        * kthreads.
+        */
+       WARN_ON(set_kthread_struct(current));
+
        /*
         * Make us the idle thread. Technically, schedule() should not be
         * called from this thread, however somewhere below it might be,