1ca2e0534SDoug Rabson /*- 2ca2e0534SDoug Rabson * Copyright (c) 2000 Doug Rabson 3ca2e0534SDoug Rabson * All rights reserved. 4ca2e0534SDoug Rabson * 5ca2e0534SDoug Rabson * Redistribution and use in source and binary forms, with or without 6ca2e0534SDoug Rabson * modification, are permitted provided that the following conditions 7ca2e0534SDoug Rabson * are met: 8ca2e0534SDoug Rabson * 1. Redistributions of source code must retain the above copyright 9ca2e0534SDoug Rabson * notice, this list of conditions and the following disclaimer. 10ca2e0534SDoug Rabson * 2. Redistributions in binary form must reproduce the above copyright 11ca2e0534SDoug Rabson * notice, this list of conditions and the following disclaimer in the 12ca2e0534SDoug Rabson * documentation and/or other materials provided with the distribution. 13ca2e0534SDoug Rabson * 14ca2e0534SDoug Rabson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15ca2e0534SDoug Rabson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16ca2e0534SDoug Rabson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17ca2e0534SDoug Rabson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18ca2e0534SDoug Rabson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19ca2e0534SDoug Rabson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20ca2e0534SDoug Rabson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21ca2e0534SDoug Rabson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22ca2e0534SDoug Rabson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23ca2e0534SDoug Rabson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24ca2e0534SDoug Rabson * SUCH DAMAGE. 25ca2e0534SDoug Rabson */ 26ca2e0534SDoug Rabson 27677b542eSDavid E. O'Brien #include <sys/cdefs.h> 28677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$"); 29677b542eSDavid E. O'Brien 30ca2e0534SDoug Rabson #include <sys/param.h> 31ca2e0534SDoug Rabson #include <sys/systm.h> 321de1c550SJohn Baldwin #include <sys/bus.h> 33282873e2SJohn Baldwin #include <sys/interrupt.h> 34ca2e0534SDoug Rabson #include <sys/kernel.h> 351de1c550SJohn Baldwin #include <sys/lock.h> 36ca2e0534SDoug Rabson #include <sys/malloc.h> 371de1c550SJohn Baldwin #include <sys/mutex.h> 381de1c550SJohn Baldwin #include <sys/taskqueue.h> 39cb32189eSKenneth D. Merry #include <sys/kthread.h> 40cb32189eSKenneth D. Merry #include <sys/unistd.h> 41ca2e0534SDoug Rabson 42959b7375SPoul-Henning Kamp static MALLOC_DEFINE(M_TASKQUEUE, "taskqueue", "Task Queues"); 43ca2e0534SDoug Rabson 44ca2e0534SDoug Rabson static STAILQ_HEAD(taskqueue_list, taskqueue) taskqueue_queues; 45ca2e0534SDoug Rabson 46062d8ff5SJohn Baldwin static void *taskqueue_ih; 477874f606SScott Long static void *taskqueue_giant_ih; 481de1c550SJohn Baldwin static struct mtx taskqueue_queues_mutex; 49cb32189eSKenneth D. Merry static struct proc *taskqueue_thread_proc; 508088699fSJohn Baldwin 51ca2e0534SDoug Rabson struct taskqueue { 52ca2e0534SDoug Rabson STAILQ_ENTRY(taskqueue) tq_link; 53ca2e0534SDoug Rabson STAILQ_HEAD(, task) tq_queue; 54ca2e0534SDoug Rabson const char *tq_name; 55ca2e0534SDoug Rabson taskqueue_enqueue_fn tq_enqueue; 56ca2e0534SDoug Rabson void *tq_context; 57ca2e0534SDoug Rabson int tq_draining; 581de1c550SJohn Baldwin struct mtx tq_mutex; 59ca2e0534SDoug Rabson }; 60ca2e0534SDoug Rabson 611de1c550SJohn Baldwin static void init_taskqueue_list(void *data); 621de1c550SJohn Baldwin 631de1c550SJohn Baldwin static void 641de1c550SJohn Baldwin init_taskqueue_list(void *data __unused) 651de1c550SJohn Baldwin { 661de1c550SJohn Baldwin 676008862bSJohn Baldwin mtx_init(&taskqueue_queues_mutex, "taskqueue list", NULL, MTX_DEF); 681de1c550SJohn Baldwin STAILQ_INIT(&taskqueue_queues); 691de1c550SJohn Baldwin } 701de1c550SJohn Baldwin SYSINIT(taskqueue_list, SI_SUB_INTRINSIC, SI_ORDER_ANY, init_taskqueue_list, 711de1c550SJohn Baldwin NULL); 721de1c550SJohn Baldwin 73ca2e0534SDoug Rabson struct taskqueue * 74ca2e0534SDoug Rabson taskqueue_create(const char *name, int mflags, 75ca2e0534SDoug Rabson taskqueue_enqueue_fn enqueue, void *context) 76ca2e0534SDoug Rabson { 77ca2e0534SDoug Rabson struct taskqueue *queue; 78ca2e0534SDoug Rabson 791de1c550SJohn Baldwin queue = malloc(sizeof(struct taskqueue), M_TASKQUEUE, mflags | M_ZERO); 80ca2e0534SDoug Rabson if (!queue) 81ca2e0534SDoug Rabson return 0; 821de1c550SJohn Baldwin 83ca2e0534SDoug Rabson STAILQ_INIT(&queue->tq_queue); 84ca2e0534SDoug Rabson queue->tq_name = name; 85ca2e0534SDoug Rabson queue->tq_enqueue = enqueue; 86ca2e0534SDoug Rabson queue->tq_context = context; 87ca2e0534SDoug Rabson queue->tq_draining = 0; 886008862bSJohn Baldwin mtx_init(&queue->tq_mutex, "taskqueue", NULL, MTX_DEF); 89ca2e0534SDoug Rabson 901de1c550SJohn Baldwin mtx_lock(&taskqueue_queues_mutex); 91ca2e0534SDoug Rabson STAILQ_INSERT_TAIL(&taskqueue_queues, queue, tq_link); 921de1c550SJohn Baldwin mtx_unlock(&taskqueue_queues_mutex); 93ca2e0534SDoug Rabson 94ca2e0534SDoug Rabson return queue; 95ca2e0534SDoug Rabson } 96ca2e0534SDoug Rabson 97ca2e0534SDoug Rabson void 98ca2e0534SDoug Rabson taskqueue_free(struct taskqueue *queue) 99ca2e0534SDoug Rabson { 1001de1c550SJohn Baldwin 1011de1c550SJohn Baldwin mtx_lock(&queue->tq_mutex); 102fbd140c7SJohn Baldwin KASSERT(queue->tq_draining == 0, ("free'ing a draining taskqueue")); 103ca2e0534SDoug Rabson queue->tq_draining = 1; 1041de1c550SJohn Baldwin mtx_unlock(&queue->tq_mutex); 105ca2e0534SDoug Rabson 106ca2e0534SDoug Rabson taskqueue_run(queue); 107ca2e0534SDoug Rabson 1081de1c550SJohn Baldwin mtx_lock(&taskqueue_queues_mutex); 109ca2e0534SDoug Rabson STAILQ_REMOVE(&taskqueue_queues, queue, taskqueue, tq_link); 1101de1c550SJohn Baldwin mtx_unlock(&taskqueue_queues_mutex); 111ca2e0534SDoug Rabson 1121de1c550SJohn Baldwin mtx_destroy(&queue->tq_mutex); 113ca2e0534SDoug Rabson free(queue, M_TASKQUEUE); 114ca2e0534SDoug Rabson } 115ca2e0534SDoug Rabson 1161de1c550SJohn Baldwin /* 1171de1c550SJohn Baldwin * Returns with the taskqueue locked. 1181de1c550SJohn Baldwin */ 119ca2e0534SDoug Rabson struct taskqueue * 120ca2e0534SDoug Rabson taskqueue_find(const char *name) 121ca2e0534SDoug Rabson { 122ca2e0534SDoug Rabson struct taskqueue *queue; 123ca2e0534SDoug Rabson 1241de1c550SJohn Baldwin mtx_lock(&taskqueue_queues_mutex); 1251de1c550SJohn Baldwin STAILQ_FOREACH(queue, &taskqueue_queues, tq_link) { 1261de1c550SJohn Baldwin mtx_lock(&queue->tq_mutex); 127ca2e0534SDoug Rabson if (!strcmp(queue->tq_name, name)) { 1281de1c550SJohn Baldwin mtx_unlock(&taskqueue_queues_mutex); 129ca2e0534SDoug Rabson return queue; 130ca2e0534SDoug Rabson } 1311de1c550SJohn Baldwin mtx_unlock(&queue->tq_mutex); 1321de1c550SJohn Baldwin } 1331de1c550SJohn Baldwin mtx_unlock(&taskqueue_queues_mutex); 134ca2e0534SDoug Rabson return 0; 135ca2e0534SDoug Rabson } 136ca2e0534SDoug Rabson 137ca2e0534SDoug Rabson int 138ca2e0534SDoug Rabson taskqueue_enqueue(struct taskqueue *queue, struct task *task) 139ca2e0534SDoug Rabson { 140ca2e0534SDoug Rabson struct task *ins; 141ca2e0534SDoug Rabson struct task *prev; 142ca2e0534SDoug Rabson 143282873e2SJohn Baldwin mtx_lock(&queue->tq_mutex); 144282873e2SJohn Baldwin 145ca2e0534SDoug Rabson /* 146ca2e0534SDoug Rabson * Don't allow new tasks on a queue which is being freed. 147ca2e0534SDoug Rabson */ 148ca2e0534SDoug Rabson if (queue->tq_draining) { 1491de1c550SJohn Baldwin mtx_unlock(&queue->tq_mutex); 150ca2e0534SDoug Rabson return EPIPE; 151ca2e0534SDoug Rabson } 152ca2e0534SDoug Rabson 153ca2e0534SDoug Rabson /* 154ca2e0534SDoug Rabson * Count multiple enqueues. 155ca2e0534SDoug Rabson */ 156ca2e0534SDoug Rabson if (task->ta_pending) { 157ca2e0534SDoug Rabson task->ta_pending++; 1581de1c550SJohn Baldwin mtx_unlock(&queue->tq_mutex); 159ca2e0534SDoug Rabson return 0; 160ca2e0534SDoug Rabson } 161ca2e0534SDoug Rabson 162ca2e0534SDoug Rabson /* 163ca2e0534SDoug Rabson * Optimise the case when all tasks have the same priority. 164ca2e0534SDoug Rabson */ 16551b86781SJeffrey Hsu prev = STAILQ_LAST(&queue->tq_queue, task, ta_link); 166ca2e0534SDoug Rabson if (!prev || prev->ta_priority >= task->ta_priority) { 167ca2e0534SDoug Rabson STAILQ_INSERT_TAIL(&queue->tq_queue, task, ta_link); 168ca2e0534SDoug Rabson } else { 169ca2e0534SDoug Rabson prev = 0; 170ca2e0534SDoug Rabson for (ins = STAILQ_FIRST(&queue->tq_queue); ins; 171ca2e0534SDoug Rabson prev = ins, ins = STAILQ_NEXT(ins, ta_link)) 172ca2e0534SDoug Rabson if (ins->ta_priority < task->ta_priority) 173ca2e0534SDoug Rabson break; 174ca2e0534SDoug Rabson 175ca2e0534SDoug Rabson if (prev) 176ca2e0534SDoug Rabson STAILQ_INSERT_AFTER(&queue->tq_queue, prev, task, ta_link); 177ca2e0534SDoug Rabson else 178ca2e0534SDoug Rabson STAILQ_INSERT_HEAD(&queue->tq_queue, task, ta_link); 179ca2e0534SDoug Rabson } 180ca2e0534SDoug Rabson 181ca2e0534SDoug Rabson task->ta_pending = 1; 182ca2e0534SDoug Rabson if (queue->tq_enqueue) 183ca2e0534SDoug Rabson queue->tq_enqueue(queue->tq_context); 184282873e2SJohn Baldwin 1851de1c550SJohn Baldwin mtx_unlock(&queue->tq_mutex); 186282873e2SJohn Baldwin 187ca2e0534SDoug Rabson return 0; 188ca2e0534SDoug Rabson } 189ca2e0534SDoug Rabson 190ca2e0534SDoug Rabson void 191ca2e0534SDoug Rabson taskqueue_run(struct taskqueue *queue) 192ca2e0534SDoug Rabson { 193ca2e0534SDoug Rabson struct task *task; 194ca2e0534SDoug Rabson int pending; 195ca2e0534SDoug Rabson 1961de1c550SJohn Baldwin mtx_lock(&queue->tq_mutex); 197ca2e0534SDoug Rabson while (STAILQ_FIRST(&queue->tq_queue)) { 198ca2e0534SDoug Rabson /* 199ca2e0534SDoug Rabson * Carefully remove the first task from the queue and 200ca2e0534SDoug Rabson * zero its pending count. 201ca2e0534SDoug Rabson */ 202ca2e0534SDoug Rabson task = STAILQ_FIRST(&queue->tq_queue); 203ca2e0534SDoug Rabson STAILQ_REMOVE_HEAD(&queue->tq_queue, ta_link); 204ca2e0534SDoug Rabson pending = task->ta_pending; 205ca2e0534SDoug Rabson task->ta_pending = 0; 206282873e2SJohn Baldwin mtx_unlock(&queue->tq_mutex); 207ca2e0534SDoug Rabson 208282873e2SJohn Baldwin task->ta_func(task->ta_context, pending); 209ca2e0534SDoug Rabson 2101de1c550SJohn Baldwin mtx_lock(&queue->tq_mutex); 211ca2e0534SDoug Rabson } 2121de1c550SJohn Baldwin mtx_unlock(&queue->tq_mutex); 213ca2e0534SDoug Rabson } 214ca2e0534SDoug Rabson 215ca2e0534SDoug Rabson static void 216ca2e0534SDoug Rabson taskqueue_swi_enqueue(void *context) 217ca2e0534SDoug Rabson { 218c86b6ff5SJohn Baldwin swi_sched(taskqueue_ih, 0); 219ca2e0534SDoug Rabson } 220ca2e0534SDoug Rabson 221ca2e0534SDoug Rabson static void 2228088699fSJohn Baldwin taskqueue_swi_run(void *dummy) 223ca2e0534SDoug Rabson { 224ca2e0534SDoug Rabson taskqueue_run(taskqueue_swi); 225ca2e0534SDoug Rabson } 226ca2e0534SDoug Rabson 2277874f606SScott Long static void 2287874f606SScott Long taskqueue_swi_giant_enqueue(void *context) 2297874f606SScott Long { 2307874f606SScott Long swi_sched(taskqueue_giant_ih, 0); 2317874f606SScott Long } 2327874f606SScott Long 2337874f606SScott Long static void 2347874f606SScott Long taskqueue_swi_giant_run(void *dummy) 2357874f606SScott Long { 2367874f606SScott Long taskqueue_run(taskqueue_swi_giant); 2377874f606SScott Long } 2387874f606SScott Long 239cb32189eSKenneth D. Merry static void 240cb32189eSKenneth D. Merry taskqueue_kthread(void *arg) 241cb32189eSKenneth D. Merry { 242cb32189eSKenneth D. Merry struct mtx kthread_mutex; 243cb32189eSKenneth D. Merry 244cb32189eSKenneth D. Merry bzero(&kthread_mutex, sizeof(kthread_mutex)); 245cb32189eSKenneth D. Merry 246cb32189eSKenneth D. Merry mtx_init(&kthread_mutex, "taskqueue kthread", NULL, MTX_DEF); 247cb32189eSKenneth D. Merry 248cb32189eSKenneth D. Merry mtx_lock(&kthread_mutex); 249cb32189eSKenneth D. Merry 250cb32189eSKenneth D. Merry for (;;) { 251cb32189eSKenneth D. Merry mtx_unlock(&kthread_mutex); 252cb32189eSKenneth D. Merry taskqueue_run(taskqueue_thread); 253cb32189eSKenneth D. Merry mtx_lock(&kthread_mutex); 254cb32189eSKenneth D. Merry msleep(&taskqueue_thread, &kthread_mutex, PWAIT, "tqthr", 0); 255cb32189eSKenneth D. Merry } 256cb32189eSKenneth D. Merry } 257cb32189eSKenneth D. Merry 258cb32189eSKenneth D. Merry static void 259cb32189eSKenneth D. Merry taskqueue_thread_enqueue(void *context) 260cb32189eSKenneth D. Merry { 261cb32189eSKenneth D. Merry wakeup(&taskqueue_thread); 262cb32189eSKenneth D. Merry } 263cb32189eSKenneth D. Merry 264ca2e0534SDoug Rabson TASKQUEUE_DEFINE(swi, taskqueue_swi_enqueue, 0, 2657874f606SScott Long swi_add(NULL, "task queue", taskqueue_swi_run, NULL, SWI_TQ, 2667874f606SScott Long INTR_MPSAFE, &taskqueue_ih)); 2677874f606SScott Long 2687874f606SScott Long TASKQUEUE_DEFINE(swi_giant, taskqueue_swi_giant_enqueue, 0, 2697874f606SScott Long swi_add(NULL, "Giant task queue", taskqueue_swi_giant_run, 2707874f606SScott Long NULL, SWI_TQ_GIANT, 0, &taskqueue_giant_ih)); 271cb32189eSKenneth D. Merry 272cb32189eSKenneth D. Merry TASKQUEUE_DEFINE(thread, taskqueue_thread_enqueue, 0, 273cb32189eSKenneth D. Merry kthread_create(taskqueue_kthread, NULL, 274cb32189eSKenneth D. Merry &taskqueue_thread_proc, RFNOWAIT, 0, "taskqueue")); 275