By having the receive out-of-memory handling timer schedule the napi
poll handler and then doing oom processing from the napi poll handler,
all code that touches receive state moves to napi context, letting us
get rid of all explicit locking in the receive paths since the only
mutual exclusion we need anymore at that point is protection against
reentering ourselves, which is provided by napi synchronisation.
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
dma_addr_t rx_desc_dma;
int rx_desc_area_size;
struct sk_buff **rx_skb;
dma_addr_t rx_desc_dma;
int rx_desc_area_size;
struct sk_buff **rx_skb;
-
- struct timer_list rx_oom;
u8 rxq_mask;
int rxq_primary;
struct napi_struct napi;
u8 rxq_mask;
int rxq_primary;
struct napi_struct napi;
+ struct timer_list rx_oom;
struct rx_queue rxq[8];
/*
struct rx_queue rxq[8];
/*
/* rx ***********************************************************************/
static void txq_reclaim(struct tx_queue *txq, int force);
/* rx ***********************************************************************/
static void txq_reclaim(struct tx_queue *txq, int force);
-static void rxq_refill(struct rx_queue *rxq)
+static int rxq_refill(struct rx_queue *rxq, int budget, int *oom)
- struct mv643xx_eth_private *mp = rxq_to_mp(rxq);
- unsigned long flags;
+ int skb_size;
+ int refilled;
- spin_lock_irqsave(&mp->lock, flags);
+ /*
+ * Reserve 2+14 bytes for an ethernet header (the hardware
+ * automatically prepends 2 bytes of dummy data to each
+ * received packet), 16 bytes for up to four VLAN tags, and
+ * 4 bytes for the trailing FCS -- 36 bytes total.
+ */
+ skb_size = rxq_to_mp(rxq)->dev->mtu + 36;
+
+ /*
+ * Make sure that the skb size is a multiple of 8 bytes, as
+ * the lower three bits of the receive descriptor's buffer
+ * size field are ignored by the hardware.
+ */
+ skb_size = (skb_size + 7) & ~7;
- while (rxq->rx_desc_count < rxq->rx_ring_size) {
- int skb_size;
+ refilled = 0;
+ while (refilled < budget && rxq->rx_desc_count < rxq->rx_ring_size) {
struct sk_buff *skb;
int unaligned;
int rx;
struct sk_buff *skb;
int unaligned;
int rx;
- /*
- * Reserve 2+14 bytes for an ethernet header (the
- * hardware automatically prepends 2 bytes of dummy
- * data to each received packet), 16 bytes for up to
- * four VLAN tags, and 4 bytes for the trailing FCS
- * -- 36 bytes total.
- */
- skb_size = mp->dev->mtu + 36;
-
- /*
- * Make sure that the skb size is a multiple of 8
- * bytes, as the lower three bits of the receive
- * descriptor's buffer size field are ignored by
- * the hardware.
- */
- skb_size = (skb_size + 7) & ~7;
-
skb = dev_alloc_skb(skb_size + dma_get_cache_alignment() - 1);
skb = dev_alloc_skb(skb_size + dma_get_cache_alignment() - 1);
+ if (skb == NULL) {
+ *oom = 1;
unaligned = (u32)skb->data & (dma_get_cache_alignment() - 1);
if (unaligned)
skb_reserve(skb, dma_get_cache_alignment() - unaligned);
unaligned = (u32)skb->data & (dma_get_cache_alignment() - 1);
if (unaligned)
skb_reserve(skb, dma_get_cache_alignment() - unaligned);
rxq->rx_desc_count++;
rx = rxq->rx_used_desc++;
rxq->rx_desc_count++;
rx = rxq->rx_used_desc++;
- if (rxq->rx_desc_count != rxq->rx_ring_size)
- mod_timer(&rxq->rx_oom, jiffies + (HZ / 10));
-
- spin_unlock_irqrestore(&mp->lock, flags);
-}
-
-static inline void rxq_refill_timer_wrapper(unsigned long data)
-{
- rxq_refill((struct rx_queue *)data);
}
static int rxq_process(struct rx_queue *rxq, int budget)
}
static int rxq_process(struct rx_queue *rxq, int budget)
struct rx_desc *rx_desc;
unsigned int cmd_sts;
struct sk_buff *skb;
struct rx_desc *rx_desc;
unsigned int cmd_sts;
struct sk_buff *skb;
- unsigned long flags;
-
- spin_lock_irqsave(&mp->lock, flags);
rx_desc = &rxq->rx_desc_area[rxq->rx_curr_desc];
cmd_sts = rx_desc->cmd_sts;
rx_desc = &rxq->rx_desc_area[rxq->rx_curr_desc];
cmd_sts = rx_desc->cmd_sts;
- if (cmd_sts & BUFFER_OWNED_BY_DMA) {
- spin_unlock_irqrestore(&mp->lock, flags);
+ if (cmd_sts & BUFFER_OWNED_BY_DMA)
rmb();
skb = rxq->rx_skb[rxq->rx_curr_desc];
rmb();
skb = rxq->rx_skb[rxq->rx_curr_desc];
if (rxq->rx_curr_desc == rxq->rx_ring_size)
rxq->rx_curr_desc = 0;
if (rxq->rx_curr_desc == rxq->rx_ring_size)
rxq->rx_curr_desc = 0;
- spin_unlock_irqrestore(&mp->lock, flags);
-
dma_unmap_single(NULL, rx_desc->buf_ptr,
rx_desc->buf_size, DMA_FROM_DEVICE);
rxq->rx_desc_count--;
dma_unmap_single(NULL, rx_desc->buf_ptr,
rx_desc->buf_size, DMA_FROM_DEVICE);
rxq->rx_desc_count--;
mp->dev->last_rx = jiffies;
}
mp->dev->last_rx = jiffies;
}
return rx;
}
static int mv643xx_eth_poll(struct napi_struct *napi, int budget)
{
struct mv643xx_eth_private *mp;
return rx;
}
static int mv643xx_eth_poll(struct napi_struct *napi, int budget)
{
struct mv643xx_eth_private *mp;
+ int work_done;
+ int oom;
int i;
mp = container_of(napi, struct mv643xx_eth_private, napi);
int i;
mp = container_of(napi, struct mv643xx_eth_private, napi);
- rx = 0;
- for (i = 7; rx < budget && i >= 0; i--)
- if (mp->rxq_mask & (1 << i))
- rx += rxq_process(mp->rxq + i, budget - rx);
+ work_done = 0;
+ oom = 0;
+ for (i = 7; work_done < budget && i >= 0; i--) {
+ if (mp->rxq_mask & (1 << i)) {
+ struct rx_queue *rxq = mp->rxq + i;
+ work_done += rxq_process(rxq, budget - work_done);
+ work_done += rxq_refill(rxq, budget - work_done, &oom);
+ }
+ }
+
+ if (work_done < budget) {
+ if (oom)
+ mod_timer(&mp->rx_oom, jiffies + (HZ / 10));
netif_rx_complete(mp->dev, napi);
wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_EXT);
}
netif_rx_complete(mp->dev, napi);
wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_EXT);
}
+ return work_done;
+}
+
+static inline void oom_timer_wrapper(unsigned long data)
+{
+ struct mv643xx_eth_private *mp = (void *)data;
+
+ napi_schedule(&mp->napi);
nexti * sizeof(struct rx_desc);
}
nexti * sizeof(struct rx_desc);
}
- init_timer(&rxq->rx_oom);
- rxq->rx_oom.data = (unsigned long)rxq;
- rxq->rx_oom.function = rxq_refill_timer_wrapper;
-
- del_timer_sync(&rxq->rx_oom);
-
for (i = 0; i < rxq->rx_ring_size; i++) {
if (rxq->rx_skb[i]) {
dev_kfree_skb(rxq->rx_skb[i]);
for (i = 0; i < rxq->rx_ring_size; i++) {
if (rxq->rx_skb[i]) {
dev_kfree_skb(rxq->rx_skb[i]);
wrl(mp, INT_MASK(mp->port_num), 0x00000000);
rdl(mp, INT_MASK(mp->port_num));
wrl(mp, INT_MASK(mp->port_num), 0x00000000);
rdl(mp, INT_MASK(mp->port_num));
- netif_rx_schedule(dev, &mp->napi);
+ napi_schedule(&mp->napi);
{
struct mv643xx_eth_private *mp = netdev_priv(dev);
int err;
{
struct mv643xx_eth_private *mp = netdev_priv(dev);
int err;
int i;
wrl(mp, INT_CAUSE(mp->port_num), 0);
int i;
wrl(mp, INT_CAUSE(mp->port_num), 0);
+ napi_enable(&mp->napi);
+
+ oom = 0;
for (i = 0; i < 8; i++) {
if ((mp->rxq_mask & (1 << i)) == 0)
continue;
for (i = 0; i < 8; i++) {
if ((mp->rxq_mask & (1 << i)) == 0)
continue;
- rxq_refill(mp->rxq + i);
+ rxq_refill(mp->rxq + i, INT_MAX, &oom);
+ }
+
+ if (oom) {
+ mp->rx_oom.expires = jiffies + (HZ / 10);
+ add_timer(&mp->rx_oom);
}
for (i = 0; i < 8; i++) {
}
for (i = 0; i < 8; i++) {
- napi_enable(&mp->napi);
-
netif_carrier_off(dev);
netif_stop_queue(dev);
netif_carrier_off(dev);
netif_stop_queue(dev);
+ del_timer_sync(&mp->rx_oom);
+
netif_carrier_off(dev);
netif_stop_queue(dev);
netif_carrier_off(dev);
netif_stop_queue(dev);
- netif_napi_add(dev, &mp->napi, mv643xx_eth_poll, 64);
-
set_params(mp, pd);
spin_lock_init(&mp->lock);
set_params(mp, pd);
spin_lock_init(&mp->lock);
}
init_pscr(mp, pd->speed, pd->duplex);
}
init_pscr(mp, pd->speed, pd->duplex);
+ netif_napi_add(dev, &mp->napi, mv643xx_eth_poll, 128);
+
+ init_timer(&mp->rx_oom);
+ mp->rx_oom.data = (unsigned long)mp;
+ mp->rx_oom.function = oom_timer_wrapper;
+
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
BUG_ON(!res);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
BUG_ON(!res);