Name: Use TASK_STOPPED and WUNTRACED Instead of Global for initfn Return Author: Rusty Russell Status: Booted on 2.6.1-rc1-bk5 Depends: Hotcpu/kthread-signal.patch.gz D: The kthread_start function needs to get know whether the initfn in D: the thread failed or not (if it did, wait for the child). Rather D: than the current shared variable and lock, we can wait for it D: either way: if initfn succeeds, thread goes to TASK_STOPPED. diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .22358-2.6.1-rc1-bk5-kthread-noglobal.pre/kernel/kthread.c .22358-2.6.1-rc1-bk5-kthread-noglobal/kernel/kthread.c --- .22358-2.6.1-rc1-bk5-kthread-noglobal.pre/kernel/kthread.c 2004-01-05 14:10:10.000000000 +1100 +++ .22358-2.6.1-rc1-bk5-kthread-noglobal/kernel/kthread.c 2004-01-05 14:10:11.000000000 +1100 @@ -14,19 +14,13 @@ #include #include -/* This makes sure only one kthread is being talked to at once: - * protects global vars below. */ -static DECLARE_MUTEX(kthread_control); - -static int kthread_init_failed; -static DECLARE_COMPLETION(kthread_ack); - struct kthread_create_info { int (*initfn)(void *data); int (*corefn)(void *data); void *data; char *name; + struct completion started; }; /* Returns a POSITIVE error number. */ @@ -46,19 +40,23 @@ static int kthread(void *data) /* OK, tell user we're spawned, wait for kthread_start/stop */ current->state = TASK_INTERRUPTIBLE; - complete(&kthread_ack); + complete(&((struct kthread_create_info *)data)->started); schedule(); if (signal_pending(current)) return EINTR; - /* Run initfn, tell the caller the result. */ + /* Run initfn: start_kthread is in wait(). */ if (k.initfn) ret = k.initfn(k.data); - kthread_init_failed = (ret < 0); - complete(&kthread_ack); if (ret < 0) - return -ret; + return -ret << 8; + + /* We succeeded. Stop ourselves to break them out of wait(). */ + current->exit_code = 1; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); while (!signal_pending(current)) { /* If it fails, just wait until kthread_stop. */ @@ -70,7 +68,7 @@ static int kthread(void *data) } current->state = TASK_RUNNING; - return -ret; + return -ret << 8; } enum kthread_op_type @@ -86,7 +84,7 @@ struct kthread_op union { /* KTHREAD_CREATE in */ - struct kthread_start_info *start; + struct kthread_create_info *create; /* KTHREAD_START, KTHREAD_STOP in */ struct task_struct *target; /* out */ @@ -100,13 +98,12 @@ static void *create_kthread(struct kthre { int pid; - init_completion(&kthread_ack); /* We want our own signal handler (we take no signals by default). */ - pid = kernel_thread(kthread, op->u.start,CLONE_FS|CLONE_FILES|SIGCHLD); + pid = kernel_thread(kthread,op->u.create,CLONE_FS|CLONE_FILES|SIGCHLD); if (pid < 0) return ERR_PTR(pid); - wait_for_completion(&kthread_ack); + wait_for_completion(&op->u.create->started); return find_task_by_pid(pid); } @@ -123,19 +120,19 @@ static void adopt_kthread(struct task_st /* We are keventd: start the thread. */ static void *start_kthread(struct kthread_op *op) { - int status = 0; - - init_completion(&kthread_ack); + int ret, status; - /* Adopt it in case it's going to die, then tell it to start... */ + /* Adopt it so we can wait() for result. */ adopt_kthread(op->u.target); - get_task_struct(op->u.target); wake_up_process(op->u.target); - wait_for_completion(&kthread_ack); - if (kthread_init_failed) - waitpid(op->u.target->tgid, &status, __WALL); - put_task_struct(op->u.target); + ret = waitpid(op->u.target->tgid, &status, __WALL|WUNTRACED); + BUG_ON(ret < 0); + if ((status & 0xff) == 0x7f) { + /* STOPPED means initfn succeeded, tell it to continue. */ + wake_up_process(op->u.target); + return NULL; + } return ERR_PTR(-((status >> 8) & 0xFF)); } @@ -144,15 +141,14 @@ static void *start_kthread(struct kthrea static void *stop_kthread(struct kthread_op *op) { int status; + pid_t pid = op->u.target->tgid; adopt_kthread(op->u.target); - get_task_struct(op->u.target); force_sig(SIGTERM, op->u.target); - waitpid(op->u.target->tgid, &status, __WALL); - put_task_struct(op->u.target); + waitpid(pid, &status, __WALL); - return ERR_PTR((status >> 8) & 0xFF); + return ERR_PTR(-((status >> 8) & 0xFF)); } /* We are keventd: do what they told us */ @@ -160,8 +156,6 @@ static void keventd_do_kthread_op(void * { struct kthread_op *op = _op; - down(&kthread_control); - switch (op->type) { case KTHREAD_CREATE: op->u.result = create_kthread(op); break; case KTHREAD_START: op->u.result = start_kthread(op); break; @@ -169,8 +164,6 @@ static void keventd_do_kthread_op(void * default: BUG(); } - - up(&kthread_control); complete(&op->done); } @@ -182,7 +175,7 @@ static struct task_struct *do_kthread_op op.type = type; op.u.target = info; init_completion(&op.done); - + /* If we're being called to start the first workqueue, we * can't use keventd. */ if (!keventd_up()) @@ -213,6 +206,7 @@ struct task_struct *kthread_create(int ( start.corefn = corefn; start.data = data; start.name = name; + init_completion(&start.started); return do_kthread_op(KTHREAD_CREATE, &start); } @@ -225,3 +219,72 @@ int kthread_stop(struct task_struct *k) { return PTR_ERR(do_kthread_op(KTHREAD_STOP, k)); } + +/* Test code */ +#include +static int init(void *data) +{ + if (data == init) { + printk("init returning %i\n", -EINVAL); + return -EINVAL; + } + return 0; +} + +static int core(void *data) +{ + if (data == core) + return -ENOENT; + if (data == do_exit) + do_exit(0); + current->state = TASK_INTERRUPTIBLE; + return 0; +} + +static int test_kthreads(void) +{ + struct task_struct *k; + int ret; + + printk("kthread: Initialization which fails.\n"); + k = kthread_create(init, core, init, "kthreadtest"); + BUG_ON(IS_ERR(k)); + ret = kthread_start(k); + BUG_ON(ret != -EINVAL); + + printk("kthread: stopped before initialization\n"); + k = kthread_create(init, core, NULL, "kthreadtest"); + BUG_ON(IS_ERR(k)); + ret = kthread_stop(k); + BUG_ON(ret != 0); + + printk("kthread: Corefn which fails.\n"); + k = kthread_create(init, core, core, "kthreadtest"); + BUG_ON(IS_ERR(k)); + ret = kthread_start(k); + BUG_ON(ret != 0); + ret = kthread_stop(k); + BUG_ON(ret != -ENOENT); + + printk("kthread: Corefn which succeeds.\n"); + k = kthread_create(init, core, NULL, "kthreadtest"); + BUG_ON(IS_ERR(k)); + ret = kthread_start(k); + BUG_ON(ret != 0); + ret = kthread_stop(k); + BUG_ON(ret != 0); + + printk("kthread: Corefn which exits.\n"); + k = kthread_create(init, core, do_exit, "kthreadtest"); + BUG_ON(IS_ERR(k)); + get_task_struct(k); + ret = kthread_start(k); + BUG_ON(ret != 0); + BUG_ON(!(k->state & (TASK_ZOMBIE|TASK_DEAD))); + put_task_struct(k); + + printk("All kthread tests passed...\n"); + return 0; +} + +module_init(test_kthreads); diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .22358-2.6.1-rc1-bk5-kthread-noglobal.pre/kernel/workqueue.c .22358-2.6.1-rc1-bk5-kthread-noglobal/kernel/workqueue.c --- .22358-2.6.1-rc1-bk5-kthread-noglobal.pre/kernel/workqueue.c 2004-01-05 14:10:10.000000000 +1100 +++ .22358-2.6.1-rc1-bk5-kthread-noglobal/kernel/workqueue.c 2004-01-05 14:10:11.000000000 +1100 @@ -181,7 +181,7 @@ static int worker_thread(void *__startup complete(&startup->done); /* Install a handler so SIGCLD is delivered */ - sa.sa.sa_handler = SIG_IGN; + sa.sa.sa_handler = SIG_DFL; sa.sa.sa_flags = 0; siginitset(&sa.sa.sa_mask, sigmask(SIGCHLD)); do_sigaction(SIGCHLD, &sa, (struct k_sigaction *)0);