2d0f2c598352fe0dd36645fe1d0edf28f76eeb1f
[akaros.git] / kern / src / kthread.c
1 /* Copyright (c) 2010 The Regents of the University of California
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * Kernel threading.  These are for blocking within the kernel for whatever
6  * reason, usually during blocking IO operations. */
7
8 #include <kthread.h>
9 #include <slab.h>
10 #include <page_alloc.h>
11 #include <pmap.h>
12 #include <smp.h>
13
14 struct kmem_cache *kthread_kcache;
15
16 void kthread_init(void)
17 {
18         kthread_kcache = kmem_cache_create("kthread", sizeof(struct kthread),
19                                            __alignof__(struct kthread), 0, 0, 0);
20 }
21
22 /* This downs the semaphore and suspends the current kernel context on its
23  * waitqueue if there are no pending signals.  Note that the case where the
24  * signal is already there is not optimized. */
25 void sleep_on(struct semaphore *sem)
26 {
27         volatile bool blocking = TRUE;  /* signal to short circuit when restarting*/
28         struct kthread *kthread;
29         struct page *page;                              /* assumption here that stacks are PGSIZE */
30         register uintptr_t new_stacktop;
31         int8_t irq_state = 0;
32         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
33
34         /* interrups would be messy here */
35         disable_irqsave(&irq_state);
36         /* Try to get the spare first.  If there is one, we'll use it (o/w, we'll
37          * get a fresh kthread.  Why we need this is more clear when we try to
38          * restart kthreads.  Having them also ought to cut down on contention.
39          * Note we do this with interrupts disabled (which protects us from
40          * concurrent modifications). */
41         if (pcpui->spare) {
42                 kthread = pcpui->spare;
43                 /* we're using the spare, so we use the page the spare held */
44                 new_stacktop = kthread->stacktop;
45                 pcpui->spare = 0;
46         } else {
47                 kthread = kmem_cache_alloc(kthread_kcache, 0);
48                 assert(kthread);
49                 assert(!kpage_alloc(&page));    /* decref'd when the kthread is freed */
50                 new_stacktop = (uintptr_t)page2kva(page) + PGSIZE;
51         }
52         /* This is the stacktop we are currently on and wish to save */
53         kthread->stacktop = get_stack_top();
54         /* Set the core's new default stack */
55         set_stack_top(new_stacktop);
56         /* The kthread needs to stay in the process context (if there is one), but
57          * we want the core (which could be a vcore) to stay in the context too.  If
58          * we want to leave, we'll need to do that in smp_idle() or elsewhere in the
59          * code. */
60         kthread->proc = current;
61         kthread->proc_tf = current_tf;
62         if (kthread->proc)
63                 kref_get(&kthread->proc->kref, 1);
64         /* Save the context, toggle blocking for the reactivation */
65         save_kernel_tf(&kthread->context);
66         if (!blocking)
67                 goto block_return_path;
68         blocking = FALSE;                                       /* for when it starts back up */
69         /* Down the semaphore.  We need this to be inline.  If we're sleeping, once
70          * we unlock the kthread could be started up again and can return and start
71          * trashing this function's stack, hence the weird control flow. */
72         spin_lock(&sem->lock);  /* no need for irqsave, since we disabled ints */
73         if (sem->nr_signals-- <= 0)
74                 TAILQ_INSERT_TAIL(&sem->waiters, kthread, link);
75         else                                                            /* we didn't sleep */
76                 goto unwind_sleep_prep;
77         spin_unlock(&sem->lock);
78         /* Switch to the core's default stack.  After this, don't use local
79          * variables.  TODO: we shouldn't be using new_stacktop either, can't always
80          * trust the register keyword (AFAIK). */
81         set_stack_pointer(new_stacktop);
82         smp_idle();
83         /* smp_idle never returns */
84         assert(0);
85 unwind_sleep_prep:
86         /* We get here if we should not sleep on sem (the signal beat the sleep).
87          * Note we are not optimizing for cases where the signal won. */
88         spin_unlock(&sem->lock);
89         printk("Didn't sleep, unwinding...\n");
90         /* Restore the core's current and default stacktop */
91         current = kthread->proc;                        /* arguably unnecessary */
92         if (kthread->proc)
93                 kref_put(&kthread->proc->kref);
94         set_stack_top(kthread->stacktop);
95         /* Save the allocs as the spare */
96         assert(!pcpui->spare);
97         pcpui->spare = kthread;
98         /* save the "freshly alloc'd" stack/page, not the one we came in on */
99         kthread->stacktop = new_stacktop;
100 block_return_path:
101         printd("Returning from being 'blocked'!\n");
102         enable_irqsave(&irq_state);
103         return;
104 }
105
106 /* Starts kthread on the calling core.  This does not return, and will handle
107  * the details of cleaning up whatever is currently running (freeing its stack,
108  * etc). */
109 void restart_kthread(struct kthread *kthread)
110 {
111         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
112         uintptr_t current_stacktop;
113         /* Avoid messy complications.  The kthread will enable_irqsave() when it
114          * comes back up. */
115         disable_irq();
116         /* Free any spare, since we need ours to become the spare (since we can't
117          * free our current kthread *before* popping it, nor can we free the current
118          * stack until we pop to the kthread's stack). */
119         if (pcpui->spare) {
120                 page_decref(kva2page((void*)kthread->stacktop - PGSIZE));
121                 kmem_cache_free(kthread_kcache, pcpui->spare);
122         }
123         current_stacktop = get_stack_top();
124         /* When a kthread runs, its stack is the default kernel stack */
125         set_stack_top(kthread->stacktop);
126         /* Set the spare stuff (current kthread, current (not kthread) stacktop) */
127         pcpui->spare = kthread;
128         kthread->stacktop = current_stacktop;
129         /* We need to load the new cr3 if we want another new (or no) process */
130         if (current != kthread->proc) {
131                 if (kthread->proc)
132                         lcr3(kthread->proc->env_cr3);
133                 else
134                         lcr3(boot_cr3);
135         }
136         /* If there's a proc current already, we'll lose that ref no matter what. */
137         if (current)
138                 kref_put(&current->kref);
139         current = kthread->proc;
140         /* Finally, restart our thread */
141         pop_kernel_tf(&kthread->context);
142 }