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