1/* $NetBSD: subr_specificdata.c,v 1.13 2008/04/28 20:24:04 martin 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 *
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.
30 */
31
32/*-
33 * Copyright (c) 2006 YAMAMOTO Takashi.
34 * All rights reserved.
35 *
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.
44 *
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.
56 */
57
58#include <sys/cdefs.h>
59__KERNEL_RCSID(0, "$NetBSD: subr_specificdata.c,v 1.13 2008/04/28 20:24:04 martin Exp $");
60
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>
66
67/*
68 * Locking notes:
69 *
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
76 */
77
78typedef struct {
79 specificdata_dtor_t ski_dtor;
80} specificdata_key_impl;
81
82struct specificdata_container {
83 size_t sc_nkey;
84 LIST_ENTRY(specificdata_container) sc_list;
85 void * sc_data[]; /* variable length */
86};
87
88#define SPECIFICDATA_CONTAINER_BYTESIZE(n) \
89 (sizeof(struct specificdata_container) + ((n) * sizeof(void *)))
90
91struct 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;
96};
97
98static void
99specificdata_container_link(specificdata_domain_t sd,
100 specificdata_container_t sc)
101{
102
103 LIST_INSERT_HEAD(&sd->sd_list, sc, sc_list);
104}
105
106static void
107specificdata_container_unlink(specificdata_domain_t sd,
108 specificdata_container_t sc)
109{
110
111 LIST_REMOVE(sc, sc_list);
112}
113
114static void
115specificdata_destroy_datum(specificdata_domain_t sd,
116 specificdata_container_t sc, specificdata_key_t key)
117{
118 specificdata_dtor_t dtor;
119 void *data;
120
121 if (key >= sc->sc_nkey)
122 return;
123
124 KASSERT(key < sd->sd_nkey);
125
126 data = sc->sc_data[key];
127 dtor = sd->sd_keys[key].ski_dtor;
128
129 if (dtor != NULL) {
130 if (data != NULL) {
131 sc->sc_data[key] = NULL;
132 (*dtor)(data);
133 }
134 } else {
135 KASSERT(data == NULL);
136 }
137}
138
139static void
140specificdata_noop_dtor(void *data)
141{
142
143 /* nothing */
144}
145
146/*
147 * specificdata_domain_create --
148 * Create a specificdata domain.
149 */
150specificdata_domain_t
151specificdata_domain_create(void)
152{
153 specificdata_domain_t sd;
154
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);
159
160 return (sd);
161}
162
163/*
164 * specificdata_domain_delete --
165 * Destroy a specificdata domain.
166 */
167void
168specificdata_domain_delete(specificdata_domain_t sd)
169{
170
171 panic("specificdata_domain_delete: not implemented");
172}
173
174/*
175 * specificdata_key_create --
176 * Create a specificdata key for a domain.
177 *
178 * Note: This is a rare operation.
179 */
180int
181specificdata_key_create(specificdata_domain_t sd, specificdata_key_t *keyp,
182 specificdata_dtor_t dtor)
183{
184 specificdata_key_impl *newkeys;
185 specificdata_key_t key = 0;
186 size_t nsz;
187
188 ASSERT_SLEEPABLE();
189
190 if (dtor == NULL)
191 dtor = specificdata_noop_dtor;
192
193 mutex_enter(&sd->sd_lock);
194
195 if (sd->sd_keys == NULL)
196 goto needalloc;
197
198 for (; key < sd->sd_nkey; key++) {
199 if (sd->sd_keys[key].ski_dtor == NULL)
200 goto gotit;
201 }
202
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);
212 }
213 sd->sd_keys = newkeys;
214 sd->sd_nkey++;
215 gotit:
216 sd->sd_keys[key].ski_dtor = dtor;
217
218 mutex_exit(&sd->sd_lock);
219
220 *keyp = key;
221 return (0);
222}
223
224/*
225 * specificdata_key_delete --
226 * Destroy a specificdata key for a domain.
227 *
228 * Note: This is a rare operation.
229 */
230void
231specificdata_key_delete(specificdata_domain_t sd, specificdata_key_t key)
232{
233 specificdata_container_t sc;
234
235 mutex_enter(&sd->sd_lock);
236
237 if (key >= sd->sd_nkey)
238 goto out;
239
240 /*
241 * Traverse all of the specificdata containers in the domain
242 * and the destroy the datum for the dying key.
243 */
244 LIST_FOREACH(sc, &sd->sd_list, sc_list) {
245 specificdata_destroy_datum(sd, sc, key);
246 }
247
248 sd->sd_keys[key].ski_dtor = NULL;
249
250 out:
251 mutex_exit(&sd->sd_lock);
252}
253
254/*
255 * specificdata_init --
256 * Initialize a specificdata container for operation in the
257 * specified domain.
258 */
259int
260specificdata_init(specificdata_domain_t sd, specificdata_reference *ref)
261{
262
263 /*
264 * Just NULL-out the container pointer; we'll allocate the
265 * container the first time specificdata is put into it.
266 */
267 ref->specdataref_container = NULL;
268 mutex_init(&ref->specdataref_lock, MUTEX_DEFAULT, IPL_NONE);
269
270 return (0);
271}
272
273/*
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.
277 */
278void
279specificdata_fini(specificdata_domain_t sd, specificdata_reference *ref)
280{
281 specificdata_container_t sc;
282 specificdata_key_t key;
283
284 ASSERT_SLEEPABLE();
285
286 mutex_destroy(&ref->specdataref_lock);
287
288 sc = ref->specdataref_container;
289 if (sc == NULL)
290 return;
291 ref->specdataref_container = NULL;
292
293 mutex_enter(&sd->sd_lock);
294
295 specificdata_container_unlink(sd, sc);
296 for (key = 0; key < sc->sc_nkey; key++) {
297 specificdata_destroy_datum(sd, sc, key);
298 }
299
300 mutex_exit(&sd->sd_lock);
301
302 kmem_free(sc, SPECIFICDATA_CONTAINER_BYTESIZE(sc->sc_nkey));
303}
304
305/*
306 * specificdata_getspecific --
307 * Get a datum from a container.
308 */
309void *
310specificdata_getspecific(specificdata_domain_t sd, specificdata_reference *ref,
311 specificdata_key_t key)
312{
313 specificdata_container_t sc;
314 void *data = NULL;
315
316 mutex_enter(&ref->specdataref_lock);
317
318 sc = ref->specdataref_container;
319 if (sc != NULL && key < sc->sc_nkey)
320 data = sc->sc_data[key];
321
322 mutex_exit(&ref->specdataref_lock);
323
324 return (data);
325}
326
327/*
328 * specificdata_getspecific_unlocked --
329 * Get a datum from a container in a lockless fashion.
330 *
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.
335 */
336void *
337specificdata_getspecific_unlocked(specificdata_domain_t sd,
338 specificdata_reference *ref,
339 specificdata_key_t key)
340{
341 specificdata_container_t sc;
342
343 sc = ref->specdataref_container;
344 if (sc != NULL && key < sc->sc_nkey)
345 return (sc->sc_data[key]);
346
347 return (NULL);
348}
349
350/*
351 * specificdata_setspecific --
352 * Put a datum into a container.
353 */
354void
355specificdata_setspecific(specificdata_domain_t sd,
356 specificdata_reference *ref,
357 specificdata_key_t key, void *data)
358{
359 specificdata_container_t sc, newsc;
360 size_t newnkey, sz;
361
362 ASSERT_SLEEPABLE();
363
364 mutex_enter(&ref->specdataref_lock);
365
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;
371 }
372
373 mutex_exit(&ref->specdataref_lock);
374
375 /*
376 * Slow path: need to resize.
377 */
378
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");
384 }
385 sz = SPECIFICDATA_CONTAINER_BYTESIZE(newnkey);
386 newsc = kmem_zalloc(sz, KM_SLEEP);
387 KASSERT(newsc != NULL);
388 newsc->sc_nkey = newnkey;
389
390 mutex_enter(&ref->specdataref_lock);
391
392 sc = ref->specdataref_container;
393 if (sc != NULL) {
394 if (key < sc->sc_nkey) {
395 /*
396 * Someone beat us to the punch. Unwind and put
397 * the object into the now large enough container.
398 */
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;
404 }
405 specificdata_container_unlink(sd, sc);
406 memcpy(newsc->sc_data, sc->sc_data,
407 sc->sc_nkey * sizeof(void *));
408 }
409 newsc->sc_data[key] = data;
410 specificdata_container_link(sd, newsc);
411 ref->specdataref_container = newsc;
412
413 mutex_exit(&ref->specdataref_lock);
414 mutex_exit(&sd->sd_lock);
415
416 if (sc != NULL)
417 kmem_free(sc, SPECIFICDATA_CONTAINER_BYTESIZE(sc->sc_nkey));
418}
419