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