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 +#include +#include +#include +#include +#include +#include +#include + +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_ */