]> nv-tegra.nvidia Code Review - linux-3.10.git/blobdiff - drivers/net/e100.c
[PATCH] e100: Fix Wake on lan related issues
[linux-3.10.git] / drivers / net / e100.c
index 0f2837ebaab88e5432f25d36121b695cc43ddd47..a43b82512b60bf97ecd71fbef834d2843ecc7552 100644 (file)
@@ -269,6 +269,12 @@ enum scb_status {
        rus_mask         = 0x3C,
 };
 
+enum ru_state  {
+       RU_SUSPENDED = 0,
+       RU_RUNNING       = 1,
+       RU_UNINITIALIZED = -1,
+};
+
 enum scb_stat_ack {
        stat_ack_not_ours    = 0x00,
        stat_ack_sw_gen      = 0x04,
@@ -510,7 +516,7 @@ struct nic {
        struct rx *rx_to_use;
        struct rx *rx_to_clean;
        struct rfd blank_rfd;
-       int ru_running;
+       enum ru_state ru_running;
 
        spinlock_t cb_lock                      ____cacheline_aligned;
        spinlock_t cmd_lock;
@@ -965,7 +971,8 @@ static void e100_configure(struct nic *nic, struct cb *cb, struct sk_buff *skb)
        if(nic->flags & multicast_all)
                config->multicast_all = 0x1;            /* 1=accept, 0=no */
 
-       if(!(nic->flags & wol_magic))
+       /* disable WoL when up */
+       if(netif_running(nic->netdev) || !(nic->flags & wol_magic))
                config->magic_packet_disable = 0x1;     /* 1=off, 0=on */
 
        if(nic->mac >= mac_82558_D101_A4) {
@@ -1204,7 +1211,9 @@ static void e100_update_stats(struct nic *nic)
                }
        }
 
-       e100_exec_cmd(nic, cuc_dump_reset, 0);
+       
+       if(e100_exec_cmd(nic, cuc_dump_reset, 0))
+               DPRINTK(TX_ERR, DEBUG, "exec cuc_dump_reset failed\n");
 }
 
 static void e100_adjust_adaptive_ifs(struct nic *nic, int speed, int duplex)
@@ -1298,7 +1307,8 @@ static int e100_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
                /* SW workaround for ICH[x] 10Mbps/half duplex Tx hang.
                   Issue a NOP command followed by a 1us delay before
                   issuing the Tx command. */
-               e100_exec_cmd(nic, cuc_nop, 0);
+               if(e100_exec_cmd(nic, cuc_nop, 0))
+                       DPRINTK(TX_ERR, DEBUG, "exec cuc_nop failed\n");
                udelay(1);
        }
 
@@ -1416,12 +1426,18 @@ static int e100_alloc_cbs(struct nic *nic)
        return 0;
 }
 
