summaryrefslogtreecommitdiff
path: root/drivers/net/spider_net.c
diff options
context:
space:
mode:
authorLinas Vepstas <linas@austin.ibm.com>2006-10-10 16:11:33 -0500
committerJeff Garzik <jeff@garzik.org>2006-10-11 04:04:26 -0400
commit204e5fa17c7ba45a89989f8da6dfe8e54d64b79b (patch)
treee69b3c2989839bac94718c6be16dd1433da30c12 /drivers/net/spider_net.c
parentb21606a773faffc2b3ec326325c433bdf37ecbdf (diff)
downloadlwn-204e5fa17c7ba45a89989f8da6dfe8e54d64b79b.tar.gz
lwn-204e5fa17c7ba45a89989f8da6dfe8e54d64b79b.zip
[PATCH] powerpc/cell spidernet low watermark patch.
Implement basic low-watermark support for the transmit queue. Hardware low-watermarks allow a properly configured kernel to continously stream data to a device and not have to handle any interrupts at all in doing so. Correct zero-interrupt operation can be actually observed for this driver, when the socket buffer is made large enough. The basic idea of a low-watermark interrupt is as follows. The device driver queues up a bunch of packets for the hardware to transmit, and then kicks the hardware to get it started. As the hardware drains the queue of pending, untransmitted packets, the device driver will want to know when the queue is almost empty, so that it can queue some more packets. If the queue drains down to the low waterark, then an interrupt will be generated. However, if the kernel/driver continues to add enough packets to keep the queue partially filled, no interrupt will actually be generated, and the hardware can continue streaming packets indefinitely in this mode. The impelmentation is done by setting the DESCR_TXDESFLG flag in one of the packets. When the hardware sees this flag, it will interrupt the device driver. Because this flag is on a fixed packet, rather than at fixed location in the queue, the code below needs to move the flag as more packets are queued up. This implementation attempts to keep the flag at about 1/4 from "empty". Signed-off-by: Linas Vepstas <linas@austin.ibm.com> Signed-off-by: James K Lewis <jklewis@us.ibm.com> Acked-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/net/spider_net.c')
-rw-r--r--drivers/net/spider_net.c43
1 files changed, 43 insertions, 0 deletions
diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c
index d779a0b9d456..96b5d00c21da 100644
--- a/drivers/net/spider_net.c
+++ b/drivers/net/spider_net.c
@@ -684,6 +684,7 @@ spider_net_prepare_tx_descr(struct spider_net_card *card,
break;
}
+ /* Chain the bus address, so that the DMA engine finds this descr. */
descr->prev->next_descr_addr = descr->bus_addr;
card->netdev->trans_start = jiffies; /* set netdev watchdog timer */
@@ -717,6 +718,41 @@ spider_net_release_tx_descr(struct spider_net_card *card)
dev_kfree_skb_any(skb);
}
+static void
+spider_net_set_low_watermark(struct spider_net_card *card)
+{
+ int status;
+ int cnt=0;
+ int i;
+ struct spider_net_descr *descr = card->tx_chain.tail;
+
+ /* Measure the length of the queue. */
+ while (descr != card->tx_chain.head) {
+ status = descr->dmac_cmd_status & SPIDER_NET_DESCR_NOT_IN_USE;
+ if (status == SPIDER_NET_DESCR_NOT_IN_USE)
+ break;
+ descr = descr->next;
+ cnt++;
+ }
+
+ /* If TX queue is short, don't even bother with interrupts */
+ if (cnt < card->tx_desc/4)
+ return;
+
+ /* Set low-watermark 3/4th's of the way into the queue. */
+ descr = card->tx_chain.tail;
+ cnt = (cnt*3)/4;
+ for (i=0;i<cnt; i++)
+ descr = descr->next;
+
+ /* Set the new watermark, clear the old watermark */
+ descr->dmac_cmd_status |= SPIDER_NET_DESCR_TXDESFLG;
+ if (card->low_watermark && card->low_watermark != descr)
+ card->low_watermark->dmac_cmd_status =
+ card->low_watermark->dmac_cmd_status & ~SPIDER_NET_DESCR_TXDESFLG;
+ card->low_watermark = descr;
+}
+
/**
* spider_net_release_tx_chain - processes sent tx descriptors
* @card: adapter structure
@@ -838,6 +874,7 @@ spider_net_xmit(struct sk_buff *skb, struct net_device *netdev)
return NETDEV_TX_BUSY;
}
+ spider_net_set_low_watermark(card);
spider_net_kick_tx_dma(card);
card->tx_chain.head = card->tx_chain.head->next;
spin_unlock_irqrestore(&chain->lock, flags);
@@ -1467,6 +1504,10 @@ spider_net_interrupt(int irq, void *ptr)
spider_net_rx_irq_off(card);
netif_rx_schedule(netdev);
}
+ if (status_reg & SPIDER_NET_TXINT ) {
+ spider_net_cleanup_tx_ring(card);
+ netif_wake_queue(netdev);
+ }
if (status_reg & SPIDER_NET_ERRINT )
spider_net_handle_error_irq(card, status_reg);
@@ -1629,6 +1670,8 @@ spider_net_open(struct net_device *netdev)
PCI_DMA_TODEVICE, card->tx_desc))
goto alloc_tx_failed;
+ card->low_watermark = NULL;
+
/* rx_chain is after tx_chain, so offset is descr + tx_count */
if (spider_net_init_chain(card, &card->rx_chain,
card->descr + card->tx_desc,