diff options
author | Sebastien Roy <seb@delphix.com> | 2016-11-17 16:00:18 -0800 |
---|---|---|
committer | Matthew Ahrens <mahrens@delphix.com> | 2016-11-17 16:22:11 -0800 |
commit | 6849994e8263545ed3c0f6f5676e47b38e14f63e (patch) | |
tree | 92bc38f81a639fcdbdbaa7ce40126f7f26d46c37 /usr/src | |
parent | ca5345b6a28e9e9bfd0c135121d62c6b35a5390d (diff) | |
download | illumos-joyent-6849994e8263545ed3c0f6f5676e47b38e14f63e.tar.gz |
7582 vmxnet3s MAC callbacks erroneously return DDI_ return values
7583 vmxnet3s rx buffer allocations can result in system unresponsiveness
Reviewed by: Pavel Zakharov <pavel.zakharov@delphix.com>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Steve Gonczi <steve.gonczi@delphix.com>
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: Dan McDonald <danmcd@omniti.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/uts/intel/io/vmxnet3s/vmxnet3.h | 9 | ||||
-rw-r--r-- | usr/src/uts/intel/io/vmxnet3s/vmxnet3_main.c | 132 | ||||
-rw-r--r-- | usr/src/uts/intel/io/vmxnet3s/vmxnet3_rx.c | 140 | ||||
-rw-r--r-- | usr/src/uts/intel/io/vmxnet3s/vmxnet3_tx.c | 4 | ||||
-rw-r--r-- | usr/src/uts/intel/io/vmxnet3s/vmxnet3_utils.c | 50 | ||||
-rw-r--r-- | usr/src/uts/intel/io/vmxnet3s/vmxnet3s.conf | 2 |
6 files changed, 217 insertions, 120 deletions
diff --git a/usr/src/uts/intel/io/vmxnet3s/vmxnet3.h b/usr/src/uts/intel/io/vmxnet3s/vmxnet3.h index b49ff852f1..039c0f2e11 100644 --- a/usr/src/uts/intel/io/vmxnet3s/vmxnet3.h +++ b/usr/src/uts/intel/io/vmxnet3s/vmxnet3.h @@ -14,7 +14,7 @@ */ /* - * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright (c) 2012, 2016 by Delphix. All rights reserved. */ #ifndef _VMXNET3_H_ @@ -148,8 +148,8 @@ typedef struct vmxnet3_softc_t { vmxnet3_rxqueue_t rxQueue; kmutex_t rxPoolLock; vmxnet3_rxpool_t rxPool; - volatile uint32_t rxNumBufs; uint32_t rxMode; + boolean_t alloc_ok; vmxnet3_dmabuf_t mfTable; kstat_t *devKstats; @@ -158,8 +158,10 @@ typedef struct vmxnet3_softc_t { uint32_t tx_pullup_failed; uint32_t tx_ring_full; uint32_t tx_error; + uint32_t rx_num_bufs; uint32_t rx_alloc_buf; uint32_t rx_alloc_failed; + uint32_t rx_pool_empty; } vmxnet3_softc_t; typedef struct vmxnet3_kstats_t { @@ -167,8 +169,11 @@ typedef struct vmxnet3_kstats_t { kstat_named_t tx_pullup_needed; kstat_named_t tx_ring_full; kstat_named_t rx_alloc_buf; + kstat_named_t rx_pool_empty; + kstat_named_t rx_num_bufs; } vmxnet3_kstats_t; +int vmxnet3_dmaerr2errno(int); int vmxnet3_alloc_dma_mem_1(vmxnet3_softc_t *dp, vmxnet3_dmabuf_t *dma, size_t size, boolean_t canSleep); int vmxnet3_alloc_dma_mem_128(vmxnet3_softc_t *dp, vmxnet3_dmabuf_t *dma, diff --git a/usr/src/uts/intel/io/vmxnet3s/vmxnet3_main.c b/usr/src/uts/intel/io/vmxnet3s/vmxnet3_main.c index 41536583f5..612dc052f1 100644 --- a/usr/src/uts/intel/io/vmxnet3s/vmxnet3_main.c +++ b/usr/src/uts/intel/io/vmxnet3s/vmxnet3_main.c @@ -14,7 +14,7 @@ */ /* - * Copyright (c) 2012, 2014 by Delphix. All rights reserved. + * Copyright (c) 2012, 2016 by Delphix. All rights reserved. */ #include <vmxnet3.h> @@ -26,6 +26,13 @@ #define BUILD_NUMBER_NUMERIC 3227872 /* + * If we run out of rxPool buffers, only allocate if the MTU is <= PAGESIZE + * so that we don't have to incur the cost of allocating multiple contiguous + * pages (very slow) in interrupt context. + */ +#define VMXNET3_ALLOC_OK(dp) ((dp)->cur_mtu <= PAGESIZE) + +/* * TODO: * - Tx data ring * - MAC_CAPAB_POLL support @@ -88,7 +95,7 @@ static ddi_dma_attr_t vmxnet3_dma_attrs_tx = { * Fetch the statistics of a vmxnet3 device. * * Returns: - * DDI_SUCCESS or DDI_FAILURE. + * 0 on success, non-zero on failure. */ static int vmxnet3_getstat(void *data, uint_t stat, uint64_t *val) @@ -100,7 +107,7 @@ vmxnet3_getstat(void *data, uint_t stat, uint64_t *val) VMXNET3_DEBUG(dp, 3, "getstat(%u)\n", stat); if (!dp->devEnabled) { - return (DDI_FAILURE); + return (EBUSY); } txStats = &VMXNET3_TQDESC(dp)->stats; @@ -130,7 +137,7 @@ vmxnet3_getstat(void *data, uint_t stat, uint64_t *val) /* nothing */ break; default: - return (DDI_FAILURE); + return (ENOTSUP); } /* @@ -190,33 +197,34 @@ vmxnet3_getstat(void *data, uint_t stat, uint64_t *val) ASSERT(B_FALSE); } - return (DDI_SUCCESS); + return (0); } /* * Allocate and initialize the shared data structures of a vmxnet3 device. * * Returns: - * DDI_SUCCESS or DDI_FAILURE. + * 0 on sucess, non-zero on failure. */ static int vmxnet3_prepare_drivershared(vmxnet3_softc_t *dp) { Vmxnet3_DriverShared *ds; size_t allocSize = sizeof (Vmxnet3_DriverShared); + int err; - if (vmxnet3_alloc_dma_mem_1(dp, &dp->sharedData, allocSize, - B_TRUE) != DDI_SUCCESS) { - return (DDI_FAILURE); + if ((err = vmxnet3_alloc_dma_mem_1(dp, &dp->sharedData, allocSize, + B_TRUE)) != 0) { + return (err); } ds = VMXNET3_DS(dp); (void) memset(ds, 0, allocSize); allocSize = sizeof (Vmxnet3_TxQueueDesc) + sizeof (Vmxnet3_RxQueueDesc); - if (vmxnet3_alloc_dma_mem_128(dp, &dp->queueDescs, allocSize, - B_TRUE) != DDI_SUCCESS) { + if ((err = vmxnet3_alloc_dma_mem_128(dp, &dp->queueDescs, allocSize, + B_TRUE)) != 0) { vmxnet3_free_dma_mem(&dp->sharedData); - return (DDI_FAILURE); + return (err); } (void) memset(dp->queueDescs.buf, 0, allocSize); @@ -254,7 +262,7 @@ vmxnet3_prepare_drivershared(vmxnet3_softc_t *dp) VMXNET3_BAR1_PUT32(dp, VMXNET3_REG_DSAH, VMXNET3_ADDR_HI(dp->sharedData.bufPA)); - return (DDI_SUCCESS); + return (0); } /* @@ -274,23 +282,24 @@ vmxnet3_destroy_drivershared(vmxnet3_softc_t *dp) * Allocate and initialize the command ring of a queue. * * Returns: - * DDI_SUCCESS or DDI_FAILURE. + * 0 on success, non-zero on error. */ static int vmxnet3_alloc_cmdring(vmxnet3_softc_t *dp, vmxnet3_cmdring_t *cmdRing) { size_t ringSize = cmdRing->size * sizeof (Vmxnet3_TxDesc); + int err; - if (vmxnet3_alloc_dma_mem_512(dp, &cmdRing->dma, ringSize, - B_TRUE) != DDI_SUCCESS) { - return (DDI_FAILURE); + if ((err = vmxnet3_alloc_dma_mem_512(dp, &cmdRing->dma, ringSize, + B_TRUE)) != 0) { + return (err); } (void) memset(cmdRing->dma.buf, 0, ringSize); cmdRing->avail = cmdRing->size; cmdRing->next2fill = 0; cmdRing->gen = VMXNET3_INIT_GEN; - return (DDI_SUCCESS); + return (0); } /* @@ -319,19 +328,20 @@ vmxnet3_alloc_compring(vmxnet3_softc_t *dp, vmxnet3_compring_t *compRing) * Initialize the tx queue of a vmxnet3 device. * * Returns: - * DDI_SUCCESS or DDI_FAILURE. + * 0 on success, non-zero on failure. */ static int vmxnet3_prepare_txqueue(vmxnet3_softc_t *dp) { Vmxnet3_TxQueueDesc *tqdesc = VMXNET3_TQDESC(dp); vmxnet3_txqueue_t *txq = &dp->txQueue; + int err; ASSERT(!(txq->cmdRing.size & VMXNET3_RING_SIZE_MASK)); ASSERT(!(txq->compRing.size & VMXNET3_RING_SIZE_MASK)); ASSERT(!txq->cmdRing.dma.buf && !txq->compRing.dma.buf); - if (vmxnet3_alloc_cmdring(dp, &txq->cmdRing) != DDI_SUCCESS) { + if ((err = vmxnet3_alloc_cmdring(dp, &txq->cmdRing)) != 0) { goto error; } tqdesc->conf.txRingBasePA = txq->cmdRing.dma.bufPA; @@ -339,7 +349,7 @@ vmxnet3_prepare_txqueue(vmxnet3_softc_t *dp) tqdesc->conf.dataRingBasePA = 0; tqdesc->conf.dataRingSize = 0; - if (vmxnet3_alloc_compring(dp, &txq->compRing) != DDI_SUCCESS) { + if ((err = vmxnet3_alloc_compring(dp, &txq->compRing)) != 0) { goto error_cmdring; } tqdesc->conf.compRingBasePA = txq->compRing.dma.bufPA; @@ -349,11 +359,11 @@ vmxnet3_prepare_txqueue(vmxnet3_softc_t *dp) sizeof (vmxnet3_metatx_t), KM_SLEEP); ASSERT(txq->metaRing); - if (vmxnet3_txqueue_init(dp, txq) != DDI_SUCCESS) { + if ((err = vmxnet3_txqueue_init(dp, txq)) != 0) { goto error_mpring; } - return (DDI_SUCCESS); + return (0); error_mpring: kmem_free(txq->metaRing, txq->cmdRing.size * sizeof (vmxnet3_metatx_t)); @@ -361,26 +371,27 @@ error_mpring: error_cmdring: vmxnet3_free_dma_mem(&txq->cmdRing.dma); error: - return (DDI_FAILURE); + return (err); } /* * Initialize the rx queue of a vmxnet3 device. * * Returns: - * DDI_SUCCESS or DDI_FAILURE. + * 0 on success, non-zero on failure. */ static int vmxnet3_prepare_rxqueue(vmxnet3_softc_t *dp) { Vmxnet3_RxQueueDesc *rqdesc = VMXNET3_RQDESC(dp); vmxnet3_rxqueue_t *rxq = &dp->rxQueue; + int err = 0; ASSERT(!(rxq->cmdRing.size & VMXNET3_RING_SIZE_MASK)); ASSERT(!(rxq->compRing.size & VMXNET3_RING_SIZE_MASK)); ASSERT(!rxq->cmdRing.dma.buf && !rxq->compRing.dma.buf); - if (vmxnet3_alloc_cmdring(dp, &rxq->cmdRing) != DDI_SUCCESS) { + if ((err = vmxnet3_alloc_cmdring(dp, &rxq->cmdRing)) != 0) { goto error; } rqdesc->conf.rxRingBasePA[0] = rxq->cmdRing.dma.bufPA; @@ -388,7 +399,7 @@ vmxnet3_prepare_rxqueue(vmxnet3_softc_t *dp) rqdesc->conf.rxRingBasePA[1] = 0; rqdesc->conf.rxRingSize[1] = 0; - if (vmxnet3_alloc_compring(dp, &rxq->compRing) != DDI_SUCCESS) { + if ((err = vmxnet3_alloc_compring(dp, &rxq->compRing)) != 0) { goto error_cmdring; } rqdesc->conf.compRingBasePA = rxq->compRing.dma.bufPA; @@ -398,11 +409,11 @@ vmxnet3_prepare_rxqueue(vmxnet3_softc_t *dp) sizeof (vmxnet3_bufdesc_t), KM_SLEEP); ASSERT(rxq->bufRing); - if (vmxnet3_rxqueue_init(dp, rxq) != DDI_SUCCESS) { + if ((err = vmxnet3_rxqueue_init(dp, rxq)) != 0) { goto error_bufring; } - return (DDI_SUCCESS); + return (0); error_bufring: kmem_free(rxq->bufRing, rxq->cmdRing.size * sizeof (vmxnet3_bufdesc_t)); @@ -410,7 +421,7 @@ error_bufring: error_cmdring: vmxnet3_free_dma_mem(&rxq->cmdRing.dma); error: - return (DDI_FAILURE); + return (err); } /* @@ -487,7 +498,7 @@ vmxnet3_refresh_linkstate(vmxnet3_softc_t *dp) * structures and send a start command to the device. * * Returns: - * DDI_SUCCESS or DDI_FAILURE. + * 0 on success, non-zero error on failure. */ static int vmxnet3_start(void *data) @@ -497,14 +508,16 @@ vmxnet3_start(void *data) Vmxnet3_RxQueueDesc *rqdesc; int txQueueSize, rxQueueSize; uint32_t ret32; + int err, dmaerr; VMXNET3_DEBUG(dp, 1, "start()\n"); /* * Allocate vmxnet3's shared data and advertise its PA */ - if (vmxnet3_prepare_drivershared(dp) != DDI_SUCCESS) { - VMXNET3_WARN(dp, "vmxnet3_prepare_drivershared() failed\n"); + if ((err = vmxnet3_prepare_drivershared(dp)) != 0) { + VMXNET3_WARN(dp, "vmxnet3_prepare_drivershared() failed: %d", + err); goto error; } tqdesc = VMXNET3_TQDESC(dp); @@ -519,12 +532,14 @@ vmxnet3_start(void *data) dp->txQueue.cmdRing.size = txQueueSize; dp->txQueue.compRing.size = txQueueSize; dp->txQueue.sharedCtrl = &tqdesc->ctrl; - if (vmxnet3_prepare_txqueue(dp) != DDI_SUCCESS) { - VMXNET3_WARN(dp, "vmxnet3_prepare_txqueue() failed\n"); + if ((err = vmxnet3_prepare_txqueue(dp)) != 0) { + VMXNET3_WARN(dp, "vmxnet3_prepare_txqueue() failed: %d", + err); goto error_shared_data; } } else { VMXNET3_WARN(dp, "invalid tx ring size (%d)\n", txQueueSize); + err = EINVAL; goto error_shared_data; } @@ -537,21 +552,24 @@ vmxnet3_start(void *data) dp->rxQueue.cmdRing.size = rxQueueSize; dp->rxQueue.compRing.size = rxQueueSize; dp->rxQueue.sharedCtrl = &rqdesc->ctrl; - if (vmxnet3_prepare_rxqueue(dp) != DDI_SUCCESS) { - VMXNET3_WARN(dp, "vmxnet3_prepare_rxqueue() failed\n"); + if ((err = vmxnet3_prepare_rxqueue(dp)) != 0) { + VMXNET3_WARN(dp, "vmxnet3_prepare_rxqueue() failed: %d", + err); goto error_tx_queue; } } else { VMXNET3_WARN(dp, "invalid rx ring size (%d)\n", rxQueueSize); + err = EINVAL; goto error_tx_queue; } /* * Allocate the Tx DMA handle */ - if (ddi_dma_alloc_handle(dp->dip, &vmxnet3_dma_attrs_tx, DDI_DMA_SLEEP, - NULL, &dp->txDmaHandle) != DDI_SUCCESS) { - VMXNET3_WARN(dp, "ddi_dma_alloc_handle() failed\n"); + if ((dmaerr = ddi_dma_alloc_handle(dp->dip, &vmxnet3_dma_attrs_tx, + DDI_DMA_SLEEP, NULL, &dp->txDmaHandle)) != DDI_SUCCESS) { + VMXNET3_WARN(dp, "ddi_dma_alloc_handle() failed: %d", dmaerr); + err = vmxnet3_dmaerr2errno(dmaerr); goto error_rx_queue; } @@ -562,6 +580,7 @@ vmxnet3_start(void *data) ret32 = VMXNET3_BAR1_GET32(dp, VMXNET3_REG_CMD); if (ret32) { VMXNET3_WARN(dp, "ACTIVATE_DEV failed: 0x%x\n", ret32); + err = ENXIO; goto error_txhandle; } dp->devEnabled = B_TRUE; @@ -586,7 +605,7 @@ vmxnet3_start(void *data) */ VMXNET3_BAR0_PUT32(dp, VMXNET3_REG_IMR, 0); - return (DDI_SUCCESS); + return (0); error_txhandle: ddi_dma_free_handle(&dp->txDmaHandle); @@ -597,7 +616,7 @@ error_tx_queue: error_shared_data: vmxnet3_destroy_drivershared(dp); error: - return (DDI_FAILURE); + return (err); } /* @@ -633,9 +652,6 @@ vmxnet3_stop(void *data) /* * Set or unset promiscuous mode on a vmxnet3 device. - * - * Returns: - * DDI_SUCCESS. */ static int vmxnet3_setpromisc(void *data, boolean_t promisc) @@ -652,21 +668,21 @@ vmxnet3_setpromisc(void *data, boolean_t promisc) vmxnet3_refresh_rxfilter(dp); - return (DDI_SUCCESS); + return (0); } /* * Add or remove a multicast address from/to a vmxnet3 device. * * Returns: - * DDI_SUCCESS or DDI_FAILURE. + * 0 on success, non-zero on failure. */ static int vmxnet3_multicst(void *data, boolean_t add, const uint8_t *macaddr) { vmxnet3_softc_t *dp = data; vmxnet3_dmabuf_t newMfTable; - int ret = DDI_SUCCESS; + int ret = 0; uint16_t macIdx; size_t allocSize; @@ -707,7 +723,7 @@ vmxnet3_multicst(void *data, boolean_t add, const uint8_t *macaddr) if (allocSize) { ret = vmxnet3_alloc_dma_mem_1(dp, &newMfTable, allocSize, B_TRUE); - ASSERT(ret == DDI_SUCCESS); + ASSERT(ret == 0); if (add) { (void) memcpy(newMfTable.buf, dp->mfTable.buf, dp->mfTable.bufLen); @@ -763,7 +779,7 @@ done: * Set the mac address of a vmxnet3 device. * * Returns: - * DDI_SUCCESS. + * 0 */ static int vmxnet3_unicst(void *data, const uint8_t *macaddr) @@ -781,7 +797,7 @@ vmxnet3_unicst(void *data, const uint8_t *macaddr) (void) memcpy(dp->macaddr, macaddr, 6); - return (DDI_SUCCESS); + return (0); } /* @@ -817,6 +833,7 @@ vmxnet3_change_mtu(vmxnet3_softc_t *dp, uint32_t new_mtu) } dp->cur_mtu = new_mtu; + dp->alloc_ok = VMXNET3_ALLOC_OK(dp); if ((ret = mac_maxsdu_update(dp->mac, new_mtu)) != 0) VMXNET3_WARN(dp, "Unable to update mac with %d mtu: %d", @@ -1055,7 +1072,7 @@ vmxnet3_reset(void *data) atomic_inc_32(&dp->reset_count); vmxnet3_stop(dp); VMXNET3_BAR1_PUT32(dp, VMXNET3_REG_CMD, VMXNET3_CMD_RESET_DEV); - if ((ret = vmxnet3_start(dp)) != DDI_SUCCESS) + if ((ret = vmxnet3_start(dp)) != 0) VMXNET3_WARN(dp, "failed to reset the device: %d", ret); } @@ -1178,6 +1195,8 @@ vmxnet3_kstat_update(kstat_t *ksp, int rw) statp->tx_pullup_needed.value.ul = dp->tx_pullup_needed; statp->tx_ring_full.value.ul = dp->tx_ring_full; statp->rx_alloc_buf.value.ul = dp->rx_alloc_buf; + statp->rx_pool_empty.value.ul = dp->rx_pool_empty; + statp->rx_num_bufs.value.ul = dp->rx_num_bufs; return (0); } @@ -1205,6 +1224,10 @@ vmxnet3_kstat_init(vmxnet3_softc_t *dp) KSTAT_DATA_ULONG); kstat_named_init(&statp->rx_alloc_buf, "rx_alloc_buf", KSTAT_DATA_ULONG); + kstat_named_init(&statp->rx_pool_empty, "rx_pool_empty", + KSTAT_DATA_ULONG); + kstat_named_init(&statp->rx_num_bufs, "rx_num_bufs", + KSTAT_DATA_ULONG); kstat_install(dp->devKstats); @@ -1241,6 +1264,7 @@ vmxnet3_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) dp->instance = ddi_get_instance(dip); dp->cur_mtu = ETHERMTU; dp->allow_jumbo = B_TRUE; + dp->alloc_ok = VMXNET3_ALLOC_OK(dp); VMXNET3_DEBUG(dp, 1, "attach()\n"); @@ -1488,10 +1512,10 @@ vmxnet3_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) return (DDI_FAILURE); } - while (dp->rxNumBufs) { + while (dp->rx_num_bufs > 0) { if (retries++ < 10) { VMXNET3_WARN(dp, "rx pending (%u), waiting 1 second\n", - dp->rxNumBufs); + dp->rx_num_bufs); delay(drv_usectohz(1000000)); } else { VMXNET3_WARN(dp, "giving up\n"); diff --git a/usr/src/uts/intel/io/vmxnet3s/vmxnet3_rx.c b/usr/src/uts/intel/io/vmxnet3s/vmxnet3_rx.c index 66c0b9231f..0099464824 100644 --- a/usr/src/uts/intel/io/vmxnet3s/vmxnet3_rx.c +++ b/usr/src/uts/intel/io/vmxnet3s/vmxnet3_rx.c @@ -13,7 +13,7 @@ * and limitations under the License. */ /* - * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright (c) 2013, 2016 by Delphix. All rights reserved. */ #include <vmxnet3.h> @@ -34,7 +34,6 @@ vmxnet3_alloc_rxbuf(vmxnet3_softc_t *dp, boolean_t canSleep) int flag = canSleep ? KM_SLEEP : KM_NOSLEEP; int err; - atomic_inc_32(&dp->rx_alloc_buf); rxBuf = kmem_zalloc(sizeof (vmxnet3_rxbuf_t), flag); if (!rxBuf) { atomic_inc_32(&dp->rx_alloc_failed); @@ -42,7 +41,7 @@ vmxnet3_alloc_rxbuf(vmxnet3_softc_t *dp, boolean_t canSleep) } if ((err = vmxnet3_alloc_dma_mem_1(dp, &rxBuf->dma, (dp->cur_mtu + 18), - canSleep)) != DDI_SUCCESS) { + canSleep)) != 0) { VMXNET3_DEBUG(dp, 0, "Failed to allocate %d bytes for rx buf, " "err:%d\n", (dp->cur_mtu + 18), err); kmem_free(rxBuf, sizeof (vmxnet3_rxbuf_t)); @@ -54,8 +53,8 @@ vmxnet3_alloc_rxbuf(vmxnet3_softc_t *dp, boolean_t canSleep) rxBuf->freeCB.free_arg = (caddr_t)rxBuf; rxBuf->dp = dp; - atomic_inc_32(&dp->rxNumBufs); - + atomic_inc_32(&dp->rx_num_bufs); + atomic_inc_32(&dp->rx_alloc_buf); return (rxBuf); } @@ -65,32 +64,36 @@ vmxnet3_free_rxbuf(vmxnet3_softc_t *dp, vmxnet3_rxbuf_t *rxBuf) vmxnet3_free_dma_mem(&rxBuf->dma); kmem_free(rxBuf, sizeof (vmxnet3_rxbuf_t)); -#ifndef DEBUG - atomic_dec_32(&dp->rxNumBufs); +#ifndef DEBUG + atomic_dec_32(&dp->rx_num_bufs); #else { - uint32_t nv = atomic_dec_32_nv(&dp->rxNumBufs); + uint32_t nv = atomic_dec_32_nv(&dp->rx_num_bufs); ASSERT(nv != (uint32_t)-1); } #endif } /* - * Return a rxBuf to the pool. + * Return a rxBuf to the pool. The init argument, when B_TRUE, indicates + * that we're being called for the purpose of pool initialization, and + * therefore, we should place the buffer in the pool even if the device + * isn't enabled. * * Returns: - * B_TRUE if there was room in the pool and the rxBuf was returned, - * B_FALSE otherwise. + * B_TRUE if the buffer was returned to the pool, or B_FALSE if it + * wasn't (e.g. if the device is stopped). */ static boolean_t -vmxnet3_put_rxpool_buf(vmxnet3_softc_t *dp, vmxnet3_rxbuf_t *rxBuf) +vmxnet3_put_rxpool_buf(vmxnet3_softc_t *dp, vmxnet3_rxbuf_t *rxBuf, + boolean_t init) { vmxnet3_rxpool_t *rxPool = &dp->rxPool; boolean_t returned = B_FALSE; mutex_enter(&dp->rxPoolLock); ASSERT(rxPool->nBufs <= rxPool->nBufsLimit); - if (dp->devEnabled && rxPool->nBufs < rxPool->nBufsLimit) { + if ((dp->devEnabled || init) && rxPool->nBufs < rxPool->nBufsLimit) { ASSERT((rxPool->listHead == NULL && rxPool->nBufs == 0) || (rxPool->listHead != NULL && rxPool->nBufs != 0)); rxBuf->next = rxPool->listHead; @@ -110,9 +113,7 @@ vmxnet3_put_rxbuf(vmxnet3_rxbuf_t *rxBuf) { vmxnet3_softc_t *dp = rxBuf->dp; - VMXNET3_DEBUG(dp, 5, "free 0x%p\n", rxBuf); - - if (!vmxnet3_put_rxpool_buf(dp, rxBuf)) + if (!vmxnet3_put_rxpool_buf(dp, rxBuf, B_FALSE)) vmxnet3_free_rxbuf(dp, rxBuf); } @@ -129,7 +130,7 @@ vmxnet3_get_rxpool_buf(vmxnet3_softc_t *dp) vmxnet3_rxbuf_t *rxBuf = NULL; mutex_enter(&dp->rxPoolLock); - if (rxPool->listHead) { + if (rxPool->listHead != NULL) { rxBuf = rxPool->listHead; rxPool->listHead = rxBuf->next; rxPool->nBufs--; @@ -141,50 +142,77 @@ vmxnet3_get_rxpool_buf(vmxnet3_softc_t *dp) } /* - * Get an unused rxBuf from either the pool or from memory. - * The returned rxBuf has a mblk associated with it. + * Fill a rxPool by allocating the maximum number of buffers. * * Returns: - * A rxBuf or NULL. + * 0 on success, non-zero on failure. */ -static vmxnet3_rxbuf_t * -vmxnet3_get_rxbuf(vmxnet3_softc_t *dp, boolean_t canSleep) +static int +vmxnet3_rxpool_init(vmxnet3_softc_t *dp) { + int err = 0; vmxnet3_rxbuf_t *rxBuf; - if ((rxBuf = vmxnet3_get_rxpool_buf(dp))) { - VMXNET3_DEBUG(dp, 5, "alloc 0x%p from pool\n", rxBuf); - } else if ((rxBuf = vmxnet3_alloc_rxbuf(dp, canSleep))) { - VMXNET3_DEBUG(dp, 5, "alloc 0x%p from mem\n", rxBuf); + ASSERT(dp->rxPool.nBufsLimit > 0); + while (dp->rxPool.nBufs < dp->rxPool.nBufsLimit) { + if ((rxBuf = vmxnet3_alloc_rxbuf(dp, B_FALSE)) == NULL) { + err = ENOMEM; + break; + } + VERIFY(vmxnet3_put_rxpool_buf(dp, rxBuf, B_TRUE)); } - if (rxBuf) { - rxBuf->mblk = desballoc((uchar_t *)rxBuf->dma.buf, - rxBuf->dma.bufLen, BPRI_MED, &rxBuf->freeCB); - if (!rxBuf->mblk) { - vmxnet3_put_rxbuf(rxBuf); - atomic_inc_32(&dp->rx_alloc_failed); - rxBuf = NULL; + if (err != 0) { + while ((rxBuf = vmxnet3_get_rxpool_buf(dp)) != NULL) { + vmxnet3_free_rxbuf(dp, rxBuf); } } - return (rxBuf); + return (err); } /* - * Populate a Rx descriptor with a new rxBuf. + * Populate a Rx descriptor with a new rxBuf. If the pool argument is B_TRUE, + * then try to take a buffer from rxPool. If the pool is empty and the + * dp->alloc_ok is true, then fall back to dynamic allocation. If pool is + * B_FALSE, then always allocate a new buffer (this is only used when + * populating the initial set of buffers in the receive queue during start). * * Returns: - * DDI_SUCCESS or DDI_FAILURE. + * 0 on success, non-zero on failure. */ static int vmxnet3_rx_populate(vmxnet3_softc_t *dp, vmxnet3_rxqueue_t *rxq, uint16_t idx, - boolean_t canSleep) + boolean_t canSleep, boolean_t pool) { - int ret = DDI_SUCCESS; - vmxnet3_rxbuf_t *rxBuf = vmxnet3_get_rxbuf(dp, canSleep); + vmxnet3_rxbuf_t *rxBuf = NULL; + + if (pool && (rxBuf = vmxnet3_get_rxpool_buf(dp)) == NULL) { + /* The maximum number of pool buffers have been allocated. */ + atomic_inc_32(&dp->rx_pool_empty); + if (!dp->alloc_ok) { + atomic_inc_32(&dp->rx_alloc_failed); + } + } + + if (rxBuf == NULL && (!pool || dp->alloc_ok)) { + rxBuf = vmxnet3_alloc_rxbuf(dp, canSleep); + } + + if (rxBuf != NULL) { + rxBuf->mblk = desballoc((uchar_t *)rxBuf->dma.buf, + rxBuf->dma.bufLen, BPRI_MED, &rxBuf->freeCB); + if (rxBuf->mblk == NULL) { + if (pool) { + VERIFY(vmxnet3_put_rxpool_buf(dp, rxBuf, + B_FALSE)); + } else { + vmxnet3_free_rxbuf(dp, rxBuf); + } + atomic_inc_32(&dp->rx_alloc_failed); + return (ENOMEM); + } - if (rxBuf) { vmxnet3_cmdring_t *cmdRing = &rxq->cmdRing; Vmxnet3_GenericDesc *rxDesc = VMXNET3_GET_DESC(cmdRing, idx); @@ -195,35 +223,45 @@ vmxnet3_rx_populate(vmxnet3_softc_t *dp, vmxnet3_rxqueue_t *rxq, uint16_t idx, membar_producer(); rxDesc->rxd.gen = cmdRing->gen; } else { - ret = DDI_FAILURE; + return (ENOMEM); } - return (ret); + return (0); } /* * Initialize a RxQueue by populating the whole Rx ring with rxBufs. * * Returns: - * DDI_SUCCESS or DDI_FAILURE. + * 0 on success, non-zero on failure. */ int vmxnet3_rxqueue_init(vmxnet3_softc_t *dp, vmxnet3_rxqueue_t *rxq) { vmxnet3_cmdring_t *cmdRing = &rxq->cmdRing; + int err; + + dp->rxPool.nBufsLimit = vmxnet3_getprop(dp, "RxBufPoolLimit", 0, + cmdRing->size * 10, cmdRing->size * 2); do { - if (vmxnet3_rx_populate(dp, rxq, cmdRing->next2fill, - B_TRUE) != DDI_SUCCESS) { + if ((err = vmxnet3_rx_populate(dp, rxq, cmdRing->next2fill, + B_TRUE, B_FALSE)) != 0) { goto error; } VMXNET3_INC_RING_IDX(cmdRing, cmdRing->next2fill); } while (cmdRing->next2fill); - dp->rxPool.nBufsLimit = vmxnet3_getprop(dp, "RxBufPoolLimit", 0, - cmdRing->size * 10, cmdRing->size * 2); + /* + * Pre-allocate rxPool buffers so that we never have to allocate + * new buffers from interrupt context when we need to replace a buffer + * in the rxqueue. + */ + if ((err = vmxnet3_rxpool_init(dp)) != 0) { + goto error; + } - return (DDI_SUCCESS); + return (0); error: while (cmdRing->next2fill) { @@ -231,7 +269,7 @@ error: vmxnet3_free_rxbuf(dp, rxq->bufRing[cmdRing->next2fill].rxBuf); } - return (DDI_FAILURE); + return (err); } /* @@ -348,8 +386,8 @@ vmxnet3_rx_intr(vmxnet3_softc_t *dp, vmxnet3_rxqueue_t *rxq) * descriptor. Grab it only if we achieve to replace * it with a fresh buffer. */ - if (vmxnet3_rx_populate(dp, rxq, rxdIdx, B_FALSE) == - DDI_SUCCESS) { + if (vmxnet3_rx_populate(dp, rxq, rxdIdx, B_FALSE, + B_TRUE) == 0) { /* Success, we can chain the mblk with the mp */ mblk->b_wptr = mblk->b_rptr + compDesc->rcd.len; *mpTail = mblk; diff --git a/usr/src/uts/intel/io/vmxnet3s/vmxnet3_tx.c b/usr/src/uts/intel/io/vmxnet3s/vmxnet3_tx.c index 8df683d892..4789bf55ef 100644 --- a/usr/src/uts/intel/io/vmxnet3s/vmxnet3_tx.c +++ b/usr/src/uts/intel/io/vmxnet3s/vmxnet3_tx.c @@ -14,7 +14,7 @@ */ /* - * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright (c) 2012, 2016 by Delphix. All rights reserved. */ #include <vmxnet3.h> @@ -39,7 +39,7 @@ typedef struct vmxnet3_offload_t { int vmxnet3_txqueue_init(vmxnet3_softc_t *dp, vmxnet3_txqueue_t *txq) { - return (DDI_SUCCESS); + return (0); } /* diff --git a/usr/src/uts/intel/io/vmxnet3s/vmxnet3_utils.c b/usr/src/uts/intel/io/vmxnet3s/vmxnet3_utils.c index 8152c8f3bb..bec73524f7 100644 --- a/usr/src/uts/intel/io/vmxnet3s/vmxnet3_utils.c +++ b/usr/src/uts/intel/io/vmxnet3s/vmxnet3_utils.c @@ -12,6 +12,9 @@ * See the License for the specific language governing permissions * and limitations under the License. */ +/* + * Copyright (c) 2016 by Delphix. All rights reserved. + */ #include <vmxnet3.h> @@ -70,11 +73,33 @@ static ddi_dma_attr_t vmxnet3_dma_attrs_512 = { .dma_attr_flags = 0 }; +int +vmxnet3_dmaerr2errno(int dmaerr) +{ + int err; + + switch (dmaerr) { + case DDI_DMA_NORESOURCES: + case DDI_DMA_TOOBIG: + err = ENOMEM; + break; + case DDI_DMA_INUSE: + err = EBUSY; + break; + case DDI_DMA_BADATTR: + case DDI_DMA_NOMAPPING: + default: + err = EINVAL; + } + + return (err); +} + /* * Allocate /size/ bytes of contiguous DMA-ble memory. * * Returns: - * DDI_SUCCESS or DDI_FAILURE. + * 0 on success, non-zero on failure. */ static int vmxnet3_alloc_dma_mem(vmxnet3_softc_t *dp, vmxnet3_dmabuf_t *dma, size_t size, @@ -82,6 +107,7 @@ vmxnet3_alloc_dma_mem(vmxnet3_softc_t *dp, vmxnet3_dmabuf_t *dma, size_t size, { ddi_dma_cookie_t cookie; uint_t cookieCount; + int dmaerr, err = 0; int (*cb) (caddr_t) = canSleep ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT; ASSERT(size != 0); @@ -89,9 +115,10 @@ vmxnet3_alloc_dma_mem(vmxnet3_softc_t *dp, vmxnet3_dmabuf_t *dma, size_t size, /* * Allocate a DMA handle */ - if (ddi_dma_alloc_handle(dp->dip, dma_attrs, cb, NULL, - &dma->dmaHandle) != DDI_SUCCESS) { - VMXNET3_WARN(dp, "ddi_dma_alloc_handle() failed\n"); + if ((dmaerr = ddi_dma_alloc_handle(dp->dip, dma_attrs, cb, NULL, + &dma->dmaHandle)) != DDI_SUCCESS) { + VMXNET3_WARN(dp, "ddi_dma_alloc_handle() failed: %d", dmaerr); + err = vmxnet3_dmaerr2errno(dmaerr); goto error; } @@ -101,24 +128,27 @@ vmxnet3_alloc_dma_mem(vmxnet3_softc_t *dp, vmxnet3_dmabuf_t *dma, size_t size, if (ddi_dma_mem_alloc(dma->dmaHandle, size, &vmxnet3_dev_attr, DDI_DMA_CONSISTENT, cb, NULL, &dma->buf, &dma->bufLen, &dma->dataHandle) != DDI_SUCCESS) { - VMXNET3_WARN(dp, "ddi_dma_mem_alloc() failed\n"); + VMXNET3_WARN(dp, "ddi_dma_mem_alloc() failed"); + err = ENOMEM; goto error_dma_handle; } /* * Map the memory */ - if (ddi_dma_addr_bind_handle(dma->dmaHandle, NULL, dma->buf, + if ((dmaerr = ddi_dma_addr_bind_handle(dma->dmaHandle, NULL, dma->buf, dma->bufLen, DDI_DMA_RDWR | DDI_DMA_STREAMING, cb, NULL, &cookie, - &cookieCount) != DDI_DMA_MAPPED) { - VMXNET3_WARN(dp, "ddi_dma_addr_bind_handle() failed\n"); + &cookieCount)) != DDI_DMA_MAPPED) { + VMXNET3_WARN(dp, "ddi_dma_addr_bind_handle() failed: %d", + dmaerr); + err = vmxnet3_dmaerr2errno(dmaerr); goto error_dma_mem; } ASSERT(cookieCount == 1); dma->bufPA = cookie.dmac_laddress; - return (DDI_SUCCESS); + return (0); error_dma_mem: ddi_dma_mem_free(&dma->dataHandle); @@ -128,7 +158,7 @@ error: dma->buf = NULL; dma->bufPA = NULL; dma->bufLen = 0; - return (DDI_FAILURE); + return (err); } int diff --git a/usr/src/uts/intel/io/vmxnet3s/vmxnet3s.conf b/usr/src/uts/intel/io/vmxnet3s/vmxnet3s.conf index 7f16946092..8345dc27f4 100644 --- a/usr/src/uts/intel/io/vmxnet3s/vmxnet3s.conf +++ b/usr/src/uts/intel/io/vmxnet3s/vmxnet3s.conf @@ -27,7 +27,7 @@ RxRingSize=4096,4096,4096,4096,4096,4096,4096,4096,4096,4096; # Minimum value: 0 # Maximum value: RxRingSize * 10 # -RxBufPoolLimit=4096,4096,4096,4096,4096,4096,4096,4096,4096,4096; +RxBufPoolLimit=16384,16384,16384,16384,16384,16384,16384,16384,16384,16384; # EnableLSO -- # |