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 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 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 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 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 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 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 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 }