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