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
co_saydone(void * arg)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
drainit(void * arg)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
test_callout(void * arg)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
execute_the_co_test(struct callout_run * rn)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
run_callout_test(struct kern_test * test)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
cocleanup(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
callout_test_modevent(module_t mod,int type,void * data)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);
285