xref: /netbsd-src/tests/modules/threadpool_tester/threadpool_tester.c (revision 78928445f59b392191cf4e73ea134ffaccc637da)
1*78928445Schristos /*	$NetBSD: threadpool_tester.c,v 1.1 2019/01/25 18:33:59 christos Exp $	*/
2*78928445Schristos 
3*78928445Schristos /*-
4*78928445Schristos  * Copyright (c) 2018 The NetBSD Foundation, Inc.
5*78928445Schristos  * All rights reserved.
6*78928445Schristos  *
7*78928445Schristos  * This code is derived from software contributed to The NetBSD Foundation
8*78928445Schristos  * by Jason R. Thorpe.
9*78928445Schristos  *
10*78928445Schristos  * Redistribution and use in source and binary forms, with or without
11*78928445Schristos  * modification, are permitted provided that the following conditions
12*78928445Schristos  * are met:
13*78928445Schristos  * 1. Redistributions of source code must retain the above copyright
14*78928445Schristos  *    notice, this list of conditions and the following disclaimer.
15*78928445Schristos  * 2. Redistributions in binary form must reproduce the above copyright
16*78928445Schristos  *    notice, this list of conditions and the following disclaimer in the
17*78928445Schristos  *    documentation and/or other materials provided with the distribution.
18*78928445Schristos  *
19*78928445Schristos  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20*78928445Schristos  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21*78928445Schristos  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22*78928445Schristos  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23*78928445Schristos  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24*78928445Schristos  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25*78928445Schristos  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26*78928445Schristos  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27*78928445Schristos  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28*78928445Schristos  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29*78928445Schristos  * POSSIBILITY OF SUCH DAMAGE.
30*78928445Schristos  */
31*78928445Schristos 
32*78928445Schristos #include <sys/cdefs.h>
33*78928445Schristos __KERNEL_RCSID(0, "$NetBSD: threadpool_tester.c,v 1.1 2019/01/25 18:33:59 christos Exp $");
34*78928445Schristos 
35*78928445Schristos #include <sys/param.h>
36*78928445Schristos #include <sys/kernel.h>
37*78928445Schristos #include <sys/module.h>
38*78928445Schristos #include <sys/sysctl.h>
39*78928445Schristos #include <sys/threadpool.h>
40*78928445Schristos 
41*78928445Schristos MODULE(MODULE_CLASS_MISC, threadpool_tester, NULL);
42*78928445Schristos 
43*78928445Schristos #ifdef THREADPOOL_VERBOSE
44*78928445Schristos #define	TP_LOG(x)		printf x
45*78928445Schristos #else
46*78928445Schristos #define	TP_LOG(x)		/* nothing */
47*78928445Schristos #endif /* THREADPOOL_VERBOSE */
48*78928445Schristos 
49*78928445Schristos static struct tester_context {
50*78928445Schristos 	kmutex_t ctx_mutex;
51*78928445Schristos 	struct sysctllog *ctx_sysctllog;
52*78928445Schristos 	struct threadpool *ctx_unbound[PRI_COUNT + 1];
53*78928445Schristos 	struct threadpool_percpu *ctx_percpu[PRI_COUNT + 1];
54*78928445Schristos 	unsigned int ctx_value;
55*78928445Schristos 	struct threadpool_job ctx_job;
56*78928445Schristos } tester_ctx;
57*78928445Schristos 
58*78928445Schristos #define	pri_to_idx(pri)		((pri) == PRI_NONE ? PRI_COUNT : (pri))
59*78928445Schristos 
60*78928445Schristos static bool
pri_is_valid(pri_t pri)61*78928445Schristos pri_is_valid(pri_t pri)
62*78928445Schristos {
63*78928445Schristos 	return (pri == PRI_NONE || (pri >= PRI_USER && pri < PRI_COUNT));
64*78928445Schristos }
65*78928445Schristos 
66*78928445Schristos static int
threadpool_tester_get_unbound(SYSCTLFN_ARGS)67*78928445Schristos threadpool_tester_get_unbound(SYSCTLFN_ARGS)
68*78928445Schristos {
69*78928445Schristos 	struct tester_context *ctx;
70*78928445Schristos 	struct threadpool *pool, *opool = NULL;
71*78928445Schristos 	struct sysctlnode node;
72*78928445Schristos 	int error, val;
73*78928445Schristos 
74*78928445Schristos 	node = *rnode;
75*78928445Schristos 	ctx = node.sysctl_data;
76*78928445Schristos 
77*78928445Schristos 	val = -1;
78*78928445Schristos 	node.sysctl_data = &val;
79*78928445Schristos 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
80*78928445Schristos 	if (error || newp == NULL)
81*78928445Schristos 		return error;
82*78928445Schristos 
83*78928445Schristos 	if (! pri_is_valid(val))
84*78928445Schristos 		return EINVAL;
85*78928445Schristos 
86*78928445Schristos 	error = threadpool_get(&pool, val);
87*78928445Schristos 	if (error) {
88*78928445Schristos 		TP_LOG(("%s: threadpool_get(..., %d) failed -> %d\n",
89*78928445Schristos 		    __func__, val, error));
90*78928445Schristos 		return error;
91*78928445Schristos 	}
92*78928445Schristos 
93*78928445Schristos 	mutex_enter(&ctx->ctx_mutex);
94*78928445Schristos 	if (ctx->ctx_unbound[pri_to_idx(val)] == NULL)
95*78928445Schristos 		ctx->ctx_unbound[pri_to_idx(val)] = pool;
96*78928445Schristos 	else
97*78928445Schristos 		opool = ctx->ctx_unbound[pri_to_idx(val)];
98*78928445Schristos 	mutex_exit(&ctx->ctx_mutex);
99*78928445Schristos 
100*78928445Schristos 	if (opool != NULL) {
101*78928445Schristos 		/* Should have gotten reference to existing pool. */
102*78928445Schristos 		TP_LOG(("%s: found existing unbound pool for pri %d (%s)\n",
103*78928445Schristos 		    __func__, val, opool == pool ? "match" : "NO MATCH"));
104*78928445Schristos 		KASSERT(opool == pool);
105*78928445Schristos 		threadpool_put(pool, val);
106*78928445Schristos 		error = EEXIST;
107*78928445Schristos 	} else {
108*78928445Schristos 		TP_LOG(("%s: created unbound pool for pri %d\n",
109*78928445Schristos 		    __func__, val));
110*78928445Schristos 	}
111*78928445Schristos 
112*78928445Schristos 	return error;
113*78928445Schristos }
114*78928445Schristos 
115*78928445Schristos static int
threadpool_tester_put_unbound(SYSCTLFN_ARGS)116*78928445Schristos threadpool_tester_put_unbound(SYSCTLFN_ARGS)
117*78928445Schristos {
118*78928445Schristos 	struct tester_context *ctx;
119*78928445Schristos 	struct threadpool *pool;
120*78928445Schristos 	struct sysctlnode node;
121*78928445Schristos 	int error, val;
122*78928445Schristos 
123*78928445Schristos 	node = *rnode;
124*78928445Schristos 	ctx = node.sysctl_data;
125*78928445Schristos 
126*78928445Schristos 	val = -1;
127*78928445Schristos 	node.sysctl_data = &val;
128*78928445Schristos 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
129*78928445Schristos 	if (error || newp == NULL)
130*78928445Schristos 		return error;
131*78928445Schristos 
132*78928445Schristos 	if (! pri_is_valid(val))
133*78928445Schristos 		return EINVAL;
134*78928445Schristos 
135*78928445Schristos 	mutex_enter(&ctx->ctx_mutex);
136*78928445Schristos 	/* We only ever maintain a single reference. */
137*78928445Schristos 	pool = ctx->ctx_unbound[pri_to_idx(val)];
138*78928445Schristos 	ctx->ctx_unbound[pri_to_idx(val)] = NULL;
139*78928445Schristos 	mutex_exit(&ctx->ctx_mutex);
140*78928445Schristos 
141*78928445Schristos 	if (pool == NULL) {
142*78928445Schristos 		TP_LOG(("%s: no unbound pool for pri %d\n",
143*78928445Schristos 		    __func__, val));
144*78928445Schristos 		return ENODEV;
145*78928445Schristos 	}
146*78928445Schristos 
147*78928445Schristos 	threadpool_put(pool, val);
148*78928445Schristos 	TP_LOG(("%s: released unbound pool for pri %d\n",
149*78928445Schristos 	    __func__, val));
150*78928445Schristos 
151*78928445Schristos 	return 0;
152*78928445Schristos }
153*78928445Schristos 
154*78928445Schristos static int
threadpool_tester_run_unbound(SYSCTLFN_ARGS)155*78928445Schristos threadpool_tester_run_unbound(SYSCTLFN_ARGS)
156*78928445Schristos {
157*78928445Schristos 	struct tester_context *ctx;
158*78928445Schristos 	struct threadpool *pool;
159*78928445Schristos 	struct sysctlnode node;
160*78928445Schristos 	int error, val;
161*78928445Schristos 
162*78928445Schristos 	node = *rnode;
163*78928445Schristos 	ctx = node.sysctl_data;
164*78928445Schristos 
165*78928445Schristos 	val = -1;
166*78928445Schristos 	node.sysctl_data = &val;
167*78928445Schristos 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
168*78928445Schristos 	if (error || newp == NULL)
169*78928445Schristos 		return error;
170*78928445Schristos 
171*78928445Schristos 	if (! pri_is_valid(val))
172*78928445Schristos 		return EINVAL;
173*78928445Schristos 
174*78928445Schristos 	mutex_enter(&ctx->ctx_mutex);
175*78928445Schristos 	pool = ctx->ctx_unbound[pri_to_idx(val)];
176*78928445Schristos 	if (pool == NULL) {
177*78928445Schristos 		TP_LOG(("%s: no unbound pool for pri %d\n",
178*78928445Schristos 		    __func__, val));
179*78928445Schristos 		mutex_exit(&ctx->ctx_mutex);
180*78928445Schristos 		return ENODEV;
181*78928445Schristos 	}
182*78928445Schristos 
183*78928445Schristos 	threadpool_schedule_job(pool, &ctx->ctx_job);
184*78928445Schristos 	TP_LOG(("%s: scheduled job on unbound pool for pri %d\n",
185*78928445Schristos 	    __func__, val));
186*78928445Schristos 	mutex_exit(&ctx->ctx_mutex);
187*78928445Schristos 
188*78928445Schristos 	return 0;
189*78928445Schristos }
190*78928445Schristos 
191*78928445Schristos static int
threadpool_tester_get_percpu(SYSCTLFN_ARGS)192*78928445Schristos threadpool_tester_get_percpu(SYSCTLFN_ARGS)
193*78928445Schristos {
194*78928445Schristos 	struct tester_context *ctx;
195*78928445Schristos 	struct threadpool_percpu *pcpu, *opcpu = NULL;
196*78928445Schristos 	struct sysctlnode node;
197*78928445Schristos 	int error, val;
198*78928445Schristos 
199*78928445Schristos 	node = *rnode;
200*78928445Schristos 	ctx = node.sysctl_data;
201*78928445Schristos 
202*78928445Schristos 	val = -1;
203*78928445Schristos 	node.sysctl_data = &val;
204*78928445Schristos 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
205*78928445Schristos 	if (error || newp == NULL)
206*78928445Schristos 		return error;
207*78928445Schristos 
208*78928445Schristos 	if (! pri_is_valid(val))
209*78928445Schristos 		return EINVAL;
210*78928445Schristos 
211*78928445Schristos 	error = threadpool_percpu_get(&pcpu, val);
212*78928445Schristos 	if (error) {
213*78928445Schristos 		TP_LOG(("%s: threadpool_percpu_get(..., %d) failed -> %d\n",
214*78928445Schristos 		    __func__, val, error));
215*78928445Schristos 		return error;
216*78928445Schristos 	}
217*78928445Schristos 
218*78928445Schristos 	mutex_enter(&ctx->ctx_mutex);
219*78928445Schristos 	if (ctx->ctx_percpu[pri_to_idx(val)] == NULL)
220*78928445Schristos 		ctx->ctx_percpu[pri_to_idx(val)] = pcpu;
221*78928445Schristos 	else
222*78928445Schristos 		opcpu = ctx->ctx_percpu[pri_to_idx(val)];
223*78928445Schristos 	mutex_exit(&ctx->ctx_mutex);
224*78928445Schristos 
225*78928445Schristos 	if (opcpu != NULL) {
226*78928445Schristos 		/* Should have gotten reference to existing pool. */
227*78928445Schristos 		TP_LOG(("%s: found existing unbound pool for pri %d (%s)\n",
228*78928445Schristos 		    __func__, val, opcpu == pcpu ? "match" : "NO MATCH"));
229*78928445Schristos 		KASSERT(opcpu == pcpu);
230*78928445Schristos 		threadpool_percpu_put(pcpu, val);
231*78928445Schristos 		error = EEXIST;
232*78928445Schristos 	} else {
233*78928445Schristos 		TP_LOG(("%s: created percpu pool for pri %d\n",
234*78928445Schristos 		    __func__, val));
235*78928445Schristos 	}
236*78928445Schristos 
237*78928445Schristos 	return error;
238*78928445Schristos }
239*78928445Schristos 
240*78928445Schristos static int
threadpool_tester_put_percpu(SYSCTLFN_ARGS)241*78928445Schristos threadpool_tester_put_percpu(SYSCTLFN_ARGS)
242*78928445Schristos {
243*78928445Schristos 	struct tester_context *ctx;
244*78928445Schristos 	struct threadpool_percpu *pcpu;
245*78928445Schristos 	struct sysctlnode node;
246*78928445Schristos 	int error, val;
247*78928445Schristos 
248*78928445Schristos 	node = *rnode;
249*78928445Schristos 	ctx = node.sysctl_data;
250*78928445Schristos 
251*78928445Schristos 	val = -1;
252*78928445Schristos 	node.sysctl_data = &val;
253*78928445Schristos 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
254*78928445Schristos 	if (error || newp == NULL)
255*78928445Schristos 		return error;
256*78928445Schristos 
257*78928445Schristos 	if (! pri_is_valid(val))
258*78928445Schristos 		return EINVAL;
259*78928445Schristos 
260*78928445Schristos 	mutex_enter(&ctx->ctx_mutex);
261*78928445Schristos 	/* We only ever maintain a single reference. */
262*78928445Schristos 	pcpu = ctx->ctx_percpu[pri_to_idx(val)];
263*78928445Schristos 	ctx->ctx_percpu[pri_to_idx(val)] = NULL;
264*78928445Schristos 	mutex_exit(&ctx->ctx_mutex);
265*78928445Schristos 
266*78928445Schristos 	if (pcpu == NULL) {
267*78928445Schristos 		TP_LOG(("%s: no percpu pool for pri %d\n",
268*78928445Schristos 		    __func__, val));
269*78928445Schristos 		return ENODEV;
270*78928445Schristos 	}
271*78928445Schristos 
272*78928445Schristos 	threadpool_percpu_put(pcpu, val);
273*78928445Schristos 	TP_LOG(("%s: released percpu pool for pri %d\n",
274*78928445Schristos 	    __func__, val));
275*78928445Schristos 
276*78928445Schristos 	return 0;
277*78928445Schristos }
278*78928445Schristos 
279*78928445Schristos static int
threadpool_tester_run_percpu(SYSCTLFN_ARGS)280*78928445Schristos threadpool_tester_run_percpu(SYSCTLFN_ARGS)
281*78928445Schristos {
282*78928445Schristos 	struct tester_context *ctx;
283*78928445Schristos 	struct threadpool_percpu *pcpu;
284*78928445Schristos 	struct threadpool *pool;
285*78928445Schristos 	struct sysctlnode node;
286*78928445Schristos 	int error, val;
287*78928445Schristos 
288*78928445Schristos 	node = *rnode;
289*78928445Schristos 	ctx = node.sysctl_data;
290*78928445Schristos 
291*78928445Schristos 	val = -1;
292*78928445Schristos 	node.sysctl_data = &val;
293*78928445Schristos 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
294*78928445Schristos 	if (error || newp == NULL)
295*78928445Schristos 		return error;
296*78928445Schristos 
297*78928445Schristos 	if (! pri_is_valid(val))
298*78928445Schristos 		return EINVAL;
299*78928445Schristos 
300*78928445Schristos 	mutex_enter(&ctx->ctx_mutex);
301*78928445Schristos 	pcpu = ctx->ctx_percpu[pri_to_idx(val)];
302*78928445Schristos 	if (pcpu == NULL) {
303*78928445Schristos 		TP_LOG(("%s: no percpu pool for pri %d\n",
304*78928445Schristos 		    __func__, val));
305*78928445Schristos 		mutex_exit(&ctx->ctx_mutex);
306*78928445Schristos 		return ENODEV;
307*78928445Schristos 	}
308*78928445Schristos 
309*78928445Schristos 	pool = threadpool_percpu_ref(pcpu);
310*78928445Schristos 	KASSERT(pool != NULL);
311*78928445Schristos 
312*78928445Schristos 	threadpool_schedule_job(pool, &ctx->ctx_job);
313*78928445Schristos 	TP_LOG(("%s: scheduled job on percpu pool for pri %d\n",
314*78928445Schristos 	    __func__, val));
315*78928445Schristos 	mutex_exit(&ctx->ctx_mutex);
316*78928445Schristos 
317*78928445Schristos 	return 0;
318*78928445Schristos }
319*78928445Schristos 
320*78928445Schristos static int
threadpool_tester_test_value(SYSCTLFN_ARGS)321*78928445Schristos threadpool_tester_test_value(SYSCTLFN_ARGS)
322*78928445Schristos {
323*78928445Schristos 	struct tester_context *ctx;
324*78928445Schristos 	struct sysctlnode node;
325*78928445Schristos 	unsigned int val;
326*78928445Schristos 	int error;
327*78928445Schristos 
328*78928445Schristos 	node = *rnode;
329*78928445Schristos 	ctx = node.sysctl_data;
330*78928445Schristos 
331*78928445Schristos 	mutex_enter(&ctx->ctx_mutex);
332*78928445Schristos 	val = ctx->ctx_value;
333*78928445Schristos 	node.sysctl_data = &val;
334*78928445Schristos 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
335*78928445Schristos 	if (error || newp == NULL) {
336*78928445Schristos 		mutex_exit(&ctx->ctx_mutex);
337*78928445Schristos 		return error;
338*78928445Schristos 	}
339*78928445Schristos 	ctx->ctx_value = val;
340*78928445Schristos 	mutex_exit(&ctx->ctx_mutex);
341*78928445Schristos 
342*78928445Schristos 	return 0;
343*78928445Schristos }
344*78928445Schristos 
345*78928445Schristos static void
threadpool_tester_job(struct threadpool_job * job)346*78928445Schristos threadpool_tester_job(struct threadpool_job *job)
347*78928445Schristos {
348*78928445Schristos 	struct tester_context *ctx =
349*78928445Schristos 	    container_of(job, struct tester_context, ctx_job);
350*78928445Schristos 	unsigned int oval, nval;
351*78928445Schristos 
352*78928445Schristos 	TP_LOG(("%s: job = %p, ctx = %p\n", __func__, job, ctx));
353*78928445Schristos 
354*78928445Schristos 	mutex_enter(&ctx->ctx_mutex);
355*78928445Schristos 	oval = ctx->ctx_value;
356*78928445Schristos 	nval = oval + 1;	/* always reference oval and nval */
357*78928445Schristos 	ctx->ctx_value = nval;
358*78928445Schristos 	mutex_exit(&ctx->ctx_mutex);
359*78928445Schristos 
360*78928445Schristos 	TP_LOG(("%s: %u -> %u\n", __func__, oval, nval));
361*78928445Schristos 	(void) kpause("tptestjob", false, hz, NULL);
362*78928445Schristos 
363*78928445Schristos 	mutex_enter(&ctx->ctx_mutex);
364*78928445Schristos 	threadpool_job_done(job);
365*78928445Schristos 	mutex_exit(&ctx->ctx_mutex);
366*78928445Schristos }
367*78928445Schristos 
368*78928445Schristos #define	RETURN_ERROR	if (error) goto return_error
369*78928445Schristos 
370*78928445Schristos static int
threadpool_tester_init(void)371*78928445Schristos threadpool_tester_init(void)
372*78928445Schristos {
373*78928445Schristos 	struct sysctllog **log = &tester_ctx.ctx_sysctllog;
374*78928445Schristos 	const struct sysctlnode *rnode, *cnode;
375*78928445Schristos 	int error;
376*78928445Schristos 
377*78928445Schristos 	mutex_init(&tester_ctx.ctx_mutex, MUTEX_DEFAULT, IPL_NONE);
378*78928445Schristos 	threadpool_job_init(&tester_ctx.ctx_job, threadpool_tester_job,
379*78928445Schristos 	    &tester_ctx.ctx_mutex, "tptest");
380*78928445Schristos 
381*78928445Schristos 	error = sysctl_createv(log, 0, NULL, &rnode, CTLFLAG_PERMANENT,
382*78928445Schristos 	    CTLTYPE_NODE, "threadpool_tester",
383*78928445Schristos 	    SYSCTL_DESCR("threadpool testing interface"),
384*78928445Schristos 	    NULL, 0, NULL, 0, CTL_KERN, CTL_CREATE, CTL_EOL);
385*78928445Schristos 	RETURN_ERROR;
386*78928445Schristos 
387*78928445Schristos 	error = sysctl_createv(log, 0, &rnode, &cnode,
388*78928445Schristos 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "get_unbound",
389*78928445Schristos 	    SYSCTL_DESCR("get unbound pool of specified priority"),
390*78928445Schristos 	    threadpool_tester_get_unbound, 0,
391*78928445Schristos 	    (void *)&tester_ctx, 0, CTL_CREATE, CTL_EOL);
392*78928445Schristos 	RETURN_ERROR;
393*78928445Schristos 
394*78928445Schristos 	error = sysctl_createv(log, 0, &rnode, &cnode,
395*78928445Schristos 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "put_unbound",
396*78928445Schristos 	    SYSCTL_DESCR("put unbound pool of specified priority"),
397*78928445Schristos 	    threadpool_tester_put_unbound, 0,
398*78928445Schristos 	    (void *)&tester_ctx, 0, CTL_CREATE, CTL_EOL);
399*78928445Schristos 	RETURN_ERROR;
400*78928445Schristos 
401*78928445Schristos 	error = sysctl_createv(log, 0, &rnode, &cnode,
402*78928445Schristos 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "run_unbound",
403*78928445Schristos 	    SYSCTL_DESCR("run on unbound pool of specified priority"),
404*78928445Schristos 	    threadpool_tester_run_unbound, 0,
405*78928445Schristos 	    (void *)&tester_ctx, 0, CTL_CREATE, CTL_EOL);
406*78928445Schristos 	RETURN_ERROR;
407*78928445Schristos 
408*78928445Schristos 	error = sysctl_createv(log, 0, &rnode, &cnode,
409*78928445Schristos 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "get_percpu",
410*78928445Schristos 	    SYSCTL_DESCR("get percpu pool of specified priority"),
411*78928445Schristos 	    threadpool_tester_get_percpu, 0,
412*78928445Schristos 	    (void *)&tester_ctx, 0, CTL_CREATE, CTL_EOL);
413*78928445Schristos 	RETURN_ERROR;
414*78928445Schristos 
415*78928445Schristos 	error = sysctl_createv(log, 0, &rnode, &cnode,
416*78928445Schristos 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "put_percpu",
417*78928445Schristos 	    SYSCTL_DESCR("put percpu pool of specified priority"),
418*78928445Schristos 	    threadpool_tester_put_percpu, 0,
419*78928445Schristos 	    (void *)&tester_ctx, 0, CTL_CREATE, CTL_EOL);
420*78928445Schristos 	RETURN_ERROR;
421*78928445Schristos 
422*78928445Schristos 	error = sysctl_createv(log, 0, &rnode, &cnode,
423*78928445Schristos 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "run_percpu",
424*78928445Schristos 	    SYSCTL_DESCR("run on percpu pool of specified priority"),
425*78928445Schristos 	    threadpool_tester_run_percpu, 0,
426*78928445Schristos 	    (void *)&tester_ctx, 0, CTL_CREATE, CTL_EOL);
427*78928445Schristos 	RETURN_ERROR;
428*78928445Schristos 
429*78928445Schristos 	error = sysctl_createv(log, 0, &rnode, &cnode,
430*78928445Schristos 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "test_value",
431*78928445Schristos 	    SYSCTL_DESCR("test value that jobs increment"),
432*78928445Schristos 	    threadpool_tester_test_value, 0,
433*78928445Schristos 	    (void *)&tester_ctx, 0, CTL_CREATE, CTL_EOL);
434*78928445Schristos 	RETURN_ERROR;
435*78928445Schristos 
436*78928445Schristos 	return 0;
437*78928445Schristos 
438*78928445Schristos  return_error:
439*78928445Schristos  	sysctl_teardown(log);
440*78928445Schristos 	return error;
441*78928445Schristos }
442*78928445Schristos 
443*78928445Schristos static int
threadpool_tester_fini(void)444*78928445Schristos threadpool_tester_fini(void)
445*78928445Schristos {
446*78928445Schristos 	pri_t pri;
447*78928445Schristos 
448*78928445Schristos 	mutex_enter(&tester_ctx.ctx_mutex);
449*78928445Schristos 	for (pri = PRI_NONE/*-1*/; pri < PRI_COUNT; pri++) {
450*78928445Schristos 		struct threadpool *pool =
451*78928445Schristos 		    tester_ctx.ctx_unbound[pri_to_idx(pri)];
452*78928445Schristos 		struct threadpool_percpu *pcpu =
453*78928445Schristos 		    tester_ctx.ctx_percpu[pri_to_idx(pri)];
454*78928445Schristos 
455*78928445Schristos 		/*
456*78928445Schristos 		 * threadpool_cancel_job() may be called on a pool
457*78928445Schristos 		 * other than what the job is scheduled on. This is
458*78928445Schristos 		 * safe; see comment in threadpool_cancel_job_async().
459*78928445Schristos 		 */
460*78928445Schristos 
461*78928445Schristos 		if (pool != NULL) {
462*78928445Schristos 			threadpool_cancel_job(pool, &tester_ctx.ctx_job);
463*78928445Schristos 			threadpool_put(pool, pri);
464*78928445Schristos 			tester_ctx.ctx_unbound[pri_to_idx(pri)] = NULL;
465*78928445Schristos 		}
466*78928445Schristos 		if (pcpu != NULL) {
467*78928445Schristos 			pool = threadpool_percpu_ref(pcpu);
468*78928445Schristos 			threadpool_cancel_job(pool, &tester_ctx.ctx_job);
469*78928445Schristos 			threadpool_percpu_put(pcpu, pri);
470*78928445Schristos 			tester_ctx.ctx_percpu[pri_to_idx(pri)] = NULL;
471*78928445Schristos 		}
472*78928445Schristos 	}
473*78928445Schristos 	mutex_exit(&tester_ctx.ctx_mutex);
474*78928445Schristos 	threadpool_job_destroy(&tester_ctx.ctx_job);
475*78928445Schristos 	mutex_destroy(&tester_ctx.ctx_mutex);
476*78928445Schristos 
477*78928445Schristos 	sysctl_teardown(&tester_ctx.ctx_sysctllog);
478*78928445Schristos 
479*78928445Schristos 	return 0;
480*78928445Schristos }
481*78928445Schristos 
482*78928445Schristos static int
threadpool_tester_modcmd(modcmd_t cmd,void * arg __unused)483*78928445Schristos threadpool_tester_modcmd(modcmd_t cmd, void *arg __unused)
484*78928445Schristos {
485*78928445Schristos 	int error;
486*78928445Schristos 
487*78928445Schristos 	switch (cmd) {
488*78928445Schristos 	case MODULE_CMD_INIT:
489*78928445Schristos 		error = threadpool_tester_init();
490*78928445Schristos 		break;
491*78928445Schristos 
492*78928445Schristos 	case MODULE_CMD_FINI:
493*78928445Schristos 		error = threadpool_tester_fini();
494*78928445Schristos 		break;
495*78928445Schristos 
496*78928445Schristos 	case MODULE_CMD_STAT:
497*78928445Schristos 	default:
498*78928445Schristos 		error = ENOTTY;
499*78928445Schristos 	}
500*78928445Schristos 
501*78928445Schristos 	return error;
502*78928445Schristos }
503