1 /* $NetBSD: subr_specificdata.c,v 1.7 2006/11/01 10:17:59 yamt 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.7 2006/11/01 10:17:59 yamt 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 void 119 specificdata_domain_unlock(specificdata_domain_t sd) 120 { 121 122 lockmgr(&sd->sd_lock, LK_RELEASE, 0); 123 } 124 125 static void 126 specificdata_container_link(specificdata_domain_t sd, 127 specificdata_container_t sc) 128 { 129 130 LIST_INSERT_HEAD(&sd->sd_list, sc, sc_list); 131 } 132 133 static void 134 specificdata_container_unlink(specificdata_domain_t sd, 135 specificdata_container_t sc) 136 { 137 138 LIST_REMOVE(sc, sc_list); 139 } 140 141 static void 142 specificdata_destroy_datum(specificdata_domain_t sd, 143 specificdata_container_t sc, specificdata_key_t key) 144 { 145 specificdata_dtor_t dtor; 146 void *data; 147 148 if (key >= sc->sc_nkey) 149 return; 150 151 KASSERT(key < sd->sd_nkey); 152 153 data = sc->sc_data[key]; 154 dtor = sd->sd_keys[key].ski_dtor; 155 156 if (dtor != NULL) { 157 if (data != NULL) { 158 sc->sc_data[key] = NULL; 159 (*dtor)(data); 160 } 161 } else { 162 KASSERT(data == NULL); 163 } 164 } 165 166 static void 167 specificdata_noop_dtor(void *data) 168 { 169 170 /* nothing */ 171 } 172 173 /* 174 * specificdata_domain_create -- 175 * Create a specifidata domain. 176 */ 177 specificdata_domain_t 178 specificdata_domain_create(void) 179 { 180 specificdata_domain_t sd; 181 182 sd = kmem_zalloc(sizeof(*sd), KM_SLEEP); 183 KASSERT(sd != NULL); 184 lockinit(&sd->sd_lock, PLOCK, "specdata", 0, 0); 185 LIST_INIT(&sd->sd_list); 186 187 return (sd); 188 } 189 190 /* 191 * specificdata_domain_delete -- 192 * Destroy a specifidata domain. 193 */ 194 void 195 specificdata_domain_delete(specificdata_domain_t sd) 196 { 197 198 panic("specificdata_domain_delete: not implemented"); 199 } 200 201 /* 202 * specificdata_key_create -- 203 * Create a specificdata key for a domain. 204 * 205 * Note: This is a rare operation. 206 */ 207 int 208 specificdata_key_create(specificdata_domain_t sd, specificdata_key_t *keyp, 209 specificdata_dtor_t dtor) 210 { 211 specificdata_key_impl *newkeys; 212 specificdata_key_t key = 0; 213 size_t nsz; 214 215 ASSERT_SLEEPABLE(NULL, __func__); 216 217 if (dtor == NULL) 218 dtor = specificdata_noop_dtor; 219 220 specificdata_domain_lock(sd); 221 222 if (sd->sd_keys == NULL) 223 goto needalloc; 224 225 for (; key < sd->sd_nkey; key++) { 226 if (sd->sd_keys[key].ski_dtor == NULL) 227 goto gotit; 228 } 229 230 needalloc: 231 nsz = (sd->sd_nkey + 1) * sizeof(*newkeys); 232 newkeys = kmem_zalloc(nsz, KM_SLEEP); 233 KASSERT(newkeys != NULL); 234 if (sd->sd_keys != NULL) { 235 size_t osz = sd->sd_nkey * sizeof(*newkeys); 236 memcpy(newkeys, sd->sd_keys, osz); 237 kmem_free(sd->sd_keys, osz); 238 } 239 sd->sd_keys = newkeys; 240 sd->sd_nkey++; 241 gotit: 242 sd->sd_keys[key].ski_dtor = dtor; 243 244 specificdata_domain_unlock(sd); 245 246 *keyp = key; 247 return (0); 248 } 249 250 /* 251 * specificdata_key_delete -- 252 * Destroy a specificdata key for a domain. 253 * 254 * Note: This is a rare operation. 255 */ 256 void 257 specificdata_key_delete(specificdata_domain_t sd, specificdata_key_t key) 258 { 259 specificdata_container_t sc; 260 261 specificdata_domain_lock(sd); 262 263 if (key >= sd->sd_nkey) 264 goto out; 265 266 /* 267 * Traverse all of the specificdata containers in the domain 268 * and the destroy the datum for the dying key. 269 */ 270 LIST_FOREACH(sc, &sd->sd_list, sc_list) { 271 specificdata_destroy_datum(sd, sc, key); 272 } 273 274 sd->sd_keys[key].ski_dtor = NULL; 275 276 out: 277 specificdata_domain_unlock(sd); 278 } 279 280 /* 281 * specificdata_init -- 282 * Initialize a specificdata container for operation in the 283 * specified domain. 284 */ 285 int 286 specificdata_init(specificdata_domain_t sd, specificdata_reference *ref) 287 { 288 289 /* 290 * Just NULL-out the container pointer; we'll allocate the 291 * container the first time specificdata is put into it. 292 */ 293 ref->specdataref_container = NULL; 294 specdataref_lock_init(ref); 295 296 return (0); 297 } 298 299 /* 300 * specificdata_fini -- 301 * Destroy a specificdata container. We destroy all of the datums 302 * stuffed into the container just as if the key were destroyed. 303 */ 304 void 305 specificdata_fini(specificdata_domain_t sd, specificdata_reference *ref) 306 { 307 specificdata_container_t sc; 308 specificdata_key_t key; 309 310 ASSERT_SLEEPABLE(NULL, __func__); 311 312 sc = ref->specdataref_container; 313 if (sc == NULL) 314 return; 315 ref->specdataref_container = NULL; 316 317 specificdata_domain_lock(sd); 318 319 specificdata_container_unlink(sd, sc); 320 for (key = 0; key < sc->sc_nkey; key++) { 321 specificdata_destroy_datum(sd, sc, key); 322 } 323 324 specificdata_domain_unlock(sd); 325 326 kmem_free(sc, SPECIFICDATA_CONTAINER_BYTESIZE(sc->sc_nkey)); 327 } 328 329 /* 330 * specificdata_getspecific -- 331 * Get a datum from a container. 332 * 333 * Note: This routine is guaranteed not to sleep. 334 */ 335 void * 336 specificdata_getspecific(specificdata_domain_t sd, specificdata_reference *ref, 337 specificdata_key_t key) 338 { 339 specificdata_container_t sc; 340 void *data = NULL; 341 342 specdataref_lock(ref); 343 344 sc = ref->specdataref_container; 345 if (sc != NULL && key < sc->sc_nkey) 346 data = sc->sc_data[key]; 347 348 specdataref_unlock(ref); 349 350 return (data); 351 } 352 353 /* 354 * specificdata_getspecific_unlocked -- 355 * Get a datum from a container in a lockless fashion. 356 * 357 * Note: When using this routine, care must be taken to ensure 358 * that no other thread could cause the specificdata_reference 359 * to become invalid (i.e. point at the wrong container) by 360 * issuing a setspecific call or destroying the container. 361 * 362 * Note #2: This routine is guaranteed not to sleep. 363 */ 364 void * 365 specificdata_getspecific_unlocked(specificdata_domain_t sd, 366 specificdata_reference *ref, 367 specificdata_key_t key) 368 { 369 specificdata_container_t sc; 370 371 sc = ref->specdataref_container; 372 if (sc != NULL && key < sc->sc_nkey) 373 return (sc->sc_data[key]); 374 375 return (NULL); 376 } 377 378 /* 379 * specificdata_setspecific -- 380 * Put a datum into a container. 381 */ 382 void 383 specificdata_setspecific(specificdata_domain_t sd, 384 specificdata_reference *ref, 385 specificdata_key_t key, void *data) 386 { 387 specificdata_container_t sc, newsc; 388 size_t newnkey, sz; 389 390 ASSERT_SLEEPABLE(NULL, __func__); 391 392 specdataref_lock(ref); 393 394 sc = ref->specdataref_container; 395 if (__predict_true(sc != NULL && key < sc->sc_nkey)) { 396 sc->sc_data[key] = data; 397 specdataref_unlock(ref); 398 return; 399 } 400 401 specdataref_unlock(ref); 402 403 /* 404 * Slow path: need to resize. 405 */ 406 407 specificdata_domain_lock(sd); 408 newnkey = sd->sd_nkey; 409 if (key >= newnkey) { 410 specificdata_domain_unlock(sd); 411 panic("specificdata_setspecific"); 412 } 413 sz = SPECIFICDATA_CONTAINER_BYTESIZE(newnkey); 414 newsc = kmem_zalloc(sz, KM_SLEEP); 415 KASSERT(newsc != NULL); 416 newsc->sc_nkey = newnkey; 417 418 specdataref_lock(ref); 419 420 sc = ref->specdataref_container; 421 if (sc != NULL) { 422 if (key < sc->sc_nkey) { 423 /* 424 * Someone beat us to the punch. Unwind and put 425 * the object into the now large enough container. 426 */ 427 sc->sc_data[key] = data; 428 specdataref_unlock(ref); 429 specificdata_domain_unlock(sd); 430 kmem_free(newsc, sz); 431 return; 432 } 433 specificdata_container_unlink(sd, sc); 434 memcpy(newsc->sc_data, sc->sc_data, 435 sc->sc_nkey * sizeof(void *)); 436 } 437 newsc->sc_data[key] = data; 438 specificdata_container_link(sd, newsc); 439 ref->specdataref_container = newsc; 440 441 specdataref_unlock(ref); 442 specificdata_domain_unlock(sd); 443 444 if (sc != NULL) 445 kmem_free(sc, SPECIFICDATA_CONTAINER_BYTESIZE(sc->sc_nkey)); 446 } 447