10d163575Sopenharmony_ci/*-
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
42u_int kobj_lookup_hits;
43u_int kobj_lookup_misses;
44#endif
45
46static struct mtx kobj_mtx;
47static int kobj_mutex_inited;
48static 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
54void
55kobj_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 */
68static const struct kobj_method null_method = {
69	0, 0,
70};
71
72int
73kobj_error_method(void)
74{
75
76	return ENXIO;
77}
78
79static void
80kobj_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
108void
109kobj_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
139void
140kobj_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
153static kobj_method_t*
154kobj_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
168static kobj_method_t*
169kobj_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
191kobj_method_t*
192kobj_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
206void
207kobj_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
237kobj_t
238kobj_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
255static void
256kobj_init_common(kobj_t obj, kobj_class_t cls)
257{
258
259	obj->ops = cls->ops;
260	cls->refs++;
261}
262
263void
264kobj_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
289void
290kobj_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
299void
300kobj_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