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