/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include static int vio_pool_cleanup_retries = 10; /* Max retries to free pool */ static int vio_pool_cleanup_delay = 10000; /* 10ms */ /* * Create a pool of mblks from which future vio_allocb() requests * will be serviced. * * NOTE: num_mblks has to non-zero and a power-of-2 * * Returns * 0 on success * EINVAL if num_mblks is zero or not a power of 2. * ENOSPC if the pool could not be created due to alloc failures. */ int vio_create_mblks(uint64_t num_mblks, size_t mblk_size, uint8_t *mblk_datap, vio_mblk_pool_t **poolp) { vio_mblk_pool_t *vmplp; vio_mblk_t *vmp; uint8_t *datap; int i; int rv; if (!(num_mblks) || (!ISP2(num_mblks))) { *poolp = 0; return (EINVAL); } vmplp = kmem_zalloc(sizeof (*vmplp), KM_SLEEP); vmplp->quelen = num_mblks; vmplp->quemask = num_mblks - 1; /* expects quelen is power-of-2 */ vmplp->mblk_size = mblk_size; mutex_init(&vmplp->hlock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(DDI_INTR_SOFTPRI_DEFAULT)); mutex_init(&vmplp->tlock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(DDI_INTR_SOFTPRI_DEFAULT)); vmplp->basep = kmem_zalloc(num_mblks * sizeof (vio_mblk_t), KM_SLEEP); if (mblk_datap == NULL) { vmplp->datap = kmem_zalloc(num_mblks * mblk_size, KM_SLEEP); } else { vmplp->datap = mblk_datap; vmplp->flag |= VMPL_FLAG_CLIENT_DATA; } vmplp->nextp = NULL; /* create a queue of pointers to free vio_mblk_t's */ vmplp->quep = kmem_zalloc(vmplp->quelen * sizeof (vio_mblk_t *), KM_SLEEP); vmplp->head = 0; vmplp->tail = 0; for (i = 0, datap = vmplp->datap; i < num_mblks; i++) { vmp = &(vmplp->basep[i]); vmp->vmplp = vmplp; vmp->datap = datap; vmp->reclaim.free_func = vio_freeb; vmp->reclaim.free_arg = (caddr_t)vmp; vmp->mp = desballoc(vmp->datap, mblk_size, BPRI_MED, &vmp->reclaim); if (vmp->mp == NULL) { /* reset tail */ vmplp->tail = vmplp->head; /* * vio_destroy_mblks() frees mblks that have been * allocated so far and then destroys the pool. */ rv = vio_destroy_mblks(vmplp); ASSERT(rv == 0); *poolp = NULL; return (ENOSPC); } vmp->index = i; vmp->state = VIO_MBLK_FREE; /* put this vmp on the free stack */ vmplp->quep[vmplp->tail] = vmp; vmplp->tail = (vmplp->tail + 1) & vmplp->quemask; datap += mblk_size; } *poolp = vmplp; return (0); } /* * Destroy the pool of mblks. This can only succeed when * all allocated mblks have been returned to the pool. * * It is up to the caller to ensure that no further mblks are * requested from the pool after destroy has been invoked. * * Returns 0 on success, EINVAL if handle is invalid, or * EBUSY if not all mblks reclaimed yet. */ int vio_destroy_mblks(vio_mblk_pool_t *vmplp) { uint64_t i; uint64_t num_mblks; vio_mblk_t *vmp; int pool_cleanup_retries = 0; if (vmplp == NULL) return (EINVAL); /* * We can only destroy the pool once all the mblks have * been reclaimed. */ do { if (vmplp->head == vmplp->tail) { break; } /* some mblks still in use */ drv_usecwait(vio_pool_cleanup_delay); } while (++pool_cleanup_retries < vio_pool_cleanup_retries); if (vmplp->head != vmplp->tail) { return (EBUSY); } num_mblks = vmplp->quelen; /* * Set pool flag to tell vio_freeb() which is invoked from freeb(), * that it is being called in the context of vio_destroy_mblks(). * This results in freeing only mblk_t and dblk_t structures for * each mp. The associated data buffers are freed below as one big * chunk through kmem_free(vmplp->datap). */ vmplp->flag |= VMPL_FLAG_DESTROYING; for (i = 0; i < num_mblks; i++) { vmp = &(vmplp->basep[i]); /* * It is possible that mblks have been allocated only upto * a certain index and the entire quelen has not been * initialized. This might happen due to desballoc() failure * while creating the pool. The below check handles this * condition. */ if (vmp->mp != NULL) freeb(vmp->mp); } vmplp->flag &= ~(VMPL_FLAG_DESTROYING); kmem_free(vmplp->basep, num_mblks * sizeof (vio_mblk_t)); if ((vmplp->flag & VMPL_FLAG_CLIENT_DATA) == 0) { kmem_free(vmplp->datap, num_mblks * vmplp->mblk_size); } kmem_free(vmplp->quep, num_mblks * sizeof (vio_mblk_t *)); mutex_destroy(&vmplp->hlock); mutex_destroy(&vmplp->tlock); kmem_free(vmplp, sizeof (*vmplp)); return (0); } /* * Allocate a vio_mblk from the free pool if one is available. * Otherwise returns NULL. */ vio_mblk_t * vio_allocb(vio_mblk_pool_t *vmplp) { vio_mblk_t *vmp = NULL; uint32_t head; mutex_enter(&vmplp->hlock); head = (vmplp->head + 1) & vmplp->quemask; if (head != vmplp->tail) { /* we have free mblks */ vmp = vmplp->quep[vmplp->head]; vmplp->head = head; ASSERT(vmp->state == VIO_MBLK_FREE); vmp->state = VIO_MBLK_BOUND; } mutex_exit(&vmplp->hlock); return (vmp); } /* * Return a mblk to the free pool. Invoked when the upper IP * layers do freemsg() etc on the mblk they were passed. */ void vio_freeb(void *arg) { vio_mblk_t *vmp = (vio_mblk_t *)arg; vio_mblk_pool_t *vmplp = vmp->vmplp; if (vmplp->flag & VMPL_FLAG_DESTROYING) { /* * This flag indicates that freeb() is being called from * vio_destroy_mblks(). * We don't need to alloc a new mblk_t/dblk_t pair for * this data buffer, return from here and the data buffer * itself will be freed in vio_destroy_mblks(). */ return; } vmp->mp = desballoc(vmp->datap, vmplp->mblk_size, BPRI_MED, &vmp->reclaim); vmp->state = VIO_MBLK_FREE; mutex_enter(&vmplp->tlock); vmplp->quep[vmplp->tail] = vmp; vmplp->tail = (vmplp->tail + 1) & vmplp->quemask; mutex_exit(&vmplp->tlock); } /* * This function searches the given mblk pool for mblks that are in the * BOUND state and moves them to the FREE state. Note that only clients that * are operating in RxDringData mode use this function. This allows such * clients to reclaim buffers that are provided to the peer as shared memory, * before calling vio_destroy_mblks(). We don't need this in other cases * as the buffer is locally managed. */ void vio_clobber_pool(vio_mblk_pool_t *vmplp) { uint64_t num_mblks = vmplp->quelen; uint64_t i; vio_mblk_t *vmp; mutex_enter(&vmplp->hlock); mutex_enter(&vmplp->tlock); for (i = 0; i < num_mblks; i++) { vmp = &(vmplp->basep[i]); if ((vmp->state & VIO_MBLK_BOUND) != 0) { /* put this vmp on the free stack */ vmp->state = VIO_MBLK_FREE; ASSERT(vmplp->tail != vmplp->head); vmplp->quep[vmplp->tail] = vmp; vmplp->tail = (vmplp->tail + 1) & vmplp->quemask; } } mutex_exit(&vmplp->tlock); mutex_exit(&vmplp->hlock); } /* * Create a multiple pools of mblks from which future vio_allocb() * or vio_multipool_allocb() requests will be serviced. * * Arguments: * vmultip -- A pointer to vio_multi_pool_t structure. * num_pools -- Number of the pools. * ... -- Variable arguments consisting a list of buffer sizes for * each pool and list of number of buffers for each pool. * * NOTE: The restrictions of vio_create_mblks() apply to this interface also. * * Returns 0 on success or an error returned by vio_create_mblks(). */ int vio_init_multipools(vio_multi_pool_t *vmultip, int num_pools, ...) { int i; int status; char *tbuf; va_list vap; vio_mblk_pool_t *fvmp = NULL; /* * Allocate memory for all of the following in one allocation. * bufsz_tbl -- sizeof (uint32_t) * num_pools * nbuf_tbl -- sizeof (uint32_t) * num_pools * vmpp -- sizeof (vio_mblk_pool_t *) * numpools */ vmultip->tbsz = (sizeof (uint32_t) * num_pools) + (sizeof (uint32_t) * num_pools) + (sizeof (vio_mblk_pool_t *) * num_pools); tbuf = kmem_zalloc(vmultip->tbsz, KM_SLEEP); vmultip->bufsz_tbl = (uint32_t *)tbuf; vmultip->nbuf_tbl = (uint32_t *)(tbuf + (sizeof (uint32_t) * num_pools)); vmultip->vmpp = (vio_mblk_pool_t **)(tbuf + (sizeof (uint32_t) * num_pools * 2)); vmultip->num_pools = num_pools; /* initialize the array first */ va_start(vap, num_pools); for (i = 0; i < num_pools; i++) { vmultip->bufsz_tbl[i] = va_arg(vap, uint32_t); } for (i = 0; i < num_pools; i++) { vmultip->nbuf_tbl[i] = va_arg(vap, uint32_t); } va_end(vap); for (i = 0; i < vmultip->num_pools; i++) { status = vio_create_mblks(vmultip->nbuf_tbl[i], vmultip->bufsz_tbl[i], NULL, &vmultip->vmpp[i]); if (status != 0) { vio_destroy_multipools(vmultip, &fvmp); /* We expect to free the pools without failure here */ ASSERT(fvmp == NULL); return (status); } } return (0); } /* * Destroy the multiple pools of mblks. This can only succeed when * all allocated mblks have been returned to the pool. * * If a pool of mblks couldn't be destroyed, then the failed vio_mblk_pool_t * pointers are returned via th fvmp list. Its the caller's * responsibility to check this list and free them later at an appropriate * time with vio_destroy_mblks(). * * Arguments: * vmultip -- A pointer to vio_multi_pool_t structure. * fvmp -- A list in which the pools that couldn't be destroyed are * returned. */ void vio_destroy_multipools(vio_multi_pool_t *vmultip, vio_mblk_pool_t **fvmp) { int i; vio_mblk_pool_t *vmp; for (i = 0; i < vmultip->num_pools; i++) { if ((vmp = vmultip->vmpp[i]) != NULL) { if (vio_destroy_mblks(vmp)) { /* * if we cannot reclaim all mblks, then * return the pool in the failed vmp * list(fvmp). */ vmp->nextp = *fvmp; *fvmp = vmp; } } } if (vmultip->tbsz != 0) kmem_free(vmultip->bufsz_tbl, vmultip->tbsz); vmultip->bufsz_tbl = NULL; vmultip->nbuf_tbl = NULL; vmultip->vmpp = NULL; vmultip->num_pools = 0; vmultip->tbsz = 0; } /* * Allocate an vio_mblk from one of the free pools, but tries the pool that * best fits size requested first. */ vio_mblk_t * vio_multipool_allocb(vio_multi_pool_t *vmultip, size_t size) { int i; vio_mblk_t *vmp = NULL; /* Try allocating any size that fits */ for (i = 0; i < vmultip->num_pools; i++) { if (size > vmultip->bufsz_tbl[i]) { continue; } vmp = vio_allocb(vmultip->vmpp[i]); if (vmp != NULL) { break; } } return (vmp); } /* * ----------------------------------------------------------------------------- * LDoms versioning functions * * Future work: the version negotiating code in the various VIO drivers * could be made common and placed here. */ /* * Description: * This function checks to see if the supplied version tuple (major,minor) * is supported by the version 'ver', negotiated during the handshake * between the client and the server (ver). * * Assumption: * This function assumes that backward compatability is not broken in * newer minor versions of the protocol (e.g. v1.5 & v1.1 support v1.0) * * Return Value: * B_TRUE - The (major,minor) version is supported * B_FALSE - not supported */ boolean_t vio_ver_is_supported(vio_ver_t ver, uint16_t major, uint16_t minor) { if ((ver.major == major) && (ver.minor >= minor)) return (B_TRUE); return (B_FALSE); }