Home | History | Annotate | Line # | Download | only in kern
subr_specificdata.c revision 1.11.14.1
      1 /*	$NetBSD: subr_specificdata.c,v 1.11.14.1 2008/04/03 12:43:04 mjf 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.14.1 2008/04/03 12:43:04 mjf 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