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