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 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <sys/types.h>
  27 #include <sys/debug.h>
  28 #include <sys/ksynch.h>
  29 #include <sys/cmn_err.h>
  30 #include <sys/kmem.h>
  31 #include <sys/ddi.h>
  32 #include <sys/errno.h>
  33 #include "nsc_thread.h"
  34 
  35 #ifdef DS_DDICT
  36 #include "../contract.h"
  37 #endif
  38 
  39 #include "../nsctl.h"
  40 #include "nskernd.h"
  41 #include <sys/nsctl/nsctl.h>
  42 
  43 #include <sys/sdt.h>              /* dtrace is S10 or later */
  44 
  45 
  46 /*
  47  * Global data
  48  */
  49 static nstset_t         *nst_sets;
  50 static nsthread_t       *nst_pending;
  51 static kmutex_t         nst_global_lock;        /* nst_sets, nst_pending */
  52 
  53 
  54 /*
  55  * nst_kmem_xalloc
  56  *
  57  * Poll for memory.
  58  */
  59 static void *
  60 nst_kmem_xalloc(size_t size, int sec, void *(*alloc)(size_t, int))
  61 {
  62         clock_t usec = sec * 1000000;
  63         void *p = NULL;
  64 
  65         while (usec > 0) {
  66                 if ((p = (*alloc)(size, KM_NOSLEEP)) != NULL)
  67                         return (p);
  68 
  69                 delay(drv_usectohz((clock_t)NST_MEMORY_TIMEOUT));
  70                 usec -= NST_MEMORY_TIMEOUT;
  71         }
  72 
  73         cmn_err(CE_WARN, "!nst_kmem_xalloc: failed to alloc %ld bytes", size);
  74         return (NULL);
  75 }
  76 
  77 
  78 #if 0
  79 /* currently unused */
  80 static void *
  81 nst_kmem_alloc(size_t size, int sec)
  82 {
  83         return (nst_kmem_xalloc(size, sec, kmem_alloc));
  84 }
  85 #endif
  86 
  87 
  88 static void *
  89 nst_kmem_zalloc(size_t size, int sec)
  90 {
  91         return (nst_kmem_xalloc(size, sec, kmem_zalloc));
  92 }
  93 
  94 
  95 /*
  96  * Queue stuff that should be in the DDI.
  97  */
  98 
  99 /*
 100  * nst_insque
 101  *
 102  * Insert entryp after predp in a doubly linked list.
 103  */
 104 static void
 105 nst_insque(nst_q_t *entryp, nst_q_t *predp)
 106 {
 107         entryp->q_back = predp;
 108         entryp->q_forw = predp->q_forw;
 109         predp->q_forw = entryp;
 110         entryp->q_forw->q_back = entryp;
 111 }
 112 #ifndef DS_DDICT
 113 #pragma inline(nst_insque)      /* compiler hint to inline this function */
 114 #endif
 115 
 116 
 117 /*
 118  * nst_remque
 119  *
 120  * Remove entryp from a doubly linked list.
 121  */
 122 static void
 123 nst_remque(nst_q_t *entryp)
 124 {
 125         entryp->q_back->q_forw = entryp->q_forw;
 126         entryp->q_forw->q_back = entryp->q_back;
 127         entryp->q_forw = entryp->q_back = NULL;
 128 }
 129 #ifndef DS_DDICT
 130 #pragma inline(nst_remque)      /* compiler hint to inline this function */
 131 #endif
 132 
 133 
 134 /*
 135  * nst_thread_init
 136  *
 137  * Initialise the dynamic part of a thread
 138  */
 139 static void
 140 nst_thread_init(nsthread_t *tp)
 141 {
 142         ASSERT(MUTEX_HELD(&((tp->tp_set)->set_lock)));
 143         ASSERT(!(tp->tp_flag & NST_TF_INUSE));
 144         tp->tp_flag = NST_TF_INUSE;
 145         tp->tp_func = NULL;
 146         tp->tp_arg = NULL;
 147 }
 148 #ifndef DS_DDICT
 149 #pragma inline(nst_thread_init) /* compiler hint to inline this function */
 150 #endif
 151 
 152 
 153 /*
 154  * nst_thread_alloc
 155  *
 156  * Return an nsthread from the free pool, NULL if none
 157  */
 158 static nsthread_t *
 159 nst_thread_alloc(nstset_t *set, const int sleep)
 160 {
 161         nsthread_t *tp = NULL;
 162 
 163         mutex_enter(&set->set_lock);
 164 
 165         if (set->set_flag & NST_SF_KILL) {
 166                 mutex_exit(&set->set_lock);
 167                 DTRACE_PROBE1(nst_thread_alloc_err_kill, nstset_t *, set);
 168                 return (NULL);
 169         }
 170 
 171         do {
 172                 tp = (nsthread_t *)set->set_free.q_forw;
 173                 if (tp != (nsthread_t *)&set->set_free)
 174                         nst_remque(&tp->tp_link);
 175                 else {
 176                         tp = NULL;
 177 
 178                         if (!sleep)
 179                                 break;
 180 
 181                         set->set_res_cnt++;
 182 
 183                         DTRACE_PROBE2(nst_thread_alloc_sleep, nstset_t *, set,
 184                             int, set->set_res_cnt);
 185 
 186                         cv_wait(&set->set_res_cv, &set->set_lock);
 187 
 188                         DTRACE_PROBE1(nst_thread_alloc_wake, nstset_t *, set);
 189 
 190                         set->set_res_cnt--;
 191 
 192                         if (set->set_flag & NST_SF_KILL)
 193                                 break;
 194                 }
 195         } while (tp == NULL);
 196 
 197         /* initialise the thread */
 198 
 199         if (tp != NULL) {
 200                 nst_thread_init(tp);
 201                 set->set_nlive++;
 202         }
 203 
 204         mutex_exit(&set->set_lock);
 205 
 206         return (tp);
 207 }
 208 
 209 
 210 /*
 211  * nst_thread_free
 212  *
 213  * Requeue a thread on the free or reuse pools.  Threads are always
 214  * queued to the tail of the list to prevent rapid recycling.
 215  *
 216  * Must be called with set->set_lock held.
 217  */
 218 static void
 219 nst_thread_free(nsthread_t *tp)
 220 {
 221         nstset_t *set = tp->tp_set;
 222 
 223         if (!set)
 224                 return;
 225 
 226         ASSERT(MUTEX_HELD(&set->set_lock));
 227 
 228         tp->tp_flag &= ~NST_TF_INUSE;
 229         if (tp->tp_flag & NST_TF_DESTROY) {
 230                 /* add self to reuse pool */
 231                 nst_insque(&tp->tp_link, set->set_reuse.q_back);
 232         } else {
 233                 /* add self to free pool */
 234                 nst_insque(&tp->tp_link, set->set_free.q_back);
 235                 if (set->set_res_cnt > 0)
 236                         cv_broadcast(&set->set_res_cv);
 237         }
 238 }
 239 
 240 
 241 /*
 242  * nst_thread_run
 243  *
 244  * The first function that a new thread runs on entry from user land.
 245  * This is the main thread function that handles thread work and death.
 246  */
 247 static void
 248 nst_thread_run(void *arg)
 249 {
 250         nsthread_t *tp;
 251         nstset_t *set;
 252         int first = 1;
 253 
 254         mutex_enter(&nst_global_lock);
 255 
 256         /* check if this thread is still on the pending list */
 257 
 258         for (tp = nst_pending; tp; tp = tp->tp_chain) {
 259                 if (tp == (nsthread_t *)arg) {
 260                         break;
 261                 }
 262         }
 263 
 264         if (!tp) {
 265                 mutex_exit(&nst_global_lock);
 266                 return;
 267         }
 268 
 269         if (!tp->tp_set) {
 270                 mutex_exit(&nst_global_lock);
 271 #ifdef DEBUG
 272                 cmn_err(CE_WARN, "!nst_thread_run(%p): already dead?",
 273                     (void *)tp);
 274 #endif
 275                 return;
 276         }
 277 
 278         /* check that the set is still on the list of sets */
 279 
 280         for (set = nst_sets; set; set = set->set_next) {
 281                 if (set == tp->tp_set) {
 282                         break;
 283                 }
 284         }
 285 
 286         if (!set) {
 287                 mutex_exit(&nst_global_lock);
 288 #ifdef DEBUG
 289                 cmn_err(CE_WARN, "!nst_thread_run(%p): no set?", (void *)tp);
 290 #endif
 291                 return;
 292         }
 293 
 294         mutex_enter(&set->set_lock);
 295 
 296         mutex_exit(&nst_global_lock);
 297 
 298         /*
 299          * Mark the parent.
 300          * The parent won't actually run until set->set_lock is dropped.
 301          */
 302 
 303         tp->tp_flag &= ~NST_TF_PENDING;
 304         cv_broadcast(&tp->tp_cv);
 305 
 306         /*
 307          * Main loop.
 308          */
 309 
 310         while (!(set->set_flag & NST_SF_KILL) &&
 311             !(tp->tp_flag & NST_TF_KILL)) {
 312                 /*
 313                  * On initial entry the caller will add this thread to
 314                  * the free pool if required, there after the thread
 315                  * must do it for itself.
 316                  */
 317 
 318                 if (first) {
 319                         first = 0;
 320                 } else {
 321                         nst_thread_free(tp);
 322                         set->set_nlive--;
 323                 }
 324 
 325                 DTRACE_PROBE1(nst_thread_run_sleep, nsthread_t *, tp);
 326 
 327                 cv_wait(&tp->tp_cv, &set->set_lock);
 328 
 329                 DTRACE_PROBE1(nst_thread_run_wake, nsthread_t *, tp);
 330 
 331                 if ((set->set_flag & NST_SF_KILL) ||
 332                     (tp->tp_flag & NST_TF_KILL)) {
 333                         break;
 334                 }
 335 
 336                 mutex_exit(&set->set_lock);
 337 
 338                 if (tp->tp_func) {
 339                         (*tp->tp_func)(tp->tp_arg);
 340                         tp->tp_func = 0;
 341                         tp->tp_arg = 0;
 342                 }
 343 #ifdef DEBUG
 344                 else {
 345                         cmn_err(CE_WARN,
 346                             "!nst_thread_run(%p): NULL function pointer",
 347                             (void *)tp);
 348                 }
 349 #endif
 350 
 351                 mutex_enter(&set->set_lock);
 352         }
 353 
 354         /* remove self from the free and/or reuse pools */
 355         if (tp->tp_link.q_forw != NULL || tp->tp_link.q_back != NULL) {
 356                 ASSERT(tp->tp_link.q_forw != NULL &&
 357                     tp->tp_link.q_back != NULL);
 358                 nst_remque(&tp->tp_link);
 359         }
 360 
 361         set->set_nthread--;
 362         tp->tp_flag &= ~NST_TF_KILL;
 363 
 364         /* wake the context that is running nst_destroy() or nst_del_thread() */
 365         cv_broadcast(&set->set_kill_cv);
 366 
 367         mutex_exit(&set->set_lock);
 368 
 369         /* suicide */
 370 }
 371 
 372 
 373 /*
 374  * nst_thread_destroy
 375  *
 376  * Free up the kernel level resources.  The thread must already be
 377  * un-chained from the set, and the caller must not be the thread
 378  * itself.
 379  */
 380 static void
 381 nst_thread_destroy(nsthread_t *tp)
 382 {
 383         if (!tp)
 384                 return;
 385 
 386         ASSERT(tp->tp_chain == NULL);
 387 
 388         tp->tp_set = NULL;
 389 
 390         if (tp->tp_flag & NST_TF_INUSE) {
 391                 cmn_err(CE_WARN, "!nst_thread_destroy(%p): still in use!",
 392                     (void *)tp);
 393                 /* leak the thread */
 394                 return;
 395         }
 396 
 397         cv_destroy(&tp->tp_cv);
 398         kmem_free(tp, sizeof (*tp));
 399 }
 400 
 401 
 402 /*
 403  * nst_thread_create
 404  *
 405  * Create and return a new thread from a threadset.
 406  */
 407 static nsthread_t *
 408 nst_thread_create(nstset_t *set)
 409 {
 410         nsthread_t *tp, **tpp;
 411         int rc;
 412 
 413         /* try and reuse a thread first */
 414 
 415         if (set->set_reuse.q_forw != &set->set_reuse) {
 416                 mutex_enter(&set->set_lock);
 417 
 418                 tp = (nsthread_t *)set->set_reuse.q_forw;
 419                 if (tp != (nsthread_t *)&set->set_reuse)
 420                         nst_remque(&tp->tp_link);
 421                 else
 422                         tp = NULL;
 423 
 424                 mutex_exit(&set->set_lock);
 425 
 426                 if (tp) {
 427                         DTRACE_PROBE2(nst_thread_create_end, nstset_t *, set,
 428                             nsthread_t *, tp);
 429                         return (tp);
 430                 }
 431         }
 432 
 433         /* create a thread using nskernd */
 434 
 435         tp = nst_kmem_zalloc(sizeof (*tp), 2);
 436         if (!tp) {
 437                 DTRACE_PROBE1(nst_thread_create_err_mem, nstset_t *, set);
 438                 return (NULL);
 439         }
 440 
 441         cv_init(&tp->tp_cv, NULL, CV_DRIVER, NULL);
 442         tp->tp_flag = NST_TF_PENDING;
 443         tp->tp_set = set;
 444 
 445         mutex_enter(&set->set_lock);
 446 
 447         if (set->set_flag & NST_SF_KILL) {
 448                 mutex_exit(&set->set_lock);
 449                 nst_thread_destroy(tp);
 450 #ifdef DEBUG
 451                 cmn_err(CE_WARN, "!nst_thread_create: called during destroy");
 452 #endif
 453                 DTRACE_PROBE2(nst_thread_create_err_kill, nstset_t *, set,
 454                     nsthread_t *, tp);
 455                 return (NULL);
 456         }
 457 
 458         set->set_pending++;
 459 
 460         mutex_exit(&set->set_lock);
 461 
 462         mutex_enter(&nst_global_lock);
 463 
 464         tp->tp_chain = nst_pending;
 465         nst_pending = tp;
 466 
 467         mutex_exit(&nst_global_lock);
 468 
 469         DTRACE_PROBE2(nst_dbg_thr_create_proc_start, nstset_t *, set,
 470             nsthread_t *, tp);
 471 
 472         rc = nsc_create_process(nst_thread_run, tp, 0);
 473 
 474         DTRACE_PROBE2(nst_dbg_thr_create_proc_end, nstset_t *, set,
 475             nsthread_t *, tp);
 476 
 477         if (!rc) {
 478                 /*
 479                  * wait for child to start and check in.
 480                  */
 481 
 482                 mutex_enter(&set->set_lock);
 483 
 484                 while (tp->tp_flag & NST_TF_PENDING)
 485                         cv_wait(&tp->tp_cv, &set->set_lock);
 486 
 487                 mutex_exit(&set->set_lock);
 488         }
 489 
 490         /*
 491          * remove from pending chain.
 492          */
 493 
 494         mutex_enter(&nst_global_lock);
 495 
 496         for (tpp = &nst_pending; (*tpp); tpp = &((*tpp)->tp_chain)) {
 497                 if (*tpp == tp) {
 498                         *tpp = tp->tp_chain;
 499                         tp->tp_chain = NULL;
 500                         break;
 501                 }
 502         }
 503 
 504         mutex_exit(&nst_global_lock);
 505 
 506         /*
 507          * Check for errors and return if required.
 508          */
 509 
 510         mutex_enter(&set->set_lock);
 511 
 512         set->set_pending--;
 513 
 514         if (rc ||
 515             (set->set_flag & NST_SF_KILL) ||
 516             (set->set_nthread + 1) > USHRT_MAX) {
 517                 if (rc == 0) {
 518                         /*
 519                          * Thread is alive, and needs to be woken and killed.
 520                          */
 521                         tp->tp_flag |= NST_TF_KILL;
 522                         cv_broadcast(&tp->tp_cv);
 523 
 524                         while (tp->tp_flag & NST_TF_KILL)
 525                                 cv_wait(&set->set_kill_cv, &set->set_lock);
 526                 }
 527                 mutex_exit(&set->set_lock);
 528 
 529                 nst_thread_destroy(tp);
 530 #ifdef DEBUG
 531                 cmn_err(CE_WARN,
 532                     "!nst_thread_create: error (rc %d, set_flag %x, "
 533                     "set_nthread %d)", rc, set->set_flag, set->set_nthread);
 534 #endif
 535                 DTRACE_PROBE2(nst_thread_create_err_proc, nstset_t *, set,
 536                     nsthread_t *, tp);
 537 
 538                 return (NULL);
 539         }
 540 
 541         /*
 542          * Move into set proper.
 543          */
 544 
 545         tp->tp_chain = set->set_chain;
 546         set->set_chain = tp;
 547         set->set_nthread++;
 548 
 549         mutex_exit(&set->set_lock);
 550 
 551         return (tp);
 552 }
 553 
 554 
 555 /*
 556  * nst_create
 557  *
 558  * Start a new thread from a thread set, returning the
 559  * address of the thread, or NULL on failure.
 560  *
 561  * All threads are created detached.
 562  *
 563  * Valid flag values:
 564  *
 565  *      NST_CREATE      - create a new thread rather than using one
 566  *                        from the threadset.  Once the thread
 567  *                        completes it will not be added to the active
 568  *                        portion of the threadset, but will be cached
 569  *                        on the reuse chain, and so is available for
 570  *                        subsequent NST_CREATE or nst_add_thread()
 571  *                        operations.
 572  *
 573  *      NST_SLEEP       - wait for a thread to be available instead of
 574  *                        returning NULL.  Has no meaning with NST_CREATE.
 575  *
 576  * Returns a pointer to the new thread, or NULL.
 577  */
 578 nsthread_t *
 579 nst_create(nstset_t *set, void (*func)(), blind_t arg, int flags)
 580 {
 581         nsthread_t *tp = NULL;
 582 
 583         if (!set)
 584                 return (NULL);
 585 
 586         if (set->set_flag & NST_SF_KILL) {
 587                 DTRACE_PROBE1(nst_create_err_kill, nstset_t *, set);
 588                 return (NULL);
 589         }
 590 
 591         if (flags & NST_CREATE) {
 592                 /* get new thread */
 593 
 594                 if ((tp = nst_thread_create(set)) == NULL)
 595                         return (NULL);
 596 
 597                 /* initialise the thread */
 598 
 599                 mutex_enter(&set->set_lock);
 600                 nst_thread_init(tp);
 601                 tp->tp_flag |= NST_TF_DESTROY;
 602                 set->set_nlive++;
 603                 mutex_exit(&set->set_lock);
 604         } else {
 605                 if (!(tp = nst_thread_alloc(set, (flags & NST_SLEEP))))
 606                         return (NULL);
 607         }
 608 
 609         /* set thread running */
 610 
 611         tp->tp_func = func;
 612         tp->tp_arg = arg;
 613 
 614         mutex_enter(&set->set_lock);
 615         cv_broadcast(&tp->tp_cv);
 616         mutex_exit(&set->set_lock);
 617 
 618         return (tp);
 619 }
 620 
 621 
 622 /*
 623  * nst_destroy
 624  *
 625  * Destroy a thread set created by nst_init(). It is the
 626  * caller's responsibility to ensure that all prior thread
 627  * calls have completed prior to this call and that the
 628  * caller is not executing from within thread context.
 629  */
 630 void
 631 nst_destroy(nstset_t *set)
 632 {
 633         nsthread_t *tp, *ntp;
 634         nstset_t *sp, **spp;
 635 
 636         if (!set)
 637                 return;
 638 
 639         mutex_enter(&nst_global_lock);
 640 
 641         for (sp = nst_sets; sp; sp = sp->set_next) {
 642                 if (sp == set) {
 643                         break;
 644                 }
 645         }
 646 
 647         if (!sp) {
 648                 mutex_exit(&nst_global_lock);
 649 #ifdef DEBUG
 650                 cmn_err(CE_WARN, "!nst_destroy(%p): no set?", (void *)set);
 651 #endif
 652                 DTRACE_PROBE1(nst_destroy_err_noset, nstset_t *, set);
 653                 return;
 654         }
 655 
 656         mutex_enter(&set->set_lock);
 657 
 658         mutex_exit(&nst_global_lock);
 659 
 660         if (set->set_flag & NST_SF_KILL) {
 661                 /*
 662                  * Wait for a pending destroy to complete
 663                  */
 664 
 665 #ifdef DEBUG
 666                 cmn_err(CE_WARN,
 667                     "!nst_destroy(%p): duplicate destroy of set", (void *)set);
 668 #endif
 669 
 670                 set->set_destroy_cnt++;
 671                 (void) cv_wait_sig(&set->set_destroy_cv, &set->set_lock);
 672                 set->set_destroy_cnt--;
 673 
 674                 mutex_exit(&set->set_lock);
 675 
 676                 DTRACE_PROBE1(nst_destroy_end, nstset_t *, set);
 677 
 678                 return;
 679         }
 680 
 681         set->set_flag |= NST_SF_KILL;
 682 
 683         /* Wake all threads in nst_create(NST_SLEEP) */
 684         cv_broadcast(&set->set_res_cv);
 685 
 686         /*
 687          * Wake all the threads chained in the set.
 688          */
 689 
 690         for (tp = set->set_chain; tp; tp = tp->tp_chain)
 691                 cv_broadcast(&tp->tp_cv);
 692 
 693         /* Wait for the threads to exit */
 694 
 695         while ((set->set_free.q_forw != &set->set_free) ||
 696             (set->set_reuse.q_forw != &set->set_reuse))
 697                 cv_wait(&set->set_kill_cv, &set->set_lock);
 698 
 699         /* Unchain and destroy all the threads in the set */
 700 
 701         tp = set->set_chain;
 702         set->set_chain = 0;
 703 
 704         while (tp) {
 705                 ntp = tp->tp_chain;
 706                 tp->tp_chain = 0;
 707 
 708                 nst_thread_destroy(tp);
 709 
 710                 tp = ntp;
 711         }
 712 
 713         mutex_exit(&set->set_lock);
 714 
 715         mutex_enter(&nst_global_lock);
 716 
 717         /* remove the set from the chain */
 718 
 719         for (spp = &nst_sets; *spp; spp = &((*spp)->set_next)) {
 720                 if (*spp == set) {
 721                         *spp = set->set_next;
 722                         set->set_next = NULL;
 723                         break;
 724                 }
 725         }
 726 
 727         mutex_exit(&nst_global_lock);
 728 
 729         mutex_enter(&set->set_lock);
 730 
 731 #ifdef DEBUG
 732         if (set->set_nthread != 0) {
 733                 cmn_err(CE_WARN, "!nst_destroy(%p): nthread != 0 (%d)",
 734                     (void *)set, set->set_nthread);
 735         }
 736 #endif
 737 
 738         /* Allow any waiters (above) to continue */
 739 
 740         cv_broadcast(&set->set_destroy_cv);
 741 
 742         while (set->set_destroy_cnt > 0 || set->set_pending > 0 ||
 743             set->set_res_cnt > 0) {
 744                 mutex_exit(&set->set_lock);
 745                 delay(drv_usectohz((clock_t)NST_KILL_TIMEOUT));
 746                 mutex_enter(&set->set_lock);
 747         }
 748 
 749         mutex_exit(&set->set_lock);
 750 
 751         if (set->set_nthread != 0) {
 752                 /* leak the set control structure */
 753 
 754                 DTRACE_PROBE1(nst_destroy_end, nstset_t *, set);
 755 
 756                 return;
 757         }
 758 
 759         cv_destroy(&set->set_res_cv);
 760         cv_destroy(&set->set_kill_cv);
 761         cv_destroy(&set->set_destroy_cv);
 762         mutex_destroy(&set->set_lock);
 763         kmem_free(set, sizeof (*set));
 764 
 765 }
 766 
 767 
 768 /*
 769  * nst_add_thread
 770  *
 771  * Add more threads into an existing thread set.
 772  * Returns the number successfully added.
 773  */
 774 int
 775 nst_add_thread(nstset_t *set, int nthread)
 776 {
 777         nsthread_t *tp;
 778         int i;
 779 
 780         if (!set || nthread < 1) {
 781 #ifdef DEBUG
 782                 cmn_err(CE_WARN,
 783                     "!nst_add_thread(%p, %d) - bad args", (void *)set, nthread);
 784 #endif
 785                 return (0);
 786         }
 787 
 788         for (i = 0; i < nthread; i++) {
 789                 /* get new thread */
 790 
 791                 if ((tp = nst_thread_create(set)) == NULL)
 792                         break;
 793 
 794                 /* add to free list */
 795 
 796                 mutex_enter(&set->set_lock);
 797                 nst_thread_free(tp);
 798                 mutex_exit(&set->set_lock);
 799         }
 800 
 801         return (i);
 802 }
 803 
 804 
 805 /*
 806  * nst_del_thread
 807  *
 808  * Removes threads from an existing thread set.
 809  * Returns the number successfully removed.
 810  */
 811 int
 812 nst_del_thread(nstset_t *set, int nthread)
 813 {
 814         nsthread_t **tpp, *tp;
 815         int i;
 816 
 817         if (!set || nthread < 1) {
 818 #ifdef DEBUG
 819                 cmn_err(CE_WARN,
 820                     "!nst_del_thread(%p, %d) - bad args", (void *)set, nthread);
 821 #endif
 822                 return (0);
 823         }
 824 
 825         for (i = 0; i < nthread; i++) {
 826                 /* get thread */
 827 
 828                 if (!(tp = nst_thread_alloc(set, FALSE)))
 829                         break;
 830 
 831                 mutex_enter(&set->set_lock);
 832 
 833                 /* unlink from the set */
 834 
 835                 for (tpp = &set->set_chain; *tpp; tpp = &(*tpp)->tp_chain) {
 836                         if (*tpp == tp) {
 837                                 *tpp = tp->tp_chain;
 838                                 tp->tp_chain = NULL;
 839                                 break;
 840                         }
 841                 }
 842 
 843                 /* kill the thread */
 844 
 845                 tp->tp_flag |= NST_TF_KILL;
 846                 tp->tp_flag &= ~NST_TF_INUSE;
 847                 cv_broadcast(&tp->tp_cv);
 848 
 849                 /* wait for thread to exit */
 850 
 851                 while (tp->tp_flag & NST_TF_KILL)
 852                         cv_wait(&set->set_kill_cv, &set->set_lock);
 853 
 854                 set->set_nlive--;
 855                 mutex_exit(&set->set_lock);
 856 
 857                 /* free kernel resources */
 858 
 859                 nst_thread_destroy(tp);
 860         }
 861 
 862         return (i);
 863 }
 864 
 865 
 866 /*
 867  * nst_init
 868  *
 869  * Initialise a new nsthread set, returning its address or
 870  * NULL in the event of failure. The set should be destroyed
 871  * by calling nst_destroy().
 872  */
 873 nstset_t *
 874 nst_init(char *name, int nthread)
 875 {
 876         nstset_t *set, *sp;
 877         int len, i;
 878 
 879         if (nthread < 1) {
 880 #ifdef DEBUG
 881                 cmn_err(CE_WARN, "!nst_init: invalid arg");
 882 #endif
 883                 return (NULL);
 884         }
 885 
 886         if (nthread > USHRT_MAX) {
 887 #ifdef DEBUG
 888                 cmn_err(CE_WARN, "!nst_init: arg limit exceeded");
 889 #endif
 890                 return (NULL);
 891         }
 892 
 893         if (!(set = nst_kmem_zalloc(sizeof (*set), 2)))
 894                 return (NULL);
 895 
 896         len = strlen(name);
 897         if (len >= sizeof (set->set_name))
 898                 len = sizeof (set->set_name) - 1;
 899 
 900         bcopy(name, set->set_name, len);
 901 
 902         mutex_init(&set->set_lock, NULL, MUTEX_DRIVER, NULL);
 903         cv_init(&set->set_destroy_cv, NULL, CV_DRIVER, NULL);
 904         cv_init(&set->set_kill_cv, NULL, CV_DRIVER, NULL);
 905         cv_init(&set->set_res_cv, NULL, CV_DRIVER, NULL);
 906 
 907         set->set_reuse.q_forw = set->set_reuse.q_back = &set->set_reuse;
 908         set->set_free.q_forw = set->set_free.q_back = &set->set_free;
 909 
 910         mutex_enter(&nst_global_lock);
 911 
 912         /* check for duplicates */
 913 
 914         for (sp = nst_sets; sp; sp = sp->set_next) {
 915                 if (strcmp(sp->set_name, set->set_name) == 0) {
 916                         /* duplicate */
 917                         mutex_exit(&nst_global_lock);
 918                         cv_destroy(&set->set_res_cv);
 919                         cv_destroy(&set->set_kill_cv);
 920                         cv_destroy(&set->set_destroy_cv);
 921                         mutex_destroy(&set->set_lock);
 922                         kmem_free(set, sizeof (*set));
 923 #ifdef DEBUG
 924                         cmn_err(CE_WARN,
 925                             "!nst_init: duplicate set \"%s\"", name);
 926 #endif
 927                         /* add threads if necessary */
 928 
 929                         if (nthread > sp->set_nthread) {
 930                                 i = nst_add_thread(sp,
 931                                     nthread - sp->set_nthread);
 932 #ifdef DEBUG
 933                                 if (i !=  (nthread - sp->set_nthread))
 934                                         cmn_err(CE_WARN,
 935                                             "!nst_init: failed to allocate %d "
 936                                             "threads (got %d)",
 937                                             (nthread - sp->set_nthread), i);
 938 #endif
 939                         }
 940 
 941                         /* return pointer to existing set */
 942 
 943                         return (sp);
 944                 }
 945         }
 946 
 947         /* add new set to chain */
 948         set->set_next = nst_sets;
 949         nst_sets = set;
 950 
 951         mutex_exit(&nst_global_lock);
 952 
 953         i = nst_add_thread(set, nthread);
 954 
 955         if (i != nthread) {
 956 #ifdef DEBUG
 957                 cmn_err(CE_WARN,
 958                     "!nst_init: failed to allocate %d threads (got %d)",
 959                     nthread, i);
 960 #endif
 961                 nst_destroy(set);
 962                 return (NULL);
 963         }
 964 
 965         return (set);
 966 }
 967 
 968 
 969 /*
 970  * nst_nlive
 971  *
 972  * Return the number of live threads in a set.
 973  */
 974 int
 975 nst_nlive(nstset_t *set)
 976 {
 977         return (set ? set->set_nlive : 0);
 978 }
 979 
 980 
 981 /*
 982  * nst_nthread
 983  *
 984  * Return the number of threads in the set.
 985  */
 986 int
 987 nst_nthread(nstset_t *set)
 988 {
 989         return (set ? set->set_nthread : 0);
 990 }
 991 
 992 
 993 /*
 994  * nst_shutdown
 995  *
 996  * Called by nskern to shutdown the nsthread software.
 997  */
 998 void
 999 nst_shutdown(void)
1000 {
1001         nstset_t *set;
1002 
1003         mutex_enter(&nst_global_lock);
1004 
1005         while ((set = nst_sets) != NULL) {
1006                 mutex_exit(&nst_global_lock);
1007                 nst_destroy(set);
1008                 mutex_enter(&nst_global_lock);
1009         }
1010 
1011         mutex_exit(&nst_global_lock);
1012         mutex_destroy(&nst_global_lock);
1013 }
1014 
1015 
1016 /*
1017  * nst_startup
1018  *
1019  * Called by nskern to initialise the nsthread software
1020  */
1021 int
1022 nst_startup(void)
1023 {
1024         mutex_init(&nst_global_lock, NULL, MUTEX_DRIVER, NULL);
1025         return (0);
1026 }