1 1.14 chs /* $NetBSD: subr_specificdata.c,v 1.14 2017/06/01 02:45:13 chs Exp $ */ 2 1.1 thorpej 3 1.1 thorpej /*- 4 1.9 ad * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc. 5 1.1 thorpej * All rights reserved. 6 1.1 thorpej * 7 1.1 thorpej * This code is derived from software contributed to The NetBSD Foundation 8 1.1 thorpej * by Jason R. Thorpe. 9 1.1 thorpej * 10 1.1 thorpej * Redistribution and use in source and binary forms, with or without 11 1.1 thorpej * modification, are permitted provided that the following conditions 12 1.1 thorpej * are met: 13 1.1 thorpej * 1. Redistributions of source code must retain the above copyright 14 1.1 thorpej * notice, this list of conditions and the following disclaimer. 15 1.1 thorpej * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 thorpej * notice, this list of conditions and the following disclaimer in the 17 1.1 thorpej * documentation and/or other materials provided with the distribution. 18 1.1 thorpej * 19 1.1 thorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 thorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 thorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 thorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 thorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 thorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 thorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 thorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 thorpej * POSSIBILITY OF SUCH DAMAGE. 30 1.1 thorpej */ 31 1.1 thorpej 32 1.1 thorpej /*- 33 1.1 thorpej * Copyright (c) 2006 YAMAMOTO Takashi. 34 1.1 thorpej * All rights reserved. 35 1.1 thorpej * 36 1.1 thorpej * Redistribution and use in source and binary forms, with or without 37 1.1 thorpej * modification, are permitted provided that the following conditions 38 1.1 thorpej * are met: 39 1.1 thorpej * 1. Redistributions of source code must retain the above copyright 40 1.1 thorpej * notice, this list of conditions and the following disclaimer. 41 1.1 thorpej * 2. Redistributions in binary form must reproduce the above copyright 42 1.1 thorpej * notice, this list of conditions and the following disclaimer in the 43 1.1 thorpej * documentation and/or other materials provided with the distribution. 44 1.1 thorpej * 45 1.1 thorpej * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 46 1.1 thorpej * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 47 1.1 thorpej * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 48 1.1 thorpej * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 49 1.1 thorpej * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 50 1.1 thorpej * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 51 1.1 thorpej * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 1.1 thorpej * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 53 1.1 thorpej * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 54 1.1 thorpej * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 55 1.1 thorpej * SUCH DAMAGE. 56 1.1 thorpej */ 57 1.1 thorpej 58 1.1 thorpej #include <sys/cdefs.h> 59 1.14 chs __KERNEL_RCSID(0, "$NetBSD: subr_specificdata.c,v 1.14 2017/06/01 02:45:13 chs Exp $"); 60 1.1 thorpej 61 1.1 thorpej #include <sys/param.h> 62 1.1 thorpej #include <sys/kmem.h> 63 1.1 thorpej #include <sys/specificdata.h> 64 1.1 thorpej #include <sys/queue.h> 65 1.9 ad #include <sys/mutex.h> 66 1.1 thorpej 67 1.1 thorpej /* 68 1.1 thorpej * Locking notes: 69 1.1 thorpej * 70 1.1 thorpej * The specdataref_container pointer in the specificdata_reference 71 1.1 thorpej * is volatile. To read it, you must hold EITHER the domain lock 72 1.1 thorpej * or the ref lock. To write it, you must hold BOTH the domain lock 73 1.1 thorpej * and the ref lock. The locks must be acquired in the following 74 1.1 thorpej * order: 75 1.1 thorpej * domain -> ref 76 1.1 thorpej */ 77 1.1 thorpej 78 1.1 thorpej typedef struct { 79 1.1 thorpej specificdata_dtor_t ski_dtor; 80 1.1 thorpej } specificdata_key_impl; 81 1.1 thorpej 82 1.1 thorpej struct specificdata_container { 83 1.1 thorpej size_t sc_nkey; 84 1.1 thorpej LIST_ENTRY(specificdata_container) sc_list; 85 1.1 thorpej void * sc_data[]; /* variable length */ 86 1.1 thorpej }; 87 1.1 thorpej 88 1.1 thorpej #define SPECIFICDATA_CONTAINER_BYTESIZE(n) \ 89 1.1 thorpej (sizeof(struct specificdata_container) + ((n) * sizeof(void *))) 90 1.1 thorpej 91 1.1 thorpej struct specificdata_domain { 92 1.9 ad kmutex_t sd_lock; 93 1.1 thorpej unsigned int sd_nkey; 94 1.1 thorpej LIST_HEAD(, specificdata_container) sd_list; 95 1.1 thorpej specificdata_key_impl *sd_keys; 96 1.1 thorpej }; 97 1.1 thorpej 98 1.1 thorpej static void 99 1.1 thorpej specificdata_container_link(specificdata_domain_t sd, 100 1.1 thorpej specificdata_container_t sc) 101 1.1 thorpej { 102 1.1 thorpej 103 1.1 thorpej LIST_INSERT_HEAD(&sd->sd_list, sc, sc_list); 104 1.1 thorpej } 105 1.1 thorpej 106 1.1 thorpej static void 107 1.7 yamt specificdata_container_unlink(specificdata_domain_t sd, 108 1.1 thorpej specificdata_container_t sc) 109 1.1 thorpej { 110 1.1 thorpej 111 1.1 thorpej LIST_REMOVE(sc, sc_list); 112 1.1 thorpej } 113 1.1 thorpej 114 1.1 thorpej static void 115 1.1 thorpej specificdata_destroy_datum(specificdata_domain_t sd, 116 1.1 thorpej specificdata_container_t sc, specificdata_key_t key) 117 1.1 thorpej { 118 1.1 thorpej specificdata_dtor_t dtor; 119 1.1 thorpej void *data; 120 1.1 thorpej 121 1.1 thorpej if (key >= sc->sc_nkey) 122 1.1 thorpej return; 123 1.1 thorpej 124 1.1 thorpej KASSERT(key < sd->sd_nkey); 125 1.1 thorpej 126 1.1 thorpej data = sc->sc_data[key]; 127 1.1 thorpej dtor = sd->sd_keys[key].ski_dtor; 128 1.1 thorpej 129 1.1 thorpej if (dtor != NULL) { 130 1.1 thorpej if (data != NULL) { 131 1.1 thorpej sc->sc_data[key] = NULL; 132 1.1 thorpej (*dtor)(data); 133 1.1 thorpej } 134 1.1 thorpej } else { 135 1.1 thorpej KASSERT(data == NULL); 136 1.1 thorpej } 137 1.1 thorpej } 138 1.1 thorpej 139 1.1 thorpej static void 140 1.7 yamt specificdata_noop_dtor(void *data) 141 1.1 thorpej { 142 1.1 thorpej 143 1.1 thorpej /* nothing */ 144 1.1 thorpej } 145 1.1 thorpej 146 1.1 thorpej /* 147 1.1 thorpej * specificdata_domain_create -- 148 1.8 elad * Create a specificdata domain. 149 1.1 thorpej */ 150 1.1 thorpej specificdata_domain_t 151 1.1 thorpej specificdata_domain_create(void) 152 1.1 thorpej { 153 1.1 thorpej specificdata_domain_t sd; 154 1.1 thorpej 155 1.1 thorpej sd = kmem_zalloc(sizeof(*sd), KM_SLEEP); 156 1.9 ad mutex_init(&sd->sd_lock, MUTEX_DEFAULT, IPL_NONE); 157 1.1 thorpej LIST_INIT(&sd->sd_list); 158 1.1 thorpej 159 1.1 thorpej return (sd); 160 1.1 thorpej } 161 1.1 thorpej 162 1.1 thorpej /* 163 1.1 thorpej * specificdata_domain_delete -- 164 1.8 elad * Destroy a specificdata domain. 165 1.1 thorpej */ 166 1.1 thorpej void 167 1.7 yamt specificdata_domain_delete(specificdata_domain_t sd) 168 1.1 thorpej { 169 1.1 thorpej 170 1.1 thorpej panic("specificdata_domain_delete: not implemented"); 171 1.1 thorpej } 172 1.1 thorpej 173 1.1 thorpej /* 174 1.1 thorpej * specificdata_key_create -- 175 1.1 thorpej * Create a specificdata key for a domain. 176 1.1 thorpej * 177 1.1 thorpej * Note: This is a rare operation. 178 1.1 thorpej */ 179 1.1 thorpej int 180 1.1 thorpej specificdata_key_create(specificdata_domain_t sd, specificdata_key_t *keyp, 181 1.1 thorpej specificdata_dtor_t dtor) 182 1.1 thorpej { 183 1.1 thorpej specificdata_key_impl *newkeys; 184 1.1 thorpej specificdata_key_t key = 0; 185 1.1 thorpej size_t nsz; 186 1.1 thorpej 187 1.12 yamt ASSERT_SLEEPABLE(); 188 1.1 thorpej 189 1.1 thorpej if (dtor == NULL) 190 1.1 thorpej dtor = specificdata_noop_dtor; 191 1.1 thorpej 192 1.9 ad mutex_enter(&sd->sd_lock); 193 1.1 thorpej 194 1.1 thorpej if (sd->sd_keys == NULL) 195 1.1 thorpej goto needalloc; 196 1.1 thorpej 197 1.1 thorpej for (; key < sd->sd_nkey; key++) { 198 1.1 thorpej if (sd->sd_keys[key].ski_dtor == NULL) 199 1.1 thorpej goto gotit; 200 1.1 thorpej } 201 1.1 thorpej 202 1.1 thorpej needalloc: 203 1.1 thorpej nsz = (sd->sd_nkey + 1) * sizeof(*newkeys); 204 1.9 ad /* XXXSMP allocating memory while holding a lock. */ 205 1.1 thorpej newkeys = kmem_zalloc(nsz, KM_SLEEP); 206 1.1 thorpej if (sd->sd_keys != NULL) { 207 1.1 thorpej size_t osz = sd->sd_nkey * sizeof(*newkeys); 208 1.1 thorpej memcpy(newkeys, sd->sd_keys, osz); 209 1.1 thorpej kmem_free(sd->sd_keys, osz); 210 1.1 thorpej } 211 1.1 thorpej sd->sd_keys = newkeys; 212 1.1 thorpej sd->sd_nkey++; 213 1.1 thorpej gotit: 214 1.1 thorpej sd->sd_keys[key].ski_dtor = dtor; 215 1.1 thorpej 216 1.9 ad mutex_exit(&sd->sd_lock); 217 1.1 thorpej 218 1.1 thorpej *keyp = key; 219 1.1 thorpej return (0); 220 1.1 thorpej } 221 1.1 thorpej 222 1.1 thorpej /* 223 1.1 thorpej * specificdata_key_delete -- 224 1.1 thorpej * Destroy a specificdata key for a domain. 225 1.1 thorpej * 226 1.1 thorpej * Note: This is a rare operation. 227 1.1 thorpej */ 228 1.1 thorpej void 229 1.1 thorpej specificdata_key_delete(specificdata_domain_t sd, specificdata_key_t key) 230 1.1 thorpej { 231 1.1 thorpej specificdata_container_t sc; 232 1.1 thorpej 233 1.9 ad mutex_enter(&sd->sd_lock); 234 1.1 thorpej 235 1.1 thorpej if (key >= sd->sd_nkey) 236 1.1 thorpej goto out; 237 1.1 thorpej 238 1.1 thorpej /* 239 1.1 thorpej * Traverse all of the specificdata containers in the domain 240 1.1 thorpej * and the destroy the datum for the dying key. 241 1.1 thorpej */ 242 1.1 thorpej LIST_FOREACH(sc, &sd->sd_list, sc_list) { 243 1.1 thorpej specificdata_destroy_datum(sd, sc, key); 244 1.1 thorpej } 245 1.1 thorpej 246 1.1 thorpej sd->sd_keys[key].ski_dtor = NULL; 247 1.1 thorpej 248 1.1 thorpej out: 249 1.9 ad mutex_exit(&sd->sd_lock); 250 1.1 thorpej } 251 1.1 thorpej 252 1.1 thorpej /* 253 1.1 thorpej * specificdata_init -- 254 1.1 thorpej * Initialize a specificdata container for operation in the 255 1.1 thorpej * specified domain. 256 1.1 thorpej */ 257 1.1 thorpej int 258 1.7 yamt specificdata_init(specificdata_domain_t sd, specificdata_reference *ref) 259 1.1 thorpej { 260 1.1 thorpej 261 1.1 thorpej /* 262 1.1 thorpej * Just NULL-out the container pointer; we'll allocate the 263 1.1 thorpej * container the first time specificdata is put into it. 264 1.1 thorpej */ 265 1.1 thorpej ref->specdataref_container = NULL; 266 1.11 ad mutex_init(&ref->specdataref_lock, MUTEX_DEFAULT, IPL_NONE); 267 1.1 thorpej 268 1.1 thorpej return (0); 269 1.1 thorpej } 270 1.1 thorpej 271 1.1 thorpej /* 272 1.1 thorpej * specificdata_fini -- 273 1.1 thorpej * Destroy a specificdata container. We destroy all of the datums 274 1.1 thorpej * stuffed into the container just as if the key were destroyed. 275 1.1 thorpej */ 276 1.1 thorpej void 277 1.1 thorpej specificdata_fini(specificdata_domain_t sd, specificdata_reference *ref) 278 1.1 thorpej { 279 1.1 thorpej specificdata_container_t sc; 280 1.1 thorpej specificdata_key_t key; 281 1.1 thorpej 282 1.12 yamt ASSERT_SLEEPABLE(); 283 1.1 thorpej 284 1.11 ad mutex_destroy(&ref->specdataref_lock); 285 1.11 ad 286 1.1 thorpej sc = ref->specdataref_container; 287 1.1 thorpej if (sc == NULL) 288 1.1 thorpej return; 289 1.1 thorpej ref->specdataref_container = NULL; 290 1.1 thorpej 291 1.9 ad mutex_enter(&sd->sd_lock); 292 1.1 thorpej 293 1.1 thorpej specificdata_container_unlink(sd, sc); 294 1.1 thorpej for (key = 0; key < sc->sc_nkey; key++) { 295 1.1 thorpej specificdata_destroy_datum(sd, sc, key); 296 1.1 thorpej } 297 1.1 thorpej 298 1.9 ad mutex_exit(&sd->sd_lock); 299 1.1 thorpej 300 1.1 thorpej kmem_free(sc, SPECIFICDATA_CONTAINER_BYTESIZE(sc->sc_nkey)); 301 1.1 thorpej } 302 1.1 thorpej 303 1.1 thorpej /* 304 1.1 thorpej * specificdata_getspecific -- 305 1.1 thorpej * Get a datum from a container. 306 1.1 thorpej */ 307 1.1 thorpej void * 308 1.7 yamt specificdata_getspecific(specificdata_domain_t sd, specificdata_reference *ref, 309 1.7 yamt specificdata_key_t key) 310 1.1 thorpej { 311 1.1 thorpej specificdata_container_t sc; 312 1.1 thorpej void *data = NULL; 313 1.1 thorpej 314 1.11 ad mutex_enter(&ref->specdataref_lock); 315 1.1 thorpej 316 1.1 thorpej sc = ref->specdataref_container; 317 1.1 thorpej if (sc != NULL && key < sc->sc_nkey) 318 1.1 thorpej data = sc->sc_data[key]; 319 1.1 thorpej 320 1.11 ad mutex_exit(&ref->specdataref_lock); 321 1.1 thorpej 322 1.1 thorpej return (data); 323 1.1 thorpej } 324 1.1 thorpej 325 1.1 thorpej /* 326 1.1 thorpej * specificdata_getspecific_unlocked -- 327 1.1 thorpej * Get a datum from a container in a lockless fashion. 328 1.1 thorpej * 329 1.1 thorpej * Note: When using this routine, care must be taken to ensure 330 1.1 thorpej * that no other thread could cause the specificdata_reference 331 1.1 thorpej * to become invalid (i.e. point at the wrong container) by 332 1.1 thorpej * issuing a setspecific call or destroying the container. 333 1.1 thorpej */ 334 1.1 thorpej void * 335 1.7 yamt specificdata_getspecific_unlocked(specificdata_domain_t sd, 336 1.1 thorpej specificdata_reference *ref, 337 1.1 thorpej specificdata_key_t key) 338 1.1 thorpej { 339 1.1 thorpej specificdata_container_t sc; 340 1.1 thorpej 341 1.1 thorpej sc = ref->specdataref_container; 342 1.1 thorpej if (sc != NULL && key < sc->sc_nkey) 343 1.1 thorpej return (sc->sc_data[key]); 344 1.1 thorpej 345 1.1 thorpej return (NULL); 346 1.1 thorpej } 347 1.1 thorpej 348 1.6 hannken /* 349 1.6 hannken * specificdata_setspecific -- 350 1.6 hannken * Put a datum into a container. 351 1.6 hannken */ 352 1.6 hannken void 353 1.6 hannken specificdata_setspecific(specificdata_domain_t sd, 354 1.6 hannken specificdata_reference *ref, 355 1.6 hannken specificdata_key_t key, void *data) 356 1.1 thorpej { 357 1.1 thorpej specificdata_container_t sc, newsc; 358 1.1 thorpej size_t newnkey, sz; 359 1.1 thorpej 360 1.12 yamt ASSERT_SLEEPABLE(); 361 1.1 thorpej 362 1.11 ad mutex_enter(&ref->specdataref_lock); 363 1.1 thorpej 364 1.1 thorpej sc = ref->specdataref_container; 365 1.1 thorpej if (__predict_true(sc != NULL && key < sc->sc_nkey)) { 366 1.1 thorpej sc->sc_data[key] = data; 367 1.11 ad mutex_exit(&ref->specdataref_lock); 368 1.6 hannken return; 369 1.1 thorpej } 370 1.1 thorpej 371 1.11 ad mutex_exit(&ref->specdataref_lock); 372 1.1 thorpej 373 1.1 thorpej /* 374 1.1 thorpej * Slow path: need to resize. 375 1.1 thorpej */ 376 1.1 thorpej 377 1.9 ad mutex_enter(&sd->sd_lock); 378 1.1 thorpej newnkey = sd->sd_nkey; 379 1.1 thorpej if (key >= newnkey) { 380 1.9 ad mutex_exit(&sd->sd_lock); 381 1.1 thorpej panic("specificdata_setspecific"); 382 1.1 thorpej } 383 1.1 thorpej sz = SPECIFICDATA_CONTAINER_BYTESIZE(newnkey); 384 1.6 hannken newsc = kmem_zalloc(sz, KM_SLEEP); 385 1.1 thorpej newsc->sc_nkey = newnkey; 386 1.1 thorpej 387 1.11 ad mutex_enter(&ref->specdataref_lock); 388 1.1 thorpej 389 1.1 thorpej sc = ref->specdataref_container; 390 1.1 thorpej if (sc != NULL) { 391 1.1 thorpej if (key < sc->sc_nkey) { 392 1.1 thorpej /* 393 1.1 thorpej * Someone beat us to the punch. Unwind and put 394 1.1 thorpej * the object into the now large enough container. 395 1.1 thorpej */ 396 1.1 thorpej sc->sc_data[key] = data; 397 1.11 ad mutex_exit(&ref->specdataref_lock); 398 1.9 ad mutex_exit(&sd->sd_lock); 399 1.1 thorpej kmem_free(newsc, sz); 400 1.6 hannken return; 401 1.1 thorpej } 402 1.1 thorpej specificdata_container_unlink(sd, sc); 403 1.1 thorpej memcpy(newsc->sc_data, sc->sc_data, 404 1.1 thorpej sc->sc_nkey * sizeof(void *)); 405 1.1 thorpej } 406 1.1 thorpej newsc->sc_data[key] = data; 407 1.1 thorpej specificdata_container_link(sd, newsc); 408 1.1 thorpej ref->specdataref_container = newsc; 409 1.1 thorpej 410 1.11 ad mutex_exit(&ref->specdataref_lock); 411 1.9 ad mutex_exit(&sd->sd_lock); 412 1.1 thorpej 413 1.1 thorpej if (sc != NULL) 414 1.1 thorpej kmem_free(sc, SPECIFICDATA_CONTAINER_BYTESIZE(sc->sc_nkey)); 415 1.1 thorpej } 416