]> err.no Git - linux-2.6/blobdiff - drivers/net/s2io.c
[PATCH] S2io: Timer based slowpath handling
[linux-2.6] / drivers / net / s2io.c
index e2144fc7df9ac602560dc13442514bc3a27ac858..ee498d248d38eafd9c72ab8768fc14af413b424a 100644 (file)
@@ -100,8 +100,7 @@ static inline int rx_buffer_level(nic_t * sp, int rxb_size, int ring)
        mac_control = &sp->mac_control;
        if ((mac_control->rings[ring].pkt_cnt - rxb_size) > 16) {
                level = LOW;
-               if ((mac_control->rings[ring].pkt_cnt - rxb_size) <
-                               MAX_RXDS_PER_BLOCK) {
+               if (rxb_size <= MAX_RXDS_PER_BLOCK) {
                        level = PANIC;
                }
        }
@@ -158,6 +157,9 @@ static char ethtool_stats_keys[][ETH_GSTRING_LEN] = {
        {"rmac_pause_cnt"},
        {"rmac_accepted_ip"},
        {"rmac_err_tcp"},
+       {"\n DRIVER STATISTICS"},
+       {"single_bit_ecc_errs"},
+       {"double_bit_ecc_errs"},
 };
 
 #define S2IO_STAT_LEN sizeof(ethtool_stats_keys)/ ETH_GSTRING_LEN
@@ -166,6 +168,12 @@ static char ethtool_stats_keys[][ETH_GSTRING_LEN] = {
 #define S2IO_TEST_LEN  sizeof(s2io_gstrings) / ETH_GSTRING_LEN
 #define S2IO_STRINGS_LEN       S2IO_TEST_LEN * ETH_GSTRING_LEN
 
+#define S2IO_TIMER_CONF(timer, handle, arg, exp)               \
+                       init_timer(&timer);                     \
+                       timer.function = handle;                \
+                       timer.data = (unsigned long) arg;       \
+                       mod_timer(&timer, (jiffies + exp))      \
+
 /*
  * Constants to be programmed into the Xena's registers, to configure
  * the XAUI.
@@ -237,7 +245,6 @@ static unsigned int tx_fifo_len[MAX_TX_FIFOS] =
 static unsigned int rx_ring_num = 1;
 static unsigned int rx_ring_sz[MAX_RX_RINGS] =
     {[0 ...(MAX_RX_RINGS - 1)] = 0 };
-static unsigned int Stats_refresh_time = 4;
 static unsigned int rts_frm_len[MAX_RX_RINGS] =
     {[0 ...(MAX_RX_RINGS - 1)] = 0 };
 static unsigned int use_continuous_tx_intrs = 1;
@@ -1083,9 +1090,6 @@ static int init_nic(struct s2io_nic *nic)
 
        /* Program statistics memory */
        writeq(mac_control->stats_mem_phy, &bar0->stat_addr);
-       val64 = SET_UPDT_PERIOD(Stats_refresh_time) |
-               STAT_CFG_STAT_RO | STAT_CFG_STAT_EN;
-       writeq(val64, &bar0->stat_cfg);
 
        /*
         * Initializing the sampling rate for the device to calculate the
@@ -1710,7 +1714,7 @@ static void free_tx_buffers(struct s2io_nic *nic)
        int i, j;
        mac_info_t *mac_control;
        struct config_param *config;
-       int cnt = 0;
+       int cnt = 0, frg_cnt;
 
        mac_control = &nic->mac_control;
        config = &nic->config;
@@ -1723,11 +1727,33 @@ static void free_tx_buffers(struct s2io_nic *nic)
                            (struct sk_buff *) ((unsigned long) txdp->
                                                Host_Control);
                        if (skb == NULL) {
-                               memset(txdp, 0, sizeof(TxD_t));
+                               memset(txdp, 0, sizeof(TxD_t) *
+                                      config->max_txds);
                                continue;
                        }
+                       frg_cnt = skb_shinfo(skb)->nr_frags;
+                       pci_unmap_single(nic->pdev, (dma_addr_t)
+                                        txdp->Buffer_Pointer,
+                                        skb->len - skb->data_len,
+                                        PCI_DMA_TODEVICE);
+                       if (frg_cnt) {
+                               TxD_t *temp;
+                               temp = txdp;
+                               txdp++;
+                               for (j = 0; j < frg_cnt; j++, txdp++) {
+                                       skb_frag_t *frag =
+                                           &skb_shinfo(skb)->frags[j];
+                                       pci_unmap_page(nic->pdev,
+                                                      (dma_addr_t)
+                                                      txdp->
+                                                      Buffer_Pointer,
+                                                      frag->size,
+                                                      PCI_DMA_TODEVICE);
+                               }
+                               txdp = temp;
+                       }
                        dev_kfree_skb(skb);
-                       memset(txdp, 0, sizeof(TxD_t));
+                       memset(txdp, 0, sizeof(TxD_t) * config->max_txds);
                        cnt++;
                }
                DBG_PRINT(INTR_DBG,
@@ -2101,6 +2127,7 @@ static int s2io_poll(struct net_device *dev, int *budget)
        u64 val64;
        int i;
 
+       atomic_inc(&nic->isr_cnt);
        mac_control = &nic->mac_control;
        config = &nic->config;
 
@@ -2136,6 +2163,7 @@ static int s2io_poll(struct net_device *dev, int *budget)
        }
        /* Re enable the Rx interrupts. */
        en_dis_able_nic_intrs(nic, RX_TRAFFIC_INTR, ENABLE_INTRS);
+       atomic_dec(&nic->isr_cnt);
        return 0;
 
 no_rx:
@@ -2149,6 +2177,7 @@ no_rx:
                        break;
                }
        }
