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 63*d7ecd801SMarius Strobl #define KOBJ_LOCK() mtx_lock(&kobj_mtx) 64*d7ecd801SMarius Strobl #define KOBJ_UNLOCK() mtx_unlock(&kobj_mtx) 65*d7ecd801SMarius Strobl #define KOBJ_ASSERT(what) mtx_assert(&kobj_mtx, what); 6691416fb2SNathan Whitehorn 67fbbb13f9SMatthew D Fleming SYSCTL_INT(_kern, OID_AUTO, kobj_methodcount, CTLFLAG_RD, 68828447e0SCameron Grant &kobj_next_id, 0, ""); 69828447e0SCameron Grant 7046ba7a35SDoug Rabson static void 7146ba7a35SDoug Rabson kobj_init_mutex(void *arg) 7246ba7a35SDoug Rabson { 73e000e001SPeter Grehan if (!kobj_mutex_inited) { 7446ba7a35SDoug Rabson mtx_init(&kobj_mtx, "kobj", NULL, MTX_DEF); 75e000e001SPeter Grehan kobj_mutex_inited = 1; 76e000e001SPeter Grehan } 7746ba7a35SDoug Rabson } 7846ba7a35SDoug Rabson 7946ba7a35SDoug Rabson SYSINIT(kobj, SI_SUB_LOCK, SI_ORDER_ANY, kobj_init_mutex, NULL); 8046ba7a35SDoug Rabson 8146ba7a35SDoug Rabson /* 8246ba7a35SDoug Rabson * This method structure is used to initialise new caches. Since the 8346ba7a35SDoug Rabson * desc pointer is NULL, it is guaranteed never to match any read 8446ba7a35SDoug Rabson * descriptors. 8546ba7a35SDoug Rabson */ 863f3f6bc3SEd Schouten static const struct kobj_method null_method = { 8746ba7a35SDoug Rabson 0, 0, 8846ba7a35SDoug Rabson }; 8946ba7a35SDoug Rabson 9046ba7a35SDoug Rabson int 91f7b77691SDoug Rabson kobj_error_method(void) 92f7b77691SDoug Rabson { 9346ba7a35SDoug Rabson 94f7b77691SDoug Rabson return ENXIO; 95f7b77691SDoug Rabson } 96f7b77691SDoug Rabson 97f7b77691SDoug Rabson static void 98f80e4547SDoug Rabson kobj_class_compile_common(kobj_class_t cls, kobj_ops_t ops) 99f7b77691SDoug Rabson { 100f7b77691SDoug Rabson kobj_method_t *m; 101f7b77691SDoug Rabson int i; 102f7b77691SDoug Rabson 103f7b77691SDoug Rabson /* 104f7b77691SDoug Rabson * Don't do anything if we are already compiled. 105f7b77691SDoug Rabson */ 106f7b77691SDoug Rabson if (cls->ops) 107f7b77691SDoug Rabson return; 108f7b77691SDoug Rabson 109f7b77691SDoug Rabson /* 110f7b77691SDoug Rabson * First register any methods which need it. 111f7b77691SDoug Rabson */ 112*d7ecd801SMarius Strobl for (i = 0, m = cls->methods; m->desc; i++, m++) { 113*d7ecd801SMarius Strobl if (m->desc->id == 0) 114*d7ecd801SMarius Strobl m->desc->id = kobj_next_id++; 115*d7ecd801SMarius Strobl } 116f7b77691SDoug Rabson 117f7b77691SDoug Rabson /* 118f80e4547SDoug Rabson * Then initialise the ops table. 119f80e4547SDoug Rabson */ 12046ba7a35SDoug Rabson for (i = 0; i < KOBJ_CACHE_SIZE; i++) 12146ba7a35SDoug Rabson ops->cache[i] = &null_method; 122f80e4547SDoug Rabson ops->cls = cls; 123f80e4547SDoug Rabson cls->ops = ops; 124f80e4547SDoug Rabson } 125f80e4547SDoug Rabson 126f80e4547SDoug Rabson void 127f80e4547SDoug Rabson kobj_class_compile(kobj_class_t cls) 128f80e4547SDoug Rabson { 129f80e4547SDoug Rabson kobj_ops_t ops; 130f80e4547SDoug Rabson 13191416fb2SNathan Whitehorn KOBJ_ASSERT(MA_NOTOWNED); 13246ba7a35SDoug Rabson 133f80e4547SDoug Rabson /* 134f80e4547SDoug Rabson * Allocate space for the compiled ops table. 135f7b77691SDoug Rabson */ 136f7b77691SDoug Rabson ops = malloc(sizeof(struct kobj_ops), M_KOBJ, M_NOWAIT); 137f7b77691SDoug Rabson if (!ops) 138*d7ecd801SMarius Strobl panic("%s: out of memory", __func__); 13946ba7a35SDoug Rabson 14091416fb2SNathan Whitehorn KOBJ_LOCK(); 14146ba7a35SDoug Rabson 14246ba7a35SDoug Rabson /* 14346ba7a35SDoug Rabson * We may have lost a race for kobj_class_compile here - check 14446ba7a35SDoug Rabson * to make sure someone else hasn't already compiled this 14546ba7a35SDoug Rabson * class. 14646ba7a35SDoug Rabson */ 14746ba7a35SDoug Rabson if (cls->ops) { 14891416fb2SNathan Whitehorn KOBJ_UNLOCK(); 14946ba7a35SDoug Rabson free(ops, M_KOBJ); 15046ba7a35SDoug Rabson return; 15146ba7a35SDoug Rabson } 15246ba7a35SDoug Rabson 153f80e4547SDoug Rabson kobj_class_compile_common(cls, ops); 15491416fb2SNathan Whitehorn KOBJ_UNLOCK(); 155f80e4547SDoug Rabson } 156f80e4547SDoug Rabson 157f80e4547SDoug Rabson void 158f80e4547SDoug Rabson kobj_class_compile_static(kobj_class_t cls, kobj_ops_t ops) 159f80e4547SDoug Rabson { 16046ba7a35SDoug Rabson 161*d7ecd801SMarius Strobl KASSERT(kobj_mutex_inited == 0, 162*d7ecd801SMarius Strobl ("%s: only supported during early cycles", __func__)); 16346ba7a35SDoug Rabson 164f80e4547SDoug Rabson /* 165f80e4547SDoug Rabson * Increment refs to make sure that the ops table is not freed. 166f80e4547SDoug Rabson */ 167f80e4547SDoug Rabson cls->refs++; 168f80e4547SDoug Rabson kobj_class_compile_common(cls, ops); 169f7b77691SDoug Rabson } 170f7b77691SDoug Rabson 17146ba7a35SDoug Rabson static kobj_method_t* 17246ba7a35SDoug Rabson kobj_lookup_method_class(kobj_class_t cls, kobjop_desc_t desc) 17346ba7a35SDoug Rabson { 17446ba7a35SDoug Rabson kobj_method_t *methods = cls->methods; 17546ba7a35SDoug Rabson kobj_method_t *ce; 17646ba7a35SDoug Rabson 17746ba7a35SDoug Rabson for (ce = methods; ce && ce->desc; ce++) { 17846ba7a35SDoug Rabson if (ce->desc == desc) { 17946ba7a35SDoug Rabson return ce; 18046ba7a35SDoug Rabson } 18146ba7a35SDoug Rabson } 18246ba7a35SDoug Rabson 1832c204a16SWarner Losh return NULL; 18446ba7a35SDoug Rabson } 18546ba7a35SDoug Rabson 18646ba7a35SDoug Rabson static kobj_method_t* 18746ba7a35SDoug Rabson kobj_lookup_method_mi(kobj_class_t cls, 188f7b77691SDoug Rabson kobjop_desc_t desc) 189f7b77691SDoug Rabson { 19046ba7a35SDoug Rabson kobj_method_t *ce; 19146ba7a35SDoug Rabson kobj_class_t *basep; 19246ba7a35SDoug Rabson 19346ba7a35SDoug Rabson ce = kobj_lookup_method_class(cls, desc); 19446ba7a35SDoug Rabson if (ce) 19546ba7a35SDoug Rabson return ce; 19646ba7a35SDoug Rabson 19746ba7a35SDoug Rabson basep = cls->baseclasses; 19846ba7a35SDoug Rabson if (basep) { 19946ba7a35SDoug Rabson for (; *basep; basep++) { 20046ba7a35SDoug Rabson ce = kobj_lookup_method_mi(*basep, desc); 20146ba7a35SDoug Rabson if (ce) 20246ba7a35SDoug Rabson return ce; 203f7b77691SDoug Rabson } 204f7b77691SDoug Rabson } 20546ba7a35SDoug Rabson 2062c204a16SWarner Losh return NULL; 20746ba7a35SDoug Rabson } 20846ba7a35SDoug Rabson 20946ba7a35SDoug Rabson kobj_method_t* 21046ba7a35SDoug Rabson kobj_lookup_method(kobj_class_t cls, 21146ba7a35SDoug Rabson kobj_method_t **cep, 21246ba7a35SDoug Rabson kobjop_desc_t desc) 21346ba7a35SDoug Rabson { 21446ba7a35SDoug Rabson kobj_method_t *ce; 21546ba7a35SDoug Rabson 21646ba7a35SDoug Rabson #ifdef KOBJ_STATS 21746ba7a35SDoug Rabson /* 21846ba7a35SDoug Rabson * Correct for the 'hit' assumption in KOBJOPLOOKUP and record 21946ba7a35SDoug Rabson * a 'miss'. 22046ba7a35SDoug Rabson */ 22146ba7a35SDoug Rabson kobj_lookup_hits--; 22242b6a681SJohn Baldwin kobj_lookup_misses++; 22346ba7a35SDoug Rabson #endif 22446ba7a35SDoug Rabson 22546ba7a35SDoug Rabson ce = kobj_lookup_method_mi(cls, desc); 22646ba7a35SDoug Rabson if (!ce) 227d09ebcecSEd Schouten ce = &desc->deflt; 22846ba7a35SDoug Rabson *cep = ce; 22946ba7a35SDoug Rabson return ce; 230f7b77691SDoug Rabson } 231f7b77691SDoug Rabson 232f7b77691SDoug Rabson void 233f7b77691SDoug Rabson kobj_class_free(kobj_class_t cls) 234f7b77691SDoug Rabson { 2352c204a16SWarner Losh void* ops = NULL; 236f7b77691SDoug Rabson 23791416fb2SNathan Whitehorn KOBJ_ASSERT(MA_NOTOWNED); 23891416fb2SNathan Whitehorn KOBJ_LOCK(); 23946ba7a35SDoug Rabson 24046ba7a35SDoug Rabson /* 24146ba7a35SDoug Rabson * Protect against a race between kobj_create and 24246ba7a35SDoug Rabson * kobj_delete. 24346ba7a35SDoug Rabson */ 24446ba7a35SDoug Rabson if (cls->refs == 0) { 245f7b77691SDoug Rabson /* 246*d7ecd801SMarius Strobl * For now we don't do anything to unregister any methods 247*d7ecd801SMarius Strobl * which are no longer used. 248f7b77691SDoug Rabson */ 249f7b77691SDoug Rabson 250f7b77691SDoug Rabson /* 251f7b77691SDoug Rabson * Free memory and clean up. 252f7b77691SDoug Rabson */ 25346ba7a35SDoug Rabson ops = cls->ops; 2542c204a16SWarner Losh cls->ops = NULL; 255f7b77691SDoug Rabson } 256f7b77691SDoug Rabson 25791416fb2SNathan Whitehorn KOBJ_UNLOCK(); 25846ba7a35SDoug Rabson 25946ba7a35SDoug Rabson if (ops) 26046ba7a35SDoug Rabson free(ops, M_KOBJ); 26146ba7a35SDoug Rabson } 26246ba7a35SDoug Rabson 263f7b77691SDoug Rabson kobj_t 264f7b77691SDoug Rabson kobj_create(kobj_class_t cls, 265f7b77691SDoug Rabson struct malloc_type *mtype, 266f7b77691SDoug Rabson int mflags) 267f7b77691SDoug Rabson { 268f7b77691SDoug Rabson kobj_t obj; 269f7b77691SDoug Rabson 270f7b77691SDoug Rabson /* 271f7b77691SDoug Rabson * Allocate and initialise the new object. 272f7b77691SDoug Rabson */ 2737cc0979fSDavid Malone obj = malloc(cls->size, mtype, mflags | M_ZERO); 274f7b77691SDoug Rabson if (!obj) 2752c204a16SWarner Losh return NULL; 276f7b77691SDoug Rabson kobj_init(obj, cls); 277f7b77691SDoug Rabson 278f7b77691SDoug Rabson return obj; 279f7b77691SDoug Rabson } 280f7b77691SDoug Rabson 281*d7ecd801SMarius Strobl static void 282*d7ecd801SMarius Strobl kobj_init_common(kobj_t obj, kobj_class_t cls) 283*d7ecd801SMarius Strobl { 284*d7ecd801SMarius Strobl 285*d7ecd801SMarius Strobl obj->ops = cls->ops; 286*d7ecd801SMarius Strobl cls->refs++; 287*d7ecd801SMarius Strobl } 288*d7ecd801SMarius Strobl 289f7b77691SDoug Rabson void 290f7b77691SDoug Rabson kobj_init(kobj_t obj, kobj_class_t cls) 291f7b77691SDoug Rabson { 29291416fb2SNathan Whitehorn KOBJ_ASSERT(MA_NOTOWNED); 29346ba7a35SDoug Rabson retry: 29491416fb2SNathan Whitehorn KOBJ_LOCK(); 29546ba7a35SDoug Rabson 296f7b77691SDoug Rabson /* 297f7b77691SDoug Rabson * Consider compiling the class' method table. 298f7b77691SDoug Rabson */ 29946ba7a35SDoug Rabson if (!cls->ops) { 30046ba7a35SDoug Rabson /* 30146ba7a35SDoug Rabson * kobj_class_compile doesn't want the lock held 30246ba7a35SDoug Rabson * because of the call to malloc - we drop the lock 30346ba7a35SDoug Rabson * and re-try. 30446ba7a35SDoug Rabson */ 30591416fb2SNathan Whitehorn KOBJ_UNLOCK(); 306f7b77691SDoug Rabson kobj_class_compile(cls); 30746ba7a35SDoug Rabson goto retry; 30846ba7a35SDoug Rabson } 309f7b77691SDoug Rabson 310*d7ecd801SMarius Strobl kobj_init_common(obj, cls); 31146ba7a35SDoug Rabson 31291416fb2SNathan Whitehorn KOBJ_UNLOCK(); 313f7b77691SDoug Rabson } 314f7b77691SDoug Rabson 315f7b77691SDoug Rabson void 316*d7ecd801SMarius Strobl kobj_init_static(kobj_t obj, kobj_class_t cls) 317*d7ecd801SMarius Strobl { 318*d7ecd801SMarius Strobl 319*d7ecd801SMarius Strobl KASSERT(kobj_mutex_inited == 0, 320*d7ecd801SMarius Strobl ("%s: only supported during early cycles", __func__)); 321*d7ecd801SMarius Strobl 322*d7ecd801SMarius Strobl kobj_init_common(obj, cls); 323*d7ecd801SMarius Strobl } 324*d7ecd801SMarius Strobl 325*d7ecd801SMarius Strobl void 326f7b77691SDoug Rabson kobj_delete(kobj_t obj, struct malloc_type *mtype) 327f7b77691SDoug Rabson { 328f7b77691SDoug Rabson kobj_class_t cls = obj->ops->cls; 32946ba7a35SDoug Rabson int refs; 330f7b77691SDoug Rabson 331f7b77691SDoug Rabson /* 332f7b77691SDoug Rabson * Consider freeing the compiled method table for the class 333f7b77691SDoug Rabson * after its last instance is deleted. As an optimisation, we 334f7b77691SDoug Rabson * should defer this for a short while to avoid thrashing. 335f7b77691SDoug Rabson */ 33691416fb2SNathan Whitehorn KOBJ_ASSERT(MA_NOTOWNED); 33791416fb2SNathan Whitehorn KOBJ_LOCK(); 3384b4a49fdSDoug Rabson cls->refs--; 33946ba7a35SDoug Rabson refs = cls->refs; 34091416fb2SNathan Whitehorn KOBJ_UNLOCK(); 34146ba7a35SDoug Rabson 34246ba7a35SDoug Rabson if (!refs) 343f7b77691SDoug Rabson kobj_class_free(cls); 344f7b77691SDoug Rabson 3452c204a16SWarner Losh obj->ops = NULL; 346f7b77691SDoug Rabson if (mtype) 347f7b77691SDoug Rabson free(obj, mtype); 348f7b77691SDoug Rabson } 349