From: Manfred Spraul attached is a forcedeth update from Carl-Daniel. The important change is that the driver now handles out of rx buffers - previously that condition resulted in an endless irq storm. Additionally the driver now detects irq storms and switches to polling if that happens - I'm sure there are other error conditions that we don't handle yet properly. The third change is automatic detection of bad mac addresses. drivers/net/forcedeth.c | 127 ++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 101 insertions(+), 26 deletions(-) diff -puN drivers/net/forcedeth.c~forcedeth-update-3 drivers/net/forcedeth.c --- 25/drivers/net/forcedeth.c~forcedeth-update-3 2003-12-14 19:21:12.000000000 -0800 +++ 25-akpm/drivers/net/forcedeth.c 2003-12-14 19:21:12.000000000 -0800 @@ -56,13 +56,18 @@ * 0.17: 16 Nov 2003: undo rx buffer size increase. Substract 1 from * the tx length. * 0.18: 17 Nov 2003: fix oops due to late initialization of dev_stats + * 0.19: 29 Nov 2003: Handle RxNoBuf, detect & handle invalid mac + * addresses, really stop rx if already running + * in start_rx, clean up a bit. + * (C) Carl-Daniel Hailfinger * * Known bugs: * The irq handling is wrong - no tx done interrupts are generated. * This means recovery from netif_stop_queue only happens in the hw timer - * interrupt (1/2 second), or if an rx packet arrives by chance. + * interrupt (1/2 second on nForce2, 1/100 second on nForce3), or if an + * rx packet arrives by chance. */ -#define FORCEDETH_VERSION "0.18" +#define FORCEDETH_VERSION "0.19" #include #include @@ -102,14 +107,16 @@ enum { #define NVREG_IRQSTAT_MIIEVENT 0x040 #define NVREG_IRQSTAT_MASK 0x1ff NvRegIrqMask = 0x004, -#define NVREG_IRQ_UNKNOWN 0x0005 #define NVREG_IRQ_RX 0x0002 +#define NVREG_IRQ_RX_NOBUF 0x0004 +#define NVREG_IRQ_TX_ERR 0x0008 #define NVREG_IRQ_TX2 0x0010 #define NVREG_IRQ_TIMER 0x0020 #define NVREG_IRQ_LINK 0x0040 #define NVREG_IRQ_TX1 0x0100 #define NVREG_IRQMASK_WANTED_1 0x005f #define NVREG_IRQMASK_WANTED_2 0x0147 +#define NVREG_IRQ_UNKNOWN (~(NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_TX_ERR|NVREG_IRQ_TX2|NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_TX1)) NvRegUnknownSetupReg6 = 0x008, #define NVREG_UNKSETUP6_VAL 3 @@ -291,6 +298,8 @@ struct ring_desc { #define RX_ALLOC_BUFSIZE (DEFAULT_MTU + 128) #define OOM_REFILL (1+HZ/20) +#define POLL_WAIT (1+HZ/100) + /* * SMP locking: * All hardware access under dev->priv->lock, except the performance @@ -329,6 +338,7 @@ struct fe_priv { dma_addr_t rx_dma[RX_RING]; unsigned int rx_buf_sz; struct timer_list oom_kick; + struct timer_list nic_poll; /* * tx specific fields. @@ -340,6 +350,12 @@ struct fe_priv { u16 tx_flags; }; +/* + * Maximum number of loops until we assume that a bit in the irq mask + * is stuck. Overridable with module param. + */ +static int max_interrupt_work = 5; + static inline struct fe_priv *get_nvpriv(struct net_device *dev) { return (struct fe_priv *) dev->priv; @@ -442,7 +458,7 @@ static void start_rx(struct net_device * dprintk(KERN_DEBUG "%s: start_rx\n", dev->name); /* Already running? Stop it. */ if (readl(base + NvRegReceiverControl) & NVREG_RCVCTL_START) { - writel(NVREG_RCVCTL_START, base + NvRegReceiverControl); + writel(0, base + NvRegReceiverControl); pci_push(base); } writel(np->linkspeed, base + NvRegLinkSpeed); @@ -562,9 +578,6 @@ static int alloc_rx(struct net_device *d dev->name, refill_rx); refill_rx++; } - if (np->refill_rx != refill_rx) { - /* FIXME: made progress. Kick hardware */ - } np->refill_rx = refill_rx; if (np->cur_rx - refill_rx == RX_RING) return 1; @@ -601,10 +614,6 @@ static int init_ring(struct net_device * for (i = 0; i < RX_RING; i++) { np->rx_ring[i].Flags = 0; } - init_timer(&np->oom_kick); - np->oom_kick.data = (unsigned long) dev; - np->oom_kick.function = &do_rx_refill; /* timer handler */ - return alloc_rx(dev); } @@ -624,13 +633,11 @@ static void drain_tx(struct net_device * } } } -static void drain_ring(struct net_device *dev) + +static void drain_rx(struct net_device *dev) { struct fe_priv *np = get_nvpriv(dev); int i; - - drain_tx(dev); - for (i = 0; i < RX_RING; i++) { np->rx_ring[i].Flags = 0; wmb(); @@ -644,6 +651,12 @@ static void drain_ring(struct net_device } } +static void drain_ring(struct net_device *dev) +{ + drain_tx(dev); + drain_rx(dev); +} + /* * start_xmit: dev->hard_start_xmit function * Called with dev->xmit_lock held. @@ -725,6 +738,7 @@ static void tx_done(struct net_device *d if (np->next_tx - np->nic_tx < TX_LIMIT_START) netif_wake_queue(dev); } + /* * tx_timeout: dev->tx_timeout function * Called with dev->xmit_lock held. @@ -734,8 +748,7 @@ static void tx_timeout(struct net_device struct fe_priv *np = get_nvpriv(dev); u8 *base = get_hwbase(dev); - dprintk(KERN_DEBUG "%s: Got tx_timeout.\n", dev->name); - dprintk(KERN_DEBUG "%s: irq: %08x\n", dev->name, + dprintk(KERN_DEBUG "%s: Got tx_timeout. irq: %08x\n", dev->name, readl(base + NvRegIrqStatus) & NVREG_IRQSTAT_MASK); spin_lock_irq(&np->lock); @@ -748,6 +761,7 @@ static void tx_timeout(struct net_device /* 3) if there are dead entries: clear everything */ if (np->next_tx != np->nic_tx) { + printk(KERN_DEBUG "%s: tx_timeout: dead entries!\n", dev->name); drain_tx(dev); np->next_tx = np->nic_tx = 0; writel((u32) (np->ring_addr + RX_RING*sizeof(struct ring_desc)), base + NvRegTxRingPhysAddr); @@ -769,7 +783,7 @@ static void rx_process(struct net_device int len; int i; if (np->cur_rx - np->refill_rx >= RX_RING) - break; /* ring empty - do not continue */ + break; /* we scanned the whole ring - do not continue */ i = np->cur_rx % RX_RING; prd = &np->rx_ring[i]; @@ -779,7 +793,8 @@ static void rx_process(struct net_device if (prd->Flags & cpu_to_le16(NV_RX_AVAIL)) break; /* still owned by hardware, */ - /* the packet is for us - immediately tear down the pci mapping, and + /* + * the packet is for us - immediately tear down the pci mapping, and * prefetch the first cacheline of the packet. */ pci_unmap_single(np->pci_dev, np->rx_dma[i], @@ -1003,10 +1018,11 @@ static irqreturn_t nic_irq(int foo, void struct fe_priv *np = get_nvpriv(dev); u8 *base = get_hwbase(dev); u32 events; + int i; dprintk(KERN_DEBUG "%s: nic_irq\n", dev->name); - for (;;) { + for (i=0; ; i++) { events = readl(base + NvRegIrqStatus) & NVREG_IRQSTAT_MASK; writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus); pci_push(base); @@ -1014,14 +1030,13 @@ static irqreturn_t nic_irq(int foo, void if (!(events & np->irqmask)) break; - /* FIXME: only call the required processing functions */ - if (events & (NVREG_IRQ_TX1|NVREG_IRQ_TX2)) { + if (events & (NVREG_IRQ_TX1|NVREG_IRQ_TX2|NVREG_IRQ_TX_ERR)) { spin_lock(&np->lock); tx_done(dev); spin_unlock(&np->lock); } - if (events & NVREG_IRQ_RX) { + if (events & (NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF)) { rx_process(dev); if (alloc_rx(dev)) { spin_lock(&np->lock); @@ -1036,14 +1051,48 @@ static irqreturn_t nic_irq(int foo, void link_irq(dev); spin_unlock(&np->lock); } + if (events & (NVREG_IRQ_TX_ERR)) { + dprintk(KERN_DEBUG "%s: received irq with events 0x%x. Probably TX fail.\n", + dev->name, events); + } if (events & (NVREG_IRQ_UNKNOWN)) { - printk("%s: received irq with unknown source 0x%x.\n", dev->name, events); + printk(KERN_DEBUG "%s: received irq with unknown events 0x%x. Please report\n", + dev->name, events); + } + if (i > max_interrupt_work) { + spin_lock(&np->lock); + /* disable interrupts on the nic */ + writel(0, base + NvRegIrqMask); + pci_push(base); + + if (!np->in_shutdown) + mod_timer(&np->nic_poll, jiffies + POLL_WAIT); + printk(KERN_DEBUG "%s: too many iterations (%d) in nic_irq.\n", dev->name, i); + spin_unlock(&np->lock); + break; } - /* FIXME: general errors, link change interrupts */ + } dprintk(KERN_DEBUG "%s: nic_irq completed\n", dev->name); - return IRQ_HANDLED; + return IRQ_RETVAL(i); +} + +static void do_nic_poll(unsigned long data) +{ + struct net_device *dev = (struct net_device *) data; + struct fe_priv *np = get_nvpriv(dev); + u8 *base = get_hwbase(dev); + + disable_irq(dev->irq); + /* + * reenable interrupts on the nic, we have to do this before calling + * nic_irq because that may decide to do otherwise + */ + writel(np->irqmask, base + NvRegIrqMask); + pci_push(base); + nic_irq((int) 0, (void *) data, (struct pt_regs *) NULL); + enable_irq(dev->irq); } static int open(struct net_device *dev) @@ -1203,6 +1252,7 @@ static int close(struct net_device *dev) synchronize_irq(dev->irq); del_timer_sync(&np->oom_kick); + del_timer_sync(&np->nic_poll); netif_stop_queue(dev); spin_lock_irq(&np->lock); @@ -1238,6 +1288,13 @@ static int __devinit probe_nic(struct pc SET_MODULE_OWNER(dev); SET_NETDEV_DEV(dev, &pci_dev->dev); + init_timer(&np->oom_kick); + np->oom_kick.data = (unsigned long) dev; + np->oom_kick.function = &do_rx_refill; /* timer handler */ + init_timer(&np->nic_poll); + np->nic_poll.data = (unsigned long) dev; + np->nic_poll.function = &do_nic_poll; /* timer handler */ + err = pci_enable_device(pci_dev); if (err) { printk(KERN_INFO "forcedeth: pci_enable_dev failed: %d\n", err); @@ -1313,6 +1370,21 @@ static int __devinit probe_nic(struct pc dev->dev_addr[4] = (np->orig_mac[0] >> 8) & 0xff; dev->dev_addr[5] = (np->orig_mac[0] >> 0) & 0xff; + if (!is_valid_ether_addr(dev->dev_addr)) { + /* + * Bad mac address. At least one bios sets the mac address + * to 01:23:45:67:89:ab + */ + printk(KERN_ERR "%s: Invalid Mac address detected: %02x:%02x:%02x:%02x:%02x:%02x\n", dev->name, + dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], + dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); + printk(KERN_ERR "Please complain to your hardware vendor. Switching to a random MAC.\n"); + dev->dev_addr[0] = 0x00; + dev->dev_addr[1] = 0x00; + dev->dev_addr[2] = 0x6c; + get_random_bytes(&dev->dev_addr[3], 3); + } + dprintk(KERN_DEBUG "%s: MAC Address %02x:%02x:%02x:%02x:%02x:%02x\n", dev->name, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); @@ -1410,6 +1482,9 @@ static void __exit exit_nic(void) pci_unregister_driver(&driver); } +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM_DESC(max_interrupt_work, "forcedeth maximum events handled per interrupt"); + MODULE_AUTHOR("Manfred Spraul "); MODULE_DESCRIPTION("Reverse Engineered nForce ethernet driver"); MODULE_LICENSE("GPL"); _