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
sad_hash_alg(void * hash_data,mod_hash_key_t key)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
sad_hash_keycmp(mod_hash_key_t key1,mod_hash_key_t key2)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
sad_hash_free_value(mod_hash_key_t key,mod_hash_val_t * val,void * arg)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
sad_apc_verify(struct apcommon * apc)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
sad_ap_verify(struct autopush * ap)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 *
sad_ap_alloc(void)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
sad_ap_rele(struct autopush * ap,str_stack_t * ss)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
sad_ap_insert(struct autopush * ap,str_stack_t * ss)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
sad_ap_remove(struct autopush * ap,str_stack_t * ss)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 *
sad_ap_find(struct apcommon * apc,str_stack_t * ss)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 *
sad_ap_find_by_dev(dev_t dev,str_stack_t * ss)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
sad_initspace(str_stack_t * ss)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
sad_freespace(str_stack_t * ss)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