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