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
78 typedef struct {
79 specificdata_dtor_t ski_dtor;
80 } specificdata_key_impl;
81
82 struct 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
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;
96 };
97
98 static void
99 specificdata_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
106 static void
107 specificdata_container_unlink(specificdata_domain_t sd,
108 specificdata_container_t sc)
109 {
110
111 LIST_REMOVE(sc, sc_list);
112 }
113
114 static void
115 specificdata_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
139 static void
140 specificdata_noop_dtor(void *data)
141 {
142
143 /* nothing */
144 }
145
146 /*
147 * specificdata_domain_create --
148 * Create a specificdata domain.
149 */
150 specificdata_domain_t
151 specificdata_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 */
167 void
168 specificdata_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 */
180 int
181 specificdata_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 */
230 void
231 specificdata_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 */
259 int
260 specificdata_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 */
278 void
279 specificdata_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 */
309 void *
310 specificdata_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 */
336 void *
337 specificdata_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 */
354 void
355 specificdata_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 }
Cache object: f07bf2cc8be543430ee6cfdb23586d2a
|