Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6b84daca authored by Stephen Hemminger's avatar Stephen Hemminger Committed by David S. Miller
Browse files

sky2: optimize transmit completion



Don't reference the list element in hardware transmit ring on transmit
completion. The list element is updated by hardware, therefore
it causes a cache miss. Do book keeping in software structure.

Signed-off-by: default avatarStephen Hemminger <shemminger@vyatta.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ee5f68fe
Loading
Loading
Loading
Loading
+31 −49
Original line number Original line Diff line number Diff line
@@ -1001,8 +1001,11 @@ static void sky2_prefetch_init(struct sky2_hw *hw, u32 qaddr,
static inline struct sky2_tx_le *get_tx_le(struct sky2_port *sky2, u16 *slot)
static inline struct sky2_tx_le *get_tx_le(struct sky2_port *sky2, u16 *slot)
{
{
	struct sky2_tx_le *le = sky2->tx_le + *slot;
	struct sky2_tx_le *le = sky2->tx_le + *slot;
	struct tx_ring_info *re = sky2->tx_ring + *slot;


	*slot = RING_NEXT(*slot, sky2->tx_ring_size);
	*slot = RING_NEXT(*slot, sky2->tx_ring_size);
	re->flags = 0;
	re->skb = NULL;
	le->ctrl = 0;
	le->ctrl = 0;
	return le;
	return le;
}
}
@@ -1021,12 +1024,6 @@ static void tx_init(struct sky2_port *sky2)
	sky2->tx_last_upper = 0;
	sky2->tx_last_upper = 0;
}
}


static inline struct tx_ring_info *tx_le_re(struct sky2_port *sky2,
					    struct sky2_tx_le *le)
{
	return sky2->tx_ring + (le - sky2->tx_le);
}

/* Update chip's next pointer */
/* Update chip's next pointer */
static inline void sky2_put_idx(struct sky2_hw *hw, unsigned q, u16 idx)
static inline void sky2_put_idx(struct sky2_hw *hw, unsigned q, u16 idx)
{
{
@@ -1563,6 +1560,19 @@ static unsigned tx_le_req(const struct sk_buff *skb)
	return count;
	return count;
}
}


static void sky2_tx_unmap(struct pci_dev *pdev,
			  const struct tx_ring_info *re)
{
	if (re->flags & TX_MAP_SINGLE)
		pci_unmap_single(pdev, pci_unmap_addr(re, mapaddr),
				 pci_unmap_len(re, maplen),
				 PCI_DMA_TODEVICE);
	else if (re->flags & TX_MAP_PAGE)
		pci_unmap_page(pdev, pci_unmap_addr(re, mapaddr),
			       pci_unmap_len(re, maplen),
			       PCI_DMA_TODEVICE);
}

