mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-24 01:41:39 +00:00
[PATCH] forcdeth: revised NAPI support
Revised version of the forcedeth NAPI support. This version is based against netdev-2.6#upstream (after the MAC patches from Ayaz today). Can't use nv_disable_hw_interrupts because NAPI only wants to mask off receive irq's and leave the others alone. Signed-off-by: Stephen Hemminger <shemminger@osdl.org> Signed-off-by: Jeff Garzik <jeff@garzik.org>
This commit is contained in:
parent
5070d34084
commit
e27cdba53b
2 changed files with 140 additions and 16 deletions
|
@ -1411,6 +1411,22 @@ config FORCEDETH
|
||||||
<file:Documentation/networking/net-modules.txt>. The module will be
|
<file:Documentation/networking/net-modules.txt>. The module will be
|
||||||
called forcedeth.
|
called forcedeth.
|
||||||
|
|
||||||
|
config FORCEDETH_NAPI
|
||||||
|
bool "Use Rx and Tx Polling (NAPI) (EXPERIMENTAL)"
|
||||||
|
depends on FORCEDETH && EXPERIMENTAL
|
||||||
|
help
|
||||||
|
NAPI is a new driver API designed to reduce CPU and interrupt load
|
||||||
|
when the driver is receiving lots of packets from the card. It is
|
||||||
|
still somewhat experimental and thus not yet enabled by default.
|
||||||
|
|
||||||
|
If your estimated Rx load is 10kpps or more, or if the card will be
|
||||||
|
deployed on potentially unfriendly networks (e.g. in a firewall),
|
||||||
|
then say Y here.
|
||||||
|
|
||||||
|
See <file:Documentation/networking/NAPI_HOWTO.txt> for more
|
||||||
|
information.
|
||||||
|
|
||||||
|
If in doubt, say N.
|
||||||
|
|
||||||
config CS89x0
|
config CS89x0
|
||||||
tristate "CS89x0 support"
|
tristate "CS89x0 support"
|
||||||
|
|
|
@ -121,6 +121,11 @@
|
||||||
* DEV_NEED_TIMERIRQ will not harm you on sane hardware, only generating a few
|
* DEV_NEED_TIMERIRQ will not harm you on sane hardware, only generating a few
|
||||||
* superfluous timer interrupts from the nic.
|
* superfluous timer interrupts from the nic.
|
||||||
*/
|
*/
|
||||||
|
#ifdef CONFIG_FORCEDETH_NAPI
|
||||||
|
#define DRIVERNAPI "-NAPI"
|
||||||
|
#else
|
||||||
|
#define DRIVERNAPI
|
||||||
|
#endif
|
||||||
#define FORCEDETH_VERSION "0.57"
|
#define FORCEDETH_VERSION "0.57"
|
||||||
#define DRV_NAME "forcedeth"
|
#define DRV_NAME "forcedeth"
|
||||||
|
|
||||||
|
@ -1279,6 +1284,16 @@ static int nv_alloc_rx(struct net_device *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If rx bufs are exhausted called after 50ms to attempt to refresh */
|
||||||
|
#ifdef CONFIG_FORCEDETH_NAPI
|
||||||
|
static void nv_do_rx_refill(unsigned long data)
|
||||||
|
{
|
||||||
|
struct net_device *dev = (struct net_device *) data;
|
||||||
|
|
||||||
|
/* Just reschedule NAPI rx processing */
|
||||||
|
netif_rx_schedule(dev);
|
||||||
|
}
|
||||||
|
#else
|
||||||
static void nv_do_rx_refill(unsigned long data)
|
static void nv_do_rx_refill(unsigned long data)
|
||||||
{
|
{
|
||||||
struct net_device *dev = (struct net_device *) data;
|
struct net_device *dev = (struct net_device *) data;
|
||||||
|
@ -1307,6 +1322,7 @@ static void nv_do_rx_refill(unsigned long data)
|
||||||
enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector);
|
enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void nv_init_rx(struct net_device *dev)
|
static void nv_init_rx(struct net_device *dev)
|
||||||
{
|
{
|
||||||
|
@ -1742,13 +1758,14 @@ static int nv_getlen(struct net_device *dev, void *packet, int datalen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nv_rx_process(struct net_device *dev)
|
static int nv_rx_process(struct net_device *dev, int limit)
|
||||||
{
|
{
|
||||||
struct fe_priv *np = netdev_priv(dev);
|
struct fe_priv *np = netdev_priv(dev);
|
||||||
u32 flags;
|
u32 flags;
|
||||||
u32 vlanflags = 0;
|
u32 vlanflags = 0;
|
||||||
|
int count;
|
||||||
|
|
||||||
for (;;) {
|
for (count = 0; count < limit; ++count) {
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
int len;
|
int len;
|
||||||
int i;
|
int i;
|
||||||
|
@ -1882,17 +1899,27 @@ static void nv_rx_process(struct net_device *dev)
|
||||||
skb->protocol = eth_type_trans(skb, dev);
|
skb->protocol = eth_type_trans(skb, dev);
|
||||||
dprintk(KERN_DEBUG "%s: nv_rx_process: packet %d with %d bytes, proto %d accepted.\n",
|
dprintk(KERN_DEBUG "%s: nv_rx_process: packet %d with %d bytes, proto %d accepted.\n",
|
||||||
dev->name, np->cur_rx, len, skb->protocol);
|
dev->name, np->cur_rx, len, skb->protocol);
|
||||||
if (np->vlangrp && (vlanflags & NV_RX3_VLAN_TAG_PRESENT)) {
|
#ifdef CONFIG_FORCEDETH_NAPI
|
||||||
vlan_hwaccel_rx(skb, np->vlangrp, vlanflags & NV_RX3_VLAN_TAG_MASK);
|
if (np->vlangrp && (vlanflags & NV_RX3_VLAN_TAG_PRESENT))
|
||||||
} else {
|
vlan_hwaccel_receive_skb(skb, np->vlangrp,
|
||||||
|
vlanflags & NV_RX3_VLAN_TAG_MASK);
|
||||||
|
else
|
||||||
|
netif_receive_skb(skb);
|
||||||
|
#else
|
||||||
|
if (np->vlangrp && (vlanflags & NV_RX3_VLAN_TAG_PRESENT))
|
||||||
|
vlan_hwaccel_rx(skb, np->vlangrp,
|
||||||
|
vlanflags & NV_RX3_VLAN_TAG_MASK);
|
||||||
|
else
|
||||||
netif_rx(skb);
|
netif_rx(skb);
|
||||||
}
|
#endif
|
||||||
dev->last_rx = jiffies;
|
dev->last_rx = jiffies;
|
||||||
np->stats.rx_packets++;
|
np->stats.rx_packets++;
|
||||||
np->stats.rx_bytes += len;
|
np->stats.rx_bytes += len;
|
||||||
next_pkt:
|
next_pkt:
|
||||||
np->cur_rx++;
|
np->cur_rx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_bufsize(struct net_device *dev)
|
static void set_bufsize(struct net_device *dev)
|
||||||
|
@ -2378,14 +2405,6 @@ static irqreturn_t nv_nic_irq(int foo, void *data, struct pt_regs *regs)
|
||||||
nv_tx_done(dev);
|
nv_tx_done(dev);
|
||||||
spin_unlock(&np->lock);
|
spin_unlock(&np->lock);
|
||||||
|
|
||||||
nv_rx_process(dev);
|
|
||||||
if (nv_alloc_rx(dev)) {
|
|
||||||
spin_lock(&np->lock);
|
|
||||||
if (!np->in_shutdown)
|
|
||||||
mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
|
|
||||||
spin_unlock(&np->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (events & NVREG_IRQ_LINK) {
|
if (events & NVREG_IRQ_LINK) {
|
||||||
spin_lock(&np->lock);
|
spin_lock(&np->lock);
|
||||||
nv_link_irq(dev);
|
nv_link_irq(dev);
|
||||||
|
@ -2405,6 +2424,29 @@ static irqreturn_t nv_nic_irq(int foo, void *data, struct pt_regs *regs)
|
||||||
printk(KERN_DEBUG "%s: received irq with unknown events 0x%x. Please report\n",
|
printk(KERN_DEBUG "%s: received irq with unknown events 0x%x. Please report\n",
|
||||||
dev->name, events);
|
dev->name, events);
|
||||||
}
|
}
|
||||||
|
#ifdef CONFIG_FORCEDETH_NAPI
|
||||||
|
if (events & NVREG_IRQ_RX_ALL) {
|
||||||
|
netif_rx_schedule(dev);
|
||||||
|
|
||||||
|
/* Disable furthur receive irq's */
|
||||||
|
spin_lock(&np->lock);
|
||||||
|
np->irqmask &= ~NVREG_IRQ_RX_ALL;
|
||||||
|
|
||||||
|
if (np->msi_flags & NV_MSI_X_ENABLED)
|
||||||
|
writel(NVREG_IRQ_RX_ALL, base + NvRegIrqMask);
|
||||||
|
else
|
||||||
|
writel(np->irqmask, base + NvRegIrqMask);
|
||||||
|
spin_unlock(&np->lock);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
nv_rx_process(dev, dev->weight);
|
||||||
|
if (nv_alloc_rx(dev)) {
|
||||||
|
spin_lock(&np->lock);
|
||||||
|
if (!np->in_shutdown)
|
||||||
|
mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
|
||||||
|
spin_unlock(&np->lock);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (i > max_interrupt_work) {
|
if (i > max_interrupt_work) {
|
||||||
spin_lock(&np->lock);
|
spin_lock(&np->lock);
|
||||||
/* disable interrupts on the nic */
|
/* disable interrupts on the nic */
|
||||||
|
@ -2476,6 +2518,63 @@ static irqreturn_t nv_nic_irq_tx(int foo, void *data, struct pt_regs *regs)
|
||||||
return IRQ_RETVAL(i);
|
return IRQ_RETVAL(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_FORCEDETH_NAPI
|
||||||
|
static int nv_napi_poll(struct net_device *dev, int *budget)
|
||||||
|
{
|
||||||
|
int pkts, limit = min(*budget, dev->quota);
|
||||||
|
struct fe_priv *np = netdev_priv(dev);
|
||||||
|
u8 __iomem *base = get_hwbase(dev);
|
||||||
|
|
||||||
|
pkts = nv_rx_process(dev, limit);
|
||||||
|
|
||||||
|
if (nv_alloc_rx(dev)) {
|
||||||
|
spin_lock_irq(&np->lock);
|
||||||
|
if (!np->in_shutdown)
|
||||||
|
mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
|
||||||
|
spin_unlock_irq(&np->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pkts < limit) {
|
||||||
|
/* all done, no more packets present */
|
||||||
|
netif_rx_complete(dev);
|
||||||
|
|
||||||
|
/* re-enable receive interrupts */
|
||||||
|
spin_lock_irq(&np->lock);
|
||||||
|
np->irqmask |= NVREG_IRQ_RX_ALL;
|
||||||
|
if (np->msi_flags & NV_MSI_X_ENABLED)
|
||||||
|
writel(NVREG_IRQ_RX_ALL, base + NvRegIrqMask);
|
||||||
|
else
|
||||||
|
writel(np->irqmask, base + NvRegIrqMask);
|
||||||
|
spin_unlock_irq(&np->lock);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
/* used up our quantum, so reschedule */
|
||||||
|
dev->quota -= pkts;
|
||||||
|
*budget -= pkts;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_FORCEDETH_NAPI
|
||||||
|
static irqreturn_t nv_nic_irq_rx(int foo, void *data, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct net_device *dev = (struct net_device *) data;
|
||||||
|
u8 __iomem *base = get_hwbase(dev);
|
||||||
|
u32 events;
|
||||||
|
|
||||||
|
events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQ_RX_ALL;
|
||||||
|
writel(NVREG_IRQ_RX_ALL, base + NvRegMSIXIrqStatus);
|
||||||
|
|
||||||
|
if (events) {
|
||||||
|
netif_rx_schedule(dev);
|
||||||
|
/* disable receive interrupts on the nic */
|
||||||
|
writel(NVREG_IRQ_RX_ALL, base + NvRegIrqMask);
|
||||||
|
pci_push(base);
|
||||||
|
}
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
#else
|
||||||
static irqreturn_t nv_nic_irq_rx(int foo, void *data, struct pt_regs *regs)
|
static irqreturn_t nv_nic_irq_rx(int foo, void *data, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
struct net_device *dev = (struct net_device *) data;
|
struct net_device *dev = (struct net_device *) data;
|
||||||
|
@ -2494,7 +2593,7 @@ static irqreturn_t nv_nic_irq_rx(int foo, void *data, struct pt_regs *regs)
|
||||||
if (!(events & np->irqmask))
|
if (!(events & np->irqmask))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
nv_rx_process(dev);
|
nv_rx_process(dev, dev->weight);
|
||||||
if (nv_alloc_rx(dev)) {
|
if (nv_alloc_rx(dev)) {
|
||||||
spin_lock_irq(&np->lock);
|
spin_lock_irq(&np->lock);
|
||||||
if (!np->in_shutdown)
|
if (!np->in_shutdown)
|
||||||
|
@ -2516,12 +2615,12 @@ static irqreturn_t nv_nic_irq_rx(int foo, void *data, struct pt_regs *regs)
|
||||||
spin_unlock_irq(&np->lock);
|
spin_unlock_irq(&np->lock);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
dprintk(KERN_DEBUG "%s: nv_nic_irq_rx completed\n", dev->name);
|
dprintk(KERN_DEBUG "%s: nv_nic_irq_rx completed\n", dev->name);
|
||||||
|
|
||||||
return IRQ_RETVAL(i);
|
return IRQ_RETVAL(i);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static irqreturn_t nv_nic_irq_other(int foo, void *data, struct pt_regs *regs)
|
static irqreturn_t nv_nic_irq_other(int foo, void *data, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
|
@ -3755,6 +3854,7 @@ static void nv_self_test(struct net_device *dev, struct ethtool_test *test, u64
|
||||||
if (test->flags & ETH_TEST_FL_OFFLINE) {
|
if (test->flags & ETH_TEST_FL_OFFLINE) {
|
||||||
if (netif_running(dev)) {
|
if (netif_running(dev)) {
|
||||||
netif_stop_queue(dev);
|
netif_stop_queue(dev);
|
||||||
|
netif_poll_disable(dev);
|
||||||
netif_tx_lock_bh(dev);
|
netif_tx_lock_bh(dev);
|
||||||
spin_lock_irq(&np->lock);
|
spin_lock_irq(&np->lock);
|
||||||
nv_disable_hw_interrupts(dev, np->irqmask);
|
nv_disable_hw_interrupts(dev, np->irqmask);
|
||||||
|
@ -3813,6 +3913,7 @@ static void nv_self_test(struct net_device *dev, struct ethtool_test *test, u64
|
||||||
nv_start_rx(dev);
|
nv_start_rx(dev);
|
||||||
nv_start_tx(dev);
|
nv_start_tx(dev);
|
||||||
netif_start_queue(dev);
|
netif_start_queue(dev);
|
||||||
|
netif_poll_enable(dev);
|
||||||
nv_enable_hw_interrupts(dev, np->irqmask);
|
nv_enable_hw_interrupts(dev, np->irqmask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4016,6 +4117,8 @@ static int nv_open(struct net_device *dev)
|
||||||
nv_start_rx(dev);
|
nv_start_rx(dev);
|
||||||
nv_start_tx(dev);
|
nv_start_tx(dev);
|
||||||
netif_start_queue(dev);
|
netif_start_queue(dev);
|
||||||
|
netif_poll_enable(dev);
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
netif_carrier_on(dev);
|
netif_carrier_on(dev);
|
||||||
} else {
|
} else {
|
||||||
|
@ -4045,6 +4148,7 @@ static int nv_close(struct net_device *dev)
|
||||||
spin_lock_irq(&np->lock);
|
spin_lock_irq(&np->lock);
|
||||||
np->in_shutdown = 1;
|
np->in_shutdown = 1;
|
||||||
spin_unlock_irq(&np->lock);
|
spin_unlock_irq(&np->lock);
|
||||||
|
netif_poll_disable(dev);
|
||||||
synchronize_irq(dev->irq);
|
synchronize_irq(dev->irq);
|
||||||
|
|
||||||
del_timer_sync(&np->oom_kick);
|
del_timer_sync(&np->oom_kick);
|
||||||
|
@ -4259,6 +4363,10 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
|
||||||
dev->set_multicast_list = nv_set_multicast;
|
dev->set_multicast_list = nv_set_multicast;
|
||||||
#ifdef CONFIG_NET_POLL_CONTROLLER
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||||
dev->poll_controller = nv_poll_controller;
|
dev->poll_controller = nv_poll_controller;
|
||||||
|
#endif
|
||||||
|
dev->weight = 64;
|
||||||
|
#ifdef CONFIG_FORCEDETH_NAPI
|
||||||
|
dev->poll = nv_napi_poll;
|
||||||
#endif
|
#endif
|
||||||
SET_ETHTOOL_OPS(dev, &ops);
|
SET_ETHTOOL_OPS(dev, &ops);
|
||||||
dev->tx_timeout = nv_tx_timeout;
|
dev->tx_timeout = nv_tx_timeout;
|
||||||
|
|
Loading…
Reference in a new issue