diff options
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/e100.c | 86 |
1 files changed, 71 insertions, 15 deletions
diff --git a/drivers/net/e100.c b/drivers/net/e100.c index 0f2837ebaab..2c8c14ebf20 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c @@ -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; @@ -1204,7 +1210,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 +1306,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 +1425,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 +1453,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 +1494,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 +1505,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 +1541,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 +1587,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 +1602,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 +1610,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 +1626,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 +1648,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); @@ -1684,7 +1738,7 @@ 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))) @@ -1759,7 +1813,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; @@ -2233,6 +2287,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); @@ -2348,7 +2403,8 @@ static int e100_resume(struct pci_dev *pdev) pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); - e100_hw_init(nic); + if(e100_hw_init(nic)) + DPRINTK(HW, ERR, "e100_hw_init failed\n"); netif_device_attach(netdev); if(netif_running(netdev)) |