162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Linux driver for VMware's vmxnet3 ethernet NIC. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2008-2022, VMware, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 762306a36Sopenharmony_ci * under the terms of the GNU General Public License as published by the 862306a36Sopenharmony_ci * Free Software Foundation; version 2 of the License and no later version. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 1162306a36Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 1262306a36Sopenharmony_ci * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 1362306a36Sopenharmony_ci * NON INFRINGEMENT. See the GNU General Public License for more 1462306a36Sopenharmony_ci * details. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License 1762306a36Sopenharmony_ci * along with this program; if not, write to the Free Software 1862306a36Sopenharmony_ci * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * The full GNU General Public License is included in this distribution in 2162306a36Sopenharmony_ci * the file called "COPYING". 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Maintained by: pv-drivers@vmware.com 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <linux/module.h> 2862306a36Sopenharmony_ci#include <net/ip6_checksum.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "vmxnet3_int.h" 3162306a36Sopenharmony_ci#include "vmxnet3_xdp.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cichar vmxnet3_driver_name[] = "vmxnet3"; 3462306a36Sopenharmony_ci#define VMXNET3_DRIVER_DESC "VMware vmxnet3 virtual NIC driver" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* 3762306a36Sopenharmony_ci * PCI Device ID Table 3862306a36Sopenharmony_ci * Last entry must be all 0s 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_cistatic const struct pci_device_id vmxnet3_pciid_table[] = { 4162306a36Sopenharmony_ci {PCI_VDEVICE(VMWARE, PCI_DEVICE_ID_VMWARE_VMXNET3)}, 4262306a36Sopenharmony_ci {0} 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, vmxnet3_pciid_table); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int enable_mq = 1; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void 5062306a36Sopenharmony_civmxnet3_write_mac_addr(struct vmxnet3_adapter *adapter, const u8 *mac); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* 5362306a36Sopenharmony_ci * Enable/Disable the given intr 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_cistatic void 5662306a36Sopenharmony_civmxnet3_enable_intr(struct vmxnet3_adapter *adapter, unsigned intr_idx) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci VMXNET3_WRITE_BAR0_REG(adapter, VMXNET3_REG_IMR + intr_idx * 8, 0); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic void 6362306a36Sopenharmony_civmxnet3_disable_intr(struct vmxnet3_adapter *adapter, unsigned intr_idx) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci VMXNET3_WRITE_BAR0_REG(adapter, VMXNET3_REG_IMR + intr_idx * 8, 1); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * Enable/Disable all intrs used by the device 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_cistatic void 7362306a36Sopenharmony_civmxnet3_enable_all_intrs(struct vmxnet3_adapter *adapter) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci int i; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci for (i = 0; i < adapter->intr.num_intrs; i++) 7862306a36Sopenharmony_ci vmxnet3_enable_intr(adapter, i); 7962306a36Sopenharmony_ci if (!VMXNET3_VERSION_GE_6(adapter) || 8062306a36Sopenharmony_ci !adapter->queuesExtEnabled) { 8162306a36Sopenharmony_ci adapter->shared->devRead.intrConf.intrCtrl &= 8262306a36Sopenharmony_ci cpu_to_le32(~VMXNET3_IC_DISABLE_ALL); 8362306a36Sopenharmony_ci } else { 8462306a36Sopenharmony_ci adapter->shared->devReadExt.intrConfExt.intrCtrl &= 8562306a36Sopenharmony_ci cpu_to_le32(~VMXNET3_IC_DISABLE_ALL); 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic void 9162306a36Sopenharmony_civmxnet3_disable_all_intrs(struct vmxnet3_adapter *adapter) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci int i; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (!VMXNET3_VERSION_GE_6(adapter) || 9662306a36Sopenharmony_ci !adapter->queuesExtEnabled) { 9762306a36Sopenharmony_ci adapter->shared->devRead.intrConf.intrCtrl |= 9862306a36Sopenharmony_ci cpu_to_le32(VMXNET3_IC_DISABLE_ALL); 9962306a36Sopenharmony_ci } else { 10062306a36Sopenharmony_ci adapter->shared->devReadExt.intrConfExt.intrCtrl |= 10162306a36Sopenharmony_ci cpu_to_le32(VMXNET3_IC_DISABLE_ALL); 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci for (i = 0; i < adapter->intr.num_intrs; i++) 10462306a36Sopenharmony_ci vmxnet3_disable_intr(adapter, i); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic void 10962306a36Sopenharmony_civmxnet3_ack_events(struct vmxnet3_adapter *adapter, u32 events) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_ECR, events); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic bool 11662306a36Sopenharmony_civmxnet3_tq_stopped(struct vmxnet3_tx_queue *tq, struct vmxnet3_adapter *adapter) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci return tq->stopped; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void 12362306a36Sopenharmony_civmxnet3_tq_start(struct vmxnet3_tx_queue *tq, struct vmxnet3_adapter *adapter) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci tq->stopped = false; 12662306a36Sopenharmony_ci netif_start_subqueue(adapter->netdev, tq - adapter->tx_queue); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void 13162306a36Sopenharmony_civmxnet3_tq_wake(struct vmxnet3_tx_queue *tq, struct vmxnet3_adapter *adapter) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci tq->stopped = false; 13462306a36Sopenharmony_ci netif_wake_subqueue(adapter->netdev, (tq - adapter->tx_queue)); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic void 13962306a36Sopenharmony_civmxnet3_tq_stop(struct vmxnet3_tx_queue *tq, struct vmxnet3_adapter *adapter) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci tq->stopped = true; 14262306a36Sopenharmony_ci tq->num_stop++; 14362306a36Sopenharmony_ci netif_stop_subqueue(adapter->netdev, (tq - adapter->tx_queue)); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci/* Check if capability is supported by UPT device or 14762306a36Sopenharmony_ci * UPT is even requested 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_cibool 15062306a36Sopenharmony_civmxnet3_check_ptcapability(u32 cap_supported, u32 cap) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci if (cap_supported & (1UL << VMXNET3_DCR_ERROR) || 15362306a36Sopenharmony_ci cap_supported & (1UL << cap)) { 15462306a36Sopenharmony_ci return true; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci return false; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/* 16262306a36Sopenharmony_ci * Check the link state. This may start or stop the tx queue. 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_cistatic void 16562306a36Sopenharmony_civmxnet3_check_link(struct vmxnet3_adapter *adapter, bool affectTxQueue) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci u32 ret; 16862306a36Sopenharmony_ci int i; 16962306a36Sopenharmony_ci unsigned long flags; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 17262306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_LINK); 17362306a36Sopenharmony_ci ret = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); 17462306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci adapter->link_speed = ret >> 16; 17762306a36Sopenharmony_ci if (ret & 1) { /* Link is up. */ 17862306a36Sopenharmony_ci netdev_info(adapter->netdev, "NIC Link is Up %d Mbps\n", 17962306a36Sopenharmony_ci adapter->link_speed); 18062306a36Sopenharmony_ci netif_carrier_on(adapter->netdev); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (affectTxQueue) { 18362306a36Sopenharmony_ci for (i = 0; i < adapter->num_tx_queues; i++) 18462306a36Sopenharmony_ci vmxnet3_tq_start(&adapter->tx_queue[i], 18562306a36Sopenharmony_ci adapter); 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci } else { 18862306a36Sopenharmony_ci netdev_info(adapter->netdev, "NIC Link is Down\n"); 18962306a36Sopenharmony_ci netif_carrier_off(adapter->netdev); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (affectTxQueue) { 19262306a36Sopenharmony_ci for (i = 0; i < adapter->num_tx_queues; i++) 19362306a36Sopenharmony_ci vmxnet3_tq_stop(&adapter->tx_queue[i], adapter); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic void 19962306a36Sopenharmony_civmxnet3_process_events(struct vmxnet3_adapter *adapter) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci int i; 20262306a36Sopenharmony_ci unsigned long flags; 20362306a36Sopenharmony_ci u32 events = le32_to_cpu(adapter->shared->ecr); 20462306a36Sopenharmony_ci if (!events) 20562306a36Sopenharmony_ci return; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci vmxnet3_ack_events(adapter, events); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* Check if link state has changed */ 21062306a36Sopenharmony_ci if (events & VMXNET3_ECR_LINK) 21162306a36Sopenharmony_ci vmxnet3_check_link(adapter, true); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* Check if there is an error on xmit/recv queues */ 21462306a36Sopenharmony_ci if (events & (VMXNET3_ECR_TQERR | VMXNET3_ECR_RQERR)) { 21562306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 21662306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 21762306a36Sopenharmony_ci VMXNET3_CMD_GET_QUEUE_STATUS); 21862306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci for (i = 0; i < adapter->num_tx_queues; i++) 22162306a36Sopenharmony_ci if (adapter->tqd_start[i].status.stopped) 22262306a36Sopenharmony_ci dev_err(&adapter->netdev->dev, 22362306a36Sopenharmony_ci "%s: tq[%d] error 0x%x\n", 22462306a36Sopenharmony_ci adapter->netdev->name, i, le32_to_cpu( 22562306a36Sopenharmony_ci adapter->tqd_start[i].status.error)); 22662306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) 22762306a36Sopenharmony_ci if (adapter->rqd_start[i].status.stopped) 22862306a36Sopenharmony_ci dev_err(&adapter->netdev->dev, 22962306a36Sopenharmony_ci "%s: rq[%d] error 0x%x\n", 23062306a36Sopenharmony_ci adapter->netdev->name, i, 23162306a36Sopenharmony_ci adapter->rqd_start[i].status.error); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci schedule_work(&adapter->work); 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci#ifdef __BIG_ENDIAN_BITFIELD 23862306a36Sopenharmony_ci/* 23962306a36Sopenharmony_ci * The device expects the bitfields in shared structures to be written in 24062306a36Sopenharmony_ci * little endian. When CPU is big endian, the following routines are used to 24162306a36Sopenharmony_ci * correctly read and write into ABI. 24262306a36Sopenharmony_ci * The general technique used here is : double word bitfields are defined in 24362306a36Sopenharmony_ci * opposite order for big endian architecture. Then before reading them in 24462306a36Sopenharmony_ci * driver the complete double word is translated using le32_to_cpu. Similarly 24562306a36Sopenharmony_ci * After the driver writes into bitfields, cpu_to_le32 is used to translate the 24662306a36Sopenharmony_ci * double words into required format. 24762306a36Sopenharmony_ci * In order to avoid touching bits in shared structure more than once, temporary 24862306a36Sopenharmony_ci * descriptors are used. These are passed as srcDesc to following functions. 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_cistatic void vmxnet3_RxDescToCPU(const struct Vmxnet3_RxDesc *srcDesc, 25162306a36Sopenharmony_ci struct Vmxnet3_RxDesc *dstDesc) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci u32 *src = (u32 *)srcDesc + 2; 25462306a36Sopenharmony_ci u32 *dst = (u32 *)dstDesc + 2; 25562306a36Sopenharmony_ci dstDesc->addr = le64_to_cpu(srcDesc->addr); 25662306a36Sopenharmony_ci *dst = le32_to_cpu(*src); 25762306a36Sopenharmony_ci dstDesc->ext1 = le32_to_cpu(srcDesc->ext1); 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic void vmxnet3_TxDescToLe(const struct Vmxnet3_TxDesc *srcDesc, 26162306a36Sopenharmony_ci struct Vmxnet3_TxDesc *dstDesc) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci int i; 26462306a36Sopenharmony_ci u32 *src = (u32 *)(srcDesc + 1); 26562306a36Sopenharmony_ci u32 *dst = (u32 *)(dstDesc + 1); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* Working backwards so that the gen bit is set at the end. */ 26862306a36Sopenharmony_ci for (i = 2; i > 0; i--) { 26962306a36Sopenharmony_ci src--; 27062306a36Sopenharmony_ci dst--; 27162306a36Sopenharmony_ci *dst = cpu_to_le32(*src); 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic void vmxnet3_RxCompToCPU(const struct Vmxnet3_RxCompDesc *srcDesc, 27762306a36Sopenharmony_ci struct Vmxnet3_RxCompDesc *dstDesc) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci int i = 0; 28062306a36Sopenharmony_ci u32 *src = (u32 *)srcDesc; 28162306a36Sopenharmony_ci u32 *dst = (u32 *)dstDesc; 28262306a36Sopenharmony_ci for (i = 0; i < sizeof(struct Vmxnet3_RxCompDesc) / sizeof(u32); i++) { 28362306a36Sopenharmony_ci *dst = le32_to_cpu(*src); 28462306a36Sopenharmony_ci src++; 28562306a36Sopenharmony_ci dst++; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci/* Used to read bitfield values from double words. */ 29162306a36Sopenharmony_cistatic u32 get_bitfield32(const __le32 *bitfield, u32 pos, u32 size) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci u32 temp = le32_to_cpu(*bitfield); 29462306a36Sopenharmony_ci u32 mask = ((1 << size) - 1) << pos; 29562306a36Sopenharmony_ci temp &= mask; 29662306a36Sopenharmony_ci temp >>= pos; 29762306a36Sopenharmony_ci return temp; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci#endif /* __BIG_ENDIAN_BITFIELD */ 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci#ifdef __BIG_ENDIAN_BITFIELD 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci# define VMXNET3_TXDESC_GET_GEN(txdesc) get_bitfield32(((const __le32 *) \ 30762306a36Sopenharmony_ci txdesc) + VMXNET3_TXD_GEN_DWORD_SHIFT, \ 30862306a36Sopenharmony_ci VMXNET3_TXD_GEN_SHIFT, VMXNET3_TXD_GEN_SIZE) 30962306a36Sopenharmony_ci# define VMXNET3_TXDESC_GET_EOP(txdesc) get_bitfield32(((const __le32 *) \ 31062306a36Sopenharmony_ci txdesc) + VMXNET3_TXD_EOP_DWORD_SHIFT, \ 31162306a36Sopenharmony_ci VMXNET3_TXD_EOP_SHIFT, VMXNET3_TXD_EOP_SIZE) 31262306a36Sopenharmony_ci# define VMXNET3_TCD_GET_GEN(tcd) get_bitfield32(((const __le32 *)tcd) + \ 31362306a36Sopenharmony_ci VMXNET3_TCD_GEN_DWORD_SHIFT, VMXNET3_TCD_GEN_SHIFT, \ 31462306a36Sopenharmony_ci VMXNET3_TCD_GEN_SIZE) 31562306a36Sopenharmony_ci# define VMXNET3_TCD_GET_TXIDX(tcd) get_bitfield32((const __le32 *)tcd, \ 31662306a36Sopenharmony_ci VMXNET3_TCD_TXIDX_SHIFT, VMXNET3_TCD_TXIDX_SIZE) 31762306a36Sopenharmony_ci# define vmxnet3_getRxComp(dstrcd, rcd, tmp) do { \ 31862306a36Sopenharmony_ci (dstrcd) = (tmp); \ 31962306a36Sopenharmony_ci vmxnet3_RxCompToCPU((rcd), (tmp)); \ 32062306a36Sopenharmony_ci } while (0) 32162306a36Sopenharmony_ci# define vmxnet3_getRxDesc(dstrxd, rxd, tmp) do { \ 32262306a36Sopenharmony_ci (dstrxd) = (tmp); \ 32362306a36Sopenharmony_ci vmxnet3_RxDescToCPU((rxd), (tmp)); \ 32462306a36Sopenharmony_ci } while (0) 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci#else 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci# define VMXNET3_TXDESC_GET_GEN(txdesc) ((txdesc)->gen) 32962306a36Sopenharmony_ci# define VMXNET3_TXDESC_GET_EOP(txdesc) ((txdesc)->eop) 33062306a36Sopenharmony_ci# define VMXNET3_TCD_GET_GEN(tcd) ((tcd)->gen) 33162306a36Sopenharmony_ci# define VMXNET3_TCD_GET_TXIDX(tcd) ((tcd)->txdIdx) 33262306a36Sopenharmony_ci# define vmxnet3_getRxComp(dstrcd, rcd, tmp) (dstrcd) = (rcd) 33362306a36Sopenharmony_ci# define vmxnet3_getRxDesc(dstrxd, rxd, tmp) (dstrxd) = (rxd) 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci#endif /* __BIG_ENDIAN_BITFIELD */ 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic void 33962306a36Sopenharmony_civmxnet3_unmap_tx_buf(struct vmxnet3_tx_buf_info *tbi, 34062306a36Sopenharmony_ci struct pci_dev *pdev) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci u32 map_type = tbi->map_type; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (map_type & VMXNET3_MAP_SINGLE) 34562306a36Sopenharmony_ci dma_unmap_single(&pdev->dev, tbi->dma_addr, tbi->len, 34662306a36Sopenharmony_ci DMA_TO_DEVICE); 34762306a36Sopenharmony_ci else if (map_type & VMXNET3_MAP_PAGE) 34862306a36Sopenharmony_ci dma_unmap_page(&pdev->dev, tbi->dma_addr, tbi->len, 34962306a36Sopenharmony_ci DMA_TO_DEVICE); 35062306a36Sopenharmony_ci else 35162306a36Sopenharmony_ci BUG_ON(map_type & ~VMXNET3_MAP_XDP); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci tbi->map_type = VMXNET3_MAP_NONE; /* to help debugging */ 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic int 35862306a36Sopenharmony_civmxnet3_unmap_pkt(u32 eop_idx, struct vmxnet3_tx_queue *tq, 35962306a36Sopenharmony_ci struct pci_dev *pdev, struct vmxnet3_adapter *adapter, 36062306a36Sopenharmony_ci struct xdp_frame_bulk *bq) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci struct vmxnet3_tx_buf_info *tbi; 36362306a36Sopenharmony_ci int entries = 0; 36462306a36Sopenharmony_ci u32 map_type; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* no out of order completion */ 36762306a36Sopenharmony_ci BUG_ON(tq->buf_info[eop_idx].sop_idx != tq->tx_ring.next2comp); 36862306a36Sopenharmony_ci BUG_ON(VMXNET3_TXDESC_GET_EOP(&(tq->tx_ring.base[eop_idx].txd)) != 1); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci tbi = &tq->buf_info[eop_idx]; 37162306a36Sopenharmony_ci BUG_ON(!tbi->skb); 37262306a36Sopenharmony_ci map_type = tbi->map_type; 37362306a36Sopenharmony_ci VMXNET3_INC_RING_IDX_ONLY(eop_idx, tq->tx_ring.size); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci while (tq->tx_ring.next2comp != eop_idx) { 37662306a36Sopenharmony_ci vmxnet3_unmap_tx_buf(tq->buf_info + tq->tx_ring.next2comp, 37762306a36Sopenharmony_ci pdev); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* update next2comp w/o tx_lock. Since we are marking more, 38062306a36Sopenharmony_ci * instead of less, tx ring entries avail, the worst case is 38162306a36Sopenharmony_ci * that the tx routine incorrectly re-queues a pkt due to 38262306a36Sopenharmony_ci * insufficient tx ring entries. 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_ci vmxnet3_cmd_ring_adv_next2comp(&tq->tx_ring); 38562306a36Sopenharmony_ci entries++; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (map_type & VMXNET3_MAP_XDP) 38962306a36Sopenharmony_ci xdp_return_frame_bulk(tbi->xdpf, bq); 39062306a36Sopenharmony_ci else 39162306a36Sopenharmony_ci dev_kfree_skb_any(tbi->skb); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* xdpf and skb are in an anonymous union. */ 39462306a36Sopenharmony_ci tbi->skb = NULL; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci return entries; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic int 40162306a36Sopenharmony_civmxnet3_tq_tx_complete(struct vmxnet3_tx_queue *tq, 40262306a36Sopenharmony_ci struct vmxnet3_adapter *adapter) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci union Vmxnet3_GenericDesc *gdesc; 40562306a36Sopenharmony_ci struct xdp_frame_bulk bq; 40662306a36Sopenharmony_ci int completed = 0; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci xdp_frame_bulk_init(&bq); 40962306a36Sopenharmony_ci rcu_read_lock(); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci gdesc = tq->comp_ring.base + tq->comp_ring.next2proc; 41262306a36Sopenharmony_ci while (VMXNET3_TCD_GET_GEN(&gdesc->tcd) == tq->comp_ring.gen) { 41362306a36Sopenharmony_ci /* Prevent any &gdesc->tcd field from being (speculatively) 41462306a36Sopenharmony_ci * read before (&gdesc->tcd)->gen is read. 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_ci dma_rmb(); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci completed += vmxnet3_unmap_pkt(VMXNET3_TCD_GET_TXIDX( 41962306a36Sopenharmony_ci &gdesc->tcd), tq, adapter->pdev, 42062306a36Sopenharmony_ci adapter, &bq); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci vmxnet3_comp_ring_adv_next2proc(&tq->comp_ring); 42362306a36Sopenharmony_ci gdesc = tq->comp_ring.base + tq->comp_ring.next2proc; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci xdp_flush_frame_bulk(&bq); 42662306a36Sopenharmony_ci rcu_read_unlock(); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (completed) { 42962306a36Sopenharmony_ci spin_lock(&tq->tx_lock); 43062306a36Sopenharmony_ci if (unlikely(vmxnet3_tq_stopped(tq, adapter) && 43162306a36Sopenharmony_ci vmxnet3_cmd_ring_desc_avail(&tq->tx_ring) > 43262306a36Sopenharmony_ci VMXNET3_WAKE_QUEUE_THRESHOLD(tq) && 43362306a36Sopenharmony_ci netif_carrier_ok(adapter->netdev))) { 43462306a36Sopenharmony_ci vmxnet3_tq_wake(tq, adapter); 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci spin_unlock(&tq->tx_lock); 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci return completed; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic void 44362306a36Sopenharmony_civmxnet3_tq_cleanup(struct vmxnet3_tx_queue *tq, 44462306a36Sopenharmony_ci struct vmxnet3_adapter *adapter) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct xdp_frame_bulk bq; 44762306a36Sopenharmony_ci u32 map_type; 44862306a36Sopenharmony_ci int i; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci xdp_frame_bulk_init(&bq); 45162306a36Sopenharmony_ci rcu_read_lock(); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci while (tq->tx_ring.next2comp != tq->tx_ring.next2fill) { 45462306a36Sopenharmony_ci struct vmxnet3_tx_buf_info *tbi; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci tbi = tq->buf_info + tq->tx_ring.next2comp; 45762306a36Sopenharmony_ci map_type = tbi->map_type; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci vmxnet3_unmap_tx_buf(tbi, adapter->pdev); 46062306a36Sopenharmony_ci if (tbi->skb) { 46162306a36Sopenharmony_ci if (map_type & VMXNET3_MAP_XDP) 46262306a36Sopenharmony_ci xdp_return_frame_bulk(tbi->xdpf, &bq); 46362306a36Sopenharmony_ci else 46462306a36Sopenharmony_ci dev_kfree_skb_any(tbi->skb); 46562306a36Sopenharmony_ci tbi->skb = NULL; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci vmxnet3_cmd_ring_adv_next2comp(&tq->tx_ring); 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci xdp_flush_frame_bulk(&bq); 47162306a36Sopenharmony_ci rcu_read_unlock(); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* sanity check, verify all buffers are indeed unmapped */ 47462306a36Sopenharmony_ci for (i = 0; i < tq->tx_ring.size; i++) 47562306a36Sopenharmony_ci BUG_ON(tq->buf_info[i].map_type != VMXNET3_MAP_NONE); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci tq->tx_ring.gen = VMXNET3_INIT_GEN; 47862306a36Sopenharmony_ci tq->tx_ring.next2fill = tq->tx_ring.next2comp = 0; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci tq->comp_ring.gen = VMXNET3_INIT_GEN; 48162306a36Sopenharmony_ci tq->comp_ring.next2proc = 0; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic void 48662306a36Sopenharmony_civmxnet3_tq_destroy(struct vmxnet3_tx_queue *tq, 48762306a36Sopenharmony_ci struct vmxnet3_adapter *adapter) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci if (tq->tx_ring.base) { 49062306a36Sopenharmony_ci dma_free_coherent(&adapter->pdev->dev, tq->tx_ring.size * 49162306a36Sopenharmony_ci sizeof(struct Vmxnet3_TxDesc), 49262306a36Sopenharmony_ci tq->tx_ring.base, tq->tx_ring.basePA); 49362306a36Sopenharmony_ci tq->tx_ring.base = NULL; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci if (tq->data_ring.base) { 49662306a36Sopenharmony_ci dma_free_coherent(&adapter->pdev->dev, 49762306a36Sopenharmony_ci tq->data_ring.size * tq->txdata_desc_size, 49862306a36Sopenharmony_ci tq->data_ring.base, tq->data_ring.basePA); 49962306a36Sopenharmony_ci tq->data_ring.base = NULL; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci if (tq->comp_ring.base) { 50262306a36Sopenharmony_ci dma_free_coherent(&adapter->pdev->dev, tq->comp_ring.size * 50362306a36Sopenharmony_ci sizeof(struct Vmxnet3_TxCompDesc), 50462306a36Sopenharmony_ci tq->comp_ring.base, tq->comp_ring.basePA); 50562306a36Sopenharmony_ci tq->comp_ring.base = NULL; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci kfree(tq->buf_info); 50862306a36Sopenharmony_ci tq->buf_info = NULL; 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci/* Destroy all tx queues */ 51362306a36Sopenharmony_civoid 51462306a36Sopenharmony_civmxnet3_tq_destroy_all(struct vmxnet3_adapter *adapter) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci int i; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci for (i = 0; i < adapter->num_tx_queues; i++) 51962306a36Sopenharmony_ci vmxnet3_tq_destroy(&adapter->tx_queue[i], adapter); 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic void 52462306a36Sopenharmony_civmxnet3_tq_init(struct vmxnet3_tx_queue *tq, 52562306a36Sopenharmony_ci struct vmxnet3_adapter *adapter) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci int i; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* reset the tx ring contents to 0 and reset the tx ring states */ 53062306a36Sopenharmony_ci memset(tq->tx_ring.base, 0, tq->tx_ring.size * 53162306a36Sopenharmony_ci sizeof(struct Vmxnet3_TxDesc)); 53262306a36Sopenharmony_ci tq->tx_ring.next2fill = tq->tx_ring.next2comp = 0; 53362306a36Sopenharmony_ci tq->tx_ring.gen = VMXNET3_INIT_GEN; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci memset(tq->data_ring.base, 0, 53662306a36Sopenharmony_ci tq->data_ring.size * tq->txdata_desc_size); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci /* reset the tx comp ring contents to 0 and reset comp ring states */ 53962306a36Sopenharmony_ci memset(tq->comp_ring.base, 0, tq->comp_ring.size * 54062306a36Sopenharmony_ci sizeof(struct Vmxnet3_TxCompDesc)); 54162306a36Sopenharmony_ci tq->comp_ring.next2proc = 0; 54262306a36Sopenharmony_ci tq->comp_ring.gen = VMXNET3_INIT_GEN; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* reset the bookkeeping data */ 54562306a36Sopenharmony_ci memset(tq->buf_info, 0, sizeof(tq->buf_info[0]) * tq->tx_ring.size); 54662306a36Sopenharmony_ci for (i = 0; i < tq->tx_ring.size; i++) 54762306a36Sopenharmony_ci tq->buf_info[i].map_type = VMXNET3_MAP_NONE; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* stats are not reset */ 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic int 55462306a36Sopenharmony_civmxnet3_tq_create(struct vmxnet3_tx_queue *tq, 55562306a36Sopenharmony_ci struct vmxnet3_adapter *adapter) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci BUG_ON(tq->tx_ring.base || tq->data_ring.base || 55862306a36Sopenharmony_ci tq->comp_ring.base || tq->buf_info); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci tq->tx_ring.base = dma_alloc_coherent(&adapter->pdev->dev, 56162306a36Sopenharmony_ci tq->tx_ring.size * sizeof(struct Vmxnet3_TxDesc), 56262306a36Sopenharmony_ci &tq->tx_ring.basePA, GFP_KERNEL); 56362306a36Sopenharmony_ci if (!tq->tx_ring.base) { 56462306a36Sopenharmony_ci netdev_err(adapter->netdev, "failed to allocate tx ring\n"); 56562306a36Sopenharmony_ci goto err; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci tq->data_ring.base = dma_alloc_coherent(&adapter->pdev->dev, 56962306a36Sopenharmony_ci tq->data_ring.size * tq->txdata_desc_size, 57062306a36Sopenharmony_ci &tq->data_ring.basePA, GFP_KERNEL); 57162306a36Sopenharmony_ci if (!tq->data_ring.base) { 57262306a36Sopenharmony_ci netdev_err(adapter->netdev, "failed to allocate tx data ring\n"); 57362306a36Sopenharmony_ci goto err; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci tq->comp_ring.base = dma_alloc_coherent(&adapter->pdev->dev, 57762306a36Sopenharmony_ci tq->comp_ring.size * sizeof(struct Vmxnet3_TxCompDesc), 57862306a36Sopenharmony_ci &tq->comp_ring.basePA, GFP_KERNEL); 57962306a36Sopenharmony_ci if (!tq->comp_ring.base) { 58062306a36Sopenharmony_ci netdev_err(adapter->netdev, "failed to allocate tx comp ring\n"); 58162306a36Sopenharmony_ci goto err; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci tq->buf_info = kcalloc_node(tq->tx_ring.size, sizeof(tq->buf_info[0]), 58562306a36Sopenharmony_ci GFP_KERNEL, 58662306a36Sopenharmony_ci dev_to_node(&adapter->pdev->dev)); 58762306a36Sopenharmony_ci if (!tq->buf_info) 58862306a36Sopenharmony_ci goto err; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci return 0; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cierr: 59362306a36Sopenharmony_ci vmxnet3_tq_destroy(tq, adapter); 59462306a36Sopenharmony_ci return -ENOMEM; 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic void 59862306a36Sopenharmony_civmxnet3_tq_cleanup_all(struct vmxnet3_adapter *adapter) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci int i; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci for (i = 0; i < adapter->num_tx_queues; i++) 60362306a36Sopenharmony_ci vmxnet3_tq_cleanup(&adapter->tx_queue[i], adapter); 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci/* 60762306a36Sopenharmony_ci * starting from ring->next2fill, allocate rx buffers for the given ring 60862306a36Sopenharmony_ci * of the rx queue and update the rx desc. stop after @num_to_alloc buffers 60962306a36Sopenharmony_ci * are allocated or allocation fails 61062306a36Sopenharmony_ci */ 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cistatic int 61362306a36Sopenharmony_civmxnet3_rq_alloc_rx_buf(struct vmxnet3_rx_queue *rq, u32 ring_idx, 61462306a36Sopenharmony_ci int num_to_alloc, struct vmxnet3_adapter *adapter) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci int num_allocated = 0; 61762306a36Sopenharmony_ci struct vmxnet3_rx_buf_info *rbi_base = rq->buf_info[ring_idx]; 61862306a36Sopenharmony_ci struct vmxnet3_cmd_ring *ring = &rq->rx_ring[ring_idx]; 61962306a36Sopenharmony_ci u32 val; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci while (num_allocated <= num_to_alloc) { 62262306a36Sopenharmony_ci struct vmxnet3_rx_buf_info *rbi; 62362306a36Sopenharmony_ci union Vmxnet3_GenericDesc *gd; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci rbi = rbi_base + ring->next2fill; 62662306a36Sopenharmony_ci gd = ring->base + ring->next2fill; 62762306a36Sopenharmony_ci rbi->comp_state = VMXNET3_RXD_COMP_PENDING; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci if (rbi->buf_type == VMXNET3_RX_BUF_XDP) { 63062306a36Sopenharmony_ci void *data = vmxnet3_pp_get_buff(rq->page_pool, 63162306a36Sopenharmony_ci &rbi->dma_addr, 63262306a36Sopenharmony_ci GFP_KERNEL); 63362306a36Sopenharmony_ci if (!data) { 63462306a36Sopenharmony_ci rq->stats.rx_buf_alloc_failure++; 63562306a36Sopenharmony_ci break; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci rbi->page = virt_to_page(data); 63862306a36Sopenharmony_ci val = VMXNET3_RXD_BTYPE_HEAD << VMXNET3_RXD_BTYPE_SHIFT; 63962306a36Sopenharmony_ci } else if (rbi->buf_type == VMXNET3_RX_BUF_SKB) { 64062306a36Sopenharmony_ci if (rbi->skb == NULL) { 64162306a36Sopenharmony_ci rbi->skb = __netdev_alloc_skb_ip_align(adapter->netdev, 64262306a36Sopenharmony_ci rbi->len, 64362306a36Sopenharmony_ci GFP_KERNEL); 64462306a36Sopenharmony_ci if (unlikely(rbi->skb == NULL)) { 64562306a36Sopenharmony_ci rq->stats.rx_buf_alloc_failure++; 64662306a36Sopenharmony_ci break; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci rbi->dma_addr = dma_map_single( 65062306a36Sopenharmony_ci &adapter->pdev->dev, 65162306a36Sopenharmony_ci rbi->skb->data, rbi->len, 65262306a36Sopenharmony_ci DMA_FROM_DEVICE); 65362306a36Sopenharmony_ci if (dma_mapping_error(&adapter->pdev->dev, 65462306a36Sopenharmony_ci rbi->dma_addr)) { 65562306a36Sopenharmony_ci dev_kfree_skb_any(rbi->skb); 65662306a36Sopenharmony_ci rbi->skb = NULL; 65762306a36Sopenharmony_ci rq->stats.rx_buf_alloc_failure++; 65862306a36Sopenharmony_ci break; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci } else { 66162306a36Sopenharmony_ci /* rx buffer skipped by the device */ 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci val = VMXNET3_RXD_BTYPE_HEAD << VMXNET3_RXD_BTYPE_SHIFT; 66462306a36Sopenharmony_ci } else { 66562306a36Sopenharmony_ci BUG_ON(rbi->buf_type != VMXNET3_RX_BUF_PAGE || 66662306a36Sopenharmony_ci rbi->len != PAGE_SIZE); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci if (rbi->page == NULL) { 66962306a36Sopenharmony_ci rbi->page = alloc_page(GFP_ATOMIC); 67062306a36Sopenharmony_ci if (unlikely(rbi->page == NULL)) { 67162306a36Sopenharmony_ci rq->stats.rx_buf_alloc_failure++; 67262306a36Sopenharmony_ci break; 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci rbi->dma_addr = dma_map_page( 67562306a36Sopenharmony_ci &adapter->pdev->dev, 67662306a36Sopenharmony_ci rbi->page, 0, PAGE_SIZE, 67762306a36Sopenharmony_ci DMA_FROM_DEVICE); 67862306a36Sopenharmony_ci if (dma_mapping_error(&adapter->pdev->dev, 67962306a36Sopenharmony_ci rbi->dma_addr)) { 68062306a36Sopenharmony_ci put_page(rbi->page); 68162306a36Sopenharmony_ci rbi->page = NULL; 68262306a36Sopenharmony_ci rq->stats.rx_buf_alloc_failure++; 68362306a36Sopenharmony_ci break; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci } else { 68662306a36Sopenharmony_ci /* rx buffers skipped by the device */ 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci val = VMXNET3_RXD_BTYPE_BODY << VMXNET3_RXD_BTYPE_SHIFT; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci gd->rxd.addr = cpu_to_le64(rbi->dma_addr); 69262306a36Sopenharmony_ci gd->dword[2] = cpu_to_le32((!ring->gen << VMXNET3_RXD_GEN_SHIFT) 69362306a36Sopenharmony_ci | val | rbi->len); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci /* Fill the last buffer but dont mark it ready, or else the 69662306a36Sopenharmony_ci * device will think that the queue is full */ 69762306a36Sopenharmony_ci if (num_allocated == num_to_alloc) { 69862306a36Sopenharmony_ci rbi->comp_state = VMXNET3_RXD_COMP_DONE; 69962306a36Sopenharmony_ci break; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci gd->dword[2] |= cpu_to_le32(ring->gen << VMXNET3_RXD_GEN_SHIFT); 70362306a36Sopenharmony_ci num_allocated++; 70462306a36Sopenharmony_ci vmxnet3_cmd_ring_adv_next2fill(ring); 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci netdev_dbg(adapter->netdev, 70862306a36Sopenharmony_ci "alloc_rx_buf: %d allocated, next2fill %u, next2comp %u\n", 70962306a36Sopenharmony_ci num_allocated, ring->next2fill, ring->next2comp); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* so that the device can distinguish a full ring and an empty ring */ 71262306a36Sopenharmony_ci BUG_ON(num_allocated != 0 && ring->next2fill == ring->next2comp); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci return num_allocated; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_cistatic void 71962306a36Sopenharmony_civmxnet3_append_frag(struct sk_buff *skb, struct Vmxnet3_RxCompDesc *rcd, 72062306a36Sopenharmony_ci struct vmxnet3_rx_buf_info *rbi) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci skb_frag_t *frag = skb_shinfo(skb)->frags + skb_shinfo(skb)->nr_frags; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci BUG_ON(skb_shinfo(skb)->nr_frags >= MAX_SKB_FRAGS); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci skb_frag_fill_page_desc(frag, rbi->page, 0, rcd->len); 72762306a36Sopenharmony_ci skb->data_len += rcd->len; 72862306a36Sopenharmony_ci skb->truesize += PAGE_SIZE; 72962306a36Sopenharmony_ci skb_shinfo(skb)->nr_frags++; 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cistatic int 73462306a36Sopenharmony_civmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx, 73562306a36Sopenharmony_ci struct vmxnet3_tx_queue *tq, struct pci_dev *pdev, 73662306a36Sopenharmony_ci struct vmxnet3_adapter *adapter) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci u32 dw2, len; 73962306a36Sopenharmony_ci unsigned long buf_offset; 74062306a36Sopenharmony_ci int i; 74162306a36Sopenharmony_ci union Vmxnet3_GenericDesc *gdesc; 74262306a36Sopenharmony_ci struct vmxnet3_tx_buf_info *tbi = NULL; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci BUG_ON(ctx->copy_size > skb_headlen(skb)); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci /* use the previous gen bit for the SOP desc */ 74762306a36Sopenharmony_ci dw2 = (tq->tx_ring.gen ^ 0x1) << VMXNET3_TXD_GEN_SHIFT; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci ctx->sop_txd = tq->tx_ring.base + tq->tx_ring.next2fill; 75062306a36Sopenharmony_ci gdesc = ctx->sop_txd; /* both loops below can be skipped */ 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci /* no need to map the buffer if headers are copied */ 75362306a36Sopenharmony_ci if (ctx->copy_size) { 75462306a36Sopenharmony_ci ctx->sop_txd->txd.addr = cpu_to_le64(tq->data_ring.basePA + 75562306a36Sopenharmony_ci tq->tx_ring.next2fill * 75662306a36Sopenharmony_ci tq->txdata_desc_size); 75762306a36Sopenharmony_ci ctx->sop_txd->dword[2] = cpu_to_le32(dw2 | ctx->copy_size); 75862306a36Sopenharmony_ci ctx->sop_txd->dword[3] = 0; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci tbi = tq->buf_info + tq->tx_ring.next2fill; 76162306a36Sopenharmony_ci tbi->map_type = VMXNET3_MAP_NONE; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci netdev_dbg(adapter->netdev, 76462306a36Sopenharmony_ci "txd[%u]: 0x%Lx 0x%x 0x%x\n", 76562306a36Sopenharmony_ci tq->tx_ring.next2fill, 76662306a36Sopenharmony_ci le64_to_cpu(ctx->sop_txd->txd.addr), 76762306a36Sopenharmony_ci ctx->sop_txd->dword[2], ctx->sop_txd->dword[3]); 76862306a36Sopenharmony_ci vmxnet3_cmd_ring_adv_next2fill(&tq->tx_ring); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci /* use the right gen for non-SOP desc */ 77162306a36Sopenharmony_ci dw2 = tq->tx_ring.gen << VMXNET3_TXD_GEN_SHIFT; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci /* linear part can use multiple tx desc if it's big */ 77562306a36Sopenharmony_ci len = skb_headlen(skb) - ctx->copy_size; 77662306a36Sopenharmony_ci buf_offset = ctx->copy_size; 77762306a36Sopenharmony_ci while (len) { 77862306a36Sopenharmony_ci u32 buf_size; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (len < VMXNET3_MAX_TX_BUF_SIZE) { 78162306a36Sopenharmony_ci buf_size = len; 78262306a36Sopenharmony_ci dw2 |= len; 78362306a36Sopenharmony_ci } else { 78462306a36Sopenharmony_ci buf_size = VMXNET3_MAX_TX_BUF_SIZE; 78562306a36Sopenharmony_ci /* spec says that for TxDesc.len, 0 == 2^14 */ 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci tbi = tq->buf_info + tq->tx_ring.next2fill; 78962306a36Sopenharmony_ci tbi->map_type = VMXNET3_MAP_SINGLE; 79062306a36Sopenharmony_ci tbi->dma_addr = dma_map_single(&adapter->pdev->dev, 79162306a36Sopenharmony_ci skb->data + buf_offset, buf_size, 79262306a36Sopenharmony_ci DMA_TO_DEVICE); 79362306a36Sopenharmony_ci if (dma_mapping_error(&adapter->pdev->dev, tbi->dma_addr)) 79462306a36Sopenharmony_ci return -EFAULT; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci tbi->len = buf_size; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci gdesc = tq->tx_ring.base + tq->tx_ring.next2fill; 79962306a36Sopenharmony_ci BUG_ON(gdesc->txd.gen == tq->tx_ring.gen); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci gdesc->txd.addr = cpu_to_le64(tbi->dma_addr); 80262306a36Sopenharmony_ci gdesc->dword[2] = cpu_to_le32(dw2); 80362306a36Sopenharmony_ci gdesc->dword[3] = 0; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci netdev_dbg(adapter->netdev, 80662306a36Sopenharmony_ci "txd[%u]: 0x%Lx 0x%x 0x%x\n", 80762306a36Sopenharmony_ci tq->tx_ring.next2fill, le64_to_cpu(gdesc->txd.addr), 80862306a36Sopenharmony_ci le32_to_cpu(gdesc->dword[2]), gdesc->dword[3]); 80962306a36Sopenharmony_ci vmxnet3_cmd_ring_adv_next2fill(&tq->tx_ring); 81062306a36Sopenharmony_ci dw2 = tq->tx_ring.gen << VMXNET3_TXD_GEN_SHIFT; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci len -= buf_size; 81362306a36Sopenharmony_ci buf_offset += buf_size; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 81762306a36Sopenharmony_ci const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; 81862306a36Sopenharmony_ci u32 buf_size; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci buf_offset = 0; 82162306a36Sopenharmony_ci len = skb_frag_size(frag); 82262306a36Sopenharmony_ci while (len) { 82362306a36Sopenharmony_ci tbi = tq->buf_info + tq->tx_ring.next2fill; 82462306a36Sopenharmony_ci if (len < VMXNET3_MAX_TX_BUF_SIZE) { 82562306a36Sopenharmony_ci buf_size = len; 82662306a36Sopenharmony_ci dw2 |= len; 82762306a36Sopenharmony_ci } else { 82862306a36Sopenharmony_ci buf_size = VMXNET3_MAX_TX_BUF_SIZE; 82962306a36Sopenharmony_ci /* spec says that for TxDesc.len, 0 == 2^14 */ 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci tbi->map_type = VMXNET3_MAP_PAGE; 83262306a36Sopenharmony_ci tbi->dma_addr = skb_frag_dma_map(&adapter->pdev->dev, frag, 83362306a36Sopenharmony_ci buf_offset, buf_size, 83462306a36Sopenharmony_ci DMA_TO_DEVICE); 83562306a36Sopenharmony_ci if (dma_mapping_error(&adapter->pdev->dev, tbi->dma_addr)) 83662306a36Sopenharmony_ci return -EFAULT; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci tbi->len = buf_size; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci gdesc = tq->tx_ring.base + tq->tx_ring.next2fill; 84162306a36Sopenharmony_ci BUG_ON(gdesc->txd.gen == tq->tx_ring.gen); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci gdesc->txd.addr = cpu_to_le64(tbi->dma_addr); 84462306a36Sopenharmony_ci gdesc->dword[2] = cpu_to_le32(dw2); 84562306a36Sopenharmony_ci gdesc->dword[3] = 0; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci netdev_dbg(adapter->netdev, 84862306a36Sopenharmony_ci "txd[%u]: 0x%llx %u %u\n", 84962306a36Sopenharmony_ci tq->tx_ring.next2fill, le64_to_cpu(gdesc->txd.addr), 85062306a36Sopenharmony_ci le32_to_cpu(gdesc->dword[2]), gdesc->dword[3]); 85162306a36Sopenharmony_ci vmxnet3_cmd_ring_adv_next2fill(&tq->tx_ring); 85262306a36Sopenharmony_ci dw2 = tq->tx_ring.gen << VMXNET3_TXD_GEN_SHIFT; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci len -= buf_size; 85562306a36Sopenharmony_ci buf_offset += buf_size; 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci ctx->eop_txd = gdesc; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci /* set the last buf_info for the pkt */ 86262306a36Sopenharmony_ci tbi->skb = skb; 86362306a36Sopenharmony_ci tbi->sop_idx = ctx->sop_txd - tq->tx_ring.base; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci return 0; 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci/* Init all tx queues */ 87062306a36Sopenharmony_cistatic void 87162306a36Sopenharmony_civmxnet3_tq_init_all(struct vmxnet3_adapter *adapter) 87262306a36Sopenharmony_ci{ 87362306a36Sopenharmony_ci int i; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci for (i = 0; i < adapter->num_tx_queues; i++) 87662306a36Sopenharmony_ci vmxnet3_tq_init(&adapter->tx_queue[i], adapter); 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci/* 88162306a36Sopenharmony_ci * parse relevant protocol headers: 88262306a36Sopenharmony_ci * For a tso pkt, relevant headers are L2/3/4 including options 88362306a36Sopenharmony_ci * For a pkt requesting csum offloading, they are L2/3 and may include L4 88462306a36Sopenharmony_ci * if it's a TCP/UDP pkt 88562306a36Sopenharmony_ci * 88662306a36Sopenharmony_ci * Returns: 88762306a36Sopenharmony_ci * -1: error happens during parsing 88862306a36Sopenharmony_ci * 0: protocol headers parsed, but too big to be copied 88962306a36Sopenharmony_ci * 1: protocol headers parsed and copied 89062306a36Sopenharmony_ci * 89162306a36Sopenharmony_ci * Other effects: 89262306a36Sopenharmony_ci * 1. related *ctx fields are updated. 89362306a36Sopenharmony_ci * 2. ctx->copy_size is # of bytes copied 89462306a36Sopenharmony_ci * 3. the portion to be copied is guaranteed to be in the linear part 89562306a36Sopenharmony_ci * 89662306a36Sopenharmony_ci */ 89762306a36Sopenharmony_cistatic int 89862306a36Sopenharmony_civmxnet3_parse_hdr(struct sk_buff *skb, struct vmxnet3_tx_queue *tq, 89962306a36Sopenharmony_ci struct vmxnet3_tx_ctx *ctx, 90062306a36Sopenharmony_ci struct vmxnet3_adapter *adapter) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci u8 protocol = 0; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci if (ctx->mss) { /* TSO */ 90562306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_4(adapter) && skb->encapsulation) { 90662306a36Sopenharmony_ci ctx->l4_offset = skb_inner_transport_offset(skb); 90762306a36Sopenharmony_ci ctx->l4_hdr_size = inner_tcp_hdrlen(skb); 90862306a36Sopenharmony_ci ctx->copy_size = ctx->l4_offset + ctx->l4_hdr_size; 90962306a36Sopenharmony_ci } else { 91062306a36Sopenharmony_ci ctx->l4_offset = skb_transport_offset(skb); 91162306a36Sopenharmony_ci ctx->l4_hdr_size = tcp_hdrlen(skb); 91262306a36Sopenharmony_ci ctx->copy_size = ctx->l4_offset + ctx->l4_hdr_size; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci } else { 91562306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) { 91662306a36Sopenharmony_ci /* For encap packets, skb_checksum_start_offset refers 91762306a36Sopenharmony_ci * to inner L4 offset. Thus, below works for encap as 91862306a36Sopenharmony_ci * well as non-encap case 91962306a36Sopenharmony_ci */ 92062306a36Sopenharmony_ci ctx->l4_offset = skb_checksum_start_offset(skb); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_4(adapter) && 92362306a36Sopenharmony_ci skb->encapsulation) { 92462306a36Sopenharmony_ci struct iphdr *iph = inner_ip_hdr(skb); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci if (iph->version == 4) { 92762306a36Sopenharmony_ci protocol = iph->protocol; 92862306a36Sopenharmony_ci } else { 92962306a36Sopenharmony_ci const struct ipv6hdr *ipv6h; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci ipv6h = inner_ipv6_hdr(skb); 93262306a36Sopenharmony_ci protocol = ipv6h->nexthdr; 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci } else { 93562306a36Sopenharmony_ci if (ctx->ipv4) { 93662306a36Sopenharmony_ci const struct iphdr *iph = ip_hdr(skb); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci protocol = iph->protocol; 93962306a36Sopenharmony_ci } else if (ctx->ipv6) { 94062306a36Sopenharmony_ci const struct ipv6hdr *ipv6h; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci ipv6h = ipv6_hdr(skb); 94362306a36Sopenharmony_ci protocol = ipv6h->nexthdr; 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci switch (protocol) { 94862306a36Sopenharmony_ci case IPPROTO_TCP: 94962306a36Sopenharmony_ci ctx->l4_hdr_size = skb->encapsulation ? inner_tcp_hdrlen(skb) : 95062306a36Sopenharmony_ci tcp_hdrlen(skb); 95162306a36Sopenharmony_ci break; 95262306a36Sopenharmony_ci case IPPROTO_UDP: 95362306a36Sopenharmony_ci ctx->l4_hdr_size = sizeof(struct udphdr); 95462306a36Sopenharmony_ci break; 95562306a36Sopenharmony_ci default: 95662306a36Sopenharmony_ci ctx->l4_hdr_size = 0; 95762306a36Sopenharmony_ci break; 95862306a36Sopenharmony_ci } 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci ctx->copy_size = min(ctx->l4_offset + 96162306a36Sopenharmony_ci ctx->l4_hdr_size, skb->len); 96262306a36Sopenharmony_ci } else { 96362306a36Sopenharmony_ci ctx->l4_offset = 0; 96462306a36Sopenharmony_ci ctx->l4_hdr_size = 0; 96562306a36Sopenharmony_ci /* copy as much as allowed */ 96662306a36Sopenharmony_ci ctx->copy_size = min_t(unsigned int, 96762306a36Sopenharmony_ci tq->txdata_desc_size, 96862306a36Sopenharmony_ci skb_headlen(skb)); 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci if (skb->len <= VMXNET3_HDR_COPY_SIZE) 97262306a36Sopenharmony_ci ctx->copy_size = skb->len; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci /* make sure headers are accessible directly */ 97562306a36Sopenharmony_ci if (unlikely(!pskb_may_pull(skb, ctx->copy_size))) 97662306a36Sopenharmony_ci goto err; 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci if (unlikely(ctx->copy_size > tq->txdata_desc_size)) { 98062306a36Sopenharmony_ci tq->stats.oversized_hdr++; 98162306a36Sopenharmony_ci ctx->copy_size = 0; 98262306a36Sopenharmony_ci return 0; 98362306a36Sopenharmony_ci } 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci return 1; 98662306a36Sopenharmony_cierr: 98762306a36Sopenharmony_ci return -1; 98862306a36Sopenharmony_ci} 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci/* 99162306a36Sopenharmony_ci * copy relevant protocol headers to the transmit ring: 99262306a36Sopenharmony_ci * For a tso pkt, relevant headers are L2/3/4 including options 99362306a36Sopenharmony_ci * For a pkt requesting csum offloading, they are L2/3 and may include L4 99462306a36Sopenharmony_ci * if it's a TCP/UDP pkt 99562306a36Sopenharmony_ci * 99662306a36Sopenharmony_ci * 99762306a36Sopenharmony_ci * Note that this requires that vmxnet3_parse_hdr be called first to set the 99862306a36Sopenharmony_ci * appropriate bits in ctx first 99962306a36Sopenharmony_ci */ 100062306a36Sopenharmony_cistatic void 100162306a36Sopenharmony_civmxnet3_copy_hdr(struct sk_buff *skb, struct vmxnet3_tx_queue *tq, 100262306a36Sopenharmony_ci struct vmxnet3_tx_ctx *ctx, 100362306a36Sopenharmony_ci struct vmxnet3_adapter *adapter) 100462306a36Sopenharmony_ci{ 100562306a36Sopenharmony_ci struct Vmxnet3_TxDataDesc *tdd; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci tdd = (struct Vmxnet3_TxDataDesc *)((u8 *)tq->data_ring.base + 100862306a36Sopenharmony_ci tq->tx_ring.next2fill * 100962306a36Sopenharmony_ci tq->txdata_desc_size); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci memcpy(tdd->data, skb->data, ctx->copy_size); 101262306a36Sopenharmony_ci netdev_dbg(adapter->netdev, 101362306a36Sopenharmony_ci "copy %u bytes to dataRing[%u]\n", 101462306a36Sopenharmony_ci ctx->copy_size, tq->tx_ring.next2fill); 101562306a36Sopenharmony_ci} 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_cistatic void 101962306a36Sopenharmony_civmxnet3_prepare_inner_tso(struct sk_buff *skb, 102062306a36Sopenharmony_ci struct vmxnet3_tx_ctx *ctx) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci struct tcphdr *tcph = inner_tcp_hdr(skb); 102362306a36Sopenharmony_ci struct iphdr *iph = inner_ip_hdr(skb); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci if (iph->version == 4) { 102662306a36Sopenharmony_ci iph->check = 0; 102762306a36Sopenharmony_ci tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, 0, 102862306a36Sopenharmony_ci IPPROTO_TCP, 0); 102962306a36Sopenharmony_ci } else { 103062306a36Sopenharmony_ci struct ipv6hdr *iph = inner_ipv6_hdr(skb); 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci tcph->check = ~csum_ipv6_magic(&iph->saddr, &iph->daddr, 0, 103362306a36Sopenharmony_ci IPPROTO_TCP, 0); 103462306a36Sopenharmony_ci } 103562306a36Sopenharmony_ci} 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_cistatic void 103862306a36Sopenharmony_civmxnet3_prepare_tso(struct sk_buff *skb, 103962306a36Sopenharmony_ci struct vmxnet3_tx_ctx *ctx) 104062306a36Sopenharmony_ci{ 104162306a36Sopenharmony_ci struct tcphdr *tcph = tcp_hdr(skb); 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci if (ctx->ipv4) { 104462306a36Sopenharmony_ci struct iphdr *iph = ip_hdr(skb); 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci iph->check = 0; 104762306a36Sopenharmony_ci tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, 0, 104862306a36Sopenharmony_ci IPPROTO_TCP, 0); 104962306a36Sopenharmony_ci } else if (ctx->ipv6) { 105062306a36Sopenharmony_ci tcp_v6_gso_csum_prep(skb); 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci} 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_cistatic int txd_estimate(const struct sk_buff *skb) 105562306a36Sopenharmony_ci{ 105662306a36Sopenharmony_ci int count = VMXNET3_TXD_NEEDED(skb_headlen(skb)) + 1; 105762306a36Sopenharmony_ci int i; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 106062306a36Sopenharmony_ci const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci count += VMXNET3_TXD_NEEDED(skb_frag_size(frag)); 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci return count; 106562306a36Sopenharmony_ci} 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci/* 106862306a36Sopenharmony_ci * Transmits a pkt thru a given tq 106962306a36Sopenharmony_ci * Returns: 107062306a36Sopenharmony_ci * NETDEV_TX_OK: descriptors are setup successfully 107162306a36Sopenharmony_ci * NETDEV_TX_OK: error occurred, the pkt is dropped 107262306a36Sopenharmony_ci * NETDEV_TX_BUSY: tx ring is full, queue is stopped 107362306a36Sopenharmony_ci * 107462306a36Sopenharmony_ci * Side-effects: 107562306a36Sopenharmony_ci * 1. tx ring may be changed 107662306a36Sopenharmony_ci * 2. tq stats may be updated accordingly 107762306a36Sopenharmony_ci * 3. shared->txNumDeferred may be updated 107862306a36Sopenharmony_ci */ 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_cistatic int 108162306a36Sopenharmony_civmxnet3_tq_xmit(struct sk_buff *skb, struct vmxnet3_tx_queue *tq, 108262306a36Sopenharmony_ci struct vmxnet3_adapter *adapter, struct net_device *netdev) 108362306a36Sopenharmony_ci{ 108462306a36Sopenharmony_ci int ret; 108562306a36Sopenharmony_ci u32 count; 108662306a36Sopenharmony_ci int num_pkts; 108762306a36Sopenharmony_ci int tx_num_deferred; 108862306a36Sopenharmony_ci unsigned long flags; 108962306a36Sopenharmony_ci struct vmxnet3_tx_ctx ctx; 109062306a36Sopenharmony_ci union Vmxnet3_GenericDesc *gdesc; 109162306a36Sopenharmony_ci#ifdef __BIG_ENDIAN_BITFIELD 109262306a36Sopenharmony_ci /* Use temporary descriptor to avoid touching bits multiple times */ 109362306a36Sopenharmony_ci union Vmxnet3_GenericDesc tempTxDesc; 109462306a36Sopenharmony_ci#endif 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci count = txd_estimate(skb); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci ctx.ipv4 = (vlan_get_protocol(skb) == cpu_to_be16(ETH_P_IP)); 109962306a36Sopenharmony_ci ctx.ipv6 = (vlan_get_protocol(skb) == cpu_to_be16(ETH_P_IPV6)); 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci ctx.mss = skb_shinfo(skb)->gso_size; 110262306a36Sopenharmony_ci if (ctx.mss) { 110362306a36Sopenharmony_ci if (skb_header_cloned(skb)) { 110462306a36Sopenharmony_ci if (unlikely(pskb_expand_head(skb, 0, 0, 110562306a36Sopenharmony_ci GFP_ATOMIC) != 0)) { 110662306a36Sopenharmony_ci tq->stats.drop_tso++; 110762306a36Sopenharmony_ci goto drop_pkt; 110862306a36Sopenharmony_ci } 110962306a36Sopenharmony_ci tq->stats.copy_skb_header++; 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci if (unlikely(count > VMXNET3_MAX_TSO_TXD_PER_PKT)) { 111262306a36Sopenharmony_ci /* tso pkts must not use more than 111362306a36Sopenharmony_ci * VMXNET3_MAX_TSO_TXD_PER_PKT entries 111462306a36Sopenharmony_ci */ 111562306a36Sopenharmony_ci if (skb_linearize(skb) != 0) { 111662306a36Sopenharmony_ci tq->stats.drop_too_many_frags++; 111762306a36Sopenharmony_ci goto drop_pkt; 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci tq->stats.linearized++; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci /* recalculate the # of descriptors to use */ 112262306a36Sopenharmony_ci count = VMXNET3_TXD_NEEDED(skb_headlen(skb)) + 1; 112362306a36Sopenharmony_ci if (unlikely(count > VMXNET3_MAX_TSO_TXD_PER_PKT)) { 112462306a36Sopenharmony_ci tq->stats.drop_too_many_frags++; 112562306a36Sopenharmony_ci goto drop_pkt; 112662306a36Sopenharmony_ci } 112762306a36Sopenharmony_ci } 112862306a36Sopenharmony_ci if (skb->encapsulation) { 112962306a36Sopenharmony_ci vmxnet3_prepare_inner_tso(skb, &ctx); 113062306a36Sopenharmony_ci } else { 113162306a36Sopenharmony_ci vmxnet3_prepare_tso(skb, &ctx); 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci } else { 113462306a36Sopenharmony_ci if (unlikely(count > VMXNET3_MAX_TXD_PER_PKT)) { 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci /* non-tso pkts must not use more than 113762306a36Sopenharmony_ci * VMXNET3_MAX_TXD_PER_PKT entries 113862306a36Sopenharmony_ci */ 113962306a36Sopenharmony_ci if (skb_linearize(skb) != 0) { 114062306a36Sopenharmony_ci tq->stats.drop_too_many_frags++; 114162306a36Sopenharmony_ci goto drop_pkt; 114262306a36Sopenharmony_ci } 114362306a36Sopenharmony_ci tq->stats.linearized++; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci /* recalculate the # of descriptors to use */ 114662306a36Sopenharmony_ci count = VMXNET3_TXD_NEEDED(skb_headlen(skb)) + 1; 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci ret = vmxnet3_parse_hdr(skb, tq, &ctx, adapter); 115162306a36Sopenharmony_ci if (ret >= 0) { 115262306a36Sopenharmony_ci BUG_ON(ret <= 0 && ctx.copy_size != 0); 115362306a36Sopenharmony_ci /* hdrs parsed, check against other limits */ 115462306a36Sopenharmony_ci if (ctx.mss) { 115562306a36Sopenharmony_ci if (unlikely(ctx.l4_offset + ctx.l4_hdr_size > 115662306a36Sopenharmony_ci VMXNET3_MAX_TX_BUF_SIZE)) { 115762306a36Sopenharmony_ci tq->stats.drop_oversized_hdr++; 115862306a36Sopenharmony_ci goto drop_pkt; 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci } else { 116162306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) { 116262306a36Sopenharmony_ci if (unlikely(ctx.l4_offset + 116362306a36Sopenharmony_ci skb->csum_offset > 116462306a36Sopenharmony_ci VMXNET3_MAX_CSUM_OFFSET)) { 116562306a36Sopenharmony_ci tq->stats.drop_oversized_hdr++; 116662306a36Sopenharmony_ci goto drop_pkt; 116762306a36Sopenharmony_ci } 116862306a36Sopenharmony_ci } 116962306a36Sopenharmony_ci } 117062306a36Sopenharmony_ci } else { 117162306a36Sopenharmony_ci tq->stats.drop_hdr_inspect_err++; 117262306a36Sopenharmony_ci goto drop_pkt; 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci spin_lock_irqsave(&tq->tx_lock, flags); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci if (count > vmxnet3_cmd_ring_desc_avail(&tq->tx_ring)) { 117862306a36Sopenharmony_ci tq->stats.tx_ring_full++; 117962306a36Sopenharmony_ci netdev_dbg(adapter->netdev, 118062306a36Sopenharmony_ci "tx queue stopped on %s, next2comp %u" 118162306a36Sopenharmony_ci " next2fill %u\n", adapter->netdev->name, 118262306a36Sopenharmony_ci tq->tx_ring.next2comp, tq->tx_ring.next2fill); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci vmxnet3_tq_stop(tq, adapter); 118562306a36Sopenharmony_ci spin_unlock_irqrestore(&tq->tx_lock, flags); 118662306a36Sopenharmony_ci return NETDEV_TX_BUSY; 118762306a36Sopenharmony_ci } 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci vmxnet3_copy_hdr(skb, tq, &ctx, adapter); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci /* fill tx descs related to addr & len */ 119362306a36Sopenharmony_ci if (vmxnet3_map_pkt(skb, &ctx, tq, adapter->pdev, adapter)) 119462306a36Sopenharmony_ci goto unlock_drop_pkt; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci /* setup the EOP desc */ 119762306a36Sopenharmony_ci ctx.eop_txd->dword[3] = cpu_to_le32(VMXNET3_TXD_CQ | VMXNET3_TXD_EOP); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci /* setup the SOP desc */ 120062306a36Sopenharmony_ci#ifdef __BIG_ENDIAN_BITFIELD 120162306a36Sopenharmony_ci gdesc = &tempTxDesc; 120262306a36Sopenharmony_ci gdesc->dword[2] = ctx.sop_txd->dword[2]; 120362306a36Sopenharmony_ci gdesc->dword[3] = ctx.sop_txd->dword[3]; 120462306a36Sopenharmony_ci#else 120562306a36Sopenharmony_ci gdesc = ctx.sop_txd; 120662306a36Sopenharmony_ci#endif 120762306a36Sopenharmony_ci tx_num_deferred = le32_to_cpu(tq->shared->txNumDeferred); 120862306a36Sopenharmony_ci if (ctx.mss) { 120962306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_4(adapter) && skb->encapsulation) { 121062306a36Sopenharmony_ci gdesc->txd.hlen = ctx.l4_offset + ctx.l4_hdr_size; 121162306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_7(adapter)) { 121262306a36Sopenharmony_ci gdesc->txd.om = VMXNET3_OM_TSO; 121362306a36Sopenharmony_ci gdesc->txd.ext1 = 1; 121462306a36Sopenharmony_ci } else { 121562306a36Sopenharmony_ci gdesc->txd.om = VMXNET3_OM_ENCAP; 121662306a36Sopenharmony_ci } 121762306a36Sopenharmony_ci gdesc->txd.msscof = ctx.mss; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM) 122062306a36Sopenharmony_ci gdesc->txd.oco = 1; 122162306a36Sopenharmony_ci } else { 122262306a36Sopenharmony_ci gdesc->txd.hlen = ctx.l4_offset + ctx.l4_hdr_size; 122362306a36Sopenharmony_ci gdesc->txd.om = VMXNET3_OM_TSO; 122462306a36Sopenharmony_ci gdesc->txd.msscof = ctx.mss; 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci num_pkts = (skb->len - gdesc->txd.hlen + ctx.mss - 1) / ctx.mss; 122762306a36Sopenharmony_ci } else { 122862306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) { 122962306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_4(adapter) && 123062306a36Sopenharmony_ci skb->encapsulation) { 123162306a36Sopenharmony_ci gdesc->txd.hlen = ctx.l4_offset + 123262306a36Sopenharmony_ci ctx.l4_hdr_size; 123362306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_7(adapter)) { 123462306a36Sopenharmony_ci gdesc->txd.om = VMXNET3_OM_CSUM; 123562306a36Sopenharmony_ci gdesc->txd.msscof = ctx.l4_offset + 123662306a36Sopenharmony_ci skb->csum_offset; 123762306a36Sopenharmony_ci gdesc->txd.ext1 = 1; 123862306a36Sopenharmony_ci } else { 123962306a36Sopenharmony_ci gdesc->txd.om = VMXNET3_OM_ENCAP; 124062306a36Sopenharmony_ci gdesc->txd.msscof = 0; /* Reserved */ 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci } else { 124362306a36Sopenharmony_ci gdesc->txd.hlen = ctx.l4_offset; 124462306a36Sopenharmony_ci gdesc->txd.om = VMXNET3_OM_CSUM; 124562306a36Sopenharmony_ci gdesc->txd.msscof = ctx.l4_offset + 124662306a36Sopenharmony_ci skb->csum_offset; 124762306a36Sopenharmony_ci } 124862306a36Sopenharmony_ci } else { 124962306a36Sopenharmony_ci gdesc->txd.om = 0; 125062306a36Sopenharmony_ci gdesc->txd.msscof = 0; 125162306a36Sopenharmony_ci } 125262306a36Sopenharmony_ci num_pkts = 1; 125362306a36Sopenharmony_ci } 125462306a36Sopenharmony_ci le32_add_cpu(&tq->shared->txNumDeferred, num_pkts); 125562306a36Sopenharmony_ci tx_num_deferred += num_pkts; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 125862306a36Sopenharmony_ci gdesc->txd.ti = 1; 125962306a36Sopenharmony_ci gdesc->txd.tci = skb_vlan_tag_get(skb); 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci /* Ensure that the write to (&gdesc->txd)->gen will be observed after 126362306a36Sopenharmony_ci * all other writes to &gdesc->txd. 126462306a36Sopenharmony_ci */ 126562306a36Sopenharmony_ci dma_wmb(); 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci /* finally flips the GEN bit of the SOP desc. */ 126862306a36Sopenharmony_ci gdesc->dword[2] = cpu_to_le32(le32_to_cpu(gdesc->dword[2]) ^ 126962306a36Sopenharmony_ci VMXNET3_TXD_GEN); 127062306a36Sopenharmony_ci#ifdef __BIG_ENDIAN_BITFIELD 127162306a36Sopenharmony_ci /* Finished updating in bitfields of Tx Desc, so write them in original 127262306a36Sopenharmony_ci * place. 127362306a36Sopenharmony_ci */ 127462306a36Sopenharmony_ci vmxnet3_TxDescToLe((struct Vmxnet3_TxDesc *)gdesc, 127562306a36Sopenharmony_ci (struct Vmxnet3_TxDesc *)ctx.sop_txd); 127662306a36Sopenharmony_ci gdesc = ctx.sop_txd; 127762306a36Sopenharmony_ci#endif 127862306a36Sopenharmony_ci netdev_dbg(adapter->netdev, 127962306a36Sopenharmony_ci "txd[%u]: SOP 0x%Lx 0x%x 0x%x\n", 128062306a36Sopenharmony_ci (u32)(ctx.sop_txd - 128162306a36Sopenharmony_ci tq->tx_ring.base), le64_to_cpu(gdesc->txd.addr), 128262306a36Sopenharmony_ci le32_to_cpu(gdesc->dword[2]), le32_to_cpu(gdesc->dword[3])); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci spin_unlock_irqrestore(&tq->tx_lock, flags); 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci if (tx_num_deferred >= le32_to_cpu(tq->shared->txThreshold)) { 128762306a36Sopenharmony_ci tq->shared->txNumDeferred = 0; 128862306a36Sopenharmony_ci VMXNET3_WRITE_BAR0_REG(adapter, 128962306a36Sopenharmony_ci adapter->tx_prod_offset + tq->qid * 8, 129062306a36Sopenharmony_ci tq->tx_ring.next2fill); 129162306a36Sopenharmony_ci } 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci return NETDEV_TX_OK; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ciunlock_drop_pkt: 129662306a36Sopenharmony_ci spin_unlock_irqrestore(&tq->tx_lock, flags); 129762306a36Sopenharmony_cidrop_pkt: 129862306a36Sopenharmony_ci tq->stats.drop_total++; 129962306a36Sopenharmony_ci dev_kfree_skb_any(skb); 130062306a36Sopenharmony_ci return NETDEV_TX_OK; 130162306a36Sopenharmony_ci} 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_cistatic int 130462306a36Sopenharmony_civmxnet3_create_pp(struct vmxnet3_adapter *adapter, 130562306a36Sopenharmony_ci struct vmxnet3_rx_queue *rq, int size) 130662306a36Sopenharmony_ci{ 130762306a36Sopenharmony_ci bool xdp_prog = vmxnet3_xdp_enabled(adapter); 130862306a36Sopenharmony_ci const struct page_pool_params pp_params = { 130962306a36Sopenharmony_ci .order = 0, 131062306a36Sopenharmony_ci .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV, 131162306a36Sopenharmony_ci .pool_size = size, 131262306a36Sopenharmony_ci .nid = NUMA_NO_NODE, 131362306a36Sopenharmony_ci .dev = &adapter->pdev->dev, 131462306a36Sopenharmony_ci .offset = VMXNET3_XDP_RX_OFFSET, 131562306a36Sopenharmony_ci .max_len = VMXNET3_XDP_MAX_FRSIZE, 131662306a36Sopenharmony_ci .dma_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE, 131762306a36Sopenharmony_ci }; 131862306a36Sopenharmony_ci struct page_pool *pp; 131962306a36Sopenharmony_ci int err; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci pp = page_pool_create(&pp_params); 132262306a36Sopenharmony_ci if (IS_ERR(pp)) 132362306a36Sopenharmony_ci return PTR_ERR(pp); 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci err = xdp_rxq_info_reg(&rq->xdp_rxq, adapter->netdev, rq->qid, 132662306a36Sopenharmony_ci rq->napi.napi_id); 132762306a36Sopenharmony_ci if (err < 0) 132862306a36Sopenharmony_ci goto err_free_pp; 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci err = xdp_rxq_info_reg_mem_model(&rq->xdp_rxq, MEM_TYPE_PAGE_POOL, pp); 133162306a36Sopenharmony_ci if (err) 133262306a36Sopenharmony_ci goto err_unregister_rxq; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci rq->page_pool = pp; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci return 0; 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_cierr_unregister_rxq: 133962306a36Sopenharmony_ci xdp_rxq_info_unreg(&rq->xdp_rxq); 134062306a36Sopenharmony_cierr_free_pp: 134162306a36Sopenharmony_ci page_pool_destroy(pp); 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci return err; 134462306a36Sopenharmony_ci} 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_civoid * 134762306a36Sopenharmony_civmxnet3_pp_get_buff(struct page_pool *pp, dma_addr_t *dma_addr, 134862306a36Sopenharmony_ci gfp_t gfp_mask) 134962306a36Sopenharmony_ci{ 135062306a36Sopenharmony_ci struct page *page; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci page = page_pool_alloc_pages(pp, gfp_mask | __GFP_NOWARN); 135362306a36Sopenharmony_ci if (unlikely(!page)) 135462306a36Sopenharmony_ci return NULL; 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci *dma_addr = page_pool_get_dma_addr(page) + pp->p.offset; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci return page_address(page); 135962306a36Sopenharmony_ci} 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_cistatic netdev_tx_t 136262306a36Sopenharmony_civmxnet3_xmit_frame(struct sk_buff *skb, struct net_device *netdev) 136362306a36Sopenharmony_ci{ 136462306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci BUG_ON(skb->queue_mapping > adapter->num_tx_queues); 136762306a36Sopenharmony_ci return vmxnet3_tq_xmit(skb, 136862306a36Sopenharmony_ci &adapter->tx_queue[skb->queue_mapping], 136962306a36Sopenharmony_ci adapter, netdev); 137062306a36Sopenharmony_ci} 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_cistatic void 137462306a36Sopenharmony_civmxnet3_rx_csum(struct vmxnet3_adapter *adapter, 137562306a36Sopenharmony_ci struct sk_buff *skb, 137662306a36Sopenharmony_ci union Vmxnet3_GenericDesc *gdesc) 137762306a36Sopenharmony_ci{ 137862306a36Sopenharmony_ci if (!gdesc->rcd.cnc && adapter->netdev->features & NETIF_F_RXCSUM) { 137962306a36Sopenharmony_ci if (gdesc->rcd.v4 && 138062306a36Sopenharmony_ci (le32_to_cpu(gdesc->dword[3]) & 138162306a36Sopenharmony_ci VMXNET3_RCD_CSUM_OK) == VMXNET3_RCD_CSUM_OK) { 138262306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 138362306a36Sopenharmony_ci if ((le32_to_cpu(gdesc->dword[0]) & 138462306a36Sopenharmony_ci (1UL << VMXNET3_RCD_HDR_INNER_SHIFT))) { 138562306a36Sopenharmony_ci skb->csum_level = 1; 138662306a36Sopenharmony_ci } 138762306a36Sopenharmony_ci WARN_ON_ONCE(!(gdesc->rcd.tcp || gdesc->rcd.udp) && 138862306a36Sopenharmony_ci !(le32_to_cpu(gdesc->dword[0]) & 138962306a36Sopenharmony_ci (1UL << VMXNET3_RCD_HDR_INNER_SHIFT))); 139062306a36Sopenharmony_ci WARN_ON_ONCE(gdesc->rcd.frg && 139162306a36Sopenharmony_ci !(le32_to_cpu(gdesc->dword[0]) & 139262306a36Sopenharmony_ci (1UL << VMXNET3_RCD_HDR_INNER_SHIFT))); 139362306a36Sopenharmony_ci } else if (gdesc->rcd.v6 && (le32_to_cpu(gdesc->dword[3]) & 139462306a36Sopenharmony_ci (1 << VMXNET3_RCD_TUC_SHIFT))) { 139562306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 139662306a36Sopenharmony_ci if ((le32_to_cpu(gdesc->dword[0]) & 139762306a36Sopenharmony_ci (1UL << VMXNET3_RCD_HDR_INNER_SHIFT))) { 139862306a36Sopenharmony_ci skb->csum_level = 1; 139962306a36Sopenharmony_ci } 140062306a36Sopenharmony_ci WARN_ON_ONCE(!(gdesc->rcd.tcp || gdesc->rcd.udp) && 140162306a36Sopenharmony_ci !(le32_to_cpu(gdesc->dword[0]) & 140262306a36Sopenharmony_ci (1UL << VMXNET3_RCD_HDR_INNER_SHIFT))); 140362306a36Sopenharmony_ci WARN_ON_ONCE(gdesc->rcd.frg && 140462306a36Sopenharmony_ci !(le32_to_cpu(gdesc->dword[0]) & 140562306a36Sopenharmony_ci (1UL << VMXNET3_RCD_HDR_INNER_SHIFT))); 140662306a36Sopenharmony_ci } else { 140762306a36Sopenharmony_ci if (gdesc->rcd.csum) { 140862306a36Sopenharmony_ci skb->csum = htons(gdesc->rcd.csum); 140962306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_PARTIAL; 141062306a36Sopenharmony_ci } else { 141162306a36Sopenharmony_ci skb_checksum_none_assert(skb); 141262306a36Sopenharmony_ci } 141362306a36Sopenharmony_ci } 141462306a36Sopenharmony_ci } else { 141562306a36Sopenharmony_ci skb_checksum_none_assert(skb); 141662306a36Sopenharmony_ci } 141762306a36Sopenharmony_ci} 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_cistatic void 142162306a36Sopenharmony_civmxnet3_rx_error(struct vmxnet3_rx_queue *rq, struct Vmxnet3_RxCompDesc *rcd, 142262306a36Sopenharmony_ci struct vmxnet3_rx_ctx *ctx, struct vmxnet3_adapter *adapter) 142362306a36Sopenharmony_ci{ 142462306a36Sopenharmony_ci rq->stats.drop_err++; 142562306a36Sopenharmony_ci if (!rcd->fcs) 142662306a36Sopenharmony_ci rq->stats.drop_fcs++; 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci rq->stats.drop_total++; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci /* 143162306a36Sopenharmony_ci * We do not unmap and chain the rx buffer to the skb. 143262306a36Sopenharmony_ci * We basically pretend this buffer is not used and will be recycled 143362306a36Sopenharmony_ci * by vmxnet3_rq_alloc_rx_buf() 143462306a36Sopenharmony_ci */ 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci /* 143762306a36Sopenharmony_ci * ctx->skb may be NULL if this is the first and the only one 143862306a36Sopenharmony_ci * desc for the pkt 143962306a36Sopenharmony_ci */ 144062306a36Sopenharmony_ci if (ctx->skb) 144162306a36Sopenharmony_ci dev_kfree_skb_irq(ctx->skb); 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci ctx->skb = NULL; 144462306a36Sopenharmony_ci} 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_cistatic u32 144862306a36Sopenharmony_civmxnet3_get_hdr_len(struct vmxnet3_adapter *adapter, struct sk_buff *skb, 144962306a36Sopenharmony_ci union Vmxnet3_GenericDesc *gdesc) 145062306a36Sopenharmony_ci{ 145162306a36Sopenharmony_ci u32 hlen, maplen; 145262306a36Sopenharmony_ci union { 145362306a36Sopenharmony_ci void *ptr; 145462306a36Sopenharmony_ci struct ethhdr *eth; 145562306a36Sopenharmony_ci struct vlan_ethhdr *veth; 145662306a36Sopenharmony_ci struct iphdr *ipv4; 145762306a36Sopenharmony_ci struct ipv6hdr *ipv6; 145862306a36Sopenharmony_ci struct tcphdr *tcp; 145962306a36Sopenharmony_ci } hdr; 146062306a36Sopenharmony_ci BUG_ON(gdesc->rcd.tcp == 0); 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci maplen = skb_headlen(skb); 146362306a36Sopenharmony_ci if (unlikely(sizeof(struct iphdr) + sizeof(struct tcphdr) > maplen)) 146462306a36Sopenharmony_ci return 0; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci if (skb->protocol == cpu_to_be16(ETH_P_8021Q) || 146762306a36Sopenharmony_ci skb->protocol == cpu_to_be16(ETH_P_8021AD)) 146862306a36Sopenharmony_ci hlen = sizeof(struct vlan_ethhdr); 146962306a36Sopenharmony_ci else 147062306a36Sopenharmony_ci hlen = sizeof(struct ethhdr); 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci hdr.eth = eth_hdr(skb); 147362306a36Sopenharmony_ci if (gdesc->rcd.v4) { 147462306a36Sopenharmony_ci BUG_ON(hdr.eth->h_proto != htons(ETH_P_IP) && 147562306a36Sopenharmony_ci hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IP)); 147662306a36Sopenharmony_ci hdr.ptr += hlen; 147762306a36Sopenharmony_ci BUG_ON(hdr.ipv4->protocol != IPPROTO_TCP); 147862306a36Sopenharmony_ci hlen = hdr.ipv4->ihl << 2; 147962306a36Sopenharmony_ci hdr.ptr += hdr.ipv4->ihl << 2; 148062306a36Sopenharmony_ci } else if (gdesc->rcd.v6) { 148162306a36Sopenharmony_ci BUG_ON(hdr.eth->h_proto != htons(ETH_P_IPV6) && 148262306a36Sopenharmony_ci hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IPV6)); 148362306a36Sopenharmony_ci hdr.ptr += hlen; 148462306a36Sopenharmony_ci /* Use an estimated value, since we also need to handle 148562306a36Sopenharmony_ci * TSO case. 148662306a36Sopenharmony_ci */ 148762306a36Sopenharmony_ci if (hdr.ipv6->nexthdr != IPPROTO_TCP) 148862306a36Sopenharmony_ci return sizeof(struct ipv6hdr) + sizeof(struct tcphdr); 148962306a36Sopenharmony_ci hlen = sizeof(struct ipv6hdr); 149062306a36Sopenharmony_ci hdr.ptr += sizeof(struct ipv6hdr); 149162306a36Sopenharmony_ci } else { 149262306a36Sopenharmony_ci /* Non-IP pkt, dont estimate header length */ 149362306a36Sopenharmony_ci return 0; 149462306a36Sopenharmony_ci } 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci if (hlen + sizeof(struct tcphdr) > maplen) 149762306a36Sopenharmony_ci return 0; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci return (hlen + (hdr.tcp->doff << 2)); 150062306a36Sopenharmony_ci} 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_cistatic int 150362306a36Sopenharmony_civmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq, 150462306a36Sopenharmony_ci struct vmxnet3_adapter *adapter, int quota) 150562306a36Sopenharmony_ci{ 150662306a36Sopenharmony_ci u32 rxprod_reg[2] = { 150762306a36Sopenharmony_ci adapter->rx_prod_offset, adapter->rx_prod2_offset 150862306a36Sopenharmony_ci }; 150962306a36Sopenharmony_ci u32 num_pkts = 0; 151062306a36Sopenharmony_ci bool skip_page_frags = false; 151162306a36Sopenharmony_ci bool encap_lro = false; 151262306a36Sopenharmony_ci struct Vmxnet3_RxCompDesc *rcd; 151362306a36Sopenharmony_ci struct vmxnet3_rx_ctx *ctx = &rq->rx_ctx; 151462306a36Sopenharmony_ci u16 segCnt = 0, mss = 0; 151562306a36Sopenharmony_ci int comp_offset, fill_offset; 151662306a36Sopenharmony_ci#ifdef __BIG_ENDIAN_BITFIELD 151762306a36Sopenharmony_ci struct Vmxnet3_RxDesc rxCmdDesc; 151862306a36Sopenharmony_ci struct Vmxnet3_RxCompDesc rxComp; 151962306a36Sopenharmony_ci#endif 152062306a36Sopenharmony_ci bool need_flush = false; 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci vmxnet3_getRxComp(rcd, &rq->comp_ring.base[rq->comp_ring.next2proc].rcd, 152362306a36Sopenharmony_ci &rxComp); 152462306a36Sopenharmony_ci while (rcd->gen == rq->comp_ring.gen) { 152562306a36Sopenharmony_ci struct vmxnet3_rx_buf_info *rbi; 152662306a36Sopenharmony_ci struct sk_buff *skb, *new_skb = NULL; 152762306a36Sopenharmony_ci struct page *new_page = NULL; 152862306a36Sopenharmony_ci dma_addr_t new_dma_addr; 152962306a36Sopenharmony_ci int num_to_alloc; 153062306a36Sopenharmony_ci struct Vmxnet3_RxDesc *rxd; 153162306a36Sopenharmony_ci u32 idx, ring_idx; 153262306a36Sopenharmony_ci struct vmxnet3_cmd_ring *ring = NULL; 153362306a36Sopenharmony_ci if (num_pkts >= quota) { 153462306a36Sopenharmony_ci /* we may stop even before we see the EOP desc of 153562306a36Sopenharmony_ci * the current pkt 153662306a36Sopenharmony_ci */ 153762306a36Sopenharmony_ci break; 153862306a36Sopenharmony_ci } 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci /* Prevent any rcd field from being (speculatively) read before 154162306a36Sopenharmony_ci * rcd->gen is read. 154262306a36Sopenharmony_ci */ 154362306a36Sopenharmony_ci dma_rmb(); 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci BUG_ON(rcd->rqID != rq->qid && rcd->rqID != rq->qid2 && 154662306a36Sopenharmony_ci rcd->rqID != rq->dataRingQid); 154762306a36Sopenharmony_ci idx = rcd->rxdIdx; 154862306a36Sopenharmony_ci ring_idx = VMXNET3_GET_RING_IDX(adapter, rcd->rqID); 154962306a36Sopenharmony_ci ring = rq->rx_ring + ring_idx; 155062306a36Sopenharmony_ci vmxnet3_getRxDesc(rxd, &rq->rx_ring[ring_idx].base[idx].rxd, 155162306a36Sopenharmony_ci &rxCmdDesc); 155262306a36Sopenharmony_ci rbi = rq->buf_info[ring_idx] + idx; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci BUG_ON(rxd->addr != rbi->dma_addr || 155562306a36Sopenharmony_ci rxd->len != rbi->len); 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci if (unlikely(rcd->eop && rcd->err)) { 155862306a36Sopenharmony_ci vmxnet3_rx_error(rq, rcd, ctx, adapter); 155962306a36Sopenharmony_ci goto rcd_done; 156062306a36Sopenharmony_ci } 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci if (rcd->sop && rcd->eop && vmxnet3_xdp_enabled(adapter)) { 156362306a36Sopenharmony_ci struct sk_buff *skb_xdp_pass; 156462306a36Sopenharmony_ci int act; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci if (VMXNET3_RX_DATA_RING(adapter, rcd->rqID)) { 156762306a36Sopenharmony_ci ctx->skb = NULL; 156862306a36Sopenharmony_ci goto skip_xdp; /* Handle it later. */ 156962306a36Sopenharmony_ci } 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci if (rbi->buf_type != VMXNET3_RX_BUF_XDP) 157262306a36Sopenharmony_ci goto rcd_done; 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci act = vmxnet3_process_xdp(adapter, rq, rcd, rbi, rxd, 157562306a36Sopenharmony_ci &skb_xdp_pass); 157662306a36Sopenharmony_ci if (act == XDP_PASS) { 157762306a36Sopenharmony_ci ctx->skb = skb_xdp_pass; 157862306a36Sopenharmony_ci goto sop_done; 157962306a36Sopenharmony_ci } 158062306a36Sopenharmony_ci ctx->skb = NULL; 158162306a36Sopenharmony_ci need_flush |= act == XDP_REDIRECT; 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci goto rcd_done; 158462306a36Sopenharmony_ci } 158562306a36Sopenharmony_ciskip_xdp: 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci if (rcd->sop) { /* first buf of the pkt */ 158862306a36Sopenharmony_ci bool rxDataRingUsed; 158962306a36Sopenharmony_ci u16 len; 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci BUG_ON(rxd->btype != VMXNET3_RXD_BTYPE_HEAD || 159262306a36Sopenharmony_ci (rcd->rqID != rq->qid && 159362306a36Sopenharmony_ci rcd->rqID != rq->dataRingQid)); 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci BUG_ON(rbi->buf_type != VMXNET3_RX_BUF_SKB && 159662306a36Sopenharmony_ci rbi->buf_type != VMXNET3_RX_BUF_XDP); 159762306a36Sopenharmony_ci BUG_ON(ctx->skb != NULL || rbi->skb == NULL); 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci if (unlikely(rcd->len == 0)) { 160062306a36Sopenharmony_ci /* Pretend the rx buffer is skipped. */ 160162306a36Sopenharmony_ci BUG_ON(!(rcd->sop && rcd->eop)); 160262306a36Sopenharmony_ci netdev_dbg(adapter->netdev, 160362306a36Sopenharmony_ci "rxRing[%u][%u] 0 length\n", 160462306a36Sopenharmony_ci ring_idx, idx); 160562306a36Sopenharmony_ci goto rcd_done; 160662306a36Sopenharmony_ci } 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci skip_page_frags = false; 160962306a36Sopenharmony_ci ctx->skb = rbi->skb; 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci rxDataRingUsed = 161262306a36Sopenharmony_ci VMXNET3_RX_DATA_RING(adapter, rcd->rqID); 161362306a36Sopenharmony_ci len = rxDataRingUsed ? rcd->len : rbi->len; 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci if (rxDataRingUsed && vmxnet3_xdp_enabled(adapter)) { 161662306a36Sopenharmony_ci struct sk_buff *skb_xdp_pass; 161762306a36Sopenharmony_ci size_t sz; 161862306a36Sopenharmony_ci int act; 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci sz = rcd->rxdIdx * rq->data_ring.desc_size; 162162306a36Sopenharmony_ci act = vmxnet3_process_xdp_small(adapter, rq, 162262306a36Sopenharmony_ci &rq->data_ring.base[sz], 162362306a36Sopenharmony_ci rcd->len, 162462306a36Sopenharmony_ci &skb_xdp_pass); 162562306a36Sopenharmony_ci if (act == XDP_PASS) { 162662306a36Sopenharmony_ci ctx->skb = skb_xdp_pass; 162762306a36Sopenharmony_ci goto sop_done; 162862306a36Sopenharmony_ci } 162962306a36Sopenharmony_ci need_flush |= act == XDP_REDIRECT; 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci goto rcd_done; 163262306a36Sopenharmony_ci } 163362306a36Sopenharmony_ci new_skb = netdev_alloc_skb_ip_align(adapter->netdev, 163462306a36Sopenharmony_ci len); 163562306a36Sopenharmony_ci if (new_skb == NULL) { 163662306a36Sopenharmony_ci /* Skb allocation failed, do not handover this 163762306a36Sopenharmony_ci * skb to stack. Reuse it. Drop the existing pkt 163862306a36Sopenharmony_ci */ 163962306a36Sopenharmony_ci rq->stats.rx_buf_alloc_failure++; 164062306a36Sopenharmony_ci ctx->skb = NULL; 164162306a36Sopenharmony_ci rq->stats.drop_total++; 164262306a36Sopenharmony_ci skip_page_frags = true; 164362306a36Sopenharmony_ci goto rcd_done; 164462306a36Sopenharmony_ci } 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci if (rxDataRingUsed && adapter->rxdataring_enabled) { 164762306a36Sopenharmony_ci size_t sz; 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci BUG_ON(rcd->len > rq->data_ring.desc_size); 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci ctx->skb = new_skb; 165262306a36Sopenharmony_ci sz = rcd->rxdIdx * rq->data_ring.desc_size; 165362306a36Sopenharmony_ci memcpy(new_skb->data, 165462306a36Sopenharmony_ci &rq->data_ring.base[sz], rcd->len); 165562306a36Sopenharmony_ci } else { 165662306a36Sopenharmony_ci ctx->skb = rbi->skb; 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci new_dma_addr = 165962306a36Sopenharmony_ci dma_map_single(&adapter->pdev->dev, 166062306a36Sopenharmony_ci new_skb->data, rbi->len, 166162306a36Sopenharmony_ci DMA_FROM_DEVICE); 166262306a36Sopenharmony_ci if (dma_mapping_error(&adapter->pdev->dev, 166362306a36Sopenharmony_ci new_dma_addr)) { 166462306a36Sopenharmony_ci dev_kfree_skb(new_skb); 166562306a36Sopenharmony_ci /* Skb allocation failed, do not 166662306a36Sopenharmony_ci * handover this skb to stack. Reuse 166762306a36Sopenharmony_ci * it. Drop the existing pkt. 166862306a36Sopenharmony_ci */ 166962306a36Sopenharmony_ci rq->stats.rx_buf_alloc_failure++; 167062306a36Sopenharmony_ci ctx->skb = NULL; 167162306a36Sopenharmony_ci rq->stats.drop_total++; 167262306a36Sopenharmony_ci skip_page_frags = true; 167362306a36Sopenharmony_ci goto rcd_done; 167462306a36Sopenharmony_ci } 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci dma_unmap_single(&adapter->pdev->dev, 167762306a36Sopenharmony_ci rbi->dma_addr, 167862306a36Sopenharmony_ci rbi->len, 167962306a36Sopenharmony_ci DMA_FROM_DEVICE); 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci /* Immediate refill */ 168262306a36Sopenharmony_ci rbi->skb = new_skb; 168362306a36Sopenharmony_ci rbi->dma_addr = new_dma_addr; 168462306a36Sopenharmony_ci rxd->addr = cpu_to_le64(rbi->dma_addr); 168562306a36Sopenharmony_ci rxd->len = rbi->len; 168662306a36Sopenharmony_ci } 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci skb_record_rx_queue(ctx->skb, rq->qid); 168962306a36Sopenharmony_ci skb_put(ctx->skb, rcd->len); 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_2(adapter) && 169262306a36Sopenharmony_ci rcd->type == VMXNET3_CDTYPE_RXCOMP_LRO) { 169362306a36Sopenharmony_ci struct Vmxnet3_RxCompDescExt *rcdlro; 169462306a36Sopenharmony_ci union Vmxnet3_GenericDesc *gdesc; 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci rcdlro = (struct Vmxnet3_RxCompDescExt *)rcd; 169762306a36Sopenharmony_ci gdesc = (union Vmxnet3_GenericDesc *)rcd; 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci segCnt = rcdlro->segCnt; 170062306a36Sopenharmony_ci WARN_ON_ONCE(segCnt == 0); 170162306a36Sopenharmony_ci mss = rcdlro->mss; 170262306a36Sopenharmony_ci if (unlikely(segCnt <= 1)) 170362306a36Sopenharmony_ci segCnt = 0; 170462306a36Sopenharmony_ci encap_lro = (le32_to_cpu(gdesc->dword[0]) & 170562306a36Sopenharmony_ci (1UL << VMXNET3_RCD_HDR_INNER_SHIFT)); 170662306a36Sopenharmony_ci } else { 170762306a36Sopenharmony_ci segCnt = 0; 170862306a36Sopenharmony_ci } 170962306a36Sopenharmony_ci } else { 171062306a36Sopenharmony_ci BUG_ON(ctx->skb == NULL && !skip_page_frags); 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci /* non SOP buffer must be type 1 in most cases */ 171362306a36Sopenharmony_ci BUG_ON(rbi->buf_type != VMXNET3_RX_BUF_PAGE); 171462306a36Sopenharmony_ci BUG_ON(rxd->btype != VMXNET3_RXD_BTYPE_BODY); 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci /* If an sop buffer was dropped, skip all 171762306a36Sopenharmony_ci * following non-sop fragments. They will be reused. 171862306a36Sopenharmony_ci */ 171962306a36Sopenharmony_ci if (skip_page_frags) 172062306a36Sopenharmony_ci goto rcd_done; 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci if (rcd->len) { 172362306a36Sopenharmony_ci new_page = alloc_page(GFP_ATOMIC); 172462306a36Sopenharmony_ci /* Replacement page frag could not be allocated. 172562306a36Sopenharmony_ci * Reuse this page. Drop the pkt and free the 172662306a36Sopenharmony_ci * skb which contained this page as a frag. Skip 172762306a36Sopenharmony_ci * processing all the following non-sop frags. 172862306a36Sopenharmony_ci */ 172962306a36Sopenharmony_ci if (unlikely(!new_page)) { 173062306a36Sopenharmony_ci rq->stats.rx_buf_alloc_failure++; 173162306a36Sopenharmony_ci dev_kfree_skb(ctx->skb); 173262306a36Sopenharmony_ci ctx->skb = NULL; 173362306a36Sopenharmony_ci skip_page_frags = true; 173462306a36Sopenharmony_ci goto rcd_done; 173562306a36Sopenharmony_ci } 173662306a36Sopenharmony_ci new_dma_addr = dma_map_page(&adapter->pdev->dev, 173762306a36Sopenharmony_ci new_page, 173862306a36Sopenharmony_ci 0, PAGE_SIZE, 173962306a36Sopenharmony_ci DMA_FROM_DEVICE); 174062306a36Sopenharmony_ci if (dma_mapping_error(&adapter->pdev->dev, 174162306a36Sopenharmony_ci new_dma_addr)) { 174262306a36Sopenharmony_ci put_page(new_page); 174362306a36Sopenharmony_ci rq->stats.rx_buf_alloc_failure++; 174462306a36Sopenharmony_ci dev_kfree_skb(ctx->skb); 174562306a36Sopenharmony_ci ctx->skb = NULL; 174662306a36Sopenharmony_ci skip_page_frags = true; 174762306a36Sopenharmony_ci goto rcd_done; 174862306a36Sopenharmony_ci } 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci dma_unmap_page(&adapter->pdev->dev, 175162306a36Sopenharmony_ci rbi->dma_addr, rbi->len, 175262306a36Sopenharmony_ci DMA_FROM_DEVICE); 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci vmxnet3_append_frag(ctx->skb, rcd, rbi); 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci /* Immediate refill */ 175762306a36Sopenharmony_ci rbi->page = new_page; 175862306a36Sopenharmony_ci rbi->dma_addr = new_dma_addr; 175962306a36Sopenharmony_ci rxd->addr = cpu_to_le64(rbi->dma_addr); 176062306a36Sopenharmony_ci rxd->len = rbi->len; 176162306a36Sopenharmony_ci } 176262306a36Sopenharmony_ci } 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_cisop_done: 176662306a36Sopenharmony_ci skb = ctx->skb; 176762306a36Sopenharmony_ci if (rcd->eop) { 176862306a36Sopenharmony_ci u32 mtu = adapter->netdev->mtu; 176962306a36Sopenharmony_ci skb->len += skb->data_len; 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci#ifdef VMXNET3_RSS 177262306a36Sopenharmony_ci if (rcd->rssType != VMXNET3_RCD_RSS_TYPE_NONE && 177362306a36Sopenharmony_ci (adapter->netdev->features & NETIF_F_RXHASH)) { 177462306a36Sopenharmony_ci enum pkt_hash_types hash_type; 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci switch (rcd->rssType) { 177762306a36Sopenharmony_ci case VMXNET3_RCD_RSS_TYPE_IPV4: 177862306a36Sopenharmony_ci case VMXNET3_RCD_RSS_TYPE_IPV6: 177962306a36Sopenharmony_ci hash_type = PKT_HASH_TYPE_L3; 178062306a36Sopenharmony_ci break; 178162306a36Sopenharmony_ci case VMXNET3_RCD_RSS_TYPE_TCPIPV4: 178262306a36Sopenharmony_ci case VMXNET3_RCD_RSS_TYPE_TCPIPV6: 178362306a36Sopenharmony_ci case VMXNET3_RCD_RSS_TYPE_UDPIPV4: 178462306a36Sopenharmony_ci case VMXNET3_RCD_RSS_TYPE_UDPIPV6: 178562306a36Sopenharmony_ci hash_type = PKT_HASH_TYPE_L4; 178662306a36Sopenharmony_ci break; 178762306a36Sopenharmony_ci default: 178862306a36Sopenharmony_ci hash_type = PKT_HASH_TYPE_L3; 178962306a36Sopenharmony_ci break; 179062306a36Sopenharmony_ci } 179162306a36Sopenharmony_ci skb_set_hash(skb, 179262306a36Sopenharmony_ci le32_to_cpu(rcd->rssHash), 179362306a36Sopenharmony_ci hash_type); 179462306a36Sopenharmony_ci } 179562306a36Sopenharmony_ci#endif 179662306a36Sopenharmony_ci vmxnet3_rx_csum(adapter, skb, 179762306a36Sopenharmony_ci (union Vmxnet3_GenericDesc *)rcd); 179862306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, adapter->netdev); 179962306a36Sopenharmony_ci if ((!rcd->tcp && !encap_lro) || 180062306a36Sopenharmony_ci !(adapter->netdev->features & NETIF_F_LRO)) 180162306a36Sopenharmony_ci goto not_lro; 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci if (segCnt != 0 && mss != 0) { 180462306a36Sopenharmony_ci skb_shinfo(skb)->gso_type = rcd->v4 ? 180562306a36Sopenharmony_ci SKB_GSO_TCPV4 : SKB_GSO_TCPV6; 180662306a36Sopenharmony_ci skb_shinfo(skb)->gso_size = mss; 180762306a36Sopenharmony_ci skb_shinfo(skb)->gso_segs = segCnt; 180862306a36Sopenharmony_ci } else if ((segCnt != 0 || skb->len > mtu) && !encap_lro) { 180962306a36Sopenharmony_ci u32 hlen; 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci hlen = vmxnet3_get_hdr_len(adapter, skb, 181262306a36Sopenharmony_ci (union Vmxnet3_GenericDesc *)rcd); 181362306a36Sopenharmony_ci if (hlen == 0) 181462306a36Sopenharmony_ci goto not_lro; 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci skb_shinfo(skb)->gso_type = 181762306a36Sopenharmony_ci rcd->v4 ? SKB_GSO_TCPV4 : SKB_GSO_TCPV6; 181862306a36Sopenharmony_ci if (segCnt != 0) { 181962306a36Sopenharmony_ci skb_shinfo(skb)->gso_segs = segCnt; 182062306a36Sopenharmony_ci skb_shinfo(skb)->gso_size = 182162306a36Sopenharmony_ci DIV_ROUND_UP(skb->len - 182262306a36Sopenharmony_ci hlen, segCnt); 182362306a36Sopenharmony_ci } else { 182462306a36Sopenharmony_ci skb_shinfo(skb)->gso_size = mtu - hlen; 182562306a36Sopenharmony_ci } 182662306a36Sopenharmony_ci } 182762306a36Sopenharmony_cinot_lro: 182862306a36Sopenharmony_ci if (unlikely(rcd->ts)) 182962306a36Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rcd->tci); 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci /* Use GRO callback if UPT is enabled */ 183262306a36Sopenharmony_ci if ((adapter->netdev->features & NETIF_F_LRO) && 183362306a36Sopenharmony_ci !rq->shared->updateRxProd) 183462306a36Sopenharmony_ci netif_receive_skb(skb); 183562306a36Sopenharmony_ci else 183662306a36Sopenharmony_ci napi_gro_receive(&rq->napi, skb); 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci ctx->skb = NULL; 183962306a36Sopenharmony_ci encap_lro = false; 184062306a36Sopenharmony_ci num_pkts++; 184162306a36Sopenharmony_ci } 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_circd_done: 184462306a36Sopenharmony_ci /* device may have skipped some rx descs */ 184562306a36Sopenharmony_ci ring = rq->rx_ring + ring_idx; 184662306a36Sopenharmony_ci rbi->comp_state = VMXNET3_RXD_COMP_DONE; 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci comp_offset = vmxnet3_cmd_ring_desc_avail(ring); 184962306a36Sopenharmony_ci fill_offset = (idx > ring->next2fill ? 0 : ring->size) + 185062306a36Sopenharmony_ci idx - ring->next2fill - 1; 185162306a36Sopenharmony_ci if (!ring->isOutOfOrder || fill_offset >= comp_offset) 185262306a36Sopenharmony_ci ring->next2comp = idx; 185362306a36Sopenharmony_ci num_to_alloc = vmxnet3_cmd_ring_desc_avail(ring); 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci /* Ensure that the writes to rxd->gen bits will be observed 185662306a36Sopenharmony_ci * after all other writes to rxd objects. 185762306a36Sopenharmony_ci */ 185862306a36Sopenharmony_ci dma_wmb(); 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci while (num_to_alloc) { 186162306a36Sopenharmony_ci rbi = rq->buf_info[ring_idx] + ring->next2fill; 186262306a36Sopenharmony_ci if (!(adapter->dev_caps[0] & (1UL << VMXNET3_CAP_OOORX_COMP))) 186362306a36Sopenharmony_ci goto refill_buf; 186462306a36Sopenharmony_ci if (ring_idx == 0) { 186562306a36Sopenharmony_ci /* ring0 Type1 buffers can get skipped; re-fill them */ 186662306a36Sopenharmony_ci if (rbi->buf_type != VMXNET3_RX_BUF_SKB) 186762306a36Sopenharmony_ci goto refill_buf; 186862306a36Sopenharmony_ci } 186962306a36Sopenharmony_ci if (rbi->comp_state == VMXNET3_RXD_COMP_DONE) { 187062306a36Sopenharmony_cirefill_buf: 187162306a36Sopenharmony_ci vmxnet3_getRxDesc(rxd, &ring->base[ring->next2fill].rxd, 187262306a36Sopenharmony_ci &rxCmdDesc); 187362306a36Sopenharmony_ci WARN_ON(!rxd->addr); 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci /* Recv desc is ready to be used by the device */ 187662306a36Sopenharmony_ci rxd->gen = ring->gen; 187762306a36Sopenharmony_ci vmxnet3_cmd_ring_adv_next2fill(ring); 187862306a36Sopenharmony_ci rbi->comp_state = VMXNET3_RXD_COMP_PENDING; 187962306a36Sopenharmony_ci num_to_alloc--; 188062306a36Sopenharmony_ci } else { 188162306a36Sopenharmony_ci /* rx completion hasn't occurred */ 188262306a36Sopenharmony_ci ring->isOutOfOrder = 1; 188362306a36Sopenharmony_ci break; 188462306a36Sopenharmony_ci } 188562306a36Sopenharmony_ci } 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ci if (num_to_alloc == 0) { 188862306a36Sopenharmony_ci ring->isOutOfOrder = 0; 188962306a36Sopenharmony_ci } 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci /* if needed, update the register */ 189262306a36Sopenharmony_ci if (unlikely(rq->shared->updateRxProd) && (ring->next2fill & 0xf) == 0) { 189362306a36Sopenharmony_ci VMXNET3_WRITE_BAR0_REG(adapter, 189462306a36Sopenharmony_ci rxprod_reg[ring_idx] + rq->qid * 8, 189562306a36Sopenharmony_ci ring->next2fill); 189662306a36Sopenharmony_ci } 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci vmxnet3_comp_ring_adv_next2proc(&rq->comp_ring); 189962306a36Sopenharmony_ci vmxnet3_getRxComp(rcd, 190062306a36Sopenharmony_ci &rq->comp_ring.base[rq->comp_ring.next2proc].rcd, &rxComp); 190162306a36Sopenharmony_ci } 190262306a36Sopenharmony_ci if (need_flush) 190362306a36Sopenharmony_ci xdp_do_flush(); 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci return num_pkts; 190662306a36Sopenharmony_ci} 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_cistatic void 191062306a36Sopenharmony_civmxnet3_rq_cleanup(struct vmxnet3_rx_queue *rq, 191162306a36Sopenharmony_ci struct vmxnet3_adapter *adapter) 191262306a36Sopenharmony_ci{ 191362306a36Sopenharmony_ci u32 i, ring_idx; 191462306a36Sopenharmony_ci struct Vmxnet3_RxDesc *rxd; 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci /* ring has already been cleaned up */ 191762306a36Sopenharmony_ci if (!rq->rx_ring[0].base) 191862306a36Sopenharmony_ci return; 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci for (ring_idx = 0; ring_idx < 2; ring_idx++) { 192162306a36Sopenharmony_ci for (i = 0; i < rq->rx_ring[ring_idx].size; i++) { 192262306a36Sopenharmony_ci struct vmxnet3_rx_buf_info *rbi; 192362306a36Sopenharmony_ci#ifdef __BIG_ENDIAN_BITFIELD 192462306a36Sopenharmony_ci struct Vmxnet3_RxDesc rxDesc; 192562306a36Sopenharmony_ci#endif 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci rbi = &rq->buf_info[ring_idx][i]; 192862306a36Sopenharmony_ci vmxnet3_getRxDesc(rxd, 192962306a36Sopenharmony_ci &rq->rx_ring[ring_idx].base[i].rxd, &rxDesc); 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci if (rxd->btype == VMXNET3_RXD_BTYPE_HEAD && 193262306a36Sopenharmony_ci rbi->page && rbi->buf_type == VMXNET3_RX_BUF_XDP) { 193362306a36Sopenharmony_ci page_pool_recycle_direct(rq->page_pool, 193462306a36Sopenharmony_ci rbi->page); 193562306a36Sopenharmony_ci rbi->page = NULL; 193662306a36Sopenharmony_ci } else if (rxd->btype == VMXNET3_RXD_BTYPE_HEAD && 193762306a36Sopenharmony_ci rbi->skb) { 193862306a36Sopenharmony_ci dma_unmap_single(&adapter->pdev->dev, rxd->addr, 193962306a36Sopenharmony_ci rxd->len, DMA_FROM_DEVICE); 194062306a36Sopenharmony_ci dev_kfree_skb(rbi->skb); 194162306a36Sopenharmony_ci rbi->skb = NULL; 194262306a36Sopenharmony_ci } else if (rxd->btype == VMXNET3_RXD_BTYPE_BODY && 194362306a36Sopenharmony_ci rbi->page) { 194462306a36Sopenharmony_ci dma_unmap_page(&adapter->pdev->dev, rxd->addr, 194562306a36Sopenharmony_ci rxd->len, DMA_FROM_DEVICE); 194662306a36Sopenharmony_ci put_page(rbi->page); 194762306a36Sopenharmony_ci rbi->page = NULL; 194862306a36Sopenharmony_ci } 194962306a36Sopenharmony_ci } 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci rq->rx_ring[ring_idx].gen = VMXNET3_INIT_GEN; 195262306a36Sopenharmony_ci rq->rx_ring[ring_idx].next2fill = 195362306a36Sopenharmony_ci rq->rx_ring[ring_idx].next2comp = 0; 195462306a36Sopenharmony_ci } 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci rq->comp_ring.gen = VMXNET3_INIT_GEN; 195762306a36Sopenharmony_ci rq->comp_ring.next2proc = 0; 195862306a36Sopenharmony_ci} 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_cistatic void 196262306a36Sopenharmony_civmxnet3_rq_cleanup_all(struct vmxnet3_adapter *adapter) 196362306a36Sopenharmony_ci{ 196462306a36Sopenharmony_ci int i; 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) 196762306a36Sopenharmony_ci vmxnet3_rq_cleanup(&adapter->rx_queue[i], adapter); 196862306a36Sopenharmony_ci rcu_assign_pointer(adapter->xdp_bpf_prog, NULL); 196962306a36Sopenharmony_ci} 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_cistatic void vmxnet3_rq_destroy(struct vmxnet3_rx_queue *rq, 197362306a36Sopenharmony_ci struct vmxnet3_adapter *adapter) 197462306a36Sopenharmony_ci{ 197562306a36Sopenharmony_ci int i; 197662306a36Sopenharmony_ci int j; 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci /* all rx buffers must have already been freed */ 197962306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 198062306a36Sopenharmony_ci if (rq->buf_info[i]) { 198162306a36Sopenharmony_ci for (j = 0; j < rq->rx_ring[i].size; j++) 198262306a36Sopenharmony_ci BUG_ON(rq->buf_info[i][j].page != NULL); 198362306a36Sopenharmony_ci } 198462306a36Sopenharmony_ci } 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 198862306a36Sopenharmony_ci if (rq->rx_ring[i].base) { 198962306a36Sopenharmony_ci dma_free_coherent(&adapter->pdev->dev, 199062306a36Sopenharmony_ci rq->rx_ring[i].size 199162306a36Sopenharmony_ci * sizeof(struct Vmxnet3_RxDesc), 199262306a36Sopenharmony_ci rq->rx_ring[i].base, 199362306a36Sopenharmony_ci rq->rx_ring[i].basePA); 199462306a36Sopenharmony_ci rq->rx_ring[i].base = NULL; 199562306a36Sopenharmony_ci } 199662306a36Sopenharmony_ci } 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci if (xdp_rxq_info_is_reg(&rq->xdp_rxq)) 199962306a36Sopenharmony_ci xdp_rxq_info_unreg(&rq->xdp_rxq); 200062306a36Sopenharmony_ci page_pool_destroy(rq->page_pool); 200162306a36Sopenharmony_ci rq->page_pool = NULL; 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_ci if (rq->data_ring.base) { 200462306a36Sopenharmony_ci dma_free_coherent(&adapter->pdev->dev, 200562306a36Sopenharmony_ci rq->rx_ring[0].size * rq->data_ring.desc_size, 200662306a36Sopenharmony_ci rq->data_ring.base, rq->data_ring.basePA); 200762306a36Sopenharmony_ci rq->data_ring.base = NULL; 200862306a36Sopenharmony_ci } 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci if (rq->comp_ring.base) { 201162306a36Sopenharmony_ci dma_free_coherent(&adapter->pdev->dev, rq->comp_ring.size 201262306a36Sopenharmony_ci * sizeof(struct Vmxnet3_RxCompDesc), 201362306a36Sopenharmony_ci rq->comp_ring.base, rq->comp_ring.basePA); 201462306a36Sopenharmony_ci rq->comp_ring.base = NULL; 201562306a36Sopenharmony_ci } 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci kfree(rq->buf_info[0]); 201862306a36Sopenharmony_ci rq->buf_info[0] = NULL; 201962306a36Sopenharmony_ci rq->buf_info[1] = NULL; 202062306a36Sopenharmony_ci} 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_cistatic void 202362306a36Sopenharmony_civmxnet3_rq_destroy_all_rxdataring(struct vmxnet3_adapter *adapter) 202462306a36Sopenharmony_ci{ 202562306a36Sopenharmony_ci int i; 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) { 202862306a36Sopenharmony_ci struct vmxnet3_rx_queue *rq = &adapter->rx_queue[i]; 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ci if (rq->data_ring.base) { 203162306a36Sopenharmony_ci dma_free_coherent(&adapter->pdev->dev, 203262306a36Sopenharmony_ci (rq->rx_ring[0].size * 203362306a36Sopenharmony_ci rq->data_ring.desc_size), 203462306a36Sopenharmony_ci rq->data_ring.base, 203562306a36Sopenharmony_ci rq->data_ring.basePA); 203662306a36Sopenharmony_ci rq->data_ring.base = NULL; 203762306a36Sopenharmony_ci rq->data_ring.desc_size = 0; 203862306a36Sopenharmony_ci } 203962306a36Sopenharmony_ci } 204062306a36Sopenharmony_ci} 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_cistatic int 204362306a36Sopenharmony_civmxnet3_rq_init(struct vmxnet3_rx_queue *rq, 204462306a36Sopenharmony_ci struct vmxnet3_adapter *adapter) 204562306a36Sopenharmony_ci{ 204662306a36Sopenharmony_ci int i, err; 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci /* initialize buf_info */ 204962306a36Sopenharmony_ci for (i = 0; i < rq->rx_ring[0].size; i++) { 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci /* 1st buf for a pkt is skbuff or xdp page */ 205262306a36Sopenharmony_ci if (i % adapter->rx_buf_per_pkt == 0) { 205362306a36Sopenharmony_ci rq->buf_info[0][i].buf_type = vmxnet3_xdp_enabled(adapter) ? 205462306a36Sopenharmony_ci VMXNET3_RX_BUF_XDP : 205562306a36Sopenharmony_ci VMXNET3_RX_BUF_SKB; 205662306a36Sopenharmony_ci rq->buf_info[0][i].len = adapter->skb_buf_size; 205762306a36Sopenharmony_ci } else { /* subsequent bufs for a pkt is frag */ 205862306a36Sopenharmony_ci rq->buf_info[0][i].buf_type = VMXNET3_RX_BUF_PAGE; 205962306a36Sopenharmony_ci rq->buf_info[0][i].len = PAGE_SIZE; 206062306a36Sopenharmony_ci } 206162306a36Sopenharmony_ci } 206262306a36Sopenharmony_ci for (i = 0; i < rq->rx_ring[1].size; i++) { 206362306a36Sopenharmony_ci rq->buf_info[1][i].buf_type = VMXNET3_RX_BUF_PAGE; 206462306a36Sopenharmony_ci rq->buf_info[1][i].len = PAGE_SIZE; 206562306a36Sopenharmony_ci } 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci /* reset internal state and allocate buffers for both rings */ 206862306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 206962306a36Sopenharmony_ci rq->rx_ring[i].next2fill = rq->rx_ring[i].next2comp = 0; 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci memset(rq->rx_ring[i].base, 0, rq->rx_ring[i].size * 207262306a36Sopenharmony_ci sizeof(struct Vmxnet3_RxDesc)); 207362306a36Sopenharmony_ci rq->rx_ring[i].gen = VMXNET3_INIT_GEN; 207462306a36Sopenharmony_ci rq->rx_ring[i].isOutOfOrder = 0; 207562306a36Sopenharmony_ci } 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_ci err = vmxnet3_create_pp(adapter, rq, 207862306a36Sopenharmony_ci rq->rx_ring[0].size + rq->rx_ring[1].size); 207962306a36Sopenharmony_ci if (err) 208062306a36Sopenharmony_ci return err; 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci if (vmxnet3_rq_alloc_rx_buf(rq, 0, rq->rx_ring[0].size - 1, 208362306a36Sopenharmony_ci adapter) == 0) { 208462306a36Sopenharmony_ci xdp_rxq_info_unreg(&rq->xdp_rxq); 208562306a36Sopenharmony_ci page_pool_destroy(rq->page_pool); 208662306a36Sopenharmony_ci rq->page_pool = NULL; 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_ci /* at least has 1 rx buffer for the 1st ring */ 208962306a36Sopenharmony_ci return -ENOMEM; 209062306a36Sopenharmony_ci } 209162306a36Sopenharmony_ci vmxnet3_rq_alloc_rx_buf(rq, 1, rq->rx_ring[1].size - 1, adapter); 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci /* reset the comp ring */ 209462306a36Sopenharmony_ci rq->comp_ring.next2proc = 0; 209562306a36Sopenharmony_ci memset(rq->comp_ring.base, 0, rq->comp_ring.size * 209662306a36Sopenharmony_ci sizeof(struct Vmxnet3_RxCompDesc)); 209762306a36Sopenharmony_ci rq->comp_ring.gen = VMXNET3_INIT_GEN; 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_ci /* reset rxctx */ 210062306a36Sopenharmony_ci rq->rx_ctx.skb = NULL; 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_ci /* stats are not reset */ 210362306a36Sopenharmony_ci return 0; 210462306a36Sopenharmony_ci} 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_cistatic int 210862306a36Sopenharmony_civmxnet3_rq_init_all(struct vmxnet3_adapter *adapter) 210962306a36Sopenharmony_ci{ 211062306a36Sopenharmony_ci int i, err = 0; 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) { 211362306a36Sopenharmony_ci err = vmxnet3_rq_init(&adapter->rx_queue[i], adapter); 211462306a36Sopenharmony_ci if (unlikely(err)) { 211562306a36Sopenharmony_ci dev_err(&adapter->netdev->dev, "%s: failed to " 211662306a36Sopenharmony_ci "initialize rx queue%i\n", 211762306a36Sopenharmony_ci adapter->netdev->name, i); 211862306a36Sopenharmony_ci break; 211962306a36Sopenharmony_ci } 212062306a36Sopenharmony_ci } 212162306a36Sopenharmony_ci return err; 212262306a36Sopenharmony_ci 212362306a36Sopenharmony_ci} 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_cistatic int 212762306a36Sopenharmony_civmxnet3_rq_create(struct vmxnet3_rx_queue *rq, struct vmxnet3_adapter *adapter) 212862306a36Sopenharmony_ci{ 212962306a36Sopenharmony_ci int i; 213062306a36Sopenharmony_ci size_t sz; 213162306a36Sopenharmony_ci struct vmxnet3_rx_buf_info *bi; 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci sz = rq->rx_ring[i].size * sizeof(struct Vmxnet3_RxDesc); 213662306a36Sopenharmony_ci rq->rx_ring[i].base = dma_alloc_coherent( 213762306a36Sopenharmony_ci &adapter->pdev->dev, sz, 213862306a36Sopenharmony_ci &rq->rx_ring[i].basePA, 213962306a36Sopenharmony_ci GFP_KERNEL); 214062306a36Sopenharmony_ci if (!rq->rx_ring[i].base) { 214162306a36Sopenharmony_ci netdev_err(adapter->netdev, 214262306a36Sopenharmony_ci "failed to allocate rx ring %d\n", i); 214362306a36Sopenharmony_ci goto err; 214462306a36Sopenharmony_ci } 214562306a36Sopenharmony_ci } 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_ci if ((adapter->rxdataring_enabled) && (rq->data_ring.desc_size != 0)) { 214862306a36Sopenharmony_ci sz = rq->rx_ring[0].size * rq->data_ring.desc_size; 214962306a36Sopenharmony_ci rq->data_ring.base = 215062306a36Sopenharmony_ci dma_alloc_coherent(&adapter->pdev->dev, sz, 215162306a36Sopenharmony_ci &rq->data_ring.basePA, 215262306a36Sopenharmony_ci GFP_KERNEL); 215362306a36Sopenharmony_ci if (!rq->data_ring.base) { 215462306a36Sopenharmony_ci netdev_err(adapter->netdev, 215562306a36Sopenharmony_ci "rx data ring will be disabled\n"); 215662306a36Sopenharmony_ci adapter->rxdataring_enabled = false; 215762306a36Sopenharmony_ci } 215862306a36Sopenharmony_ci } else { 215962306a36Sopenharmony_ci rq->data_ring.base = NULL; 216062306a36Sopenharmony_ci rq->data_ring.desc_size = 0; 216162306a36Sopenharmony_ci } 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_ci sz = rq->comp_ring.size * sizeof(struct Vmxnet3_RxCompDesc); 216462306a36Sopenharmony_ci rq->comp_ring.base = dma_alloc_coherent(&adapter->pdev->dev, sz, 216562306a36Sopenharmony_ci &rq->comp_ring.basePA, 216662306a36Sopenharmony_ci GFP_KERNEL); 216762306a36Sopenharmony_ci if (!rq->comp_ring.base) { 216862306a36Sopenharmony_ci netdev_err(adapter->netdev, "failed to allocate rx comp ring\n"); 216962306a36Sopenharmony_ci goto err; 217062306a36Sopenharmony_ci } 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_ci bi = kcalloc_node(rq->rx_ring[0].size + rq->rx_ring[1].size, 217362306a36Sopenharmony_ci sizeof(rq->buf_info[0][0]), GFP_KERNEL, 217462306a36Sopenharmony_ci dev_to_node(&adapter->pdev->dev)); 217562306a36Sopenharmony_ci if (!bi) 217662306a36Sopenharmony_ci goto err; 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_ci rq->buf_info[0] = bi; 217962306a36Sopenharmony_ci rq->buf_info[1] = bi + rq->rx_ring[0].size; 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci return 0; 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_cierr: 218462306a36Sopenharmony_ci vmxnet3_rq_destroy(rq, adapter); 218562306a36Sopenharmony_ci return -ENOMEM; 218662306a36Sopenharmony_ci} 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_ciint 219062306a36Sopenharmony_civmxnet3_rq_create_all(struct vmxnet3_adapter *adapter) 219162306a36Sopenharmony_ci{ 219262306a36Sopenharmony_ci int i, err = 0; 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci adapter->rxdataring_enabled = VMXNET3_VERSION_GE_3(adapter); 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) { 219762306a36Sopenharmony_ci err = vmxnet3_rq_create(&adapter->rx_queue[i], adapter); 219862306a36Sopenharmony_ci if (unlikely(err)) { 219962306a36Sopenharmony_ci dev_err(&adapter->netdev->dev, 220062306a36Sopenharmony_ci "%s: failed to create rx queue%i\n", 220162306a36Sopenharmony_ci adapter->netdev->name, i); 220262306a36Sopenharmony_ci goto err_out; 220362306a36Sopenharmony_ci } 220462306a36Sopenharmony_ci } 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_ci if (!adapter->rxdataring_enabled) 220762306a36Sopenharmony_ci vmxnet3_rq_destroy_all_rxdataring(adapter); 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci return err; 221062306a36Sopenharmony_cierr_out: 221162306a36Sopenharmony_ci vmxnet3_rq_destroy_all(adapter); 221262306a36Sopenharmony_ci return err; 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_ci} 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_ci/* Multiple queue aware polling function for tx and rx */ 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_cistatic int 221962306a36Sopenharmony_civmxnet3_do_poll(struct vmxnet3_adapter *adapter, int budget) 222062306a36Sopenharmony_ci{ 222162306a36Sopenharmony_ci int rcd_done = 0, i; 222262306a36Sopenharmony_ci if (unlikely(adapter->shared->ecr)) 222362306a36Sopenharmony_ci vmxnet3_process_events(adapter); 222462306a36Sopenharmony_ci for (i = 0; i < adapter->num_tx_queues; i++) 222562306a36Sopenharmony_ci vmxnet3_tq_tx_complete(&adapter->tx_queue[i], adapter); 222662306a36Sopenharmony_ci 222762306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) 222862306a36Sopenharmony_ci rcd_done += vmxnet3_rq_rx_complete(&adapter->rx_queue[i], 222962306a36Sopenharmony_ci adapter, budget); 223062306a36Sopenharmony_ci return rcd_done; 223162306a36Sopenharmony_ci} 223262306a36Sopenharmony_ci 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_cistatic int 223562306a36Sopenharmony_civmxnet3_poll(struct napi_struct *napi, int budget) 223662306a36Sopenharmony_ci{ 223762306a36Sopenharmony_ci struct vmxnet3_rx_queue *rx_queue = container_of(napi, 223862306a36Sopenharmony_ci struct vmxnet3_rx_queue, napi); 223962306a36Sopenharmony_ci int rxd_done; 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ci rxd_done = vmxnet3_do_poll(rx_queue->adapter, budget); 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci if (rxd_done < budget) { 224462306a36Sopenharmony_ci napi_complete_done(napi, rxd_done); 224562306a36Sopenharmony_ci vmxnet3_enable_all_intrs(rx_queue->adapter); 224662306a36Sopenharmony_ci } 224762306a36Sopenharmony_ci return rxd_done; 224862306a36Sopenharmony_ci} 224962306a36Sopenharmony_ci 225062306a36Sopenharmony_ci/* 225162306a36Sopenharmony_ci * NAPI polling function for MSI-X mode with multiple Rx queues 225262306a36Sopenharmony_ci * Returns the # of the NAPI credit consumed (# of rx descriptors processed) 225362306a36Sopenharmony_ci */ 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_cistatic int 225662306a36Sopenharmony_civmxnet3_poll_rx_only(struct napi_struct *napi, int budget) 225762306a36Sopenharmony_ci{ 225862306a36Sopenharmony_ci struct vmxnet3_rx_queue *rq = container_of(napi, 225962306a36Sopenharmony_ci struct vmxnet3_rx_queue, napi); 226062306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = rq->adapter; 226162306a36Sopenharmony_ci int rxd_done; 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci /* When sharing interrupt with corresponding tx queue, process 226462306a36Sopenharmony_ci * tx completions in that queue as well 226562306a36Sopenharmony_ci */ 226662306a36Sopenharmony_ci if (adapter->share_intr == VMXNET3_INTR_BUDDYSHARE) { 226762306a36Sopenharmony_ci struct vmxnet3_tx_queue *tq = 226862306a36Sopenharmony_ci &adapter->tx_queue[rq - adapter->rx_queue]; 226962306a36Sopenharmony_ci vmxnet3_tq_tx_complete(tq, adapter); 227062306a36Sopenharmony_ci } 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci rxd_done = vmxnet3_rq_rx_complete(rq, adapter, budget); 227362306a36Sopenharmony_ci 227462306a36Sopenharmony_ci if (rxd_done < budget) { 227562306a36Sopenharmony_ci napi_complete_done(napi, rxd_done); 227662306a36Sopenharmony_ci vmxnet3_enable_intr(adapter, rq->comp_ring.intr_idx); 227762306a36Sopenharmony_ci } 227862306a36Sopenharmony_ci return rxd_done; 227962306a36Sopenharmony_ci} 228062306a36Sopenharmony_ci 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI 228362306a36Sopenharmony_ci 228462306a36Sopenharmony_ci/* 228562306a36Sopenharmony_ci * Handle completion interrupts on tx queues 228662306a36Sopenharmony_ci * Returns whether or not the intr is handled 228762306a36Sopenharmony_ci */ 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_cistatic irqreturn_t 229062306a36Sopenharmony_civmxnet3_msix_tx(int irq, void *data) 229162306a36Sopenharmony_ci{ 229262306a36Sopenharmony_ci struct vmxnet3_tx_queue *tq = data; 229362306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = tq->adapter; 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_ci if (adapter->intr.mask_mode == VMXNET3_IMM_ACTIVE) 229662306a36Sopenharmony_ci vmxnet3_disable_intr(adapter, tq->comp_ring.intr_idx); 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_ci /* Handle the case where only one irq is allocate for all tx queues */ 229962306a36Sopenharmony_ci if (adapter->share_intr == VMXNET3_INTR_TXSHARE) { 230062306a36Sopenharmony_ci int i; 230162306a36Sopenharmony_ci for (i = 0; i < adapter->num_tx_queues; i++) { 230262306a36Sopenharmony_ci struct vmxnet3_tx_queue *txq = &adapter->tx_queue[i]; 230362306a36Sopenharmony_ci vmxnet3_tq_tx_complete(txq, adapter); 230462306a36Sopenharmony_ci } 230562306a36Sopenharmony_ci } else { 230662306a36Sopenharmony_ci vmxnet3_tq_tx_complete(tq, adapter); 230762306a36Sopenharmony_ci } 230862306a36Sopenharmony_ci vmxnet3_enable_intr(adapter, tq->comp_ring.intr_idx); 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_ci return IRQ_HANDLED; 231162306a36Sopenharmony_ci} 231262306a36Sopenharmony_ci 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_ci/* 231562306a36Sopenharmony_ci * Handle completion interrupts on rx queues. Returns whether or not the 231662306a36Sopenharmony_ci * intr is handled 231762306a36Sopenharmony_ci */ 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_cistatic irqreturn_t 232062306a36Sopenharmony_civmxnet3_msix_rx(int irq, void *data) 232162306a36Sopenharmony_ci{ 232262306a36Sopenharmony_ci struct vmxnet3_rx_queue *rq = data; 232362306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = rq->adapter; 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_ci /* disable intr if needed */ 232662306a36Sopenharmony_ci if (adapter->intr.mask_mode == VMXNET3_IMM_ACTIVE) 232762306a36Sopenharmony_ci vmxnet3_disable_intr(adapter, rq->comp_ring.intr_idx); 232862306a36Sopenharmony_ci napi_schedule(&rq->napi); 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_ci return IRQ_HANDLED; 233162306a36Sopenharmony_ci} 233262306a36Sopenharmony_ci 233362306a36Sopenharmony_ci/* 233462306a36Sopenharmony_ci *---------------------------------------------------------------------------- 233562306a36Sopenharmony_ci * 233662306a36Sopenharmony_ci * vmxnet3_msix_event -- 233762306a36Sopenharmony_ci * 233862306a36Sopenharmony_ci * vmxnet3 msix event intr handler 233962306a36Sopenharmony_ci * 234062306a36Sopenharmony_ci * Result: 234162306a36Sopenharmony_ci * whether or not the intr is handled 234262306a36Sopenharmony_ci * 234362306a36Sopenharmony_ci *---------------------------------------------------------------------------- 234462306a36Sopenharmony_ci */ 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_cistatic irqreturn_t 234762306a36Sopenharmony_civmxnet3_msix_event(int irq, void *data) 234862306a36Sopenharmony_ci{ 234962306a36Sopenharmony_ci struct net_device *dev = data; 235062306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(dev); 235162306a36Sopenharmony_ci 235262306a36Sopenharmony_ci /* disable intr if needed */ 235362306a36Sopenharmony_ci if (adapter->intr.mask_mode == VMXNET3_IMM_ACTIVE) 235462306a36Sopenharmony_ci vmxnet3_disable_intr(adapter, adapter->intr.event_intr_idx); 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_ci if (adapter->shared->ecr) 235762306a36Sopenharmony_ci vmxnet3_process_events(adapter); 235862306a36Sopenharmony_ci 235962306a36Sopenharmony_ci vmxnet3_enable_intr(adapter, adapter->intr.event_intr_idx); 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_ci return IRQ_HANDLED; 236262306a36Sopenharmony_ci} 236362306a36Sopenharmony_ci 236462306a36Sopenharmony_ci#endif /* CONFIG_PCI_MSI */ 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci 236762306a36Sopenharmony_ci/* Interrupt handler for vmxnet3 */ 236862306a36Sopenharmony_cistatic irqreturn_t 236962306a36Sopenharmony_civmxnet3_intr(int irq, void *dev_id) 237062306a36Sopenharmony_ci{ 237162306a36Sopenharmony_ci struct net_device *dev = dev_id; 237262306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(dev); 237362306a36Sopenharmony_ci 237462306a36Sopenharmony_ci if (adapter->intr.type == VMXNET3_IT_INTX) { 237562306a36Sopenharmony_ci u32 icr = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_ICR); 237662306a36Sopenharmony_ci if (unlikely(icr == 0)) 237762306a36Sopenharmony_ci /* not ours */ 237862306a36Sopenharmony_ci return IRQ_NONE; 237962306a36Sopenharmony_ci } 238062306a36Sopenharmony_ci 238162306a36Sopenharmony_ci 238262306a36Sopenharmony_ci /* disable intr if needed */ 238362306a36Sopenharmony_ci if (adapter->intr.mask_mode == VMXNET3_IMM_ACTIVE) 238462306a36Sopenharmony_ci vmxnet3_disable_all_intrs(adapter); 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci napi_schedule(&adapter->rx_queue[0].napi); 238762306a36Sopenharmony_ci 238862306a36Sopenharmony_ci return IRQ_HANDLED; 238962306a36Sopenharmony_ci} 239062306a36Sopenharmony_ci 239162306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 239262306a36Sopenharmony_ci 239362306a36Sopenharmony_ci/* netpoll callback. */ 239462306a36Sopenharmony_cistatic void 239562306a36Sopenharmony_civmxnet3_netpoll(struct net_device *netdev) 239662306a36Sopenharmony_ci{ 239762306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ci switch (adapter->intr.type) { 240062306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI 240162306a36Sopenharmony_ci case VMXNET3_IT_MSIX: { 240262306a36Sopenharmony_ci int i; 240362306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) 240462306a36Sopenharmony_ci vmxnet3_msix_rx(0, &adapter->rx_queue[i]); 240562306a36Sopenharmony_ci break; 240662306a36Sopenharmony_ci } 240762306a36Sopenharmony_ci#endif 240862306a36Sopenharmony_ci case VMXNET3_IT_MSI: 240962306a36Sopenharmony_ci default: 241062306a36Sopenharmony_ci vmxnet3_intr(0, adapter->netdev); 241162306a36Sopenharmony_ci break; 241262306a36Sopenharmony_ci } 241362306a36Sopenharmony_ci 241462306a36Sopenharmony_ci} 241562306a36Sopenharmony_ci#endif /* CONFIG_NET_POLL_CONTROLLER */ 241662306a36Sopenharmony_ci 241762306a36Sopenharmony_cistatic int 241862306a36Sopenharmony_civmxnet3_request_irqs(struct vmxnet3_adapter *adapter) 241962306a36Sopenharmony_ci{ 242062306a36Sopenharmony_ci struct vmxnet3_intr *intr = &adapter->intr; 242162306a36Sopenharmony_ci int err = 0, i; 242262306a36Sopenharmony_ci int vector = 0; 242362306a36Sopenharmony_ci 242462306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI 242562306a36Sopenharmony_ci if (adapter->intr.type == VMXNET3_IT_MSIX) { 242662306a36Sopenharmony_ci for (i = 0; i < adapter->num_tx_queues; i++) { 242762306a36Sopenharmony_ci if (adapter->share_intr != VMXNET3_INTR_BUDDYSHARE) { 242862306a36Sopenharmony_ci sprintf(adapter->tx_queue[i].name, "%s-tx-%d", 242962306a36Sopenharmony_ci adapter->netdev->name, vector); 243062306a36Sopenharmony_ci err = request_irq( 243162306a36Sopenharmony_ci intr->msix_entries[vector].vector, 243262306a36Sopenharmony_ci vmxnet3_msix_tx, 0, 243362306a36Sopenharmony_ci adapter->tx_queue[i].name, 243462306a36Sopenharmony_ci &adapter->tx_queue[i]); 243562306a36Sopenharmony_ci } else { 243662306a36Sopenharmony_ci sprintf(adapter->tx_queue[i].name, "%s-rxtx-%d", 243762306a36Sopenharmony_ci adapter->netdev->name, vector); 243862306a36Sopenharmony_ci } 243962306a36Sopenharmony_ci if (err) { 244062306a36Sopenharmony_ci dev_err(&adapter->netdev->dev, 244162306a36Sopenharmony_ci "Failed to request irq for MSIX, %s, " 244262306a36Sopenharmony_ci "error %d\n", 244362306a36Sopenharmony_ci adapter->tx_queue[i].name, err); 244462306a36Sopenharmony_ci return err; 244562306a36Sopenharmony_ci } 244662306a36Sopenharmony_ci 244762306a36Sopenharmony_ci /* Handle the case where only 1 MSIx was allocated for 244862306a36Sopenharmony_ci * all tx queues */ 244962306a36Sopenharmony_ci if (adapter->share_intr == VMXNET3_INTR_TXSHARE) { 245062306a36Sopenharmony_ci for (; i < adapter->num_tx_queues; i++) 245162306a36Sopenharmony_ci adapter->tx_queue[i].comp_ring.intr_idx 245262306a36Sopenharmony_ci = vector; 245362306a36Sopenharmony_ci vector++; 245462306a36Sopenharmony_ci break; 245562306a36Sopenharmony_ci } else { 245662306a36Sopenharmony_ci adapter->tx_queue[i].comp_ring.intr_idx 245762306a36Sopenharmony_ci = vector++; 245862306a36Sopenharmony_ci } 245962306a36Sopenharmony_ci } 246062306a36Sopenharmony_ci if (adapter->share_intr == VMXNET3_INTR_BUDDYSHARE) 246162306a36Sopenharmony_ci vector = 0; 246262306a36Sopenharmony_ci 246362306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) { 246462306a36Sopenharmony_ci if (adapter->share_intr != VMXNET3_INTR_BUDDYSHARE) 246562306a36Sopenharmony_ci sprintf(adapter->rx_queue[i].name, "%s-rx-%d", 246662306a36Sopenharmony_ci adapter->netdev->name, vector); 246762306a36Sopenharmony_ci else 246862306a36Sopenharmony_ci sprintf(adapter->rx_queue[i].name, "%s-rxtx-%d", 246962306a36Sopenharmony_ci adapter->netdev->name, vector); 247062306a36Sopenharmony_ci err = request_irq(intr->msix_entries[vector].vector, 247162306a36Sopenharmony_ci vmxnet3_msix_rx, 0, 247262306a36Sopenharmony_ci adapter->rx_queue[i].name, 247362306a36Sopenharmony_ci &(adapter->rx_queue[i])); 247462306a36Sopenharmony_ci if (err) { 247562306a36Sopenharmony_ci netdev_err(adapter->netdev, 247662306a36Sopenharmony_ci "Failed to request irq for MSIX, " 247762306a36Sopenharmony_ci "%s, error %d\n", 247862306a36Sopenharmony_ci adapter->rx_queue[i].name, err); 247962306a36Sopenharmony_ci return err; 248062306a36Sopenharmony_ci } 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_ci adapter->rx_queue[i].comp_ring.intr_idx = vector++; 248362306a36Sopenharmony_ci } 248462306a36Sopenharmony_ci 248562306a36Sopenharmony_ci sprintf(intr->event_msi_vector_name, "%s-event-%d", 248662306a36Sopenharmony_ci adapter->netdev->name, vector); 248762306a36Sopenharmony_ci err = request_irq(intr->msix_entries[vector].vector, 248862306a36Sopenharmony_ci vmxnet3_msix_event, 0, 248962306a36Sopenharmony_ci intr->event_msi_vector_name, adapter->netdev); 249062306a36Sopenharmony_ci intr->event_intr_idx = vector; 249162306a36Sopenharmony_ci 249262306a36Sopenharmony_ci } else if (intr->type == VMXNET3_IT_MSI) { 249362306a36Sopenharmony_ci adapter->num_rx_queues = 1; 249462306a36Sopenharmony_ci err = request_irq(adapter->pdev->irq, vmxnet3_intr, 0, 249562306a36Sopenharmony_ci adapter->netdev->name, adapter->netdev); 249662306a36Sopenharmony_ci } else { 249762306a36Sopenharmony_ci#endif 249862306a36Sopenharmony_ci adapter->num_rx_queues = 1; 249962306a36Sopenharmony_ci err = request_irq(adapter->pdev->irq, vmxnet3_intr, 250062306a36Sopenharmony_ci IRQF_SHARED, adapter->netdev->name, 250162306a36Sopenharmony_ci adapter->netdev); 250262306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI 250362306a36Sopenharmony_ci } 250462306a36Sopenharmony_ci#endif 250562306a36Sopenharmony_ci intr->num_intrs = vector + 1; 250662306a36Sopenharmony_ci if (err) { 250762306a36Sopenharmony_ci netdev_err(adapter->netdev, 250862306a36Sopenharmony_ci "Failed to request irq (intr type:%d), error %d\n", 250962306a36Sopenharmony_ci intr->type, err); 251062306a36Sopenharmony_ci } else { 251162306a36Sopenharmony_ci /* Number of rx queues will not change after this */ 251262306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) { 251362306a36Sopenharmony_ci struct vmxnet3_rx_queue *rq = &adapter->rx_queue[i]; 251462306a36Sopenharmony_ci rq->qid = i; 251562306a36Sopenharmony_ci rq->qid2 = i + adapter->num_rx_queues; 251662306a36Sopenharmony_ci rq->dataRingQid = i + 2 * adapter->num_rx_queues; 251762306a36Sopenharmony_ci } 251862306a36Sopenharmony_ci 251962306a36Sopenharmony_ci /* init our intr settings */ 252062306a36Sopenharmony_ci for (i = 0; i < intr->num_intrs; i++) 252162306a36Sopenharmony_ci intr->mod_levels[i] = UPT1_IML_ADAPTIVE; 252262306a36Sopenharmony_ci if (adapter->intr.type != VMXNET3_IT_MSIX) { 252362306a36Sopenharmony_ci adapter->intr.event_intr_idx = 0; 252462306a36Sopenharmony_ci for (i = 0; i < adapter->num_tx_queues; i++) 252562306a36Sopenharmony_ci adapter->tx_queue[i].comp_ring.intr_idx = 0; 252662306a36Sopenharmony_ci adapter->rx_queue[0].comp_ring.intr_idx = 0; 252762306a36Sopenharmony_ci } 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci netdev_info(adapter->netdev, 253062306a36Sopenharmony_ci "intr type %u, mode %u, %u vectors allocated\n", 253162306a36Sopenharmony_ci intr->type, intr->mask_mode, intr->num_intrs); 253262306a36Sopenharmony_ci } 253362306a36Sopenharmony_ci 253462306a36Sopenharmony_ci return err; 253562306a36Sopenharmony_ci} 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_cistatic void 253962306a36Sopenharmony_civmxnet3_free_irqs(struct vmxnet3_adapter *adapter) 254062306a36Sopenharmony_ci{ 254162306a36Sopenharmony_ci struct vmxnet3_intr *intr = &adapter->intr; 254262306a36Sopenharmony_ci BUG_ON(intr->type == VMXNET3_IT_AUTO || intr->num_intrs <= 0); 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_ci switch (intr->type) { 254562306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI 254662306a36Sopenharmony_ci case VMXNET3_IT_MSIX: 254762306a36Sopenharmony_ci { 254862306a36Sopenharmony_ci int i, vector = 0; 254962306a36Sopenharmony_ci 255062306a36Sopenharmony_ci if (adapter->share_intr != VMXNET3_INTR_BUDDYSHARE) { 255162306a36Sopenharmony_ci for (i = 0; i < adapter->num_tx_queues; i++) { 255262306a36Sopenharmony_ci free_irq(intr->msix_entries[vector++].vector, 255362306a36Sopenharmony_ci &(adapter->tx_queue[i])); 255462306a36Sopenharmony_ci if (adapter->share_intr == VMXNET3_INTR_TXSHARE) 255562306a36Sopenharmony_ci break; 255662306a36Sopenharmony_ci } 255762306a36Sopenharmony_ci } 255862306a36Sopenharmony_ci 255962306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) { 256062306a36Sopenharmony_ci free_irq(intr->msix_entries[vector++].vector, 256162306a36Sopenharmony_ci &(adapter->rx_queue[i])); 256262306a36Sopenharmony_ci } 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ci free_irq(intr->msix_entries[vector].vector, 256562306a36Sopenharmony_ci adapter->netdev); 256662306a36Sopenharmony_ci BUG_ON(vector >= intr->num_intrs); 256762306a36Sopenharmony_ci break; 256862306a36Sopenharmony_ci } 256962306a36Sopenharmony_ci#endif 257062306a36Sopenharmony_ci case VMXNET3_IT_MSI: 257162306a36Sopenharmony_ci free_irq(adapter->pdev->irq, adapter->netdev); 257262306a36Sopenharmony_ci break; 257362306a36Sopenharmony_ci case VMXNET3_IT_INTX: 257462306a36Sopenharmony_ci free_irq(adapter->pdev->irq, adapter->netdev); 257562306a36Sopenharmony_ci break; 257662306a36Sopenharmony_ci default: 257762306a36Sopenharmony_ci BUG(); 257862306a36Sopenharmony_ci } 257962306a36Sopenharmony_ci} 258062306a36Sopenharmony_ci 258162306a36Sopenharmony_ci 258262306a36Sopenharmony_cistatic void 258362306a36Sopenharmony_civmxnet3_restore_vlan(struct vmxnet3_adapter *adapter) 258462306a36Sopenharmony_ci{ 258562306a36Sopenharmony_ci u32 *vfTable = adapter->shared->devRead.rxFilterConf.vfTable; 258662306a36Sopenharmony_ci u16 vid; 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_ci /* allow untagged pkts */ 258962306a36Sopenharmony_ci VMXNET3_SET_VFTABLE_ENTRY(vfTable, 0); 259062306a36Sopenharmony_ci 259162306a36Sopenharmony_ci for_each_set_bit(vid, adapter->active_vlans, VLAN_N_VID) 259262306a36Sopenharmony_ci VMXNET3_SET_VFTABLE_ENTRY(vfTable, vid); 259362306a36Sopenharmony_ci} 259462306a36Sopenharmony_ci 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_cistatic int 259762306a36Sopenharmony_civmxnet3_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid) 259862306a36Sopenharmony_ci{ 259962306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_ci if (!(netdev->flags & IFF_PROMISC)) { 260262306a36Sopenharmony_ci u32 *vfTable = adapter->shared->devRead.rxFilterConf.vfTable; 260362306a36Sopenharmony_ci unsigned long flags; 260462306a36Sopenharmony_ci 260562306a36Sopenharmony_ci VMXNET3_SET_VFTABLE_ENTRY(vfTable, vid); 260662306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 260762306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 260862306a36Sopenharmony_ci VMXNET3_CMD_UPDATE_VLAN_FILTERS); 260962306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 261062306a36Sopenharmony_ci } 261162306a36Sopenharmony_ci 261262306a36Sopenharmony_ci set_bit(vid, adapter->active_vlans); 261362306a36Sopenharmony_ci 261462306a36Sopenharmony_ci return 0; 261562306a36Sopenharmony_ci} 261662306a36Sopenharmony_ci 261762306a36Sopenharmony_ci 261862306a36Sopenharmony_cistatic int 261962306a36Sopenharmony_civmxnet3_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid) 262062306a36Sopenharmony_ci{ 262162306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 262262306a36Sopenharmony_ci 262362306a36Sopenharmony_ci if (!(netdev->flags & IFF_PROMISC)) { 262462306a36Sopenharmony_ci u32 *vfTable = adapter->shared->devRead.rxFilterConf.vfTable; 262562306a36Sopenharmony_ci unsigned long flags; 262662306a36Sopenharmony_ci 262762306a36Sopenharmony_ci VMXNET3_CLEAR_VFTABLE_ENTRY(vfTable, vid); 262862306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 262962306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 263062306a36Sopenharmony_ci VMXNET3_CMD_UPDATE_VLAN_FILTERS); 263162306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 263262306a36Sopenharmony_ci } 263362306a36Sopenharmony_ci 263462306a36Sopenharmony_ci clear_bit(vid, adapter->active_vlans); 263562306a36Sopenharmony_ci 263662306a36Sopenharmony_ci return 0; 263762306a36Sopenharmony_ci} 263862306a36Sopenharmony_ci 263962306a36Sopenharmony_ci 264062306a36Sopenharmony_cistatic u8 * 264162306a36Sopenharmony_civmxnet3_copy_mc(struct net_device *netdev) 264262306a36Sopenharmony_ci{ 264362306a36Sopenharmony_ci u8 *buf = NULL; 264462306a36Sopenharmony_ci u32 sz = netdev_mc_count(netdev) * ETH_ALEN; 264562306a36Sopenharmony_ci 264662306a36Sopenharmony_ci /* struct Vmxnet3_RxFilterConf.mfTableLen is u16. */ 264762306a36Sopenharmony_ci if (sz <= 0xffff) { 264862306a36Sopenharmony_ci /* We may be called with BH disabled */ 264962306a36Sopenharmony_ci buf = kmalloc(sz, GFP_ATOMIC); 265062306a36Sopenharmony_ci if (buf) { 265162306a36Sopenharmony_ci struct netdev_hw_addr *ha; 265262306a36Sopenharmony_ci int i = 0; 265362306a36Sopenharmony_ci 265462306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, netdev) 265562306a36Sopenharmony_ci memcpy(buf + i++ * ETH_ALEN, ha->addr, 265662306a36Sopenharmony_ci ETH_ALEN); 265762306a36Sopenharmony_ci } 265862306a36Sopenharmony_ci } 265962306a36Sopenharmony_ci return buf; 266062306a36Sopenharmony_ci} 266162306a36Sopenharmony_ci 266262306a36Sopenharmony_ci 266362306a36Sopenharmony_cistatic void 266462306a36Sopenharmony_civmxnet3_set_mc(struct net_device *netdev) 266562306a36Sopenharmony_ci{ 266662306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 266762306a36Sopenharmony_ci unsigned long flags; 266862306a36Sopenharmony_ci struct Vmxnet3_RxFilterConf *rxConf = 266962306a36Sopenharmony_ci &adapter->shared->devRead.rxFilterConf; 267062306a36Sopenharmony_ci u8 *new_table = NULL; 267162306a36Sopenharmony_ci dma_addr_t new_table_pa = 0; 267262306a36Sopenharmony_ci bool new_table_pa_valid = false; 267362306a36Sopenharmony_ci u32 new_mode = VMXNET3_RXM_UCAST; 267462306a36Sopenharmony_ci 267562306a36Sopenharmony_ci if (netdev->flags & IFF_PROMISC) { 267662306a36Sopenharmony_ci u32 *vfTable = adapter->shared->devRead.rxFilterConf.vfTable; 267762306a36Sopenharmony_ci memset(vfTable, 0, VMXNET3_VFT_SIZE * sizeof(*vfTable)); 267862306a36Sopenharmony_ci 267962306a36Sopenharmony_ci new_mode |= VMXNET3_RXM_PROMISC; 268062306a36Sopenharmony_ci } else { 268162306a36Sopenharmony_ci vmxnet3_restore_vlan(adapter); 268262306a36Sopenharmony_ci } 268362306a36Sopenharmony_ci 268462306a36Sopenharmony_ci if (netdev->flags & IFF_BROADCAST) 268562306a36Sopenharmony_ci new_mode |= VMXNET3_RXM_BCAST; 268662306a36Sopenharmony_ci 268762306a36Sopenharmony_ci if (netdev->flags & IFF_ALLMULTI) 268862306a36Sopenharmony_ci new_mode |= VMXNET3_RXM_ALL_MULTI; 268962306a36Sopenharmony_ci else 269062306a36Sopenharmony_ci if (!netdev_mc_empty(netdev)) { 269162306a36Sopenharmony_ci new_table = vmxnet3_copy_mc(netdev); 269262306a36Sopenharmony_ci if (new_table) { 269362306a36Sopenharmony_ci size_t sz = netdev_mc_count(netdev) * ETH_ALEN; 269462306a36Sopenharmony_ci 269562306a36Sopenharmony_ci rxConf->mfTableLen = cpu_to_le16(sz); 269662306a36Sopenharmony_ci new_table_pa = dma_map_single( 269762306a36Sopenharmony_ci &adapter->pdev->dev, 269862306a36Sopenharmony_ci new_table, 269962306a36Sopenharmony_ci sz, 270062306a36Sopenharmony_ci DMA_TO_DEVICE); 270162306a36Sopenharmony_ci if (!dma_mapping_error(&adapter->pdev->dev, 270262306a36Sopenharmony_ci new_table_pa)) { 270362306a36Sopenharmony_ci new_mode |= VMXNET3_RXM_MCAST; 270462306a36Sopenharmony_ci new_table_pa_valid = true; 270562306a36Sopenharmony_ci rxConf->mfTablePA = cpu_to_le64( 270662306a36Sopenharmony_ci new_table_pa); 270762306a36Sopenharmony_ci } 270862306a36Sopenharmony_ci } 270962306a36Sopenharmony_ci if (!new_table_pa_valid) { 271062306a36Sopenharmony_ci netdev_info(netdev, 271162306a36Sopenharmony_ci "failed to copy mcast list, setting ALL_MULTI\n"); 271262306a36Sopenharmony_ci new_mode |= VMXNET3_RXM_ALL_MULTI; 271362306a36Sopenharmony_ci } 271462306a36Sopenharmony_ci } 271562306a36Sopenharmony_ci 271662306a36Sopenharmony_ci if (!(new_mode & VMXNET3_RXM_MCAST)) { 271762306a36Sopenharmony_ci rxConf->mfTableLen = 0; 271862306a36Sopenharmony_ci rxConf->mfTablePA = 0; 271962306a36Sopenharmony_ci } 272062306a36Sopenharmony_ci 272162306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 272262306a36Sopenharmony_ci if (new_mode != rxConf->rxMode) { 272362306a36Sopenharmony_ci rxConf->rxMode = cpu_to_le32(new_mode); 272462306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 272562306a36Sopenharmony_ci VMXNET3_CMD_UPDATE_RX_MODE); 272662306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 272762306a36Sopenharmony_ci VMXNET3_CMD_UPDATE_VLAN_FILTERS); 272862306a36Sopenharmony_ci } 272962306a36Sopenharmony_ci 273062306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 273162306a36Sopenharmony_ci VMXNET3_CMD_UPDATE_MAC_FILTERS); 273262306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 273362306a36Sopenharmony_ci 273462306a36Sopenharmony_ci if (new_table_pa_valid) 273562306a36Sopenharmony_ci dma_unmap_single(&adapter->pdev->dev, new_table_pa, 273662306a36Sopenharmony_ci rxConf->mfTableLen, DMA_TO_DEVICE); 273762306a36Sopenharmony_ci kfree(new_table); 273862306a36Sopenharmony_ci} 273962306a36Sopenharmony_ci 274062306a36Sopenharmony_civoid 274162306a36Sopenharmony_civmxnet3_rq_destroy_all(struct vmxnet3_adapter *adapter) 274262306a36Sopenharmony_ci{ 274362306a36Sopenharmony_ci int i; 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) 274662306a36Sopenharmony_ci vmxnet3_rq_destroy(&adapter->rx_queue[i], adapter); 274762306a36Sopenharmony_ci} 274862306a36Sopenharmony_ci 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci/* 275162306a36Sopenharmony_ci * Set up driver_shared based on settings in adapter. 275262306a36Sopenharmony_ci */ 275362306a36Sopenharmony_ci 275462306a36Sopenharmony_cistatic void 275562306a36Sopenharmony_civmxnet3_setup_driver_shared(struct vmxnet3_adapter *adapter) 275662306a36Sopenharmony_ci{ 275762306a36Sopenharmony_ci struct Vmxnet3_DriverShared *shared = adapter->shared; 275862306a36Sopenharmony_ci struct Vmxnet3_DSDevRead *devRead = &shared->devRead; 275962306a36Sopenharmony_ci struct Vmxnet3_DSDevReadExt *devReadExt = &shared->devReadExt; 276062306a36Sopenharmony_ci struct Vmxnet3_TxQueueConf *tqc; 276162306a36Sopenharmony_ci struct Vmxnet3_RxQueueConf *rqc; 276262306a36Sopenharmony_ci int i; 276362306a36Sopenharmony_ci 276462306a36Sopenharmony_ci memset(shared, 0, sizeof(*shared)); 276562306a36Sopenharmony_ci 276662306a36Sopenharmony_ci /* driver settings */ 276762306a36Sopenharmony_ci shared->magic = cpu_to_le32(VMXNET3_REV1_MAGIC); 276862306a36Sopenharmony_ci devRead->misc.driverInfo.version = cpu_to_le32( 276962306a36Sopenharmony_ci VMXNET3_DRIVER_VERSION_NUM); 277062306a36Sopenharmony_ci devRead->misc.driverInfo.gos.gosBits = (sizeof(void *) == 4 ? 277162306a36Sopenharmony_ci VMXNET3_GOS_BITS_32 : VMXNET3_GOS_BITS_64); 277262306a36Sopenharmony_ci devRead->misc.driverInfo.gos.gosType = VMXNET3_GOS_TYPE_LINUX; 277362306a36Sopenharmony_ci *((u32 *)&devRead->misc.driverInfo.gos) = cpu_to_le32( 277462306a36Sopenharmony_ci *((u32 *)&devRead->misc.driverInfo.gos)); 277562306a36Sopenharmony_ci devRead->misc.driverInfo.vmxnet3RevSpt = cpu_to_le32(1); 277662306a36Sopenharmony_ci devRead->misc.driverInfo.uptVerSpt = cpu_to_le32(1); 277762306a36Sopenharmony_ci 277862306a36Sopenharmony_ci devRead->misc.ddPA = cpu_to_le64(adapter->adapter_pa); 277962306a36Sopenharmony_ci devRead->misc.ddLen = cpu_to_le32(sizeof(struct vmxnet3_adapter)); 278062306a36Sopenharmony_ci 278162306a36Sopenharmony_ci /* set up feature flags */ 278262306a36Sopenharmony_ci if (adapter->netdev->features & NETIF_F_RXCSUM) 278362306a36Sopenharmony_ci devRead->misc.uptFeatures |= UPT1_F_RXCSUM; 278462306a36Sopenharmony_ci 278562306a36Sopenharmony_ci if (adapter->netdev->features & NETIF_F_LRO) { 278662306a36Sopenharmony_ci devRead->misc.uptFeatures |= UPT1_F_LRO; 278762306a36Sopenharmony_ci devRead->misc.maxNumRxSG = cpu_to_le16(1 + MAX_SKB_FRAGS); 278862306a36Sopenharmony_ci } 278962306a36Sopenharmony_ci if (adapter->netdev->features & NETIF_F_HW_VLAN_CTAG_RX) 279062306a36Sopenharmony_ci devRead->misc.uptFeatures |= UPT1_F_RXVLAN; 279162306a36Sopenharmony_ci 279262306a36Sopenharmony_ci if (adapter->netdev->features & (NETIF_F_GSO_UDP_TUNNEL | 279362306a36Sopenharmony_ci NETIF_F_GSO_UDP_TUNNEL_CSUM)) 279462306a36Sopenharmony_ci devRead->misc.uptFeatures |= UPT1_F_RXINNEROFLD; 279562306a36Sopenharmony_ci 279662306a36Sopenharmony_ci devRead->misc.mtu = cpu_to_le32(adapter->netdev->mtu); 279762306a36Sopenharmony_ci devRead->misc.queueDescPA = cpu_to_le64(adapter->queue_desc_pa); 279862306a36Sopenharmony_ci devRead->misc.queueDescLen = cpu_to_le32( 279962306a36Sopenharmony_ci adapter->num_tx_queues * sizeof(struct Vmxnet3_TxQueueDesc) + 280062306a36Sopenharmony_ci adapter->num_rx_queues * sizeof(struct Vmxnet3_RxQueueDesc)); 280162306a36Sopenharmony_ci 280262306a36Sopenharmony_ci /* tx queue settings */ 280362306a36Sopenharmony_ci devRead->misc.numTxQueues = adapter->num_tx_queues; 280462306a36Sopenharmony_ci for (i = 0; i < adapter->num_tx_queues; i++) { 280562306a36Sopenharmony_ci struct vmxnet3_tx_queue *tq = &adapter->tx_queue[i]; 280662306a36Sopenharmony_ci BUG_ON(adapter->tx_queue[i].tx_ring.base == NULL); 280762306a36Sopenharmony_ci tqc = &adapter->tqd_start[i].conf; 280862306a36Sopenharmony_ci tqc->txRingBasePA = cpu_to_le64(tq->tx_ring.basePA); 280962306a36Sopenharmony_ci tqc->dataRingBasePA = cpu_to_le64(tq->data_ring.basePA); 281062306a36Sopenharmony_ci tqc->compRingBasePA = cpu_to_le64(tq->comp_ring.basePA); 281162306a36Sopenharmony_ci tqc->ddPA = cpu_to_le64(~0ULL); 281262306a36Sopenharmony_ci tqc->txRingSize = cpu_to_le32(tq->tx_ring.size); 281362306a36Sopenharmony_ci tqc->dataRingSize = cpu_to_le32(tq->data_ring.size); 281462306a36Sopenharmony_ci tqc->txDataRingDescSize = cpu_to_le32(tq->txdata_desc_size); 281562306a36Sopenharmony_ci tqc->compRingSize = cpu_to_le32(tq->comp_ring.size); 281662306a36Sopenharmony_ci tqc->ddLen = cpu_to_le32(0); 281762306a36Sopenharmony_ci tqc->intrIdx = tq->comp_ring.intr_idx; 281862306a36Sopenharmony_ci } 281962306a36Sopenharmony_ci 282062306a36Sopenharmony_ci /* rx queue settings */ 282162306a36Sopenharmony_ci devRead->misc.numRxQueues = adapter->num_rx_queues; 282262306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) { 282362306a36Sopenharmony_ci struct vmxnet3_rx_queue *rq = &adapter->rx_queue[i]; 282462306a36Sopenharmony_ci rqc = &adapter->rqd_start[i].conf; 282562306a36Sopenharmony_ci rqc->rxRingBasePA[0] = cpu_to_le64(rq->rx_ring[0].basePA); 282662306a36Sopenharmony_ci rqc->rxRingBasePA[1] = cpu_to_le64(rq->rx_ring[1].basePA); 282762306a36Sopenharmony_ci rqc->compRingBasePA = cpu_to_le64(rq->comp_ring.basePA); 282862306a36Sopenharmony_ci rqc->ddPA = cpu_to_le64(~0ULL); 282962306a36Sopenharmony_ci rqc->rxRingSize[0] = cpu_to_le32(rq->rx_ring[0].size); 283062306a36Sopenharmony_ci rqc->rxRingSize[1] = cpu_to_le32(rq->rx_ring[1].size); 283162306a36Sopenharmony_ci rqc->compRingSize = cpu_to_le32(rq->comp_ring.size); 283262306a36Sopenharmony_ci rqc->ddLen = cpu_to_le32(0); 283362306a36Sopenharmony_ci rqc->intrIdx = rq->comp_ring.intr_idx; 283462306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_3(adapter)) { 283562306a36Sopenharmony_ci rqc->rxDataRingBasePA = 283662306a36Sopenharmony_ci cpu_to_le64(rq->data_ring.basePA); 283762306a36Sopenharmony_ci rqc->rxDataRingDescSize = 283862306a36Sopenharmony_ci cpu_to_le16(rq->data_ring.desc_size); 283962306a36Sopenharmony_ci } 284062306a36Sopenharmony_ci } 284162306a36Sopenharmony_ci 284262306a36Sopenharmony_ci#ifdef VMXNET3_RSS 284362306a36Sopenharmony_ci memset(adapter->rss_conf, 0, sizeof(*adapter->rss_conf)); 284462306a36Sopenharmony_ci 284562306a36Sopenharmony_ci if (adapter->rss) { 284662306a36Sopenharmony_ci struct UPT1_RSSConf *rssConf = adapter->rss_conf; 284762306a36Sopenharmony_ci 284862306a36Sopenharmony_ci devRead->misc.uptFeatures |= UPT1_F_RSS; 284962306a36Sopenharmony_ci devRead->misc.numRxQueues = adapter->num_rx_queues; 285062306a36Sopenharmony_ci rssConf->hashType = UPT1_RSS_HASH_TYPE_TCP_IPV4 | 285162306a36Sopenharmony_ci UPT1_RSS_HASH_TYPE_IPV4 | 285262306a36Sopenharmony_ci UPT1_RSS_HASH_TYPE_TCP_IPV6 | 285362306a36Sopenharmony_ci UPT1_RSS_HASH_TYPE_IPV6; 285462306a36Sopenharmony_ci rssConf->hashFunc = UPT1_RSS_HASH_FUNC_TOEPLITZ; 285562306a36Sopenharmony_ci rssConf->hashKeySize = UPT1_RSS_MAX_KEY_SIZE; 285662306a36Sopenharmony_ci rssConf->indTableSize = VMXNET3_RSS_IND_TABLE_SIZE; 285762306a36Sopenharmony_ci netdev_rss_key_fill(rssConf->hashKey, sizeof(rssConf->hashKey)); 285862306a36Sopenharmony_ci 285962306a36Sopenharmony_ci for (i = 0; i < rssConf->indTableSize; i++) 286062306a36Sopenharmony_ci rssConf->indTable[i] = ethtool_rxfh_indir_default( 286162306a36Sopenharmony_ci i, adapter->num_rx_queues); 286262306a36Sopenharmony_ci 286362306a36Sopenharmony_ci devRead->rssConfDesc.confVer = 1; 286462306a36Sopenharmony_ci devRead->rssConfDesc.confLen = cpu_to_le32(sizeof(*rssConf)); 286562306a36Sopenharmony_ci devRead->rssConfDesc.confPA = 286662306a36Sopenharmony_ci cpu_to_le64(adapter->rss_conf_pa); 286762306a36Sopenharmony_ci } 286862306a36Sopenharmony_ci 286962306a36Sopenharmony_ci#endif /* VMXNET3_RSS */ 287062306a36Sopenharmony_ci 287162306a36Sopenharmony_ci /* intr settings */ 287262306a36Sopenharmony_ci if (!VMXNET3_VERSION_GE_6(adapter) || 287362306a36Sopenharmony_ci !adapter->queuesExtEnabled) { 287462306a36Sopenharmony_ci devRead->intrConf.autoMask = adapter->intr.mask_mode == 287562306a36Sopenharmony_ci VMXNET3_IMM_AUTO; 287662306a36Sopenharmony_ci devRead->intrConf.numIntrs = adapter->intr.num_intrs; 287762306a36Sopenharmony_ci for (i = 0; i < adapter->intr.num_intrs; i++) 287862306a36Sopenharmony_ci devRead->intrConf.modLevels[i] = adapter->intr.mod_levels[i]; 287962306a36Sopenharmony_ci 288062306a36Sopenharmony_ci devRead->intrConf.eventIntrIdx = adapter->intr.event_intr_idx; 288162306a36Sopenharmony_ci devRead->intrConf.intrCtrl |= cpu_to_le32(VMXNET3_IC_DISABLE_ALL); 288262306a36Sopenharmony_ci } else { 288362306a36Sopenharmony_ci devReadExt->intrConfExt.autoMask = adapter->intr.mask_mode == 288462306a36Sopenharmony_ci VMXNET3_IMM_AUTO; 288562306a36Sopenharmony_ci devReadExt->intrConfExt.numIntrs = adapter->intr.num_intrs; 288662306a36Sopenharmony_ci for (i = 0; i < adapter->intr.num_intrs; i++) 288762306a36Sopenharmony_ci devReadExt->intrConfExt.modLevels[i] = adapter->intr.mod_levels[i]; 288862306a36Sopenharmony_ci 288962306a36Sopenharmony_ci devReadExt->intrConfExt.eventIntrIdx = adapter->intr.event_intr_idx; 289062306a36Sopenharmony_ci devReadExt->intrConfExt.intrCtrl |= cpu_to_le32(VMXNET3_IC_DISABLE_ALL); 289162306a36Sopenharmony_ci } 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_ci /* rx filter settings */ 289462306a36Sopenharmony_ci devRead->rxFilterConf.rxMode = 0; 289562306a36Sopenharmony_ci vmxnet3_restore_vlan(adapter); 289662306a36Sopenharmony_ci vmxnet3_write_mac_addr(adapter, adapter->netdev->dev_addr); 289762306a36Sopenharmony_ci 289862306a36Sopenharmony_ci /* the rest are already zeroed */ 289962306a36Sopenharmony_ci} 290062306a36Sopenharmony_ci 290162306a36Sopenharmony_cistatic void 290262306a36Sopenharmony_civmxnet3_init_bufsize(struct vmxnet3_adapter *adapter) 290362306a36Sopenharmony_ci{ 290462306a36Sopenharmony_ci struct Vmxnet3_DriverShared *shared = adapter->shared; 290562306a36Sopenharmony_ci union Vmxnet3_CmdInfo *cmdInfo = &shared->cu.cmdInfo; 290662306a36Sopenharmony_ci unsigned long flags; 290762306a36Sopenharmony_ci 290862306a36Sopenharmony_ci if (!VMXNET3_VERSION_GE_7(adapter)) 290962306a36Sopenharmony_ci return; 291062306a36Sopenharmony_ci 291162306a36Sopenharmony_ci cmdInfo->ringBufSize = adapter->ringBufSize; 291262306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 291362306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 291462306a36Sopenharmony_ci VMXNET3_CMD_SET_RING_BUFFER_SIZE); 291562306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 291662306a36Sopenharmony_ci} 291762306a36Sopenharmony_ci 291862306a36Sopenharmony_cistatic void 291962306a36Sopenharmony_civmxnet3_init_coalesce(struct vmxnet3_adapter *adapter) 292062306a36Sopenharmony_ci{ 292162306a36Sopenharmony_ci struct Vmxnet3_DriverShared *shared = adapter->shared; 292262306a36Sopenharmony_ci union Vmxnet3_CmdInfo *cmdInfo = &shared->cu.cmdInfo; 292362306a36Sopenharmony_ci unsigned long flags; 292462306a36Sopenharmony_ci 292562306a36Sopenharmony_ci if (!VMXNET3_VERSION_GE_3(adapter)) 292662306a36Sopenharmony_ci return; 292762306a36Sopenharmony_ci 292862306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 292962306a36Sopenharmony_ci cmdInfo->varConf.confVer = 1; 293062306a36Sopenharmony_ci cmdInfo->varConf.confLen = 293162306a36Sopenharmony_ci cpu_to_le32(sizeof(*adapter->coal_conf)); 293262306a36Sopenharmony_ci cmdInfo->varConf.confPA = cpu_to_le64(adapter->coal_conf_pa); 293362306a36Sopenharmony_ci 293462306a36Sopenharmony_ci if (adapter->default_coal_mode) { 293562306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 293662306a36Sopenharmony_ci VMXNET3_CMD_GET_COALESCE); 293762306a36Sopenharmony_ci } else { 293862306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 293962306a36Sopenharmony_ci VMXNET3_CMD_SET_COALESCE); 294062306a36Sopenharmony_ci } 294162306a36Sopenharmony_ci 294262306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 294362306a36Sopenharmony_ci} 294462306a36Sopenharmony_ci 294562306a36Sopenharmony_cistatic void 294662306a36Sopenharmony_civmxnet3_init_rssfields(struct vmxnet3_adapter *adapter) 294762306a36Sopenharmony_ci{ 294862306a36Sopenharmony_ci struct Vmxnet3_DriverShared *shared = adapter->shared; 294962306a36Sopenharmony_ci union Vmxnet3_CmdInfo *cmdInfo = &shared->cu.cmdInfo; 295062306a36Sopenharmony_ci unsigned long flags; 295162306a36Sopenharmony_ci 295262306a36Sopenharmony_ci if (!VMXNET3_VERSION_GE_4(adapter)) 295362306a36Sopenharmony_ci return; 295462306a36Sopenharmony_ci 295562306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 295662306a36Sopenharmony_ci 295762306a36Sopenharmony_ci if (adapter->default_rss_fields) { 295862306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 295962306a36Sopenharmony_ci VMXNET3_CMD_GET_RSS_FIELDS); 296062306a36Sopenharmony_ci adapter->rss_fields = 296162306a36Sopenharmony_ci VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); 296262306a36Sopenharmony_ci } else { 296362306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_7(adapter)) { 296462306a36Sopenharmony_ci if ((adapter->rss_fields & VMXNET3_RSS_FIELDS_UDPIP4 || 296562306a36Sopenharmony_ci adapter->rss_fields & VMXNET3_RSS_FIELDS_UDPIP6) && 296662306a36Sopenharmony_ci vmxnet3_check_ptcapability(adapter->ptcap_supported[0], 296762306a36Sopenharmony_ci VMXNET3_CAP_UDP_RSS)) { 296862306a36Sopenharmony_ci adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_UDP_RSS; 296962306a36Sopenharmony_ci } else { 297062306a36Sopenharmony_ci adapter->dev_caps[0] &= ~(1UL << VMXNET3_CAP_UDP_RSS); 297162306a36Sopenharmony_ci } 297262306a36Sopenharmony_ci 297362306a36Sopenharmony_ci if ((adapter->rss_fields & VMXNET3_RSS_FIELDS_ESPIP4) && 297462306a36Sopenharmony_ci vmxnet3_check_ptcapability(adapter->ptcap_supported[0], 297562306a36Sopenharmony_ci VMXNET3_CAP_ESP_RSS_IPV4)) { 297662306a36Sopenharmony_ci adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_ESP_RSS_IPV4; 297762306a36Sopenharmony_ci } else { 297862306a36Sopenharmony_ci adapter->dev_caps[0] &= ~(1UL << VMXNET3_CAP_ESP_RSS_IPV4); 297962306a36Sopenharmony_ci } 298062306a36Sopenharmony_ci 298162306a36Sopenharmony_ci if ((adapter->rss_fields & VMXNET3_RSS_FIELDS_ESPIP6) && 298262306a36Sopenharmony_ci vmxnet3_check_ptcapability(adapter->ptcap_supported[0], 298362306a36Sopenharmony_ci VMXNET3_CAP_ESP_RSS_IPV6)) { 298462306a36Sopenharmony_ci adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_ESP_RSS_IPV6; 298562306a36Sopenharmony_ci } else { 298662306a36Sopenharmony_ci adapter->dev_caps[0] &= ~(1UL << VMXNET3_CAP_ESP_RSS_IPV6); 298762306a36Sopenharmony_ci } 298862306a36Sopenharmony_ci 298962306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DCR, adapter->dev_caps[0]); 299062306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_DCR0_REG); 299162306a36Sopenharmony_ci adapter->dev_caps[0] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); 299262306a36Sopenharmony_ci } 299362306a36Sopenharmony_ci cmdInfo->setRssFields = adapter->rss_fields; 299462306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 299562306a36Sopenharmony_ci VMXNET3_CMD_SET_RSS_FIELDS); 299662306a36Sopenharmony_ci /* Not all requested RSS may get applied, so get and 299762306a36Sopenharmony_ci * cache what was actually applied. 299862306a36Sopenharmony_ci */ 299962306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 300062306a36Sopenharmony_ci VMXNET3_CMD_GET_RSS_FIELDS); 300162306a36Sopenharmony_ci adapter->rss_fields = 300262306a36Sopenharmony_ci VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); 300362306a36Sopenharmony_ci } 300462306a36Sopenharmony_ci 300562306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 300662306a36Sopenharmony_ci} 300762306a36Sopenharmony_ci 300862306a36Sopenharmony_ciint 300962306a36Sopenharmony_civmxnet3_activate_dev(struct vmxnet3_adapter *adapter) 301062306a36Sopenharmony_ci{ 301162306a36Sopenharmony_ci int err, i; 301262306a36Sopenharmony_ci u32 ret; 301362306a36Sopenharmony_ci unsigned long flags; 301462306a36Sopenharmony_ci 301562306a36Sopenharmony_ci netdev_dbg(adapter->netdev, "%s: skb_buf_size %d, rx_buf_per_pkt %d," 301662306a36Sopenharmony_ci " ring sizes %u %u %u\n", adapter->netdev->name, 301762306a36Sopenharmony_ci adapter->skb_buf_size, adapter->rx_buf_per_pkt, 301862306a36Sopenharmony_ci adapter->tx_queue[0].tx_ring.size, 301962306a36Sopenharmony_ci adapter->rx_queue[0].rx_ring[0].size, 302062306a36Sopenharmony_ci adapter->rx_queue[0].rx_ring[1].size); 302162306a36Sopenharmony_ci 302262306a36Sopenharmony_ci vmxnet3_tq_init_all(adapter); 302362306a36Sopenharmony_ci err = vmxnet3_rq_init_all(adapter); 302462306a36Sopenharmony_ci if (err) { 302562306a36Sopenharmony_ci netdev_err(adapter->netdev, 302662306a36Sopenharmony_ci "Failed to init rx queue error %d\n", err); 302762306a36Sopenharmony_ci goto rq_err; 302862306a36Sopenharmony_ci } 302962306a36Sopenharmony_ci 303062306a36Sopenharmony_ci err = vmxnet3_request_irqs(adapter); 303162306a36Sopenharmony_ci if (err) { 303262306a36Sopenharmony_ci netdev_err(adapter->netdev, 303362306a36Sopenharmony_ci "Failed to setup irq for error %d\n", err); 303462306a36Sopenharmony_ci goto irq_err; 303562306a36Sopenharmony_ci } 303662306a36Sopenharmony_ci 303762306a36Sopenharmony_ci vmxnet3_setup_driver_shared(adapter); 303862306a36Sopenharmony_ci 303962306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DSAL, VMXNET3_GET_ADDR_LO( 304062306a36Sopenharmony_ci adapter->shared_pa)); 304162306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DSAH, VMXNET3_GET_ADDR_HI( 304262306a36Sopenharmony_ci adapter->shared_pa)); 304362306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 304462306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 304562306a36Sopenharmony_ci VMXNET3_CMD_ACTIVATE_DEV); 304662306a36Sopenharmony_ci ret = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); 304762306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 304862306a36Sopenharmony_ci 304962306a36Sopenharmony_ci if (ret != 0) { 305062306a36Sopenharmony_ci netdev_err(adapter->netdev, 305162306a36Sopenharmony_ci "Failed to activate dev: error %u\n", ret); 305262306a36Sopenharmony_ci err = -EINVAL; 305362306a36Sopenharmony_ci goto activate_err; 305462306a36Sopenharmony_ci } 305562306a36Sopenharmony_ci 305662306a36Sopenharmony_ci vmxnet3_init_bufsize(adapter); 305762306a36Sopenharmony_ci vmxnet3_init_coalesce(adapter); 305862306a36Sopenharmony_ci vmxnet3_init_rssfields(adapter); 305962306a36Sopenharmony_ci 306062306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) { 306162306a36Sopenharmony_ci VMXNET3_WRITE_BAR0_REG(adapter, 306262306a36Sopenharmony_ci adapter->rx_prod_offset + i * VMXNET3_REG_ALIGN, 306362306a36Sopenharmony_ci adapter->rx_queue[i].rx_ring[0].next2fill); 306462306a36Sopenharmony_ci VMXNET3_WRITE_BAR0_REG(adapter, (adapter->rx_prod2_offset + 306562306a36Sopenharmony_ci (i * VMXNET3_REG_ALIGN)), 306662306a36Sopenharmony_ci adapter->rx_queue[i].rx_ring[1].next2fill); 306762306a36Sopenharmony_ci } 306862306a36Sopenharmony_ci 306962306a36Sopenharmony_ci /* Apply the rx filter settins last. */ 307062306a36Sopenharmony_ci vmxnet3_set_mc(adapter->netdev); 307162306a36Sopenharmony_ci 307262306a36Sopenharmony_ci /* 307362306a36Sopenharmony_ci * Check link state when first activating device. It will start the 307462306a36Sopenharmony_ci * tx queue if the link is up. 307562306a36Sopenharmony_ci */ 307662306a36Sopenharmony_ci vmxnet3_check_link(adapter, true); 307762306a36Sopenharmony_ci netif_tx_wake_all_queues(adapter->netdev); 307862306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) 307962306a36Sopenharmony_ci napi_enable(&adapter->rx_queue[i].napi); 308062306a36Sopenharmony_ci vmxnet3_enable_all_intrs(adapter); 308162306a36Sopenharmony_ci clear_bit(VMXNET3_STATE_BIT_QUIESCED, &adapter->state); 308262306a36Sopenharmony_ci return 0; 308362306a36Sopenharmony_ci 308462306a36Sopenharmony_ciactivate_err: 308562306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DSAL, 0); 308662306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DSAH, 0); 308762306a36Sopenharmony_ci vmxnet3_free_irqs(adapter); 308862306a36Sopenharmony_ciirq_err: 308962306a36Sopenharmony_cirq_err: 309062306a36Sopenharmony_ci /* free up buffers we allocated */ 309162306a36Sopenharmony_ci vmxnet3_rq_cleanup_all(adapter); 309262306a36Sopenharmony_ci return err; 309362306a36Sopenharmony_ci} 309462306a36Sopenharmony_ci 309562306a36Sopenharmony_ci 309662306a36Sopenharmony_civoid 309762306a36Sopenharmony_civmxnet3_reset_dev(struct vmxnet3_adapter *adapter) 309862306a36Sopenharmony_ci{ 309962306a36Sopenharmony_ci unsigned long flags; 310062306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 310162306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_RESET_DEV); 310262306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 310362306a36Sopenharmony_ci} 310462306a36Sopenharmony_ci 310562306a36Sopenharmony_ci 310662306a36Sopenharmony_ciint 310762306a36Sopenharmony_civmxnet3_quiesce_dev(struct vmxnet3_adapter *adapter) 310862306a36Sopenharmony_ci{ 310962306a36Sopenharmony_ci int i; 311062306a36Sopenharmony_ci unsigned long flags; 311162306a36Sopenharmony_ci if (test_and_set_bit(VMXNET3_STATE_BIT_QUIESCED, &adapter->state)) 311262306a36Sopenharmony_ci return 0; 311362306a36Sopenharmony_ci 311462306a36Sopenharmony_ci 311562306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 311662306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 311762306a36Sopenharmony_ci VMXNET3_CMD_QUIESCE_DEV); 311862306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 311962306a36Sopenharmony_ci vmxnet3_disable_all_intrs(adapter); 312062306a36Sopenharmony_ci 312162306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) 312262306a36Sopenharmony_ci napi_disable(&adapter->rx_queue[i].napi); 312362306a36Sopenharmony_ci netif_tx_disable(adapter->netdev); 312462306a36Sopenharmony_ci adapter->link_speed = 0; 312562306a36Sopenharmony_ci netif_carrier_off(adapter->netdev); 312662306a36Sopenharmony_ci 312762306a36Sopenharmony_ci vmxnet3_tq_cleanup_all(adapter); 312862306a36Sopenharmony_ci vmxnet3_rq_cleanup_all(adapter); 312962306a36Sopenharmony_ci vmxnet3_free_irqs(adapter); 313062306a36Sopenharmony_ci return 0; 313162306a36Sopenharmony_ci} 313262306a36Sopenharmony_ci 313362306a36Sopenharmony_ci 313462306a36Sopenharmony_cistatic void 313562306a36Sopenharmony_civmxnet3_write_mac_addr(struct vmxnet3_adapter *adapter, const u8 *mac) 313662306a36Sopenharmony_ci{ 313762306a36Sopenharmony_ci u32 tmp; 313862306a36Sopenharmony_ci 313962306a36Sopenharmony_ci tmp = *(u32 *)mac; 314062306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_MACL, tmp); 314162306a36Sopenharmony_ci 314262306a36Sopenharmony_ci tmp = (mac[5] << 8) | mac[4]; 314362306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_MACH, tmp); 314462306a36Sopenharmony_ci} 314562306a36Sopenharmony_ci 314662306a36Sopenharmony_ci 314762306a36Sopenharmony_cistatic int 314862306a36Sopenharmony_civmxnet3_set_mac_addr(struct net_device *netdev, void *p) 314962306a36Sopenharmony_ci{ 315062306a36Sopenharmony_ci struct sockaddr *addr = p; 315162306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 315262306a36Sopenharmony_ci 315362306a36Sopenharmony_ci dev_addr_set(netdev, addr->sa_data); 315462306a36Sopenharmony_ci vmxnet3_write_mac_addr(adapter, addr->sa_data); 315562306a36Sopenharmony_ci 315662306a36Sopenharmony_ci return 0; 315762306a36Sopenharmony_ci} 315862306a36Sopenharmony_ci 315962306a36Sopenharmony_ci 316062306a36Sopenharmony_ci/* ==================== initialization and cleanup routines ============ */ 316162306a36Sopenharmony_ci 316262306a36Sopenharmony_cistatic int 316362306a36Sopenharmony_civmxnet3_alloc_pci_resources(struct vmxnet3_adapter *adapter) 316462306a36Sopenharmony_ci{ 316562306a36Sopenharmony_ci int err; 316662306a36Sopenharmony_ci unsigned long mmio_start, mmio_len; 316762306a36Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 316862306a36Sopenharmony_ci 316962306a36Sopenharmony_ci err = pci_enable_device(pdev); 317062306a36Sopenharmony_ci if (err) { 317162306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to enable adapter: error %d\n", err); 317262306a36Sopenharmony_ci return err; 317362306a36Sopenharmony_ci } 317462306a36Sopenharmony_ci 317562306a36Sopenharmony_ci err = pci_request_selected_regions(pdev, (1 << 2) - 1, 317662306a36Sopenharmony_ci vmxnet3_driver_name); 317762306a36Sopenharmony_ci if (err) { 317862306a36Sopenharmony_ci dev_err(&pdev->dev, 317962306a36Sopenharmony_ci "Failed to request region for adapter: error %d\n", err); 318062306a36Sopenharmony_ci goto err_enable_device; 318162306a36Sopenharmony_ci } 318262306a36Sopenharmony_ci 318362306a36Sopenharmony_ci pci_set_master(pdev); 318462306a36Sopenharmony_ci 318562306a36Sopenharmony_ci mmio_start = pci_resource_start(pdev, 0); 318662306a36Sopenharmony_ci mmio_len = pci_resource_len(pdev, 0); 318762306a36Sopenharmony_ci adapter->hw_addr0 = ioremap(mmio_start, mmio_len); 318862306a36Sopenharmony_ci if (!adapter->hw_addr0) { 318962306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to map bar0\n"); 319062306a36Sopenharmony_ci err = -EIO; 319162306a36Sopenharmony_ci goto err_ioremap; 319262306a36Sopenharmony_ci } 319362306a36Sopenharmony_ci 319462306a36Sopenharmony_ci mmio_start = pci_resource_start(pdev, 1); 319562306a36Sopenharmony_ci mmio_len = pci_resource_len(pdev, 1); 319662306a36Sopenharmony_ci adapter->hw_addr1 = ioremap(mmio_start, mmio_len); 319762306a36Sopenharmony_ci if (!adapter->hw_addr1) { 319862306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to map bar1\n"); 319962306a36Sopenharmony_ci err = -EIO; 320062306a36Sopenharmony_ci goto err_bar1; 320162306a36Sopenharmony_ci } 320262306a36Sopenharmony_ci return 0; 320362306a36Sopenharmony_ci 320462306a36Sopenharmony_cierr_bar1: 320562306a36Sopenharmony_ci iounmap(adapter->hw_addr0); 320662306a36Sopenharmony_cierr_ioremap: 320762306a36Sopenharmony_ci pci_release_selected_regions(pdev, (1 << 2) - 1); 320862306a36Sopenharmony_cierr_enable_device: 320962306a36Sopenharmony_ci pci_disable_device(pdev); 321062306a36Sopenharmony_ci return err; 321162306a36Sopenharmony_ci} 321262306a36Sopenharmony_ci 321362306a36Sopenharmony_ci 321462306a36Sopenharmony_cistatic void 321562306a36Sopenharmony_civmxnet3_free_pci_resources(struct vmxnet3_adapter *adapter) 321662306a36Sopenharmony_ci{ 321762306a36Sopenharmony_ci BUG_ON(!adapter->pdev); 321862306a36Sopenharmony_ci 321962306a36Sopenharmony_ci iounmap(adapter->hw_addr0); 322062306a36Sopenharmony_ci iounmap(adapter->hw_addr1); 322162306a36Sopenharmony_ci pci_release_selected_regions(adapter->pdev, (1 << 2) - 1); 322262306a36Sopenharmony_ci pci_disable_device(adapter->pdev); 322362306a36Sopenharmony_ci} 322462306a36Sopenharmony_ci 322562306a36Sopenharmony_ci 322662306a36Sopenharmony_civoid 322762306a36Sopenharmony_civmxnet3_adjust_rx_ring_size(struct vmxnet3_adapter *adapter) 322862306a36Sopenharmony_ci{ 322962306a36Sopenharmony_ci size_t sz, i, ring0_size, ring1_size, comp_size; 323062306a36Sopenharmony_ci /* With version7 ring1 will have only T0 buffers */ 323162306a36Sopenharmony_ci if (!VMXNET3_VERSION_GE_7(adapter)) { 323262306a36Sopenharmony_ci if (adapter->netdev->mtu <= VMXNET3_MAX_SKB_BUF_SIZE - 323362306a36Sopenharmony_ci VMXNET3_MAX_ETH_HDR_SIZE) { 323462306a36Sopenharmony_ci adapter->skb_buf_size = adapter->netdev->mtu + 323562306a36Sopenharmony_ci VMXNET3_MAX_ETH_HDR_SIZE; 323662306a36Sopenharmony_ci if (adapter->skb_buf_size < VMXNET3_MIN_T0_BUF_SIZE) 323762306a36Sopenharmony_ci adapter->skb_buf_size = VMXNET3_MIN_T0_BUF_SIZE; 323862306a36Sopenharmony_ci 323962306a36Sopenharmony_ci adapter->rx_buf_per_pkt = 1; 324062306a36Sopenharmony_ci } else { 324162306a36Sopenharmony_ci adapter->skb_buf_size = VMXNET3_MAX_SKB_BUF_SIZE; 324262306a36Sopenharmony_ci sz = adapter->netdev->mtu - VMXNET3_MAX_SKB_BUF_SIZE + 324362306a36Sopenharmony_ci VMXNET3_MAX_ETH_HDR_SIZE; 324462306a36Sopenharmony_ci adapter->rx_buf_per_pkt = 1 + (sz + PAGE_SIZE - 1) / PAGE_SIZE; 324562306a36Sopenharmony_ci } 324662306a36Sopenharmony_ci } else { 324762306a36Sopenharmony_ci adapter->skb_buf_size = min((int)adapter->netdev->mtu + VMXNET3_MAX_ETH_HDR_SIZE, 324862306a36Sopenharmony_ci VMXNET3_MAX_SKB_BUF_SIZE); 324962306a36Sopenharmony_ci adapter->rx_buf_per_pkt = 1; 325062306a36Sopenharmony_ci adapter->ringBufSize.ring1BufSizeType0 = cpu_to_le16(adapter->skb_buf_size); 325162306a36Sopenharmony_ci adapter->ringBufSize.ring1BufSizeType1 = 0; 325262306a36Sopenharmony_ci adapter->ringBufSize.ring2BufSizeType1 = cpu_to_le16(PAGE_SIZE); 325362306a36Sopenharmony_ci } 325462306a36Sopenharmony_ci 325562306a36Sopenharmony_ci /* 325662306a36Sopenharmony_ci * for simplicity, force the ring0 size to be a multiple of 325762306a36Sopenharmony_ci * rx_buf_per_pkt * VMXNET3_RING_SIZE_ALIGN 325862306a36Sopenharmony_ci */ 325962306a36Sopenharmony_ci sz = adapter->rx_buf_per_pkt * VMXNET3_RING_SIZE_ALIGN; 326062306a36Sopenharmony_ci ring0_size = adapter->rx_queue[0].rx_ring[0].size; 326162306a36Sopenharmony_ci ring0_size = (ring0_size + sz - 1) / sz * sz; 326262306a36Sopenharmony_ci ring0_size = min_t(u32, ring0_size, VMXNET3_RX_RING_MAX_SIZE / 326362306a36Sopenharmony_ci sz * sz); 326462306a36Sopenharmony_ci ring1_size = adapter->rx_queue[0].rx_ring[1].size; 326562306a36Sopenharmony_ci ring1_size = (ring1_size + sz - 1) / sz * sz; 326662306a36Sopenharmony_ci ring1_size = min_t(u32, ring1_size, VMXNET3_RX_RING2_MAX_SIZE / 326762306a36Sopenharmony_ci sz * sz); 326862306a36Sopenharmony_ci /* For v7 and later, keep ring size power of 2 for UPT */ 326962306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_7(adapter)) { 327062306a36Sopenharmony_ci ring0_size = rounddown_pow_of_two(ring0_size); 327162306a36Sopenharmony_ci ring1_size = rounddown_pow_of_two(ring1_size); 327262306a36Sopenharmony_ci } 327362306a36Sopenharmony_ci comp_size = ring0_size + ring1_size; 327462306a36Sopenharmony_ci 327562306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) { 327662306a36Sopenharmony_ci struct vmxnet3_rx_queue *rq = &adapter->rx_queue[i]; 327762306a36Sopenharmony_ci 327862306a36Sopenharmony_ci rq->rx_ring[0].size = ring0_size; 327962306a36Sopenharmony_ci rq->rx_ring[1].size = ring1_size; 328062306a36Sopenharmony_ci rq->comp_ring.size = comp_size; 328162306a36Sopenharmony_ci } 328262306a36Sopenharmony_ci} 328362306a36Sopenharmony_ci 328462306a36Sopenharmony_ci 328562306a36Sopenharmony_ciint 328662306a36Sopenharmony_civmxnet3_create_queues(struct vmxnet3_adapter *adapter, u32 tx_ring_size, 328762306a36Sopenharmony_ci u32 rx_ring_size, u32 rx_ring2_size, 328862306a36Sopenharmony_ci u16 txdata_desc_size, u16 rxdata_desc_size) 328962306a36Sopenharmony_ci{ 329062306a36Sopenharmony_ci int err = 0, i; 329162306a36Sopenharmony_ci 329262306a36Sopenharmony_ci for (i = 0; i < adapter->num_tx_queues; i++) { 329362306a36Sopenharmony_ci struct vmxnet3_tx_queue *tq = &adapter->tx_queue[i]; 329462306a36Sopenharmony_ci tq->tx_ring.size = tx_ring_size; 329562306a36Sopenharmony_ci tq->data_ring.size = tx_ring_size; 329662306a36Sopenharmony_ci tq->comp_ring.size = tx_ring_size; 329762306a36Sopenharmony_ci tq->txdata_desc_size = txdata_desc_size; 329862306a36Sopenharmony_ci tq->shared = &adapter->tqd_start[i].ctrl; 329962306a36Sopenharmony_ci tq->stopped = true; 330062306a36Sopenharmony_ci tq->adapter = adapter; 330162306a36Sopenharmony_ci tq->qid = i; 330262306a36Sopenharmony_ci err = vmxnet3_tq_create(tq, adapter); 330362306a36Sopenharmony_ci /* 330462306a36Sopenharmony_ci * Too late to change num_tx_queues. We cannot do away with 330562306a36Sopenharmony_ci * lesser number of queues than what we asked for 330662306a36Sopenharmony_ci */ 330762306a36Sopenharmony_ci if (err) 330862306a36Sopenharmony_ci goto queue_err; 330962306a36Sopenharmony_ci } 331062306a36Sopenharmony_ci 331162306a36Sopenharmony_ci adapter->rx_queue[0].rx_ring[0].size = rx_ring_size; 331262306a36Sopenharmony_ci adapter->rx_queue[0].rx_ring[1].size = rx_ring2_size; 331362306a36Sopenharmony_ci vmxnet3_adjust_rx_ring_size(adapter); 331462306a36Sopenharmony_ci 331562306a36Sopenharmony_ci adapter->rxdataring_enabled = VMXNET3_VERSION_GE_3(adapter); 331662306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) { 331762306a36Sopenharmony_ci struct vmxnet3_rx_queue *rq = &adapter->rx_queue[i]; 331862306a36Sopenharmony_ci /* qid and qid2 for rx queues will be assigned later when num 331962306a36Sopenharmony_ci * of rx queues is finalized after allocating intrs */ 332062306a36Sopenharmony_ci rq->shared = &adapter->rqd_start[i].ctrl; 332162306a36Sopenharmony_ci rq->adapter = adapter; 332262306a36Sopenharmony_ci rq->data_ring.desc_size = rxdata_desc_size; 332362306a36Sopenharmony_ci err = vmxnet3_rq_create(rq, adapter); 332462306a36Sopenharmony_ci if (err) { 332562306a36Sopenharmony_ci if (i == 0) { 332662306a36Sopenharmony_ci netdev_err(adapter->netdev, 332762306a36Sopenharmony_ci "Could not allocate any rx queues. " 332862306a36Sopenharmony_ci "Aborting.\n"); 332962306a36Sopenharmony_ci goto queue_err; 333062306a36Sopenharmony_ci } else { 333162306a36Sopenharmony_ci netdev_info(adapter->netdev, 333262306a36Sopenharmony_ci "Number of rx queues changed " 333362306a36Sopenharmony_ci "to : %d.\n", i); 333462306a36Sopenharmony_ci adapter->num_rx_queues = i; 333562306a36Sopenharmony_ci err = 0; 333662306a36Sopenharmony_ci break; 333762306a36Sopenharmony_ci } 333862306a36Sopenharmony_ci } 333962306a36Sopenharmony_ci } 334062306a36Sopenharmony_ci 334162306a36Sopenharmony_ci if (!adapter->rxdataring_enabled) 334262306a36Sopenharmony_ci vmxnet3_rq_destroy_all_rxdataring(adapter); 334362306a36Sopenharmony_ci 334462306a36Sopenharmony_ci return err; 334562306a36Sopenharmony_ciqueue_err: 334662306a36Sopenharmony_ci vmxnet3_tq_destroy_all(adapter); 334762306a36Sopenharmony_ci return err; 334862306a36Sopenharmony_ci} 334962306a36Sopenharmony_ci 335062306a36Sopenharmony_cistatic int 335162306a36Sopenharmony_civmxnet3_open(struct net_device *netdev) 335262306a36Sopenharmony_ci{ 335362306a36Sopenharmony_ci struct vmxnet3_adapter *adapter; 335462306a36Sopenharmony_ci int err, i; 335562306a36Sopenharmony_ci 335662306a36Sopenharmony_ci adapter = netdev_priv(netdev); 335762306a36Sopenharmony_ci 335862306a36Sopenharmony_ci for (i = 0; i < adapter->num_tx_queues; i++) 335962306a36Sopenharmony_ci spin_lock_init(&adapter->tx_queue[i].tx_lock); 336062306a36Sopenharmony_ci 336162306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_3(adapter)) { 336262306a36Sopenharmony_ci unsigned long flags; 336362306a36Sopenharmony_ci u16 txdata_desc_size; 336462306a36Sopenharmony_ci 336562306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 336662306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 336762306a36Sopenharmony_ci VMXNET3_CMD_GET_TXDATA_DESC_SIZE); 336862306a36Sopenharmony_ci txdata_desc_size = VMXNET3_READ_BAR1_REG(adapter, 336962306a36Sopenharmony_ci VMXNET3_REG_CMD); 337062306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 337162306a36Sopenharmony_ci 337262306a36Sopenharmony_ci if ((txdata_desc_size < VMXNET3_TXDATA_DESC_MIN_SIZE) || 337362306a36Sopenharmony_ci (txdata_desc_size > VMXNET3_TXDATA_DESC_MAX_SIZE) || 337462306a36Sopenharmony_ci (txdata_desc_size & VMXNET3_TXDATA_DESC_SIZE_MASK)) { 337562306a36Sopenharmony_ci adapter->txdata_desc_size = 337662306a36Sopenharmony_ci sizeof(struct Vmxnet3_TxDataDesc); 337762306a36Sopenharmony_ci } else { 337862306a36Sopenharmony_ci adapter->txdata_desc_size = txdata_desc_size; 337962306a36Sopenharmony_ci } 338062306a36Sopenharmony_ci } else { 338162306a36Sopenharmony_ci adapter->txdata_desc_size = sizeof(struct Vmxnet3_TxDataDesc); 338262306a36Sopenharmony_ci } 338362306a36Sopenharmony_ci 338462306a36Sopenharmony_ci err = vmxnet3_create_queues(adapter, 338562306a36Sopenharmony_ci adapter->tx_ring_size, 338662306a36Sopenharmony_ci adapter->rx_ring_size, 338762306a36Sopenharmony_ci adapter->rx_ring2_size, 338862306a36Sopenharmony_ci adapter->txdata_desc_size, 338962306a36Sopenharmony_ci adapter->rxdata_desc_size); 339062306a36Sopenharmony_ci if (err) 339162306a36Sopenharmony_ci goto queue_err; 339262306a36Sopenharmony_ci 339362306a36Sopenharmony_ci err = vmxnet3_activate_dev(adapter); 339462306a36Sopenharmony_ci if (err) 339562306a36Sopenharmony_ci goto activate_err; 339662306a36Sopenharmony_ci 339762306a36Sopenharmony_ci return 0; 339862306a36Sopenharmony_ci 339962306a36Sopenharmony_ciactivate_err: 340062306a36Sopenharmony_ci vmxnet3_rq_destroy_all(adapter); 340162306a36Sopenharmony_ci vmxnet3_tq_destroy_all(adapter); 340262306a36Sopenharmony_ciqueue_err: 340362306a36Sopenharmony_ci return err; 340462306a36Sopenharmony_ci} 340562306a36Sopenharmony_ci 340662306a36Sopenharmony_ci 340762306a36Sopenharmony_cistatic int 340862306a36Sopenharmony_civmxnet3_close(struct net_device *netdev) 340962306a36Sopenharmony_ci{ 341062306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 341162306a36Sopenharmony_ci 341262306a36Sopenharmony_ci /* 341362306a36Sopenharmony_ci * Reset_work may be in the middle of resetting the device, wait for its 341462306a36Sopenharmony_ci * completion. 341562306a36Sopenharmony_ci */ 341662306a36Sopenharmony_ci while (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state)) 341762306a36Sopenharmony_ci usleep_range(1000, 2000); 341862306a36Sopenharmony_ci 341962306a36Sopenharmony_ci vmxnet3_quiesce_dev(adapter); 342062306a36Sopenharmony_ci 342162306a36Sopenharmony_ci vmxnet3_rq_destroy_all(adapter); 342262306a36Sopenharmony_ci vmxnet3_tq_destroy_all(adapter); 342362306a36Sopenharmony_ci 342462306a36Sopenharmony_ci clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state); 342562306a36Sopenharmony_ci 342662306a36Sopenharmony_ci 342762306a36Sopenharmony_ci return 0; 342862306a36Sopenharmony_ci} 342962306a36Sopenharmony_ci 343062306a36Sopenharmony_ci 343162306a36Sopenharmony_civoid 343262306a36Sopenharmony_civmxnet3_force_close(struct vmxnet3_adapter *adapter) 343362306a36Sopenharmony_ci{ 343462306a36Sopenharmony_ci int i; 343562306a36Sopenharmony_ci 343662306a36Sopenharmony_ci /* 343762306a36Sopenharmony_ci * we must clear VMXNET3_STATE_BIT_RESETTING, otherwise 343862306a36Sopenharmony_ci * vmxnet3_close() will deadlock. 343962306a36Sopenharmony_ci */ 344062306a36Sopenharmony_ci BUG_ON(test_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state)); 344162306a36Sopenharmony_ci 344262306a36Sopenharmony_ci /* we need to enable NAPI, otherwise dev_close will deadlock */ 344362306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) 344462306a36Sopenharmony_ci napi_enable(&adapter->rx_queue[i].napi); 344562306a36Sopenharmony_ci /* 344662306a36Sopenharmony_ci * Need to clear the quiesce bit to ensure that vmxnet3_close 344762306a36Sopenharmony_ci * can quiesce the device properly 344862306a36Sopenharmony_ci */ 344962306a36Sopenharmony_ci clear_bit(VMXNET3_STATE_BIT_QUIESCED, &adapter->state); 345062306a36Sopenharmony_ci dev_close(adapter->netdev); 345162306a36Sopenharmony_ci} 345262306a36Sopenharmony_ci 345362306a36Sopenharmony_ci 345462306a36Sopenharmony_cistatic int 345562306a36Sopenharmony_civmxnet3_change_mtu(struct net_device *netdev, int new_mtu) 345662306a36Sopenharmony_ci{ 345762306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 345862306a36Sopenharmony_ci int err = 0; 345962306a36Sopenharmony_ci 346062306a36Sopenharmony_ci netdev->mtu = new_mtu; 346162306a36Sopenharmony_ci 346262306a36Sopenharmony_ci /* 346362306a36Sopenharmony_ci * Reset_work may be in the middle of resetting the device, wait for its 346462306a36Sopenharmony_ci * completion. 346562306a36Sopenharmony_ci */ 346662306a36Sopenharmony_ci while (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state)) 346762306a36Sopenharmony_ci usleep_range(1000, 2000); 346862306a36Sopenharmony_ci 346962306a36Sopenharmony_ci if (netif_running(netdev)) { 347062306a36Sopenharmony_ci vmxnet3_quiesce_dev(adapter); 347162306a36Sopenharmony_ci vmxnet3_reset_dev(adapter); 347262306a36Sopenharmony_ci 347362306a36Sopenharmony_ci /* we need to re-create the rx queue based on the new mtu */ 347462306a36Sopenharmony_ci vmxnet3_rq_destroy_all(adapter); 347562306a36Sopenharmony_ci vmxnet3_adjust_rx_ring_size(adapter); 347662306a36Sopenharmony_ci err = vmxnet3_rq_create_all(adapter); 347762306a36Sopenharmony_ci if (err) { 347862306a36Sopenharmony_ci netdev_err(netdev, 347962306a36Sopenharmony_ci "failed to re-create rx queues, " 348062306a36Sopenharmony_ci " error %d. Closing it.\n", err); 348162306a36Sopenharmony_ci goto out; 348262306a36Sopenharmony_ci } 348362306a36Sopenharmony_ci 348462306a36Sopenharmony_ci err = vmxnet3_activate_dev(adapter); 348562306a36Sopenharmony_ci if (err) { 348662306a36Sopenharmony_ci netdev_err(netdev, 348762306a36Sopenharmony_ci "failed to re-activate, error %d. " 348862306a36Sopenharmony_ci "Closing it\n", err); 348962306a36Sopenharmony_ci goto out; 349062306a36Sopenharmony_ci } 349162306a36Sopenharmony_ci } 349262306a36Sopenharmony_ci 349362306a36Sopenharmony_ciout: 349462306a36Sopenharmony_ci clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state); 349562306a36Sopenharmony_ci if (err) 349662306a36Sopenharmony_ci vmxnet3_force_close(adapter); 349762306a36Sopenharmony_ci 349862306a36Sopenharmony_ci return err; 349962306a36Sopenharmony_ci} 350062306a36Sopenharmony_ci 350162306a36Sopenharmony_ci 350262306a36Sopenharmony_cistatic void 350362306a36Sopenharmony_civmxnet3_declare_features(struct vmxnet3_adapter *adapter) 350462306a36Sopenharmony_ci{ 350562306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 350662306a36Sopenharmony_ci 350762306a36Sopenharmony_ci netdev->hw_features = NETIF_F_SG | NETIF_F_RXCSUM | 350862306a36Sopenharmony_ci NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_CTAG_TX | 350962306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_TSO | NETIF_F_TSO6 | 351062306a36Sopenharmony_ci NETIF_F_LRO | NETIF_F_HIGHDMA; 351162306a36Sopenharmony_ci 351262306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_4(adapter)) { 351362306a36Sopenharmony_ci netdev->hw_features |= NETIF_F_GSO_UDP_TUNNEL | 351462306a36Sopenharmony_ci NETIF_F_GSO_UDP_TUNNEL_CSUM; 351562306a36Sopenharmony_ci 351662306a36Sopenharmony_ci netdev->hw_enc_features = NETIF_F_SG | NETIF_F_RXCSUM | 351762306a36Sopenharmony_ci NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_CTAG_TX | 351862306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_TSO | NETIF_F_TSO6 | 351962306a36Sopenharmony_ci NETIF_F_LRO | NETIF_F_GSO_UDP_TUNNEL | 352062306a36Sopenharmony_ci NETIF_F_GSO_UDP_TUNNEL_CSUM; 352162306a36Sopenharmony_ci } 352262306a36Sopenharmony_ci 352362306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_7(adapter)) { 352462306a36Sopenharmony_ci unsigned long flags; 352562306a36Sopenharmony_ci 352662306a36Sopenharmony_ci if (vmxnet3_check_ptcapability(adapter->ptcap_supported[0], 352762306a36Sopenharmony_ci VMXNET3_CAP_GENEVE_CHECKSUM_OFFLOAD)) { 352862306a36Sopenharmony_ci adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_GENEVE_CHECKSUM_OFFLOAD; 352962306a36Sopenharmony_ci } 353062306a36Sopenharmony_ci if (vmxnet3_check_ptcapability(adapter->ptcap_supported[0], 353162306a36Sopenharmony_ci VMXNET3_CAP_VXLAN_CHECKSUM_OFFLOAD)) { 353262306a36Sopenharmony_ci adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_VXLAN_CHECKSUM_OFFLOAD; 353362306a36Sopenharmony_ci } 353462306a36Sopenharmony_ci if (vmxnet3_check_ptcapability(adapter->ptcap_supported[0], 353562306a36Sopenharmony_ci VMXNET3_CAP_GENEVE_TSO)) { 353662306a36Sopenharmony_ci adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_GENEVE_TSO; 353762306a36Sopenharmony_ci } 353862306a36Sopenharmony_ci if (vmxnet3_check_ptcapability(adapter->ptcap_supported[0], 353962306a36Sopenharmony_ci VMXNET3_CAP_VXLAN_TSO)) { 354062306a36Sopenharmony_ci adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_VXLAN_TSO; 354162306a36Sopenharmony_ci } 354262306a36Sopenharmony_ci if (vmxnet3_check_ptcapability(adapter->ptcap_supported[0], 354362306a36Sopenharmony_ci VMXNET3_CAP_GENEVE_OUTER_CHECKSUM_OFFLOAD)) { 354462306a36Sopenharmony_ci adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_GENEVE_OUTER_CHECKSUM_OFFLOAD; 354562306a36Sopenharmony_ci } 354662306a36Sopenharmony_ci if (vmxnet3_check_ptcapability(adapter->ptcap_supported[0], 354762306a36Sopenharmony_ci VMXNET3_CAP_VXLAN_OUTER_CHECKSUM_OFFLOAD)) { 354862306a36Sopenharmony_ci adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_VXLAN_OUTER_CHECKSUM_OFFLOAD; 354962306a36Sopenharmony_ci } 355062306a36Sopenharmony_ci 355162306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DCR, adapter->dev_caps[0]); 355262306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 355362306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_DCR0_REG); 355462306a36Sopenharmony_ci adapter->dev_caps[0] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); 355562306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 355662306a36Sopenharmony_ci 355762306a36Sopenharmony_ci if (!(adapter->dev_caps[0] & (1UL << VMXNET3_CAP_GENEVE_CHECKSUM_OFFLOAD)) && 355862306a36Sopenharmony_ci !(adapter->dev_caps[0] & (1UL << VMXNET3_CAP_VXLAN_CHECKSUM_OFFLOAD)) && 355962306a36Sopenharmony_ci !(adapter->dev_caps[0] & (1UL << VMXNET3_CAP_GENEVE_TSO)) && 356062306a36Sopenharmony_ci !(adapter->dev_caps[0] & (1UL << VMXNET3_CAP_VXLAN_TSO))) { 356162306a36Sopenharmony_ci netdev->hw_enc_features &= ~NETIF_F_GSO_UDP_TUNNEL; 356262306a36Sopenharmony_ci netdev->hw_features &= ~NETIF_F_GSO_UDP_TUNNEL; 356362306a36Sopenharmony_ci } 356462306a36Sopenharmony_ci if (!(adapter->dev_caps[0] & (1UL << VMXNET3_CAP_GENEVE_OUTER_CHECKSUM_OFFLOAD)) && 356562306a36Sopenharmony_ci !(adapter->dev_caps[0] & (1UL << VMXNET3_CAP_VXLAN_OUTER_CHECKSUM_OFFLOAD))) { 356662306a36Sopenharmony_ci netdev->hw_enc_features &= ~NETIF_F_GSO_UDP_TUNNEL_CSUM; 356762306a36Sopenharmony_ci netdev->hw_features &= ~NETIF_F_GSO_UDP_TUNNEL_CSUM; 356862306a36Sopenharmony_ci } 356962306a36Sopenharmony_ci } 357062306a36Sopenharmony_ci 357162306a36Sopenharmony_ci netdev->vlan_features = netdev->hw_features & 357262306a36Sopenharmony_ci ~(NETIF_F_HW_VLAN_CTAG_TX | 357362306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_RX); 357462306a36Sopenharmony_ci netdev->features = netdev->hw_features | NETIF_F_HW_VLAN_CTAG_FILTER; 357562306a36Sopenharmony_ci} 357662306a36Sopenharmony_ci 357762306a36Sopenharmony_ci 357862306a36Sopenharmony_cistatic void 357962306a36Sopenharmony_civmxnet3_read_mac_addr(struct vmxnet3_adapter *adapter, u8 *mac) 358062306a36Sopenharmony_ci{ 358162306a36Sopenharmony_ci u32 tmp; 358262306a36Sopenharmony_ci 358362306a36Sopenharmony_ci tmp = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_MACL); 358462306a36Sopenharmony_ci *(u32 *)mac = tmp; 358562306a36Sopenharmony_ci 358662306a36Sopenharmony_ci tmp = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_MACH); 358762306a36Sopenharmony_ci mac[4] = tmp & 0xff; 358862306a36Sopenharmony_ci mac[5] = (tmp >> 8) & 0xff; 358962306a36Sopenharmony_ci} 359062306a36Sopenharmony_ci 359162306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI 359262306a36Sopenharmony_ci 359362306a36Sopenharmony_ci/* 359462306a36Sopenharmony_ci * Enable MSIx vectors. 359562306a36Sopenharmony_ci * Returns : 359662306a36Sopenharmony_ci * VMXNET3_LINUX_MIN_MSIX_VECT when only minimum number of vectors required 359762306a36Sopenharmony_ci * were enabled. 359862306a36Sopenharmony_ci * number of vectors which were enabled otherwise (this number is greater 359962306a36Sopenharmony_ci * than VMXNET3_LINUX_MIN_MSIX_VECT) 360062306a36Sopenharmony_ci */ 360162306a36Sopenharmony_ci 360262306a36Sopenharmony_cistatic int 360362306a36Sopenharmony_civmxnet3_acquire_msix_vectors(struct vmxnet3_adapter *adapter, int nvec) 360462306a36Sopenharmony_ci{ 360562306a36Sopenharmony_ci int ret = pci_enable_msix_range(adapter->pdev, 360662306a36Sopenharmony_ci adapter->intr.msix_entries, nvec, nvec); 360762306a36Sopenharmony_ci 360862306a36Sopenharmony_ci if (ret == -ENOSPC && nvec > VMXNET3_LINUX_MIN_MSIX_VECT) { 360962306a36Sopenharmony_ci dev_err(&adapter->netdev->dev, 361062306a36Sopenharmony_ci "Failed to enable %d MSI-X, trying %d\n", 361162306a36Sopenharmony_ci nvec, VMXNET3_LINUX_MIN_MSIX_VECT); 361262306a36Sopenharmony_ci 361362306a36Sopenharmony_ci ret = pci_enable_msix_range(adapter->pdev, 361462306a36Sopenharmony_ci adapter->intr.msix_entries, 361562306a36Sopenharmony_ci VMXNET3_LINUX_MIN_MSIX_VECT, 361662306a36Sopenharmony_ci VMXNET3_LINUX_MIN_MSIX_VECT); 361762306a36Sopenharmony_ci } 361862306a36Sopenharmony_ci 361962306a36Sopenharmony_ci if (ret < 0) { 362062306a36Sopenharmony_ci dev_err(&adapter->netdev->dev, 362162306a36Sopenharmony_ci "Failed to enable MSI-X, error: %d\n", ret); 362262306a36Sopenharmony_ci } 362362306a36Sopenharmony_ci 362462306a36Sopenharmony_ci return ret; 362562306a36Sopenharmony_ci} 362662306a36Sopenharmony_ci 362762306a36Sopenharmony_ci 362862306a36Sopenharmony_ci#endif /* CONFIG_PCI_MSI */ 362962306a36Sopenharmony_ci 363062306a36Sopenharmony_cistatic void 363162306a36Sopenharmony_civmxnet3_alloc_intr_resources(struct vmxnet3_adapter *adapter) 363262306a36Sopenharmony_ci{ 363362306a36Sopenharmony_ci u32 cfg; 363462306a36Sopenharmony_ci unsigned long flags; 363562306a36Sopenharmony_ci 363662306a36Sopenharmony_ci /* intr settings */ 363762306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 363862306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 363962306a36Sopenharmony_ci VMXNET3_CMD_GET_CONF_INTR); 364062306a36Sopenharmony_ci cfg = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); 364162306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 364262306a36Sopenharmony_ci adapter->intr.type = cfg & 0x3; 364362306a36Sopenharmony_ci adapter->intr.mask_mode = (cfg >> 2) & 0x3; 364462306a36Sopenharmony_ci 364562306a36Sopenharmony_ci if (adapter->intr.type == VMXNET3_IT_AUTO) { 364662306a36Sopenharmony_ci adapter->intr.type = VMXNET3_IT_MSIX; 364762306a36Sopenharmony_ci } 364862306a36Sopenharmony_ci 364962306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI 365062306a36Sopenharmony_ci if (adapter->intr.type == VMXNET3_IT_MSIX) { 365162306a36Sopenharmony_ci int i, nvec, nvec_allocated; 365262306a36Sopenharmony_ci 365362306a36Sopenharmony_ci nvec = adapter->share_intr == VMXNET3_INTR_TXSHARE ? 365462306a36Sopenharmony_ci 1 : adapter->num_tx_queues; 365562306a36Sopenharmony_ci nvec += adapter->share_intr == VMXNET3_INTR_BUDDYSHARE ? 365662306a36Sopenharmony_ci 0 : adapter->num_rx_queues; 365762306a36Sopenharmony_ci nvec += 1; /* for link event */ 365862306a36Sopenharmony_ci nvec = nvec > VMXNET3_LINUX_MIN_MSIX_VECT ? 365962306a36Sopenharmony_ci nvec : VMXNET3_LINUX_MIN_MSIX_VECT; 366062306a36Sopenharmony_ci 366162306a36Sopenharmony_ci for (i = 0; i < nvec; i++) 366262306a36Sopenharmony_ci adapter->intr.msix_entries[i].entry = i; 366362306a36Sopenharmony_ci 366462306a36Sopenharmony_ci nvec_allocated = vmxnet3_acquire_msix_vectors(adapter, nvec); 366562306a36Sopenharmony_ci if (nvec_allocated < 0) 366662306a36Sopenharmony_ci goto msix_err; 366762306a36Sopenharmony_ci 366862306a36Sopenharmony_ci /* If we cannot allocate one MSIx vector per queue 366962306a36Sopenharmony_ci * then limit the number of rx queues to 1 367062306a36Sopenharmony_ci */ 367162306a36Sopenharmony_ci if (nvec_allocated == VMXNET3_LINUX_MIN_MSIX_VECT && 367262306a36Sopenharmony_ci nvec != VMXNET3_LINUX_MIN_MSIX_VECT) { 367362306a36Sopenharmony_ci if (adapter->share_intr != VMXNET3_INTR_BUDDYSHARE 367462306a36Sopenharmony_ci || adapter->num_rx_queues != 1) { 367562306a36Sopenharmony_ci adapter->share_intr = VMXNET3_INTR_TXSHARE; 367662306a36Sopenharmony_ci netdev_err(adapter->netdev, 367762306a36Sopenharmony_ci "Number of rx queues : 1\n"); 367862306a36Sopenharmony_ci adapter->num_rx_queues = 1; 367962306a36Sopenharmony_ci } 368062306a36Sopenharmony_ci } 368162306a36Sopenharmony_ci 368262306a36Sopenharmony_ci adapter->intr.num_intrs = nvec_allocated; 368362306a36Sopenharmony_ci return; 368462306a36Sopenharmony_ci 368562306a36Sopenharmony_cimsix_err: 368662306a36Sopenharmony_ci /* If we cannot allocate MSIx vectors use only one rx queue */ 368762306a36Sopenharmony_ci dev_info(&adapter->pdev->dev, 368862306a36Sopenharmony_ci "Failed to enable MSI-X, error %d. " 368962306a36Sopenharmony_ci "Limiting #rx queues to 1, try MSI.\n", nvec_allocated); 369062306a36Sopenharmony_ci 369162306a36Sopenharmony_ci adapter->intr.type = VMXNET3_IT_MSI; 369262306a36Sopenharmony_ci } 369362306a36Sopenharmony_ci 369462306a36Sopenharmony_ci if (adapter->intr.type == VMXNET3_IT_MSI) { 369562306a36Sopenharmony_ci if (!pci_enable_msi(adapter->pdev)) { 369662306a36Sopenharmony_ci adapter->num_rx_queues = 1; 369762306a36Sopenharmony_ci adapter->intr.num_intrs = 1; 369862306a36Sopenharmony_ci return; 369962306a36Sopenharmony_ci } 370062306a36Sopenharmony_ci } 370162306a36Sopenharmony_ci#endif /* CONFIG_PCI_MSI */ 370262306a36Sopenharmony_ci 370362306a36Sopenharmony_ci adapter->num_rx_queues = 1; 370462306a36Sopenharmony_ci dev_info(&adapter->netdev->dev, 370562306a36Sopenharmony_ci "Using INTx interrupt, #Rx queues: 1.\n"); 370662306a36Sopenharmony_ci adapter->intr.type = VMXNET3_IT_INTX; 370762306a36Sopenharmony_ci 370862306a36Sopenharmony_ci /* INT-X related setting */ 370962306a36Sopenharmony_ci adapter->intr.num_intrs = 1; 371062306a36Sopenharmony_ci} 371162306a36Sopenharmony_ci 371262306a36Sopenharmony_ci 371362306a36Sopenharmony_cistatic void 371462306a36Sopenharmony_civmxnet3_free_intr_resources(struct vmxnet3_adapter *adapter) 371562306a36Sopenharmony_ci{ 371662306a36Sopenharmony_ci if (adapter->intr.type == VMXNET3_IT_MSIX) 371762306a36Sopenharmony_ci pci_disable_msix(adapter->pdev); 371862306a36Sopenharmony_ci else if (adapter->intr.type == VMXNET3_IT_MSI) 371962306a36Sopenharmony_ci pci_disable_msi(adapter->pdev); 372062306a36Sopenharmony_ci else 372162306a36Sopenharmony_ci BUG_ON(adapter->intr.type != VMXNET3_IT_INTX); 372262306a36Sopenharmony_ci} 372362306a36Sopenharmony_ci 372462306a36Sopenharmony_ci 372562306a36Sopenharmony_cistatic void 372662306a36Sopenharmony_civmxnet3_tx_timeout(struct net_device *netdev, unsigned int txqueue) 372762306a36Sopenharmony_ci{ 372862306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 372962306a36Sopenharmony_ci adapter->tx_timeout_count++; 373062306a36Sopenharmony_ci 373162306a36Sopenharmony_ci netdev_err(adapter->netdev, "tx hang\n"); 373262306a36Sopenharmony_ci schedule_work(&adapter->work); 373362306a36Sopenharmony_ci} 373462306a36Sopenharmony_ci 373562306a36Sopenharmony_ci 373662306a36Sopenharmony_cistatic void 373762306a36Sopenharmony_civmxnet3_reset_work(struct work_struct *data) 373862306a36Sopenharmony_ci{ 373962306a36Sopenharmony_ci struct vmxnet3_adapter *adapter; 374062306a36Sopenharmony_ci 374162306a36Sopenharmony_ci adapter = container_of(data, struct vmxnet3_adapter, work); 374262306a36Sopenharmony_ci 374362306a36Sopenharmony_ci /* if another thread is resetting the device, no need to proceed */ 374462306a36Sopenharmony_ci if (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state)) 374562306a36Sopenharmony_ci return; 374662306a36Sopenharmony_ci 374762306a36Sopenharmony_ci /* if the device is closed, we must leave it alone */ 374862306a36Sopenharmony_ci rtnl_lock(); 374962306a36Sopenharmony_ci if (netif_running(adapter->netdev)) { 375062306a36Sopenharmony_ci netdev_notice(adapter->netdev, "resetting\n"); 375162306a36Sopenharmony_ci vmxnet3_quiesce_dev(adapter); 375262306a36Sopenharmony_ci vmxnet3_reset_dev(adapter); 375362306a36Sopenharmony_ci vmxnet3_activate_dev(adapter); 375462306a36Sopenharmony_ci } else { 375562306a36Sopenharmony_ci netdev_info(adapter->netdev, "already closed\n"); 375662306a36Sopenharmony_ci } 375762306a36Sopenharmony_ci rtnl_unlock(); 375862306a36Sopenharmony_ci 375962306a36Sopenharmony_ci netif_wake_queue(adapter->netdev); 376062306a36Sopenharmony_ci clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state); 376162306a36Sopenharmony_ci} 376262306a36Sopenharmony_ci 376362306a36Sopenharmony_ci 376462306a36Sopenharmony_cistatic int 376562306a36Sopenharmony_civmxnet3_probe_device(struct pci_dev *pdev, 376662306a36Sopenharmony_ci const struct pci_device_id *id) 376762306a36Sopenharmony_ci{ 376862306a36Sopenharmony_ci static const struct net_device_ops vmxnet3_netdev_ops = { 376962306a36Sopenharmony_ci .ndo_open = vmxnet3_open, 377062306a36Sopenharmony_ci .ndo_stop = vmxnet3_close, 377162306a36Sopenharmony_ci .ndo_start_xmit = vmxnet3_xmit_frame, 377262306a36Sopenharmony_ci .ndo_set_mac_address = vmxnet3_set_mac_addr, 377362306a36Sopenharmony_ci .ndo_change_mtu = vmxnet3_change_mtu, 377462306a36Sopenharmony_ci .ndo_fix_features = vmxnet3_fix_features, 377562306a36Sopenharmony_ci .ndo_set_features = vmxnet3_set_features, 377662306a36Sopenharmony_ci .ndo_features_check = vmxnet3_features_check, 377762306a36Sopenharmony_ci .ndo_get_stats64 = vmxnet3_get_stats64, 377862306a36Sopenharmony_ci .ndo_tx_timeout = vmxnet3_tx_timeout, 377962306a36Sopenharmony_ci .ndo_set_rx_mode = vmxnet3_set_mc, 378062306a36Sopenharmony_ci .ndo_vlan_rx_add_vid = vmxnet3_vlan_rx_add_vid, 378162306a36Sopenharmony_ci .ndo_vlan_rx_kill_vid = vmxnet3_vlan_rx_kill_vid, 378262306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 378362306a36Sopenharmony_ci .ndo_poll_controller = vmxnet3_netpoll, 378462306a36Sopenharmony_ci#endif 378562306a36Sopenharmony_ci .ndo_bpf = vmxnet3_xdp, 378662306a36Sopenharmony_ci .ndo_xdp_xmit = vmxnet3_xdp_xmit, 378762306a36Sopenharmony_ci }; 378862306a36Sopenharmony_ci int err; 378962306a36Sopenharmony_ci u32 ver; 379062306a36Sopenharmony_ci struct net_device *netdev; 379162306a36Sopenharmony_ci struct vmxnet3_adapter *adapter; 379262306a36Sopenharmony_ci u8 mac[ETH_ALEN]; 379362306a36Sopenharmony_ci int size; 379462306a36Sopenharmony_ci int num_tx_queues; 379562306a36Sopenharmony_ci int num_rx_queues; 379662306a36Sopenharmony_ci int queues; 379762306a36Sopenharmony_ci unsigned long flags; 379862306a36Sopenharmony_ci 379962306a36Sopenharmony_ci if (!pci_msi_enabled()) 380062306a36Sopenharmony_ci enable_mq = 0; 380162306a36Sopenharmony_ci 380262306a36Sopenharmony_ci#ifdef VMXNET3_RSS 380362306a36Sopenharmony_ci if (enable_mq) 380462306a36Sopenharmony_ci num_rx_queues = min(VMXNET3_DEVICE_MAX_RX_QUEUES, 380562306a36Sopenharmony_ci (int)num_online_cpus()); 380662306a36Sopenharmony_ci else 380762306a36Sopenharmony_ci#endif 380862306a36Sopenharmony_ci num_rx_queues = 1; 380962306a36Sopenharmony_ci 381062306a36Sopenharmony_ci if (enable_mq) 381162306a36Sopenharmony_ci num_tx_queues = min(VMXNET3_DEVICE_MAX_TX_QUEUES, 381262306a36Sopenharmony_ci (int)num_online_cpus()); 381362306a36Sopenharmony_ci else 381462306a36Sopenharmony_ci num_tx_queues = 1; 381562306a36Sopenharmony_ci 381662306a36Sopenharmony_ci netdev = alloc_etherdev_mq(sizeof(struct vmxnet3_adapter), 381762306a36Sopenharmony_ci max(num_tx_queues, num_rx_queues)); 381862306a36Sopenharmony_ci if (!netdev) 381962306a36Sopenharmony_ci return -ENOMEM; 382062306a36Sopenharmony_ci 382162306a36Sopenharmony_ci pci_set_drvdata(pdev, netdev); 382262306a36Sopenharmony_ci adapter = netdev_priv(netdev); 382362306a36Sopenharmony_ci adapter->netdev = netdev; 382462306a36Sopenharmony_ci adapter->pdev = pdev; 382562306a36Sopenharmony_ci 382662306a36Sopenharmony_ci adapter->tx_ring_size = VMXNET3_DEF_TX_RING_SIZE; 382762306a36Sopenharmony_ci adapter->rx_ring_size = VMXNET3_DEF_RX_RING_SIZE; 382862306a36Sopenharmony_ci adapter->rx_ring2_size = VMXNET3_DEF_RX_RING2_SIZE; 382962306a36Sopenharmony_ci 383062306a36Sopenharmony_ci err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 383162306a36Sopenharmony_ci if (err) { 383262306a36Sopenharmony_ci dev_err(&pdev->dev, "dma_set_mask failed\n"); 383362306a36Sopenharmony_ci goto err_set_mask; 383462306a36Sopenharmony_ci } 383562306a36Sopenharmony_ci 383662306a36Sopenharmony_ci spin_lock_init(&adapter->cmd_lock); 383762306a36Sopenharmony_ci adapter->adapter_pa = dma_map_single(&adapter->pdev->dev, adapter, 383862306a36Sopenharmony_ci sizeof(struct vmxnet3_adapter), 383962306a36Sopenharmony_ci DMA_TO_DEVICE); 384062306a36Sopenharmony_ci if (dma_mapping_error(&adapter->pdev->dev, adapter->adapter_pa)) { 384162306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to map dma\n"); 384262306a36Sopenharmony_ci err = -EFAULT; 384362306a36Sopenharmony_ci goto err_set_mask; 384462306a36Sopenharmony_ci } 384562306a36Sopenharmony_ci adapter->shared = dma_alloc_coherent( 384662306a36Sopenharmony_ci &adapter->pdev->dev, 384762306a36Sopenharmony_ci sizeof(struct Vmxnet3_DriverShared), 384862306a36Sopenharmony_ci &adapter->shared_pa, GFP_KERNEL); 384962306a36Sopenharmony_ci if (!adapter->shared) { 385062306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to allocate memory\n"); 385162306a36Sopenharmony_ci err = -ENOMEM; 385262306a36Sopenharmony_ci goto err_alloc_shared; 385362306a36Sopenharmony_ci } 385462306a36Sopenharmony_ci 385562306a36Sopenharmony_ci err = vmxnet3_alloc_pci_resources(adapter); 385662306a36Sopenharmony_ci if (err < 0) 385762306a36Sopenharmony_ci goto err_alloc_pci; 385862306a36Sopenharmony_ci 385962306a36Sopenharmony_ci ver = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_VRRS); 386062306a36Sopenharmony_ci if (ver & (1 << VMXNET3_REV_7)) { 386162306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, 386262306a36Sopenharmony_ci VMXNET3_REG_VRRS, 386362306a36Sopenharmony_ci 1 << VMXNET3_REV_7); 386462306a36Sopenharmony_ci adapter->version = VMXNET3_REV_7 + 1; 386562306a36Sopenharmony_ci } else if (ver & (1 << VMXNET3_REV_6)) { 386662306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, 386762306a36Sopenharmony_ci VMXNET3_REG_VRRS, 386862306a36Sopenharmony_ci 1 << VMXNET3_REV_6); 386962306a36Sopenharmony_ci adapter->version = VMXNET3_REV_6 + 1; 387062306a36Sopenharmony_ci } else if (ver & (1 << VMXNET3_REV_5)) { 387162306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, 387262306a36Sopenharmony_ci VMXNET3_REG_VRRS, 387362306a36Sopenharmony_ci 1 << VMXNET3_REV_5); 387462306a36Sopenharmony_ci adapter->version = VMXNET3_REV_5 + 1; 387562306a36Sopenharmony_ci } else if (ver & (1 << VMXNET3_REV_4)) { 387662306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, 387762306a36Sopenharmony_ci VMXNET3_REG_VRRS, 387862306a36Sopenharmony_ci 1 << VMXNET3_REV_4); 387962306a36Sopenharmony_ci adapter->version = VMXNET3_REV_4 + 1; 388062306a36Sopenharmony_ci } else if (ver & (1 << VMXNET3_REV_3)) { 388162306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, 388262306a36Sopenharmony_ci VMXNET3_REG_VRRS, 388362306a36Sopenharmony_ci 1 << VMXNET3_REV_3); 388462306a36Sopenharmony_ci adapter->version = VMXNET3_REV_3 + 1; 388562306a36Sopenharmony_ci } else if (ver & (1 << VMXNET3_REV_2)) { 388662306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, 388762306a36Sopenharmony_ci VMXNET3_REG_VRRS, 388862306a36Sopenharmony_ci 1 << VMXNET3_REV_2); 388962306a36Sopenharmony_ci adapter->version = VMXNET3_REV_2 + 1; 389062306a36Sopenharmony_ci } else if (ver & (1 << VMXNET3_REV_1)) { 389162306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, 389262306a36Sopenharmony_ci VMXNET3_REG_VRRS, 389362306a36Sopenharmony_ci 1 << VMXNET3_REV_1); 389462306a36Sopenharmony_ci adapter->version = VMXNET3_REV_1 + 1; 389562306a36Sopenharmony_ci } else { 389662306a36Sopenharmony_ci dev_err(&pdev->dev, 389762306a36Sopenharmony_ci "Incompatible h/w version (0x%x) for adapter\n", ver); 389862306a36Sopenharmony_ci err = -EBUSY; 389962306a36Sopenharmony_ci goto err_ver; 390062306a36Sopenharmony_ci } 390162306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Using device version %d\n", adapter->version); 390262306a36Sopenharmony_ci 390362306a36Sopenharmony_ci ver = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_UVRS); 390462306a36Sopenharmony_ci if (ver & 1) { 390562306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_UVRS, 1); 390662306a36Sopenharmony_ci } else { 390762306a36Sopenharmony_ci dev_err(&pdev->dev, 390862306a36Sopenharmony_ci "Incompatible upt version (0x%x) for adapter\n", ver); 390962306a36Sopenharmony_ci err = -EBUSY; 391062306a36Sopenharmony_ci goto err_ver; 391162306a36Sopenharmony_ci } 391262306a36Sopenharmony_ci 391362306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_7(adapter)) { 391462306a36Sopenharmony_ci adapter->devcap_supported[0] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_DCR); 391562306a36Sopenharmony_ci adapter->ptcap_supported[0] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_PTCR); 391662306a36Sopenharmony_ci if (adapter->devcap_supported[0] & (1UL << VMXNET3_CAP_LARGE_BAR)) { 391762306a36Sopenharmony_ci adapter->dev_caps[0] = adapter->devcap_supported[0] & 391862306a36Sopenharmony_ci (1UL << VMXNET3_CAP_LARGE_BAR); 391962306a36Sopenharmony_ci } 392062306a36Sopenharmony_ci if (!(adapter->ptcap_supported[0] & (1UL << VMXNET3_DCR_ERROR)) && 392162306a36Sopenharmony_ci adapter->ptcap_supported[0] & (1UL << VMXNET3_CAP_OOORX_COMP) && 392262306a36Sopenharmony_ci adapter->devcap_supported[0] & (1UL << VMXNET3_CAP_OOORX_COMP)) { 392362306a36Sopenharmony_ci adapter->dev_caps[0] |= adapter->devcap_supported[0] & 392462306a36Sopenharmony_ci (1UL << VMXNET3_CAP_OOORX_COMP); 392562306a36Sopenharmony_ci } 392662306a36Sopenharmony_ci if (adapter->dev_caps[0]) 392762306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DCR, adapter->dev_caps[0]); 392862306a36Sopenharmony_ci 392962306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 393062306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_DCR0_REG); 393162306a36Sopenharmony_ci adapter->dev_caps[0] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); 393262306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 393362306a36Sopenharmony_ci } 393462306a36Sopenharmony_ci 393562306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_7(adapter) && 393662306a36Sopenharmony_ci adapter->dev_caps[0] & (1UL << VMXNET3_CAP_LARGE_BAR)) { 393762306a36Sopenharmony_ci adapter->tx_prod_offset = VMXNET3_REG_LB_TXPROD; 393862306a36Sopenharmony_ci adapter->rx_prod_offset = VMXNET3_REG_LB_RXPROD; 393962306a36Sopenharmony_ci adapter->rx_prod2_offset = VMXNET3_REG_LB_RXPROD2; 394062306a36Sopenharmony_ci } else { 394162306a36Sopenharmony_ci adapter->tx_prod_offset = VMXNET3_REG_TXPROD; 394262306a36Sopenharmony_ci adapter->rx_prod_offset = VMXNET3_REG_RXPROD; 394362306a36Sopenharmony_ci adapter->rx_prod2_offset = VMXNET3_REG_RXPROD2; 394462306a36Sopenharmony_ci } 394562306a36Sopenharmony_ci 394662306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_6(adapter)) { 394762306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 394862306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 394962306a36Sopenharmony_ci VMXNET3_CMD_GET_MAX_QUEUES_CONF); 395062306a36Sopenharmony_ci queues = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); 395162306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 395262306a36Sopenharmony_ci if (queues > 0) { 395362306a36Sopenharmony_ci adapter->num_rx_queues = min(num_rx_queues, ((queues >> 8) & 0xff)); 395462306a36Sopenharmony_ci adapter->num_tx_queues = min(num_tx_queues, (queues & 0xff)); 395562306a36Sopenharmony_ci } else { 395662306a36Sopenharmony_ci adapter->num_rx_queues = min(num_rx_queues, 395762306a36Sopenharmony_ci VMXNET3_DEVICE_DEFAULT_RX_QUEUES); 395862306a36Sopenharmony_ci adapter->num_tx_queues = min(num_tx_queues, 395962306a36Sopenharmony_ci VMXNET3_DEVICE_DEFAULT_TX_QUEUES); 396062306a36Sopenharmony_ci } 396162306a36Sopenharmony_ci if (adapter->num_rx_queues > VMXNET3_MAX_RX_QUEUES || 396262306a36Sopenharmony_ci adapter->num_tx_queues > VMXNET3_MAX_TX_QUEUES) { 396362306a36Sopenharmony_ci adapter->queuesExtEnabled = true; 396462306a36Sopenharmony_ci } else { 396562306a36Sopenharmony_ci adapter->queuesExtEnabled = false; 396662306a36Sopenharmony_ci } 396762306a36Sopenharmony_ci } else { 396862306a36Sopenharmony_ci adapter->queuesExtEnabled = false; 396962306a36Sopenharmony_ci num_rx_queues = rounddown_pow_of_two(num_rx_queues); 397062306a36Sopenharmony_ci num_tx_queues = rounddown_pow_of_two(num_tx_queues); 397162306a36Sopenharmony_ci adapter->num_rx_queues = min(num_rx_queues, 397262306a36Sopenharmony_ci VMXNET3_DEVICE_DEFAULT_RX_QUEUES); 397362306a36Sopenharmony_ci adapter->num_tx_queues = min(num_tx_queues, 397462306a36Sopenharmony_ci VMXNET3_DEVICE_DEFAULT_TX_QUEUES); 397562306a36Sopenharmony_ci } 397662306a36Sopenharmony_ci dev_info(&pdev->dev, 397762306a36Sopenharmony_ci "# of Tx queues : %d, # of Rx queues : %d\n", 397862306a36Sopenharmony_ci adapter->num_tx_queues, adapter->num_rx_queues); 397962306a36Sopenharmony_ci 398062306a36Sopenharmony_ci adapter->rx_buf_per_pkt = 1; 398162306a36Sopenharmony_ci 398262306a36Sopenharmony_ci size = sizeof(struct Vmxnet3_TxQueueDesc) * adapter->num_tx_queues; 398362306a36Sopenharmony_ci size += sizeof(struct Vmxnet3_RxQueueDesc) * adapter->num_rx_queues; 398462306a36Sopenharmony_ci adapter->tqd_start = dma_alloc_coherent(&adapter->pdev->dev, size, 398562306a36Sopenharmony_ci &adapter->queue_desc_pa, 398662306a36Sopenharmony_ci GFP_KERNEL); 398762306a36Sopenharmony_ci 398862306a36Sopenharmony_ci if (!adapter->tqd_start) { 398962306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to allocate memory\n"); 399062306a36Sopenharmony_ci err = -ENOMEM; 399162306a36Sopenharmony_ci goto err_ver; 399262306a36Sopenharmony_ci } 399362306a36Sopenharmony_ci adapter->rqd_start = (struct Vmxnet3_RxQueueDesc *)(adapter->tqd_start + 399462306a36Sopenharmony_ci adapter->num_tx_queues); 399562306a36Sopenharmony_ci 399662306a36Sopenharmony_ci adapter->pm_conf = dma_alloc_coherent(&adapter->pdev->dev, 399762306a36Sopenharmony_ci sizeof(struct Vmxnet3_PMConf), 399862306a36Sopenharmony_ci &adapter->pm_conf_pa, 399962306a36Sopenharmony_ci GFP_KERNEL); 400062306a36Sopenharmony_ci if (adapter->pm_conf == NULL) { 400162306a36Sopenharmony_ci err = -ENOMEM; 400262306a36Sopenharmony_ci goto err_alloc_pm; 400362306a36Sopenharmony_ci } 400462306a36Sopenharmony_ci 400562306a36Sopenharmony_ci#ifdef VMXNET3_RSS 400662306a36Sopenharmony_ci 400762306a36Sopenharmony_ci adapter->rss_conf = dma_alloc_coherent(&adapter->pdev->dev, 400862306a36Sopenharmony_ci sizeof(struct UPT1_RSSConf), 400962306a36Sopenharmony_ci &adapter->rss_conf_pa, 401062306a36Sopenharmony_ci GFP_KERNEL); 401162306a36Sopenharmony_ci if (adapter->rss_conf == NULL) { 401262306a36Sopenharmony_ci err = -ENOMEM; 401362306a36Sopenharmony_ci goto err_alloc_rss; 401462306a36Sopenharmony_ci } 401562306a36Sopenharmony_ci#endif /* VMXNET3_RSS */ 401662306a36Sopenharmony_ci 401762306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_3(adapter)) { 401862306a36Sopenharmony_ci adapter->coal_conf = 401962306a36Sopenharmony_ci dma_alloc_coherent(&adapter->pdev->dev, 402062306a36Sopenharmony_ci sizeof(struct Vmxnet3_CoalesceScheme) 402162306a36Sopenharmony_ci , 402262306a36Sopenharmony_ci &adapter->coal_conf_pa, 402362306a36Sopenharmony_ci GFP_KERNEL); 402462306a36Sopenharmony_ci if (!adapter->coal_conf) { 402562306a36Sopenharmony_ci err = -ENOMEM; 402662306a36Sopenharmony_ci goto err_coal_conf; 402762306a36Sopenharmony_ci } 402862306a36Sopenharmony_ci adapter->coal_conf->coalMode = VMXNET3_COALESCE_DISABLED; 402962306a36Sopenharmony_ci adapter->default_coal_mode = true; 403062306a36Sopenharmony_ci } 403162306a36Sopenharmony_ci 403262306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_4(adapter)) { 403362306a36Sopenharmony_ci adapter->default_rss_fields = true; 403462306a36Sopenharmony_ci adapter->rss_fields = VMXNET3_RSS_FIELDS_DEFAULT; 403562306a36Sopenharmony_ci } 403662306a36Sopenharmony_ci 403762306a36Sopenharmony_ci SET_NETDEV_DEV(netdev, &pdev->dev); 403862306a36Sopenharmony_ci vmxnet3_declare_features(adapter); 403962306a36Sopenharmony_ci netdev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT | 404062306a36Sopenharmony_ci NETDEV_XDP_ACT_NDO_XMIT; 404162306a36Sopenharmony_ci 404262306a36Sopenharmony_ci adapter->rxdata_desc_size = VMXNET3_VERSION_GE_3(adapter) ? 404362306a36Sopenharmony_ci VMXNET3_DEF_RXDATA_DESC_SIZE : 0; 404462306a36Sopenharmony_ci 404562306a36Sopenharmony_ci if (adapter->num_tx_queues == adapter->num_rx_queues) 404662306a36Sopenharmony_ci adapter->share_intr = VMXNET3_INTR_BUDDYSHARE; 404762306a36Sopenharmony_ci else 404862306a36Sopenharmony_ci adapter->share_intr = VMXNET3_INTR_DONTSHARE; 404962306a36Sopenharmony_ci 405062306a36Sopenharmony_ci vmxnet3_alloc_intr_resources(adapter); 405162306a36Sopenharmony_ci 405262306a36Sopenharmony_ci#ifdef VMXNET3_RSS 405362306a36Sopenharmony_ci if (adapter->num_rx_queues > 1 && 405462306a36Sopenharmony_ci adapter->intr.type == VMXNET3_IT_MSIX) { 405562306a36Sopenharmony_ci adapter->rss = true; 405662306a36Sopenharmony_ci netdev->hw_features |= NETIF_F_RXHASH; 405762306a36Sopenharmony_ci netdev->features |= NETIF_F_RXHASH; 405862306a36Sopenharmony_ci dev_dbg(&pdev->dev, "RSS is enabled.\n"); 405962306a36Sopenharmony_ci } else { 406062306a36Sopenharmony_ci adapter->rss = false; 406162306a36Sopenharmony_ci } 406262306a36Sopenharmony_ci#endif 406362306a36Sopenharmony_ci 406462306a36Sopenharmony_ci vmxnet3_read_mac_addr(adapter, mac); 406562306a36Sopenharmony_ci dev_addr_set(netdev, mac); 406662306a36Sopenharmony_ci 406762306a36Sopenharmony_ci netdev->netdev_ops = &vmxnet3_netdev_ops; 406862306a36Sopenharmony_ci vmxnet3_set_ethtool_ops(netdev); 406962306a36Sopenharmony_ci netdev->watchdog_timeo = 5 * HZ; 407062306a36Sopenharmony_ci 407162306a36Sopenharmony_ci /* MTU range: 60 - 9190 */ 407262306a36Sopenharmony_ci netdev->min_mtu = VMXNET3_MIN_MTU; 407362306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_6(adapter)) 407462306a36Sopenharmony_ci netdev->max_mtu = VMXNET3_V6_MAX_MTU; 407562306a36Sopenharmony_ci else 407662306a36Sopenharmony_ci netdev->max_mtu = VMXNET3_MAX_MTU; 407762306a36Sopenharmony_ci 407862306a36Sopenharmony_ci INIT_WORK(&adapter->work, vmxnet3_reset_work); 407962306a36Sopenharmony_ci set_bit(VMXNET3_STATE_BIT_QUIESCED, &adapter->state); 408062306a36Sopenharmony_ci 408162306a36Sopenharmony_ci if (adapter->intr.type == VMXNET3_IT_MSIX) { 408262306a36Sopenharmony_ci int i; 408362306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) { 408462306a36Sopenharmony_ci netif_napi_add(adapter->netdev, 408562306a36Sopenharmony_ci &adapter->rx_queue[i].napi, 408662306a36Sopenharmony_ci vmxnet3_poll_rx_only); 408762306a36Sopenharmony_ci } 408862306a36Sopenharmony_ci } else { 408962306a36Sopenharmony_ci netif_napi_add(adapter->netdev, &adapter->rx_queue[0].napi, 409062306a36Sopenharmony_ci vmxnet3_poll); 409162306a36Sopenharmony_ci } 409262306a36Sopenharmony_ci 409362306a36Sopenharmony_ci netif_set_real_num_tx_queues(adapter->netdev, adapter->num_tx_queues); 409462306a36Sopenharmony_ci netif_set_real_num_rx_queues(adapter->netdev, adapter->num_rx_queues); 409562306a36Sopenharmony_ci 409662306a36Sopenharmony_ci netif_carrier_off(netdev); 409762306a36Sopenharmony_ci err = register_netdev(netdev); 409862306a36Sopenharmony_ci 409962306a36Sopenharmony_ci if (err) { 410062306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to register adapter\n"); 410162306a36Sopenharmony_ci goto err_register; 410262306a36Sopenharmony_ci } 410362306a36Sopenharmony_ci 410462306a36Sopenharmony_ci vmxnet3_check_link(adapter, false); 410562306a36Sopenharmony_ci return 0; 410662306a36Sopenharmony_ci 410762306a36Sopenharmony_cierr_register: 410862306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_3(adapter)) { 410962306a36Sopenharmony_ci dma_free_coherent(&adapter->pdev->dev, 411062306a36Sopenharmony_ci sizeof(struct Vmxnet3_CoalesceScheme), 411162306a36Sopenharmony_ci adapter->coal_conf, adapter->coal_conf_pa); 411262306a36Sopenharmony_ci } 411362306a36Sopenharmony_ci vmxnet3_free_intr_resources(adapter); 411462306a36Sopenharmony_cierr_coal_conf: 411562306a36Sopenharmony_ci#ifdef VMXNET3_RSS 411662306a36Sopenharmony_ci dma_free_coherent(&adapter->pdev->dev, sizeof(struct UPT1_RSSConf), 411762306a36Sopenharmony_ci adapter->rss_conf, adapter->rss_conf_pa); 411862306a36Sopenharmony_cierr_alloc_rss: 411962306a36Sopenharmony_ci#endif 412062306a36Sopenharmony_ci dma_free_coherent(&adapter->pdev->dev, sizeof(struct Vmxnet3_PMConf), 412162306a36Sopenharmony_ci adapter->pm_conf, adapter->pm_conf_pa); 412262306a36Sopenharmony_cierr_alloc_pm: 412362306a36Sopenharmony_ci dma_free_coherent(&adapter->pdev->dev, size, adapter->tqd_start, 412462306a36Sopenharmony_ci adapter->queue_desc_pa); 412562306a36Sopenharmony_cierr_ver: 412662306a36Sopenharmony_ci vmxnet3_free_pci_resources(adapter); 412762306a36Sopenharmony_cierr_alloc_pci: 412862306a36Sopenharmony_ci dma_free_coherent(&adapter->pdev->dev, 412962306a36Sopenharmony_ci sizeof(struct Vmxnet3_DriverShared), 413062306a36Sopenharmony_ci adapter->shared, adapter->shared_pa); 413162306a36Sopenharmony_cierr_alloc_shared: 413262306a36Sopenharmony_ci dma_unmap_single(&adapter->pdev->dev, adapter->adapter_pa, 413362306a36Sopenharmony_ci sizeof(struct vmxnet3_adapter), DMA_TO_DEVICE); 413462306a36Sopenharmony_cierr_set_mask: 413562306a36Sopenharmony_ci free_netdev(netdev); 413662306a36Sopenharmony_ci return err; 413762306a36Sopenharmony_ci} 413862306a36Sopenharmony_ci 413962306a36Sopenharmony_ci 414062306a36Sopenharmony_cistatic void 414162306a36Sopenharmony_civmxnet3_remove_device(struct pci_dev *pdev) 414262306a36Sopenharmony_ci{ 414362306a36Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 414462306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 414562306a36Sopenharmony_ci int size = 0; 414662306a36Sopenharmony_ci int num_rx_queues, rx_queues; 414762306a36Sopenharmony_ci unsigned long flags; 414862306a36Sopenharmony_ci 414962306a36Sopenharmony_ci#ifdef VMXNET3_RSS 415062306a36Sopenharmony_ci if (enable_mq) 415162306a36Sopenharmony_ci num_rx_queues = min(VMXNET3_DEVICE_MAX_RX_QUEUES, 415262306a36Sopenharmony_ci (int)num_online_cpus()); 415362306a36Sopenharmony_ci else 415462306a36Sopenharmony_ci#endif 415562306a36Sopenharmony_ci num_rx_queues = 1; 415662306a36Sopenharmony_ci if (!VMXNET3_VERSION_GE_6(adapter)) { 415762306a36Sopenharmony_ci num_rx_queues = rounddown_pow_of_two(num_rx_queues); 415862306a36Sopenharmony_ci } 415962306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_6(adapter)) { 416062306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 416162306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 416262306a36Sopenharmony_ci VMXNET3_CMD_GET_MAX_QUEUES_CONF); 416362306a36Sopenharmony_ci rx_queues = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); 416462306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 416562306a36Sopenharmony_ci if (rx_queues > 0) 416662306a36Sopenharmony_ci rx_queues = (rx_queues >> 8) & 0xff; 416762306a36Sopenharmony_ci else 416862306a36Sopenharmony_ci rx_queues = min(num_rx_queues, VMXNET3_DEVICE_DEFAULT_RX_QUEUES); 416962306a36Sopenharmony_ci num_rx_queues = min(num_rx_queues, rx_queues); 417062306a36Sopenharmony_ci } else { 417162306a36Sopenharmony_ci num_rx_queues = min(num_rx_queues, 417262306a36Sopenharmony_ci VMXNET3_DEVICE_DEFAULT_RX_QUEUES); 417362306a36Sopenharmony_ci } 417462306a36Sopenharmony_ci 417562306a36Sopenharmony_ci cancel_work_sync(&adapter->work); 417662306a36Sopenharmony_ci 417762306a36Sopenharmony_ci unregister_netdev(netdev); 417862306a36Sopenharmony_ci 417962306a36Sopenharmony_ci vmxnet3_free_intr_resources(adapter); 418062306a36Sopenharmony_ci vmxnet3_free_pci_resources(adapter); 418162306a36Sopenharmony_ci if (VMXNET3_VERSION_GE_3(adapter)) { 418262306a36Sopenharmony_ci dma_free_coherent(&adapter->pdev->dev, 418362306a36Sopenharmony_ci sizeof(struct Vmxnet3_CoalesceScheme), 418462306a36Sopenharmony_ci adapter->coal_conf, adapter->coal_conf_pa); 418562306a36Sopenharmony_ci } 418662306a36Sopenharmony_ci#ifdef VMXNET3_RSS 418762306a36Sopenharmony_ci dma_free_coherent(&adapter->pdev->dev, sizeof(struct UPT1_RSSConf), 418862306a36Sopenharmony_ci adapter->rss_conf, adapter->rss_conf_pa); 418962306a36Sopenharmony_ci#endif 419062306a36Sopenharmony_ci dma_free_coherent(&adapter->pdev->dev, sizeof(struct Vmxnet3_PMConf), 419162306a36Sopenharmony_ci adapter->pm_conf, adapter->pm_conf_pa); 419262306a36Sopenharmony_ci 419362306a36Sopenharmony_ci size = sizeof(struct Vmxnet3_TxQueueDesc) * adapter->num_tx_queues; 419462306a36Sopenharmony_ci size += sizeof(struct Vmxnet3_RxQueueDesc) * num_rx_queues; 419562306a36Sopenharmony_ci dma_free_coherent(&adapter->pdev->dev, size, adapter->tqd_start, 419662306a36Sopenharmony_ci adapter->queue_desc_pa); 419762306a36Sopenharmony_ci dma_free_coherent(&adapter->pdev->dev, 419862306a36Sopenharmony_ci sizeof(struct Vmxnet3_DriverShared), 419962306a36Sopenharmony_ci adapter->shared, adapter->shared_pa); 420062306a36Sopenharmony_ci dma_unmap_single(&adapter->pdev->dev, adapter->adapter_pa, 420162306a36Sopenharmony_ci sizeof(struct vmxnet3_adapter), DMA_TO_DEVICE); 420262306a36Sopenharmony_ci free_netdev(netdev); 420362306a36Sopenharmony_ci} 420462306a36Sopenharmony_ci 420562306a36Sopenharmony_cistatic void vmxnet3_shutdown_device(struct pci_dev *pdev) 420662306a36Sopenharmony_ci{ 420762306a36Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 420862306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 420962306a36Sopenharmony_ci unsigned long flags; 421062306a36Sopenharmony_ci 421162306a36Sopenharmony_ci /* Reset_work may be in the middle of resetting the device, wait for its 421262306a36Sopenharmony_ci * completion. 421362306a36Sopenharmony_ci */ 421462306a36Sopenharmony_ci while (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state)) 421562306a36Sopenharmony_ci usleep_range(1000, 2000); 421662306a36Sopenharmony_ci 421762306a36Sopenharmony_ci if (test_and_set_bit(VMXNET3_STATE_BIT_QUIESCED, 421862306a36Sopenharmony_ci &adapter->state)) { 421962306a36Sopenharmony_ci clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state); 422062306a36Sopenharmony_ci return; 422162306a36Sopenharmony_ci } 422262306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 422362306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 422462306a36Sopenharmony_ci VMXNET3_CMD_QUIESCE_DEV); 422562306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 422662306a36Sopenharmony_ci vmxnet3_disable_all_intrs(adapter); 422762306a36Sopenharmony_ci 422862306a36Sopenharmony_ci clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state); 422962306a36Sopenharmony_ci} 423062306a36Sopenharmony_ci 423162306a36Sopenharmony_ci 423262306a36Sopenharmony_ci#ifdef CONFIG_PM 423362306a36Sopenharmony_ci 423462306a36Sopenharmony_cistatic int 423562306a36Sopenharmony_civmxnet3_suspend(struct device *device) 423662306a36Sopenharmony_ci{ 423762306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(device); 423862306a36Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 423962306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 424062306a36Sopenharmony_ci struct Vmxnet3_PMConf *pmConf; 424162306a36Sopenharmony_ci struct ethhdr *ehdr; 424262306a36Sopenharmony_ci struct arphdr *ahdr; 424362306a36Sopenharmony_ci u8 *arpreq; 424462306a36Sopenharmony_ci struct in_device *in_dev; 424562306a36Sopenharmony_ci struct in_ifaddr *ifa; 424662306a36Sopenharmony_ci unsigned long flags; 424762306a36Sopenharmony_ci int i = 0; 424862306a36Sopenharmony_ci 424962306a36Sopenharmony_ci if (!netif_running(netdev)) 425062306a36Sopenharmony_ci return 0; 425162306a36Sopenharmony_ci 425262306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) 425362306a36Sopenharmony_ci napi_disable(&adapter->rx_queue[i].napi); 425462306a36Sopenharmony_ci 425562306a36Sopenharmony_ci vmxnet3_disable_all_intrs(adapter); 425662306a36Sopenharmony_ci vmxnet3_free_irqs(adapter); 425762306a36Sopenharmony_ci vmxnet3_free_intr_resources(adapter); 425862306a36Sopenharmony_ci 425962306a36Sopenharmony_ci netif_device_detach(netdev); 426062306a36Sopenharmony_ci 426162306a36Sopenharmony_ci /* Create wake-up filters. */ 426262306a36Sopenharmony_ci pmConf = adapter->pm_conf; 426362306a36Sopenharmony_ci memset(pmConf, 0, sizeof(*pmConf)); 426462306a36Sopenharmony_ci 426562306a36Sopenharmony_ci if (adapter->wol & WAKE_UCAST) { 426662306a36Sopenharmony_ci pmConf->filters[i].patternSize = ETH_ALEN; 426762306a36Sopenharmony_ci pmConf->filters[i].maskSize = 1; 426862306a36Sopenharmony_ci memcpy(pmConf->filters[i].pattern, netdev->dev_addr, ETH_ALEN); 426962306a36Sopenharmony_ci pmConf->filters[i].mask[0] = 0x3F; /* LSB ETH_ALEN bits */ 427062306a36Sopenharmony_ci 427162306a36Sopenharmony_ci pmConf->wakeUpEvents |= VMXNET3_PM_WAKEUP_FILTER; 427262306a36Sopenharmony_ci i++; 427362306a36Sopenharmony_ci } 427462306a36Sopenharmony_ci 427562306a36Sopenharmony_ci if (adapter->wol & WAKE_ARP) { 427662306a36Sopenharmony_ci rcu_read_lock(); 427762306a36Sopenharmony_ci 427862306a36Sopenharmony_ci in_dev = __in_dev_get_rcu(netdev); 427962306a36Sopenharmony_ci if (!in_dev) { 428062306a36Sopenharmony_ci rcu_read_unlock(); 428162306a36Sopenharmony_ci goto skip_arp; 428262306a36Sopenharmony_ci } 428362306a36Sopenharmony_ci 428462306a36Sopenharmony_ci ifa = rcu_dereference(in_dev->ifa_list); 428562306a36Sopenharmony_ci if (!ifa) { 428662306a36Sopenharmony_ci rcu_read_unlock(); 428762306a36Sopenharmony_ci goto skip_arp; 428862306a36Sopenharmony_ci } 428962306a36Sopenharmony_ci 429062306a36Sopenharmony_ci pmConf->filters[i].patternSize = ETH_HLEN + /* Ethernet header*/ 429162306a36Sopenharmony_ci sizeof(struct arphdr) + /* ARP header */ 429262306a36Sopenharmony_ci 2 * ETH_ALEN + /* 2 Ethernet addresses*/ 429362306a36Sopenharmony_ci 2 * sizeof(u32); /*2 IPv4 addresses */ 429462306a36Sopenharmony_ci pmConf->filters[i].maskSize = 429562306a36Sopenharmony_ci (pmConf->filters[i].patternSize - 1) / 8 + 1; 429662306a36Sopenharmony_ci 429762306a36Sopenharmony_ci /* ETH_P_ARP in Ethernet header. */ 429862306a36Sopenharmony_ci ehdr = (struct ethhdr *)pmConf->filters[i].pattern; 429962306a36Sopenharmony_ci ehdr->h_proto = htons(ETH_P_ARP); 430062306a36Sopenharmony_ci 430162306a36Sopenharmony_ci /* ARPOP_REQUEST in ARP header. */ 430262306a36Sopenharmony_ci ahdr = (struct arphdr *)&pmConf->filters[i].pattern[ETH_HLEN]; 430362306a36Sopenharmony_ci ahdr->ar_op = htons(ARPOP_REQUEST); 430462306a36Sopenharmony_ci arpreq = (u8 *)(ahdr + 1); 430562306a36Sopenharmony_ci 430662306a36Sopenharmony_ci /* The Unicast IPv4 address in 'tip' field. */ 430762306a36Sopenharmony_ci arpreq += 2 * ETH_ALEN + sizeof(u32); 430862306a36Sopenharmony_ci *(__be32 *)arpreq = ifa->ifa_address; 430962306a36Sopenharmony_ci 431062306a36Sopenharmony_ci rcu_read_unlock(); 431162306a36Sopenharmony_ci 431262306a36Sopenharmony_ci /* The mask for the relevant bits. */ 431362306a36Sopenharmony_ci pmConf->filters[i].mask[0] = 0x00; 431462306a36Sopenharmony_ci pmConf->filters[i].mask[1] = 0x30; /* ETH_P_ARP */ 431562306a36Sopenharmony_ci pmConf->filters[i].mask[2] = 0x30; /* ARPOP_REQUEST */ 431662306a36Sopenharmony_ci pmConf->filters[i].mask[3] = 0x00; 431762306a36Sopenharmony_ci pmConf->filters[i].mask[4] = 0xC0; /* IPv4 TIP */ 431862306a36Sopenharmony_ci pmConf->filters[i].mask[5] = 0x03; /* IPv4 TIP */ 431962306a36Sopenharmony_ci 432062306a36Sopenharmony_ci pmConf->wakeUpEvents |= VMXNET3_PM_WAKEUP_FILTER; 432162306a36Sopenharmony_ci i++; 432262306a36Sopenharmony_ci } 432362306a36Sopenharmony_ci 432462306a36Sopenharmony_ciskip_arp: 432562306a36Sopenharmony_ci if (adapter->wol & WAKE_MAGIC) 432662306a36Sopenharmony_ci pmConf->wakeUpEvents |= VMXNET3_PM_WAKEUP_MAGIC; 432762306a36Sopenharmony_ci 432862306a36Sopenharmony_ci pmConf->numFilters = i; 432962306a36Sopenharmony_ci 433062306a36Sopenharmony_ci adapter->shared->devRead.pmConfDesc.confVer = cpu_to_le32(1); 433162306a36Sopenharmony_ci adapter->shared->devRead.pmConfDesc.confLen = cpu_to_le32(sizeof( 433262306a36Sopenharmony_ci *pmConf)); 433362306a36Sopenharmony_ci adapter->shared->devRead.pmConfDesc.confPA = 433462306a36Sopenharmony_ci cpu_to_le64(adapter->pm_conf_pa); 433562306a36Sopenharmony_ci 433662306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 433762306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 433862306a36Sopenharmony_ci VMXNET3_CMD_UPDATE_PMCFG); 433962306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 434062306a36Sopenharmony_ci 434162306a36Sopenharmony_ci pci_save_state(pdev); 434262306a36Sopenharmony_ci pci_enable_wake(pdev, pci_choose_state(pdev, PMSG_SUSPEND), 434362306a36Sopenharmony_ci adapter->wol); 434462306a36Sopenharmony_ci pci_disable_device(pdev); 434562306a36Sopenharmony_ci pci_set_power_state(pdev, pci_choose_state(pdev, PMSG_SUSPEND)); 434662306a36Sopenharmony_ci 434762306a36Sopenharmony_ci return 0; 434862306a36Sopenharmony_ci} 434962306a36Sopenharmony_ci 435062306a36Sopenharmony_ci 435162306a36Sopenharmony_cistatic int 435262306a36Sopenharmony_civmxnet3_resume(struct device *device) 435362306a36Sopenharmony_ci{ 435462306a36Sopenharmony_ci int err; 435562306a36Sopenharmony_ci unsigned long flags; 435662306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(device); 435762306a36Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 435862306a36Sopenharmony_ci struct vmxnet3_adapter *adapter = netdev_priv(netdev); 435962306a36Sopenharmony_ci 436062306a36Sopenharmony_ci if (!netif_running(netdev)) 436162306a36Sopenharmony_ci return 0; 436262306a36Sopenharmony_ci 436362306a36Sopenharmony_ci pci_set_power_state(pdev, PCI_D0); 436462306a36Sopenharmony_ci pci_restore_state(pdev); 436562306a36Sopenharmony_ci err = pci_enable_device_mem(pdev); 436662306a36Sopenharmony_ci if (err != 0) 436762306a36Sopenharmony_ci return err; 436862306a36Sopenharmony_ci 436962306a36Sopenharmony_ci pci_enable_wake(pdev, PCI_D0, 0); 437062306a36Sopenharmony_ci 437162306a36Sopenharmony_ci vmxnet3_alloc_intr_resources(adapter); 437262306a36Sopenharmony_ci 437362306a36Sopenharmony_ci /* During hibernate and suspend, device has to be reinitialized as the 437462306a36Sopenharmony_ci * device state need not be preserved. 437562306a36Sopenharmony_ci */ 437662306a36Sopenharmony_ci 437762306a36Sopenharmony_ci /* Need not check adapter state as other reset tasks cannot run during 437862306a36Sopenharmony_ci * device resume. 437962306a36Sopenharmony_ci */ 438062306a36Sopenharmony_ci spin_lock_irqsave(&adapter->cmd_lock, flags); 438162306a36Sopenharmony_ci VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 438262306a36Sopenharmony_ci VMXNET3_CMD_QUIESCE_DEV); 438362306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->cmd_lock, flags); 438462306a36Sopenharmony_ci vmxnet3_tq_cleanup_all(adapter); 438562306a36Sopenharmony_ci vmxnet3_rq_cleanup_all(adapter); 438662306a36Sopenharmony_ci 438762306a36Sopenharmony_ci vmxnet3_reset_dev(adapter); 438862306a36Sopenharmony_ci err = vmxnet3_activate_dev(adapter); 438962306a36Sopenharmony_ci if (err != 0) { 439062306a36Sopenharmony_ci netdev_err(netdev, 439162306a36Sopenharmony_ci "failed to re-activate on resume, error: %d", err); 439262306a36Sopenharmony_ci vmxnet3_force_close(adapter); 439362306a36Sopenharmony_ci return err; 439462306a36Sopenharmony_ci } 439562306a36Sopenharmony_ci netif_device_attach(netdev); 439662306a36Sopenharmony_ci 439762306a36Sopenharmony_ci return 0; 439862306a36Sopenharmony_ci} 439962306a36Sopenharmony_ci 440062306a36Sopenharmony_cistatic const struct dev_pm_ops vmxnet3_pm_ops = { 440162306a36Sopenharmony_ci .suspend = vmxnet3_suspend, 440262306a36Sopenharmony_ci .resume = vmxnet3_resume, 440362306a36Sopenharmony_ci .freeze = vmxnet3_suspend, 440462306a36Sopenharmony_ci .restore = vmxnet3_resume, 440562306a36Sopenharmony_ci}; 440662306a36Sopenharmony_ci#endif 440762306a36Sopenharmony_ci 440862306a36Sopenharmony_cistatic struct pci_driver vmxnet3_driver = { 440962306a36Sopenharmony_ci .name = vmxnet3_driver_name, 441062306a36Sopenharmony_ci .id_table = vmxnet3_pciid_table, 441162306a36Sopenharmony_ci .probe = vmxnet3_probe_device, 441262306a36Sopenharmony_ci .remove = vmxnet3_remove_device, 441362306a36Sopenharmony_ci .shutdown = vmxnet3_shutdown_device, 441462306a36Sopenharmony_ci#ifdef CONFIG_PM 441562306a36Sopenharmony_ci .driver.pm = &vmxnet3_pm_ops, 441662306a36Sopenharmony_ci#endif 441762306a36Sopenharmony_ci}; 441862306a36Sopenharmony_ci 441962306a36Sopenharmony_ci 442062306a36Sopenharmony_cistatic int __init 442162306a36Sopenharmony_civmxnet3_init_module(void) 442262306a36Sopenharmony_ci{ 442362306a36Sopenharmony_ci pr_info("%s - version %s\n", VMXNET3_DRIVER_DESC, 442462306a36Sopenharmony_ci VMXNET3_DRIVER_VERSION_REPORT); 442562306a36Sopenharmony_ci return pci_register_driver(&vmxnet3_driver); 442662306a36Sopenharmony_ci} 442762306a36Sopenharmony_ci 442862306a36Sopenharmony_cimodule_init(vmxnet3_init_module); 442962306a36Sopenharmony_ci 443062306a36Sopenharmony_ci 443162306a36Sopenharmony_cistatic void 443262306a36Sopenharmony_civmxnet3_exit_module(void) 443362306a36Sopenharmony_ci{ 443462306a36Sopenharmony_ci pci_unregister_driver(&vmxnet3_driver); 443562306a36Sopenharmony_ci} 443662306a36Sopenharmony_ci 443762306a36Sopenharmony_cimodule_exit(vmxnet3_exit_module); 443862306a36Sopenharmony_ci 443962306a36Sopenharmony_ciMODULE_AUTHOR("VMware, Inc."); 444062306a36Sopenharmony_ciMODULE_DESCRIPTION(VMXNET3_DRIVER_DESC); 444162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 444262306a36Sopenharmony_ciMODULE_VERSION(VMXNET3_DRIVER_VERSION_STRING); 4443