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