sad_conf.c revision 3448:aaf16568054b
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*/
74static uint_t
75sad_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 */
86static int
87sad_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 */
140int
141sad_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
173int
174sad_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
193struct autopush *
194sad_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
203void
204sad_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
216void
217sad_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
225void
226sad_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
236struct autopush *
237sad_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
251struct autopush *
252sad_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
278void
279sad_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
293void
294sad_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