sge.c

来自「linux 内核源代码」· C语言 代码 · 共 2,293 行 · 第 1/5 页

C
2,293
字号
	t3_write_reg(adap, A_SG_KDOORBELL, V_EGRCNTX(q->cntxt_id));}static inline void __refill_fl(struct adapter *adap, struct sge_fl *fl){	refill_fl(adap, fl, min(16U, fl->size - fl->credits), GFP_ATOMIC);}/** *	recycle_rx_buf - recycle a receive buffer *	@adapter: the adapter *	@q: the SGE free list *	@idx: index of buffer to recycle * *	Recycles the specified buffer on the given free list by adding it at *	the next available slot on the list. */static void recycle_rx_buf(struct adapter *adap, struct sge_fl *q,			   unsigned int idx){	struct rx_desc *from = &q->desc[idx];	struct rx_desc *to = &q->desc[q->pidx];	q->sdesc[q->pidx] = q->sdesc[idx];	to->addr_lo = from->addr_lo;	/* already big endian */	to->addr_hi = from->addr_hi;	/* likewise */	wmb();	to->len_gen = cpu_to_be32(V_FLD_GEN1(q->gen));	to->gen2 = cpu_to_be32(V_FLD_GEN2(q->gen));	q->credits++;	if (++q->pidx == q->size) {		q->pidx = 0;		q->gen ^= 1;	}	t3_write_reg(adap, A_SG_KDOORBELL, V_EGRCNTX(q->cntxt_id));}/** *	alloc_ring - allocate resources for an SGE descriptor ring *	@pdev: the PCI device *	@nelem: the number of descriptors *	@elem_size: the size of each descriptor *	@sw_size: the size of the SW state associated with each ring element *	@phys: the physical address of the allocated ring *	@metadata: address of the array holding the SW state for the ring * *	Allocates resources for an SGE descriptor ring, such as Tx queues, *	free buffer lists, or response queues.  Each SGE ring requires *	space for its HW descriptors plus, optionally, space for the SW state *	associated with each HW entry (the metadata).  The function returns *	three values: the virtual address for the HW ring (the return value *	of the function), the physical address of the HW ring, and the address *	of the SW ring. */static void *alloc_ring(struct pci_dev *pdev, size_t nelem, size_t elem_size,			size_t sw_size, dma_addr_t * phys, void *metadata){	size_t len = nelem * elem_size;	void *s = NULL;	void *p = dma_alloc_coherent(&pdev->dev, len, phys, GFP_KERNEL);	if (!p)		return NULL;	if (sw_size) {		s = kcalloc(nelem, sw_size, GFP_KERNEL);		if (!s) {			dma_free_coherent(&pdev->dev, len, p, *phys);			return NULL;		}	}	if (metadata)		*(void **)metadata = s;	memset(p, 0, len);	return p;}/** *	free_qset - free the resources of an SGE queue set *	@adapter: the adapter owning the queue set *	@q: the queue set * *	Release the HW and SW resources associated with an SGE queue set, such *	as HW contexts, packet buffers, and descriptor rings.  Traffic to the *	queue set must be quiesced prior to calling this. */static void t3_free_qset(struct adapter *adapter, struct sge_qset *q){	int i;	struct pci_dev *pdev = adapter->pdev;	if (q->tx_reclaim_timer.function)		del_timer_sync(&q->tx_reclaim_timer);	for (i = 0; i < SGE_RXQ_PER_SET; ++i)		if (q->fl[i].desc) {			spin_lock(&adapter->sge.reg_lock);			t3_sge_disable_fl(adapter, q->fl[i].cntxt_id);			spin_unlock(&adapter->sge.reg_lock);			free_rx_bufs(pdev, &q->fl[i]);			kfree(q->fl[i].sdesc);			dma_free_coherent(&pdev->dev,					  q->fl[i].size *					  sizeof(struct rx_desc), q->fl[i].desc,					  q->fl[i].phys_addr);		}	for (i = 0; i < SGE_TXQ_PER_SET; ++i)		if (q->txq[i].desc) {			spin_lock(&adapter->sge.reg_lock);			t3_sge_enable_ecntxt(adapter, q->txq[i].cntxt_id, 0);			spin_unlock(&adapter->sge.reg_lock);			if (q->txq[i].sdesc) {				free_tx_desc(adapter, &q->txq[i],					     q->txq[i].in_use);				kfree(q->txq[i].sdesc);			}			dma_free_coherent(&pdev->dev,					  q->txq[i].size *					  sizeof(struct tx_desc),					  q->txq[i].desc, q->txq[i].phys_addr);			__skb_queue_purge(&q->txq[i].sendq);		}	if (q->rspq.desc) {		spin_lock(&adapter->sge.reg_lock);		t3_sge_disable_rspcntxt(adapter, q->rspq.cntxt_id);		spin_unlock(&adapter->sge.reg_lock);		dma_free_coherent(&pdev->dev,				  q->rspq.size * sizeof(struct rsp_desc),				  q->rspq.desc, q->rspq.phys_addr);	}	memset(q, 0, sizeof(*q));}/** *	init_qset_cntxt - initialize an SGE queue set context info *	@qs: the queue set *	@id: the queue set id * *	Initializes the TIDs and context ids for the queues of a queue set. */static void init_qset_cntxt(struct sge_qset *qs, unsigned int id){	qs->rspq.cntxt_id = id;	qs->fl[0].cntxt_id = 2 * id;	qs->fl[1].cntxt_id = 2 * id + 1;	qs->txq[TXQ_ETH].cntxt_id = FW_TUNNEL_SGEEC_START + id;	qs->txq[TXQ_ETH].token = FW_TUNNEL_TID_START + id;	qs->txq[TXQ_OFLD].cntxt_id = FW_OFLD_SGEEC_START + id;	qs->txq[TXQ_CTRL].cntxt_id = FW_CTRL_SGEEC_START + id;	qs->txq[TXQ_CTRL].token = FW_CTRL_TID_START + id;}/** *	sgl_len - calculates the size of an SGL of the given capacity *	@n: the number of SGL entries * *	Calculates the number of flits needed for a scatter/gather list that *	can hold the given number of entries. */static inline unsigned int sgl_len(unsigned int n){	/* alternatively: 3 * (n / 2) + 2 * (n & 1) */	return (3 * n) / 2 + (n & 1);}/** *	flits_to_desc - returns the num of Tx descriptors for the given flits *	@n: the number of flits * *	Calculates the number of Tx descriptors needed for the supplied number *	of flits. */static inline unsigned int flits_to_desc(unsigned int n){	BUG_ON(n >= ARRAY_SIZE(flit_desc_map));	return flit_desc_map[n];}/** *	get_packet - return the next ingress packet buffer from a free list *	@adap: the adapter that received the packet *	@fl: the SGE free list holding the packet *	@len: the packet length including any SGE padding *	@drop_thres: # of remaining buffers before we start dropping packets * *	Get the next packet from a free list and complete setup of the *	sk_buff.  If the packet is small we make a copy and recycle the *	original buffer, otherwise we use the original buffer itself.  If a *	positive drop threshold is supplied packets are dropped and their *	buffers recycled if (a) the number of remaining buffers is under the *	threshold and the packet is too big to copy, or (b) the packet should *	be copied but there is no memory for the copy. */static struct sk_buff *get_packet(struct adapter *adap, struct sge_fl *fl,				  unsigned int len, unsigned int drop_thres){	struct sk_buff *skb = NULL;	struct rx_sw_desc *sd = &fl->sdesc[fl->cidx];	prefetch(sd->skb->data);	fl->credits--;	if (len <= SGE_RX_COPY_THRES) {		skb = alloc_skb(len, GFP_ATOMIC);		if (likely(skb != NULL)) {			__skb_put(skb, len);			pci_dma_sync_single_for_cpu(adap->pdev,					    pci_unmap_addr(sd, dma_addr), len,					    PCI_DMA_FROMDEVICE);			memcpy(skb->data, sd->skb->data, len);			pci_dma_sync_single_for_device(adap->pdev,					    pci_unmap_addr(sd, dma_addr), len,					    PCI_DMA_FROMDEVICE);		} else if (!drop_thres)			goto use_orig_buf;recycle:		recycle_rx_buf(adap, fl, fl->cidx);		return skb;	}	if (unlikely(fl->credits < drop_thres))		goto recycle;use_orig_buf:	pci_unmap_single(adap->pdev, pci_unmap_addr(sd, dma_addr),			 fl->buf_size, PCI_DMA_FROMDEVICE);	skb = sd->skb;	skb_put(skb, len);	__refill_fl(adap, fl);	return skb;}/** *	get_packet_pg - return the next ingress packet buffer from a free list *	@adap: the adapter that received the packet *	@fl: the SGE free list holding the packet *	@len: the packet length including any SGE padding *	@drop_thres: # of remaining buffers before we start dropping packets * *	Get the next packet from a free list populated with page chunks. *	If the packet is small we make a copy and recycle the original buffer, *	otherwise we attach the original buffer as a page fragment to a fresh *	sk_buff.  If a positive drop threshold is supplied packets are dropped *	and their buffers recycled if (a) the number of remaining buffers is *	under the threshold and the packet is too big to copy, or (b) there's *	no system memory. * * 	Note: this function is similar to @get_packet but deals with Rx buffers * 	that are page chunks rather than sk_buffs. */static struct sk_buff *get_packet_pg(struct adapter *adap, struct sge_fl *fl,				     unsigned int len, unsigned int drop_thres){	struct sk_buff *skb = NULL;	struct rx_sw_desc *sd = &fl->sdesc[fl->cidx];	if (len <= SGE_RX_COPY_THRES) {		skb = alloc_skb(len, GFP_ATOMIC);		if (likely(skb != NULL)) {			__skb_put(skb, len);			pci_dma_sync_single_for_cpu(adap->pdev,					    pci_unmap_addr(sd, dma_addr), len,					    PCI_DMA_FROMDEVICE);			memcpy(skb->data, sd->pg_chunk.va, len);			pci_dma_sync_single_for_device(adap->pdev,					    pci_unmap_addr(sd, dma_addr), len,					    PCI_DMA_FROMDEVICE);		} else if (!drop_thres)			return NULL;recycle:		fl->credits--;		recycle_rx_buf(adap, fl, fl->cidx);		return skb;	}	if (unlikely(fl->credits <= drop_thres))		goto recycle;	skb = alloc_skb(SGE_RX_PULL_LEN, GFP_ATOMIC);	if (unlikely(!skb)) {		if (!drop_thres)			return NULL;		goto recycle;	}	pci_unmap_single(adap->pdev, pci_unmap_addr(sd, dma_addr),			 fl->buf_size, PCI_DMA_FROMDEVICE);	__skb_put(skb, SGE_RX_PULL_LEN);	memcpy(skb->data, sd->pg_chunk.va, SGE_RX_PULL_LEN);	skb_fill_page_desc(skb, 0, sd->pg_chunk.page,			   sd->pg_chunk.offset + SGE_RX_PULL_LEN,			   len - SGE_RX_PULL_LEN);	skb->len = len;	skb->data_len = len - SGE_RX_PULL_LEN;	skb->truesize += skb->data_len;	fl->credits--;	/*	 * We do not refill FLs here, we let the caller do it to overlap a	 * prefetch.	 */	return skb;}/** *	get_imm_packet - return the next ingress packet buffer from a response *	@resp: the response descriptor containing the packet data * *	Return a packet containing the immediate data of the given response. */static inline struct sk_buff *get_imm_packet(const struct rsp_desc *resp){	struct sk_buff *skb = alloc_skb(IMMED_PKT_SIZE, GFP_ATOMIC);	if (skb) {		__skb_put(skb, IMMED_PKT_SIZE);		skb_copy_to_linear_data(skb, resp->imm_data, IMMED_PKT_SIZE);	}	return skb;}/** *	calc_tx_descs - calculate the number of Tx descriptors for a packet *	@skb: the packet * * 	Returns the number of Tx descriptors needed for the given Ethernet * 	packet.  Ethernet packets require addition of WR and CPL headers. */static inline unsigned int calc_tx_descs(const struct sk_buff *skb){	unsigned int flits;	if (skb->len <= WR_LEN - sizeof(struct cpl_tx_pkt))		return 1;	flits = sgl_len(skb_shinfo(skb)->nr_frags + 1) + 2;	if (skb_shinfo(skb)->gso_size)		flits++;	return flits_to_desc(flits);}/** *	make_sgl - populate a scatter/gather list for a packet *	@skb: the packet *	@sgp: the SGL to populate *	@start: start address of skb main body data to include in the SGL *	@len: length of skb main body data to include in the SGL *	@pdev: the PCI device * *	Generates a scatter/gather list for the buffers that make up a packet *	and returns the SGL size in 8-byte words.  The caller must size the SGL *	appropriately. */static inline unsigned int make_sgl(const struct sk_buff *skb,				    struct sg_ent *sgp, unsigned char *start,				    unsigned int len, struct pci_dev *pdev){	dma_addr_t mapping;	unsigned int i, j = 0, nfrags;	if (len) {		mapping = pci_map_single(pdev, start, len, PCI_DMA_TODEVICE);		sgp->len[0] = cpu_to_be32(len);		sgp->addr[0] = cpu_to_be64(mapping);		j = 1;	}	nfrags = skb_shinfo(skb)->nr_frags;	for (i = 0; i < nfrags; i++) {		skb_frag_t *frag = &skb_shinfo(skb)->frags[i];		mapping = pci_map_page(pdev, frag->page, frag->page_offset,				       frag->size, PCI_DMA_TODEVICE);		sgp->len[j] = cpu_to_be32(frag->size);		sgp->addr[j] = cpu_to_be64(mapping);		j ^= 1;		if (j == 0)			++sgp;	}	if (j)		sgp->len[j] = 0;	return ((nfrags + (len != 0)) * 3) / 2 + j;}/** *	check_ring_tx_db - check and potentially ring a Tx queue's doorbell *	@adap: the adapter *	@q: the Tx queue * *	Ring the doorbel if a Tx queue is asleep.  There is a natural race, *	where the HW is going to sleep just after we checked, however, *	then the interrupt handler will detect the outstanding TX packet *	and ring the doorbell for us. * *	When GTS is disabled we unconditionally ring the doorbell. */static inline void check_ring_tx_db(struct adapter *adap, struct sge_txq *q){#if USE_GTS	clear_bit(TXQ_LAST_PKT_DB, &q->flags);	if (test_and_set_bit(TXQ_RUNNING, &q->flags) == 0) {		set_bit(TXQ_LAST_PKT_DB, &q->flags);		t3_write_reg(adap, A_SG_KDOORBELL,			     F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id));	}#else	wmb();			/* write descriptors before telling HW */	t3_write_reg(adap, A_SG_KDOORBELL,		     F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id));#endif}static inline void wr_gen2(struct tx_desc *d, unsigned int gen){#if SGE_NUM_GENBITS == 2	d->flit[TX_DESC_FLITS - 1] = cpu_to_be64(gen);#endif}/** *	write_wr_hdr_sgl - write a WR header and, optionally, SGL *	@ndesc: number of Tx descriptors spanned by the SGL *	@skb: the packet corresponding to the WR *	@d: first Tx descriptor to be written *	@pidx: index of above descriptors *	@q: the SGE Tx queue *	@sgl: the SGL *	@flits: number of flits to the start of the SGL in the first descriptor *	@sgl_flits: the SGL size in flits *	@gen: the Tx descriptor generation *	@wr_hi: top 32 bits of WR header based on WR type (big endian) *	@wr_lo: low 32 bits of WR header based on WR type (big endian) * *	Write a work request header and an associated SGL.  If the SGL is *	small enough to fit into one Tx descriptor it has already been written *	and we just need to write the WR header.  Otherwise we distribute the *	SGL across the number of descriptors it spans. */static void write_wr_hdr_sgl(unsigned int ndesc, struct sk_buff *skb,			     struct tx_desc *d, unsigned int pidx,			     const struct sge_txq *q,			     const struct sg_ent *sgl,			     unsigned int flits, unsigned int sgl_flits,			     unsigned int gen, __be32 wr_hi,			     __be32 wr_lo){	struct work_request_hdr *wrp = (struct work_request_hdr *)d;	struct tx_sw_desc *sd = &q->sdesc[pidx];	sd->skb = skb;	if (need_skb_unmap()) {		struct unmap_info *ui = (struct unmap_info *)skb->cb;		ui->fragidx = 0;		ui->addr_idx = 0;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?