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 * $FreeBSD$ 27ca2e0534SDoug Rabson */ 28ca2e0534SDoug Rabson 29ca2e0534SDoug Rabson #include <sys/param.h> 30ca2e0534SDoug Rabson #include <sys/systm.h> 311de1c550SJohn Baldwin #include <sys/bus.h> 32282873e2SJohn Baldwin #include <sys/interrupt.h> 33ca2e0534SDoug Rabson #include <sys/kernel.h> 341de1c550SJohn Baldwin #include <sys/lock.h> 35ca2e0534SDoug Rabson #include <sys/malloc.h> 361de1c550SJohn Baldwin #include <sys/mutex.h> 371de1c550SJohn Baldwin #include <sys/taskqueue.h> 38ca2e0534SDoug Rabson 39959b7375SPoul-Henning Kamp static MALLOC_DEFINE(M_TASKQUEUE, "taskqueue", "Task Queues"); 40ca2e0534SDoug Rabson 41ca2e0534SDoug Rabson static STAILQ_HEAD(taskqueue_list, taskqueue) taskqueue_queues; 42ca2e0534SDoug Rabson 43062d8ff5SJohn Baldwin static void *taskqueue_ih; 441de1c550SJohn Baldwin static struct mtx taskqueue_queues_mutex; 458088699fSJohn Baldwin 46ca2e0534SDoug Rabson struct taskqueue { 47ca2e0534SDoug Rabson STAILQ_ENTRY(taskqueue) tq_link; 48ca2e0534SDoug Rabson STAILQ_HEAD(, task) tq_queue; 49ca2e0534SDoug Rabson const char *tq_name; 50ca2e0534SDoug Rabson taskqueue_enqueue_fn tq_enqueue; 51ca2e0534SDoug Rabson void *tq_context; 52ca2e0534SDoug Rabson int tq_draining; 531de1c550SJohn Baldwin struct mtx tq_mutex; 54ca2e0534SDoug Rabson }; 55ca2e0534SDoug Rabson 561de1c550SJohn Baldwin static void init_taskqueue_list(void *data); 571de1c550SJohn Baldwin 581de1c550SJohn Baldwin static void 591de1c550SJohn Baldwin init_taskqueue_list(void *data __unused) 601de1c550SJohn Baldwin { 611de1c550SJohn Baldwin 621de1c550SJohn Baldwin mtx_init(&taskqueue_queues_mutex, "taskqueue list", MTX_DEF); 631de1c550SJohn Baldwin STAILQ_INIT(&taskqueue_queues); 641de1c550SJohn Baldwin } 651de1c550SJohn Baldwin SYSINIT(taskqueue_list, SI_SUB_INTRINSIC, SI_ORDER_ANY, init_taskqueue_list, 661de1c550SJohn Baldwin NULL); 671de1c550SJohn Baldwin 68ca2e0534SDoug Rabson struct taskqueue * 69ca2e0534SDoug Rabson taskqueue_create(const char *name, int mflags, 70ca2e0534SDoug Rabson taskqueue_enqueue_fn enqueue, void *context) 71ca2e0534SDoug Rabson { 72ca2e0534SDoug Rabson struct taskqueue *queue; 73ca2e0534SDoug Rabson 741de1c550SJohn Baldwin queue = malloc(sizeof(struct taskqueue), M_TASKQUEUE, mflags | M_ZERO); 75ca2e0534SDoug Rabson if (!queue) 76ca2e0534SDoug Rabson return 0; 771de1c550SJohn Baldwin 78ca2e0534SDoug Rabson STAILQ_INIT(&queue->tq_queue); 79ca2e0534SDoug Rabson queue->tq_name = name; 80ca2e0534SDoug Rabson queue->tq_enqueue = enqueue; 81ca2e0534SDoug Rabson queue->tq_context = context; 82ca2e0534SDoug Rabson queue->tq_draining = 0; 831de1c550SJohn Baldwin mtx_init(&queue->tq_mutex, "taskqueue", MTX_DEF); 84ca2e0534SDoug Rabson 851de1c550SJohn Baldwin mtx_lock(&taskqueue_queues_mutex); 86ca2e0534SDoug Rabson STAILQ_INSERT_TAIL(&taskqueue_queues, queue, tq_link); 871de1c550SJohn Baldwin mtx_unlock(&taskqueue_queues_mutex); 88ca2e0534SDoug Rabson 89ca2e0534SDoug Rabson return queue; 90ca2e0534SDoug Rabson } 91ca2e0534SDoug Rabson 92ca2e0534SDoug Rabson void 93ca2e0534SDoug Rabson taskqueue_free(struct taskqueue *queue) 94ca2e0534SDoug Rabson { 951de1c550SJohn Baldwin 961de1c550SJohn Baldwin mtx_lock(&queue->tq_mutex); 97ca2e0534SDoug Rabson queue->tq_draining = 1; 981de1c550SJohn Baldwin mtx_unlock(&queue->tq_mutex); 99ca2e0534SDoug Rabson 100ca2e0534SDoug Rabson taskqueue_run(queue); 101ca2e0534SDoug Rabson 1021de1c550SJohn Baldwin mtx_lock(&taskqueue_queues_mutex); 103ca2e0534SDoug Rabson STAILQ_REMOVE(&taskqueue_queues, queue, taskqueue, tq_link); 1041de1c550SJohn Baldwin mtx_unlock(&taskqueue_queues_mutex); 105ca2e0534SDoug Rabson 1061de1c550SJohn Baldwin mtx_destroy(&queue->tq_mutex); 107ca2e0534SDoug Rabson free(queue, M_TASKQUEUE); 108ca2e0534SDoug Rabson } 109ca2e0534SDoug Rabson 1101de1c550SJohn Baldwin /* 1111de1c550SJohn Baldwin * Returns with the taskqueue locked. 1121de1c550SJohn Baldwin */ 113ca2e0534SDoug Rabson struct taskqueue * 114ca2e0534SDoug Rabson taskqueue_find(const char *name) 115ca2e0534SDoug Rabson { 116ca2e0534SDoug Rabson struct taskqueue *queue; 117ca2e0534SDoug Rabson 1181de1c550SJohn Baldwin mtx_lock(&taskqueue_queues_mutex); 1191de1c550SJohn Baldwin STAILQ_FOREACH(queue, &taskqueue_queues, tq_link) { 1201de1c550SJohn Baldwin mtx_lock(&queue->tq_mutex); 121ca2e0534SDoug Rabson if (!strcmp(queue->tq_name, name)) { 1221de1c550SJohn Baldwin mtx_unlock(&taskqueue_queues_mutex); 123ca2e0534SDoug Rabson return queue; 124ca2e0534SDoug Rabson } 1251de1c550SJohn Baldwin mtx_unlock(&queue->tq_mutex); 1261de1c550SJohn Baldwin } 1271de1c550SJohn Baldwin mtx_unlock(&taskqueue_queues_mutex); 128ca2e0534SDoug Rabson return 0; 129ca2e0534SDoug Rabson } 130ca2e0534SDoug Rabson 131ca2e0534SDoug Rabson int 132ca2e0534SDoug Rabson taskqueue_enqueue(struct taskqueue *queue, struct task *task) 133ca2e0534SDoug Rabson { 134ca2e0534SDoug Rabson struct task *ins; 135ca2e0534SDoug Rabson struct task *prev; 136ca2e0534SDoug Rabson 137282873e2SJohn Baldwin mtx_lock(&queue->tq_mutex); 138282873e2SJohn Baldwin 139ca2e0534SDoug Rabson /* 140ca2e0534SDoug Rabson * Don't allow new tasks on a queue which is being freed. 141ca2e0534SDoug Rabson */ 142ca2e0534SDoug Rabson if (queue->tq_draining) { 1431de1c550SJohn Baldwin mtx_unlock(&queue->tq_mutex); 144ca2e0534SDoug Rabson return EPIPE; 145ca2e0534SDoug Rabson } 146ca2e0534SDoug Rabson 147ca2e0534SDoug Rabson /* 148ca2e0534SDoug Rabson * Count multiple enqueues. 149ca2e0534SDoug Rabson */ 150ca2e0534SDoug Rabson if (task->ta_pending) { 151ca2e0534SDoug Rabson task->ta_pending++; 1521de1c550SJohn Baldwin mtx_unlock(&queue->tq_mutex); 153ca2e0534SDoug Rabson return 0; 154ca2e0534SDoug Rabson } 155ca2e0534SDoug Rabson 156ca2e0534SDoug Rabson /* 157ca2e0534SDoug Rabson * Optimise the case when all tasks have the same priority. 158ca2e0534SDoug Rabson */ 15951b86781SJeffrey Hsu prev = STAILQ_LAST(&queue->tq_queue, task, ta_link); 160ca2e0534SDoug Rabson if (!prev || prev->ta_priority >= task->ta_priority) { 161ca2e0534SDoug Rabson STAILQ_INSERT_TAIL(&queue->tq_queue, task, ta_link); 162ca2e0534SDoug Rabson } else { 163ca2e0534SDoug Rabson prev = 0; 164ca2e0534SDoug Rabson for (ins = STAILQ_FIRST(&queue->tq_queue); ins; 165ca2e0534SDoug Rabson prev = ins, ins = STAILQ_NEXT(ins, ta_link)) 166ca2e0534SDoug Rabson if (ins->ta_priority < task->ta_priority) 167ca2e0534SDoug Rabson break; 168ca2e0534SDoug Rabson 169ca2e0534SDoug Rabson if (prev) 170ca2e0534SDoug Rabson STAILQ_INSERT_AFTER(&queue->tq_queue, prev, task, ta_link); 171ca2e0534SDoug Rabson else 172ca2e0534SDoug Rabson STAILQ_INSERT_HEAD(&queue->tq_queue, task, ta_link); 173ca2e0534SDoug Rabson } 174ca2e0534SDoug Rabson 175ca2e0534SDoug Rabson task->ta_pending = 1; 176ca2e0534SDoug Rabson if (queue->tq_enqueue) 177ca2e0534SDoug Rabson queue->tq_enqueue(queue->tq_context); 178282873e2SJohn Baldwin 1791de1c550SJohn Baldwin mtx_unlock(&queue->tq_mutex); 180282873e2SJohn Baldwin 181ca2e0534SDoug Rabson return 0; 182ca2e0534SDoug Rabson } 183ca2e0534SDoug Rabson 184ca2e0534SDoug Rabson void 185ca2e0534SDoug Rabson taskqueue_run(struct taskqueue *queue) 186ca2e0534SDoug Rabson { 187ca2e0534SDoug Rabson struct task *task; 188ca2e0534SDoug Rabson int pending; 189ca2e0534SDoug Rabson 1901de1c550SJohn Baldwin mtx_lock(&queue->tq_mutex); 191ca2e0534SDoug Rabson while (STAILQ_FIRST(&queue->tq_queue)) { 192ca2e0534SDoug Rabson /* 193ca2e0534SDoug Rabson * Carefully remove the first task from the queue and 194ca2e0534SDoug Rabson * zero its pending count. 195ca2e0534SDoug Rabson */ 196ca2e0534SDoug Rabson task = STAILQ_FIRST(&queue->tq_queue); 197ca2e0534SDoug Rabson STAILQ_REMOVE_HEAD(&queue->tq_queue, ta_link); 198ca2e0534SDoug Rabson pending = task->ta_pending; 199ca2e0534SDoug Rabson task->ta_pending = 0; 200282873e2SJohn Baldwin mtx_unlock(&queue->tq_mutex); 201ca2e0534SDoug Rabson 202282873e2SJohn Baldwin task->ta_func(task->ta_context, pending); 203ca2e0534SDoug Rabson 2041de1c550SJohn Baldwin mtx_lock(&queue->tq_mutex); 205ca2e0534SDoug Rabson } 2061de1c550SJohn Baldwin mtx_unlock(&queue->tq_mutex); 207ca2e0534SDoug Rabson } 208ca2e0534SDoug Rabson 209ca2e0534SDoug Rabson static void 210ca2e0534SDoug Rabson taskqueue_swi_enqueue(void *context) 211ca2e0534SDoug Rabson { 212c86b6ff5SJohn Baldwin swi_sched(taskqueue_ih, 0); 213ca2e0534SDoug Rabson } 214ca2e0534SDoug Rabson 215ca2e0534SDoug Rabson static void 2168088699fSJohn Baldwin taskqueue_swi_run(void *dummy) 217ca2e0534SDoug Rabson { 218ca2e0534SDoug Rabson taskqueue_run(taskqueue_swi); 219ca2e0534SDoug Rabson } 220ca2e0534SDoug Rabson 221ca2e0534SDoug Rabson TASKQUEUE_DEFINE(swi, taskqueue_swi_enqueue, 0, 222062d8ff5SJohn Baldwin swi_add(NULL, "task queue", taskqueue_swi_run, NULL, SWI_TQ, 0, 223062d8ff5SJohn Baldwin &taskqueue_ih)); 224