xref: /illumos-gate/usr/src/uts/common/io/sad_conf.c (revision 2d6eb4a5e0a47d30189497241345dc5466bb68ab)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51110f384Sedp  * Common Development and Distribution License (the "License").
61110f384Sedp  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*2ea701aaSyz147064  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*
277c478bd9Sstevel@tonic-gate  * Config dependent data structures for the Streams Administrative Driver
287c478bd9Sstevel@tonic-gate  * (or "Ballad of the SAD Cafe").
297c478bd9Sstevel@tonic-gate  */
307c478bd9Sstevel@tonic-gate #include <sys/types.h>
317c478bd9Sstevel@tonic-gate #include <sys/conf.h>
327c478bd9Sstevel@tonic-gate #include <sys/stream.h>
331110f384Sedp #include <sys/strsubr.h>
347c478bd9Sstevel@tonic-gate #include <sys/sad.h>
357c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
361110f384Sedp #include <sys/sysmacros.h>
377c478bd9Sstevel@tonic-gate 
387c478bd9Sstevel@tonic-gate /*
391110f384Sedp  * Currently we store all the sad data in a hash table keyed by major
401110f384Sedp  * number.  This is far from ideal.  It means that if a single device
411110f384Sedp  * starts using lots of SAP_ONE entries all its entries will hash
421110f384Sedp  * to the same bucket and we'll get very long chains for that bucket.
431110f384Sedp  *
441110f384Sedp  * Unfortunately, it's not possible to hash by a different key or to easily
451110f384Sedp  * break up our one hash into seperate hashs.  The reason is because
461110f384Sedp  * the hash contains mixed data types.  Ie, it has three different
471110f384Sedp  * types of autopush nodes in it:  SAP_ALL, SAP_RANGE, SAP_ONE.  Not
481110f384Sedp  * only does the hash table contain nodes of different types, but we
491110f384Sedp  * have to be able to search the table with a node of one type that
501110f384Sedp  * might match another node with a different type.  (ie, we might search
511110f384Sedp  * for a SAP_ONE node with a value that matches a SAP_ALL node in the
521110f384Sedp  * hash, or vice versa.)
531110f384Sedp  *
541110f384Sedp  * An ideal solution would probably be an AVL tree sorted by major
551110f384Sedp  * numbers.  Each node in the AVL tree would have the following optional
561110f384Sedp  * data associated with it:
571110f384Sedp  *	- a single SAP_ALL autopush node
581110f384Sedp  *	- an or avl tree or hash table of SAP_RANGE and SAP_ONE autopush
591110f384Sedp  *	  nodes indexed by minor numbers.  perhaps two separate tables,
601110f384Sedp  *	  one for each type of autopush nodes.
611110f384Sedp  *
621110f384Sedp  * Note that regardless of how the data is stored there can't be any overlap
631110f384Sedp  * stored between autopush nodes.  For example, if there is a SAP_ALL node
641110f384Sedp  * for a given major number then there can't be any SAP_RANGE or SAP_ONE
651110f384Sedp  * nodes for that same major number.
667c478bd9Sstevel@tonic-gate  */
671110f384Sedp 
681110f384Sedp /*
691110f384Sedp  * Private Internal Interfaces
701110f384Sedp  */
711110f384Sedp /*ARGSUSED*/
721110f384Sedp static uint_t
sad_hash_alg(void * hash_data,mod_hash_key_t key)731110f384Sedp sad_hash_alg(void *hash_data, mod_hash_key_t key)
741110f384Sedp {
751110f384Sedp 	struct apcommon *apc = (struct apcommon *)key;
761110f384Sedp 
771110f384Sedp 	ASSERT(sad_apc_verify(apc) == 0);
781110f384Sedp 	return (apc->apc_major);
791110f384Sedp }
801110f384Sedp 
811110f384Sedp /*
821110f384Sedp  * Compare hash keys based off of major, minor, lastminor, and type.
831110f384Sedp  */
841110f384Sedp static int
sad_hash_keycmp(mod_hash_key_t key1,mod_hash_key_t key2)851110f384Sedp sad_hash_keycmp(mod_hash_key_t key1, mod_hash_key_t key2)
861110f384Sedp {
871110f384Sedp 	struct apcommon *apc1 = (struct apcommon *)key1;
881110f384Sedp 	struct apcommon *apc2 = (struct apcommon *)key2;
891110f384Sedp 
901110f384Sedp 	ASSERT(sad_apc_verify(apc1) == 0);
911110f384Sedp 	ASSERT(sad_apc_verify(apc2) == 0);
921110f384Sedp 
931110f384Sedp 	/* Filter out cases where the major number doesn't match. */
941110f384Sedp 	if (apc1->apc_major != apc2->apc_major)
951110f384Sedp 		return (1);
961110f384Sedp 
971110f384Sedp 	/* If either type is SAP_ALL then we're done. */
981110f384Sedp 	if ((apc1->apc_cmd == SAP_ALL) || (apc2->apc_cmd == SAP_ALL))
991110f384Sedp 		return (0);
1001110f384Sedp 
1011110f384Sedp 	/* Deal with the case where both types are SAP_ONE. */
1021110f384Sedp 	if ((apc1->apc_cmd == SAP_ONE) && (apc2->apc_cmd == SAP_ONE)) {
1031110f384Sedp 		/* Check if the minor numbers match. */
1041110f384Sedp 		return (apc1->apc_minor != apc2->apc_minor);
1051110f384Sedp 	}
1061110f384Sedp 
1071110f384Sedp 	/* Deal with the case where both types are SAP_RANGE. */
1081110f384Sedp 	if ((apc1->apc_cmd == SAP_RANGE) && (apc2->apc_cmd == SAP_RANGE)) {
1091110f384Sedp 		/* Check for overlapping ranges. */
1101110f384Sedp 		if ((apc1->apc_lastminor < apc2->apc_minor) ||
1111110f384Sedp 		    (apc1->apc_minor > apc2->apc_lastminor))
1121110f384Sedp 			return (1);
1131110f384Sedp 		return (0);
1141110f384Sedp 	}
1151110f384Sedp 
1161110f384Sedp 	/*
1171110f384Sedp 	 * We know that one type is SAP_ONE and the other is SAP_RANGE.
1181110f384Sedp 	 * So now let's do range matching.
1191110f384Sedp 	 */
1201110f384Sedp 	if (apc1->apc_cmd == SAP_RANGE) {
1211110f384Sedp 		ASSERT(apc2->apc_cmd == SAP_ONE);
1221110f384Sedp 		if ((apc1->apc_lastminor < apc2->apc_minor) ||
1231110f384Sedp 		    (apc1->apc_minor > apc2->apc_minor))
1241110f384Sedp 			return (1);
1251110f384Sedp 	} else {
1261110f384Sedp 		ASSERT(apc1->apc_cmd == SAP_ONE);
1271110f384Sedp 		ASSERT(apc2->apc_cmd == SAP_RANGE);
1281110f384Sedp 		if ((apc1->apc_minor < apc2->apc_minor) ||
1291110f384Sedp 		    (apc1->apc_minor > apc2->apc_lastminor))
1301110f384Sedp 			return (1);
1311110f384Sedp 	}
1321110f384Sedp 	return (0);
1331110f384Sedp }
1341110f384Sedp 
135*2ea701aaSyz147064 /*ARGSUSED*/
136*2ea701aaSyz147064 static uint_t
sad_hash_free_value(mod_hash_key_t key,mod_hash_val_t * val,void * arg)137*2ea701aaSyz147064 sad_hash_free_value(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
138*2ea701aaSyz147064 {
139*2ea701aaSyz147064 	struct autopush *ap = (struct autopush *)val;
140*2ea701aaSyz147064 
141*2ea701aaSyz147064 	ASSERT(ap->ap_cnt > 0);
142*2ea701aaSyz147064 	if (--(ap->ap_cnt) == 0)
143*2ea701aaSyz147064 		kmem_free(ap, sizeof (struct autopush));
144*2ea701aaSyz147064 
145*2ea701aaSyz147064 	return (MH_WALK_CONTINUE);
146*2ea701aaSyz147064 }
147*2ea701aaSyz147064 
1481110f384Sedp /*
1491110f384Sedp  * External Interfaces
1501110f384Sedp  */
1511110f384Sedp int
sad_apc_verify(struct apcommon * apc)1521110f384Sedp sad_apc_verify(struct apcommon *apc)
1531110f384Sedp {
1541110f384Sedp 	/* sanity check the number of modules to push */
1551110f384Sedp 	if ((apc->apc_npush == 0) || (apc->apc_npush > MAXAPUSH) ||
1561110f384Sedp 	    (apc->apc_npush > nstrpush))
1571110f384Sedp 		return (EINVAL);
1581110f384Sedp 
1591110f384Sedp 	/* Check for NODEV major vaule */
1601110f384Sedp 	if (apc->apc_major == -1)
1611110f384Sedp 		return (EINVAL);
1621110f384Sedp 
1631110f384Sedp 	switch (apc->apc_cmd) {
1641110f384Sedp 	case SAP_ALL:
1651110f384Sedp 	case SAP_ONE:
16650110644Sedp 		/*
16750110644Sedp 		 * Really, we'd like to be strict here and make sure that
16850110644Sedp 		 * apc_lastminor is 0 (since setting apc_lastminor for
16950110644Sedp 		 * SAP_ALL and SAP_ONE commands doesn't make any sense),
17050110644Sedp 		 * but we can't since historically apc_lastminor has been
17150110644Sedp 		 * silently ignored for non-SAP_RANGE commands.
17250110644Sedp 		 */
1731110f384Sedp 		break;
1741110f384Sedp 	case SAP_RANGE:
1751110f384Sedp 		if (apc->apc_lastminor <= apc->apc_minor)
1761110f384Sedp 			return (ERANGE);
1771110f384Sedp 		break;
1781110f384Sedp 	default:
1791110f384Sedp 		return (EINVAL);
1801110f384Sedp 	}
1811110f384Sedp 	return (0);
1821110f384Sedp }
1831110f384Sedp 
1841110f384Sedp int
sad_ap_verify(struct autopush * ap)1851110f384Sedp sad_ap_verify(struct autopush *ap)
1861110f384Sedp {
1871110f384Sedp 	int ret, i;
1881110f384Sedp 
1891110f384Sedp 	if ((ret = sad_apc_verify(&ap->ap_common)) != 0)
1901110f384Sedp 		return (ret);
1911110f384Sedp 
1921110f384Sedp 	/*
1931110f384Sedp 	 * Validate that the specified list of modules exist.  Note that
1941110f384Sedp 	 * ap_npush has already been sanity checked by sad_apc_verify().
1951110f384Sedp 	 */
1961110f384Sedp 	for (i = 0; i < ap->ap_npush; i++) {
1971110f384Sedp 		ap->ap_list[i][FMNAMESZ] = '\0';
1981110f384Sedp 		if (fmodsw_find(ap->ap_list[i], FMODSW_LOAD) == NULL)
1991110f384Sedp 			return (EINVAL);
2001110f384Sedp 	}
2011110f384Sedp 	return (0);
2021110f384Sedp }
2031110f384Sedp 
2041110f384Sedp struct autopush *
sad_ap_alloc(void)2051110f384Sedp sad_ap_alloc(void)
2061110f384Sedp {
2071110f384Sedp 	struct autopush *ap_new;
2081110f384Sedp 
2091110f384Sedp 	ap_new = kmem_zalloc(sizeof (struct autopush), KM_SLEEP);
2101110f384Sedp 	ap_new->ap_cnt = 1;
2111110f384Sedp 	return (ap_new);
2121110f384Sedp }
2131110f384Sedp 
2141110f384Sedp void
sad_ap_rele(struct autopush * ap,str_stack_t * ss)215f4b3ec61Sdh155122 sad_ap_rele(struct autopush *ap, str_stack_t *ss)
2161110f384Sedp {
217f4b3ec61Sdh155122 	mutex_enter(&ss->ss_sad_lock);
2181110f384Sedp 	ASSERT(ap->ap_cnt > 0);
2191110f384Sedp 	if (--(ap->ap_cnt) == 0) {
220f4b3ec61Sdh155122 		mutex_exit(&ss->ss_sad_lock);
2211110f384Sedp 		kmem_free(ap, sizeof (struct autopush));
2221110f384Sedp 	} else {
223f4b3ec61Sdh155122 		mutex_exit(&ss->ss_sad_lock);
2241110f384Sedp 	}
2251110f384Sedp }
2261110f384Sedp 
2271110f384Sedp void
sad_ap_insert(struct autopush * ap,str_stack_t * ss)228f4b3ec61Sdh155122 sad_ap_insert(struct autopush *ap, str_stack_t *ss)
2291110f384Sedp {
230f4b3ec61Sdh155122 	ASSERT(MUTEX_HELD(&ss->ss_sad_lock));
2311110f384Sedp 	ASSERT(sad_apc_verify(&ap->ap_common) == 0);
232f4b3ec61Sdh155122 	ASSERT(sad_ap_find(&ap->ap_common, ss) == NULL);
233f4b3ec61Sdh155122 	(void) mod_hash_insert(ss->ss_sad_hash, &ap->ap_common, ap);
2341110f384Sedp }
2351110f384Sedp 
2361110f384Sedp void
sad_ap_remove(struct autopush * ap,str_stack_t * ss)237f4b3ec61Sdh155122 sad_ap_remove(struct autopush *ap, str_stack_t *ss)
2381110f384Sedp {
2391110f384Sedp 	struct autopush	*ap_removed = NULL;
2401110f384Sedp 
241f4b3ec61Sdh155122 	ASSERT(MUTEX_HELD(&ss->ss_sad_lock));
242f4b3ec61Sdh155122 	(void) mod_hash_remove(ss->ss_sad_hash, &ap->ap_common,
2431110f384Sedp 	    (mod_hash_val_t *)&ap_removed);
2441110f384Sedp 	ASSERT(ap == ap_removed);
2451110f384Sedp }
2461110f384Sedp 
2471110f384Sedp struct autopush *
sad_ap_find(struct apcommon * apc,str_stack_t * ss)248f4b3ec61Sdh155122 sad_ap_find(struct apcommon *apc, str_stack_t *ss)
2491110f384Sedp {
2501110f384Sedp 	struct autopush	*ap_result = NULL;
2511110f384Sedp 
252f4b3ec61Sdh155122 	ASSERT(MUTEX_HELD(&ss->ss_sad_lock));
2531110f384Sedp 	ASSERT(sad_apc_verify(apc) == 0);
2541110f384Sedp 
255f4b3ec61Sdh155122 	(void) mod_hash_find(ss->ss_sad_hash, apc,
256f4b3ec61Sdh155122 	    (mod_hash_val_t *)&ap_result);
2571110f384Sedp 	if (ap_result != NULL)
2581110f384Sedp 		ap_result->ap_cnt++;
2591110f384Sedp 	return (ap_result);
2601110f384Sedp }
2611110f384Sedp 
2621110f384Sedp struct autopush *
sad_ap_find_by_dev(dev_t dev,str_stack_t * ss)263f4b3ec61Sdh155122 sad_ap_find_by_dev(dev_t dev, str_stack_t *ss)
2641110f384Sedp {
2651110f384Sedp 	struct apcommon	apc;
2661110f384Sedp 	struct autopush	*ap_result;
2671110f384Sedp 
268f4b3ec61Sdh155122 	ASSERT(MUTEX_NOT_HELD(&ss->ss_sad_lock));
2691110f384Sedp 
2701110f384Sedp 	/* prepare an apcommon structure to search with */
2711110f384Sedp 	apc.apc_cmd = SAP_ONE;
2721110f384Sedp 	apc.apc_major = getmajor(dev);
2731110f384Sedp 	apc.apc_minor = getminor(dev);
2741110f384Sedp 
2751110f384Sedp 	/*
2761110f384Sedp 	 * the following values must be set but initialized to have a
2771110f384Sedp 	 * valid apcommon struct, but since we're only using this
2781110f384Sedp 	 * structure to do a query the values are never actually used.
2791110f384Sedp 	 */
2801110f384Sedp 	apc.apc_npush = 1;
2811110f384Sedp 	apc.apc_lastminor = 0;
2821110f384Sedp 
283f4b3ec61Sdh155122 	mutex_enter(&ss->ss_sad_lock);
284f4b3ec61Sdh155122 	ap_result = sad_ap_find(&apc, ss);
285f4b3ec61Sdh155122 	mutex_exit(&ss->ss_sad_lock);
2861110f384Sedp 	return (ap_result);
2871110f384Sedp }
2887c478bd9Sstevel@tonic-gate 
2897c478bd9Sstevel@tonic-gate void
sad_initspace(str_stack_t * ss)290f4b3ec61Sdh155122 sad_initspace(str_stack_t *ss)
2917c478bd9Sstevel@tonic-gate {
292f4b3ec61Sdh155122 	mutex_init(&ss->ss_sad_lock, NULL, MUTEX_DEFAULT, NULL);
293f4b3ec61Sdh155122 	ss->ss_sad_hash_nchains = 127;
294f4b3ec61Sdh155122 	ss->ss_sadcnt = 16;
295f4b3ec61Sdh155122 
296f4b3ec61Sdh155122 	ss->ss_saddev = kmem_zalloc(ss->ss_sadcnt * sizeof (struct saddev),
297f4b3ec61Sdh155122 	    KM_SLEEP);
298f4b3ec61Sdh155122 	ss->ss_sad_hash = mod_hash_create_extended("sad_hash",
299f4b3ec61Sdh155122 	    ss->ss_sad_hash_nchains, mod_hash_null_keydtor,
300f4b3ec61Sdh155122 	    mod_hash_null_valdtor,
3011110f384Sedp 	    sad_hash_alg, NULL, sad_hash_keycmp, KM_SLEEP);
3027c478bd9Sstevel@tonic-gate }
303f4b3ec61Sdh155122 
304f4b3ec61Sdh155122 void
sad_freespace(str_stack_t * ss)305f4b3ec61Sdh155122 sad_freespace(str_stack_t *ss)
306f4b3ec61Sdh155122 {
307f4b3ec61Sdh155122 	kmem_free(ss->ss_saddev, ss->ss_sadcnt * sizeof (struct saddev));
308f4b3ec61Sdh155122 	ss->ss_saddev = NULL;
309f4b3ec61Sdh155122 
310*2ea701aaSyz147064 	mutex_enter(&ss->ss_sad_lock);
311*2ea701aaSyz147064 	mod_hash_walk(ss->ss_sad_hash, sad_hash_free_value, NULL);
312f4b3ec61Sdh155122 	mod_hash_destroy_hash(ss->ss_sad_hash);
313f4b3ec61Sdh155122 	ss->ss_sad_hash = NULL;
314*2ea701aaSyz147064 	mutex_exit(&ss->ss_sad_lock);
315f4b3ec61Sdh155122 
316f4b3ec61Sdh155122 	mutex_destroy(&ss->ss_sad_lock);
317f4b3ec61Sdh155122 }
318