Add taskqueue system for easy-to-use SWIs among other things.
Reviewed by: arch
This commit is contained in:
parent
b67ad017c0
commit
2281181f80
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
203
sys/kern/subr_taskqueue.c
Normal 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));
|
@ -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
113
sys/sys/taskqueue.h
Normal 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_ */
|
Loading…
Reference in New Issue
Block a user