diff options
Diffstat (limited to 'kernel/drivers/net/vmxnet3')
-rw-r--r-- | kernel/drivers/net/vmxnet3/vmxnet3_defs.h | 38 | ||||
-rw-r--r-- | kernel/drivers/net/vmxnet3/vmxnet3_drv.c | 242 | ||||
-rw-r--r-- | kernel/drivers/net/vmxnet3/vmxnet3_ethtool.c | 122 | ||||
-rw-r--r-- | kernel/drivers/net/vmxnet3/vmxnet3_int.h | 8 |
4 files changed, 340 insertions, 70 deletions
diff --git a/kernel/drivers/net/vmxnet3/vmxnet3_defs.h b/kernel/drivers/net/vmxnet3/vmxnet3_defs.h index 3718d024f..221a53025 100644 --- a/kernel/drivers/net/vmxnet3/vmxnet3_defs.h +++ b/kernel/drivers/net/vmxnet3/vmxnet3_defs.h @@ -1,7 +1,7 @@ /* * Linux driver for VMware's vmxnet3 ethernet NIC. * - * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved. + * Copyright (C) 2008-2015, VMware, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -277,6 +277,40 @@ struct Vmxnet3_RxCompDesc { #endif /* __BIG_ENDIAN_BITFIELD */ }; +struct Vmxnet3_RxCompDescExt { + __le32 dword1; + u8 segCnt; /* Number of aggregated packets */ + u8 dupAckCnt; /* Number of duplicate Acks */ + __le16 tsDelta; /* TCP timestamp difference */ + __le32 dword2; +#ifdef __BIG_ENDIAN_BITFIELD + u32 gen:1; /* generation bit */ + u32 type:7; /* completion type */ + u32 fcs:1; /* Frame CRC correct */ + u32 frg:1; /* IP Fragment */ + u32 v4:1; /* IPv4 */ + u32 v6:1; /* IPv6 */ + u32 ipc:1; /* IP Checksum Correct */ + u32 tcp:1; /* TCP packet */ + u32 udp:1; /* UDP packet */ + u32 tuc:1; /* TCP/UDP Checksum Correct */ + u32 mss:16; +#else + u32 mss:16; + u32 tuc:1; /* TCP/UDP Checksum Correct */ + u32 udp:1; /* UDP packet */ + u32 tcp:1; /* TCP packet */ + u32 ipc:1; /* IP Checksum Correct */ + u32 v6:1; /* IPv6 */ + u32 v4:1; /* IPv4 */ + u32 frg:1; /* IP Fragment */ + u32 fcs:1; /* Frame CRC correct */ + u32 type:7; /* completion type */ + u32 gen:1; /* generation bit */ +#endif /* __BIG_ENDIAN_BITFIELD */ +}; + + /* fields in RxCompDesc we access via Vmxnet3_GenericDesc.dword[3] */ #define VMXNET3_RCD_TUC_SHIFT 16 #define VMXNET3_RCD_IPC_SHIFT 19 @@ -310,6 +344,7 @@ union Vmxnet3_GenericDesc { struct Vmxnet3_RxDesc rxd; struct Vmxnet3_TxCompDesc tcd; struct Vmxnet3_RxCompDesc rcd; + struct Vmxnet3_RxCompDescExt rcdExt; }; #define VMXNET3_INIT_GEN 1 @@ -361,6 +396,7 @@ enum { /* completion descriptor types */ #define VMXNET3_CDTYPE_TXCOMP 0 /* Tx Completion Descriptor */ #define VMXNET3_CDTYPE_RXCOMP 3 /* Rx Completion Descriptor */ +#define VMXNET3_CDTYPE_RXCOMP_LRO 4 /* Rx Completion Descriptor for LRO */ enum { VMXNET3_GOS_BITS_UNK = 0, /* unknown */ diff --git a/kernel/drivers/net/vmxnet3/vmxnet3_drv.c b/kernel/drivers/net/vmxnet3/vmxnet3_drv.c index 61c0840c4..0cbf520ce 100644 --- a/kernel/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/kernel/drivers/net/vmxnet3/vmxnet3_drv.c @@ -587,6 +587,12 @@ vmxnet3_rq_alloc_rx_buf(struct vmxnet3_rx_queue *rq, u32 ring_idx, &adapter->pdev->dev, rbi->skb->data, rbi->len, PCI_DMA_FROMDEVICE); + if (dma_mapping_error(&adapter->pdev->dev, + rbi->dma_addr)) { + dev_kfree_skb_any(rbi->skb); + rq->stats.rx_buf_alloc_failure++; + break; + } } else { /* rx buffer skipped by the device */ } @@ -605,13 +611,18 @@ vmxnet3_rq_alloc_rx_buf(struct vmxnet3_rx_queue *rq, u32 ring_idx, &adapter->pdev->dev, rbi->page, 0, PAGE_SIZE, PCI_DMA_FROMDEVICE); + if (dma_mapping_error(&adapter->pdev->dev, + rbi->dma_addr)) { + put_page(rbi->page); + rq->stats.rx_buf_alloc_failure++; + break; + } } else { /* rx buffers skipped by the device */ } val = VMXNET3_RXD_BTYPE_BODY << VMXNET3_RXD_BTYPE_SHIFT; } - BUG_ON(rbi->dma_addr == 0); gd->rxd.addr = cpu_to_le64(rbi->dma_addr); gd->dword[2] = cpu_to_le32((!ring->gen << VMXNET3_RXD_GEN_SHIFT) | val | rbi->len); @@ -655,7 +666,7 @@ vmxnet3_append_frag(struct sk_buff *skb, struct Vmxnet3_RxCompDesc *rcd, } -static void +static int vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx, struct vmxnet3_tx_queue *tq, struct pci_dev *pdev, struct vmxnet3_adapter *adapter) @@ -715,6 +726,8 @@ vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx, tbi->dma_addr = dma_map_single(&adapter->pdev->dev, skb->data + buf_offset, buf_size, PCI_DMA_TODEVICE); + if (dma_mapping_error(&adapter->pdev->dev, tbi->dma_addr)) + return -EFAULT; tbi->len = buf_size; @@ -755,6 +768,8 @@ vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx, tbi->dma_addr = skb_frag_dma_map(&adapter->pdev->dev, frag, buf_offset, buf_size, DMA_TO_DEVICE); + if (dma_mapping_error(&adapter->pdev->dev, tbi->dma_addr)) + return -EFAULT; tbi->len = buf_size; @@ -782,6 +797,8 @@ vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx, /* set the last buf_info for the pkt */ tbi->skb = skb; tbi->sop_idx = ctx->sop_txd - tq->tx_ring.base; + + return 0; } @@ -861,6 +878,9 @@ vmxnet3_parse_and_copy_hdr(struct sk_buff *skb, struct vmxnet3_tx_queue *tq, , skb_headlen(skb)); } + if (skb->len <= VMXNET3_HDR_COPY_SIZE) + ctx->copy_size = skb->len; + /* make sure headers are accessible directly */ if (unlikely(!pskb_may_pull(skb, ctx->copy_size))) goto err; @@ -1017,7 +1037,8 @@ vmxnet3_tq_xmit(struct sk_buff *skb, struct vmxnet3_tx_queue *tq, } /* fill tx descs related to addr & len */ - vmxnet3_map_pkt(skb, &ctx, tq, adapter->pdev, adapter); + if (vmxnet3_map_pkt(skb, &ctx, tq, adapter->pdev, adapter)) + goto unlock_drop_pkt; /* setup the EOP desc */ ctx.eop_txd->dword[3] = cpu_to_le32(VMXNET3_TXD_CQ | VMXNET3_TXD_EOP); @@ -1160,6 +1181,52 @@ vmxnet3_rx_error(struct vmxnet3_rx_queue *rq, struct Vmxnet3_RxCompDesc *rcd, } +static u32 +vmxnet3_get_hdr_len(struct vmxnet3_adapter *adapter, struct sk_buff *skb, + union Vmxnet3_GenericDesc *gdesc) +{ + u32 hlen, maplen; + union { + void *ptr; + struct ethhdr *eth; + struct iphdr *ipv4; + struct ipv6hdr *ipv6; + struct tcphdr *tcp; + } hdr; + BUG_ON(gdesc->rcd.tcp == 0); + + maplen = skb_headlen(skb); + if (unlikely(sizeof(struct iphdr) + sizeof(struct tcphdr) > maplen)) + return 0; + + hdr.eth = eth_hdr(skb); + if (gdesc->rcd.v4) { + BUG_ON(hdr.eth->h_proto != htons(ETH_P_IP)); + hdr.ptr += sizeof(struct ethhdr); + BUG_ON(hdr.ipv4->protocol != IPPROTO_TCP); + hlen = hdr.ipv4->ihl << 2; + hdr.ptr += hdr.ipv4->ihl << 2; + } else if (gdesc->rcd.v6) { + BUG_ON(hdr.eth->h_proto != htons(ETH_P_IPV6)); + hdr.ptr += sizeof(struct ethhdr); + /* Use an estimated value, since we also need to handle + * TSO case. + */ + if (hdr.ipv6->nexthdr != IPPROTO_TCP) + return sizeof(struct ipv6hdr) + sizeof(struct tcphdr); + hlen = sizeof(struct ipv6hdr); + hdr.ptr += sizeof(struct ipv6hdr); + } else { + /* Non-IP pkt, dont estimate header length */ + return 0; + } + + if (hlen + sizeof(struct tcphdr) > maplen) + return 0; + + return (hlen + (hdr.tcp->doff << 2)); +} + static int vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq, struct vmxnet3_adapter *adapter, int quota) @@ -1167,10 +1234,11 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq, static const u32 rxprod_reg[2] = { VMXNET3_REG_RXPROD, VMXNET3_REG_RXPROD2 }; - u32 num_rxd = 0; + u32 num_pkts = 0; bool skip_page_frags = false; struct Vmxnet3_RxCompDesc *rcd; struct vmxnet3_rx_ctx *ctx = &rq->rx_ctx; + u16 segCnt = 0, mss = 0; #ifdef __BIG_ENDIAN_BITFIELD struct Vmxnet3_RxDesc rxCmdDesc; struct Vmxnet3_RxCompDesc rxComp; @@ -1181,17 +1249,17 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq, struct vmxnet3_rx_buf_info *rbi; struct sk_buff *skb, *new_skb = NULL; struct page *new_page = NULL; + dma_addr_t new_dma_addr; int num_to_alloc; struct Vmxnet3_RxDesc *rxd; u32 idx, ring_idx; struct vmxnet3_cmd_ring *ring = NULL; - if (num_rxd >= quota) { + if (num_pkts >= quota) { /* we may stop even before we see the EOP desc of * the current pkt */ break; } - num_rxd++; BUG_ON(rcd->rqID != rq->qid && rcd->rqID != rq->qid2); idx = rcd->rxdIdx; ring_idx = rcd->rqID < adapter->num_rx_queues ? 0 : 1; @@ -1238,6 +1306,21 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq, skip_page_frags = true; goto rcd_done; } + new_dma_addr = dma_map_single(&adapter->pdev->dev, + new_skb->data, rbi->len, + PCI_DMA_FROMDEVICE); + if (dma_mapping_error(&adapter->pdev->dev, + new_dma_addr)) { + dev_kfree_skb(new_skb); + /* Skb allocation failed, do not handover this + * skb to stack. Reuse it. Drop the existing pkt + */ + rq->stats.rx_buf_alloc_failure++; + ctx->skb = NULL; + rq->stats.drop_total++; + skip_page_frags = true; + goto rcd_done; + } dma_unmap_single(&adapter->pdev->dev, rbi->dma_addr, rbi->len, @@ -1254,12 +1337,22 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq, /* Immediate refill */ rbi->skb = new_skb; - rbi->dma_addr = dma_map_single(&adapter->pdev->dev, - rbi->skb->data, rbi->len, - PCI_DMA_FROMDEVICE); + rbi->dma_addr = new_dma_addr; rxd->addr = cpu_to_le64(rbi->dma_addr); rxd->len = rbi->len; - + if (adapter->version == 2 && + rcd->type == VMXNET3_CDTYPE_RXCOMP_LRO) { + struct Vmxnet3_RxCompDescExt *rcdlro; + rcdlro = (struct Vmxnet3_RxCompDescExt *)rcd; + + segCnt = rcdlro->segCnt; + BUG_ON(segCnt <= 1); + mss = rcdlro->mss; + if (unlikely(segCnt <= 1)) + segCnt = 0; + } else { + segCnt = 0; + } } else { BUG_ON(ctx->skb == NULL && !skip_page_frags); @@ -1273,47 +1366,85 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq, if (skip_page_frags) goto rcd_done; - new_page = alloc_page(GFP_ATOMIC); - if (unlikely(new_page == NULL)) { + if (rcd->len) { + new_page = alloc_page(GFP_ATOMIC); /* Replacement page frag could not be allocated. * Reuse this page. Drop the pkt and free the * skb which contained this page as a frag. Skip * processing all the following non-sop frags. */ - rq->stats.rx_buf_alloc_failure++; - dev_kfree_skb(ctx->skb); - ctx->skb = NULL; - skip_page_frags = true; - goto rcd_done; - } + if (unlikely(!new_page)) { + rq->stats.rx_buf_alloc_failure++; + dev_kfree_skb(ctx->skb); + ctx->skb = NULL; + skip_page_frags = true; + goto rcd_done; + } + new_dma_addr = dma_map_page(&adapter->pdev->dev, + new_page, + 0, PAGE_SIZE, + PCI_DMA_FROMDEVICE); + if (dma_mapping_error(&adapter->pdev->dev, + new_dma_addr)) { + put_page(new_page); + rq->stats.rx_buf_alloc_failure++; + dev_kfree_skb(ctx->skb); + ctx->skb = NULL; + skip_page_frags = true; + goto rcd_done; + } - if (rcd->len) { dma_unmap_page(&adapter->pdev->dev, rbi->dma_addr, rbi->len, PCI_DMA_FROMDEVICE); vmxnet3_append_frag(ctx->skb, rcd, rbi); - } - /* Immediate refill */ - rbi->page = new_page; - rbi->dma_addr = dma_map_page(&adapter->pdev->dev, - rbi->page, - 0, PAGE_SIZE, - PCI_DMA_FROMDEVICE); - rxd->addr = cpu_to_le64(rbi->dma_addr); - rxd->len = rbi->len; + /* Immediate refill */ + rbi->page = new_page; + rbi->dma_addr = new_dma_addr; + rxd->addr = cpu_to_le64(rbi->dma_addr); + rxd->len = rbi->len; + } } skb = ctx->skb; if (rcd->eop) { + u32 mtu = adapter->netdev->mtu; skb->len += skb->data_len; vmxnet3_rx_csum(adapter, skb, (union Vmxnet3_GenericDesc *)rcd); skb->protocol = eth_type_trans(skb, adapter->netdev); - + if (!rcd->tcp || !adapter->lro) + goto not_lro; + + if (segCnt != 0 && mss != 0) { + skb_shinfo(skb)->gso_type = rcd->v4 ? + SKB_GSO_TCPV4 : SKB_GSO_TCPV6; + skb_shinfo(skb)->gso_size = mss; + skb_shinfo(skb)->gso_segs = segCnt; + } else if (segCnt != 0 || skb->len > mtu) { + u32 hlen; + + hlen = vmxnet3_get_hdr_len(adapter, skb, + (union Vmxnet3_GenericDesc *)rcd); + if (hlen == 0) + goto not_lro; + + skb_shinfo(skb)->gso_type = + rcd->v4 ? SKB_GSO_TCPV4 : SKB_GSO_TCPV6; + if (segCnt != 0) { + skb_shinfo(skb)->gso_segs = segCnt; + skb_shinfo(skb)->gso_size = + DIV_ROUND_UP(skb->len - + hlen, segCnt); + } else { + skb_shinfo(skb)->gso_size = mtu - hlen; + } + } +not_lro: if (unlikely(rcd->ts)) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rcd->tci); @@ -1323,6 +1454,7 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq, napi_gro_receive(&rq->napi, skb); ctx->skb = NULL; + num_pkts++; } rcd_done: @@ -1353,7 +1485,7 @@ rcd_done: &rq->comp_ring.base[rq->comp_ring.next2proc].rcd, &rxComp); } - return num_rxd; + return num_pkts; } @@ -2067,16 +2199,18 @@ vmxnet3_set_mc(struct net_device *netdev) if (!netdev_mc_empty(netdev)) { new_table = vmxnet3_copy_mc(netdev); if (new_table) { - rxConf->mfTableLen = cpu_to_le16( - netdev_mc_count(netdev) * ETH_ALEN); + size_t sz = netdev_mc_count(netdev) * ETH_ALEN; + + rxConf->mfTableLen = cpu_to_le16(sz); new_table_pa = dma_map_single( &adapter->pdev->dev, new_table, - rxConf->mfTableLen, + sz, PCI_DMA_TODEVICE); } - if (new_table_pa) { + if (!dma_mapping_error(&adapter->pdev->dev, + new_table_pa)) { new_mode |= VMXNET3_RXM_MCAST; rxConf->mfTablePA = cpu_to_le64(new_table_pa); } else { @@ -2984,6 +3118,11 @@ vmxnet3_probe_device(struct pci_dev *pdev, adapter->adapter_pa = dma_map_single(&adapter->pdev->dev, adapter, sizeof(struct vmxnet3_adapter), PCI_DMA_TODEVICE); + if (dma_mapping_error(&adapter->pdev->dev, adapter->adapter_pa)) { + dev_err(&pdev->dev, "Failed to map dma\n"); + err = -EFAULT; + goto err_dma_map; + } adapter->shared = dma_alloc_coherent( &adapter->pdev->dev, sizeof(struct Vmxnet3_DriverShared), @@ -3038,14 +3177,19 @@ vmxnet3_probe_device(struct pci_dev *pdev, goto err_alloc_pci; ver = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_VRRS); - if (ver & 1) { + if (ver & 2) { + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_VRRS, 2); + adapter->version = 2; + } else if (ver & 1) { VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_VRRS, 1); + adapter->version = 1; } else { dev_err(&pdev->dev, "Incompatible h/w version (0x%x) for adapter\n", ver); err = -EBUSY; goto err_ver; } + dev_dbg(&pdev->dev, "Using device version %d\n", adapter->version); ver = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_UVRS); if (ver & 1) { @@ -3137,6 +3281,7 @@ err_alloc_queue_desc: err_alloc_shared: dma_unmap_single(&adapter->pdev->dev, adapter->adapter_pa, sizeof(struct vmxnet3_adapter), PCI_DMA_TODEVICE); +err_dma_map: free_netdev(netdev); return err; } @@ -3184,6 +3329,32 @@ vmxnet3_remove_device(struct pci_dev *pdev) free_netdev(netdev); } +static void vmxnet3_shutdown_device(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + unsigned long flags; + + /* Reset_work may be in the middle of resetting the device, wait for its + * completion. + */ + while (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state)) + msleep(1); + + if (test_and_set_bit(VMXNET3_STATE_BIT_QUIESCED, + &adapter->state)) { + clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state); + return; + } + spin_lock_irqsave(&adapter->cmd_lock, flags); + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, + VMXNET3_CMD_QUIESCE_DEV); + spin_unlock_irqrestore(&adapter->cmd_lock, flags); + vmxnet3_disable_all_intrs(adapter); + + clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state); +} + #ifdef CONFIG_PM @@ -3360,6 +3531,7 @@ static struct pci_driver vmxnet3_driver = { .id_table = vmxnet3_pciid_table, .probe = vmxnet3_probe_device, .remove = vmxnet3_remove_device, + .shutdown = vmxnet3_shutdown_device, #ifdef CONFIG_PM .driver.pm = &vmxnet3_pm_ops, #endif diff --git a/kernel/drivers/net/vmxnet3/vmxnet3_ethtool.c b/kernel/drivers/net/vmxnet3/vmxnet3_ethtool.c index c1d0e7a9d..9ba11d737 100644 --- a/kernel/drivers/net/vmxnet3/vmxnet3_ethtool.c +++ b/kernel/drivers/net/vmxnet3/vmxnet3_ethtool.c @@ -183,16 +183,22 @@ vmxnet3_get_sset_count(struct net_device *netdev, int sset) } -/* Should be multiple of 4 */ -#define NUM_TX_REGS 8 -#define NUM_RX_REGS 12 - +/* This is a version 2 of the vmxnet3 ethtool_regs which goes hand in hand with + * the version 2 of the vmxnet3 support for ethtool(8) --register-dump. + * Therefore, if any registers are added, removed or modified, then a version + * bump and a corresponding change in the vmxnet3 support for ethtool(8) + * --register-dump would be required. + */ static int vmxnet3_get_regs_len(struct net_device *netdev) { struct vmxnet3_adapter *adapter = netdev_priv(netdev); - return (adapter->num_tx_queues * NUM_TX_REGS * sizeof(u32) + - adapter->num_rx_queues * NUM_RX_REGS * sizeof(u32)); + + return ((9 /* BAR1 registers */ + + (1 + adapter->intr.num_intrs) + + (1 + adapter->num_tx_queues * 17 /* Tx queue registers */) + + (1 + adapter->num_rx_queues * 23 /* Rx queue registers */)) * + sizeof(u32)); } @@ -208,10 +214,6 @@ vmxnet3_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->n_stats = vmxnet3_get_sset_count(netdev, ETH_SS_STATS); - drvinfo->testinfo_len = 0; - drvinfo->eedump_len = 0; - drvinfo->regdump_len = vmxnet3_get_regs_len(netdev); } @@ -342,6 +344,12 @@ vmxnet3_get_ethtool_stats(struct net_device *netdev, } +/* This is a version 2 of the vmxnet3 ethtool_regs which goes hand in hand with + * the version 2 of the vmxnet3 support for ethtool(8) --register-dump. + * Therefore, if any registers are added, removed or modified, then a version + * bump and a corresponding change in the vmxnet3 support for ethtool(8) + * --register-dump would be required. + */ static void vmxnet3_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p) { @@ -351,40 +359,90 @@ vmxnet3_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p) memset(p, 0, vmxnet3_get_regs_len(netdev)); - regs->version = 1; + regs->version = 2; /* Update vmxnet3_get_regs_len if we want to dump more registers */ - /* make each ring use multiple of 16 bytes */ - for (i = 0; i < adapter->num_tx_queues; i++) { - buf[j++] = adapter->tx_queue[i].tx_ring.next2fill; - buf[j++] = adapter->tx_queue[i].tx_ring.next2comp; - buf[j++] = adapter->tx_queue[i].tx_ring.gen; - buf[j++] = 0; + buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_VRRS); + buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_UVRS); + buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_DSAL); + buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_DSAH); + buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); + buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_MACL); + buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_MACH); + buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_ICR); + buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_ECR); + + buf[j++] = adapter->intr.num_intrs; + for (i = 0; i < adapter->intr.num_intrs; i++) { + buf[j++] = VMXNET3_READ_BAR0_REG(adapter, VMXNET3_REG_IMR + + i * VMXNET3_REG_ALIGN); + } - buf[j++] = adapter->tx_queue[i].comp_ring.next2proc; - buf[j++] = adapter->tx_queue[i].comp_ring.gen; - buf[j++] = adapter->tx_queue[i].stopped; - buf[j++] = 0; + buf[j++] = adapter->num_tx_queues; + for (i = 0; i < adapter->num_tx_queues; i++) { + struct vmxnet3_tx_queue *tq = &adapter->tx_queue[i]; + + buf[j++] = VMXNET3_READ_BAR0_REG(adapter, VMXNET3_REG_TXPROD + + i * VMXNET3_REG_ALIGN); + + buf[j++] = VMXNET3_GET_ADDR_LO(tq->tx_ring.basePA); + buf[j++] = VMXNET3_GET_ADDR_HI(tq->tx_ring.basePA); + buf[j++] = tq->tx_ring.size; + buf[j++] = tq->tx_ring.next2fill; + buf[j++] = tq->tx_ring.next2comp; + buf[j++] = tq->tx_ring.gen; + + buf[j++] = VMXNET3_GET_ADDR_LO(tq->data_ring.basePA); + buf[j++] = VMXNET3_GET_ADDR_HI(tq->data_ring.basePA); + buf[j++] = tq->data_ring.size; + /* transmit data ring buffer size */ + buf[j++] = VMXNET3_HDR_COPY_SIZE; + + buf[j++] = VMXNET3_GET_ADDR_LO(tq->comp_ring.basePA); + buf[j++] = VMXNET3_GET_ADDR_HI(tq->comp_ring.basePA); + buf[j++] = tq->comp_ring.size; + buf[j++] = tq->comp_ring.next2proc; + buf[j++] = tq->comp_ring.gen; + + buf[j++] = tq->stopped; } + buf[j++] = adapter->num_rx_queues; for (i = 0; i < adapter->num_rx_queues; i++) { - buf[j++] = adapter->rx_queue[i].rx_ring[0].next2fill; - buf[j++] = adapter->rx_queue[i].rx_ring[0].next2comp; - buf[j++] = adapter->rx_queue[i].rx_ring[0].gen; + struct vmxnet3_rx_queue *rq = &adapter->rx_queue[i]; + + buf[j++] = VMXNET3_READ_BAR0_REG(adapter, VMXNET3_REG_RXPROD + + i * VMXNET3_REG_ALIGN); + buf[j++] = VMXNET3_READ_BAR0_REG(adapter, VMXNET3_REG_RXPROD2 + + i * VMXNET3_REG_ALIGN); + + buf[j++] = VMXNET3_GET_ADDR_LO(rq->rx_ring[0].basePA); + buf[j++] = VMXNET3_GET_ADDR_HI(rq->rx_ring[0].basePA); + buf[j++] = rq->rx_ring[0].size; + buf[j++] = rq->rx_ring[0].next2fill; + buf[j++] = rq->rx_ring[0].next2comp; + buf[j++] = rq->rx_ring[0].gen; + + buf[j++] = VMXNET3_GET_ADDR_LO(rq->rx_ring[1].basePA); + buf[j++] = VMXNET3_GET_ADDR_HI(rq->rx_ring[1].basePA); + buf[j++] = rq->rx_ring[1].size; + buf[j++] = rq->rx_ring[1].next2fill; + buf[j++] = rq->rx_ring[1].next2comp; + buf[j++] = rq->rx_ring[1].gen; + + /* receive data ring */ buf[j++] = 0; - - buf[j++] = adapter->rx_queue[i].rx_ring[1].next2fill; - buf[j++] = adapter->rx_queue[i].rx_ring[1].next2comp; - buf[j++] = adapter->rx_queue[i].rx_ring[1].gen; buf[j++] = 0; - - buf[j++] = adapter->rx_queue[i].comp_ring.next2proc; - buf[j++] = adapter->rx_queue[i].comp_ring.gen; buf[j++] = 0; buf[j++] = 0; - } + buf[j++] = VMXNET3_GET_ADDR_LO(rq->comp_ring.basePA); + buf[j++] = VMXNET3_GET_ADDR_HI(rq->comp_ring.basePA); + buf[j++] = rq->comp_ring.size; + buf[j++] = rq->comp_ring.next2proc; + buf[j++] = rq->comp_ring.gen; + } } diff --git a/kernel/drivers/net/vmxnet3/vmxnet3_int.h b/kernel/drivers/net/vmxnet3/vmxnet3_int.h index 6bb769ae7..bdb8a6c0f 100644 --- a/kernel/drivers/net/vmxnet3/vmxnet3_int.h +++ b/kernel/drivers/net/vmxnet3/vmxnet3_int.h @@ -69,10 +69,10 @@ /* * Version numbers */ -#define VMXNET3_DRIVER_VERSION_STRING "1.3.5.0-k" +#define VMXNET3_DRIVER_VERSION_STRING "1.4.5.0-k" /* a 32-bit int, each byte encode a verion number in VMXNET3_DRIVER_VERSION */ -#define VMXNET3_DRIVER_VERSION_NUM 0x01030500 +#define VMXNET3_DRIVER_VERSION_NUM 0x01040500 #if defined(CONFIG_PCI_MSI) /* RSS only makes sense if MSI-X is supported. */ @@ -328,6 +328,10 @@ struct vmxnet3_adapter { u8 __iomem *hw_addr0; /* for BAR 0 */ u8 __iomem *hw_addr1; /* for BAR 1 */ + u8 version; + + bool rxcsum; + bool lro; #ifdef VMXNET3_RSS struct UPT1_RSSConf *rss_conf; |