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

Commit 19a33d4e authored by Stephen Hemminger's avatar Stephen Hemminger Committed by Jeff Garzik
Browse files

[PATCH] skge: Rx buffer optimization



Optimize the receive buffer management code to replenish the
buffers immediately (like tg3).

Signed-off-by: default avatarStephen Hemmminger <shemminger@osdl.org>
parent d25f5a67
Loading
Loading
Loading
Loading
+162 −103
Original line number Original line Diff line number Diff line
@@ -49,6 +49,8 @@
#define DEFAULT_RX_RING_SIZE	512
#define DEFAULT_RX_RING_SIZE	512
#define MAX_TX_RING_SIZE	1024
#define MAX_TX_RING_SIZE	1024
#define MAX_RX_RING_SIZE	4096
#define MAX_RX_RING_SIZE	4096
#define RX_COPY_THRESHOLD	128
#define RX_BUF_SIZE		1536
#define PHY_RETRIES	        1000
#define PHY_RETRIES	        1000
#define ETH_JUMBO_MTU		9000
#define ETH_JUMBO_MTU		9000
#define TX_WATCHDOG		(5 * HZ)
#define TX_WATCHDOG		(5 * HZ)
@@ -746,6 +748,7 @@ static int skge_ring_alloc(struct skge_ring *ring, void *vaddr, u64 base)


	for (i = 0, e = ring->start, d = vaddr; i < ring->count; i++, e++, d++) {
	for (i = 0, e = ring->start, d = vaddr; i < ring->count; i++, e++, d++) {
		e->desc = d;
		e->desc = d;
		e->skb = NULL;
		if (i == ring->count - 1) {
		if (i == ring->count - 1) {
			e->next = ring->start;
			e->next = ring->start;
			d->next_offset = base;
			d->next_offset = base;
@@ -759,24 +762,23 @@ static int skge_ring_alloc(struct skge_ring *ring, void *vaddr, u64 base)
	return 0;
	return 0;
}
}


/* Setup buffer for receiving */
static struct sk_buff *skge_rx_alloc(struct net_device *dev, unsigned int size)
static inline int skge_rx_alloc(struct skge_port *skge,
				struct skge_element *e)
{
{
	unsigned long bufsize = skge->netdev->mtu + ETH_HLEN; /* VLAN? */
	struct sk_buff *skb = dev_alloc_skb(size);
	struct skge_rx_desc *rd = e->desc;
	struct sk_buff *skb;
	u64 map;


	skb = dev_alloc_skb(bufsize + NET_IP_ALIGN);
	if (likely(skb)) {
	if (unlikely(!skb)) {
		skb->dev = dev;
		printk(KERN_DEBUG PFX "%s: out of memory for receive\n",
		skb_reserve(skb, NET_IP_ALIGN);
		       skge->netdev->name);
	}
		return -ENOMEM;
	return skb;
}
}


	skb->dev = skge->netdev;
/* Allocate and setup a new buffer for receiving */
	skb_reserve(skb, NET_IP_ALIGN);
static void skge_rx_setup(struct skge_port *skge, struct skge_element *e,
			  struct sk_buff *skb, unsigned int bufsize)
{
	struct skge_rx_desc *rd = e->desc;
	u64 map;


	map = pci_map_single(skge->hw->pdev, skb->data, bufsize,
	map = pci_map_single(skge->hw->pdev, skb->data, bufsize,
			     PCI_DMA_FROMDEVICE);
			     PCI_DMA_FROMDEVICE);
@@ -794,20 +796,37 @@ static inline int skge_rx_alloc(struct skge_port *skge,
	rd->control = BMU_OWN | BMU_STF | BMU_IRQ_EOF | BMU_TCP_CHECK | bufsize;
	rd->control = BMU_OWN | BMU_STF | BMU_IRQ_EOF | BMU_TCP_CHECK | bufsize;
	pci_unmap_addr_set(e, mapaddr, map);
	pci_unmap_addr_set(e, mapaddr, map);
	pci_unmap_len_set(e, maplen, bufsize);
	pci_unmap_len_set(e, maplen, bufsize);
	return 0;
}
}


/* Free all unused buffers in receive ring, assumes receiver stopped */
/* Resume receiving using existing skb,
 * Note: DMA address is not changed by chip.
 * 	 MTU not changed while receiver active.
 */
static void skge_rx_reuse(struct skge_element *e, unsigned int size)
{
	struct skge_rx_desc *rd = e->desc;

	rd->csum2 = 0;
	rd->csum2_start = ETH_HLEN;

	wmb();

	rd->control = BMU_OWN | BMU_STF | BMU_IRQ_EOF | BMU_TCP_CHECK | size;
}


/* Free all  buffers in receive ring, assumes receiver stopped */
static void skge_rx_clean(struct skge_port *skge)
static void skge_rx_clean(struct skge_port *skge)
{
{
	struct skge_hw *hw = skge->hw;
	struct skge_hw *hw = skge->hw;
	struct skge_ring *ring = &skge->rx_ring;
	struct skge_ring *ring = &skge->rx_ring;
	struct skge_element *e;
	struct skge_element *e;


	for (e = ring->to_clean; e != ring->to_use; e = e->next) {
	e = ring->start;
	do {
		struct skge_rx_desc *rd = e->desc;
		struct skge_rx_desc *rd = e->desc;
		rd->control = 0;
		rd->control = 0;

		if (e->skb) {
			pci_unmap_single(hw->pdev,
			pci_unmap_single(hw->pdev,
					 pci_unmap_addr(e, mapaddr),
					 pci_unmap_addr(e, mapaddr),
					 pci_unmap_len(e, maplen),
					 pci_unmap_len(e, maplen),
@@ -815,34 +834,31 @@ static void skge_rx_clean(struct skge_port *skge)
			dev_kfree_skb(e->skb);
			dev_kfree_skb(e->skb);
			e->skb = NULL;
			e->skb = NULL;
		}
		}
	ring->to_clean = e;
	} while ((e = e->next) != ring->start);
}
}



/* Allocate buffers for receive ring
/* Allocate buffers for receive ring
 * For receive: to_use   is refill location
 * For receive:  to_clean is next received frame.
 *              to_clean is next received frame.
 *
 * if (to_use == to_clean)
 *	 then ring all frames in ring need buffers
 * if (to_use->next == to_clean)
 *	 then ring all frames in ring have buffers
 */
 */
static int skge_rx_fill(struct skge_port *skge)
static int skge_rx_fill(struct skge_port *skge)
{
{
	struct skge_ring *ring = &skge->rx_ring;
	struct skge_ring *ring = &skge->rx_ring;
	struct skge_element *e;
	struct skge_element *e;
	int ret = 0;
	unsigned int bufsize = skge->rx_buf_size;


	for (e = ring->to_use; e->next != ring->to_clean; e = e->next) {
	e = ring->start;
		if (skge_rx_alloc(skge, e)) {
	do {
			ret = 1;
		struct sk_buff *skb = skge_rx_alloc(skge->netdev, bufsize);
			break;
		}


	}
		if (!skb)
	ring->to_use = e;
			return -ENOMEM;

		skge_rx_setup(skge, e, skb, bufsize);
	} while ( (e = e->next) != ring->start);


	return ret;
	ring->to_clean = ring->start;
	return 0;
}
}


static void skge_link_up(struct skge_port *skge)
static void skge_link_up(struct skge_port *skge)
@@ -2048,6 +2064,12 @@ static int skge_up(struct net_device *dev)
	if (netif_msg_ifup(skge))
	if (netif_msg_ifup(skge))
		printk(KERN_INFO PFX "%s: enabling interface\n", dev->name);
		printk(KERN_INFO PFX "%s: enabling interface\n", dev->name);


	if (dev->mtu > RX_BUF_SIZE)
		skge->rx_buf_size = dev->mtu + ETH_HLEN + NET_IP_ALIGN;
	else
		skge->rx_buf_size = RX_BUF_SIZE;


	rx_size = skge->rx_ring.count * sizeof(struct skge_rx_desc);
	rx_size = skge->rx_ring.count * sizeof(struct skge_rx_desc);
	tx_size = skge->tx_ring.count * sizeof(struct skge_tx_desc);
	tx_size = skge->tx_ring.count * sizeof(struct skge_tx_desc);
	skge->mem_size = tx_size + rx_size;
	skge->mem_size = tx_size + rx_size;
@@ -2060,7 +2082,8 @@ static int skge_up(struct net_device *dev)
	if ((err = skge_ring_alloc(&skge->rx_ring, skge->mem, skge->dma)))
	if ((err = skge_ring_alloc(&skge->rx_ring, skge->mem, skge->dma)))
		goto free_pci_mem;
		goto free_pci_mem;


	if (skge_rx_fill(skge))
	err = skge_rx_fill(skge);
	if (err)
		goto free_rx_ring;
		goto free_rx_ring;


	if ((err = skge_ring_alloc(&skge->tx_ring, skge->mem + rx_size,
	if ((err = skge_ring_alloc(&skge->tx_ring, skge->mem + rx_size,
@@ -2284,6 +2307,7 @@ static int skge_xmit_frame(struct sk_buff *skb, struct net_device *dev)


static inline void skge_tx_free(struct skge_hw *hw, struct skge_element *e)
static inline void skge_tx_free(struct skge_hw *hw, struct skge_element *e)
{
{
	/* This ring element can be skb or fragment */
	if (e->skb) {
	if (e->skb) {
		pci_unmap_single(hw->pdev,
		pci_unmap_single(hw->pdev,
			       pci_unmap_addr(e, mapaddr),
			       pci_unmap_addr(e, mapaddr),
@@ -2328,16 +2352,17 @@ static void skge_tx_timeout(struct net_device *dev)
static int skge_change_mtu(struct net_device *dev, int new_mtu)
static int skge_change_mtu(struct net_device *dev, int new_mtu)
{
{
	int err = 0;
	int err = 0;
	int running = netif_running(dev);


	if (new_mtu < ETH_ZLEN || new_mtu > ETH_JUMBO_MTU)
	if (new_mtu < ETH_ZLEN || new_mtu > ETH_JUMBO_MTU)
		return -EINVAL;
		return -EINVAL;


	dev->mtu = new_mtu;


	if (netif_running(dev)) {
	if (running)
		skge_down(dev);
		skge_down(dev);
	dev->mtu = new_mtu;
	if (running)
		skge_up(dev);
		skge_up(dev);
	}


	return err;
	return err;
}
}
@@ -2436,11 +2461,9 @@ static void skge_rx_error(struct skge_port *skge, int slot,
		printk(KERN_DEBUG PFX "%s: rx err, slot %d control 0x%x status 0x%x\n",
		printk(KERN_DEBUG PFX "%s: rx err, slot %d control 0x%x status 0x%x\n",
		       skge->netdev->name, slot, control, status);
		       skge->netdev->name, slot, control, status);


	if ((control & (BMU_EOF|BMU_STF)) != (BMU_STF|BMU_EOF)
	if ((control & (BMU_EOF|BMU_STF)) != (BMU_STF|BMU_EOF))
	    || (control & BMU_BBC) > skge->netdev->mtu + VLAN_ETH_HLEN)
		skge->net_stats.rx_length_errors++;
		skge->net_stats.rx_length_errors++;
	else {
	else if (skge->hw->chip_id == CHIP_ID_GENESIS) {
		if (skge->hw->chip_id == CHIP_ID_GENESIS) {
		if (status & (XMR_FS_RUNT|XMR_FS_LNG_ERR))
		if (status & (XMR_FS_RUNT|XMR_FS_LNG_ERR))
			skge->net_stats.rx_length_errors++;
			skge->net_stats.rx_length_errors++;
		if (status & XMR_FS_FRA_ERR)
		if (status & XMR_FS_FRA_ERR)
@@ -2456,8 +2479,58 @@ static void skge_rx_error(struct skge_port *skge, int slot,
			skge->net_stats.rx_crc_errors++;
			skge->net_stats.rx_crc_errors++;
	}
	}
}
}

/* Get receive buffer from descriptor.
 * Handles copy of small buffers and reallocation failures
 */
static inline struct sk_buff *skge_rx_get(struct skge_port *skge,
					  struct skge_element *e,
					  unsigned int len)
{
	struct sk_buff *nskb, *skb;

	if (len < RX_COPY_THRESHOLD) {
		nskb = skge_rx_alloc(skge->netdev, len + NET_IP_ALIGN);
		if (unlikely(!nskb))
			return NULL;

		pci_dma_sync_single_for_cpu(skge->hw->pdev,
					    pci_unmap_addr(e, mapaddr),
					    len, PCI_DMA_FROMDEVICE);
		memcpy(nskb->data, e->skb->data, len);
		pci_dma_sync_single_for_device(skge->hw->pdev,
					       pci_unmap_addr(e, mapaddr),
					       len, PCI_DMA_FROMDEVICE);

		if (skge->rx_csum) {
			struct skge_rx_desc *rd = e->desc;
			nskb->csum = le16_to_cpu(rd->csum2);
			nskb->ip_summed = CHECKSUM_HW;
		}
		skge_rx_reuse(e, skge->rx_buf_size);
		return nskb;
	} else {
		nskb = skge_rx_alloc(skge->netdev, skge->rx_buf_size);
		if (unlikely(!nskb))
			return NULL;

		pci_unmap_single(skge->hw->pdev,
				 pci_unmap_addr(e, mapaddr),
				 pci_unmap_len(e, maplen),
				 PCI_DMA_FROMDEVICE);
		skb = e->skb;
		if (skge->rx_csum) {
			struct skge_rx_desc *rd = e->desc;
			skb->csum = le16_to_cpu(rd->csum2);
			skb->ip_summed = CHECKSUM_HW;
		}

		skge_rx_setup(skge, e, nskb, skge->rx_buf_size);
		return skb;
	}
}
}



static int skge_poll(struct net_device *dev, int *budget)
static int skge_poll(struct net_device *dev, int *budget)
{
{
	struct skge_port *skge = netdev_priv(dev);
	struct skge_port *skge = netdev_priv(dev);
@@ -2466,14 +2539,12 @@ static int skge_poll(struct net_device *dev, int *budget)
	struct skge_element *e;
	struct skge_element *e;
	unsigned int to_do = min(dev->quota, *budget);
	unsigned int to_do = min(dev->quota, *budget);
	unsigned int work_done = 0;
	unsigned int work_done = 0;
	int done;


	pr_debug("skge_poll\n");
	pr_debug("skge_poll\n");


	for (e = ring->to_clean; e != ring->to_use && work_done < to_do;
	for (e = ring->to_clean; work_done < to_do; e = e->next) {
	     e = e->next) {
		struct skge_rx_desc *rd = e->desc;
		struct skge_rx_desc *rd = e->desc;
		struct sk_buff *skb = e->skb;
		struct sk_buff *skb;
		u32 control, len, status;
		u32 control, len, status;


		rmb();
		rmb();
@@ -2482,19 +2553,12 @@ static int skge_poll(struct net_device *dev, int *budget)
			break;
			break;


		len = control & BMU_BBC;
		len = control & BMU_BBC;
		e->skb = NULL;

		pci_unmap_single(hw->pdev,
				 pci_unmap_addr(e, mapaddr),
				 pci_unmap_len(e, maplen),
				 PCI_DMA_FROMDEVICE);

		status = rd->status;
		status = rd->status;
		if ((control & (BMU_EOF|BMU_STF)) != (BMU_STF|BMU_EOF)

		     || len > dev->mtu + VLAN_ETH_HLEN
		if (unlikely((control & (BMU_EOF|BMU_STF)) != (BMU_STF|BMU_EOF)
		     || bad_phy_status(hw, status)) {
			     || bad_phy_status(hw, status))) {
			skge_rx_error(skge, e - ring->start, control, status);
			skge_rx_error(skge, e - ring->start, control, status);
			dev_kfree_skb(skb);
			skge_rx_reuse(e, skge->rx_buf_size);
			continue;
			continue;
		}
		}


@@ -2502,42 +2566,37 @@ static int skge_poll(struct net_device *dev, int *budget)
		    printk(KERN_DEBUG PFX "%s: rx slot %td status 0x%x len %d\n",
		    printk(KERN_DEBUG PFX "%s: rx slot %td status 0x%x len %d\n",
			   dev->name, e - ring->start, rd->status, len);
			   dev->name, e - ring->start, rd->status, len);


		skb = skge_rx_get(skge, e, len);
		if (likely(skb)) {
			skb_put(skb, len);
			skb_put(skb, len);
			skb->protocol = eth_type_trans(skb, dev);
			skb->protocol = eth_type_trans(skb, dev);


		if (skge->rx_csum) {
			skb->csum = le16_to_cpu(rd->csum2);
			skb->ip_summed = CHECKSUM_HW;
		}

			dev->last_rx = jiffies;
			dev->last_rx = jiffies;
			netif_receive_skb(skb);
			netif_receive_skb(skb);


			++work_done;
			++work_done;
		} else
			skge_rx_reuse(e, skge->rx_buf_size);
	}
	}
	ring->to_clean = e;
	ring->to_clean = e;


	*budget -= work_done;
	dev->quota -= work_done;
	done = work_done < to_do;

	if (skge_rx_fill(skge))
		done = 0;

	/* restart receiver */
	/* restart receiver */
	wmb();
	wmb();
	skge_write8(hw, Q_ADDR(rxqaddr[skge->port], Q_CSR),
	skge_write8(hw, Q_ADDR(rxqaddr[skge->port], Q_CSR),
		    CSR_START | CSR_IRQ_CL_F);
		    CSR_START | CSR_IRQ_CL_F);


	if (done) {
	*budget -= work_done;
	dev->quota -= work_done;

	if (work_done >=  to_do)
		return 1; /* not done */

	local_irq_disable();
	local_irq_disable();
	__netif_rx_complete(dev);
	__netif_rx_complete(dev);
	hw->intr_mask |= portirqmask[skge->port];
	hw->intr_mask |= portirqmask[skge->port];
	skge_write32(hw, B0_IMSK, hw->intr_mask);
	skge_write32(hw, B0_IMSK, hw->intr_mask);
	local_irq_enable();
	local_irq_enable();
	}
	return 0;

	return !done;
}
}


static inline void skge_tx_intr(struct net_device *dev)
static inline void skge_tx_intr(struct net_device *dev)
+1 −0
Original line number Original line Diff line number Diff line
@@ -2503,6 +2503,7 @@ struct skge_port {
	void		     *mem;	/* PCI memory for rings */
	void		     *mem;	/* PCI memory for rings */
	dma_addr_t	     dma;
	dma_addr_t	     dma;
	unsigned long	     mem_size;
	unsigned long	     mem_size;
	unsigned int	     rx_buf_size;


	struct timer_list    led_blink;
	struct timer_list    led_blink;
};
};