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