1 /*-
2 * Copyright (c) 2018, Matthew Macy <[email protected]>
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 #include <sys/param.h>
29 #include <sys/types.h>
30 #include <sys/proc.h>
31 #include <sys/counter.h>
32 #include <sys/epoch.h>
33 #include <sys/gtaskqueue.h>
34 #include <sys/kernel.h>
35 #include <sys/kthread.h>
36 #include <sys/lock.h>
37 #include <sys/malloc.h>
38 #include <sys/module.h>
39 #include <sys/mutex.h>
40 #include <sys/proc.h>
41 #include <sys/sched.h>
42 #include <sys/smp.h>
43 #include <sys/sysctl.h>
44 #include <sys/systm.h>
45
46
47 struct epoch_test_instance {
48 int threadid;
49 };
50
51 static int inited;
52 static int iterations;
53 #define ET_EXITING 0x1
54 static volatile int state_flags;
55 static struct mtx state_mtx __aligned(CACHE_LINE_SIZE*2);
56 MTX_SYSINIT(state_mtx, &state_mtx, "epoch state mutex", MTX_DEF);
57 static struct mtx mutexA __aligned(CACHE_LINE_SIZE*2);
58 MTX_SYSINIT(mutexA, &mutexA, "epoch mutexA", MTX_DEF);
59 static struct mtx mutexB __aligned(CACHE_LINE_SIZE*2);
60 MTX_SYSINIT(mutexB, &mutexB, "epoch mutexB", MTX_DEF);
61 epoch_t test_epoch;
62
63 static void
epoch_testcase1(struct epoch_test_instance * eti)64 epoch_testcase1(struct epoch_test_instance *eti)
65 {
66 int i, startticks;
67 struct mtx *mtxp;
68 struct epoch_tracker et;
69
70 startticks = ticks;
71 i = 0;
72 if (eti->threadid & 0x1)
73 mtxp = &mutexA;
74 else
75 mtxp = &mutexB;
76
77 while (i < iterations) {
78 epoch_enter_preempt(test_epoch, &et);
79 mtx_lock(mtxp);
80 i++;
81 mtx_unlock(mtxp);
82 epoch_exit_preempt(test_epoch, &et);
83 epoch_wait_preempt(test_epoch);
84 }
85 printf("test1: thread: %d took %d ticks to complete %d iterations\n",
86 eti->threadid, ticks - startticks, iterations);
87 }
88
89 static void
epoch_testcase2(struct epoch_test_instance * eti)90 epoch_testcase2(struct epoch_test_instance *eti)
91 {
92 int i, startticks;
93 struct mtx *mtxp;
94 struct epoch_tracker et;
95
96 startticks = ticks;
97 i = 0;
98 mtxp = &mutexA;
99
100 while (i < iterations) {
101 epoch_enter_preempt(test_epoch, &et);
102 mtx_lock(mtxp);
103 DELAY(1);
104 i++;
105 mtx_unlock(mtxp);
106 epoch_exit_preempt(test_epoch, &et);
107 epoch_wait_preempt(test_epoch);
108 }
109 printf("test2: thread: %d took %d ticks to complete %d iterations\n",
110 eti->threadid, ticks - startticks, iterations);
111 }
112
113 static void
testloop(void * arg)114 testloop(void *arg) {
115
116 mtx_lock(&state_mtx);
117 while ((state_flags & ET_EXITING) == 0) {
118 msleep(&state_mtx, &state_mtx, 0, "epoch start wait", 0);
119 if (state_flags & ET_EXITING)
120 goto out;
121 mtx_unlock(&state_mtx);
122 epoch_testcase2(arg);
123 pause("W", 500);
124 epoch_testcase1(arg);
125 mtx_lock(&state_mtx);
126 }
127 out:
128 mtx_unlock(&state_mtx);
129 kthread_exit();
130 }
131
132 static struct thread *testthreads[MAXCPU];
133 static struct epoch_test_instance etilist[MAXCPU];
134
135 static int
test_modinit(void)136 test_modinit(void)
137 {
138 struct thread *td;
139 int i, error, pri_range, pri_off;
140
141 pri_range = PRI_MIN_TIMESHARE - PRI_MIN_REALTIME;
142 test_epoch = epoch_alloc("test_epoch", EPOCH_PREEMPT);
143 for (i = 0; i < mp_ncpus*2; i++) {
144 etilist[i].threadid = i;
145 error = kthread_add(testloop, &etilist[i], NULL, &testthreads[i],
146 0, 0, "epoch_test_%d", i);
147 if (error) {
148 printf("%s: kthread_add(epoch_test): error %d", __func__,
149 error);
150 } else {
151 pri_off = (i*4)%pri_range;
152 td = testthreads[i];
153 thread_lock(td);
154 sched_prio(td, PRI_MIN_REALTIME + pri_off);
155 thread_unlock(td);
156 }
157 }
158 inited = 1;
159 return (0);
160 }
161
162 static int
epochtest_execute(SYSCTL_HANDLER_ARGS)163 epochtest_execute(SYSCTL_HANDLER_ARGS)
164 {
165 int error, v;
166
167 if (inited == 0)
168 return (ENOENT);
169
170 v = 0;
171 error = sysctl_handle_int(oidp, &v, 0, req);
172 if (error)
173 return (error);
174 if (req->newptr == NULL)
175 return (error);
176 if (v == 0)
177 return (0);
178 mtx_lock(&state_mtx);
179 iterations = v;
180 wakeup(&state_mtx);
181 mtx_unlock(&state_mtx);
182
183 return (0);
184 }
185
186 SYSCTL_NODE(_kern, OID_AUTO, epochtest, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
187 "Epoch Test Framework");
188 SYSCTL_PROC(_kern_epochtest, OID_AUTO, runtest,
189 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
190 0, 0, epochtest_execute, "I",
191 "Execute an epoch test");
192
193 static int
epoch_test_module_event_handler(module_t mod,int what,void * arg __unused)194 epoch_test_module_event_handler(module_t mod, int what, void *arg __unused)
195 {
196 int err;
197
198 switch (what) {
199 case MOD_LOAD:
200 if ((err = test_modinit()) != 0)
201 return (err);
202 break;
203 case MOD_UNLOAD:
204 mtx_lock(&state_mtx);
205 state_flags = ET_EXITING;
206 wakeup(&state_mtx);
207 mtx_unlock(&state_mtx);
208 /* yes --- gross */
209 pause("epoch unload", 3*hz);
210 epoch_free(test_epoch);
211 break;
212 default:
213 return (EOPNOTSUPP);
214 }
215
216 return (0);
217 }
218
219 static moduledata_t epoch_test_moduledata = {
220 "epoch_test",
221 epoch_test_module_event_handler,
222 NULL
223 };
224
225 MODULE_VERSION(epoch_test, 1);
226 DECLARE_MODULE(epoch_test, epoch_test_moduledata, SI_SUB_PSEUDO, SI_ORDER_ANY);
227