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