stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 1 | /* |
| 2 | * CDDL HEADER START |
| 3 | * |
| 4 | * The contents of this file are subject to the terms of the |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 5 | * Common Development and Distribution License (the "License"). |
| 6 | * You may not use this file except in compliance with the License. |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 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 | /* |
yz147064 | 2ea701a | 2008-03-18 20:15:41 -0700 | [diff] [blame] | 22 | * Copyright 2008 Sun Microsystems, Inc. All rights reserved. |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 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> |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 35 | #include <sys/strsubr.h> |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 36 | #include <sys/sad.h> |
| 37 | #include <sys/kmem.h> |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 38 | #include <sys/sysmacros.h> |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 39 | |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 40 | /* |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 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. |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 68 | */ |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 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 | |
yz147064 | 2ea701a | 2008-03-18 20:15:41 -0700 | [diff] [blame] | 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 | |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 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: |
edp | 5011064 | 2006-12-12 11:52:22 -0800 | [diff] [blame] | 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 | */ |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 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 |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 217 | sad_ap_rele(struct autopush *ap, str_stack_t *ss) |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 218 | { |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 219 | mutex_enter(&ss->ss_sad_lock); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 220 | ASSERT(ap->ap_cnt > 0); |
| 221 | if (--(ap->ap_cnt) == 0) { |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 222 | mutex_exit(&ss->ss_sad_lock); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 223 | kmem_free(ap, sizeof (struct autopush)); |
| 224 | } else { |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 225 | mutex_exit(&ss->ss_sad_lock); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 226 | } |
| 227 | } |
| 228 | |
| 229 | void |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 230 | sad_ap_insert(struct autopush *ap, str_stack_t *ss) |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 231 | { |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 232 | ASSERT(MUTEX_HELD(&ss->ss_sad_lock)); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 233 | ASSERT(sad_apc_verify(&ap->ap_common) == 0); |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 234 | ASSERT(sad_ap_find(&ap->ap_common, ss) == NULL); |
| 235 | (void) mod_hash_insert(ss->ss_sad_hash, &ap->ap_common, ap); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 236 | } |
| 237 | |
| 238 | void |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 239 | sad_ap_remove(struct autopush *ap, str_stack_t *ss) |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 240 | { |
| 241 | struct autopush *ap_removed = NULL; |
| 242 | |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 243 | ASSERT(MUTEX_HELD(&ss->ss_sad_lock)); |
| 244 | (void) mod_hash_remove(ss->ss_sad_hash, &ap->ap_common, |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 245 | (mod_hash_val_t *)&ap_removed); |
| 246 | ASSERT(ap == ap_removed); |
| 247 | } |
| 248 | |
| 249 | struct autopush * |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 250 | sad_ap_find(struct apcommon *apc, str_stack_t *ss) |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 251 | { |
| 252 | struct autopush *ap_result = NULL; |
| 253 | |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 254 | ASSERT(MUTEX_HELD(&ss->ss_sad_lock)); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 255 | ASSERT(sad_apc_verify(apc) == 0); |
| 256 | |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 257 | (void) mod_hash_find(ss->ss_sad_hash, apc, |
| 258 | (mod_hash_val_t *)&ap_result); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 259 | if (ap_result != NULL) |
| 260 | ap_result->ap_cnt++; |
| 261 | return (ap_result); |
| 262 | } |
| 263 | |
| 264 | struct autopush * |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 265 | sad_ap_find_by_dev(dev_t dev, str_stack_t *ss) |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 266 | { |
| 267 | struct apcommon apc; |
| 268 | struct autopush *ap_result; |
| 269 | |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 270 | ASSERT(MUTEX_NOT_HELD(&ss->ss_sad_lock)); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 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 | |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 285 | mutex_enter(&ss->ss_sad_lock); |
| 286 | ap_result = sad_ap_find(&apc, ss); |
| 287 | mutex_exit(&ss->ss_sad_lock); |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 288 | return (ap_result); |
| 289 | } |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 290 | |
| 291 | void |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 292 | sad_initspace(str_stack_t *ss) |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 293 | { |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 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, |
edp | 1110f38 | 2006-11-02 09:52:48 -0800 | [diff] [blame] | 303 | sad_hash_alg, NULL, sad_hash_keycmp, KM_SLEEP); |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 304 | } |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 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 | |
yz147064 | 2ea701a | 2008-03-18 20:15:41 -0700 | [diff] [blame] | 312 | mutex_enter(&ss->ss_sad_lock); |
| 313 | mod_hash_walk(ss->ss_sad_hash, sad_hash_free_value, NULL); |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 314 | mod_hash_destroy_hash(ss->ss_sad_hash); |
| 315 | ss->ss_sad_hash = NULL; |
yz147064 | 2ea701a | 2008-03-18 20:15:41 -0700 | [diff] [blame] | 316 | mutex_exit(&ss->ss_sad_lock); |
dh155122 | f4b3ec6 | 2007-01-19 16:59:38 -0800 | [diff] [blame] | 317 | |
| 318 | mutex_destroy(&ss->ss_sad_lock); |
| 319 | } |