+       atomic_dec(&nic->isr_cnt);
        return 1;
 }
 #endif
@@ -2169,7 +2198,6 @@ static void rx_intr_handler(ring_info_t *ring_data)
 {
        nic_t *nic = ring_data->nic;
        struct net_device *dev = (struct net_device *) nic->dev;
-       XENA_dev_config_t __iomem *bar0 = nic->bar0;
        int get_block, get_offset, put_block, put_offset, ring_bufs;
        rx_curr_get_info_t get_info, put_info;
        RxD_t *rxdp;
@@ -2177,14 +2205,12 @@ static void rx_intr_handler(ring_info_t *ring_data)
 #ifndef CONFIG_S2IO_NAPI
        int pkt_cnt = 0;
 #endif
-       register u64 val64;
-
-       /*
-        * rx_traffic_int reg is an R1 register, hence we read and write
-        * back the same value in the register to clear it
-        */
-       val64 = readq(&bar0->tx_traffic_int);
-       writeq(val64, &bar0->tx_traffic_int);
+       spin_lock(&nic->rx_lock);
+       if (atomic_read(&nic->card_state) == CARD_DOWN) {
+               DBG_PRINT(ERR_DBG, "%s: %s going down for reset\n",
+                         __FUNCTION__, dev->name);
+               spin_unlock(&nic->rx_lock);
+       }
 
        get_info = ring_data->rx_curr_get_info;
        get_block = get_info.block_index;
@@ -2210,6 +2236,7 @@ static void rx_intr_handler(ring_info_t *ring_data)
                        DBG_PRINT(ERR_DBG, "%s: The skb is ",
                                  dev->name);
                        DBG_PRINT(ERR_DBG, "Null in Rx Intr\n");
+                       spin_unlock(&nic->rx_lock);
                        return;
                }
 #ifndef CONFIG_2BUFF_MODE
@@ -2262,6 +2289,7 @@ static void rx_intr_handler(ring_info_t *ring_data)
                        break;
 #endif
        }
