1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2000,2003 Doug Rabson
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 #include <sys/param.h>
31 #include <sys/kobj.h>
32 #include <sys/malloc.h>
33 #include <sys/mutex.h>
34 #include <sys/systm.h>
35
36 #ifdef TEST
37 #include "usertest.h"
38 #endif
39
40 #ifdef KOBJ_STATS
41
42 u_int kobj_lookup_hits;
43 u_int kobj_lookup_misses;
44 #endif
45
46 static struct mtx kobj_mtx;
47 static int kobj_mutex_inited;
48 static int kobj_next_id = 1;
49
50 #define KOBJ_LOCK() mtx_lock(&kobj_mtx)
51 #define KOBJ_UNLOCK() mtx_unlock(&kobj_mtx)
52 #define KOBJ_ASSERT(what) mtx_assert(&kobj_mtx, what);
53
54 void
kobj_init_mutex(void * arg)55 kobj_init_mutex(void *arg)
56 {
57 if (!kobj_mutex_inited) {
58 mtx_init(&kobj_mtx, "kobj", NULL, MTX_DEF);
59 kobj_mutex_inited = 1;
60 }
61 }
62
63 /*
64 * This method structure is used to initialise new caches. Since the
65 * desc pointer is NULL, it is guaranteed never to match any read
66 * descriptors.
67 */
68 static const struct kobj_method null_method = {
69 0, 0,
70 };
71
72 int
kobj_error_method(void)73 kobj_error_method(void)
74 {
75
76 return ENXIO;
77 }
78
79 static void
kobj_class_compile_common(kobj_class_t cls,kobj_ops_t ops)80 kobj_class_compile_common(kobj_class_t cls, kobj_ops_t ops)
81 {
82 kobj_method_t *m = NULL;
83 int i;
84
85 /*
86 * Don't do anything if we are already compiled.
87 */
88 if (cls->ops)
89 return;
90
91 /*
92 * First register any methods which need it.
93 */
94 for (i = 0, m = cls->methods; m->desc; i++, m++) {
95 if (m->desc->id == 0)
96 m->desc->id = kobj_next_id++;
97 }
98
99 /*
100 * Then initialise the ops table.
101 */
102 for (i = 0; i < KOBJ_CACHE_SIZE; i++)
103 ops->cache[i] = &null_method;
104 ops->cls = cls;
105 cls->ops = ops;
106 }
107
108 void
kobj_class_compile(kobj_class_t cls)109 kobj_class_compile(kobj_class_t cls)
110 {
111 kobj_ops_t ops;
112
113 KOBJ_ASSERT(MA_NOTOWNED);
114
115 /*
116 * Allocate space for the compiled ops table.
117 */
118 ops = bsd_malloc(sizeof(struct kobj_ops), M_KOBJ, M_NOWAIT);
119 if (!ops)
120 panic("%s: out of memory", __func__);
121
122 KOBJ_LOCK();
123
124 /*
125 * We may have lost a race for kobj_class_compile here - check
126 * to make sure someone else hasn't already compiled this
127 * class.
128 */
129 if (cls->ops) {
130 KOBJ_UNLOCK();
131 bsd_free(ops, M_KOBJ);
132 return;
133 }
134
135 kobj_class_compile_common(cls, ops);
136 KOBJ_UNLOCK();
137 }
138
139 void
kobj_class_compile_static(kobj_class_t cls,kobj_ops_t ops)140 kobj_class_compile_static(kobj_class_t cls, kobj_ops_t ops)
141 {
142
143 KASSERT(kobj_mutex_inited == 0,
144 ("%s: only supported during early cycles", __func__));
145
146 /*
147 * Increment refs to make sure that the ops table is not freed.
148 */
149 cls->refs++;
150 kobj_class_compile_common(cls, ops);
151 }
152
153 static kobj_method_t*
kobj_lookup_method_class(kobj_class_t cls,kobjop_desc_t desc)154 kobj_lookup_method_class(kobj_class_t cls, kobjop_desc_t desc)
155 {
156 kobj_method_t *methods = cls->methods;
157 kobj_method_t *ce;
158
159 for (ce = methods; ce && ce->desc; ce++) {
160 if (ce->desc == desc) {
161 return ce;
162 }
163 }
164
165 return NULL;
166 }
167
168 static kobj_method_t*
kobj_lookup_method_mi(kobj_class_t cls,kobjop_desc_t desc)169 kobj_lookup_method_mi(kobj_class_t cls,
170 kobjop_desc_t desc)
171 {
172 kobj_method_t *ce = NULL;
173 kobj_class_t *basep = NULL;
174
175 ce = kobj_lookup_method_class(cls, desc);
176 if (ce)
177 return ce;
178
179 basep = cls->baseclasses;
180 if (basep) {
181 for (; *basep; basep++) {
182 ce = kobj_lookup_method_mi(*basep, desc);
183 if (ce)
184 return ce;
185 }
186 }
187
188 return NULL;
189 }
190
191 kobj_method_t*
kobj_lookup_method(kobj_class_t cls,kobj_method_t ** cep,kobjop_desc_t desc)192 kobj_lookup_method(kobj_class_t cls,
193 kobj_method_t **cep,
194 kobjop_desc_t desc)
195 {
196 kobj_method_t *ce;
197
198 ce = kobj_lookup_method_mi(cls, desc);
199 if (!ce)
200 ce = &desc->deflt;
201 if (cep)
202 *cep = ce;
203 return ce;
204 }
205
206 void
kobj_class_free(kobj_class_t cls)207 kobj_class_free(kobj_class_t cls)
208 {
209 void* ops = NULL;
210
211 KOBJ_ASSERT(MA_NOTOWNED);
212 KOBJ_LOCK();
213
214 /*
215 * Protect against a race between kobj_create and
216 * kobj_delete.
217 */
218 if (cls->refs == 0) {
219 /*
220 * For now we don't do anything to unregister any methods
221 * which are no longer used.
222 */
223
224 /*
225 * Free memory and clean up.
226 */
227 ops = cls->ops;
228 cls->ops = NULL;
229 }
230
231 KOBJ_UNLOCK();
232
233 if (ops)
234 bsd_free(ops, M_KOBJ);
235 }
236
237 kobj_t
kobj_create(kobj_class_t cls,struct malloc_type * mtype,int mflags)238 kobj_create(kobj_class_t cls,
239 struct malloc_type *mtype,
240 int mflags)
241 {
242 kobj_t obj;
243
244 /*
245 * Allocate and initialise the new object.
246 */
247 obj = bsd_malloc(cls->size, mtype, mflags | M_ZERO);
248 if (!obj)
249 return NULL;
250 kobj_init(obj, cls);
251
252 return obj;
253 }
254
255 static void
kobj_init_common(kobj_t obj,kobj_class_t cls)256 kobj_init_common(kobj_t obj, kobj_class_t cls)
257 {
258
259 obj->ops = cls->ops;
260 cls->refs++;
261 }
262
263 void
kobj_init(kobj_t obj,kobj_class_t cls)264 kobj_init(kobj_t obj, kobj_class_t cls)
265 {
266 KOBJ_ASSERT(MA_NOTOWNED);
267 retry:
268 KOBJ_LOCK();
269
270 /*
271 * Consider compiling the class' method table.
272 */
273 if (!cls->ops) {
274 /*
275 * kobj_class_compile doesn't want the lock held
276 * because of the call to malloc - we drop the lock
277 * and re-try.
278 */
279 KOBJ_UNLOCK();
280 kobj_class_compile(cls);
281 goto retry;
282 }
283
284 kobj_init_common(obj, cls);
285
286 KOBJ_UNLOCK();
287 }
288
289 void
kobj_init_static(kobj_t obj,kobj_class_t cls)290 kobj_init_static(kobj_t obj, kobj_class_t cls)
291 {
292
293 KASSERT(kobj_mutex_inited == 0,
294 ("%s: only supported during early cycles", __func__));
295
296 kobj_init_common(obj, cls);
297 }
298
299 void
kobj_delete(kobj_t obj,struct malloc_type * mtype)300 kobj_delete(kobj_t obj, struct malloc_type *mtype)
301 {
302 kobj_class_t cls = obj->ops->cls;
303 int refs;
304
305 /*
306 * Consider freeing the compiled method table for the class
307 * after its last instance is deleted. As an optimisation, we
308 * should defer this for a short while to avoid thrashing.
309 */
310 KOBJ_ASSERT(MA_NOTOWNED);
311 KOBJ_LOCK();
312 cls->refs--;
313 refs = cls->refs;
314 KOBJ_UNLOCK();
315
316 if (!refs)
317 kobj_class_free(cls);
318
319 obj->ops = NULL;
320 if (mtype)
321 bsd_free(obj, mtype);
322 }
323