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