1f7b77691SDoug Rabson /*- 28a36da99SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 38a36da99SPedro 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, 70b05ca429SPawel Biernacki &kobj_next_id, 0, 71b05ca429SPawel Biernacki "Number of kernel object methods registered"); 72828447e0SCameron Grant 7346ba7a35SDoug Rabson static void 7446ba7a35SDoug Rabson kobj_init_mutex(void *arg) 7546ba7a35SDoug Rabson { 76e000e001SPeter Grehan if (!kobj_mutex_inited) { 7746ba7a35SDoug Rabson mtx_init(&kobj_mtx, "kobj", NULL, MTX_DEF); 78e000e001SPeter Grehan kobj_mutex_inited = 1; 79e000e001SPeter Grehan } 8046ba7a35SDoug Rabson } 8146ba7a35SDoug Rabson 8246ba7a35SDoug Rabson SYSINIT(kobj, SI_SUB_LOCK, SI_ORDER_ANY, kobj_init_mutex, NULL); 8346ba7a35SDoug Rabson 8446ba7a35SDoug Rabson /* 8546ba7a35SDoug Rabson * This method structure is used to initialise new caches. Since the 8646ba7a35SDoug Rabson * desc pointer is NULL, it is guaranteed never to match any read 8746ba7a35SDoug Rabson * descriptors. 8846ba7a35SDoug Rabson */ 893f3f6bc3SEd Schouten static const struct kobj_method null_method = { 9046ba7a35SDoug Rabson 0, 0, 9146ba7a35SDoug Rabson }; 9246ba7a35SDoug Rabson 9346ba7a35SDoug Rabson int 94f7b77691SDoug Rabson kobj_error_method(void) 95f7b77691SDoug Rabson { 9646ba7a35SDoug Rabson 97f7b77691SDoug Rabson return ENXIO; 98f7b77691SDoug Rabson } 99f7b77691SDoug Rabson 100f7b77691SDoug Rabson static void 101f80e4547SDoug Rabson kobj_class_compile_common(kobj_class_t cls, kobj_ops_t ops) 102f7b77691SDoug Rabson { 103f7b77691SDoug Rabson kobj_method_t *m; 104f7b77691SDoug Rabson int i; 105f7b77691SDoug Rabson 106f7b77691SDoug Rabson /* 107f7b77691SDoug Rabson * Don't do anything if we are already compiled. 108f7b77691SDoug Rabson */ 109f7b77691SDoug Rabson if (cls->ops) 110f7b77691SDoug Rabson return; 111f7b77691SDoug Rabson 112f7b77691SDoug Rabson /* 113f7b77691SDoug Rabson * First register any methods which need it. 114f7b77691SDoug Rabson */ 115*1eebd615SWarner Losh for (m = cls->methods; m->desc; m++) { 116d7ecd801SMarius Strobl if (m->desc->id == 0) 117d7ecd801SMarius Strobl m->desc->id = kobj_next_id++; 118d7ecd801SMarius Strobl } 119f7b77691SDoug Rabson 120f7b77691SDoug Rabson /* 121f80e4547SDoug Rabson * Then initialise the ops table. 122f80e4547SDoug Rabson */ 12346ba7a35SDoug Rabson for (i = 0; i < KOBJ_CACHE_SIZE; i++) 12446ba7a35SDoug Rabson ops->cache[i] = &null_method; 125f80e4547SDoug Rabson ops->cls = cls; 126f80e4547SDoug Rabson cls->ops = ops; 127f80e4547SDoug Rabson } 128f80e4547SDoug Rabson 129919e7b53SMark Johnston static int 130919e7b53SMark Johnston kobj_class_compile1(kobj_class_t cls, int mflags) 131f80e4547SDoug Rabson { 132f80e4547SDoug Rabson kobj_ops_t ops; 133f80e4547SDoug Rabson 13491416fb2SNathan Whitehorn KOBJ_ASSERT(MA_NOTOWNED); 13546ba7a35SDoug Rabson 136919e7b53SMark Johnston ops = malloc(sizeof(struct kobj_ops), M_KOBJ, mflags); 137919e7b53SMark Johnston if (ops == NULL) 138919e7b53SMark Johnston return (ENOMEM); 13946ba7a35SDoug Rabson 14046ba7a35SDoug Rabson /* 14146ba7a35SDoug Rabson * We may have lost a race for kobj_class_compile here - check 14246ba7a35SDoug Rabson * to make sure someone else hasn't already compiled this 14346ba7a35SDoug Rabson * class. 14446ba7a35SDoug Rabson */ 145919e7b53SMark Johnston KOBJ_LOCK(); 14646ba7a35SDoug Rabson if (cls->ops) { 14791416fb2SNathan Whitehorn KOBJ_UNLOCK(); 14846ba7a35SDoug Rabson free(ops, M_KOBJ); 149919e7b53SMark Johnston return (0); 15046ba7a35SDoug Rabson } 151f80e4547SDoug Rabson kobj_class_compile_common(cls, ops); 15291416fb2SNathan Whitehorn KOBJ_UNLOCK(); 153919e7b53SMark Johnston return (0); 154919e7b53SMark Johnston } 155919e7b53SMark Johnston 156919e7b53SMark Johnston void 157919e7b53SMark Johnston kobj_class_compile(kobj_class_t cls) 158919e7b53SMark Johnston { 159919e7b53SMark Johnston int error; 160919e7b53SMark Johnston 161919e7b53SMark Johnston error = kobj_class_compile1(cls, M_WAITOK); 162919e7b53SMark Johnston KASSERT(error == 0, ("kobj_class_compile1 returned %d", error)); 163f80e4547SDoug Rabson } 164f80e4547SDoug Rabson 165f80e4547SDoug Rabson void 166f80e4547SDoug Rabson kobj_class_compile_static(kobj_class_t cls, kobj_ops_t ops) 167f80e4547SDoug Rabson { 16846ba7a35SDoug Rabson 169d7ecd801SMarius Strobl KASSERT(kobj_mutex_inited == 0, 170d7ecd801SMarius Strobl ("%s: only supported during early cycles", __func__)); 17146ba7a35SDoug Rabson 172f80e4547SDoug Rabson /* 173f80e4547SDoug Rabson * Increment refs to make sure that the ops table is not freed. 174f80e4547SDoug Rabson */ 175f80e4547SDoug Rabson cls->refs++; 176f80e4547SDoug Rabson kobj_class_compile_common(cls, ops); 177f7b77691SDoug Rabson } 178f7b77691SDoug Rabson 17946ba7a35SDoug Rabson static kobj_method_t* 18046ba7a35SDoug Rabson kobj_lookup_method_class(kobj_class_t cls, kobjop_desc_t desc) 18146ba7a35SDoug Rabson { 18246ba7a35SDoug Rabson kobj_method_t *methods = cls->methods; 18346ba7a35SDoug Rabson kobj_method_t *ce; 18446ba7a35SDoug Rabson 18546ba7a35SDoug Rabson for (ce = methods; ce && ce->desc; ce++) { 18646ba7a35SDoug Rabson if (ce->desc == desc) { 18746ba7a35SDoug Rabson return ce; 18846ba7a35SDoug Rabson } 18946ba7a35SDoug Rabson } 19046ba7a35SDoug Rabson 1912c204a16SWarner Losh return NULL; 19246ba7a35SDoug Rabson } 19346ba7a35SDoug Rabson 19446ba7a35SDoug Rabson static kobj_method_t* 19546ba7a35SDoug Rabson kobj_lookup_method_mi(kobj_class_t cls, 196f7b77691SDoug Rabson kobjop_desc_t desc) 197f7b77691SDoug Rabson { 19846ba7a35SDoug Rabson kobj_method_t *ce; 19946ba7a35SDoug Rabson kobj_class_t *basep; 20046ba7a35SDoug Rabson 20146ba7a35SDoug Rabson ce = kobj_lookup_method_class(cls, desc); 20246ba7a35SDoug Rabson if (ce) 20346ba7a35SDoug Rabson return ce; 20446ba7a35SDoug Rabson 20546ba7a35SDoug Rabson basep = cls->baseclasses; 20646ba7a35SDoug Rabson if (basep) { 20746ba7a35SDoug Rabson for (; *basep; basep++) { 20846ba7a35SDoug Rabson ce = kobj_lookup_method_mi(*basep, desc); 20946ba7a35SDoug Rabson if (ce) 21046ba7a35SDoug Rabson return ce; 211f7b77691SDoug Rabson } 212f7b77691SDoug Rabson } 21346ba7a35SDoug Rabson 2142c204a16SWarner Losh return NULL; 21546ba7a35SDoug Rabson } 21646ba7a35SDoug Rabson 21746ba7a35SDoug Rabson kobj_method_t* 21846ba7a35SDoug Rabson kobj_lookup_method(kobj_class_t cls, 21946ba7a35SDoug Rabson kobj_method_t **cep, 22046ba7a35SDoug Rabson kobjop_desc_t desc) 22146ba7a35SDoug Rabson { 22246ba7a35SDoug Rabson kobj_method_t *ce; 22346ba7a35SDoug Rabson 22446ba7a35SDoug Rabson ce = kobj_lookup_method_mi(cls, desc); 22546ba7a35SDoug Rabson if (!ce) 226d09ebcecSEd Schouten ce = &desc->deflt; 22726d877f5SMarius Strobl if (cep) 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 /* 246d7ecd801SMarius Strobl * For now we don't do anything to unregister any methods 247d7ecd801SMarius 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 263d7ecd801SMarius Strobl static void 264d7ecd801SMarius Strobl kobj_init_common(kobj_t obj, kobj_class_t cls) 265d7ecd801SMarius Strobl { 266d7ecd801SMarius Strobl 267d7ecd801SMarius Strobl obj->ops = cls->ops; 268d7ecd801SMarius Strobl cls->refs++; 269d7ecd801SMarius Strobl } 270d7ecd801SMarius Strobl 271919e7b53SMark Johnston static int 272919e7b53SMark Johnston kobj_init1(kobj_t obj, kobj_class_t cls, int mflags) 273f7b77691SDoug Rabson { 274919e7b53SMark Johnston int error; 27546ba7a35SDoug Rabson 276919e7b53SMark Johnston KOBJ_LOCK(); 277919e7b53SMark Johnston while (cls->ops == NULL) { 27846ba7a35SDoug Rabson /* 27946ba7a35SDoug Rabson * kobj_class_compile doesn't want the lock held 28046ba7a35SDoug Rabson * because of the call to malloc - we drop the lock 28146ba7a35SDoug Rabson * and re-try. 28246ba7a35SDoug Rabson */ 28391416fb2SNathan Whitehorn KOBJ_UNLOCK(); 284919e7b53SMark Johnston error = kobj_class_compile1(cls, mflags); 285919e7b53SMark Johnston if (error != 0) 286919e7b53SMark Johnston return (error); 287919e7b53SMark Johnston KOBJ_LOCK(); 288919e7b53SMark Johnston } 289919e7b53SMark Johnston kobj_init_common(obj, cls); 290919e7b53SMark Johnston KOBJ_UNLOCK(); 291919e7b53SMark Johnston return (0); 29246ba7a35SDoug Rabson } 293f7b77691SDoug Rabson 294919e7b53SMark Johnston kobj_t 295919e7b53SMark Johnston kobj_create(kobj_class_t cls, struct malloc_type *mtype, int mflags) 296919e7b53SMark Johnston { 297919e7b53SMark Johnston kobj_t obj; 29846ba7a35SDoug Rabson 299919e7b53SMark Johnston obj = malloc(cls->size, mtype, mflags | M_ZERO); 300919e7b53SMark Johnston if (obj == NULL) 301919e7b53SMark Johnston return (NULL); 302919e7b53SMark Johnston if (kobj_init1(obj, cls, mflags) != 0) { 303919e7b53SMark Johnston free(obj, mtype); 304919e7b53SMark Johnston return (NULL); 305919e7b53SMark Johnston } 306919e7b53SMark Johnston return (obj); 307919e7b53SMark Johnston } 308919e7b53SMark Johnston 309919e7b53SMark Johnston void 310919e7b53SMark Johnston kobj_init(kobj_t obj, kobj_class_t cls) 311919e7b53SMark Johnston { 312919e7b53SMark Johnston int error; 313919e7b53SMark Johnston 314919e7b53SMark Johnston error = kobj_init1(obj, cls, M_NOWAIT); 315919e7b53SMark Johnston if (error != 0) 316919e7b53SMark Johnston panic("kobj_init1 failed: error %d", error); 317f7b77691SDoug Rabson } 318f7b77691SDoug Rabson 319f7b77691SDoug Rabson void 320d7ecd801SMarius Strobl kobj_init_static(kobj_t obj, kobj_class_t cls) 321d7ecd801SMarius Strobl { 322d7ecd801SMarius Strobl 323d7ecd801SMarius Strobl KASSERT(kobj_mutex_inited == 0, 324d7ecd801SMarius Strobl ("%s: only supported during early cycles", __func__)); 325d7ecd801SMarius Strobl 326d7ecd801SMarius Strobl kobj_init_common(obj, cls); 327d7ecd801SMarius Strobl } 328d7ecd801SMarius Strobl 329d7ecd801SMarius Strobl void 330f7b77691SDoug Rabson kobj_delete(kobj_t obj, struct malloc_type *mtype) 331f7b77691SDoug Rabson { 332f7b77691SDoug Rabson kobj_class_t cls = obj->ops->cls; 33346ba7a35SDoug Rabson int refs; 334f7b77691SDoug Rabson 335f7b77691SDoug Rabson /* 336f7b77691SDoug Rabson * Consider freeing the compiled method table for the class 337f7b77691SDoug Rabson * after its last instance is deleted. As an optimisation, we 338f7b77691SDoug Rabson * should defer this for a short while to avoid thrashing. 339f7b77691SDoug Rabson */ 34091416fb2SNathan Whitehorn KOBJ_ASSERT(MA_NOTOWNED); 34191416fb2SNathan Whitehorn KOBJ_LOCK(); 3424b4a49fdSDoug Rabson cls->refs--; 34346ba7a35SDoug Rabson refs = cls->refs; 34491416fb2SNathan Whitehorn KOBJ_UNLOCK(); 34546ba7a35SDoug Rabson 34646ba7a35SDoug Rabson if (!refs) 347f7b77691SDoug Rabson kobj_class_free(cls); 348f7b77691SDoug Rabson 3492c204a16SWarner Losh obj->ops = NULL; 350f7b77691SDoug Rabson if (mtype) 351f7b77691SDoug Rabson free(obj, mtype); 352f7b77691SDoug Rabson } 353