npf_worker.c revision 1.8 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.8 rmind __KERNEL_RCSID(0, "$NetBSD: npf_worker.c,v 1.8 2020/05/30 14:16:56 rmind 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.8 rmind cv_init(&winfo->cv, "npfgccv");
85 1.8 rmind LIST_INIT(&winfo->instances);
86 1.8 rmind worker_info = winfo;
87 1.2 christos
88 1.2 christos for (unsigned i = 0; i < nworkers; i++) {
89 1.3 rmind if (kthread_create(PRI_NONE, KTHREAD_MPSAFE | KTHREAD_MUSTJOIN,
90 1.8 rmind NULL, npf_worker, winfo, &winfo->worker[i], "npfgc%u", i)) {
91 1.2 christos npf_worker_sysfini();
92 1.2 christos return ENOMEM;
93 1.2 christos }
94 1.1 rmind }
95 1.1 rmind return 0;
96 1.1 rmind }
97 1.1 rmind
98 1.1 rmind void
99 1.1 rmind npf_worker_sysfini(void)
100 1.1 rmind {
101 1.8 rmind npf_workerinfo_t *winfo = worker_info;
102 1.8 rmind unsigned nworkers;
103 1.8 rmind
104 1.8 rmind if (!winfo) {
105 1.8 rmind return;
106 1.8 rmind }
107 1.8 rmind
108 1.8 rmind /* Notify the workers to exit. */
109 1.8 rmind mutex_enter(&winfo->lock);
110 1.8 rmind winfo->exit = true;
111 1.8 rmind cv_broadcast(&winfo->cv);
112 1.8 rmind mutex_exit(&winfo->lock);
113 1.8 rmind
114 1.8 rmind /* Wait for them to finish and then destroy. */
115 1.8 rmind nworkers = winfo->worker_count;
116 1.8 rmind for (unsigned i = 0; i < nworkers; i++) {
117 1.8 rmind lwp_t *worker;
118 1.8 rmind
119 1.8 rmind if ((worker = winfo->worker[i]) != NULL) {
120 1.8 rmind kthread_join(worker);
121 1.8 rmind }
122 1.8 rmind }
123 1.8 rmind cv_destroy(&winfo->cv);
124 1.8 rmind mutex_destroy(&winfo->lock);
125 1.8 rmind kmem_free(winfo, offsetof(npf_workerinfo_t, worker[nworkers]));
126 1.8 rmind worker_info = NULL;
127 1.8 rmind }
128 1.8 rmind
129 1.8 rmind int
130 1.8 rmind npf_worker_addfunc(npf_t *npf, npf_workfunc_t work)
131 1.8 rmind {
132 1.8 rmind KASSERTMSG(npf->worker_flags == 0,
133 1.8 rmind "the task must be added before the npf_worker_enlist() call");
134 1.2 christos
135 1.8 rmind for (unsigned i = 0; i < NPF_MAX_WORKS; i++) {
136 1.8 rmind if (npf->worker_funcs[i] == NULL) {
137 1.8 rmind npf->worker_funcs[i] = work;
138 1.8 rmind return 0;
139 1.8 rmind }
140 1.6 rmind }
141 1.8 rmind return -1;
142 1.1 rmind }
143 1.1 rmind
144 1.1 rmind void
145 1.2 christos npf_worker_signal(npf_t *npf)
146 1.1 rmind {
147 1.8 rmind npf_workerinfo_t *winfo = worker_info;
148 1.2 christos
149 1.8 rmind if ((npf->worker_flags & WFLAG_ACTIVE) == 0) {
150 1.8 rmind return;
151 1.8 rmind }
152 1.8 rmind KASSERT(winfo != NULL);
153 1.8 rmind
154 1.8 rmind mutex_enter(&winfo->lock);
155 1.8 rmind cv_signal(&winfo->cv);
156 1.8 rmind mutex_exit(&winfo->lock);
157 1.1 rmind }
158 1.1 rmind
159 1.8 rmind /*
160 1.8 rmind * npf_worker_enlist: add the NPF instance for worker(s) to process.
161 1.8 rmind */
162 1.8 rmind void
163 1.8 rmind npf_worker_enlist(npf_t *npf)
164 1.1 rmind {
165 1.8 rmind npf_workerinfo_t *winfo = worker_info;
166 1.8 rmind
167 1.8 rmind KASSERT(npf->worker_flags == 0);
168 1.8 rmind if (!winfo) {
169 1.8 rmind return;
170 1.1 rmind }
171 1.8 rmind
172 1.8 rmind mutex_enter(&winfo->lock);
173 1.8 rmind LIST_INSERT_HEAD(&winfo->instances, npf, worker_entry);
174 1.8 rmind npf->worker_flags |= WFLAG_ACTIVE;
175 1.8 rmind mutex_exit(&winfo->lock);
176 1.1 rmind }
177 1.1 rmind
178 1.8 rmind /*
179 1.8 rmind * npf_worker_discharge: remove the NPF instance the list for workers.
180 1.8 rmind *
181 1.8 rmind * => May block waiting for a worker to finish processing the instance.
182 1.8 rmind */
183 1.1 rmind void
184 1.8 rmind npf_worker_discharge(npf_t *npf)
185 1.1 rmind {
186 1.8 rmind npf_workerinfo_t *winfo = worker_info;
187 1.6 rmind
188 1.8 rmind if ((npf->worker_flags & WFLAG_ACTIVE) == 0) {
189 1.6 rmind return;
190 1.6 rmind }
191 1.8 rmind KASSERT(winfo != NULL);
192 1.2 christos
193 1.8 rmind /*
194 1.8 rmind * Notify the worker(s) that we are removing this instance.
195 1.8 rmind */
196 1.8 rmind mutex_enter(&winfo->lock);
197 1.8 rmind KASSERT(npf->worker_flags & WFLAG_ACTIVE);
198 1.8 rmind npf->worker_flags |= WFLAG_REMOVE;
199 1.8 rmind cv_broadcast(&winfo->cv);
200 1.8 rmind
201 1.8 rmind /* Wait for a worker to process this request. */
202 1.8 rmind while (npf->worker_flags & WFLAG_ACTIVE) {
203 1.8 rmind cv_wait(&winfo->exit_cv, &winfo->lock);
204 1.8 rmind }
205 1.8 rmind mutex_exit(&winfo->lock);
206 1.8 rmind KASSERT(npf->worker_flags == 0);
207 1.8 rmind }
208 1.2 christos
209 1.8 rmind static void
210 1.8 rmind remove_npf_instance(npf_workerinfo_t *winfo, npf_t *npf)
211 1.8 rmind {
212 1.8 rmind KASSERT(mutex_owned(&winfo->lock));
213 1.8 rmind KASSERT(npf->worker_flags & WFLAG_ACTIVE);
214 1.8 rmind KASSERT(npf->worker_flags & WFLAG_REMOVE);
215 1.8 rmind
216 1.8 rmind /*
217 1.8 rmind * Remove the NPF instance:
218 1.8 rmind * - Release any structures owned by the worker.
219 1.8 rmind * - Remove the instance from the list.
220 1.8 rmind * - Notify any thread waiting for removal to complete.
221 1.8 rmind */
222 1.8 rmind if (npf->worker_flags & WFLAG_INITED) {
223 1.8 rmind npfk_thread_unregister(npf);
224 1.8 rmind }
225 1.8 rmind LIST_REMOVE(npf, worker_entry);
226 1.8 rmind npf->worker_flags = 0;
227 1.8 rmind cv_broadcast(&winfo->exit_cv);
228 1.1 rmind }
229 1.1 rmind
230 1.8 rmind static unsigned
231 1.8 rmind process_npf_instance(npf_workerinfo_t *winfo, npf_t *npf)
232 1.1 rmind {
233 1.8 rmind npf_workfunc_t work;
234 1.8 rmind
235 1.8 rmind KASSERT(mutex_owned(&winfo->lock));
236 1.8 rmind
237 1.8 rmind if (npf->worker_flags & WFLAG_REMOVE) {
238 1.8 rmind remove_npf_instance(winfo, npf);
239 1.8 rmind return NPF_GC_MAXWAIT;
240 1.8 rmind }
241 1.2 christos
242 1.8 rmind if ((npf->worker_flags & WFLAG_INITED) == 0) {
243 1.8 rmind npfk_thread_register(npf);
244 1.8 rmind npf->worker_flags |= WFLAG_INITED;
245 1.6 rmind }
246 1.6 rmind
247 1.8 rmind /* Run the jobs. */
248 1.8 rmind for (unsigned i = 0; i < NPF_MAX_WORKS; i++) {
249 1.8 rmind if ((work = npf->worker_funcs[i]) == NULL) {
250 1.2 christos break;
251 1.2 christos }
252 1.8 rmind work(npf);
253 1.1 rmind }
254 1.8 rmind
255 1.8 rmind return MAX(MIN(npf->worker_wait_time, NPF_GC_MAXWAIT), NPF_GC_MINWAIT);
256 1.1 rmind }
257 1.1 rmind
258 1.8 rmind /*
259 1.8 rmind * npf_worker: the main worker loop, processing enlisted NPF instances.
260 1.8 rmind *
261 1.8 rmind * XXX: Currently, npf_workerinfo_t::lock would serialize all workers,
262 1.8 rmind * so there is no point to have more than one worker; but there might
263 1.8 rmind * not be much point anyway.
264 1.8 rmind */
265 1.1 rmind static void
266 1.1 rmind npf_worker(void *arg)
267 1.1 rmind {
268 1.8 rmind npf_workerinfo_t *winfo = arg;
269 1.8 rmind npf_t *npf;
270 1.2 christos
271 1.8 rmind mutex_enter(&winfo->lock);
272 1.8 rmind while (!winfo->exit) {
273 1.8 rmind unsigned wait_time = NPF_GC_MAXWAIT;
274 1.8 rmind
275 1.8 rmind /*
276 1.8 rmind * Iterate all instances. We do not use LIST_FOREACH here,
277 1.8 rmind * since the instance can be removed.
278 1.8 rmind */
279 1.8 rmind npf = LIST_FIRST(&winfo->instances);
280 1.2 christos while (npf) {
281 1.8 rmind npf_t *next = LIST_NEXT(npf, worker_entry);
282 1.8 rmind unsigned i_wait_time = process_npf_instance(winfo, npf);
283 1.8 rmind wait_time = MIN(wait_time, i_wait_time);
284 1.8 rmind npf = next;
285 1.8 rmind }
286 1.2 christos
287 1.8 rmind /*
288 1.8 rmind * Sleep and periodically wake up, unless we get notified.
289 1.8 rmind */
290 1.8 rmind if (winfo->exit) {
291 1.8 rmind break;
292 1.1 rmind }
293 1.8 rmind cv_timedwait(&winfo->cv, &winfo->lock, mstohz(wait_time));
294 1.8 rmind }
295 1.8 rmind mutex_exit(&winfo->lock);
296 1.8 rmind
297 1.8 rmind KASSERTMSG(LIST_EMPTY(&winfo->instances),
298 1.8 rmind "NPF instances must be discharged before the npfk_sysfini() call");
299 1.1 rmind
300 1.1 rmind kthread_exit(0);
301 1.1 rmind }
302