subr_specificdata.c revision 1.9 1 /* $NetBSD: subr_specificdata.c,v 1.9 2007/02/15 15:40:52 ad 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.9 2007/02/15 15:40:52 ad Exp $");
67
68 #include <sys/param.h>
69 #include <sys/kmem.h>
70 #include <sys/proc.h>
71 #include <sys/specificdata.h>
72 #include <sys/queue.h>
73 #include <sys/mutex.h>
74
75 /*
76 * Locking notes:
77 *
78 * The specdataref_container pointer in the specificdata_reference
79 * is volatile. To read it, you must hold EITHER the domain lock
80 * or the ref lock. To write it, you must hold BOTH the domain lock
81 * and the ref lock. The locks must be acquired in the following
82 * order:
83 * domain -> ref
84 */
85
86 typedef struct {
87 specificdata_dtor_t ski_dtor;
88 } specificdata_key_impl;
89
90 struct specificdata_container {
91 size_t sc_nkey;
92 LIST_ENTRY(specificdata_container) sc_list;
93 void * sc_data[]; /* variable length */
94 };
95
96 #define SPECIFICDATA_CONTAINER_BYTESIZE(n) \
97 (sizeof(struct specificdata_container) + ((n) * sizeof(void *)))
98
99 struct specificdata_domain {
100 kmutex_t sd_lock;
101 unsigned int sd_nkey;
102 LIST_HEAD(, specificdata_container) sd_list;
103 specificdata_key_impl *sd_keys;
104 };
105
106 #define specdataref_lock_init(ref) \
107 simple_lock_init(&(ref)->specdataref_slock)
108 #define specdataref_lock(ref) simple_lock(&(ref)->specdataref_slock)
109 #define specdataref_unlock(ref) simple_unlock(&(ref)->specdataref_slock)
110
111 static void
112 specificdata_container_link(specificdata_domain_t sd,
113 specificdata_container_t sc)
114 {
115
116 LIST_INSERT_HEAD(&sd->sd_list, sc, sc_list);
117 }
118
119 static void
120 specificdata_container_unlink(specificdata_domain_t sd,
121 specificdata_container_t sc)
122 {
123
124 LIST_REMOVE(sc, sc_list);
125 }
126
127 static void
128 specificdata_destroy_datum(specificdata_domain_t sd,
129 specificdata_container_t sc, specificdata_key_t key)
130 {
131 specificdata_dtor_t dtor;
132 void *data;
133
134 if (key >= sc->sc_nkey)
135 return;
136
137 KASSERT(key < sd->sd_nkey);
138
139 data = sc->sc_data[key];
140 dtor = sd->sd_keys[key].ski_dtor;
141
142 if (dtor != NULL) {
143 if (data != NULL) {
144 sc->sc_data[key] = NULL;
145 (*dtor)(data);
146 }
147 } else {
148 KASSERT(data == NULL);
149 }
150 }
151
152 static void
153 specificdata_noop_dtor(void *data)
154 {
155
156 /* nothing */
157 }
158
159 /*
160 * specificdata_domain_create --
161 * Create a specificdata domain.
162 */
163 specificdata_domain_t
164 specificdata_domain_create(void)
165 {
166 specificdata_domain_t sd;
167
168 sd = kmem_zalloc(sizeof(*sd), KM_SLEEP);
169 KASSERT(sd != NULL);
170 mutex_init(&sd->sd_lock, MUTEX_DEFAULT, IPL_NONE);
171 LIST_INIT(&sd->sd_list);
172
173 return (sd);
174 }
175
176 /*
177 * specificdata_domain_delete --
178 * Destroy a specificdata domain.
179 */
180 void
181 specificdata_domain_delete(specificdata_domain_t sd)
182 {
183
184 panic("specificdata_domain_delete: not implemented");
185 }
186
187 /*
188 * specificdata_key_create --
189 * Create a specificdata key for a domain.
190 *
191 * Note: This is a rare operation.
192 */
193 int
194 specificdata_key_create(specificdata_domain_t sd, specificdata_key_t *keyp,
195 specificdata_dtor_t dtor)
196 {
197 specificdata_key_impl *newkeys;
198 specificdata_key_t key = 0;
199 size_t nsz;
200
201 ASSERT_SLEEPABLE(NULL, __func__);
202
203 if (dtor == NULL)
204 dtor = specificdata_noop_dtor;
205
206 mutex_enter(&sd->sd_lock);
207
208 if (sd->sd_keys == NULL)
209 goto needalloc;
210
211 for (; key < sd->sd_nkey; key++) {
212 if (sd->sd_keys[key].ski_dtor == NULL)
213 goto gotit;
214 }
215
216 needalloc:
217 nsz = (sd->sd_nkey + 1) * sizeof(*newkeys);
218 /* XXXSMP allocating memory while holding a lock. */
219 newkeys = kmem_zalloc(nsz, KM_SLEEP);
220 KASSERT(newkeys != NULL);
221 if (sd->sd_keys != NULL) {
222 size_t osz = sd->sd_nkey * sizeof(*newkeys);
223 memcpy(newkeys, sd->sd_keys, osz);
224 kmem_free(sd->sd_keys, osz);
225 }
226 sd->sd_keys = newkeys;
227 sd->sd_nkey++;
228 gotit:
229 sd->sd_keys[key].ski_dtor = dtor;
230
231 mutex_exit(&sd->sd_lock);
232
233 *keyp = key;
234 return (0);
235 }
236
237 /*
238 * specificdata_key_delete --
239 * Destroy a specificdata key for a domain.
240 *
241 * Note: This is a rare operation.
242 */
243 void
244 specificdata_key_delete(specificdata_domain_t sd, specificdata_key_t key)
245 {
246 specificdata_container_t sc;
247
248 mutex_enter(&sd->sd_lock);
249
250 if (key >= sd->sd_nkey)
251 goto out;
252
253 /*
254 * Traverse all of the specificdata containers in the domain
255 * and the destroy the datum for the dying key.
256 */
257 LIST_FOREACH(sc, &sd->sd_list, sc_list) {
258 specificdata_destroy_datum(sd, sc, key);
259 }
260
261 sd->sd_keys[key].ski_dtor = NULL;
262
263 out:
264 mutex_exit(&sd->sd_lock);
265 }
266
267 /*
268 * specificdata_init --
269 * Initialize a specificdata container for operation in the
270 * specified domain.
271 */
272 int
273 specificdata_init(specificdata_domain_t sd, specificdata_reference *ref)
274 {
275
276 /*
277 * Just NULL-out the container pointer; we'll allocate the
278 * container the first time specificdata is put into it.
279 */
280 ref->specdataref_container = NULL;
281 specdataref_lock_init(ref);
282
283 return (0);
284 }
285
286 /*
287 * specificdata_fini --
288 * Destroy a specificdata container. We destroy all of the datums
289 * stuffed into the container just as if the key were destroyed.
290 */
291 void
292 specificdata_fini(specificdata_domain_t sd, specificdata_reference *ref)
293 {
294 specificdata_container_t sc;
295 specificdata_key_t key;
296
297 ASSERT_SLEEPABLE(NULL, __func__);
298
299 sc = ref->specdataref_container;
300 if (sc == NULL)
301 return;
302 ref->specdataref_container = NULL;
303
304 mutex_enter(&sd->sd_lock);
305
306 specificdata_container_unlink(sd, sc);
307 for (key = 0; key < sc->sc_nkey; key++) {
308 specificdata_destroy_datum(sd, sc, key);
309 }
310
311 mutex_exit(&sd->sd_lock);
312
313 kmem_free(sc, SPECIFICDATA_CONTAINER_BYTESIZE(sc->sc_nkey));
314 }
315
316 /*
317 * specificdata_getspecific --
318 * Get a datum from a container.
319 *
320 * Note: This routine is guaranteed not to sleep.
321 */
322 void *
323 specificdata_getspecific(specificdata_domain_t sd, specificdata_reference *ref,
324 specificdata_key_t key)
325 {
326 specificdata_container_t sc;
327 void *data = NULL;
328
329 specdataref_lock(ref);
330
331 sc = ref->specdataref_container;
332 if (sc != NULL && key < sc->sc_nkey)
333 data = sc->sc_data[key];
334
335 specdataref_unlock(ref);
336
337 return (data);
338 }
339
340 /*
341 * specificdata_getspecific_unlocked --
342 * Get a datum from a container in a lockless fashion.
343 *
344 * Note: When using this routine, care must be taken to ensure
345 * that no other thread could cause the specificdata_reference
346 * to become invalid (i.e. point at the wrong container) by
347 * issuing a setspecific call or destroying the container.
348 *
349 * Note #2: This routine is guaranteed not to sleep.
350 */
351 void *
352 specificdata_getspecific_unlocked(specificdata_domain_t sd,
353 specificdata_reference *ref,
354 specificdata_key_t key)
355 {
356 specificdata_container_t sc;
357
358 sc = ref->specdataref_container;
359 if (sc != NULL && key < sc->sc_nkey)
360 return (sc->sc_data[key]);
361
362 return (NULL);
363 }
364
365 /*
366 * specificdata_setspecific --
367 * Put a datum into a container.
368 */
369 void
370 specificdata_setspecific(specificdata_domain_t sd,
371 specificdata_reference *ref,
372 specificdata_key_t key, void *data)
373 {
374 specificdata_container_t sc, newsc;
375 size_t newnkey, sz;
376
377 ASSERT_SLEEPABLE(NULL, __func__);
378
379 specdataref_lock(ref);
380
381 sc = ref->specdataref_container;
382 if (__predict_true(sc != NULL && key < sc->sc_nkey)) {
383 sc->sc_data[key] = data;
384 specdataref_unlock(ref);
385 return;
386 }
387
388 specdataref_unlock(ref);
389
390 /*
391 * Slow path: need to resize.
392 */
393
394 mutex_enter(&sd->sd_lock);
395 newnkey = sd->sd_nkey;
396 if (key >= newnkey) {
397 mutex_exit(&sd->sd_lock);
398 panic("specificdata_setspecific");
399 }
400 sz = SPECIFICDATA_CONTAINER_BYTESIZE(newnkey);
401 newsc = kmem_zalloc(sz, KM_SLEEP);
402 KASSERT(newsc != NULL);
403 newsc->sc_nkey = newnkey;
404
405 specdataref_lock(ref);
406
407 sc = ref->specdataref_container;
408 if (sc != NULL) {
409 if (key < sc->sc_nkey) {
410 /*
411 * Someone beat us to the punch. Unwind and put
412 * the object into the now large enough container.
413 */
414 sc->sc_data[key] = data;
415 specdataref_unlock(ref);
416 mutex_exit(&sd->sd_lock);
417 kmem_free(newsc, sz);
418 return;
419 }
420 specificdata_container_unlink(sd, sc);
421 memcpy(newsc->sc_data, sc->sc_data,
422 sc->sc_nkey * sizeof(void *));
423 }
424 newsc->sc_data[key] = data;
425 specificdata_container_link(sd, newsc);
426 ref->specdataref_container = newsc;
427
428 specdataref_unlock(ref);
429 mutex_exit(&sd->sd_lock);
430
431 if (sc != NULL)
432 kmem_free(sc, SPECIFICDATA_CONTAINER_BYTESIZE(sc->sc_nkey));
433 }
434