-static inline void e100_start_receiver(struct nic *nic)
+static inline void e100_start_receiver(struct nic *nic, struct rx *rx)
 {
+       if(!nic->rxs) return;
+       if(RU_SUSPENDED != nic->ru_running) return;
+
+       /* handle init time starts */
+       if(!rx) rx = nic->rxs;
+
        /* (Re)start RU if suspended or idle and RFA is non-NULL */
-       if(!nic->ru_running && nic->rx_to_clean->skb) {
-               e100_exec_cmd(nic, ruc_start, nic->rx_to_clean->dma_addr);
-               nic->ru_running = 1;
+       if(rx->skb) {
+               e100_exec_cmd(nic, ruc_start, rx->dma_addr);
+               nic->ru_running = RU_RUNNING;
        }
 }
 
@@ -1438,6 +1454,13 @@ static inline int e100_rx_alloc_skb(struct nic *nic, struct rx *rx)
        rx->dma_addr = pci_map_single(nic->pdev, rx->skb->data,
                RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL);
 
+       if(pci_dma_mapping_error(rx->dma_addr)) {
+               dev_kfree_skb_any(rx->skb);
+               rx->skb = 0;
+               rx->dma_addr = 0;
+               return -ENOMEM;
+       }
+
        /* Link the RFD to end of RFA by linking previous RFD to
         * this one, and clearing EL bit of previous.  */
        if(rx->prev->skb) {
@@ -1472,7 +1495,7 @@ static inline int e100_rx_indicate(struct nic *nic, struct rx *rx,
 
        /* If data isn't ready, nothing to indicate */
        if(unlikely(!(rfd_status & cb_complete)))
-               return -EAGAIN;
+               return -ENODATA;
 
        /* Get actual data size */
        actual_size = le16_to_cpu(rfd->actual_size) & 0x3FFF;
@@ -1483,6 +1506,10 @@ static inline int e100_rx_indicate(struct nic *nic, struct rx *rx,
        pci_unmap_single(nic->pdev, rx->dma_addr,
                RFD_BUF_LEN, PCI_DMA_FROMDEVICE);
 
+       /* this allows for a fast restart without re-enabling interrupts */
+       if(le16_to_cpu(rfd->command) & cb_el)
+               nic->ru_running = RU_SUSPENDED;
+
        /* Pull off the RFD and put the actual data (minus eth hdr) */
        skb_reserve(skb, sizeof(struct rfd));
        skb_put(skb, actual_size);
@@ -1515,20 +1542,45 @@ static inline void e100_rx_clean(struct nic *nic, unsigned int *work_done,
        unsigned int work_to_do)
 {
        struct rx *rx;
+       int restart_required = 0;
+       struct rx *rx_to_start = NULL;
+
+       /* are we already rnr? then pay attention!!! this ensures that
+        * the state machine progression never allows a start with a 
+        * partially cleaned list, avoiding a race between hardware
+        * and rx_to_clean when in NAPI mode */
+       if(RU_SUSPENDED == nic->ru_running)
+               restart_required = 1;
 
        /* Indicate newly arrived packets */
        for(rx = nic->rx_to_clean; rx->skb; rx = nic->rx_to_clean = rx->next) {
-               if(e100_rx_indicate(nic, rx, work_done, work_to_do))
+               int err = e100_rx_indicate(nic, rx, work_done, work_to_do);
+               if(-EAGAIN == err) {
+                       /* hit quota so have more work to do, restart once
+                        * cleanup is complete */
+                       restart_required = 0;
+                       break;
+               } else if(-ENODATA == err)
                        break; /* No more to clean */
        }
 
+       /* save our starting point as the place we'll restart the receiver */
+       if(restart_required)
+               rx_to_start = nic->rx_to_clean;
+
        /* Alloc new skbs to refill list */
        for(rx = nic->rx_to_use; !rx->skb; rx = nic->rx_to_use = rx->next) {
                if(unlikely(e100_rx_alloc_skb(nic, rx)))
                        break; /* Better luck next time (see watchdog) */
        }
 
-       e100_start_receiver(nic);
+       if(restart_required) {
+               // ack the rnr?
+               writeb(stat_ack_rnr, &nic->csr->scb.stat_ack);
+               e100_start_receiver(nic, rx_to_start);
+               if(work_done)
+                       (*work_done)++;
+       }
 }
 
 static void e100_rx_clean_list(struct nic *nic)
@@ -1536,6 +1588,8 @@ static void e100_rx_clean_list(struct nic *nic)
        struct rx *rx;
        unsigned int i, count = nic->params.rfds.count;
 
+       nic->ru_running = RU_UNINITIALIZED;
+
        if(nic->rxs) {
                for(rx = nic->rxs, i = 0; i < count; rx++, i++) {
                        if(rx->skb) {
@@ -1549,7 +1603,6 @@ static void e100_rx_clean_list(struct nic *nic)
        }
 
        nic->rx_to_use = nic->rx_to_clean = NULL;
-       nic->ru_running = 0;
 }
 
 static int e100_rx_alloc_list(struct nic *nic)
@@ -1558,6 +1611,7 @@ static int e100_rx_alloc_list(struct nic *nic)
        unsigned int i, count = nic->params.rfds.count;
 
        nic->rx_to_use = nic->rx_to_clean = NULL;
+       nic->ru_running = RU_UNINITIALIZED;
 
        if(!(nic->rxs = kmalloc(sizeof(struct rx) * count, GFP_ATOMIC)))
                return -ENOMEM;
@@ -1573,6 +1627,7 @@ static int e100_rx_alloc_list(struct nic *nic)
        }
 
        nic->rx_to_use = nic->rx_to_clean = nic->rxs;
+       nic->ru_running = RU_SUSPENDED;
 
        return 0;
 }
@@ -1594,7 +1649,7 @@ static irqreturn_t e100_intr(int irq, void *dev_id, struct pt_regs *regs)
 
        /* We hit Receive No Resource (RNR); restart RU after cleaning */
        if(stat_ack & stat_ack_rnr)
-               nic->ru_running = 0;
+               nic->ru_running = RU_SUSPENDED;
 
        e100_disable_irq(nic);
        netif_rx_schedule(netdev);
@@ -1664,6 +1719,7 @@ static int e100_change_mtu(struct net_device *netdev, int new_mtu)
        return 0;
 }
 
+#ifdef CONFIG_PM
 static int e100_asf(struct nic *nic)
 {
        /* ASF can be enabled from eeprom */
@@ -1672,6 +1728,7 @@ static int e100_asf(struct nic *nic)
           !(nic->eeprom[eeprom_config_asf] & eeprom_gcl) &&
           ((nic->eeprom[eeprom_smbus_addr] & 0xFF) != 0xFE));
 }
+#endif
 
 static int e100_up(struct nic *nic)
 {
@@ -1684,13 +1741,16 @@ static int e100_up(struct nic *nic)
        if((err = e100_hw_init(nic)))
                goto err_clean_cbs;
        e100_set_multicast_list(nic->netdev);
-       e100_start_receiver(nic);
+       e100_start_receiver(nic, 0);
        mod_timer(&nic->watchdog, jiffies);
        if((err = request_irq(nic->pdev->irq, e100_intr, SA_SHIRQ,
                nic->netdev->name, nic->netdev)))
                goto err_no_irq;
-       e100_enable_irq(nic);
        netif_wake_queue(nic->netdev);
+       netif_poll_enable(nic->netdev);
+       /* enable ints _after_ enabling poll, preventing a race between
+        * disable ints+schedule */
+       e100_enable_irq(nic);
        return 0;
 
 err_no_irq:
@@ -1704,11 +1764,13 @@ err_rx_clean_list:
 
 static void e100_down(struct nic *nic)
 {
+       /* wait here for poll to complete */
+       netif_poll_disable(nic->netdev);
+       netif_stop_queue(nic->netdev);
        e100_hw_reset(nic);
        free_irq(nic->pdev->irq, nic->netdev);
        del_timer_sync(&nic->watchdog);
        netif_carrier_off(nic->netdev);
-       netif_stop_queue(nic->netdev);
        e100_clean_cbs(nic);
        e100_rx_clean_list(nic);
 }
@@ -1759,7 +1821,7 @@ static int e100_loopback_test(struct nic *nic, enum loopback loopback_mode)
                mdio_write(nic->netdev, nic->mii.phy_id, MII_BMCR,
                        BMCR_LOOPBACK);
 
-       e100_start_receiver(nic);
+       e100_start_receiver(nic, 0);
 
        if(!(skb = dev_alloc_skb(ETH_DATA_LEN))) {
                err = -ENOMEM;
@@ -1879,7 +1941,6 @@ static int e100_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
        else
                nic->flags &= ~wol_magic;
 
-       pci_enable_wake(nic->pdev, 0, nic->flags & (wol_magic | e100_asf(nic)));
        e100_exec_cb(nic, NULL, e100_configure);
 
        return 0;
@@ -2233,6 +2294,7 @@ static int __devinit e100_probe(struct pci_dev *pdev,
 
        e100_get_defaults(nic);
 
+       /* locks must be initialized before calling hw_reset */
        spin_lock_init(&nic->cb_lock);
        spin_lock_init(&nic->cmd_lock);
 
@@ -2276,7 +2338,8 @@ static int __devinit e100_probe(struct pci_dev *pdev,
           (nic->eeprom[eeprom_id] & eeprom_id_wol))
                nic->flags |= wol_magic;
 
-       pci_enable_wake(pdev, 0, nic->flags & (wol_magic | e100_asf(nic)));
+       /* ack any pending wake events, disable PME */
+       pci_enable_wake(pdev, 0, 0);
 
        strcpy(netdev->name, "eth%d");
        if((err = register_netdev(netdev))) {
@@ -2348,7 +2411,10 @@ static int e100_resume(struct pci_dev *pdev)
 
        pci_set_power_state(pdev, PCI_D0);
        pci_restore_state(pdev);
-       e100_hw_init(nic);
+       /* ack any pending wake events, disable PME */
+       pci_enable_wake(pdev, 0, 0);
+       if(e100_hw_init(nic))
+               DPRINTK(HW, ERR, "e100_hw_init failed\n");
 
        netif_device_attach(netdev);
        if(netif_running(netdev))
@@ -2358,6 +2424,21 @@ static int e100_resume(struct pci_dev *pdev)
 }
 #endif
 
+
+static void e100_shutdown(struct device *dev)
+{
+       struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+       struct net_device *netdev = pci_get_drvdata(pdev);
+       struct nic *nic = netdev_priv(netdev);
+
+#ifdef CONFIG_PM
+       pci_enable_wake(pdev, 0, nic->flags & (wol_magic | e100_asf(nic)));
+#else
+       pci_enable_wake(pdev, 0, nic->flags & (wol_magic));
+#endif
+}
+
+
 static struct pci_driver e100_driver = {
        .name =         DRV_NAME,
        .id_table =     e100_id_table,
@@ -2367,6 +2448,11 @@ static struct pci_driver e100_driver = {
        .suspend =      e100_suspend,
        .resume =       e100_resume,
 #endif
+
+       .driver = {
+               .shutdown = e100_shutdown,
+       }
+
 };
 
 static int __init e100_init_module(void)