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