1f7b77691SDoug Rabson /*- 2*8a36da99SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3*8a36da99SPedro F. Giffuni * 446ba7a35SDoug Rabson * Copyright (c) 2000,2003 Doug Rabson 5f7b77691SDoug Rabson * All rights reserved. 6f7b77691SDoug Rabson * 7f7b77691SDoug Rabson * Redistribution and use in source and binary forms, with or without 8f7b77691SDoug Rabson * modification, are permitted provided that the following conditions 9f7b77691SDoug Rabson * are met: 10f7b77691SDoug Rabson * 1. Redistributions of source code must retain the above copyright 11f7b77691SDoug Rabson * notice, this list of conditions and the following disclaimer. 12f7b77691SDoug Rabson * 2. Redistributions in binary form must reproduce the above copyright 13f7b77691SDoug Rabson * notice, this list of conditions and the following disclaimer in the 14f7b77691SDoug Rabson * documentation and/or other materials provided with the distribution. 15f7b77691SDoug Rabson * 16f7b77691SDoug Rabson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17f7b77691SDoug Rabson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18f7b77691SDoug Rabson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19f7b77691SDoug Rabson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20f7b77691SDoug Rabson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21f7b77691SDoug Rabson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22f7b77691SDoug Rabson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23f7b77691SDoug Rabson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24f7b77691SDoug Rabson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25f7b77691SDoug Rabson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26f7b77691SDoug Rabson * SUCH DAMAGE. 27f7b77691SDoug Rabson */ 28f7b77691SDoug Rabson 29677b542eSDavid E. O'Brien #include <sys/cdefs.h> 30677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$"); 31677b542eSDavid E. O'Brien 32f7b77691SDoug Rabson #include <sys/param.h> 33f7b77691SDoug Rabson #include <sys/kernel.h> 3446ba7a35SDoug Rabson #include <sys/kobj.h> 3546ba7a35SDoug Rabson #include <sys/lock.h> 3646ba7a35SDoug Rabson #include <sys/malloc.h> 3746ba7a35SDoug Rabson #include <sys/mutex.h> 38828447e0SCameron Grant #include <sys/sysctl.h> 39f7b77691SDoug Rabson #ifndef TEST 40f7b77691SDoug Rabson #include <sys/systm.h> 41f7b77691SDoug Rabson #endif 42f7b77691SDoug Rabson 43f7b77691SDoug Rabson #ifdef TEST 44f7b77691SDoug Rabson #include "usertest.h" 45f7b77691SDoug Rabson #endif 46f7b77691SDoug Rabson 47f7b77691SDoug Rabson static MALLOC_DEFINE(M_KOBJ, "kobj", "Kernel object structures"); 48f7b77691SDoug Rabson 49f7b77691SDoug Rabson #ifdef KOBJ_STATS 50f7b77691SDoug Rabson 513316a80bSKelly Yancey u_int kobj_lookup_hits; 523316a80bSKelly Yancey u_int kobj_lookup_misses; 53f7b77691SDoug Rabson 543316a80bSKelly Yancey SYSCTL_UINT(_kern, OID_AUTO, kobj_hits, CTLFLAG_RD, 55828447e0SCameron Grant &kobj_lookup_hits, 0, ""); 563316a80bSKelly Yancey SYSCTL_UINT(_kern, OID_AUTO, kobj_misses, CTLFLAG_RD, 57828447e0SCameron Grant &kobj_lookup_misses, 0, ""); 58f7b77691SDoug Rabson 59f7b77691SDoug Rabson #endif 60f7b77691SDoug Rabson 6146ba7a35SDoug Rabson static struct mtx kobj_mtx; 62e000e001SPeter Grehan static int kobj_mutex_inited; 63f7b77691SDoug Rabson static int kobj_next_id = 1; 64f7b77691SDoug Rabson 65d7ecd801SMarius Strobl #define KOBJ_LOCK() mtx_lock(&kobj_mtx) 66d7ecd801SMarius Strobl #define KOBJ_UNLOCK() mtx_unlock(&kobj_mtx) 67d7ecd801SMarius Strobl #define KOBJ_ASSERT(what) mtx_assert(&kobj_mtx, what); 6891416fb2SNathan Whitehorn 69fbbb13f9SMatthew D Fleming SYSCTL_INT(_kern, OID_AUTO, kobj_methodcount, CTLFLAG_RD, 70828447e0SCameron Grant &kobj_next_id, 0, ""); 71828447e0SCameron Grant 7246ba7a35SDoug Rabson static void 7346ba7a35SDoug Rabson kobj_init_mutex(void *arg) 7446ba7a35SDoug Rabson { 75e000e001SPeter Grehan if (!kobj_mutex_inited) { 7646ba7a35SDoug Rabson mtx_init(&kobj_mtx, "kobj", NULL, MTX_DEF); 77e000e001SPeter Grehan kobj_mutex_inited = 1; 78e000e001SPeter Grehan } 7946ba7a35SDoug Rabson } 8046ba7a35SDoug Rabson 8146ba7a35SDoug Rabson SYSINIT(kobj, SI_SUB_LOCK, SI_ORDER_ANY, kobj_init_mutex, NULL); 8246ba7a35SDoug Rabson 8346ba7a35SDoug Rabson /* 8446ba7a35SDoug Rabson * This method structure is used to initialise new caches. Since the 8546ba7a35SDoug Rabson * desc pointer is NULL, it is guaranteed never to match any read 8646ba7a35SDoug Rabson * descriptors. 8746ba7a35SDoug Rabson */ 883f3f6bc3SEd Schouten static const struct kobj_method null_method = { 8946ba7a35SDoug Rabson 0, 0, 9046ba7a35SDoug Rabson }; 9146ba7a35SDoug Rabson 9246ba7a35SDoug Rabson int 93f7b77691SDoug Rabson kobj_error_method(void) 94f7b77691SDoug Rabson { 9546ba7a35SDoug Rabson 96f7b77691SDoug Rabson return ENXIO; 97f7b77691SDoug Rabson } 98f7b77691SDoug Rabson 99f7b77691SDoug Rabson static void 100f80e4547SDoug Rabson kobj_class_compile_common(kobj_class_t cls, kobj_ops_t ops) 101f7b77691SDoug Rabson { 102f7b77691SDoug Rabson kobj_method_t *m; 103f7b77691SDoug Rabson int i; 104f7b77691SDoug Rabson 105f7b77691SDoug Rabson /* 106f7b77691SDoug Rabson * Don't do anything if we are already compiled. 107f7b77691SDoug Rabson */ 108f7b77691SDoug Rabson if (cls->ops) 109f7b77691SDoug Rabson return; 110f7b77691SDoug Rabson 111f7b77691SDoug Rabson /* 112f7b77691SDoug Rabson * First register any methods which need it. 113f7b77691SDoug Rabson */ 114d7ecd801SMarius Strobl for (i = 0, m = cls->methods; m->desc; i++, m++) { 115d7ecd801SMarius Strobl if (m->desc->id == 0) 116d7ecd801SMarius Strobl m->desc->id = kobj_next_id++; 117d7ecd801SMarius Strobl } 118f7b77691SDoug Rabson 119f7b77691SDoug Rabson /* 120f80e4547SDoug Rabson * Then initialise the ops table. 121f80e4547SDoug Rabson */ 12246ba7a35SDoug Rabson for (i = 0; i < KOBJ_CACHE_SIZE; i++) 12346ba7a35SDoug Rabson ops->cache[i] = &null_method; 124f80e4547SDoug Rabson ops->cls = cls; 125f80e4547SDoug Rabson cls->ops = ops; 126f80e4547SDoug Rabson } 127f80e4547SDoug Rabson 128f80e4547SDoug Rabson void 129f80e4547SDoug Rabson kobj_class_compile(kobj_class_t cls) 130f80e4547SDoug Rabson { 131f80e4547SDoug Rabson kobj_ops_t ops; 132f80e4547SDoug Rabson 13391416fb2SNathan Whitehorn KOBJ_ASSERT(MA_NOTOWNED); 13446ba7a35SDoug Rabson 135f80e4547SDoug Rabson /* 136f80e4547SDoug Rabson * Allocate space for the compiled ops table. 137f7b77691SDoug Rabson */ 138f7b77691SDoug Rabson ops = malloc(sizeof(struct kobj_ops), M_KOBJ, M_NOWAIT); 139f7b77691SDoug Rabson if (!ops) 140d7ecd801SMarius Strobl panic("%s: out of memory", __func__); 14146ba7a35SDoug Rabson 14291416fb2SNathan Whitehorn KOBJ_LOCK(); 14346ba7a35SDoug Rabson 14446ba7a35SDoug Rabson /* 14546ba7a35SDoug Rabson * We may have lost a race for kobj_class_compile here - check 14646ba7a35SDoug Rabson * to make sure someone else hasn't already compiled this 14746ba7a35SDoug Rabson * class. 14846ba7a35SDoug Rabson */ 14946ba7a35SDoug Rabson if (cls->ops) { 15091416fb2SNathan Whitehorn KOBJ_UNLOCK(); 15146ba7a35SDoug Rabson free(ops, M_KOBJ); 15246ba7a35SDoug Rabson return; 15346ba7a35SDoug Rabson } 15446ba7a35SDoug Rabson 155f80e4547SDoug Rabson kobj_class_compile_common(cls, ops); 15691416fb2SNathan Whitehorn KOBJ_UNLOCK(); 157f80e4547SDoug Rabson } 158f80e4547SDoug Rabson 159f80e4547SDoug Rabson void 160f80e4547SDoug Rabson kobj_class_compile_static(kobj_class_t cls, kobj_ops_t ops) 161f80e4547SDoug Rabson { 16246ba7a35SDoug Rabson 163d7ecd801SMarius Strobl KASSERT(kobj_mutex_inited == 0, 164d7ecd801SMarius Strobl ("%s: only supported during early cycles", __func__)); 16546ba7a35SDoug Rabson 166f80e4547SDoug Rabson /* 167f80e4547SDoug Rabson * Increment refs to make sure that the ops table is not freed. 168f80e4547SDoug Rabson */ 169f80e4547SDoug Rabson cls->refs++; 170f80e4547SDoug Rabson kobj_class_compile_common(cls, ops); 171f7b77691SDoug Rabson } 172f7b77691SDoug Rabson 17346ba7a35SDoug Rabson static kobj_method_t* 17446ba7a35SDoug Rabson kobj_lookup_method_class(kobj_class_t cls, kobjop_desc_t desc) 17546ba7a35SDoug Rabson { 17646ba7a35SDoug Rabson kobj_method_t *methods = cls->methods; 17746ba7a35SDoug Rabson kobj_method_t *ce; 17846ba7a35SDoug Rabson 17946ba7a35SDoug Rabson for (ce = methods; ce && ce->desc; ce++) { 18046ba7a35SDoug Rabson if (ce->desc == desc) { 18146ba7a35SDoug Rabson return ce; 18246ba7a35SDoug Rabson } 18346ba7a35SDoug Rabson } 18446ba7a35SDoug Rabson 1852c204a16SWarner Losh return NULL; 18646ba7a35SDoug Rabson } 18746ba7a35SDoug Rabson 18846ba7a35SDoug Rabson static kobj_method_t* 18946ba7a35SDoug Rabson kobj_lookup_method_mi(kobj_class_t cls, 190f7b77691SDoug Rabson kobjop_desc_t desc) 191f7b77691SDoug Rabson { 19246ba7a35SDoug Rabson kobj_method_t *ce; 19346ba7a35SDoug Rabson kobj_class_t *basep; 19446ba7a35SDoug Rabson 19546ba7a35SDoug Rabson ce = kobj_lookup_method_class(cls, desc); 19646ba7a35SDoug Rabson if (ce) 19746ba7a35SDoug Rabson return ce; 19846ba7a35SDoug Rabson 19946ba7a35SDoug Rabson basep = cls->baseclasses; 20046ba7a35SDoug Rabson if (basep) { 20146ba7a35SDoug Rabson for (; *basep; basep++) { 20246ba7a35SDoug Rabson ce = kobj_lookup_method_mi(*basep, desc); 20346ba7a35SDoug Rabson if (ce) 20446ba7a35SDoug Rabson return ce; 205f7b77691SDoug Rabson } 206f7b77691SDoug Rabson } 20746ba7a35SDoug Rabson 2082c204a16SWarner Losh return NULL; 20946ba7a35SDoug Rabson } 21046ba7a35SDoug Rabson 21146ba7a35SDoug Rabson kobj_method_t* 21246ba7a35SDoug Rabson kobj_lookup_method(kobj_class_t cls, 21346ba7a35SDoug Rabson kobj_method_t **cep, 21446ba7a35SDoug Rabson kobjop_desc_t desc) 21546ba7a35SDoug Rabson { 21646ba7a35SDoug Rabson kobj_method_t *ce; 21746ba7a35SDoug Rabson 21846ba7a35SDoug Rabson ce = kobj_lookup_method_mi(cls, desc); 21946ba7a35SDoug Rabson if (!ce) 220d09ebcecSEd Schouten ce = &desc->deflt; 22126d877f5SMarius Strobl if (cep) 22246ba7a35SDoug Rabson *cep = ce; 22346ba7a35SDoug Rabson return ce; 224f7b77691SDoug Rabson } 225f7b77691SDoug Rabson 226f7b77691SDoug Rabson void 227f7b77691SDoug Rabson kobj_class_free(kobj_class_t cls) 228f7b77691SDoug Rabson { 2292c204a16SWarner Losh void* ops = NULL; 230f7b77691SDoug Rabson 23191416fb2SNathan Whitehorn KOBJ_ASSERT(MA_NOTOWNED); 23291416fb2SNathan Whitehorn KOBJ_LOCK(); 23346ba7a35SDoug Rabson 23446ba7a35SDoug Rabson /* 23546ba7a35SDoug Rabson * Protect against a race between kobj_create and 23646ba7a35SDoug Rabson * kobj_delete. 23746ba7a35SDoug Rabson */ 23846ba7a35SDoug Rabson if (cls->refs == 0) { 239f7b77691SDoug Rabson /* 240d7ecd801SMarius Strobl * For now we don't do anything to unregister any methods 241d7ecd801SMarius Strobl * which are no longer used. 242f7b77691SDoug Rabson */ 243f7b77691SDoug Rabson 244f7b77691SDoug Rabson /* 245f7b77691SDoug Rabson * Free memory and clean up. 246f7b77691SDoug Rabson */ 24746ba7a35SDoug Rabson ops = cls->ops; 2482c204a16SWarner Losh cls->ops = NULL; 249f7b77691SDoug Rabson } 250f7b77691SDoug Rabson 25191416fb2SNathan Whitehorn KOBJ_UNLOCK(); 25246ba7a35SDoug Rabson 25346ba7a35SDoug Rabson if (ops) 25446ba7a35SDoug Rabson free(ops, M_KOBJ); 25546ba7a35SDoug Rabson } 25646ba7a35SDoug Rabson 257f7b77691SDoug Rabson kobj_t 258f7b77691SDoug Rabson kobj_create(kobj_class_t cls, 259f7b77691SDoug Rabson struct malloc_type *mtype, 260f7b77691SDoug Rabson int mflags) 261f7b77691SDoug Rabson { 262f7b77691SDoug Rabson kobj_t obj; 263f7b77691SDoug Rabson 264f7b77691SDoug Rabson /* 265f7b77691SDoug Rabson * Allocate and initialise the new object. 266f7b77691SDoug Rabson */ 2677cc0979fSDavid Malone obj = malloc(cls->size, mtype, mflags | M_ZERO); 268f7b77691SDoug Rabson if (!obj) 2692c204a16SWarner Losh return NULL; 270f7b77691SDoug Rabson kobj_init(obj, cls); 271f7b77691SDoug Rabson 272f7b77691SDoug Rabson return obj; 273f7b77691SDoug Rabson } 274f7b77691SDoug Rabson 275d7ecd801SMarius Strobl static void 276d7ecd801SMarius Strobl kobj_init_common(kobj_t obj, kobj_class_t cls) 277d7ecd801SMarius Strobl { 278d7ecd801SMarius Strobl 279d7ecd801SMarius Strobl obj->ops = cls->ops; 280d7ecd801SMarius Strobl cls->refs++; 281d7ecd801SMarius Strobl } 282d7ecd801SMarius Strobl 283f7b77691SDoug Rabson void 284f7b77691SDoug Rabson kobj_init(kobj_t obj, kobj_class_t cls) 285f7b77691SDoug Rabson { 28691416fb2SNathan Whitehorn KOBJ_ASSERT(MA_NOTOWNED); 28746ba7a35SDoug Rabson retry: 28891416fb2SNathan Whitehorn KOBJ_LOCK(); 28946ba7a35SDoug Rabson 290f7b77691SDoug Rabson /* 291f7b77691SDoug Rabson * Consider compiling the class' method table. 292f7b77691SDoug Rabson */ 29346ba7a35SDoug Rabson if (!cls->ops) { 29446ba7a35SDoug Rabson /* 29546ba7a35SDoug Rabson * kobj_class_compile doesn't want the lock held 29646ba7a35SDoug Rabson * because of the call to malloc - we drop the lock 29746ba7a35SDoug Rabson * and re-try. 29846ba7a35SDoug Rabson */ 29991416fb2SNathan Whitehorn KOBJ_UNLOCK(); 300f7b77691SDoug Rabson kobj_class_compile(cls); 30146ba7a35SDoug Rabson goto retry; 30246ba7a35SDoug Rabson } 303f7b77691SDoug Rabson 304d7ecd801SMarius Strobl kobj_init_common(obj, cls); 30546ba7a35SDoug Rabson 30691416fb2SNathan Whitehorn KOBJ_UNLOCK(); 307f7b77691SDoug Rabson } 308f7b77691SDoug Rabson 309f7b77691SDoug Rabson void 310d7ecd801SMarius Strobl kobj_init_static(kobj_t obj, kobj_class_t cls) 311d7ecd801SMarius Strobl { 312d7ecd801SMarius Strobl 313d7ecd801SMarius Strobl KASSERT(kobj_mutex_inited == 0, 314d7ecd801SMarius Strobl ("%s: only supported during early cycles", __func__)); 315d7ecd801SMarius Strobl 316d7ecd801SMarius Strobl kobj_init_common(obj, cls); 317d7ecd801SMarius Strobl } 318d7ecd801SMarius Strobl 319d7ecd801SMarius Strobl void 320f7b77691SDoug Rabson kobj_delete(kobj_t obj, struct malloc_type *mtype) 321f7b77691SDoug Rabson { 322f7b77691SDoug Rabson kobj_class_t cls = obj->ops->cls; 32346ba7a35SDoug Rabson int refs; 324f7b77691SDoug Rabson 325f7b77691SDoug Rabson /* 326f7b77691SDoug Rabson * Consider freeing the compiled method table for the class 327f7b77691SDoug Rabson * after its last instance is deleted. As an optimisation, we 328f7b77691SDoug Rabson * should defer this for a short while to avoid thrashing. 329f7b77691SDoug Rabson */ 33091416fb2SNathan Whitehorn KOBJ_ASSERT(MA_NOTOWNED); 33191416fb2SNathan Whitehorn KOBJ_LOCK(); 3324b4a49fdSDoug Rabson cls->refs--; 33346ba7a35SDoug Rabson refs = cls->refs; 33491416fb2SNathan Whitehorn KOBJ_UNLOCK(); 33546ba7a35SDoug Rabson 33646ba7a35SDoug Rabson if (!refs) 337f7b77691SDoug Rabson kobj_class_free(cls); 338f7b77691SDoug Rabson 3392c204a16SWarner Losh obj->ops = NULL; 340f7b77691SDoug Rabson if (mtype) 341f7b77691SDoug Rabson free(obj, mtype); 342f7b77691SDoug Rabson } 343