summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/alpha/alpha/ipl_funcs.c3
-rw-r--r--sys/alpha/include/ipl.h5
-rw-r--r--sys/conf/files1
-rw-r--r--sys/i386/include/ipl.h3
-rw-r--r--sys/i386/isa/ipl_funcs.c3
-rw-r--r--sys/kern/subr_taskqueue.c203
-rw-r--r--sys/sys/systm.h3
-rw-r--r--sys/sys/taskqueue.h113
8 files changed, 333 insertions, 1 deletions
diff --git a/sys/alpha/alpha/ipl_funcs.c b/sys/alpha/alpha/ipl_funcs.c
index 0995d17a5b6c..8c2cb67c27fa 100644
--- a/sys/alpha/alpha/ipl_funcs.c
+++ b/sys/alpha/alpha/ipl_funcs.c
@@ -158,6 +158,7 @@ GENSET(setsoftnet, &ipending, 1 << SWI_NET)
GENSET(setsoftcamnet, &ipending, 1 << SWI_CAMNET)
GENSET(setsoftcambio, &ipending, 1 << SWI_CAMBIO)
GENSET(setsoftvm, &ipending, 1 << SWI_VM)
+GENSET(setsofttq, &ipending, 1 << SWI_TQ)
GENSET(setsoftclock, &ipending, 1 << SWI_CLOCK)
GENSET(schedsofttty, &idelayed, 1 << SWI_TTY)
@@ -165,6 +166,7 @@ GENSET(schedsoftnet, &idelayed, 1 << SWI_NET)
GENSET(schedsoftcamnet, &idelayed, 1 << SWI_CAMNET)
GENSET(schedsoftcambio, &idelayed, 1 << SWI_CAMBIO)
GENSET(schedsoftvm, &idelayed, 1 << SWI_VM)
+GENSET(schedsofttq, &idelayed, 1 << SWI_TQ)
GENSET(schedsoftclock, &idelayed, 1 << SWI_CLOCK)
#ifdef INVARIANT_SUPPORT
@@ -227,6 +229,7 @@ GENSPLASSERT(splsoftcamnet, SOFT) /* XXX no corresponding spl for alpha */
GENSPLASSERT(splsoftclock, SOFT)
GENSPLASSERT(splsofttty, SOFT) /* XXX no corresponding spl for alpha */
GENSPLASSERT(splsoftvm, SOFT)
+GENSPLASSERT(splsofttq, SOFT)
GENSPLASSERT(splstatclock, CLOCK)
GENSPLASSERT(spltty, IO)
GENSPLASSERT(splvm, IO)
diff --git a/sys/alpha/include/ipl.h b/sys/alpha/include/ipl.h
index 201aad06e4c5..ea93fbb39d12 100644
--- a/sys/alpha/include/ipl.h
+++ b/sys/alpha/include/ipl.h
@@ -41,6 +41,7 @@
#define SWI_CAMBIO 3
#define SWI_VM 4
#define SWI_CLOCK 5
+#define SWI_TQ 6
#define NSWI 32
#define NHWI 0
@@ -75,6 +76,7 @@ static __inline int name(void) \
SPLUP(splsoftcam, SOFT)
SPLUP(splsoftnet, SOFT)
SPLUP(splsoftvm, SOFT)
+SPLUP(splsofttq, SOFT)
SPLUP(splnet, IO)
SPLUP(splbio, IO)
SPLUP(splcam, IO)
@@ -103,13 +105,13 @@ splx(int s)
spl0();
}
-
extern void setdelayed(void);
extern void setsofttty(void);
extern void setsoftnet(void);
extern void setsoftcamnet(void);
extern void setsoftcambio(void);
extern void setsoftvm(void);
+extern void setsofttq(void);
extern void setsoftclock(void);
extern void schedsofttty(void);
@@ -117,6 +119,7 @@ extern void schedsoftnet(void);
extern void schedsoftcamnet(void);
extern void schedsoftcambio(void);
extern void schedsoftvm(void);
+extern void schedsofttq(void);
extern void schedsoftclock(void);
#if 0
diff --git a/sys/conf/files b/sys/conf/files
index ba8c672f1f51..b3bc1d3fbe08 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -429,6 +429,7 @@ kern/subr_prf.c standard
kern/subr_prof.c standard
kern/subr_rman.c standard
kern/subr_scanf.c standard
+kern/subr_taskqueue.c standard
kern/subr_xxx.c standard
kern/sys_generic.c standard
kern/sys_pipe.c standard
diff --git a/sys/i386/include/ipl.h b/sys/i386/include/ipl.h
index 2a73a6acd3fb..0b93fa08f32b 100644
--- a/sys/i386/include/ipl.h
+++ b/sys/i386/include/ipl.h
@@ -52,6 +52,7 @@
#define SWI_CAMNET (NHWI + 2)
#define SWI_CAMBIO (NHWI + 3)
#define SWI_VM (NHWI + 4)
+#define SWI_TQ (NHWI + 5)
#define SWI_CLOCK 30
#define NSWI (32 - NHWI)
@@ -63,6 +64,7 @@
#define SWI_CAMNET_PENDING (1 << SWI_CAMNET)
#define SWI_CAMBIO_PENDING (1 << SWI_CAMBIO)
#define SWI_VM_PENDING (1 << SWI_VM)
+#define SWI_TQ_PENDING (1 << SWI_TQ)
#define SWI_CLOCK_PENDING (1 << SWI_CLOCK)
/*
@@ -82,6 +84,7 @@
#define SWI_CAMBIO_MASK (SWI_CAMBIO_PENDING | SWI_CLOCK_MASK)
#define SWI_NET_MASK (SWI_NET_PENDING | SWI_CLOCK_MASK)
#define SWI_VM_MASK (SWI_VM_PENDING | SWI_CLOCK_MASK)
+#define SWI_TQ_MASK (SWI_TQ_PENDING | SWI_CLOCK_MASK)
#define SWI_CLOCK_MASK SWI_CLOCK_PENDING
#define SWI_MASK (~HWI_MASK)
diff --git a/sys/i386/isa/ipl_funcs.c b/sys/i386/isa/ipl_funcs.c
index 043d6b432cf6..d27d97fa9b1f 100644
--- a/sys/i386/isa/ipl_funcs.c
+++ b/sys/i386/isa/ipl_funcs.c
@@ -55,12 +55,14 @@ DO_SETBITS(setsoftclock, &ipending, SWI_CLOCK_PENDING)
DO_SETBITS(setsoftnet, &ipending, SWI_NET_PENDING)
DO_SETBITS(setsofttty, &ipending, SWI_TTY_PENDING)
DO_SETBITS(setsoftvm, &ipending, SWI_VM_PENDING)
+DO_SETBITS(setsofttq, &ipending, SWI_TQ_PENDING)
DO_SETBITS(schedsoftcamnet, &idelayed, SWI_CAMNET_PENDING)
DO_SETBITS(schedsoftcambio, &idelayed, SWI_CAMBIO_PENDING)
DO_SETBITS(schedsoftnet, &idelayed, SWI_NET_PENDING)
DO_SETBITS(schedsofttty, &idelayed, SWI_TTY_PENDING)
DO_SETBITS(schedsoftvm, &idelayed, SWI_VM_PENDING)
+DO_SETBITS(schedsofttq, &idelayed, SWI_TQ_PENDING)
unsigned
softclockpending(void)
@@ -272,6 +274,7 @@ GENSPL(splsoftcamnet, |=, SWI_CAMNET_MASK, 10)
GENSPL(splsoftclock, =, SWI_CLOCK_MASK, 11)
GENSPL(splsofttty, |=, SWI_TTY_MASK, 12)
GENSPL(splsoftvm, |=, SWI_VM_MASK, 16)
+GENSPL(splsofttq, |=, SWI_TQ_MASK, 17)
GENSPL(splstatclock, |=, stat_imask, 13)
GENSPL(spltty, |=, tty_imask, 14)
GENSPL(splvm, |=, net_imask | bio_imask | cam_imask, 15)
diff --git a/sys/kern/subr_taskqueue.c b/sys/kern/subr_taskqueue.c
new file mode 100644
index 000000000000..ab47b4ff2ff7
--- /dev/null
+++ b/sys/kern/subr_taskqueue.c
@@ -0,0 +1,203 @@
+/*-
+ * Copyright (c) 2000 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/taskqueue.h>
+#include <sys/interrupt.h>
+#include <sys/malloc.h>
+#include <machine/ipl.h>
+
+MALLOC_DEFINE(M_TASKQUEUE, "taskqueue", "Task Queues");
+
+static STAILQ_HEAD(taskqueue_list, taskqueue) taskqueue_queues;
+
+struct taskqueue {
+ STAILQ_ENTRY(taskqueue) tq_link;
+ STAILQ_HEAD(, task) tq_queue;
+ const char *tq_name;
+ taskqueue_enqueue_fn tq_enqueue;
+ void *tq_context;
+ int tq_draining;
+};
+
+struct taskqueue *
+taskqueue_create(const char *name, int mflags,
+ taskqueue_enqueue_fn enqueue, void *context)
+{
+ struct taskqueue *queue;
+ static int once = 1;
+ int s;
+
+ queue = malloc(sizeof(struct taskqueue), M_TASKQUEUE, mflags);
+ if (!queue)
+ return 0;
+ STAILQ_INIT(&queue->tq_queue);
+ queue->tq_name = name;
+ queue->tq_enqueue = enqueue;
+ queue->tq_context = context;
+ queue->tq_draining = 0;
+
+ s = splhigh();
+ if (once) {
+ STAILQ_INIT(&taskqueue_queues);
+ once = 0;
+ }
+ STAILQ_INSERT_TAIL(&taskqueue_queues, queue, tq_link);
+ splx(s);
+
+ return queue;
+}
+
+void
+taskqueue_free(struct taskqueue *queue)
+{
+ int s = splhigh();
+ queue->tq_draining = 1;
+ splx(s);
+
+ taskqueue_run(queue);
+
+ s = splhigh();
+ STAILQ_REMOVE(&taskqueue_queues, queue, taskqueue, tq_link);
+ splx(s);
+
+ free(queue, M_TASKQUEUE);
+}
+
+struct taskqueue *
+taskqueue_find(const char *name)
+{
+ struct taskqueue *queue;
+ int s;
+
+ s = splhigh();
+ STAILQ_FOREACH(queue, &taskqueue_queues, tq_link)
+ if (!strcmp(queue->tq_name, name)) {
+ splx(s);
+ return queue;
+ }
+ splx(s);
+ return 0;
+}
+
+int
+taskqueue_enqueue(struct taskqueue *queue, struct task *task)
+{
+ struct task *ins;
+ struct task *prev;
+
+ int s = splhigh();
+
+ /*
+ * Don't allow new tasks on a queue which is being freed.
+ */
+ if (queue->tq_draining) {
+ splx(s);
+ return EPIPE;
+ }
+
+ /*
+ * Count multiple enqueues.
+ */
+ if (task->ta_pending) {
+ task->ta_pending++;
+ splx(s);
+ return 0;
+ }
+
+ /*
+ * Optimise the case when all tasks have the same priority.
+ */
+ prev = STAILQ_LAST(&queue->tq_queue);
+ if (!prev || prev->ta_priority >= task->ta_priority) {
+ STAILQ_INSERT_TAIL(&queue->tq_queue, task, ta_link);
+ } else {
+ prev = 0;
+ for (ins = STAILQ_FIRST(&queue->tq_queue); ins;
+ prev = ins, ins = STAILQ_NEXT(ins, ta_link))
+ if (ins->ta_priority < task->ta_priority)
+ break;
+
+ if (prev)
+ STAILQ_INSERT_AFTER(&queue->tq_queue, prev, task, ta_link);
+ else
+ STAILQ_INSERT_HEAD(&queue->tq_queue, task, ta_link);
+ }
+
+ task->ta_pending = 1;
+ if (queue->tq_enqueue)
+ queue->tq_enqueue(queue->tq_context);
+
+ splx(s);
+
+ return 0;
+}
+
+void
+taskqueue_run(struct taskqueue *queue)
+{
+ int s;
+ struct task *task;
+ int pending;
+
+ s = splhigh();
+ while (STAILQ_FIRST(&queue->tq_queue)) {
+ /*
+ * Carefully remove the first task from the queue and
+ * zero its pending count.
+ */
+ task = STAILQ_FIRST(&queue->tq_queue);
+ STAILQ_REMOVE_HEAD(&queue->tq_queue, ta_link);
+ pending = task->ta_pending;
+ task->ta_pending = 0;
+ splx(s);
+
+ task->ta_func(task->ta_context, pending);
+
+ s = splhigh();
+ }
+ splx(s);
+}
+
+static void
+taskqueue_swi_enqueue(void *context)
+{
+ setsofttq();
+}
+
+static void
+taskqueue_swi_run(void)
+{
+ taskqueue_run(taskqueue_swi);
+}
+
+TASKQUEUE_DEFINE(swi, taskqueue_swi_enqueue, 0,
+ register_swi(SWI_TQ, taskqueue_swi_run));
diff --git a/sys/sys/systm.h b/sys/sys/systm.h
index ed493b7c391c..c0731f4af1ba 100644
--- a/sys/sys/systm.h
+++ b/sys/sys/systm.h
@@ -217,11 +217,13 @@ void setsoftclock __P((void));
void setsoftnet __P((void));
void setsofttty __P((void));
void setsoftvm __P((void));
+void setsofttq __P((void));
void schedsoftcamnet __P((void));
void schedsoftcambio __P((void));
void schedsoftnet __P((void));
void schedsofttty __P((void));
void schedsoftvm __P((void));
+void schedsofttq __P((void));
intrmask_t softclockpending __P((void));
void spl0 __P((void));
intrmask_t splbio __P((void));
@@ -236,6 +238,7 @@ intrmask_t splsoftcamnet __P((void));
intrmask_t splsoftclock __P((void));
intrmask_t splsofttty __P((void));
intrmask_t splsoftvm __P((void));
+intrmask_t splsofttq __P((void));
intrmask_t splstatclock __P((void));
intrmask_t spltty __P((void));
intrmask_t splvm __P((void));
diff --git a/sys/sys/taskqueue.h b/sys/sys/taskqueue.h
new file mode 100644
index 000000000000..e337a53686f7
--- /dev/null
+++ b/sys/sys/taskqueue.h
@@ -0,0 +1,113 @@
+/*-
+ * Copyright (c) 2000 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _SYS_TASKQUEUE_H_
+#define _SYS_TASKQUEUE_H_
+
+#ifdef _KERNEL
+
+struct taskqueue;
+
+/*
+ * Each task includes a function which is called from
+ * taskqueue_run(). The first argument is taken from the 'ta_context'
+ * field of struct task and the second argument is a count of how many
+ * times the task was enqueued before the call to taskqueue_run().
+ */
+typedef void (*task_fn)(void *context, int pending);
+
+/*
+ * A notification callback function which is called from
+ * taskqueue_enqueue(). The context argument is given in the call to
+ * taskqueue_create(). This function would normally be used to allow the
+ * queue to arrange to run itself later (e.g. by scheduling a software
+ * interrupt or waking a kernel thread).
+ */
+typedef void (*taskqueue_enqueue_fn)(void *context);
+
+struct task {
+ STAILQ_ENTRY(task) ta_link; /* link for queue */
+ int ta_pending; /* count times queued */
+ int ta_priority; /* priority of task in queue */
+ task_fn ta_func; /* task handler */
+ void *ta_context; /* argument for handler */
+};
+
+struct taskqueue *taskqueue_create(const char *name, int mflags,
+ taskqueue_enqueue_fn enqueue,
+ void *context);
+void taskqueue_free(struct taskqueue *queue);
+struct taskqueue *taskqueue_find(const char *name);
+int taskqueue_enqueue(struct taskqueue *queue,
+ struct task *task);
+void taskqueue_run(struct taskqueue *queue);
+
+/*
+ * Initialise a task structure.
+ */
+#define TASK_INIT(task, priority, func, context) do { \
+ task->ta_pending = 0; \
+ task->ta_priority = priority; \
+ task->ta_func = func; \
+ task->ta_context = context; \
+} while (0)
+
+/*
+ * Declare a reference to a taskqueue.
+ */
+#define TASKQUEUE_DECLARE(name) \
+ \
+extern struct taskqueue *taskqueue_##name
+
+/*
+ * Define and initialise a taskqueue.
+ */
+#define TASKQUEUE_DEFINE(name, enqueue, context, init) \
+ \
+struct taskqueue *taskqueue_##name; \
+ \
+static void \
+taskqueue_define_##name(void *arg) \
+{ \
+ taskqueue_##name = \
+ taskqueue_create(#name, M_NOWAIT, enqueue, context); \
+ init; \
+} \
+ \
+SYSINIT(taskqueue_##name, SI_SUB_CONFIGURE, SI_ORDER_SECOND, \
+ taskqueue_define_##name, NULL);
+
+/*
+ * This queue is serviced by a software interrupt handler. To enqueue
+ * a task, call taskqueue_enqueue(&taskqueue_swi, &task).
+ */
+TASKQUEUE_DECLARE(swi);
+
+#endif /* _KERNEL */
+
+#endif /* !_SYS_TASKQUEUE_H_ */