Expand PMF_FN_* macros.
[netbsd-mini2440.git] / sys / kern / subr_specificdata.c
bloba9e70fce127a3ce38067350f2cf2a3803e62671a
1 /* $NetBSD: subr_specificdata.c,v 1.12 2008/03/17 08:27:50 yamt Exp $ */
3 /*-
4 * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe.
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.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 /*-
33 * Copyright (c) 2006 YAMAMOTO Takashi.
34 * All rights reserved.
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
45 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * SUCH DAMAGE.
58 #include <sys/cdefs.h>
59 __KERNEL_RCSID(0, "$NetBSD: subr_specificdata.c,v 1.12 2008/03/17 08:27:50 yamt Exp $");
61 #include <sys/param.h>
62 #include <sys/kmem.h>
63 #include <sys/specificdata.h>
64 #include <sys/queue.h>
65 #include <sys/mutex.h>
68 * Locking notes:
70 * The specdataref_container pointer in the specificdata_reference
71 * is volatile. To read it, you must hold EITHER the domain lock
72 * or the ref lock. To write it, you must hold BOTH the domain lock
73 * and the ref lock. The locks must be acquired in the following
74 * order:
75 * domain -> ref
78 typedef struct {
79 specificdata_dtor_t ski_dtor;
80 } specificdata_key_impl;
82 struct specificdata_container {
83 size_t sc_nkey;
84 LIST_ENTRY(specificdata_container) sc_list;
85 void * sc_data[]; /* variable length */
88 #define SPECIFICDATA_CONTAINER_BYTESIZE(n) \
89 (sizeof(struct specificdata_container) + ((n) * sizeof(void *)))
91 struct specificdata_domain {
92 kmutex_t sd_lock;
93 unsigned int sd_nkey;
94 LIST_HEAD(, specificdata_container) sd_list;
95 specificdata_key_impl *sd_keys;
98 static void
99 specificdata_container_link(specificdata_domain_t sd,
100 specificdata_container_t sc)
103 LIST_INSERT_HEAD(&sd->sd_list, sc, sc_list);
106 static void
107 specificdata_container_unlink(specificdata_domain_t sd,
108 specificdata_container_t sc)
111 LIST_REMOVE(sc, sc_list);
114 static void
115 specificdata_destroy_datum(specificdata_domain_t sd,
116 specificdata_container_t sc, specificdata_key_t key)
118 specificdata_dtor_t dtor;
119 void *data;
121 if (key >= sc->sc_nkey)
122 return;
124 KASSERT(key < sd->sd_nkey);
126 data = sc->sc_data[key];
127 dtor = sd->sd_keys[key].ski_dtor;
129 if (dtor != NULL) {
130 if (data != NULL) {
131 sc->sc_data[key] = NULL;
132 (*dtor)(data);
134 } else {
135 KASSERT(data == NULL);
139 static void
140 specificdata_noop_dtor(void *data)
143 /* nothing */
147 * specificdata_domain_create --
148 * Create a specificdata domain.
150 specificdata_domain_t
151 specificdata_domain_create(void)
153 specificdata_domain_t sd;
155 sd = kmem_zalloc(sizeof(*sd), KM_SLEEP);
156 KASSERT(sd != NULL);
157 mutex_init(&sd->sd_lock, MUTEX_DEFAULT, IPL_NONE);
158 LIST_INIT(&sd->sd_list);
160 return (sd);
164 * specificdata_domain_delete --
165 * Destroy a specificdata domain.
167 void
168 specificdata_domain_delete(specificdata_domain_t sd)
171 panic("specificdata_domain_delete: not implemented");
175 * specificdata_key_create --
176 * Create a specificdata key for a domain.
178 * Note: This is a rare operation.
181 specificdata_key_create(specificdata_domain_t sd, specificdata_key_t *keyp,
182 specificdata_dtor_t dtor)
184 specificdata_key_impl *newkeys;
185 specificdata_key_t key = 0;
186 size_t nsz;
188 ASSERT_SLEEPABLE();
190 if (dtor == NULL)
191 dtor = specificdata_noop_dtor;
193 mutex_enter(&sd->sd_lock);
195 if (sd->sd_keys == NULL)
196 goto needalloc;
198 for (; key < sd->sd_nkey; key++) {
199 if (sd->sd_keys[key].ski_dtor == NULL)
200 goto gotit;
203 needalloc:
204 nsz = (sd->sd_nkey + 1) * sizeof(*newkeys);
205 /* XXXSMP allocating memory while holding a lock. */
206 newkeys = kmem_zalloc(nsz, KM_SLEEP);
207 KASSERT(newkeys != NULL);
208 if (sd->sd_keys != NULL) {
209 size_t osz = sd->sd_nkey * sizeof(*newkeys);
210 memcpy(newkeys, sd->sd_keys, osz);
211 kmem_free(sd->sd_keys, osz);
213 sd->sd_keys = newkeys;
214 sd->sd_nkey++;
215 gotit:
216 sd->sd_keys[key].ski_dtor = dtor;
218 mutex_exit(&sd->sd_lock);
220 *keyp = key;
221 return (0);
225 * specificdata_key_delete --
226 * Destroy a specificdata key for a domain.
228 * Note: This is a rare operation.
230 void
231 specificdata_key_delete(specificdata_domain_t sd, specificdata_key_t key)
233 specificdata_container_t sc;
235 mutex_enter(&sd->sd_lock);
237 if (key >= sd->sd_nkey)
238 goto out;
241 * Traverse all of the specificdata containers in the domain
242 * and the destroy the datum for the dying key.
244 LIST_FOREACH(sc, &sd->sd_list, sc_list) {
245 specificdata_destroy_datum(sd, sc, key);
248 sd->sd_keys[key].ski_dtor = NULL;
250 out:
251 mutex_exit(&sd->sd_lock);
255 * specificdata_init --
256 * Initialize a specificdata container for operation in the
257 * specified domain.
260 specificdata_init(specificdata_domain_t sd, specificdata_reference *ref)
264 * Just NULL-out the container pointer; we'll allocate the
265 * container the first time specificdata is put into it.
267 ref->specdataref_container = NULL;
268 mutex_init(&ref->specdataref_lock, MUTEX_DEFAULT, IPL_NONE);
270 return (0);
274 * specificdata_fini --
275 * Destroy a specificdata container. We destroy all of the datums
276 * stuffed into the container just as if the key were destroyed.
278 void
279 specificdata_fini(specificdata_domain_t sd, specificdata_reference *ref)
281 specificdata_container_t sc;
282 specificdata_key_t key;
284 ASSERT_SLEEPABLE();
286 mutex_destroy(&ref->specdataref_lock);
288 sc = ref->specdataref_container;
289 if (sc == NULL)
290 return;
291 ref->specdataref_container = NULL;
293 mutex_enter(&sd->sd_lock);
295 specificdata_container_unlink(sd, sc);
296 for (key = 0; key < sc->sc_nkey; key++) {
297 specificdata_destroy_datum(sd, sc, key);
300 mutex_exit(&sd->sd_lock);
302 kmem_free(sc, SPECIFICDATA_CONTAINER_BYTESIZE(sc->sc_nkey));
306 * specificdata_getspecific --
307 * Get a datum from a container.
309 void *
310 specificdata_getspecific(specificdata_domain_t sd, specificdata_reference *ref,
311 specificdata_key_t key)
313 specificdata_container_t sc;
314 void *data = NULL;
316 mutex_enter(&ref->specdataref_lock);
318 sc = ref->specdataref_container;
319 if (sc != NULL && key < sc->sc_nkey)
320 data = sc->sc_data[key];
322 mutex_exit(&ref->specdataref_lock);
324 return (data);
328 * specificdata_getspecific_unlocked --
329 * Get a datum from a container in a lockless fashion.
331 * Note: When using this routine, care must be taken to ensure
332 * that no other thread could cause the specificdata_reference
333 * to become invalid (i.e. point at the wrong container) by
334 * issuing a setspecific call or destroying the container.
336 void *
337 specificdata_getspecific_unlocked(specificdata_domain_t sd,
338 specificdata_reference *ref,
339 specificdata_key_t key)
341 specificdata_container_t sc;
343 sc = ref->specdataref_container;
344 if (sc != NULL && key < sc->sc_nkey)
345 return (sc->sc_data[key]);
347 return (NULL);
351 * specificdata_setspecific --
352 * Put a datum into a container.
354 void
355 specificdata_setspecific(specificdata_domain_t sd,
356 specificdata_reference *ref,
357 specificdata_key_t key, void *data)
359 specificdata_container_t sc, newsc;
360 size_t newnkey, sz;
362 ASSERT_SLEEPABLE();
364 mutex_enter(&ref->specdataref_lock);
366 sc = ref->specdataref_container;
367 if (__predict_true(sc != NULL && key < sc->sc_nkey)) {
368 sc->sc_data[key] = data;
369 mutex_exit(&ref->specdataref_lock);
370 return;
373 mutex_exit(&ref->specdataref_lock);
376 * Slow path: need to resize.
379 mutex_enter(&sd->sd_lock);
380 newnkey = sd->sd_nkey;
381 if (key >= newnkey) {
382 mutex_exit(&sd->sd_lock);
383 panic("specificdata_setspecific");
385 sz = SPECIFICDATA_CONTAINER_BYTESIZE(newnkey);
386 newsc = kmem_zalloc(sz, KM_SLEEP);
387 KASSERT(newsc != NULL);
388 newsc->sc_nkey = newnkey;
390 mutex_enter(&ref->specdataref_lock);
392 sc = ref->specdataref_container;
393 if (sc != NULL) {
394 if (key < sc->sc_nkey) {
396 * Someone beat us to the punch. Unwind and put
397 * the object into the now large enough container.
399 sc->sc_data[key] = data;
400 mutex_exit(&ref->specdataref_lock);
401 mutex_exit(&sd->sd_lock);
402 kmem_free(newsc, sz);
403 return;
405 specificdata_container_unlink(sd, sc);
406 memcpy(newsc->sc_data, sc->sc_data,
407 sc->sc_nkey * sizeof(void *));
409 newsc->sc_data[key] = data;
410 specificdata_container_link(sd, newsc);
411 ref->specdataref_container = newsc;
413 mutex_exit(&ref->specdataref_lock);
414 mutex_exit(&sd->sd_lock);
416 if (sc != NULL)
417 kmem_free(sc, SPECIFICDATA_CONTAINER_BYTESIZE(sc->sc_nkey));