diff options
author | Stephen Hemminger <shemminger@osdl.org> | 2006-12-08 14:08:33 -0500 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2006-12-11 09:51:07 -0500 |
commit | 7fe26a60e08f38c797851fb3b444d753af616112 (patch) | |
tree | d112dd98b9db72805e57c157ac670f74cd41ad0e /drivers/net/chelsio | |
parent | 0f0d84e52cb2a6e0b1d101484a92121410135da1 (diff) |
[PATCH] chelsio: working NAPI
This driver tries to enable/disable NAPI at runtime, but
does so in an unsafe manner, and the NAPI interrupt handling is
a mess. Replace it with a compile time selected NAPI implementation.
Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/net/chelsio')
-rw-r--r-- | drivers/net/chelsio/cxgb2.c | 23 | ||||
-rw-r--r-- | drivers/net/chelsio/sge.c | 115 | ||||
-rw-r--r-- | drivers/net/chelsio/sge.h | 4 |
3 files changed, 59 insertions, 83 deletions
diff --git a/drivers/net/chelsio/cxgb2.c b/drivers/net/chelsio/cxgb2.c index de48eadddbc4..fd5d821f3f2a 100644 --- a/drivers/net/chelsio/cxgb2.c +++ b/drivers/net/chelsio/cxgb2.c | |||
@@ -220,9 +220,8 @@ static int cxgb_up(struct adapter *adapter) | |||
220 | 220 | ||
221 | t1_interrupts_clear(adapter); | 221 | t1_interrupts_clear(adapter); |
222 | 222 | ||
223 | adapter->params.has_msi = !disable_msi && pci_enable_msi(adapter->pdev) == 0; | 223 | adapter->params.has_msi = !disable_msi && !pci_enable_msi(adapter->pdev); |
224 | err = request_irq(adapter->pdev->irq, | 224 | err = request_irq(adapter->pdev->irq, t1_interrupt, |
225 | t1_select_intr_handler(adapter), | ||
226 | adapter->params.has_msi ? 0 : IRQF_SHARED, | 225 | adapter->params.has_msi ? 0 : IRQF_SHARED, |
227 | adapter->name, adapter); | 226 | adapter->name, adapter); |
228 | if (err) { | 227 | if (err) { |
@@ -764,18 +763,7 @@ static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c) | |||
764 | { | 763 | { |
765 | struct adapter *adapter = dev->priv; | 764 | struct adapter *adapter = dev->priv; |
766 | 765 | ||
767 | /* | 766 | adapter->params.sge.rx_coalesce_usecs = c->rx_coalesce_usecs; |
768 | * If RX coalescing is requested we use NAPI, otherwise interrupts. | ||
769 | * This choice can be made only when all ports and the TOE are off. | ||
770 | */ | ||
771 | if (adapter->open_device_map == 0) | ||
772 | adapter->params.sge.polling = c->use_adaptive_rx_coalesce; | ||
773 | |||
774 | if (adapter->params.sge.polling) { | ||
775 | adapter->params.sge.rx_coalesce_usecs = 0; | ||
776 | } else { | ||
777 | adapter->params.sge.rx_coalesce_usecs = c->rx_coalesce_usecs; | ||
778 | } | ||
779 | adapter->params.sge.coalesce_enable = c->use_adaptive_rx_coalesce; | 767 | adapter->params.sge.coalesce_enable = c->use_adaptive_rx_coalesce; |
780 | adapter->params.sge.sample_interval_usecs = c->rate_sample_interval; | 768 | adapter->params.sge.sample_interval_usecs = c->rate_sample_interval; |
781 | t1_sge_set_coalesce_params(adapter->sge, &adapter->params.sge); | 769 | t1_sge_set_coalesce_params(adapter->sge, &adapter->params.sge); |
@@ -944,7 +932,7 @@ static void t1_netpoll(struct net_device *dev) | |||
944 | struct adapter *adapter = dev->priv; | 932 | struct adapter *adapter = dev->priv; |
945 | 933 | ||
946 | local_irq_save(flags); | 934 | local_irq_save(flags); |
947 | t1_select_intr_handler(adapter)(adapter->pdev->irq, adapter); | 935 | t1_interrupt(adapter->pdev->irq, adapter); |
948 | local_irq_restore(flags); | 936 | local_irq_restore(flags); |
949 | } | 937 | } |
950 | #endif | 938 | #endif |
@@ -1165,7 +1153,10 @@ static int __devinit init_one(struct pci_dev *pdev, | |||
1165 | #ifdef CONFIG_NET_POLL_CONTROLLER | 1153 | #ifdef CONFIG_NET_POLL_CONTROLLER |
1166 | netdev->poll_controller = t1_netpoll; | 1154 | netdev->poll_controller = t1_netpoll; |
1167 | #endif | 1155 | #endif |
1156 | #ifdef CONFIG_CHELSIO_T1_NAPI | ||
1168 | netdev->weight = 64; | 1157 | netdev->weight = 64; |
1158 | netdev->poll = t1_poll; | ||
1159 | #endif | ||
1169 | 1160 | ||
1170 | SET_ETHTOOL_OPS(netdev, &t1_ethtool_ops); | 1161 | SET_ETHTOOL_OPS(netdev, &t1_ethtool_ops); |
1171 | } | 1162 | } |
diff --git a/drivers/net/chelsio/sge.c b/drivers/net/chelsio/sge.c index 0ca8d876e16f..659cb2252e44 100644 --- a/drivers/net/chelsio/sge.c +++ b/drivers/net/chelsio/sge.c | |||
@@ -1413,16 +1413,20 @@ static int sge_rx(struct sge *sge, struct freelQ *fl, unsigned int len) | |||
1413 | 1413 | ||
1414 | if (unlikely(adapter->vlan_grp && p->vlan_valid)) { | 1414 | if (unlikely(adapter->vlan_grp && p->vlan_valid)) { |
1415 | st->vlan_xtract++; | 1415 | st->vlan_xtract++; |
1416 | if (adapter->params.sge.polling) | 1416 | #ifdef CONFIG_CHELSIO_T1_NAPI |
1417 | vlan_hwaccel_receive_skb(skb, adapter->vlan_grp, | 1417 | vlan_hwaccel_receive_skb(skb, adapter->vlan_grp, |
1418 | ntohs(p->vlan)); | 1418 | ntohs(p->vlan)); |
1419 | else | 1419 | #else |
1420 | vlan_hwaccel_rx(skb, adapter->vlan_grp, | 1420 | vlan_hwaccel_rx(skb, adapter->vlan_grp, |
1421 | ntohs(p->vlan)); | 1421 | ntohs(p->vlan)); |
1422 | } else if (adapter->params.sge.polling) | 1422 | #endif |
1423 | } else { | ||
1424 | #ifdef CONFIG_CHELSIO_T1_NAPI | ||
1423 | netif_receive_skb(skb); | 1425 | netif_receive_skb(skb); |
1424 | else | 1426 | #else |
1425 | netif_rx(skb); | 1427 | netif_rx(skb); |
1428 | #endif | ||
1429 | } | ||
1426 | return 0; | 1430 | return 0; |
1427 | } | 1431 | } |
1428 | 1432 | ||
@@ -1572,6 +1576,7 @@ static int process_responses(struct adapter *adapter, int budget) | |||
1572 | return budget; | 1576 | return budget; |
1573 | } | 1577 | } |
1574 | 1578 | ||
1579 | #ifdef CONFIG_CHELSIO_T1_NAPI | ||
1575 | /* | 1580 | /* |
1576 | * A simpler version of process_responses() that handles only pure (i.e., | 1581 | * A simpler version of process_responses() that handles only pure (i.e., |
1577 | * non data-carrying) responses. Such respones are too light-weight to justify | 1582 | * non data-carrying) responses. Such respones are too light-weight to justify |
@@ -1619,92 +1624,76 @@ static int process_pure_responses(struct adapter *adapter, struct respQ_e *e) | |||
1619 | * or protection from interrupts as data interrupts are off at this point and | 1624 | * or protection from interrupts as data interrupts are off at this point and |
1620 | * other adapter interrupts do not interfere. | 1625 | * other adapter interrupts do not interfere. |
1621 | */ | 1626 | */ |
1622 | static int t1_poll(struct net_device *dev, int *budget) | 1627 | int t1_poll(struct net_device *dev, int *budget) |
1623 | { | 1628 | { |
1624 | struct adapter *adapter = dev->priv; | 1629 | struct adapter *adapter = dev->priv; |
1625 | int effective_budget = min(*budget, dev->quota); | 1630 | int effective_budget = min(*budget, dev->quota); |
1626 | |||
1627 | int work_done = process_responses(adapter, effective_budget); | 1631 | int work_done = process_responses(adapter, effective_budget); |
1632 | |||
1628 | *budget -= work_done; | 1633 | *budget -= work_done; |
1629 | dev->quota -= work_done; | 1634 | dev->quota -= work_done; |
1630 | 1635 | ||
1631 | if (work_done >= effective_budget) | 1636 | if (work_done >= effective_budget) |
1632 | return 1; | 1637 | return 1; |
1633 | 1638 | ||
1639 | spin_lock_irq(&adapter->async_lock); | ||
1634 | __netif_rx_complete(dev); | 1640 | __netif_rx_complete(dev); |
1635 | |||
1636 | /* | ||
1637 | * Because we don't atomically flush the following write it is | ||
1638 | * possible that in very rare cases it can reach the device in a way | ||
1639 | * that races with a new response being written plus an error interrupt | ||
1640 | * causing the NAPI interrupt handler below to return unhandled status | ||
1641 | * to the OS. To protect against this would require flushing the write | ||
1642 | * and doing both the write and the flush with interrupts off. Way too | ||
1643 | * expensive and unjustifiable given the rarity of the race. | ||
1644 | */ | ||
1645 | writel(adapter->sge->respQ.cidx, adapter->regs + A_SG_SLEEPING); | 1641 | writel(adapter->sge->respQ.cidx, adapter->regs + A_SG_SLEEPING); |
1646 | return 0; | 1642 | writel(adapter->slow_intr_mask | F_PL_INTR_SGE_DATA, |
1647 | } | 1643 | adapter->regs + A_PL_ENABLE); |
1644 | spin_unlock_irq(&adapter->async_lock); | ||
1648 | 1645 | ||
1649 | /* | 1646 | return 0; |
1650 | * Returns true if the device is already scheduled for polling. | ||
1651 | */ | ||
1652 | static inline int napi_is_scheduled(struct net_device *dev) | ||
1653 | { | ||
1654 | return test_bit(__LINK_STATE_RX_SCHED, &dev->state); | ||
1655 | } | 1647 | } |
1656 | 1648 | ||
1657 | /* | 1649 | /* |
1658 | * NAPI version of the main interrupt handler. | 1650 | * NAPI version of the main interrupt handler. |
1659 | */ | 1651 | */ |
1660 | static irqreturn_t t1_interrupt_napi(int irq, void *data) | 1652 | irqreturn_t t1_interrupt(int irq, void *data) |
1661 | { | 1653 | { |
1662 | int handled; | ||
1663 | struct adapter *adapter = data; | 1654 | struct adapter *adapter = data; |
1655 | struct net_device *dev = adapter->sge->netdev; | ||
1664 | struct sge *sge = adapter->sge; | 1656 | struct sge *sge = adapter->sge; |
1665 | struct respQ *q = &adapter->sge->respQ; | 1657 | u32 cause; |
1658 | int handled = 0; | ||
1666 | 1659 | ||
1667 | /* | 1660 | cause = readl(adapter->regs + A_PL_CAUSE); |
1668 | * Clear the SGE_DATA interrupt first thing. Normally the NAPI | 1661 | if (cause == 0 || cause == ~0) |
1669 | * handler has control of the response queue and the interrupt handler | 1662 | return IRQ_NONE; |
1670 | * can look at the queue reliably only once it knows NAPI is off. | ||
1671 | * We can't wait that long to clear the SGE_DATA interrupt because we | ||
1672 | * could race with t1_poll rearming the SGE interrupt, so we need to | ||
1673 | * clear the interrupt speculatively and really early on. | ||
1674 | */ | ||
1675 | writel(F_PL_INTR_SGE_DATA, adapter->regs + A_PL_CAUSE); | ||
1676 | 1663 | ||
1677 | spin_lock(&adapter->async_lock); | 1664 | spin_lock(&adapter->async_lock); |
1678 | if (!napi_is_scheduled(sge->netdev)) { | 1665 | if (cause & F_PL_INTR_SGE_DATA) { |
1666 | struct respQ *q = &adapter->sge->respQ; | ||
1679 | struct respQ_e *e = &q->entries[q->cidx]; | 1667 | struct respQ_e *e = &q->entries[q->cidx]; |
1680 | 1668 | ||
1681 | if (e->GenerationBit == q->genbit) { | 1669 | handled = 1; |
1682 | if (e->DataValid || | 1670 | writel(F_PL_INTR_SGE_DATA, adapter->regs + A_PL_CAUSE); |
1683 | process_pure_responses(adapter, e)) { | 1671 | |
1684 | if (likely(__netif_rx_schedule_prep(sge->netdev))) | 1672 | if (e->GenerationBit == q->genbit && |
1685 | __netif_rx_schedule(sge->netdev); | 1673 | __netif_rx_schedule_prep(dev)) { |
1686 | else if (net_ratelimit()) | 1674 | if (e->DataValid || process_pure_responses(adapter, e)) { |
1687 | printk(KERN_INFO | 1675 | /* mask off data IRQ */ |
1688 | "NAPI schedule failure!\n"); | 1676 | writel(adapter->slow_intr_mask, |
1689 | } else | 1677 | adapter->regs + A_PL_ENABLE); |
1690 | writel(q->cidx, adapter->regs + A_SG_SLEEPING); | 1678 | __netif_rx_schedule(sge->netdev); |
1691 | 1679 | goto unlock; | |
1692 | handled = 1; | 1680 | } |
1693 | goto unlock; | 1681 | /* no data, no NAPI needed */ |
1694 | } else | 1682 | netif_poll_enable(dev); |
1695 | writel(q->cidx, adapter->regs + A_SG_SLEEPING); | 1683 | |
1696 | } else if (readl(adapter->regs + A_PL_CAUSE) & F_PL_INTR_SGE_DATA) { | 1684 | } |
1697 | printk(KERN_ERR "data interrupt while NAPI running\n"); | 1685 | writel(q->cidx, adapter->regs + A_SG_SLEEPING); |
1698 | } | 1686 | } else |
1699 | 1687 | handled = t1_slow_intr_handler(adapter); | |
1700 | handled = t1_slow_intr_handler(adapter); | 1688 | |
1701 | if (!handled) | 1689 | if (!handled) |
1702 | sge->stats.unhandled_irqs++; | 1690 | sge->stats.unhandled_irqs++; |
1703 | unlock: | 1691 | unlock: |
1704 | spin_unlock(&adapter->async_lock); | 1692 | spin_unlock(&adapter->async_lock); |
1705 | return IRQ_RETVAL(handled != 0); | 1693 | return IRQ_RETVAL(handled != 0); |
1706 | } | 1694 | } |
1707 | 1695 | ||
1696 | #else | ||
1708 | /* | 1697 | /* |
1709 | * Main interrupt handler, optimized assuming that we took a 'DATA' | 1698 | * Main interrupt handler, optimized assuming that we took a 'DATA' |
1710 | * interrupt. | 1699 | * interrupt. |
@@ -1720,7 +1709,7 @@ static irqreturn_t t1_interrupt_napi(int irq, void *data) | |||
1720 | * 5. If we took an interrupt, but no valid respQ descriptors was found we | 1709 | * 5. If we took an interrupt, but no valid respQ descriptors was found we |
1721 | * let the slow_intr_handler run and do error handling. | 1710 | * let the slow_intr_handler run and do error handling. |
1722 | */ | 1711 | */ |
1723 | static irqreturn_t t1_interrupt(int irq, void *cookie) | 1712 | irqreturn_t t1_interrupt(int irq, void *cookie) |
1724 | { | 1713 | { |
1725 | int work_done; | 1714 | int work_done; |
1726 | struct respQ_e *e; | 1715 | struct respQ_e *e; |
@@ -1752,11 +1741,7 @@ static irqreturn_t t1_interrupt(int irq, void *cookie) | |||
1752 | spin_unlock(&adapter->async_lock); | 1741 | spin_unlock(&adapter->async_lock); |
1753 | return IRQ_RETVAL(work_done != 0); | 1742 | return IRQ_RETVAL(work_done != 0); |
1754 | } | 1743 | } |
1755 | 1744 | #endif | |
1756 | irq_handler_t t1_select_intr_handler(adapter_t *adapter) | ||
1757 | { | ||
1758 | return adapter->params.sge.polling ? t1_interrupt_napi : t1_interrupt; | ||
1759 | } | ||
1760 | 1745 | ||
1761 | /* | 1746 | /* |
1762 | * Enqueues the sk_buff onto the cmdQ[qid] and has hardware fetch it. | 1747 | * Enqueues the sk_buff onto the cmdQ[qid] and has hardware fetch it. |
@@ -2033,7 +2018,6 @@ static void sge_tx_reclaim_cb(unsigned long data) | |||
2033 | */ | 2018 | */ |
2034 | int t1_sge_set_coalesce_params(struct sge *sge, struct sge_params *p) | 2019 | int t1_sge_set_coalesce_params(struct sge *sge, struct sge_params *p) |
2035 | { | 2020 | { |
2036 | sge->netdev->poll = t1_poll; | ||
2037 | sge->fixed_intrtimer = p->rx_coalesce_usecs * | 2021 | sge->fixed_intrtimer = p->rx_coalesce_usecs * |
2038 | core_ticks_per_usec(sge->adapter); | 2022 | core_ticks_per_usec(sge->adapter); |
2039 | writel(sge->fixed_intrtimer, sge->adapter->regs + A_SG_INTRTIMER); | 2023 | writel(sge->fixed_intrtimer, sge->adapter->regs + A_SG_INTRTIMER); |
@@ -2234,7 +2218,6 @@ struct sge * __devinit t1_sge_create(struct adapter *adapter, | |||
2234 | 2218 | ||
2235 | p->coalesce_enable = 0; | 2219 | p->coalesce_enable = 0; |
2236 | p->sample_interval_usecs = 0; | 2220 | p->sample_interval_usecs = 0; |
2237 | p->polling = 0; | ||
2238 | 2221 | ||
2239 | return sge; | 2222 | return sge; |
2240 | nomem_port: | 2223 | nomem_port: |
diff --git a/drivers/net/chelsio/sge.h b/drivers/net/chelsio/sge.h index 7ceb0117d039..d132a0ef2a22 100644 --- a/drivers/net/chelsio/sge.h +++ b/drivers/net/chelsio/sge.h | |||
@@ -76,7 +76,9 @@ struct sge *t1_sge_create(struct adapter *, struct sge_params *); | |||
76 | int t1_sge_configure(struct sge *, struct sge_params *); | 76 | int t1_sge_configure(struct sge *, struct sge_params *); |
77 | int t1_sge_set_coalesce_params(struct sge *, struct sge_params *); | 77 | int t1_sge_set_coalesce_params(struct sge *, struct sge_params *); |
78 | void t1_sge_destroy(struct sge *); | 78 | void t1_sge_destroy(struct sge *); |
79 | irq_handler_t t1_select_intr_handler(adapter_t *adapter); | 79 | irqreturn_t t1_interrupt(int irq, void *cookie); |
80 | int t1_poll(struct net_device *, int *); | ||
81 | |||
80 | int t1_start_xmit(struct sk_buff *skb, struct net_device *dev); | 82 | int t1_start_xmit(struct sk_buff *skb, struct net_device *dev); |
81 | void t1_set_vlan_accel(struct adapter *adapter, int on_off); | 83 | void t1_set_vlan_accel(struct adapter *adapter, int on_off); |
82 | void t1_sge_start(struct sge *); | 84 | void t1_sge_start(struct sge *); |