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
39struct 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
48kmutex_t nsc_proc_lock;
49kcondvar_t nsc_proc_cv;
50
51static struct nsc_nlwp *nsc_nlwp_top;
52
53void
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
61void
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
79int
80nsc_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 */
156void
157nsc_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
203int
204nsc_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
291void
292nsc_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 */
319void
320nsc_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