+       spin_unlock(&nic->rx_lock);
 }
 
 /**
@@ -2279,20 +2307,11 @@ static void rx_intr_handler(ring_info_t *ring_data)
 static void tx_intr_handler(fifo_info_t *fifo_data)
 {
        nic_t *nic = fifo_data->nic;
-       XENA_dev_config_t __iomem *bar0 = nic->bar0;
        struct net_device *dev = (struct net_device *) nic->dev;
        tx_curr_get_info_t get_info, put_info;
        struct sk_buff *skb;
        TxD_t *txdlp;
        u16 j, frg_cnt;
-       register u64 val64 = 0;
-
-       /*
-        * tx_traffic_int reg is an R1 register, hence we read and write
-        * back the same value in the register to clear it
-        */
-       val64 = readq(&bar0->tx_traffic_int);
-       writeq(val64, &bar0->tx_traffic_int);
 
        get_info = fifo_data->tx_curr_get_info;
        put_info = fifo_data->tx_curr_put_info;
@@ -2345,7 +2364,6 @@ static void tx_intr_handler(fifo_info_t *fifo_data)
                       (sizeof(TxD_t) * fifo_data->max_txds));
 
                /* Updating the statistics block */
-               nic->stats.tx_packets++;
                nic->stats.tx_bytes += skb->len;
                dev_kfree_skb_irq(skb);
 
@@ -2393,13 +2411,16 @@ static void alarm_intr_handler(struct s2io_nic *nic)
        writeq(val64, &bar0->mc_err_reg);
        if (val64 & (MC_ERR_REG_ECC_ALL_SNG | MC_ERR_REG_ECC_ALL_DBL)) {
                if (val64 & MC_ERR_REG_ECC_ALL_DBL) {
+                       nic->mac_control.stats_info->sw_stat.
+                               double_ecc_errs++;
                        DBG_PRINT(ERR_DBG, "%s: Device indicates ",
                                  dev->name);
                        DBG_PRINT(ERR_DBG, "double ECC error!!\n");
                        netif_stop_queue(dev);
                        schedule_work(&nic->rst_timer_task);
                } else {
-                       /* Device can recover from Single ECC errors */
+                       nic->mac_control.stats_info->sw_stat.
+                               single_ecc_errs++;
                }
        }
 
@@ -2695,7 +2716,7 @@ int s2io_open(struct net_device *dev)
         * Nic is initialized
         */
        netif_carrier_off(dev);
-       sp->last_link_state = LINK_DOWN;
+       sp->last_link_state = 0; /* Unkown link state */
 
        /* Initialize H/W and enable interrupts */
        if (s2io_card_up(sp)) {
@@ -2726,6 +2747,7 @@ int s2io_open(struct net_device *dev)
 setting_mac_address_failed:
        free_irq(sp->pdev->irq, dev);
 isr_registration_failed:
+       del_timer_sync(&sp->alarm_timer);
        s2io_reset(sp);
 hw_init_failed:
        return err;
@@ -2783,7 +2805,6 @@ int s2io_xmit(struct sk_buff *skb, struct net_device *dev)
 #endif
        mac_info_t *mac_control;
        struct config_param *config;
-       XENA_dev_config_t __iomem *bar0 = sp->bar0;
 
        mac_control = &sp->mac_control;
        config = &sp->config;
@@ -2855,6 +2876,8 @@ int s2io_xmit(struct sk_buff *skb, struct net_device *dev)
        val64 = mac_control->fifos[queue].list_info[put_off].list_phy_addr;
        writeq(val64, &tx_fifo->TxDL_Pointer);
 
+       wmb();
+
        val64 = (TX_FIFO_LAST_TXD_NUM(frg_cnt) | TX_FIFO_FIRST_LIST |
                 TX_FIFO_LAST_LIST);
 
@@ -2864,9 +2887,6 @@ int s2io_xmit(struct sk_buff *skb, struct net_device *dev)
 #endif
        writeq(val64, &tx_fifo->List_Control);
 
-       /* Perform a PCI read to flush previous writes */
-       val64 = readq(&bar0->general_int_status);
-
        put_off++;
        put_off %= mac_control->fifos[queue].tx_curr_put_info.fifo_len + 1;
        mac_control->fifos[queue].tx_curr_put_info.offset = put_off;
@@ -2885,6 +2905,15 @@ int s2io_xmit(struct sk_buff *skb, struct net_device *dev)
        return 0;
 }
 
