subr_kobj.c revision 227343
150477Speter/*- 21817Sdg * Copyright (c) 2000,2003 Doug Rabson 31817Sdg * All rights reserved. 41541Srgrimes * 51541Srgrimes * Redistribution and use in source and binary forms, with or without 61541Srgrimes * modification, are permitted provided that the following conditions 7160798Sjhb * are met: 81541Srgrimes * 1. Redistributions of source code must retain the above copyright 9146806Srwatson * notice, this list of conditions and the following disclaimer. 10146806Srwatson * 2. Redistributions in binary form must reproduce the above copyright 11146806Srwatson * notice, this list of conditions and the following disclaimer in the 12146806Srwatson * documentation and/or other materials provided with the distribution. 13146806Srwatson * 14160798Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15160798Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1611294Sswallace * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1710905Sbde * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 181541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1910905Sbde * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2010905Sbde * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 211541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 221541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 231541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 241541Srgrimes * SUCH DAMAGE. 251541Srgrimes */ 2699855Salfred 271541Srgrimes#include <sys/cdefs.h> 281541Srgrimes__FBSDID("$FreeBSD: head/sys/kern/subr_kobj.c 227343 2011-11-08 15:38:21Z ed $"); 291541Srgrimes 3069449Salfred#include <sys/param.h> 31160797Sjhb#include <sys/kernel.h> 32160797Sjhb#include <sys/kobj.h> 33104747Srwatson#include <sys/lock.h> 34104747Srwatson#include <sys/malloc.h> 35123408Speter#include <sys/mutex.h> 36123408Speter#include <sys/sysctl.h> 371541Srgrimes#ifndef TEST 381541Srgrimes#include <sys/systm.h> 3911294Sswallace#endif 4011294Sswallace 4111294Sswallace#ifdef TEST 4211294Sswallace#include "usertest.h" 431541Srgrimes#endif 441541Srgrimes 451541Srgrimesstatic MALLOC_DEFINE(M_KOBJ, "kobj", "Kernel object structures"); 461541Srgrimes 471541Srgrimes#ifdef KOBJ_STATS 481541Srgrimes 49160798Sjhbu_int kobj_lookup_hits; 50160798Sjhbu_int kobj_lookup_misses; 51146806Srwatson 52160798SjhbSYSCTL_UINT(_kern, OID_AUTO, kobj_hits, CTLFLAG_RD, 53160798Sjhb &kobj_lookup_hits, 0, ""); 54146806SrwatsonSYSCTL_UINT(_kern, OID_AUTO, kobj_misses, CTLFLAG_RD, 55160798Sjhb &kobj_lookup_misses, 0, ""); 56146806Srwatson 57160798Sjhb#endif 5812216Sbde 5912216Sbdestatic struct mtx kobj_mtx; 6012216Sbdestatic int kobj_mutex_inited; 61160798Sjhbstatic int kobj_next_id = 1; 62160798Sjhb 63146806Srwatson/* 64146806Srwatson * In the event that kobj_mtx has not been initialized yet, 65160798Sjhb * we will ignore it, and run without locks in order to support 66160798Sjhb * use of KOBJ before mutexes are available. This early in the boot 67160798Sjhb * process, everything is single threaded and so races should not 68146806Srwatson * happen. This is used to provide the PMAP layer on PowerPC, as well 69160798Sjhb * as board support. 70160798Sjhb */ 71160798Sjhb 72160798Sjhb#define KOBJ_LOCK() if (kobj_mutex_inited) mtx_lock(&kobj_mtx); 73160798Sjhb#define KOBJ_UNLOCK() if (kobj_mutex_inited) mtx_unlock(&kobj_mtx); 74160798Sjhb#define KOBJ_ASSERT(what) if (kobj_mutex_inited) mtx_assert(&kobj_mtx,what); 75146806Srwatson 76160798SjhbSYSCTL_INT(_kern, OID_AUTO, kobj_methodcount, CTLFLAG_RD, 77146806Srwatson &kobj_next_id, 0, ""); 78160798Sjhb 79146806Srwatsonstatic void 80160798Sjhbkobj_init_mutex(void *arg) 81160798Sjhb{ 82146806Srwatson if (!kobj_mutex_inited) { 8312216Sbde mtx_init(&kobj_mtx, "kobj", NULL, MTX_DEF); 84160798Sjhb kobj_mutex_inited = 1; 85160798Sjhb } 86160798Sjhb} 87160798Sjhb 88160798SjhbSYSINIT(kobj, SI_SUB_LOCK, SI_ORDER_ANY, kobj_init_mutex, NULL); 89146806Srwatson 90160798Sjhb/* 91146806Srwatson * This method structure is used to initialise new caches. Since the 92160798Sjhb * desc pointer is NULL, it is guaranteed never to match any read 93146806Srwatson * descriptors. 94160798Sjhb */ 95146806Srwatsonstatic const struct kobj_method null_method = { 96146806Srwatson 0, 0, 97146806Srwatson}; 98160798Sjhb 99146806Srwatsonint 100146806Srwatsonkobj_error_method(void) 101160798Sjhb{ 102146806Srwatson 103146806Srwatson return ENXIO; 104160798Sjhb} 105146806Srwatson 106146806Srwatsonstatic void 107160798Sjhbkobj_register_method(struct kobjop_desc *desc) 108160798Sjhb{ 109160798Sjhb KOBJ_ASSERT(MA_OWNED); 110160798Sjhb 111160798Sjhb if (desc->id == 0) { 112160798Sjhb desc->id = kobj_next_id++; 113160798Sjhb } 114160798Sjhb} 115160798Sjhb 116160798Sjhbstatic void 117160798Sjhbkobj_unregister_method(struct kobjop_desc *desc) 118160798Sjhb{ 119146806Srwatson} 120160798Sjhb 121146806Srwatsonstatic void 122160798Sjhbkobj_class_compile_common(kobj_class_t cls, kobj_ops_t ops) 123146806Srwatson{ 124146806Srwatson kobj_method_t *m; 125160798Sjhb int i; 126160798Sjhb 12721776Sbde KOBJ_ASSERT(MA_OWNED); 12821776Sbde 12921776Sbde /* 130160798Sjhb * Don't do anything if we are already compiled. 131146806Srwatson */ 132160798Sjhb if (cls->ops) 133160798Sjhb return; 134160798Sjhb 135162373Srwatson /* 136146806Srwatson * First register any methods which need it. 137160798Sjhb */ 138146806Srwatson for (i = 0, m = cls->methods; m->desc; i++, m++) 139160798Sjhb kobj_register_method(m->desc); 140160798Sjhb 141160798Sjhb /* 142160798Sjhb * Then initialise the ops table. 143146806Srwatson */ 144160798Sjhb for (i = 0; i < KOBJ_CACHE_SIZE; i++) 145146806Srwatson ops->cache[i] = &null_method; 146160798Sjhb ops->cls = cls; 147146806Srwatson cls->ops = ops; 148160798Sjhb} 149160798Sjhb 150160798Sjhbvoid 151146806Srwatsonkobj_class_compile(kobj_class_t cls) 152146806Srwatson{ 153160798Sjhb kobj_ops_t ops; 154146806Srwatson 155160798Sjhb KOBJ_ASSERT(MA_NOTOWNED); 156146806Srwatson 157160798Sjhb /* 158146806Srwatson * Allocate space for the compiled ops table. 159146806Srwatson */ 160160798Sjhb ops = malloc(sizeof(struct kobj_ops), M_KOBJ, M_NOWAIT); 161160798Sjhb if (!ops) 162160798Sjhb panic("kobj_compile_methods: out of memory"); 163146806Srwatson 164160798Sjhb KOBJ_LOCK(); 165146806Srwatson 166160798Sjhb /* 167160798Sjhb * We may have lost a race for kobj_class_compile here - check 168146806Srwatson * to make sure someone else hasn't already compiled this 169160798Sjhb * class. 170146806Srwatson */ 171146806Srwatson if (cls->ops) { 172146806Srwatson KOBJ_UNLOCK(); 173160798Sjhb free(ops, M_KOBJ); 174146806Srwatson return; 175160798Sjhb } 176146806Srwatson 177160798Sjhb kobj_class_compile_common(cls, ops); 178146806Srwatson KOBJ_UNLOCK(); 179160798Sjhb} 180160798Sjhb 181160798Sjhbvoid 182146806Srwatsonkobj_class_compile_static(kobj_class_t cls, kobj_ops_t ops) 183160798Sjhb{ 184160798Sjhb 185160798Sjhb KOBJ_ASSERT(MA_NOTOWNED); 186146806Srwatson 187160798Sjhb /* 188146806Srwatson * Increment refs to make sure that the ops table is not freed. 189146806Srwatson */ 190160798Sjhb KOBJ_LOCK(); 191146806Srwatson 192146806Srwatson cls->refs++; 193160798Sjhb kobj_class_compile_common(cls, ops); 194160798Sjhb 195146806Srwatson KOBJ_UNLOCK(); 196160798Sjhb} 197123750Speter 19812216Sbdestatic kobj_method_t* 199160798Sjhbkobj_lookup_method_class(kobj_class_t cls, kobjop_desc_t desc) 200146806Srwatson{ 201146806Srwatson kobj_method_t *methods = cls->methods; 202160798Sjhb kobj_method_t *ce; 203160798Sjhb 204146806Srwatson for (ce = methods; ce && ce->desc; ce++) { 205160798Sjhb if (ce->desc == desc) { 206146806Srwatson return ce; 207160798Sjhb } 208146806Srwatson } 209160798Sjhb 210146806Srwatson return NULL; 211160798Sjhb} 212160798Sjhb 213146806Srwatsonstatic kobj_method_t* 214160798Sjhbkobj_lookup_method_mi(kobj_class_t cls, 215146806Srwatson kobjop_desc_t desc) 216160798Sjhb{ 217146806Srwatson kobj_method_t *ce; 218160798Sjhb kobj_class_t *basep; 219146806Srwatson 220160798Sjhb ce = kobj_lookup_method_class(cls, desc); 221146806Srwatson if (ce) 222160798Sjhb return ce; 223146806Srwatson 224160798Sjhb basep = cls->baseclasses; 225146806Srwatson if (basep) { 226160798Sjhb for (; *basep; basep++) { 227160798Sjhb ce = kobj_lookup_method_mi(*basep, desc); 228160798Sjhb if (ce) 22921776Sbde return ce; 23021776Sbde } 231160798Sjhb } 232146806Srwatson 233160798Sjhb return NULL; 234146806Srwatson} 235160798Sjhb 236146806Srwatsonkobj_method_t* 237146806Srwatsonkobj_lookup_method(kobj_class_t cls, 238160798Sjhb kobj_method_t **cep, 239146806Srwatson kobjop_desc_t desc) 240160798Sjhb{ 241146806Srwatson kobj_method_t *ce; 242160798Sjhb 243146806Srwatson#ifdef KOBJ_STATS 244146806Srwatson /* 245160798Sjhb * Correct for the 'hit' assumption in KOBJOPLOOKUP and record 246146806Srwatson * a 'miss'. 247160798Sjhb */ 248146806Srwatson kobj_lookup_hits--; 249160798Sjhb kobj_lookup_misses++; 250146806Srwatson#endif 251160798Sjhb 252160798Sjhb ce = kobj_lookup_method_mi(cls, desc); 253160798Sjhb if (!ce) 254146806Srwatson ce = desc->deflt; 255146806Srwatson *cep = ce; 256146806Srwatson return ce; 257160798Sjhb} 258160798Sjhb 259160798Sjhbvoid 260160798Sjhbkobj_class_free(kobj_class_t cls) 261160798Sjhb{ 262160798Sjhb int i; 263160798Sjhb kobj_method_t *m; 264160798Sjhb void* ops = NULL; 265146806Srwatson 266160798Sjhb KOBJ_ASSERT(MA_NOTOWNED); 267160798Sjhb KOBJ_LOCK(); 268146806Srwatson 269160798Sjhb /* 270160798Sjhb * Protect against a race between kobj_create and 271160798Sjhb * kobj_delete. 272146806Srwatson */ 273146806Srwatson if (cls->refs == 0) { 274160798Sjhb /* 275146806Srwatson * Unregister any methods which are no longer used. 276160798Sjhb */ 277146806Srwatson for (i = 0, m = cls->methods; m->desc; i++, m++) 278160798Sjhb kobj_unregister_method(m->desc); 279160798Sjhb 280160798Sjhb /* 281146806Srwatson * Free memory and clean up. 282160798Sjhb */ 283146806Srwatson ops = cls->ops; 284160798Sjhb cls->ops = NULL; 285160798Sjhb } 286160798Sjhb 287146806Srwatson KOBJ_UNLOCK(); 288160798Sjhb 289160798Sjhb if (ops) 290146806Srwatson free(ops, M_KOBJ); 291146806Srwatson} 2921541Srgrimes 2931541Srgrimeskobj_t 2941541Srgrimeskobj_create(kobj_class_t cls, 2951541Srgrimes struct malloc_type *mtype, 2961541Srgrimes int mflags) 297146806Srwatson{ 298146806Srwatson kobj_t obj; 299146806Srwatson 300146806Srwatson /* 30130740Sphk * Allocate and initialise the new object. 302161325Sjhb */ 303160798Sjhb obj = malloc(cls->size, mtype, mflags | M_ZERO); 304146806Srwatson if (!obj) 305160798Sjhb return NULL; 306146806Srwatson kobj_init(obj, cls); 307160798Sjhb 308146806Srwatson return obj; 309146806Srwatson} 310160798Sjhb 311146806Srwatsonvoid 312160798Sjhbkobj_init(kobj_t obj, kobj_class_t cls) 313146806Srwatson{ 314160798Sjhb KOBJ_ASSERT(MA_NOTOWNED); 315146806Srwatson retry: 316160798Sjhb KOBJ_LOCK(); 317146806Srwatson 318160798Sjhb /* 319161952Srwatson * Consider compiling the class' method table. 320161952Srwatson */ 321146806Srwatson if (!cls->ops) { 322146806Srwatson /* 323146806Srwatson * kobj_class_compile doesn't want the lock held 32469449Salfred * because of the call to malloc - we drop the lock 325160798Sjhb * and re-try. 326146806Srwatson */ 32769449Salfred KOBJ_UNLOCK(); 328123750Speter kobj_class_compile(cls); 329160798Sjhb goto retry; 330146806Srwatson } 33169449Salfred 332123750Speter obj->ops = cls->ops; 333160798Sjhb cls->refs++; 334146806Srwatson 335123750Speter KOBJ_UNLOCK(); 336146806Srwatson} 337160798Sjhb 338146806Srwatsonvoid 339160798Sjhbkobj_delete(kobj_t obj, struct malloc_type *mtype) 340146806Srwatson{ 341146806Srwatson kobj_class_t cls = obj->ops->cls; 342161946Srwatson int refs; 343146806Srwatson 344146806Srwatson /* 345146806Srwatson * Consider freeing the compiled method table for the class 346146806Srwatson * after its last instance is deleted. As an optimisation, we 3471541Srgrimes * should defer this for a short while to avoid thrashing. 34849428Sjkh */ 349160798Sjhb KOBJ_ASSERT(MA_NOTOWNED); 350160798Sjhb KOBJ_LOCK(); 351160798Sjhb cls->refs--; 352146806Srwatson refs = cls->refs; 353146806Srwatson KOBJ_UNLOCK(); 354146806Srwatson 355146806Srwatson if (!refs) 356160798Sjhb kobj_class_free(cls); 357160798Sjhb 358160798Sjhb obj->ops = NULL; 359160798Sjhb if (mtype) 360160798Sjhb free(obj, mtype); 361146806Srwatson} 362160798Sjhb