xref: /titanic_44/usr/src/uts/common/avs/ns/solaris/nsc_proc.c (revision fcf3ce441efd61da9bb2884968af01cb7c1452cc)
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