xref: /illumos-gate/usr/src/uts/common/io/sad_conf.c (revision f48205be)
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 2007 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 /*
138  * External Interfaces
139  */
140 int
141 sad_apc_verify(struct apcommon *apc)
142 {
143 	/* sanity check the number of modules to push */
144 	if ((apc->apc_npush == 0) || (apc->apc_npush > MAXAPUSH) ||
145 	    (apc->apc_npush > nstrpush))
146 		return (EINVAL);
147 
148 	/* Check for NODEV major vaule */
149 	if (apc->apc_major == -1)
150 		return (EINVAL);
151 
152 	switch (apc->apc_cmd) {
153 	case SAP_ALL:
154 	case SAP_ONE:
155 		/*
156 		 * Really, we'd like to be strict here and make sure that
157 		 * apc_lastminor is 0 (since setting apc_lastminor for
158 		 * SAP_ALL and SAP_ONE commands doesn't make any sense),
159 		 * but we can't since historically apc_lastminor has been
160 		 * silently ignored for non-SAP_RANGE commands.
161 		 */
162 		break;
163 	case SAP_RANGE:
164 		if (apc->apc_lastminor <= apc->apc_minor)
165 			return (ERANGE);
166 		break;
167 	default:
168 		return (EINVAL);
169 	}
170 	return (0);
171 }
172 
173 int
174 sad_ap_verify(struct autopush *ap)
175 {
176 	int ret, i;
177 
178 	if ((ret = sad_apc_verify(&ap->ap_common)) != 0)
179 		return (ret);
180 
181 	/*
182 	 * Validate that the specified list of modules exist.  Note that
183 	 * ap_npush has already been sanity checked by sad_apc_verify().
184 	 */
185 	for (i = 0; i < ap->ap_npush; i++) {
186 		ap->ap_list[i][FMNAMESZ] = '\0';
187 		if (fmodsw_find(ap->ap_list[i], FMODSW_LOAD) == NULL)
188 			return (EINVAL);
189 	}
190 	return (0);
191 }
192 
193 struct autopush *
194 sad_ap_alloc(void)
195 {
196 	struct autopush *ap_new;
197 
198 	ap_new = kmem_zalloc(sizeof (struct autopush), KM_SLEEP);
199 	ap_new->ap_cnt = 1;
200 	return (ap_new);
201 }
202 
203 void
204 sad_ap_rele(struct autopush *ap, str_stack_t *ss)
205 {
206 	mutex_enter(&ss->ss_sad_lock);
207 	ASSERT(ap->ap_cnt > 0);
208 	if (--(ap->ap_cnt) == 0) {
209 		mutex_exit(&ss->ss_sad_lock);
210 		kmem_free(ap, sizeof (struct autopush));
211 	} else {
212 		mutex_exit(&ss->ss_sad_lock);
213 	}
214 }
215 
216 void
217 sad_ap_insert(struct autopush *ap, str_stack_t *ss)
218 {
219 	ASSERT(MUTEX_HELD(&ss->ss_sad_lock));
220 	ASSERT(sad_apc_verify(&ap->ap_common) == 0);
221 	ASSERT(sad_ap_find(&ap->ap_common, ss) == NULL);
222 	(void) mod_hash_insert(ss->ss_sad_hash, &ap->ap_common, ap);
223 }
224 
225 void
226 sad_ap_remove(struct autopush *ap, str_stack_t *ss)
227 {
228 	struct autopush	*ap_removed = NULL;
229 
230 	ASSERT(MUTEX_HELD(&ss->ss_sad_lock));
231 	(void) mod_hash_remove(ss->ss_sad_hash, &ap->ap_common,
232 	    (mod_hash_val_t *)&ap_removed);
233 	ASSERT(ap == ap_removed);
234 }
235 
236 struct autopush *
237 sad_ap_find(struct apcommon *apc, str_stack_t *ss)
238 {
239 	struct autopush	*ap_result = NULL;
240 
241 	ASSERT(MUTEX_HELD(&ss->ss_sad_lock));
242 	ASSERT(sad_apc_verify(apc) == 0);
243 
244 	(void) mod_hash_find(ss->ss_sad_hash, apc,
245 	    (mod_hash_val_t *)&ap_result);
246 	if (ap_result != NULL)
247 		ap_result->ap_cnt++;
248 	return (ap_result);
249 }
250 
251 struct autopush *
252 sad_ap_find_by_dev(dev_t dev, str_stack_t *ss)
253 {
254 	struct apcommon	apc;
255 	struct autopush	*ap_result;
256 
257 	ASSERT(MUTEX_NOT_HELD(&ss->ss_sad_lock));
258 
259 	/* prepare an apcommon structure to search with */
260 	apc.apc_cmd = SAP_ONE;
261 	apc.apc_major = getmajor(dev);
262 	apc.apc_minor = getminor(dev);
263 
264 	/*
265 	 * the following values must be set but initialized to have a
266 	 * valid apcommon struct, but since we're only using this
267 	 * structure to do a query the values are never actually used.
268 	 */
269 	apc.apc_npush = 1;
270 	apc.apc_lastminor = 0;
271 
272 	mutex_enter(&ss->ss_sad_lock);
273 	ap_result = sad_ap_find(&apc, ss);
274 	mutex_exit(&ss->ss_sad_lock);
275 	return (ap_result);
276 }
277 
278 void
279 sad_initspace(str_stack_t *ss)
280 {
281 	mutex_init(&ss->ss_sad_lock, NULL, MUTEX_DEFAULT, NULL);
282 	ss->ss_sad_hash_nchains = 127;
283 	ss->ss_sadcnt = 16;
284 
285 	ss->ss_saddev = kmem_zalloc(ss->ss_sadcnt * sizeof (struct saddev),
286 	    KM_SLEEP);
287 	ss->ss_sad_hash = mod_hash_create_extended("sad_hash",
288 	    ss->ss_sad_hash_nchains, mod_hash_null_keydtor,
289 	    mod_hash_null_valdtor,
290 	    sad_hash_alg, NULL, sad_hash_keycmp, KM_SLEEP);
291 }
292 
293 void
294 sad_freespace(str_stack_t *ss)
295 {
296 	kmem_free(ss->ss_saddev, ss->ss_sadcnt * sizeof (struct saddev));
297 	ss->ss_saddev = NULL;
298 
299 	mod_hash_destroy_hash(ss->ss_sad_hash);
300 	ss->ss_sad_hash = NULL;
301 
302 	mutex_destroy(&ss->ss_sad_lock);
303 }
304