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 #include <sys/param.h>
27 #include <sys/systm.h>
28 #include <sys/conf.h>
29 #include <sys/cpuctl.h>
30 #include <sys/fcntl.h>
31 #include <sys/ioccom.h>
32 #include <sys/kernel.h>
33 #include <sys/libkern.h>
34 #include <sys/malloc.h>
35 #include <sys/module.h>
36 #include <sys/mutex.h>
37 #include <sys/pcpu.h>
38 #include <sys/pmckern.h>
39 #include <sys/priv.h>
40 #include <sys/proc.h>
41 #include <sys/queue.h>
42 #include <sys/sched.h>
43 #include <sys/smp.h>
44 #include <sys/sysctl.h>
45 #include <sys/uio.h>
46 #include <tests/kern_testfrwk.h>
47 #include <tests/callout_test.h>
48 #include <machine/cpu.h>
49 
50 MALLOC_DEFINE(M_CALLTMP, "Temp callout Memory", "CalloutTest");
51 
52 struct callout_run {
53 	struct mtx lock;
54 	struct callout *co_array;
55 	int co_test;
56 	int co_number_callouts;
57 	int co_return_npa;
58 	int co_completed;
59 	int callout_waiting;
60 	int drain_calls;
61 	int cnt_zero;
62 	int cnt_one;
63 	int index;
64 };
65 
66 static struct callout_run *comaster[MAXCPU];
67 
68 uint64_t callout_total = 0;
69 
70 static void execute_the_co_test(struct callout_run *rn);
71 
72 static void
co_saydone(void * arg)73 co_saydone(void *arg)
74 {
75 	struct callout_run *rn;
76 
77 	rn = (struct callout_run *)arg;
78 	printf("The callout test is now complete for thread %d\n",
79 	    rn->index);
80 	printf("number_callouts:%d\n",
81 	    rn->co_number_callouts);
82 	printf("Callouts that bailed (Not PENDING or ACTIVE cleared):%d\n",
83 	    rn->co_return_npa);
84 	printf("Callouts that completed:%d\n", rn->co_completed);
85 	printf("Drain calls:%d\n", rn->drain_calls);
86 	printf("Zero returns:%d non-zero:%d\n",
87 	    rn->cnt_zero,
88 	    rn->cnt_one);
89 
90 }
91 
92 static void
drainit(void * arg)93 drainit(void *arg)
94 {
95 	struct callout_run *rn;
96 
97 	rn = (struct callout_run *)arg;
98 	mtx_lock(&rn->lock);
99 	rn->drain_calls++;
100 	mtx_unlock(&rn->lock);
101 }
102 
103 static void
test_callout(void * arg)104 test_callout(void *arg)
105 {
106 	struct callout_run *rn;
107 	int cpu;
108 
109 	critical_enter();
110 	cpu = curcpu;
111 	critical_exit();
112 	rn = (struct callout_run *)arg;
113 	atomic_add_int(&rn->callout_waiting, 1);
114 	mtx_lock(&rn->lock);
115 	if (callout_pending(&rn->co_array[cpu]) ||
116 	    !callout_active(&rn->co_array[cpu])) {
117 		rn->co_return_npa++;
118 		atomic_subtract_int(&rn->callout_waiting, 1);
119 		mtx_unlock(&rn->lock);
120 		return;
121 	}
122 	callout_deactivate(&rn->co_array[cpu]);
123 	rn->co_completed++;
124 	mtx_unlock(&rn->lock);
125 	atomic_subtract_int(&rn->callout_waiting, 1);
126 }
127 
128 void
execute_the_co_test(struct callout_run * rn)129 execute_the_co_test(struct callout_run *rn)
130 {
131 	int i, ret, cpu;
132 	uint32_t tk_s, tk_e, tk_d;
133 
134 	mtx_lock(&rn->lock);
135 	rn->callout_waiting = 0;
136 	for (i = 0; i < rn->co_number_callouts; i++) {
137 		if (rn->co_test == 1) {
138 			/* start all on spread out cpu's */
139 			cpu = i % mp_ncpus;
140 			callout_reset_sbt_on(&rn->co_array[i], 3, 0, test_callout, rn,
141 			    cpu, 0);
142 		} else {
143 			/* Start all on the same CPU */
144 			callout_reset_sbt_on(&rn->co_array[i], 3, 0, test_callout, rn,
145 			    rn->index, 0);
146 		}
147 	}
148 	tk_s = ticks;
149 	while (rn->callout_waiting != rn->co_number_callouts) {
150 		cpu_spinwait();
151 		tk_e = ticks;
152 		tk_d = tk_e - tk_s;
153 		if (tk_d > 100) {
154 			break;
155 		}
156 	}
157 	/* OK everyone is waiting and we have the lock */
158 	for (i = 0; i < rn->co_number_callouts; i++) {
159 		ret = callout_async_drain(&rn->co_array[i], drainit);
160 		if (ret) {
161 			rn->cnt_one++;
162 		} else {
163 			rn->cnt_zero++;
164 		}
165 	}
166 	rn->callout_waiting -= rn->cnt_one;
167 	mtx_unlock(&rn->lock);
168 	/* Now wait until all are done */
169 	tk_s = ticks;
170 	while (rn->callout_waiting > 0) {
171 		cpu_spinwait();
172 		tk_e = ticks;
173 		tk_d = tk_e - tk_s;
174 		if (tk_d > 100) {
175 			break;
176 		}
177 	}
178 	co_saydone((void *)rn);
179 }
180 
181 
182 static void
run_callout_test(struct kern_test * test)183 run_callout_test(struct kern_test *test)
184 {
185 	struct callout_test *u;
186 	size_t sz;
187 	int i;
188 	struct callout_run *rn;
189 	int index = test->tot_threads_running;
190 
191 	u = (struct callout_test *)test->test_options;
192 	if (comaster[index] == NULL) {
193 		rn = comaster[index] = malloc(sizeof(struct callout_run), M_CALLTMP, M_WAITOK);
194 		memset(comaster[index], 0, sizeof(struct callout_run));
195 		mtx_init(&rn->lock, "callouttest", NULL, MTX_DUPOK);
196 		rn->index = index;
197 	} else {
198 		rn = comaster[index];
199 		rn->co_number_callouts = rn->co_return_npa = 0;
200 		rn->co_completed = rn->callout_waiting = 0;
201 		rn->drain_calls = rn->cnt_zero = rn->cnt_one = 0;
202 		if (rn->co_array) {
203 			free(rn->co_array, M_CALLTMP);
204 			rn->co_array = NULL;
205 		}
206 	}
207 	rn->co_number_callouts = u->number_of_callouts;
208 	rn->co_test = u->test_number;
209 	sz = sizeof(struct callout) * rn->co_number_callouts;
210 	rn->co_array = malloc(sz, M_CALLTMP, M_WAITOK);
211 	for (i = 0; i < rn->co_number_callouts; i++) {
212 		callout_init(&rn->co_array[i], CALLOUT_MPSAFE);
213 	}
214 	execute_the_co_test(rn);
215 }
216 
217 int callout_test_is_loaded = 0;
218 
219 static void
cocleanup(void)220 cocleanup(void)
221 {
222 	int i;
223 
224 	for (i = 0; i < MAXCPU; i++) {
225 		if (comaster[i]) {
226 			if (comaster[i]->co_array) {
227 				free(comaster[i]->co_array, M_CALLTMP);
228 				comaster[i]->co_array = NULL;
229 			}
230 			free(comaster[i], M_CALLTMP);
231 			comaster[i] = NULL;
232 		}
233 	}
234 }
235 
236 static int
callout_test_modevent(module_t mod,int type,void * data)237 callout_test_modevent(module_t mod, int type, void *data)
238 {
239 	int err = 0;
240 
241 	switch (type) {
242 	case MOD_LOAD:
243 		err = kern_testframework_register("callout_test",
244 		    run_callout_test);
245 		if (err) {
246 			printf("Can't load callout_test err:%d returned\n",
247 			    err);
248 		} else {
249 			memset(comaster, 0, sizeof(comaster));
250 			callout_test_is_loaded = 1;
251 		}
252 		break;
253 	case MOD_QUIESCE:
254 		err = kern_testframework_deregister("callout_test");
255 		if (err == 0) {
256 			callout_test_is_loaded = 0;
257 			cocleanup();
258 		}
259 		break;
260 	case MOD_UNLOAD:
261 		if (callout_test_is_loaded) {
262 			err = kern_testframework_deregister("callout_test");
263 			if (err == 0) {
264 				cocleanup();
265 				callout_test_is_loaded = 0;
266 			}
267 		}
268 		break;
269 	default:
270 		return (EOPNOTSUPP);
271 	}
272 	return (err);
273 }
274 
275 static moduledata_t callout_test_mod = {
276 	.name = "callout_test",
277 	.evhand = callout_test_modevent,
278 	.priv = 0
279 };
280 
281 MODULE_DEPEND(callout_test, kern_testframework, 1, 1, 1);
282 DECLARE_MODULE(callout_test, callout_test_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
283