1 /* $NetBSD: subr_specificdata.c,v 1.13 2008/04/28 20:24:04 martin Exp $ */ 2 3 /*- 4 * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /*- 33 * Copyright (c) 2006 YAMAMOTO Takashi. 34 * All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 45 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 48 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 55 * SUCH DAMAGE. 56 */ 57 58 #include <sys/cdefs.h> 59 __KERNEL_RCSID(0, "$NetBSD: subr_specificdata.c,v 1.13 2008/04/28 20:24:04 martin Exp $"); 60 61 #include <sys/param.h> 62 #include <sys/kmem.h> 63 #include <sys/specificdata.h> 64 #include <sys/queue.h> 65 #include <sys/mutex.h> 66 67 /* 68 * Locking notes: 69 * 70 * The specdataref_container pointer in the specificdata_reference 71 * is volatile. To read it, you must hold EITHER the domain lock 72 * or the ref lock. To write it, you must hold BOTH the domain lock 73 * and the ref lock. The locks must be acquired in the following 74 * order: 75 * domain -> ref 76 */ 77 78 typedef struct { 79 specificdata_dtor_t ski_dtor; 80 } specificdata_key_impl; 81 82 struct specificdata_container { 83 size_t sc_nkey; 84 LIST_ENTRY(specificdata_container) sc_list; 85 void * sc_data[]; /* variable length */ 86 }; 87 88 #define SPECIFICDATA_CONTAINER_BYTESIZE(n) \ 89 (sizeof(struct specificdata_container) + ((n) * sizeof(void *))) 90 91 struct specificdata_domain { 92 kmutex_t sd_lock; 93 unsigned int sd_nkey; 94 LIST_HEAD(, specificdata_container) sd_list; 95 specificdata_key_impl *sd_keys; 96 }; 97 98 static void 99 specificdata_container_link(specificdata_domain_t sd, 100 specificdata_container_t sc) 101 { 102 103 LIST_INSERT_HEAD(&sd->sd_list, sc, sc_list); 104 } 105 106 static void 107 specificdata_container_unlink(specificdata_domain_t sd, 108 specificdata_container_t sc) 109 { 110 111 LIST_REMOVE(sc, sc_list); 112 } 113 114 static void 115 specificdata_destroy_datum(specificdata_domain_t sd, 116 specificdata_container_t sc, specificdata_key_t key) 117 { 118 specificdata_dtor_t dtor; 119 void *data; 120 121 if (key >= sc->sc_nkey) 122 return; 123 124 KASSERT(key < sd->sd_nkey); 125 126 data = sc->sc_data[key]; 127 dtor = sd->sd_keys[key].ski_dtor; 128 129 if (dtor != NULL) { 130 if (data != NULL) { 131 sc->sc_data[key] = NULL; 132 (*dtor)(data); 133 } 134 } else { 135 KASSERT(data == NULL); 136 } 137 } 138 139 static void 140 specificdata_noop_dtor(void *data) 141 { 142 143 /* nothing */ 144 } 145 146 /* 147 * specificdata_domain_create -- 148 * Create a specificdata domain. 149 */ 150 specificdata_domain_t 151 specificdata_domain_create(void) 152 { 153 specificdata_domain_t sd; 154 155 sd = kmem_zalloc(sizeof(*sd), KM_SLEEP); 156 KASSERT(sd != NULL); 157 mutex_init(&sd->sd_lock, MUTEX_DEFAULT, IPL_NONE); 158 LIST_INIT(&sd->sd_list); 159 160 return (sd); 161 } 162 163 /* 164 * specificdata_domain_delete -- 165 * Destroy a specificdata domain. 166 */ 167 void 168 specificdata_domain_delete(specificdata_domain_t sd) 169 { 170 171 panic("specificdata_domain_delete: not implemented"); 172 } 173 174 /* 175 * specificdata_key_create -- 176 * Create a specificdata key for a domain. 177 * 178 * Note: This is a rare operation. 179 */ 180 int 181 specificdata_key_create(specificdata_domain_t sd, specificdata_key_t *keyp, 182 specificdata_dtor_t dtor) 183 { 184 specificdata_key_impl *newkeys; 185 specificdata_key_t key = 0; 186 size_t nsz; 187 188 ASSERT_SLEEPABLE(); 189 190 if (dtor == NULL) 191 dtor = specificdata_noop_dtor; 192 193 mutex_enter(&sd->sd_lock); 194 195 if (sd->sd_keys == NULL) 196 goto needalloc; 197 198 for (; key < sd->sd_nkey; key++) { 199 if (sd->sd_keys[key].ski_dtor == NULL) 200 goto gotit; 201 } 202 203 needalloc: 204 nsz = (sd->sd_nkey + 1) * sizeof(*newkeys); 205 /* XXXSMP allocating memory while holding a lock. */ 206 newkeys = kmem_zalloc(nsz, KM_SLEEP); 207 KASSERT(newkeys != NULL); 208 if (sd->sd_keys != NULL) { 209 size_t osz = sd->sd_nkey * sizeof(*newkeys); 210 memcpy(newkeys, sd->sd_keys, osz); 211 kmem_free(sd->sd_keys, osz); 212 } 213 sd->sd_keys = newkeys; 214 sd->sd_nkey++; 215 gotit: 216 sd->sd_keys[key].ski_dtor = dtor; 217 218 mutex_exit(&sd->sd_lock); 219 220 *keyp = key; 221 return (0); 222 } 223 224 /* 225 * specificdata_key_delete -- 226 * Destroy a specificdata key for a domain. 227 * 228 * Note: This is a rare operation. 229 */ 230 void 231 specificdata_key_delete(specificdata_domain_t sd, specificdata_key_t key) 232 { 233 specificdata_container_t sc; 234 235 mutex_enter(&sd->sd_lock); 236 237 if (key >= sd->sd_nkey) 238 goto out; 239 240 /* 241 * Traverse all of the specificdata containers in the domain 242 * and the destroy the datum for the dying key. 243 */ 244 LIST_FOREACH(sc, &sd->sd_list, sc_list) { 245 specificdata_destroy_datum(sd, sc, key); 246 } 247 248 sd->sd_keys[key].ski_dtor = NULL; 249 250 out: 251 mutex_exit(&sd->sd_lock); 252 } 253 254 /* 255 * specificdata_init -- 256 * Initialize a specificdata container for operation in the 257 * specified domain. 258 */ 259 int 260 specificdata_init(specificdata_domain_t sd, specificdata_reference *ref) 261 { 262 263 /* 264 * Just NULL-out the container pointer; we'll allocate the 265 * container the first time specificdata is put into it. 266 */ 267 ref->specdataref_container = NULL; 268 mutex_init(&ref->specdataref_lock, MUTEX_DEFAULT, IPL_NONE); 269 270 return (0); 271 } 272 273 /* 274 * specificdata_fini -- 275 * Destroy a specificdata container. We destroy all of the datums 276 * stuffed into the container just as if the key were destroyed. 277 */ 278 void 279 specificdata_fini(specificdata_domain_t sd, specificdata_reference *ref) 280 { 281 specificdata_container_t sc; 282 specificdata_key_t key; 283 284 ASSERT_SLEEPABLE(); 285 286 mutex_destroy(&ref->specdataref_lock); 287 288 sc = ref->specdataref_container; 289 if (sc == NULL) 290 return; 291 ref->specdataref_container = NULL; 292 293 mutex_enter(&sd->sd_lock); 294 295 specificdata_container_unlink(sd, sc); 296 for (key = 0; key < sc->sc_nkey; key++) { 297 specificdata_destroy_datum(sd, sc, key); 298 } 299 300 mutex_exit(&sd->sd_lock); 301 302 kmem_free(sc, SPECIFICDATA_CONTAINER_BYTESIZE(sc->sc_nkey)); 303 } 304 305 /* 306 * specificdata_getspecific -- 307 * Get a datum from a container. 308 */ 309 void * 310 specificdata_getspecific(specificdata_domain_t sd, specificdata_reference *ref, 311 specificdata_key_t key) 312 { 313 specificdata_container_t sc; 314 void *data = NULL; 315 316 mutex_enter(&ref->specdataref_lock); 317 318 sc = ref->specdataref_container; 319 if (sc != NULL && key < sc->sc_nkey) 320 data = sc->sc_data[key]; 321 322 mutex_exit(&ref->specdataref_lock); 323 324 return (data); 325 } 326 327 /* 328 * specificdata_getspecific_unlocked -- 329 * Get a datum from a container in a lockless fashion. 330 * 331 * Note: When using this routine, care must be taken to ensure 332 * that no other thread could cause the specificdata_reference 333 * to become invalid (i.e. point at the wrong container) by 334 * issuing a setspecific call or destroying the container. 335 */ 336 void * 337 specificdata_getspecific_unlocked(specificdata_domain_t sd, 338 specificdata_reference *ref, 339 specificdata_key_t key) 340 { 341 specificdata_container_t sc; 342 343 sc = ref->specdataref_container; 344 if (sc != NULL && key < sc->sc_nkey) 345 return (sc->sc_data[key]); 346 347 return (NULL); 348 } 349 350 /* 351 * specificdata_setspecific -- 352 * Put a datum into a container. 353 */ 354 void 355 specificdata_setspecific(specificdata_domain_t sd, 356 specificdata_reference *ref, 357 specificdata_key_t key, void *data) 358 { 359 specificdata_container_t sc, newsc; 360 size_t newnkey, sz; 361 362 ASSERT_SLEEPABLE(); 363 364 mutex_enter(&ref->specdataref_lock); 365 366 sc = ref->specdataref_container; 367 if (__predict_true(sc != NULL && key < sc->sc_nkey)) { 368 sc->sc_data[key] = data; 369 mutex_exit(&ref->specdataref_lock); 370 return; 371 } 372 373 mutex_exit(&ref->specdataref_lock); 374 375 /* 376 * Slow path: need to resize. 377 */ 378 379 mutex_enter(&sd->sd_lock); 380 newnkey = sd->sd_nkey; 381 if (key >= newnkey) { 382 mutex_exit(&sd->sd_lock); 383 panic("specificdata_setspecific"); 384 } 385 sz = SPECIFICDATA_CONTAINER_BYTESIZE(newnkey); 386 newsc = kmem_zalloc(sz, KM_SLEEP); 387 KASSERT(newsc != NULL); 388 newsc->sc_nkey = newnkey; 389 390 mutex_enter(&ref->specdataref_lock); 391 392 sc = ref->specdataref_container; 393 if (sc != NULL) { 394 if (key < sc->sc_nkey) { 395 /* 396 * Someone beat us to the punch. Unwind and put 397 * the object into the now large enough container. 398 */ 399 sc->sc_data[key] = data; 400 mutex_exit(&ref->specdataref_lock); 401 mutex_exit(&sd->sd_lock); 402 kmem_free(newsc, sz); 403 return; 404 } 405 specificdata_container_unlink(sd, sc); 406 memcpy(newsc->sc_data, sc->sc_data, 407 sc->sc_nkey * sizeof(void *)); 408 } 409 newsc->sc_data[key] = data; 410 specificdata_container_link(sd, newsc); 411 ref->specdataref_container = newsc; 412 413 mutex_exit(&ref->specdataref_lock); 414 mutex_exit(&sd->sd_lock); 415 416 if (sc != NULL) 417 kmem_free(sc, SPECIFICDATA_CONTAINER_BYTESIZE(sc->sc_nkey)); 418 } 419