1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Config dependent data structures for the Streams Administrative Driver 28 * (or "Ballad of the SAD Cafe"). 29 */ 30 #include <sys/types.h> 31 #include <sys/conf.h> 32 #include <sys/stream.h> 33 #include <sys/strsubr.h> 34 #include <sys/sad.h> 35 #include <sys/kmem.h> 36 #include <sys/sysmacros.h> 37 38 /* 39 * Currently we store all the sad data in a hash table keyed by major 40 * number. This is far from ideal. It means that if a single device 41 * starts using lots of SAP_ONE entries all its entries will hash 42 * to the same bucket and we'll get very long chains for that bucket. 43 * 44 * Unfortunately, it's not possible to hash by a different key or to easily 45 * break up our one hash into seperate hashs. The reason is because 46 * the hash contains mixed data types. Ie, it has three different 47 * types of autopush nodes in it: SAP_ALL, SAP_RANGE, SAP_ONE. Not 48 * only does the hash table contain nodes of different types, but we 49 * have to be able to search the table with a node of one type that 50 * might match another node with a different type. (ie, we might search 51 * for a SAP_ONE node with a value that matches a SAP_ALL node in the 52 * hash, or vice versa.) 53 * 54 * An ideal solution would probably be an AVL tree sorted by major 55 * numbers. Each node in the AVL tree would have the following optional 56 * data associated with it: 57 * - a single SAP_ALL autopush node 58 * - an or avl tree or hash table of SAP_RANGE and SAP_ONE autopush 59 * nodes indexed by minor numbers. perhaps two separate tables, 60 * one for each type of autopush nodes. 61 * 62 * Note that regardless of how the data is stored there can't be any overlap 63 * stored between autopush nodes. For example, if there is a SAP_ALL node 64 * for a given major number then there can't be any SAP_RANGE or SAP_ONE 65 * nodes for that same major number. 66 */ 67 68 /* 69 * Private Internal Interfaces 70 */ 71 /*ARGSUSED*/ 72 static uint_t 73 sad_hash_alg(void *hash_data, mod_hash_key_t key) 74 { 75 struct apcommon *apc = (struct apcommon *)key; 76 77 ASSERT(sad_apc_verify(apc) == 0); 78 return (apc->apc_major); 79 } 80 81 /* 82 * Compare hash keys based off of major, minor, lastminor, and type. 83 */ 84 static int 85 sad_hash_keycmp(mod_hash_key_t key1, mod_hash_key_t key2) 86 { 87 struct apcommon *apc1 = (struct apcommon *)key1; 88 struct apcommon *apc2 = (struct apcommon *)key2; 89 90 ASSERT(sad_apc_verify(apc1) == 0); 91 ASSERT(sad_apc_verify(apc2) == 0); 92 93 /* Filter out cases where the major number doesn't match. */ 94 if (apc1->apc_major != apc2->apc_major) 95 return (1); 96 97 /* If either type is SAP_ALL then we're done. */ 98 if ((apc1->apc_cmd == SAP_ALL) || (apc2->apc_cmd == SAP_ALL)) 99 return (0); 100 101 /* Deal with the case where both types are SAP_ONE. */ 102 if ((apc1->apc_cmd == SAP_ONE) && (apc2->apc_cmd == SAP_ONE)) { 103 /* Check if the minor numbers match. */ 104 return (apc1->apc_minor != apc2->apc_minor); 105 } 106 107 /* Deal with the case where both types are SAP_RANGE. */ 108 if ((apc1->apc_cmd == SAP_RANGE) && (apc2->apc_cmd == SAP_RANGE)) { 109 /* Check for overlapping ranges. */ 110 if ((apc1->apc_lastminor < apc2->apc_minor) || 111 (apc1->apc_minor > apc2->apc_lastminor)) 112 return (1); 113 return (0); 114 } 115 116 /* 117 * We know that one type is SAP_ONE and the other is SAP_RANGE. 118 * So now let's do range matching. 119 */ 120 if (apc1->apc_cmd == SAP_RANGE) { 121 ASSERT(apc2->apc_cmd == SAP_ONE); 122 if ((apc1->apc_lastminor < apc2->apc_minor) || 123 (apc1->apc_minor > apc2->apc_minor)) 124 return (1); 125 } else { 126 ASSERT(apc1->apc_cmd == SAP_ONE); 127 ASSERT(apc2->apc_cmd == SAP_RANGE); 128 if ((apc1->apc_minor < apc2->apc_minor) || 129 (apc1->apc_minor > apc2->apc_lastminor)) 130 return (1); 131 } 132 return (0); 133 } 134 135 /*ARGSUSED*/ 136 static uint_t 137 sad_hash_free_value(mod_hash_key_t key, mod_hash_val_t *val, void *arg) 138 { 139 struct autopush *ap = (struct autopush *)val; 140 141 ASSERT(ap->ap_cnt > 0); 142 if (--(ap->ap_cnt) == 0) 143 kmem_free(ap, sizeof (struct autopush)); 144 145 return (MH_WALK_CONTINUE); 146 } 147 148 /* 149 * External Interfaces 150 */ 151 int 152 sad_apc_verify(struct apcommon *apc) 153 { 154 /* sanity check the number of modules to push */ 155 if ((apc->apc_npush == 0) || (apc->apc_npush > MAXAPUSH) || 156 (apc->apc_npush > nstrpush)) 157 return (EINVAL); 158 159 /* Check for NODEV major vaule */ 160 if (apc->apc_major == -1) 161 return (EINVAL); 162 163 switch (apc->apc_cmd) { 164 case SAP_ALL: 165 case SAP_ONE: 166 /* 167 * Really, we'd like to be strict here and make sure that 168 * apc_lastminor is 0 (since setting apc_lastminor for 169 * SAP_ALL and SAP_ONE commands doesn't make any sense), 170 * but we can't since historically apc_lastminor has been 171 * silently ignored for non-SAP_RANGE commands. 172 */ 173 break; 174 case SAP_RANGE: 175 if (apc->apc_lastminor <= apc->apc_minor) 176 return (ERANGE); 177 break; 178 default: 179 return (EINVAL); 180 } 181 return (0); 182 } 183 184 int 185 sad_ap_verify(struct autopush *ap) 186 { 187 int ret, i; 188 189 if ((ret = sad_apc_verify(&ap->ap_common)) != 0) 190 return (ret); 191 192 /* 193 * Validate that the specified list of modules exist. Note that 194 * ap_npush has already been sanity checked by sad_apc_verify(). 195 */ 196 for (i = 0; i < ap->ap_npush; i++) { 197 ap->ap_list[i][FMNAMESZ] = '\0'; 198 if (fmodsw_find(ap->ap_list[i], FMODSW_LOAD) == NULL) 199 return (EINVAL); 200 } 201 return (0); 202 } 203 204 struct autopush * 205 sad_ap_alloc(void) 206 { 207 struct autopush *ap_new; 208 209 ap_new = kmem_zalloc(sizeof (struct autopush), KM_SLEEP); 210 ap_new->ap_cnt = 1; 211 return (ap_new); 212 } 213 214 void 215 sad_ap_rele(struct autopush *ap, str_stack_t *ss) 216 { 217 mutex_enter(&ss->ss_sad_lock); 218 ASSERT(ap->ap_cnt > 0); 219 if (--(ap->ap_cnt) == 0) { 220 mutex_exit(&ss->ss_sad_lock); 221 kmem_free(ap, sizeof (struct autopush)); 222 } else { 223 mutex_exit(&ss->ss_sad_lock); 224 } 225 } 226 227 void 228 sad_ap_insert(struct autopush *ap, str_stack_t *ss) 229 { 230 ASSERT(MUTEX_HELD(&ss->ss_sad_lock)); 231 ASSERT(sad_apc_verify(&ap->ap_common) == 0); 232 ASSERT(sad_ap_find(&ap->ap_common, ss) == NULL); 233 (void) mod_hash_insert(ss->ss_sad_hash, &ap->ap_common, ap); 234 } 235 236 void 237 sad_ap_remove(struct autopush *ap, str_stack_t *ss) 238 { 239 struct autopush *ap_removed = NULL; 240 241 ASSERT(MUTEX_HELD(&ss->ss_sad_lock)); 242 (void) mod_hash_remove(ss->ss_sad_hash, &ap->ap_common, 243 (mod_hash_val_t *)&ap_removed); 244 ASSERT(ap == ap_removed); 245 } 246 247 struct autopush * 248 sad_ap_find(struct apcommon *apc, str_stack_t *ss) 249 { 250 struct autopush *ap_result = NULL; 251 252 ASSERT(MUTEX_HELD(&ss->ss_sad_lock)); 253 ASSERT(sad_apc_verify(apc) == 0); 254 255 (void) mod_hash_find(ss->ss_sad_hash, apc, 256 (mod_hash_val_t *)&ap_result); 257 if (ap_result != NULL) 258 ap_result->ap_cnt++; 259 return (ap_result); 260 } 261 262 struct autopush * 263 sad_ap_find_by_dev(dev_t dev, str_stack_t *ss) 264 { 265 struct apcommon apc; 266 struct autopush *ap_result; 267 268 ASSERT(MUTEX_NOT_HELD(&ss->ss_sad_lock)); 269 270 /* prepare an apcommon structure to search with */ 271 apc.apc_cmd = SAP_ONE; 272 apc.apc_major = getmajor(dev); 273 apc.apc_minor = getminor(dev); 274 275 /* 276 * the following values must be set but initialized to have a 277 * valid apcommon struct, but since we're only using this 278 * structure to do a query the values are never actually used. 279 */ 280 apc.apc_npush = 1; 281 apc.apc_lastminor = 0; 282 283 mutex_enter(&ss->ss_sad_lock); 284 ap_result = sad_ap_find(&apc, ss); 285 mutex_exit(&ss->ss_sad_lock); 286 return (ap_result); 287 } 288 289 void 290 sad_initspace(str_stack_t *ss) 291 { 292 mutex_init(&ss->ss_sad_lock, NULL, MUTEX_DEFAULT, NULL); 293 ss->ss_sad_hash_nchains = 127; 294 ss->ss_sadcnt = 16; 295 296 ss->ss_saddev = kmem_zalloc(ss->ss_sadcnt * sizeof (struct saddev), 297 KM_SLEEP); 298 ss->ss_sad_hash = mod_hash_create_extended("sad_hash", 299 ss->ss_sad_hash_nchains, mod_hash_null_keydtor, 300 mod_hash_null_valdtor, 301 sad_hash_alg, NULL, sad_hash_keycmp, KM_SLEEP); 302 } 303 304 void 305 sad_freespace(str_stack_t *ss) 306 { 307 kmem_free(ss->ss_saddev, ss->ss_sadcnt * sizeof (struct saddev)); 308 ss->ss_saddev = NULL; 309 310 mutex_enter(&ss->ss_sad_lock); 311 mod_hash_walk(ss->ss_sad_hash, sad_hash_free_value, NULL); 312 mod_hash_destroy_hash(ss->ss_sad_hash); 313 ss->ss_sad_hash = NULL; 314 mutex_exit(&ss->ss_sad_lock); 315 316 mutex_destroy(&ss->ss_sad_lock); 317 } 318