subr_taskqueue.c revision 111528
161033Sdfr/*- 261033Sdfr * Copyright (c) 2000 Doug Rabson 361033Sdfr * All rights reserved. 461033Sdfr * 561033Sdfr * Redistribution and use in source and binary forms, with or without 661033Sdfr * modification, are permitted provided that the following conditions 761033Sdfr * are met: 861033Sdfr * 1. Redistributions of source code must retain the above copyright 961033Sdfr * notice, this list of conditions and the following disclaimer. 1061033Sdfr * 2. Redistributions in binary form must reproduce the above copyright 1161033Sdfr * notice, this list of conditions and the following disclaimer in the 1261033Sdfr * documentation and/or other materials provided with the distribution. 1361033Sdfr * 1461033Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1561033Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1661033Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1761033Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1861033Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1961033Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2061033Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2161033Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2261033Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2361033Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2461033Sdfr * SUCH DAMAGE. 2561033Sdfr * 2661033Sdfr * $FreeBSD: head/sys/kern/subr_taskqueue.c 111528 2003-02-26 03:15:42Z scottl $ 2761033Sdfr */ 2861033Sdfr 2961033Sdfr#include <sys/param.h> 3085521Sjhb#include <sys/systm.h> 3165822Sjhb#include <sys/bus.h> 3285560Sjhb#include <sys/interrupt.h> 3361033Sdfr#include <sys/kernel.h> 3485521Sjhb#include <sys/lock.h> 3561033Sdfr#include <sys/malloc.h> 3685521Sjhb#include <sys/mutex.h> 3785521Sjhb#include <sys/taskqueue.h> 3861033Sdfr 3969774Sphkstatic MALLOC_DEFINE(M_TASKQUEUE, "taskqueue", "Task Queues"); 4061033Sdfr 4161033Sdfrstatic STAILQ_HEAD(taskqueue_list, taskqueue) taskqueue_queues; 4261033Sdfr 4372238Sjhbstatic void *taskqueue_ih; 44111528Sscottlstatic void *taskqueue_giant_ih; 4585521Sjhbstatic struct mtx taskqueue_queues_mutex; 4667551Sjhb 4761033Sdfrstruct taskqueue { 4861033Sdfr STAILQ_ENTRY(taskqueue) tq_link; 4961033Sdfr STAILQ_HEAD(, task) tq_queue; 5061033Sdfr const char *tq_name; 5161033Sdfr taskqueue_enqueue_fn tq_enqueue; 5261033Sdfr void *tq_context; 5361033Sdfr int tq_draining; 5485521Sjhb struct mtx tq_mutex; 5561033Sdfr}; 5661033Sdfr 5785521Sjhbstatic void init_taskqueue_list(void *data); 5885521Sjhb 5985521Sjhbstatic void 6085521Sjhbinit_taskqueue_list(void *data __unused) 6185521Sjhb{ 6285521Sjhb 6393818Sjhb mtx_init(&taskqueue_queues_mutex, "taskqueue list", NULL, MTX_DEF); 6485521Sjhb STAILQ_INIT(&taskqueue_queues); 6585521Sjhb} 6685521SjhbSYSINIT(taskqueue_list, SI_SUB_INTRINSIC, SI_ORDER_ANY, init_taskqueue_list, 6785521Sjhb NULL); 6885521Sjhb 6961033Sdfrstruct taskqueue * 7061033Sdfrtaskqueue_create(const char *name, int mflags, 7161033Sdfr taskqueue_enqueue_fn enqueue, void *context) 7261033Sdfr{ 7361033Sdfr struct taskqueue *queue; 7461033Sdfr 7585521Sjhb queue = malloc(sizeof(struct taskqueue), M_TASKQUEUE, mflags | M_ZERO); 7661033Sdfr if (!queue) 7761033Sdfr return 0; 7885521Sjhb 7961033Sdfr STAILQ_INIT(&queue->tq_queue); 8061033Sdfr queue->tq_name = name; 8161033Sdfr queue->tq_enqueue = enqueue; 8261033Sdfr queue->tq_context = context; 8361033Sdfr queue->tq_draining = 0; 8493818Sjhb mtx_init(&queue->tq_mutex, "taskqueue", NULL, MTX_DEF); 8561033Sdfr 8685521Sjhb mtx_lock(&taskqueue_queues_mutex); 8761033Sdfr STAILQ_INSERT_TAIL(&taskqueue_queues, queue, tq_link); 8885521Sjhb mtx_unlock(&taskqueue_queues_mutex); 8961033Sdfr 9061033Sdfr return queue; 9161033Sdfr} 9261033Sdfr 9361033Sdfrvoid 9461033Sdfrtaskqueue_free(struct taskqueue *queue) 9561033Sdfr{ 9685521Sjhb 9785521Sjhb mtx_lock(&queue->tq_mutex); 98101153Sjhb KASSERT(queue->tq_draining == 0, ("free'ing a draining taskqueue")); 9961033Sdfr queue->tq_draining = 1; 10085521Sjhb mtx_unlock(&queue->tq_mutex); 10161033Sdfr 10261033Sdfr taskqueue_run(queue); 10361033Sdfr 10485521Sjhb mtx_lock(&taskqueue_queues_mutex); 10561033Sdfr STAILQ_REMOVE(&taskqueue_queues, queue, taskqueue, tq_link); 10685521Sjhb mtx_unlock(&taskqueue_queues_mutex); 10761033Sdfr 10885521Sjhb mtx_destroy(&queue->tq_mutex); 10961033Sdfr free(queue, M_TASKQUEUE); 11061033Sdfr} 11161033Sdfr 11285521Sjhb/* 11385521Sjhb * Returns with the taskqueue locked. 11485521Sjhb */ 11561033Sdfrstruct taskqueue * 11661033Sdfrtaskqueue_find(const char *name) 11761033Sdfr{ 11861033Sdfr struct taskqueue *queue; 11961033Sdfr 12085521Sjhb mtx_lock(&taskqueue_queues_mutex); 12185521Sjhb STAILQ_FOREACH(queue, &taskqueue_queues, tq_link) { 12285521Sjhb mtx_lock(&queue->tq_mutex); 12361033Sdfr if (!strcmp(queue->tq_name, name)) { 12485521Sjhb mtx_unlock(&taskqueue_queues_mutex); 12561033Sdfr return queue; 12661033Sdfr } 12785521Sjhb mtx_unlock(&queue->tq_mutex); 12885521Sjhb } 12985521Sjhb mtx_unlock(&taskqueue_queues_mutex); 13061033Sdfr return 0; 13161033Sdfr} 13261033Sdfr 13361033Sdfrint 13461033Sdfrtaskqueue_enqueue(struct taskqueue *queue, struct task *task) 13561033Sdfr{ 13661033Sdfr struct task *ins; 13761033Sdfr struct task *prev; 13861033Sdfr 13985560Sjhb mtx_lock(&queue->tq_mutex); 14085560Sjhb 14161033Sdfr /* 14261033Sdfr * Don't allow new tasks on a queue which is being freed. 14361033Sdfr */ 14461033Sdfr if (queue->tq_draining) { 14585521Sjhb mtx_unlock(&queue->tq_mutex); 14661033Sdfr return EPIPE; 14761033Sdfr } 14861033Sdfr 14961033Sdfr /* 15061033Sdfr * Count multiple enqueues. 15161033Sdfr */ 15261033Sdfr if (task->ta_pending) { 15361033Sdfr task->ta_pending++; 15485521Sjhb mtx_unlock(&queue->tq_mutex); 15561033Sdfr return 0; 15661033Sdfr } 15761033Sdfr 15861033Sdfr /* 15961033Sdfr * Optimise the case when all tasks have the same priority. 16061033Sdfr */ 16164199Shsu prev = STAILQ_LAST(&queue->tq_queue, task, ta_link); 16261033Sdfr if (!prev || prev->ta_priority >= task->ta_priority) { 16361033Sdfr STAILQ_INSERT_TAIL(&queue->tq_queue, task, ta_link); 16461033Sdfr } else { 16561033Sdfr prev = 0; 16661033Sdfr for (ins = STAILQ_FIRST(&queue->tq_queue); ins; 16761033Sdfr prev = ins, ins = STAILQ_NEXT(ins, ta_link)) 16861033Sdfr if (ins->ta_priority < task->ta_priority) 16961033Sdfr break; 17061033Sdfr 17161033Sdfr if (prev) 17261033Sdfr STAILQ_INSERT_AFTER(&queue->tq_queue, prev, task, ta_link); 17361033Sdfr else 17461033Sdfr STAILQ_INSERT_HEAD(&queue->tq_queue, task, ta_link); 17561033Sdfr } 17661033Sdfr 17761033Sdfr task->ta_pending = 1; 17861033Sdfr if (queue->tq_enqueue) 17961033Sdfr queue->tq_enqueue(queue->tq_context); 18085560Sjhb 18185521Sjhb mtx_unlock(&queue->tq_mutex); 18285560Sjhb 18361033Sdfr return 0; 18461033Sdfr} 18561033Sdfr 18661033Sdfrvoid 18761033Sdfrtaskqueue_run(struct taskqueue *queue) 18861033Sdfr{ 18961033Sdfr struct task *task; 19061033Sdfr int pending; 19161033Sdfr 19285521Sjhb mtx_lock(&queue->tq_mutex); 19361033Sdfr while (STAILQ_FIRST(&queue->tq_queue)) { 19461033Sdfr /* 19561033Sdfr * Carefully remove the first task from the queue and 19661033Sdfr * zero its pending count. 19761033Sdfr */ 19861033Sdfr task = STAILQ_FIRST(&queue->tq_queue); 19961033Sdfr STAILQ_REMOVE_HEAD(&queue->tq_queue, ta_link); 20061033Sdfr pending = task->ta_pending; 20161033Sdfr task->ta_pending = 0; 20285560Sjhb mtx_unlock(&queue->tq_mutex); 20361033Sdfr 20485560Sjhb task->ta_func(task->ta_context, pending); 20561033Sdfr 20685521Sjhb mtx_lock(&queue->tq_mutex); 20761033Sdfr } 20885521Sjhb mtx_unlock(&queue->tq_mutex); 20961033Sdfr} 21061033Sdfr 21161033Sdfrstatic void 21261033Sdfrtaskqueue_swi_enqueue(void *context) 21361033Sdfr{ 21488900Sjhb swi_sched(taskqueue_ih, 0); 21561033Sdfr} 21661033Sdfr 21761033Sdfrstatic void 21867551Sjhbtaskqueue_swi_run(void *dummy) 21961033Sdfr{ 22061033Sdfr taskqueue_run(taskqueue_swi); 22161033Sdfr} 22261033Sdfr 223111528Sscottlstatic void 224111528Sscottltaskqueue_swi_giant_enqueue(void *context) 225111528Sscottl{ 226111528Sscottl swi_sched(taskqueue_giant_ih, 0); 227111528Sscottl} 228111528Sscottl 229111528Sscottlstatic void 230111528Sscottltaskqueue_swi_giant_run(void *dummy) 231111528Sscottl{ 232111528Sscottl taskqueue_run(taskqueue_swi_giant); 233111528Sscottl} 234111528Sscottl 23561033SdfrTASKQUEUE_DEFINE(swi, taskqueue_swi_enqueue, 0, 236111528Sscottl swi_add(NULL, "task queue", taskqueue_swi_run, NULL, SWI_TQ, 237111528Sscottl INTR_MPSAFE, &taskqueue_ih)); 238111528Sscottl 239111528SscottlTASKQUEUE_DEFINE(swi_giant, taskqueue_swi_giant_enqueue, 0, 240111528Sscottl swi_add(NULL, "Giant task queue", taskqueue_swi_giant_run, 241111528Sscottl NULL, SWI_TQ_GIANT, 0, &taskqueue_giant_ih)); 242