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