Clean up profiler configure and usage functions.
[akaros.git] / kern / src / kthread.c
index 48eba0f..173722d 100644 (file)
@@ -12,6 +12,7 @@
 #include <smp.h>
 #include <schedule.h>
 #include <kstack.h>
+#include <arch/uaccess.h>
 
 uintptr_t get_kstack(void)
 {
@@ -207,7 +208,7 @@ static void __ktask_wrapper(uint32_t srcid, long a0, long a1, long a2)
        void *arg = (void*)a1;
        char *name = (char*)a2;
        struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
-       assert(pcpui->cur_kthread->is_ktask);
+       assert(is_ktask(pcpui->cur_kthread));
        pcpui->cur_kthread->name = name;
        /* There are some rendezs out there that aren't wrapped.  Though no one can
         * abort them.  Yet. */
@@ -335,16 +336,16 @@ void sem_down(struct semaphore *sem)
                new_kthread = pcpui->spare;
                new_stacktop = new_kthread->stacktop;
                pcpui->spare = 0;
-               /* Based on how we set is_ktask (in PRKM), we'll usually have a spare
-                * with is_ktask set, even though the default setting is off.  The
-                * reason is that the launching of blocked kthreads also uses PRKM, and
-                * that KMSG (__launch_kthread) doesn't return.  Thus the soon-to-be
-                * spare kthread, that is launching another, has is_ktask set. */
-               new_kthread->is_ktask = FALSE;
+               /* The old flags could have KTH_IS_KTASK set.  The reason is that the
+                * launching of blocked kthreads also uses PRKM, and that KMSG
+                * (__launch_kthread) doesn't return.  Thus the soon-to-be spare
+                * kthread, that is launching another, has flags & KTH_IS_KTASK set. */
+               new_kthread->flags = KTH_DEFAULT_FLAGS;
                new_kthread->proc = 0;
                new_kthread->name = 0;
        } else {
                new_kthread = __kthread_zalloc();
+               new_kthread->flags = KTH_DEFAULT_FLAGS;
                new_stacktop = get_kstack();
                new_kthread->stacktop = new_stacktop;
 #ifdef CONFIG_KTHREAD_POISON
@@ -366,16 +367,18 @@ void sem_down(struct semaphore *sem)
 #endif /* CONFIG_KTHREAD_POISON */
        /* Kthreads that are ktasks are not related to any process, and do not need
         * to work in a process's address space.  They can operate in any address
-        * space that has the kernel mapped (like boot_pgdir, or any pgdir).
+        * space that has the kernel mapped (like boot_pgdir, or any pgdir).  Some
+        * ktasks may switch_to, at which point they do care about the address
+        * space and must maintain a reference.
         *
-        * Other kthreads need to stay in the process context (if there is one), but
-        * we want the core (which could be a vcore) to stay in the context too.  In
-        * the future, we could check owning_proc. If it isn't set, we could leave
-        * the process context and transfer the refcnt to kthread->proc. */
-       if (!kthread->is_ktask) {
+        * Normal kthreads need to stay in the process context, but we want the core
+        * (which could be a vcore) to stay in the context too. */
+       if (kthread->flags & KTH_SAVE_ADDR_SPACE) {
                kthread->proc = current;
-               if (kthread->proc)      /* still could be none, like during init */
-                       proc_incref(kthread->proc, 1);
+               assert(kthread->proc);
+               /* In the future, we could check owning_proc. If it isn't set, we could
+                * clear current and transfer the refcnt to kthread->proc. */
+               proc_incref(kthread->proc, 1);
        } else {
                kthread->proc = 0;
        } 
@@ -727,7 +730,8 @@ void cv_broadcast_irqsave(struct cond_var *cv, int8_t *irq_state)
        enable_irqsave(irq_state);
 }
 
-/* Helper, aborts and releases a CLE.  dereg_ spinwaits on abort_in_progress. */
+/* Helper, aborts and releases a CLE.  dereg_ spinwaits on abort_in_progress.
+ * This can throw a PF */
 static void __abort_and_release_cle(struct cv_lookup_elm *cle)
 {
        int8_t irq_state = 0;
@@ -758,8 +762,10 @@ static void __abort_and_release_cle(struct cv_lookup_elm *cle)
  *   all the memory for CLE is safe */
 bool abort_sysc(struct proc *p, struct syscall *sysc)
 {
+       ERRSTACK(1);
        struct cv_lookup_elm *cle;
        int8_t irq_state = 0;
+
        spin_lock_irqsave(&p->abort_list_lock);
        TAILQ_FOREACH(cle, &p->abortable_sleepers, link) {
                if (cle->sysc == sysc) {
@@ -772,7 +778,9 @@ bool abort_sysc(struct proc *p, struct syscall *sysc)
        spin_unlock_irqsave(&p->abort_list_lock);
        if (!cle)
                return FALSE;
-       __abort_and_release_cle(cle);
+       if (!waserror())        /* discard error */
+               __abort_and_release_cle(cle);
+       poperror();
        return TRUE;
 }
 
@@ -785,11 +793,13 @@ static int __abort_all_sysc(struct proc *p,
                             bool (*should_abort)(struct cv_lookup_elm*, void*),
                             void *arg)
 {
+       ERRSTACK(1);
        struct cv_lookup_elm *cle;
        int8_t irq_state = 0;
        struct cv_lookup_tailq abortall_list;
-       struct proc *old_proc = switch_to(p);
+       uintptr_t old_proc = switch_to(p);
        int ret = 0;
+
        /* Concerns: we need to not remove them from their original list, since
         * concurrent wake ups will cause a dereg, which will remove from the list.
         * We also can't touch freed memory, so we need a refcnt to keep cles
@@ -804,8 +814,11 @@ static int __abort_all_sysc(struct proc *p,
                ret++;
        }
        spin_unlock_irqsave(&p->abort_list_lock);
-       TAILQ_FOREACH(cle, &abortall_list, abortall_link)
-               __abort_and_release_cle(cle);
+       if (!waserror()) { /* discard error */
+               TAILQ_FOREACH(cle, &abortall_list, abortall_link)
+                       __abort_and_release_cle(cle);
+       }
+       poperror();
        switch_back(p, old_proc);
        return ret;
 }
@@ -820,9 +833,23 @@ void abort_all_sysc(struct proc *p)
        __abort_all_sysc(p, always_abort, 0);
 }
 
+/* cle->sysc could be a bad pointer.  we can either use copy_from_user (btw,
+ * we're already in their addr space) or we can use a waserror in
+ * __abort_all_sysc().  Both options are fine.  I went with it here for a couple
+ * reasons.  It is only this abort function pointer that accesses sysc, though
+ * that could change.  Our syscall aborting isn't plugged into a broader error()
+ * handler yet, which means we'd want to poperror instead of nexterror in
+ * __abort_all_sysc, and that would required int ret getting a volatile flag. */
 static bool sysc_uses_fd(struct cv_lookup_elm *cle, void *fd)
 {
-       return syscall_uses_fd(cle->sysc, (int)(long)fd);
+       struct syscall local_sysc;
+       int err;
+
+       err = copy_from_user(&local_sysc, cle->sysc, sizeof(struct syscall));
+       /* Trigger an abort on error */
+       if (err)
+               return TRUE;
+       return syscall_uses_fd(&local_sysc, (int)(long)fd);
 }
 
 int abort_all_sysc_fd(struct proc *p, int fd)
@@ -840,12 +867,11 @@ void __reg_abortable_cv(struct cv_lookup_elm *cle, struct cond_var *cv)
        cle->cv = cv;
        cle->kthread = pcpui->cur_kthread;
        /* Could be a ktask.  Can build in support for aborting these later */
-       if (cle->kthread->is_ktask) {
+       if (is_ktask(cle->kthread)) {
                cle->sysc = 0;
                return;
        }
        cle->sysc = cle->kthread->sysc;
-       assert(cle->sysc);
        cle->proc = pcpui->cur_proc;
        atomic_init(&cle->abort_in_progress, 0);
        spin_lock_irqsave(&cle->proc->abort_list_lock);
@@ -860,7 +886,7 @@ void __reg_abortable_cv(struct cv_lookup_elm *cle, struct cond_var *cv)
  * CV lock.  So if we hold the CV lock, we can deadlock (circular dependency).*/
 void dereg_abortable_cv(struct cv_lookup_elm *cle)
 {
-       if (cle->kthread->is_ktask)
+       if (is_ktask(cle->kthread))
                return;
        assert(cle->proc);
        spin_lock_irqsave(&cle->proc->abort_list_lock);
@@ -876,11 +902,48 @@ void dereg_abortable_cv(struct cv_lookup_elm *cle)
  * this with things for ktasks in the future. */
 bool should_abort(struct cv_lookup_elm *cle)
 {
-       if (cle->kthread->is_ktask)
+       struct syscall local_sysc;
+       int err;
+
+       if (is_ktask(cle->kthread))
                return FALSE;
        if (cle->proc && (cle->proc->state == PROC_DYING))
                return TRUE;
-       if (cle->sysc && (atomic_read(&cle->sysc->flags) & SC_ABORT))
-               return TRUE;
+       if (cle->sysc) {
+               assert(cle->proc && (cle->proc == current));
+               err = copy_from_user(&local_sysc, cle->sysc,
+                                    offsetof(struct syscall, flags) +
+                                    sizeof(cle->sysc->flags));
+               /* just go ahead and abort if there was an error */
+               if (err || (atomic_read(&local_sysc.flags) & SC_ABORT))
+                       return TRUE;
+       }
        return FALSE;
 }
+
+/* Sometimes the kernel needs to switch out of process context and into a
+ * 'process-less' kernel thread.  This is basically a ktask.  We use this mostly
+ * when performing file ops as the kernel.  It's nasty, and all uses of this
+ * probably should be removed.  (TODO: KFOP). */
+uintptr_t switch_to_ktask(void)
+{
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       struct kthread *kth = pcpui->cur_kthread;
+
+       if (is_ktask(kth))
+               return 0;
+       /* We leave the SAVE_ADDR_SPACE flag on.  Now we're basically a ktask that
+        * cares about its addr space, since we need to return to it (not that we're
+        * leaving). */
+       kth->flags |= KTH_IS_KTASK;
+       return 1;
+}
+
+void switch_back_from_ktask(uintptr_t old_ret)
+{
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+       struct kthread *kth = pcpui->cur_kthread;
+
+       if (old_ret)
+               kth->flags &= ~KTH_IS_KTASK;
+}