X-Git-Url: http://git.rohieb.name/openwrt.git/blobdiff_plain/309e8eb4759e4a0fd6e0b0424fccb18a04f9b545..3bce36abcac07acebbd3cc1ddaf583c599a2cd4e:/target/linux/ar7/files/drivers/net/cpmac.c diff --git a/target/linux/ar7/files/drivers/net/cpmac.c b/target/linux/ar7/files/drivers/net/cpmac.c index 1d0ba11d2..4b6bfe1be 100644 --- a/target/linux/ar7/files/drivers/net/cpmac.c +++ b/target/linux/ar7/files/drivers/net/cpmac.c @@ -37,6 +37,7 @@ #include #include #include +#include MODULE_AUTHOR("Eugene Konev "); MODULE_DESCRIPTION("TI AR7 ethernet driver (CPMAC)"); @@ -191,6 +192,7 @@ struct cpmac_desc { #define CPMAC_EOQ 0x1000 struct sk_buff *skb; struct cpmac_desc *next; + struct cpmac_desc *prev; dma_addr_t mapping; dma_addr_t data_mapping; }; @@ -211,6 +213,7 @@ struct cpmac_priv { struct net_device *dev; struct work_struct reset_work; struct platform_device *pdev; + atomic_t reset_pending; }; static irqreturn_t cpmac_irq(int, void *); @@ -244,6 +247,16 @@ static void cpmac_dump_desc(struct net_device *dev, struct cpmac_desc *desc) printk("\n"); } +static void cpmac_dump_all_desc(struct net_device *dev) +{ + struct cpmac_priv *priv = netdev_priv(dev); + struct cpmac_desc *dump = priv->rx_head; + do { + cpmac_dump_desc(dev, dump); + dump = dump->next; + } while (dump != priv->rx_head); +} + static void cpmac_dump_skb(struct net_device *dev, struct sk_buff *skb) { int i; @@ -414,8 +427,8 @@ static struct sk_buff *cpmac_rx_one(struct net_device *dev, static int cpmac_poll(struct net_device *dev, int *budget) { struct sk_buff *skb; - struct cpmac_desc *desc; - int received = 0, quota = min(dev->quota, *budget); + struct cpmac_desc *desc, *restart; + int received = 0, processed = 0, quota = min(dev->quota, *budget); struct cpmac_priv *priv = netdev_priv(dev); spin_lock(&priv->rx_lock); @@ -423,12 +436,31 @@ static int cpmac_poll(struct net_device *dev, int *budget) if (netif_msg_rx_err(priv) && net_ratelimit()) printk(KERN_WARNING "%s: rx: polling, but no queue\n", dev->name); + spin_unlock(&priv->rx_lock); netif_rx_complete(dev); return 0; } desc = priv->rx_head; + restart = NULL; while ((received < quota) && ((desc->dataflags & CPMAC_OWN) == 0)) { + processed++; + + if ((desc->dataflags & CPMAC_EOQ) != 0) { + /* The last update to eoq->hw_next didn't happen soon enough, and the + * receiver stopped here. Remember this descriptor so we can restart + * the receiver after freeing some space. + */ + if (unlikely(restart)) { + if (netif_msg_rx_err(priv)) + printk(KERN_ERR "%s: poll found a duplicate EOQ: %p and %p\n", + dev->name, restart, desc); + goto fatal_error; + } + + restart = desc->next; + } + skb = cpmac_rx_one(dev, priv, desc); if (likely(skb)) { netif_receive_skb(skb); @@ -436,6 +468,44 @@ static int cpmac_poll(struct net_device *dev, int *budget) } desc = desc->next; } + + if (desc != priv->rx_head) { + /* We freed some buffers, but not the whole ring, add what we did free to the rx list */ + desc->prev->hw_next = (u32)0; + priv->rx_head->prev->hw_next = priv->rx_head->mapping; + } + + /* Optimization: If we did not actually process an EOQ (perhaps because of + * quota limits), check to see if the tail of the queue has EOQ set. We + * should immediately restart in that case so that the receiver can restart + * and run in parallel with more packet processing. This lets us handle slightly + * larger bursts before running out of ring space (assuming dev->weight < ring_size) + */ + if (!restart && + (priv->rx_head->prev->dataflags & (CPMAC_OWN|CPMAC_EOQ)) == CPMAC_EOQ && + (priv->rx_head->dataflags & CPMAC_OWN) != 0) { + /* reset EOQ so the poll loop (above) doesn't try to restart this when it + * eventually gets to this descriptor. + */ + priv->rx_head->prev->dataflags &= ~CPMAC_EOQ; + restart = priv->rx_head; + } + + if (restart) { + dev->stats.rx_errors++; + dev->stats.rx_fifo_errors++; + if (netif_msg_rx_err(priv) && net_ratelimit()) + printk(KERN_WARNING "%s: rx dma ring overrun\n", dev->name); + + if (unlikely((restart->dataflags & CPMAC_OWN) == 0)) { + if (netif_msg_drv(priv)) + printk(KERN_ERR "%s: cpmac_poll is trying to restart rx from a descriptor that's not free: %p\n", + dev->name, restart); + goto fatal_error; + } + + cpmac_write(priv->regs, CPMAC_RX_PTR(0), restart->mapping); + } priv->rx_head = desc; spin_unlock(&priv->rx_lock); @@ -444,14 +514,37 @@ static int cpmac_poll(struct net_device *dev, int *budget) if (unlikely(netif_msg_rx_status(priv))) printk(KERN_DEBUG "%s: poll processed %d packets\n", dev->name, received); - if (desc->dataflags & CPMAC_OWN) { + + if (processed == 0) { + /* we ran out of packets to read, revert to interrupt-driven mode */ netif_rx_complete(dev); - cpmac_write(priv->regs, CPMAC_RX_PTR(0), (u32)desc->mapping); cpmac_write(priv->regs, CPMAC_RX_INT_ENABLE, 1); return 0; } return 1; + +fatal_error: + /* Something went horribly wrong. Reset hardware to try to recover rather than wedging. */ + + if (netif_msg_drv(priv)) { + printk(KERN_ERR "%s: cpmac_poll is confused. Resetting hardware\n", dev->name); + cpmac_dump_all_desc(dev); + printk(KERN_DEBUG "%s: RX_PTR(0)=0x%08x RX_ACK(0)=0x%08x\n", + dev->name, + cpmac_read(priv->regs, CPMAC_RX_PTR(0)), + cpmac_read(priv->regs, CPMAC_RX_ACK(0))); + } + + spin_unlock(&priv->rx_lock); + netif_rx_complete(dev); + netif_stop_queue(dev); + + atomic_inc(&priv->reset_pending); + cpmac_hw_stop(dev); + if (!schedule_work(&priv->reset_work)) + atomic_dec(&priv->reset_pending); + return 0; } static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev) @@ -460,15 +553,11 @@ static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev) struct cpmac_desc *desc; struct cpmac_priv *priv = netdev_priv(dev); - if (unlikely(skb_padto(skb, ETH_ZLEN))) { - if (netif_msg_tx_err(priv) && net_ratelimit()) - printk(KERN_WARNING - "%s: tx: padding failed, dropping\n", dev->name); - spin_lock(&priv->lock); - dev->stats.tx_dropped++; - spin_unlock(&priv->lock); - return -ENOMEM; - } + if (unlikely(atomic_read(&priv->reset_pending))) + return NETDEV_TX_BUSY; + + if (unlikely(skb_padto(skb, ETH_ZLEN))) + return NETDEV_TX_OK; len = max(skb->len, ETH_ZLEN); queue = skb->queue_mapping; @@ -481,13 +570,9 @@ static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev) desc = &priv->desc_ring[queue]; if (unlikely(desc->dataflags & CPMAC_OWN)) { if (netif_msg_tx_err(priv) && net_ratelimit()) - printk(KERN_WARNING "%s: tx dma ring full, dropping\n", + printk(KERN_WARNING "%s: tx dma ring full\n", dev->name); - spin_lock(&priv->lock); - dev->stats.tx_dropped++; - spin_unlock(&priv->lock); - dev_kfree_skb_any(skb); - return -ENOMEM; + return NETDEV_TX_BUSY; } spin_lock(&priv->lock); @@ -509,7 +594,7 @@ static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev) cpmac_dump_skb(dev, skb); cpmac_write(priv->regs, CPMAC_TX_PTR(queue), (u32)desc->mapping); - return 0; + return NETDEV_TX_OK; } static void cpmac_end_xmit(struct net_device *dev, int queue) @@ -636,8 +721,11 @@ static void cpmac_clear_rx(struct net_device *dev) desc->dataflags = CPMAC_OWN; dev->stats.rx_dropped++; } + desc->hw_next = desc->next->mapping; desc = desc->next; } + + priv->rx_head->prev->hw_next = 0; } static void cpmac_clear_tx(struct net_device *dev) @@ -646,16 +734,18 @@ static void cpmac_clear_tx(struct net_device *dev) int i; if (unlikely(!priv->desc_ring)) return; - for (i = 0; i < CPMAC_QUEUES; i++) + for (i = 0; i < CPMAC_QUEUES; i++) { + priv->desc_ring[i].dataflags = 0; if (priv->desc_ring[i].skb) { dev_kfree_skb_any(priv->desc_ring[i].skb); - if (netif_subqueue_stopped(dev, i)) - netif_wake_subqueue(dev, i); + priv->desc_ring[i].skb = NULL; } + } } static void cpmac_hw_error(struct work_struct *work) { + int i; struct cpmac_priv *priv = container_of(work, struct cpmac_priv, reset_work); @@ -664,7 +754,45 @@ static void cpmac_hw_error(struct work_struct *work) spin_unlock(&priv->rx_lock); cpmac_clear_tx(priv->dev); cpmac_hw_start(priv->dev); - netif_start_queue(priv->dev); + barrier(); + atomic_dec(&priv->reset_pending); + + for (i = 0; i < CPMAC_QUEUES; i++) { + netif_wake_subqueue(priv->dev, i); + } + netif_wake_queue(priv->dev); +} + +static void cpmac_check_status(struct net_device *dev) +{ + struct cpmac_priv *priv = netdev_priv(dev); + + u32 macstatus = cpmac_read(priv->regs, CPMAC_MAC_STATUS); + int rx_channel = (macstatus >> 8) & 7; + int rx_code = (macstatus >> 12) & 15; + int tx_channel = (macstatus >> 16) & 7; + int tx_code = (macstatus >> 20) & 15; + + if (rx_code || tx_code) { + if (netif_msg_drv(priv) && net_ratelimit()) { + /* Can't find any documentation on what these error codes actually are. + * So just log them and hope.. + */ + if (rx_code) + printk(KERN_WARNING "%s: host error %d on rx channel %d (macstatus %08x), resetting\n", + dev->name, rx_code, rx_channel, macstatus); + if (tx_code) + printk(KERN_WARNING "%s: host error %d on tx channel %d (macstatus %08x), resetting\n", + dev->name, tx_code, tx_channel, macstatus); + } + + netif_stop_queue(dev); + cpmac_hw_stop(dev); + if (schedule_work(&priv->reset_work)) + atomic_inc(&priv->reset_pending); + if (unlikely(netif_msg_hw(priv))) + cpmac_dump_regs(dev); + } } static irqreturn_t cpmac_irq(int irq, void *dev_id) @@ -696,46 +824,33 @@ static irqreturn_t cpmac_irq(int irq, void *dev_id) cpmac_write(priv->regs, CPMAC_MAC_EOI_VECTOR, 0); - if (unlikely(status & (MAC_INT_HOST | MAC_INT_STATUS))) { - if (netif_msg_drv(priv) && net_ratelimit()) - printk(KERN_ERR "%s: hw error, resetting...\n", - dev->name); - netif_stop_queue(dev); - cpmac_hw_stop(dev); - schedule_work(&priv->reset_work); - if (unlikely(netif_msg_hw(priv))) - cpmac_dump_regs(dev); - } + if (unlikely(status & (MAC_INT_HOST | MAC_INT_STATUS))) + cpmac_check_status(dev); return IRQ_HANDLED; } static void cpmac_tx_timeout(struct net_device *dev) { - struct cpmac_priv *priv = netdev_priv(dev); int i; + struct cpmac_priv *priv = netdev_priv(dev); spin_lock(&priv->lock); dev->stats.tx_errors++; spin_unlock(&priv->lock); if (netif_msg_tx_err(priv) && net_ratelimit()) printk(KERN_WARNING "%s: transmit timeout\n", dev->name); - /* - * FIXME: waking up random queue is not the best thing to - * do... on the other hand why we got here at all? - */ -#ifdef CONFIG_NETDEVICES_MULTIQUEUE - for (i = 0; i < CPMAC_QUEUES; i++) - if (priv->desc_ring[i].skb) { - dev_kfree_skb_any(priv->desc_ring[i].skb); - netif_wake_subqueue(dev, i); - break; - } -#else - if (priv->desc_ring[0].skb) - dev_kfree_skb_any(priv->desc_ring[0].skb); - netif_wake_queue(dev); -#endif + + atomic_inc(&priv->reset_pending); + barrier(); + cpmac_clear_tx(dev); + barrier(); + atomic_dec(&priv->reset_pending); + + netif_wake_queue(priv->dev); + for (i = 0; i < CPMAC_QUEUES; i++) { + netif_wake_subqueue(dev, i); + } } static int cpmac_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) @@ -794,7 +909,7 @@ static int cpmac_set_ringparam(struct net_device *dev, struct ethtool_ringparam* { struct cpmac_priv *priv = netdev_priv(dev); - if (dev->flags && IFF_UP) + if (netif_running(dev)) return -EBUSY; priv->ring_size = ring->rx_pending; return 0; @@ -920,9 +1035,12 @@ static int cpmac_open(struct net_device *dev) desc->buflen = CPMAC_SKB_SIZE; desc->dataflags = CPMAC_OWN; desc->next = &priv->rx_head[(i + 1) % priv->ring_size]; + desc->next->prev = desc; desc->hw_next = (u32)desc->next->mapping; } + priv->rx_head->prev->hw_next = (u32)0; + if ((res = request_irq(dev->irq, cpmac_irq, IRQF_SHARED, dev->name, dev))) { if (netif_msg_drv(priv)) @@ -931,6 +1049,7 @@ static int cpmac_open(struct net_device *dev) goto fail_irq; } + atomic_set(&priv->reset_pending, 0); INIT_WORK(&priv->reset_work, cpmac_hw_error); cpmac_hw_start(dev); @@ -1061,13 +1180,13 @@ static int __devinit cpmac_probe(struct platform_device *pdev) dev->tx_timeout = cpmac_tx_timeout; dev->ethtool_ops = &cpmac_ethtool_ops; dev->poll = cpmac_poll; - dev->weight = 64; dev->features |= NETIF_F_MULTI_QUEUE; spin_lock_init(&priv->lock); spin_lock_init(&priv->rx_lock); priv->dev = dev; priv->ring_size = 64; + dev->weight = max(4, priv->ring_size/4); priv->msg_enable = netif_msg_init(debug_level, 0xff); memcpy(dev->dev_addr, pdata->dev_addr, sizeof(dev->dev_addr)); if (phy_id == 31) {