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