xref: /onnv-gate/usr/src/uts/sun4v/io/vio_util.c (revision 4647:41f3e0ae7859)
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 /*
23*4647Sraghuram  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
242336Snarayan  * Use is subject to license terms.
252336Snarayan  */
262336Snarayan 
272336Snarayan #pragma ident	"%Z%%M%	%I%	%E% SMI"
282336Snarayan 
292336Snarayan #include <sys/types.h>
302336Snarayan #include <sys/sysmacros.h>
312336Snarayan #include <sys/cmn_err.h>
322336Snarayan #include <sys/errno.h>
332336Snarayan #include <sys/kmem.h>
342336Snarayan #include <sys/ksynch.h>
352336Snarayan #include <sys/stream.h>
362336Snarayan #include <sys/ddi.h>
372336Snarayan #include <sys/sunddi.h>
382336Snarayan #include <sys/vio_util.h>
392336Snarayan 
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  *
462336Snarayan  * Returns 0 on success or EINVAL if num_mblks is zero or not
472336Snarayan  * a power of 2.
482336Snarayan  */
492336Snarayan int
502336Snarayan vio_create_mblks(uint64_t num_mblks, size_t mblk_size, vio_mblk_pool_t **poolp)
512336Snarayan {
522336Snarayan 	vio_mblk_pool_t		*vmplp;
532336Snarayan 	vio_mblk_t		*vmp;
542336Snarayan 	uint8_t			*datap;
552336Snarayan 	int			i;
562336Snarayan 
572336Snarayan 	if (!(num_mblks) || (!ISP2(num_mblks))) {
582336Snarayan 		*poolp = 0;
592336Snarayan 		return (EINVAL);
602336Snarayan 	}
612336Snarayan 
622336Snarayan 	vmplp = kmem_zalloc(sizeof (*vmplp), KM_SLEEP);
632336Snarayan 	vmplp->quelen = num_mblks;
642336Snarayan 	vmplp->quemask = num_mblks - 1; /* expects quelen is power-of-2 */
652336Snarayan 	vmplp->mblk_size = mblk_size;
662336Snarayan 
672336Snarayan 	mutex_init(&vmplp->hlock, NULL, MUTEX_DRIVER,
68*4647Sraghuram 		DDI_INTR_PRI(DDI_INTR_SOFTPRI_DEFAULT));
692336Snarayan 	mutex_init(&vmplp->tlock, NULL, MUTEX_DRIVER,
70*4647Sraghuram 		DDI_INTR_PRI(DDI_INTR_SOFTPRI_DEFAULT));
712336Snarayan 
722336Snarayan 	vmplp->basep = kmem_zalloc(num_mblks * sizeof (vio_mblk_t), KM_SLEEP);
732336Snarayan 	vmplp->datap = kmem_zalloc(num_mblks * mblk_size, KM_SLEEP);
742336Snarayan 	vmplp->nextp = NULL;
752336Snarayan 
762336Snarayan 	/* create a queue of pointers to free vio_mblk_t's */
77*4647Sraghuram 	vmplp->quep = kmem_zalloc(vmplp->quelen *
78*4647Sraghuram 		sizeof (vio_mblk_t *), KM_SLEEP);
792336Snarayan 	vmplp->head = 0;
802336Snarayan 	vmplp->tail =  0;
812336Snarayan 
822336Snarayan 	for (i = 0, datap = vmplp->datap; i < num_mblks; i++) {
832336Snarayan 
842336Snarayan 		vmp = &(vmplp->basep[i]);
852336Snarayan 		vmp->vmplp = vmplp;
862336Snarayan 		vmp->datap = datap;
872336Snarayan 		vmp->reclaim.free_func = vio_freeb;
882336Snarayan 		vmp->reclaim.free_arg = (caddr_t)vmp;
892336Snarayan 		vmp->mp = desballoc(vmp->datap, mblk_size, BPRI_MED,
902336Snarayan 		    &vmp->reclaim);
912336Snarayan 
922336Snarayan 		if (vmp->mp == NULL)
932336Snarayan 			continue;
942336Snarayan 
952336Snarayan 		/* put this vmp on the free stack */
962336Snarayan 		vmplp->quep[vmplp->tail] = vmp;
972336Snarayan 		vmplp->tail = (vmplp->tail + 1) & vmplp->quemask;
982336Snarayan 
992336Snarayan 		datap += mblk_size;
1002336Snarayan 	}
1012336Snarayan 
1022336Snarayan 	*poolp = vmplp;
1032336Snarayan 	return (0);
1042336Snarayan }
1052336Snarayan 
1062336Snarayan /*
1072336Snarayan  * Destroy the pool of mblks. This can only succeed when
1082336Snarayan  * all allocated mblks have been returned to the pool.
1092336Snarayan  *
1102336Snarayan  * It is up to the caller to ensure that no further mblks are
1112336Snarayan  * requested from the pool after destroy has been invoked.
1122336Snarayan  *
1132336Snarayan  * Returns 0 on success, EINVAL if handle is invalid, or
1142336Snarayan  * EBUSY if not all mblks reclaimed yet.
1152336Snarayan  */
1162336Snarayan int
1172336Snarayan vio_destroy_mblks(vio_mblk_pool_t *vmplp)
1182336Snarayan {
1192793Slm66018 	uint64_t i;
1202793Slm66018 	uint64_t num_mblks;
1212793Slm66018 	vio_mblk_t *vmp;
1222793Slm66018 
1232336Snarayan 	if (vmplp == NULL)
1242336Snarayan 		return (EINVAL);
1252336Snarayan 
1262336Snarayan 	/*
1272336Snarayan 	 * We can only destroy the pool once all the mblks have
1282336Snarayan 	 * been reclaimed.
1292336Snarayan 	 */
1302336Snarayan 	if (vmplp->head != vmplp->tail) {
1312336Snarayan 		/* some mblks still in use */
1322336Snarayan 		return (EBUSY);
1332336Snarayan 	}
1342336Snarayan 
1352793Slm66018 	num_mblks = vmplp->quelen;
1362793Slm66018 
1372793Slm66018 	/*
1382793Slm66018 	 * Set pool flag to tell vio_freeb() which is invoked from freeb(),
1392793Slm66018 	 * that it is being called in the context of vio_destroy_mblks().
1402793Slm66018 	 * This results in freeing only mblk_t and dblk_t structures for
1412793Slm66018 	 * each mp. The associated data buffers are freed below as one big
1422793Slm66018 	 * chunk through kmem_free(vmplp->datap).
1432793Slm66018 	 */
1442793Slm66018 	vmplp->flag |= VMPL_FLAG_DESTROYING;
1452793Slm66018 	for (i = 0; i < num_mblks; i++) {
1462793Slm66018 		vmp = &(vmplp->basep[i]);
1472793Slm66018 		if (vmp->mp)
1482793Slm66018 			freeb(vmp->mp);
1492793Slm66018 	}
1502793Slm66018 	vmplp->flag &= ~(VMPL_FLAG_DESTROYING);
1512793Slm66018 
1522793Slm66018 	kmem_free(vmplp->basep, num_mblks * sizeof (vio_mblk_t));
1532793Slm66018 	kmem_free(vmplp->datap, num_mblks * vmplp->mblk_size);
1542793Slm66018 	kmem_free(vmplp->quep, num_mblks * sizeof (vio_mblk_t *));
1552336Snarayan 
1562336Snarayan 	mutex_destroy(&vmplp->hlock);
1572336Snarayan 	mutex_destroy(&vmplp->tlock);
1582336Snarayan 
1592336Snarayan 	kmem_free(vmplp, sizeof (*vmplp));
1602336Snarayan 
1612336Snarayan 	return (0);
1622336Snarayan }
1632336Snarayan 
1642336Snarayan /*
1652336Snarayan  * Allocate a mblk from the free pool if one is available.
1662336Snarayan  * Otherwise returns NULL.
1672336Snarayan  */
1682336Snarayan mblk_t *
1692336Snarayan vio_allocb(vio_mblk_pool_t *vmplp)
1702336Snarayan {
1712336Snarayan 	vio_mblk_t	*vmp = NULL;
1722336Snarayan 	mblk_t		*mp = NULL;
1732336Snarayan 	uint32_t	head;
1742336Snarayan 
1752336Snarayan 	mutex_enter(&vmplp->hlock);
1762336Snarayan 	head = (vmplp->head + 1) & vmplp->quemask;
1772336Snarayan 	if (head != vmplp->tail) {
1782336Snarayan 		/* we have free mblks */
1792336Snarayan 		vmp = vmplp->quep[vmplp->head];
1802336Snarayan 		mp = vmp->mp;
1812336Snarayan 		vmplp->head = head;
1822336Snarayan 	}
1832336Snarayan 	mutex_exit(&vmplp->hlock);
1842336Snarayan 
1852336Snarayan 	return (mp);
1862336Snarayan }
1872336Snarayan 
1882336Snarayan /*
1892336Snarayan  * Return a mblk to the free pool. Invoked when the upper IP
1902336Snarayan  * layers do freemsg() etc on the mblk they were passed.
1912336Snarayan  */
1922336Snarayan void
1932336Snarayan vio_freeb(void *arg)
1942336Snarayan {
1952336Snarayan 	vio_mblk_t	*vmp = (vio_mblk_t *)arg;
1962336Snarayan 	vio_mblk_pool_t	*vmplp = vmp->vmplp;
1972336Snarayan 
1982793Slm66018 	if (vmplp->flag & VMPL_FLAG_DESTROYING) {
1992793Slm66018 		/*
2002793Slm66018 		 * This flag indicates that freeb() is being called from
2012793Slm66018 		 * vio_destroy_mblks().
2022793Slm66018 		 * We don't need to alloc a new mblk_t/dblk_t pair for
2032793Slm66018 		 * this data buffer, return from here and the data buffer
2042793Slm66018 		 * itself will be freed in vio_destroy_mblks().
2052793Slm66018 		 */
2062793Slm66018 		return;
2072793Slm66018 	}
2082793Slm66018 
2092336Snarayan 	vmp->mp = desballoc(vmp->datap, vmplp->mblk_size,
210*4647Sraghuram 		BPRI_MED, &vmp->reclaim);
2112336Snarayan 
2122336Snarayan 	mutex_enter(&vmplp->tlock);
2132336Snarayan 	vmplp->quep[vmplp->tail] = vmp;
2142336Snarayan 	vmplp->tail = (vmplp->tail + 1) & vmplp->quemask;
2152336Snarayan 	mutex_exit(&vmplp->tlock);
2162336Snarayan }
217*4647Sraghuram 
218*4647Sraghuram /*
219*4647Sraghuram  * Create a multiple pools of mblks from which future vio_allocb()
220*4647Sraghuram  * or vio_multipool_allocb() requests will be serviced.
221*4647Sraghuram  *
222*4647Sraghuram  * Arguments:
223*4647Sraghuram  *	vmultip -- A pointer to vio_multi_pool_t structure.
224*4647Sraghuram  *	num_pools -- Number of the pools.
225*4647Sraghuram  *	... -- Variable arguments consisting a list of buffer sizes for
226*4647Sraghuram  *		each pool and list of number of buffers for each pool.
227*4647Sraghuram  *
228*4647Sraghuram  * NOTE: The restrictions of vio_create_mblks() apply to this interface also.
229*4647Sraghuram  *
230*4647Sraghuram  * Returns 0 on success or an error returned by vio_create_mblks().
231*4647Sraghuram  */
232*4647Sraghuram int
233*4647Sraghuram vio_init_multipools(vio_multi_pool_t *vmultip, int num_pools, ...)
234*4647Sraghuram {
235*4647Sraghuram 	int		i;
236*4647Sraghuram 	int		status;
237*4647Sraghuram 	char		*tbuf;
238*4647Sraghuram 	va_list		vap;
239*4647Sraghuram 	vio_mblk_pool_t *fvmp = NULL;
240*4647Sraghuram 
241*4647Sraghuram 	/*
242*4647Sraghuram 	 * Allocate memory for all of the following in one allocation.
243*4647Sraghuram 	 * 	bufsz_tbl -- sizeof (uint32_t) * num_pools
244*4647Sraghuram 	 * 	nbuf_tbl  -- sizeof (uint32_t) * num_pools
245*4647Sraghuram 	 *	vmpp	  -- sizeof (vio_mblk_pool_t *) * numpools
246*4647Sraghuram 	 */
247*4647Sraghuram 	vmultip->tbsz = (sizeof (uint32_t) * num_pools) +
248*4647Sraghuram 		(sizeof (uint32_t) * num_pools) +
249*4647Sraghuram 		(sizeof (vio_mblk_pool_t *) * num_pools);
250*4647Sraghuram 	tbuf = kmem_zalloc(vmultip->tbsz, KM_SLEEP);
251*4647Sraghuram 	vmultip->bufsz_tbl = (uint32_t *)tbuf;
252*4647Sraghuram 	vmultip->nbuf_tbl = (uint32_t *)(tbuf +
253*4647Sraghuram 		(sizeof (uint32_t) * num_pools));
254*4647Sraghuram 	vmultip->vmpp = (vio_mblk_pool_t **)(tbuf +
255*4647Sraghuram 		(sizeof (uint32_t) * num_pools * 2));
256*4647Sraghuram 	vmultip->num_pools = num_pools;
257*4647Sraghuram 
258*4647Sraghuram 	/* initialize the array first */
259*4647Sraghuram 	va_start(vap, num_pools);
260*4647Sraghuram 	for (i = 0; i < num_pools; i++) {
261*4647Sraghuram 		vmultip->bufsz_tbl[i] = va_arg(vap, uint32_t);
262*4647Sraghuram 	}
263*4647Sraghuram 	for (i = 0; i < num_pools; i++) {
264*4647Sraghuram 		vmultip->nbuf_tbl[i] = va_arg(vap, uint32_t);
265*4647Sraghuram 	}
266*4647Sraghuram 	va_end(vap);
267*4647Sraghuram 
268*4647Sraghuram 	for (i = 0; i < vmultip->num_pools; i++) {
269*4647Sraghuram 		status = vio_create_mblks(vmultip->nbuf_tbl[i],
270*4647Sraghuram 			vmultip->bufsz_tbl[i], &vmultip->vmpp[i]);
271*4647Sraghuram 		if (status != 0) {
272*4647Sraghuram 			vio_destroy_multipools(vmultip, &fvmp);
273*4647Sraghuram 			/* We expect to free the pools without failure here */
274*4647Sraghuram 			ASSERT(fvmp == NULL);
275*4647Sraghuram 			return (status);
276*4647Sraghuram 		}
277*4647Sraghuram 	}
278*4647Sraghuram 	return (0);
279*4647Sraghuram }
280*4647Sraghuram 
281*4647Sraghuram /*
282*4647Sraghuram  * Destroy the multiple pools of mblks. This can only succeed when
283*4647Sraghuram  * all allocated mblks have been returned to the pool.
284*4647Sraghuram  *
285*4647Sraghuram  * If a pool of mblks couldn't be destroyed, then the failed vio_mblk_pool_t
286*4647Sraghuram  * pointers are returned via th fvmp list. Its the caller's
287*4647Sraghuram  * responsibility to check this list and free them later at an appropriate
288*4647Sraghuram  * time with vio_destroy_mblks().
289*4647Sraghuram  *
290*4647Sraghuram  * Arguments:
291*4647Sraghuram  *	vmultip -- A pointer to vio_multi_pool_t structure.
292*4647Sraghuram  *	fvmp -- A list in which the pools that couldn't be destroyed are
293*4647Sraghuram  *		returned.
294*4647Sraghuram  */
295*4647Sraghuram void
296*4647Sraghuram vio_destroy_multipools(vio_multi_pool_t *vmultip, vio_mblk_pool_t **fvmp)
297*4647Sraghuram {
298*4647Sraghuram 	int i;
299*4647Sraghuram 	vio_mblk_pool_t *vmp;
300*4647Sraghuram 
301*4647Sraghuram 	for (i = 0; i < vmultip->num_pools; i++) {
302*4647Sraghuram 		if ((vmp = vmultip->vmpp[i]) != NULL) {
303*4647Sraghuram 			if (vio_destroy_mblks(vmp)) {
304*4647Sraghuram 				/*
305*4647Sraghuram 				 * if we cannot reclaim all mblks, then
306*4647Sraghuram 				 * return the pool in the failed vmp
307*4647Sraghuram 				 * list(fvmp).
308*4647Sraghuram 				 */
309*4647Sraghuram 				vmp->nextp =  *fvmp;
310*4647Sraghuram 				*fvmp = vmp;
311*4647Sraghuram 			}
312*4647Sraghuram 		}
313*4647Sraghuram 	}
314*4647Sraghuram 	kmem_free(vmultip->bufsz_tbl, vmultip->tbsz);
315*4647Sraghuram 	vmultip->bufsz_tbl = NULL;
316*4647Sraghuram 	vmultip->nbuf_tbl = NULL;
317*4647Sraghuram 	vmultip->vmpp = NULL;
318*4647Sraghuram }
319*4647Sraghuram 
320*4647Sraghuram 
321*4647Sraghuram /*
322*4647Sraghuram  * Allocate an mblk from one of the free pools, but tries the pool that
323*4647Sraghuram  * best fits size requested first.
324*4647Sraghuram  */
325*4647Sraghuram mblk_t *
326*4647Sraghuram vio_multipool_allocb(vio_multi_pool_t *vmultip, size_t size)
327*4647Sraghuram {
328*4647Sraghuram 	int i;
329*4647Sraghuram 	mblk_t *mp = NULL;
330*4647Sraghuram 
331*4647Sraghuram 	/* Try allocating any size that fits */
332*4647Sraghuram 	for (i = 0; i < vmultip->num_pools; i++) {
333*4647Sraghuram 		if (size > vmultip->bufsz_tbl[i]) {
334*4647Sraghuram 			continue;
335*4647Sraghuram 		}
336*4647Sraghuram 		mp = vio_allocb(vmultip->vmpp[i]);
337*4647Sraghuram 		if (mp != NULL) {
338*4647Sraghuram 			break;
339*4647Sraghuram 		}
340*4647Sraghuram 	}
341*4647Sraghuram 	return (mp);
342*4647Sraghuram }
343