1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2000,2003 Doug Rabson 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/kernel.h> 31 #include <sys/kobj.h> 32 #include <sys/lock.h> 33 #include <sys/malloc.h> 34 #include <sys/mutex.h> 35 #include <sys/sysctl.h> 36 #ifndef TEST 37 #include <sys/systm.h> 38 #endif 39 40 #ifdef TEST 41 #include "usertest.h" 42 #endif 43 44 static MALLOC_DEFINE(M_KOBJ, "kobj", "Kernel object structures"); 45 46 #ifdef KOBJ_STATS 47 48 u_int kobj_lookup_hits; 49 u_int kobj_lookup_misses; 50 51 SYSCTL_UINT(_kern, OID_AUTO, kobj_hits, CTLFLAG_RD, 52 &kobj_lookup_hits, 0, ""); 53 SYSCTL_UINT(_kern, OID_AUTO, kobj_misses, CTLFLAG_RD, 54 &kobj_lookup_misses, 0, ""); 55 56 #endif 57 58 static struct mtx kobj_mtx; 59 static int kobj_mutex_inited; 60 static int kobj_next_id = 1; 61 62 #define KOBJ_LOCK() mtx_lock(&kobj_mtx) 63 #define KOBJ_UNLOCK() mtx_unlock(&kobj_mtx) 64 #define KOBJ_ASSERT(what) mtx_assert(&kobj_mtx, what); 65 66 SYSCTL_INT(_kern, OID_AUTO, kobj_methodcount, CTLFLAG_RD, 67 &kobj_next_id, 0, 68 "Number of kernel object methods registered"); 69 70 static void 71 kobj_init_mutex(void *arg) 72 { 73 if (!kobj_mutex_inited) { 74 mtx_init(&kobj_mtx, "kobj", NULL, MTX_DEF); 75 kobj_mutex_inited = 1; 76 } 77 } 78 79 SYSINIT(kobj, SI_SUB_LOCK, SI_ORDER_ANY, kobj_init_mutex, NULL); 80 81 /* 82 * This method structure is used to initialise new caches. Since the 83 * desc pointer is NULL, it is guaranteed never to match any read 84 * descriptors. 85 */ 86 static const struct kobj_method null_method = { 87 0, 0, 88 }; 89 90 int 91 kobj_error_method(void) 92 { 93 94 return ENXIO; 95 } 96 97 static inline size_t 98 kobj_data_size_roundup(size_t size) 99 { 100 101 return (roundup2((size), _Alignof(__max_align_t))); 102 } 103 104 static void 105 kobj_class_init_total_size(kobj_class_t cls) 106 { 107 kobj_class_t *bases; 108 size_t size; 109 int i; 110 111 MPASS(!cls->total_size_inited); 112 bases = cls->baseclasses; 113 114 size = kobj_data_size_roundup(cls->size); 115 if (bases != NULL) { 116 for (i = 0; bases[i] != NULL; i++) { 117 if (!bases[i]->total_size_inited) 118 kobj_class_init_total_size(bases[i]); 119 size += bases[i]->total_size; 120 } 121 } 122 cls->total_size_inited = true; 123 cls->total_size = size; 124 } 125 126 static void 127 kobj_class_compile_common(kobj_class_t cls, kobj_ops_t ops) 128 { 129 kobj_method_t *m; 130 int i; 131 132 /* 133 * Don't do anything if we are already compiled. 134 */ 135 if (cls->ops) 136 return; 137 138 /* 139 * First register any methods which need it. 140 */ 141 for (m = cls->methods; m->desc; m++) { 142 if (m->desc->id == 0) 143 m->desc->id = kobj_next_id++; 144 } 145 146 /* 147 * Then initialise the ops table. 148 */ 149 for (i = 0; i < KOBJ_CACHE_SIZE; i++) 150 ops->cache[i] = &null_method; 151 ops->cls = cls; 152 cls->ops = ops; 153 154 /* 155 * Then compute total object data size. 156 */ 157 if (!cls->total_size_inited) 158 kobj_class_init_total_size(cls); 159 } 160 161 static int 162 kobj_class_compile1(kobj_class_t cls, int mflags) 163 { 164 kobj_ops_t ops; 165 166 KOBJ_ASSERT(MA_NOTOWNED); 167 168 ops = malloc(sizeof(struct kobj_ops), M_KOBJ, mflags); 169 if (ops == NULL) 170 return (ENOMEM); 171 172 /* 173 * We may have lost a race for kobj_class_compile here - check 174 * to make sure someone else hasn't already compiled this 175 * class. 176 */ 177 KOBJ_LOCK(); 178 if (cls->ops) { 179 KOBJ_UNLOCK(); 180 free(ops, M_KOBJ); 181 return (0); 182 } 183 kobj_class_compile_common(cls, ops); 184 KOBJ_UNLOCK(); 185 return (0); 186 } 187 188 void 189 kobj_class_compile(kobj_class_t cls) 190 { 191 int error __diagused; 192 193 error = kobj_class_compile1(cls, M_WAITOK); 194 KASSERT(error == 0, ("kobj_class_compile1 returned %d", error)); 195 } 196 197 void 198 kobj_class_compile_static(kobj_class_t cls, kobj_ops_t ops) 199 { 200 201 KASSERT(kobj_mutex_inited == 0, 202 ("%s: only supported during early cycles", __func__)); 203 204 /* 205 * Increment refs to make sure that the ops table is not freed. 206 */ 207 cls->refs++; 208 kobj_class_compile_common(cls, ops); 209 } 210 211 static kobj_method_t* 212 kobj_lookup_method_class(kobj_class_t cls, kobjop_desc_t desc) 213 { 214 kobj_method_t *methods = cls->methods; 215 kobj_method_t *ce; 216 217 for (ce = methods; ce && ce->desc; ce++) { 218 if (ce->desc == desc) { 219 return ce; 220 } 221 } 222 223 return NULL; 224 } 225 226 static kobj_method_t* 227 kobj_lookup_method_mi(kobj_class_t cls, 228 kobjop_desc_t desc) 229 { 230 kobj_method_t *ce; 231 kobj_class_t *basep; 232 233 ce = kobj_lookup_method_class(cls, desc); 234 if (ce) 235 return ce; 236 237 basep = cls->baseclasses; 238 if (basep) { 239 for (; *basep; basep++) { 240 ce = kobj_lookup_method_mi(*basep, desc); 241 if (ce) 242 return ce; 243 } 244 } 245 246 return NULL; 247 } 248 249 kobj_method_t* 250 kobj_lookup_method(kobj_class_t cls, 251 kobj_method_t **cep, 252 kobjop_desc_t desc) 253 { 254 kobj_method_t *ce; 255 256 ce = kobj_lookup_method_mi(cls, desc); 257 if (!ce) 258 ce = &desc->deflt; 259 if (cep) 260 *cep = ce; 261 return ce; 262 } 263 264 void 265 kobj_class_free(kobj_class_t cls) 266 { 267 void* ops = NULL; 268 269 KOBJ_ASSERT(MA_NOTOWNED); 270 KOBJ_LOCK(); 271 272 /* 273 * Protect against a race between kobj_create and 274 * kobj_delete. 275 */ 276 if (cls->refs == 0) { 277 /* 278 * For now we don't do anything to unregister any methods 279 * which are no longer used. 280 */ 281 282 /* 283 * Free memory and clean up. 284 */ 285 ops = cls->ops; 286 cls->ops = NULL; 287 } 288 289 KOBJ_UNLOCK(); 290 291 if (ops) 292 free(ops, M_KOBJ); 293 } 294 295 static void 296 kobj_init_common(kobj_t obj, kobj_class_t cls) 297 { 298 299 obj->ops = cls->ops; 300 cls->refs++; 301 } 302 303 static int 304 kobj_init1(kobj_t obj, kobj_class_t cls, int mflags) 305 { 306 int error; 307 308 KOBJ_LOCK(); 309 while (cls->ops == NULL) { 310 /* 311 * kobj_class_compile doesn't want the lock held 312 * because of the call to malloc - we drop the lock 313 * and re-try. 314 */ 315 KOBJ_UNLOCK(); 316 error = kobj_class_compile1(cls, mflags); 317 if (error != 0) 318 return (error); 319 KOBJ_LOCK(); 320 } 321 kobj_init_common(obj, cls); 322 KOBJ_UNLOCK(); 323 return (0); 324 } 325 326 kobj_t 327 kobj_create(kobj_class_t cls, struct malloc_type *mtype, int mflags) 328 { 329 kobj_t obj; 330 331 obj = malloc(cls->size, mtype, mflags | M_ZERO); 332 if (obj == NULL) 333 return (NULL); 334 if (kobj_init1(obj, cls, mflags) != 0) { 335 free(obj, mtype); 336 return (NULL); 337 } 338 return (obj); 339 } 340 341 void 342 kobj_init(kobj_t obj, kobj_class_t cls) 343 { 344 int error; 345 346 error = kobj_init1(obj, cls, M_WAITOK); 347 if (error != 0) 348 panic("kobj_init1 failed: error %d", error); 349 } 350 351 void 352 kobj_init_static(kobj_t obj, kobj_class_t cls) 353 { 354 355 KASSERT(cls->ops != NULL, 356 ("%s: class %p is not compiled", __func__, cls)); 357 KASSERT(kobj_mutex_inited == 0, 358 ("%s: only supported during early cycles", __func__)); 359 360 kobj_init_common(obj, cls); 361 } 362 363 void 364 kobj_delete(kobj_t obj, struct malloc_type *mtype) 365 { 366 kobj_class_t cls = obj->ops->cls; 367 int refs; 368 369 /* 370 * Consider freeing the compiled method table for the class 371 * after its last instance is deleted. As an optimisation, we 372 * should defer this for a short while to avoid thrashing. 373 */ 374 KOBJ_ASSERT(MA_NOTOWNED); 375 KOBJ_LOCK(); 376 cls->refs--; 377 refs = cls->refs; 378 KOBJ_UNLOCK(); 379 380 if (!refs) 381 kobj_class_free(cls); 382 383 obj->ops = NULL; 384 if (mtype) 385 free(obj, mtype); 386 } 387 388 static inline bool 389 kobj_get_instance_offset(kobj_class_t cls, kobj_class_t subclass, size_t *offs) 390 { 391 kobj_class_t *bases; 392 size_t tmp; 393 int i; 394 395 if (cls == subclass) 396 return (true); 397 398 bases = cls->baseclasses; 399 *offs += kobj_data_size_roundup(cls->size); 400 401 if (bases == NULL || bases[0] == NULL) 402 return (false); 403 404 /* Try most common cases (offset of direct subclass) first. */ 405 tmp = *offs; 406 for (i = 0; bases[i] != NULL; i++) { 407 if (bases[i] == subclass) { 408 *offs = tmp; 409 return (true); 410 } 411 tmp += bases[i]->total_size; 412 } 413 414 /* Then do recursion for nested classes. */ 415 for (i = 0; bases[i] != NULL; i++) { 416 if (kobj_get_instance_offset(bases[i], subclass, offs)) 417 return (true); 418 } 419 return (false); 420 } 421 422 /* 423 * Get the data offset for the given class. 424 * layout is: [main | base0 | base1 | ... ] 425 */ 426 size_t 427 kobj_instance_offset(kobj_class_t cls, kobj_class_t subclass) 428 { 429 size_t offs; 430 431 if (cls == subclass) 432 return (0); 433 434 offs = 0; 435 if (!kobj_get_instance_offset(cls, subclass, &offs)) 436 panic("%s: class %s not found in hierarchy of %s", __func__, 437 subclass->name, cls->name); 438 return (offs); 439 } 440 441 /* 442 * Get the total data size of this class and all its subclasses. 443 */ 444 size_t 445 kobj_total_data_size(kobj_class_t cls) 446 { 447 448 MPASS(cls->total_size_inited); 449 450 return (cls->total_size); 451 } 452