+static void
+s2io_alarm_handle(unsigned long data)
+{
+       nic_t *sp = (nic_t *)data;
+
+       alarm_intr_handler(sp);
+       mod_timer(&sp->alarm_timer, jiffies + HZ / 2);
+}
+
 /**
  *  s2io_isr - ISR handler of the device .
  *  @irq: the irq of the device.
@@ -2905,10 +2934,11 @@ static irqreturn_t s2io_isr(int irq, void *dev_id, struct pt_regs *regs)
        nic_t *sp = dev->priv;
        XENA_dev_config_t __iomem *bar0 = sp->bar0;
        int i;
-       u64 reason = 0;
+       u64 reason = 0, val64;
        mac_info_t *mac_control;
        struct config_param *config;
 
+       atomic_inc(&sp->isr_cnt);
        mac_control = &sp->mac_control;
        config = &sp->config;
 
@@ -2924,12 +2954,10 @@ static irqreturn_t s2io_isr(int irq, void *dev_id, struct pt_regs *regs)
 
        if (!reason) {
                /* The interrupt was not raised by Xena. */
+               atomic_dec(&sp->isr_cnt);
                return IRQ_NONE;
        }
 
-       if (reason & (GEN_ERROR_INTR))
-               alarm_intr_handler(sp);
-
 #ifdef CONFIG_S2IO_NAPI
        if (reason & GEN_INTR_RXTRAFFIC) {
                if (netif_rx_schedule_prep(dev)) {
@@ -2941,6 +2969,13 @@ static irqreturn_t s2io_isr(int irq, void *dev_id, struct pt_regs *regs)
 #else
        /* If Intr is because of Rx Traffic */
        if (reason & GEN_INTR_RXTRAFFIC) {
+               /*
+                * rx_traffic_int reg is an R1 register, writing all 1's
+                * will ensure that the actual interrupt causing bit get's
+                * cleared and hence a read can be avoided.
+                */
+               val64 = 0xFFFFFFFFFFFFFFFFULL;
+               writeq(val64, &bar0->rx_traffic_int);
                for (i = 0; i < config->rx_ring_num; i++) {
                        rx_intr_handler(&mac_control->rings[i]);
                }
@@ -2949,6 +2984,14 @@ static irqreturn_t s2io_isr(int irq, void *dev_id, struct pt_regs *regs)
 
        /* If Intr is because of Tx Traffic */
        if (reason & GEN_INTR_TXTRAFFIC) {
+               /*
+                * tx_traffic_int reg is an R1 register, writing all 1's
+                * will ensure that the actual interrupt causing bit get's
+                * cleared and hence a read can be avoided.
+                */
+               val64 = 0xFFFFFFFFFFFFFFFFULL;
+               writeq(val64, &bar0->tx_traffic_int);
+
                for (i = 0; i < config->tx_fifo_num; i++)
                        tx_intr_handler(&mac_control->fifos[i]);
        }
@@ -2972,6 +3015,7 @@ static irqreturn_t s2io_isr(int irq, void *dev_id, struct pt_regs *regs)
                                          dev->name);
                                DBG_PRINT(ERR_DBG, " in ISR!!\n");
                                clear_bit(0, (&sp->tasklet_status));
+                               atomic_dec(&sp->isr_cnt);
                                return IRQ_HANDLED;
                        }
                        clear_bit(0, (&sp->tasklet_status));
@@ -2981,9 +3025,36 @@ static irqreturn_t s2io_isr(int irq, void *dev_id, struct pt_regs *regs)
        }
 #endif
 
+       atomic_dec(&sp->isr_cnt);
        return IRQ_HANDLED;
 }
 
