Subject: Re: timedwork
To: None <plunky@rya-online.net>
From: YAMAMOTO Takashi <yamt@mwd.biglobe.ne.jp>
List: tech-kern
Date: 01/13/2007 21:11:06
--NextPart-20070113210704-2118200
Content-Type: Text/Plain; charset=us-ascii
> I'm thinking something like:
>
> callout_init(&c);
> callout_setwork(&c, func, arg);
> callout_schedule(&c, timeout);
i implemented it. see the attached patch.
once callout_setwork() is called on a callout,
callout_schedule() is only legal operation on the callout.
(i'm not a fan of making primitives have rich functionalities...)
YAMAMOTO Takashi
--NextPart-20070113210704-2118200
Content-Type: Text/Plain; charset=us-ascii
Content-Disposition: attachment; filename="a.diff"
Index: sys/callout.h
===================================================================
--- sys/callout.h (revision 1464)
+++ sys/callout.h (working copy)
@@ -68,6 +68,8 @@
#ifndef _SYS_CALLOUT_H_
#define _SYS_CALLOUT_H_
+#include <sys/workqueue.h>
+
/*
* The following funkyness is to appease gcc3's strict aliasing.
*/
@@ -90,19 +92,25 @@ struct callout_circq {
#define cq_prev_l cq_prev.list
struct callout {
- struct callout_circq c_list; /* linkage on queue */
+ union {
+ struct callout_circq u_list; /* linkage on queue */
+ struct work u_work;
+ } c_u;
void (*c_func)(void *); /* function to call */
void *c_arg; /* function argument */
int c_time; /* when callout fires */
int c_flags; /* state of this entry */
};
+#define c_list c_u.u_list
+#define c_work c_u.u_work
+#define CALLOUT_THREAD 0x0001 /* invoked in a thread context. */
#define CALLOUT_PENDING 0x0002 /* callout is on the queue */
#define CALLOUT_FIRED 0x0004 /* callout has fired */
#define CALLOUT_INVOKING 0x0008 /* callout function is being invoked */
#define CALLOUT_INITIALIZER_SETFUNC(func, arg) \
- { {{NULL}, {NULL}}, func, arg, 0, 0 }
+ { .c_func = func, .c_arg = arg }
#define CALLOUT_INITIALIZER CALLOUT_INITIALIZER_SETFUNC(NULL, NULL)
@@ -110,6 +118,7 @@ struct callout {
void callout_startup(void);
void callout_init(struct callout *);
void callout_setfunc(struct callout *, void (*)(void *), void *);
+int callout_setwork(struct callout *, void (*)(void *), void *);
void callout_reset(struct callout *, int, void (*)(void *), void *);
void callout_schedule(struct callout *, int);
void callout_stop(struct callout *);
Index: kern/kern_timeout.c
===================================================================
--- kern/kern_timeout.c (revision 1885)
+++ kern/kern_timeout.c (working copy)
@@ -78,6 +78,8 @@ __KERNEL_RCSID(0, "$NetBSD: kern_timeout
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/callout.h>
+#include <sys/once.h>
+#include <sys/workqueue.h>
#ifdef DDB
#include <machine/db_machdep.h>
@@ -193,6 +195,8 @@ do { \
static struct evcnt callout_ev_late;
#endif
+static struct workqueue *callout_workqueue;
+
/*
* callout_startup:
*
@@ -267,6 +271,40 @@ callout_reset(struct callout *c, int to_
CALLOUT_UNLOCK(s);
}
+static void
+callout_worker(struct work *wk, void *arg)
+{
+ struct callout *c = (void *)wk;
+
+ KASSERT(&c->c_work == wk);
+ (*c->c_func)(c->c_arg);
+}
+
+static int
+calloutthread_init(void)
+{
+
+ return workqueue_create(&callout_workqueue, "callout",
+ callout_worker, NULL, PUSER - 1, IPL_SOFTCLOCK, 0);
+}
+
+/*
+ */
+int
+callout_setwork(struct callout *c, void (*func)(void *), void *arg)
+{
+ static ONCE_DECL(control);
+ int error;
+
+ error = RUN_ONCE(&control, calloutthread_init);
+ if (error) {
+ return error;
+ }
+ c->c_flags |= CALLOUT_THREAD;
+ callout_setfunc(c, func, arg);
+ return 0;
+}
+
/*
* callout_schedule:
*
@@ -380,12 +418,19 @@ softclock(void *v)
c->c_flags = (c->c_flags & ~CALLOUT_PENDING) |
(CALLOUT_FIRED|CALLOUT_INVOKING);
- func = c->c_func;
- arg = c->c_arg;
+ if ((c->c_flags & CALLOUT_THREAD) != 0) {
+ CALLOUT_UNLOCK(s);
+ workqueue_enqueue(callout_workqueue,
+ &c->c_work);
+ CALLOUT_LOCK(s);
+ } else {
+ func = c->c_func;
+ arg = c->c_arg;
- CALLOUT_UNLOCK(s);
- (*func)(arg);
- CALLOUT_LOCK(s);
+ CALLOUT_UNLOCK(s);
+ (*func)(arg);
+ CALLOUT_LOCK(s);
+ }
}
}
--NextPart-20070113210704-2118200--