1 /*- 2 * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org> 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 AUTHORS 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 AUTHORS 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/kernel.h> 32 #include <sys/systm.h> 33 #include <sys/sysctl.h> 34 #include <sys/errno.h> 35 #include <sys/malloc.h> 36 #include <sys/lock.h> 37 #include <sys/mutex.h> 38 #include <sys/queue.h> 39 #include <sys/proc.h> 40 #include <sys/osd.h> 41 42 /* OSD (Object Specific Data) */ 43 44 static MALLOC_DEFINE(M_OSD, "osd", "Object Specific Data"); 45 46 static int osd_debug = 0; 47 TUNABLE_INT("debug.osd", &osd_debug); 48 SYSCTL_INT(_debug, OID_AUTO, osd, CTLFLAG_RW, &osd_debug, 0, "OSD debug level"); 49 50 #define OSD_DEBUG(...) do { \ 51 if (osd_debug) { \ 52 printf("OSD (%s:%u): ", __func__, __LINE__); \ 53 printf(__VA_ARGS__); \ 54 printf("\n"); \ 55 } \ 56 } while (0) 57 58 /* 59 * Lists of objects with OSD. 60 */ 61 static LIST_HEAD(, osd) osd_list[OSD_LAST + 1]; 62 static osd_destructor_t *osd_destructors[OSD_LAST + 1]; 63 static u_int osd_nslots[OSD_LAST + 1]; 64 static struct mtx osd_lock[OSD_LAST + 1]; 65 66 static void 67 osd_default_destructor(void *value __unused) 68 { 69 /* Do nothing. */ 70 } 71 72 int 73 osd_register(u_int type, osd_destructor_t destructor) 74 { 75 void *newptr; 76 u_int i; 77 78 KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type.")); 79 80 /* 81 * If no destructor is given, use default one. We need to use some 82 * destructor, because NULL destructor means unused slot. 83 */ 84 if (destructor == NULL) 85 destructor = osd_default_destructor; 86 87 mtx_lock(&osd_lock[type]); 88 /* 89 * First, we try to find unused slot. 90 */ 91 for (i = 0; i < osd_nslots[type]; i++) { 92 if (osd_destructors[type][i] == NULL) { 93 OSD_DEBUG("Unused slot found (type=%u, slot=%u).", 94 type, i); 95 break; 96 } 97 } 98 /* 99 * If no unused slot was found, allocate one. 100 */ 101 if (i == osd_nslots[type]) { 102 osd_nslots[type]++; 103 newptr = realloc(osd_destructors[type], 104 sizeof(osd_destructor_t) * osd_nslots[type], M_OSD, 105 M_NOWAIT | M_ZERO); 106 if (newptr == NULL) { 107 mtx_unlock(&osd_lock[type]); 108 return (0); 109 } 110 osd_destructors[type] = newptr; 111 OSD_DEBUG("New slot allocated (type=%u, slot=%u).", 112 type, i + 1); 113 } 114 osd_destructors[type][i] = destructor; 115 mtx_unlock(&osd_lock[type]); 116 return (i + 1); 117 } 118 119 void 120 osd_deregister(u_int type, u_int slot) 121 { 122 struct osd *osd, *tosd; 123 124 KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type.")); 125 KASSERT(slot > 0, ("Invalid slot.")); 126 KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot.")); 127 128 mtx_lock(&osd_lock[type]); 129 /* 130 * Free all OSD for the given slot. 131 */ 132 LIST_FOREACH_SAFE(osd, &osd_list[type], osd_next, tosd) { 133 osd_del(type, osd, slot); 134 } 135 /* 136 * Set destructor to NULL to free the slot. 137 */ 138 osd_destructors[type][slot - 1] = NULL; 139 if (slot == osd_nslots[type]) { 140 osd_nslots[type]--; 141 osd_destructors[type] = realloc(osd_destructors[type], 142 sizeof(osd_destructor_t) * osd_nslots[type], M_OSD, 143 M_NOWAIT | M_ZERO); 144 /* 145 * We always reallocate to smaller size, so we assume it will 146 * always succeed. 147 */ 148 KASSERT(osd_destructors[type] != NULL, ("realloc() failed")); 149 OSD_DEBUG("Deregistration of the last slot (type=%u, slot=%u).", 150 type, slot); 151 } else { 152 OSD_DEBUG("Slot deregistration (type=%u, slot=%u).", 153 type, slot); 154 } 155 mtx_unlock(&osd_lock[type]); 156 } 157 158 int 159 osd_set(u_int type, struct osd *osd, u_int slot, void *value) 160 { 161 162 KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type.")); 163 KASSERT(slot > 0, ("Invalid slot.")); 164 KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot.")); 165 166 if (osd->osd_nslots == 0) { 167 /* 168 * First OSD for this object, so we need to allocate space and 169 * put it onto the list. 170 */ 171 osd->osd_slots = malloc(sizeof(void *) * slot, M_OSD, 172 M_NOWAIT | M_ZERO); 173 if (osd->osd_slots == NULL) 174 return (ENOMEM); 175 osd->osd_nslots = slot; 176 mtx_lock(&osd_lock[type]); 177 LIST_INSERT_HEAD(&osd_list[type], osd, osd_next); 178 mtx_unlock(&osd_lock[type]); 179 OSD_DEBUG("Setting first slot (type=%u).", type); 180 } else if (slot > osd->osd_nslots) { 181 void *newptr; 182 183 /* 184 * Too few slots allocated here, needs to extend the array. 185 */ 186 newptr = realloc(osd->osd_slots, sizeof(void *) * slot, M_OSD, 187 M_NOWAIT | M_ZERO); 188 if (newptr == NULL) 189 return (ENOMEM); 190 osd->osd_slots = newptr; 191 osd->osd_nslots = slot; 192 OSD_DEBUG("Growing slots array (type=%u).", type); 193 } 194 OSD_DEBUG("Setting slot value (type=%u, slot=%u, value=%p).", type, 195 slot, value); 196 osd->osd_slots[slot - 1] = value; 197 return (0); 198 } 199 200 void * 201 osd_get(u_int type, struct osd *osd, u_int slot) 202 { 203 204 KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type.")); 205 KASSERT(slot > 0, ("Invalid slot.")); 206 KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot.")); 207 208 if (slot > osd->osd_nslots) { 209 OSD_DEBUG("Slot doesn't exist (type=%u, slot=%u).", type, slot); 210 return (NULL); 211 } 212 213 OSD_DEBUG("Returning slot value (type=%u, slot=%u, value=%p).", type, 214 slot, osd->osd_slots[slot - 1]); 215 return (osd->osd_slots[slot - 1]); 216 } 217 218 void 219 osd_del(u_int type, struct osd *osd, u_int slot) 220 { 221 int i; 222 223 KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type.")); 224 KASSERT(slot > 0, ("Invalid slot.")); 225 KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot.")); 226 227 OSD_DEBUG("Deleting slot (type=%u, slot=%u).", type, slot); 228 229 if (slot > osd->osd_nslots) { 230 OSD_DEBUG("Slot doesn't exist (type=%u, slot=%u).", type, slot); 231 return; 232 } 233 osd_destructors[type][slot - 1](osd->osd_slots[slot - 1]); 234 osd->osd_slots[slot - 1] = NULL; 235 for (i = osd->osd_nslots - 1; i >= 0; i--) { 236 if (osd->osd_slots[i] != NULL) { 237 OSD_DEBUG("Slot still has a value (type=%u, slot=%u).", type, i + 1); 238 break; 239 } 240 } 241 if (i == -1) { 242 int unlock; 243 244 /* No values left for this object. */ 245 OSD_DEBUG("No more slots left (type=%u).", type); 246 if ((unlock = !mtx_owned(&osd_lock[type]))) 247 mtx_lock(&osd_lock[type]); 248 LIST_REMOVE(osd, osd_next); 249 if (unlock) 250 mtx_unlock(&osd_lock[type]); 251 free(osd->osd_slots, M_OSD); 252 osd->osd_slots = NULL; 253 osd->osd_nslots = 0; 254 } else if (slot == osd->osd_nslots) { 255 /* This was the last slot. */ 256 osd->osd_slots = realloc(osd->osd_slots, 257 sizeof(void *) * (i + 1), M_OSD, M_NOWAIT | M_ZERO); 258 /* 259 * We always reallocate to smaller size, so we assume it will 260 * always succeed. 261 */ 262 KASSERT(osd->osd_slots != NULL, ("realloc() failed")); 263 osd->osd_nslots = i + 1; 264 OSD_DEBUG("Reducing slots array to %u (type=%u).", 265 osd->osd_nslots, type); 266 } 267 } 268 269 void 270 osd_exit(u_int type, struct osd *osd) 271 { 272 u_int i; 273 274 KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type.")); 275 276 if (osd->osd_nslots == 0) { 277 KASSERT(osd->osd_slots == NULL, ("Non-null osd_slots.")); 278 /* No OSD attached, just leave. */ 279 return; 280 } 281 282 mtx_lock(&osd_lock[type]); 283 for (i = 1; i <= osd->osd_nslots; i++) 284 osd_del(type, osd, i); 285 mtx_unlock(&osd_lock[type]); 286 OSD_DEBUG("Object exit (type=%u).", type); 287 } 288 289 static void 290 osd_init(void *arg __unused) 291 { 292 u_int i; 293 294 for (i = OSD_FIRST; i <= OSD_LAST; i++) { 295 osd_nslots[i] = 0; 296 LIST_INIT(&osd_list[i]); 297 mtx_init(&osd_lock[i], "osd", NULL, MTX_DEF); 298 osd_destructors[i] = NULL; 299 } 300 } 301 SYSINIT(osd, SI_SUB_LOCK, SI_ORDER_ANY, osd_init, NULL); 302