+/**
+ * s2io_updt_stats -
+ */
+static void s2io_updt_stats(nic_t *sp)
+{
+       XENA_dev_config_t __iomem *bar0 = sp->bar0;
+       u64 val64;
+       int cnt = 0;
+
+       if (atomic_read(&sp->card_state) == CARD_UP) {
+               /* Apprx 30us on a 133 MHz bus */
+               val64 = SET_UPDT_CLICKS(10) |
+                       STAT_CFG_ONE_SHOT_EN | STAT_CFG_STAT_EN;
+               writeq(val64, &bar0->stat_cfg);
+               do {
+                       udelay(100);
+                       val64 = readq(&bar0->stat_cfg);
+                       if (!(val64 & BIT(0)))
+                               break;
+                       cnt++;
+                       if (cnt == 5)
+                               break; /* Updt failed */
+               } while(1);
+       }
+}
+
 /**
  *  s2io_get_stats - Updates the device statistics structure.
  *  @dev : pointer to the device structure.
@@ -3004,6 +3075,11 @@ struct net_device_stats *s2io_get_stats(struct net_device *dev)
        mac_control = &sp->mac_control;
        config = &sp->config;
 
+       /* Configure Stats for immediate updt */
+       s2io_updt_stats(sp);
+
+       sp->stats.tx_packets =
+               le32_to_cpu(mac_control->stats_info->tmac_frms);
        sp->stats.tx_errors =
                le32_to_cpu(mac_control->stats_info->tmac_any_err_frms);
        sp->stats.rx_errors =
@@ -4018,6 +4094,7 @@ static void s2io_get_ethtool_stats(struct net_device *dev,
        nic_t *sp = dev->priv;
        StatInfo_t *stat_info = sp->mac_control.stats_info;
 
+       s2io_updt_stats(sp);
        tmp_stats[i++] = le32_to_cpu(stat_info->tmac_frms);
        tmp_stats[i++] = le32_to_cpu(stat_info->tmac_data_octets);
        tmp_stats[i++] = le64_to_cpu(stat_info->tmac_drop_frms);
@@ -4057,6 +4134,9 @@ static void s2io_get_ethtool_stats(struct net_device *dev,
        tmp_stats[i++] = le32_to_cpu(stat_info->rmac_pause_cnt);
        tmp_stats[i++] = le32_to_cpu(stat_info->rmac_accepted_ip);
        tmp_stats[i++] = le32_to_cpu(stat_info->rmac_err_tcp);
+       tmp_stats[i++] = 0;
+       tmp_stats[i++] = stat_info->sw_stat.single_ecc_errs;
+       tmp_stats[i++] = stat_info->sw_stat.double_ecc_errs;
 }
 
 int s2io_ethtool_get_regs_len(struct net_device *dev)
@@ -4180,14 +4260,6 @@ int s2io_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 int s2io_change_mtu(struct net_device *dev, int new_mtu)
 {
        nic_t *sp = dev->priv;
-       XENA_dev_config_t __iomem *bar0 = sp->bar0;
-       register u64 val64;
-
-       if (netif_running(dev)) {
-               DBG_PRINT(ERR_DBG, "%s: Must be stopped to ", dev->name);
-               DBG_PRINT(ERR_DBG, "change its MTU\n");
-               return -EBUSY;
-       }
 
        if ((new_mtu < MIN_MTU) || (new_mtu > S2IO_JUMBO_SIZE)) {
                DBG_PRINT(ERR_DBG, "%s: MTU size is invalid.\n",
@@ -4195,11 +4267,22 @@ int s2io_change_mtu(struct net_device *dev, int new_mtu)
                return -EPERM;
        }
 
-       /* Set the new MTU into the PYLD register of the NIC */
-       val64 = new_mtu;
-       writeq(vBIT(val64, 2, 14), &bar0->rmac_max_pyld_len);
-
        dev->mtu = new_mtu;
+       if (netif_running(dev)) {
+               s2io_card_down(sp);
+               netif_stop_queue(dev);
+               if (s2io_card_up(sp)) {
+                       DBG_PRINT(ERR_DBG, "%s: Device bring up failed\n",
+                                 __FUNCTION__);
+               }
+               if (netif_queue_stopped(dev))
+                       netif_wake_queue(dev);
+       } else { /* Device is down */
+               XENA_dev_config_t __iomem *bar0 = sp->bar0;
+               u64 val64 = new_mtu;
+
+               writeq(vBIT(val64, 2, 14), &bar0->rmac_max_pyld_len);
+       }
 
        return 0;
 }
@@ -4324,6 +4407,7 @@ static void s2io_card_down(nic_t * sp)
        unsigned long flags;
        register u64 val64 = 0;
 
+       del_timer_sync(&sp->alarm_timer);
        /* If s2io_set_link task is executing, wait till it completes. */
        while (test_and_set_bit(0, &(sp->link_state))) {
                msleep(50);
@@ -4353,14 +4437,27 @@ static void s2io_card_down(nic_t * sp)
                        break;
                }
        } while (1);
-       spin_lock_irqsave(&sp->tx_lock, flags);
        s2io_reset(sp);
 
-       /* Free all unused Tx and Rx buffers */
+       /* Waiting till all Interrupt handlers are complete */
+       cnt = 0;
+       do {
+               msleep(10);
+               if (!atomic_read(&sp->isr_cnt))
+                       break;
+               cnt++;
+       } while(cnt < 5);
+
+       spin_lock_irqsave(&sp->tx_lock, flags);
+       /* Free all Tx buffers */
        free_tx_buffers(sp);
+       spin_unlock_irqrestore(&sp->tx_lock, flags);
+
+       /* Free all Rx buffers */
+       spin_lock_irqsave(&sp->rx_lock, flags);
        free_rx_buffers(sp);
+       spin_unlock_irqrestore(&sp->rx_lock, flags);
 
-       spin_unlock_irqrestore(&sp->tx_lock, flags);
        clear_bit(0, &(sp->link_state));
 }
 
