Add taskqueue system for easy-to-use SWIs among other things.

Reviewed by: arch
This commit is contained in:
dfr 2000-05-28 15:45:30 +00:00
parent b67ad017c0
commit 2281181f80
8 changed files with 333 additions and 1 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

203
sys/kern/subr_taskqueue.c Normal file
View File

@ -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));

View File

@ -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));

113
sys/sys/taskqueue.h Normal file
View File

@ -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_ */