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