@@ -4413,6 +4510,8 @@ static int s2io_card_up(nic_t * sp)
                return -ENODEV;
        }
 
+       S2IO_TIMER_CONF(sp->alarm_timer, s2io_alarm_handle, sp, (HZ/2));
+
        atomic_set(&sp->card_state, CARD_UP);
        return 0;
 }
@@ -4505,6 +4604,11 @@ static int rx_osm_handler(ring_info_t *ring_data, RxD_t * rxdp)
                unsigned long long err = rxdp->Control_1 & RXD_T_CODE;
                DBG_PRINT(ERR_DBG, "%s: Rx error Value: 0x%llx\n",
                          dev->name, err);
+               dev_kfree_skb(skb);
+               sp->stats.rx_crc_errors++;
+               atomic_dec(&sp->rx_bufs_left[ring_no]);
+               rxdp->Host_Control = 0;
+               return 0;
        }
 
        /* Updating statistics */
@@ -4647,7 +4751,6 @@ module_param(tx_fifo_num, int, 0);
 module_param(rx_ring_num, int, 0);
 module_param_array(tx_fifo_len, uint, NULL, 0);
 module_param_array(rx_ring_sz, uint, NULL, 0);
-module_param(Stats_refresh_time, int, 0);
 module_param_array(rts_frm_len, uint, NULL, 0);
 module_param(use_continuous_tx_intrs, int, 1);
 module_param(rmac_pause_time, int, 0);
@@ -4804,6 +4907,9 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre)
        for (i = 0; i < config->rx_ring_num; i++)
                atomic_set(&sp->rx_bufs_left[i], 0);
 
+       /* Initialize the number of ISRs currently running */
+       atomic_set(&sp->isr_cnt, 0);
+
        /*  initialize the shared memory used by the NIC and the host */
        if (init_shared_mem(sp)) {
                DBG_PRINT(ERR_DBG, "%s: Memory allocation failed\n",
@@ -4938,6 +5044,7 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre)
 #ifndef CONFIG_S2IO_NAPI
        spin_lock_init(&sp->put_lock);
 #endif
+       spin_lock_init(&sp->rx_lock);
 
        /*
         * SXE-002: Configure link and activity LED to init state
@@ -4961,13 +5068,16 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre)
                goto register_failed;
        }
 
+       /* Initialize device name */
+       strcpy(sp->name, dev->name);
+       strcat(sp->name, ": Neterion Xframe I 10GbE adapter");
+
        /*
         * Make Link state as off at this point, when the Link change
         * interrupt comes the state will be automatically changed to
         * the right state.
         */
        netif_carrier_off(dev);
-       sp->last_link_state = LINK_DOWN;
 
        return 0;