1 /*-
2 * Copyright (c) 2015 Netflix, Inc.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25 #include <sys/cdefs.h>
26 __FBSDID("$FreeBSD$");
27
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/conf.h>
31 #include <sys/cpuctl.h>
32 #include <sys/fcntl.h>
33 #include <sys/ioccom.h>
34 #include <sys/kernel.h>
35 #include <sys/libkern.h>
36 #include <sys/malloc.h>
37 #include <sys/module.h>
38 #include <sys/mutex.h>
39 #include <sys/pcpu.h>
40 #include <sys/pmckern.h>
41 #include <sys/priv.h>
42 #include <sys/proc.h>
43 #include <sys/queue.h>
44 #include <sys/sched.h>
45 #include <sys/smp.h>
46 #include <sys/sysctl.h>
47 #include <sys/uio.h>
48 #include <tests/kern_testfrwk.h>
49 #include <tests/callout_test.h>
50 #include <machine/cpu.h>
51
52 MALLOC_DEFINE(M_CALLTMP, "Temp callout Memory", "CalloutTest");
53
54 struct callout_run {
55 struct mtx lock;
56 struct callout *co_array;
57 int co_test;
58 int co_number_callouts;
59 int co_return_npa;
60 int co_completed;
61 int callout_waiting;
62 int drain_calls;
63 int cnt_zero;
64 int cnt_one;
65 int index;
66 };
67
68 static struct callout_run *comaster[MAXCPU];
69
70 uint64_t callout_total = 0;
71
72 static void execute_the_co_test(struct callout_run *rn);
73
74 static void
75 co_saydone(void *arg)
76 {
77 struct callout_run *rn;
78
79 rn = (struct callout_run *)arg;
80 printf("The callout test is now complete for thread %d\n",
81 rn->index);
82 printf("number_callouts:%d\n",
83 rn->co_number_callouts);
84 printf("Callouts that bailed (Not PENDING or ACTIVE cleared):%d\n",
85 rn->co_return_npa);
86 printf("Callouts that completed:%d\n", rn->co_completed);
87 printf("Drain calls:%d\n", rn->drain_calls);
88 printf("Zero returns:%d non-zero:%d\n",
89 rn->cnt_zero,
90 rn->cnt_one);
91
92 }
93
94 static void
95 drainit(void *arg)
96 {
97 struct callout_run *rn;
98
99 rn = (struct callout_run *)arg;
100 mtx_lock(&rn->lock);
101 rn->drain_calls++;
102 mtx_unlock(&rn->lock);
103 }
104
105 static void
106 test_callout(void *arg)
107 {
108 struct callout_run *rn;
109 int cpu;
110
111 critical_enter();
112 cpu = curcpu;
113 critical_exit();
114 rn = (struct callout_run *)arg;
115 atomic_add_int(&rn->callout_waiting, 1);
116 mtx_lock(&rn->lock);
117 if (callout_pending(&rn->co_array[cpu]) ||
118 !callout_active(&rn->co_array[cpu])) {
119 rn->co_return_npa++;
120 atomic_subtract_int(&rn->callout_waiting, 1);
121 mtx_unlock(&rn->lock);
122 return;
123 }
124 callout_deactivate(&rn->co_array[cpu]);
125 rn->co_completed++;
126 mtx_unlock(&rn->lock);
127 atomic_subtract_int(&rn->callout_waiting, 1);
128 }
129
130 void
131 execute_the_co_test(struct callout_run *rn)
132 {
133 int i, ret, cpu;
134 uint32_t tk_s, tk_e, tk_d;
135
136 mtx_lock(&rn->lock);
137 rn->callout_waiting = 0;
138 for (i = 0; i < rn->co_number_callouts; i++) {
139 if (rn->co_test == 1) {
140 /* start all on spread out cpu's */
141 cpu = i % mp_ncpus;
142 callout_reset_sbt_on(&rn->co_array[i], 3, 0, test_callout, rn,
143 cpu, 0);
144 } else {
145 /* Start all on the same CPU */
146 callout_reset_sbt_on(&rn->co_array[i], 3, 0, test_callout, rn,
147 rn->index, 0);
148 }
149 }
150 tk_s = ticks;
151 while (rn->callout_waiting != rn->co_number_callouts) {
152 cpu_spinwait();
153 tk_e = ticks;
154 tk_d = tk_e - tk_s;
155 if (tk_d > 100) {
156 break;
157 }
158 }
159 /* OK everyone is waiting and we have the lock */
160 for (i = 0; i < rn->co_number_callouts; i++) {
161 ret = callout_async_drain(&rn->co_array[i], drainit);
162 if (ret) {
163 rn->cnt_one++;
164 } else {
165 rn->cnt_zero++;
166 }
167 }
168 rn->callout_waiting -= rn->cnt_one;
169 mtx_unlock(&rn->lock);
170 /* Now wait until all are done */
171 tk_s = ticks;
172 while (rn->callout_waiting > 0) {
173 cpu_spinwait();
174 tk_e = ticks;
175 tk_d = tk_e - tk_s;
176 if (tk_d > 100) {
177 break;
178 }
179 }
180 co_saydone((void *)rn);
181 }
182
183
184 static void
185 run_callout_test(struct kern_test *test)
186 {
187 struct callout_test *u;
188 size_t sz;
189 int i;
190 struct callout_run *rn;
191 int index = test->tot_threads_running;
192
193 u = (struct callout_test *)test->test_options;
194 if (comaster[index] == NULL) {
195 rn = comaster[index] = malloc(sizeof(struct callout_run), M_CALLTMP, M_WAITOK);
196 memset(comaster[index], 0, sizeof(struct callout_run));
197 mtx_init(&rn->lock, "callouttest", NULL, MTX_DUPOK);
198 rn->index = index;
199 } else {
200 rn = comaster[index];
201 rn->co_number_callouts = rn->co_return_npa = 0;
202 rn->co_completed = rn->callout_waiting = 0;
203 rn->drain_calls = rn->cnt_zero = rn->cnt_one = 0;
204 if (rn->co_array) {
205 free(rn->co_array, M_CALLTMP);
206 rn->co_array = NULL;
207 }
208 }
209 rn->co_number_callouts = u->number_of_callouts;
210 rn->co_test = u->test_number;
211 sz = sizeof(struct callout) * rn->co_number_callouts;
212 rn->co_array = malloc(sz, M_CALLTMP, M_WAITOK);
213 for (i = 0; i < rn->co_number_callouts; i++) {
214 callout_init(&rn->co_array[i], CALLOUT_MPSAFE);
215 }
216 execute_the_co_test(rn);
217 }
218
219 int callout_test_is_loaded = 0;
220
221 static void
222 cocleanup(void)
223 {
224 int i;
225
226 for (i = 0; i < MAXCPU; i++) {
227 if (comaster[i]) {
228 if (comaster[i]->co_array) {
229 free(comaster[i]->co_array, M_CALLTMP);
230 comaster[i]->co_array = NULL;
231 }
232 free(comaster[i], M_CALLTMP);
233 comaster[i] = NULL;
234 }
235 }
236 }
237
238 static int
239 callout_test_modevent(module_t mod, int type, void *data)
240 {
241 int err = 0;
242
243 switch (type) {
244 case MOD_LOAD:
245 err = kern_testframework_register("callout_test",
246 run_callout_test);
247 if (err) {
248 printf("Can't load callout_test err:%d returned\n",
249 err);
250 } else {
251 memset(comaster, 0, sizeof(comaster));
252 callout_test_is_loaded = 1;
253 }
254 break;
255 case MOD_QUIESCE:
256 err = kern_testframework_deregister("callout_test");
257 if (err == 0) {
258 callout_test_is_loaded = 0;
259 cocleanup();
260 }
261 break;
262 case MOD_UNLOAD:
263 if (callout_test_is_loaded) {
264 err = kern_testframework_deregister("callout_test");
265 if (err == 0) {
266 cocleanup();
267 callout_test_is_loaded = 0;
268 }
269 }
270 break;
271 default:
272 return (EOPNOTSUPP);
273 }
274 return (err);
275 }
276
277 static moduledata_t callout_test_mod = {
278 .name = "callout_test",
279 .evhand = callout_test_modevent,
280 .priv = 0
281 };
282
283 MODULE_DEPEND(callout_test, kern_testframework, 1, 1, 1);
284 DECLARE_MODULE(callout_test, callout_test_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
Cache object: 0f8964466ae45a8e49c90c9dfe8148c1
|