svn commit: r205243 - in user/lstewart/alq_varlen_head/tools/test: . alq

Lawrence Stewart lstewart at FreeBSD.org
Wed Mar 17 07:08:57 UTC 2010


Author: lstewart
Date: Wed Mar 17 07:08:57 2010
New Revision: 205243
URL: http://svn.freebsd.org/changeset/base/205243

Log:
  Commit ALQ testing code.
  
  Sponsored by:	FreeBSD Foundation

Added:
  user/lstewart/alq_varlen_head/tools/test/alq/
  user/lstewart/alq_varlen_head/tools/test/alq/Makefile   (contents, props changed)
  user/lstewart/alq_varlen_head/tools/test/alq/alq_varlen_test.c   (contents, props changed)
Modified:
  user/lstewart/alq_varlen_head/tools/test/README

Modified: user/lstewart/alq_varlen_head/tools/test/README
==============================================================================
--- user/lstewart/alq_varlen_head/tools/test/README	Wed Mar 17 06:41:10 2010	(r205242)
+++ user/lstewart/alq_varlen_head/tools/test/README	Wed Mar 17 07:08:57 2010	(r205243)
@@ -7,6 +7,7 @@ and try to break it and/or measuring per
 
 Please make a subdir per program, and add a brief description to this file.
 
