Home | History | Annotate | Line # | Download | only in npf
npf_worker.c revision 1.2
      1 /*	$NetBSD: npf_worker.c,v 1.2 2016/12/26 23:05:06 christos Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2010-2015 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This material is based upon work partially supported by The
      8  * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #ifdef _KERNEL
     33 #include <sys/cdefs.h>
     34 __KERNEL_RCSID(0, "$NetBSD: npf_worker.c,v 1.2 2016/12/26 23:05:06 christos Exp $");
     35 
     36 #include <sys/param.h>
     37 #include <sys/types.h>
     38 
     39 #include <sys/mutex.h>
     40 #include <sys/kmem.h>
     41 #include <sys/kernel.h>
     42 #include <sys/kthread.h>
     43 #include <sys/cprng.h>
     44 #endif
     45 
     46 #include "npf_impl.h"
     47 
     48 typedef struct npf_worker {
     49 	kmutex_t		worker_lock;
     50 	kcondvar_t		worker_cv;
     51 	npf_workfunc_t		work_funcs[NPF_MAX_WORKS];
     52 	bool			worker_exit;
     53 	lwp_t *			worker_lwp;
     54 	npf_t *			instances;
     55 } npf_worker_t;
     56 
     57 #define	W_INTERVAL		mstohz(1 * 1000)
     58 
     59 static void			npf_worker(void *) __dead;
     60 
     61 static npf_worker_t *		npf_workers		__read_mostly;
     62 static unsigned			npf_worker_count	__read_mostly;
     63 
     64 int
     65 npf_worker_sysinit(unsigned nworkers)
     66 {
     67 	npf_workers = kmem_zalloc(sizeof(npf_worker_t) * nworkers, KM_SLEEP);
     68 	npf_worker_count = nworkers;
     69 
     70 	for (unsigned i = 0; i < nworkers; i++) {
     71 		npf_worker_t *wrk = &npf_workers[i];
     72 
     73 		mutex_init(&wrk->worker_lock, MUTEX_DEFAULT, IPL_SOFTNET);
     74 		cv_init(&wrk->worker_cv, "npfgccv");
     75 
     76 		if (kthread_create(PRI_NONE, KTHREAD_MPSAFE |
     77 		    KTHREAD_MUSTJOIN, NULL, npf_worker, wrk, &wrk->worker_lwp,
     78 			"npfgc-%u", i)) {
     79 			npf_worker_sysfini();
     80 			return ENOMEM;
     81 		}
     82 	}
     83 	return 0;
     84 }
     85 
     86 void
     87 npf_worker_sysfini(void)
     88 {
     89 	for (unsigned i = 0; i < npf_worker_count; i++) {
     90 		npf_worker_t *wrk = &npf_workers[i];
     91 
     92 		/* Notify the worker and wait for the exit. */
     93 		mutex_enter(&wrk->worker_lock);
     94 		wrk->worker_exit = true;
     95 		cv_broadcast(&wrk->worker_cv);
     96 		mutex_exit(&wrk->worker_lock);
     97 
     98 		if (wrk->worker_lwp) {
     99 			kthread_join(wrk->worker_lwp);
    100 		}
    101 
    102 		/* LWP has exited, destroy the structures. */
    103 		cv_destroy(&wrk->worker_cv);
    104 		mutex_destroy(&wrk->worker_lock);
    105 	}
    106 	kmem_free(npf_workers, sizeof(npf_worker_t) * npf_worker_count);
    107 }
    108 
    109 void
    110 npf_worker_signal(npf_t *npf)
    111 {
    112 	const unsigned idx = npf->worker_id;
    113 	npf_worker_t *wrk = &npf_workers[idx];
    114 
    115 	mutex_enter(&wrk->worker_lock);
    116 	cv_signal(&wrk->worker_cv);
    117 	mutex_exit(&wrk->worker_lock);
    118 }
    119 
    120 static bool
    121 npf_worker_testset(npf_worker_t *wrk, npf_workfunc_t find, npf_workfunc_t set)
    122 {
    123 	for (u_int i = 0; i < NPF_MAX_WORKS; i++) {
    124 		if (wrk->work_funcs[i] == find) {
    125 			wrk->work_funcs[i] = set;
    126 			return true;
    127 		}
    128 	}
    129 	return false;
    130 }
    131 
    132 void
    133 npf_worker_register(npf_t *npf, npf_workfunc_t func)
    134 {
    135 	const unsigned idx = cprng_fast32() % npf_worker_count;
    136 	npf_worker_t *wrk = &npf_workers[idx];
    137 
    138 	mutex_enter(&wrk->worker_lock);
    139 
    140 	npf->worker_id = idx;
    141 	npf->worker_entry = wrk->instances;
    142 	wrk->instances = npf;
    143 
    144 	npf_worker_testset(wrk, NULL, func);
    145 	mutex_exit(&wrk->worker_lock);
    146 }
    147 
    148 void
    149 npf_worker_unregister(npf_t *npf, npf_workfunc_t func)
    150 {
    151 	const unsigned idx = npf->worker_id;
    152 	npf_worker_t *wrk = &npf_workers[idx];
    153 	npf_t *instance;
    154 
    155 	mutex_enter(&wrk->worker_lock);
    156 	npf_worker_testset(wrk, func, NULL);
    157 	if ((instance = wrk->instances) == npf) {
    158 		wrk->instances = instance->worker_entry;
    159 	} else while (instance) {
    160 		if (instance->worker_entry == npf) {
    161 			instance->worker_entry = npf->worker_entry;
    162 			break;
    163 		}
    164 		instance = instance->worker_entry;
    165 	}
    166 	mutex_exit(&wrk->worker_lock);
    167 }
    168 
    169 static void
    170 npf_worker(void *arg)
    171 {
    172 	npf_worker_t *wrk = arg;
    173 
    174 	KASSERT(wrk != NULL);
    175 	KASSERT(!wrk->worker_exit);
    176 
    177 	while (!wrk->worker_exit) {
    178 		npf_t *npf;
    179 
    180 		npf = wrk->instances;
    181 		while (npf) {
    182 			u_int i = NPF_MAX_WORKS;
    183 			npf_workfunc_t work;
    184 
    185 			/* Run the jobs. */
    186 			while (i--) {
    187 				if ((work = wrk->work_funcs[i]) != NULL) {
    188 					work(npf);
    189 				}
    190 			}
    191 			/* Next .. */
    192 			npf = npf->worker_entry;
    193 		}
    194 		if (wrk->worker_exit)
    195 			break;
    196 
    197 		/* Sleep and periodically wake up, unless we get notified. */
    198 		mutex_enter(&wrk->worker_lock);
    199 		cv_timedwait(&wrk->worker_cv, &wrk->worker_lock, W_INTERVAL);
    200 		mutex_exit(&wrk->worker_lock);
    201 	}
    202 	kthread_exit(0);
    203 }
    204