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