Commit 4145ade2 authored by Ayaz Abdulla's avatar Ayaz Abdulla Committed by David S. Miller

forcedeth: add interrupt moderation logic

This patch adds the logic to moderate the interrupts by changing the
mode between throughput and poll. If there has been a large amount of
time without any burst of network load, the code will transition to pure
throughput mode (where each tx/rx/other will cause an interrupt). If
bursts of network load occurs, it will transition to poll based mode to
help reduce cpu utilization (it will not interrupt on each packet) while
maintaining the optimum network bandwidth utilization.
Signed-off-by: default avatarAyaz Abdulla <aabdulla@nvidia.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b67874ac
...@@ -593,6 +593,9 @@ union ring_type { ...@@ -593,6 +593,9 @@ union ring_type {
#define NV_TX_LIMIT_COUNT 16 #define NV_TX_LIMIT_COUNT 16
#define NV_DYNAMIC_THRESHOLD 4
#define NV_DYNAMIC_MAX_QUIET_COUNT 2048
/* statistics */ /* statistics */
struct nv_ethtool_str { struct nv_ethtool_str {
char name[ETH_GSTRING_LEN]; char name[ETH_GSTRING_LEN];
...@@ -750,6 +753,7 @@ struct fe_priv { ...@@ -750,6 +753,7 @@ struct fe_priv {
u16 gigabit; u16 gigabit;
int intr_test; int intr_test;
int recover_error; int recover_error;
int quiet_count;
/* General data: RO fields */ /* General data: RO fields */
dma_addr_t ring_addr; dma_addr_t ring_addr;
...@@ -832,7 +836,7 @@ struct fe_priv { ...@@ -832,7 +836,7 @@ struct fe_priv {
* Maximum number of loops until we assume that a bit in the irq mask * Maximum number of loops until we assume that a bit in the irq mask
* is stuck. Overridable with module param. * is stuck. Overridable with module param.
*/ */
static int max_interrupt_work = 15; static int max_interrupt_work = 4;
/* /*
* Optimization can be either throuput mode or cpu mode * Optimization can be either throuput mode or cpu mode
...@@ -3418,11 +3422,43 @@ static void nv_msi_workaround(struct fe_priv *np) ...@@ -3418,11 +3422,43 @@ static void nv_msi_workaround(struct fe_priv *np)
} }
} }
static inline int nv_change_interrupt_mode(struct net_device *dev, int total_work)
{
struct fe_priv *np = netdev_priv(dev);
if (optimization_mode == NV_OPTIMIZATION_MODE_DYNAMIC) {
if (total_work > NV_DYNAMIC_THRESHOLD) {
/* transition to poll based interrupts */
np->quiet_count = 0;
if (np->irqmask != NVREG_IRQMASK_CPU) {
np->irqmask = NVREG_IRQMASK_CPU;
return 1;
}
} else {
if (np->quiet_count < NV_DYNAMIC_MAX_QUIET_COUNT) {
np->quiet_count++;
} else {
/* reached a period of low activity, switch
to per tx/rx packet interrupts */
if (np->irqmask != NVREG_IRQMASK_THROUGHPUT) {
np->irqmask = NVREG_IRQMASK_THROUGHPUT;
return 1;
}
}
}
}
return 0;
}
static irqreturn_t nv_nic_irq(int foo, void *data) static irqreturn_t nv_nic_irq(int foo, void *data)
{ {
struct net_device *dev = (struct net_device *) data; struct net_device *dev = (struct net_device *) data;
struct fe_priv *np = netdev_priv(dev); struct fe_priv *np = netdev_priv(dev);
u8 __iomem *base = get_hwbase(dev); u8 __iomem *base = get_hwbase(dev);
#ifndef CONFIG_FORCEDETH_NAPI
int total_work = 0;
int loop_count = 0;
#endif
dprintk(KERN_DEBUG "%s: nv_nic_irq\n", dev->name); dprintk(KERN_DEBUG "%s: nv_nic_irq\n", dev->name);
...@@ -3449,19 +3485,35 @@ static irqreturn_t nv_nic_irq(int foo, void *data) ...@@ -3449,19 +3485,35 @@ static irqreturn_t nv_nic_irq(int foo, void *data)
spin_unlock(&np->lock); spin_unlock(&np->lock);
return IRQ_HANDLED;
#else #else
spin_lock(&np->lock); do
nv_tx_done(dev, np->tx_ring_size); {
spin_unlock(&np->lock); int work = 0;
if ((work = nv_rx_process(dev, RX_WORK_PER_LOOP))) {
if (nv_rx_process(dev, RX_WORK_PER_LOOP)) { if (unlikely(nv_alloc_rx(dev))) {
if (unlikely(nv_alloc_rx(dev))) { spin_lock(&np->lock);
spin_lock(&np->lock); if (!np->in_shutdown)
if (!np->in_shutdown) mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
mod_timer(&np->oom_kick, jiffies + OOM_REFILL); spin_unlock(&np->lock);
spin_unlock(&np->lock); }
} }
spin_lock(&np->lock);
work += nv_tx_done(dev, TX_WORK_PER_LOOP);
spin_unlock(&np->lock);
if (!work)
break;
total_work += work;
loop_count++;
}
while (loop_count < max_interrupt_work);
if (nv_change_interrupt_mode(dev, total_work)) {
/* setup new irq mask */
writel(np->irqmask, base + NvRegIrqMask);
} }
if (unlikely(np->events & NVREG_IRQ_LINK)) { if (unlikely(np->events & NVREG_IRQ_LINK)) {
...@@ -3507,6 +3559,10 @@ static irqreturn_t nv_nic_irq_optimized(int foo, void *data) ...@@ -3507,6 +3559,10 @@ static irqreturn_t nv_nic_irq_optimized(int foo, void *data)
struct net_device *dev = (struct net_device *) data; struct net_device *dev = (struct net_device *) data;
struct fe_priv *np = netdev_priv(dev); struct fe_priv *np = netdev_priv(dev);
u8 __iomem *base = get_hwbase(dev); u8 __iomem *base = get_hwbase(dev);
#ifndef CONFIG_FORCEDETH_NAPI
int total_work = 0;
int loop_count = 0;
#endif
dprintk(KERN_DEBUG "%s: nv_nic_irq_optimized\n", dev->name); dprintk(KERN_DEBUG "%s: nv_nic_irq_optimized\n", dev->name);
...@@ -3533,19 +3589,35 @@ static irqreturn_t nv_nic_irq_optimized(int foo, void *data) ...@@ -3533,19 +3589,35 @@ static irqreturn_t nv_nic_irq_optimized(int foo, void *data)
spin_unlock(&np->lock); spin_unlock(&np->lock);
return IRQ_HANDLED;
#else #else
spin_lock(&np->lock); do
nv_tx_done_optimized(dev, TX_WORK_PER_LOOP); {
spin_unlock(&np->lock); int work = 0;
if ((work = nv_rx_process_optimized(dev, RX_WORK_PER_LOOP))) {
if (nv_rx_process_optimized(dev, RX_WORK_PER_LOOP)) { if (unlikely(nv_alloc_rx_optimized(dev))) {
if (unlikely(nv_alloc_rx_optimized(dev))) { spin_lock(&np->lock);
spin_lock(&np->lock); if (!np->in_shutdown)
if (!np->in_shutdown) mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
mod_timer(&np->oom_kick, jiffies + OOM_REFILL); spin_unlock(&np->lock);
spin_unlock(&np->lock); }
} }
spin_lock(&np->lock);
work += nv_tx_done_optimized(dev, TX_WORK_PER_LOOP);
spin_unlock(&np->lock);
if (!work)
break;
total_work += work;
loop_count++;
}
while (loop_count < max_interrupt_work);
if (nv_change_interrupt_mode(dev, total_work)) {
/* setup new irq mask */
writel(np->irqmask, base + NvRegIrqMask);
} }
if (unlikely(np->events & NVREG_IRQ_LINK)) { if (unlikely(np->events & NVREG_IRQ_LINK)) {
...@@ -3632,21 +3704,22 @@ static int nv_napi_poll(struct napi_struct *napi, int budget) ...@@ -3632,21 +3704,22 @@ static int nv_napi_poll(struct napi_struct *napi, int budget)
struct net_device *dev = np->dev; struct net_device *dev = np->dev;
u8 __iomem *base = get_hwbase(dev); u8 __iomem *base = get_hwbase(dev);
unsigned long flags; unsigned long flags;
int pkts, retcode; int retcode;
int tx_work, rx_work;
if (!nv_optimized(np)) { if (!nv_optimized(np)) {
spin_lock_irqsave(&np->lock, flags); spin_lock_irqsave(&np->lock, flags);
nv_tx_done(dev, np->tx_ring_size); tx_work = nv_tx_done(dev, np->tx_ring_size);
spin_unlock_irqrestore(&np->lock, flags); spin_unlock_irqrestore(&np->lock, flags);
pkts = nv_rx_process(dev, budget); rx_work = nv_rx_process(dev, budget);
retcode = nv_alloc_rx(dev); retcode = nv_alloc_rx(dev);
} else { } else {
spin_lock_irqsave(&np->lock, flags); spin_lock_irqsave(&np->lock, flags);
nv_tx_done_optimized(dev, np->tx_ring_size); tx_work = nv_tx_done_optimized(dev, np->tx_ring_size);
spin_unlock_irqrestore(&np->lock, flags); spin_unlock_irqrestore(&np->lock, flags);
pkts = nv_rx_process_optimized(dev, budget); rx_work = nv_rx_process_optimized(dev, budget);
retcode = nv_alloc_rx_optimized(dev); retcode = nv_alloc_rx_optimized(dev);
} }
...@@ -3657,6 +3730,8 @@ static int nv_napi_poll(struct napi_struct *napi, int budget) ...@@ -3657,6 +3730,8 @@ static int nv_napi_poll(struct napi_struct *napi, int budget)
spin_unlock_irqrestore(&np->lock, flags); spin_unlock_irqrestore(&np->lock, flags);
} }
nv_change_interrupt_mode(dev, tx_work + rx_work);
if (unlikely(np->events & NVREG_IRQ_LINK)) { if (unlikely(np->events & NVREG_IRQ_LINK)) {
spin_lock_irqsave(&np->lock, flags); spin_lock_irqsave(&np->lock, flags);
nv_link_irq(dev); nv_link_irq(dev);
...@@ -3677,10 +3752,10 @@ static int nv_napi_poll(struct napi_struct *napi, int budget) ...@@ -3677,10 +3752,10 @@ static int nv_napi_poll(struct napi_struct *napi, int budget)
} }
spin_unlock_irqrestore(&np->lock, flags); spin_unlock_irqrestore(&np->lock, flags);
__napi_complete(napi); __napi_complete(napi);
return pkts; return rx_work;
} }
if (pkts < budget) { if (rx_work < budget) {
/* re-enable interrupts /* re-enable interrupts
(msix not enabled in napi) */ (msix not enabled in napi) */
spin_lock_irqsave(&np->lock, flags); spin_lock_irqsave(&np->lock, flags);
...@@ -3691,7 +3766,7 @@ static int nv_napi_poll(struct napi_struct *napi, int budget) ...@@ -3691,7 +3766,7 @@ static int nv_napi_poll(struct napi_struct *napi, int budget)
spin_unlock_irqrestore(&np->lock, flags); spin_unlock_irqrestore(&np->lock, flags);
} }
return pkts; return rx_work;
} }
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment