cxgb4vf: Add T4 Virtual Function Scatter-Gather Engine DMA code
Subsystems (2)
PATCH 6/9 cxgb4vf: Add T4 Virtual Function Scatter-Gather Engine DMA code
Add T4 Virtual Function Scatter-Gather Engine DMA code.
Signed-off-by Casey Leedom
--- drivers/net/cxgb4vf/sge.c | 2460 +++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 2460 insertions(+), 0 deletions(-)
drivers/net/cxgb4vf/sge.c new file +2460 -0
@@ -0,0 +1,2460 @@ +/* +* This file is part of the Chelsio T4 PCI-E SR-IOV Virtual Function Ethernet +* driver for Linux. +* +* Copyright (c) 2009-2010 Chelsio Communications, Inc. All rights reserved. +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* Redistribution and use in source and binary forms, with or +* without modification, are permitted provided that the following +* conditions are met: +* +* - Redistributions of source code must retain the above +* copyright notice, this list of conditions and the following +* disclaimer. +* +* - Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/if_vlan.h> +#include <linux/ip.h> +#include <net/ipv6.h> +#include <net/tcp.h> +#include <linux/dma-mapping.h> + +#include "t4vf_common.h" +#include "t4vf_defs.h" + +#include "../cxgb4/t4_regs.h" +#include "../cxgb4/t4fw_api.h" +#include "../cxgb4/t4_msg.h" + +/* +* Decoded Adapter Parameters. +*/ +static u32 FL_PG_ORDER ;/* large page allocation size */ +static u32 STAT_LEN ;/* length of status page at ring end */ +static u32 PKTSHIFT ;/* padding between CPL and packet data */ +static u32 FL_ALIGN ;/* response queue message alignment */ + +/* +* Constants ... +*/ +enum { +/* +* Egress Queue sizes, producer and consumer indices are all in units +* of Egress Context Units bytes. Note that as far as the hardware is +* concerned, the free list is an Egress Queue (the host produces free +* buffers which the hardware consumes) and free list entries are +* 64-bit PCI DMA addresses. +*/ +EQ_UNIT =SGE_EQ_IDXSIZE , +FL_PER_EQ_UNIT =EQ_UNIT /sizeof (__be64), +TXD_PER_EQ_UNIT =EQ_UNIT /sizeof (__be64), + +/* +* Max number of TX descriptors we clean up at a time. Should be +* modest as freeing skbs isn't cheap and it happens while holding +* locks. We just need to free packets faster than they arrive, we +* eventually catch up and keep the amortized cost reasonable. +*/ +MAX_TX_RECLAIM =16 , + +/* +* Max number of Rx buffers we replenish at a time. Again keep this +* modest, allocating buffers isn't cheap either. +*/ +MAX_RX_REFILL =16 , + +/* +* Period of the Rx queue check timer. This timer is infrequent as it +* has something to do only when the system experiences severe memory +* shortage. +*/ +RX_QCHECK_PERIOD = (HZ /2 ), + +/* +* Period of the TX queue check timer and the maximum number of TX +* descriptors to be reclaimed by the TX timer. +*/ +TX_QCHECK_PERIOD = (HZ /2 ), +MAX_TIMER_TX_RECLAIM =100 , + +/* +* An FL with <= FL_STARVE_THRES buffers is starving and a periodic +* timer will attempt to refill it. +*/ +FL_STARVE_THRES =4 , + +/* +* Suspend an Ethernet TX queue with fewer available descriptors than +* this. We always want to have room for a maximum sized packet: +* inline immediate data + MAX_SKB_FRAGS. This is the same as +* calc_tx_flits() for a TSO packet with nr_frags == MAX_SKB_FRAGS +* (see that function and its helpers for a description of the +* calculation). +*/ +ETHTXQ_MAX_FRAGS =MAX_SKB_FRAGS +1 , +ETHTXQ_MAX_SGL_LEN = ((3 * (ETHTXQ_MAX_FRAGS -1 ))/2 + + ((ETHTXQ_MAX_FRAGS -1 ) &1 ) + +2 ), +ETHTXQ_MAX_HDR = (sizeof (struct fw_eth_tx_pkt_vm_wr ) + +sizeof (struct cpl_tx_pkt_lso_core ) + +sizeof (struct cpl_tx_pkt_core )) /sizeof (__be64), +ETHTXQ_MAX_FLITS =ETHTXQ_MAX_SGL_LEN +ETHTXQ_MAX_HDR , + +ETHTXQ_STOP_THRES =1 +DIV_ROUND_UP (ETHTXQ_MAX_FLITS ,TXD_PER_EQ_UNIT ), + +/* +* Max TX descriptor space we allow for an Ethernet packet to be +* inlined into a WR. This is limited by the maximum value which +* we can specify for immediate data in the firmware Ethernet TX +* Work Request. +*/ +MAX_IMM_TX_PKT_LEN =FW_WR_IMMDLEN_MASK , + +/* +* Max size of a WR sent through a control TX queue. +*/ +MAX_CTRL_WR_LEN =256 , + +/* +* Maximum amount of data which we'll ever need to inline into a +* TX ring: max(MAX_IMM_TX_PKT_LEN, MAX_CTRL_WR_LEN). +*/ +MAX_IMM_TX_LEN = (MAX_IMM_TX_PKT_LEN >MAX_CTRL_WR_LEN + ?MAX_IMM_TX_PKT_LEN + :MAX_CTRL_WR_LEN ), + +/* +* For incoming packets less than RX_COPY_THRES, we copy the data into +* an skb rather than referencing the data. We allocate enough +* in-line room in skb's to accommodate pulling in RX_PULL_LEN bytes +* of the data (header). +*/ +RX_COPY_THRES =256 , +RX_PULL_LEN =128 , +}; + +/* +* Can't define this in the above enum because PKTSHIFT isn't a constant in +* the VF Driver ... +*/ +#define RX_PKT_PULL_LEN (RX_PULL_LEN + PKTSHIFT) + +/* +* Software state per TX descriptor. +*/ +struct tx_sw_desc { +struct sk_buff *skb;/* socket buffer of TX data source */ +struct ulptx_sgl *sgl;/* scatter/gather list in TX Queue */ +}; + +/* +* Software state per RX Free List descriptor. We keep track of the allocated +* FL page, its size, and its PCI DMA address (if the page is mapped). The FL +* page size and its PCI DMA mapped state are stored in the low bits of the +* PCI DMA address as per below. +*/ +struct rx_sw_desc { +struct page *page;/* Free List page buffer */ +dma_addr_t dma_addr;/* PCI DMA address (if mapped) */ +/* and flags (see below) */ +}; + +/* +* The low bits of rx_sw_desc.dma_addr have special meaning. Note that the +* SGE also uses the low 4 bits to determine the size of the buffer. It uses +* those bits to index into the SGE_FL_BUFFER_SIZE[index] register array. +* Since we only use SGE_FL_BUFFER_SIZE0 and SGE_FL_BUFFER_SIZE1, these low 4 +* bits can only contain a 0 or a 1 to indicate which size buffer we're giving +* to the SGE. Thus, our software state of "is the buffer mapped for DMA" is +* maintained in an inverse sense so the hardware never sees that bit high. +*/ +enum { +RX_LARGE_BUF =1 <<0 ,/* buffer is SGE_FL_BUFFER_SIZE[1] */ +RX_UNMAPPED_BUF =1 <<1 ,/* buffer is not mapped */ +}; + +/** +* get_buf_addr - return DMA buffer address of software descriptor +* @sdesc: pointer to the software buffer descriptor +* +* Return the DMA buffer address of a software descriptor (stripping out +* our low-order flag bits). +*/ +static inline dma_addr_t get_buf_addr (const struct rx_sw_desc *sdesc) +{ +return sdesc->dma_addr & ~(dma_addr_t)(RX_LARGE_BUF |RX_UNMAPPED_BUF ); +} + +/** +* is_buf_mapped - is buffer mapped for DMA? +* @sdesc: pointer to the software buffer descriptor +* +* Determine whether the buffer associated with a software descriptor in +* mapped for DMA or not. +*/ +static inline bool is_buf_mapped (const struct rx_sw_desc *sdesc) +{ +return !(sdesc->dma_addr &RX_UNMAPPED_BUF ); +} + +/** +* need_skb_unmap - does the platform need unmapping of sk_buffs? +* +* Returns true if the platfrom needs sk_buff unmapping. The compiler +* optimizes away unecessary code if this returns true. +*/ +static inline int need_skb_unmap (void ) +{ +/* +* This structure is used to tell if the platfrom needs buffer +* unmapping by checking if DECLARE_PCI_UNMAP_ADDR defines anything. +*/ +struct dummy { +DECLARE_PCI_UNMAP_ADDR (addr); + }; + +return sizeof (struct dummy ) !=0 ; +} + +/** +* txq_avail - return the number of available slots in a TX queue +* @tq: the TX queue +* +* Returns the number of available descriptors in a TX queue. +*/ +static inline unsigned int txq_avail (const struct sge_txq *tq) +{ +return tq->size -1 - tq->in_use; +} + +/** +* fl_cap - return the capacity of a Free List +* @fl: the Free List +* +* Returns the capacity of a Free List. The capacity is less than the +* size because an Egress Queue Index Unit worth of descriptors needs to +* be left unpopulated, otherwise the Producer and Consumer indices PIDX +* and CIDX will match and the hardware will think the FL is empty. +*/ +static inline unsigned int fl_cap (const struct sge_fl *fl) +{ +return fl->size -FL_PER_EQ_UNIT ; +} + +/** +* fl_starving - return whether a Free List is starving. +* @fl: the Free List +* +* Tests specified Free List to see whether the number of buffers +* available to the hardware has falled below our "starvation" +* threshhold. +*/ +static inline bool fl_starving (const struct sge_fl *fl) +{ +return fl->avail - fl->pend_cred <=FL_STARVE_THRES ; +} + +/** +* map_skb - map an skb for DMA to the device +* @dev: the egress net device +* @skb: the packet to map +* @addr: a pointer to the base of the DMA mapping array +* +* Map an skb for DMA to the device and return an array of DMA addresses. +*/ +static int map_skb (struct device *dev,const struct sk_buff *skb, +dma_addr_t *addr) +{ +const skb_frag_t *fp, *end; +const struct skb_shared_info *si; + + *addr =dma_map_single (dev, skb->data,skb_headlen (skb),DMA_TO_DEVICE ); +if (dma_mapping_error (dev, *addr)) + goto out_err; + + si =skb_shinfo (skb); + end = &si->frags[si->nr_frags]; +for (fp = si->frags; fp < end; fp++) { + *++addr =dma_map_page (dev, fp->page, fp->page_offset, fp->size, +DMA_TO_DEVICE ); +if (dma_mapping_error (dev, *addr)) + goto unwind; + } +return 0 ; + +unwind: +while (fp-- > si->frags) +dma_unmap_page (dev, *--addr, fp->size,DMA_TO_DEVICE ); +dma_unmap_single (dev, addr[-1 ],skb_headlen (skb),DMA_TO_DEVICE ); + +out_err: +return -ENOMEM ; +} + +static void unmap_sgl (struct device *dev,const struct sk_buff *skb, +const struct ulptx_sgl *sgl,const struct sge_txq *tq) +{ +const struct ulptx_sge_pair *p; +unsigned int nfrags =skb_shinfo (skb)->nr_frags; + +if (likely (skb_headlen (skb))) +dma_unmap_single (dev,be64_to_cpu (sgl->addr0), +be32_to_cpu (sgl->len0),DMA_TO_DEVICE ); +else { +dma_unmap_page (dev,be64_to_cpu (sgl->addr0), +be32_to_cpu (sgl->len0),DMA_TO_DEVICE ); + nfrags--; + } + +/* +* the complexity below is because of the possibility of a wrap-around +* in the middle of an SGL +*/ +for (p = sgl->sge; nfrags >=2 ; nfrags -=2 ) { +if (likely ((u8 *)(p +1 ) <= (u8 *)tq->stat)) { +unmap: +dma_unmap_page (dev,be64_to_cpu (p->addr[0 ]), +be32_to_cpu (p->len[0 ]),DMA_TO_DEVICE ); +dma_unmap_page (dev,be64_to_cpu (p->addr[1 ]), +be32_to_cpu (p->len[1 ]),DMA_TO_DEVICE ); + p++; + }else if ((u8 *)p == (u8 *)tq->stat) { + p = (const struct ulptx_sge_pair *)tq->desc; + goto unmap; + }else if ((u8 *)p +8 == (u8 *)tq->stat) { +const __be64 *addr = (const __be64 *)tq->desc; + +dma_unmap_page (dev,be64_to_cpu (addr[0 ]), +be32_to_cpu (p->len[0 ]),DMA_TO_DEVICE ); +dma_unmap_page (dev,be64_to_cpu (addr[1 ]), +be32_to_cpu (p->len[1 ]),DMA_TO_DEVICE ); + p = (const struct ulptx_sge_pair *)&addr[2 ]; + }else { +const __be64 *addr = (const __be64 *)tq->desc; + +dma_unmap_page (dev,be64_to_cpu (p->addr[0 ]), +be32_to_cpu (p->len[0 ]),DMA_TO_DEVICE ); +dma_unmap_page (dev,be64_to_cpu (addr[0 ]), +be32_to_cpu (p->len[1 ]),DMA_TO_DEVICE ); + p = (const struct ulptx_sge_pair *)&addr[1 ]; + } + } +if (nfrags) { +__be64 addr; + +if ((u8 *)p == (u8 *)tq->stat) + p = (const struct ulptx_sge_pair *)tq->desc; + addr = ((u8 *)p +16 <= (u8 *)tq->stat + ? p->addr[0 ] + : *(const __be64 *)tq->desc); +dma_unmap_page (dev,be64_to_cpu (addr),be32_to_cpu (p->len[0 ]), +DMA_TO_DEVICE ); + } +} + +/** +* free_tx_desc - reclaims TX descriptors and their buffers +* @adapter: the adapter +* @tq: the TX queue to reclaim descriptors from +* @n: the number of descriptors to reclaim +* @unmap: whether the buffers should be unmapped for DMA +* +* Reclaims TX descriptors from an SGE TX queue and frees the associated +* TX buffers. Called with the TX queue lock held. +*/ +static void free_tx_desc (struct adapter *adapter,struct sge_txq *tq, +unsigned int n,bool unmap) +{ +struct tx_sw_desc *sdesc; +unsigned int cidx = tq->cidx; +struct device *dev = adapter->pdev_dev; + +const int need_unmap =need_skb_unmap () && unmap; + + sdesc = &tq->sdesc[cidx]; +while (n--) { +/* +* If we kept a reference to the original TX skb, we need to +* unmap it from PCI DMA space (if required) and free it. +*/ +if (sdesc->skb) { +if (need_unmap) +unmap_sgl (dev, sdesc->skb, sdesc->sgl, tq); +kfree_skb (sdesc->skb); + sdesc->skb =NULL ; + } + + sdesc++; +if (++cidx == tq->size) { + cidx =0 ; + sdesc = tq->sdesc; + } + } + tq->cidx = cidx; +} + +/* +* Return the number of reclaimable descriptors in a TX queue. +*/ +static inline int reclaimable (const struct sge_txq *tq) +{ +int hw_cidx =be16_to_cpu (tq->stat->cidx); +int reclaimable = hw_cidx - tq->cidx; +if (reclaimable <0 ) + reclaimable += tq->size; +return reclaimable; +} + +/** +* reclaim_completed_tx - reclaims completed TX descriptors +* @adapter: the adapter +* @tq: the TX queue to reclaim completed descriptors from +* @unmap: whether the buffers should be unmapped for DMA +* +* Reclaims TX descriptors that the SGE has indicated it has processed, +* and frees the associated buffers if possible. Called with the TX +* queue locked. +*/ +static inline void reclaim_completed_tx (struct adapter *adapter, +struct sge_txq *tq, +bool unmap) +{ +int avail =reclaimable (tq); + +if (avail) { +/* +* Limit the amount of clean up work we do at a time to keep +* the TX lock hold time O(1). +*/ +if (avail >MAX_TX_RECLAIM ) + avail =MAX_TX_RECLAIM ; + +free_tx_desc (adapter, tq, avail, unmap); + tq->in_use -= avail; + } +} + +/** +* get_buf_size - return the size of an RX Free List buffer. +* @sdesc: pointer to the software buffer descriptor +*/ +static inline int get_buf_size (const struct rx_sw_desc *sdesc) +{ +return FL_PG_ORDER >0 && (sdesc->dma_addr &RX_LARGE_BUF ) + ? (PAGE_SIZE <<FL_PG_ORDER ) + :PAGE_SIZE ; +} + +/** +* free_rx_bufs - free RX buffers on an SGE Free List +* @adapter: the adapter +* @fl: the SGE Free List to free buffers from +* @n: how many buffers to free +* +* Release the next @n buffers on an SGE Free List RX queue. The +* buffers must be made inaccessible to hardware before calling this +* function. +*/ +static void free_rx_bufs (struct adapter *adapter,struct sge_fl *fl,int n) +{ +while (n--) { +struct rx_sw_desc *sdesc = &fl->sdesc[fl->cidx]; + +if (is_buf_mapped (sdesc)) +dma_unmap_page (adapter->pdev_dev,get_buf_addr (sdesc), +get_buf_size (sdesc),PCI_DMA_FROMDEVICE ); +put_page (sdesc->page); + sdesc->page =NULL ; +if (++fl->cidx == fl->size) + fl->cidx =0 ; + fl->avail--; + } +} + +/** +* unmap_rx_buf - unmap the current RX buffer on an SGE Free List +* @adapter: the adapter +* @fl: the SGE Free List +* +* Unmap the current buffer on an SGE Free List RX queue. The +* buffer must be made inaccessible to HW before calling this function. +* +* This is similar to @free_rx_bufs above but does not free the buffer. +* Do note that the FL still loses any further access to the buffer. +* This is used predominantly to "transfer ownership" of an FL buffer +* to another entity (typically an skb's fragment list). +*/ +static void unmap_rx_buf (struct adapter *adapter,struct sge_fl *fl) +{ +struct rx_sw_desc *sdesc = &fl->sdesc[fl->cidx]; + +if (is_buf_mapped (sdesc)) +dma_unmap_page (adapter->pdev_dev,get_buf_addr (sdesc), +get_buf_size (sdesc),PCI_DMA_FROMDEVICE ); + sdesc->page =NULL ; +if (++fl->cidx == fl->size) + fl->cidx =0 ; + fl->avail--; +} + +/** +* ring_fl_db - righ doorbell on free list +* @adapter: the adapter +* @fl: the Free List whose doorbell should be rung ... +* +* Tell the Scatter Gather Engine that there are new free list entries +* available. +*/ +static inline void ring_fl_db (struct adapter *adapter,struct sge_fl *fl) +{ +/* +* The SGE keeps track of its Producer and Consumer Indices in terms +* of Egress Queue Units so we can only tell it about integral numbers +* of multiples of Free List Entries per Egress Queue Units ... +*/ +if (fl->pend_cred >=FL_PER_EQ_UNIT ) { +wmb (); +t4_write_reg (adapter,T4VF_SGE_BASE_ADDR +SGE_VF_KDOORBELL , +DBPRIO | +QID (fl->cntxt_id) | +PIDX (fl->pend_cred /FL_PER_EQ_UNIT )); + fl->pend_cred %=FL_PER_EQ_UNIT ; + } +} + +/** +* set_rx_sw_desc - initialize software RX buffer descriptor +* @sdesc: pointer to the softwore RX buffer descriptor +* @page: pointer to the page data structure backing the RX buffer +* @dma_addr: PCI DMA address (possibly with low-bit flags) +*/ +static inline void set_rx_sw_desc (struct rx_sw_desc *sdesc,struct page *page, +dma_addr_t dma_addr) +{ + sdesc->page = page; + sdesc->dma_addr = dma_addr; +} + +/* +* Support for poisoning RX buffers ... +*/ +#define POISON_BUF_VAL -1 + +static inline void poison_buf (struct page *page,size_t sz) +{ +#if POISON_BUF_VAL >=0 +memset (page_address (page),POISON_BUF_VAL , sz); +#endif +} + +/** +* refill_fl - refill an SGE RX buffer ring +* @adapter: the adapter +* @fl: the Free List ring to refill +* @n: the number of new buffers to allocate +* @gfp: the gfp flags for the allocations +* +* (Re)populate an SGE free-buffer queue with up to @n new packet buffers, +* allocated with the supplied gfp flags. The caller must assure that +* @n does not exceed the queue's capacity -- i.e. (cidx == pidx) _IN +* EGRESS QUEUE UNITS_ indicates an empty Free List! Returns the number +* of buffers allocated. If afterwards the queue is found critically low, +* mark it as starving in the bitmap of starving FLs. +*/ +static unsigned int refill_fl (struct adapter *adapter,struct sge_fl *fl, +int n,gfp_t gfp) +{ +struct page *page; +dma_addr_t dma_addr; +unsigned int cred = fl->avail; +__be64 *d = &fl->desc[fl->pidx]; +struct rx_sw_desc *sdesc = &fl->sdesc[fl->pidx]; + +/* +* Sanity: ensure that the result of adding n Free List buffers +* won't result in wrapping the SGE's Producer Index around to +* it's Consumer Index thereby indicating an empty Free List ... +*/ +BUG_ON (fl->avail + n > fl->size -FL_PER_EQ_UNIT ); + +/* +* If we support large pages, prefer large buffers and fail over to +* small pages if we can't allocate large pages to satisfy the refill. +* If we don't support large pages, drop directly into the small page +* allocation code. +*/ +if (FL_PG_ORDER ==0 ) + goto alloc_small_pages; + +while (n) { + page =alloc_pages (gfp | __GFP_COMP | __GFP_NOWARN, +FL_PG_ORDER ); +if (unlikely (!page)) { +/* +* We've failed inour attempt to allocate a "large +* page". Fail over to the "small page" allocation +* below. +*/ + fl->large_alloc_failed++; +break ; + } +poison_buf (page,PAGE_SIZE <<FL_PG_ORDER ); + + dma_addr =dma_map_page (adapter->pdev_dev, page,0 , +PAGE_SIZE <<FL_PG_ORDER , +PCI_DMA_FROMDEVICE ); +if (unlikely (dma_mapping_error (adapter->pdev_dev, dma_addr))) { +/* +* We've run out of DMA mapping space. Free up the +* buffer and return with what we've managed to put +* into the free list. We don't want to fail over to +* the small page allocation below in this case +* because DMA mapping resources are typically +* critical resources once they become scarse. +*/ +__free_pages (page,FL_PG_ORDER ); + goto out; + } + dma_addr |=RX_LARGE_BUF ; + *d++ =cpu_to_be64 (dma_addr); + +set_rx_sw_desc (sdesc, page, dma_addr); + sdesc++; + + fl->avail++; +if (++fl->pidx == fl->size) { + fl->pidx =0 ; + sdesc = fl->sdesc; + d = fl->desc; + } + n--; + } + +alloc_small_pages: +while (n--) { + page =__netdev_alloc_page (adapter->port[0 ], + gfp | __GFP_NOWARN); +if (unlikely (!page)) { + fl->alloc_failed++; +break ; + } +poison_buf (page,PAGE_SIZE ); + + dma_addr =dma_map_page (adapter->pdev_dev, page,0 ,PAGE_SIZE , +PCI_DMA_FROMDEVICE ); +if (unlikely (dma_mapping_error (adapter->pdev_dev, dma_addr))) { +netdev_free_page (adapter->port[0 ], page); +break ; + } + *d++ =cpu_to_be64 (dma_addr); + +set_rx_sw_desc (sdesc, page, dma_addr); + sdesc++; + + fl->avail++; +if (++fl->pidx == fl->size) { + fl->pidx =0 ; + sdesc = fl->sdesc; + d = fl->desc; + } + } + +out: +/* +* Update our accounting state to incorporate the new Free List +* buffers, tell the hardware about them and return the number of +* bufers which we were able to allocate. +*/ + cred = fl->avail - cred; + fl->pend_cred += cred; +ring_fl_db (adapter, fl); + +if (unlikely (fl_starving (fl))) { +smp_wmb (); +set_bit (fl->cntxt_id, adapter->sge.starving_fl); + } + +return cred; +} + +/* +* Refill a Free List to its capacity or the Maximum Refill Increment, +* whichever is smaller ... +*/ +static inline void __refill_fl (struct adapter *adapter,struct sge_fl *fl) +{ +refill_fl (adapter, fl, +min ((unsigned int )MAX_RX_REFILL ,fl_cap (fl) - fl->avail), +GFP_ATOMIC ); +} + +/** +* alloc_ring - allocate resources for an SGE descriptor ring +* @dev: the PCI device's core device +* @nelem: the number of descriptors +* @hwsize: the size of each hardware descriptor +* @swsize: the size of each software descriptor +* @busaddrp: the physical PCI bus address of the allocated ring +* @swringp: return address pointer for software ring +* @stat_size: extra space in hardware ring for status information +* +* Allocates resources for an SGE descriptor ring, such as TX queues, +* free buffer lists, response queues, etc. Each SGE ring requires +* space for its hardware descriptors plus, optionally, space for software +* state associated with each hardware entry (the metadata). The function +* returns three values: the virtual address for the hardware ring (the +* return value of the function), the PCI bus address of the hardware +* ring (in *busaddrp), and the address of the software ring (in swringp). +* Both the hardware and software rings are returned zeroed out. +*/ +static void *alloc_ring (struct device *dev,size_t nelem,size_t hwsize, +size_t swsize,dma_addr_t *busaddrp,void *swringp, +size_t stat_size) +{ +/* +* Allocate the hardware ring and PCI DMA bus address space for said. +*/ +size_t hwlen = nelem * hwsize + stat_size; +void *hwring =dma_alloc_coherent (dev, hwlen, busaddrp,GFP_KERNEL ); + +if (!hwring) +return NULL ; + +/* +* If the caller wants a software ring, allocate it and return a +* pointer to it in *swringp. +*/ +BUG_ON ((swsize !=0 ) != (swringp !=NULL )); +if (swsize) { +void *swring =kcalloc (nelem, swsize,GFP_KERNEL ); + +if (!swring) { +dma_free_coherent (dev, hwlen, hwring, *busaddrp); +return NULL ; + } + *(void **)swringp = swring; + } + +/* +* Zero out the hardware ring and return its address as our function +* value. +*/ +memset (hwring,0 , hwlen); +return hwring; +} + +/** +* sgl_len - calculates the size of an SGL of the given capacity +* @n: the number of SGL entries +* +* Calculates the number of flits (8-byte units) needed for a Direct +* Scatter/Gather List that can hold the given number of entries. +*/ +static inline unsigned int sgl_len (unsigned int n) +{ +/* +* A Direct Scatter Gather List uses 32-bit lengths and 64-bit PCI DMA +* addresses. The DSGL Work Request starts off with a 32-bit DSGL +* ULPTX header, then Length0, then Address0, then, for 1 <= i <= N, +* repeated sequences of { Length[i], Length[i+1], Address[i], +* Address[i+1] } (this ensures that all addresses are on 64-bit +* boundaries). If N is even, then Length[N+1] should be set to 0 and +* Address[N+1] is omitted. +* +* The following calculation incorporates all of the above. It's +* somewhat hard to follow but, briefly: the "+2" accounts for the +* first two flits which include the DSGL header, Length0 and +* Address0; the "(3*(n-1))/2" covers the main body of list entries (3 +* flits for every pair of the remaining N) +1 if (n-1) is odd; and +* finally the "+((n-1)&1)" adds the one remaining flit needed if +* (n-1) is odd ... +*/ + n--; +return (3 * n) /2 + (n &1 ) +2 ; +} + +/** +* flits_to_desc - returns the num of TX descriptors for the given flits +* @flits: the number of flits +* +* Returns the number of TX descriptors needed for the supplied number +* of flits. +*/ +static inline unsigned int flits_to_desc (unsigned int flits) +{ +BUG_ON (flits >SGE_MAX_WR_LEN /sizeof (__be64)); +return DIV_ROUND_UP (flits,TXD_PER_EQ_UNIT ); +} + +/** +* is_eth_imm - can an Ethernet packet be sent as immediate data? +* @skb: the packet +* +* Returns whether an Ethernet packet is small enough to fit completely as +* immediate data. +*/ +static inline int is_eth_imm (const struct sk_buff *skb) +{ +/* +* The VF Driver uses the FW_ETH_TX_PKT_VM_WR firmware Work Request +* which does not accommodate immediate data. We could dike out all +* of the support code for immediate data but that would tie our hands +* too much if we ever want to enhace the firmware. It would also +* create more differences between the PF and VF Drivers. +*/ +return false; +} + +/** +* calc_tx_flits - calculate the number of flits for a packet TX WR +* @skb: the packet +* +* Returns the number of flits needed for a TX Work Request for the +* given Ethernet packet, including the needed WR and CPL headers. +*/ +static inline unsigned int calc_tx_flits (const struct sk_buff *skb) +{ +unsigned int flits; + +/* +* If the skb is small enough, we can pump it out as a work request +* with only immediate data. In that case we just have to have the +* TX Packet header plus the skb data in the Work Request. +*/ +if (is_eth_imm (skb)) +return DIV_ROUND_UP (skb->len +sizeof (struct cpl_tx_pkt ), +sizeof (__be64)); + +/* +* Otherwise, we're going to have to construct a Scatter gather list +* of the skb body and fragments. We also include the flits necessary +* for the TX Packet Work Request and CPL. We always have a firmware +* Write Header (incorporated as part of the cpl_tx_pkt_lso and +* cpl_tx_pkt structures), followed by either a TX Packet Write CPL +* message or, if we're doing a Large Send Offload, an LSO CPL message +* with an embeded TX Packet Write CPL message. +*/ + flits =sgl_len (skb_shinfo (skb)->nr_frags +1 ); +if (skb_shinfo (skb)->gso_size) + flits += (sizeof (struct fw_eth_tx_pkt_vm_wr ) + +sizeof (struct cpl_tx_pkt_lso_core ) + +sizeof (struct cpl_tx_pkt_core )) /sizeof (__be64); +else + flits += (sizeof (struct fw_eth_tx_pkt_vm_wr ) + +sizeof (struct cpl_tx_pkt_core )) /sizeof (__be64); +return flits; +} + +/** +* write_sgl - populate a Scatter/Gather List for a packet +* @skb: the packet +* @tq: the TX queue we are writing into +* @sgl: starting location for writing the SGL +* @end: points right after the end of the SGL +* @start: start offset into skb main-body data to include in the SGL +* @addr: the list of DMA bus addresses for the SGL elements +* +* Generates a Scatter/Gather List for the buffers that make up a packet. +* The caller must provide adequate space for the SGL that will be written. +* The SGL includes all of the packet's page fragments and the data in its +* main body except for the first @start bytes. @pos must be 16-byte +* aligned and within a TX descriptor with available space. @end points +* write after the end of the SGL but does not account for any potential +* wrap around, i.e., @end > @tq->stat. +*/ +static void write_sgl (const struct sk_buff *skb,struct sge_txq *tq, +struct ulptx_sgl *sgl,u64 *end,unsigned int start, +const dma_addr_t *addr) +{ +unsigned int i, len; +struct ulptx_sge_pair *to; +const struct skb_shared_info *si =skb_shinfo (skb); +unsigned int nfrags = si->nr_frags; +struct ulptx_sge_pair buf[MAX_SKB_FRAGS /2 +1 ]; + + len =skb_headlen (skb) - start; +if (likely (len)) { + sgl->len0 =htonl (len); + sgl->addr0 =cpu_to_be64 (addr[0 ] + start); + nfrags++; + }else { + sgl->len0 =htonl (si->frags[0 ].size); + sgl->addr0 =cpu_to_be64 (addr[1 ]); + } + + sgl->cmd_nsge =htonl (ULPTX_CMD (ULP_TX_SC_DSGL ) | +ULPTX_NSGE (nfrags)); +if (likely (--nfrags ==0 )) +return ; +/* +* Most of the complexity below deals with the possibility we hit the +* end of the queue in the middle of writing the SGL. For this case +* only we create the SGL in a temporary buffer and then copy it. +*/ + to = (u8 *)end > (u8 *)tq->stat ? buf : sgl->sge; + +for (i = (nfrags != si->nr_frags); nfrags >=2 ; nfrags -=2 , to++) { + to->len[0 ] =cpu_to_be32 (si->frags[i].size); + to->len[1 ] =cpu_to_be32 (si->frags[++i].size); + to->addr[0 ] =cpu_to_be64 (addr[i]); + to->addr[1 ] =cpu_to_be64 (addr[++i]); + } +if (nfrags) { + to->len[0 ] =cpu_to_be32 (si->frags[i].size); + to->len[1 ] =cpu_to_be32 (0 ); + to->addr[0 ] =cpu_to_be64 (addr[i +1 ]); + } +if (unlikely ((u8 *)end > (u8 *)tq->stat)) { +unsigned int part0 = (u8 *)tq->stat - (u8 *)sgl->sge, part1; + +if (likely (part0)) +memcpy (sgl->sge, buf, part0); + part1 = (u8 *)end - (u8 *)tq->stat; +memcpy (tq->desc, (u8 *)buf + part0, part1); + end = (void *)tq->desc + part1; + } +if ((uintptr_t )end &8 )/* 0-pad to multiple of 16 */ + *(u64 *)end =0 ; +} + +/** +* check_ring_tx_db - check and potentially ring a TX queue's doorbell +* @adapter: the adapter +* @tq: the TX queue +* @n: number of new descriptors to give to HW +* +* Ring the doorbel for a TX queue. +*/ +static inline void ring_tx_db (struct adapter *adapter,struct sge_txq *tq, +int n) +{ +/* +* Warn if we write doorbells with the wrong priority and write +* descriptors before telling HW. +*/ +WARN_ON ((QID (tq->cntxt_id) |PIDX (n)) &DBPRIO ); +wmb (); +t4_write_reg (adapter,T4VF_SGE_BASE_ADDR +SGE_VF_KDOORBELL , +QID (tq->cntxt_id) |PIDX (n)); +} + +/** +* inline_tx_skb - inline a packet's data into TX descriptors +* @skb: the packet +* @tq: the TX queue where the packet will be inlined +* @pos: starting position in the TX queue to inline the packet +* +* Inline a packet's contents directly into TX descriptors, starting at +* the given position within the TX DMA ring. +* Most of the complexity of this operation is dealing with wrap arounds +* in the middle of the packet we want to inline. +*/ +static void inline_tx_skb (const struct sk_buff *skb,const struct sge_txq *tq, +void *pos) +{ +u64 *p; +int left = (void *)tq->stat - pos; + +if (likely (skb->len <= left)) { +if (likely (!skb->data_len)) +skb_copy_from_linear_data (skb, pos, skb->len); +else +skb_copy_bits (skb,0 , pos, skb->len); + pos += skb->len; + }else { +skb_copy_bits (skb,0 , pos, left); +skb_copy_bits (skb, left, tq->desc, skb->len - left); + pos = (void *)tq->desc + (skb->len - left); + } + +/* 0-pad to multiple of 16 */ + p =PTR_ALIGN (pos,8 ); +if ((uintptr_t )p &8 ) + *p =0 ; +} + +/* +* Figure out what HW csum a packet wants and return the appropriate control +* bits. +*/ +static u64 hwcsum (const struct sk_buff *skb) +{ +int csum_type; +const struct iphdr *iph =ip_hdr (skb); + +if (iph->version ==4 ) { +if (iph->protocol ==IPPROTO_TCP ) + csum_type =TX_CSUM_TCPIP ; +else if (iph->protocol ==IPPROTO_UDP ) + csum_type =TX_CSUM_UDPIP ; +else { +nocsum: +/* +* unknown protocol, disable HW csum +* and hope a bad packet is detected +*/ +return TXPKT_L4CSUM_DIS ; + } + }else { +/* +* this doesn't work with extension headers +*/ +const struct ipv6hdr *ip6h = (const struct ipv6hdr *)iph; + +if (ip6h->nexthdr ==IPPROTO_TCP ) + csum_type =TX_CSUM_TCPIP6 ; +else if (ip6h->nexthdr ==IPPROTO_UDP ) + csum_type =TX_CSUM_UDPIP6 ; +else + goto nocsum; + } + +if (likely (csum_type >=TX_CSUM_TCPIP )) +return TXPKT_CSUM_TYPE (csum_type) | +TXPKT_IPHDR_LEN (skb_network_header_len (skb)) | +TXPKT_ETHHDR_LEN (skb_network_offset (skb) -ETH_HLEN ); +else { +int start =skb_transport_offset (skb); + +return TXPKT_CSUM_TYPE (csum_type) | +TXPKT_CSUM_START (start) | +TXPKT_CSUM_LOC (start + skb->csum_offset); + } +} + +/* +* Stop an Ethernet TX queue and record that state change. +*/ +static void txq_stop (struct sge_eth_txq *txq) +{ +netif_tx_stop_queue (txq->txq); + txq->q.stops++; +} + +/* +* Advance our software state for a TX queue by adding n in use descriptors. +*/ +static inline void txq_advance (struct sge_txq *tq,unsigned int n) +{ + tq->in_use += n; + tq->pidx += n; +if (tq->pidx >= tq->size) + tq->pidx -= tq->size; +} + +/** +* t4vf_eth_xmit - add a packet to an Ethernet TX queue +* @skb: the packet +* @dev: the egress net device +* +* Add a packet to an SGE Ethernet TX queue. Runs with softirqs disabled. +*/ +int t4vf_eth_xmit (struct sk_buff *skb,struct net_device *dev) +{ +u64 cntrl, *end; +int qidx, credits; +unsigned int flits, ndesc; +struct adapter *adapter; +struct sge_eth_txq *txq; +const struct port_info *pi; +struct fw_eth_tx_pkt_vm_wr *wr; +struct cpl_tx_pkt_core *cpl; +const struct skb_shared_info *ssi; +dma_addr_t addr[MAX_SKB_FRAGS +1 ]; +const size_t fw_hdr_copy_len = (sizeof (wr->ethmacdst) + +sizeof (wr->ethmacsrc) + +sizeof (wr->ethtype) + +sizeof (wr->vlantci)); + +/* +* The chip minimum packet length is 10 octets but the firmware +* command that we are using requires that we copy the Ethernet header +* (including the VLAN tag) into the header so we reject anything +* smaller than that ... +*/ +if (unlikely (skb->len < fw_hdr_copy_len)) + goto out_free; + +/* +* Figure out which TX Queue we're going to use. +*/ + pi =netdev_priv (dev); + adapter = pi->adapter; + qidx =skb_get_queue_mapping (skb); +BUG_ON (qidx >= pi->nqsets); + txq = &adapter->sge.ethtxq[pi->first_qset + qidx]; + +/* +* Take this opportunity to reclaim any TX Descriptors whose DMA +* transfers have completed. +*/ +reclaim_completed_tx (adapter, &txq->q, true); + +/* +* Calculate the number of flits and TX Descriptors we're going to +* need along with how many TX Descriptors will be left over after +* we inject our Work Request. +*/ + flits =calc_tx_flits (skb); + ndesc =flits_to_desc (flits); + credits =txq_avail (&txq->q) - ndesc; + +if (unlikely (credits <0 )) { +/* +* Not enough room for this packet's Work Request. Stop the +* TX Queue and return a "busy" condition. The queue will get +* started later on when the firmware informs us that space +* has opened up. +*/ +txq_stop (txq); +dev_err (adapter->pdev_dev, +"%s: TX ring %u full while queue awake!\n" , + dev->name, qidx); +return NETDEV_TX_BUSY ; + } + +if (!is_eth_imm (skb) && +unlikely (map_skb (adapter->pdev_dev, skb, addr) <0 )) { +/* +* We need to map the skb into PCI DMA space (because it can't +* be in-lined directly into the Work Request) and the mapping +* operation failed. Record the error and drop the packet. +*/ + txq->mapping_err++; + goto out_free; + } + +if (unlikely (credits <ETHTXQ_STOP_THRES )) { +/* +* After we're done injecting the Work Request for this +* packet, we'll be below our "stop threshhold" so stop the TX +* Queue now. The queue will get started later on when the +* firmware informs us that space has opened up. +*/ +txq_stop (txq); + } + +/* +* Start filling in our Work Request. Note that we do _not_ handle +* the WR Header wrapping around the TX Descriptor Ring. If our +* maximum header size ever exceeds one TX Descriptor, we'll need to +* do something else here. +*/ +BUG_ON (DIV_ROUND_UP (ETHTXQ_MAX_HDR ,TXD_PER_EQ_UNIT ) >1 ); + wr = (void *)&txq->q.desc[txq->q.pidx]; + wr->equiq_to_len16 =cpu_to_be32 (FW_WR_LEN16 (DIV_ROUND_UP (flits,2 ))); + wr->r3[0 ] =cpu_to_be64 (0 ); + wr->r3[1 ] =cpu_to_be64 (0 ); +skb_copy_from_linear_data (skb, (void *)wr->ethmacdst, fw_hdr_copy_len); + end = (u64 *)wr + flits; + +/* +* If this is a Large Send Offload packet we'll put in an LSO CPL +* message with an encapsulated TX Packet CPL message. Otherwise we +* just use a TX Packet CPL message. +*/ + ssi =skb_shinfo (skb); +if (ssi->gso_size) { +struct cpl_tx_pkt_lso_core *lso = (void *)(wr +1 ); +bool v6 = (ssi->gso_type &SKB_GSO_TCPV6 ) !=0 ; +int l3hdr_len =skb_network_header_len (skb); +int eth_xtra_len =skb_network_offset (skb) -ETH_HLEN ; + + wr->op_immdlen = +cpu_to_be32 (FW_WR_OP (FW_ETH_TX_PKT_VM_WR ) | +FW_WR_IMMDLEN (sizeof (*lso) + +sizeof (*cpl))); +/* +* Fill in the LSO CPL message. +*/ + lso->lso_ctrl = +cpu_to_be32 (LSO_OPCODE (CPL_TX_PKT_LSO ) | +LSO_FIRST_SLICE | +LSO_LAST_SLICE | +LSO_IPV6 (v6) | +LSO_ETHHDR_LEN (eth_xtra_len/4 ) | +LSO_IPHDR_LEN (l3hdr_len/4 ) | +LSO_TCPHDR_LEN (tcp_hdr (skb)->doff)); + lso->ipid_ofst =cpu_to_be16 (0 ); + lso->mss =cpu_to_be16 (ssi->gso_size); + lso->seqno_offset =cpu_to_be32 (0 ); + lso->len =cpu_to_be32 (skb->len); + +/* +* Set up TX Packet CPL pointer, control word and perform +* accounting. +*/ + cpl = (void *)(lso +1 ); + cntrl = (TXPKT_CSUM_TYPE (v6 ?TX_CSUM_TCPIP6 :TX_CSUM_TCPIP ) | +TXPKT_IPHDR_LEN (l3hdr_len) | +TXPKT_ETHHDR_LEN (eth_xtra_len)); + txq->tso++; + txq->tx_cso += ssi->gso_segs; + }else { +int len; + + len =is_eth_imm (skb) ? skb->len +sizeof (*cpl) :sizeof (*cpl); + wr->op_immdlen = +cpu_to_be32 (FW_WR_OP (FW_ETH_TX_PKT_VM_WR ) | +FW_WR_IMMDLEN (len)); + +/* +* Set up TX Packet CPL pointer, control word and perform +* accounting. +*/ + cpl = (void *)(wr +1 ); +if (skb->ip_summed ==CHECKSUM_PARTIAL ) { + cntrl =hwcsum (skb) |TXPKT_IPCSUM_DIS ; + txq->tx_cso++; + }else + cntrl =TXPKT_L4CSUM_DIS |TXPKT_IPCSUM_DIS ; + } + +/* +* If there's a VLAN tag present, add that to the list of things to +* do in this Work Request. +*/ +if (vlan_tx_tag_present (skb)) { + txq->vlan_ins++; + cntrl |=TXPKT_VLAN_VLD |TXPKT_VLAN (vlan_tx_tag_get (skb)); + } + +/* +* Fill in the TX Packet CPL message header. +*/ + cpl->ctrl0 =cpu_to_be32 (TXPKT_OPCODE (CPL_TX_PKT_XT ) | +TXPKT_INTF (pi->port_id) | +TXPKT_PF (0 )); + cpl->pack =cpu_to_be16 (0 ); + cpl->len =cpu_to_be16 (skb->len); + cpl->ctrl1 =cpu_to_be64 (cntrl); + +#ifdef T4_TRACE +T4_TRACE5 (adapter->tb[txq->q.cntxt_id &7 ], +"eth_xmit: ndesc %u, credits %u, pidx %u, len %u, frags %u" , + ndesc, credits, txq->q.pidx, skb->len, ssi->nr_frags); +#endif + +/* +* Fill in the body of the TX Packet CPL message with either in-lined +* data or a Scatter/Gather List. +*/ +if (is_eth_imm (skb)) { +/* +* In-line the packet's data and free the skb since we don't +* need it any longer. +*/ +inline_tx_skb (skb, &txq->q, cpl +1 ); +dev_kfree_skb (skb); + }else { +/* +* Write the skb's Scatter/Gather list into the TX Packet CPL +* message and retain a pointer to the skb so we can free it +* later when its DMA completes. (We store the skb pointer +* in the Software Descriptor corresponding to the last TX +* Descriptor used by the Work Request.) +* +* The retained skb will be freed when the corresponding TX +* Descriptors are reclaimed after their DMAs complete. +* However, this could take quite a while since, in general, +* the hardware is set up to be lazy about sending DMA +* completion notifications to us and we mostly perform TX +* reclaims in the transmit routine. +* +* This is good for performamce but means that we rely on new +* TX packets arriving to run the destructors of completed +* packets, which open up space in their sockets' send queues. +* Sometimes we do not get such new packets causing TX to +* stall. A single UDP transmitter is a good example of this +* situation. We have a clean up timer that periodically +* reclaims completed packets but it doesn't run often enough +* (nor do we want it to) to prevent lengthy stalls. A +* solution to this problem is to run the destructor early, +* after the packet is queued but before it's DMAd. A con is +* that we lie to socket memory accounting, but the amount of +* extra memory is reasonable (limited by the number of TX +* descriptors), the packets do actually get freed quickly by +* new packets almost always, and for protocols like TCP that +* wait for acks to really free up the data the extra memory +* is even less. On the positive side we run the destructors +* on the sending CPU rather than on a potentially different +* completing CPU, usually a good thing. We also run them +* without holding our TX queue lock, unlike what +* reclaim_completed_tx() would otherwise do. +* +* XXX Actually the above is somewhat incorrect since we don't +* XXX yet have a periodic timer which reclaims TX Descriptors. +* XXX What's our plan for this? +* XXX +* XXX Also, we don't currently have a TX Queue lock but +* XXX that may be the result of not having any current +* XXX asynchronous path for reclaiming completed TX +* XXX Descriptors ... +* +* Run the destructor before telling the DMA engine about the +* packet to make sure it doesn't complete and get freed +* prematurely. +*/ +struct ulptx_sgl *sgl = (struct ulptx_sgl *)(cpl +1 ); +struct sge_txq *tq = &txq->q; +int last_desc; + +/* +* If the Work Request header was an exact multiple of our TX +* Descriptor length, then it's possible that the starting SGL +* pointer lines up exactly with the end of our TX Descriptor +* ring. If that's the case, wrap around to the beginning +* here ... +*/ +if (unlikely ((void *)sgl == (void *)tq->stat)) { + sgl = (void *)tq->desc; + end = (void *)((void *)tq->desc + + ((void *)end - (void *)tq->stat)); + } + +write_sgl (skb, tq, sgl, end,0 , addr); +skb_orphan (skb); + + last_desc = tq->pidx + ndesc -1 ; +if (last_desc >= tq->size) + last_desc -= tq->size; + tq->sdesc[last_desc].skb = skb; + tq->sdesc[last_desc].sgl = sgl; + } + +/* +* Advance our internal TX Queue state, tell the hardware about +* the new TX descriptors and return success. +*/ +txq_advance (&txq->q, ndesc); + dev->trans_start = jiffies; +ring_tx_db (adapter, &txq->q, ndesc); +return NETDEV_TX_OK ; + +out_free: +/* +* An error of some sort happened. Free the TX skb and tell the +* OS that we've "dealt" with the packet ... +*/ +dev_kfree_skb (skb); +return NETDEV_TX_OK ; +} + +/** +* t4vf_pktgl_free - free a packet gather list +* @gl: the gather list +* +* Releases the pages of a packet gather list. We do not own the last +* page on the list and do not free it. +*/ +void t4vf_pktgl_free (const struct pkt_gl *gl) +{ +int frag; + + frag = gl->nfrags -1 ; +while (frag--) +put_page (gl->frags[frag].page); +} + +/** +* copy_frags - copy fragments from gather list into skb_shared_info +* @si: destination skb shared info structure +* @gl: source internal packet gather list +* @offset: packet start offset in first page +* +* Copy an internal packet gather list into a Linux skb_shared_info +* structure. +*/ +static inline void copy_frags (struct skb_shared_info *si, +const struct pkt_gl *gl, +unsigned int offset) +{ +unsigned int n; + +/* usually there's just one frag */ + si->frags[0 ].page = gl->frags[0 ].page; + si->frags[0 ].page_offset = gl->frags[0 ].page_offset + offset; + si->frags[0 ].size = gl->frags[0 ].size - offset; + si->nr_frags = gl->nfrags; + + n = gl->nfrags -1 ; +if (n) +memcpy (&si->frags[1 ], &gl->frags[1 ], n *sizeof (skb_frag_t)); + +/* get a reference to the last page, we don't own it */ +get_page (gl->frags[n].page); +} + +/** +* do_gro - perform Generic Receive Offload ingress packet processing +* @rxq: ingress RX Ethernet Queue +* @gl: gather list for ingress packet +* @pkt: CPL header for last packet fragment +* +* Perform Generic Receive Offload (GRO) ingress packet processing. +* We use the standard Linux GRO interfaces for this. +*/ +static void do_gro (struct sge_eth_rxq *rxq,const struct pkt_gl *gl, +const struct cpl_rx_pkt *pkt) +{ +int ret; +struct sk_buff *skb; + + skb =napi_get_frags (&rxq->rspq.napi); +if (unlikely (!skb)) { +t4vf_pktgl_free (gl); + rxq->stats.rx_drops++; +return ; + } + +copy_frags (skb_shinfo (skb), gl,PKTSHIFT ); + skb->len = gl->tot_len -PKTSHIFT ; + skb->data_len = skb->len; + skb->truesize += skb->data_len; + skb->ip_summed =CHECKSUM_UNNECESSARY ; +skb_record_rx_queue (skb, rxq->rspq.idx); + +if (unlikely (pkt->vlan_ex)) { +struct port_info *pi =netdev_priv (rxq->rspq.netdev); +struct vlan_group *grp = pi->vlan_grp; + + rxq->stats.vlan_ex++; +if (likely (grp)) { + ret =vlan_gro_frags (&rxq->rspq.napi, grp, +be16_to_cpu (pkt->vlan)); + goto stats; + } + } + ret =napi_gro_frags (&rxq->rspq.napi); + +stats: +if (ret ==GRO_HELD ) + rxq->stats.lro_pkts++; +else if (ret ==GRO_MERGED || ret ==GRO_MERGED_FREE ) + rxq->stats.lro_merged++; + rxq->stats.pkts++; + rxq->stats.rx_cso++; +} + +/** +* t4vf_ethrx_handler - process an ingress ethernet packet +* @rspq: the response queue that received the packet +* @rsp: the response queue descriptor holding the RX_PKT message +* @gl: the gather list of packet fragments +* +* Process an ingress ethernet packet and deliver it to the stack. +*/ +int t4vf_ethrx_handler (struct sge_rspq *rspq,const __be64 *rsp, +const struct pkt_gl *gl) +{ +struct sk_buff *skb; +struct port_info *pi; +struct skb_shared_info *ssi; +const struct cpl_rx_pkt *pkt = (void *)&rsp[1 ]; +bool csum_ok = pkt->csum_calc && !pkt->err_vec; +unsigned int len =be16_to_cpu (pkt->len); + struct sge_eth_rxq *rxq = container_of(rspq, struct sge_eth_rxq, rspq); + +/* +* If this is a good TCP packet and we have Generic Receive Offload +* enabled, handle the packet in the GRO path. +*/ +if ((pkt->l2info &cpu_to_be32 (RXF_TCP )) && + (rspq->netdev->features &NETIF_F_GRO ) && csum_ok && + !pkt->ip_frag) { +do_gro (rxq, gl, pkt); +return 0 ; + } + +/* +* If the ingress packet is small enough, allocate an skb large enough +* for all of the data and copy it inline. Otherwise, allocate an skb +* with enough room to pull in the header and reference the rest of +* the data via the skb fragment list. +*/ +if (len <=RX_COPY_THRES ) { +/* small packets have only one fragment */ + skb =alloc_skb (gl->frags[0 ].size,GFP_ATOMIC ); +if (!skb) + goto nomem; +__skb_put (skb, gl->frags[0 ].size); +skb_copy_to_linear_data (skb, gl->va, gl->frags[0 ].size); + }else { + skb =alloc_skb (RX_PKT_PULL_LEN ,GFP_ATOMIC ); +if (!skb) + goto nomem; +__skb_put (skb,RX_PKT_PULL_LEN ); +skb_copy_to_linear_data (skb, gl->va,RX_PKT_PULL_LEN ); + + ssi =skb_shinfo (skb); + ssi->frags[0 ].page = gl->frags[0 ].page; + ssi->frags[0 ].page_offset = (gl->frags[0 ].page_offset + +RX_PKT_PULL_LEN ); + ssi->frags[0 ].size = gl->frags[0 ].size -RX_PKT_PULL_LEN ; +if (gl->nfrags >1 ) +memcpy (&ssi->frags[1 ], &gl->frags[1 ], + (gl->nfrags-1 ) *sizeof (skb_frag_t)); + ssi->nr_frags = gl->nfrags; + skb->len = len +PKTSHIFT ; + skb->data_len = skb->len -RX_PKT_PULL_LEN ; + skb->truesize += skb->data_len; + +/* Get a reference for the last page, we don't own it */ +get_page (gl->frags[gl->nfrags -1 ].page); + } + +__skb_pull (skb,PKTSHIFT ); + skb->protocol =eth_type_trans (skb, rspq->netdev); +skb_record_rx_queue (skb, rspq->idx); + skb->dev->last_rx = jiffies;/* XXX removed 2.6.29 */ + pi =netdev_priv (skb->dev); + rxq->stats.pkts++; + +if (csum_ok && (pi->rx_offload &RX_CSO ) && !pkt->err_vec && + (be32_to_cpu (pkt->l2info) & (RXF_UDP |RXF_TCP ))) { +if (!pkt->ip_frag) + skb->ip_summed =CHECKSUM_UNNECESSARY ; +else { +__sum16 c = (__force __sum16 )pkt->csum; + skb->csum =csum_unfold (c); + skb->ip_summed =CHECKSUM_COMPLETE ; + } + rxq->stats.rx_cso++; + }else + skb->ip_summed =CHECKSUM_NONE ; + +if (unlikely (pkt->vlan_ex)) { +struct vlan_group *grp = pi->vlan_grp; + + rxq->stats.vlan_ex++; +if (likely (grp)) +vlan_hwaccel_receive_skb (skb, grp, +be16_to_cpu (pkt->vlan)); +else +dev_kfree_skb_any (skb); + }else +netif_receive_skb (skb); + +return 0 ; + +nomem: +t4vf_pktgl_free (gl); + rxq->stats.rx_drops++; +return 0 ; +} + +/** +* is_new_response - check if a response is newly written +* @rc: the response control descriptor +* @rspq: the response queue +* +* Returns true if a response descriptor contains a yet unprocessed +* response. +*/ +static inline bool is_new_response (const struct rsp_ctrl *rc, +const struct sge_rspq *rspq) +{ +return RSPD_GEN (rc->type_gen) == rspq->gen; +} + +/** +* restore_rx_bufs - put back a packet's RX buffers +* @gl: the packet gather list +* @fl: the SGE Free List +* @nfrags: how many fragments in @si +* +* Called when we find out that the current packet, @si, can't be +* processed right away for some reason. This is a very rare event and +* there's no effort to make this suspension/resumption process +* particularly efficient. +* +* We implement the suspension by putting all of the RX buffers associated +* with the current packet back on the original Free List. The buffers +* have already been unmapped and are left unmapped, we mark them as +* unmapped in order to prevent further unmapping attempts. (Effectively +* this function undoes the series of @unmap_rx_buf calls which were done +* to create the current packet's gather list.) This leaves us ready to +* restart processing of the packet the next time we start processing the +* RX Queue ... +*/ +static void restore_rx_bufs (const struct pkt_gl *gl,struct sge_fl *fl, +int frags) +{ +struct rx_sw_desc *sdesc; + +while (frags--) { +if (fl->cidx ==0 ) + fl->cidx = fl->size -1 ; +else + fl->cidx--; + sdesc = &fl->sdesc[fl->cidx]; + sdesc->page = gl->frags[frags].page; + sdesc->dma_addr |=RX_UNMAPPED_BUF ; + fl->avail++; + } +} + +/** +* rspq_next - advance to the next entry in a response queue +* @rspq: the queue +* +* Updates the state of a response queue to advance it to the next entry. +*/ +static inline void rspq_next (struct sge_rspq *rspq) +{ + rspq->cur_desc = (void *)rspq->cur_desc + rspq->iqe_len; +if (unlikely (++rspq->cidx == rspq->size)) { + rspq->cidx =0 ; + rspq->gen ^=1 ; + rspq->cur_desc = rspq->desc; + } +} + +/** +* process_responses - process responses from an SGE response queue +* @rspq: the ingress response queue to process +* @budget: how many responses can be processed in this round +* +* Process responses from a Scatter Gather Engine response queue up to +* the supplied budget. Responses include received packets as well as +* control messages from firmware or hardware. +* +* Additionally choose the interrupt holdoff time for the next interrupt +* on this queue. If the system is under memory shortage use a fairly +* long delay to help recovery. +*/ +int process_responses (struct sge_rspq *rspq,int budget) +{ + struct sge_eth_rxq *rxq = container_of(rspq, struct sge_eth_rxq, rspq); +int budget_left = budget; + +while (likely (budget_left)) { +int ret, rsp_type; +const struct rsp_ctrl *rc; + + rc = (void *)rspq->cur_desc + (rspq->iqe_len -sizeof (*rc)); +if (!is_new_response (rc, rspq)) +break ; + +/* +* Figure out what kind of response we've received from the +* SGE. +*/ +rmb (); + rsp_type =RSPD_TYPE (rc->type_gen); +if (likely (rsp_type ==RSP_TYPE_FLBUF )) { +skb_frag_t *fp; +struct pkt_gl gl; +const struct rx_sw_desc *sdesc; +u32 bufsz, frag; +u32 len =be32_to_cpu (rc->pldbuflen_qid); + +/* +* If we get a "new buffer" message from the SGE we +* need to move on to the next Free List buffer. +*/ +if (len &RSPD_NEWBUF ) { +/* +* We get one "new buffer" message when we +* first start up a queue so we need to ignore +* it when our offset into the buffer is 0. +*/ +if (likely (rspq->offset >0 )) { +free_rx_bufs (rspq->adapter, &rxq->fl, +1 ); + rspq->offset =0 ; + } + len =RSPD_LEN (len); + } + +/* +* Gather packet fragments. +*/ +for (frag =0 , fp = gl.frags;/**/ ; frag++, fp++) { +BUG_ON (frag >=MAX_SKB_FRAGS ); +BUG_ON (rxq->fl.avail ==0 ); + sdesc = &rxq->fl.sdesc[rxq->fl.cidx]; + bufsz =get_buf_size (sdesc); + fp->page = sdesc->page; + fp->page_offset = rspq->offset; + fp->size =min (bufsz, len); + len -= fp->size; +if (!len) +break ; +unmap_rx_buf (rspq->adapter, &rxq->fl); + } + gl.nfrags = frag+1 ; + +/* +* Last buffer remains mapped so explicitly make it +* coherent for CPU access and start preloading first +* cache line ... +*/ +dma_sync_single_for_cpu (rspq->adapter->pdev_dev, +get_buf_addr (sdesc), + fp->size,DMA_FROM_DEVICE ); + gl.va = (page_address (gl.frags[0 ].page) + + gl.frags[0 ].page_offset); +prefetch (gl.va); + +/* +* Hand the new ingress packet to the handler for +* this Response Queue. +*/ + ret = rspq->handler (rspq, rspq->cur_desc, &gl); +if (likely (ret ==0 )) + rspq->offset +=ALIGN (fp->size,FL_ALIGN ); +else +restore_rx_bufs (&gl, &rxq->fl, frag); + }else if (likely (rsp_type ==RSP_TYPE_CPL )) { + ret = rspq->handler (rspq, rspq->cur_desc,NULL ); + }else { +WARN_ON (rsp_type >RSP_TYPE_CPL ); + ret =0 ; + } + +if (unlikely (ret)) { +/* +* Couldn't process descriptor, back off for recovery. +* We use the SGE's last timer which has the longest +* interrupt coalescing value ... +*/ +const int NOMEM_TIMER_IDX =SGE_NTIMERS -1 ; + rspq->next_intr_params = +QINTR_TIMER_IDX (NOMEM_TIMER_IDX ); +break ; + } + +rspq_next (rspq); + budget_left--; + } + +/* +* If this is a Response Queue with an associated Free List and +* at least two Egress Queue units available in the Free List +* for new buffer pointers, refill the Free List. +*/ +if (rspq->offset >=0 && + rxq->fl.size - rxq->fl.avail >=2 *FL_PER_EQ_UNIT ) +__refill_fl (rspq->adapter, &rxq->fl); +return budget - budget_left; +} + +/** +* napi_rx_handler - the NAPI handler for RX processing +* @napi: the napi instance +* @budget: how many packets we can process in this round +* +* Handler for new data events when using NAPI. This does not need any +* locking or protection from interrupts as data interrupts are off at +* this point and other adapter interrupts do not interfere (the latter +* in not a concern at all with MSI-X as non-data interrupts then have +* a separate handler). +*/ +static int napi_rx_handler (struct napi_struct *napi,int budget) +{ +unsigned int intr_params; + struct sge_rspq *rspq = container_of(napi, struct sge_rspq, napi); +int work_done =process_responses (rspq, budget); + +if (likely (work_done < budget)) { +napi_complete (napi); + intr_params = rspq->next_intr_params; + rspq->next_intr_params = rspq->intr_params; + }else + intr_params =QINTR_TIMER_IDX (SGE_TIMER_UPD_CIDX ); + +t4_write_reg (rspq->adapter, +T4VF_SGE_BASE_ADDR +SGE_VF_GTS , +CIDXINC (work_done) | +INGRESSQID ((u32 )rspq->cntxt_id) | +SEINTARM (intr_params)); +return work_done; +} + +/* +* The MSI-X interrupt handler for an SGE response queue for the NAPI case +* (i.e., response queue serviced by NAPI polling). +*/ +irqreturn_t t4vf_sge_intr_msix (int irq,void *cookie) +{ +struct sge_rspq *rspq = cookie; + +napi_schedule (&rspq->napi); +return IRQ_HANDLED ; +} + +/* +* Process the indirect interrupt entries in the interrupt queue and kick off +* NAPI for each queue that has generated an entry. +*/ +static unsigned int process_intrq (struct adapter *adapter) +{ +struct sge *s = &adapter->sge; +struct sge_rspq *intrq = &s->intrq; +unsigned int work_done; + +spin_lock (&adapter->sge.intrq_lock); +for (work_done =0 ; ; work_done++) { +const struct rsp_ctrl *rc; +unsigned int qid, iq_idx; +struct sge_rspq *rspq; + +/* +* Grab the next response from the interrupt queue and bail +* out if it's not a new response. +*/ + rc = (void *)intrq->cur_desc + (intrq->iqe_len -sizeof (*rc)); +if (!is_new_response (rc, intrq)) +break ; + +/* +* If the response isn't a forwarded interrupt message issue a +* error and go on to the next response message. This should +* never happen ... +*/ +rmb (); +if (unlikely (RSPD_TYPE (rc->type_gen) !=RSP_TYPE_INTR )) { +dev_err (adapter->pdev_dev, +"Unexpected INTRQ response type %d\n" , +RSPD_TYPE (rc->type_gen)); +continue ; + } + +/* +* Extract the Queue ID from the interrupt message and perform +* sanity checking to make sure it really refers to one of our +* Ingress Queues which is active and matches the queue's ID. +* None of these error conditions should ever happen so we may +* want to either make them fatal and/or conditionalized under +* DEBUG. +*/ + qid =RSPD_QID (be32_to_cpu (rc->pldbuflen_qid)); + iq_idx =IQ_IDX (s, qid); +if (unlikely (iq_idx >=MAX_INGQ )) { +dev_err (adapter->pdev_dev, +"Ingress QID %d out of range\n" , qid); +continue ; + } + rspq = s->ingr_map[iq_idx]; +if (unlikely (rspq ==NULL )) { +dev_err (adapter->pdev_dev, +"Ingress QID %d RSPQ=NULL\n" , qid); +continue ; + } +if (unlikely (rspq->abs_id != qid)) { +dev_err (adapter->pdev_dev, +"Ingress QID %d refers to RSPQ %d\n" , + qid, rspq->abs_id); +continue ; + } + +/* +* Schedule NAPI processing on the indicated Response Queue +* and move on to the next entry in the Forwarded Interrupt +* Queue. +*/ +napi_schedule (&rspq->napi); +rspq_next (intrq); + } + +t4_write_reg (adapter,T4VF_SGE_BASE_ADDR +SGE_VF_GTS , +CIDXINC (work_done) | +INGRESSQID (intrq->cntxt_id) | +SEINTARM (intrq->intr_params)); + +spin_unlock (&adapter->sge.intrq_lock); + +return work_done; +} + +/* +* The MSI interrupt handler handles data events from SGE response queues as +* well as error and other async events as they all use the same MSI vector. +*/ +irqreturn_t t4vf_intr_msi (int irq,void *cookie) +{ +struct adapter *adapter = cookie; + +process_intrq (adapter); +return IRQ_HANDLED ; +} + +/** +* t4vf_intr_handler - select the top-level interrupt handler +* @adapter: the adapter +* +* Selects the top-level interrupt handler based on the type of interrupts +* (MSI-X or MSI). +*/ +irq_handler_t t4vf_intr_handler (struct adapter *adapter) +{ +BUG_ON ((adapter->flags & (USING_MSIX |USING_MSI )) ==0 ); +if (adapter->flags &USING_MSIX ) +return t4vf_sge_intr_msix; +else +return t4vf_intr_msi; +} + +/** +* sge_rx_timer_cb - perform periodic maintenance of SGE RX queues +* @data: the adapter +* +* Runs periodically from a timer to perform maintenance of SGE RX queues. +* +* a) Replenishes RX queues that have run out due to memory shortage. +* Normally new RX buffers are added when existing ones are consumed but +* when out of memory a queue can become empty. We schedule NAPI to do +* the actual refill. +*/ +static void sge_rx_timer_cb (unsigned long data) +{ +struct adapter *adapter = (struct adapter *)data; +struct sge *s = &adapter->sge; +unsigned int i; + +/* +* Scan the "Starving Free Lists" flag array looking for any Free +* Lists in need of more free buffers. If we find one and it's not +* being actively polled, then bump its "starving" counter and attempt +* to refill it. If we're successful in adding enough buffers to push +* the Free List over the starving threshold, then we can clear its +* "starving" status. +*/ +for (i =0 ; i <ARRAY_SIZE (s->starving_fl); i++) { +unsigned long m; + +for (m = s->starving_fl[i]; m; m &= m -1 ) { +unsigned int id =__ffs (m) + i *BITS_PER_LONG ; +struct sge_fl *fl = s->egr_map[id]; + +clear_bit (id, s->starving_fl); +smp_mb__after_clear_bit (); + +/* +* Since we are accessing fl without a lock there's a +* small probability of a false positive where we +* schedule napi but the FL is no longer starving. +* No biggie. +*/ +if (fl_starving (fl)) { +struct sge_eth_rxq *rxq; + + rxq = container_of(fl, struct sge_eth_rxq, fl); +if (napi_reschedule (&rxq->rspq.napi)) + fl->starving++; +else +set_bit (id, s->starving_fl); + } + } + } + +/* +* Reschedule the next scan for starving Free Lists ... +*/ +mod_timer (&s->rx_timer, jiffies +RX_QCHECK_PERIOD ); +} + +/** +* sge_tx_timer_cb - perform periodic maintenance of SGE Tx queues +* @data: the adapter +* +* Runs periodically from a timer to perform maintenance of SGE TX queues. +* +* b) Reclaims completed Tx packets for the Ethernet queues. Normally +* packets are cleaned up by new Tx packets, this timer cleans up packets +* when no new packets are being submitted. This is essential for pktgen, +* at least. +*/ +static void sge_tx_timer_cb (unsigned long data) +{ +struct adapter *adapter = (struct adapter *)data; +struct sge *s = &adapter->sge; +unsigned int i, budget; + + budget =MAX_TIMER_TX_RECLAIM ; + i = s->ethtxq_rover; +do { +struct sge_eth_txq *txq = &s->ethtxq[i]; + +if (reclaimable (&txq->q) &&__netif_tx_trylock (txq->txq)) { +int avail =reclaimable (&txq->q); + +if (avail > budget) + avail = budget; + +free_tx_desc (adapter, &txq->q, avail, true); + txq->q.in_use -= avail; +__netif_tx_unlock (txq->txq); + + budget -= avail; +if (!budget) +break ; + } + + i++; +if (i >= s->ethqsets) + i =0 ; + }while (i != s->ethtxq_rover); + s->ethtxq_rover = i; + +/* +* If we found too many reclaimable packets schedule a timer in the +* near future to continue where we left off. Otherwise the next timer +* will be at its normal interval. +*/ +mod_timer (&s->tx_timer, jiffies + (budget ?TX_QCHECK_PERIOD :2 )); +} + +/** +* t4vf_sge_alloc_rxq - allocate an SGE RX Queue +* @adapter: the adapter +* @rspq: pointer to to the new rxq's Response Queue to be filled in +* @iqasynch: if 0, a normal rspq; if 1, an asynchronous event queue +* @dev: the network device associated with the new rspq +* @intr_dest: MSI-X vector index (overriden in MSI mode) +* @fl: pointer to the new rxq's Free List to be filled in +* @hnd: the interrupt handler to invoke for the rspq +*/ +int t4vf_sge_alloc_rxq (struct adapter *adapter,struct sge_rspq *rspq, +bool iqasynch,struct net_device *dev, +int intr_dest, +struct sge_fl *fl,rspq_handler_t hnd) +{ +struct port_info *pi =netdev_priv (dev); +struct fw_iq_cmd cmd, rpl; +int ret, iqandst, flsz =0 ; + +/* +* If we're using MSI interrupts and we're not initializing the +* Forwarded Interrupt Queue itself, then set up this queue for +* indirect interrupts to the Forwarded Interrupt Queue. Obviously +* the Forwarded Interrupt Queue must be set up before any other +* ingress queue ... +*/ +if ((adapter->flags &USING_MSI ) && rspq != &adapter->sge.intrq) { + iqandst =SGE_INTRDST_IQ ; + intr_dest = adapter->sge.intrq.abs_id; + }else + iqandst =SGE_INTRDST_PCI ; + +/* +* Allocate the hardware ring for the Response Queue. The size needs +* to be a multiple of 16 which includes the mandatory status entry +* (regardless of whether the Status Page capabilities are enabled or +* not). +*/ + rspq->size =roundup (rspq->size,16 ); + rspq->desc =alloc_ring (adapter->pdev_dev, rspq->size, rspq->iqe_len, +0 , &rspq->phys_addr,NULL ,0 ); +if (!rspq->desc) +return -ENOMEM ; + +/* +* Fill in the Ingress Queue Command. Note: Ideally this code would +* be in t4vf_hw.c but there are so many parameters and dependencies +* on our Linux SGE state that we would end up having to pass tons of +* parameters. We'll have to think about how this might be migrated +* into OS-independent common code ... +*/ +memset (&cmd,0 ,sizeof (cmd)); + cmd.op_to_vfn =cpu_to_be32 (FW_CMD_OP (FW_IQ_CMD ) | +FW_CMD_REQUEST | +FW_CMD_WRITE | +FW_CMD_EXEC ); + cmd.alloc_to_len16 =cpu_to_be32 (FW_IQ_CMD_ALLOC | +FW_IQ_CMD_IQSTART (1 ) | +FW_LEN16 (cmd)); + cmd.type_to_iqandstindex = +cpu_to_be32 (FW_IQ_CMD_TYPE (FW_IQ_TYPE_FL_INT_CAP ) | +FW_IQ_CMD_IQASYNCH (iqasynch) | +FW_IQ_CMD_VIID (pi->viid) | +FW_IQ_CMD_IQANDST (iqandst) | +FW_IQ_CMD_IQANUS (1 ) | +FW_IQ_CMD_IQANUD (SGE_UPDATEDEL_INTR ) | +FW_IQ_CMD_IQANDSTINDEX (intr_dest)); + cmd.iqdroprss_to_iqesize = +cpu_to_be16 (FW_IQ_CMD_IQPCIECH (pi->port_id) | +FW_IQ_CMD_IQGTSMODE | +FW_IQ_CMD_IQINTCNTTHRESH (rspq->pktcnt_idx) | +FW_IQ_CMD_IQESIZE (ilog2 (rspq->iqe_len) -4 )); + cmd.iqsize =cpu_to_be16 (rspq->size); + cmd.iqaddr =cpu_to_be64 (rspq->phys_addr); + +if (fl) { +/* +* Allocate the ring for the hardware free list (with space +* for its status page) along with the associated software +* descriptor ring. The free list size needs to be a multiple +* of the Egress Queue Unit. +*/ + fl->size =roundup (fl->size,FL_PER_EQ_UNIT ); + fl->desc =alloc_ring (adapter->pdev_dev, fl->size, +sizeof (__be64),sizeof (struct rx_sw_desc ), + &fl->addr, &fl->sdesc,STAT_LEN ); +if (!fl->desc) { + ret = -ENOMEM ; + goto err; + } + +/* +* Calculate the size of the hardware free list ring plus +* status page (which the SGE will place at the end of the +* free list ring) in Egress Queue Units. +*/ + flsz = (fl->size /FL_PER_EQ_UNIT + +STAT_LEN /EQ_UNIT ); + +/* +* Fill in all the relevant firmware Ingress Queue Command +* fields for the free list. +*/ + cmd.iqns_to_fl0congen = +cpu_to_be32 ( +FW_IQ_CMD_FL0HOSTFCMODE (SGE_HOSTFCMODE_NONE ) | +FW_IQ_CMD_FL0PACKEN | +FW_IQ_CMD_FL0PADEN ); + cmd.fl0dcaen_to_fl0cidxfthresh = +cpu_to_be16 ( +FW_IQ_CMD_FL0FBMIN (SGE_FETCHBURSTMIN_64B ) | +FW_IQ_CMD_FL0FBMAX (SGE_FETCHBURSTMAX_512B )); + cmd.fl0size =cpu_to_be16 (flsz); + cmd.fl0addr =cpu_to_be64 (fl->addr); + } + +/* +* Issue the firmware Ingress Queue Command and extract the results if +* it completes successfully. +*/ + ret =t4vf_wr_mbox (adapter, &cmd,sizeof (cmd), &rpl); +if (ret) + goto err; + +netif_napi_add (dev, &rspq->napi, napi_rx_handler,64 ); + rspq->cur_desc = rspq->desc; + rspq->cidx =0 ; + rspq->gen =1 ; + rspq->next_intr_params = rspq->intr_params; + rspq->cntxt_id =be16_to_cpu (rpl.iqid); + rspq->abs_id =be16_to_cpu (rpl.physiqid); + rspq->size--;/* subtract status entry */ + rspq->adapter = adapter; + rspq->netdev = dev; + rspq->handler = hnd; + +/* set offset to -1 to distinguish ingress queues without FL */ + rspq->offset = fl ?0 :-1 ; + +if (fl) { + fl->cntxt_id =be16_to_cpu (rpl.fl0id); + fl->avail =0 ; + fl->pend_cred =0 ; + fl->pidx =0 ; + fl->cidx =0 ; + fl->alloc_failed =0 ; + fl->large_alloc_failed =0 ; + fl->starving =0 ; +refill_fl (adapter, fl,fl_cap (fl),GFP_KERNEL ); + } + +return 0 ; + +err: +/* +* An error occurred. Clean up our partial allocation state and +* return the error. +*/ +if (rspq->desc) { +dma_free_coherent (adapter->pdev_dev, rspq->size * rspq->iqe_len, + rspq->desc, rspq->phys_addr); + rspq->desc =NULL ; + } +if (fl && fl->desc) { +kfree (fl->sdesc); + fl->sdesc =NULL ; +dma_free_coherent (adapter->pdev_dev, flsz *EQ_UNIT , + fl->desc, fl->addr); + fl->desc =NULL ; + } +return ret; +} + +/** +* t4vf_sge_alloc_eth_txq - allocate an SGE Ethernet TX Queue +* @adapter: the adapter +* @txq: pointer to the new txq to be filled in +* @devq: the network TX queue associated with the new txq +* @iqid: the relative ingress queue ID to which events relating to +* the new txq should be directed +*/ +int t4vf_sge_alloc_eth_txq (struct adapter *adapter,struct sge_eth_txq *txq, +struct net_device *dev,struct netdev_queue *devq, +unsigned int iqid) +{ +int ret, nentries; +struct fw_eq_eth_cmd cmd, rpl; +struct port_info *pi =netdev_priv (dev); + +/* +* Calculate the size of the hardware TX Queue (including the +* status age on the end) in units of TX Descriptors. +*/ + nentries = txq->q.size +STAT_LEN /sizeof (struct tx_desc ); + +/* +* Allocate the hardware ring for the TX ring (with space for its +* status page) along with the associated software descriptor ring. +*/ + txq->q.desc =alloc_ring (adapter->pdev_dev, txq->q.size, +sizeof (struct tx_desc ), +sizeof (struct tx_sw_desc ), + &txq->q.phys_addr, &txq->q.sdesc,STAT_LEN ); +if (!txq->q.desc) +return -ENOMEM ; + +/* +* Fill in the Egress Queue Command. Note: As with the direct use of +* the firmware Ingress Queue COmmand above in our RXQ allocation +* routine, ideally, this code would be in t4vf_hw.c. Again, we'll +* have to see if there's some reasonable way to parameterize it +* into the common code ... +*/ +memset (&cmd,0 ,sizeof (cmd)); + cmd.op_to_vfn =cpu_to_be32 (FW_CMD_OP (FW_EQ_ETH_CMD ) | +FW_CMD_REQUEST | +FW_CMD_WRITE | +FW_CMD_EXEC ); + cmd.alloc_to_len16 =cpu_to_be32 (FW_EQ_ETH_CMD_ALLOC | +FW_EQ_ETH_CMD_EQSTART | +FW_LEN16 (cmd)); + cmd.viid_pkd =cpu_to_be32 (FW_EQ_ETH_CMD_VIID (pi->viid)); + cmd.fetchszm_to_iqid = +cpu_to_be32 (FW_EQ_ETH_CMD_HOSTFCMODE (SGE_HOSTFCMODE_STPG ) | +FW_EQ_ETH_CMD_PCIECHN (pi->port_id) | +FW_EQ_ETH_CMD_IQID (iqid)); + cmd.dcaen_to_eqsize = +cpu_to_be32 (FW_EQ_ETH_CMD_FBMIN (SGE_FETCHBURSTMIN_64B ) | +FW_EQ_ETH_CMD_FBMAX (SGE_FETCHBURSTMAX_512B ) | +FW_EQ_ETH_CMD_CIDXFTHRESH (SGE_CIDXFLUSHTHRESH_32 ) | +FW_EQ_ETH_CMD_EQSIZE (nentries)); + cmd.eqaddr =cpu_to_be64 (txq->q.phys_addr); + +/* +* Issue the firmware Egress Queue Command and extract the results if +* it completes successfully. +*/ + ret =t4vf_wr_mbox (adapter, &cmd,sizeof (cmd), &rpl); +if (ret) { +/* +* The girmware Ingress Queue Command failed for some reason. +* Free up our partial allocation state and return the error. +*/ +kfree (txq->q.sdesc); + txq->q.sdesc =NULL ; +dma_free_coherent (adapter->pdev_dev, + nentries *sizeof (struct tx_desc ), + txq->q.desc, txq->q.phys_addr); + txq->q.desc =NULL ; +return ret; + } + + txq->q.in_use =0 ; + txq->q.cidx =0 ; + txq->q.pidx =0 ; + txq->q.stat = (void *)&txq->q.desc[txq->q.size]; + txq->q.cntxt_id =FW_EQ_ETH_CMD_EQID_GET (be32_to_cpu (rpl.eqid_pkd)); + txq->q.abs_id = +FW_EQ_ETH_CMD_PHYSEQID_GET (be32_to_cpu (rpl.physeqid_pkd)); + txq->txq = devq; + txq->tso =0 ; + txq->tx_cso =0 ; + txq->vlan_ins =0 ; + txq->q.stops =0 ; + txq->q.restarts =0 ; + txq->mapping_err =0 ; +return 0 ; +} + +/* +* Free the DMA map resources associated with a TX queue. +*/ +static void free_txq (struct adapter *adapter,struct sge_txq *tq) +{ +dma_free_coherent (adapter->pdev_dev, + tq->size *sizeof (*tq->desc) +STAT_LEN , + tq->desc, tq->phys_addr); + tq->cntxt_id =0 ; + tq->sdesc =NULL ; + tq->desc =NULL ; +} + +/* +* Free the resources associated with a response queue (possibly including a +* free list). +*/ +static void free_rspq_fl (struct adapter *adapter,struct sge_rspq *rspq, +struct sge_fl *fl) +{ +unsigned int flid = fl ? fl->cntxt_id :0xffff ; + +t4vf_iq_free (adapter,FW_IQ_TYPE_FL_INT_CAP , + rspq->cntxt_id, flid,0xffff ); +dma_free_coherent (adapter->pdev_dev, (rspq->size +1 ) * rspq->iqe_len, + rspq->desc, rspq->phys_addr); +netif_napi_del (&rspq->napi); + rspq->netdev =NULL ; + rspq->cntxt_id =0 ; + rspq->abs_id =0 ; + rspq->desc =NULL ; + +if (fl) { +free_rx_bufs (adapter, fl, fl->avail); +dma_free_coherent (adapter->pdev_dev, + fl->size *sizeof (*fl->desc) +STAT_LEN , + fl->desc, fl->addr); +kfree (fl->sdesc); + fl->sdesc =NULL ; + fl->cntxt_id =0 ; + fl->desc =NULL ; + } +} + +/** +* t4vf_free_sge_resources - free SGE resources +* @adapter: the adapter +* +* Frees resources used by the SGE queue sets. +*/ +void t4vf_free_sge_resources (struct adapter *adapter) +{ +struct sge *s = &adapter->sge; +struct sge_eth_rxq *rxq = s->ethrxq; +struct sge_eth_txq *txq = s->ethtxq; +struct sge_rspq *evtq = &s->fw_evtq; +struct sge_rspq *intrq = &s->intrq; +int qs; + +for (qs =0 ; qs < adapter->sge.ethqsets; qs++) { +if (rxq->rspq.desc) +free_rspq_fl (adapter, &rxq->rspq, &rxq->fl); +if (txq->q.desc) { +t4vf_eth_eq_free (adapter, txq->q.cntxt_id); +free_tx_desc (adapter, &txq->q, txq->q.in_use, true); +kfree (txq->q.sdesc); +free_txq (adapter, &txq->q); + } + } +if (evtq->desc) +free_rspq_fl (adapter, evtq,NULL ); +if (intrq->desc) +free_rspq_fl (adapter, intrq,NULL ); +} + +/** +* t4vf_sge_start - enable SGE operation +* @adapter: the adapter +* +* Start tasklets and timers associated with the DMA engine. +*/ +void t4vf_sge_start (struct adapter *adapter) +{ + adapter->sge.ethtxq_rover =0 ; +mod_timer (&adapter->sge.rx_timer, jiffies +RX_QCHECK_PERIOD ); +mod_timer (&adapter->sge.tx_timer, jiffies +TX_QCHECK_PERIOD ); +} + +/** +* t4vf_sge_stop - disable SGE operation +* @adapter: the adapter +* +* Stop tasklets and timers associated with the DMA engine. Note that +* this is effective only if measures have been taken to disable any HW +* events that may restart them. +*/ +void t4vf_sge_stop (struct adapter *adapter) +{ +struct sge *s = &adapter->sge; + +if (s->rx_timer.function) +del_timer_sync (&s->rx_timer); +if (s->tx_timer.function) +del_timer_sync (&s->tx_timer); +} + +/** +* t4vf_sge_init - initialize SGE +* @adapter: the adapter +* +* Performs SGE initialization needed every time after a chip reset. +* We do not initialize any of the queue sets here, instead the driver +* top-level must request those individually. We also do not enable DMA +* here, that should be done after the queues have been set up. +*/ +int t4vf_sge_init (struct adapter *adapter) +{ +struct sge_params *sge_params = &adapter->params.sge; +u32 fl0 = sge_params->sge_fl_buffer_size[0 ]; +u32 fl1 = sge_params->sge_fl_buffer_size[1 ]; +struct sge *s = &adapter->sge; + +/* +* Start by vetting the basic SGE parameters which have been set up by +* the Physical Function Driver. Ideally we should be able to deal +* with _any_ configuration. Practice is different ... +*/ +if (fl0 !=PAGE_SIZE || (fl1 !=0 && fl1 <= fl0)) { +dev_err (adapter->pdev_dev,"bad SGE FL buffer sizes [%d, %d]\n" , + fl0, fl1); +return -EINVAL ; + } +if ((sge_params->sge_control &RXPKTCPLMODE ) ==0 ) { +dev_err (adapter->pdev_dev,"bad SGE CPL MODE\n" ); +return -EINVAL ; + } + +/* +* Now translate the adapter parameters into our internal forms. +*/ +if (fl1) +FL_PG_ORDER =ilog2 (fl1) -PAGE_SHIFT ; +STAT_LEN = ((sge_params->sge_control &EGRSTATUSPAGESIZE ) ?128 :64 ); +PKTSHIFT =PKTSHIFT_GET (sge_params->sge_control); +FL_ALIGN =1 << (INGPADBOUNDARY_GET (sge_params->sge_control) + +INGPADBOUNDARY_SHIFT ); + +/* +* Set up tasklet timers. +*/ +setup_timer (&s->rx_timer, sge_rx_timer_cb, (unsigned long )adapter); +setup_timer (&s->tx_timer, sge_tx_timer_cb, (unsigned long )adapter); + +/* +* Initialize Forwarded Interrupt Queue lock. +*/ +spin_lock_init (&s->intrq_lock); + +return 0 ; +}