xref: /freebsd/sys/tests/epoch/epoch_test.c (revision 06bf2a6aefbf98f0717954368a8791cd70bfba30)
1*06bf2a6aSMatt Macy /*-
2*06bf2a6aSMatt Macy  * Copyright (c) 2018, Matthew Macy <mmacy@freebsd.org>
3*06bf2a6aSMatt Macy  *
4*06bf2a6aSMatt Macy  * Redistribution and use in source and binary forms, with or without
5*06bf2a6aSMatt Macy  * modification, are permitted provided that the following conditions are met:
6*06bf2a6aSMatt Macy  *
7*06bf2a6aSMatt Macy  *  1. Redistributions of source code must retain the above copyright notice,
8*06bf2a6aSMatt Macy  *     this list of conditions and the following disclaimer.
9*06bf2a6aSMatt Macy  *
10*06bf2a6aSMatt Macy  *  2. Neither the name of Matthew Macy nor the names of its
11*06bf2a6aSMatt Macy  *     contributors may be used to endorse or promote products derived from
12*06bf2a6aSMatt Macy  *     this software without specific prior written permission.
13*06bf2a6aSMatt Macy  *
14*06bf2a6aSMatt Macy  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15*06bf2a6aSMatt Macy  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*06bf2a6aSMatt Macy  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*06bf2a6aSMatt Macy  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18*06bf2a6aSMatt Macy  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19*06bf2a6aSMatt Macy  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20*06bf2a6aSMatt Macy  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21*06bf2a6aSMatt Macy  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22*06bf2a6aSMatt Macy  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23*06bf2a6aSMatt Macy  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24*06bf2a6aSMatt Macy  * POSSIBILITY OF SUCH DAMAGE.
25*06bf2a6aSMatt Macy  */
26*06bf2a6aSMatt Macy 
27*06bf2a6aSMatt Macy #include <sys/cdefs.h>
28*06bf2a6aSMatt Macy __FBSDID("$FreeBSD$");
29*06bf2a6aSMatt Macy 
30*06bf2a6aSMatt Macy #include <sys/param.h>
31*06bf2a6aSMatt Macy #include <sys/types.h>
32*06bf2a6aSMatt Macy #include <sys/counter.h>
33*06bf2a6aSMatt Macy #include <sys/epoch.h>
34*06bf2a6aSMatt Macy #include <sys/gtaskqueue.h>
35*06bf2a6aSMatt Macy #include <sys/kernel.h>
36*06bf2a6aSMatt Macy #include <sys/kthread.h>
37*06bf2a6aSMatt Macy #include <sys/lock.h>
38*06bf2a6aSMatt Macy #include <sys/malloc.h>
39*06bf2a6aSMatt Macy #include <sys/module.h>
40*06bf2a6aSMatt Macy #include <sys/mutex.h>
41*06bf2a6aSMatt Macy #include <sys/smp.h>
42*06bf2a6aSMatt Macy #include <sys/sysctl.h>
43*06bf2a6aSMatt Macy #include <sys/systm.h>
44*06bf2a6aSMatt Macy 
45*06bf2a6aSMatt Macy 
46*06bf2a6aSMatt Macy struct epoch_test_instance {
47*06bf2a6aSMatt Macy 	int threadid;
48*06bf2a6aSMatt Macy };
49*06bf2a6aSMatt Macy 
50*06bf2a6aSMatt Macy static int inited;
51*06bf2a6aSMatt Macy static int iterations;
52*06bf2a6aSMatt Macy #define ET_EXITING 0x1
53*06bf2a6aSMatt Macy static volatile int state_flags;
54*06bf2a6aSMatt Macy static struct mtx state_mtx __aligned(CACHE_LINE_SIZE*2);
55*06bf2a6aSMatt Macy MTX_SYSINIT(state_mtx, &state_mtx, "epoch state mutex", MTX_DEF);
56*06bf2a6aSMatt Macy static struct mtx mutexA __aligned(CACHE_LINE_SIZE*2);
57*06bf2a6aSMatt Macy MTX_SYSINIT(mutexA, &mutexA, "epoch mutexA", MTX_DEF);
58*06bf2a6aSMatt Macy static struct mtx mutexB __aligned(CACHE_LINE_SIZE*2);
59*06bf2a6aSMatt Macy MTX_SYSINIT(mutexB, &mutexB, "epoch mutexB", MTX_DEF);
60*06bf2a6aSMatt Macy epoch_t test_epoch;
61*06bf2a6aSMatt Macy 
62*06bf2a6aSMatt Macy static void
63*06bf2a6aSMatt Macy epoch_testcase1(struct epoch_test_instance *eti)
64*06bf2a6aSMatt Macy {
65*06bf2a6aSMatt Macy 	int i, startticks;
66*06bf2a6aSMatt Macy 	struct mtx *mtxp;
67*06bf2a6aSMatt Macy 
68*06bf2a6aSMatt Macy 	startticks = ticks;
69*06bf2a6aSMatt Macy 	i = 0;
70*06bf2a6aSMatt Macy 	if (eti->threadid & 0x1)
71*06bf2a6aSMatt Macy 		mtxp = &mutexA;
72*06bf2a6aSMatt Macy 	else
73*06bf2a6aSMatt Macy 		mtxp = &mutexB;
74*06bf2a6aSMatt Macy 
75*06bf2a6aSMatt Macy 	while (i < iterations) {
76*06bf2a6aSMatt Macy 		epoch_enter(test_epoch);
77*06bf2a6aSMatt Macy 		mtx_lock(mtxp);
78*06bf2a6aSMatt Macy 		i++;
79*06bf2a6aSMatt Macy 		mtx_unlock(mtxp);
80*06bf2a6aSMatt Macy 		epoch_exit(test_epoch);
81*06bf2a6aSMatt Macy 		epoch_wait(test_epoch);
82*06bf2a6aSMatt Macy 	}
83*06bf2a6aSMatt Macy 	printf("test1: thread: %d took %d ticks to complete %d iterations\n",
84*06bf2a6aSMatt Macy 		   eti->threadid, ticks - startticks, iterations);
85*06bf2a6aSMatt Macy }
86*06bf2a6aSMatt Macy 
87*06bf2a6aSMatt Macy static void
88*06bf2a6aSMatt Macy epoch_testcase2(struct epoch_test_instance *eti)
89*06bf2a6aSMatt Macy {
90*06bf2a6aSMatt Macy 	int i, startticks;
91*06bf2a6aSMatt Macy 	struct mtx *mtxp;
92*06bf2a6aSMatt Macy 
93*06bf2a6aSMatt Macy 	startticks = ticks;
94*06bf2a6aSMatt Macy 	i = 0;
95*06bf2a6aSMatt Macy 	mtxp = &mutexA;
96*06bf2a6aSMatt Macy 
97*06bf2a6aSMatt Macy 	while (i < iterations) {
98*06bf2a6aSMatt Macy 		epoch_enter(test_epoch);
99*06bf2a6aSMatt Macy 		mtx_lock(mtxp);
100*06bf2a6aSMatt Macy 		DELAY(1);
101*06bf2a6aSMatt Macy 		i++;
102*06bf2a6aSMatt Macy 		mtx_unlock(mtxp);
103*06bf2a6aSMatt Macy 		epoch_exit(test_epoch);
104*06bf2a6aSMatt Macy 		epoch_wait(test_epoch);
105*06bf2a6aSMatt Macy 	}
106*06bf2a6aSMatt Macy 	printf("test2: thread: %d took %d ticks to complete %d iterations\n",
107*06bf2a6aSMatt Macy 		   eti->threadid, ticks - startticks, iterations);
108*06bf2a6aSMatt Macy }
109*06bf2a6aSMatt Macy 
110*06bf2a6aSMatt Macy static void
111*06bf2a6aSMatt Macy testloop(void *arg) {
112*06bf2a6aSMatt Macy 
113*06bf2a6aSMatt Macy 	mtx_lock(&state_mtx);
114*06bf2a6aSMatt Macy 	while ((state_flags & ET_EXITING) == 0) {
115*06bf2a6aSMatt Macy 		msleep(&state_mtx, &state_mtx, 0, "epoch start wait", 0);
116*06bf2a6aSMatt Macy 		if (state_flags & ET_EXITING)
117*06bf2a6aSMatt Macy 			goto out;
118*06bf2a6aSMatt Macy 		mtx_unlock(&state_mtx);
119*06bf2a6aSMatt Macy 		epoch_testcase2(arg);
120*06bf2a6aSMatt Macy 		pause("W", 500);
121*06bf2a6aSMatt Macy 		epoch_testcase1(arg);
122*06bf2a6aSMatt Macy 		mtx_lock(&state_mtx);
123*06bf2a6aSMatt Macy 	}
124*06bf2a6aSMatt Macy  out:
125*06bf2a6aSMatt Macy 	mtx_unlock(&state_mtx);
126*06bf2a6aSMatt Macy 	kthread_exit();
127*06bf2a6aSMatt Macy }
128*06bf2a6aSMatt Macy 
129*06bf2a6aSMatt Macy static struct thread *testthreads[MAXCPU];
130*06bf2a6aSMatt Macy static struct epoch_test_instance etilist[MAXCPU];
131*06bf2a6aSMatt Macy 
132*06bf2a6aSMatt Macy static int
133*06bf2a6aSMatt Macy test_modinit(void)
134*06bf2a6aSMatt Macy {
135*06bf2a6aSMatt Macy 	int i, error;
136*06bf2a6aSMatt Macy 
137*06bf2a6aSMatt Macy 	test_epoch = epoch_alloc();
138*06bf2a6aSMatt Macy 	for (i = 0; i < mp_ncpus; i++) {
139*06bf2a6aSMatt Macy 		etilist[i].threadid = i;
140*06bf2a6aSMatt Macy 		error = kthread_add(testloop, &etilist[i], NULL, &testthreads[i],
141*06bf2a6aSMatt Macy 							0, 0, "epoch_test_%d", i);
142*06bf2a6aSMatt Macy 		if (error) {
143*06bf2a6aSMatt Macy 			printf("%s: kthread_add(epoch_test): error %d", __func__,
144*06bf2a6aSMatt Macy 				   error);
145*06bf2a6aSMatt Macy 		}
146*06bf2a6aSMatt Macy 	}
147*06bf2a6aSMatt Macy 	inited = 1;
148*06bf2a6aSMatt Macy 	return (0);
149*06bf2a6aSMatt Macy }
150*06bf2a6aSMatt Macy 
151*06bf2a6aSMatt Macy static int
152*06bf2a6aSMatt Macy epochtest_execute(SYSCTL_HANDLER_ARGS)
153*06bf2a6aSMatt Macy {
154*06bf2a6aSMatt Macy 	int error, v;
155*06bf2a6aSMatt Macy 
156*06bf2a6aSMatt Macy 	if (inited == 0)
157*06bf2a6aSMatt Macy 		return (ENOENT);
158*06bf2a6aSMatt Macy 
159*06bf2a6aSMatt Macy 	v = 0;
160*06bf2a6aSMatt Macy 	error = sysctl_handle_int(oidp, &v, 0, req);
161*06bf2a6aSMatt Macy 	if (error)
162*06bf2a6aSMatt Macy 		return (error);
163*06bf2a6aSMatt Macy 	if (req->newptr == NULL)
164*06bf2a6aSMatt Macy 		return (error);
165*06bf2a6aSMatt Macy 	if (v == 0)
166*06bf2a6aSMatt Macy 		return (0);
167*06bf2a6aSMatt Macy 	mtx_lock(&state_mtx);
168*06bf2a6aSMatt Macy 	iterations = v;
169*06bf2a6aSMatt Macy 	wakeup(&state_mtx);
170*06bf2a6aSMatt Macy 	mtx_unlock(&state_mtx);
171*06bf2a6aSMatt Macy 
172*06bf2a6aSMatt Macy 	return (0);
173*06bf2a6aSMatt Macy }
174*06bf2a6aSMatt Macy 
175*06bf2a6aSMatt Macy SYSCTL_NODE(_kern, OID_AUTO, epochtest, CTLFLAG_RW, 0, "Epoch Test Framework");
176*06bf2a6aSMatt Macy SYSCTL_PROC(_kern_epochtest, OID_AUTO, runtest, (CTLTYPE_INT | CTLFLAG_RW),
177*06bf2a6aSMatt Macy 			0, 0, epochtest_execute, "I", "Execute an epoch test");
178*06bf2a6aSMatt Macy 
179*06bf2a6aSMatt Macy static int
180*06bf2a6aSMatt Macy epoch_test_module_event_handler(module_t mod, int what, void *arg __unused)
181*06bf2a6aSMatt Macy {
182*06bf2a6aSMatt Macy 	int err;
183*06bf2a6aSMatt Macy 
184*06bf2a6aSMatt Macy 	switch (what) {
185*06bf2a6aSMatt Macy 	case MOD_LOAD:
186*06bf2a6aSMatt Macy 		if ((err = test_modinit()) != 0)
187*06bf2a6aSMatt Macy 			return (err);
188*06bf2a6aSMatt Macy 		break;
189*06bf2a6aSMatt Macy 	case MOD_UNLOAD:
190*06bf2a6aSMatt Macy 		mtx_lock(&state_mtx);
191*06bf2a6aSMatt Macy 		state_flags = ET_EXITING;
192*06bf2a6aSMatt Macy 		wakeup(&state_mtx);
193*06bf2a6aSMatt Macy 		mtx_unlock(&state_mtx);
194*06bf2a6aSMatt Macy 		/* yes --- gross */
195*06bf2a6aSMatt Macy 		pause("epoch unload", 3*hz);
196*06bf2a6aSMatt Macy 		break;
197*06bf2a6aSMatt Macy 	default:
198*06bf2a6aSMatt Macy 		return (EOPNOTSUPP);
199*06bf2a6aSMatt Macy 	}
200*06bf2a6aSMatt Macy 
201*06bf2a6aSMatt Macy 	return (0);
202*06bf2a6aSMatt Macy }
203*06bf2a6aSMatt Macy 
204*06bf2a6aSMatt Macy static moduledata_t epoch_test_moduledata = {
205*06bf2a6aSMatt Macy 	"epoch_test",
206*06bf2a6aSMatt Macy 	epoch_test_module_event_handler,
207*06bf2a6aSMatt Macy 	NULL
208*06bf2a6aSMatt Macy };
209*06bf2a6aSMatt Macy 
210*06bf2a6aSMatt Macy MODULE_VERSION(epoch_test, 1);
211*06bf2a6aSMatt Macy DECLARE_MODULE(epoch_test, epoch_test_moduledata, SI_SUB_PSEUDO, SI_ORDER_ANY);
212