xref: /freebsd/sys/tests/epoch/epoch_test.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
106bf2a6aSMatt Macy /*-
206bf2a6aSMatt Macy  * Copyright (c) 2018, Matthew Macy <mmacy@freebsd.org>
306bf2a6aSMatt Macy  *
406bf2a6aSMatt Macy  * Redistribution and use in source and binary forms, with or without
506bf2a6aSMatt Macy  * modification, are permitted provided that the following conditions are met:
606bf2a6aSMatt Macy  *
706bf2a6aSMatt Macy  *  1. Redistributions of source code must retain the above copyright notice,
806bf2a6aSMatt Macy  *     this list of conditions and the following disclaimer.
906bf2a6aSMatt Macy  *
1006bf2a6aSMatt Macy  *  2. Neither the name of Matthew Macy nor the names of its
1106bf2a6aSMatt Macy  *     contributors may be used to endorse or promote products derived from
1206bf2a6aSMatt Macy  *     this software without specific prior written permission.
1306bf2a6aSMatt Macy  *
1406bf2a6aSMatt Macy  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1506bf2a6aSMatt Macy  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1606bf2a6aSMatt Macy  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1706bf2a6aSMatt Macy  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
1806bf2a6aSMatt Macy  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1906bf2a6aSMatt Macy  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2006bf2a6aSMatt Macy  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2106bf2a6aSMatt Macy  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2206bf2a6aSMatt Macy  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2306bf2a6aSMatt Macy  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2406bf2a6aSMatt Macy  * POSSIBILITY OF SUCH DAMAGE.
2506bf2a6aSMatt Macy  */
2606bf2a6aSMatt Macy 
2706bf2a6aSMatt Macy #include <sys/param.h>
2806bf2a6aSMatt Macy #include <sys/types.h>
295e68a3dfSMatt Macy #include <sys/proc.h>
3006bf2a6aSMatt Macy #include <sys/counter.h>
3106bf2a6aSMatt Macy #include <sys/epoch.h>
3206bf2a6aSMatt Macy #include <sys/gtaskqueue.h>
3306bf2a6aSMatt Macy #include <sys/kernel.h>
3406bf2a6aSMatt Macy #include <sys/kthread.h>
3506bf2a6aSMatt Macy #include <sys/lock.h>
3606bf2a6aSMatt Macy #include <sys/malloc.h>
3706bf2a6aSMatt Macy #include <sys/module.h>
3806bf2a6aSMatt Macy #include <sys/mutex.h>
39ef7f29d8SMatt Macy #include <sys/proc.h>
40ef7f29d8SMatt Macy #include <sys/sched.h>
4106bf2a6aSMatt Macy #include <sys/smp.h>
4206bf2a6aSMatt Macy #include <sys/sysctl.h>
4306bf2a6aSMatt Macy #include <sys/systm.h>
4406bf2a6aSMatt Macy 
4506bf2a6aSMatt Macy 
4606bf2a6aSMatt Macy struct epoch_test_instance {
4706bf2a6aSMatt Macy 	int threadid;
4806bf2a6aSMatt Macy };
4906bf2a6aSMatt Macy 
5006bf2a6aSMatt Macy static int inited;
5106bf2a6aSMatt Macy static int iterations;
5206bf2a6aSMatt Macy #define ET_EXITING 0x1
5306bf2a6aSMatt Macy static volatile int state_flags;
5406bf2a6aSMatt Macy static struct mtx state_mtx __aligned(CACHE_LINE_SIZE*2);
5506bf2a6aSMatt Macy MTX_SYSINIT(state_mtx, &state_mtx, "epoch state mutex", MTX_DEF);
5606bf2a6aSMatt Macy static struct mtx mutexA __aligned(CACHE_LINE_SIZE*2);
5706bf2a6aSMatt Macy MTX_SYSINIT(mutexA, &mutexA, "epoch mutexA", MTX_DEF);
5806bf2a6aSMatt Macy static struct mtx mutexB __aligned(CACHE_LINE_SIZE*2);
5906bf2a6aSMatt Macy MTX_SYSINIT(mutexB, &mutexB, "epoch mutexB", MTX_DEF);
6006bf2a6aSMatt Macy epoch_t test_epoch;
6106bf2a6aSMatt Macy 
6206bf2a6aSMatt Macy static void
epoch_testcase1(struct epoch_test_instance * eti)6306bf2a6aSMatt Macy epoch_testcase1(struct epoch_test_instance *eti)
6406bf2a6aSMatt Macy {
6506bf2a6aSMatt Macy 	int i, startticks;
6606bf2a6aSMatt Macy 	struct mtx *mtxp;
67471826c2SMatt Macy 	struct epoch_tracker et;
6806bf2a6aSMatt Macy 
6906bf2a6aSMatt Macy 	startticks = ticks;
7006bf2a6aSMatt Macy 	i = 0;
7106bf2a6aSMatt Macy 	if (eti->threadid & 0x1)
7206bf2a6aSMatt Macy 		mtxp = &mutexA;
7306bf2a6aSMatt Macy 	else
7406bf2a6aSMatt Macy 		mtxp = &mutexB;
7506bf2a6aSMatt Macy 
7606bf2a6aSMatt Macy 	while (i < iterations) {
77471826c2SMatt Macy 		epoch_enter_preempt(test_epoch, &et);
7806bf2a6aSMatt Macy 		mtx_lock(mtxp);
7906bf2a6aSMatt Macy 		i++;
8006bf2a6aSMatt Macy 		mtx_unlock(mtxp);
81471826c2SMatt Macy 		epoch_exit_preempt(test_epoch, &et);
8270398c2fSMatt Macy 		epoch_wait_preempt(test_epoch);
8306bf2a6aSMatt Macy 	}
8406bf2a6aSMatt Macy 	printf("test1: thread: %d took %d ticks to complete %d iterations\n",
8506bf2a6aSMatt Macy 		   eti->threadid, ticks - startticks, iterations);
8606bf2a6aSMatt Macy }
8706bf2a6aSMatt Macy 
8806bf2a6aSMatt Macy static void
epoch_testcase2(struct epoch_test_instance * eti)8906bf2a6aSMatt Macy epoch_testcase2(struct epoch_test_instance *eti)
9006bf2a6aSMatt Macy {
9106bf2a6aSMatt Macy 	int i, startticks;
9206bf2a6aSMatt Macy 	struct mtx *mtxp;
93471826c2SMatt Macy 	struct epoch_tracker et;
9406bf2a6aSMatt Macy 
9506bf2a6aSMatt Macy 	startticks = ticks;
9606bf2a6aSMatt Macy 	i = 0;
9706bf2a6aSMatt Macy 	mtxp = &mutexA;
9806bf2a6aSMatt Macy 
9906bf2a6aSMatt Macy 	while (i < iterations) {
100471826c2SMatt Macy 		epoch_enter_preempt(test_epoch, &et);
10106bf2a6aSMatt Macy 		mtx_lock(mtxp);
10206bf2a6aSMatt Macy 		DELAY(1);
10306bf2a6aSMatt Macy 		i++;
10406bf2a6aSMatt Macy 		mtx_unlock(mtxp);
105471826c2SMatt Macy 		epoch_exit_preempt(test_epoch, &et);
10670398c2fSMatt Macy 		epoch_wait_preempt(test_epoch);
10706bf2a6aSMatt Macy 	}
10806bf2a6aSMatt Macy 	printf("test2: thread: %d took %d ticks to complete %d iterations\n",
10906bf2a6aSMatt Macy 		   eti->threadid, ticks - startticks, iterations);
11006bf2a6aSMatt Macy }
11106bf2a6aSMatt Macy 
11206bf2a6aSMatt Macy static void
testloop(void * arg)11306bf2a6aSMatt Macy testloop(void *arg) {
11406bf2a6aSMatt Macy 
11506bf2a6aSMatt Macy 	mtx_lock(&state_mtx);
11606bf2a6aSMatt Macy 	while ((state_flags & ET_EXITING) == 0) {
11706bf2a6aSMatt Macy 		msleep(&state_mtx, &state_mtx, 0, "epoch start wait", 0);
11806bf2a6aSMatt Macy 		if (state_flags & ET_EXITING)
11906bf2a6aSMatt Macy 			goto out;
12006bf2a6aSMatt Macy 		mtx_unlock(&state_mtx);
12106bf2a6aSMatt Macy 		epoch_testcase2(arg);
12206bf2a6aSMatt Macy 		pause("W", 500);
12306bf2a6aSMatt Macy 		epoch_testcase1(arg);
12406bf2a6aSMatt Macy 		mtx_lock(&state_mtx);
12506bf2a6aSMatt Macy 	}
12606bf2a6aSMatt Macy  out:
12706bf2a6aSMatt Macy 	mtx_unlock(&state_mtx);
12806bf2a6aSMatt Macy 	kthread_exit();
12906bf2a6aSMatt Macy }
13006bf2a6aSMatt Macy 
13106bf2a6aSMatt Macy static struct thread *testthreads[MAXCPU];
13206bf2a6aSMatt Macy static struct epoch_test_instance etilist[MAXCPU];
13306bf2a6aSMatt Macy 
13406bf2a6aSMatt Macy static int
test_modinit(void)13506bf2a6aSMatt Macy test_modinit(void)
13606bf2a6aSMatt Macy {
137ef7f29d8SMatt Macy 	struct thread *td;
138ef7f29d8SMatt Macy 	int i, error, pri_range, pri_off;
13906bf2a6aSMatt Macy 
140ef7f29d8SMatt Macy 	pri_range = PRI_MIN_TIMESHARE - PRI_MIN_REALTIME;
141*b1acfb24SHans Petter Selasky 	test_epoch = epoch_alloc("test_epoch", EPOCH_PREEMPT);
142ef7f29d8SMatt Macy 	for (i = 0; i < mp_ncpus*2; i++) {
14306bf2a6aSMatt Macy 		etilist[i].threadid = i;
14406bf2a6aSMatt Macy 		error = kthread_add(testloop, &etilist[i], NULL, &testthreads[i],
14506bf2a6aSMatt Macy 							0, 0, "epoch_test_%d", i);
14606bf2a6aSMatt Macy 		if (error) {
14706bf2a6aSMatt Macy 			printf("%s: kthread_add(epoch_test): error %d", __func__,
14806bf2a6aSMatt Macy 				   error);
149ef7f29d8SMatt Macy 		} else {
150ef7f29d8SMatt Macy 			pri_off = (i*4)%pri_range;
151ef7f29d8SMatt Macy 			td = testthreads[i];
152ef7f29d8SMatt Macy 			thread_lock(td);
153ef7f29d8SMatt Macy 			sched_prio(td, PRI_MIN_REALTIME + pri_off);
154ef7f29d8SMatt Macy 			thread_unlock(td);
15506bf2a6aSMatt Macy 		}
15606bf2a6aSMatt Macy 	}
15706bf2a6aSMatt Macy 	inited = 1;
15806bf2a6aSMatt Macy 	return (0);
15906bf2a6aSMatt Macy }
16006bf2a6aSMatt Macy 
16106bf2a6aSMatt Macy static int
epochtest_execute(SYSCTL_HANDLER_ARGS)16206bf2a6aSMatt Macy epochtest_execute(SYSCTL_HANDLER_ARGS)
16306bf2a6aSMatt Macy {
16406bf2a6aSMatt Macy 	int error, v;
16506bf2a6aSMatt Macy 
16606bf2a6aSMatt Macy 	if (inited == 0)
16706bf2a6aSMatt Macy 		return (ENOENT);
16806bf2a6aSMatt Macy 
16906bf2a6aSMatt Macy 	v = 0;
17006bf2a6aSMatt Macy 	error = sysctl_handle_int(oidp, &v, 0, req);
17106bf2a6aSMatt Macy 	if (error)
17206bf2a6aSMatt Macy 		return (error);
17306bf2a6aSMatt Macy 	if (req->newptr == NULL)
17406bf2a6aSMatt Macy 		return (error);
17506bf2a6aSMatt Macy 	if (v == 0)
17606bf2a6aSMatt Macy 		return (0);
17706bf2a6aSMatt Macy 	mtx_lock(&state_mtx);
17806bf2a6aSMatt Macy 	iterations = v;
17906bf2a6aSMatt Macy 	wakeup(&state_mtx);
18006bf2a6aSMatt Macy 	mtx_unlock(&state_mtx);
18106bf2a6aSMatt Macy 
18206bf2a6aSMatt Macy 	return (0);
18306bf2a6aSMatt Macy }
18406bf2a6aSMatt Macy 
1857029da5cSPawel Biernacki SYSCTL_NODE(_kern, OID_AUTO, epochtest, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
1867029da5cSPawel Biernacki     "Epoch Test Framework");
1877029da5cSPawel Biernacki SYSCTL_PROC(_kern_epochtest, OID_AUTO, runtest,
1887029da5cSPawel Biernacki     CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
1897029da5cSPawel Biernacki     0, 0, epochtest_execute, "I",
1907029da5cSPawel Biernacki     "Execute an epoch test");
19106bf2a6aSMatt Macy 
19206bf2a6aSMatt Macy static int
epoch_test_module_event_handler(module_t mod,int what,void * arg __unused)19306bf2a6aSMatt Macy epoch_test_module_event_handler(module_t mod, int what, void *arg __unused)
19406bf2a6aSMatt Macy {
19506bf2a6aSMatt Macy 	int err;
19606bf2a6aSMatt Macy 
19706bf2a6aSMatt Macy 	switch (what) {
19806bf2a6aSMatt Macy 	case MOD_LOAD:
19906bf2a6aSMatt Macy 		if ((err = test_modinit()) != 0)
20006bf2a6aSMatt Macy 			return (err);
20106bf2a6aSMatt Macy 		break;
20206bf2a6aSMatt Macy 	case MOD_UNLOAD:
20306bf2a6aSMatt Macy 		mtx_lock(&state_mtx);
20406bf2a6aSMatt Macy 		state_flags = ET_EXITING;
20506bf2a6aSMatt Macy 		wakeup(&state_mtx);
20606bf2a6aSMatt Macy 		mtx_unlock(&state_mtx);
20706bf2a6aSMatt Macy 		/* yes --- gross */
20806bf2a6aSMatt Macy 		pause("epoch unload", 3*hz);
209*b1acfb24SHans Petter Selasky 		epoch_free(test_epoch);
21006bf2a6aSMatt Macy 		break;
21106bf2a6aSMatt Macy 	default:
21206bf2a6aSMatt Macy 		return (EOPNOTSUPP);
21306bf2a6aSMatt Macy 	}
21406bf2a6aSMatt Macy 
21506bf2a6aSMatt Macy 	return (0);
21606bf2a6aSMatt Macy }
21706bf2a6aSMatt Macy 
21806bf2a6aSMatt Macy static moduledata_t epoch_test_moduledata = {
21906bf2a6aSMatt Macy 	"epoch_test",
22006bf2a6aSMatt Macy 	epoch_test_module_event_handler,
22106bf2a6aSMatt Macy 	NULL
22206bf2a6aSMatt Macy };
22306bf2a6aSMatt Macy 
22406bf2a6aSMatt Macy MODULE_VERSION(epoch_test, 1);
22506bf2a6aSMatt Macy DECLARE_MODULE(epoch_test, epoch_test_moduledata, SI_SUB_PSEUDO, SI_ORDER_ANY);
226