Sleep on block requests using kthreads
[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 #include <schedule.h>
14
15 struct kmem_cache *kthread_kcache;
16
17 void kthread_init(void)
18 {
19         kthread_kcache = kmem_cache_create("kthread", sizeof(struct kthread),
20                                            __alignof__(struct kthread), 0, 0, 0);
21 }
22
23 /* This downs the semaphore and suspends the current kernel context on its
24  * waitqueue if there are no pending signals.  Note that the case where the
25  * signal is already there is not optimized. */
26 void sleep_on(struct semaphore *sem)
27 {
28         volatile bool blocking = TRUE;  /* signal to short circuit when restarting*/
29         struct kthread *kthread;
30         struct page *page;                              /* assumption here that stacks are PGSIZE */
31         register uintptr_t new_stacktop;
32         int8_t irq_state = 0;
33         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
34
35         /* interrups would be messy here */
36         disable_irqsave(&irq_state);
37         /* Try to get the spare first.  If there is one, we'll use it (o/w, we'll
38          * get a fresh kthread.  Why we need this is more clear when we try to
39          * restart kthreads.  Having them also ought to cut down on contention.
40          * Note we do this with interrupts disabled (which protects us from
41          * concurrent modifications). */
42         if (pcpui->spare) {
43                 kthread = pcpui->spare;
44                 /* we're using the spare, so we use the page the spare held */
45                 new_stacktop = kthread->stacktop;
46                 pcpui->spare = 0;
47         } else {
48                 kthread = kmem_cache_alloc(kthread_kcache, 0);
49                 assert(kthread);
50                 assert(!kpage_alloc(&page));    /* decref'd when the kthread is freed */
51                 new_stacktop = (uintptr_t)page2kva(page) + PGSIZE;
52         }
53         /* This is the stacktop we are currently on and wish to save */
54         kthread->stacktop = get_stack_top();
55         /* Set the core's new default stack */
56         set_stack_top(new_stacktop);
57         /* The kthread needs to stay in the process context (if there is one), but
58          * we want the core (which could be a vcore) to stay in the context too.  If
59          * we want to leave, we'll need to do that in smp_idle() or elsewhere in the
60          * code. */
61         kthread->proc = current;
62         kthread->proc_tf = current_tf;
63         if (kthread->proc)
64                 kref_get(&kthread->proc->kref, 1);
65         /* Save the context, toggle blocking for the reactivation */
66         save_kernel_tf(&kthread->context);
67         if (!blocking)
68                 goto block_return_path;
69         blocking = FALSE;                                       /* for when it starts back up */
70         /* Down the semaphore.  We need this to be inline.  If we're sleeping, once
71          * we unlock the kthread could be started up again and can return and start
72          * trashing this function's stack, hence the weird control flow. */
73         spin_lock(&sem->lock);  /* no need for irqsave, since we disabled ints */
74         if (sem->nr_signals-- <= 0)
75                 TAILQ_INSERT_TAIL(&sem->waiters, kthread, link);
76         else                                                            /* we didn't sleep */
77                 goto unwind_sleep_prep;
78         spin_unlock(&sem->lock);
79         /* Switch to the core's default stack.  After this, don't use local
80          * variables.  TODO: we shouldn't be using new_stacktop either, can't always
81          * trust the register keyword (AFAIK). */
82         set_stack_pointer(new_stacktop);
83         smp_idle();
84         /* smp_idle never returns */
85         assert(0);
86 unwind_sleep_prep:
87         /* We get here if we should not sleep on sem (the signal beat the sleep).
88          * Note we are not optimizing for cases where the signal won. */
89         spin_unlock(&sem->lock);
90         printd("Didn't sleep, unwinding...\n");
91         /* Restore the core's current and default stacktop */
92         current = kthread->proc;                        /* arguably unnecessary */
93         if (kthread->proc)
94                 kref_put(&kthread->proc->kref);
95         set_stack_top(kthread->stacktop);
96         /* Save the allocs as the spare */
97         assert(!pcpui->spare);
98         pcpui->spare = kthread;
99         /* save the "freshly alloc'd" stack/page, not the one we came in on */
100         kthread->stacktop = new_stacktop;
101 block_return_path:
102         printd("Returning from being 'blocked'!\n");
103         enable_irqsave(&irq_state);
104         return;
105 }
106
107 /* Starts kthread on the calling core.  This does not return, and will handle
108  * the details of cleaning up whatever is currently running (freeing its stack,
109  * etc). */
110 void restart_kthread(struct kthread *kthread)
111 {
112         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
113         uintptr_t current_stacktop;
114         /* Avoid messy complications.  The kthread will enable_irqsave() when it
115          * comes back up. */
116         disable_irq();
117         /* Free any spare, since we need ours to become the spare (since we can't
118          * free our current kthread *before* popping it, nor can we free the current
119          * stack until we pop to the kthread's stack). */
120         if (pcpui->spare) {
121                 page_decref(kva2page((void*)kthread->stacktop - PGSIZE));
122                 kmem_cache_free(kthread_kcache, pcpui->spare);
123         }
124         current_stacktop = get_stack_top();
125         /* When a kthread runs, its stack is the default kernel stack */
126         set_stack_top(kthread->stacktop);
127         /* Set the spare stuff (current kthread, current (not kthread) stacktop) */
128         pcpui->spare = kthread;
129         kthread->stacktop = current_stacktop;
130         /* We need to load the new cr3 if we want another new (or no) process */
131         if (current != kthread->proc) {
132                 if (kthread->proc)
133                         lcr3(kthread->proc->env_cr3);
134                 else
135                         lcr3(boot_cr3);
136         }
137         /* If there's a proc current already, we'll lose that ref no matter what. */
138         if (current)
139                 kref_put(&current->kref);
140         current = kthread->proc;
141         if (kthread->proc_tf)
142                 set_current_tf(kthread->proc_tf);
143         /* Finally, restart our thread */
144         pop_kernel_tf(&kthread->context);
145 }
146
147 /* Kmsg handler to launch/run a kthread.  This must be a routine message, since
148  * it does not return.  Furthermore, like all routine kmsgs that don't return,
149  * this needs to handle the fact that it won't return to the given TF (which is
150  * a proc's TF, since this was routine). */
151 void __launch_kthread(struct trapframe *tf, uint32_t srcid, void *a0, void *a1,
152                           void *a2)
153 {
154         struct kthread *kthread = (struct kthread*)a0;
155         struct proc *cur_proc = current;
156         /* If there is no proc running, don't worry about not returning. */
157         if (cur_proc) {
158                 if (cur_proc != kthread->proc) {
159                         /* we're running the kthread from a different proc.  For now, we
160                          * can't be _M, since that would be taking away someone's vcore to
161                          * process another process's work. */
162                         assert(cur_proc->state == PROC_RUNNING_S);
163                         spin_lock(&cur_proc->proc_lock);
164                         /* Wrap up / yield the current _S proc, which calls schedule_proc */
165                         __proc_yield_s(cur_proc, tf);
166                         spin_unlock(&cur_proc->proc_lock);
167                 } else {
168                         assert(cur_proc->state == PROC_RUNNING_M);
169                         /* TODO: might need to do something here, though it will depend on
170                          * how we handle async local calls. */
171                 }
172
173         }
174         restart_kthread(kthread);
175         assert(0);
176 }