1 /* $NetBSD: subr_specificdata.c,v 1.12 2008/03/17 08:27:50 yamt Exp $ */
4 * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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
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
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>
63 #include <sys/specificdata.h>
64 #include <sys/queue.h>
65 #include <sys/mutex.h>
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
79 specificdata_dtor_t ski_dtor
;
80 } specificdata_key_impl
;
82 struct specificdata_container
{
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
{
94 LIST_HEAD(, specificdata_container
) sd_list
;
95 specificdata_key_impl
*sd_keys
;
99 specificdata_container_link(specificdata_domain_t sd
,
100 specificdata_container_t sc
)
103 LIST_INSERT_HEAD(&sd
->sd_list
, sc
, sc_list
);
107 specificdata_container_unlink(specificdata_domain_t sd
,
108 specificdata_container_t sc
)
111 LIST_REMOVE(sc
, sc_list
);
115 specificdata_destroy_datum(specificdata_domain_t sd
,
116 specificdata_container_t sc
, specificdata_key_t key
)
118 specificdata_dtor_t dtor
;
121 if (key
>= sc
->sc_nkey
)
124 KASSERT(key
< sd
->sd_nkey
);
126 data
= sc
->sc_data
[key
];
127 dtor
= sd
->sd_keys
[key
].ski_dtor
;
131 sc
->sc_data
[key
] = NULL
;
135 KASSERT(data
== NULL
);
140 specificdata_noop_dtor(void *data
)
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
);
157 mutex_init(&sd
->sd_lock
, MUTEX_DEFAULT
, IPL_NONE
);
158 LIST_INIT(&sd
->sd_list
);
164 * specificdata_domain_delete --
165 * Destroy a specificdata domain.
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;
191 dtor
= specificdata_noop_dtor
;
193 mutex_enter(&sd
->sd_lock
);
195 if (sd
->sd_keys
== NULL
)
198 for (; key
< sd
->sd_nkey
; key
++) {
199 if (sd
->sd_keys
[key
].ski_dtor
== NULL
)
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
;
216 sd
->sd_keys
[key
].ski_dtor
= dtor
;
218 mutex_exit(&sd
->sd_lock
);
225 * specificdata_key_delete --
226 * Destroy a specificdata key for a domain.
228 * Note: This is a rare operation.
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
)
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
;
251 mutex_exit(&sd
->sd_lock
);
255 * specificdata_init --
256 * Initialize a specificdata container for operation in the
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
);
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.
279 specificdata_fini(specificdata_domain_t sd
, specificdata_reference
*ref
)
281 specificdata_container_t sc
;
282 specificdata_key_t key
;
286 mutex_destroy(&ref
->specdataref_lock
);
288 sc
= ref
->specdataref_container
;
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.
310 specificdata_getspecific(specificdata_domain_t sd
, specificdata_reference
*ref
,
311 specificdata_key_t key
)
313 specificdata_container_t sc
;
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
);
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.
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
]);
351 * specificdata_setspecific --
352 * Put a datum into a container.
355 specificdata_setspecific(specificdata_domain_t sd
,
356 specificdata_reference
*ref
,
357 specificdata_key_t key
, void *data
)
359 specificdata_container_t sc
, newsc
;
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
);
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
;
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
);
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
);
417 kmem_free(sc
, SPECIFICDATA_CONTAINER_BYTESIZE(sc
->sc_nkey
));