threads.c revision 1.22 1 /* $NetBSD: threads.c,v 1.22 2014/03/15 15:15:27 pooka Exp $ */
2
3 /*
4 * Copyright (c) 2007-2009 Antti Kantee. All Rights Reserved.
5 *
6 * Development of this software was supported by
7 * The Finnish Cultural Foundation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
19 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: threads.c,v 1.22 2014/03/15 15:15:27 pooka Exp $");
33
34 #include <sys/param.h>
35 #include <sys/atomic.h>
36 #include <sys/kmem.h>
37 #include <sys/kthread.h>
38 #include <sys/malloc.h>
39 #include <sys/systm.h>
40
41 #include <rump/rumpuser.h>
42
43 #include "rump_private.h"
44
45 struct kthdesc {
46 void (*f)(void *);
47 void *arg;
48 struct lwp *mylwp;
49 };
50
51 static bool threads_are_go;
52 static struct rumpuser_mtx *thrmtx;
53 static struct rumpuser_cv *thrcv;
54
55 static void *
56 threadbouncer(void *arg)
57 {
58 struct kthdesc *k = arg;
59 struct lwp *l = k->mylwp;
60 void (*f)(void *);
61 void *thrarg;
62
63 f = k->f;
64 thrarg = k->arg;
65
66 /* don't allow threads to run before all CPUs have fully attached */
67 if (!threads_are_go) {
68 rumpuser_mutex_enter_nowrap(thrmtx);
69 while (!threads_are_go) {
70 rumpuser_cv_wait_nowrap(thrcv, thrmtx);
71 }
72 rumpuser_mutex_exit(thrmtx);
73 }
74
75 /* schedule ourselves */
76 rump_lwproc_curlwp_set(l);
77 rump_schedule();
78
79 /* free dance struct */
80 free(k, M_TEMP);
81
82 if ((curlwp->l_pflag & LP_MPSAFE) == 0)
83 KERNEL_LOCK(1, NULL);
84
85 f(thrarg);
86
87 panic("unreachable, should kthread_exit()");
88 }
89
90 void
91 rump_thread_init(void)
92 {
93
94 rumpuser_mutex_init(&thrmtx, RUMPUSER_MTX_SPIN);
95 rumpuser_cv_init(&thrcv);
96 }
97
98 void
99 rump_thread_allow(void)
100 {
101
102 rumpuser_mutex_enter(thrmtx);
103 threads_are_go = true;
104 rumpuser_cv_broadcast(thrcv);
105 rumpuser_mutex_exit(thrmtx);
106
107 }
108
109 static struct {
110 const char *t_name;
111 bool t_ncmp;
112 } nothreads[] = {
113 { "vrele", false },
114 { "vdrain", false },
115 { "cachegc", false },
116 { "nfssilly", false },
117 { "unpgc", false },
118 { "pmf", true },
119 { "xcall", true },
120 };
121
122 int
123 kthread_create(pri_t pri, int flags, struct cpu_info *ci,
124 void (*func)(void *), void *arg, lwp_t **newlp, const char *fmt, ...)
125 {
126 char thrstore[MAXCOMLEN];
127 const char *thrname = NULL;
128 va_list ap;
129 struct kthdesc *k;
130 struct lwp *l;
131 int rv;
132
133 thrstore[0] = '\0';
134 if (fmt) {
135 va_start(ap, fmt);
136 vsnprintf(thrstore, sizeof(thrstore), fmt, ap);
137 va_end(ap);
138 thrname = thrstore;
139 }
140
141 /*
142 * We don't want a module unload thread.
143 * (XXX: yes, this is a kludge too, and the kernel should
144 * have a more flexible method for configuring which threads
145 * we want).
146 */
147 if (strcmp(thrstore, "modunload") == 0) {
148 return 0;
149 }
150
151 if (!rump_threads) {
152 bool matched;
153 int i;
154
155 /* do we want to fake it? */
156 for (i = 0; i < __arraycount(nothreads); i++) {
157 if (nothreads[i].t_ncmp) {
158 matched = strncmp(thrstore, nothreads[i].t_name,
159 strlen(nothreads[i].t_name)) == 0;
160 } else {
161 matched = strcmp(thrstore,
162 nothreads[i].t_name) == 0;
163 }
164 if (matched) {
165 aprint_error("rump kernel threads not enabled, "
166 "%s not functional\n", nothreads[i].t_name);
167 return 0;
168 }
169 }
170 panic("threads not available");
171 }
172 KASSERT(fmt != NULL);
173
174 k = malloc(sizeof(*k), M_TEMP, M_WAITOK);
175 k->f = func;
176 k->arg = arg;
177 k->mylwp = l = rump__lwproc_alloclwp(&proc0);
178 l->l_flag |= LW_SYSTEM;
179 if (flags & KTHREAD_MPSAFE)
180 l->l_pflag |= LP_MPSAFE;
181 if (flags & KTHREAD_INTR)
182 l->l_pflag |= LP_INTR;
183 if (ci) {
184 l->l_pflag |= LP_BOUND;
185 l->l_target_cpu = ci;
186 }
187 if (thrname) {
188 l->l_name = kmem_alloc(MAXCOMLEN, KM_SLEEP);
189 strlcpy(l->l_name, thrname, MAXCOMLEN);
190 }
191
192 rv = rumpuser_thread_create(threadbouncer, k, thrname,
193 (flags & KTHREAD_MUSTJOIN) == KTHREAD_MUSTJOIN,
194 pri, ci ? ci->ci_index : -1, &l->l_ctxlink);
195 if (rv)
196 return rv;
197
198 if (newlp) {
199 *newlp = l;
200 } else {
201 KASSERT((flags & KTHREAD_MUSTJOIN) == 0);
202 }
203
204 return 0;
205 }
206
207 void
208 kthread_exit(int ecode)
209 {
210
211 if ((curlwp->l_pflag & LP_MPSAFE) == 0)
212 KERNEL_UNLOCK_LAST(NULL);
213 rump_lwproc_releaselwp();
214 /* unschedule includes membar */
215 rump_unschedule();
216 rumpuser_thread_exit();
217 }
218
219 int
220 kthread_join(struct lwp *l)
221 {
222 int rv;
223
224 KASSERT(l->l_ctxlink != NULL);
225 rv = rumpuser_thread_join(l->l_ctxlink);
226 membar_consumer();
227
228 return rv;
229 }
230