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