summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/spider_net.c86
-rw-r--r--drivers/net/spider_net.h3
2 files changed, 82 insertions, 7 deletions
diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c
index 69005d113d4d..c99980a14203 100644
--- a/drivers/net/spider_net.c
+++ b/drivers/net/spider_net.c
@@ -1051,6 +1051,66 @@ static void show_rx_chain(struct spider_net_card *card)
#endif
/**
+ * spider_net_resync_head_ptr - Advance head ptr past empty descrs
+ *
+ * If the driver fails to keep up and empty the queue, then the
+ * hardware wil run out of room to put incoming packets. This
+ * will cause the hardware to skip descrs that are full (instead
+ * of halting/retrying). Thus, once the driver runs, it wil need
+ * to "catch up" to where the hardware chain pointer is at.
+ */
+static void spider_net_resync_head_ptr(struct spider_net_card *card)
+{
+ unsigned long flags;
+ struct spider_net_descr_chain *chain = &card->rx_chain;
+ struct spider_net_descr *descr;
+ int i, status;
+
+ /* Advance head pointer past any empty descrs */
+ descr = chain->head;
+ status = spider_net_get_descr_status(descr->hwdescr);
+
+ if (status == SPIDER_NET_DESCR_NOT_IN_USE)
+ return;
+
+ spin_lock_irqsave(&chain->lock, flags);
+
+ descr = chain->head;
+ status = spider_net_get_descr_status(descr->hwdescr);
+ for (i=0; i<chain->num_desc; i++) {
+ if (status != SPIDER_NET_DESCR_CARDOWNED) break;
+ descr = descr->next;
+ status = spider_net_get_descr_status(descr->hwdescr);
+ }
+ chain->head = descr;
+
+ spin_unlock_irqrestore(&chain->lock, flags);
+}
+
+static int spider_net_resync_tail_ptr(struct spider_net_card *card)
+{
+ struct spider_net_descr_chain *chain = &card->rx_chain;
+ struct spider_net_descr *descr;
+ int i, status;
+
+ /* Advance tail pointer past any empty and reaped descrs */
+ descr = chain->tail;
+ status = spider_net_get_descr_status(descr->hwdescr);
+
+ for (i=0; i<chain->num_desc; i++) {
+ if ((status != SPIDER_NET_DESCR_CARDOWNED) &&
+ (status != SPIDER_NET_DESCR_NOT_IN_USE)) break;
+ descr = descr->next;
+ status = spider_net_get_descr_status(descr->hwdescr);
+ }
+ chain->tail = descr;
+
+ if ((i == chain->num_desc) || (i == 0))
+ return 1;
+ return 0;
+}
+
+/**
* spider_net_decode_one_descr - processes an RX descriptor
* @card: card structure
*
@@ -1175,6 +1235,12 @@ spider_net_poll(struct net_device *netdev, int *budget)
}
}
+ if ((packets_done == 0) && (card->num_rx_ints != 0)) {
+ no_more_packets = spider_net_resync_tail_ptr(card);
+ spider_net_resync_head_ptr(card);
+ }
+ card->num_rx_ints = 0;
+
netdev->quota -= packets_done;
*budget -= packets_done;
spider_net_refill_rx_chain(card);
@@ -1421,7 +1487,11 @@ spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg)
if (netif_msg_intr(card) && net_ratelimit())
pr_err("Spider RX RAM full, incoming packets "
"might be discarded!\n");
- spider_net_rx_irq_off(card);
+ /* Could happen when rx chain is full */
+ spider_net_resync_head_ptr(card);
+ spider_net_refill_rx_chain(card);
+ spider_net_enable_rxdmac(card);
+ card->num_rx_ints ++;
netif_rx_schedule(card->netdev);
show_error = 0;
break;
@@ -1437,12 +1507,11 @@ spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg)
case SPIDER_NET_GDCDCEINT: /* fallthrough */
case SPIDER_NET_GDBDCEINT: /* fallthrough */
case SPIDER_NET_GDADCEINT:
- if (netif_msg_intr(card) && net_ratelimit())
- pr_err("got descriptor chain end interrupt, "
- "restarting DMAC %c.\n",
- 'D'-(i-SPIDER_NET_GDDDCEINT)/3);
+ spider_net_resync_head_ptr(card);
spider_net_refill_rx_chain(card);
spider_net_enable_rxdmac(card);
+ card->num_rx_ints ++;
+ netif_rx_schedule(card->netdev);
show_error = 0;
break;
@@ -1451,9 +1520,12 @@ spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg)
case SPIDER_NET_GDCINVDINT: /* fallthrough */
case SPIDER_NET_GDBINVDINT: /* fallthrough */
case SPIDER_NET_GDAINVDINT:
- /* could happen when rx chain is full */
+ /* Could happen when rx chain is full */
+ spider_net_resync_head_ptr(card);
spider_net_refill_rx_chain(card);
spider_net_enable_rxdmac(card);
+ card->num_rx_ints ++;
+ netif_rx_schedule(card->netdev);
show_error = 0;
break;
@@ -1546,6 +1618,7 @@ spider_net_interrupt(int irq, void *ptr)
if (status_reg & SPIDER_NET_RXINT ) {
spider_net_rx_irq_off(card);
netif_rx_schedule(netdev);
+ card->num_rx_ints ++;
}
if (status_reg & SPIDER_NET_TXINT)
netif_rx_schedule(netdev);
@@ -2191,6 +2264,7 @@ spider_net_setup_netdev(struct spider_net_card *card)
* NETIF_F_HW_VLAN_FILTER */
netdev->irq = card->pdev->irq;
+ card->num_rx_ints = 0;
dn = pci_device_to_OF_node(card->pdev);
if (!dn)
diff --git a/drivers/net/spider_net.h b/drivers/net/spider_net.h
index 4a1e0d28a502..b620f181227c 100644
--- a/drivers/net/spider_net.h
+++ b/drivers/net/spider_net.h
@@ -25,7 +25,7 @@
#ifndef _SPIDER_NET_H
#define _SPIDER_NET_H
-#define VERSION "2.0 A"
+#define VERSION "2.0 B"
#include "sungem_phy.h"
@@ -461,6 +461,7 @@ struct spider_net_card {
struct work_struct tx_timeout_task;
atomic_t tx_timeout_task_counter;
wait_queue_head_t waitq;
+ int num_rx_ints;
/* for ethtool */
int msg_enable;