/*
/*
 * Put one packet in ring for transmit.
 * Put one packet in ring for transmit.
 * A single packet can generate multiple list elements, and
 * A single packet can generate multiple list elements, and
@@ -1667,16 +1677,17 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
		}
		}
	}
	}


	re = sky2->tx_ring + slot;
	re->flags = TX_MAP_SINGLE;
	pci_unmap_addr_set(re, mapaddr, mapping);
	pci_unmap_len_set(re, maplen, len);

	le = get_tx_le(sky2, &slot);
	le = get_tx_le(sky2, &slot);
	le->addr = cpu_to_le32(lower_32_bits(mapping));
	le->addr = cpu_to_le32(lower_32_bits(mapping));
	le->length = cpu_to_le16(len);
	le->length = cpu_to_le16(len);
	le->ctrl = ctrl;
	le->ctrl = ctrl;
	le->opcode = mss ? (OP_LARGESEND | HW_OWNER) : (OP_PACKET | HW_OWNER);
	le->opcode = mss ? (OP_LARGESEND | HW_OWNER) : (OP_PACKET | HW_OWNER);


	re = tx_le_re(sky2, le);
	re->skb = skb;
	pci_unmap_addr_set(re, mapaddr, mapping);
	pci_unmap_len_set(re, maplen, len);


	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
		const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
		const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
@@ -1695,18 +1706,19 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
			le->opcode = OP_ADDR64 | HW_OWNER;
			le->opcode = OP_ADDR64 | HW_OWNER;
		}
		}


		re = sky2->tx_ring + slot;
		re->flags = TX_MAP_PAGE;
		pci_unmap_addr_set(re, mapaddr, mapping);
		pci_unmap_len_set(re, maplen, frag->size);

		le = get_tx_le(sky2, &slot);
		le = get_tx_le(sky2, &slot);
		le->addr = cpu_to_le32(lower_32_bits(mapping));
		le->addr = cpu_to_le32(lower_32_bits(mapping));
		le->length = cpu_to_le16(frag->size);
		le->length = cpu_to_le16(frag->size);
		le->ctrl = ctrl;
		le->ctrl = ctrl;
		le->opcode = OP_BUFFER | HW_OWNER;
		le->opcode = OP_BUFFER | HW_OWNER;

		re = tx_le_re(sky2, le);
		re->skb = skb;
		pci_unmap_addr_set(re, mapaddr, mapping);
		pci_unmap_len_set(re, maplen, frag->size);
	}
	}


	re->skb = skb;
	le->ctrl |= EOP;
	le->ctrl |= EOP;


	sky2->tx_prod = slot;
	sky2->tx_prod = slot;
@@ -1720,23 +1732,9 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)


mapping_unwind:
mapping_unwind:
	for (i = sky2->tx_prod; i != slot; i = RING_NEXT(i, sky2->tx_ring_size)) {
	for (i = sky2->tx_prod; i != slot; i = RING_NEXT(i, sky2->tx_ring_size)) {
		le = sky2->tx_le + i;
		re = sky2->tx_ring + i;
		re = sky2->tx_ring + i;


		switch(le->opcode & ~HW_OWNER) {
		sky2_tx_unmap(hw->pdev, re);
		case OP_LARGESEND:
		case OP_PACKET:
			pci_unmap_single(hw->pdev,
					 pci_unmap_addr(re, mapaddr),
					 pci_unmap_len(re, maplen),
					 PCI_DMA_TODEVICE);
			break;
		case OP_BUFFER:
			pci_unmap_page(hw->pdev, pci_unmap_addr(re, mapaddr),
				       pci_unmap_len(re, maplen),
				       PCI_DMA_TODEVICE);
			break;
		}
	}
	}


mapping_error:
mapping_error:
@@ -1759,34 +1757,18 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
static void sky2_tx_complete(struct sky2_port *sky2, u16 done)
static void sky2_tx_complete(struct sky2_port *sky2, u16 done)
{
{
	struct net_device *dev = sky2->netdev;
	struct net_device *dev = sky2->netdev;
	struct pci_dev *pdev = sky2->hw->pdev;
	unsigned idx;
	unsigned idx;


	BUG_ON(done >= sky2->tx_ring_size);
	BUG_ON(done >= sky2->tx_ring_size);


	for (idx = sky2->tx_cons; idx != done;
	for (idx = sky2->tx_cons; idx != done;
	     idx = RING_NEXT(idx, sky2->tx_ring_size)) {
	     idx = RING_NEXT(idx, sky2->tx_ring_size)) {
		struct sky2_tx_le *le = sky2->tx_le + idx;
		struct tx_ring_info *re = sky2->tx_ring + idx;
		struct tx_ring_info *re = sky2->tx_ring + idx;

		switch(le->opcode & ~HW_OWNER) {
		case OP_LARGESEND:
		case OP_PACKET:
			pci_unmap_single(pdev,
					 pci_unmap_addr(re, mapaddr),
					 pci_unmap_len(re, maplen),
					 PCI_DMA_TODEVICE);
			break;
		case OP_BUFFER:
			pci_unmap_page(pdev, pci_unmap_addr(re, mapaddr),
				       pci_unmap_len(re, maplen),
				       PCI_DMA_TODEVICE);
			break;
		}

		if (le->ctrl & EOP) {
		struct sk_buff *skb = re->skb;
		struct sk_buff *skb = re->skb;


		sky2_tx_unmap(sky2->hw->pdev, re);

		if (skb) {
			if (unlikely(netif_msg_tx_done(sky2)))
			if (unlikely(netif_msg_tx_done(sky2)))
				printk(KERN_DEBUG "%s: tx done %u\n",
				printk(KERN_DEBUG "%s: tx done %u\n",
				       dev->name, idx);
				       dev->name, idx);
+3 −0
Original line number Original line Diff line number Diff line
@@ -1984,6 +1984,9 @@ struct sky2_status_le {


struct tx_ring_info {
struct tx_ring_info {
	struct sk_buff	*skb;
	struct sk_buff	*skb;
	unsigned long flags;
#define TX_MAP_SINGLE   0x0001
#define TX_MAP_PAGE     000002
	DECLARE_PCI_UNMAP_ADDR(mapaddr);
	DECLARE_PCI_UNMAP_ADDR(mapaddr);
	DECLARE_PCI_UNMAP_LEN(maplen);
	DECLARE_PCI_UNMAP_LEN(maplen);
};
};