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