summaryrefslogtreecommitdiff
path: root/libdde_linux26/lib/src/arch/l4/process.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdde_linux26/lib/src/arch/l4/process.c')
-rw-r--r--libdde_linux26/lib/src/arch/l4/process.c343
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);