* Implement POSIX (XSI)'s header file re_comp.h
[dragonfly.git] / sys / kern / subr_taskqueue.c
1 /*-
2  * Copyright (c) 2000 Doug Rabson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *      $FreeBSD: src/sys/kern/subr_taskqueue.c,v 1.1.2.2 2001/03/31 03:33:44 hsu Exp $
27  *      $DragonFly: src/sys/kern/subr_taskqueue.c,v 1.3 2003/06/29 03:28:44 dillon Exp $
28  */
29
30 #include <sys/param.h>
31 #include <sys/queue.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/taskqueue.h>
35 #include <sys/interrupt.h>
36 #include <sys/malloc.h>
37 #include <machine/ipl.h>
38
39 MALLOC_DEFINE(M_TASKQUEUE, "taskqueue", "Task Queues");
40
41 static STAILQ_HEAD(taskqueue_list, taskqueue) taskqueue_queues;
42
43 struct taskqueue {
44         STAILQ_ENTRY(taskqueue) tq_link;
45         STAILQ_HEAD(, task)     tq_queue;
46         const char              *tq_name;
47         taskqueue_enqueue_fn    tq_enqueue;
48         void                    *tq_context;
49         int                     tq_draining;
50 };
51
52 struct taskqueue *
53 taskqueue_create(const char *name, int mflags,
54                  taskqueue_enqueue_fn enqueue, void *context)
55 {
56         struct taskqueue *queue;
57         static int once = 1;
58         int s;
59
60         queue = malloc(sizeof(struct taskqueue), M_TASKQUEUE, mflags);
61         if (!queue)
62                 return 0;
63         STAILQ_INIT(&queue->tq_queue);
64         queue->tq_name = name;
65         queue->tq_enqueue = enqueue;
66         queue->tq_context = context;
67         queue->tq_draining = 0;
68
69         s = splhigh();
70         if (once) {
71                 STAILQ_INIT(&taskqueue_queues);
72                 once = 0;
73         }
74         STAILQ_INSERT_TAIL(&taskqueue_queues, queue, tq_link);
75         splx(s);
76
77         return queue;
78 }
79
80 void
81 taskqueue_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
96 struct taskqueue *
97 taskqueue_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
112 int
113 taskqueue_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
165 void
166 taskqueue_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
191 static void
192 taskqueue_swi_enqueue(void *context)
193 {
194         setsofttq();
195 }
196
197 static void
198 taskqueue_swi_run(void *arg)
199 {
200         taskqueue_run(taskqueue_swi);
201 }
202
203 TASKQUEUE_DEFINE(swi, taskqueue_swi_enqueue, 0,
204                  register_swi(SWI_TQ, taskqueue_swi_run, NULL, "swi_taskq"));