Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Merge branch 'devel'
[palacios.git] / kitten / kernel / task.c
diff --git a/kitten/kernel/task.c b/kitten/kernel/task.c
new file mode 100644 (file)
index 0000000..238f4b7
--- /dev/null
@@ -0,0 +1,237 @@
+#include <lwk/kernel.h>
+#include <lwk/xcall.h>
+#include <lwk/htable.h>
+#include <lwk/aspace.h>
+#include <lwk/task.h>
+#include <lwk/sched.h>
+#include <arch/uaccess.h>
+
+/**
+ * ID space used to allocate task IDs.
+ */
+static idspace_t idspace;
+
+/**
+ * Hash table used to lookup task structures by ID.
+ */
+static htable_t htable;
+
+/**
+ * Lock for serializing access to the htable.
+ */
+static DEFINE_SPINLOCK(htable_lock);
+
+int __init
+task_subsys_init(void)
+{
+       if (idspace_create(__TASK_MIN_ID, __TASK_MAX_ID, &idspace))
+               panic("Failed to create task ID space.");
+
+       if (htable_create(7 /* 2^7 bins */,
+                         offsetof(struct task_struct, id),
+                         offsetof(struct task_struct, ht_link),
+                         &htable))
+               panic("Failed to create task hash table.");
+
+       return 0;
+}
+
+int
+task_get_myid(id_t *id)
+{
+       *id = current->id;
+       return 0;
+}
+
+int
+sys_task_get_myid(id_t __user *id)
+{
+       int status;
+       id_t _id;
+
+       if ((status = task_get_myid(&_id)) != 0)
+               return status;
+
+       if (id && copy_to_user(id, &_id, sizeof(*id)))
+               return -EINVAL;
+
+       return 0;
+}
+
+int
+__task_reserve_id(id_t id)
+{
+       return idspace_alloc_id(idspace, id, NULL);
+}
+
+int
+__task_create(id_t id, const char *name,
+              const start_state_t *start_state,
+              struct task_struct **task)
+{
+       int status;
+       union task_union *task_union;
+       struct task_struct *tsk;
+
+       if ((task_union = kmem_get_pages(TASK_ORDER)) == NULL)
+               return -ENOMEM;
+
+       tsk = &task_union->task_info;
+
+       /*
+        * Initialize the new task. kmem_alloc() allocates zeroed memory
+        * so fields with an initial state of zero do not need to be explicitly
+        * initialized.
+        */
+       tsk->id = id;
+       if (name)
+               strlcpy(tsk->name, name, sizeof(tsk->name));
+       hlist_node_init(&tsk->ht_link);
+       tsk->state = TASKSTATE_READY;
+       tsk->uid = start_state->uid;
+       tsk->gid = start_state->gid;
+       tsk->aspace = aspace_acquire(start_state->aspace_id);
+       if (!tsk->aspace) {
+               status = -ENOENT;
+               goto error1;
+       }
+       tsk->sighand = NULL;
+       if (start_state->cpumask) {
+               cpumask_user2kernel(start_state->cpumask, &tsk->cpumask);
+               if (!cpus_subset(tsk->cpumask, current->cpumask)) {
+                       status = -EINVAL;
+                       goto error2;
+               }
+       } else {
+               tsk->cpumask = current->cpumask;
+       }
+       if ((start_state->cpu_id >= NR_CPUS)
+            || !cpu_isset(start_state->cpu_id, tsk->cpumask)) {
+               status = -EINVAL;
+               goto error2;
+       }
+       tsk->cpu_id = start_state->cpu_id;
+       list_head_init(&tsk->sched_link);
+       tsk->ptrace = 0;
+       tsk->flags = 0;
+       tsk->exit_status = 0;
+
+       /* Do architecture-specific initialization */
+       if ((status = arch_task_create(tsk, start_state)) != 0)
+               goto error2;
+
+       if (task)
+               *task = tsk;
+       return 0;
+
+error2:
+       if (tsk->aspace)
+               aspace_release(tsk->aspace);
+error1:
+       kmem_free_pages(task_union, TASK_ORDER);
+       return status;
+}
+
+int
+task_create(id_t id_request, const char *name,
+            const start_state_t *start_state, id_t *id)
+{
+       id_t new_id;
+       struct task_struct *new_task;
+       int status;
+       unsigned long irqstate;
+
+       /* Allocate an ID for the new task */
+       if ((status = idspace_alloc_id(idspace, id_request, &new_id)) != 0)
+               return status;
+
+       /* Create and initialize a new task */
+       if ((status = __task_create(new_id, name, start_state, &new_task))) {
+               idspace_free_id(idspace, new_id);
+               return status;
+       }
+
+       /* Add new task to a hash table, for quick lookups by ID */
+       spin_lock_irqsave(&htable_lock, irqstate);
+       BUG_ON(htable_add(htable, new_task));
+       spin_unlock_irqrestore(&htable_lock, irqstate);
+
+       /* Add the new task to the target CPU's run queue */
+       sched_add_task(new_task);
+
+       if (id)
+               *id = new_task->id;
+       return 0;
+}
+
+int
+sys_task_create(id_t id_request, const char __user *name,
+                const start_state_t __user *start_state, id_t __user *id)
+{
+       int status;
+       start_state_t _start_state;
+       user_cpumask_t _cpumask;
+       char _name[16];
+       id_t _id;
+
+       if (current->uid != 0)
+               return -EPERM;
+
+       if (copy_from_user(&_start_state, start_state, sizeof(_start_state)))
+               return -EINVAL;
+
+       if (_start_state.aspace_id == KERNEL_ASPACE_ID)
+               return -EINVAL;
+
+       if (_start_state.cpumask) {
+               if (copy_from_user(&_cpumask, _start_state.cpumask, sizeof(_cpumask)))
+                       return -EINVAL;
+               _start_state.cpumask = &_cpumask;
+       }
+
+       if (name && (strncpy_from_user(_name, name, sizeof(_name)) < 0))
+               return -EFAULT;
+       _name[sizeof(_name) - 1] = '\0';
+
+       if ((status = task_create(id_request, _name, &_start_state, &_id)) != 0)
+               return status;
+
+       if (id && copy_to_user(id, &_id, sizeof(*id)))
+               return -EFAULT;
+
+       return 0;
+}
+
+int
+task_exit(int status)
+{
+       /* Mark the task as exited...
+        * schedule() will remove it from the run queue */
+       current->exit_status = status;
+       current->state = TASKSTATE_EXIT_ZOMBIE;
+       schedule(); /* task is dead, so this should never return */
+       BUG();
+       while (1) {}
+}
+
+int
+sys_task_exit(int status)
+{
+       return task_exit(status);
+}
+
+int
+task_yield(void)
+{
+       /*
+        * Nothing to do, schedule() will be automatically
+        * called before returning to user-space
+        */
+       return 0;
+}
+
+int
+sys_task_yield(void)
+{
+       return task_yield();
+}