/*
* Copyright (C) 2007-2014 VMware, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of the Common
* Development and Distribution License (the "License") version 1.0
* and no later version. You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the License at
*
* See the License for the specific language governing permissions
* and limitations under the License.
*/
/*
* Copyright (c) 2012, 2016 by Delphix. All rights reserved.
*/
#include <vmxnet3.h>
/*
* This driver is based on VMware's version 3227872, and contains additional
* enhancements (see README.txt).
*/
/*
* 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.
*/
/*
* TODO:
* - Tx data ring
* - MAC_CAPAB_POLL support
* - Dynamic RX pool
*/
static int vmxnet3_start(void *);
static void vmxnet3_stop(void *);
static int vmxnet3_setpromisc(void *, boolean_t);
static int vmxnet3_unicst(void *, const uint8_t *);
void *);
const void *);
static void vmxnet3_prop_info(void *, const char *, mac_prop_id_t,
int vmxnet3s_debug = 0;
/* MAC callbacks */
.mc_stop = vmxnet3_stop,
.mc_tx = vmxnet3_tx,
};
/* Tx DMA engine description */
.dma_attr_addr_lo = 0x0000000000000000ull,
.dma_attr_addr_hi = 0xFFFFFFFFFFFFFFFFull,
.dma_attr_count_max = 0xFFFFFFFFFFFFFFFFull,
.dma_attr_align = 0x0000000000000001ull,
.dma_attr_burstsizes = 0x0000000000000001ull,
.dma_attr_minxfer = 0x00000001,
.dma_attr_maxxfer = 0x000000000000FFFFull,
.dma_attr_seg = 0xFFFFFFFFFFFFFFFFull,
.dma_attr_sgllen = -1,
.dma_attr_granular = 0x00000001,
.dma_attr_flags = 0
};
/* --- */
/*
* Fetch the statistics of a vmxnet3 device.
*
* Returns:
* 0 on success, non-zero on failure.
*/
static int
{
if (!dp->devEnabled) {
return (EBUSY);
}
/*
* First touch the related register
*/
switch (stat) {
case MAC_STAT_MULTIRCV:
case MAC_STAT_BRDCSTRCV:
case MAC_STAT_MULTIXMT:
case MAC_STAT_BRDCSTXMT:
case MAC_STAT_NORCVBUF:
case MAC_STAT_IERRORS:
case MAC_STAT_NOXMTBUF:
case MAC_STAT_OERRORS:
case MAC_STAT_RBYTES:
case MAC_STAT_IPACKETS:
case MAC_STAT_OBYTES:
case MAC_STAT_OPACKETS:
break;
case MAC_STAT_IFSPEED:
case MAC_STAT_COLLISIONS:
case ETHER_STAT_LINK_DUPLEX:
/* nothing */
break;
default:
return (ENOTSUP);
}
/*
* Then fetch the corresponding stat
*/
switch (stat) {
case MAC_STAT_IFSPEED:
break;
case MAC_STAT_MULTIRCV:
break;
case MAC_STAT_BRDCSTRCV:
break;
case MAC_STAT_MULTIXMT:
break;
case MAC_STAT_BRDCSTXMT:
break;
case MAC_STAT_NORCVBUF:
break;
case MAC_STAT_IERRORS:
break;
case MAC_STAT_NOXMTBUF:
break;
case MAC_STAT_OERRORS:
break;
case MAC_STAT_COLLISIONS:
*val = 0;
break;
case MAC_STAT_RBYTES:
break;
case MAC_STAT_IPACKETS:
break;
case MAC_STAT_OBYTES:
break;
case MAC_STAT_OPACKETS:
break;
case ETHER_STAT_LINK_DUPLEX:
*val = LINK_DUPLEX_FULL;
break;
default:
}
return (0);
}
/*
* Allocate and initialize the shared data structures of a vmxnet3 device.
*
* Returns:
* 0 on sucess, non-zero on failure.
*/
static int
{
int err;
B_TRUE)) != 0) {
return (err);
}
B_TRUE)) != 0) {
return (err);
}
/* Take care of most of devRead */
#ifdef _LP64
#else
#endif
/* XXX: ds->devRead.misc.maxNumRxSG */
/* TxQueue and RxQueue information is filled in other functions */
/* XXX: ds->intr.modLevels */
return (0);
}
/*
* Destroy the shared data structures of a vmxnet3 device.
*/
static void
{
}
/*
* Allocate and initialize the command ring of a queue.
*
* Returns:
* 0 on success, non-zero on error.
*/
static int
{
int err;
B_TRUE)) != 0) {
return (err);
}
return (0);
}
/*
* Allocate and initialize the completion ring of a queue.
*
* Returns:
* DDI_SUCCESS or DDI_FAILURE.
*/
static int
{
B_TRUE) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* Initialize the tx queue of a vmxnet3 device.
*
* Returns:
* 0 on success, non-zero on failure.
*/
static int
{
int err;
goto error;
}
goto error_cmdring;
}
sizeof (vmxnet3_metatx_t), KM_SLEEP);
goto error_mpring;
}
return (0);
return (err);
}
/*
* Initialize the rx queue of a vmxnet3 device.
*
* Returns:
* 0 on success, non-zero on failure.
*/
static int
{
int err = 0;
goto error;
}
goto error_cmdring;
}
sizeof (vmxnet3_bufdesc_t), KM_SLEEP);
goto error_bufring;
}
return (0);
return (err);
}
/*
* Destroy the tx queue of a vmxnet3 device.
*/
static void
{
}
/*
* Destroy the rx queue of a vmxnet3 device.
*/
static void
{
}
/*
* Apply new RX filters settings to a vmxnet3 device.
*/
static void
{
}
/*
* Fetch the link state of a vmxnet3 device.
*/
static void
{
if (ret32 & 1) {
} else {
}
}
/*
* Start a vmxnet3 device: allocate and initialize the shared data
* structures and send a start command to the device.
*
* Returns:
* 0 on success, non-zero error on failure.
*/
static int
{
/*
* Allocate vmxnet3's shared data and advertise its PA
*/
err);
goto error;
}
/*
* Create and initialize the tx queue
*/
if (!(txQueueSize & VMXNET3_RING_SIZE_MASK)) {
err);
goto error_shared_data;
}
} else {
goto error_shared_data;
}
/*
* Create and initialize the rx queue
*/
if (!(rxQueueSize & VMXNET3_RING_SIZE_MASK)) {
err);
goto error_tx_queue;
}
} else {
goto error_tx_queue;
}
/*
* Allocate the Tx DMA handle
*/
goto error_rx_queue;
}
/*
* Activate the device
*/
if (ret32) {
goto error_txhandle;
}
/*
* Update the RX filters, must be done after ACTIVATE_DEV
*/
/*
* Get the link state now because no events will be generated
*/
/*
* Finally, unmask the interrupt
*/
return (0);
return (err);
}
/*
* Stop a vmxnet3 device: send a stop command to the device and
* de-allocate the shared data structures.
*/
static void
{
/*
* Take the 2 locks related to asynchronous events.
* These events should always check dp->devEnabled before poking dp.
*/
}
/*
* Set or unset promiscuous mode on a vmxnet3 device.
*/
static int
{
if (promisc) {
} else {
}
return (0);
}
/*
*
* Returns:
* 0 on success, non-zero on failure.
*/
static int
{
int ret = 0;
/*
* First lookup the position of the given MAC to check if it is
* present in the existing MF table.
*/
break;
}
}
/*
* Check for 2 situations we can handle gracefully by bailing out:
* Adding an already existing filter or removing a non-existing one.
*/
goto done;
}
goto done;
}
/*
* Create the new MF table
*/
if (allocSize) {
B_TRUE);
if (add) {
macaddr, 6);
} else {
macIdx);
}
} else {
newMfTable.bufPA = 0;
newMfTable.bufLen = 0;
}
/*
* Now handle 2 corner cases: if we're creating the first filter or
* removing the last one, we have to update rxMode accordingly.
*/
}
}
/*
* Now replace the old MF table with the new one
*/
}
done:
/* Always update the filters */
return (ret);
}
/*
* Set the mac address of a vmxnet3 device.
*
* Returns:
* 0
*/
static int
{
return (0);
}
/*
* Change the MTU as seen by the driver. This is only supported when
* the mac is stopped.
*
* Returns:
* EBUSY if the device is enabled.
* EINVAL for invalid MTU values.
* 0 on success.
*/
static int
{
int ret;
if (dp->devEnabled)
return (EBUSY);
return (0);
}
return (EINVAL);
"accept-jumbo is not enabled.\n", ETHERMTU);
return (EINVAL);
}
return (ret);
}
/* ARGSUSED */
static int
{
int ret = 0;
switch (prop_id) {
case MAC_PROP_MTU:
break;
default:
prop_id);
}
return (ret);
}
/* ARGSUSED */
static int
{
int ret;
switch (prop_id) {
case MAC_PROP_MTU: {
break;
}
default:
prop_id);
}
return (ret);
}
/* ARGSUSED */
static void
{
switch (prop_id) {
case MAC_PROP_MTU:
break;
default:
prop_id);
}
}
/*
* accept-jumbo ndd parameted for the interface.
*
* Side effects:
* MTU can be changed and device can be reset. An ACK or NACK is conveyed
* to the calling function from the mblk which was used to call this
* function.
*/
static void
{
int data;
case ND_SET:
/*
* The mblk in continuation would contain the ndd parameter name
* and data value to be set
*/
if (!mp1) {
break;
}
/* Force null termination */
/*
* "logic throughout nd_xxx assumes single data block for ioctl.
* However, existing code sends in some big buffers."
*/
}
/*
* Go past the end of this null terminated string to get the
* data value.
*/
valp++;
/*
* We are already beyond the readable area of mblk and
* still haven't found the end of param string.
*/
"No data value found to be set to param\n");
data = -1;
} else {
/* Now this points to data string */
valp++;
/* Get numeric value of first letter */
}
if (data == 1) {
"Accepting jumbo frames\n");
} else if (data == 0) {
"Rejecting jumbo frames\n");
} else {
" use 0 or 1\n");
ret = -1;
}
}
break;
default:
}
ret = -1;
break;
}
if (ret == 0)
else
}
/*
* Get the capabilities of a vmxnet3 device.
*
* Returns:
* B_TRUE if the capability is supported, B_FALSE otherwise.
*/
static boolean_t
{
switch (capab) {
case MAC_CAPAB_HCKSUM: {
break;
}
case MAC_CAPAB_LSO: {
break;
}
default:
}
return (ret);
}
/*
* Reset a vmxnet3 device. Only to be used when the device is wedged.
*
* Side effects:
* The device is reset.
*/
static void
{
int ret;
}
/*
* Process pending events on a vmxnet3 device.
*
* Returns:
* B_TRUE if the link state changed, B_FALSE otherwise.
*/
static boolean_t
{
if (events) {
}
}
} else {
"ddi_taskq_dispatch() failed\n");
}
}
if (events & VMXNET3_ECR_LINK) {
}
if (events & VMXNET3_ECR_DIC) {
}
}
return (linkStateChanged);
}
/*
* Interrupt handler of a vmxnet3 device.
*
* Returns:
* DDI_INTR_CLAIMED or DDI_INTR_UNCLAIMED.
*/
/* ARGSUSED1 */
static uint_t
{
if (dp->devEnabled) {
goto intr_unclaimed;
}
}
if (linkStateChanged) {
}
if (mustUpdateTx) {
}
if (mps) {
}
return (DDI_INTR_CLAIMED);
}
return (DDI_INTR_UNCLAIMED);
}
static int
{
if (rw == KSTAT_WRITE)
return (EACCES);
return (0);
}
static int
{
sizeof (vmxnet3_kstats_t) / sizeof (kstat_named_t), 0);
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* Probe and attach a vmxnet3 instance to the stack.
*
* Returns:
* DDI_SUCCESS or DDI_FAILURE.
*/
static int
{
if (cmd != DDI_ATTACH) {
goto error;
}
/*
* Allocate the soft state
*/
/*
* Get access to the PCI bus configuration space
*/
goto error_soft_state;
}
/*
* Make sure the chip is a vmxnet3 device
*/
if (vendorId != PCI_VENDOR_ID_VMWARE ||
goto error_pci_config;
}
/*
* Make sure we can access the registers through the I/O space
*/
/*
* Map the I/O space in memory
*/
goto error_pci_config;
}
goto error_regs_map_0;
}
/*
* Check the version number of the virtual device
*/
} else {
goto error_regs_map_1;
}
} else {
goto error_regs_map_1;
}
goto error_regs_map_1;
}
/*
* Read the MAC address from the device
*/
/*
* Register with the MAC framework
*/
goto error_kstat;
}
macr->m_instance = 0;
macr->m_pdata_size = 0;
if (ret != DDI_SUCCESS) {
goto error_kstat;
}
/*
* Register the interrupt(s) in this order of preference:
* MSI-X, MSI, INTx
*/
switch (ret32 & 0x3) {
case VMXNET3_IT_AUTO:
case VMXNET3_IT_MSIX:
if (err == DDI_SUCCESS)
break;
err);
/* FALLTHROUGH */
case VMXNET3_IT_MSI:
break;
/* FALLTHROUGH */
case VMXNET3_IT_INTX:
break;
}
/* FALLTHROUGH */
default:
goto error_mac;
}
goto error_intr;
}
goto error_intr;
}
/*
* Create a task queue to reset the device if it wedges.
*/
TASKQ_DEFAULTPRI, 0);
goto error_intr;
}
/*
* Initialize our mutexes now that we know the interrupt priority
* This _must_ be done before ddi_intr_enable()
*/
goto error_mutexes;
}
if (err != DDI_SUCCESS) {
goto error_intr_handler;
}
if (err != DDI_SUCCESS) {
"err:%d\n", err);
goto error_intr_handler;
}
} else {
if ((err != DDI_SUCCESS)) {
err);
goto error_intr_handler;
}
}
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
/*
* Detach a vmxnet3 instance from the stack.
*
* Returns:
* DDI_SUCCESS or DDI_FAILURE.
*/
static int
{
unsigned int retries = 0;
int ret;
if (cmd != DDI_DETACH) {
return (DDI_FAILURE);
}
while (dp->rx_num_bufs > 0) {
if (retries++ < 10) {
dp->rx_num_bufs);
} else {
return (DDI_FAILURE);
}
}
} else {
}
if (ret != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
/*
* Structures used by the module loader
*/
NULL,
NULL,
&mod_driverops, /* drv_modops */
VMXNET3_IDENT, /* drv_linkinfo */
&vmxnet3_dev_ops /* drv_dev_ops */
};
MODREV_1, /* ml_rev */
};
/* Module load entry point */
int
_init(void)
{
int ret;
if (ret != DDI_SUCCESS) {
}
return (ret);
}
/* Module unload entry point */
int
_fini(void)
{
int ret;
if (ret == DDI_SUCCESS) {
}
return (ret);
}
/* Module info entry point */
int
{
}
void
{
}