subr_taskqueue.c revision 69774
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 69774 2000-12-08 20:09:00Z phk $
2761033Sdfr */
2861033Sdfr
2961033Sdfr#include <sys/param.h>
3065822Sjhb#include <sys/bus.h>
3161033Sdfr#include <sys/queue.h>
3261033Sdfr#include <sys/systm.h>
3361033Sdfr#include <sys/kernel.h>
3461033Sdfr#include <sys/taskqueue.h>
3561033Sdfr#include <sys/interrupt.h>
3666698Sjhb#include <sys/ipl.h>
3761033Sdfr#include <sys/malloc.h>
3861033Sdfr
3969774Sphkstatic MALLOC_DEFINE(M_TASKQUEUE, "taskqueue", "Task Queues");
4061033Sdfr
4161033Sdfrstatic STAILQ_HEAD(taskqueue_list, taskqueue) taskqueue_queues;
4261033Sdfr
4367551Sjhbstatic struct intrhand *taskqueue_ih;
4467551Sjhb
4561033Sdfrstruct taskqueue {
4661033Sdfr	STAILQ_ENTRY(taskqueue)	tq_link;
4761033Sdfr	STAILQ_HEAD(, task)	tq_queue;
4861033Sdfr	const char		*tq_name;
4961033Sdfr	taskqueue_enqueue_fn	tq_enqueue;
5061033Sdfr	void			*tq_context;
5161033Sdfr	int			tq_draining;
5261033Sdfr};
5361033Sdfr
5461033Sdfrstruct taskqueue *
5561033Sdfrtaskqueue_create(const char *name, int mflags,
5661033Sdfr		 taskqueue_enqueue_fn enqueue, void *context)
5761033Sdfr{
5861033Sdfr	struct taskqueue *queue;
5961033Sdfr	static int once = 1;
6061033Sdfr	int s;
6161033Sdfr
6261033Sdfr	queue = malloc(sizeof(struct taskqueue), M_TASKQUEUE, mflags);
6361033Sdfr	if (!queue)
6461033Sdfr		return 0;
6561033Sdfr	STAILQ_INIT(&queue->tq_queue);
6661033Sdfr	queue->tq_name = name;
6761033Sdfr	queue->tq_enqueue = enqueue;
6861033Sdfr	queue->tq_context = context;
6961033Sdfr	queue->tq_draining = 0;
7061033Sdfr
7161033Sdfr	s = splhigh();
7261033Sdfr	if (once) {
7361033Sdfr		STAILQ_INIT(&taskqueue_queues);
7461033Sdfr		once = 0;
7561033Sdfr	}
7661033Sdfr	STAILQ_INSERT_TAIL(&taskqueue_queues, queue, tq_link);
7761033Sdfr	splx(s);
7861033Sdfr
7961033Sdfr	return queue;
8061033Sdfr}
8161033Sdfr
8261033Sdfrvoid
8361033Sdfrtaskqueue_free(struct taskqueue *queue)
8461033Sdfr{
8561033Sdfr	int s = splhigh();
8661033Sdfr	queue->tq_draining = 1;
8761033Sdfr	splx(s);
8861033Sdfr
8961033Sdfr	taskqueue_run(queue);
9061033Sdfr
9161033Sdfr	s = splhigh();
9261033Sdfr	STAILQ_REMOVE(&taskqueue_queues, queue, taskqueue, tq_link);
9361033Sdfr	splx(s);
9461033Sdfr
9561033Sdfr	free(queue, M_TASKQUEUE);
9661033Sdfr}
9761033Sdfr
9861033Sdfrstruct taskqueue *
9961033Sdfrtaskqueue_find(const char *name)
10061033Sdfr{
10161033Sdfr	struct taskqueue *queue;
10261033Sdfr	int s;
10361033Sdfr
10461033Sdfr	s = splhigh();
10561033Sdfr	STAILQ_FOREACH(queue, &taskqueue_queues, tq_link)
10661033Sdfr		if (!strcmp(queue->tq_name, name)) {
10761033Sdfr			splx(s);
10861033Sdfr			return queue;
10961033Sdfr		}
11061033Sdfr	splx(s);
11161033Sdfr	return 0;
11261033Sdfr}
11361033Sdfr
11461033Sdfrint
11561033Sdfrtaskqueue_enqueue(struct taskqueue *queue, struct task *task)
11661033Sdfr{
11761033Sdfr	struct task *ins;
11861033Sdfr	struct task *prev;
11961033Sdfr
12061033Sdfr	int s = splhigh();
12161033Sdfr
12261033Sdfr	/*
12361033Sdfr	 * Don't allow new tasks on a queue which is being freed.
12461033Sdfr	 */
12561033Sdfr	if (queue->tq_draining) {
12661033Sdfr		splx(s);
12761033Sdfr		return EPIPE;
12861033Sdfr	}
12961033Sdfr
13061033Sdfr	/*
13161033Sdfr	 * Count multiple enqueues.
13261033Sdfr	 */
13361033Sdfr	if (task->ta_pending) {
13461033Sdfr		task->ta_pending++;
13561033Sdfr		splx(s);
13661033Sdfr		return 0;
13761033Sdfr	}
13861033Sdfr
13961033Sdfr	/*
14061033Sdfr	 * Optimise the case when all tasks have the same priority.
14161033Sdfr	 */
14264199Shsu	prev = STAILQ_LAST(&queue->tq_queue, task, ta_link);
14361033Sdfr	if (!prev || prev->ta_priority >= task->ta_priority) {
14461033Sdfr		STAILQ_INSERT_TAIL(&queue->tq_queue, task, ta_link);
14561033Sdfr	} else {
14661033Sdfr		prev = 0;
14761033Sdfr		for (ins = STAILQ_FIRST(&queue->tq_queue); ins;
14861033Sdfr		     prev = ins, ins = STAILQ_NEXT(ins, ta_link))
14961033Sdfr			if (ins->ta_priority < task->ta_priority)
15061033Sdfr				break;
15161033Sdfr
15261033Sdfr		if (prev)
15361033Sdfr			STAILQ_INSERT_AFTER(&queue->tq_queue, prev, task, ta_link);
15461033Sdfr		else
15561033Sdfr			STAILQ_INSERT_HEAD(&queue->tq_queue, task, ta_link);
15661033Sdfr	}
15761033Sdfr
15861033Sdfr	task->ta_pending = 1;
15961033Sdfr	if (queue->tq_enqueue)
16061033Sdfr		queue->tq_enqueue(queue->tq_context);
16161033Sdfr
16261033Sdfr	splx(s);
16361033Sdfr
16461033Sdfr	return 0;
16561033Sdfr}
16661033Sdfr
16761033Sdfrvoid
16861033Sdfrtaskqueue_run(struct taskqueue *queue)
16961033Sdfr{
17061033Sdfr	int s;
17161033Sdfr	struct task *task;
17261033Sdfr	int pending;
17361033Sdfr
17461033Sdfr	s = splhigh();
17561033Sdfr	while (STAILQ_FIRST(&queue->tq_queue)) {
17661033Sdfr		/*
17761033Sdfr		 * Carefully remove the first task from the queue and
17861033Sdfr		 * zero its pending count.
17961033Sdfr		 */
18061033Sdfr		task = STAILQ_FIRST(&queue->tq_queue);
18161033Sdfr		STAILQ_REMOVE_HEAD(&queue->tq_queue, ta_link);
18261033Sdfr		pending = task->ta_pending;
18361033Sdfr		task->ta_pending = 0;
18461033Sdfr		splx(s);
18561033Sdfr
18661033Sdfr		task->ta_func(task->ta_context, pending);
18761033Sdfr
18861033Sdfr		s = splhigh();
18961033Sdfr	}
19061033Sdfr	splx(s);
19161033Sdfr}
19261033Sdfr
19361033Sdfrstatic void
19461033Sdfrtaskqueue_swi_enqueue(void *context)
19561033Sdfr{
19667551Sjhb	sched_swi(taskqueue_ih, SWI_NOSWITCH);
19761033Sdfr}
19861033Sdfr
19961033Sdfrstatic void
20067551Sjhbtaskqueue_swi_run(void *dummy)
20161033Sdfr{
20261033Sdfr	taskqueue_run(taskqueue_swi);
20361033Sdfr}
20461033Sdfr
20561033SdfrTASKQUEUE_DEFINE(swi, taskqueue_swi_enqueue, 0,
20667551Sjhb		 taskqueue_ih = sinthand_add("task queue", NULL,
20767551Sjhb		     taskqueue_swi_run, NULL, SWI_TQ, 0));
208