subr_taskqueue.c revision 65822
190075Sobrien/*- 290075Sobrien * Copyright (c) 2000 Doug Rabson 390075Sobrien * All rights reserved. 490075Sobrien * 5169689Skan * Redistribution and use in source and binary forms, with or without 6132718Skan * modification, are permitted provided that the following conditions 790075Sobrien * are met: 890075Sobrien * 1. Redistributions of source code must retain the above copyright 990075Sobrien * notice, this list of conditions and the following disclaimer. 1090075Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1190075Sobrien * notice, this list of conditions and the following disclaimer in the 1290075Sobrien * documentation and/or other materials provided with the distribution. 1390075Sobrien * 1490075Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1590075Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1690075Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1790075Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1890075Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1990075Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2090075Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2190075Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22169689Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23169689Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2490075Sobrien * SUCH DAMAGE. 2590075Sobrien * 2690075Sobrien * $FreeBSD: head/sys/kern/subr_taskqueue.c 65822 2000-09-13 18:33:25Z jhb $ 2790075Sobrien */ 2890075Sobrien 29132718Skan#include <sys/param.h> 3090075Sobrien#include <sys/bus.h> 31132718Skan#include <sys/queue.h> 32132718Skan#include <sys/systm.h> 3390075Sobrien#include <sys/kernel.h> 3490075Sobrien#include <sys/taskqueue.h> 3590075Sobrien#include <sys/interrupt.h> 3690075Sobrien#include <sys/malloc.h> 3790075Sobrien#include <machine/ipl.h> 3890075Sobrien 3990075SobrienMALLOC_DEFINE(M_TASKQUEUE, "taskqueue", "Task Queues"); 4090075Sobrien 41132718Skanstatic STAILQ_HEAD(taskqueue_list, taskqueue) taskqueue_queues; 4290075Sobrien 43169689Skanstruct taskqueue { 44169689Skan STAILQ_ENTRY(taskqueue) tq_link; 4590075Sobrien STAILQ_HEAD(, task) tq_queue; 4690075Sobrien const char *tq_name; 4790075Sobrien taskqueue_enqueue_fn tq_enqueue; 4890075Sobrien void *tq_context; 4990075Sobrien int tq_draining; 5090075Sobrien}; 51132718Skan 5290075Sobrienstruct taskqueue * 5390075Sobrientaskqueue_create(const char *name, int mflags, 5490075Sobrien taskqueue_enqueue_fn enqueue, void *context) 55169689Skan{ 5690075Sobrien struct taskqueue *queue; 5790075Sobrien static int once = 1; 58169689Skan int s; 59169689Skan 6090075Sobrien queue = malloc(sizeof(struct taskqueue), M_TASKQUEUE, mflags); 6190075Sobrien if (!queue) 6290075Sobrien return 0; 6390075Sobrien STAILQ_INIT(&queue->tq_queue); 6490075Sobrien queue->tq_name = name; 6590075Sobrien queue->tq_enqueue = enqueue; 6690075Sobrien queue->tq_context = context; 6790075Sobrien queue->tq_draining = 0; 6890075Sobrien 6990075Sobrien s = splhigh(); 7090075Sobrien if (once) { 7190075Sobrien STAILQ_INIT(&taskqueue_queues); 7290075Sobrien once = 0; 7390075Sobrien } 7490075Sobrien STAILQ_INSERT_TAIL(&taskqueue_queues, queue, tq_link); 75 splx(s); 76 77 return queue; 78} 79 80void 81taskqueue_free(struct taskqueue *queue) 82{ 83 int s = splhigh(); 84 queue->tq_draining = 1; 85 splx(s); 86 87 taskqueue_run(queue); 88 89 s = splhigh(); 90 STAILQ_REMOVE(&taskqueue_queues, queue, taskqueue, tq_link); 91 splx(s); 92 93 free(queue, M_TASKQUEUE); 94} 95 96struct taskqueue * 97taskqueue_find(const char *name) 98{ 99 struct taskqueue *queue; 100 int s; 101 102 s = splhigh(); 103 STAILQ_FOREACH(queue, &taskqueue_queues, tq_link) 104 if (!strcmp(queue->tq_name, name)) { 105 splx(s); 106 return queue; 107 } 108 splx(s); 109 return 0; 110} 111 112int 113taskqueue_enqueue(struct taskqueue *queue, struct task *task) 114{ 115 struct task *ins; 116 struct task *prev; 117 118 int s = splhigh(); 119 120 /* 121 * Don't allow new tasks on a queue which is being freed. 122 */ 123 if (queue->tq_draining) { 124 splx(s); 125 return EPIPE; 126 } 127 128 /* 129 * Count multiple enqueues. 130 */ 131 if (task->ta_pending) { 132 task->ta_pending++; 133 splx(s); 134 return 0; 135 } 136 137 /* 138 * Optimise the case when all tasks have the same priority. 139 */ 140 prev = STAILQ_LAST(&queue->tq_queue, task, ta_link); 141 if (!prev || prev->ta_priority >= task->ta_priority) { 142 STAILQ_INSERT_TAIL(&queue->tq_queue, task, ta_link); 143 } else { 144 prev = 0; 145 for (ins = STAILQ_FIRST(&queue->tq_queue); ins; 146 prev = ins, ins = STAILQ_NEXT(ins, ta_link)) 147 if (ins->ta_priority < task->ta_priority) 148 break; 149 150 if (prev) 151 STAILQ_INSERT_AFTER(&queue->tq_queue, prev, task, ta_link); 152 else 153 STAILQ_INSERT_HEAD(&queue->tq_queue, task, ta_link); 154 } 155 156 task->ta_pending = 1; 157 if (queue->tq_enqueue) 158 queue->tq_enqueue(queue->tq_context); 159 160 splx(s); 161 162 return 0; 163} 164 165void 166taskqueue_run(struct taskqueue *queue) 167{ 168 int s; 169 struct task *task; 170 int pending; 171 172 s = splhigh(); 173 while (STAILQ_FIRST(&queue->tq_queue)) { 174 /* 175 * Carefully remove the first task from the queue and 176 * zero its pending count. 177 */ 178 task = STAILQ_FIRST(&queue->tq_queue); 179 STAILQ_REMOVE_HEAD(&queue->tq_queue, ta_link); 180 pending = task->ta_pending; 181 task->ta_pending = 0; 182 splx(s); 183 184 task->ta_func(task->ta_context, pending); 185 186 s = splhigh(); 187 } 188 splx(s); 189} 190 191static void 192taskqueue_swi_enqueue(void *context) 193{ 194 setsofttq(); 195} 196 197static void 198taskqueue_swi_run(void) 199{ 200 taskqueue_run(taskqueue_swi); 201} 202 203TASKQUEUE_DEFINE(swi, taskqueue_swi_enqueue, 0, 204 register_swi(SWI_TQ, taskqueue_swi_run)); 205