diff options
Diffstat (limited to 'libdde_linux26/lib/src/arch/l4/process.c')
-rw-r--r-- | libdde_linux26/lib/src/arch/l4/process.c | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/libdde_linux26/lib/src/arch/l4/process.c b/libdde_linux26/lib/src/arch/l4/process.c new file mode 100644 index 00000000..ac700f82 --- /dev/null +++ b/libdde_linux26/lib/src/arch/l4/process.c @@ -0,0 +1,343 @@ +#include <dde.h> +#include <dde26.h> + +#include <asm/atomic.h> + +#include <linux/init_task.h> +#include <linux/kernel.h> +#include <linux/kthread.h> +#include <linux/list.h> +#include <linux/thread_info.h> +#include <linux/sched.h> +#include <linux/pid.h> +#include <linux/vmalloc.h> + +#include "local.h" + +/***************************************************************************** + ** Current() implementation ** + *****************************************************************************/ +struct thread_info *current_thread_info(void) +{ + dde26_thread_data *cur = (dde26_thread_data *)ddekit_thread_get_my_data(); + return &LX_THREAD(cur); +} + +struct task_struct *get_current(void) +{ + return current_thread_info()->task; +} + +/***************************************************************************** + ** PID-related stuff ** + ** ** + ** Linux manages lists of PIDs that are handed out to processes so that at ** + ** a later point it is able to determine which task_struct belongs to a ** + ** certain PID. We implement this with a single list holding the mappings ** + ** for all our threads. ** + *****************************************************************************/ + +LIST_HEAD(_pid_task_list); +ddekit_lock_t _pid_task_list_lock; + +/** PID to task_struct mapping */ +struct pid2task +{ + struct list_head list; /**< list data */ + struct pid *pid; /**< PID */ + struct task_struct *ts; /**< task struct */ +}; + +struct pid init_struct_pid = INIT_STRUCT_PID; + +void put_pid(struct pid *pid) +{ + if (pid) + atomic_dec(&pid->count); + // no freeing here, our struct pid's are always allocated as + // part of the dde26_thread_data +} + +/** Attach PID to a certain task struct. */ +void attach_pid(struct task_struct *task, enum pid_type type + __attribute__((unused)), struct pid *pid) +{ + /* Initialize a new pid2task mapping */ + struct pid2task *pt = kmalloc(sizeof(struct pid2task), GFP_KERNEL); + pt->pid = get_pid(pid); + pt->ts = task; + + /* add to list */ + ddekit_lock_lock(&_pid_task_list_lock); + list_add(&pt->list, &_pid_task_list); + ddekit_lock_unlock(&_pid_task_list_lock); +} + +/** Detach PID from a task struct. */ +void detach_pid(struct task_struct *task, enum pid_type type __attribute__((unused))) +{ + struct list_head *p, *n, *h; + + h = &_pid_task_list; + + ddekit_lock_lock(&_pid_task_list_lock); + /* search for mapping with given task struct and free it if necessary */ + list_for_each_safe(p, n, h) { + struct pid2task *pt = list_entry(p, struct pid2task, list); + if (pt->ts == task) { + put_pid(pt->pid); + list_del(p); + kfree(pt); + break; + } + } + ddekit_lock_unlock(&_pid_task_list_lock); +} + +struct task_struct *find_task_by_pid_type(int type, int nr) +{ + struct list_head *h, *p; + h = &_pid_task_list; + + ddekit_lock_lock(&_pid_task_list_lock); + list_for_each(p, h) { + struct pid2task *pt = list_entry(p, struct pid2task, list); + if (pid_nr(pt->pid) == nr) { + ddekit_lock_unlock(&_pid_task_list_lock); + return pt->ts; + } + } + ddekit_lock_unlock(&_pid_task_list_lock); + + return NULL; +} + + +struct task_struct *find_task_by_pid_ns(int nr, struct pid_namespace *ns) +{ + /* we don't implement PID name spaces */ + return find_task_by_pid_type(0, nr); +} + +struct task_struct *find_task_by_pid(int nr) +{ + return find_task_by_pid_type(0, nr); +} + +/***************************************************************************** + ** kernel_thread() implementation ** + *****************************************************************************/ +/* Struct containing thread data for a newly created kthread. */ +struct __kthread_data +{ + int (*fn)(void *); + void *arg; + ddekit_lock_t lock; + dde26_thread_data *kthread; +}; + +/** Counter for running kthreads. It is used to create unique names + * for kthreads. + */ +static atomic_t kthread_count = ATOMIC_INIT(0); + +/** Entry point for new kernel threads. Make this thread a DDE26 + * worker and then execute the real thread fn. + */ +static void __kthread_helper(void *arg) +{ + struct __kthread_data *k = (struct __kthread_data *)arg; + + /* + * Make a copy of the fn and arg pointers, as the kthread struct is + * deleted by our parent after notifying it and this may happen before we + * get to execute the function. + */ + int (*_fn)(void*) = k->fn; + void *_arg = k->arg; + + l4dde26_process_add_worker(); + + /* + * Handshake with creator - we store our thread data in the + * kthread struct and then unlock the lock to notify our + * creator about completing setup + */ + k->kthread = (dde26_thread_data *)ddekit_thread_get_my_data(); + ddekit_lock_unlock(&k->lock); + + do_exit(_fn(_arg)); +} + +/** Our implementation of Linux' kernel_thread() function. Setup a new + * thread running our __kthread_helper() function. + */ +int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) +{ + ddekit_thread_t *t; + char name[20]; + struct __kthread_data *kt = vmalloc(sizeof(struct __kthread_data)); + ddekit_lock_t lock; + + /* Initialize (and grab) handshake lock */ + ddekit_lock_init(&lock); + ddekit_lock_lock(&lock); + + int threadnum = atomic_inc_return(&kthread_count); + kt->fn = fn; + kt->arg = arg; + kt->lock = lock; // Copy lock ptr, note that kt is freed by the + // new thread, so we MUST NOT use kt->lock after + // this point! + + snprintf(name, 20, ".kthread%x", threadnum); + t = ddekit_thread_create(__kthread_helper, + (void *)kt, name); + Assert(t); + + ddekit_lock_lock(&lock); + ddekit_lock_deinit(&lock); + + return pid_nr(VPID_P(kt->kthread)); +} + +/** Our implementation of exit(). For DDE purposes this only relates + * to kernel threads. + */ +void do_exit(long code) +{ + ddekit_thread_t *t = DDEKIT_THREAD(lxtask_to_ddethread(current)); +// printk("Thread %s exits with code %x\n", ddekit_thread_get_name(t), code); + + /* do some cleanup */ + detach_pid(current, 0); + + /* goodbye, cruel world... */ + ddekit_thread_exit(); +} + +/***************************************************************************** + ** Misc functions ** + *****************************************************************************/ + + +char *get_task_comm(char *buf, struct task_struct *tsk) +{ + char *ret; + /* buf must be at least sizeof(tsk->comm) in size */ + task_lock(tsk); + ret = strncpy(buf, tsk->comm, sizeof(tsk->comm)); + task_unlock(tsk); + return ret; +} + + +void set_task_comm(struct task_struct *tsk, char *buf) +{ + task_lock(tsk); + strlcpy(tsk->comm, buf, sizeof(tsk->comm)); + task_unlock(tsk); +} + + +/***************************************************************************** + ** DDEKit gluecode, init functions ** + *****************************************************************************/ +/* Initialize a dde26 thread. + * + * - Allocate thread data, as well as a Linux task struct, + * - Fill in default values for thread_info, and task, + * - Adapt task struct's thread_info backreference + * - Initialize the DDE sleep lock + */ +static dde26_thread_data *init_dde26_thread(void) +{ + /* + * Virtual PID counter + */ + static atomic_t pid_counter = ATOMIC_INIT(0); + dde26_thread_data *t = vmalloc(sizeof(dde26_thread_data)); + Assert(t); + + memcpy(&t->_vpid, &init_struct_pid, sizeof(struct pid)); + t->_vpid.numbers[0].nr = atomic_inc_return(&pid_counter); + + memcpy(&LX_THREAD(t), &init_thread, sizeof(struct thread_info)); + + LX_TASK(t) = vmalloc(sizeof(struct task_struct)); + Assert(LX_TASK(t)); + + memcpy(LX_TASK(t), &init_task, sizeof(struct task_struct)); + + /* nice: Linux backreferences a task`s thread_info from the + * task struct (which in turn can be found using the + * thread_info...) */ + LX_TASK(t)->stack = &LX_THREAD(t); + + /* initialize this thread's sleep lock */ + SLEEP_LOCK(t) = ddekit_sem_init(0); + + return t; +} + +/* Process setup for worker threads */ +int l4dde26_process_add_worker(void) +{ + dde26_thread_data *cur = init_dde26_thread(); + + /* If this function is called for a kernel_thread, the thread already has + * been set up and we just need to store a reference to the ddekit struct. + * However, this function may also be called directly to turn an L4 thread + * into a DDE thread. Then, we need to initialize here. */ + cur->_ddekit_thread = ddekit_thread_myself(); + if (cur->_ddekit_thread == NULL) + cur->_ddekit_thread = ddekit_thread_setup_myself(".dde26_thread"); + Assert(cur->_ddekit_thread); + + ddekit_thread_set_my_data(cur); + + attach_pid(LX_TASK(cur), 0, &cur->_vpid); + + /* Linux' default is to have this set to 1 initially and let the + * scheduler set this to 0 later on. + */ + current_thread_info()->preempt_count = 0; + + return 0; +} + + +/** + * Add an already existing DDEKit thread to the set of threads known to the + * Linux environment. This is used for the timer thread, which is actually a + * DDEKit thread, but Linux code shall see it as a Linux thread as well. + */ +int l4dde26_process_from_ddekit(ddekit_thread_t *t) +{ + Assert(t); + + dde26_thread_data *cur = init_dde26_thread(); + cur->_ddekit_thread = t; + ddekit_thread_set_data(t, cur); + attach_pid(LX_TASK(cur), 0, &cur->_vpid); + + return 0; +} + +/** Function to initialize the first DDE process. + */ +int __init l4dde26_process_init(void) +{ + ddekit_lock_init_unlocked(&_pid_task_list_lock); + + int kthreadd_pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); + kthreadd_task = find_task_by_pid(kthreadd_pid); + + l4dde26_process_add_worker(); + + return 0; +} + +DEFINE_PER_CPU(int, cpu_number); + +//dde_process_initcall(l4dde26_process_init); |