xref: /onnv-gate/usr/src/uts/sun4v/io/vio_util.c (revision 9235:2e98898d0a13)
12336Snarayan /*
22336Snarayan  * CDDL HEADER START
32336Snarayan  *
42336Snarayan  * The contents of this file are subject to the terms of the
52336Snarayan  * Common Development and Distribution License (the "License").
62336Snarayan  * You may not use this file except in compliance with the License.
72336Snarayan  *
82336Snarayan  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92336Snarayan  * or http://www.opensolaris.org/os/licensing.
102336Snarayan  * See the License for the specific language governing permissions
112336Snarayan  * and limitations under the License.
122336Snarayan  *
132336Snarayan  * When distributing Covered Code, include this CDDL HEADER in each
142336Snarayan  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152336Snarayan  * If applicable, add the following below this CDDL HEADER, with the
162336Snarayan  * fields enclosed by brackets "[]" replaced with your own identifying
172336Snarayan  * information: Portions Copyright [yyyy] [name of copyright owner]
182336Snarayan  *
192336Snarayan  * CDDL HEADER END
202336Snarayan  */
212336Snarayan 
222336Snarayan /*
239217SWentao.Yang@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
242336Snarayan  * Use is subject to license terms.
252336Snarayan  */
262336Snarayan 
272336Snarayan #include <sys/types.h>
282336Snarayan #include <sys/sysmacros.h>
292336Snarayan #include <sys/errno.h>
302336Snarayan #include <sys/kmem.h>
312336Snarayan #include <sys/ksynch.h>
322336Snarayan #include <sys/stream.h>
332336Snarayan #include <sys/ddi.h>
342336Snarayan #include <sys/sunddi.h>
352336Snarayan #include <sys/vio_util.h>
362336Snarayan 
379217SWentao.Yang@Sun.COM static int vio_pool_cleanup_retries = 10;	/* Max retries to free pool */
389217SWentao.Yang@Sun.COM static int vio_pool_cleanup_delay = 10000;	/* 10ms */
399217SWentao.Yang@Sun.COM 
402336Snarayan /*
412336Snarayan  * Create a pool of mblks from which future vio_allocb() requests
422336Snarayan  * will be serviced.
432336Snarayan  *
442336Snarayan  * NOTE: num_mblks has to non-zero and a power-of-2
452336Snarayan  *
469217SWentao.Yang@Sun.COM  * Returns
479217SWentao.Yang@Sun.COM  *	0 on success
489217SWentao.Yang@Sun.COM  *	EINVAL if num_mblks is zero or not a power of 2.
499217SWentao.Yang@Sun.COM  *	ENOSPC if the pool could not be created due to alloc failures.
502336Snarayan  */
512336Snarayan int
522336Snarayan vio_create_mblks(uint64_t num_mblks, size_t mblk_size, vio_mblk_pool_t **poolp)
532336Snarayan {
542336Snarayan 	vio_mblk_pool_t		*vmplp;
552336Snarayan 	vio_mblk_t		*vmp;
562336Snarayan 	uint8_t			*datap;
572336Snarayan 	int			i;
58*9235SWentao.Yang@Sun.COM 	int			rv;
592336Snarayan 
602336Snarayan 	if (!(num_mblks) || (!ISP2(num_mblks))) {
612336Snarayan 		*poolp = 0;
622336Snarayan 		return (EINVAL);
632336Snarayan 	}
642336Snarayan 
652336Snarayan 	vmplp = kmem_zalloc(sizeof (*vmplp), KM_SLEEP);
662336Snarayan 	vmplp->quelen = num_mblks;
672336Snarayan 	vmplp->quemask = num_mblks - 1; /* expects quelen is power-of-2 */
682336Snarayan 	vmplp->mblk_size = mblk_size;
692336Snarayan 
702336Snarayan 	mutex_init(&vmplp->hlock, NULL, MUTEX_DRIVER,
714650Sraghuram 	    DDI_INTR_PRI(DDI_INTR_SOFTPRI_DEFAULT));
722336Snarayan 	mutex_init(&vmplp->tlock, NULL, MUTEX_DRIVER,
734650Sraghuram 	    DDI_INTR_PRI(DDI_INTR_SOFTPRI_DEFAULT));
742336Snarayan 
752336Snarayan 	vmplp->basep = kmem_zalloc(num_mblks * sizeof (vio_mblk_t), KM_SLEEP);
762336Snarayan 	vmplp->datap = kmem_zalloc(num_mblks * mblk_size, KM_SLEEP);
772336Snarayan 	vmplp->nextp = NULL;
782336Snarayan 
792336Snarayan 	/* create a queue of pointers to free vio_mblk_t's */
804647Sraghuram 	vmplp->quep = kmem_zalloc(vmplp->quelen *
814650Sraghuram 	    sizeof (vio_mblk_t *), KM_SLEEP);
822336Snarayan 	vmplp->head = 0;
832336Snarayan 	vmplp->tail =  0;
842336Snarayan 
852336Snarayan 	for (i = 0, datap = vmplp->datap; i < num_mblks; i++) {
862336Snarayan 
872336Snarayan 		vmp = &(vmplp->basep[i]);
882336Snarayan 		vmp->vmplp = vmplp;
892336Snarayan 		vmp->datap = datap;
902336Snarayan 		vmp->reclaim.free_func = vio_freeb;
912336Snarayan 		vmp->reclaim.free_arg = (caddr_t)vmp;
922336Snarayan 		vmp->mp = desballoc(vmp->datap, mblk_size, BPRI_MED,
932336Snarayan 		    &vmp->reclaim);
942336Snarayan 
959217SWentao.Yang@Sun.COM 		if (vmp->mp == NULL) {
969217SWentao.Yang@Sun.COM 			/* reset tail */
979217SWentao.Yang@Sun.COM 			vmplp->tail = vmplp->head;
989217SWentao.Yang@Sun.COM 
999217SWentao.Yang@Sun.COM 			/*
1009217SWentao.Yang@Sun.COM 			 * vio_destroy_mblks() frees mblks that have been
1019217SWentao.Yang@Sun.COM 			 * allocated so far and then destroys the pool.
1029217SWentao.Yang@Sun.COM 			 */
103*9235SWentao.Yang@Sun.COM 			rv = vio_destroy_mblks(vmplp);
104*9235SWentao.Yang@Sun.COM 			ASSERT(rv == 0);
1059217SWentao.Yang@Sun.COM 
1069217SWentao.Yang@Sun.COM 			*poolp = NULL;
1079217SWentao.Yang@Sun.COM 			return (ENOSPC);
1089217SWentao.Yang@Sun.COM 		}
1092336Snarayan 
1102336Snarayan 		/* put this vmp on the free stack */
1112336Snarayan 		vmplp->quep[vmplp->tail] = vmp;
1122336Snarayan 		vmplp->tail = (vmplp->tail + 1) & vmplp->quemask;
1132336Snarayan 
1142336Snarayan 		datap += mblk_size;
1152336Snarayan 	}
1162336Snarayan 
1172336Snarayan 	*poolp = vmplp;
1182336Snarayan 	return (0);
1192336Snarayan }
1202336Snarayan 
1212336Snarayan /*
1222336Snarayan  * Destroy the pool of mblks. This can only succeed when
1232336Snarayan  * all allocated mblks have been returned to the pool.
1242336Snarayan  *
1252336Snarayan  * It is up to the caller to ensure that no further mblks are
1262336Snarayan  * requested from the pool after destroy has been invoked.
1272336Snarayan  *
1282336Snarayan  * Returns 0 on success, EINVAL if handle is invalid, or
1292336Snarayan  * EBUSY if not all mblks reclaimed yet.
1302336Snarayan  */
1312336Snarayan int
1322336Snarayan vio_destroy_mblks(vio_mblk_pool_t *vmplp)
1332336Snarayan {
1349217SWentao.Yang@Sun.COM 	uint64_t	i;
1359217SWentao.Yang@Sun.COM 	uint64_t	num_mblks;
1369217SWentao.Yang@Sun.COM 	vio_mblk_t	*vmp;
1379217SWentao.Yang@Sun.COM 	int		pool_cleanup_retries = 0;
1389217SWentao.Yang@Sun.COM 
1392793Slm66018 
1402336Snarayan 	if (vmplp == NULL)
1412336Snarayan 		return (EINVAL);
1422336Snarayan 
1432336Snarayan 	/*
1442336Snarayan 	 * We can only destroy the pool once all the mblks have
1452336Snarayan 	 * been reclaimed.
1462336Snarayan 	 */
1479217SWentao.Yang@Sun.COM 	do {
1489217SWentao.Yang@Sun.COM 		if (vmplp->head == vmplp->tail) {
1499217SWentao.Yang@Sun.COM 			break;
1509217SWentao.Yang@Sun.COM 		}
1519217SWentao.Yang@Sun.COM 
1529217SWentao.Yang@Sun.COM 		/* some mblks still in use */
1539217SWentao.Yang@Sun.COM 		drv_usecwait(vio_pool_cleanup_delay);
1549217SWentao.Yang@Sun.COM 	} while (++pool_cleanup_retries < vio_pool_cleanup_retries);
1559217SWentao.Yang@Sun.COM 
1562336Snarayan 	if (vmplp->head != vmplp->tail) {
1572336Snarayan 		return (EBUSY);
1582336Snarayan 	}
1592336Snarayan 
1602793Slm66018 	num_mblks = vmplp->quelen;
1612793Slm66018 
1622793Slm66018 	/*
1632793Slm66018 	 * Set pool flag to tell vio_freeb() which is invoked from freeb(),
1642793Slm66018 	 * that it is being called in the context of vio_destroy_mblks().
1652793Slm66018 	 * This results in freeing only mblk_t and dblk_t structures for
1662793Slm66018 	 * each mp. The associated data buffers are freed below as one big
1672793Slm66018 	 * chunk through kmem_free(vmplp->datap).
1682793Slm66018 	 */
1692793Slm66018 	vmplp->flag |= VMPL_FLAG_DESTROYING;
1702793Slm66018 	for (i = 0; i < num_mblks; i++) {
1712793Slm66018 		vmp = &(vmplp->basep[i]);
1729217SWentao.Yang@Sun.COM 		/*
1739217SWentao.Yang@Sun.COM 		 * It is possible that mblks have been allocated only upto
1749217SWentao.Yang@Sun.COM 		 * a certain index and the entire quelen has not been
1759217SWentao.Yang@Sun.COM 		 * initialized. This might happen due to desballoc() failure
1769217SWentao.Yang@Sun.COM 		 * while creating the pool. The below check handles this
1779217SWentao.Yang@Sun.COM 		 * condition.
1789217SWentao.Yang@Sun.COM 		 */
1799217SWentao.Yang@Sun.COM 		if (vmp->mp != NULL)
1802793Slm66018 			freeb(vmp->mp);
1812793Slm66018 	}
1822793Slm66018 	vmplp->flag &= ~(VMPL_FLAG_DESTROYING);
1832793Slm66018 
1842793Slm66018 	kmem_free(vmplp->basep, num_mblks * sizeof (vio_mblk_t));
1852793Slm66018 	kmem_free(vmplp->datap, num_mblks * vmplp->mblk_size);
1862793Slm66018 	kmem_free(vmplp->quep, num_mblks * sizeof (vio_mblk_t *));
1872336Snarayan 
1882336Snarayan 	mutex_destroy(&vmplp->hlock);
1892336Snarayan 	mutex_destroy(&vmplp->tlock);
1902336Snarayan 
1912336Snarayan 	kmem_free(vmplp, sizeof (*vmplp));
1922336Snarayan 
1932336Snarayan 	return (0);
1942336Snarayan }
1952336Snarayan 
1962336Snarayan /*
1972336Snarayan  * Allocate a mblk from the free pool if one is available.
1982336Snarayan  * Otherwise returns NULL.
1992336Snarayan  */
2002336Snarayan mblk_t *
2012336Snarayan vio_allocb(vio_mblk_pool_t *vmplp)
2022336Snarayan {
2032336Snarayan 	vio_mblk_t	*vmp = NULL;
2042336Snarayan 	mblk_t		*mp = NULL;
2052336Snarayan 	uint32_t	head;
2062336Snarayan 
2072336Snarayan 	mutex_enter(&vmplp->hlock);
2082336Snarayan 	head = (vmplp->head + 1) & vmplp->quemask;
2092336Snarayan 	if (head != vmplp->tail) {
2102336Snarayan 		/* we have free mblks */
2112336Snarayan 		vmp = vmplp->quep[vmplp->head];
2122336Snarayan 		mp = vmp->mp;
2132336Snarayan 		vmplp->head = head;
2142336Snarayan 	}
2152336Snarayan 	mutex_exit(&vmplp->hlock);
2162336Snarayan 
2172336Snarayan 	return (mp);
2182336Snarayan }
2192336Snarayan 
2202336Snarayan /*
2212336Snarayan  * Return a mblk to the free pool. Invoked when the upper IP
2222336Snarayan  * layers do freemsg() etc on the mblk they were passed.
2232336Snarayan  */
2242336Snarayan void
2252336Snarayan vio_freeb(void *arg)
2262336Snarayan {
2272336Snarayan 	vio_mblk_t	*vmp = (vio_mblk_t *)arg;
2282336Snarayan 	vio_mblk_pool_t	*vmplp = vmp->vmplp;
2292336Snarayan 
2302793Slm66018 	if (vmplp->flag & VMPL_FLAG_DESTROYING) {
2312793Slm66018 		/*
2322793Slm66018 		 * This flag indicates that freeb() is being called from
2332793Slm66018 		 * vio_destroy_mblks().
2342793Slm66018 		 * We don't need to alloc a new mblk_t/dblk_t pair for
2352793Slm66018 		 * this data buffer, return from here and the data buffer
2362793Slm66018 		 * itself will be freed in vio_destroy_mblks().
2372793Slm66018 		 */
2382793Slm66018 		return;
2392793Slm66018 	}
2402793Slm66018 
2412336Snarayan 	vmp->mp = desballoc(vmp->datap, vmplp->mblk_size,
2424650Sraghuram 	    BPRI_MED, &vmp->reclaim);
2432336Snarayan 
2442336Snarayan 	mutex_enter(&vmplp->tlock);
2452336Snarayan 	vmplp->quep[vmplp->tail] = vmp;
2462336Snarayan 	vmplp->tail = (vmplp->tail + 1) & vmplp->quemask;
2472336Snarayan 	mutex_exit(&vmplp->tlock);
2482336Snarayan }
2494647Sraghuram 
2504647Sraghuram /*
2514647Sraghuram  * Create a multiple pools of mblks from which future vio_allocb()
2524647Sraghuram  * or vio_multipool_allocb() requests will be serviced.
2534647Sraghuram  *
2544647Sraghuram  * Arguments:
2554647Sraghuram  *	vmultip -- A pointer to vio_multi_pool_t structure.
2564647Sraghuram  *	num_pools -- Number of the pools.
2574647Sraghuram  *	... -- Variable arguments consisting a list of buffer sizes for
2584647Sraghuram  *		each pool and list of number of buffers for each pool.
2594647Sraghuram  *
2604647Sraghuram  * NOTE: The restrictions of vio_create_mblks() apply to this interface also.
2614647Sraghuram  *
2624647Sraghuram  * Returns 0 on success or an error returned by vio_create_mblks().
2634647Sraghuram  */
2644647Sraghuram int
2654647Sraghuram vio_init_multipools(vio_multi_pool_t *vmultip, int num_pools, ...)
2664647Sraghuram {
2674647Sraghuram 	int		i;
2684647Sraghuram 	int		status;
2694647Sraghuram 	char		*tbuf;
2704647Sraghuram 	va_list		vap;
2714647Sraghuram 	vio_mblk_pool_t *fvmp = NULL;
2724647Sraghuram 
2734647Sraghuram 	/*
2744647Sraghuram 	 * Allocate memory for all of the following in one allocation.
2754647Sraghuram 	 * 	bufsz_tbl -- sizeof (uint32_t) * num_pools
2764647Sraghuram 	 * 	nbuf_tbl  -- sizeof (uint32_t) * num_pools
2774647Sraghuram 	 *	vmpp	  -- sizeof (vio_mblk_pool_t *) * numpools
2784647Sraghuram 	 */
2794647Sraghuram 	vmultip->tbsz = (sizeof (uint32_t) * num_pools) +
2804650Sraghuram 	    (sizeof (uint32_t) * num_pools) +
2814650Sraghuram 	    (sizeof (vio_mblk_pool_t *) * num_pools);
2824647Sraghuram 	tbuf = kmem_zalloc(vmultip->tbsz, KM_SLEEP);
2834647Sraghuram 	vmultip->bufsz_tbl = (uint32_t *)tbuf;
2844647Sraghuram 	vmultip->nbuf_tbl = (uint32_t *)(tbuf +
2854650Sraghuram 	    (sizeof (uint32_t) * num_pools));
2864647Sraghuram 	vmultip->vmpp = (vio_mblk_pool_t **)(tbuf +
2874650Sraghuram 	    (sizeof (uint32_t) * num_pools * 2));
2884647Sraghuram 	vmultip->num_pools = num_pools;
2894647Sraghuram 
2904647Sraghuram 	/* initialize the array first */
2914647Sraghuram 	va_start(vap, num_pools);
2924647Sraghuram 	for (i = 0; i < num_pools; i++) {
2934647Sraghuram 		vmultip->bufsz_tbl[i] = va_arg(vap, uint32_t);
2944647Sraghuram 	}
2954647Sraghuram 	for (i = 0; i < num_pools; i++) {
2964647Sraghuram 		vmultip->nbuf_tbl[i] = va_arg(vap, uint32_t);
2974647Sraghuram 	}
2984647Sraghuram 	va_end(vap);
2994647Sraghuram 
3004647Sraghuram 	for (i = 0; i < vmultip->num_pools; i++) {
3014647Sraghuram 		status = vio_create_mblks(vmultip->nbuf_tbl[i],
3024650Sraghuram 		    vmultip->bufsz_tbl[i], &vmultip->vmpp[i]);
3034647Sraghuram 		if (status != 0) {
3044647Sraghuram 			vio_destroy_multipools(vmultip, &fvmp);
3054647Sraghuram 			/* We expect to free the pools without failure here */
3064647Sraghuram 			ASSERT(fvmp == NULL);
3074647Sraghuram 			return (status);
3084647Sraghuram 		}
3094647Sraghuram 	}
3104647Sraghuram 	return (0);
3114647Sraghuram }
3124647Sraghuram 
3134647Sraghuram /*
3144647Sraghuram  * Destroy the multiple pools of mblks. This can only succeed when
3154647Sraghuram  * all allocated mblks have been returned to the pool.
3164647Sraghuram  *
3174647Sraghuram  * If a pool of mblks couldn't be destroyed, then the failed vio_mblk_pool_t
3184647Sraghuram  * pointers are returned via th fvmp list. Its the caller's
3194647Sraghuram  * responsibility to check this list and free them later at an appropriate
3204647Sraghuram  * time with vio_destroy_mblks().
3214647Sraghuram  *
3224647Sraghuram  * Arguments:
3234647Sraghuram  *	vmultip -- A pointer to vio_multi_pool_t structure.
3244647Sraghuram  *	fvmp -- A list in which the pools that couldn't be destroyed are
3254647Sraghuram  *		returned.
3264647Sraghuram  */
3274647Sraghuram void
3284647Sraghuram vio_destroy_multipools(vio_multi_pool_t *vmultip, vio_mblk_pool_t **fvmp)
3294647Sraghuram {
3304647Sraghuram 	int i;
3314647Sraghuram 	vio_mblk_pool_t *vmp;
3324647Sraghuram 
3334647Sraghuram 	for (i = 0; i < vmultip->num_pools; i++) {
3344647Sraghuram 		if ((vmp = vmultip->vmpp[i]) != NULL) {
3354647Sraghuram 			if (vio_destroy_mblks(vmp)) {
3364647Sraghuram 				/*
3374647Sraghuram 				 * if we cannot reclaim all mblks, then
3384647Sraghuram 				 * return the pool in the failed vmp
3394647Sraghuram 				 * list(fvmp).
3404647Sraghuram 				 */
3414647Sraghuram 				vmp->nextp =  *fvmp;
3424647Sraghuram 				*fvmp = vmp;
3434647Sraghuram 			}
3444647Sraghuram 		}
3454647Sraghuram 	}
3469217SWentao.Yang@Sun.COM 	if (vmultip->tbsz != 0)
3479217SWentao.Yang@Sun.COM 		kmem_free(vmultip->bufsz_tbl, vmultip->tbsz);
3484647Sraghuram 	vmultip->bufsz_tbl = NULL;
3494647Sraghuram 	vmultip->nbuf_tbl = NULL;
3504647Sraghuram 	vmultip->vmpp = NULL;
3517529SSriharsha.Basavapatna@Sun.COM 	vmultip->num_pools = 0;
3527529SSriharsha.Basavapatna@Sun.COM 	vmultip->tbsz = 0;
3534647Sraghuram }
3544647Sraghuram 
3554647Sraghuram 
3564647Sraghuram /*
3574647Sraghuram  * Allocate an mblk from one of the free pools, but tries the pool that
3584647Sraghuram  * best fits size requested first.
3594647Sraghuram  */
3604647Sraghuram mblk_t *
3614647Sraghuram vio_multipool_allocb(vio_multi_pool_t *vmultip, size_t size)
3624647Sraghuram {
3634647Sraghuram 	int i;
3644647Sraghuram 	mblk_t *mp = NULL;
3654647Sraghuram 
3664647Sraghuram 	/* Try allocating any size that fits */
3674647Sraghuram 	for (i = 0; i < vmultip->num_pools; i++) {
3684647Sraghuram 		if (size > vmultip->bufsz_tbl[i]) {
3694647Sraghuram 			continue;
3704647Sraghuram 		}
3714647Sraghuram 		mp = vio_allocb(vmultip->vmpp[i]);
3724647Sraghuram 		if (mp != NULL) {
3734647Sraghuram 			break;
3744647Sraghuram 		}
3754647Sraghuram 	}
3764647Sraghuram 	return (mp);
3774647Sraghuram }
3785365Slm66018 
3795365Slm66018 /*
3805365Slm66018  * -----------------------------------------------------------------------------
3815365Slm66018  * LDoms versioning functions
3825365Slm66018  *
3835365Slm66018  * Future work: the version negotiating code in the various VIO drivers
3845365Slm66018  * could be made common and placed here.
3855365Slm66018  */
3865365Slm66018 
3875365Slm66018 /*
3885365Slm66018  * Description:
3895365Slm66018  *	This function checks to see if the supplied version tuple (major,minor)
3905365Slm66018  *	is supported by the version 'ver', negotiated during the handshake
3915365Slm66018  *	between the client and the server (ver).
3925365Slm66018  *
3935365Slm66018  * Assumption:
3945365Slm66018  *	This function assumes that backward compatability is not broken in
3955365Slm66018  *	newer minor versions of the protocol (e.g. v1.5 & v1.1 support v1.0)
3965365Slm66018  *
3975365Slm66018  * Return Value:
3985365Slm66018  *	B_TRUE		- The (major,minor) version is supported
3995365Slm66018  *	B_FALSE		- not supported
4005365Slm66018  */
4015365Slm66018 boolean_t
4025365Slm66018 vio_ver_is_supported(vio_ver_t ver, uint16_t major, uint16_t minor)
4035365Slm66018 {
4045365Slm66018 	if ((ver.major == major) && (ver.minor >= minor))
4055365Slm66018 		return (B_TRUE);
4065365Slm66018 
4075365Slm66018 	return (B_FALSE);
4085365Slm66018 }
409