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 + -
显示快捷键?