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