Home | History | Annotate | Line # | Download | only in npf
npf_worker.c revision 1.9
      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.9     rmind __KERNEL_RCSID(0, "$NetBSD: npf_worker.c,v 1.9 2020/05/30 20:54:54 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.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.8     rmind 	while (!winfo->exit) {
    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