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