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("test_epoch", 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 epoch_free(test_epoch);
213 break;
214 default:
215 return (EOPNOTSUPP);
216 }
217
218 return (0);
219 }
220
221 static moduledata_t epoch_test_moduledata = {
222 "epoch_test",
223 epoch_test_module_event_handler,
224 NULL
225 };
226
227 MODULE_VERSION(epoch_test, 1);
228 DECLARE_MODULE(epoch_test, epoch_test_moduledata, SI_SUB_PSEUDO, SI_ORDER_ANY);
Cache object: 29a3d6b455d0e3210da05df85b6265e6
|