[PATCH] skge: Rx buffer optimization
Optimize the receive buffer management code to replenish the buffers immediately (like tg3). Signed-off-by: Stephen Hemmminger <shemminger@osdl.org>
This commit is contained in:
parent
d25f5a6774
commit
19a33d4e6b
|
@ -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;
|
||||||
|
|
||||||
return ret;
|
skge_rx_setup(skge, e, skb, bufsize);
|
||||||
|
} while ( (e = e->next) != ring->start);
|
||||||
|
|
||||||
|
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,7 +2479,57 @@ 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)
|
||||||
{
|
{
|
||||||
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue