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 }