xref: /illumos-gate/usr/src/uts/common/io/sad_conf.c (revision 60a3f738d56f92ae8b80e4b62a2331c6e1f2311f)
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 2006 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 struct saddev		*saddev;	/* sad device array */
41 int			sadcnt = 16;	/* number of sad devices */
42 
43 /*
44  * Currently we store all the sad data in a hash table keyed by major
45  * number.  This is far from ideal.  It means that if a single device
46  * starts using lots of SAP_ONE entries all its entries will hash
47  * to the same bucket and we'll get very long chains for that bucket.
48  *
49  * Unfortunately, it's not possible to hash by a different key or to easily
50  * break up our one hash into seperate hashs.  The reason is because
51  * the hash contains mixed data types.  Ie, it has three different
52  * types of autopush nodes in it:  SAP_ALL, SAP_RANGE, SAP_ONE.  Not
53  * only does the hash table contain nodes of different types, but we
54  * have to be able to search the table with a node of one type that
55  * might match another node with a different type.  (ie, we might search
56  * for a SAP_ONE node with a value that matches a SAP_ALL node in the
57  * hash, or vice versa.)
58  *
59  * An ideal solution would probably be an AVL tree sorted by major
60  * numbers.  Each node in the AVL tree would have the following optional
61  * data associated with it:
62  *	- a single SAP_ALL autopush node
63  *	- an or avl tree or hash table of SAP_RANGE and SAP_ONE autopush
64  *	  nodes indexed by minor numbers.  perhaps two separate tables,
65  *	  one for each type of autopush nodes.
66  *
67  * Note that regardless of how the data is stored there can't be any overlap
68  * stored between autopush nodes.  For example, if there is a SAP_ALL node
69  * for a given major number then there can't be any SAP_RANGE or SAP_ONE
70  * nodes for that same major number.
71  */
72 kmutex_t		sad_lock;		/* protects sad_hash */
73 static mod_hash_t	*sad_hash;
74 static size_t		sad_hash_nchains = 127;
75 
76 /*
77  * Private Internal Interfaces
78  */
79 /*ARGSUSED*/
80 static uint_t
81 sad_hash_alg(void *hash_data, mod_hash_key_t key)
82 {
83 	struct apcommon *apc = (struct apcommon *)key;
84 
85 	ASSERT(sad_apc_verify(apc) == 0);
86 	return (apc->apc_major);
87 }
88 
89 /*
90  * Compare hash keys based off of major, minor, lastminor, and type.
91  */
92 static int
93 sad_hash_keycmp(mod_hash_key_t key1, mod_hash_key_t key2)
94 {
95 	struct apcommon *apc1 = (struct apcommon *)key1;
96 	struct apcommon *apc2 = (struct apcommon *)key2;
97 
98 	ASSERT(sad_apc_verify(apc1) == 0);
99 	ASSERT(sad_apc_verify(apc2) == 0);
100 
101 	/* Filter out cases where the major number doesn't match. */
102 	if (apc1->apc_major != apc2->apc_major)
103 		return (1);
104 
105 	/* If either type is SAP_ALL then we're done. */
106 	if ((apc1->apc_cmd == SAP_ALL) || (apc2->apc_cmd == SAP_ALL))
107 		return (0);
108 
109 	/* Deal with the case where both types are SAP_ONE. */
110 	if ((apc1->apc_cmd == SAP_ONE) && (apc2->apc_cmd == SAP_ONE)) {
111 		/* Check if the minor numbers match. */
112 		return (apc1->apc_minor != apc2->apc_minor);
113 	}
114 
115 	/* Deal with the case where both types are SAP_RANGE. */
116 	if ((apc1->apc_cmd == SAP_RANGE) && (apc2->apc_cmd == SAP_RANGE)) {
117 		/* Check for overlapping ranges. */
118 		if ((apc1->apc_lastminor < apc2->apc_minor) ||
119 		    (apc1->apc_minor > apc2->apc_lastminor))
120 			return (1);
121 		return (0);
122 	}
123 
124 	/*
125 	 * We know that one type is SAP_ONE and the other is SAP_RANGE.
126 	 * So now let's do range matching.
127 	 */
128 	if (apc1->apc_cmd == SAP_RANGE) {
129 		ASSERT(apc2->apc_cmd == SAP_ONE);
130 		if ((apc1->apc_lastminor < apc2->apc_minor) ||
131 		    (apc1->apc_minor > apc2->apc_minor))
132 			return (1);
133 	} else {
134 		ASSERT(apc1->apc_cmd == SAP_ONE);
135 		ASSERT(apc2->apc_cmd == SAP_RANGE);
136 		if ((apc1->apc_minor < apc2->apc_minor) ||
137 		    (apc1->apc_minor > apc2->apc_lastminor))
138 			return (1);
139 	}
140 	return (0);
141 }
142 
143 /*
144  * External Interfaces
145  */
146 int
147 sad_apc_verify(struct apcommon *apc)
148 {
149 	/* sanity check the number of modules to push */
150 	if ((apc->apc_npush == 0) || (apc->apc_npush > MAXAPUSH) ||
151 	    (apc->apc_npush > nstrpush))
152 		return (EINVAL);
153 
154 	/* Check for NODEV major vaule */
155 	if (apc->apc_major == -1)
156 		return (EINVAL);
157 
158 	switch (apc->apc_cmd) {
159 	case SAP_ALL:
160 	case SAP_ONE:
161 		/* lastminor should not have been specified */
162 		if (apc->apc_lastminor != 0)
163 			return (EINVAL);
164 		break;
165 	case SAP_RANGE:
166 		if (apc->apc_lastminor <= apc->apc_minor)
167 			return (ERANGE);
168 		break;
169 	default:
170 		return (EINVAL);
171 	}
172 	return (0);
173 }
174 
175 int
176 sad_ap_verify(struct autopush *ap)
177 {
178 	int ret, i;
179 
180 	if ((ret = sad_apc_verify(&ap->ap_common)) != 0)
181 		return (ret);
182 
183 	/*
184 	 * Validate that the specified list of modules exist.  Note that
185 	 * ap_npush has already been sanity checked by sad_apc_verify().
186 	 */
187 	for (i = 0; i < ap->ap_npush; i++) {
188 		ap->ap_list[i][FMNAMESZ] = '\0';
189 		if (fmodsw_find(ap->ap_list[i], FMODSW_LOAD) == NULL)
190 			return (EINVAL);
191 	}
192 	return (0);
193 }
194 
195 struct autopush *
196 sad_ap_alloc(void)
197 {
198 	struct autopush *ap_new;
199 
200 	ap_new = kmem_zalloc(sizeof (struct autopush), KM_SLEEP);
201 	ap_new->ap_cnt = 1;
202 	return (ap_new);
203 }
204 
205 void
206 sad_ap_rele(struct autopush *ap)
207 {
208 	mutex_enter(&sad_lock);
209 	ASSERT(ap->ap_cnt > 0);
210 	if (--(ap->ap_cnt) == 0) {
211 		mutex_exit(&sad_lock);
212 		kmem_free(ap, sizeof (struct autopush));
213 	} else {
214 		mutex_exit(&sad_lock);
215 	}
216 }
217 
218 void
219 sad_ap_insert(struct autopush *ap)
220 {
221 	ASSERT(MUTEX_HELD(&sad_lock));
222 	ASSERT(sad_apc_verify(&ap->ap_common) == 0);
223 	ASSERT(sad_ap_find(&ap->ap_common) == NULL);
224 	(void) mod_hash_insert(sad_hash, &ap->ap_common, ap);
225 }
226 
227 void
228 sad_ap_remove(struct autopush *ap)
229 {
230 	struct autopush	*ap_removed = NULL;
231 
232 	ASSERT(MUTEX_HELD(&sad_lock));
233 	(void) mod_hash_remove(sad_hash, &ap->ap_common,
234 	    (mod_hash_val_t *)&ap_removed);
235 	ASSERT(ap == ap_removed);
236 }
237 
238 struct autopush *
239 sad_ap_find(struct apcommon *apc)
240 {
241 	struct autopush	*ap_result = NULL;
242 
243 	ASSERT(MUTEX_HELD(&sad_lock));
244 	ASSERT(sad_apc_verify(apc) == 0);
245 
246 	(void) mod_hash_find(sad_hash, apc, (mod_hash_val_t *)&ap_result);
247 	if (ap_result != NULL)
248 		ap_result->ap_cnt++;
249 	return (ap_result);
250 }
251 
252 struct autopush *
253 sad_ap_find_by_dev(dev_t dev)
254 {
255 	struct apcommon	apc;
256 	struct autopush	*ap_result;
257 
258 	ASSERT(MUTEX_NOT_HELD(&sad_lock));
259 
260 	/* prepare an apcommon structure to search with */
261 	apc.apc_cmd = SAP_ONE;
262 	apc.apc_major = getmajor(dev);
263 	apc.apc_minor = getminor(dev);
264 
265 	/*
266 	 * the following values must be set but initialized to have a
267 	 * valid apcommon struct, but since we're only using this
268 	 * structure to do a query the values are never actually used.
269 	 */
270 	apc.apc_npush = 1;
271 	apc.apc_lastminor = 0;
272 
273 	mutex_enter(&sad_lock);
274 	ap_result = sad_ap_find(&apc);
275 	mutex_exit(&sad_lock);
276 	return (ap_result);
277 }
278 
279 void
280 sad_initspace(void)
281 {
282 	saddev = kmem_zalloc(sadcnt * sizeof (struct saddev), KM_SLEEP);
283 	sad_hash = mod_hash_create_extended("sad_hash",
284 	    sad_hash_nchains, mod_hash_null_keydtor, mod_hash_null_valdtor,
285 	    sad_hash_alg, NULL, sad_hash_keycmp, KM_SLEEP);
286 }
287