xref: /netbsd-src/sys/net/npf/npf_worker.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: npf_worker.c,v 1.4 2017/12/10 01:18:21 rmind 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.4 2017/12/10 01:18:21 rmind 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 | KTHREAD_MUSTJOIN,
77 		    NULL, npf_worker, wrk, &wrk->worker_lwp, "npfgc-%u", i)) {
78 			npf_worker_sysfini();
79 			return ENOMEM;
80 		}
81 	}
82 	return 0;
83 }
84 
85 void
86 npf_worker_sysfini(void)
87 {
88 	for (unsigned i = 0; i < npf_worker_count; i++) {
89 		npf_worker_t *wrk = &npf_workers[i];
90 
91 		/* Notify the worker and wait for the exit. */
92 		mutex_enter(&wrk->worker_lock);
93 		wrk->worker_exit = true;
94 		cv_broadcast(&wrk->worker_cv);
95 		mutex_exit(&wrk->worker_lock);
96 
97 		if (wrk->worker_lwp) {
98 			kthread_join(wrk->worker_lwp);
99 		}
100 
101 		/* LWP has exited, destroy the structures. */
102 		cv_destroy(&wrk->worker_cv);
103 		mutex_destroy(&wrk->worker_lock);
104 	}
105 	kmem_free(npf_workers, sizeof(npf_worker_t) * npf_worker_count);
106 }
107 
108 void
109 npf_worker_signal(npf_t *npf)
110 {
111 	const unsigned idx = npf->worker_id;
112 	npf_worker_t *wrk = &npf_workers[idx];
113 
114 	mutex_enter(&wrk->worker_lock);
115 	cv_signal(&wrk->worker_cv);
116 	mutex_exit(&wrk->worker_lock);
117 }
118 
119 static bool
120 npf_worker_testset(npf_worker_t *wrk, npf_workfunc_t find, npf_workfunc_t set)
121 {
122 	for (u_int i = 0; i < NPF_MAX_WORKS; i++) {
123 		if (wrk->work_funcs[i] == find) {
124 			wrk->work_funcs[i] = set;
125 			return true;
126 		}
127 	}
128 	return false;
129 }
130 
131 void
132 npf_worker_register(npf_t *npf, npf_workfunc_t func)
133 {
134 	const unsigned idx = cprng_fast32() % npf_worker_count;
135 	npf_worker_t *wrk = &npf_workers[idx];
136 
137 	mutex_enter(&wrk->worker_lock);
138 
139 	npf->worker_id = idx;
140 	npf->worker_entry = wrk->instances;
141 	wrk->instances = npf;
142 
143 	npf_worker_testset(wrk, NULL, func);
144 	mutex_exit(&wrk->worker_lock);
145 }
146 
147 void
148 npf_worker_unregister(npf_t *npf, npf_workfunc_t func)
149 {
150 	const unsigned idx = npf->worker_id;
151 	npf_worker_t *wrk;
152 	npf_t *instance;
153 
154 	if (!npf_worker_count)
155 		return;
156 	wrk = &npf_workers[idx];
157 	mutex_enter(&wrk->worker_lock);
158 	npf_worker_testset(wrk, func, NULL);
159 	if ((instance = wrk->instances) == npf) {
160 		wrk->instances = instance->worker_entry;
161 	} else while (instance) {
162 		if (instance->worker_entry == npf) {
163 			instance->worker_entry = npf->worker_entry;
164 			break;
165 		}
166 		instance = instance->worker_entry;
167 	}
168 	mutex_exit(&wrk->worker_lock);
169 }
170 
171 static void
172 npf_worker(void *arg)
173 {
174 	npf_worker_t *wrk = arg;
175 
176 	KASSERT(wrk != NULL);
177 	KASSERT(!wrk->worker_exit);
178 
179 	while (!wrk->worker_exit) {
180 		npf_t *npf;
181 
182 		npf = wrk->instances;
183 		while (npf) {
184 			u_int i = NPF_MAX_WORKS;
185 			npf_workfunc_t work;
186 
187 			/* Run the jobs. */
188 			while (i--) {
189 				if ((work = wrk->work_funcs[i]) != NULL) {
190 					work(npf);
191 				}
192 			}
193 			/* Next .. */
194 			npf = npf->worker_entry;
195 		}
196 		if (wrk->worker_exit)
197 			break;
198 
199 		/* Sleep and periodically wake up, unless we get notified. */
200 		mutex_enter(&wrk->worker_lock);
201 		cv_timedwait(&wrk->worker_cv, &wrk->worker_lock, W_INTERVAL);
202 		mutex_exit(&wrk->worker_lock);
203 	}
204 	kthread_exit(0);
205 }
206