]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/tests/callout_test/callout_test.c
MFC r343755:
[FreeBSD/FreeBSD.git] / sys / tests / callout_test / callout_test.c
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);