1 /* $NetBSD: workqueue.c,v 1.10 2023/08/10 22:20:20 riastradh Exp $ */
2
3 /*-
4 * Copyright (c) 2017 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <sys/cdefs.h>
31 #if !defined(lint)
32 __RCSID("$NetBSD: workqueue.c,v 1.10 2023/08/10 22:20:20 riastradh Exp $");
33 #endif /* !lint */
34
35 #include <sys/param.h>
36 #include <sys/condvar.h>
37 #include <sys/kernel.h>
38 #include <sys/kmem.h>
39 #include <sys/kthread.h>
40 #include <sys/mutex.h>
41 #include <sys/workqueue.h>
42
43 #include "kernspace.h"
44
45 struct test_softc {
46 kmutex_t mtx;
47 kcondvar_t cv;
48 struct workqueue *wq;
49 struct work wk;
50 int counter;
51 bool pause;
52 };
53
54 static void
rump_work1(struct work * wk,void * arg)55 rump_work1(struct work *wk, void *arg)
56 {
57 struct test_softc *sc = arg;
58
59 memset(wk, 0x5a, sizeof(*wk));
60
61 if (sc->pause)
62 kpause("tstwk1", /*intr*/false, /*timo*/2, /*lock*/NULL);
63
64 mutex_enter(&sc->mtx);
65 ++sc->counter;
66 cv_broadcast(&sc->cv);
67 mutex_exit(&sc->mtx);
68 }
69
70 static struct test_softc *
create_sc(void)71 create_sc(void)
72 {
73 int rv;
74 struct test_softc *sc;
75
76 sc = kmem_zalloc(sizeof(*sc), KM_SLEEP);
77 mutex_init(&sc->mtx, MUTEX_DEFAULT, IPL_NONE);
78 cv_init(&sc->cv, "rumpwqcv");
79 rv = workqueue_create(&sc->wq, "rumpwq",
80 rump_work1, sc, PRI_SOFTNET, IPL_SOFTNET, 0);
81 if (rv)
82 panic("workqueue creation failed: %d", rv);
83
84 sc->counter = 0;
85
86 return sc;
87 }
88
89 static void
destroy_sc(struct test_softc * sc)90 destroy_sc(struct test_softc *sc)
91 {
92
93 cv_destroy(&sc->cv);
94 mutex_destroy(&sc->mtx);
95 workqueue_destroy(sc->wq);
96 }
97
98 void
rumptest_workqueue1()99 rumptest_workqueue1()
100 {
101 struct test_softc *sc;
102
103 sc = create_sc();
104
105 #define ITERATIONS 12435
106 for (int i = 0; i < ITERATIONS; ++i) {
107 int e;
108 mutex_enter(&sc->mtx);
109 workqueue_enqueue(sc->wq, &sc->wk, NULL);
110 e = cv_timedwait(&sc->cv, &sc->mtx, hz * 2);
111 if (e != 0)
112 panic("cv_timedwait timed out (i=%d)", i);
113 mutex_exit(&sc->mtx);
114 }
115
116 KASSERT(sc->counter == ITERATIONS);
117
118 destroy_sc(sc);
119 #undef ITERATIONS
120 }
121
122 void
rumptest_workqueue_wait(void)123 rumptest_workqueue_wait(void)
124 {
125 struct test_softc *sc;
126 struct work dummy;
127
128 sc = create_sc();
129
130 #define ITERATIONS 12435
131 for (size_t i = 0; i < ITERATIONS; ++i) {
132 KASSERT(sc->counter == i);
133 workqueue_enqueue(sc->wq, &sc->wk, NULL);
134 workqueue_wait(sc->wq, &sc->wk);
135 KASSERT(sc->counter == (i + 1));
136 }
137
138 KASSERT(sc->counter == ITERATIONS);
139
140 /* Wait for a work that is not enqueued. Just return immediately. */
141 workqueue_wait(sc->wq, &dummy);
142
143 destroy_sc(sc);
144 #undef ITERATIONS
145 }
146
147 void
rumptest_workqueue_wait_pause(void)148 rumptest_workqueue_wait_pause(void)
149 {
150 struct test_softc *sc;
151 struct work dummy;
152
153 sc = create_sc();
154 sc->pause = true;
155
156 #define ITERATIONS 1
157 for (size_t i = 0; i < ITERATIONS; ++i) {
158 struct work wk;
159
160 KASSERT(sc->counter == i);
161 workqueue_enqueue(sc->wq, &wk, NULL);
162 workqueue_enqueue(sc->wq, &sc->wk, NULL);
163 kpause("tstwk2", /*intr*/false, /*timo*/1, /*lock*/NULL);
164 workqueue_wait(sc->wq, &sc->wk);
165 workqueue_wait(sc->wq, &wk);
166 KASSERT(sc->counter == (i + 2));
167 }
168
169 KASSERT(sc->counter == 2*ITERATIONS);
170
171 /* Wait for a work that is not enqueued. Just return immediately. */
172 workqueue_wait(sc->wq, &dummy);
173
174 destroy_sc(sc);
175 #undef ITERATIONS
176 }
177