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 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/queue.h> 32 #include <sys/malloc.h> 33 #include <sys/kernel.h> 34 #include <sys/module.h> 35 #include <sys/errno.h> 36 #include <sys/sysctl.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 #ifdef KOBJ_STATS 49 50 u_int kobj_lookup_hits; 51 u_int kobj_lookup_misses; 52 53 SYSCTL_UINT(_kern, OID_AUTO, kobj_hits, CTLFLAG_RD, 54 &kobj_lookup_hits, 0, ""); 55 SYSCTL_UINT(_kern, OID_AUTO, kobj_misses, CTLFLAG_RD, 56 &kobj_lookup_misses, 0, ""); 57 58 #endif 59 60 static int kobj_next_id = 1; 61 62 SYSCTL_UINT(_kern, OID_AUTO, kobj_methodcount, CTLFLAG_RD, 63 &kobj_next_id, 0, ""); 64 65 static int 66 kobj_error_method(void) 67 { 68 return ENXIO; 69 } 70 71 static void 72 kobj_register_method(struct kobjop_desc *desc) 73 { 74 if (desc->id == 0) { 75 KASSERT((kobj_next_id < KOBJ_CACHE_SIZE), ("kobj method table overflow")); 76 desc->id = kobj_next_id++; 77 } 78 } 79 80 static void 81 kobj_unregister_method(struct kobjop_desc *desc) 82 { 83 } 84 85 static void 86 kobj_class_compile_common(kobj_class_t cls, kobj_ops_t ops) 87 { 88 kobj_method_t *m; 89 int i; 90 91 /* 92 * Don't do anything if we are already compiled. 93 */ 94 if (cls->ops) 95 return; 96 97 /* 98 * First register any methods which need it. 99 */ 100 for (i = 0, m = cls->methods; m->desc; i++, m++) 101 kobj_register_method(m->desc); 102 103 /* 104 * Then initialise the ops table. 105 */ 106 bzero(ops, sizeof(struct kobj_ops)); 107 ops->cls = cls; 108 cls->ops = ops; 109 } 110 111 void 112 kobj_class_compile(kobj_class_t cls) 113 { 114 kobj_ops_t ops; 115 116 /* 117 * Allocate space for the compiled ops table. 118 */ 119 ops = malloc(sizeof(struct kobj_ops), M_KOBJ, M_NOWAIT); 120 if (!ops) 121 panic("kobj_compile_methods: out of memory"); 122 kobj_class_compile_common(cls, ops); 123 } 124 125 void 126 kobj_class_compile_static(kobj_class_t cls, kobj_ops_t ops) 127 { 128 /* 129 * Increment refs to make sure that the ops table is not freed. 130 */ 131 cls->refs++; 132 kobj_class_compile_common(cls, ops); 133 } 134 135 void 136 kobj_lookup_method(kobj_method_t *methods, 137 kobj_method_t *ce, 138 kobjop_desc_t desc) 139 { 140 ce->desc = desc; 141 for (; methods && methods->desc; methods++) { 142 if (methods->desc == desc) { 143 ce->func = methods->func; 144 return; 145 } 146 } 147 if (desc->deflt) 148 ce->func = desc->deflt; 149 else 150 ce->func = kobj_error_method; 151 return; 152 } 153 154 void 155 kobj_class_free(kobj_class_t cls) 156 { 157 int i; 158 kobj_method_t *m; 159 160 /* 161 * Unregister any methods which are no longer used. 162 */ 163 for (i = 0, m = cls->methods; m->desc; i++, m++) 164 kobj_unregister_method(m->desc); 165 166 /* 167 * Free memory and clean up. 168 */ 169 free(cls->ops, M_KOBJ); 170 cls->ops = 0; 171 } 172 173 kobj_t 174 kobj_create(kobj_class_t cls, 175 struct malloc_type *mtype, 176 int mflags) 177 { 178 kobj_t obj; 179 180 /* 181 * Allocate and initialise the new object. 182 */ 183 obj = malloc(cls->size, mtype, mflags | M_ZERO); 184 if (!obj) 185 return 0; 186 kobj_init(obj, cls); 187 188 return obj; 189 } 190 191 void 192 kobj_init(kobj_t obj, kobj_class_t cls) 193 { 194 /* 195 * Consider compiling the class' method table. 196 */ 197 if (!cls->ops) 198 kobj_class_compile(cls); 199 200 obj->ops = cls->ops; 201 cls->refs++; 202 } 203 204 void 205 kobj_delete(kobj_t obj, struct malloc_type *mtype) 206 { 207 kobj_class_t cls = obj->ops->cls; 208 209 /* 210 * Consider freeing the compiled method table for the class 211 * after its last instance is deleted. As an optimisation, we 212 * should defer this for a short while to avoid thrashing. 213 */ 214 cls->refs--; 215 if (!cls->refs) 216 kobj_class_free(cls); 217 218 obj->ops = 0; 219 if (mtype) 220 free(obj, mtype); 221 } 222