1*8329232eSGordon Ross 
2*8329232eSGordon Ross /*
3*8329232eSGordon Ross  * CDDL HEADER START
4*8329232eSGordon Ross  *
5*8329232eSGordon Ross  * The contents of this file are subject to the terms of the
6*8329232eSGordon Ross  * Common Development and Distribution License (the "License").
7*8329232eSGordon Ross  * You may not use this file except in compliance with the License.
8*8329232eSGordon Ross  *
9*8329232eSGordon Ross  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*8329232eSGordon Ross  * or http://www.opensolaris.org/os/licensing.
11*8329232eSGordon Ross  * See the License for the specific language governing permissions
12*8329232eSGordon Ross  * and limitations under the License.
13*8329232eSGordon Ross  *
14*8329232eSGordon Ross  * When distributing Covered Code, include this CDDL HEADER in each
15*8329232eSGordon Ross  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*8329232eSGordon Ross  * If applicable, add the following below this CDDL HEADER, with the
17*8329232eSGordon Ross  * fields enclosed by brackets "[]" replaced with your own identifying
18*8329232eSGordon Ross  * information: Portions Copyright [yyyy] [name of copyright owner]
19*8329232eSGordon Ross  *
20*8329232eSGordon Ross  * CDDL HEADER END
21*8329232eSGordon Ross  */
22*8329232eSGordon Ross 
23*8329232eSGordon Ross /*
24*8329232eSGordon Ross  * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
25*8329232eSGordon Ross  * Copyright 2014 Garrett D'Amore <garrett@damore.org>
26*8329232eSGordon Ross  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
27*8329232eSGordon Ross  */
28*8329232eSGordon Ross 
29*8329232eSGordon Ross #include <sys/types.h>
30*8329232eSGordon Ross #include <sys/debug.h>
31*8329232eSGordon Ross #include <sys/sunddi.h>
32*8329232eSGordon Ross 
33*8329232eSGordon Ross #define	MIN_N_ITEMS	8
34*8329232eSGordon Ross 
35*8329232eSGordon Ross typedef struct i_ddi_soft_state {
36*8329232eSGordon Ross 	void		**array;	/* the array of pointers */
37*8329232eSGordon Ross 	kmutex_t	lock;		/* serialize access to this struct */
38*8329232eSGordon Ross 	size_t		size;		/* how many bytes per state struct */
39*8329232eSGordon Ross 	size_t		n_items;	/* how many structs herein */
40*8329232eSGordon Ross 	void		*next;		/* unused */
41*8329232eSGordon Ross } i_ddi_soft_state;
42*8329232eSGordon Ross 
43*8329232eSGordon Ross 
44*8329232eSGordon Ross void *
ddi_get_soft_state(void * state,int item)45*8329232eSGordon Ross ddi_get_soft_state(void *state, int item)
46*8329232eSGordon Ross {
47*8329232eSGordon Ross 	i_ddi_soft_state	*ss = (i_ddi_soft_state *)state;
48*8329232eSGordon Ross 	void *ret = NULL;
49*8329232eSGordon Ross 
50*8329232eSGordon Ross 	ASSERT((ss != NULL) && (item >= 0));
51*8329232eSGordon Ross 
52*8329232eSGordon Ross 	mutex_enter(&ss->lock);
53*8329232eSGordon Ross 
54*8329232eSGordon Ross 	if (item < ss->n_items && ss->array != NULL)
55*8329232eSGordon Ross 		ret = ss->array[item];
56*8329232eSGordon Ross 
57*8329232eSGordon Ross 	mutex_exit(&ss->lock);
58*8329232eSGordon Ross 
59*8329232eSGordon Ross 	return (ret);
60*8329232eSGordon Ross }
61*8329232eSGordon Ross 
62*8329232eSGordon Ross 
63*8329232eSGordon Ross int
ddi_soft_state_init(void ** state_p,size_t size,size_t n_items)64*8329232eSGordon Ross ddi_soft_state_init(void **state_p, size_t size, size_t n_items)
65*8329232eSGordon Ross {
66*8329232eSGordon Ross 	i_ddi_soft_state	*ss;
67*8329232eSGordon Ross 
68*8329232eSGordon Ross 	if (state_p == NULL || size == 0)
69*8329232eSGordon Ross 		return (EINVAL);
70*8329232eSGordon Ross 
71*8329232eSGordon Ross 	ss = kmem_zalloc(sizeof (*ss), KM_SLEEP);
72*8329232eSGordon Ross 	mutex_init(&ss->lock, NULL, MUTEX_DRIVER, NULL);
73*8329232eSGordon Ross 	ss->size = size;
74*8329232eSGordon Ross 
75*8329232eSGordon Ross 	if (n_items < MIN_N_ITEMS)
76*8329232eSGordon Ross 		ss->n_items = MIN_N_ITEMS;
77*8329232eSGordon Ross 	else {
78*8329232eSGordon Ross 		ss->n_items = n_items;
79*8329232eSGordon Ross 	}
80*8329232eSGordon Ross 
81*8329232eSGordon Ross 	ASSERT(ss->n_items >= n_items);
82*8329232eSGordon Ross 
83*8329232eSGordon Ross 	ss->array = kmem_zalloc(ss->n_items * sizeof (void *), KM_SLEEP);
84*8329232eSGordon Ross 
85*8329232eSGordon Ross 	*state_p = ss;
86*8329232eSGordon Ross 	return (0);
87*8329232eSGordon Ross }
88*8329232eSGordon Ross 
89*8329232eSGordon Ross /*
90*8329232eSGordon Ross  * Allocate a state structure of size 'size' to be associated
91*8329232eSGordon Ross  * with item 'item'.
92*8329232eSGordon Ross  *
93*8329232eSGordon Ross  * In this implementation, the array is extended to
94*8329232eSGordon Ross  * allow the requested offset, if needed.
95*8329232eSGordon Ross  */
96*8329232eSGordon Ross int
ddi_soft_state_zalloc(void * state,int item)97*8329232eSGordon Ross ddi_soft_state_zalloc(void *state, int item)
98*8329232eSGordon Ross {
99*8329232eSGordon Ross 	i_ddi_soft_state	*ss = (i_ddi_soft_state *)state;
100*8329232eSGordon Ross 	void			**array;
101*8329232eSGordon Ross 	void			*new_element;
102*8329232eSGordon Ross 
103*8329232eSGordon Ross 	if ((state == NULL) || (item < 0))
104*8329232eSGordon Ross 		return (DDI_FAILURE);
105*8329232eSGordon Ross 
106*8329232eSGordon Ross 	mutex_enter(&ss->lock);
107*8329232eSGordon Ross 	if (ss->size == 0) {
108*8329232eSGordon Ross 		mutex_exit(&ss->lock);
109*8329232eSGordon Ross 		cmn_err(CE_WARN, "ddi_soft_state_zalloc: bad handle: %s",
110*8329232eSGordon Ross 		    "fake");
111*8329232eSGordon Ross 		return (DDI_FAILURE);
112*8329232eSGordon Ross 	}
113*8329232eSGordon Ross 
114*8329232eSGordon Ross 	array = ss->array;	/* NULL if ss->n_items == 0 */
115*8329232eSGordon Ross 	ASSERT(ss->n_items != 0 && array != NULL);
116*8329232eSGordon Ross 
117*8329232eSGordon Ross 	/*
118*8329232eSGordon Ross 	 * refuse to tread on an existing element
119*8329232eSGordon Ross 	 */
120*8329232eSGordon Ross 	if (item < ss->n_items && array[item] != NULL) {
121*8329232eSGordon Ross 		mutex_exit(&ss->lock);
122*8329232eSGordon Ross 		return (DDI_FAILURE);
123*8329232eSGordon Ross 	}
124*8329232eSGordon Ross 
125*8329232eSGordon Ross 	/*
126*8329232eSGordon Ross 	 * Allocate a new element to plug in
127*8329232eSGordon Ross 	 */
128*8329232eSGordon Ross 	new_element = kmem_zalloc(ss->size, KM_SLEEP);
129*8329232eSGordon Ross 
130*8329232eSGordon Ross 	/*
131*8329232eSGordon Ross 	 * Check if the array is big enough, if not, grow it.
132*8329232eSGordon Ross 	 */
133*8329232eSGordon Ross 	if (item >= ss->n_items) {
134*8329232eSGordon Ross 		void			**new_array;
135*8329232eSGordon Ross 		size_t			new_n_items;
136*8329232eSGordon Ross 
137*8329232eSGordon Ross 		/*
138*8329232eSGordon Ross 		 * Allocate a new array of the right length, copy
139*8329232eSGordon Ross 		 * all the old pointers to the new array, then
140*8329232eSGordon Ross 		 * if it exists at all, put the old array on the
141*8329232eSGordon Ross 		 * dirty list.
142*8329232eSGordon Ross 		 *
143*8329232eSGordon Ross 		 * Note that we can't kmem_free() the old array.
144*8329232eSGordon Ross 		 *
145*8329232eSGordon Ross 		 * Why -- well the 'get' operation is 'mutex-free', so we
146*8329232eSGordon Ross 		 * can't easily catch a suspended thread that is just about
147*8329232eSGordon Ross 		 * to dereference the array we just grew out of.  So we
148*8329232eSGordon Ross 		 * cons up a header and put it on a list of 'dirty'
149*8329232eSGordon Ross 		 * pointer arrays.  (Dirty in the sense that there may
150*8329232eSGordon Ross 		 * be suspended threads somewhere that are in the middle
151*8329232eSGordon Ross 		 * of referencing them).  Fortunately, we -can- garbage
152*8329232eSGordon Ross 		 * collect it all at ddi_soft_state_fini time.
153*8329232eSGordon Ross 		 */
154*8329232eSGordon Ross 		new_n_items = ss->n_items;
155*8329232eSGordon Ross 		while (new_n_items < (1 + item))
156*8329232eSGordon Ross 			new_n_items <<= 1;	/* double array size .. */
157*8329232eSGordon Ross 
158*8329232eSGordon Ross 		ASSERT(new_n_items >= (1 + item));	/* sanity check! */
159*8329232eSGordon Ross 
160*8329232eSGordon Ross 		new_array = kmem_zalloc(new_n_items * sizeof (void *),
161*8329232eSGordon Ross 		    KM_SLEEP);
162*8329232eSGordon Ross 		/*
163*8329232eSGordon Ross 		 * Copy the pointers into the new array
164*8329232eSGordon Ross 		 */
165*8329232eSGordon Ross 		bcopy(array, new_array, ss->n_items * sizeof (void *));
166*8329232eSGordon Ross 
167*8329232eSGordon Ross 		/*
168*8329232eSGordon Ross 		 * Free the old array now.  Note that
169*8329232eSGordon Ross 		 * ddi_get_soft_state takes the mutex.
170*8329232eSGordon Ross 		 */
171*8329232eSGordon Ross 		kmem_free(ss->array, ss->n_items * sizeof (void *));
172*8329232eSGordon Ross 
173*8329232eSGordon Ross 		ss->array = (array = new_array);
174*8329232eSGordon Ross 		ss->n_items = new_n_items;
175*8329232eSGordon Ross 	}
176*8329232eSGordon Ross 
177*8329232eSGordon Ross 	ASSERT(array != NULL && item < ss->n_items && array[item] == NULL);
178*8329232eSGordon Ross 
179*8329232eSGordon Ross 	array[item] = new_element;
180*8329232eSGordon Ross 
181*8329232eSGordon Ross 	mutex_exit(&ss->lock);
182*8329232eSGordon Ross 	return (DDI_SUCCESS);
183*8329232eSGordon Ross }
184*8329232eSGordon Ross 
185*8329232eSGordon Ross void
ddi_soft_state_free(void * state,int item)186*8329232eSGordon Ross ddi_soft_state_free(void *state, int item)
187*8329232eSGordon Ross {
188*8329232eSGordon Ross 	i_ddi_soft_state	*ss = (i_ddi_soft_state *)state;
189*8329232eSGordon Ross 	void			**array;
190*8329232eSGordon Ross 	void			*element;
191*8329232eSGordon Ross 	static char		msg[] = "ddi_soft_state_free:";
192*8329232eSGordon Ross 
193*8329232eSGordon Ross 	if (ss == NULL) {
194*8329232eSGordon Ross 		cmn_err(CE_WARN, "%s null handle: %s",
195*8329232eSGordon Ross 		    msg, "fake");
196*8329232eSGordon Ross 		return;
197*8329232eSGordon Ross 	}
198*8329232eSGordon Ross 
199*8329232eSGordon Ross 	element = NULL;
200*8329232eSGordon Ross 
201*8329232eSGordon Ross 	mutex_enter(&ss->lock);
202*8329232eSGordon Ross 
203*8329232eSGordon Ross 	if ((array = ss->array) == NULL || ss->size == 0) {
204*8329232eSGordon Ross 		cmn_err(CE_WARN, "%s bad handle: %s",
205*8329232eSGordon Ross 		    msg, "fake");
206*8329232eSGordon Ross 	} else if (item < 0 || item >= ss->n_items) {
207*8329232eSGordon Ross 		cmn_err(CE_WARN, "%s item %d not in range [0..%lu]: %s",
208*8329232eSGordon Ross 		    msg, item, (ulong_t)ss->n_items - 1, "fake");
209*8329232eSGordon Ross 	} else if (array[item] != NULL) {
210*8329232eSGordon Ross 		element = array[item];
211*8329232eSGordon Ross 		array[item] = NULL;
212*8329232eSGordon Ross 	}
213*8329232eSGordon Ross 
214*8329232eSGordon Ross 	mutex_exit(&ss->lock);
215*8329232eSGordon Ross 
216*8329232eSGordon Ross 	if (element)
217*8329232eSGordon Ross 		kmem_free(element, ss->size);
218*8329232eSGordon Ross }
219*8329232eSGordon Ross 
220*8329232eSGordon Ross /*
221*8329232eSGordon Ross  * Free the entire set of pointers, and any
222*8329232eSGordon Ross  * soft state structures contained therein.
223*8329232eSGordon Ross  *
224*8329232eSGordon Ross  * Note that we don't grab the ss->lock mutex, even though
225*8329232eSGordon Ross  * we're inspecting the various fields of the data structure.
226*8329232eSGordon Ross  *
227*8329232eSGordon Ross  * There is an implicit assumption that this routine will
228*8329232eSGordon Ross  * never run concurrently with any of the above on this
229*8329232eSGordon Ross  * particular state structure i.e. by the time the driver
230*8329232eSGordon Ross  * calls this routine, there should be no other threads
231*8329232eSGordon Ross  * running in the driver.
232*8329232eSGordon Ross  */
233*8329232eSGordon Ross void
ddi_soft_state_fini(void ** state_p)234*8329232eSGordon Ross ddi_soft_state_fini(void **state_p)
235*8329232eSGordon Ross {
236*8329232eSGordon Ross 	i_ddi_soft_state	*ss;
237*8329232eSGordon Ross 	int			item;
238*8329232eSGordon Ross 	static char		msg[] = "ddi_soft_state_fini:";
239*8329232eSGordon Ross 
240*8329232eSGordon Ross 	if (state_p == NULL ||
241*8329232eSGordon Ross 	    (ss = (i_ddi_soft_state *)(*state_p)) == NULL) {
242*8329232eSGordon Ross 		cmn_err(CE_WARN, "%s null handle: %s",
243*8329232eSGordon Ross 		    msg, "fake");
244*8329232eSGordon Ross 		return;
245*8329232eSGordon Ross 	}
246*8329232eSGordon Ross 
247*8329232eSGordon Ross 	if (ss->size == 0) {
248*8329232eSGordon Ross 		cmn_err(CE_WARN, "%s bad handle: %s",
249*8329232eSGordon Ross 		    msg, "fake");
250*8329232eSGordon Ross 		return;
251*8329232eSGordon Ross 	}
252*8329232eSGordon Ross 
253*8329232eSGordon Ross 	if (ss->n_items > 0) {
254*8329232eSGordon Ross 		for (item = 0; item < ss->n_items; item++)
255*8329232eSGordon Ross 			ddi_soft_state_free(ss, item);
256*8329232eSGordon Ross 		kmem_free(ss->array, ss->n_items * sizeof (void *));
257*8329232eSGordon Ross 	}
258*8329232eSGordon Ross 
259*8329232eSGordon Ross 	mutex_destroy(&ss->lock);
260*8329232eSGordon Ross 	kmem_free(ss, sizeof (*ss));
261*8329232eSGordon Ross 
262*8329232eSGordon Ross 	*state_p = NULL;
263*8329232eSGordon Ross }
264