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> 309a94c9c5SJohn Baldwin #include <sys/bus.h> 31ca2e0534SDoug Rabson #include <sys/queue.h> 32ca2e0534SDoug Rabson #include <sys/systm.h> 33ca2e0534SDoug Rabson #include <sys/kernel.h> 34ca2e0534SDoug Rabson #include <sys/taskqueue.h> 35ca2e0534SDoug Rabson #include <sys/interrupt.h> 361931cf94SJohn Baldwin #include <sys/ipl.h> 37ca2e0534SDoug Rabson #include <sys/malloc.h> 38ca2e0534SDoug Rabson 39ca2e0534SDoug Rabson MALLOC_DEFINE(M_TASKQUEUE, "taskqueue", "Task Queues"); 40ca2e0534SDoug Rabson 41ca2e0534SDoug Rabson static STAILQ_HEAD(taskqueue_list, taskqueue) taskqueue_queues; 42ca2e0534SDoug Rabson 438088699fSJohn Baldwin static struct intrhand *taskqueue_ih; 448088699fSJohn Baldwin 45ca2e0534SDoug Rabson struct taskqueue { 46ca2e0534SDoug Rabson STAILQ_ENTRY(taskqueue) tq_link; 47ca2e0534SDoug Rabson STAILQ_HEAD(, task) tq_queue; 48ca2e0534SDoug Rabson const char *tq_name; 49ca2e0534SDoug Rabson taskqueue_enqueue_fn tq_enqueue; 50ca2e0534SDoug Rabson void *tq_context; 51ca2e0534SDoug Rabson int tq_draining; 52ca2e0534SDoug Rabson }; 53ca2e0534SDoug Rabson 54ca2e0534SDoug Rabson struct taskqueue * 55ca2e0534SDoug Rabson taskqueue_create(const char *name, int mflags, 56ca2e0534SDoug Rabson taskqueue_enqueue_fn enqueue, void *context) 57ca2e0534SDoug Rabson { 58ca2e0534SDoug Rabson struct taskqueue *queue; 59ca2e0534SDoug Rabson static int once = 1; 60ca2e0534SDoug Rabson int s; 61ca2e0534SDoug Rabson 62ca2e0534SDoug Rabson queue = malloc(sizeof(struct taskqueue), M_TASKQUEUE, mflags); 63ca2e0534SDoug Rabson if (!queue) 64ca2e0534SDoug Rabson return 0; 65ca2e0534SDoug Rabson STAILQ_INIT(&queue->tq_queue); 66ca2e0534SDoug Rabson queue->tq_name = name; 67ca2e0534SDoug Rabson queue->tq_enqueue = enqueue; 68ca2e0534SDoug Rabson queue->tq_context = context; 69ca2e0534SDoug Rabson queue->tq_draining = 0; 70ca2e0534SDoug Rabson 71ca2e0534SDoug Rabson s = splhigh(); 72ca2e0534SDoug Rabson if (once) { 73ca2e0534SDoug Rabson STAILQ_INIT(&taskqueue_queues); 74ca2e0534SDoug Rabson once = 0; 75ca2e0534SDoug Rabson } 76ca2e0534SDoug Rabson STAILQ_INSERT_TAIL(&taskqueue_queues, queue, tq_link); 77ca2e0534SDoug Rabson splx(s); 78ca2e0534SDoug Rabson 79ca2e0534SDoug Rabson return queue; 80ca2e0534SDoug Rabson } 81ca2e0534SDoug Rabson 82ca2e0534SDoug Rabson void 83ca2e0534SDoug Rabson taskqueue_free(struct taskqueue *queue) 84ca2e0534SDoug Rabson { 85ca2e0534SDoug Rabson int s = splhigh(); 86ca2e0534SDoug Rabson queue->tq_draining = 1; 87ca2e0534SDoug Rabson splx(s); 88ca2e0534SDoug Rabson 89ca2e0534SDoug Rabson taskqueue_run(queue); 90ca2e0534SDoug Rabson 91ca2e0534SDoug Rabson s = splhigh(); 92ca2e0534SDoug Rabson STAILQ_REMOVE(&taskqueue_queues, queue, taskqueue, tq_link); 93ca2e0534SDoug Rabson splx(s); 94ca2e0534SDoug Rabson 95ca2e0534SDoug Rabson free(queue, M_TASKQUEUE); 96ca2e0534SDoug Rabson } 97ca2e0534SDoug Rabson 98ca2e0534SDoug Rabson struct taskqueue * 99ca2e0534SDoug Rabson taskqueue_find(const char *name) 100ca2e0534SDoug Rabson { 101ca2e0534SDoug Rabson struct taskqueue *queue; 102ca2e0534SDoug Rabson int s; 103ca2e0534SDoug Rabson 104ca2e0534SDoug Rabson s = splhigh(); 105ca2e0534SDoug Rabson STAILQ_FOREACH(queue, &taskqueue_queues, tq_link) 106ca2e0534SDoug Rabson if (!strcmp(queue->tq_name, name)) { 107ca2e0534SDoug Rabson splx(s); 108ca2e0534SDoug Rabson return queue; 109ca2e0534SDoug Rabson } 110ca2e0534SDoug Rabson splx(s); 111ca2e0534SDoug Rabson return 0; 112ca2e0534SDoug Rabson } 113ca2e0534SDoug Rabson 114ca2e0534SDoug Rabson int 115ca2e0534SDoug Rabson taskqueue_enqueue(struct taskqueue *queue, struct task *task) 116ca2e0534SDoug Rabson { 117ca2e0534SDoug Rabson struct task *ins; 118ca2e0534SDoug Rabson struct task *prev; 119ca2e0534SDoug Rabson 120ca2e0534SDoug Rabson int s = splhigh(); 121ca2e0534SDoug Rabson 122ca2e0534SDoug Rabson /* 123ca2e0534SDoug Rabson * Don't allow new tasks on a queue which is being freed. 124ca2e0534SDoug Rabson */ 125ca2e0534SDoug Rabson if (queue->tq_draining) { 126ca2e0534SDoug Rabson splx(s); 127ca2e0534SDoug Rabson return EPIPE; 128ca2e0534SDoug Rabson } 129ca2e0534SDoug Rabson 130ca2e0534SDoug Rabson /* 131ca2e0534SDoug Rabson * Count multiple enqueues. 132ca2e0534SDoug Rabson */ 133ca2e0534SDoug Rabson if (task->ta_pending) { 134ca2e0534SDoug Rabson task->ta_pending++; 135ca2e0534SDoug Rabson splx(s); 136ca2e0534SDoug Rabson return 0; 137ca2e0534SDoug Rabson } 138ca2e0534SDoug Rabson 139ca2e0534SDoug Rabson /* 140ca2e0534SDoug Rabson * Optimise the case when all tasks have the same priority. 141ca2e0534SDoug Rabson */ 14251b86781SJeffrey Hsu prev = STAILQ_LAST(&queue->tq_queue, task, ta_link); 143ca2e0534SDoug Rabson if (!prev || prev->ta_priority >= task->ta_priority) { 144ca2e0534SDoug Rabson STAILQ_INSERT_TAIL(&queue->tq_queue, task, ta_link); 145ca2e0534SDoug Rabson } else { 146ca2e0534SDoug Rabson prev = 0; 147ca2e0534SDoug Rabson for (ins = STAILQ_FIRST(&queue->tq_queue); ins; 148ca2e0534SDoug Rabson prev = ins, ins = STAILQ_NEXT(ins, ta_link)) 149ca2e0534SDoug Rabson if (ins->ta_priority < task->ta_priority) 150ca2e0534SDoug Rabson break; 151ca2e0534SDoug Rabson 152ca2e0534SDoug Rabson if (prev) 153ca2e0534SDoug Rabson STAILQ_INSERT_AFTER(&queue->tq_queue, prev, task, ta_link); 154ca2e0534SDoug Rabson else 155ca2e0534SDoug Rabson STAILQ_INSERT_HEAD(&queue->tq_queue, task, ta_link); 156ca2e0534SDoug Rabson } 157ca2e0534SDoug Rabson 158ca2e0534SDoug Rabson task->ta_pending = 1; 159ca2e0534SDoug Rabson if (queue->tq_enqueue) 160ca2e0534SDoug Rabson queue->tq_enqueue(queue->tq_context); 161ca2e0534SDoug Rabson 162ca2e0534SDoug Rabson splx(s); 163ca2e0534SDoug Rabson 164ca2e0534SDoug Rabson return 0; 165ca2e0534SDoug Rabson } 166ca2e0534SDoug Rabson 167ca2e0534SDoug Rabson void 168ca2e0534SDoug Rabson taskqueue_run(struct taskqueue *queue) 169ca2e0534SDoug Rabson { 170ca2e0534SDoug Rabson int s; 171ca2e0534SDoug Rabson struct task *task; 172ca2e0534SDoug Rabson int pending; 173ca2e0534SDoug Rabson 174ca2e0534SDoug Rabson s = splhigh(); 175ca2e0534SDoug Rabson while (STAILQ_FIRST(&queue->tq_queue)) { 176ca2e0534SDoug Rabson /* 177ca2e0534SDoug Rabson * Carefully remove the first task from the queue and 178ca2e0534SDoug Rabson * zero its pending count. 179ca2e0534SDoug Rabson */ 180ca2e0534SDoug Rabson task = STAILQ_FIRST(&queue->tq_queue); 181ca2e0534SDoug Rabson STAILQ_REMOVE_HEAD(&queue->tq_queue, ta_link); 182ca2e0534SDoug Rabson pending = task->ta_pending; 183ca2e0534SDoug Rabson task->ta_pending = 0; 184ca2e0534SDoug Rabson splx(s); 185ca2e0534SDoug Rabson 186ca2e0534SDoug Rabson task->ta_func(task->ta_context, pending); 187ca2e0534SDoug Rabson 188ca2e0534SDoug Rabson s = splhigh(); 189ca2e0534SDoug Rabson } 190ca2e0534SDoug Rabson splx(s); 191ca2e0534SDoug Rabson } 192ca2e0534SDoug Rabson 193ca2e0534SDoug Rabson static void 194ca2e0534SDoug Rabson taskqueue_swi_enqueue(void *context) 195ca2e0534SDoug Rabson { 1968088699fSJohn Baldwin sched_swi(taskqueue_ih, SWI_NOSWITCH); 197ca2e0534SDoug Rabson } 198ca2e0534SDoug Rabson 199ca2e0534SDoug Rabson static void 2008088699fSJohn Baldwin taskqueue_swi_run(void *dummy) 201ca2e0534SDoug Rabson { 202ca2e0534SDoug Rabson taskqueue_run(taskqueue_swi); 203ca2e0534SDoug Rabson } 204ca2e0534SDoug Rabson 205ca2e0534SDoug Rabson TASKQUEUE_DEFINE(swi, taskqueue_swi_enqueue, 0, 2068088699fSJohn Baldwin taskqueue_ih = sinthand_add("task queue", NULL, 2078088699fSJohn Baldwin taskqueue_swi_run, NULL, SWI_TQ, 0)); 208