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