sge.c
来自「linux 内核源代码」· C语言 代码 · 共 2,209 行 · 第 1/5 页
C
2,209 行
if (!skb) { if (!s->num) return NULL; } else { skbq = &s->p[skb->dev->if_port].skbq; __skb_queue_tail(skbq, skb); s->num++; skb = NULL; } if (credits < MAX_SKB_FRAGS + 1) goto out;again: for (i = 0; i < MAX_NPORTS; i++) { s->port = ++s->port & (MAX_NPORTS - 1); skbq = &s->p[s->port].skbq; skb = skb_peek(skbq); if (!skb) continue; len = skb->len; if (len <= s->p[s->port].avail) { s->p[s->port].avail -= len; s->num--; __skb_unlink(skb, skbq); goto out; } skb = NULL; } if (update-- && sched_update_avail(sge)) goto again;out: /* If there are more pending skbs, we use the hardware to schedule us * again. */ if (s->num && !skb) { struct cmdQ *q = &sge->cmdQ[0]; clear_bit(CMDQ_STAT_LAST_PKT_DB, &q->status); if (test_and_set_bit(CMDQ_STAT_RUNNING, &q->status) == 0) { set_bit(CMDQ_STAT_LAST_PKT_DB, &q->status); writel(F_CMDQ0_ENABLE, sge->adapter->regs + A_SG_DOORBELL); } } pr_debug("sched_skb ret %p\n", skb); return skb;}/* * 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 = pci_alloc_consistent(pdev, size, &q->dma_addr); if (!q->entries) goto err_no_mem; size = sizeof(struct freelQ_ce) * q->size; q->centries = kzalloc(size, GFP_KERNEL); if (!q->centries) goto err_no_mem; } /* * 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; size = (16 * 1024) - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); sge->freelQ[sge->jumbo_fl].rx_buffer_size = size; /* * 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 = pci_alloc_consistent(pdev, size, &sge->respQ.dma_addr); if (!sge->respQ.entries) goto err_no_mem; 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 (likely(pci_unmap_len(ce, dma_len))) { pci_unmap_single(pdev, pci_unmap_addr(ce, dma_addr), pci_unmap_len(ce, dma_len), PCI_DMA_TODEVICE); if (q->sop) q->sop = 0; } if (ce->skb) { dev_kfree_skb_any(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; unsigned int size, i; for (i = 0; i < SGE_CMDQ_N; i++) { struct cmdQ *q = &sge->cmdQ[i]; if (q->centries) { if (q->in_use) free_cmdQ_buffers(sge, q, q->in_use); kfree(q->centries); } if (q->entries) { size = sizeof(struct cmdQ_e) * q->size; pci_free_consistent(pdev, size, q->entries, q->dma_addr); } }}/* * Allocates basic TX resources, consisting of memory mapped command Qs. */static int alloc_tx_resources(struct sge *sge, struct sge_params *p){ struct pci_dev *pdev = sge->adapter->pdev; unsigned int size, i; for (i = 0; i < SGE_CMDQ_N; i++) { struct cmdQ *q = &sge->cmdQ[i]; q->genbit = 1; q->sop = 1; q->size = p->cmdQ_size[i]; q->in_use = 0; q->status = 0; q->processed = q->cleaned = 0; q->stop_thres = 0; spin_lock_init(&q->lock); size = sizeof(struct cmdQ_e) * q->size; q->entries = pci_alloc_consistent(pdev, size, &q->dma_addr); if (!q->entries) goto err_no_mem; size = sizeof(struct cmdQ_ce) * q->size; q->centries = kzalloc(size, GFP_KERNEL); if (!q->centries) goto err_no_mem; } /* * CommandQ 0 handles Ethernet and TOE packets, while queue 1 is TOE * only. For queue 0 set the stop threshold so we can handle one more * packet from each port, plus reserve an additional 24 entries for * Ethernet packets only. Queue 1 never suspends nor do we reserve * space for Ethernet packets. */ sge->cmdQ[0].stop_thres = sge->adapter->params.nports * (MAX_SKB_FRAGS + 1); return 0;err_no_mem: free_tx_resources(sge); return -ENOMEM;}static inline void setup_ring_params(struct adapter *adapter, u64 addr, u32 size, int base_reg_lo, int base_reg_hi, int size_reg){ writel((u32)addr, adapter->regs + base_reg_lo); writel(addr >> 32, adapter->regs + base_reg_hi); writel(size, adapter->regs + size_reg);}/* * Enable/disable VLAN acceleration. */void t1_set_vlan_accel(struct adapter *adapter, int on_off){ struct sge *sge = adapter->sge; sge->sge_control &= ~F_VLAN_XTRACT; if (on_off) sge->sge_control |= F_VLAN_XTRACT; if (adapter->open_device_map) { writel(sge->sge_control, adapter->regs + A_SG_CONTROL); readl(adapter->regs + A_SG_CONTROL); /* flush */ }}/* * Programs the various SGE registers. However, the engine is not yet enabled, * but sge->sge_control is setup and ready to go. */static void configure_sge(struct sge *sge, struct sge_params *p){ struct adapter *ap = sge->adapter; writel(0, ap->regs + A_SG_CONTROL); setup_ring_params(ap, sge->cmdQ[0].dma_addr, sge->cmdQ[0].size, A_SG_CMD0BASELWR, A_SG_CMD0BASEUPR, A_SG_CMD0SIZE); setup_ring_params(ap, sge->cmdQ[1].dma_addr, sge->cmdQ[1].size, A_SG_CMD1BASELWR, A_SG_CMD1BASEUPR, A_SG_CMD1SIZE); setup_ring_params(ap, sge->freelQ[0].dma_addr, sge->freelQ[0].size, A_SG_FL0BASELWR, A_SG_FL0BASEUPR, A_SG_FL0SIZE); setup_ring_params(ap, sge->freelQ[1].dma_addr, sge->freelQ[1].size, A_SG_FL1BASELWR, A_SG_FL1BASEUPR, A_SG_FL1SIZE); /* The threshold comparison uses <. */ writel(SGE_RX_SM_BUF_SIZE + 1, ap->regs + A_SG_FLTHRESHOLD); setup_ring_params(ap, sge->respQ.dma_addr, sge->respQ.size, A_SG_RSPBASELWR, A_SG_RSPBASEUPR, A_SG_RSPSIZE); writel((u32)sge->respQ.size - 1, ap->regs + A_SG_RSPQUEUECREDIT); sge->sge_control = F_CMDQ0_ENABLE | F_CMDQ1_ENABLE | F_FL0_ENABLE | F_FL1_ENABLE | F_CPL_ENABLE | F_RESPONSE_QUEUE_ENABLE | V_CMDQ_PRIORITY(2) | F_DISABLE_CMDQ1_GTS | F_ISCSI_COALESCE | V_RX_PKT_OFFSET(sge->rx_pkt_pad);#if defined(__BIG_ENDIAN_BITFIELD) sge->sge_control |= F_ENABLE_BIG_ENDIAN;#endif /* Initialize no-resource timer */ sge->intrtimer_nres = SGE_INTRTIMER_NRES * core_ticks_per_usec(ap); t1_sge_set_coalesce_params(sge, p);}/* * Return the payload capacity of the jumbo free-list buffers. */static inline unsigned int jumbo_payload_capacity(const struct sge *sge){ return sge->freelQ[sge->jumbo_fl].rx_buffer_size - sge->freelQ[sge->jumbo_fl].dma_offset - sizeof(struct cpl_rx_data);}/* * Frees all SGE related resources and the sge structure itself */void t1_sge_destroy(struct sge *sge){ int i; for_each_port(sge->adapter, i) free_percpu(sge->port_stats[i]); kfree(sge->tx_sched); free_tx_resources(sge); free_rx_resources(sge); kfree(sge);}/* * Allocates new RX buffers on the freelist Q (and tracks them on the freelist * context Q) until the Q is full or alloc_skb fails. * * It is possible that the generation bits already match, indicating that the * buffer is already valid and nothing needs to be done. This happens when we * copied a received buffer into a new sk_buff during the interrupt processing. * * If the SGE doesn't automatically align packets properly (!sge->rx_pkt_pad), * we specify a RX_OFFSET in order to make sure that the IP header is 4B * aligned. */static void refill_free_list(struct sge *sge, struct freelQ *q){ struct pci_dev *pdev = sge->adapter->pdev; struct freelQ_ce *ce = &q->centries[q->pidx]; struct freelQ_e *e = &q->entries[q->pidx]; unsigned int dma_len = q->rx_buffer_size - q->dma_offset; while (q->credits < q->size) { struct sk_buff *skb; dma_addr_t mapping; skb = alloc_skb(q->rx_buffer_size, GFP_ATOMIC); if (!skb) break; skb_reserve(skb, q->dma_offset); mapping = pci_map_single(pdev, skb->data, dma_len, PCI_DMA_FROMDEVICE); skb_reserve(skb, sge->rx_pkt_pad); ce->skb = skb; pci_unmap_addr_set(ce, dma_addr, mapping); pci_unmap_len_set(ce, dma_len, dma_len); e->addr_lo = (u32)mapping; e->addr_hi = (u64)mapping >> 32; e->len_gen = V_CMD_LEN(dma_len) | V_CMD_GEN1(q->genbit); wmb(); e->gen2 = V_CMD_GEN2(q->genbit); e++; ce++; if (++q->pidx == q->size) { q->pidx = 0; q->genbit ^= 1; ce = q->centries; e = q->entries; } q->credits++; }}/* * Calls refill_free_list for both free lists. If we cannot fill at least 1/4 * of both rings, we go into 'few interrupt mode' in order to give the system * time to free up resources. */static void freelQs_empty(struct sge *sge){ struct adapter *adapter = sge->adapter; u32 irq_reg = readl(adapter->regs + A_SG_INT_ENABLE); u32 irqholdoff_reg; refill_free_list(sge, &sge->freelQ[0]); refill_free_list(sge, &sge->freelQ[1]); if (sge->freelQ[0].credits > (sge->freelQ[0].size >> 2) && sge->freelQ[1].credits > (sge->freelQ[1].size >> 2)) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?