📄 sge.c
字号:
/***************************************************************************** * * * File: sge.c * * $Revision: 1.26 $ * * $Date: 2005/06/21 18:29:48 $ * * Description: * * DMA engine. * * part of the Chelsio 10Gb Ethernet Driver. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License, version 2, as * * published by the Free Software Foundation. * * * * You should have received a copy of the GNU General Public License along * * with this program; if not, write to the Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * * * http://www.chelsio.com * * * * Copyright (c) 2003 - 2005 Chelsio Communications, Inc. * * All rights reserved. * * * * Maintainers: maintainers@chelsio.com * * * * Authors: Dimitrios Michailidis <dm@chelsio.com> * * Tina Yang <tainay@chelsio.com> * * Felix Marti <felix@chelsio.com> * * Scott Bardone <sbardone@chelsio.com> * * Kurt Ottaway <kottaway@chelsio.com> * * Frank DiMambro <frank@chelsio.com> * * * * History: * * * ****************************************************************************/#include "common.h"#include <linux/config.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/pci.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/if_vlan.h>#include <linux/skbuff.h>#include <linux/init.h>#include <linux/mm.h>#include <linux/ip.h>#include <linux/in.h>#include <linux/if_arp.h>#include "cpl5_cmd.h"#include "sge.h"#include "regs.h"#include "espi.h"#ifdef NETIF_F_TSO#include <linux/tcp.h>#endif#define SGE_CMDQ_N 2#define SGE_FREELQ_N 2#define SGE_CMDQ0_E_N 1024#define SGE_CMDQ1_E_N 128#define SGE_FREEL_SIZE 4096#define SGE_JUMBO_FREEL_SIZE 512#define SGE_FREEL_REFILL_THRESH 16#define SGE_RESPQ_E_N 1024#define SGE_INTRTIMER_NRES 1000#define SGE_RX_COPY_THRES 256#define SGE_RX_SM_BUF_SIZE 1536# define SGE_RX_DROP_THRES 2#define SGE_RESPQ_REPLENISH_THRES (SGE_RESPQ_E_N / 4)/* * Period of the TX buffer reclaim timer. This timer does not need to run * frequently as TX buffers are usually reclaimed by new TX packets. */#define TX_RECLAIM_PERIOD (HZ / 4)#ifndef NET_IP_ALIGN# define NET_IP_ALIGN 2#endif#define M_CMD_LEN 0x7fffffff#define V_CMD_LEN(v) (v)#define G_CMD_LEN(v) ((v) & M_CMD_LEN)#define V_CMD_GEN1(v) ((v) << 31)#define V_CMD_GEN2(v) (v)#define F_CMD_DATAVALID (1 << 1)#define F_CMD_SOP (1 << 2)#define V_CMD_EOP(v) ((v) << 3)/* * Command queue, receive buffer list, and response queue descriptors. */#if defined(__BIG_ENDIAN_BITFIELD)struct cmdQ_e { u32 addr_lo; u32 len_gen; u32 flags; u32 addr_hi;};struct freelQ_e { u32 addr_lo; u32 len_gen; u32 gen2; u32 addr_hi;};struct respQ_e { u32 Qsleeping : 4; u32 Cmdq1CreditReturn : 5; u32 Cmdq1DmaComplete : 5; u32 Cmdq0CreditReturn : 5; u32 Cmdq0DmaComplete : 5; u32 FreelistQid : 2; u32 CreditValid : 1; u32 DataValid : 1; u32 Offload : 1; u32 Eop : 1; u32 Sop : 1; u32 GenerationBit : 1; u32 BufferLength;};#elif defined(__LITTLE_ENDIAN_BITFIELD)struct cmdQ_e { u32 len_gen; u32 addr_lo; u32 addr_hi; u32 flags;};struct freelQ_e { u32 len_gen; u32 addr_lo; u32 addr_hi; u32 gen2;};struct respQ_e { u32 BufferLength; u32 GenerationBit : 1; u32 Sop : 1; u32 Eop : 1; u32 Offload : 1; u32 DataValid : 1; u32 CreditValid : 1; u32 FreelistQid : 2; u32 Cmdq0DmaComplete : 5; u32 Cmdq0CreditReturn : 5; u32 Cmdq1DmaComplete : 5; u32 Cmdq1CreditReturn : 5; u32 Qsleeping : 4;} ;#endif/* * SW Context Command and Freelist Queue Descriptors */struct cmdQ_ce { struct sk_buff *skb; DECLARE_PCI_UNMAP_ADDR(dma_addr); DECLARE_PCI_UNMAP_LEN(dma_len);};struct freelQ_ce { struct sk_buff *skb; DECLARE_PCI_UNMAP_ADDR(dma_addr); DECLARE_PCI_UNMAP_LEN(dma_len);};/* * SW command, freelist and response rings */struct cmdQ { unsigned long status; /* HW DMA fetch status */ unsigned int in_use; /* # of in-use command descriptors */ unsigned int size; /* # of descriptors */ unsigned int processed; /* total # of descs HW has processed */ unsigned int cleaned; /* total # of descs SW has reclaimed */ unsigned int stop_thres; /* SW TX queue suspend threshold */ u16 pidx; /* producer index (SW) */ u16 cidx; /* consumer index (HW) */ u8 genbit; /* current generation (=valid) bit */ u8 sop; /* is next entry start of packet? */ struct cmdQ_e *entries; /* HW command descriptor Q */ struct cmdQ_ce *centries; /* SW command context descriptor Q */ spinlock_t lock; /* Lock to protect cmdQ enqueuing */ dma_addr_t dma_addr; /* DMA addr HW command descriptor Q */};struct freelQ { unsigned int credits; /* # of available RX buffers */ unsigned int size; /* free list capacity */ u16 pidx; /* producer index (SW) */ u16 cidx; /* consumer index (HW) */ u16 rx_buffer_size; /* Buffer size on this free list */ u16 dma_offset; /* DMA offset to align IP headers */ u16 recycleq_idx; /* skb recycle q to use */ u8 genbit; /* current generation (=valid) bit */ struct freelQ_e *entries; /* HW freelist descriptor Q */ struct freelQ_ce *centries; /* SW freelist context descriptor Q */ dma_addr_t dma_addr; /* DMA addr HW freelist descriptor Q */};struct respQ { unsigned int credits; /* credits to be returned to SGE */ unsigned int size; /* # of response Q descriptors */ u16 cidx; /* consumer index (SW) */ u8 genbit; /* current generation(=valid) bit */ struct respQ_e *entries; /* HW response descriptor Q */ dma_addr_t dma_addr; /* DMA addr HW response descriptor Q */};/* Bit flags for cmdQ.status */enum { CMDQ_STAT_RUNNING = 1, /* fetch engine is running */ CMDQ_STAT_LAST_PKT_DB = 2 /* last packet rung the doorbell */};/* * Main SGE data structure * * Interrupts are handled by a single CPU and it is likely that on a MP system * the application is migrated to another CPU. In that scenario, we try to * seperate the RX(in irq context) and TX state in order to decrease memory * contention. */struct sge { struct adapter *adapter; /* adapter backpointer */ struct net_device *netdev; /* netdevice backpointer */ struct freelQ freelQ[SGE_FREELQ_N]; /* buffer free lists */ struct respQ respQ; /* response Q */ unsigned long stopped_tx_queues; /* bitmap of suspended Tx queues */ unsigned int rx_pkt_pad; /* RX padding for L2 packets */ unsigned int jumbo_fl; /* jumbo freelist Q index */ unsigned int intrtimer_nres; /* no-resource interrupt timer */ unsigned int fixed_intrtimer;/* non-adaptive interrupt timer */ struct timer_list tx_reclaim_timer; /* reclaims TX buffers */ struct timer_list espibug_timer; unsigned int espibug_timeout; struct sk_buff *espibug_skb; u32 sge_control; /* shadow value of sge control reg */ struct sge_intr_counts stats; struct sge_port_stats port_stats[MAX_NPORTS]; struct cmdQ cmdQ[SGE_CMDQ_N] ____cacheline_aligned_in_smp;};/* * PIO to indicate that memory mapped Q contains valid descriptor(s). */static inline void doorbell_pio(struct adapter *adapter, u32 val){ wmb(); writel(val, adapter->regs + A_SG_DOORBELL);}/* * Frees all RX buffers on the freelist Q. The caller must make sure that * the SGE is turned off before calling this function. */static void free_freelQ_buffers(struct pci_dev *pdev, struct freelQ *q){ unsigned int cidx = q->cidx; while (q->credits--) { struct freelQ_ce *ce = &q->centries[cidx]; pci_unmap_single(pdev, pci_unmap_addr(ce, dma_addr), pci_unmap_len(ce, dma_len), PCI_DMA_FROMDEVICE); dev_kfree_skb(ce->skb); ce->skb = NULL; if (++cidx == q->size) cidx = 0; }}/* * Free RX free list and response queue resources. */static void free_rx_resources(struct sge *sge){ struct pci_dev *pdev = sge->adapter->pdev; unsigned int size, i; if (sge->respQ.entries) { size = sizeof(struct respQ_e) * sge->respQ.size; pci_free_consistent(pdev, size, sge->respQ.entries, sge->respQ.dma_addr); } for (i = 0; i < SGE_FREELQ_N; i++) { struct freelQ *q = &sge->freelQ[i]; if (q->centries) { free_freelQ_buffers(pdev, q); kfree(q->centries); } if (q->entries) { size = sizeof(struct freelQ_e) * q->size; pci_free_consistent(pdev, size, q->entries, q->dma_addr); } }}/* * Allocates basic RX resources, consisting of memory mapped freelist Qs and a * response queue. */static int alloc_rx_resources(struct sge *sge, struct sge_params *p){ struct pci_dev *pdev = sge->adapter->pdev; unsigned int size, i; for (i = 0; i < SGE_FREELQ_N; i++) { struct freelQ *q = &sge->freelQ[i]; q->genbit = 1; q->size = p->freelQ_size[i]; q->dma_offset = sge->rx_pkt_pad ? 0 : NET_IP_ALIGN; size = sizeof(struct freelQ_e) * q->size; q->entries = (struct freelQ_e *) pci_alloc_consistent(pdev, size, &q->dma_addr); if (!q->entries) goto err_no_mem; memset(q->entries, 0, size); size = sizeof(struct freelQ_ce) * q->size; q->centries = kmalloc(size, GFP_KERNEL); if (!q->centries) goto err_no_mem; memset(q->centries, 0, size); } /* * Calculate the buffer sizes for the two free lists. FL0 accommodates * regular sized Ethernet frames, FL1 is sized not to exceed 16K, * including all the sk_buff overhead. * * Note: For T2 FL0 and FL1 are reversed. */ sge->freelQ[!sge->jumbo_fl].rx_buffer_size = SGE_RX_SM_BUF_SIZE + sizeof(struct cpl_rx_data) + sge->freelQ[!sge->jumbo_fl].dma_offset; sge->freelQ[sge->jumbo_fl].rx_buffer_size = (16 * 1024) - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); /* * Setup which skb recycle Q should be used when recycling buffers from * each free list. */ sge->freelQ[!sge->jumbo_fl].recycleq_idx = 0; sge->freelQ[sge->jumbo_fl].recycleq_idx = 1; sge->respQ.genbit = 1; sge->respQ.size = SGE_RESPQ_E_N; sge->respQ.credits = 0; size = sizeof(struct respQ_e) * sge->respQ.size; sge->respQ.entries = (struct respQ_e *) pci_alloc_consistent(pdev, size, &sge->respQ.dma_addr); if (!sge->respQ.entries) goto err_no_mem; memset(sge->respQ.entries, 0, size); return 0;err_no_mem: free_rx_resources(sge); return -ENOMEM;}/* * Reclaims n TX descriptors and frees the buffers associated with them. */static void free_cmdQ_buffers(struct sge *sge, struct cmdQ *q, unsigned int n){ struct cmdQ_ce *ce; struct pci_dev *pdev = sge->adapter->pdev; unsigned int cidx = q->cidx; q->in_use -= n; ce = &q->centries[cidx]; while (n--) { if (q->sop) pci_unmap_single(pdev, pci_unmap_addr(ce, dma_addr), pci_unmap_len(ce, dma_len), PCI_DMA_TODEVICE); else pci_unmap_page(pdev, pci_unmap_addr(ce, dma_addr), pci_unmap_len(ce, dma_len), PCI_DMA_TODEVICE); q->sop = 0; if (ce->skb) { dev_kfree_skb(ce->skb); q->sop = 1; } ce++; if (++cidx == q->size) { cidx = 0; ce = q->centries; } } q->cidx = cidx;}/* * Free TX resources. * * Assumes that SGE is stopped and all interrupts are disabled. */static void free_tx_resources(struct sge *sge){ struct pci_dev *pdev = sge->adapter->pdev;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -