1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <sys/types.h>
27 #include <sys/ksynch.h>
28 #include <sys/errno.h>
29 #include <sys/cmn_err.h>
30 #include <sys/conf.h>
31 #include <sys/kmem.h>
32 #include <sys/ddi.h>
33
34 #define __NSC_GEN__
35 #include <sys/nsctl/nsc_rmspin.h>
36 #include "../nsctl.h"
37 #include "nskernd.h"
38
39 struct nsc_nlwp {
40 struct nsc_nlwp *next;
41 void (*fn)(void *);
42 void *arg;
43 volatile int ready;
44 int errno;
45 kcondvar_t child_cv;
46 };
47
48 kmutex_t nsc_proc_lock;
49 kcondvar_t nsc_proc_cv;
50
51 static struct nsc_nlwp *nsc_nlwp_top;
52
53 void
_nsc_start_proc(void)54 _nsc_start_proc(void)
55 {
56 mutex_init(&nsc_proc_lock, NULL, MUTEX_DRIVER, NULL);
57 cv_init(&nsc_proc_cv, NULL, CV_DRIVER, NULL);
58 }
59
60
61 void
_nsc_stop_proc(void)62 _nsc_stop_proc(void)
63 {
64 mutex_destroy(&nsc_proc_lock);
65 cv_destroy(&nsc_proc_cv);
66 }
67
68
69 /*
70 * Create a daemon (server) proc.
71 *
72 * If 'rt' is TRUE, then increase the scheduling priority of the lwp.
73 * Exactly how, if at all, this feature is implemented is at the
74 * discretion of nskernd.
75 *
76 * Returns 0 or errno.
77 */
78
79 int
nsc_create_process(void (* func)(void *),void * arg,boolean_t rt)80 nsc_create_process(void (*func)(void *), void *arg, boolean_t rt)
81 {
82 struct nsc_nlwp *nlwp, **nlwpp;
83 struct nskernd *nsk = NULL;
84 int rc = 0;
85
86 nlwp = kmem_zalloc(sizeof (*nlwp), KM_NOSLEEP);
87 nsk = kmem_zalloc(sizeof (*nsk), KM_NOSLEEP);
88 if (!nlwp || !nsk) {
89 if (nlwp) {
90 kmem_free(nlwp, sizeof (*nlwp));
91 }
92 if (nsk) {
93 kmem_free(nsk, sizeof (*nsk));
94 }
95 return (ENOMEM);
96 }
97
98 nlwp->fn = func;
99 nlwp->arg = arg;
100
101 mutex_enter(&nsc_proc_lock);
102
103 nlwp->next = nsc_nlwp_top;
104 nsc_nlwp_top = nlwp;
105
106 mutex_exit(&nsc_proc_lock);
107
108 nsk->command = NSKERND_NEWLWP;
109 nsk->data1 = (uint64_t)(unsigned long)nlwp;
110 nsk->data2 = (uint64_t)rt;
111
112 rc = nskernd_get(nsk);
113
114 /* user level returns error in nsk->data1 */
115 if (!rc && nsk->data1)
116 rc = nsk->data1;
117
118 mutex_enter(&nsc_proc_lock);
119
120 if (!rc) {
121 /*
122 * wait for the child to start and check in.
123 */
124
125 while (! nlwp->ready) {
126 cv_wait(&nsc_proc_cv, &nsc_proc_lock);
127 }
128 }
129
130 /*
131 * remove from list of outstanding requests.
132 */
133
134 for (nlwpp = &nsc_nlwp_top; (*nlwpp); nlwpp = &((*nlwpp)->next)) {
135 if (*nlwpp == nlwp) {
136 *nlwpp = nlwp->next;
137 break;
138 }
139 }
140
141 mutex_exit(&nsc_proc_lock);
142
143 kmem_free(nlwp, sizeof (*nlwp));
144 kmem_free(nsk, sizeof (*nsk));
145 return (rc);
146 }
147
148
149 /*
150 * Child lwp calls this function when it returns to the kernel.
151 *
152 * Check if the args are still on the pending list. If they are, then
153 * run the required function. If they are not, then something went
154 * wrong, so just return back to userland and die.
155 */
156 void
nsc_runlwp(uint64_t arg)157 nsc_runlwp(uint64_t arg)
158 {
159 struct nsc_nlwp *nlwp;
160 void (*fn)(void *);
161 void *fn_arg;
162
163 fn_arg = NULL;
164 fn = NULL;
165
166 mutex_enter(&nsc_proc_lock);
167
168 /*
169 * check that the request is still on the list of work to do
170 */
171
172 for (nlwp = nsc_nlwp_top; nlwp; nlwp = nlwp->next) {
173 if (nlwp == (struct nsc_nlwp *)(unsigned long)arg) {
174 fn_arg = nlwp->arg;
175 fn = nlwp->fn;
176
177 /* mark as ready */
178 nlwp->ready = 1;
179 cv_broadcast(&nsc_proc_cv);
180
181 break;
182 }
183 }
184
185 mutex_exit(&nsc_proc_lock);
186
187 if (fn) {
188 (*fn)(fn_arg);
189 }
190 }
191
192
193 /*
194 * Create a thread that acquires an inter-node lock.
195 *
196 * mode - 0 (read), 1 (write).
197 * lockp - used to return the opaque address of a sync structure, which
198 * must be passed to nsc_do_unlock() later.
199 *
200 * Returns 0 or errno.
201 */
202
203 int
nsc_do_lock(int mode,void ** lockp)204 nsc_do_lock(int mode, void **lockp)
205 {
206 struct nsc_nlwp *nlwp = NULL, **nlwpp;
207 struct nskernd *nsk = NULL;
208 int rc = 0;
209
210 nlwp = kmem_zalloc(sizeof (*nlwp), KM_NOSLEEP);
211 nsk = kmem_zalloc(sizeof (*nsk), KM_NOSLEEP);
212 if (!nlwp || !nsk) {
213 if (nlwp) {
214 kmem_free(nlwp, sizeof (*nlwp));
215 }
216 if (nsk) {
217 kmem_free(nsk, sizeof (*nsk));
218 }
219 return (ENOMEM);
220 }
221
222 cv_init(&nlwp->child_cv, NULL, CV_DRIVER, NULL);
223
224 mutex_enter(&nsc_proc_lock);
225
226 nlwp->next = nsc_nlwp_top;
227 nsc_nlwp_top = nlwp;
228
229 mutex_exit(&nsc_proc_lock);
230
231 nsk->command = NSKERND_LOCK;
232 nsk->data1 = (uint64_t)(unsigned long)nlwp;
233 nsk->data2 = (uint64_t)mode;
234
235 rc = nskernd_get(nsk);
236
237 /* user level returns error in nsk->data1 */
238 if (!rc && nsk->data1)
239 rc = nsk->data1;
240
241 mutex_enter(&nsc_proc_lock);
242
243 if (!rc) {
244 /*
245 * wait for the child to start and check in.
246 */
247
248 while (! nlwp->ready) {
249 cv_wait(&nsc_proc_cv, &nsc_proc_lock);
250 }
251
252 /* retrieve errno from child's lock operation */
253 rc = (int)nlwp->errno;
254 }
255
256 if (rc) {
257 /*
258 * error - remove from list of outstanding requests as
259 * child will not be checking in (nskernd_get() failed
260 * or user thread create failed) or will not be waiting
261 * (child thread lock failure).
262 */
263
264 for (nlwpp = &nsc_nlwp_top; (*nlwpp);
265 nlwpp = &((*nlwpp)->next)) {
266 if (*nlwpp == nlwp) {
267 *nlwpp = nlwp->next;
268 break;
269 }
270 }
271
272 mutex_exit(&nsc_proc_lock);
273
274 cv_destroy(&nlwp->child_cv);
275 kmem_free(nlwp, sizeof (*nlwp));
276 kmem_free(nsk, sizeof (*nsk));
277 *lockp = NULL;
278 return (rc);
279 }
280
281 /* success, return argument for nsc_do_unlock() */
282
283 mutex_exit(&nsc_proc_lock);
284
285 kmem_free(nsk, sizeof (*nsk));
286 *lockp = nlwp;
287 return (0);
288 }
289
290
291 void
nsc_do_unlock(void * arg)292 nsc_do_unlock(void *arg)
293 {
294 struct nsc_nlwp *nlwp;
295
296 /* find child on work list */
297
298 mutex_enter(&nsc_proc_lock);
299
300 for (nlwp = nsc_nlwp_top; nlwp; nlwp = nlwp->next) {
301 if (nlwp == (struct nsc_nlwp *)arg) {
302 /* signal unlock */
303 nlwp->ready = 0;
304 cv_broadcast(&nlwp->child_cv);
305 }
306 }
307
308 mutex_exit(&nsc_proc_lock);
309 }
310
311
312 /*
313 * Lock child thread calls this function when it returns to the kernel.
314 *
315 * Check if the args are still on the pending list. If they are, then
316 * post the lock results and wait for the unlock. If they are not,
317 * then something went wrong, so just return back to userland and die.
318 */
319 void
nsc_lockchild(uint64_t arg,uint64_t errno)320 nsc_lockchild(uint64_t arg, uint64_t errno)
321 {
322 struct nsc_nlwp *nlwp, **nlwpp;
323
324 if (!arg) {
325 return;
326 }
327
328 mutex_enter(&nsc_proc_lock);
329
330 /*
331 * check that the request is still on the list of work to do
332 */
333
334 for (nlwp = nsc_nlwp_top; nlwp; nlwp = nlwp->next) {
335 if (nlwp == (struct nsc_nlwp *)(unsigned long)arg) {
336 /* mark as ready */
337 nlwp->errno = (int)errno;
338 nlwp->ready = 1;
339 cv_broadcast(&nsc_proc_cv);
340 break;
341 }
342 }
343
344 if (!nlwp || errno) {
345 /*
346 * Error - either this request is no longer on the work
347 * queue, or there was an error in the userland lock code
348 * in which case the lock caller (currently blocked in
349 * nsc_do_lock() will do the cleanup.
350 */
351 mutex_exit(&nsc_proc_lock);
352 return;
353 }
354
355 /*
356 * no errors, so wait for an unlock
357 */
358
359 while (nlwp->ready) {
360 cv_wait(&nlwp->child_cv, &nsc_proc_lock);
361 }
362
363 /*
364 * remove self from list of outstanding requests.
365 */
366
367 for (nlwpp = &nsc_nlwp_top; (*nlwpp); nlwpp = &((*nlwpp)->next)) {
368 if (*nlwpp == nlwp) {
369 *nlwpp = nlwp->next;
370 break;
371 }
372 }
373
374 /*
375 * cleanup
376 */
377
378 cv_destroy(&nlwp->child_cv);
379 kmem_free(nlwp, sizeof (*nlwp));
380
381 mutex_exit(&nsc_proc_lock);
382 }
383