cassini.c
来自「linux 内核源代码」· C语言 代码 · 共 2,335 行 · 第 1/5 页
C
2,335 行
entry = TX_DESC_NEXT(ring, entry); continue; } /* however, we might get only a partial skb release. */ count -= skb_shinfo(skb)->nr_frags + + cp->tx_tiny_use[ring][entry].nbufs + 1; if (count < 0) break; if (netif_msg_tx_done(cp)) printk(KERN_DEBUG "%s: tx[%d] done, slot %d\n", cp->dev->name, ring, entry); skbs[entry] = NULL; cp->tx_tiny_use[ring][entry].nbufs = 0; for (frag = 0; frag <= skb_shinfo(skb)->nr_frags; frag++) { struct cas_tx_desc *txd = txds + entry; daddr = le64_to_cpu(txd->buffer); dlen = CAS_VAL(TX_DESC_BUFLEN, le64_to_cpu(txd->control)); pci_unmap_page(cp->pdev, daddr, dlen, PCI_DMA_TODEVICE); entry = TX_DESC_NEXT(ring, entry); /* tiny buffer may follow */ if (cp->tx_tiny_use[ring][entry].used) { cp->tx_tiny_use[ring][entry].used = 0; entry = TX_DESC_NEXT(ring, entry); } } spin_lock(&cp->stat_lock[ring]); cp->net_stats[ring].tx_packets++; cp->net_stats[ring].tx_bytes += skb->len; spin_unlock(&cp->stat_lock[ring]); dev_kfree_skb_irq(skb); } cp->tx_old[ring] = entry; /* this is wrong for multiple tx rings. the net device needs * multiple queues for this to do the right thing. we wait * for 2*packets to be available when using tiny buffers */ if (netif_queue_stopped(dev) && (TX_BUFFS_AVAIL(cp, ring) > CAS_TABORT(cp)*(MAX_SKB_FRAGS + 1))) netif_wake_queue(dev); spin_unlock(&cp->tx_lock[ring]);}static void cas_tx(struct net_device *dev, struct cas *cp, u32 status){ int limit, ring;#ifdef USE_TX_COMPWB u64 compwb = le64_to_cpu(cp->init_block->tx_compwb);#endif if (netif_msg_intr(cp)) printk(KERN_DEBUG "%s: tx interrupt, status: 0x%x, %llx\n", cp->dev->name, status, (unsigned long long)compwb); /* process all the rings */ for (ring = 0; ring < N_TX_RINGS; ring++) {#ifdef USE_TX_COMPWB /* use the completion writeback registers */ limit = (CAS_VAL(TX_COMPWB_MSB, compwb) << 8) | CAS_VAL(TX_COMPWB_LSB, compwb); compwb = TX_COMPWB_NEXT(compwb);#else limit = readl(cp->regs + REG_TX_COMPN(ring));#endif if (cp->tx_old[ring] != limit) cas_tx_ringN(cp, ring, limit); }}static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc, int entry, const u64 *words, struct sk_buff **skbref){ int dlen, hlen, len, i, alloclen; int off, swivel = RX_SWIVEL_OFF_VAL; struct cas_page *page; struct sk_buff *skb; void *addr, *crcaddr; __sum16 csum; char *p; hlen = CAS_VAL(RX_COMP2_HDR_SIZE, words[1]); dlen = CAS_VAL(RX_COMP1_DATA_SIZE, words[0]); len = hlen + dlen; if (RX_COPY_ALWAYS || (words[2] & RX_COMP3_SMALL_PKT)) alloclen = len; else alloclen = max(hlen, RX_COPY_MIN); skb = dev_alloc_skb(alloclen + swivel + cp->crc_size); if (skb == NULL) return -1; *skbref = skb; skb_reserve(skb, swivel); p = skb->data; addr = crcaddr = NULL; if (hlen) { /* always copy header pages */ i = CAS_VAL(RX_COMP2_HDR_INDEX, words[1]); page = cp->rx_pages[CAS_VAL(RX_INDEX_RING, i)][CAS_VAL(RX_INDEX_NUM, i)]; off = CAS_VAL(RX_COMP2_HDR_OFF, words[1]) * 0x100 + swivel; i = hlen; if (!dlen) /* attach FCS */ i += cp->crc_size; pci_dma_sync_single_for_cpu(cp->pdev, page->dma_addr + off, i, PCI_DMA_FROMDEVICE); addr = cas_page_map(page->buffer); memcpy(p, addr + off, i); pci_dma_sync_single_for_device(cp->pdev, page->dma_addr + off, i, PCI_DMA_FROMDEVICE); cas_page_unmap(addr); RX_USED_ADD(page, 0x100); p += hlen; swivel = 0; } if (alloclen < (hlen + dlen)) { skb_frag_t *frag = skb_shinfo(skb)->frags; /* normal or jumbo packets. we use frags */ i = CAS_VAL(RX_COMP1_DATA_INDEX, words[0]); page = cp->rx_pages[CAS_VAL(RX_INDEX_RING, i)][CAS_VAL(RX_INDEX_NUM, i)]; off = CAS_VAL(RX_COMP1_DATA_OFF, words[0]) + swivel; hlen = min(cp->page_size - off, dlen); if (hlen < 0) { if (netif_msg_rx_err(cp)) { printk(KERN_DEBUG "%s: rx page overflow: " "%d\n", cp->dev->name, hlen); } dev_kfree_skb_irq(skb); return -1; } i = hlen; if (i == dlen) /* attach FCS */ i += cp->crc_size; pci_dma_sync_single_for_cpu(cp->pdev, page->dma_addr + off, i, PCI_DMA_FROMDEVICE); /* make sure we always copy a header */ swivel = 0; if (p == (char *) skb->data) { /* not split */ addr = cas_page_map(page->buffer); memcpy(p, addr + off, RX_COPY_MIN); pci_dma_sync_single_for_device(cp->pdev, page->dma_addr + off, i, PCI_DMA_FROMDEVICE); cas_page_unmap(addr); off += RX_COPY_MIN; swivel = RX_COPY_MIN; RX_USED_ADD(page, cp->mtu_stride); } else { RX_USED_ADD(page, hlen); } skb_put(skb, alloclen); skb_shinfo(skb)->nr_frags++; skb->data_len += hlen - swivel; skb->truesize += hlen - swivel; skb->len += hlen - swivel; get_page(page->buffer); frag->page = page->buffer; frag->page_offset = off; frag->size = hlen - swivel; /* any more data? */ if ((words[0] & RX_COMP1_SPLIT_PKT) && ((dlen -= hlen) > 0)) { hlen = dlen; off = 0; i = CAS_VAL(RX_COMP2_NEXT_INDEX, words[1]); page = cp->rx_pages[CAS_VAL(RX_INDEX_RING, i)][CAS_VAL(RX_INDEX_NUM, i)]; pci_dma_sync_single_for_cpu(cp->pdev, page->dma_addr, hlen + cp->crc_size, PCI_DMA_FROMDEVICE); pci_dma_sync_single_for_device(cp->pdev, page->dma_addr, hlen + cp->crc_size, PCI_DMA_FROMDEVICE); skb_shinfo(skb)->nr_frags++; skb->data_len += hlen; skb->len += hlen; frag++; get_page(page->buffer); frag->page = page->buffer; frag->page_offset = 0; frag->size = hlen; RX_USED_ADD(page, hlen + cp->crc_size); } if (cp->crc_size) { addr = cas_page_map(page->buffer); crcaddr = addr + off + hlen; } } else { /* copying packet */ if (!dlen) goto end_copy_pkt; i = CAS_VAL(RX_COMP1_DATA_INDEX, words[0]); page = cp->rx_pages[CAS_VAL(RX_INDEX_RING, i)][CAS_VAL(RX_INDEX_NUM, i)]; off = CAS_VAL(RX_COMP1_DATA_OFF, words[0]) + swivel; hlen = min(cp->page_size - off, dlen); if (hlen < 0) { if (netif_msg_rx_err(cp)) { printk(KERN_DEBUG "%s: rx page overflow: " "%d\n", cp->dev->name, hlen); } dev_kfree_skb_irq(skb); return -1; } i = hlen; if (i == dlen) /* attach FCS */ i += cp->crc_size; pci_dma_sync_single_for_cpu(cp->pdev, page->dma_addr + off, i, PCI_DMA_FROMDEVICE); addr = cas_page_map(page->buffer); memcpy(p, addr + off, i); pci_dma_sync_single_for_device(cp->pdev, page->dma_addr + off, i, PCI_DMA_FROMDEVICE); cas_page_unmap(addr); if (p == (char *) skb->data) /* not split */ RX_USED_ADD(page, cp->mtu_stride); else RX_USED_ADD(page, i); /* any more data? */ if ((words[0] & RX_COMP1_SPLIT_PKT) && ((dlen -= hlen) > 0)) { p += hlen; i = CAS_VAL(RX_COMP2_NEXT_INDEX, words[1]); page = cp->rx_pages[CAS_VAL(RX_INDEX_RING, i)][CAS_VAL(RX_INDEX_NUM, i)]; pci_dma_sync_single_for_cpu(cp->pdev, page->dma_addr, dlen + cp->crc_size, PCI_DMA_FROMDEVICE); addr = cas_page_map(page->buffer); memcpy(p, addr, dlen + cp->crc_size); pci_dma_sync_single_for_device(cp->pdev, page->dma_addr, dlen + cp->crc_size, PCI_DMA_FROMDEVICE); cas_page_unmap(addr); RX_USED_ADD(page, dlen + cp->crc_size); }end_copy_pkt: if (cp->crc_size) { addr = NULL; crcaddr = skb->data + alloclen; } skb_put(skb, alloclen); } csum = (__force __sum16)htons(CAS_VAL(RX_COMP4_TCP_CSUM, words[3])); if (cp->crc_size) { /* checksum includes FCS. strip it out. */ csum = csum_fold(csum_partial(crcaddr, cp->crc_size, csum_unfold(csum))); if (addr) cas_page_unmap(addr); } skb->csum = csum_unfold(~csum); skb->ip_summed = CHECKSUM_COMPLETE; skb->protocol = eth_type_trans(skb, cp->dev); return len;}/* we can handle up to 64 rx flows at a time. we do the same thing * as nonreassm except that we batch up the buffers. * NOTE: we currently just treat each flow as a bunch of packets that * we pass up. a better way would be to coalesce the packets * into a jumbo packet. to do that, we need to do the following: * 1) the first packet will have a clean split between header and * data. save both. * 2) each time the next flow packet comes in, extend the * data length and merge the checksums. * 3) on flow release, fix up the header. * 4) make sure the higher layer doesn't care. * because packets get coalesced, we shouldn't run into fragment count * issues. */static inline void cas_rx_flow_pkt(struct cas *cp, const u64 *words, struct sk_buff *skb){ int flowid = CAS_VAL(RX_COMP3_FLOWID, words[2]) & (N_RX_FLOWS - 1); struct sk_buff_head *flow = &cp->rx_flows[flowid]; /* this is protected at a higher layer, so no need to * do any additional locking here. stick the buffer * at the end. */ __skb_insert(skb, flow->prev, (struct sk_buff *) flow, flow); if (words[0] & RX_COMP1_RELEASE_FLOW) { while ((skb = __skb_dequeue(flow))) { cas_skb_release(skb); } }}/* put rx descriptor back on ring. if a buffer is in use by a higher * layer, this will need to put in a replacement. */static void cas_post_page(struct cas *cp, const int ring, const int index){ cas_page_t *new; int entry; entry = cp->rx_old[ring]; new = cas_page_swap(cp, ring, index); cp->init_rxds[ring][entry].buffer = cpu_to_le64(new->dma_addr); cp->init_rxds[ring][entry].index = cpu_to_le64(CAS_BASE(RX_INDEX_NUM, index) | CAS_BASE(RX_INDEX_RING, ring)); entry = RX_DESC_ENTRY(ring, entry + 1); cp->rx_old[ring] = entry; if (entry % 4) return; if (ring == 0) writel(entry, cp->regs + REG_RX_KICK); else if ((N_RX_DESC_RINGS > 1) && (cp->cas_flags & CAS_FLAG_REG_PLUS)) writel(entry, cp->regs + REG_PLUS_RX_KICK1);}/* only when things are bad */static int cas_post_rxds_ringN(struct cas *cp, int ring, int num){ unsigned int entry, last, count, released; int cluster; cas_page_t **page = cp->rx_pages[ring]; entry = cp->rx_old[ring]; if (netif_msg_intr(cp)) printk(KERN_DEBUG "%s: rxd[%d] interrupt, done: %d\n", cp->dev->name, ring, entry); cluster = -1; count = entry & 0x3; last = RX_DESC_ENTRY(ring, num ? entry + num - 4: entry - 4); released = 0; while (entry != last) { /* make a new buffer if it's still in use */ if (page_count(page[entry]->buffer) > 1) { cas_page_t *new = cas_page_dequeue(cp); if (!new) { /* let the timer know that we need to * do this again */ cp->cas_flags |= CAS_FLAG_RXD_POST(ring); if (!timer_pending(&cp->link_timer)) mod_timer(&cp->link_timer, jiffies + CAS_LINK_FAST_TIMEOUT); cp->rx_old[ring] = entry; cp->rx_last[ring] = num ? num - released : 0; return -ENOMEM; } spin_lock(&cp->rx_inuse_lock); list_add(&page[entry]->list, &cp->rx_inuse_list); spin_unlock(&cp->rx_inuse_lock); cp->init_rxds[ring][entry].buffer = cpu_to_le64(new->dma_addr); page[entry] = new; } if (++count == 4) { cluster = entry; count = 0; } released++; entry = RX_DESC_ENTRY(ring, entry + 1); } cp->rx_old[ring] = entry; if (cluster < 0) return 0; if (ring == 0) writel(cluster, cp->regs + REG_RX_KICK); else if ((N_RX_DESC_RINGS > 1) && (cp->cas_flags & CAS_FLAG_REG_PLUS)) writel(cluster, cp->regs + REG_PLUS_RX_KICK1); return 0;}/* process a completion ring. packets are set up in three basic ways: * small packets: should be copied header + data in single buffer. * large packets: header and data in a single buffer. * split packets: header in a separate buffer from data. * data may be in multiple pages. data may be > 256 * bytes but in a single page. * * NOTE: RX page posting is done in this routine as well. while there's * the capability of using multiple RX completion rings, it isn't * really worthwhile due to the fact that the page posting will * force serialization on the single descriptor ring. */static int cas_rx_ringN(struct cas *cp, int ring, int budget){ struct cas_rx_comp *rxcs = cp->init_rxcs[ring]; int entry, drops; int npackets = 0; if (netif_msg_intr(cp)) printk(KERN_DEBUG "%s: rx[%d] interrupt, done: %d/%d\n", cp->dev->name, ring, readl(cp->regs + REG_RX_COMP_HEAD), cp->rx_new[ring]); entry = cp->rx_new[ring]; drops = 0; while (1) { struct cas_rx_comp *rxc = rxcs + entry; struct sk_buff *skb; int type, len; u64 words[4]; int i, dring; words[0] = le64_to_cpu(rxc->word1); words[1] = le64_to_cpu(rxc->word2); words[2] = le64_to_cpu(rxc->word3); words[3] = le64_to_cpu(rxc->word4); /* don't touch if still owned by hw */ type = CAS_VAL(RX_COMP1_TYPE, words[0]); if (type == 0) break; /* hw hasn't cleared the zero bit yet */ if (words[3] & RX_COMP4_ZERO) { break; } /* get info on the packet */ if (words[3] & (RX_COMP4_LEN_MISMATCH | RX_COMP4_BAD)) { spin_lock(&cp->stat_lock[ring]); cp->net_stats[ring].rx_errors++; if (words[3] & RX_COMP4_LEN_MISMATCH) cp->net_stats[ring].rx_length_errors++; if (words[3] & RX_COMP4_BAD) cp->net_stats[ring].rx_crc_errors++; spin_unlock(&cp->stat_lock[ring]); /* We'll just return it to Cassini. */ drop_it: spin_lock(&cp->stat_lock[r
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?