Name: Simplified kthread patch Author: Rusty Russell and Christoph Hellwig Status: Relatively Trivial D: This is a simplified version of Christoph Hellwig's kthread patch. D: It provides a clean way of creating and terminating kernel threads. D: This version uses the context thread to ensure no contamination D: between the context creating the thread (eg. modprobe) and the D: created thread, and allows for printf-style names. diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.5.31/include/linux/kthread.h working-2.5.31-kthread/include/linux/kthread.h --- linux-2.5.31/include/linux/kthread.h 1970-01-01 10:00:00.000000000 +1000 +++ working-2.5.31-kthread/include/linux/kthread.h 2002-08-26 16:23:25.000000000 +1000 @@ -0,0 +1,24 @@ +#ifndef _LINUX_KTHREAD_H +#define _LINUX_KTHREAD_H +#include + +/* Opaque handle. */ +struct kthread_struct; + +/* If thread creation fails, or init() returns non-zero, error + is in PTR_ERR() of return value. + Otherwise main() is run until it returns non-zero, or + kthread_stop() is called. + Either way, cleanup() is called finally. +*/ +extern struct kthread_struct *kthread(int (*init)(void *), + int (*main)(void *), + void (*cleanup)(void *), + void *data, + const char *namefmt, ...) +__attribute__ ((format(printf, 4, 5))); + +/* Stop the kernel thread: returns last return value of main(). */ +extern int kthread_stop(struct kthread_struct *kt); + +#endif /* _LINUX_KTHREAD_H */ diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.5.31/kernel/Makefile working-2.5.31-kthread/kernel/Makefile --- linux-2.5.31/kernel/Makefile 2002-08-11 15:31:43.000000000 +1000 +++ working-2.5.31-kthread/kernel/Makefile 2002-08-26 11:38:00.000000000 +1000 @@ -10,12 +10,12 @@ O_TARGET := kernel.o export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o \ - printk.o platform.o suspend.o dma.o + printk.o platform.o suspend.o dma.o kthread.o obj-y = sched.o fork.o exec_domain.o panic.o printk.o \ module.o exit.o itimer.o time.o softirq.o resource.o \ sysctl.o capability.o ptrace.o timer.o user.o \ - signal.o sys.o kmod.o context.o futex.o platform.o + signal.o sys.o kmod.o context.o futex.o platform.o kthread.o obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o obj-$(CONFIG_SMP) += cpu.o diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.5.31/kernel/kthread.c working-2.5.31-kthread/kernel/kthread.c --- linux-2.5.31/kernel/kthread.c 1970-01-01 10:00:00.000000000 +1000 +++ working-2.5.31-kthread/kernel/kthread.c 2002-08-26 16:43:40.000000000 +1000 @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2002 Rusty Russell, IBM. + * Inspired by kthread implementation Copyright (c) 2002 Christoph Hellwig. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +struct kthread_struct +{ + struct list_head list; + + int (*init)(void *); + void (*main)(void *); + void (*cleanup)(void *); + void *data; + char name[sizeof(((struct task_struct *)0)->comm)]; + + /* Creation complete */ + struct completion created; + struct task_struct *task; /* ERR_PTR() for init or fork error */ + + /* True if we are supposed to stop now */ + int stop; + /* Termination complete. */ + struct completion terminated; + int main_return; +}; + +static int do_kthread(void *__k) +{ + struct kthread_struct *kt = __k; + int ret; + + /* Copy name across */ + memcpy(current->comm, kt->name, sizeof(current->comm)); + + /* Set up signal handling: term signal for breaking main loop */ + spin_lock_irq(&curtask->sigmask_lock); + siginitsetinv(&curtask->blocked, sigmask(SIGTERM)); + spin_unlock_irq(&curtask->sigmask_lock); + + /* Do the initialization */ + ret = kt->init(kt->data); + if (ret < 0) { + kt->task = ERR_PTR(ret); + complete(&kt->created); + return 0; + } + + /* We have begun. */ + kt->task = current; + complete(&kt->created); + + do { + kt->main_return = kt->main(kt->data); + } while (kt->main_return == 0 && !signal_pending(current)); + + kt->cleanup(kt->data); + complete(&kt->terminated); +} + +/* We create thread from context thread, as that is cloned from init, + and hence is guaranteed "clean". */ +static void kthread_create(void *__k) +{ + int pid; + struct kthread_struct *kt = __k; + + pid = kernel_thread(do_kthread,kt, CLONE_FS|CLONE_FILES|CLONE_SIGHAND); + if (pid < 0) { + kt->task = ERR_PTR(pid); + complete(&kt->created); + } +} + +struct kthread_struct *kthread(int (*init)(void *), + void (*main)(void *), + void (*cleanup)(void *), + void *data, + const char *namefmt, ...) +{ + struct kthread_struct *kt; + struct tq_struct tq; + va_list args; + + kt = kmalloc(sizeof(*kt), GFP_KERNEL); + if (!kt) + return -ENOMEM; + kt->init = init; + kt->main = main; + kt->cleanup = cleanup; + kt->data = data; + kt->stop = 0; + init_completion(&kt->created); + init_completion(&kt->terminated): + + /* Fill in name */ + va_start(args, namefmt); + vsnprintf(kt->name, namefmt, args); + va_end(args); + + /* Start the thread */ + INIT_TQUEUE(&tq, kthread_create, kt); + schedule_task(&tq); + wait_for_completion(&kt->created); + + if (IS_ERR(kt->task)) { + int err = PTR_ERR(kt->task); + kfree(kt); + return ERR_PTR(err); + } + return kt; +} + +/* Stop the kernel thread: returns last return value of main(). */ +int kthread_stop(struct kthread_struct *kt) +{ + int ret; + + /* You can't stop it twice. */ + BUG_ON(kt->stop); + + kt->stop = 1; + /* Make sure other CPU sees this before signal hits */ + wmb(); + send_sig(SIGTERM, find_task_by_pid(kt->pid)); + + wait_for_completion(&kt->terminated); + ret = kt->main_return; + kfree(kt); + + return ret; +} + +EXPORT_SYMBOL(kthread); +EXPORT_SYMBOL(kthread_stop);