subr_taskqueue.c revision 88900
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 88900 2002-01-05 08:47:13Z jhb $ 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; 4485521Sjhbstatic struct mtx taskqueue_queues_mutex; 4567551Sjhb 4661033Sdfrstruct taskqueue { 4761033Sdfr STAILQ_ENTRY(taskqueue) tq_link; 4861033Sdfr STAILQ_HEAD(, task) tq_queue; 4961033Sdfr const char *tq_name; 5061033Sdfr taskqueue_enqueue_fn tq_enqueue; 5161033Sdfr void *tq_context; 5261033Sdfr int tq_draining; 5385521Sjhb struct mtx tq_mutex; 5461033Sdfr}; 5561033Sdfr 5685521Sjhbstatic void init_taskqueue_list(void *data); 5785521Sjhb 5885521Sjhbstatic void 5985521Sjhbinit_taskqueue_list(void *data __unused) 6085521Sjhb{ 6185521Sjhb 6285521Sjhb mtx_init(&taskqueue_queues_mutex, "taskqueue list", MTX_DEF); 6385521Sjhb STAILQ_INIT(&taskqueue_queues); 6485521Sjhb} 6585521SjhbSYSINIT(taskqueue_list, SI_SUB_INTRINSIC, SI_ORDER_ANY, init_taskqueue_list, 6685521Sjhb NULL); 6785521Sjhb 6861033Sdfrstruct taskqueue * 6961033Sdfrtaskqueue_create(const char *name, int mflags, 7061033Sdfr taskqueue_enqueue_fn enqueue, void *context) 7161033Sdfr{ 7261033Sdfr struct taskqueue *queue; 7361033Sdfr 7485521Sjhb queue = malloc(sizeof(struct taskqueue), M_TASKQUEUE, mflags | M_ZERO); 7561033Sdfr if (!queue) 7661033Sdfr return 0; 7785521Sjhb 7861033Sdfr STAILQ_INIT(&queue->tq_queue); 7961033Sdfr queue->tq_name = name; 8061033Sdfr queue->tq_enqueue = enqueue; 8161033Sdfr queue->tq_context = context; 8261033Sdfr queue->tq_draining = 0; 8385521Sjhb mtx_init(&queue->tq_mutex, "taskqueue", MTX_DEF); 8461033Sdfr 8585521Sjhb mtx_lock(&taskqueue_queues_mutex); 8661033Sdfr STAILQ_INSERT_TAIL(&taskqueue_queues, queue, tq_link); 8785521Sjhb mtx_unlock(&taskqueue_queues_mutex); 8861033Sdfr 8961033Sdfr return queue; 9061033Sdfr} 9161033Sdfr 9261033Sdfrvoid 9361033Sdfrtaskqueue_free(struct taskqueue *queue) 9461033Sdfr{ 9585521Sjhb 9685521Sjhb mtx_lock(&queue->tq_mutex); 9761033Sdfr queue->tq_draining = 1; 9885521Sjhb mtx_unlock(&queue->tq_mutex); 9961033Sdfr 10061033Sdfr taskqueue_run(queue); 10161033Sdfr 10285521Sjhb mtx_lock(&taskqueue_queues_mutex); 10361033Sdfr STAILQ_REMOVE(&taskqueue_queues, queue, taskqueue, tq_link); 10485521Sjhb mtx_unlock(&taskqueue_queues_mutex); 10561033Sdfr 10685521Sjhb mtx_destroy(&queue->tq_mutex); 10761033Sdfr free(queue, M_TASKQUEUE); 10861033Sdfr} 10961033Sdfr 11085521Sjhb/* 11185521Sjhb * Returns with the taskqueue locked. 11285521Sjhb */ 11361033Sdfrstruct taskqueue * 11461033Sdfrtaskqueue_find(const char *name) 11561033Sdfr{ 11661033Sdfr struct taskqueue *queue; 11761033Sdfr 11885521Sjhb mtx_lock(&taskqueue_queues_mutex); 11985521Sjhb STAILQ_FOREACH(queue, &taskqueue_queues, tq_link) { 12085521Sjhb mtx_lock(&queue->tq_mutex); 12161033Sdfr if (!strcmp(queue->tq_name, name)) { 12285521Sjhb mtx_unlock(&taskqueue_queues_mutex); 12361033Sdfr return queue; 12461033Sdfr } 12585521Sjhb mtx_unlock(&queue->tq_mutex); 12685521Sjhb } 12785521Sjhb mtx_unlock(&taskqueue_queues_mutex); 12861033Sdfr return 0; 12961033Sdfr} 13061033Sdfr 13161033Sdfrint 13261033Sdfrtaskqueue_enqueue(struct taskqueue *queue, struct task *task) 13361033Sdfr{ 13461033Sdfr struct task *ins; 13561033Sdfr struct task *prev; 13661033Sdfr 13785560Sjhb mtx_lock(&queue->tq_mutex); 13885560Sjhb 13961033Sdfr /* 14061033Sdfr * Don't allow new tasks on a queue which is being freed. 14161033Sdfr */ 14261033Sdfr if (queue->tq_draining) { 14385521Sjhb mtx_unlock(&queue->tq_mutex); 14461033Sdfr return EPIPE; 14561033Sdfr } 14661033Sdfr 14761033Sdfr /* 14861033Sdfr * Count multiple enqueues. 14961033Sdfr */ 15061033Sdfr if (task->ta_pending) { 15161033Sdfr task->ta_pending++; 15285521Sjhb mtx_unlock(&queue->tq_mutex); 15361033Sdfr return 0; 15461033Sdfr } 15561033Sdfr 15661033Sdfr /* 15761033Sdfr * Optimise the case when all tasks have the same priority. 15861033Sdfr */ 15964199Shsu prev = STAILQ_LAST(&queue->tq_queue, task, ta_link); 16061033Sdfr if (!prev || prev->ta_priority >= task->ta_priority) { 16161033Sdfr STAILQ_INSERT_TAIL(&queue->tq_queue, task, ta_link); 16261033Sdfr } else { 16361033Sdfr prev = 0; 16461033Sdfr for (ins = STAILQ_FIRST(&queue->tq_queue); ins; 16561033Sdfr prev = ins, ins = STAILQ_NEXT(ins, ta_link)) 16661033Sdfr if (ins->ta_priority < task->ta_priority) 16761033Sdfr break; 16861033Sdfr 16961033Sdfr if (prev) 17061033Sdfr STAILQ_INSERT_AFTER(&queue->tq_queue, prev, task, ta_link); 17161033Sdfr else 17261033Sdfr STAILQ_INSERT_HEAD(&queue->tq_queue, task, ta_link); 17361033Sdfr } 17461033Sdfr 17561033Sdfr task->ta_pending = 1; 17661033Sdfr if (queue->tq_enqueue) 17761033Sdfr queue->tq_enqueue(queue->tq_context); 17885560Sjhb 17985521Sjhb mtx_unlock(&queue->tq_mutex); 18085560Sjhb 18161033Sdfr return 0; 18261033Sdfr} 18361033Sdfr 18461033Sdfrvoid 18561033Sdfrtaskqueue_run(struct taskqueue *queue) 18661033Sdfr{ 18761033Sdfr struct task *task; 18861033Sdfr int pending; 18961033Sdfr 19085521Sjhb mtx_lock(&queue->tq_mutex); 19161033Sdfr while (STAILQ_FIRST(&queue->tq_queue)) { 19261033Sdfr /* 19361033Sdfr * Carefully remove the first task from the queue and 19461033Sdfr * zero its pending count. 19561033Sdfr */ 19661033Sdfr task = STAILQ_FIRST(&queue->tq_queue); 19761033Sdfr STAILQ_REMOVE_HEAD(&queue->tq_queue, ta_link); 19861033Sdfr pending = task->ta_pending; 19961033Sdfr task->ta_pending = 0; 20085560Sjhb mtx_unlock(&queue->tq_mutex); 20161033Sdfr 20285560Sjhb task->ta_func(task->ta_context, pending); 20361033Sdfr 20485521Sjhb mtx_lock(&queue->tq_mutex); 20561033Sdfr } 20685521Sjhb mtx_unlock(&queue->tq_mutex); 20761033Sdfr} 20861033Sdfr 20961033Sdfrstatic void 21061033Sdfrtaskqueue_swi_enqueue(void *context) 21161033Sdfr{ 21288900Sjhb swi_sched(taskqueue_ih, 0); 21361033Sdfr} 21461033Sdfr 21561033Sdfrstatic void 21667551Sjhbtaskqueue_swi_run(void *dummy) 21761033Sdfr{ 21861033Sdfr taskqueue_run(taskqueue_swi); 21961033Sdfr} 22061033Sdfr 22161033SdfrTASKQUEUE_DEFINE(swi, taskqueue_swi_enqueue, 0, 22272238Sjhb swi_add(NULL, "task queue", taskqueue_swi_run, NULL, SWI_TQ, 0, 22372238Sjhb &taskqueue_ih)); 224