Home | History | Annotate | Download | only in io
      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 /*
     23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #include <sys/types.h>
     28 #include <sys/sysmacros.h>
     29 #include <sys/errno.h>
     30 #include <sys/kmem.h>
     31 #include <sys/ksynch.h>
     32 #include <sys/stream.h>
     33 #include <sys/ddi.h>
     34 #include <sys/sunddi.h>
     35 #include <sys/vio_util.h>
     36 
     37 static int vio_pool_cleanup_retries = 10;	/* Max retries to free pool */
     38 static int vio_pool_cleanup_delay = 10000;	/* 10ms */
     39 
     40 /*
     41  * Create a pool of mblks from which future vio_allocb() requests
     42  * will be serviced.
     43  *
     44  * NOTE: num_mblks has to non-zero and a power-of-2
     45  *
     46  * Returns
     47  *	0 on success
     48  *	EINVAL if num_mblks is zero or not a power of 2.
     49  *	ENOSPC if the pool could not be created due to alloc failures.
     50  */
     51 int
     52 vio_create_mblks(uint64_t num_mblks, size_t mblk_size, vio_mblk_pool_t **poolp)
     53 {
     54 	vio_mblk_pool_t		*vmplp;
     55 	vio_mblk_t		*vmp;
     56 	uint8_t			*datap;
     57 	int			i;
     58 	int			rv;
     59 
     60 	if (!(num_mblks) || (!ISP2(num_mblks))) {
     61 		*poolp = 0;
     62 		return (EINVAL);
     63 	}
     64 
     65 	vmplp = kmem_zalloc(sizeof (*vmplp), KM_SLEEP);
     66 	vmplp->quelen = num_mblks;
     67 	vmplp->quemask = num_mblks - 1; /* expects quelen is power-of-2 */
     68 	vmplp->mblk_size = mblk_size;
     69 
     70 	mutex_init(&vmplp->hlock, NULL, MUTEX_DRIVER,
     71 	    DDI_INTR_PRI(DDI_INTR_SOFTPRI_DEFAULT));
     72 	mutex_init(&vmplp->tlock, NULL, MUTEX_DRIVER,
     73 	    DDI_INTR_PRI(DDI_INTR_SOFTPRI_DEFAULT));
     74 
     75 	vmplp->basep = kmem_zalloc(num_mblks * sizeof (vio_mblk_t), KM_SLEEP);
     76 	vmplp->datap = kmem_zalloc(num_mblks * mblk_size, KM_SLEEP);
     77 	vmplp->nextp = NULL;
     78 
     79 	/* create a queue of pointers to free vio_mblk_t's */
     80 	vmplp->quep = kmem_zalloc(vmplp->quelen *
     81 	    sizeof (vio_mblk_t *), KM_SLEEP);
     82 	vmplp->head = 0;
     83 	vmplp->tail =  0;
     84 
     85 	for (i = 0, datap = vmplp->datap; i < num_mblks; i++) {
     86 
     87 		vmp = &(vmplp->basep[i]);
     88 		vmp->vmplp = vmplp;
     89 		vmp->datap = datap;
     90 		vmp->reclaim.free_func = vio_freeb;
     91 		vmp->reclaim.free_arg = (caddr_t)vmp;
     92 		vmp->mp = desballoc(vmp->datap, mblk_size, BPRI_MED,
     93 		    &vmp->reclaim);
     94 
     95 		if (vmp->mp == NULL) {
     96 			/* reset tail */
     97 			vmplp->tail = vmplp->head;
     98 
     99 			/*
    100 			 * vio_destroy_mblks() frees mblks that have been
    101 			 * allocated so far and then destroys the pool.
    102 			 */
    103 			rv = vio_destroy_mblks(vmplp);
    104 			ASSERT(rv == 0);
    105 
    106 			*poolp = NULL;
    107 			return (ENOSPC);
    108 		}
    109 
    110 		/* put this vmp on the free stack */
    111 		vmplp->quep[vmplp->tail] = vmp;
    112 		vmplp->tail = (vmplp->tail + 1) & vmplp->quemask;
    113 
    114 		datap += mblk_size;
    115 	}
    116 
    117 	*poolp = vmplp;
    118 	return (0);
    119 }
    120 
    121 /*
    122  * Destroy the pool of mblks. This can only succeed when
    123  * all allocated mblks have been returned to the pool.
    124  *
    125  * It is up to the caller to ensure that no further mblks are
    126  * requested from the pool after destroy has been invoked.
    127  *
    128  * Returns 0 on success, EINVAL if handle is invalid, or
    129  * EBUSY if not all mblks reclaimed yet.
    130  */
    131 int
    132 vio_destroy_mblks(vio_mblk_pool_t *vmplp)
    133 {
    134 	uint64_t	i;
    135 	uint64_t	num_mblks;
    136 	vio_mblk_t	*vmp;
    137 	int		pool_cleanup_retries = 0;
    138 
    139 
    140 	if (vmplp == NULL)
    141 		return (EINVAL);
    142 
    143 	/*
    144 	 * We can only destroy the pool once all the mblks have
    145 	 * been reclaimed.
    146 	 */
    147 	do {
    148 		if (vmplp->head == vmplp->tail) {
    149 			break;
    150 		}
    151 
    152 		/* some mblks still in use */
    153 		drv_usecwait(vio_pool_cleanup_delay);
    154 	} while (++pool_cleanup_retries < vio_pool_cleanup_retries);
    155 
    156 	if (vmplp->head != vmplp->tail) {
    157 		return (EBUSY);
    158 	}
    159 
    160 	num_mblks = vmplp->quelen;
    161 
    162 	/*
    163 	 * Set pool flag to tell vio_freeb() which is invoked from freeb(),
    164 	 * that it is being called in the context of vio_destroy_mblks().
    165 	 * This results in freeing only mblk_t and dblk_t structures for
    166 	 * each mp. The associated data buffers are freed below as one big
    167 	 * chunk through kmem_free(vmplp->datap).
    168 	 */
    169 	vmplp->flag |= VMPL_FLAG_DESTROYING;
    170 	for (i = 0; i < num_mblks; i++) {
    171 		vmp = &(vmplp->basep[i]);
    172 		/*
    173 		 * It is possible that mblks have been allocated only upto
    174 		 * a certain index and the entire quelen has not been
    175 		 * initialized. This might happen due to desballoc() failure
    176 		 * while creating the pool. The below check handles this
    177 		 * condition.
    178 		 */
    179 		if (vmp->mp != NULL)
    180 			freeb(vmp->mp);
    181 	}
    182 	vmplp->flag &= ~(VMPL_FLAG_DESTROYING);
    183 
    184 	kmem_free(vmplp->basep, num_mblks * sizeof (vio_mblk_t));
    185 	kmem_free(vmplp->datap, num_mblks * vmplp->mblk_size);
    186 	kmem_free(vmplp->quep, num_mblks * sizeof (vio_mblk_t *));
    187 
    188 	mutex_destroy(&vmplp->hlock);
    189 	mutex_destroy(&vmplp->tlock);
    190 
    191 	kmem_free(vmplp, sizeof (*vmplp));
    192 
    193 	return (0);
    194 }
    195 
    196 /*
    197  * Allocate a mblk from the free pool if one is available.
    198  * Otherwise returns NULL.
    199  */
    200 mblk_t *
    201 vio_allocb(vio_mblk_pool_t *vmplp)
    202 {
    203 	vio_mblk_t	*vmp = NULL;
    204 	mblk_t		*mp = NULL;
    205 	uint32_t	head;
    206 
    207 	mutex_enter(&vmplp->hlock);
    208 	head = (vmplp->head + 1) & vmplp->quemask;
    209 	if (head != vmplp->tail) {
    210 		/* we have free mblks */
    211 		vmp = vmplp->quep[vmplp->head];
    212 		mp = vmp->mp;
    213 		vmplp->head = head;
    214 	}
    215 	mutex_exit(&vmplp->hlock);
    216 
    217 	return (mp);
    218 }
    219 
    220 /*
    221  * Return a mblk to the free pool. Invoked when the upper IP
    222  * layers do freemsg() etc on the mblk they were passed.
    223  */
    224 void
    225 vio_freeb(void *arg)
    226 {
    227 	vio_mblk_t	*vmp = (vio_mblk_t *)arg;
    228 	vio_mblk_pool_t	*vmplp = vmp->vmplp;
    229 
    230 	if (vmplp->flag & VMPL_FLAG_DESTROYING) {
    231 		/*
    232 		 * This flag indicates that freeb() is being called from
    233 		 * vio_destroy_mblks().
    234 		 * We don't need to alloc a new mblk_t/dblk_t pair for
    235 		 * this data buffer, return from here and the data buffer
    236 		 * itself will be freed in vio_destroy_mblks().
    237 		 */
    238 		return;
    239 	}
    240 
    241 	vmp->mp = desballoc(vmp->datap, vmplp->mblk_size,
    242 	    BPRI_MED, &vmp->reclaim);
    243 
    244 	mutex_enter(&vmplp->tlock);
    245 	vmplp->quep[vmplp->tail] = vmp;
    246 	vmplp->tail = (vmplp->tail + 1) & vmplp->quemask;
    247 	mutex_exit(&vmplp->tlock);
    248 }
    249 
    250 /*
    251  * Create a multiple pools of mblks from which future vio_allocb()
    252  * or vio_multipool_allocb() requests will be serviced.
    253  *
    254  * Arguments:
    255  *	vmultip -- A pointer to vio_multi_pool_t structure.
    256  *	num_pools -- Number of the pools.
    257  *	... -- Variable arguments consisting a list of buffer sizes for
    258  *		each pool and list of number of buffers for each pool.
    259  *
    260  * NOTE: The restrictions of vio_create_mblks() apply to this interface also.
    261  *
    262  * Returns 0 on success or an error returned by vio_create_mblks().
    263  */
    264 int
    265 vio_init_multipools(vio_multi_pool_t *vmultip, int num_pools, ...)
    266 {
    267 	int		i;
    268 	int		status;
    269 	char		*tbuf;
    270 	va_list		vap;
    271 	vio_mblk_pool_t *fvmp = NULL;
    272 
    273 	/*
    274 	 * Allocate memory for all of the following in one allocation.
    275 	 * 	bufsz_tbl -- sizeof (uint32_t) * num_pools
    276 	 * 	nbuf_tbl  -- sizeof (uint32_t) * num_pools
    277 	 *	vmpp	  -- sizeof (vio_mblk_pool_t *) * numpools
    278 	 */
    279 	vmultip->tbsz = (sizeof (uint32_t) * num_pools) +
    280 	    (sizeof (uint32_t) * num_pools) +
    281 	    (sizeof (vio_mblk_pool_t *) * num_pools);
    282 	tbuf = kmem_zalloc(vmultip->tbsz, KM_SLEEP);
    283 	vmultip->bufsz_tbl = (uint32_t *)tbuf;
    284 	vmultip->nbuf_tbl = (uint32_t *)(tbuf +
    285 	    (sizeof (uint32_t) * num_pools));
    286 	vmultip->vmpp = (vio_mblk_pool_t **)(tbuf +
    287 	    (sizeof (uint32_t) * num_pools * 2));
    288 	vmultip->num_pools = num_pools;
    289 
    290 	/* initialize the array first */
    291 	va_start(vap, num_pools);
    292 	for (i = 0; i < num_pools; i++) {
    293 		vmultip->bufsz_tbl[i] = va_arg(vap, uint32_t);
    294 	}
    295 	for (i = 0; i < num_pools; i++) {
    296 		vmultip->nbuf_tbl[i] = va_arg(vap, uint32_t);
    297 	}
    298 	va_end(vap);
    299 
    300 	for (i = 0; i < vmultip->num_pools; i++) {
    301 		status = vio_create_mblks(vmultip->nbuf_tbl[i],
    302 		    vmultip->bufsz_tbl[i], &vmultip->vmpp[i]);
    303 		if (status != 0) {
    304 			vio_destroy_multipools(vmultip, &fvmp);
    305 			/* We expect to free the pools without failure here */
    306 			ASSERT(fvmp == NULL);
    307 			return (status);
    308 		}
    309 	}
    310 	return (0);
    311 }
    312 
    313 /*
    314  * Destroy the multiple pools of mblks. This can only succeed when
    315  * all allocated mblks have been returned to the pool.
    316  *
    317  * If a pool of mblks couldn't be destroyed, then the failed vio_mblk_pool_t
    318  * pointers are returned via th fvmp list. Its the caller's
    319  * responsibility to check this list and free them later at an appropriate
    320  * time with vio_destroy_mblks().
    321  *
    322  * Arguments:
    323  *	vmultip -- A pointer to vio_multi_pool_t structure.
    324  *	fvmp -- A list in which the pools that couldn't be destroyed are
    325  *		returned.
    326  */
    327 void
    328 vio_destroy_multipools(vio_multi_pool_t *vmultip, vio_mblk_pool_t **fvmp)
    329 {
    330 	int i;
    331 	vio_mblk_pool_t *vmp;
    332 
    333 	for (i = 0; i < vmultip->num_pools; i++) {
    334 		if ((vmp = vmultip->vmpp[i]) != NULL) {
    335 			if (vio_destroy_mblks(vmp)) {
    336 				/*
    337 				 * if we cannot reclaim all mblks, then
    338 				 * return the pool in the failed vmp
    339 				 * list(fvmp).
    340 				 */
    341 				vmp->nextp =  *fvmp;
    342 				*fvmp = vmp;
    343 			}
    344 		}
    345 	}
    346 	if (vmultip->tbsz != 0)
    347 		kmem_free(vmultip->bufsz_tbl, vmultip->tbsz);
    348 	vmultip->bufsz_tbl = NULL;
    349 	vmultip->nbuf_tbl = NULL;
    350 	vmultip->vmpp = NULL;
    351 	vmultip->num_pools = 0;
    352 	vmultip->tbsz = 0;
    353 }
    354 
    355 
    356 /*
    357  * Allocate an mblk from one of the free pools, but tries the pool that
    358  * best fits size requested first.
    359  */
    360 mblk_t *
    361 vio_multipool_allocb(vio_multi_pool_t *vmultip, size_t size)
    362 {
    363 	int i;
    364 	mblk_t *mp = NULL;
    365 
    366 	/* Try allocating any size that fits */
    367 	for (i = 0; i < vmultip->num_pools; i++) {
    368 		if (size > vmultip->bufsz_tbl[i]) {
    369 			continue;
    370 		}
    371 		mp = vio_allocb(vmultip->vmpp[i]);
    372 		if (mp != NULL) {
    373 			break;
    374 		}
    375 	}
    376 	return (mp);
    377 }
    378 
    379 /*
    380  * -----------------------------------------------------------------------------
    381  * LDoms versioning functions
    382  *
    383  * Future work: the version negotiating code in the various VIO drivers
    384  * could be made common and placed here.
    385  */
    386 
    387 /*
    388  * Description:
    389  *	This function checks to see if the supplied version tuple (major,minor)
    390  *	is supported by the version 'ver', negotiated during the handshake
    391  *	between the client and the server (ver).
    392  *
    393  * Assumption:
    394  *	This function assumes that backward compatability is not broken in
    395  *	newer minor versions of the protocol (e.g. v1.5 & v1.1 support v1.0)
    396  *
    397  * Return Value:
    398  *	B_TRUE		- The (major,minor) version is supported
    399  *	B_FALSE		- not supported
    400  */
    401 boolean_t
    402 vio_ver_is_supported(vio_ver_t ver, uint16_t major, uint16_t minor)
    403 {
    404 	if ((ver.major == major) && (ver.minor >= minor))
    405 		return (B_TRUE);
    406 
    407 	return (B_FALSE);
    408 }
    409