xref: /freebsd/sys/kern/subr_kobj.c (revision 2c204a16314db5f3e7b100139a8dfd7deb4e2bd0)
1f7b77691SDoug Rabson /*-
246ba7a35SDoug Rabson  * Copyright (c) 2000,2003 Doug Rabson
3f7b77691SDoug Rabson  * All rights reserved.
4f7b77691SDoug Rabson  *
5f7b77691SDoug Rabson  * Redistribution and use in source and binary forms, with or without
6f7b77691SDoug Rabson  * modification, are permitted provided that the following conditions
7f7b77691SDoug Rabson  * are met:
8f7b77691SDoug Rabson  * 1. Redistributions of source code must retain the above copyright
9f7b77691SDoug Rabson  *    notice, this list of conditions and the following disclaimer.
10f7b77691SDoug Rabson  * 2. Redistributions in binary form must reproduce the above copyright
11f7b77691SDoug Rabson  *    notice, this list of conditions and the following disclaimer in the
12f7b77691SDoug Rabson  *    documentation and/or other materials provided with the distribution.
13f7b77691SDoug Rabson  *
14f7b77691SDoug Rabson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15f7b77691SDoug Rabson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16f7b77691SDoug Rabson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17f7b77691SDoug Rabson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18f7b77691SDoug Rabson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19f7b77691SDoug Rabson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20f7b77691SDoug Rabson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21f7b77691SDoug Rabson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22f7b77691SDoug Rabson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23f7b77691SDoug Rabson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24f7b77691SDoug Rabson  * SUCH DAMAGE.
25f7b77691SDoug Rabson  */
26f7b77691SDoug Rabson 
27677b542eSDavid E. O'Brien #include <sys/cdefs.h>
28677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$");
29677b542eSDavid E. O'Brien 
30f7b77691SDoug Rabson #include <sys/param.h>
31f7b77691SDoug Rabson #include <sys/kernel.h>
3246ba7a35SDoug Rabson #include <sys/kobj.h>
3346ba7a35SDoug Rabson #include <sys/lock.h>
3446ba7a35SDoug Rabson #include <sys/malloc.h>
3546ba7a35SDoug Rabson #include <sys/mutex.h>
36828447e0SCameron Grant #include <sys/sysctl.h>
37f7b77691SDoug Rabson #ifndef TEST
38f7b77691SDoug Rabson #include <sys/systm.h>
39f7b77691SDoug Rabson #endif
40f7b77691SDoug Rabson 
41f7b77691SDoug Rabson #ifdef TEST
42f7b77691SDoug Rabson #include "usertest.h"
43f7b77691SDoug Rabson #endif
44f7b77691SDoug Rabson 
45f7b77691SDoug Rabson static MALLOC_DEFINE(M_KOBJ, "kobj", "Kernel object structures");
46f7b77691SDoug Rabson 
47f7b77691SDoug Rabson #ifdef KOBJ_STATS
48f7b77691SDoug Rabson 
493316a80bSKelly Yancey u_int kobj_lookup_hits;
503316a80bSKelly Yancey u_int kobj_lookup_misses;
51f7b77691SDoug Rabson 
523316a80bSKelly Yancey SYSCTL_UINT(_kern, OID_AUTO, kobj_hits, CTLFLAG_RD,
53828447e0SCameron Grant 	   &kobj_lookup_hits, 0, "");
543316a80bSKelly Yancey SYSCTL_UINT(_kern, OID_AUTO, kobj_misses, CTLFLAG_RD,
55828447e0SCameron Grant 	   &kobj_lookup_misses, 0, "");
56f7b77691SDoug Rabson 
57f7b77691SDoug Rabson #endif
58f7b77691SDoug Rabson 
5946ba7a35SDoug Rabson static struct mtx kobj_mtx;
60e000e001SPeter Grehan static int kobj_mutex_inited;
61f7b77691SDoug Rabson static int kobj_next_id = 1;
62f7b77691SDoug Rabson 
6391416fb2SNathan Whitehorn /*
6491416fb2SNathan Whitehorn  * In the event that kobj_mtx has not been initialized yet,
6591416fb2SNathan Whitehorn  * we will ignore it, and run without locks in order to support
6691416fb2SNathan Whitehorn  * use of KOBJ before mutexes are available. This early in the boot
6791416fb2SNathan Whitehorn  * process, everything is single threaded and so races should not
6891416fb2SNathan Whitehorn  * happen. This is used to provide the PMAP layer on PowerPC, as well
6991416fb2SNathan Whitehorn  * as board support.
7091416fb2SNathan Whitehorn  */
7191416fb2SNathan Whitehorn 
7291416fb2SNathan Whitehorn #define KOBJ_LOCK()	if (kobj_mutex_inited) mtx_lock(&kobj_mtx);
7391416fb2SNathan Whitehorn #define KOBJ_UNLOCK()	if (kobj_mutex_inited) mtx_unlock(&kobj_mtx);
7491416fb2SNathan Whitehorn #define KOBJ_ASSERT(what) if (kobj_mutex_inited) mtx_assert(&kobj_mtx,what);
7591416fb2SNathan Whitehorn 
76828447e0SCameron Grant SYSCTL_UINT(_kern, OID_AUTO, kobj_methodcount, CTLFLAG_RD,
77828447e0SCameron Grant 	   &kobj_next_id, 0, "");
78828447e0SCameron Grant 
7946ba7a35SDoug Rabson static void
8046ba7a35SDoug Rabson kobj_init_mutex(void *arg)
8146ba7a35SDoug Rabson {
82e000e001SPeter Grehan 	if (!kobj_mutex_inited) {
8346ba7a35SDoug Rabson 		mtx_init(&kobj_mtx, "kobj", NULL, MTX_DEF);
84e000e001SPeter Grehan 		kobj_mutex_inited = 1;
85e000e001SPeter Grehan 	}
8646ba7a35SDoug Rabson }
8746ba7a35SDoug Rabson 
8846ba7a35SDoug Rabson SYSINIT(kobj, SI_SUB_LOCK, SI_ORDER_ANY, kobj_init_mutex, NULL);
8946ba7a35SDoug Rabson 
9046ba7a35SDoug Rabson /*
9146ba7a35SDoug Rabson  * This method structure is used to initialise new caches. Since the
9246ba7a35SDoug Rabson  * desc pointer is NULL, it is guaranteed never to match any read
9346ba7a35SDoug Rabson  * descriptors.
9446ba7a35SDoug Rabson  */
9546ba7a35SDoug Rabson static struct kobj_method null_method = {
9646ba7a35SDoug Rabson 	0, 0,
9746ba7a35SDoug Rabson };
9846ba7a35SDoug Rabson 
9946ba7a35SDoug Rabson int
100f7b77691SDoug Rabson kobj_error_method(void)
101f7b77691SDoug Rabson {
10246ba7a35SDoug Rabson 
103f7b77691SDoug Rabson 	return ENXIO;
104f7b77691SDoug Rabson }
105f7b77691SDoug Rabson 
106f7b77691SDoug Rabson static void
107f7b77691SDoug Rabson kobj_register_method(struct kobjop_desc *desc)
108f7b77691SDoug Rabson {
10991416fb2SNathan Whitehorn 	KOBJ_ASSERT(MA_OWNED);
11046ba7a35SDoug Rabson 
111828447e0SCameron Grant 	if (desc->id == 0) {
112f7b77691SDoug Rabson 		desc->id = kobj_next_id++;
113f7b77691SDoug Rabson 	}
114828447e0SCameron Grant }
115f7b77691SDoug Rabson 
116f7b77691SDoug Rabson static void
117f7b77691SDoug Rabson kobj_unregister_method(struct kobjop_desc *desc)
118f7b77691SDoug Rabson {
119f7b77691SDoug Rabson }
120f7b77691SDoug Rabson 
121f80e4547SDoug Rabson static void
122f80e4547SDoug Rabson kobj_class_compile_common(kobj_class_t cls, kobj_ops_t ops)
123f7b77691SDoug Rabson {
124f7b77691SDoug Rabson 	kobj_method_t *m;
125f7b77691SDoug Rabson 	int i;
126f7b77691SDoug Rabson 
12791416fb2SNathan Whitehorn 	KOBJ_ASSERT(MA_OWNED);
12846ba7a35SDoug Rabson 
129f7b77691SDoug Rabson 	/*
130f7b77691SDoug Rabson 	 * Don't do anything if we are already compiled.
131f7b77691SDoug Rabson 	 */
132f7b77691SDoug Rabson 	if (cls->ops)
133f7b77691SDoug Rabson 		return;
134f7b77691SDoug Rabson 
135f7b77691SDoug Rabson 	/*
136f7b77691SDoug Rabson 	 * First register any methods which need it.
137f7b77691SDoug Rabson 	 */
138f7b77691SDoug Rabson 	for (i = 0, m = cls->methods; m->desc; i++, m++)
139f7b77691SDoug Rabson 		kobj_register_method(m->desc);
140f7b77691SDoug Rabson 
141f7b77691SDoug Rabson 	/*
142f80e4547SDoug Rabson 	 * Then initialise the ops table.
143f80e4547SDoug Rabson 	 */
14446ba7a35SDoug Rabson 	for (i = 0; i < KOBJ_CACHE_SIZE; i++)
14546ba7a35SDoug Rabson 		ops->cache[i] = &null_method;
146f80e4547SDoug Rabson 	ops->cls = cls;
147f80e4547SDoug Rabson 	cls->ops = ops;
148f80e4547SDoug Rabson }
149f80e4547SDoug Rabson 
150f80e4547SDoug Rabson void
151f80e4547SDoug Rabson kobj_class_compile(kobj_class_t cls)
152f80e4547SDoug Rabson {
153f80e4547SDoug Rabson 	kobj_ops_t ops;
154f80e4547SDoug Rabson 
15591416fb2SNathan Whitehorn 	KOBJ_ASSERT(MA_NOTOWNED);
15646ba7a35SDoug Rabson 
157f80e4547SDoug Rabson 	/*
158f80e4547SDoug Rabson 	 * Allocate space for the compiled ops table.
159f7b77691SDoug Rabson 	 */
160f7b77691SDoug Rabson 	ops = malloc(sizeof(struct kobj_ops), M_KOBJ, M_NOWAIT);
161f7b77691SDoug Rabson 	if (!ops)
162f7b77691SDoug Rabson 		panic("kobj_compile_methods: out of memory");
16346ba7a35SDoug Rabson 
16491416fb2SNathan Whitehorn 	KOBJ_LOCK();
16546ba7a35SDoug Rabson 
16646ba7a35SDoug Rabson 	/*
16746ba7a35SDoug Rabson 	 * We may have lost a race for kobj_class_compile here - check
16846ba7a35SDoug Rabson 	 * to make sure someone else hasn't already compiled this
16946ba7a35SDoug Rabson 	 * class.
17046ba7a35SDoug Rabson 	 */
17146ba7a35SDoug Rabson 	if (cls->ops) {
17291416fb2SNathan Whitehorn 		KOBJ_UNLOCK();
17346ba7a35SDoug Rabson 		free(ops, M_KOBJ);
17446ba7a35SDoug Rabson 		return;
17546ba7a35SDoug Rabson 	}
17646ba7a35SDoug Rabson 
177f80e4547SDoug Rabson 	kobj_class_compile_common(cls, ops);
17891416fb2SNathan Whitehorn 	KOBJ_UNLOCK();
179f80e4547SDoug Rabson }
180f80e4547SDoug Rabson 
181f80e4547SDoug Rabson void
182f80e4547SDoug Rabson kobj_class_compile_static(kobj_class_t cls, kobj_ops_t ops)
183f80e4547SDoug Rabson {
18446ba7a35SDoug Rabson 
18591416fb2SNathan Whitehorn 	KOBJ_ASSERT(MA_NOTOWNED);
18646ba7a35SDoug Rabson 
187f80e4547SDoug Rabson 	/*
188f80e4547SDoug Rabson 	 * Increment refs to make sure that the ops table is not freed.
189f80e4547SDoug Rabson 	 */
19091416fb2SNathan Whitehorn 	KOBJ_LOCK();
19191416fb2SNathan Whitehorn 
192f80e4547SDoug Rabson 	cls->refs++;
193f80e4547SDoug Rabson 	kobj_class_compile_common(cls, ops);
19491416fb2SNathan Whitehorn 
19591416fb2SNathan Whitehorn 	KOBJ_UNLOCK();
196f7b77691SDoug Rabson }
197f7b77691SDoug Rabson 
19846ba7a35SDoug Rabson static kobj_method_t*
19946ba7a35SDoug Rabson kobj_lookup_method_class(kobj_class_t cls, kobjop_desc_t desc)
20046ba7a35SDoug Rabson {
20146ba7a35SDoug Rabson 	kobj_method_t *methods = cls->methods;
20246ba7a35SDoug Rabson 	kobj_method_t *ce;
20346ba7a35SDoug Rabson 
20446ba7a35SDoug Rabson 	for (ce = methods; ce && ce->desc; ce++) {
20546ba7a35SDoug Rabson 		if (ce->desc == desc) {
20646ba7a35SDoug Rabson 			return ce;
20746ba7a35SDoug Rabson 		}
20846ba7a35SDoug Rabson 	}
20946ba7a35SDoug Rabson 
2102c204a16SWarner Losh 	return NULL;
21146ba7a35SDoug Rabson }
21246ba7a35SDoug Rabson 
21346ba7a35SDoug Rabson static kobj_method_t*
21446ba7a35SDoug Rabson kobj_lookup_method_mi(kobj_class_t cls,
215f7b77691SDoug Rabson 		      kobjop_desc_t desc)
216f7b77691SDoug Rabson {
21746ba7a35SDoug Rabson 	kobj_method_t *ce;
21846ba7a35SDoug Rabson 	kobj_class_t *basep;
21946ba7a35SDoug Rabson 
22046ba7a35SDoug Rabson 	ce = kobj_lookup_method_class(cls, desc);
22146ba7a35SDoug Rabson 	if (ce)
22246ba7a35SDoug Rabson 		return ce;
22346ba7a35SDoug Rabson 
22446ba7a35SDoug Rabson 	basep = cls->baseclasses;
22546ba7a35SDoug Rabson 	if (basep) {
22646ba7a35SDoug Rabson 		for (; *basep; basep++) {
22746ba7a35SDoug Rabson 			ce = kobj_lookup_method_mi(*basep, desc);
22846ba7a35SDoug Rabson 			if (ce)
22946ba7a35SDoug Rabson 				return ce;
230f7b77691SDoug Rabson 		}
231f7b77691SDoug Rabson 	}
23246ba7a35SDoug Rabson 
2332c204a16SWarner Losh 	return NULL;
23446ba7a35SDoug Rabson }
23546ba7a35SDoug Rabson 
23646ba7a35SDoug Rabson kobj_method_t*
23746ba7a35SDoug Rabson kobj_lookup_method(kobj_class_t cls,
23846ba7a35SDoug Rabson 		   kobj_method_t **cep,
23946ba7a35SDoug Rabson 		   kobjop_desc_t desc)
24046ba7a35SDoug Rabson {
24146ba7a35SDoug Rabson 	kobj_method_t *ce;
24246ba7a35SDoug Rabson 
24346ba7a35SDoug Rabson #ifdef KOBJ_STATS
24446ba7a35SDoug Rabson 	/*
24546ba7a35SDoug Rabson 	 * Correct for the 'hit' assumption in KOBJOPLOOKUP and record
24646ba7a35SDoug Rabson 	 * a 'miss'.
24746ba7a35SDoug Rabson 	 */
24846ba7a35SDoug Rabson 	kobj_lookup_hits--;
24942b6a681SJohn Baldwin 	kobj_lookup_misses++;
25046ba7a35SDoug Rabson #endif
25146ba7a35SDoug Rabson 
25246ba7a35SDoug Rabson 	ce = kobj_lookup_method_mi(cls, desc);
25346ba7a35SDoug Rabson 	if (!ce)
25446ba7a35SDoug Rabson 		ce = desc->deflt;
25546ba7a35SDoug Rabson 	*cep = ce;
25646ba7a35SDoug Rabson 	return ce;
257f7b77691SDoug Rabson }
258f7b77691SDoug Rabson 
259f7b77691SDoug Rabson void
260f7b77691SDoug Rabson kobj_class_free(kobj_class_t cls)
261f7b77691SDoug Rabson {
262f7b77691SDoug Rabson 	int i;
263f7b77691SDoug Rabson 	kobj_method_t *m;
2642c204a16SWarner Losh 	void* ops = NULL;
265f7b77691SDoug Rabson 
26691416fb2SNathan Whitehorn 	KOBJ_ASSERT(MA_NOTOWNED);
26791416fb2SNathan Whitehorn 	KOBJ_LOCK();
26846ba7a35SDoug Rabson 
26946ba7a35SDoug Rabson 	/*
27046ba7a35SDoug Rabson 	 * Protect against a race between kobj_create and
27146ba7a35SDoug Rabson 	 * kobj_delete.
27246ba7a35SDoug Rabson 	 */
27346ba7a35SDoug Rabson 	if (cls->refs == 0) {
274f7b77691SDoug Rabson 		/*
275f7b77691SDoug Rabson 		 * Unregister any methods which are no longer used.
276f7b77691SDoug Rabson 		 */
277f7b77691SDoug Rabson 		for (i = 0, m = cls->methods; m->desc; i++, m++)
278f7b77691SDoug Rabson 			kobj_unregister_method(m->desc);
279f7b77691SDoug Rabson 
280f7b77691SDoug Rabson 		/*
281f7b77691SDoug Rabson 		 * Free memory and clean up.
282f7b77691SDoug Rabson 		 */
28346ba7a35SDoug Rabson 		ops = cls->ops;
2842c204a16SWarner Losh 		cls->ops = NULL;
285f7b77691SDoug Rabson 	}
286f7b77691SDoug Rabson 
28791416fb2SNathan Whitehorn 	KOBJ_UNLOCK();
28846ba7a35SDoug Rabson 
28946ba7a35SDoug Rabson 	if (ops)
29046ba7a35SDoug Rabson 		free(ops, M_KOBJ);
29146ba7a35SDoug Rabson }
29246ba7a35SDoug Rabson 
293f7b77691SDoug Rabson kobj_t
294f7b77691SDoug Rabson kobj_create(kobj_class_t cls,
295f7b77691SDoug Rabson 	    struct malloc_type *mtype,
296f7b77691SDoug Rabson 	    int mflags)
297f7b77691SDoug Rabson {
298f7b77691SDoug Rabson 	kobj_t obj;
299f7b77691SDoug Rabson 
300f7b77691SDoug Rabson 	/*
301f7b77691SDoug Rabson 	 * Allocate and initialise the new object.
302f7b77691SDoug Rabson 	 */
3037cc0979fSDavid Malone 	obj = malloc(cls->size, mtype, mflags | M_ZERO);
304f7b77691SDoug Rabson 	if (!obj)
3052c204a16SWarner Losh 		return NULL;
306f7b77691SDoug Rabson 	kobj_init(obj, cls);
307f7b77691SDoug Rabson 
308f7b77691SDoug Rabson 	return obj;
309f7b77691SDoug Rabson }
310f7b77691SDoug Rabson 
311f7b77691SDoug Rabson void
312f7b77691SDoug Rabson kobj_init(kobj_t obj, kobj_class_t cls)
313f7b77691SDoug Rabson {
31491416fb2SNathan Whitehorn 	KOBJ_ASSERT(MA_NOTOWNED);
31546ba7a35SDoug Rabson   retry:
31691416fb2SNathan Whitehorn 	KOBJ_LOCK();
31746ba7a35SDoug Rabson 
318f7b77691SDoug Rabson 	/*
319f7b77691SDoug Rabson 	 * Consider compiling the class' method table.
320f7b77691SDoug Rabson 	 */
32146ba7a35SDoug Rabson 	if (!cls->ops) {
32246ba7a35SDoug Rabson 		/*
32346ba7a35SDoug Rabson 		 * kobj_class_compile doesn't want the lock held
32446ba7a35SDoug Rabson 		 * because of the call to malloc - we drop the lock
32546ba7a35SDoug Rabson 		 * and re-try.
32646ba7a35SDoug Rabson 		 */
32791416fb2SNathan Whitehorn 		KOBJ_UNLOCK();
328f7b77691SDoug Rabson 		kobj_class_compile(cls);
32946ba7a35SDoug Rabson 		goto retry;
33046ba7a35SDoug Rabson 	}
331f7b77691SDoug Rabson 
332f7b77691SDoug Rabson 	obj->ops = cls->ops;
3334b4a49fdSDoug Rabson 	cls->refs++;
33446ba7a35SDoug Rabson 
33591416fb2SNathan Whitehorn 	KOBJ_UNLOCK();
336f7b77691SDoug Rabson }
337f7b77691SDoug Rabson 
338f7b77691SDoug Rabson void
339f7b77691SDoug Rabson kobj_delete(kobj_t obj, struct malloc_type *mtype)
340f7b77691SDoug Rabson {
341f7b77691SDoug Rabson 	kobj_class_t cls = obj->ops->cls;
34246ba7a35SDoug Rabson 	int refs;
343f7b77691SDoug Rabson 
344f7b77691SDoug Rabson 	/*
345f7b77691SDoug Rabson 	 * Consider freeing the compiled method table for the class
346f7b77691SDoug Rabson 	 * after its last instance is deleted. As an optimisation, we
347f7b77691SDoug Rabson 	 * should defer this for a short while to avoid thrashing.
348f7b77691SDoug Rabson 	 */
34991416fb2SNathan Whitehorn 	KOBJ_ASSERT(MA_NOTOWNED);
35091416fb2SNathan Whitehorn 	KOBJ_LOCK();
3514b4a49fdSDoug Rabson 	cls->refs--;
35246ba7a35SDoug Rabson 	refs = cls->refs;
35391416fb2SNathan Whitehorn 	KOBJ_UNLOCK();
35446ba7a35SDoug Rabson 
35546ba7a35SDoug Rabson 	if (!refs)
356f7b77691SDoug Rabson 		kobj_class_free(cls);
357f7b77691SDoug Rabson 
3582c204a16SWarner Losh 	obj->ops = NULL;
359f7b77691SDoug Rabson 	if (mtype)
360f7b77691SDoug Rabson 		free(obj, mtype);
361f7b77691SDoug Rabson }
362