+alq		A kernel module to put ALQ(9) through its paces.
 devrandom	Programs to test /dev/*random.
 dtrace		DTrace test suite
 malloc		A program to test and benchmark malloc().

Added: user/lstewart/alq_varlen_head/tools/test/alq/Makefile
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ user/lstewart/alq_varlen_head/tools/test/alq/Makefile	Wed Mar 17 07:08:57 2010	(r205243)
@@ -0,0 +1,17 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+KMOD=alqtest
+SRCS=alq_varlen_test.c alq_varlen_test.h
+CLEANFILES=alq_varlen_test.h
+
+alq_varlen_test.h:
+	@awk -F "\n" '{ if(index($$0, "struct alq {") > 0) p=1; if(p == 1) { print $$0; if($$0 == "};") exit; } }' ${.CURDIR}/../../../sys/kern/kern_alq.c >> ${.TARGET}
+	grep "#define	AQ_" ${.CURDIR}/../../../sys/kern/kern_alq.c >> ${.TARGET}
+
+#KMOD=alqmodtest
+#SRCS=alq_mod_test.c
+
+.include <bsd.kmod.mk> 
+

Added: user/lstewart/alq_varlen_head/tools/test/alq/alq_varlen_test.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ user/lstewart/alq_varlen_head/tools/test/alq/alq_varlen_test.c	Wed Mar 17 07:08:57 2010	(r205243)
@@ -0,0 +1,536 @@
+/*-
+ * Copyright (c) 2008-2009 Lawrence Stewart <lstewart at freebsd.org>
+ * Copyright (c) 2009-2010, The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed at the Centre for Advanced
+ * Internet Architectures, Swinburne University of Technology, Melbourne,
+ * Australia by Lawrence Stewart under sponsorship from the FreeBSD Foundation.
+ *
+ * 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 AUTHORS 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 AUTHORS 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/param.h>
+#include <sys/alq.h>
+#include <sys/errno.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/sbuf.h>
+#include <sys/systm.h>
+#include <sys/unistd.h>
+
+#include <machine/stdarg.h>
+
+#include "alq_varlen_test.h"
+
+MALLOC_DECLARE(M_ALQTEST);
+MALLOC_DEFINE(M_ALQTEST, "alqtest", "dynamic memory used by alqtest");
+
+#define ENABLE	0x01
+#define DISABLE	0x02
+
+#define NUM_TEST_RUNS 1
+
+static volatile uint8_t run_test_thread;
+
+static char logfile[PATH_MAX] = "/tmp/alqtest.log";
+
+static struct thread *alq_test_thr = NULL;
+
+static struct mtx alqtestmtx;
+
+typedef int (*testfunc)(struct sbuf *, struct sbuf *);
+
+struct writen_thr_data {
+	char name[20];
+	struct mtx dlock;
+	struct alq *q;
+	u_int id;
+	struct thread *thr;
+	volatile u_int finished;
+	char *buf;
+	u_int buflen;
+	u_int nwrites;
+};
+
+typedef const enum {
+	BLACK = 30,
+	RED,
+	GREEN,
+	YELLOW,
+	BLUE,
+	MAGENTA,
+	CYAN,
+	WHITE
+} fgcolor_t;
+
+static int
+alqtest_printf(struct sbuf *s, fgcolor_t c, const char *fmt, ...)
+{
+	int ret;
+	va_list ap1, ap2;
+
+	va_start(ap1, fmt);
+	va_copy(ap2, ap1);
+
+	printf("\033[%dm", c);
+	vprintf(fmt, ap1);
+	printf("\033[0m");
+
+	sbuf_printf(s, "\033[%dm", c);
+	ret = sbuf_vprintf(s, fmt, ap2);
+	sbuf_printf(s, "\033[0m");
+
+	va_end(ap2);
+	va_end(ap1);
+
+	return (ret);
+}
+
+static char
+alqtest_randchar(void)
+{
+	uint32_t c;
+
+	/* generate a random character in the ascii range [32, 126] */
+	while ((c = arc4random() % 126) < 32);
+
+	return ((char)c);
+}
+
+static uint32_t
+alqtest_rand(uint32_t lower, uint32_t upper)
+{
+	uint32_t n;
+
+	while ((n = arc4random() % (upper+1)) < lower);
+
+	return (n);
+}
+
+/*static void
+alqtest_doio_callback(void)
+{
+	printf("doing io baby!\n");
+}*/
+
+static void
+alqtest_writen_thread(void *arg)
+{
+	uint32_t n, i;
+	
+	struct writen_thr_data *data = (struct writen_thr_data *)arg;
+	printf("tid %d id %d started\n", data->thr->td_tid, data->id);
+
+	for (i = 0; i < data->nwrites; i++) {
+		n = alqtest_rand(1, data->buflen);
+		//sbuf_printf(s, "--- msg==%d,msglen==%d\n", i, n);
+		//printf("--- thread==%d,msg==%d,msglen==%d\n", data->id, i, n);
+		alq_writen(data->q, data->buf, n, ALQ_WAITOK);
+	}
+
+	mtx_lock(&data->dlock);
+	data->finished = 1;
+	mtx_unlock(&data->dlock);
+	kthread_exit();
+}
+
+static int
+alqtest_writen(struct sbuf *s, struct sbuf *debug)
+{
+#define	NMSGS		10000
+#define	MAX_THREADS	50
+	struct alq *testalq;
+	const int buflen = 100;
+	int i, n, ret, errors, nthreads, threadsactive, nsecs;
+	char buf[buflen+1];
+	struct writen_thr_data *thrdata[MAX_THREADS];
+	ret = errors = 0;
+	nthreads = 1;
+
+	for (i = 0; i < MAX_THREADS; i++) {
+		thrdata[i] = malloc(sizeof(struct writen_thr_data), M_ALQTEST,
+		    M_WAITOK|M_ZERO);
+		sprintf(thrdata[i]->name, "writen_thr_data_%d", i);
+		mtx_init(&thrdata[i]->dlock, thrdata[i]->name, NULL, MTX_DEF);
+	}
+
+	alqtest_printf(s, 0, "- variable length message writing\n");
+
+	/* test variable length message writing */
+	ret = alq_open_flags(&testalq, logfile, curthread->td_ucred, 0600, buflen,
+	    0, ALQ_ORDERED);
+
+	/*testalq->doio_debugcallback = &alqtest_doio_callback;*/
+
+	for (i = 0; i < sizeof(buf); i++)
+		buf[i] = alqtest_randchar();
+
+	alqtest_printf(s, 0, "-- nthreads==1,nmsgs==1,msglen==1,buflen=%d,"
+	    "flags==ALQ_WAITOK|ALQ_NOACTIVATE\n", buflen);
+	alq_writen(testalq, buf, 1, ALQ_WAITOK|ALQ_NOACTIVATE);
+
+	if ((buflen-1 != testalq->aq_freebytes) &&
+	    (1 != testalq->aq_writehead) &&
+	    (0 != testalq->aq_writetail)) {
+		errors++;
+		sbuf_printf(debug,
+		    "alq->%-15s\texpected=%d\tactual=%d\n", "aq_freebytes",
+		    buflen-1, testalq->aq_freebytes);
+		sbuf_printf(debug, "alq->%-15s\texpected=%d\tactual=%d\n",
+		    "aq_writehead", 1, testalq->aq_writehead);
+		sbuf_printf(debug, "alq->%-15s\texpected=%d\tactual=%d\n",
+		    "aq_writetail", 0, testalq->aq_writetail);
+	}
+
+	alq_flush(testalq);
+
+	if ((buflen != testalq->aq_freebytes) &&
+	    (0 != testalq->aq_writehead) &&
+	    (0 != testalq->aq_writetail)) {
+		errors++;
+		sbuf_printf(debug, "alq->%-15s\texpected=%d\tactual=%d\n",
+		    "aq_freebytes", buflen, testalq->aq_freebytes);
+		sbuf_printf(debug, "alq->%-15s\texpected=%d\tactual=%d\n",
+		    "aq_writehead", 0, testalq->aq_writehead);
+		sbuf_printf(debug, "alq->%-15s\texpected=%d\tactual=%d\n",
+		    "aq_writetail", 0, testalq->aq_writetail);
+	}
+
+	alqtest_printf(s, 0, "-- nthreads==1,nmsgs==1,msglen==%d,buflen=%d,flags==ALQ_WAITOK|ALQ_NOACTIVATE\n", buflen, buflen);
+	alq_writen(testalq, buf, buflen, ALQ_WAITOK | ALQ_NOACTIVATE);
+
+	if ((0 != testalq->aq_freebytes) &&
+	    (0 != testalq->aq_writehead) &&
+	    (0 != testalq->aq_writetail)) {
+		errors++;
+		sbuf_printf(debug, "alq->%-15s\texpected=%d\tactual=%d\n",
+		    "aq_freebytes", 0, testalq->aq_freebytes);
+		sbuf_printf(debug, "alq->%-15s\texpected=%d\tactual=%d\n",
+		    "aq_writehead", 0, testalq->aq_writehead);
+		sbuf_printf(debug, "alq->%-15s\texpected=%d\tactual=%d\n",
+		     "aq_writetail", 0, testalq->aq_writetail);
+	}
+
+	alq_flush(testalq);
+
+	if ((buflen != testalq->aq_freebytes) &&
+	    (0 != testalq->aq_writehead) &&
+	    (0 != testalq->aq_writetail)) {
+		errors++;
+		sbuf_printf(debug, "alq->%-15s\texpected=%d\tactual=%d\n",
+		    "aq_freebytes", buflen, testalq->aq_freebytes);
+		sbuf_printf(debug, "alq->%-15s\texpected=%d\tactual=%d\n",
+		    "aq_writehead", 0, testalq->aq_writehead);
+		sbuf_printf(debug, "alq->%-15s\texpected=%d\tactual=%d\n",
+		    "aq_writetail", 0, testalq->aq_writetail);
+	}
+
+	alqtest_printf(s, 0, "-- nthreads==1,nmsgs==%d,buflen=%d,msglen==[1,%d],"
+	    "flags==ALQ_WAITOK|ALQ_NOACTIVATE\n", NMSGS, buflen, buflen);
+
+	for (i = 0; i < NMSGS; i++) {
+		n = alqtest_rand(1, buflen);
+		sbuf_printf(debug, "--- msg==%d,msglen==%d\n", i, n);
+		alq_writen(testalq, buf, n, ALQ_WAITOK|ALQ_NOACTIVATE);
+
+		alq_flush(testalq);
+
+		if ((buflen != testalq->aq_freebytes) &&
+		    (0 != testalq->aq_writehead) &&
+		    (0 != testalq->aq_writetail)) {
+			errors++;
+			sbuf_printf(debug,
+			    "alq->%-15s\texpected=%d\tactual=%d\n",
+			    "aq_freebytes", buflen, testalq->aq_freebytes);
+			sbuf_printf(debug,
+			    "alq->%-15s\texpected=%d\tactual=%d\n",
+			    "aq_writehead", 0, testalq->aq_writehead);
+			sbuf_printf(debug,
+			    "alq->%-15s\texpected=%d\tactual=%d\n",
+			    "aq_writetail", 0, testalq->aq_writetail);
+		}
+	}
+
+	alqtest_printf(s, 0,
+	    "-- nthreads==1,nmsgs==%d,buflen=%d,msglen==[1,%d],flags==ALQ_WAITOK\n",
+	    NMSGS, buflen, buflen);
+
+	for (i = 0; i < NMSGS; i++) {
+		n = alqtest_rand(1, buflen);
+		sbuf_printf(s, "--- msg==%d,msglen==%d\n", i, n);
+		alq_writen(testalq, buf, n, ALQ_WAITOK);
+	}
+
+	alq_flush(testalq);
+
+	nthreads = 5;
+	threadsactive = 0;
+	alqtest_printf(s, 0,
+	    "-- nthreads=%d,nmsgs==%d,buflen=%d,msglen==[1,%d],flags==ALQ_WAITOK\n",
+	    nthreads, NMSGS, buflen, buflen);
+	for (i = 0; i < nthreads; i++) {
+		thrdata[i]->q = testalq;
+		thrdata[i]->id = i;
+		thrdata[i]->finished = 0;
+		thrdata[i]->buf = buf;
+		thrdata[i]->buflen = buflen;
+		thrdata[i]->nwrites = NMSGS;
+		kthread_add(&alqtest_writen_thread, thrdata[i], NULL,
+		    &thrdata[i]->thr, RFNOWAIT, 0, thrdata[i]->name);
+		threadsactive++;
+	}
+
+	nsecs = 0;
+	while (threadsactive) {
+		//printf("threadsactive: %d\n", threadsactive);
+		for (i = 0; i < nthreads; i++) {
+			mtx_lock(&thrdata[i]->dlock);
+			if (thrdata[i]->thr != NULL &&
+			    thrdata[i]->finished) {
+				printf("tid %d id %d finished\n",
+				    thrdata[i]->thr->td_tid, thrdata[i]->id);
+				threadsactive--;
+				thrdata[i]->thr = NULL;
+			}
+			mtx_unlock(&thrdata[i]->dlock);
+		}
+		pause("alqtest", hz);
+		nsecs++;
+
+		if (nsecs > 20) {
+			printf("It is likely that a bug has stalled one or more "
+			"of the writing threads. Calling alq_flush() to see if "
+			"we can loosen things up\n");
+			alq_flush(testalq);
+
+			if (nsecs > 22) {
+				printf("Also trying wakeup_one(&aq_waiters) and"
+				"wakeup_one(alq)\n");
+				wakeup_one(&testalq->aq_waiters);
+				wakeup_one(testalq);
+				nsecs = 0;
+			}
+		}
+	}
+
+	/* Cleanup. */
+	alq_close(testalq);
+	for (i = 0; i < MAX_THREADS; i++) {
+		mtx_destroy(&thrdata[i]->dlock);
+		free(thrdata[i], M_ALQTEST);
+	}
+
+	return (errors);
+}
+
+static int
+alqtest_open(struct sbuf *s, struct sbuf *debug)
+{
+	struct alq *testalq;
+	const int buflen = 100;
+	int ret = 0, errors = 0;
+
+	alqtest_printf(s, 0, "- variable length message queue creation\n");
+
+	/* test variable length message queue creation */
+	ret = alq_open(&testalq, logfile, curthread->td_ucred, 0600, buflen,
+	    0);
+
+	if (0 != testalq->aq_entmax) {
+		errors++;
+		sbuf_printf(debug, "alq->%-15s\texpected=%d\tactual=%d\n",
+		    "aq_entmax", 0, testalq->aq_entmax);
+	}
+
+	if (0 != testalq->aq_entlen) {
+		errors++;
+		sbuf_printf(debug, "alq->%-15s\texpected=%d\tactual=%d\n",
+		    "aq_entlen", 0, testalq->aq_entlen);
+	}
+
+	if (buflen != testalq->aq_freebytes) {
+		errors++;
+		sbuf_printf(debug, "alq->%-15s\texpected=%d\tactual=%d\n",
+		    "aq_freebytes", buflen, testalq->aq_freebytes);
+	}
+
+	if (buflen != testalq->aq_buflen) {
+		errors++;
+		sbuf_printf(debug, "alq->%-15s\texpected=%d\tactual=%d\n",
+		    "aq_buflen", buflen, testalq->aq_buflen);
+	}
+
+	if (0 != testalq->aq_writehead) {
+		errors++;
+		sbuf_printf(debug, "alq->%-15s\texpected=%d\tactual=%d\n",
+		    "aq_writehead", 0, testalq->aq_writehead);
+	}
+
+	if (0 != testalq->aq_writetail) {
+		errors++;
+		sbuf_printf(debug, "alq->%-15s\texpected=%d\tactual=%d\n",
+		    "aq_writetail", 0, testalq->aq_writetail);
+	}
+
+	if (AQ_VARLEN != testalq->aq_flags) {
+		errors++;
+		sbuf_printf(debug, "alq->%-15s\texpected=%d\tactual=%d\n",
+		    "aq_flags", 0, testalq->aq_flags);
+	}
+
+	alq_close(testalq);
+
+	return (errors);
+}
+
+static void
+run_test(struct sbuf *s, const char *test_banner, testfunc test)
+{
+	struct sbuf *debug = NULL;
+
+	if ((debug = sbuf_new(NULL, NULL, 1024, SBUF_AUTOEXTEND)) != NULL) {
+		alqtest_printf(s, 0, "########################################\n");
+		alqtest_printf(s, GREEN, "%s\n", test_banner);
+		if (test(s, debug)) {
+			sbuf_finish(debug);
+			alqtest_printf(s, RED, "!!ERROR(S) FOUND!!\n");
+			alqtest_printf(s, 0, "%s", sbuf_data(debug));
+			alqtest_printf(s, RED, "!!ERROR(S) FOUND!!\n");
+		}
+		alqtest_printf(s, 0, "########################################\n\n");
+		sbuf_delete(debug);
+	}
+}
+
+static void
+alqtest_thread(void *arg)
+{
+	struct sbuf *s = NULL;
+	long runs = 0;
+
+	/* loop until thread is signalled to exit */
+	while (run_test_thread && runs < NUM_TEST_RUNS) {
+		if ((s = sbuf_new(NULL, NULL, 1024, SBUF_AUTOEXTEND)) != NULL) {
+			alqtest_printf(s, 0, "TEST RUN: %ld\n", ++runs);
+
+			run_test(s, "alq_open", &alqtest_open);
+			run_test(s, "alq_writen", &alqtest_writen);
+
+			sbuf_finish(s);
+			/*printf("%s", sbuf_data(s));*/
+			sbuf_delete(s);
+		}
+	}
+
+	kthread_exit();
+}
+
+static int
+manage_test_ops(uint8_t action)
+{
+	int error = 0;
+	//struct sbuf *s = NULL;
+
+	/* init an autosizing sbuf that initially holds 200 chars */
+	//if ((s = sbuf_new(NULL, NULL, 200, SBUF_AUTOEXTEND)) == NULL)
+	//	return -1;
+
+	if (action == ENABLE) {
+
+		run_test_thread = 1;
+
+		kthread_add(&alqtest_thread, NULL, NULL, &alq_test_thr,
+		    RFNOWAIT, 0, "alq_test_thr");
+	}
+	else if (action == DISABLE && alq_test_thr != NULL) {
+		/* tell the test thread that it should exit now */
+		run_test_thread = 0;
+		alq_test_thr = NULL;
+	}
+
+	return (error);
+}
+
+static int
+deinit(void)
+{
+	manage_test_ops(DISABLE);
+	mtx_destroy(&alqtestmtx);
+	return (0);
+}
+
+static int
+init(void)
+{
+	mtx_init(&alqtestmtx, "alqtestmtx", NULL, MTX_DEF);
+	manage_test_ops(ENABLE);
+	return (0);
+}
+
+/*
+ * This is the function that is called to load and unload the module.
+ * When the module is loaded, this function is called once with
+ * "what" == MOD_LOAD
+ * When the module is unloaded, this function is called twice with
+ * "what" = MOD_QUIESCE first, followed by "what" = MOD_UNLOAD second
+ * When the system is shut down e.g. CTRL-ALT-DEL or using the shutdown command,
+ * this function is called once with "what" = MOD_SHUTDOWN
+ * When the system is shut down, the handler isn't called until the very end
+ * of the shutdown sequence i.e. after the disks have been synced.
+ */
+static int alqtest_load_handler(module_t mod, int what, void *arg)
+{
+	switch(what) {
+		case MOD_LOAD:
+			return init();
+			break;
+
+		case MOD_QUIESCE:
+		case MOD_SHUTDOWN:
+			return deinit();
+			break;
+
+		case MOD_UNLOAD:
+			return (0);
+			break;
+
+		default:
+			return (EINVAL);
+			break;
+	}
+}
+
+static moduledata_t alqtest_mod =
+{
+	"alqtest",
+	alqtest_load_handler,
+	NULL
+};
+
+DECLARE_MODULE(alqtest, alqtest_mod, SI_SUB_SMP, SI_ORDER_ANY);
+MODULE_DEPEND(alqtest, alq, 1, 1, 1);


More information about the svn-src-user mailing list