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