summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorSebastien Roy <seb@delphix.com>2016-11-17 16:00:18 -0800
committerMatthew Ahrens <mahrens@delphix.com>2016-11-17 16:22:11 -0800
commit6849994e8263545ed3c0f6f5676e47b38e14f63e (patch)
tree92bc38f81a639fcdbdbaa7ce40126f7f26d46c37 /usr/src
parentca5345b6a28e9e9bfd0c135121d62c6b35a5390d (diff)
downloadillumos-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.h9
-rw-r--r--usr/src/uts/intel/io/vmxnet3s/vmxnet3_main.c132
-rw-r--r--usr/src/uts/intel/io/vmxnet3s/vmxnet3_rx.c140
-rw-r--r--usr/src/uts/intel/io/vmxnet3s/vmxnet3_tx.c4
-rw-r--r--usr/src/uts/intel/io/vmxnet3s/vmxnet3_utils.c50
-rw-r--r--usr/src/uts/intel/io/vmxnet3s/vmxnet3s.conf2
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 --
#