1 /*- 2 * Copyright (c) 2000 Doug Rabson 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: src/sys/kern/subr_kobj.c,v 1.4.2.1 2001/02/02 19:49:13 cg Exp $ 27 */ 28 29 #include <sys/param.h> 30 #include <sys/queue.h> 31 #include <sys/malloc.h> 32 #include <sys/kernel.h> 33 #include <sys/module.h> 34 #include <sys/errno.h> 35 #include <sys/thread.h> 36 #include <sys/thread2.h> 37 #ifndef TEST 38 #include <sys/systm.h> 39 #endif 40 #include <sys/kobj.h> 41 42 #ifdef TEST 43 #include "usertest.h" 44 #endif 45 46 static MALLOC_DEFINE(M_KOBJ, "kobj", "Kernel object structures"); 47 48 static struct lwkt_token kobj_token; 49 static int kobj_next_id = 1; 50 51 static void 52 kobj_init_token(void *arg) 53 { 54 lwkt_token_init(&kobj_token, "kobj"); 55 } 56 57 SYSINIT(kobj, SI_BOOT1_LOCK, SI_ORDER_ANY, kobj_init_token, NULL); 58 59 /* 60 * This method structure is used to initialise new caches. Since the 61 * desc pointer is NULL, it is guaranteed never to match any real 62 * descriptors. 63 */ 64 static const struct kobj_method null_method = { 65 0, 0, 66 }; 67 68 int 69 kobj_error_method(void) 70 { 71 return ENXIO; 72 } 73 74 static void 75 kobj_register_method(struct kobjop_desc *desc) 76 { 77 if (desc->id == 0) 78 desc->id = kobj_next_id++; 79 } 80 81 static void 82 kobj_unregister_method(struct kobjop_desc *desc) 83 { 84 } 85 86 static void 87 kobj_class_compile(kobj_class_t cls) 88 { 89 kobj_method_t *m; 90 kobj_ops_t ops; 91 int i; 92 93 /* 94 * Don't do anything if we are already compiled. 95 */ 96 if (cls->ops) 97 return; 98 99 /* 100 * Allocate space for the compiled ops table. 101 */ 102 ops = kmalloc(sizeof(struct kobj_ops), M_KOBJ, M_INTWAIT); 103 for (i = 0; i < KOBJ_CACHE_SIZE; i++) 104 ops->cache[i] = &null_method; 105 if (cls->ops) { 106 /* 107 * In case of preemption, another thread might have been faster, 108 * but that's fine for us. 109 */ 110 kfree(ops, M_KOBJ); 111 return; 112 } 113 114 ops->cls = cls; 115 cls->ops = ops; 116 117 /* 118 * Afterwards register any methods which need it. 119 */ 120 for (m = cls->methods; m->desc; m++) 121 kobj_register_method(m->desc); 122 } 123 124 static kobj_method_t * 125 kobj_lookup_method_class(kobj_class_t cls, kobjop_desc_t desc) 126 { 127 kobj_method_t *methods = cls->methods; 128 kobj_method_t *ce; 129 130 for (ce = methods; ce && ce->desc; ce++) 131 if (ce->desc == desc) 132 return(ce); 133 134 return(0); 135 } 136 137 static kobj_method_t * 138 kobj_lookup_method_mi(kobj_class_t cls, kobjop_desc_t desc) 139 { 140 kobj_method_t *ce; 141 kobj_class_t *basep; 142 143 ce = kobj_lookup_method_class(cls, desc); 144 if (ce) 145 return(ce); 146 147 basep = cls->baseclasses; 148 if (basep) { 149 for (; *basep; basep++) { 150 ce = kobj_lookup_method_mi(*basep, desc); 151 if (ce) 152 return(ce); 153 } 154 } 155 156 return(0); 157 } 158 159 kobj_method_t* 160 kobj_lookup_method(kobj_class_t cls, 161 kobj_method_t **cep, 162 kobjop_desc_t desc) 163 { 164 kobj_method_t *ce; 165 166 ce = kobj_lookup_method_mi(cls, desc); 167 if (!ce) 168 ce = &desc->deflt; 169 *cep = ce; 170 return(ce); 171 } 172 173 /* 174 * This is called from the KOBJOPLOOKUP() macro in sys/kobj.h and 175 * replaces the original large body of the macro with a single 176 * procedure call. 177 */ 178 kobjop_t 179 kobj_lookup_method_cache(kobj_class_t cls, kobj_method_t **cep, 180 kobjop_desc_t desc) 181 { 182 kobj_method_t *ce; 183 184 cep = &cep[desc->id & (KOBJ_CACHE_SIZE-1)]; 185 ce = *cep; 186 if (ce->desc != desc) 187 ce = kobj_lookup_method(cls, cep, desc); 188 return(ce->func); 189 } 190 191 static void 192 kobj_class_free(kobj_class_t cls) 193 { 194 int i; 195 kobj_method_t *m; 196 197 /* 198 * Unregister any methods which are no longer used. 199 */ 200 for (i = 0, m = cls->methods; m->desc; i++, m++) 201 kobj_unregister_method(m->desc); 202 203 /* 204 * Free memory and clean up. 205 */ 206 kfree(cls->ops, M_KOBJ); 207 cls->ops = 0; 208 } 209 210 void 211 kobj_class_instantiate(kobj_class_t cls) 212 { 213 lwkt_gettoken(&kobj_token); 214 crit_enter(); 215 216 if (!cls->ops) 217 kobj_class_compile(cls); 218 cls->refs++; 219 220 crit_exit(); 221 lwkt_reltoken(&kobj_token); 222 } 223 224 void 225 kobj_class_uninstantiate(kobj_class_t cls) 226 { 227 lwkt_gettoken(&kobj_token); 228 crit_enter(); 229 230 cls->refs--; 231 if (cls->refs == 0) 232 kobj_class_free(cls); 233 234 crit_exit(); 235 lwkt_reltoken(&kobj_token); 236 } 237 238 kobj_t 239 kobj_create(kobj_class_t cls, 240 struct malloc_type *mtype, 241 int mflags) 242 { 243 kobj_t obj; 244 245 /* 246 * Allocate and initialise the new object. 247 */ 248 obj = kmalloc(cls->size, mtype, mflags | M_ZERO); 249 if (!obj) 250 return 0; 251 kobj_init(obj, cls); 252 253 return obj; 254 } 255 256 void 257 kobj_init(kobj_t obj, kobj_class_t cls) 258 { 259 kobj_class_instantiate(cls); 260 obj->ops = cls->ops; 261 } 262 263 void 264 kobj_delete(kobj_t obj, struct malloc_type *mtype) 265 { 266 kobj_class_t cls = obj->ops->cls; 267 268 kobj_class_uninstantiate(cls); 269 270 obj->ops = 0; 271 if (mtype) 272 kfree(obj, mtype); 273 } 274