1/*-
2 * Copyright 2007 John Birrell <jb@FreeBSD.org>
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 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 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 * $FreeBSD$
26 *
27 */
28
29#include <sys/cdefs.h>
30#include <sys/systm.h>
31#include <sys/kernel.h>
32#include <sys/conf.h>
33#include <sys/kthread.h>
34#include <sys/module.h>
35#include <sys/sysctl.h>
36#include <sys/cyclic.h>
37#include <sys/time.h>
38
39static struct timespec test_001_start;
40
41static void
42cyclic_test_001_func(void *arg)
43{
44	struct timespec ts;
45
46	nanotime(&ts);
47	timespecsub(&ts,&test_001_start);
48	printf("%s: called after %lu.%09lu on curcpu %d\n",__func__,(u_long) ts.tv_sec,(u_long) ts.tv_nsec, curcpu);
49}
50
51static void
52cyclic_test_001(void)
53{
54	int error = 0;
55	cyc_handler_t hdlr;
56	cyc_time_t when;
57	cyclic_id_t id;
58
59	printf("%s: starting\n",__func__);
60
61	hdlr.cyh_func = (cyc_func_t) cyclic_test_001_func;
62        hdlr.cyh_arg = 0;
63
64        when.cyt_when = 0;
65        when.cyt_interval = 1000000000;
66
67	nanotime(&test_001_start);
68
69	mutex_enter(&cpu_lock);
70
71        id = cyclic_add(&hdlr, &when);
72
73	mutex_exit(&cpu_lock);
74
75	DELAY(1200000);
76
77	mutex_enter(&cpu_lock);
78
79	cyclic_remove(id);
80
81	mutex_exit(&cpu_lock);
82
83	printf("%s: %s\n",__func__, error == 0 ? "passed":"failed");
84}
85
86static struct timespec test_002_start;
87
88static void
89cyclic_test_002_func(void *arg)
90{
91	struct timespec ts;
92
93	nanotime(&ts);
94	timespecsub(&ts,&test_002_start);
95	printf("%s: called after %lu.%09lu on curcpu %d\n",__func__,(u_long) ts.tv_sec,(u_long) ts.tv_nsec, curcpu);
96}
97
98static void
99cyclic_test_002_online(void *arg, cpu_t *c, cyc_handler_t *hdlr, cyc_time_t *t)
100{
101	printf("%s: online on curcpu %d\n",__func__, curcpu);
102	hdlr->cyh_func = cyclic_test_002_func;
103	hdlr->cyh_arg = NULL;
104	t->cyt_when = 0;
105	t->cyt_interval = 1000000000;
106}
107
108static void
109cyclic_test_002_offline(void *arg, cpu_t *c, void *arg1)
110{
111	printf("%s: offline on curcpu %d\n",__func__, curcpu);
112}
113
114static void
115cyclic_test_002(void)
116{
117	int error = 0;
118	cyc_omni_handler_t hdlr;
119	cyclic_id_t id;
120
121	printf("%s: starting\n",__func__);
122
123	hdlr.cyo_online = cyclic_test_002_online;
124	hdlr.cyo_offline = cyclic_test_002_offline;
125	hdlr.cyo_arg = NULL;
126
127	nanotime(&test_002_start);
128
129	mutex_enter(&cpu_lock);
130
131        id = cyclic_add_omni(&hdlr);
132
133	mutex_exit(&cpu_lock);
134
135	DELAY(1200000);
136
137	mutex_enter(&cpu_lock);
138
139	cyclic_remove(id);
140
141	mutex_exit(&cpu_lock);
142
143	printf("%s: %s\n",__func__, error == 0 ? "passed":"failed");
144}
145
146static struct timespec test_003_start;
147
148static void
149cyclic_test_003_func(void *arg)
150{
151	struct timespec ts;
152
153	nanotime(&ts);
154	timespecsub(&ts,&test_003_start);
155	printf("%s: called after %lu.%09lu on curcpu %d id %ju\n",__func__,(u_long) ts.tv_sec,(u_long) ts.tv_nsec, curcpu, (uintmax_t)(uintptr_t) arg);
156}
157
158static void
159cyclic_test_003(void)
160{
161	int error = 0;
162	cyc_handler_t hdlr;
163	cyc_time_t when;
164	cyclic_id_t id;
165	cyclic_id_t id1;
166	cyclic_id_t id2;
167	cyclic_id_t id3;
168
169	printf("%s: starting\n",__func__);
170
171	hdlr.cyh_func = (cyc_func_t) cyclic_test_003_func;
172
173        when.cyt_when = 0;
174
175	nanotime(&test_003_start);
176
177	mutex_enter(&cpu_lock);
178
179        when.cyt_interval = 200000000;
180        hdlr.cyh_arg = (void *) 0UL;
181        id = cyclic_add(&hdlr, &when);
182
183        when.cyt_interval = 400000000;
184        hdlr.cyh_arg = (void *) 1UL;
185        id1 = cyclic_add(&hdlr, &when);
186
187        hdlr.cyh_arg = (void *) 2UL;
188        when.cyt_interval = 1000000000;
189        id2 = cyclic_add(&hdlr, &when);
190
191        hdlr.cyh_arg = (void *) 3UL;
192        when.cyt_interval = 1300000000;
193        id3 = cyclic_add(&hdlr, &when);
194
195	mutex_exit(&cpu_lock);
196
197	DELAY(1200000);
198
199	mutex_enter(&cpu_lock);
200
201	cyclic_remove(id);
202	cyclic_remove(id1);
203	cyclic_remove(id2);
204	cyclic_remove(id3);
205
206	mutex_exit(&cpu_lock);
207
208	printf("%s: %s\n",__func__, error == 0 ? "passed":"failed");
209}
210
211/* Kernel thread command routine. */
212static void
213cyclic_run_tests(void *arg)
214{
215	intptr_t cmd = (intptr_t) arg;
216
217	switch (cmd) {
218	case 1:
219		cyclic_test_001();
220		break;
221	case 2:
222		cyclic_test_002();
223		break;
224	case 3:
225		cyclic_test_003();
226		break;
227	default:
228		cyclic_test_001();
229		cyclic_test_002();
230		cyclic_test_003();
231		break;
232	}
233
234	printf("%s: finished\n",__func__);
235
236	kthread_exit();
237}
238
239static int
240cyclic_test(SYSCTL_HANDLER_ARGS)
241{
242	int error, cmd = 0;
243
244	error = sysctl_wire_old_buffer(req, sizeof(int));
245	if (error == 0)
246		error = sysctl_handle_int(oidp, &cmd, 0, req);
247	if (error != 0 || req->newptr == NULL)
248		return (error);
249
250	/* Check for command validity. */
251	switch (cmd) {
252	case 1:
253	case 2:
254	case -1:
255		/*
256		 * Execute the tests in a kernel thread to avoid blocking
257		 * the sysctl. Look for the results in the syslog.
258		 */
259		error = kthread_add(cyclic_run_tests, (void *)(uintptr_t) cmd,
260		    NULL, NULL, 0, 0, "cyctest%d", cmd);
261		break;
262	default:
263		printf("Usage: debug.cyclic.test=(1..9) or -1 for all tests\n");
264		error = EINVAL;
265		break;
266	}
267
268	return (error);
269}
270
271SYSCTL_NODE(_debug, OID_AUTO, cyclic, CTLFLAG_RW, NULL, "Cyclic nodes");
272SYSCTL_PROC(_debug_cyclic, OID_AUTO, test, CTLTYPE_INT | CTLFLAG_RW, 0, 0,
273    cyclic_test, "I", "Enables a cyclic test. Use -1 for all tests.");
274
275static int
276cyclic_test_modevent(module_t mod, int type, void *data)
277{
278	int error = 0;
279
280	switch (type) {
281	case MOD_LOAD:
282		break;
283
284	case MOD_UNLOAD:
285		break;
286
287	case MOD_SHUTDOWN:
288		break;
289
290	default:
291		error = EOPNOTSUPP;
292		break;
293
294	}
295	return (error);
296}
297
298DEV_MODULE(cyclic_test, cyclic_test_modevent, NULL);
299MODULE_VERSION(cyclic_test, 1);
300MODULE_DEPEND(cyclic_test, cyclic, 1, 1, 1);
301MODULE_DEPEND(cyclic_test, opensolaris, 1, 1, 1);
302