1 /* $NetBSD: subr_specificdata.c,v 1.7 2006/11/01 10:17:59 yamt 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.7 2006/11/01 10:17:59 yamt 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,
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)
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)
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, specificdata_reference *ref)
287 {
288
289 /*
290 * Just NULL-out the container pointer; we'll allocate the
291 * container the first time specificdata is put into it.
292 */
293 ref->specdataref_container = NULL;
294 specdataref_lock_init(ref);
295
296 return (0);
297 }
298
299 /*
300 * specificdata_fini --
301 * Destroy a specificdata container. We destroy all of the datums
302 * stuffed into the container just as if the key were destroyed.
303 */
304 void
305 specificdata_fini(specificdata_domain_t sd, specificdata_reference *ref)
306 {
307 specificdata_container_t sc;
308 specificdata_key_t key;
309
310 ASSERT_SLEEPABLE(NULL, __func__);
311
312 sc = ref->specdataref_container;
313 if (sc == NULL)
314 return;
315 ref->specdataref_container = NULL;
316
317 specificdata_domain_lock(sd);
318
319 specificdata_container_unlink(sd, sc);
320 for (key = 0; key < sc->sc_nkey; key++) {
321 specificdata_destroy_datum(sd, sc, key);
322 }
323
324 specificdata_domain_unlock(sd);
325
326 kmem_free(sc, SPECIFICDATA_CONTAINER_BYTESIZE(sc->sc_nkey));
327 }
328
329 /*
330 * specificdata_getspecific --
331 * Get a datum from a container.
332 *
333 * Note: This routine is guaranteed not to sleep.
334 */
335 void *
336 specificdata_getspecific(specificdata_domain_t sd, specificdata_reference *ref,
337 specificdata_key_t key)
338 {
339 specificdata_container_t sc;
340 void *data = NULL;
341
342 specdataref_lock(ref);
343
344 sc = ref->specdataref_container;
345 if (sc != NULL && key < sc->sc_nkey)
346 data = sc->sc_data[key];
347
348 specdataref_unlock(ref);
349
350 return (data);
351 }
352
353 /*
354 * specificdata_getspecific_unlocked --
355 * Get a datum from a container in a lockless fashion.
356 *
357 * Note: When using this routine, care must be taken to ensure
358 * that no other thread could cause the specificdata_reference
359 * to become invalid (i.e. point at the wrong container) by
360 * issuing a setspecific call or destroying the container.
361 *
362 * Note #2: This routine is guaranteed not to sleep.
363 */
364 void *
365 specificdata_getspecific_unlocked(specificdata_domain_t sd,
366 specificdata_reference *ref,
367 specificdata_key_t key)
368 {
369 specificdata_container_t sc;
370
371 sc = ref->specdataref_container;
372 if (sc != NULL && key < sc->sc_nkey)
373 return (sc->sc_data[key]);
374
375 return (NULL);
376 }
377
378 /*
379 * specificdata_setspecific --
380 * Put a datum into a container.
381 */
382 void
383 specificdata_setspecific(specificdata_domain_t sd,
384 specificdata_reference *ref,
385 specificdata_key_t key, void *data)
386 {
387 specificdata_container_t sc, newsc;
388 size_t newnkey, sz;
389
390 ASSERT_SLEEPABLE(NULL, __func__);
391
392 specdataref_lock(ref);
393
394 sc = ref->specdataref_container;
395 if (__predict_true(sc != NULL && key < sc->sc_nkey)) {
396 sc->sc_data[key] = data;
397 specdataref_unlock(ref);
398 return;
399 }
400
401 specdataref_unlock(ref);
402
403 /*
404 * Slow path: need to resize.
405 */
406
407 specificdata_domain_lock(sd);
408 newnkey = sd->sd_nkey;
409 if (key >= newnkey) {
410 specificdata_domain_unlock(sd);
411 panic("specificdata_setspecific");
412 }
413 sz = SPECIFICDATA_CONTAINER_BYTESIZE(newnkey);
414 newsc = kmem_zalloc(sz, KM_SLEEP);
415 KASSERT(newsc != NULL);
416 newsc->sc_nkey = newnkey;
417
418 specdataref_lock(ref);
419
420 sc = ref->specdataref_container;
421 if (sc != NULL) {
422 if (key < sc->sc_nkey) {
423 /*
424 * Someone beat us to the punch. Unwind and put
425 * the object into the now large enough container.
426 */
427 sc->sc_data[key] = data;
428 specdataref_unlock(ref);
429 specificdata_domain_unlock(sd);
430 kmem_free(newsc, sz);
431 return;
432 }
433 specificdata_container_unlink(sd, sc);
434 memcpy(newsc->sc_data, sc->sc_data,
435 sc->sc_nkey * sizeof(void *));
436 }
437 newsc->sc_data[key] = data;
438 specificdata_container_link(sd, newsc);
439 ref->specdataref_container = newsc;
440
441 specdataref_unlock(ref);
442 specificdata_domain_unlock(sd);
443
444 if (sc != NULL)
445 kmem_free(sc, SPECIFICDATA_CONTAINER_BYTESIZE(sc->sc_nkey));
446 }
Cache object: 0c04517897024542db51a6b83c11011a
|