xref: /netbsd-src/sys/kern/subr_specificdata.c (revision fd34ea77eb8af7799b3fb5a3f29af9eb231073c6)
1*fd34ea77Schs /*	$NetBSD: subr_specificdata.c,v 1.14 2017/06/01 02:45:13 chs Exp $	*/
231adc576Sthorpej 
331adc576Sthorpej /*-
49abeea58Sad  * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc.
531adc576Sthorpej  * All rights reserved.
631adc576Sthorpej  *
731adc576Sthorpej  * This code is derived from software contributed to The NetBSD Foundation
831adc576Sthorpej  * by Jason R. Thorpe.
931adc576Sthorpej  *
1031adc576Sthorpej  * Redistribution and use in source and binary forms, with or without
1131adc576Sthorpej  * modification, are permitted provided that the following conditions
1231adc576Sthorpej  * are met:
1331adc576Sthorpej  * 1. Redistributions of source code must retain the above copyright
1431adc576Sthorpej  *    notice, this list of conditions and the following disclaimer.
1531adc576Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
1631adc576Sthorpej  *    notice, this list of conditions and the following disclaimer in the
1731adc576Sthorpej  *    documentation and/or other materials provided with the distribution.
1831adc576Sthorpej  *
1931adc576Sthorpej  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2031adc576Sthorpej  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2131adc576Sthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2231adc576Sthorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2331adc576Sthorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2431adc576Sthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2531adc576Sthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2631adc576Sthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2731adc576Sthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2831adc576Sthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2931adc576Sthorpej  * POSSIBILITY OF SUCH DAMAGE.
3031adc576Sthorpej  */
3131adc576Sthorpej 
3231adc576Sthorpej /*-
3331adc576Sthorpej  * Copyright (c) 2006 YAMAMOTO Takashi.
3431adc576Sthorpej  * All rights reserved.
3531adc576Sthorpej  *
3631adc576Sthorpej  * Redistribution and use in source and binary forms, with or without
3731adc576Sthorpej  * modification, are permitted provided that the following conditions
3831adc576Sthorpej  * are met:
3931adc576Sthorpej  * 1. Redistributions of source code must retain the above copyright
4031adc576Sthorpej  *    notice, this list of conditions and the following disclaimer.
4131adc576Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
4231adc576Sthorpej  *    notice, this list of conditions and the following disclaimer in the
4331adc576Sthorpej  *    documentation and/or other materials provided with the distribution.
4431adc576Sthorpej  *
4531adc576Sthorpej  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
4631adc576Sthorpej  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4731adc576Sthorpej  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4831adc576Sthorpej  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
4931adc576Sthorpej  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5031adc576Sthorpej  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5131adc576Sthorpej  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5231adc576Sthorpej  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
5331adc576Sthorpej  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5431adc576Sthorpej  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5531adc576Sthorpej  * SUCH DAMAGE.
5631adc576Sthorpej  */
5731adc576Sthorpej 
5831adc576Sthorpej #include <sys/cdefs.h>
59*fd34ea77Schs __KERNEL_RCSID(0, "$NetBSD: subr_specificdata.c,v 1.14 2017/06/01 02:45:13 chs Exp $");
6031adc576Sthorpej 
6131adc576Sthorpej #include <sys/param.h>
6231adc576Sthorpej #include <sys/kmem.h>
6331adc576Sthorpej #include <sys/specificdata.h>
6431adc576Sthorpej #include <sys/queue.h>
659abeea58Sad #include <sys/mutex.h>
6631adc576Sthorpej 
6731adc576Sthorpej /*
6831adc576Sthorpej  * Locking notes:
6931adc576Sthorpej  *
7031adc576Sthorpej  * The specdataref_container pointer in the specificdata_reference
7131adc576Sthorpej  * is volatile.  To read it, you must hold EITHER the domain lock
7231adc576Sthorpej  * or the ref lock.  To write it, you must hold BOTH the domain lock
7331adc576Sthorpej  * and the ref lock.  The locks must be acquired in the following
7431adc576Sthorpej  * order:
7531adc576Sthorpej  *	domain -> ref
7631adc576Sthorpej  */
7731adc576Sthorpej 
7831adc576Sthorpej typedef struct {
7931adc576Sthorpej 	specificdata_dtor_t	ski_dtor;
8031adc576Sthorpej } specificdata_key_impl;
8131adc576Sthorpej 
8231adc576Sthorpej struct specificdata_container {
8331adc576Sthorpej 	size_t		sc_nkey;
8431adc576Sthorpej 	LIST_ENTRY(specificdata_container) sc_list;
8531adc576Sthorpej 	void *		sc_data[];	/* variable length */
8631adc576Sthorpej };
8731adc576Sthorpej 
8831adc576Sthorpej #define	SPECIFICDATA_CONTAINER_BYTESIZE(n)		\
8931adc576Sthorpej 	(sizeof(struct specificdata_container) + ((n) * sizeof(void *)))
9031adc576Sthorpej 
9131adc576Sthorpej struct specificdata_domain {
929abeea58Sad 	kmutex_t	sd_lock;
9331adc576Sthorpej 	unsigned int	sd_nkey;
9431adc576Sthorpej 	LIST_HEAD(, specificdata_container) sd_list;
9531adc576Sthorpej 	specificdata_key_impl *sd_keys;
9631adc576Sthorpej };
9731adc576Sthorpej 
9831adc576Sthorpej static void
specificdata_container_link(specificdata_domain_t sd,specificdata_container_t sc)9931adc576Sthorpej specificdata_container_link(specificdata_domain_t sd,
10031adc576Sthorpej 			    specificdata_container_t sc)
10131adc576Sthorpej {
10231adc576Sthorpej 
10331adc576Sthorpej 	LIST_INSERT_HEAD(&sd->sd_list, sc, sc_list);
10431adc576Sthorpej }
10531adc576Sthorpej 
10631adc576Sthorpej static void
specificdata_container_unlink(specificdata_domain_t sd,specificdata_container_t sc)1071a7bc55dSyamt specificdata_container_unlink(specificdata_domain_t sd,
10831adc576Sthorpej 			      specificdata_container_t sc)
10931adc576Sthorpej {
11031adc576Sthorpej 
11131adc576Sthorpej 	LIST_REMOVE(sc, sc_list);
11231adc576Sthorpej }
11331adc576Sthorpej 
11431adc576Sthorpej static void
specificdata_destroy_datum(specificdata_domain_t sd,specificdata_container_t sc,specificdata_key_t key)11531adc576Sthorpej specificdata_destroy_datum(specificdata_domain_t sd,
11631adc576Sthorpej 			   specificdata_container_t sc, specificdata_key_t key)
11731adc576Sthorpej {
11831adc576Sthorpej 	specificdata_dtor_t dtor;
11931adc576Sthorpej 	void *data;
12031adc576Sthorpej 
12131adc576Sthorpej 	if (key >= sc->sc_nkey)
12231adc576Sthorpej 		return;
12331adc576Sthorpej 
12431adc576Sthorpej 	KASSERT(key < sd->sd_nkey);
12531adc576Sthorpej 
12631adc576Sthorpej 	data = sc->sc_data[key];
12731adc576Sthorpej 	dtor = sd->sd_keys[key].ski_dtor;
12831adc576Sthorpej 
12931adc576Sthorpej 	if (dtor != NULL) {
13031adc576Sthorpej 		if (data != NULL) {
13131adc576Sthorpej 			sc->sc_data[key] = NULL;
13231adc576Sthorpej 			(*dtor)(data);
13331adc576Sthorpej 		}
13431adc576Sthorpej 	} else {
13531adc576Sthorpej 		KASSERT(data == NULL);
13631adc576Sthorpej 	}
13731adc576Sthorpej }
13831adc576Sthorpej 
13931adc576Sthorpej static void
specificdata_noop_dtor(void * data)1401a7bc55dSyamt specificdata_noop_dtor(void *data)
14131adc576Sthorpej {
14231adc576Sthorpej 
14331adc576Sthorpej 	/* nothing */
14431adc576Sthorpej }
14531adc576Sthorpej 
14631adc576Sthorpej /*
14731adc576Sthorpej  * specificdata_domain_create --
148fe9f141dSelad  *	Create a specificdata domain.
14931adc576Sthorpej  */
15031adc576Sthorpej specificdata_domain_t
specificdata_domain_create(void)15131adc576Sthorpej specificdata_domain_create(void)
15231adc576Sthorpej {
15331adc576Sthorpej 	specificdata_domain_t sd;
15431adc576Sthorpej 
15531adc576Sthorpej 	sd = kmem_zalloc(sizeof(*sd), KM_SLEEP);
1569abeea58Sad 	mutex_init(&sd->sd_lock, MUTEX_DEFAULT, IPL_NONE);
15731adc576Sthorpej 	LIST_INIT(&sd->sd_list);
15831adc576Sthorpej 
15931adc576Sthorpej 	return (sd);
16031adc576Sthorpej }
16131adc576Sthorpej 
16231adc576Sthorpej /*
16331adc576Sthorpej  * specificdata_domain_delete --
164fe9f141dSelad  *	Destroy a specificdata domain.
16531adc576Sthorpej  */
16631adc576Sthorpej void
specificdata_domain_delete(specificdata_domain_t sd)1671a7bc55dSyamt specificdata_domain_delete(specificdata_domain_t sd)
16831adc576Sthorpej {
16931adc576Sthorpej 
17031adc576Sthorpej 	panic("specificdata_domain_delete: not implemented");
17131adc576Sthorpej }
17231adc576Sthorpej 
17331adc576Sthorpej /*
17431adc576Sthorpej  * specificdata_key_create --
17531adc576Sthorpej  *	Create a specificdata key for a domain.
17631adc576Sthorpej  *
17731adc576Sthorpej  *	Note: This is a rare operation.
17831adc576Sthorpej  */
17931adc576Sthorpej int
specificdata_key_create(specificdata_domain_t sd,specificdata_key_t * keyp,specificdata_dtor_t dtor)18031adc576Sthorpej specificdata_key_create(specificdata_domain_t sd, specificdata_key_t *keyp,
18131adc576Sthorpej 			specificdata_dtor_t dtor)
18231adc576Sthorpej {
18331adc576Sthorpej 	specificdata_key_impl *newkeys;
18431adc576Sthorpej 	specificdata_key_t key = 0;
18531adc576Sthorpej 	size_t nsz;
18631adc576Sthorpej 
187a67bae0bSyamt 	ASSERT_SLEEPABLE();
18831adc576Sthorpej 
18931adc576Sthorpej 	if (dtor == NULL)
19031adc576Sthorpej 		dtor = specificdata_noop_dtor;
19131adc576Sthorpej 
1929abeea58Sad 	mutex_enter(&sd->sd_lock);
19331adc576Sthorpej 
19431adc576Sthorpej 	if (sd->sd_keys == NULL)
19531adc576Sthorpej 		goto needalloc;
19631adc576Sthorpej 
19731adc576Sthorpej 	for (; key < sd->sd_nkey; key++) {
19831adc576Sthorpej 		if (sd->sd_keys[key].ski_dtor == NULL)
19931adc576Sthorpej 			goto gotit;
20031adc576Sthorpej 	}
20131adc576Sthorpej 
20231adc576Sthorpej  needalloc:
20331adc576Sthorpej 	nsz = (sd->sd_nkey + 1) * sizeof(*newkeys);
2049abeea58Sad 	/* XXXSMP allocating memory while holding a lock. */
20531adc576Sthorpej 	newkeys = kmem_zalloc(nsz, KM_SLEEP);
20631adc576Sthorpej 	if (sd->sd_keys != NULL) {
20731adc576Sthorpej 		size_t osz = sd->sd_nkey * sizeof(*newkeys);
20831adc576Sthorpej 		memcpy(newkeys, sd->sd_keys, osz);
20931adc576Sthorpej 		kmem_free(sd->sd_keys, osz);
21031adc576Sthorpej 	}
21131adc576Sthorpej 	sd->sd_keys = newkeys;
21231adc576Sthorpej 	sd->sd_nkey++;
21331adc576Sthorpej  gotit:
21431adc576Sthorpej 	sd->sd_keys[key].ski_dtor = dtor;
21531adc576Sthorpej 
2169abeea58Sad 	mutex_exit(&sd->sd_lock);
21731adc576Sthorpej 
21831adc576Sthorpej 	*keyp = key;
21931adc576Sthorpej 	return (0);
22031adc576Sthorpej }
22131adc576Sthorpej 
22231adc576Sthorpej /*
22331adc576Sthorpej  * specificdata_key_delete --
22431adc576Sthorpej  *	Destroy a specificdata key for a domain.
22531adc576Sthorpej  *
22631adc576Sthorpej  *	Note: This is a rare operation.
22731adc576Sthorpej  */
22831adc576Sthorpej void
specificdata_key_delete(specificdata_domain_t sd,specificdata_key_t key)22931adc576Sthorpej specificdata_key_delete(specificdata_domain_t sd, specificdata_key_t key)
23031adc576Sthorpej {
23131adc576Sthorpej 	specificdata_container_t sc;
23231adc576Sthorpej 
2339abeea58Sad 	mutex_enter(&sd->sd_lock);
23431adc576Sthorpej 
23531adc576Sthorpej 	if (key >= sd->sd_nkey)
23631adc576Sthorpej 		goto out;
23731adc576Sthorpej 
23831adc576Sthorpej 	/*
23931adc576Sthorpej 	 * Traverse all of the specificdata containers in the domain
24031adc576Sthorpej 	 * and the destroy the datum for the dying key.
24131adc576Sthorpej 	 */
24231adc576Sthorpej 	LIST_FOREACH(sc, &sd->sd_list, sc_list) {
24331adc576Sthorpej 		specificdata_destroy_datum(sd, sc, key);
24431adc576Sthorpej 	}
24531adc576Sthorpej 
24631adc576Sthorpej 	sd->sd_keys[key].ski_dtor = NULL;
24731adc576Sthorpej 
24831adc576Sthorpej  out:
2499abeea58Sad 	mutex_exit(&sd->sd_lock);
25031adc576Sthorpej }
25131adc576Sthorpej 
25231adc576Sthorpej /*
25331adc576Sthorpej  * specificdata_init --
25431adc576Sthorpej  *	Initialize a specificdata container for operation in the
25531adc576Sthorpej  *	specified domain.
25631adc576Sthorpej  */
25731adc576Sthorpej int
specificdata_init(specificdata_domain_t sd,specificdata_reference * ref)2581a7bc55dSyamt specificdata_init(specificdata_domain_t sd, specificdata_reference *ref)
25931adc576Sthorpej {
26031adc576Sthorpej 
26131adc576Sthorpej 	/*
26231adc576Sthorpej 	 * Just NULL-out the container pointer; we'll allocate the
26331adc576Sthorpej 	 * container the first time specificdata is put into it.
26431adc576Sthorpej 	 */
26531adc576Sthorpej 	ref->specdataref_container = NULL;
266d18c6ca4Sad 	mutex_init(&ref->specdataref_lock, MUTEX_DEFAULT, IPL_NONE);
26731adc576Sthorpej 
26831adc576Sthorpej 	return (0);
26931adc576Sthorpej }
27031adc576Sthorpej 
27131adc576Sthorpej /*
27231adc576Sthorpej  * specificdata_fini --
27331adc576Sthorpej  *	Destroy a specificdata container.  We destroy all of the datums
27431adc576Sthorpej  *	stuffed into the container just as if the key were destroyed.
27531adc576Sthorpej  */
27631adc576Sthorpej void
specificdata_fini(specificdata_domain_t sd,specificdata_reference * ref)27731adc576Sthorpej specificdata_fini(specificdata_domain_t sd, specificdata_reference *ref)
27831adc576Sthorpej {
27931adc576Sthorpej 	specificdata_container_t sc;
28031adc576Sthorpej 	specificdata_key_t key;
28131adc576Sthorpej 
282a67bae0bSyamt 	ASSERT_SLEEPABLE();
28331adc576Sthorpej 
284d18c6ca4Sad 	mutex_destroy(&ref->specdataref_lock);
285d18c6ca4Sad 
28631adc576Sthorpej 	sc = ref->specdataref_container;
28731adc576Sthorpej 	if (sc == NULL)
28831adc576Sthorpej 		return;
28931adc576Sthorpej 	ref->specdataref_container = NULL;
29031adc576Sthorpej 
2919abeea58Sad 	mutex_enter(&sd->sd_lock);
29231adc576Sthorpej 
29331adc576Sthorpej 	specificdata_container_unlink(sd, sc);
29431adc576Sthorpej 	for (key = 0; key < sc->sc_nkey; key++) {
29531adc576Sthorpej 		specificdata_destroy_datum(sd, sc, key);
29631adc576Sthorpej 	}
29731adc576Sthorpej 
2989abeea58Sad 	mutex_exit(&sd->sd_lock);
29931adc576Sthorpej 
30031adc576Sthorpej 	kmem_free(sc, SPECIFICDATA_CONTAINER_BYTESIZE(sc->sc_nkey));
30131adc576Sthorpej }
30231adc576Sthorpej 
30331adc576Sthorpej /*
30431adc576Sthorpej  * specificdata_getspecific --
30531adc576Sthorpej  *	Get a datum from a container.
30631adc576Sthorpej  */
30731adc576Sthorpej void *
specificdata_getspecific(specificdata_domain_t sd,specificdata_reference * ref,specificdata_key_t key)3081a7bc55dSyamt specificdata_getspecific(specificdata_domain_t sd, specificdata_reference *ref,
3091a7bc55dSyamt 			 specificdata_key_t key)
31031adc576Sthorpej {
31131adc576Sthorpej 	specificdata_container_t sc;
31231adc576Sthorpej 	void *data = NULL;
31331adc576Sthorpej 
314d18c6ca4Sad 	mutex_enter(&ref->specdataref_lock);
31531adc576Sthorpej 
31631adc576Sthorpej 	sc = ref->specdataref_container;
31731adc576Sthorpej 	if (sc != NULL && key < sc->sc_nkey)
31831adc576Sthorpej 		data = sc->sc_data[key];
31931adc576Sthorpej 
320d18c6ca4Sad 	mutex_exit(&ref->specdataref_lock);
32131adc576Sthorpej 
32231adc576Sthorpej 	return (data);
32331adc576Sthorpej }
32431adc576Sthorpej 
32531adc576Sthorpej /*
32631adc576Sthorpej  * specificdata_getspecific_unlocked --
32731adc576Sthorpej  *	Get a datum from a container in a lockless fashion.
32831adc576Sthorpej  *
32931adc576Sthorpej  *	Note: When using this routine, care must be taken to ensure
33031adc576Sthorpej  *	that no other thread could cause the specificdata_reference
33131adc576Sthorpej  *	to become invalid (i.e. point at the wrong container) by
33231adc576Sthorpej  *	issuing a setspecific call or destroying the container.
33331adc576Sthorpej  */
33431adc576Sthorpej void *
specificdata_getspecific_unlocked(specificdata_domain_t sd,specificdata_reference * ref,specificdata_key_t key)3351a7bc55dSyamt specificdata_getspecific_unlocked(specificdata_domain_t sd,
33631adc576Sthorpej 				  specificdata_reference *ref,
33731adc576Sthorpej 				  specificdata_key_t key)
33831adc576Sthorpej {
33931adc576Sthorpej 	specificdata_container_t sc;
34031adc576Sthorpej 
34131adc576Sthorpej 	sc = ref->specdataref_container;
34231adc576Sthorpej 	if (sc != NULL && key < sc->sc_nkey)
34331adc576Sthorpej 		return (sc->sc_data[key]);
34431adc576Sthorpej 
34531adc576Sthorpej 	return (NULL);
34631adc576Sthorpej }
34731adc576Sthorpej 
3488ea60a7fShannken /*
3498ea60a7fShannken  * specificdata_setspecific --
3508ea60a7fShannken  *      Put a datum into a container.
3518ea60a7fShannken  */
3528ea60a7fShannken void
specificdata_setspecific(specificdata_domain_t sd,specificdata_reference * ref,specificdata_key_t key,void * data)3538ea60a7fShannken specificdata_setspecific(specificdata_domain_t sd,
354e160c7cdSthorpej 			 specificdata_reference *ref,
3558ea60a7fShannken 			 specificdata_key_t key, void *data)
35631adc576Sthorpej {
35731adc576Sthorpej 	specificdata_container_t sc, newsc;
35831adc576Sthorpej 	size_t newnkey, sz;
35931adc576Sthorpej 
360a67bae0bSyamt 	ASSERT_SLEEPABLE();
36131adc576Sthorpej 
362d18c6ca4Sad 	mutex_enter(&ref->specdataref_lock);
36331adc576Sthorpej 
36431adc576Sthorpej 	sc = ref->specdataref_container;
36531adc576Sthorpej 	if (__predict_true(sc != NULL && key < sc->sc_nkey)) {
36631adc576Sthorpej 		sc->sc_data[key] = data;
367d18c6ca4Sad 		mutex_exit(&ref->specdataref_lock);
3688ea60a7fShannken 		return;
36931adc576Sthorpej 	}
37031adc576Sthorpej 
371d18c6ca4Sad 	mutex_exit(&ref->specdataref_lock);
37231adc576Sthorpej 
37331adc576Sthorpej 	/*
37431adc576Sthorpej 	 * Slow path: need to resize.
37531adc576Sthorpej 	 */
37631adc576Sthorpej 
3779abeea58Sad 	mutex_enter(&sd->sd_lock);
37831adc576Sthorpej 	newnkey = sd->sd_nkey;
37931adc576Sthorpej 	if (key >= newnkey) {
3809abeea58Sad 		mutex_exit(&sd->sd_lock);
38131adc576Sthorpej 		panic("specificdata_setspecific");
38231adc576Sthorpej 	}
38331adc576Sthorpej 	sz = SPECIFICDATA_CONTAINER_BYTESIZE(newnkey);
3848ea60a7fShannken 	newsc = kmem_zalloc(sz, KM_SLEEP);
38531adc576Sthorpej 	newsc->sc_nkey = newnkey;
38631adc576Sthorpej 
387d18c6ca4Sad 	mutex_enter(&ref->specdataref_lock);
38831adc576Sthorpej 
38931adc576Sthorpej 	sc = ref->specdataref_container;
39031adc576Sthorpej 	if (sc != NULL) {
39131adc576Sthorpej 		if (key < sc->sc_nkey) {
39231adc576Sthorpej 			/*
39331adc576Sthorpej 			 * Someone beat us to the punch.  Unwind and put
39431adc576Sthorpej 			 * the object into the now large enough container.
39531adc576Sthorpej 			 */
39631adc576Sthorpej 			sc->sc_data[key] = data;
397d18c6ca4Sad 			mutex_exit(&ref->specdataref_lock);
3989abeea58Sad 			mutex_exit(&sd->sd_lock);
39931adc576Sthorpej 			kmem_free(newsc, sz);
4008ea60a7fShannken 			return;
40131adc576Sthorpej 		}
40231adc576Sthorpej 		specificdata_container_unlink(sd, sc);
40331adc576Sthorpej 		memcpy(newsc->sc_data, sc->sc_data,
40431adc576Sthorpej 		       sc->sc_nkey * sizeof(void *));
40531adc576Sthorpej 	}
40631adc576Sthorpej 	newsc->sc_data[key] = data;
40731adc576Sthorpej 	specificdata_container_link(sd, newsc);
40831adc576Sthorpej 	ref->specdataref_container = newsc;
40931adc576Sthorpej 
410d18c6ca4Sad 	mutex_exit(&ref->specdataref_lock);
4119abeea58Sad 	mutex_exit(&sd->sd_lock);
41231adc576Sthorpej 
41331adc576Sthorpej 	if (sc != NULL)
41431adc576Sthorpej 		kmem_free(sc, SPECIFICDATA_CONTAINER_BYTESIZE(sc->sc_nkey));
41531adc576Sthorpej }
416