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 | } |
419 | |