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, 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 128*919e7b53SMark Johnston static int 129*919e7b53SMark Johnston kobj_class_compile1(kobj_class_t cls, int mflags) 130f80e4547SDoug Rabson { 131f80e4547SDoug Rabson kobj_ops_t ops; 132f80e4547SDoug Rabson 13391416fb2SNathan Whitehorn KOBJ_ASSERT(MA_NOTOWNED); 13446ba7a35SDoug Rabson 135*919e7b53SMark Johnston ops = malloc(sizeof(struct kobj_ops), M_KOBJ, mflags); 136*919e7b53SMark Johnston if (ops == NULL) 137*919e7b53SMark Johnston return (ENOMEM); 13846ba7a35SDoug Rabson 13946ba7a35SDoug Rabson /* 14046ba7a35SDoug Rabson * We may have lost a race for kobj_class_compile here - check 14146ba7a35SDoug Rabson * to make sure someone else hasn't already compiled this 14246ba7a35SDoug Rabson * class. 14346ba7a35SDoug Rabson */ 144*919e7b53SMark Johnston KOBJ_LOCK(); 14546ba7a35SDoug Rabson if (cls->ops) { 14691416fb2SNathan Whitehorn KOBJ_UNLOCK(); 14746ba7a35SDoug Rabson free(ops, M_KOBJ); 148*919e7b53SMark Johnston return (0); 14946ba7a35SDoug Rabson } 150f80e4547SDoug Rabson kobj_class_compile_common(cls, ops); 15191416fb2SNathan Whitehorn KOBJ_UNLOCK(); 152*919e7b53SMark Johnston return (0); 153*919e7b53SMark Johnston } 154*919e7b53SMark Johnston 155*919e7b53SMark Johnston void 156*919e7b53SMark Johnston kobj_class_compile(kobj_class_t cls) 157*919e7b53SMark Johnston { 158*919e7b53SMark Johnston int error; 159*919e7b53SMark Johnston 160*919e7b53SMark Johnston error = kobj_class_compile1(cls, M_WAITOK); 161*919e7b53SMark Johnston KASSERT(error == 0, ("kobj_class_compile1 returned %d", error)); 162f80e4547SDoug Rabson } 163f80e4547SDoug Rabson 164f80e4547SDoug Rabson void 165f80e4547SDoug Rabson kobj_class_compile_static(kobj_class_t cls, kobj_ops_t ops) 166f80e4547SDoug Rabson { 16746ba7a35SDoug Rabson 168d7ecd801SMarius Strobl KASSERT(kobj_mutex_inited == 0, 169d7ecd801SMarius Strobl ("%s: only supported during early cycles", __func__)); 17046ba7a35SDoug Rabson 171f80e4547SDoug Rabson /* 172f80e4547SDoug Rabson * Increment refs to make sure that the ops table is not freed. 173f80e4547SDoug Rabson */ 174f80e4547SDoug Rabson cls->refs++; 175f80e4547SDoug Rabson kobj_class_compile_common(cls, ops); 176f7b77691SDoug Rabson } 177f7b77691SDoug Rabson 17846ba7a35SDoug Rabson static kobj_method_t* 17946ba7a35SDoug Rabson kobj_lookup_method_class(kobj_class_t cls, kobjop_desc_t desc) 18046ba7a35SDoug Rabson { 18146ba7a35SDoug Rabson kobj_method_t *methods = cls->methods; 18246ba7a35SDoug Rabson kobj_method_t *ce; 18346ba7a35SDoug Rabson 18446ba7a35SDoug Rabson for (ce = methods; ce && ce->desc; ce++) { 18546ba7a35SDoug Rabson if (ce->desc == desc) { 18646ba7a35SDoug Rabson return ce; 18746ba7a35SDoug Rabson } 18846ba7a35SDoug Rabson } 18946ba7a35SDoug Rabson 1902c204a16SWarner Losh return NULL; 19146ba7a35SDoug Rabson } 19246ba7a35SDoug Rabson 19346ba7a35SDoug Rabson static kobj_method_t* 19446ba7a35SDoug Rabson kobj_lookup_method_mi(kobj_class_t cls, 195f7b77691SDoug Rabson kobjop_desc_t desc) 196f7b77691SDoug Rabson { 19746ba7a35SDoug Rabson kobj_method_t *ce; 19846ba7a35SDoug Rabson kobj_class_t *basep; 19946ba7a35SDoug Rabson 20046ba7a35SDoug Rabson ce = kobj_lookup_method_class(cls, desc); 20146ba7a35SDoug Rabson if (ce) 20246ba7a35SDoug Rabson return ce; 20346ba7a35SDoug Rabson 20446ba7a35SDoug Rabson basep = cls->baseclasses; 20546ba7a35SDoug Rabson if (basep) { 20646ba7a35SDoug Rabson for (; *basep; basep++) { 20746ba7a35SDoug Rabson ce = kobj_lookup_method_mi(*basep, desc); 20846ba7a35SDoug Rabson if (ce) 20946ba7a35SDoug Rabson return ce; 210f7b77691SDoug Rabson } 211f7b77691SDoug Rabson } 21246ba7a35SDoug Rabson 2132c204a16SWarner Losh return NULL; 21446ba7a35SDoug Rabson } 21546ba7a35SDoug Rabson 21646ba7a35SDoug Rabson kobj_method_t* 21746ba7a35SDoug Rabson kobj_lookup_method(kobj_class_t cls, 21846ba7a35SDoug Rabson kobj_method_t **cep, 21946ba7a35SDoug Rabson kobjop_desc_t desc) 22046ba7a35SDoug Rabson { 22146ba7a35SDoug Rabson kobj_method_t *ce; 22246ba7a35SDoug Rabson 22346ba7a35SDoug Rabson ce = kobj_lookup_method_mi(cls, desc); 22446ba7a35SDoug Rabson if (!ce) 225d09ebcecSEd Schouten ce = &desc->deflt; 22626d877f5SMarius Strobl if (cep) 22746ba7a35SDoug Rabson *cep = ce; 22846ba7a35SDoug Rabson return ce; 229f7b77691SDoug Rabson } 230f7b77691SDoug Rabson 231f7b77691SDoug Rabson void 232f7b77691SDoug Rabson kobj_class_free(kobj_class_t cls) 233f7b77691SDoug Rabson { 2342c204a16SWarner Losh void* ops = NULL; 235f7b77691SDoug Rabson 23691416fb2SNathan Whitehorn KOBJ_ASSERT(MA_NOTOWNED); 23791416fb2SNathan Whitehorn KOBJ_LOCK(); 23846ba7a35SDoug Rabson 23946ba7a35SDoug Rabson /* 24046ba7a35SDoug Rabson * Protect against a race between kobj_create and 24146ba7a35SDoug Rabson * kobj_delete. 24246ba7a35SDoug Rabson */ 24346ba7a35SDoug Rabson if (cls->refs == 0) { 244f7b77691SDoug Rabson /* 245d7ecd801SMarius Strobl * For now we don't do anything to unregister any methods 246d7ecd801SMarius Strobl * which are no longer used. 247f7b77691SDoug Rabson */ 248f7b77691SDoug Rabson 249f7b77691SDoug Rabson /* 250f7b77691SDoug Rabson * Free memory and clean up. 251f7b77691SDoug Rabson */ 25246ba7a35SDoug Rabson ops = cls->ops; 2532c204a16SWarner Losh cls->ops = NULL; 254f7b77691SDoug Rabson } 255f7b77691SDoug Rabson 25691416fb2SNathan Whitehorn KOBJ_UNLOCK(); 25746ba7a35SDoug Rabson 25846ba7a35SDoug Rabson if (ops) 25946ba7a35SDoug Rabson free(ops, M_KOBJ); 26046ba7a35SDoug Rabson } 26146ba7a35SDoug Rabson 262d7ecd801SMarius Strobl static void 263d7ecd801SMarius Strobl kobj_init_common(kobj_t obj, kobj_class_t cls) 264d7ecd801SMarius Strobl { 265d7ecd801SMarius Strobl 266d7ecd801SMarius Strobl obj->ops = cls->ops; 267d7ecd801SMarius Strobl cls->refs++; 268d7ecd801SMarius Strobl } 269d7ecd801SMarius Strobl 270*919e7b53SMark Johnston static int 271*919e7b53SMark Johnston kobj_init1(kobj_t obj, kobj_class_t cls, int mflags) 272f7b77691SDoug Rabson { 273*919e7b53SMark Johnston int error; 27446ba7a35SDoug Rabson 275*919e7b53SMark Johnston KOBJ_LOCK(); 276*919e7b53SMark Johnston while (cls->ops == NULL) { 27746ba7a35SDoug Rabson /* 27846ba7a35SDoug Rabson * kobj_class_compile doesn't want the lock held 27946ba7a35SDoug Rabson * because of the call to malloc - we drop the lock 28046ba7a35SDoug Rabson * and re-try. 28146ba7a35SDoug Rabson */ 28291416fb2SNathan Whitehorn KOBJ_UNLOCK(); 283*919e7b53SMark Johnston error = kobj_class_compile1(cls, mflags); 284*919e7b53SMark Johnston if (error != 0) 285*919e7b53SMark Johnston return (error); 286*919e7b53SMark Johnston KOBJ_LOCK(); 287*919e7b53SMark Johnston } 288*919e7b53SMark Johnston kobj_init_common(obj, cls); 289*919e7b53SMark Johnston KOBJ_UNLOCK(); 290*919e7b53SMark Johnston return (0); 29146ba7a35SDoug Rabson } 292f7b77691SDoug Rabson 293*919e7b53SMark Johnston kobj_t 294*919e7b53SMark Johnston kobj_create(kobj_class_t cls, struct malloc_type *mtype, int mflags) 295*919e7b53SMark Johnston { 296*919e7b53SMark Johnston kobj_t obj; 29746ba7a35SDoug Rabson 298*919e7b53SMark Johnston obj = malloc(cls->size, mtype, mflags | M_ZERO); 299*919e7b53SMark Johnston if (obj == NULL) 300*919e7b53SMark Johnston return (NULL); 301*919e7b53SMark Johnston if (kobj_init1(obj, cls, mflags) != 0) { 302*919e7b53SMark Johnston free(obj, mtype); 303*919e7b53SMark Johnston return (NULL); 304*919e7b53SMark Johnston } 305*919e7b53SMark Johnston return (obj); 306*919e7b53SMark Johnston } 307*919e7b53SMark Johnston 308*919e7b53SMark Johnston void 309*919e7b53SMark Johnston kobj_init(kobj_t obj, kobj_class_t cls) 310*919e7b53SMark Johnston { 311*919e7b53SMark Johnston int error; 312*919e7b53SMark Johnston 313*919e7b53SMark Johnston error = kobj_init1(obj, cls, M_NOWAIT); 314*919e7b53SMark Johnston if (error != 0) 315*919e7b53SMark Johnston panic("kobj_init1 failed: error %d", error); 316f7b77691SDoug Rabson } 317f7b77691SDoug Rabson 318f7b77691SDoug Rabson void 319d7ecd801SMarius Strobl kobj_init_static(kobj_t obj, kobj_class_t cls) 320d7ecd801SMarius Strobl { 321d7ecd801SMarius Strobl 322d7ecd801SMarius Strobl KASSERT(kobj_mutex_inited == 0, 323d7ecd801SMarius Strobl ("%s: only supported during early cycles", __func__)); 324d7ecd801SMarius Strobl 325d7ecd801SMarius Strobl kobj_init_common(obj, cls); 326d7ecd801SMarius Strobl } 327d7ecd801SMarius Strobl 328d7ecd801SMarius Strobl void 329f7b77691SDoug Rabson kobj_delete(kobj_t obj, struct malloc_type *mtype) 330f7b77691SDoug Rabson { 331f7b77691SDoug Rabson kobj_class_t cls = obj->ops->cls; 33246ba7a35SDoug Rabson int refs; 333f7b77691SDoug Rabson 334f7b77691SDoug Rabson /* 335f7b77691SDoug Rabson * Consider freeing the compiled method table for the class 336f7b77691SDoug Rabson * after its last instance is deleted. As an optimisation, we 337f7b77691SDoug Rabson * should defer this for a short while to avoid thrashing. 338f7b77691SDoug Rabson */ 33991416fb2SNathan Whitehorn KOBJ_ASSERT(MA_NOTOWNED); 34091416fb2SNathan Whitehorn KOBJ_LOCK(); 3414b4a49fdSDoug Rabson cls->refs--; 34246ba7a35SDoug Rabson refs = cls->refs; 34391416fb2SNathan Whitehorn KOBJ_UNLOCK(); 34446ba7a35SDoug Rabson 34546ba7a35SDoug Rabson if (!refs) 346f7b77691SDoug Rabson kobj_class_free(cls); 347f7b77691SDoug Rabson 3482c204a16SWarner Losh obj->ops = NULL; 349f7b77691SDoug Rabson if (mtype) 350f7b77691SDoug Rabson free(obj, mtype); 351f7b77691SDoug Rabson } 352