npf_worker.c revision 1.10 1 1.1 rmind /*-
2 1.8 rmind * Copyright (c) 2010-2020 The NetBSD Foundation, Inc.
3 1.1 rmind * All rights reserved.
4 1.1 rmind *
5 1.1 rmind * This material is based upon work partially supported by The
6 1.1 rmind * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
7 1.1 rmind *
8 1.1 rmind * Redistribution and use in source and binary forms, with or without
9 1.1 rmind * modification, are permitted provided that the following conditions
10 1.1 rmind * are met:
11 1.1 rmind * 1. Redistributions of source code must retain the above copyright
12 1.1 rmind * notice, this list of conditions and the following disclaimer.
13 1.1 rmind * 2. Redistributions in binary form must reproduce the above copyright
14 1.1 rmind * notice, this list of conditions and the following disclaimer in the
15 1.1 rmind * documentation and/or other materials provided with the distribution.
16 1.1 rmind *
17 1.1 rmind * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18 1.1 rmind * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 1.1 rmind * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 1.1 rmind * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21 1.1 rmind * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 1.1 rmind * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 1.1 rmind * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 1.1 rmind * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 1.1 rmind * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 1.1 rmind * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 1.1 rmind * POSSIBILITY OF SUCH DAMAGE.
28 1.1 rmind */
29 1.1 rmind
30 1.2 christos #ifdef _KERNEL
31 1.1 rmind #include <sys/cdefs.h>
32 1.10 riastrad __KERNEL_RCSID(0, "$NetBSD: npf_worker.c,v 1.10 2020/08/27 18:49:36 riastradh Exp $");
33 1.1 rmind
34 1.1 rmind #include <sys/param.h>
35 1.1 rmind #include <sys/types.h>
36 1.1 rmind
37 1.1 rmind #include <sys/mutex.h>
38 1.2 christos #include <sys/kmem.h>
39 1.1 rmind #include <sys/kernel.h>
40 1.1 rmind #include <sys/kthread.h>
41 1.2 christos #include <sys/cprng.h>
42 1.2 christos #endif
43 1.1 rmind
44 1.1 rmind #include "npf_impl.h"
45 1.1 rmind
46 1.2 christos typedef struct npf_worker {
47 1.8 rmind kmutex_t lock;
48 1.8 rmind kcondvar_t cv;
49 1.8 rmind kcondvar_t exit_cv;
50 1.8 rmind bool exit;
51 1.8 rmind LIST_HEAD(, npf) instances;
52 1.8 rmind unsigned worker_count;
53 1.8 rmind lwp_t * worker[];
54 1.8 rmind } npf_workerinfo_t;
55 1.1 rmind
56 1.8 rmind #define NPF_GC_MINWAIT (10) // 10 ms
57 1.8 rmind #define NPF_GC_MAXWAIT (10 * 1000) // 10 sec
58 1.1 rmind
59 1.8 rmind /*
60 1.8 rmind * Flags for the npf_t::worker_flags field.
61 1.8 rmind */
62 1.8 rmind #define WFLAG_ACTIVE 0x01 // instance enqueued for workers
63 1.8 rmind #define WFLAG_INITED 0x02 // worker setup the instance
64 1.8 rmind #define WFLAG_REMOVE 0x04 // remove the instance
65 1.1 rmind
66 1.8 rmind static void npf_worker(void *) __dead;
67 1.8 rmind static npf_workerinfo_t * worker_info __read_mostly;
68 1.1 rmind
69 1.1 rmind int
70 1.2 christos npf_worker_sysinit(unsigned nworkers)
71 1.1 rmind {
72 1.8 rmind const size_t len = offsetof(npf_workerinfo_t, worker[nworkers]);
73 1.8 rmind npf_workerinfo_t *winfo;
74 1.8 rmind
75 1.8 rmind KASSERT(worker_info == NULL);
76 1.8 rmind
77 1.8 rmind if (!nworkers) {
78 1.8 rmind return 0;
79 1.6 rmind }
80 1.8 rmind
81 1.8 rmind winfo = kmem_zalloc(len, KM_SLEEP);
82 1.8 rmind winfo->worker_count = nworkers;
83 1.8 rmind mutex_init(&winfo->lock, MUTEX_DEFAULT, IPL_SOFTNET);
84 1.9 rmind cv_init(&winfo->exit_cv, "npfgcx");
85 1.9 rmind cv_init(&winfo->cv, "npfgcw");
86 1.8 rmind LIST_INIT(&winfo->instances);
87 1.8 rmind worker_info = winfo;
88 1.2 christos
89 1.2 christos for (unsigned i = 0; i < nworkers; i++) {
90 1.3 rmind if (kthread_create(PRI_NONE, KTHREAD_MPSAFE | KTHREAD_MUSTJOIN,
91 1.8 rmind NULL, npf_worker, winfo, &winfo->worker[i], "npfgc%u", i)) {
92 1.2 christos npf_worker_sysfini();
93 1.2 christos return ENOMEM;
94 1.2 christos }
95 1.1 rmind }
96 1.1 rmind return 0;
97 1.1 rmind }
98 1.1 rmind
99 1.1 rmind void
100 1.1 rmind npf_worker_sysfini(void)
101 1.1 rmind {
102 1.8 rmind npf_workerinfo_t *winfo = worker_info;
103 1.8 rmind unsigned nworkers;
104 1.8 rmind
105 1.8 rmind if (!winfo) {
106 1.8 rmind return;
107 1.8 rmind }
108 1.8 rmind
109 1.8 rmind /* Notify the workers to exit. */
110 1.8 rmind mutex_enter(&winfo->lock);
111 1.8 rmind winfo->exit = true;
112 1.8 rmind cv_broadcast(&winfo->cv);
113 1.8 rmind mutex_exit(&winfo->lock);
114 1.8 rmind
115 1.8 rmind /* Wait for them to finish and then destroy. */
116 1.8 rmind nworkers = winfo->worker_count;
117 1.8 rmind for (unsigned i = 0; i < nworkers; i++) {
118 1.8 rmind lwp_t *worker;
119 1.8 rmind
120 1.8 rmind if ((worker = winfo->worker[i]) != NULL) {
121 1.8 rmind kthread_join(worker);
122 1.8 rmind }
123 1.8 rmind }
124 1.8 rmind cv_destroy(&winfo->cv);
125 1.9 rmind cv_destroy(&winfo->exit_cv);
126 1.8 rmind mutex_destroy(&winfo->lock);
127 1.8 rmind kmem_free(winfo, offsetof(npf_workerinfo_t, worker[nworkers]));
128 1.8 rmind worker_info = NULL;
129 1.8 rmind }
130 1.8 rmind
131 1.8 rmind int
132 1.8 rmind npf_worker_addfunc(npf_t *npf, npf_workfunc_t work)
133 1.8 rmind {
134 1.8 rmind KASSERTMSG(npf->worker_flags == 0,
135 1.8 rmind "the task must be added before the npf_worker_enlist() call");
136 1.2 christos
137 1.8 rmind for (unsigned i = 0; i < NPF_MAX_WORKS; i++) {
138 1.8 rmind if (npf->worker_funcs[i] == NULL) {
139 1.8 rmind npf->worker_funcs[i] = work;
140 1.8 rmind return 0;
141 1.8 rmind }
142 1.6 rmind }
143 1.8 rmind return -1;
144 1.1 rmind }
145 1.1 rmind
146 1.1 rmind void
147 1.2 christos npf_worker_signal(npf_t *npf)
148 1.1 rmind {
149 1.8 rmind npf_workerinfo_t *winfo = worker_info;
150 1.2 christos
151 1.8 rmind if ((npf->worker_flags & WFLAG_ACTIVE) == 0) {
152 1.8 rmind return;
153 1.8 rmind }
154 1.8 rmind KASSERT(winfo != NULL);
155 1.8 rmind
156 1.8 rmind mutex_enter(&winfo->lock);
157 1.8 rmind cv_signal(&winfo->cv);
158 1.8 rmind mutex_exit(&winfo->lock);
159 1.1 rmind }
160 1.1 rmind
161 1.8 rmind /*
162 1.8 rmind * npf_worker_enlist: add the NPF instance for worker(s) to process.
163 1.8 rmind */
164 1.8 rmind void
165 1.8 rmind npf_worker_enlist(npf_t *npf)
166 1.1 rmind {
167 1.8 rmind npf_workerinfo_t *winfo = worker_info;
168 1.8 rmind
169 1.8 rmind KASSERT(npf->worker_flags == 0);
170 1.8 rmind if (!winfo) {
171 1.8 rmind return;
172 1.1 rmind }
173 1.8 rmind
174 1.8 rmind mutex_enter(&winfo->lock);
175 1.8 rmind LIST_INSERT_HEAD(&winfo->instances, npf, worker_entry);
176 1.8 rmind npf->worker_flags |= WFLAG_ACTIVE;
177 1.8 rmind mutex_exit(&winfo->lock);
178 1.1 rmind }
179 1.1 rmind
180 1.8 rmind /*
181 1.8 rmind * npf_worker_discharge: remove the NPF instance the list for workers.
182 1.8 rmind *
183 1.8 rmind * => May block waiting for a worker to finish processing the instance.
184 1.8 rmind */
185 1.1 rmind void
186 1.8 rmind npf_worker_discharge(npf_t *npf)
187 1.1 rmind {
188 1.8 rmind npf_workerinfo_t *winfo = worker_info;
189 1.6 rmind
190 1.8 rmind if ((npf->worker_flags & WFLAG_ACTIVE) == 0) {
191 1.6 rmind return;
192 1.6 rmind }
193 1.8 rmind KASSERT(winfo != NULL);
194 1.2 christos
195 1.8 rmind /*
196 1.8 rmind * Notify the worker(s) that we are removing this instance.
197 1.8 rmind */
198 1.8 rmind mutex_enter(&winfo->lock);
199 1.8 rmind KASSERT(npf->worker_flags & WFLAG_ACTIVE);
200 1.8 rmind npf->worker_flags |= WFLAG_REMOVE;
201 1.8 rmind cv_broadcast(&winfo->cv);
202 1.8 rmind
203 1.8 rmind /* Wait for a worker to process this request. */
204 1.8 rmind while (npf->worker_flags & WFLAG_ACTIVE) {
205 1.8 rmind cv_wait(&winfo->exit_cv, &winfo->lock);
206 1.8 rmind }
207 1.8 rmind mutex_exit(&winfo->lock);
208 1.8 rmind KASSERT(npf->worker_flags == 0);
209 1.8 rmind }
210 1.2 christos
211 1.8 rmind static void
212 1.8 rmind remove_npf_instance(npf_workerinfo_t *winfo, npf_t *npf)
213 1.8 rmind {
214 1.8 rmind KASSERT(mutex_owned(&winfo->lock));
215 1.8 rmind KASSERT(npf->worker_flags & WFLAG_ACTIVE);
216 1.8 rmind KASSERT(npf->worker_flags & WFLAG_REMOVE);
217 1.8 rmind
218 1.8 rmind /*
219 1.8 rmind * Remove the NPF instance:
220 1.8 rmind * - Release any structures owned by the worker.
221 1.8 rmind * - Remove the instance from the list.
222 1.8 rmind * - Notify any thread waiting for removal to complete.
223 1.8 rmind */
224 1.8 rmind if (npf->worker_flags & WFLAG_INITED) {
225 1.8 rmind npfk_thread_unregister(npf);
226 1.8 rmind }
227 1.8 rmind LIST_REMOVE(npf, worker_entry);
228 1.8 rmind npf->worker_flags = 0;
229 1.8 rmind cv_broadcast(&winfo->exit_cv);
230 1.1 rmind }
231 1.1 rmind
232 1.8 rmind static unsigned
233 1.8 rmind process_npf_instance(npf_workerinfo_t *winfo, npf_t *npf)
234 1.1 rmind {
235 1.8 rmind npf_workfunc_t work;
236 1.8 rmind
237 1.8 rmind KASSERT(mutex_owned(&winfo->lock));
238 1.8 rmind
239 1.8 rmind if (npf->worker_flags & WFLAG_REMOVE) {
240 1.8 rmind remove_npf_instance(winfo, npf);
241 1.8 rmind return NPF_GC_MAXWAIT;
242 1.8 rmind }
243 1.2 christos
244 1.8 rmind if ((npf->worker_flags & WFLAG_INITED) == 0) {
245 1.8 rmind npfk_thread_register(npf);
246 1.8 rmind npf->worker_flags |= WFLAG_INITED;
247 1.6 rmind }
248 1.6 rmind
249 1.8 rmind /* Run the jobs. */
250 1.8 rmind for (unsigned i = 0; i < NPF_MAX_WORKS; i++) {
251 1.8 rmind if ((work = npf->worker_funcs[i]) == NULL) {
252 1.2 christos break;
253 1.2 christos }
254 1.8 rmind work(npf);
255 1.1 rmind }
256 1.8 rmind
257 1.8 rmind return MAX(MIN(npf->worker_wait_time, NPF_GC_MAXWAIT), NPF_GC_MINWAIT);
258 1.1 rmind }
259 1.1 rmind
260 1.8 rmind /*
261 1.8 rmind * npf_worker: the main worker loop, processing enlisted NPF instances.
262 1.8 rmind *
263 1.8 rmind * XXX: Currently, npf_workerinfo_t::lock would serialize all workers,
264 1.8 rmind * so there is no point to have more than one worker; but there might
265 1.8 rmind * not be much point anyway.
266 1.8 rmind */
267 1.1 rmind static void
268 1.1 rmind npf_worker(void *arg)
269 1.1 rmind {
270 1.8 rmind npf_workerinfo_t *winfo = arg;
271 1.8 rmind npf_t *npf;
272 1.2 christos
273 1.8 rmind mutex_enter(&winfo->lock);
274 1.10 riastrad for (;;) {
275 1.8 rmind unsigned wait_time = NPF_GC_MAXWAIT;
276 1.8 rmind
277 1.8 rmind /*
278 1.8 rmind * Iterate all instances. We do not use LIST_FOREACH here,
279 1.8 rmind * since the instance can be removed.
280 1.8 rmind */
281 1.8 rmind npf = LIST_FIRST(&winfo->instances);
282 1.2 christos while (npf) {
283 1.8 rmind npf_t *next = LIST_NEXT(npf, worker_entry);
284 1.8 rmind unsigned i_wait_time = process_npf_instance(winfo, npf);
285 1.8 rmind wait_time = MIN(wait_time, i_wait_time);
286 1.8 rmind npf = next;
287 1.8 rmind }
288 1.2 christos
289 1.8 rmind /*
290 1.8 rmind * Sleep and periodically wake up, unless we get notified.
291 1.8 rmind */
292 1.8 rmind if (winfo->exit) {
293 1.8 rmind break;
294 1.1 rmind }
295 1.8 rmind cv_timedwait(&winfo->cv, &winfo->lock, mstohz(wait_time));
296 1.8 rmind }
297 1.8 rmind mutex_exit(&winfo->lock);
298 1.8 rmind
299 1.8 rmind KASSERTMSG(LIST_EMPTY(&winfo->instances),
300 1.8 rmind "NPF instances must be discharged before the npfk_sysfini() call");
301 1.1 rmind
302 1.1 rmind kthread_exit(0);
303 1.1 rmind }
304