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