Home | History | Annotate | Line # | Download | only in common
      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 2010 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 /*
     26  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
     27  * Copyright 2012 Garrett D'Amore <garrett (at) damore.org>.  All rights reserved.
     28  * Copyright (c) 2014 by Delphix. All rights reserved.
     29  */
     30 
     31 #include <sys/zfs_context.h>
     32 
     33 int taskq_now;
     34 taskq_t *system_taskq;
     35 
     36 #define	TASKQ_ACTIVE	0x00010000
     37 #define	TASKQ_NAMELEN	31
     38 
     39 struct taskq {
     40 	char		tq_name[TASKQ_NAMELEN + 1];
     41 	kmutex_t	tq_lock;
     42 	krwlock_t	tq_threadlock;
     43 	kcondvar_t	tq_dispatch_cv;
     44 	kcondvar_t	tq_wait_cv;
     45 	thread_t	*tq_threadlist;
     46 	int		tq_flags;
     47 	int		tq_active;
     48 	int		tq_nthreads;
     49 	int		tq_nalloc;
     50 	int		tq_minalloc;
     51 	int		tq_maxalloc;
     52 	kcondvar_t	tq_maxalloc_cv;
     53 	int		tq_maxalloc_wait;
     54 	taskq_ent_t	*tq_freelist;
     55 	taskq_ent_t	tq_task;
     56 };
     57 
     58 static taskq_ent_t *
     59 task_alloc(taskq_t *tq, int tqflags)
     60 {
     61 	taskq_ent_t *t;
     62 	int rv;
     63 
     64 again:	if ((t = tq->tq_freelist) != NULL && tq->tq_nalloc >= tq->tq_minalloc) {
     65 		tq->tq_freelist = t->tqent_next;
     66 	} else {
     67 		if (tq->tq_nalloc >= tq->tq_maxalloc) {
     68 			if (!(tqflags & KM_SLEEP))
     69 				return (NULL);
     70 
     71 			/*
     72 			 * We don't want to exceed tq_maxalloc, but we can't
     73 			 * wait for other tasks to complete (and thus free up
     74 			 * task structures) without risking deadlock with
     75 			 * the caller.  So, we just delay for one second
     76 			 * to throttle the allocation rate. If we have tasks
     77 			 * complete before one second timeout expires then
     78 			 * taskq_ent_free will signal us and we will
     79 			 * immediately retry the allocation.
     80 			 */
     81 			tq->tq_maxalloc_wait++;
     82 			rv = cv_timedwait(&tq->tq_maxalloc_cv,
     83 			    &tq->tq_lock, ddi_get_lbolt() + hz);
     84 			tq->tq_maxalloc_wait--;
     85 			if (rv > 0)
     86 				goto again;		/* signaled */
     87 		}
     88 		mutex_exit(&tq->tq_lock);
     89 
     90 		t = kmem_alloc(sizeof (taskq_ent_t), tqflags & KM_SLEEP);
     91 
     92 		mutex_enter(&tq->tq_lock);
     93 		if (t != NULL)
     94 			tq->tq_nalloc++;
     95 	}
     96 	return (t);
     97 }
     98 
     99 static void
    100 task_free(taskq_t *tq, taskq_ent_t *t)
    101 {
    102 	if (tq->tq_nalloc <= tq->tq_minalloc) {
    103 		t->tqent_next = tq->tq_freelist;
    104 		tq->tq_freelist = t;
    105 	} else {
    106 		tq->tq_nalloc--;
    107 		mutex_exit(&tq->tq_lock);
    108 		kmem_free(t, sizeof (taskq_ent_t));
    109 		mutex_enter(&tq->tq_lock);
    110 	}
    111 
    112 	if (tq->tq_maxalloc_wait)
    113 		cv_signal(&tq->tq_maxalloc_cv);
    114 }
    115 
    116 taskqid_t
    117 taskq_dispatch(taskq_t *tq, task_func_t func, void *arg, uint_t tqflags)
    118 {
    119 	taskq_ent_t *t;
    120 
    121 	if (taskq_now) {
    122 		func(arg);
    123 		return (1);
    124 	}
    125 
    126 	mutex_enter(&tq->tq_lock);
    127 	ASSERT(tq->tq_flags & TASKQ_ACTIVE);
    128 	if ((t = task_alloc(tq, tqflags)) == NULL) {
    129 		mutex_exit(&tq->tq_lock);
    130 		return (0);
    131 	}
    132 	if (tqflags & TQ_FRONT) {
    133 		t->tqent_next = tq->tq_task.tqent_next;
    134 		t->tqent_prev = &tq->tq_task;
    135 	} else {
    136 		t->tqent_next = &tq->tq_task;
    137 		t->tqent_prev = tq->tq_task.tqent_prev;
    138 	}
    139 	t->tqent_next->tqent_prev = t;
    140 	t->tqent_prev->tqent_next = t;
    141 	t->tqent_func = func;
    142 	t->tqent_arg = arg;
    143 	t->tqent_flags = 0;
    144 	cv_signal(&tq->tq_dispatch_cv);
    145 	mutex_exit(&tq->tq_lock);
    146 	return (1);
    147 }
    148 
    149 void
    150 taskq_dispatch_ent(taskq_t *tq, task_func_t func, void *arg, uint_t flags,
    151     taskq_ent_t *t)
    152 {
    153 	ASSERT(func != NULL);
    154 	ASSERT(!(tq->tq_flags & TASKQ_DYNAMIC));
    155 
    156 	/*
    157 	 * Mark it as a prealloc'd task.  This is important
    158 	 * to ensure that we don't free it later.
    159 	 */
    160 	t->tqent_flags |= TQENT_FLAG_PREALLOC;
    161 	/*
    162 	 * Enqueue the task to the underlying queue.
    163 	 */
    164 	mutex_enter(&tq->tq_lock);
    165 
    166 	if (flags & TQ_FRONT) {
    167 		t->tqent_next = tq->tq_task.tqent_next;
    168 		t->tqent_prev = &tq->tq_task;
    169 	} else {
    170 		t->tqent_next = &tq->tq_task;
    171 		t->tqent_prev = tq->tq_task.tqent_prev;
    172 	}
    173 	t->tqent_next->tqent_prev = t;
    174 	t->tqent_prev->tqent_next = t;
    175 	t->tqent_func = func;
    176 	t->tqent_arg = arg;
    177 	cv_signal(&tq->tq_dispatch_cv);
    178 	mutex_exit(&tq->tq_lock);
    179 }
    180 
    181 void
    182 taskq_wait(taskq_t *tq)
    183 {
    184 	mutex_enter(&tq->tq_lock);
    185 	while (tq->tq_task.tqent_next != &tq->tq_task || tq->tq_active != 0)
    186 		cv_wait(&tq->tq_wait_cv, &tq->tq_lock);
    187 	mutex_exit(&tq->tq_lock);
    188 }
    189 
    190 static void *
    191 taskq_thread(void *arg)
    192 {
    193 	taskq_t *tq = arg;
    194 	taskq_ent_t *t;
    195 	boolean_t prealloc;
    196 
    197 	mutex_enter(&tq->tq_lock);
    198 	while (tq->tq_flags & TASKQ_ACTIVE) {
    199 		if ((t = tq->tq_task.tqent_next) == &tq->tq_task) {
    200 			if (--tq->tq_active == 0)
    201 				cv_broadcast(&tq->tq_wait_cv);
    202 			cv_wait(&tq->tq_dispatch_cv, &tq->tq_lock);
    203 			tq->tq_active++;
    204 			continue;
    205 		}
    206 		t->tqent_prev->tqent_next = t->tqent_next;
    207 		t->tqent_next->tqent_prev = t->tqent_prev;
    208 		t->tqent_next = NULL;
    209 		t->tqent_prev = NULL;
    210 		prealloc = t->tqent_flags & TQENT_FLAG_PREALLOC;
    211 		mutex_exit(&tq->tq_lock);
    212 
    213 		rw_enter(&tq->tq_threadlock, RW_READER);
    214 		t->tqent_func(t->tqent_arg);
    215 		rw_exit(&tq->tq_threadlock);
    216 
    217 		mutex_enter(&tq->tq_lock);
    218 		if (!prealloc)
    219 			task_free(tq, t);
    220 	}
    221 	tq->tq_nthreads--;
    222 	cv_broadcast(&tq->tq_wait_cv);
    223 	mutex_exit(&tq->tq_lock);
    224 	return (NULL);
    225 }
    226 
    227 /*ARGSUSED*/
    228 taskq_t *
    229 taskq_create(const char *name, int nthreads, pri_t pri,
    230 	int minalloc, int maxalloc, uint_t flags)
    231 {
    232 	taskq_t *tq = kmem_zalloc(sizeof (taskq_t), KM_SLEEP);
    233 	int t;
    234 
    235 	if (flags & TASKQ_THREADS_CPU_PCT) {
    236 		int pct;
    237 		ASSERT3S(nthreads, >=, 0);
    238 		ASSERT3S(nthreads, <=, 100);
    239 		pct = MIN(nthreads, 100);
    240 		pct = MAX(pct, 0);
    241 
    242 		nthreads = (sysconf(_SC_NPROCESSORS_ONLN) * pct) / 100;
    243 		nthreads = MAX(nthreads, 1);	/* need at least 1 thread */
    244 	} else {
    245 		ASSERT3S(nthreads, >=, 1);
    246 	}
    247 
    248 	rw_init(&tq->tq_threadlock, NULL, RW_DEFAULT, NULL);
    249 	mutex_init(&tq->tq_lock, NULL, MUTEX_DEFAULT, NULL);
    250 	cv_init(&tq->tq_dispatch_cv, NULL, CV_DEFAULT, NULL);
    251 	cv_init(&tq->tq_wait_cv, NULL, CV_DEFAULT, NULL);
    252 	cv_init(&tq->tq_maxalloc_cv, NULL, CV_DEFAULT, NULL);
    253 	(void) strncpy(tq->tq_name, name, TASKQ_NAMELEN + 1);
    254 	tq->tq_flags = flags | TASKQ_ACTIVE;
    255 	tq->tq_active = nthreads;
    256 	tq->tq_nthreads = nthreads;
    257 	tq->tq_minalloc = minalloc;
    258 	tq->tq_maxalloc = maxalloc;
    259 	tq->tq_task.tqent_next = &tq->tq_task;
    260 	tq->tq_task.tqent_prev = &tq->tq_task;
    261 	tq->tq_threadlist = kmem_alloc(nthreads * sizeof (thread_t), KM_SLEEP);
    262 
    263 	if (flags & TASKQ_PREPOPULATE) {
    264 		mutex_enter(&tq->tq_lock);
    265 		while (minalloc-- > 0)
    266 			task_free(tq, task_alloc(tq, KM_SLEEP));
    267 		mutex_exit(&tq->tq_lock);
    268 	}
    269 
    270 	for (t = 0; t < nthreads; t++)
    271 		(void) thr_create(0, 0, taskq_thread,
    272 		    tq, THR_BOUND, &tq->tq_threadlist[t]);
    273 
    274 	return (tq);
    275 }
    276 
    277 void
    278 taskq_destroy(taskq_t *tq)
    279 {
    280 	int t;
    281 	int nthreads = tq->tq_nthreads;
    282 
    283 	taskq_wait(tq);
    284 
    285 	mutex_enter(&tq->tq_lock);
    286 
    287 	tq->tq_flags &= ~TASKQ_ACTIVE;
    288 	cv_broadcast(&tq->tq_dispatch_cv);
    289 
    290 	while (tq->tq_nthreads != 0)
    291 		cv_wait(&tq->tq_wait_cv, &tq->tq_lock);
    292 
    293 	tq->tq_minalloc = 0;
    294 	while (tq->tq_nalloc != 0) {
    295 		ASSERT(tq->tq_freelist != NULL);
    296 		task_free(tq, task_alloc(tq, KM_SLEEP));
    297 	}
    298 
    299 	mutex_exit(&tq->tq_lock);
    300 
    301 	for (t = 0; t < nthreads; t++)
    302 		(void) thr_join(tq->tq_threadlist[t], NULL, NULL);
    303 
    304 	kmem_free(tq->tq_threadlist, nthreads * sizeof (thread_t));
    305 
    306 	rw_destroy(&tq->tq_threadlock);
    307 	mutex_destroy(&tq->tq_lock);
    308 	cv_destroy(&tq->tq_dispatch_cv);
    309 	cv_destroy(&tq->tq_wait_cv);
    310 	cv_destroy(&tq->tq_maxalloc_cv);
    311 
    312 	kmem_free(tq, sizeof (taskq_t));
    313 }
    314 
    315 int
    316 taskq_member(taskq_t *tq, void *t)
    317 {
    318 	int i;
    319 
    320 	if (taskq_now)
    321 		return (1);
    322 
    323 	for (i = 0; i < tq->tq_nthreads; i++)
    324 		if (tq->tq_threadlist[i] == (thread_t)(uintptr_t)t)
    325 			return (1);
    326 
    327 	return (0);
    328 }
    329 
    330 void
    331 system_taskq_init(void)
    332 {
    333 	system_taskq = taskq_create("system_taskq", 64, minclsyspri, 4, 512,
    334 	    TASKQ_DYNAMIC | TASKQ_PREPOPULATE);
    335 }
    336 
    337 void
    338 system_taskq_fini(void)
    339 {
    340 	taskq_destroy(system_taskq);
    341 	system_taskq = NULL; /* defensive */
    342 }
    343