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