Subject: timedwork
To: None <tech-kern@netbsd.org>
From: YAMAMOTO Takashi <yamt@mwd.biglobe.ne.jp>
List: tech-kern
Date: 01/12/2007 19:32:07
--NextPart-20070112185002-0350008
Content-Type: Text/Plain; charset=us-ascii
hi,
unless anyone objects, i'll introduce something like callout(9)
but its callback function is invoked in a thread context. (see a.diff)
i want to use it to schedule vmem rehashing. (see b.diff)
we probably will re-consider it when/if we make softclock be handled by
the interrupt thread.
YAMAMOTO Takashi
--NextPart-20070112185002-0350008
Content-Type: Text/Plain; charset=us-ascii
Content-Disposition: attachment; filename="a.diff"
Index: sys/timedwork.h
===================================================================
--- sys/timedwork.h (revision 0)
+++ sys/timedwork.h (revision 0)
@@ -0,0 +1,39 @@
+/* $NetBSD$ */
+
+/*-
+ * Copyright (c)2007 YAMAMOTO Takashi,
+ * 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.
+ */
+
+#ifndef _SYS_TIMEDWORK_H_
+#define _SYS_TIMEDWORK_H_
+
+typedef struct timedwork timedwork_t;
+
+timedwork_t *timedwork_create(void);
+void timedwork_destroy(timedwork_t *);
+void timedwork_setfunc(timedwork_t *, void (*)(void *), void *);
+void timedwork_schedule(timedwork_t *, int);
+
+#endif /* _SYS_TIMEDWORK_H_ */
Index: kern/subr_timedwork.c
===================================================================
--- kern/subr_timedwork.c (revision 0)
+++ kern/subr_timedwork.c (revision 0)
@@ -0,0 +1,110 @@
+/* $NetBSD$ */
+
+/*-
+ * Copyright (c)2007 YAMAMOTO Takashi,
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/callout.h>
+#include <sys/kmem.h>
+#include <sys/once.h>
+#include <sys/workqueue.h>
+#include <sys/timedwork.h>
+
+static struct workqueue *timedwork_workqueue;
+
+struct timedwork {
+ union {
+ struct work u_work;
+ struct callout u_callout;
+ } tw_u;
+#define tw_work tw_u.u_work
+#define tw_callout tw_u.u_callout
+ void (*tw_func)(void *);
+ void *tw_arg;
+};
+
+static void
+timedwork_worker(struct work *wk, void *arg)
+{
+ timedwork_t *tw = (void *)wk;
+
+ (*tw->tw_func)(tw->tw_arg);
+}
+
+static void
+timedwork_timer(void *vp)
+{
+ timedwork_t *tw = vp;
+
+ workqueue_enqueue(timedwork_workqueue, &tw->tw_work);
+}
+
+static int
+timedwork_init(void)
+{
+
+ return workqueue_create(&timedwork_workqueue, "timedwork",
+ timedwork_worker, NULL, PUSER - 1, IPL_SOFTCLOCK, 0);
+}
+
+timedwork_t *
+timedwork_create(void)
+{
+ static ONCE_DECL(control);
+
+ if (RUN_ONCE(&control, timedwork_init)) {
+ return NULL;
+ }
+
+ return kmem_alloc(sizeof(timedwork_t), KM_SLEEP);
+}
+
+void
+timedwork_destroy(timedwork_t *tw)
+{
+
+ kmem_free(tw, sizeof(*tw));
+}
+
+void
+timedwork_setfunc(timedwork_t *tw, void (*func)(void *), void *arg)
+{
+
+ tw->tw_func = func;
+ tw->tw_arg = arg;
+}
+
+void
+timedwork_schedule(timedwork_t *tw, int ticks)
+{
+
+ callout_init(&tw->tw_callout);
+ callout_reset(&tw->tw_callout, ticks, timedwork_timer, tw);
+}
Index: conf/files
===================================================================
--- conf/files (revision 1944)
+++ conf/files (working copy)
@@ -1338,6 +1338,7 @@ file kern/subr_prof.c
file kern/subr_once.c
file kern/subr_optstr.c
file kern/subr_specificdata.c
+file kern/subr_timedwork.c
file kern/subr_userconf.c userconf
file kern/subr_vmem.c
file kern/subr_workqueue.c
--NextPart-20070112185002-0350008
Content-Type: Text/Plain; charset=us-ascii
Content-Disposition: attachment; filename="b.diff"
Index: sys/vmem.h
===================================================================
--- sys/vmem.h (revision 1848)
+++ sys/vmem.h (working copy)
@@ -58,6 +58,7 @@ void vmem_xfree(vmem_t *, vmem_addr_t, v
vmem_addr_t vmem_add(vmem_t *, vmem_addr_t, vmem_size_t, vm_flag_t);
vmem_size_t vmem_roundup_size(vmem_t *, vmem_size_t);
boolean_t vmem_reap(vmem_t *);
+void vmem_rehash_start(void);
/* vm_flag_t */
#define VM_SLEEP 0x00000001
Index: kern/init_main.c
===================================================================
--- kern/init_main.c (revision 1946)
+++ kern/init_main.c (working copy)
@@ -116,6 +116,7 @@ __KERNEL_RCSID(0, "$NetBSD: init_main.c,
#include <sys/event.h>
#include <sys/mbuf.h>
#include <sys/iostat.h>
+#include <sys/vmem.h>
#ifdef FAST_IPSEC
#include <netipsec/ipsec.h>
#endif
@@ -540,6 +541,8 @@ main(void)
uvm_aiodone_worker, NULL, PVM, IPL_BIO, 0))
panic("fork aiodoned");
+ vmem_rehash_start();
+
#if defined(MULTIPROCESSOR)
/* Boot the secondary processors. */
cpu_boot_secondary_processors();
Index: kern/subr_vmem.c
===================================================================
--- kern/subr_vmem.c (revision 1922)
+++ kern/subr_vmem.c (working copy)
@@ -51,11 +51,13 @@ __KERNEL_RCSID(0, "$NetBSD: subr_vmem.c,
#if defined(_KERNEL)
#include <sys/systm.h>
+#include <sys/kernel.h> /* hz */
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/once.h>
#include <sys/pool.h>
#include <sys/proc.h>
+#include <sys/timedwork.h>
#include <sys/vmem.h>
#else /* defined(_KERNEL) */
#include "../sys/vmem.h"
@@ -73,6 +75,7 @@ __KERNEL_RCSID(0, "$NetBSD: subr_vmem.c,
#define LOCK_ASSERT(a) /* nothing */
#define simple_lock_init(a) /* nothing */
#define simple_lock(a) /* nothing */
+#define simple_lock_try(a) 1
#define simple_unlock(a) /* nothing */
#define ASSERT_SLEEPABLE(lk, msg) /* nothing */
#endif /* defined(_KERNEL) */
@@ -85,7 +88,10 @@ void vmem_dump(const vmem_t *);
#endif /* defined(VMEM_DEBUG) */
#define VMEM_MAXORDER (sizeof(vmem_size_t) * CHAR_BIT)
-#define VMEM_HASHSIZE_INIT 4096 /* XXX */
+
+#define VMEM_HASHSIZE_MIN 1 /* XXX */
+#define VMEM_HASHSIZE_MAX 8192 /* XXX */
+#define VMEM_HASHSIZE_INIT VMEM_HASHSIZE_MIN
#define VM_FITMASK (VM_BESTFIT | VM_INSTANTFIT)
@@ -123,6 +129,7 @@ struct vmem {
size_t vm_quantum_mask;
int vm_quantum_shift;
const char *vm_name;
+ LIST_ENTRY(vmem) vm_alllist;
#if defined(QCACHE)
/* quantum cache */
@@ -134,6 +141,7 @@ struct vmem {
};
#define VMEM_LOCK(vm) simple_lock(&vm->vm_lock)
+#define VMEM_TRYLOCK(vm) simple_lock_try(&vm->vm_lock)
#define VMEM_UNLOCK(vm) simple_unlock(&vm->vm_lock)
#define VMEM_LOCK_INIT(vm) simple_lock_init(&vm->vm_lock);
#define VMEM_ASSERT_LOCKED(vm) \
@@ -404,6 +412,25 @@ bt_insfree(vmem_t *vm, bt_t *bt)
/* ---- vmem internal functions */
+#if defined(_KERNEL)
+static LIST_HEAD(, vmem) vmem_list = LIST_HEAD_INITIALIZER(vmem_list);
+static struct lock vmem_list_lock = LOCK_INITIALIZER(PLOCK, "vmemlist", 0, 0);
+
+static void
+vmem_listlock(void)
+{
+
+ lockmgr(&vmem_list_lock, LK_EXCLUSIVE, NULL);
+}
+
+static void
+vmem_listunlock(void)
+{
+
+ lockmgr(&vmem_list_lock, LK_RELEASE, NULL);
+}
+#endif /* defined(_KERNEL) */
+
#if defined(QCACHE)
static inline vm_flag_t
prf_to_vmf(int prflags)
@@ -599,6 +626,31 @@ vmem_add1(vmem_t *vm, vmem_addr_t addr,
return addr;
}
+static void
+vmem_destroy1(vmem_t *vm)
+{
+
+ VMEM_ASSERT_UNLOCKED(vm);
+
+#if defined(QCACHE)
+ qc_destroy(vm);
+#endif /* defined(QCACHE) */
+ if (vm->vm_hashlist != NULL) {
+ int i;
+
+ for (i = 0; i < vm->vm_hashsize; i++) {
+ bt_t *bt;
+
+ while ((bt = LIST_FIRST(&vm->vm_hashlist[i])) != NULL) {
+ KASSERT(bt->bt_type == BT_TYPE_SPAN_STATIC);
+ bt_free(vm, bt);
+ }
+ }
+ xfree(vm->vm_hashlist);
+ }
+ xfree(vm);
+}
+
static int
vmem_import(vmem_t *vm, vmem_size_t size, vm_flag_t flags)
{
@@ -644,7 +696,10 @@ vmem_rehash(vmem_t *vm, size_t newhashsi
LIST_INIT(&newhashlist[i]);
}
- VMEM_LOCK(vm);
+ if (!VMEM_TRYLOCK(vm)) {
+ xfree(newhashlist);
+ return EBUSY;
+ }
oldhashlist = vm->vm_hashlist;
oldhashsize = vm->vm_hashsize;
vm->vm_hashlist = newhashlist;
@@ -739,6 +794,7 @@ vmem_create(const char *name, vmem_addr_
KASSERT((flags & (VM_SLEEP|VM_NOSLEEP)) != 0);
KASSERT((~flags & (VM_SLEEP|VM_NOSLEEP)) != 0);
+ KASSERT((flags & VM_SLEEP) != 0); /* XXX for now... */
#if defined(_KERNEL)
if (RUN_ONCE(&control, vmem_init)) {
@@ -769,17 +825,23 @@ vmem_create(const char *name, vmem_addr_
}
vm->vm_hashlist = NULL;
if (vmem_rehash(vm, VMEM_HASHSIZE_INIT, flags)) {
- vmem_destroy(vm);
+ vmem_destroy1(vm);
return NULL;
}
if (size != 0) {
if (vmem_add(vm, base, size, flags) == 0) {
- vmem_destroy(vm);
+ vmem_destroy1(vm);
return NULL;
}
}
+#if defined(_KERNEL)
+ vmem_listlock();
+ LIST_INSERT_HEAD(&vmem_list, vm, vm_alllist);
+ vmem_listunlock();
+#endif /* defined(_KERNEL) */
+
return vm;
}
@@ -787,25 +849,13 @@ void
vmem_destroy(vmem_t *vm)
{
- VMEM_ASSERT_UNLOCKED(vm);
-
-#if defined(QCACHE)
- qc_destroy(vm);
-#endif /* defined(QCACHE) */
- if (vm->vm_hashlist != NULL) {
- int i;
-
- for (i = 0; i < vm->vm_hashsize; i++) {
- bt_t *bt;
+#if defined(_KERNEL)
+ vmem_listlock();
+ LIST_REMOVE(vm, vm_alllist);
+ vmem_listunlock();
+#endif /* defined(_KERNEL) */
- while ((bt = LIST_FIRST(&vm->vm_hashlist[i])) != NULL) {
- KASSERT(bt->bt_type == BT_TYPE_SPAN_STATIC);
- bt_free(vm, bt);
- }
- }
- xfree(vm->vm_hashlist);
- }
- xfree(vm);
+ vmem_destroy1(vm);
}
vmem_size_t
@@ -1117,6 +1167,64 @@ vmem_reap(vmem_t *vm)
return didsomething;
}
+/* ---- rehash */
+
+#if defined(_KERNEL)
+static timedwork_t *vmem_rehash_work;
+static int vmem_rehash_interval;
+
+static void
+vmem_rehash_all(void *dummy)
+{
+ vmem_t *vm;
+
+ vmem_listlock();
+ LIST_FOREACH(vm, &vmem_list, vm_alllist) {
+ size_t desired;
+ size_t current;
+ int s;
+
+ s = splvm();
+ if (!VMEM_TRYLOCK(vm)) {
+ splx(s);
+ continue;
+ }
+ desired = vm->vm_nbusytag;
+ current = vm->vm_hashsize;
+ VMEM_UNLOCK(vm);
+ splx(s);
+
+ if (desired > VMEM_HASHSIZE_MAX) {
+ desired = VMEM_HASHSIZE_MAX;
+ } else if (desired < VMEM_HASHSIZE_MIN) {
+ desired = VMEM_HASHSIZE_MIN;
+ }
+ if (desired > current * 2 || desired * 2 < current) {
+ s = splvm();
+ vmem_rehash(vm, desired, VM_NOSLEEP);
+ splx(s);
+ }
+ }
+ vmem_listunlock();
+
+ timedwork_schedule(vmem_rehash_work, vmem_rehash_interval);
+}
+
+void
+vmem_rehash_start(void)
+{
+
+ vmem_rehash_work = timedwork_create();
+ if (vmem_rehash_work == NULL) {
+ panic("%s: failed to create timedwork", __func__);
+ }
+ timedwork_setfunc(vmem_rehash_work, vmem_rehash_all, NULL);
+
+ vmem_rehash_interval = hz * 10;
+ timedwork_schedule(vmem_rehash_work, vmem_rehash_interval);
+}
+#endif /* defined(_KERNEL) */
+
/* ---- debug */
#if defined(VMEM_DEBUG)
@@ -1188,7 +1296,7 @@ main()
#endif
vm = vmem_create("test", VMEM_ADDR_NULL, 0, 1,
- NULL, NULL, NULL, 0, VM_NOSLEEP);
+ NULL, NULL, NULL, 0, VM_SLEEP);
if (vm == NULL) {
printf("vmem_create\n");
exit(EXIT_FAILURE);
--NextPart-20070112185002-0350008--