xref: /illumos-gate/usr/src/uts/common/io/sad_conf.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
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