1*b819cea2SGordon Ross /* 2*b819cea2SGordon Ross * CDDL HEADER START 3*b819cea2SGordon Ross * 4*b819cea2SGordon Ross * The contents of this file are subject to the terms of the 5*b819cea2SGordon Ross * Common Development and Distribution License (the "License"). 6*b819cea2SGordon Ross * You may not use this file except in compliance with the License. 7*b819cea2SGordon Ross * 8*b819cea2SGordon Ross * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*b819cea2SGordon Ross * or http://www.opensolaris.org/os/licensing. 10*b819cea2SGordon Ross * See the License for the specific language governing permissions 11*b819cea2SGordon Ross * and limitations under the License. 12*b819cea2SGordon Ross * 13*b819cea2SGordon Ross * When distributing Covered Code, include this CDDL HEADER in each 14*b819cea2SGordon Ross * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*b819cea2SGordon Ross * If applicable, add the following below this CDDL HEADER, with the 16*b819cea2SGordon Ross * fields enclosed by brackets "[]" replaced with your own identifying 17*b819cea2SGordon Ross * information: Portions Copyright [yyyy] [name of copyright owner] 18*b819cea2SGordon Ross * 19*b819cea2SGordon Ross * CDDL HEADER END 20*b819cea2SGordon Ross */ 21*b819cea2SGordon Ross /* 22*b819cea2SGordon Ross * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 23*b819cea2SGordon Ross * Use is subject to license terms. 24*b819cea2SGordon Ross */ 25*b819cea2SGordon Ross /* 26*b819cea2SGordon Ross * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved. 27*b819cea2SGordon Ross * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 28*b819cea2SGordon Ross */ 29*b819cea2SGordon Ross 30*b819cea2SGordon Ross #include <sys/taskq_impl.h> 31*b819cea2SGordon Ross 32*b819cea2SGordon Ross #include <sys/class.h> 33*b819cea2SGordon Ross #include <sys/debug.h> 34*b819cea2SGordon Ross #include <sys/ksynch.h> 35*b819cea2SGordon Ross #include <sys/kmem.h> 36*b819cea2SGordon Ross #include <sys/time.h> 37*b819cea2SGordon Ross #include <sys/systm.h> 38*b819cea2SGordon Ross #include <sys/sysmacros.h> 39*b819cea2SGordon Ross #include <sys/unistd.h> 40*b819cea2SGordon Ross 41*b819cea2SGordon Ross /* avoid <unistd.h> */ 42*b819cea2SGordon Ross extern long sysconf(int); 43*b819cea2SGordon Ross 44*b819cea2SGordon Ross /* avoiding <thread.h> */ 45*b819cea2SGordon Ross typedef unsigned int thread_t; 46*b819cea2SGordon Ross typedef unsigned int thread_key_t; 47*b819cea2SGordon Ross 48*b819cea2SGordon Ross extern int thr_create(void *, size_t, void *(*)(void *), void *, long, 49*b819cea2SGordon Ross thread_t *); 50*b819cea2SGordon Ross extern int thr_join(thread_t, thread_t *, void **); 51*b819cea2SGordon Ross 52*b819cea2SGordon Ross /* 53*b819cea2SGordon Ross * POSIX.1c Note: 54*b819cea2SGordon Ross * THR_BOUND is defined same as PTHREAD_SCOPE_SYSTEM in <pthread.h> 55*b819cea2SGordon Ross * THR_DETACHED is defined same as PTHREAD_CREATE_DETACHED in <pthread.h> 56*b819cea2SGordon Ross * Any changes in these definitions should be reflected in <pthread.h> 57*b819cea2SGordon Ross */ 58*b819cea2SGordon Ross #define THR_BOUND 0x00000001 /* = PTHREAD_SCOPE_SYSTEM */ 59*b819cea2SGordon Ross #define THR_NEW_LWP 0x00000002 60*b819cea2SGordon Ross #define THR_DETACHED 0x00000040 /* = PTHREAD_CREATE_DETACHED */ 61*b819cea2SGordon Ross #define THR_SUSPENDED 0x00000080 62*b819cea2SGordon Ross #define THR_DAEMON 0x00000100 63*b819cea2SGordon Ross 64*b819cea2SGordon Ross 65*b819cea2SGordon Ross int taskq_now; 66*b819cea2SGordon Ross taskq_t *system_taskq; 67*b819cea2SGordon Ross 68*b819cea2SGordon Ross #define TASKQ_ACTIVE 0x00010000 69*b819cea2SGordon Ross 70*b819cea2SGordon Ross struct taskq { 71*b819cea2SGordon Ross kmutex_t tq_lock; 72*b819cea2SGordon Ross krwlock_t tq_threadlock; 73*b819cea2SGordon Ross kcondvar_t tq_dispatch_cv; 74*b819cea2SGordon Ross kcondvar_t tq_wait_cv; 75*b819cea2SGordon Ross thread_t *tq_threadlist; 76*b819cea2SGordon Ross int tq_flags; 77*b819cea2SGordon Ross int tq_active; 78*b819cea2SGordon Ross int tq_nthreads; 79*b819cea2SGordon Ross int tq_nalloc; 80*b819cea2SGordon Ross int tq_minalloc; 81*b819cea2SGordon Ross int tq_maxalloc; 82*b819cea2SGordon Ross kcondvar_t tq_maxalloc_cv; 83*b819cea2SGordon Ross int tq_maxalloc_wait; 84*b819cea2SGordon Ross taskq_ent_t *tq_freelist; 85*b819cea2SGordon Ross taskq_ent_t tq_task; 86*b819cea2SGordon Ross }; 87*b819cea2SGordon Ross 88*b819cea2SGordon Ross static taskq_ent_t * 89*b819cea2SGordon Ross task_alloc(taskq_t *tq, int tqflags) 90*b819cea2SGordon Ross { 91*b819cea2SGordon Ross taskq_ent_t *t; 92*b819cea2SGordon Ross int rv; 93*b819cea2SGordon Ross 94*b819cea2SGordon Ross again: if ((t = tq->tq_freelist) != NULL && tq->tq_nalloc >= tq->tq_minalloc) { 95*b819cea2SGordon Ross tq->tq_freelist = t->tqent_next; 96*b819cea2SGordon Ross } else { 97*b819cea2SGordon Ross if (tq->tq_nalloc >= tq->tq_maxalloc) { 98*b819cea2SGordon Ross if (!(tqflags & KM_SLEEP)) 99*b819cea2SGordon Ross return (NULL); 100*b819cea2SGordon Ross 101*b819cea2SGordon Ross /* 102*b819cea2SGordon Ross * We don't want to exceed tq_maxalloc, but we can't 103*b819cea2SGordon Ross * wait for other tasks to complete (and thus free up 104*b819cea2SGordon Ross * task structures) without risking deadlock with 105*b819cea2SGordon Ross * the caller. So, we just delay for one second 106*b819cea2SGordon Ross * to throttle the allocation rate. If we have tasks 107*b819cea2SGordon Ross * complete before one second timeout expires then 108*b819cea2SGordon Ross * taskq_ent_free will signal us and we will 109*b819cea2SGordon Ross * immediately retry the allocation. 110*b819cea2SGordon Ross */ 111*b819cea2SGordon Ross tq->tq_maxalloc_wait++; 112*b819cea2SGordon Ross rv = cv_timedwait(&tq->tq_maxalloc_cv, 113*b819cea2SGordon Ross &tq->tq_lock, ddi_get_lbolt() + hz); 114*b819cea2SGordon Ross tq->tq_maxalloc_wait--; 115*b819cea2SGordon Ross if (rv > 0) 116*b819cea2SGordon Ross goto again; /* signaled */ 117*b819cea2SGordon Ross } 118*b819cea2SGordon Ross mutex_exit(&tq->tq_lock); 119*b819cea2SGordon Ross 120*b819cea2SGordon Ross t = kmem_alloc(sizeof (taskq_ent_t), tqflags); 121*b819cea2SGordon Ross 122*b819cea2SGordon Ross mutex_enter(&tq->tq_lock); 123*b819cea2SGordon Ross if (t != NULL) 124*b819cea2SGordon Ross tq->tq_nalloc++; 125*b819cea2SGordon Ross } 126*b819cea2SGordon Ross return (t); 127*b819cea2SGordon Ross } 128*b819cea2SGordon Ross 129*b819cea2SGordon Ross static void 130*b819cea2SGordon Ross task_free(taskq_t *tq, taskq_ent_t *t) 131*b819cea2SGordon Ross { 132*b819cea2SGordon Ross if (tq->tq_nalloc <= tq->tq_minalloc) { 133*b819cea2SGordon Ross t->tqent_next = tq->tq_freelist; 134*b819cea2SGordon Ross tq->tq_freelist = t; 135*b819cea2SGordon Ross } else { 136*b819cea2SGordon Ross tq->tq_nalloc--; 137*b819cea2SGordon Ross mutex_exit(&tq->tq_lock); 138*b819cea2SGordon Ross kmem_free(t, sizeof (taskq_ent_t)); 139*b819cea2SGordon Ross mutex_enter(&tq->tq_lock); 140*b819cea2SGordon Ross } 141*b819cea2SGordon Ross 142*b819cea2SGordon Ross if (tq->tq_maxalloc_wait) 143*b819cea2SGordon Ross cv_signal(&tq->tq_maxalloc_cv); 144*b819cea2SGordon Ross } 145*b819cea2SGordon Ross 146*b819cea2SGordon Ross taskqid_t 147*b819cea2SGordon Ross taskq_dispatch(taskq_t *tq, task_func_t func, void *arg, uint_t tqflags) 148*b819cea2SGordon Ross { 149*b819cea2SGordon Ross taskq_ent_t *t; 150*b819cea2SGordon Ross 151*b819cea2SGordon Ross if (taskq_now) { 152*b819cea2SGordon Ross func(arg); 153*b819cea2SGordon Ross return (1); 154*b819cea2SGordon Ross } 155*b819cea2SGordon Ross 156*b819cea2SGordon Ross mutex_enter(&tq->tq_lock); 157*b819cea2SGordon Ross ASSERT(tq->tq_flags & TASKQ_ACTIVE); 158*b819cea2SGordon Ross if ((t = task_alloc(tq, tqflags)) == NULL) { 159*b819cea2SGordon Ross mutex_exit(&tq->tq_lock); 160*b819cea2SGordon Ross return (0); 161*b819cea2SGordon Ross } 162*b819cea2SGordon Ross if (tqflags & TQ_FRONT) { 163*b819cea2SGordon Ross t->tqent_next = tq->tq_task.tqent_next; 164*b819cea2SGordon Ross t->tqent_prev = &tq->tq_task; 165*b819cea2SGordon Ross } else { 166*b819cea2SGordon Ross t->tqent_next = &tq->tq_task; 167*b819cea2SGordon Ross t->tqent_prev = tq->tq_task.tqent_prev; 168*b819cea2SGordon Ross } 169*b819cea2SGordon Ross t->tqent_next->tqent_prev = t; 170*b819cea2SGordon Ross t->tqent_prev->tqent_next = t; 171*b819cea2SGordon Ross t->tqent_func = func; 172*b819cea2SGordon Ross t->tqent_arg = arg; 173*b819cea2SGordon Ross t->tqent_flags = 0; 174*b819cea2SGordon Ross cv_signal(&tq->tq_dispatch_cv); 175*b819cea2SGordon Ross mutex_exit(&tq->tq_lock); 176*b819cea2SGordon Ross return (1); 177*b819cea2SGordon Ross } 178*b819cea2SGordon Ross 179*b819cea2SGordon Ross void 180*b819cea2SGordon Ross taskq_dispatch_ent(taskq_t *tq, task_func_t func, void *arg, uint_t flags, 181*b819cea2SGordon Ross taskq_ent_t *t) 182*b819cea2SGordon Ross { 183*b819cea2SGordon Ross ASSERT(func != NULL); 184*b819cea2SGordon Ross ASSERT(!(tq->tq_flags & TASKQ_DYNAMIC)); 185*b819cea2SGordon Ross 186*b819cea2SGordon Ross /* 187*b819cea2SGordon Ross * Mark it as a prealloc'd task. This is important 188*b819cea2SGordon Ross * to ensure that we don't free it later. 189*b819cea2SGordon Ross */ 190*b819cea2SGordon Ross t->tqent_flags |= TQENT_FLAG_PREALLOC; 191*b819cea2SGordon Ross /* 192*b819cea2SGordon Ross * Enqueue the task to the underlying queue. 193*b819cea2SGordon Ross */ 194*b819cea2SGordon Ross mutex_enter(&tq->tq_lock); 195*b819cea2SGordon Ross 196*b819cea2SGordon Ross if (flags & TQ_FRONT) { 197*b819cea2SGordon Ross t->tqent_next = tq->tq_task.tqent_next; 198*b819cea2SGordon Ross t->tqent_prev = &tq->tq_task; 199*b819cea2SGordon Ross } else { 200*b819cea2SGordon Ross t->tqent_next = &tq->tq_task; 201*b819cea2SGordon Ross t->tqent_prev = tq->tq_task.tqent_prev; 202*b819cea2SGordon Ross } 203*b819cea2SGordon Ross t->tqent_next->tqent_prev = t; 204*b819cea2SGordon Ross t->tqent_prev->tqent_next = t; 205*b819cea2SGordon Ross t->tqent_func = func; 206*b819cea2SGordon Ross t->tqent_arg = arg; 207*b819cea2SGordon Ross cv_signal(&tq->tq_dispatch_cv); 208*b819cea2SGordon Ross mutex_exit(&tq->tq_lock); 209*b819cea2SGordon Ross } 210*b819cea2SGordon Ross 211*b819cea2SGordon Ross void 212*b819cea2SGordon Ross taskq_wait(taskq_t *tq) 213*b819cea2SGordon Ross { 214*b819cea2SGordon Ross mutex_enter(&tq->tq_lock); 215*b819cea2SGordon Ross while (tq->tq_task.tqent_next != &tq->tq_task || tq->tq_active != 0) 216*b819cea2SGordon Ross cv_wait(&tq->tq_wait_cv, &tq->tq_lock); 217*b819cea2SGordon Ross mutex_exit(&tq->tq_lock); 218*b819cea2SGordon Ross } 219*b819cea2SGordon Ross 220*b819cea2SGordon Ross static void * 221*b819cea2SGordon Ross taskq_thread(void *arg) 222*b819cea2SGordon Ross { 223*b819cea2SGordon Ross taskq_t *tq = arg; 224*b819cea2SGordon Ross taskq_ent_t *t; 225*b819cea2SGordon Ross boolean_t prealloc; 226*b819cea2SGordon Ross 227*b819cea2SGordon Ross mutex_enter(&tq->tq_lock); 228*b819cea2SGordon Ross while (tq->tq_flags & TASKQ_ACTIVE) { 229*b819cea2SGordon Ross if ((t = tq->tq_task.tqent_next) == &tq->tq_task) { 230*b819cea2SGordon Ross if (--tq->tq_active == 0) 231*b819cea2SGordon Ross cv_broadcast(&tq->tq_wait_cv); 232*b819cea2SGordon Ross cv_wait(&tq->tq_dispatch_cv, &tq->tq_lock); 233*b819cea2SGordon Ross tq->tq_active++; 234*b819cea2SGordon Ross continue; 235*b819cea2SGordon Ross } 236*b819cea2SGordon Ross t->tqent_prev->tqent_next = t->tqent_next; 237*b819cea2SGordon Ross t->tqent_next->tqent_prev = t->tqent_prev; 238*b819cea2SGordon Ross t->tqent_next = NULL; 239*b819cea2SGordon Ross t->tqent_prev = NULL; 240*b819cea2SGordon Ross prealloc = t->tqent_flags & TQENT_FLAG_PREALLOC; 241*b819cea2SGordon Ross mutex_exit(&tq->tq_lock); 242*b819cea2SGordon Ross 243*b819cea2SGordon Ross rw_enter(&tq->tq_threadlock, RW_READER); 244*b819cea2SGordon Ross t->tqent_func(t->tqent_arg); 245*b819cea2SGordon Ross rw_exit(&tq->tq_threadlock); 246*b819cea2SGordon Ross 247*b819cea2SGordon Ross mutex_enter(&tq->tq_lock); 248*b819cea2SGordon Ross if (!prealloc) 249*b819cea2SGordon Ross task_free(tq, t); 250*b819cea2SGordon Ross } 251*b819cea2SGordon Ross tq->tq_nthreads--; 252*b819cea2SGordon Ross cv_broadcast(&tq->tq_wait_cv); 253*b819cea2SGordon Ross mutex_exit(&tq->tq_lock); 254*b819cea2SGordon Ross return (NULL); 255*b819cea2SGordon Ross } 256*b819cea2SGordon Ross 257*b819cea2SGordon Ross /*ARGSUSED*/ 258*b819cea2SGordon Ross taskq_t * 259*b819cea2SGordon Ross taskq_create(const char *name, int nthr, pri_t pri, int minalloc, 260*b819cea2SGordon Ross int maxalloc, uint_t flags) 261*b819cea2SGordon Ross { 262*b819cea2SGordon Ross return (taskq_create_proc(name, nthr, pri, 263*b819cea2SGordon Ross minalloc, maxalloc, NULL, flags)); 264*b819cea2SGordon Ross } 265*b819cea2SGordon Ross 266*b819cea2SGordon Ross /*ARGSUSED*/ 267*b819cea2SGordon Ross taskq_t * 268*b819cea2SGordon Ross taskq_create_proc(const char *name, int nthreads, pri_t pri, 269*b819cea2SGordon Ross int minalloc, int maxalloc, proc_t *proc, uint_t flags) 270*b819cea2SGordon Ross { 271*b819cea2SGordon Ross taskq_t *tq = kmem_zalloc(sizeof (taskq_t), KM_SLEEP); 272*b819cea2SGordon Ross int t; 273*b819cea2SGordon Ross 274*b819cea2SGordon Ross if (flags & TASKQ_THREADS_CPU_PCT) { 275*b819cea2SGordon Ross int pct; 276*b819cea2SGordon Ross ASSERT3S(nthreads, >=, 0); 277*b819cea2SGordon Ross ASSERT3S(nthreads, <=, 100); 278*b819cea2SGordon Ross pct = MIN(nthreads, 100); 279*b819cea2SGordon Ross pct = MAX(pct, 0); 280*b819cea2SGordon Ross 281*b819cea2SGordon Ross nthreads = (sysconf(_SC_NPROCESSORS_ONLN) * pct) / 100; 282*b819cea2SGordon Ross nthreads = MAX(nthreads, 1); /* need at least 1 thread */ 283*b819cea2SGordon Ross } else { 284*b819cea2SGordon Ross ASSERT3S(nthreads, >=, 1); 285*b819cea2SGordon Ross } 286*b819cea2SGordon Ross 287*b819cea2SGordon Ross rw_init(&tq->tq_threadlock, NULL, RW_DEFAULT, NULL); 288*b819cea2SGordon Ross mutex_init(&tq->tq_lock, NULL, MUTEX_DEFAULT, NULL); 289*b819cea2SGordon Ross cv_init(&tq->tq_dispatch_cv, NULL, CV_DEFAULT, NULL); 290*b819cea2SGordon Ross cv_init(&tq->tq_wait_cv, NULL, CV_DEFAULT, NULL); 291*b819cea2SGordon Ross cv_init(&tq->tq_maxalloc_cv, NULL, CV_DEFAULT, NULL); 292*b819cea2SGordon Ross tq->tq_flags = flags | TASKQ_ACTIVE; 293*b819cea2SGordon Ross tq->tq_active = nthreads; 294*b819cea2SGordon Ross tq->tq_nthreads = nthreads; 295*b819cea2SGordon Ross tq->tq_minalloc = minalloc; 296*b819cea2SGordon Ross tq->tq_maxalloc = maxalloc; 297*b819cea2SGordon Ross tq->tq_task.tqent_next = &tq->tq_task; 298*b819cea2SGordon Ross tq->tq_task.tqent_prev = &tq->tq_task; 299*b819cea2SGordon Ross tq->tq_threadlist = kmem_alloc(nthreads * sizeof (thread_t), KM_SLEEP); 300*b819cea2SGordon Ross 301*b819cea2SGordon Ross if (flags & TASKQ_PREPOPULATE) { 302*b819cea2SGordon Ross mutex_enter(&tq->tq_lock); 303*b819cea2SGordon Ross while (minalloc-- > 0) 304*b819cea2SGordon Ross task_free(tq, task_alloc(tq, KM_SLEEP)); 305*b819cea2SGordon Ross mutex_exit(&tq->tq_lock); 306*b819cea2SGordon Ross } 307*b819cea2SGordon Ross 308*b819cea2SGordon Ross for (t = 0; t < nthreads; t++) 309*b819cea2SGordon Ross (void) thr_create(0, 0, taskq_thread, 310*b819cea2SGordon Ross tq, THR_BOUND, &tq->tq_threadlist[t]); 311*b819cea2SGordon Ross 312*b819cea2SGordon Ross return (tq); 313*b819cea2SGordon Ross } 314*b819cea2SGordon Ross 315*b819cea2SGordon Ross void 316*b819cea2SGordon Ross taskq_destroy(taskq_t *tq) 317*b819cea2SGordon Ross { 318*b819cea2SGordon Ross int t; 319*b819cea2SGordon Ross int nthreads = tq->tq_nthreads; 320*b819cea2SGordon Ross 321*b819cea2SGordon Ross taskq_wait(tq); 322*b819cea2SGordon Ross 323*b819cea2SGordon Ross mutex_enter(&tq->tq_lock); 324*b819cea2SGordon Ross 325*b819cea2SGordon Ross tq->tq_flags &= ~TASKQ_ACTIVE; 326*b819cea2SGordon Ross cv_broadcast(&tq->tq_dispatch_cv); 327*b819cea2SGordon Ross 328*b819cea2SGordon Ross while (tq->tq_nthreads != 0) 329*b819cea2SGordon Ross cv_wait(&tq->tq_wait_cv, &tq->tq_lock); 330*b819cea2SGordon Ross 331*b819cea2SGordon Ross tq->tq_minalloc = 0; 332*b819cea2SGordon Ross while (tq->tq_nalloc != 0) { 333*b819cea2SGordon Ross ASSERT(tq->tq_freelist != NULL); 334*b819cea2SGordon Ross task_free(tq, task_alloc(tq, KM_SLEEP)); 335*b819cea2SGordon Ross } 336*b819cea2SGordon Ross 337*b819cea2SGordon Ross mutex_exit(&tq->tq_lock); 338*b819cea2SGordon Ross 339*b819cea2SGordon Ross for (t = 0; t < nthreads; t++) 340*b819cea2SGordon Ross (void) thr_join(tq->tq_threadlist[t], NULL, NULL); 341*b819cea2SGordon Ross 342*b819cea2SGordon Ross kmem_free(tq->tq_threadlist, nthreads * sizeof (thread_t)); 343*b819cea2SGordon Ross 344*b819cea2SGordon Ross rw_destroy(&tq->tq_threadlock); 345*b819cea2SGordon Ross mutex_destroy(&tq->tq_lock); 346*b819cea2SGordon Ross cv_destroy(&tq->tq_dispatch_cv); 347*b819cea2SGordon Ross cv_destroy(&tq->tq_wait_cv); 348*b819cea2SGordon Ross cv_destroy(&tq->tq_maxalloc_cv); 349*b819cea2SGordon Ross 350*b819cea2SGordon Ross kmem_free(tq, sizeof (taskq_t)); 351*b819cea2SGordon Ross } 352*b819cea2SGordon Ross 353*b819cea2SGordon Ross int 354*b819cea2SGordon Ross taskq_member(taskq_t *tq, struct _kthread *t) 355*b819cea2SGordon Ross { 356*b819cea2SGordon Ross int i; 357*b819cea2SGordon Ross 358*b819cea2SGordon Ross if (taskq_now) 359*b819cea2SGordon Ross return (1); 360*b819cea2SGordon Ross 361*b819cea2SGordon Ross for (i = 0; i < tq->tq_nthreads; i++) 362*b819cea2SGordon Ross if (tq->tq_threadlist[i] == (thread_t)(uintptr_t)t) 363*b819cea2SGordon Ross return (1); 364*b819cea2SGordon Ross 365*b819cea2SGordon Ross return (0); 366*b819cea2SGordon Ross } 367*b819cea2SGordon Ross 368*b819cea2SGordon Ross void 369*b819cea2SGordon Ross system_taskq_init(void) 370*b819cea2SGordon Ross { 371*b819cea2SGordon Ross system_taskq = taskq_create("system_taskq", 64, minclsyspri, 4, 512, 372*b819cea2SGordon Ross TASKQ_DYNAMIC | TASKQ_PREPOPULATE); 373*b819cea2SGordon Ross } 374*b819cea2SGordon Ross 375*b819cea2SGordon Ross void 376*b819cea2SGordon Ross system_taskq_fini(void) 377*b819cea2SGordon Ross { 378*b819cea2SGordon Ross taskq_destroy(system_taskq); 379*b819cea2SGordon Ross system_taskq = NULL; /* defensive */ 380*b819cea2SGordon Ross } 381