* Re: [PATCH 0/6] forcedeth interrupt and task overhaul, v2
From: Jeff Garzik @ 2007-10-17 5:59 UTC (permalink / raw)
To: netdev, LKML; +Cc: Manfred Spraul, Ingo Molnar, Ayaz Abdulla
In-Reply-To: <20071017055251.GA19972@havoc.gtf.org>
Jeff Garzik wrote:
> These six changes can be found in the 'fe' branch of
> git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/netdev-2.6.git
Note the change from the previous 'fe-lock' branch, which is now left
as-is on kernel.org, but not being updated.
^ permalink raw reply
* [PATCH 6/6] [netdrvr] interrupt handling overhaul
From: Jeff Garzik @ 2007-10-17 5:55 UTC (permalink / raw)
To: netdev, LKML; +Cc: Manfred Spraul, Ingo Molnar, Ayaz Abdulla
In-Reply-To: <20071017055251.GA19972@havoc.gtf.org>
commit 4f97856cd73ad3ccee06f1856c60cb1ed8f44ceb
Author: Jeff Garzik <jeff@garzik.org>
Date: Tue Oct 16 19:48:15 2007 -0400
[netdrvr] interrupt handling overhaul
* eliminate the work loops in the interrupt handlers. they are no
longer needed, now that NAPI and other asynchronous tasks handle our
work (and can be independently controlled by means other than
disable_irq or spin_lock_irqsave).
* make ->poll_controller call interrupt handler directly
* now that nv_do_nic_poll()'s only users are those that request
recovery, we can rename it to nv_reset_task(), and convert it from a
timer-context function to a workqueue.
* ->tx_timeout TX reset is replaced by requesting recovery
* in the main irq handler (INTx or MSI), the spinlock is always
taken in the common case: TX-or-RX work available. take the lock once
for any work, near the beginning of the irq handler (after status is
read and cleared).
This accomplishes the goal of getting a np->irqmask access inside the
lock, simplifies the code, and enables better control over irq
masking.
* no need to take lock, simply to schedule timer
* MSI-X RX/TX irq handling micro-optimization
* use IRQ_NONE/IRQ_HANDLED in nv_nic_irq_test(), rather than the less
readable versions of the same values.
* in nv_close(), make sure to cancel the workqueue tasks first thing,
to ensure they (mainly the reset task) will not race with interface
shutdown.
* move now-pointless tests of np->recover_error and netif_running()
in nv_reset_task(), resulting in a large de-indent of existing code.
* eliminate now-useless max_interrupt_work module option. though we
might want to consider permitting tuning of NAPI weights via ethtool,
in the future.
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
drivers/net/forcedeth.c | 385 +++++++++++++++++++-----------------------------
1 file changed, 158 insertions(+), 227 deletions(-)
4f97856cd73ad3ccee06f1856c60cb1ed8f44ceb
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index 81fe016..8af9a96 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -58,6 +58,7 @@
#include <linux/init.h>
#include <linux/if_vlan.h>
#include <linux/dma-mapping.h>
+#include <linux/workqueue.h>
#include <asm/irq.h>
#include <asm/io.h>
@@ -676,7 +677,6 @@ struct fe_priv {
unsigned int phy_model;
u16 gigabit;
int intr_test;
- int recover_error;
/* General data: RO fields */
dma_addr_t ring_addr;
@@ -701,14 +701,14 @@ struct fe_priv {
struct nv_skb_map *first_rx_ctx, *last_rx_ctx;
struct nv_skb_map *rx_skb;
- union ring_type rx_ring;
- unsigned int rx_buf_sz;
- unsigned int pkt_limit;
- struct timer_list oom_kick;
- struct timer_list nic_poll;
- struct delayed_work stats_task;
- u32 nic_poll_irq;
- int rx_ring_size;
+ union ring_type rx_ring;
+ unsigned int rx_buf_sz;
+ unsigned int pkt_limit;
+ struct timer_list oom_kick;
+ struct work_struct reset_task;
+ struct delayed_work stats_task;
+ u32 nic_poll_irq;
+ int rx_ring_size;
/* media detection workaround.
* Locking: Within irq hander or disable_irq+spin_lock(&np->lock);
@@ -739,12 +739,6 @@ struct fe_priv {
};
/*
- * Maximum number of loops until we assume that a bit in the irq mask
- * is stuck. Overridable with module param.
- */
-static int max_interrupt_work = 5;
-
-/*
* Optimization can be either throuput mode or cpu mode
*
* Throughput Mode: Every tx and rx packet will generate an interrupt.
@@ -2116,27 +2110,9 @@ static void nv_tx_timeout(struct net_device *dev)
spin_lock_irq(&np->lock);
- /* 1) stop tx engine */
- nv_stop_tx(dev);
-
- /* 2) check that the packets were not sent already: */
- if (!nv_optimized(np))
- nv_tx_done(dev, np->tx_ring_size);
- else
- nv_tx_done_optimized(dev, np->tx_ring_size);
-
- /* 3) if there are dead entries: clear everything */
- if (np->get_tx_ctx != np->put_tx_ctx) {
- printk(KERN_DEBUG "%s: tx_timeout: dead entries!\n", dev->name);
- nv_drain_tx(dev);
- nv_init_tx(dev);
- setup_hw_rings(dev, NV_SETUP_TX_RING);
- }
-
- netif_wake_queue(dev);
+ np->nic_poll_irq = np->irqmask;
+ schedule_work(&np->reset_task);
- /* 4) restart tx engine */
- nv_start_tx(dev);
spin_unlock_irq(&np->lock);
}
@@ -2890,101 +2866,82 @@ static irqreturn_t __nv_nic_irq(struct net_device *dev, bool optimized)
struct fe_priv *np = netdev_priv(dev);
u8 __iomem *base = np->base;
u32 events, updmask = 0;
- int i;
+ bool msix = np->msi_flags & NV_MSI_X_ENABLED;
dprintk(KERN_DEBUG "%s: __nv_nic_irq\n", dev->name);
- for (i=0; ; i++) {
- if (!(np->msi_flags & NV_MSI_X_ENABLED)) {
- events = readl(base + NvRegIrqStatus) & NVREG_IRQSTAT_MASK;
- writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
- } else {
- events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQSTAT_MASK;
- writel(NVREG_IRQSTAT_MASK, base + NvRegMSIXIrqStatus);
- }
- dprintk(KERN_DEBUG "%s: irq: %08x\n", dev->name, events);
- if (!(events & np->irqmask))
- break;
+ if (!msix) {
+ events = readl(base + NvRegIrqStatus) & NVREG_IRQSTAT_MASK;
+ writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
+ } else {
+ events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQSTAT_MASK;
+ writel(NVREG_IRQSTAT_MASK, base + NvRegMSIXIrqStatus);
+ }
- if (events & NVREG_IRQ_RX_ALL) {
- netif_rx_schedule(dev, &np->napi);
- updmask |= NVREG_IRQ_RX_ALL;
- }
+ dprintk(KERN_DEBUG "%s: irq: %08x\n", dev->name, events);
- if (events & NVREG_IRQ_TX_ALL) {
- netif_rx_schedule(dev, &np->tx_napi);
- updmask |= NVREG_IRQ_TX_ALL;
- }
+ if (!events)
+ return IRQ_NONE;
- if (updmask) {
- spin_lock(&np->lock);
- np->irqmask &= ~updmask;
+ spin_lock(&np->lock);
- if (np->msi_flags & NV_MSI_X_ENABLED)
- writel(updmask, base + NvRegIrqMask);
- else
- writel(np->irqmask, base + NvRegIrqMask);
- spin_unlock(&np->lock);
- }
+ if (!(events & np->irqmask))
+ goto out;
- if (unlikely(events & NVREG_IRQ_LINK)) {
- spin_lock(&np->lock);
- nv_link_irq(dev);
- spin_unlock(&np->lock);
- }
- if (unlikely(np->need_linktimer && time_after(jiffies, np->link_timeout))) {
- spin_lock(&np->lock);
- nv_linkchange(dev);
- spin_unlock(&np->lock);
- np->link_timeout = jiffies + LINK_TIMEOUT;
- }
- if (unlikely(events & (NVREG_IRQ_TX_ERR))) {
- dprintk(KERN_DEBUG "%s: received irq with events 0x%x. Probably TX fail.\n",
- dev->name, events);
- }
- if (unlikely(events & (NVREG_IRQ_UNKNOWN))) {
- printk(KERN_DEBUG "%s: received irq with unknown events 0x%x. Please report\n",
- dev->name, events);
- }
- if (unlikely(events & NVREG_IRQ_RECOVER_ERROR)) {
- spin_lock(&np->lock);
- /* disable interrupts on the nic */
- if (!(np->msi_flags & NV_MSI_X_ENABLED))
- writel(0, base + NvRegIrqMask);
- else
- writel(np->irqmask, base + NvRegIrqMask);
- pci_push(base);
+ if (events & NVREG_IRQ_RX_ALL) {
+ netif_rx_schedule(dev, &np->napi);
+ updmask |= NVREG_IRQ_RX_ALL;
+ }
- if (netif_running(dev)) {
- np->nic_poll_irq = np->irqmask;
- np->recover_error = 1;
- mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
- }
- spin_unlock(&np->lock);
- break;
- }
- if (unlikely(i > max_interrupt_work)) {
- spin_lock(&np->lock);
- /* disable interrupts on the nic */
- if (!(np->msi_flags & NV_MSI_X_ENABLED))
- writel(0, base + NvRegIrqMask);
- else
- writel(np->irqmask, base + NvRegIrqMask);
- pci_push(base);
+ if (events & NVREG_IRQ_TX_ALL) {
+ netif_rx_schedule(dev, &np->tx_napi);
+ updmask |= NVREG_IRQ_TX_ALL;
+ }
- if (netif_running(dev)) {
- np->nic_poll_irq = np->irqmask;
- mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
- }
- spin_unlock(&np->lock);
- printk(KERN_DEBUG "%s: too many iterations (%d) in nv_nic_irq.\n", dev->name, i);
- break;
- }
+ if (updmask) {
+ np->irqmask &= ~updmask;
+
+ if (!msix)
+ writel(updmask, base + NvRegIrqMask);
+ else
+ writel(np->irqmask, base + NvRegIrqMask);
+ }
+
+ if (unlikely(events & NVREG_IRQ_LINK))
+ nv_link_irq(dev);
+
+ if (unlikely(np->need_linktimer && time_after(jiffies, np->link_timeout))) {
+ nv_linkchange(dev);
+ np->link_timeout = jiffies + LINK_TIMEOUT;
+ }
+ if (unlikely(events & (NVREG_IRQ_TX_ERR))) {
+ dprintk(KERN_DEBUG "%s: received irq with events 0x%x. Probably TX fail.\n",
+ dev->name, events);
+ }
+ if (unlikely(events & (NVREG_IRQ_UNKNOWN))) {
+ printk(KERN_DEBUG "%s: received irq with unknown events 0x%x. Please report\n",
+ dev->name, events);
+ }
+ if (unlikely(events & NVREG_IRQ_RECOVER_ERROR)) {
+ /* disable interrupts on the nic */
+ if (!msix)
+ writel(0, base + NvRegIrqMask);
+ else
+ writel(np->irqmask, base + NvRegIrqMask);
+ pci_push(base);
+ if (netif_running(dev)) {
+ np->nic_poll_irq = np->irqmask;
+ schedule_work(&np->reset_task);
+ }
}
+
+out:
+ spin_unlock(&np->lock);
+
dprintk(KERN_DEBUG "%s: __nv_nic_irq completed\n", dev->name);
- return IRQ_RETVAL(i);
+ return IRQ_HANDLED;
}
static irqreturn_t nv_nic_irq(int foo, void *data)
@@ -3015,12 +2972,8 @@ static int nv_napi_poll(struct napi_struct *napi, int budget)
retcode = nv_alloc_rx_optimized(dev);
}
- if (retcode) {
- spin_lock_irqsave(&np->lock, flags);
- if (netif_running(dev))
- mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
- spin_unlock_irqrestore(&np->lock, flags);
- }
+ if (retcode && netif_running(dev))
+ mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
if (pkts < budget) {
/* re-enable receive interrupts */
@@ -3047,14 +3000,16 @@ static irqreturn_t nv_nic_irq_rx(int foo, void *data)
u32 events;
events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQ_RX_ALL;
- writel(NVREG_IRQ_RX_ALL, base + NvRegMSIXIrqStatus);
- if (events) {
+ if (likely(events)) {
+ writel(NVREG_IRQ_RX_ALL, base + NvRegMSIXIrqStatus);
+
netif_rx_schedule(dev, &np->napi);
/* disable receive interrupts on the nic */
writel(NVREG_IRQ_RX_ALL, base + NvRegIrqMask);
pci_push(base);
}
+
return IRQ_HANDLED;
}
@@ -3066,14 +3021,16 @@ static irqreturn_t nv_nic_irq_tx(int foo, void *data)
u32 events;
events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQ_TX_ALL;
- writel(NVREG_IRQ_TX_ALL, base + NvRegMSIXIrqStatus);
- if (events) {
+ if (likely(events)) {
+ writel(NVREG_IRQ_TX_ALL, base + NvRegMSIXIrqStatus);
+
netif_rx_schedule(dev, &np->tx_napi);
/* disable receive interrupts on the nic */
writel(NVREG_IRQ_TX_ALL, base + NvRegIrqMask);
pci_push(base);
}
+
return IRQ_HANDLED;
}
@@ -3115,71 +3072,46 @@ static irqreturn_t nv_nic_irq_other(int foo, void *data)
struct fe_priv *np = netdev_priv(dev);
u8 __iomem *base = get_hwbase(dev);
u32 events;
- int i;
- unsigned long flags;
dprintk(KERN_DEBUG "%s: nv_nic_irq_other\n", dev->name);
- for (i=0; ; i++) {
- events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQ_OTHER;
- writel(NVREG_IRQ_OTHER, base + NvRegMSIXIrqStatus);
- dprintk(KERN_DEBUG "%s: irq: %08x\n", dev->name, events);
- if (!(events & np->irqmask))
- break;
+ spin_lock(&np->lock);
- /* check tx in case we reached max loop limit in tx isr */
- spin_lock_irqsave(&np->lock, flags);
- nv_tx_done_optimized(dev, TX_WORK_PER_LOOP);
- spin_unlock_irqrestore(&np->lock, flags);
+ events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQ_OTHER;
+ writel(NVREG_IRQ_OTHER, base + NvRegMSIXIrqStatus);
- if (events & NVREG_IRQ_LINK) {
- spin_lock_irqsave(&np->lock, flags);
- nv_link_irq(dev);
- spin_unlock_irqrestore(&np->lock, flags);
- }
- if (np->need_linktimer && time_after(jiffies, np->link_timeout)) {
- spin_lock_irqsave(&np->lock, flags);
- nv_linkchange(dev);
- spin_unlock_irqrestore(&np->lock, flags);
- np->link_timeout = jiffies + LINK_TIMEOUT;
- }
- if (events & NVREG_IRQ_RECOVER_ERROR) {
- spin_lock_irq(&np->lock);
- /* disable interrupts on the nic */
- writel(NVREG_IRQ_OTHER, base + NvRegIrqMask);
- pci_push(base);
+ dprintk(KERN_DEBUG "%s: irq: %08x\n", dev->name, events);
+ if (!(events & np->irqmask))
+ goto out;
- if (netif_running(dev)) {
- np->nic_poll_irq |= NVREG_IRQ_OTHER;
- np->recover_error = 1;
- mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
- }
- spin_unlock_irq(&np->lock);
- break;
- }
- if (events & (NVREG_IRQ_UNKNOWN)) {
- printk(KERN_DEBUG "%s: received irq with unknown events 0x%x. Please report\n",
- dev->name, events);
- }
- if (unlikely(i > max_interrupt_work)) {
- spin_lock_irqsave(&np->lock, flags);
- /* disable interrupts on the nic */
- writel(NVREG_IRQ_OTHER, base + NvRegIrqMask);
- pci_push(base);
+ if (events & NVREG_IRQ_LINK)
+ nv_link_irq(dev);
- if (netif_running(dev)) {
- np->nic_poll_irq |= NVREG_IRQ_OTHER;
- mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
- }
- spin_unlock_irqrestore(&np->lock, flags);
- printk(KERN_DEBUG "%s: too many iterations (%d) in nv_nic_irq_other.\n", dev->name, i);
- break;
- }
+ if (np->need_linktimer && time_after(jiffies, np->link_timeout)) {
+ nv_linkchange(dev);
+ np->link_timeout = jiffies + LINK_TIMEOUT;
+ }
+ if (events & NVREG_IRQ_RECOVER_ERROR) {
+ /* disable interrupts on the nic */
+ writel(NVREG_IRQ_OTHER, base + NvRegIrqMask);
+ pci_push(base);
+ if (netif_running(dev)) {
+ np->nic_poll_irq |= NVREG_IRQ_OTHER;
+ schedule_work(&np->reset_task);
+ }
+ }
+ if (events & (NVREG_IRQ_UNKNOWN)) {
+ printk(KERN_DEBUG "%s: received irq with unknown events 0x%x. Please report\n",
+ dev->name, events);
}
+
+out:
+ spin_unlock(&np->lock);
+
dprintk(KERN_DEBUG "%s: nv_nic_irq_other completed\n", dev->name);
- return IRQ_RETVAL(i);
+ return IRQ_HANDLED;
}
static irqreturn_t nv_nic_irq_test(int foo, void *data)
@@ -3201,7 +3133,7 @@ static irqreturn_t nv_nic_irq_test(int foo, void *data)
pci_push(base);
dprintk(KERN_DEBUG "%s: irq: %08x\n", dev->name, events);
if (!(events & NVREG_IRQ_TIMER))
- return IRQ_RETVAL(0);
+ return IRQ_NONE;
spin_lock(&np->lock);
np->intr_test = 1;
@@ -3209,7 +3141,7 @@ static irqreturn_t nv_nic_irq_test(int foo, void *data)
dprintk(KERN_DEBUG "%s: nv_nic_irq_test completed\n", dev->name);
- return IRQ_RETVAL(1);
+ return IRQ_HANDLED;
}
static void set_msix_vector_map(struct net_device *dev, u32 vector, u32 irqmask)
@@ -3356,11 +3288,11 @@ static void nv_free_irq(struct net_device *dev)
}
}
-static void nv_do_nic_poll(unsigned long data)
+static void nv_reset_task(struct work_struct *work)
{
- struct net_device *dev = (struct net_device *) data;
- struct fe_priv *np = netdev_priv(dev);
- u8 __iomem *base = get_hwbase(dev);
+ struct fe_priv *np = container_of(work, struct fe_priv, reset_task);
+ struct net_device *dev = np->dev;
+ u8 __iomem *base = np->base;
u32 mask = 0;
/*
@@ -3389,39 +3321,38 @@ static void nv_do_nic_poll(unsigned long data)
mask |= NVREG_IRQ_OTHER;
}
}
+
+ dev_printk(KERN_INFO, &np->pci_dev->dev, "resetting MAC "
+ "(pimask %x, mask %x)\n",
+ np->nic_poll_irq, mask);
+
np->nic_poll_irq = 0;
- if (np->recover_error) {
- np->recover_error = 0;
- printk(KERN_INFO "forcedeth: MAC in recoverable error state\n");
- if (netif_running(dev)) {
- netif_tx_lock_bh(dev);
- spin_lock(&np->lock);
- /* stop engines */
- nv_stop_rxtx(dev);
- nv_txrx_reset(dev);
- /* drain rx queue */
- nv_drain_rxtx(dev);
- /* reinit driver view of the rx queue */
- set_bufsize(dev);
- if (nv_init_ring(dev))
- mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
+ netif_tx_lock_bh(dev);
+ spin_lock(&np->lock);
+ /* stop engines */
+ nv_stop_rxtx(dev);
+ nv_txrx_reset(dev);
+ /* drain rx queue */
+ nv_drain_rxtx(dev);
+ /* reinit driver view of the rx queue */
+ set_bufsize(dev);
+ if (nv_init_ring(dev))
+ mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
- /* reinit nic view of the rx queue */
- writel(np->rx_buf_sz, base + NvRegOffloadConfig);
- setup_hw_rings(dev, NV_SETUP_RX_RING | NV_SETUP_TX_RING);
- writel( ((np->rx_ring_size-1) << NVREG_RINGSZ_RXSHIFT) + ((np->tx_ring_size-1) << NVREG_RINGSZ_TXSHIFT),
- base + NvRegRingSizes);
- pci_push(base);
- writel(NVREG_TXRXCTL_KICK|np->txrxctl_bits, get_hwbase(dev) + NvRegTxRxControl);
- pci_push(base);
+ /* reinit nic view of the rx queue */
+ writel(np->rx_buf_sz, base + NvRegOffloadConfig);
+ setup_hw_rings(dev, NV_SETUP_RX_RING | NV_SETUP_TX_RING);
+ writel( ((np->rx_ring_size-1) << NVREG_RINGSZ_RXSHIFT) + ((np->tx_ring_size-1) << NVREG_RINGSZ_TXSHIFT),
+ base + NvRegRingSizes);
+ pci_push(base);
+ writel(NVREG_TXRXCTL_KICK|np->txrxctl_bits, get_hwbase(dev) + NvRegTxRxControl);
+ pci_push(base);
- /* restart rx engine */
- nv_start_rxtx(dev);
- spin_unlock(&np->lock);
- netif_tx_unlock_bh(dev);
- }
- }
+ /* restart rx engine */
+ nv_start_rxtx(dev);
+ spin_unlock(&np->lock);
+ netif_tx_unlock_bh(dev);
/* FIXME: Do we need synchronize_irq(dev->irq) here? */
@@ -3456,7 +3387,13 @@ static void nv_do_nic_poll(unsigned long data)
#ifdef CONFIG_NET_POLL_CONTROLLER
static void nv_poll_controller(struct net_device *dev)
{
- nv_do_nic_poll((unsigned long) dev);
+ struct fe_priv *np = netdev_priv(dev);
+ bool optimized = nv_optimized(np);
+ unsigned long flags;
+
+ local_irq_save(flags);
+ __nv_nic_irq(dev, optimized);
+ local_irq_restore(flags);
}
#endif
@@ -4667,18 +4604,16 @@ static int nv_close(struct net_device *dev)
struct fe_priv *np = netdev_priv(dev);
u8 __iomem *base;
- spin_lock_irq(&np->lock);
- spin_unlock_irq(&np->lock);
+ cancel_work_sync(&np->reset_task);
+ cancel_delayed_work_sync(&np->stats_task);
napi_disable(&np->tx_napi);
napi_disable(&np->napi);
- synchronize_irq(dev->irq);
del_timer_sync(&np->oom_kick);
- del_timer_sync(&np->nic_poll);
- cancel_delayed_work_sync(&np->stats_task);
- netif_stop_queue(dev);
+ netif_tx_disable(dev);
+
spin_lock_irq(&np->lock);
nv_stop_rxtx(dev);
nv_txrx_reset(dev);
@@ -4736,9 +4671,7 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
init_timer(&np->oom_kick);
np->oom_kick.data = (unsigned long) dev;
np->oom_kick.function = &nv_do_rx_refill; /* timer handler */
- init_timer(&np->nic_poll);
- np->nic_poll.data = (unsigned long) dev;
- np->nic_poll.function = &nv_do_nic_poll; /* timer handler */
+ INIT_WORK(&np->reset_task, nv_reset_task);
INIT_DELAYED_WORK(&np->stats_task, nv_stats_task);
err = pci_enable_device(pci_dev);
@@ -5337,8 +5270,6 @@ static void __exit exit_nic(void)
pci_unregister_driver(&driver);
}
-module_param(max_interrupt_work, int, 0);
-MODULE_PARM_DESC(max_interrupt_work, "forcedeth maximum events handled per interrupt");
module_param(optimization_mode, int, 0);
MODULE_PARM_DESC(optimization_mode, "In throughput mode (0), every tx & rx packet will generate an interrupt. In CPU mode (1), interrupts are controlled by a timer.");
module_param(poll_interval, int, 0);
^ permalink raw reply related
* [PATCH 5/6] forcedeth: use NAPI for TX completion
From: Jeff Garzik @ 2007-10-17 5:54 UTC (permalink / raw)
To: netdev, LKML; +Cc: Manfred Spraul, Ingo Molnar, Ayaz Abdulla
In-Reply-To: <20071017055251.GA19972@havoc.gtf.org>
commit a7c00e796597b797ceac3c18e8b85c124196c5ab
Author: Jeff Garzik <jeff@garzik.org>
Date: Tue Oct 16 17:33:19 2007 -0400
[netdrvr] forcedeth: use NAPI for TX completion
A hand-rolled TX poll & work limit system was already in place, so it
was easy to convert the TX path to use NAPI.
This simplifies the code, and enables future improvements and
simplifications.
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
drivers/net/forcedeth.c | 170 +++++++++++++++++++++++++-----------------------
1 file changed, 90 insertions(+), 80 deletions(-)
a7c00e796597b797ceac3c18e8b85c124196c5ab
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index 32a8893..81fe016 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -660,6 +660,7 @@ struct fe_priv {
struct net_device *dev;
struct napi_struct napi;
+ struct napi_struct tx_napi;
/* General data:
* Locking: spin_lock(&np->lock); */
@@ -725,7 +726,6 @@ struct fe_priv {
union ring_type tx_ring;
u32 tx_flags;
int tx_ring_size;
- int tx_stop;
/* vlan fields */
struct vlan_group *vlangrp;
@@ -1732,10 +1732,7 @@ static int nv_start_xmit(struct sk_buff *skb, struct net_device *dev)
empty_slots = nv_get_empty_tx_slots(np);
if (unlikely(empty_slots <= entries)) {
- spin_lock_irq(&np->lock);
netif_stop_queue(dev);
- np->tx_stop = 1;
- spin_unlock_irq(&np->lock);
return NETDEV_TX_BUSY;
}
@@ -1848,10 +1845,7 @@ static int nv_start_xmit_optimized(struct sk_buff *skb, struct net_device *dev)
empty_slots = nv_get_empty_tx_slots(np);
if (unlikely(empty_slots <= entries)) {
- spin_lock_irq(&np->lock);
netif_stop_queue(dev);
- np->tx_stop = 1;
- spin_unlock_irq(&np->lock);
return NETDEV_TX_BUSY;
}
@@ -1956,14 +1950,15 @@ static int nv_start_xmit_optimized(struct sk_buff *skb, struct net_device *dev)
*
* Caller must own np->lock.
*/
-static void nv_tx_done(struct net_device *dev)
+static int nv_tx_done(struct net_device *dev, int limit)
{
struct fe_priv *np = netdev_priv(dev);
u32 flags;
- struct ring_desc* orig_get_tx = np->get_tx.orig;
+ int tx_work = 0;
while ((np->get_tx.orig != np->put_tx.orig) &&
- !((flags = le32_to_cpu(np->get_tx.orig->flaglen)) & NV_TX_VALID)) {
+ !((flags = le32_to_cpu(np->get_tx.orig->flaglen)) & NV_TX_VALID) &&
+ (tx_work < limit)) {
dprintk(KERN_DEBUG "%s: nv_tx_done: flags 0x%x.\n",
dev->name, flags);
@@ -2008,22 +2003,25 @@ static void nv_tx_done(struct net_device *dev)
np->get_tx.orig = np->first_tx.orig;
if (unlikely(np->get_tx_ctx++ == np->last_tx_ctx))
np->get_tx_ctx = np->first_tx_ctx;
+
+ tx_work++;
}
- if (unlikely((np->tx_stop == 1) && (np->get_tx.orig != orig_get_tx))) {
- np->tx_stop = 0;
+
+ if (tx_work)
netif_wake_queue(dev);
- }
+
+ return tx_work;
}
-static void nv_tx_done_optimized(struct net_device *dev, int limit)
+static int nv_tx_done_optimized(struct net_device *dev, int limit)
{
struct fe_priv *np = netdev_priv(dev);
u32 flags;
- struct ring_desc_ex* orig_get_tx = np->get_tx.ex;
+ int tx_work = 0;
while ((np->get_tx.ex != np->put_tx.ex) &&
!((flags = le32_to_cpu(np->get_tx.ex->flaglen)) & NV_TX_VALID) &&
- (limit-- > 0)) {
+ (tx_work < limit)) {
dprintk(KERN_DEBUG "%s: nv_tx_done_optimized: flags 0x%x.\n",
dev->name, flags);
@@ -2043,11 +2041,14 @@ static void nv_tx_done_optimized(struct net_device *dev, int limit)
np->get_tx.ex = np->first_tx.ex;
if (unlikely(np->get_tx_ctx++ == np->last_tx_ctx))
np->get_tx_ctx = np->first_tx_ctx;
+
+ tx_work++;
}
- if (unlikely((np->tx_stop == 1) && (np->get_tx.ex != orig_get_tx))) {
- np->tx_stop = 0;
+
+ if (tx_work)
netif_wake_queue(dev);
- }
+
+ return tx_work;
}
/*
@@ -2120,7 +2121,7 @@ static void nv_tx_timeout(struct net_device *dev)
/* 2) check that the packets were not sent already: */
if (!nv_optimized(np))
- nv_tx_done(dev);
+ nv_tx_done(dev, np->tx_ring_size);
else
nv_tx_done_optimized(dev, np->tx_ring_size);
@@ -2888,7 +2889,7 @@ static irqreturn_t __nv_nic_irq(struct net_device *dev, bool optimized)
{
struct fe_priv *np = netdev_priv(dev);
u8 __iomem *base = np->base;
- u32 events;
+ u32 events, updmask = 0;
int i;
dprintk(KERN_DEBUG "%s: __nv_nic_irq\n", dev->name);
@@ -2905,22 +2906,22 @@ static irqreturn_t __nv_nic_irq(struct net_device *dev, bool optimized)
if (!(events & np->irqmask))
break;
- spin_lock(&np->lock);
- if (optimized)
- nv_tx_done_optimized(dev, TX_WORK_PER_LOOP);
- else
- nv_tx_done(dev);
- spin_unlock(&np->lock);
-
if (events & NVREG_IRQ_RX_ALL) {
netif_rx_schedule(dev, &np->napi);
+ updmask |= NVREG_IRQ_RX_ALL;
+ }
+
+ if (events & NVREG_IRQ_TX_ALL) {
+ netif_rx_schedule(dev, &np->tx_napi);
+ updmask |= NVREG_IRQ_TX_ALL;
+ }
- /* Disable furthur receive irq's */
+ if (updmask) {
spin_lock(&np->lock);
- np->irqmask &= ~NVREG_IRQ_RX_ALL;
+ np->irqmask &= ~updmask;
if (np->msi_flags & NV_MSI_X_ENABLED)
- writel(NVREG_IRQ_RX_ALL, base + NvRegIrqMask);
+ writel(updmask, base + NvRegIrqMask);
else
writel(np->irqmask, base + NvRegIrqMask);
spin_unlock(&np->lock);
@@ -2998,58 +2999,11 @@ static irqreturn_t nv_nic_irq_optimized(int foo, void *data)
return __nv_nic_irq(dev, true);
}
-static irqreturn_t nv_nic_irq_tx(int foo, void *data)
-{
- struct net_device *dev = data;
- struct fe_priv *np = netdev_priv(dev);
- u8 __iomem *base = get_hwbase(dev);
- u32 events;
- int i;
- unsigned long flags;
-
- dprintk(KERN_DEBUG "%s: nv_nic_irq_tx\n", dev->name);
-
- for (i=0; ; i++) {
- events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQ_TX_ALL;
- writel(NVREG_IRQ_TX_ALL, base + NvRegMSIXIrqStatus);
- dprintk(KERN_DEBUG "%s: tx irq: %08x\n", dev->name, events);
- if (!(events & np->irqmask))
- break;
-
- spin_lock_irqsave(&np->lock, flags);
- nv_tx_done_optimized(dev, TX_WORK_PER_LOOP);
- spin_unlock_irqrestore(&np->lock, flags);
-
- if (unlikely(events & (NVREG_IRQ_TX_ERR))) {
- dprintk(KERN_DEBUG "%s: received irq with events 0x%x. Probably TX fail.\n",
- dev->name, events);
- }
- if (unlikely(i > max_interrupt_work)) {
- spin_lock_irqsave(&np->lock, flags);
- /* disable interrupts on the nic */
- writel(NVREG_IRQ_TX_ALL, base + NvRegIrqMask);
- pci_push(base);
-
- if (netif_running(dev)) {
- np->nic_poll_irq |= NVREG_IRQ_TX_ALL;
- mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
- }
- spin_unlock_irqrestore(&np->lock, flags);
- printk(KERN_DEBUG "%s: too many iterations (%d) in nv_nic_irq_tx.\n", dev->name, i);
- break;
- }
-
- }
- dprintk(KERN_DEBUG "%s: nv_nic_irq_tx completed\n", dev->name);
-
- return IRQ_RETVAL(i);
-}
-
static int nv_napi_poll(struct napi_struct *napi, int budget)
{
struct fe_priv *np = container_of(napi, struct fe_priv, napi);
struct net_device *dev = np->dev;
- u8 __iomem *base = get_hwbase(dev);
+ u8 __iomem *base = np->base;
unsigned long flags;
int pkts, retcode;
@@ -3089,7 +3043,7 @@ static irqreturn_t nv_nic_irq_rx(int foo, void *data)
{
struct net_device *dev = data;
struct fe_priv *np = netdev_priv(dev);
- u8 __iomem *base = get_hwbase(dev);
+ u8 __iomem *base = np->base;
u32 events;
events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQ_RX_ALL;
@@ -3104,6 +3058,57 @@ static irqreturn_t nv_nic_irq_rx(int foo, void *data)
return IRQ_HANDLED;
}
+static irqreturn_t nv_nic_irq_tx(int foo, void *data)
+{
+ struct net_device *dev = data;
+ struct fe_priv *np = netdev_priv(dev);
+ u8 __iomem *base = np->base;
+ u32 events;
+
+ events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQ_TX_ALL;
+ writel(NVREG_IRQ_TX_ALL, base + NvRegMSIXIrqStatus);
+
+ if (events) {
+ netif_rx_schedule(dev, &np->tx_napi);
+ /* disable receive interrupts on the nic */
+ writel(NVREG_IRQ_TX_ALL, base + NvRegIrqMask);
+ pci_push(base);
+ }
+ return IRQ_HANDLED;
+}
+
+static int nv_napi_tx_poll(struct napi_struct *napi, int budget)
+{
+ struct fe_priv *np = container_of(napi, struct fe_priv, tx_napi);
+ struct net_device *dev = np->dev;
+ u8 __iomem *base = np->base;
+ unsigned long flags;
+ int pkts;
+
+ spin_lock_irqsave(&np->lock, flags);
+
+ if (nv_optimized(np))
+ pkts = nv_tx_done_optimized(dev, budget);
+ else
+ pkts = nv_tx_done(dev, budget);
+
+ if (pkts < budget) {
+ /* re-enable receive interrupts */
+
+ __netif_rx_complete(dev, napi);
+
+ 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_irqrestore(&np->lock, flags);
+
+ return pkts;
+}
+
static irqreturn_t nv_nic_irq_other(int foo, void *data)
{
struct net_device *dev = data;
@@ -4348,6 +4353,7 @@ static void nv_self_test(struct net_device *dev, struct ethtool_test *test, u64
if (netif_running(dev)) {
netif_stop_queue(dev);
+ napi_disable(&np->tx_napi);
napi_disable(&np->napi);
netif_tx_lock_bh(dev);
@@ -4406,6 +4412,7 @@ static void nv_self_test(struct net_device *dev, struct ethtool_test *test, u64
netif_start_queue(dev);
napi_enable(&np->napi);
+ napi_enable(&np->tx_napi);
nv_enable_hw_interrupts(dev, np->irqmask);
}
@@ -4632,6 +4639,7 @@ static int nv_open(struct net_device *dev)
nv_start_rxtx(dev);
netif_start_queue(dev);
napi_enable(&np->napi);
+ napi_enable(&np->tx_napi);
if (ret) {
netif_carrier_on(dev);
@@ -4662,6 +4670,7 @@ static int nv_close(struct net_device *dev)
spin_lock_irq(&np->lock);
spin_unlock_irq(&np->lock);
+ napi_disable(&np->tx_napi);
napi_disable(&np->napi);
synchronize_irq(dev->irq);
@@ -4876,6 +4885,7 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
#endif
netif_napi_add(dev, &np->napi, nv_napi_poll, RX_WORK_PER_LOOP);
+ netif_napi_add(dev, &np->tx_napi, nv_napi_tx_poll, TX_WORK_PER_LOOP);
SET_ETHTOOL_OPS(dev, &ops);
dev->tx_timeout = nv_tx_timeout;
^ permalink raw reply related
* [PATCH 4/6] forcedeth: unconditionally enable NAPI
From: Jeff Garzik @ 2007-10-17 5:54 UTC (permalink / raw)
To: netdev, LKML; +Cc: Manfred Spraul, Ingo Molnar, Ayaz Abdulla
In-Reply-To: <20071017055251.GA19972@havoc.gtf.org>
commit 8f61debaeb334bce0ccba1a1384d549a377c1e8e
Author: Jeff Garzik <jeff@garzik.org>
Date: Tue Oct 16 12:55:08 2007 -0400
[netdrvr] forcedeth: unconditionally enable NAPI
Remove all !CONFIG_FORCEDETH_NAPI code, and the Kconfig option,
enabling NAPI unconditionally.
The NIC driver recreates a lot of this functionality manually.
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
drivers/net/Kconfig | 17 -----
drivers/net/forcedeth.c | 143 ++----------------------------------------------
2 files changed, 7 insertions(+), 153 deletions(-)
8f61debaeb334bce0ccba1a1384d549a377c1e8e
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 83d52c8..eafdd58 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -1430,23 +1430,6 @@ config FORCEDETH
<file:Documentation/networking/net-modules.txt>. The module will be
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
tristate "CS89x0 support"
depends on NET_PCI && (ISA || MACH_IXDP2351 || ARCH_IXDP2X01 || ARCH_PNX010X)
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index a4baad7..32a8893 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -1546,7 +1546,6 @@ static int nv_alloc_rx_optimized(struct net_device *dev)
}
/* 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;
@@ -1555,41 +1554,6 @@ static void nv_do_rx_refill(unsigned long data)
/* Just reschedule NAPI rx processing */
netif_rx_schedule(dev, &np->napi);
}
-#else
-static void nv_do_rx_refill(unsigned long data)
-{
- struct net_device *dev = (struct net_device *) data;
- struct fe_priv *np = netdev_priv(dev);
- int retcode;
-
- if (!using_multi_irqs(dev)) {
- if (np->msi_flags & NV_MSI_X_ENABLED)
- disable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector);
- else
- disable_irq(dev->irq);
- } else {
- disable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector);
- }
- if (!nv_optimized(np))
- retcode = nv_alloc_rx(dev);
- else
- retcode = nv_alloc_rx_optimized(dev);
- if (retcode) {
- spin_lock_irq(&np->lock);
- if (netif_running(dev))
- mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
- spin_unlock_irq(&np->lock);
- }
- if (!using_multi_irqs(dev)) {
- if (np->msi_flags & NV_MSI_X_ENABLED)
- enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector);
- else
- enable_irq(dev->irq);
- } else {
- enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector);
- }
-}
-#endif
static void nv_init_rx(struct net_device *dev)
{
@@ -2347,11 +2311,7 @@ static int nv_rx_process(struct net_device *dev, int limit)
skb->protocol = eth_type_trans(skb, dev);
dprintk(KERN_DEBUG "%s: nv_rx_process: %d bytes, proto %d accepted.\n",
dev->name, len, skb->protocol);
-#ifdef CONFIG_FORCEDETH_NAPI
netif_receive_skb(skb);
-#else
- netif_rx(skb);
-#endif
dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += len;
@@ -2446,27 +2406,14 @@ static int nv_rx_process_optimized(struct net_device *dev, int limit)
dev->name, len, skb->protocol);
if (likely(!np->vlangrp)) {
-#ifdef CONFIG_FORCEDETH_NAPI
netif_receive_skb(skb);
-#else
- netif_rx(skb);
-#endif
} else {
vlanflags = le32_to_cpu(np->get_rx.ex->buflow);
if (vlanflags & NV_RX3_VLAN_TAG_PRESENT) {
-#ifdef CONFIG_FORCEDETH_NAPI
vlan_hwaccel_receive_skb(skb, np->vlangrp,
vlanflags & NV_RX3_VLAN_TAG_MASK);
-#else
- vlan_hwaccel_rx(skb, np->vlangrp,
- vlanflags & NV_RX3_VLAN_TAG_MASK);
-#endif
} else {
-#ifdef CONFIG_FORCEDETH_NAPI
netif_receive_skb(skb);
-#else
- netif_rx(skb);
-#endif
}
}
@@ -2965,7 +2912,6 @@ static irqreturn_t __nv_nic_irq(struct net_device *dev, bool optimized)
nv_tx_done(dev);
spin_unlock(&np->lock);
-#ifdef CONFIG_FORCEDETH_NAPI
if (events & NVREG_IRQ_RX_ALL) {
netif_rx_schedule(dev, &np->napi);
@@ -2979,27 +2925,7 @@ static irqreturn_t __nv_nic_irq(struct net_device *dev, bool optimized)
writel(np->irqmask, base + NvRegIrqMask);
spin_unlock(&np->lock);
}
-#else
- if (optimized) {
- if (nv_rx_process_optimized(dev, RX_WORK_PER_LOOP)) {
- if (unlikely(nv_alloc_rx_optimized(dev))) {
- spin_lock(&np->lock);
- if (netif_running(dev))
- mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
- spin_unlock(&np->lock);
- }
- }
- } else {
- if (nv_rx_process(dev, RX_WORK_PER_LOOP)) {
- if (unlikely(nv_alloc_rx(dev))) {
- spin_lock(&np->lock);
- if (netif_running(dev))
- mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
- spin_unlock(&np->lock);
- }
- }
- }
-#endif
+
if (unlikely(events & NVREG_IRQ_LINK)) {
spin_lock(&np->lock);
nv_link_irq(dev);
@@ -3119,7 +3045,6 @@ static irqreturn_t nv_nic_irq_tx(int foo, void *data)
return IRQ_RETVAL(i);
}
-#ifdef CONFIG_FORCEDETH_NAPI
static int nv_napi_poll(struct napi_struct *napi, int budget)
{
struct fe_priv *np = container_of(napi, struct fe_priv, napi);
@@ -3159,9 +3084,7 @@ static int nv_napi_poll(struct napi_struct *napi, int budget)
}
return pkts;
}
-#endif
-#ifdef CONFIG_FORCEDETH_NAPI
static irqreturn_t nv_nic_irq_rx(int foo, void *data)
{
struct net_device *dev = data;
@@ -3180,54 +3103,6 @@ static irqreturn_t nv_nic_irq_rx(int foo, void *data)
}
return IRQ_HANDLED;
}
-#else
-static irqreturn_t nv_nic_irq_rx(int foo, void *data)
-{
- struct net_device *dev = data;
- struct fe_priv *np = netdev_priv(dev);
- u8 __iomem *base = get_hwbase(dev);
- u32 events;
- int i;
- unsigned long flags;
-
- dprintk(KERN_DEBUG "%s: nv_nic_irq_rx\n", dev->name);
-
- for (i=0; ; i++) {
- events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQ_RX_ALL;
- writel(NVREG_IRQ_RX_ALL, base + NvRegMSIXIrqStatus);
- dprintk(KERN_DEBUG "%s: rx irq: %08x\n", dev->name, events);
- if (!(events & np->irqmask))
- break;
-
- if (nv_rx_process_optimized(dev, RX_WORK_PER_LOOP)) {
- if (unlikely(nv_alloc_rx_optimized(dev))) {
- spin_lock_irqsave(&np->lock, flags);
- if (netif_running(dev))
- mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
- spin_unlock_irqrestore(&np->lock, flags);
- }
- }
-
- if (unlikely(i > max_interrupt_work)) {
- spin_lock_irqsave(&np->lock, flags);
- /* disable interrupts on the nic */
- writel(NVREG_IRQ_RX_ALL, base + NvRegIrqMask);
- pci_push(base);
-
- if (netif_running(dev)) {
- np->nic_poll_irq |= NVREG_IRQ_RX_ALL;
- mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
- }
- spin_unlock_irqrestore(&np->lock, flags);
- printk(KERN_DEBUG "%s: too many iterations (%d) in nv_nic_irq_rx.\n", dev->name, i);
- break;
- }
- }
- dprintk(KERN_DEBUG "%s: nv_nic_irq_rx completed\n", dev->name);
-
- return IRQ_RETVAL(i);
-}
-#endif
static irqreturn_t nv_nic_irq_other(int foo, void *data)
{
@@ -4472,9 +4347,9 @@ static void nv_self_test(struct net_device *dev, struct ethtool_test *test, u64
if (test->flags & ETH_TEST_FL_OFFLINE) {
if (netif_running(dev)) {
netif_stop_queue(dev);
-#ifdef CONFIG_FORCEDETH_NAPI
+
napi_disable(&np->napi);
-#endif
+
netif_tx_lock_bh(dev);
spin_lock_irq(&np->lock);
nv_disable_hw_interrupts(dev, np->irqmask);
@@ -4529,9 +4404,9 @@ static void nv_self_test(struct net_device *dev, struct ethtool_test *test, u64
/* restart rx engine */
nv_start_rxtx(dev);
netif_start_queue(dev);
-#ifdef CONFIG_FORCEDETH_NAPI
+
napi_enable(&np->napi);
-#endif
+
nv_enable_hw_interrupts(dev, np->irqmask);
}
}
@@ -4756,9 +4631,7 @@ static int nv_open(struct net_device *dev)
ret = nv_update_linkspeed(dev);
nv_start_rxtx(dev);
netif_start_queue(dev);
-#ifdef CONFIG_FORCEDETH_NAPI
napi_enable(&np->napi);
-#endif
if (ret) {
netif_carrier_on(dev);
@@ -4789,9 +4662,7 @@ static int nv_close(struct net_device *dev)
spin_lock_irq(&np->lock);
spin_unlock_irq(&np->lock);
-#ifdef CONFIG_FORCEDETH_NAPI
napi_disable(&np->napi);
-#endif
synchronize_irq(dev->irq);
del_timer_sync(&np->oom_kick);
@@ -5003,9 +4874,9 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
#ifdef CONFIG_NET_POLL_CONTROLLER
dev->poll_controller = nv_poll_controller;
#endif
-#ifdef CONFIG_FORCEDETH_NAPI
+
netif_napi_add(dev, &np->napi, nv_napi_poll, RX_WORK_PER_LOOP);
-#endif
+
SET_ETHTOOL_OPS(dev, &ops);
dev->tx_timeout = nv_tx_timeout;
dev->watchdog_timeo = NV_WATCHDOG_TIMEO;
^ permalink raw reply related
* [PATCH 3/6] forcedeth: eliminate some duplicate irq handling code
From: Jeff Garzik @ 2007-10-17 5:54 UTC (permalink / raw)
To: netdev, LKML; +Cc: Manfred Spraul, Ingo Molnar, Ayaz Abdulla
In-Reply-To: <20071017055251.GA19972@havoc.gtf.org>
commit c6ad879c65e6f91c7f61b86936e2ea39b16711da
Author: Jeff Garzik <jeff@garzik.org>
Date: Tue Oct 16 11:43:27 2007 -0400
[netdrvr] forcedeth: eliminate some duplicate irq handling code
* nv_nic_irq_optimized() is the exactly same as nv_nic_irq(), save
for three function calls. Consolidate together into a single function
__nv_nic_irq().
* remove pointless casts from void* in other irq handling code
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
drivers/net/forcedeth.c | 167 ++++++++++--------------------------------------
1 file changed, 38 insertions(+), 129 deletions(-)
c6ad879c65e6f91c7f61b86936e2ea39b16711da
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index 39ade56..a4baad7 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -2937,15 +2937,14 @@ static void nv_link_irq(struct net_device *dev)
dprintk(KERN_DEBUG "%s: link change notification done.\n", dev->name);
}
-static irqreturn_t nv_nic_irq(int foo, void *data)
+static irqreturn_t __nv_nic_irq(struct net_device *dev, bool optimized)
{
- struct net_device *dev = (struct net_device *) data;
struct fe_priv *np = netdev_priv(dev);
- u8 __iomem *base = get_hwbase(dev);
+ u8 __iomem *base = np->base;
u32 events;
int i;
- dprintk(KERN_DEBUG "%s: nv_nic_irq\n", dev->name);
+ dprintk(KERN_DEBUG "%s: __nv_nic_irq\n", dev->name);
for (i=0; ; i++) {
if (!(np->msi_flags & NV_MSI_X_ENABLED)) {
@@ -2960,7 +2959,10 @@ static irqreturn_t nv_nic_irq(int foo, void *data)
break;
spin_lock(&np->lock);
- nv_tx_done(dev);
+ if (optimized)
+ nv_tx_done_optimized(dev, TX_WORK_PER_LOOP);
+ else
+ nv_tx_done(dev);
spin_unlock(&np->lock);
#ifdef CONFIG_FORCEDETH_NAPI
@@ -2978,12 +2980,23 @@ static irqreturn_t nv_nic_irq(int foo, void *data)
spin_unlock(&np->lock);
}
#else
- if (nv_rx_process(dev, RX_WORK_PER_LOOP)) {
- if (unlikely(nv_alloc_rx(dev))) {
- spin_lock(&np->lock);
- if (netif_running(dev))
- mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
- spin_unlock(&np->lock);
+ if (optimized) {
+ if (nv_rx_process_optimized(dev, RX_WORK_PER_LOOP)) {
+ if (unlikely(nv_alloc_rx_optimized(dev))) {
+ spin_lock(&np->lock);
+ if (netif_running(dev))
+ mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
+ spin_unlock(&np->lock);
+ }
+ }
+ } else {
+ if (nv_rx_process(dev, RX_WORK_PER_LOOP)) {
+ if (unlikely(nv_alloc_rx(dev))) {
+ spin_lock(&np->lock);
+ if (netif_running(dev))
+ mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
+ spin_unlock(&np->lock);
+ }
}
}
#endif
@@ -3042,130 +3055,26 @@ static irqreturn_t nv_nic_irq(int foo, void *data)
}
}
- dprintk(KERN_DEBUG "%s: nv_nic_irq completed\n", dev->name);
+ dprintk(KERN_DEBUG "%s: __nv_nic_irq completed\n", dev->name);
return IRQ_RETVAL(i);
}
-/**
- * All _optimized functions are used to help increase performance
- * (reduce CPU and increase throughput). They use descripter version 3,
- * compiler directives, and reduce memory accesses.
- */
-static irqreturn_t nv_nic_irq_optimized(int foo, void *data)
+static irqreturn_t nv_nic_irq(int foo, void *data)
{
- struct net_device *dev = (struct net_device *) data;
- struct fe_priv *np = netdev_priv(dev);
- u8 __iomem *base = get_hwbase(dev);
- u32 events;
- int i;
-
- dprintk(KERN_DEBUG "%s: nv_nic_irq_optimized\n", dev->name);
-
- for (i=0; ; i++) {
- if (!(np->msi_flags & NV_MSI_X_ENABLED)) {
- events = readl(base + NvRegIrqStatus) & NVREG_IRQSTAT_MASK;
- writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
- } else {
- events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQSTAT_MASK;
- writel(NVREG_IRQSTAT_MASK, base + NvRegMSIXIrqStatus);
- }
- dprintk(KERN_DEBUG "%s: irq: %08x\n", dev->name, events);
- if (!(events & np->irqmask))
- break;
-
- spin_lock(&np->lock);
- nv_tx_done_optimized(dev, TX_WORK_PER_LOOP);
- spin_unlock(&np->lock);
-
-#ifdef CONFIG_FORCEDETH_NAPI
- if (events & NVREG_IRQ_RX_ALL) {
- netif_rx_schedule(dev, &np->napi);
-
- /* 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
- if (nv_rx_process_optimized(dev, RX_WORK_PER_LOOP)) {
- if (unlikely(nv_alloc_rx_optimized(dev))) {
- spin_lock(&np->lock);
- if (netif_running(dev))
- mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
- spin_unlock(&np->lock);
- }
- }
-#endif
- if (unlikely(events & NVREG_IRQ_LINK)) {
- spin_lock(&np->lock);
- nv_link_irq(dev);
- spin_unlock(&np->lock);
- }
- if (unlikely(np->need_linktimer && time_after(jiffies, np->link_timeout))) {
- spin_lock(&np->lock);
- nv_linkchange(dev);
- spin_unlock(&np->lock);
- np->link_timeout = jiffies + LINK_TIMEOUT;
- }
- if (unlikely(events & (NVREG_IRQ_TX_ERR))) {
- dprintk(KERN_DEBUG "%s: received irq with events 0x%x. Probably TX fail.\n",
- dev->name, events);
- }
- if (unlikely(events & (NVREG_IRQ_UNKNOWN))) {
- printk(KERN_DEBUG "%s: received irq with unknown events 0x%x. Please report\n",
- dev->name, events);
- }
- if (unlikely(events & NVREG_IRQ_RECOVER_ERROR)) {
- spin_lock(&np->lock);
- /* disable interrupts on the nic */
- if (!(np->msi_flags & NV_MSI_X_ENABLED))
- writel(0, base + NvRegIrqMask);
- else
- writel(np->irqmask, base + NvRegIrqMask);
- pci_push(base);
-
- if (netif_running(dev)) {
- np->nic_poll_irq = np->irqmask;
- np->recover_error = 1;
- mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
- }
- spin_unlock(&np->lock);
- break;
- }
-
- if (unlikely(i > max_interrupt_work)) {
- spin_lock(&np->lock);
- /* disable interrupts on the nic */
- if (!(np->msi_flags & NV_MSI_X_ENABLED))
- writel(0, base + NvRegIrqMask);
- else
- writel(np->irqmask, base + NvRegIrqMask);
- pci_push(base);
-
- if (netif_running(dev)) {
- np->nic_poll_irq = np->irqmask;
- mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
- }
- spin_unlock(&np->lock);
- printk(KERN_DEBUG "%s: too many iterations (%d) in nv_nic_irq.\n", dev->name, i);
- break;
- }
-
- }
- dprintk(KERN_DEBUG "%s: nv_nic_irq_optimized completed\n", dev->name);
+ struct net_device *dev = data;
+ return __nv_nic_irq(dev, false);
+}
- return IRQ_RETVAL(i);
+static irqreturn_t nv_nic_irq_optimized(int foo, void *data)
+{
+ struct net_device *dev = data;
+ return __nv_nic_irq(dev, true);
}
static irqreturn_t nv_nic_irq_tx(int foo, void *data)
{
- struct net_device *dev = (struct net_device *) data;
+ struct net_device *dev = data;
struct fe_priv *np = netdev_priv(dev);
u8 __iomem *base = get_hwbase(dev);
u32 events;
@@ -3255,7 +3164,7 @@ static int nv_napi_poll(struct napi_struct *napi, int budget)
#ifdef CONFIG_FORCEDETH_NAPI
static irqreturn_t nv_nic_irq_rx(int foo, void *data)
{
- struct net_device *dev = (struct net_device *) data;
+ struct net_device *dev = data;
struct fe_priv *np = netdev_priv(dev);
u8 __iomem *base = get_hwbase(dev);
u32 events;
@@ -3274,7 +3183,7 @@ static irqreturn_t nv_nic_irq_rx(int foo, void *data)
#else
static irqreturn_t nv_nic_irq_rx(int foo, void *data)
{
- struct net_device *dev = (struct net_device *) data;
+ struct net_device *dev = data;
struct fe_priv *np = netdev_priv(dev);
u8 __iomem *base = get_hwbase(dev);
u32 events;
@@ -3322,7 +3231,7 @@ static irqreturn_t nv_nic_irq_rx(int foo, void *data)
static irqreturn_t nv_nic_irq_other(int foo, void *data)
{
- struct net_device *dev = (struct net_device *) data;
+ struct net_device *dev = data;
struct fe_priv *np = netdev_priv(dev);
u8 __iomem *base = get_hwbase(dev);
u32 events;
@@ -3395,7 +3304,7 @@ static irqreturn_t nv_nic_irq_other(int foo, void *data)
static irqreturn_t nv_nic_irq_test(int foo, void *data)
{
- struct net_device *dev = (struct net_device *) data;
+ struct net_device *dev = data;
struct fe_priv *np = netdev_priv(dev);
u8 __iomem *base = get_hwbase(dev);
u32 events;
^ permalink raw reply related
* [PATCH 2/6] forcedeth: timer overhaul
From: Jeff Garzik @ 2007-10-17 5:53 UTC (permalink / raw)
To: netdev, LKML; +Cc: Manfred Spraul, Ingo Molnar, Ayaz Abdulla
In-Reply-To: <20071017055251.GA19972@havoc.gtf.org>
commit 160511126b6be7f15da33f7cab7374b12cb59999
Author: Jeff Garzik <jeff@garzik.org>
Date: Tue Oct 16 02:22:39 2007 -0400
[netdrvr] forcedeth: timer overhaul
* remove np->in_shutdown, it mirrors netif_running()
* convert stats timer to delayed workqueue
* retrieve hw stats inside spinlock
* split out the 'stop' portions of nv_stop_rx() and nv_stop_rx(),
to enable future calling of these operations without the reg_delay()
that immediately follows.
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
drivers/net/forcedeth.c | 135 +++++++++++++++++++++++++++---------------------
1 file changed, 77 insertions(+), 58 deletions(-)
160511126b6be7f15da33f7cab7374b12cb59999
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index 2d518fc..39ade56 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -664,7 +664,7 @@ struct fe_priv {
/* General data:
* Locking: spin_lock(&np->lock); */
struct nv_ethtool_stats estats;
- int in_shutdown;
+
u32 linkspeed;
int duplex;
int autoneg;
@@ -705,7 +705,7 @@ struct fe_priv {
unsigned int pkt_limit;
struct timer_list oom_kick;
struct timer_list nic_poll;
- struct timer_list stats_poll;
+ struct delayed_work stats_task;
u32 nic_poll_irq;
int rx_ring_size;
@@ -1264,18 +1264,28 @@ static void nv_start_rx(struct net_device *dev)
pci_push(base);
}
-static void nv_stop_rx(struct net_device *dev)
+static void __nv_stop_rx(struct fe_priv *np)
{
- struct fe_priv *np = netdev_priv(dev);
- u8 __iomem *base = get_hwbase(dev);
+ u8 __iomem *base = np->base;
u32 rx_ctrl = readl(base + NvRegReceiverControl);
- dprintk(KERN_DEBUG "%s: nv_stop_rx\n", dev->name);
+ dprintk(KERN_DEBUG "%s: __nv_stop_rx\n", dev->name);
if (!np->mac_in_use)
rx_ctrl &= ~NVREG_RCVCTL_START;
else
rx_ctrl |= NVREG_RCVCTL_RX_PATH_EN;
writel(rx_ctrl, base + NvRegReceiverControl);
+}
+
+static void nv_stop_rx(struct net_device *dev)
+{
+ struct fe_priv *np = netdev_priv(dev);
+ u8 __iomem *base = get_hwbase(dev);
+
+ dprintk(KERN_DEBUG "%s: nv_stop_rx\n", dev->name);
+
+ __nv_stop_rx(np);
+
reg_delay(dev, NvRegReceiverStatus, NVREG_RCVSTAT_BUSY, 0,
NV_RXSTOP_DELAY1, NV_RXSTOP_DELAY1MAX,
KERN_INFO "nv_stop_rx: ReceiverStatus remained busy");
@@ -1299,18 +1309,29 @@ static void nv_start_tx(struct net_device *dev)
pci_push(base);
}
-static void nv_stop_tx(struct net_device *dev)
+static void __nv_stop_tx(struct fe_priv *np)
{
- struct fe_priv *np = netdev_priv(dev);
- u8 __iomem *base = get_hwbase(dev);
+ u8 __iomem *base = np->base;
u32 tx_ctrl = readl(base + NvRegTransmitterControl);
- dprintk(KERN_DEBUG "%s: nv_stop_tx\n", dev->name);
+ dprintk(KERN_DEBUG "%s: __nv_stop_tx\n", dev->name);
+
if (!np->mac_in_use)
tx_ctrl &= ~NVREG_XMITCTL_START;
else
tx_ctrl |= NVREG_XMITCTL_TX_PATH_EN;
writel(tx_ctrl, base + NvRegTransmitterControl);
+}
+
+static void nv_stop_tx(struct net_device *dev)
+{
+ struct fe_priv *np = netdev_priv(dev);
+ u8 __iomem *base = get_hwbase(dev);
+
+ dprintk(KERN_DEBUG "%s: nv_stop_tx\n", dev->name);
+
+ __nv_stop_tx(np);
+
reg_delay(dev, NvRegTransmitterStatus, NVREG_XMITSTAT_BUSY, 0,
NV_TXSTOP_DELAY1, NV_TXSTOP_DELAY1MAX,
KERN_INFO "nv_stop_tx: TransmitterStatus remained busy");
@@ -1364,10 +1385,9 @@ static void nv_mac_reset(struct net_device *dev)
pci_push(base);
}
-static void nv_get_hw_stats(struct net_device *dev)
+static void __nv_get_hw_stats(struct fe_priv *np)
{
- struct fe_priv *np = netdev_priv(dev);
- u8 __iomem *base = get_hwbase(dev);
+ u8 __iomem *base = np->base;
np->estats.tx_bytes += readl(base + NvRegTxCnt);
np->estats.tx_zero_rexmt += readl(base + NvRegTxZeroReXmt);
@@ -1419,6 +1439,15 @@ static void nv_get_hw_stats(struct net_device *dev)
}
}
+static void nv_get_hw_stats(struct fe_priv *np)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&np->lock, flags);
+ __nv_get_hw_stats(np);
+ spin_unlock_irqrestore(&np->lock, flags);
+}
+
/*
* nv_get_stats: dev->get_stats function
* Get latest stats value from the nic.
@@ -1431,7 +1460,7 @@ static struct net_device_stats *nv_get_stats(struct net_device *dev)
/* If the nic supports hw counters then retrieve latest values */
if (np->driver_data & (DEV_HAS_STATISTICS_V1|DEV_HAS_STATISTICS_V2)) {
- nv_get_hw_stats(dev);
+ nv_get_hw_stats(np);
/* copy to net_device stats */
dev->stats.tx_bytes = np->estats.tx_bytes;
@@ -1547,7 +1576,7 @@ static void nv_do_rx_refill(unsigned long data)
retcode = nv_alloc_rx_optimized(dev);
if (retcode) {
spin_lock_irq(&np->lock);
- if (!np->in_shutdown)
+ if (netif_running(dev))
mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
spin_unlock_irq(&np->lock);
}
@@ -2507,10 +2536,9 @@ static int nv_change_mtu(struct net_device *dev, int new_mtu)
nv_drain_rxtx(dev);
/* reinit driver view of the rx queue */
set_bufsize(dev);
- if (nv_init_ring(dev)) {
- if (!np->in_shutdown)
- mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
- }
+ if (nv_init_ring(dev))
+ mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
+
/* reinit nic view of the rx queue */
writel(np->rx_buf_sz, base + NvRegOffloadConfig);
setup_hw_rings(dev, NV_SETUP_RX_RING | NV_SETUP_TX_RING);
@@ -2953,7 +2981,7 @@ static irqreturn_t nv_nic_irq(int foo, void *data)
if (nv_rx_process(dev, RX_WORK_PER_LOOP)) {
if (unlikely(nv_alloc_rx(dev))) {
spin_lock(&np->lock);
- if (!np->in_shutdown)
+ if (netif_running(dev))
mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
spin_unlock(&np->lock);
}
@@ -2987,7 +3015,7 @@ static irqreturn_t nv_nic_irq(int foo, void *data)
writel(np->irqmask, base + NvRegIrqMask);
pci_push(base);
- if (!np->in_shutdown) {
+ if (netif_running(dev)) {
np->nic_poll_irq = np->irqmask;
np->recover_error = 1;
mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
@@ -3004,7 +3032,7 @@ static irqreturn_t nv_nic_irq(int foo, void *data)
writel(np->irqmask, base + NvRegIrqMask);
pci_push(base);
- if (!np->in_shutdown) {
+ if (netif_running(dev)) {
np->nic_poll_irq = np->irqmask;
mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
}
@@ -3068,7 +3096,7 @@ static irqreturn_t nv_nic_irq_optimized(int foo, void *data)
if (nv_rx_process_optimized(dev, RX_WORK_PER_LOOP)) {
if (unlikely(nv_alloc_rx_optimized(dev))) {
spin_lock(&np->lock);
- if (!np->in_shutdown)
+ if (netif_running(dev))
mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
spin_unlock(&np->lock);
}
@@ -3102,7 +3130,7 @@ static irqreturn_t nv_nic_irq_optimized(int foo, void *data)
writel(np->irqmask, base + NvRegIrqMask);
pci_push(base);
- if (!np->in_shutdown) {
+ if (netif_running(dev)) {
np->nic_poll_irq = np->irqmask;
np->recover_error = 1;
mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
@@ -3120,7 +3148,7 @@ static irqreturn_t nv_nic_irq_optimized(int foo, void *data)
writel(np->irqmask, base + NvRegIrqMask);
pci_push(base);
- if (!np->in_shutdown) {
+ if (netif_running(dev)) {
np->nic_poll_irq = np->irqmask;
mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
}
@@ -3167,7 +3195,7 @@ static irqreturn_t nv_nic_irq_tx(int foo, void *data)
writel(NVREG_IRQ_TX_ALL, base + NvRegIrqMask);
pci_push(base);
- if (!np->in_shutdown) {
+ if (netif_running(dev)) {
np->nic_poll_irq |= NVREG_IRQ_TX_ALL;
mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
}
@@ -3201,7 +3229,7 @@ static int nv_napi_poll(struct napi_struct *napi, int budget)
if (retcode) {
spin_lock_irqsave(&np->lock, flags);
- if (!np->in_shutdown)
+ if (netif_running(dev))
mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
spin_unlock_irqrestore(&np->lock, flags);
}
@@ -3265,7 +3293,7 @@ static irqreturn_t nv_nic_irq_rx(int foo, void *data)
if (nv_rx_process_optimized(dev, RX_WORK_PER_LOOP)) {
if (unlikely(nv_alloc_rx_optimized(dev))) {
spin_lock_irqsave(&np->lock, flags);
- if (!np->in_shutdown)
+ if (netif_running(dev))
mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
spin_unlock_irqrestore(&np->lock, flags);
}
@@ -3277,7 +3305,7 @@ static irqreturn_t nv_nic_irq_rx(int foo, void *data)
writel(NVREG_IRQ_RX_ALL, base + NvRegIrqMask);
pci_push(base);
- if (!np->in_shutdown) {
+ if (netif_running(dev)) {
np->nic_poll_irq |= NVREG_IRQ_RX_ALL;
mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
}
@@ -3332,7 +3360,7 @@ static irqreturn_t nv_nic_irq_other(int foo, void *data)
writel(NVREG_IRQ_OTHER, base + NvRegIrqMask);
pci_push(base);
- if (!np->in_shutdown) {
+ if (netif_running(dev)) {
np->nic_poll_irq |= NVREG_IRQ_OTHER;
np->recover_error = 1;
mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
@@ -3350,7 +3378,7 @@ static irqreturn_t nv_nic_irq_other(int foo, void *data)
writel(NVREG_IRQ_OTHER, base + NvRegIrqMask);
pci_push(base);
- if (!np->in_shutdown) {
+ if (netif_running(dev)) {
np->nic_poll_irq |= NVREG_IRQ_OTHER;
mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
}
@@ -3587,10 +3615,9 @@ static void nv_do_nic_poll(unsigned long data)
nv_drain_rxtx(dev);
/* reinit driver view of the rx queue */
set_bufsize(dev);
- if (nv_init_ring(dev)) {
- if (!np->in_shutdown)
- mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
- }
+ if (nv_init_ring(dev))
+ mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
+
/* reinit nic view of the rx queue */
writel(np->rx_buf_sz, base + NvRegOffloadConfig);
setup_hw_rings(dev, NV_SETUP_RX_RING | NV_SETUP_TX_RING);
@@ -3644,15 +3671,14 @@ static void nv_poll_controller(struct net_device *dev)
}
#endif
-static void nv_do_stats_poll(unsigned long data)
+static void nv_stats_task(struct work_struct *work)
{
- struct net_device *dev = (struct net_device *) data;
- struct fe_priv *np = netdev_priv(dev);
+ struct fe_priv *np =
+ container_of(work, struct fe_priv, stats_task.work);
- nv_get_hw_stats(dev);
+ nv_get_hw_stats(np);
- if (!np->in_shutdown)
- mod_timer(&np->stats_poll, jiffies + STATS_INTERVAL);
+ schedule_delayed_work(&np->stats_task, STATS_INTERVAL);
}
static void nv_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
@@ -4099,10 +4125,8 @@ static int nv_set_ringparam(struct net_device *dev, struct ethtool_ringparam* ri
if (netif_running(dev)) {
/* reinit driver view of the queues */
set_bufsize(dev);
- if (nv_init_ring(dev)) {
- if (!np->in_shutdown)
- mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
- }
+ if (nv_init_ring(dev))
+ mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
/* reinit nic view of the queues */
writel(np->rx_buf_sz, base + NvRegOffloadConfig);
@@ -4283,7 +4307,7 @@ static void nv_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *e
struct fe_priv *np = netdev_priv(dev);
/* update stats */
- nv_do_stats_poll((unsigned long)dev);
+ nv_get_hw_stats(np);
memcpy(buffer, &np->estats, nv_get_sset_count(dev, ETH_SS_STATS)*sizeof(u64));
}
@@ -4582,10 +4606,9 @@ static void nv_self_test(struct net_device *dev, struct ethtool_test *test, u64
if (netif_running(dev)) {
/* reinit driver view of the rx queue */
set_bufsize(dev);
- if (nv_init_ring(dev)) {
- if (!np->in_shutdown)
- mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
- }
+ if (nv_init_ring(dev))
+ mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
+
/* reinit nic view of the rx queue */
writel(np->rx_buf_sz, base + NvRegOffloadConfig);
setup_hw_rings(dev, NV_SETUP_RX_RING | NV_SETUP_TX_RING);
@@ -4734,8 +4757,6 @@ static int nv_open(struct net_device *dev)
nv_txrx_reset(dev);
writel(0, base + NvRegUnknownSetupReg6);
- np->in_shutdown = 0;
-
/* give hw rings */
setup_hw_rings(dev, NV_SETUP_RX_RING | NV_SETUP_TX_RING);
writel( ((np->rx_ring_size-1) << NVREG_RINGSZ_RXSHIFT) + ((np->tx_ring_size-1) << NVREG_RINGSZ_TXSHIFT),
@@ -4841,7 +4862,7 @@ static int nv_open(struct net_device *dev)
/* start statistics timer */
if (np->driver_data & (DEV_HAS_STATISTICS_V1|DEV_HAS_STATISTICS_V2))
- mod_timer(&np->stats_poll, jiffies + STATS_INTERVAL);
+ schedule_delayed_work(&np->stats_task, STATS_INTERVAL);
spin_unlock_irq(&np->lock);
@@ -4857,8 +4878,8 @@ static int nv_close(struct net_device *dev)
u8 __iomem *base;
spin_lock_irq(&np->lock);
- np->in_shutdown = 1;
spin_unlock_irq(&np->lock);
+
#ifdef CONFIG_FORCEDETH_NAPI
napi_disable(&np->napi);
#endif
@@ -4866,7 +4887,7 @@ static int nv_close(struct net_device *dev)
del_timer_sync(&np->oom_kick);
del_timer_sync(&np->nic_poll);
- del_timer_sync(&np->stats_poll);
+ cancel_delayed_work_sync(&np->stats_task);
netif_stop_queue(dev);
spin_lock_irq(&np->lock);
@@ -4929,9 +4950,7 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
init_timer(&np->nic_poll);
np->nic_poll.data = (unsigned long) dev;
np->nic_poll.function = &nv_do_nic_poll; /* timer handler */
- init_timer(&np->stats_poll);
- np->stats_poll.data = (unsigned long) dev;
- np->stats_poll.function = &nv_do_stats_poll; /* timer handler */
+ INIT_DELAYED_WORK(&np->stats_task, nv_stats_task);
err = pci_enable_device(pci_dev);
if (err)
^ permalink raw reply related
* Re: [patch] net: avoid race between netpoll and network fast path
From: Tina Yang @ 2007-10-17 5:46 UTC (permalink / raw)
To: David Miller; +Cc: mpm, netdev
In-Reply-To: <20071016.210613.71104656.davem@davemloft.net>
David Miller wrote:
> From: Tina Yang <tina.yang@oracle.com>
> Date: Tue, 16 Oct 2007 20:45:04 -0700
>
>> The current netpoll design and implementation has serveral race issues with the
>> network fast path that panics/hangs the system or causes interface timeout/reset
>> but the fix is likely to have impact on the overall system performance and could
>> involve a large number of drivers. The proposal is to disable the problem code
>> for normal operations but only to enable it at the time of crash in case polling
>> is necessary. Tests that have been done included the bug fix verification
>> as well as regression check on the netlog results in various crash modes.
>>
>> Signed-off-by: Tina Yang <tina.yang@oracle.com>
>
> This is at best a kludge, and it's the wrong way to approach this problem.
>
> Fix the bug, and fix it right.
>
> If you disable that stretch of code, what you've done is make the
> netpoll code hang and/or drop console messages if the TX queue is full
> in the driver and the only way to liberate TX space is to call into
> ->poll().
Isn't net_rx_action() calling ->poll() to free the TX space ?
TX queue full can only be emptied when the device is done transmitting
not because of netpoll ->poll() it. The softirq (net_rx_action)
is the purpose for such an event. Netconsole messages will be
dropped if the device can't keep up with it regardless of netpoll
->poll() or not. If no dropping can be tolerated, then the
netpoll upper layer probably should be redesigned to buffer the data.
The poll_list currently is in a per_cpu structure, not being
protected globally that netpoll thread from any cpu can
trash it.
>
> You haven't shown the precise race that leads to corruption so that someone
> so motivated can guide you towards a more correct fix if you are not
> capable of implementing it properly on your own.
The precise race is
1) net_rx_action get the dev from poll_list
2) at the same time, netpoll poll_napi() get a hold of the poll lock
and calls ->poll(), remove dev from the poll list
3) after it finishes, net_rx_action get the poll lock, and calls
->poll() the second time, and panic when trying to remove (again)
the dev from the poll list.
and I had logged all the crash info from the crash scenes into the
bug database.
As Matt Mackall had acknowledged, the network fast path went to great
length to reduce locking overhead, should that be undone because of
netpoll if that's what it takes to fix it more correctly ?
^ permalink raw reply
* [PATCH 1/6] forcedeth: internal simplifications; changelog removal
From: Jeff Garzik @ 2007-10-17 5:53 UTC (permalink / raw)
To: netdev, LKML; +Cc: Manfred Spraul, Ingo Molnar, Ayaz Abdulla
In-Reply-To: <20071017055251.GA19972@havoc.gtf.org>
commit 0aeb1f867bc76029f599f73ac757a50f7641ccc5
Author: Jeff Garzik <jeff@garzik.org>
Date: Tue Oct 16 01:40:30 2007 -0400
[netdrvr] forcedeth: internal simplifications; changelog removal
* remove changelog from source; its kept in git repository
* consolidate descriptor version tests using nv_optimized()
* consolidate NIC DMA start, stop and drain into
nv_start_txrx(), nv_stop_txrx(), nv_drain_txrx()
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
drivers/net/forcedeth.c | 234 +++++++++++++++---------------------------------
1 file changed, 74 insertions(+), 160 deletions(-)
0aeb1f867bc76029f599f73ac757a50f7641ccc5
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index cfbb7aa..2d518fc 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -29,90 +29,6 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
- * Changelog:
- * 0.01: 05 Oct 2003: First release that compiles without warnings.
- * 0.02: 05 Oct 2003: Fix bug for nv_drain_tx: do not try to free NULL skbs.
- * Check all PCI BARs for the register window.
- * udelay added to mii_rw.
- * 0.03: 06 Oct 2003: Initialize dev->irq.
- * 0.04: 07 Oct 2003: Initialize np->lock, reduce handled irqs, add printks.
- * 0.05: 09 Oct 2003: printk removed again, irq status print tx_timeout.
- * 0.06: 10 Oct 2003: MAC Address read updated, pff flag generation updated,
- * irq mask updated
- * 0.07: 14 Oct 2003: Further irq mask updates.
- * 0.08: 20 Oct 2003: rx_desc.Length initialization added, nv_alloc_rx refill
- * added into irq handler, NULL check for drain_ring.
- * 0.09: 20 Oct 2003: Basic link speed irq implementation. Only handle the
- * requested interrupt sources.
- * 0.10: 20 Oct 2003: First cleanup for release.
- * 0.11: 21 Oct 2003: hexdump for tx added, rx buffer sizes increased.
- * MAC Address init fix, set_multicast cleanup.
- * 0.12: 23 Oct 2003: Cleanups for release.
- * 0.13: 25 Oct 2003: Limit for concurrent tx packets increased to 10.
- * Set link speed correctly. start rx before starting
- * tx (nv_start_rx sets the link speed).
- * 0.14: 25 Oct 2003: Nic dependant irq mask.
- * 0.15: 08 Nov 2003: fix smp deadlock with set_multicast_list during
- * open.
- * 0.16: 15 Nov 2003: include file cleanup for ppc64, rx buffer size
- * increased to 1628 bytes.
- * 0.17: 16 Nov 2003: undo rx buffer size increase. Substract 1 from
- * the tx length.
- * 0.18: 17 Nov 2003: fix oops due to late initialization of dev_stats
- * 0.19: 29 Nov 2003: Handle RxNoBuf, detect & handle invalid mac
- * addresses, really stop rx if already running
- * in nv_start_rx, clean up a bit.
- * 0.20: 07 Dec 2003: alloc fixes
- * 0.21: 12 Jan 2004: additional alloc fix, nic polling fix.
- * 0.22: 19 Jan 2004: reprogram timer to a sane rate, avoid lockup
- * on close.
- * 0.23: 26 Jan 2004: various small cleanups
- * 0.24: 27 Feb 2004: make driver even less anonymous in backtraces
- * 0.25: 09 Mar 2004: wol support
- * 0.26: 03 Jun 2004: netdriver specific annotation, sparse-related fixes
- * 0.27: 19 Jun 2004: Gigabit support, new descriptor rings,
- * added CK804/MCP04 device IDs, code fixes
- * for registers, link status and other minor fixes.
- * 0.28: 21 Jun 2004: Big cleanup, making driver mostly endian safe
- * 0.29: 31 Aug 2004: Add backup timer for link change notification.
- * 0.30: 25 Sep 2004: rx checksum support for nf 250 Gb. Add rx reset
- * into nv_close, otherwise reenabling for wol can
- * cause DMA to kfree'd memory.
- * 0.31: 14 Nov 2004: ethtool support for getting/setting link
- * capabilities.
- * 0.32: 16 Apr 2005: RX_ERROR4 handling added.
- * 0.33: 16 May 2005: Support for MCP51 added.
- * 0.34: 18 Jun 2005: Add DEV_NEED_LINKTIMER to all nForce nics.
- * 0.35: 26 Jun 2005: Support for MCP55 added.
- * 0.36: 28 Jun 2005: Add jumbo frame support.
- * 0.37: 10 Jul 2005: Additional ethtool support, cleanup of pci id list
- * 0.38: 16 Jul 2005: tx irq rewrite: Use global flags instead of
- * per-packet flags.
- * 0.39: 18 Jul 2005: Add 64bit descriptor support.
- * 0.40: 19 Jul 2005: Add support for mac address change.
- * 0.41: 30 Jul 2005: Write back original MAC in nv_close instead
- * of nv_remove
- * 0.42: 06 Aug 2005: Fix lack of link speed initialization
- * in the second (and later) nv_open call
- * 0.43: 10 Aug 2005: Add support for tx checksum.
- * 0.44: 20 Aug 2005: Add support for scatter gather and segmentation.
- * 0.45: 18 Sep 2005: Remove nv_stop/start_rx from every link check
- * 0.46: 20 Oct 2005: Add irq optimization modes.
- * 0.47: 26 Oct 2005: Add phyaddr 0 in phy scan.
- * 0.48: 24 Dec 2005: Disable TSO, bugfix for pci_map_single
- * 0.49: 10 Dec 2005: Fix tso for large buffers.
- * 0.50: 20 Jan 2006: Add 8021pq tagging support.
- * 0.51: 20 Jan 2006: Add 64bit consistent memory allocation for rings.
- * 0.52: 20 Jan 2006: Add MSI/MSIX support.
- * 0.53: 19 Mar 2006: Fix init from low power mode and add hw reset.
- * 0.54: 21 Mar 2006: Fix spin locks for multi irqs and cleanup.
- * 0.55: 22 Mar 2006: Add flow control (pause frame).
- * 0.56: 22 Mar 2006: Additional ethtool config and moduleparam support.
- * 0.57: 14 May 2006: Mac address set in probe/remove and order corrections.
- * 0.58: 30 Oct 2006: Added support for sideband management unit.
- * 0.59: 30 Oct 2006: Added support for recoverable error.
- * 0.60: 20 Jan 2007: Code optimizations for rings, rx & tx data paths, and stats.
- *
* Known bugs:
* We suspect that on some hardware no TX done interrupts are generated.
* This means recovery from netif_stop_queue only happens if the hw timer
@@ -123,11 +39,6 @@
* DEV_NEED_TIMERIRQ will not harm you on sane hardware, only generating a few
* superfluous timer interrupts from the nic.
*/
-#ifdef CONFIG_FORCEDETH_NAPI
-#define DRIVERNAPI "-NAPI"
-#else
-#define DRIVERNAPI
-#endif
#define FORCEDETH_VERSION "0.61"
#define DRV_NAME "forcedeth"
@@ -908,6 +819,13 @@ static inline u32 nv_descr_getlength_ex(struct ring_desc_ex *prd, u32 v)
return le32_to_cpu(prd->flaglen) & LEN_MASK_V2;
}
+static bool nv_optimized(struct fe_priv *np)
+{
+ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2)
+ return false;
+ return true;
+}
+
static int reg_delay(struct net_device *dev, int offset, u32 mask, u32 target,
int delay, int delaymax, const char *msg)
{
@@ -934,7 +852,7 @@ static void setup_hw_rings(struct net_device *dev, int rxtx_flags)
struct fe_priv *np = get_nvpriv(dev);
u8 __iomem *base = get_hwbase(dev);
- if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
+ if (!nv_optimized(np)) {
if (rxtx_flags & NV_SETUP_RX_RING) {
writel((u32) cpu_to_le64(np->ring_addr), base + NvRegRxRingPhysAddr);
}
@@ -957,7 +875,7 @@ static void free_rings(struct net_device *dev)
{
struct fe_priv *np = get_nvpriv(dev);
- if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
+ if (!nv_optimized(np)) {
if (np->rx_ring.orig)
pci_free_consistent(np->pci_dev, sizeof(struct ring_desc) * (np->rx_ring_size + np->tx_ring_size),
np->rx_ring.orig, np->ring_addr);
@@ -1403,6 +1321,18 @@ static void nv_stop_tx(struct net_device *dev)
base + NvRegTransmitPoll);
}
+static void nv_start_rxtx(struct net_device *dev)
+{
+ nv_start_rx(dev);
+ nv_start_tx(dev);
+}
+
+static void nv_stop_rxtx(struct net_device *dev)
+{
+ nv_stop_rx(dev);
+ nv_stop_tx(dev);
+}
+
static void nv_txrx_reset(struct net_device *dev)
{
struct fe_priv *np = netdev_priv(dev);
@@ -1611,7 +1541,7 @@ static void nv_do_rx_refill(unsigned long data)
} else {
disable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector);
}
- if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2)
+ if (!nv_optimized(np))
retcode = nv_alloc_rx(dev);
else
retcode = nv_alloc_rx_optimized(dev);
@@ -1636,8 +1566,10 @@ static void nv_init_rx(struct net_device *dev)
{
struct fe_priv *np = netdev_priv(dev);
int i;
+
np->get_rx = np->put_rx = np->first_rx = np->rx_ring;
- if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2)
+
+ if (!nv_optimized(np))
np->last_rx.orig = &np->rx_ring.orig[np->rx_ring_size-1];
else
np->last_rx.ex = &np->rx_ring.ex[np->rx_ring_size-1];
@@ -1645,7 +1577,7 @@ static void nv_init_rx(struct net_device *dev)
np->last_rx_ctx = &np->rx_skb[np->rx_ring_size-1];
for (i = 0; i < np->rx_ring_size; i++) {
- if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
+ if (!nv_optimized(np)) {
np->rx_ring.orig[i].flaglen = 0;
np->rx_ring.orig[i].buf = 0;
} else {
@@ -1663,8 +1595,10 @@ static void nv_init_tx(struct net_device *dev)
{
struct fe_priv *np = netdev_priv(dev);
int i;
+
np->get_tx = np->put_tx = np->first_tx = np->tx_ring;
- if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2)
+
+ if (!nv_optimized(np))
np->last_tx.orig = &np->tx_ring.orig[np->tx_ring_size-1];
else
np->last_tx.ex = &np->tx_ring.ex[np->tx_ring_size-1];
@@ -1672,7 +1606,7 @@ static void nv_init_tx(struct net_device *dev)
np->last_tx_ctx = &np->tx_skb[np->tx_ring_size-1];
for (i = 0; i < np->tx_ring_size; i++) {
- if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
+ if (!nv_optimized(np)) {
np->tx_ring.orig[i].flaglen = 0;
np->tx_ring.orig[i].buf = 0;
} else {
@@ -1692,7 +1626,8 @@ static int nv_init_ring(struct net_device *dev)
nv_init_tx(dev);
nv_init_rx(dev);
- if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2)
+
+ if (!nv_optimized(np))
return nv_alloc_rx(dev);
else
return nv_alloc_rx_optimized(dev);
@@ -1723,7 +1658,7 @@ static void nv_drain_tx(struct net_device *dev)
unsigned int i;
for (i = 0; i < np->tx_ring_size; i++) {
- if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
+ if (!nv_optimized(np)) {
np->tx_ring.orig[i].flaglen = 0;
np->tx_ring.orig[i].buf = 0;
} else {
@@ -1743,7 +1678,7 @@ static void nv_drain_rx(struct net_device *dev)
int i;
for (i = 0; i < np->rx_ring_size; i++) {
- if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
+ if (!nv_optimized(np)) {
np->rx_ring.orig[i].flaglen = 0;
np->rx_ring.orig[i].buf = 0;
} else {
@@ -1764,7 +1699,7 @@ static void nv_drain_rx(struct net_device *dev)
}
}
-static void drain_ring(struct net_device *dev)
+static void nv_drain_rxtx(struct net_device *dev)
{
nv_drain_tx(dev);
nv_drain_rx(dev);
@@ -2155,7 +2090,7 @@ static void nv_tx_timeout(struct net_device *dev)
}
printk(KERN_INFO "%s: Dumping tx ring\n", dev->name);
for (i=0;i<np->tx_ring_size;i+= 4) {
- if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
+ if (!nv_optimized(np)) {
printk(KERN_INFO "%03x: %08x %08x // %08x %08x // %08x %08x // %08x %08x\n",
i,
le32_to_cpu(np->tx_ring.orig[i].buf),
@@ -2191,7 +2126,7 @@ static void nv_tx_timeout(struct net_device *dev)
nv_stop_tx(dev);
/* 2) check that the packets were not sent already: */
- if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2)
+ if (!nv_optimized(np))
nv_tx_done(dev);
else
nv_tx_done_optimized(dev, np->tx_ring_size);
@@ -2566,12 +2501,10 @@ static int nv_change_mtu(struct net_device *dev, int new_mtu)
netif_tx_lock_bh(dev);
spin_lock(&np->lock);
/* stop engines */
- nv_stop_rx(dev);
- nv_stop_tx(dev);
+ nv_stop_rxtx(dev);
nv_txrx_reset(dev);
/* drain rx queue */
- nv_drain_rx(dev);
- nv_drain_tx(dev);
+ nv_drain_rxtx(dev);
/* reinit driver view of the rx queue */
set_bufsize(dev);
if (nv_init_ring(dev)) {
@@ -2588,8 +2521,7 @@ static int nv_change_mtu(struct net_device *dev, int new_mtu)
pci_push(base);
/* restart rx engine */
- nv_start_rx(dev);
- nv_start_tx(dev);
+ nv_start_rxtx(dev);
spin_unlock(&np->lock);
netif_tx_unlock_bh(dev);
nv_enable_irq(dev);
@@ -3259,7 +3191,7 @@ static int nv_napi_poll(struct napi_struct *napi, int budget)
unsigned long flags;
int pkts, retcode;
- if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
+ if (!nv_optimized(np)) {
pkts = nv_rx_process(dev, budget);
retcode = nv_alloc_rx(dev);
} else {
@@ -3500,7 +3432,7 @@ static int nv_request_irq(struct net_device *dev, int intr_test)
if (intr_test) {
handler = nv_nic_irq_test;
} else {
- if (np->desc_ver == DESC_VER_3)
+ if (nv_optimized(np))
handler = nv_nic_irq_optimized;
else
handler = nv_nic_irq;
@@ -3649,12 +3581,10 @@ static void nv_do_nic_poll(unsigned long data)
netif_tx_lock_bh(dev);
spin_lock(&np->lock);
/* stop engines */
- nv_stop_rx(dev);
- nv_stop_tx(dev);
+ nv_stop_rxtx(dev);
nv_txrx_reset(dev);
/* drain rx queue */
- nv_drain_rx(dev);
- nv_drain_tx(dev);
+ nv_drain_rxtx(dev);
/* reinit driver view of the rx queue */
set_bufsize(dev);
if (nv_init_ring(dev)) {
@@ -3671,8 +3601,7 @@ static void nv_do_nic_poll(unsigned long data)
pci_push(base);
/* restart rx engine */
- nv_start_rx(dev);
- nv_start_tx(dev);
+ nv_start_rxtx(dev);
spin_unlock(&np->lock);
netif_tx_unlock_bh(dev);
}
@@ -3684,7 +3613,7 @@ static void nv_do_nic_poll(unsigned long data)
pci_push(base);
if (!using_multi_irqs(dev)) {
- if (np->desc_ver == DESC_VER_3)
+ if (nv_optimized(np))
nv_nic_irq_optimized(0, dev);
else
nv_nic_irq(0, dev);
@@ -3881,8 +3810,7 @@ static int nv_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
netif_tx_lock_bh(dev);
spin_lock(&np->lock);
/* stop engines */
- nv_stop_rx(dev);
- nv_stop_tx(dev);
+ nv_stop_rxtx(dev);
spin_unlock(&np->lock);
netif_tx_unlock_bh(dev);
}
@@ -3988,8 +3916,7 @@ static int nv_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
}
if (netif_running(dev)) {
- nv_start_rx(dev);
- nv_start_tx(dev);
+ nv_start_rxtx(dev);
nv_enable_irq(dev);
}
@@ -4032,8 +3959,7 @@ static int nv_nway_reset(struct net_device *dev)
netif_tx_lock_bh(dev);
spin_lock(&np->lock);
/* stop engines */
- nv_stop_rx(dev);
- nv_stop_tx(dev);
+ nv_stop_rxtx(dev);
spin_unlock(&np->lock);
netif_tx_unlock_bh(dev);
printk(KERN_INFO "%s: link down.\n", dev->name);
@@ -4053,8 +3979,7 @@ static int nv_nway_reset(struct net_device *dev)
}
if (netif_running(dev)) {
- nv_start_rx(dev);
- nv_start_tx(dev);
+ nv_start_rxtx(dev);
nv_enable_irq(dev);
}
ret = 0;
@@ -4111,7 +4036,7 @@ static int nv_set_ringparam(struct net_device *dev, struct ethtool_ringparam* ri
}
/* allocate new rings */
- if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
+ if (!nv_optimized(np)) {
rxtx_ring = pci_alloc_consistent(np->pci_dev,
sizeof(struct ring_desc) * (ring->rx_pending + ring->tx_pending),
&ring_addr);
@@ -4124,7 +4049,7 @@ static int nv_set_ringparam(struct net_device *dev, struct ethtool_ringparam* ri
tx_skbuff = kmalloc(sizeof(struct nv_skb_map) * ring->tx_pending, GFP_KERNEL);
if (!rxtx_ring || !rx_skbuff || !tx_skbuff) {
/* fall back to old rings */
- if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
+ if (!nv_optimized(np)) {
if (rxtx_ring)
pci_free_consistent(np->pci_dev, sizeof(struct ring_desc) * (ring->rx_pending + ring->tx_pending),
rxtx_ring, ring_addr);
@@ -4145,12 +4070,10 @@ static int nv_set_ringparam(struct net_device *dev, struct ethtool_ringparam* ri
netif_tx_lock_bh(dev);
spin_lock(&np->lock);
/* stop engines */
- nv_stop_rx(dev);
- nv_stop_tx(dev);
+ nv_stop_rxtx(dev);
nv_txrx_reset(dev);
/* drain queues */
- nv_drain_rx(dev);
- nv_drain_tx(dev);
+ nv_drain_rxtx(dev);
/* delete queues */
free_rings(dev);
}
@@ -4158,7 +4081,8 @@ static int nv_set_ringparam(struct net_device *dev, struct ethtool_ringparam* ri
/* set new values */
np->rx_ring_size = ring->rx_pending;
np->tx_ring_size = ring->tx_pending;
- if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
+
+ if (!nv_optimized(np)) {
np->rx_ring.orig = (struct ring_desc*)rxtx_ring;
np->tx_ring.orig = &np->rx_ring.orig[np->rx_ring_size];
} else {
@@ -4190,8 +4114,7 @@ static int nv_set_ringparam(struct net_device *dev, struct ethtool_ringparam* ri
pci_push(base);
/* restart engines */
- nv_start_rx(dev);
- nv_start_tx(dev);
+ nv_start_rxtx(dev);
spin_unlock(&np->lock);
netif_tx_unlock_bh(dev);
nv_enable_irq(dev);
@@ -4232,8 +4155,7 @@ static int nv_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam*
netif_tx_lock_bh(dev);
spin_lock(&np->lock);
/* stop engines */
- nv_stop_rx(dev);
- nv_stop_tx(dev);
+ nv_stop_rxtx(dev);
spin_unlock(&np->lock);
netif_tx_unlock_bh(dev);
}
@@ -4274,8 +4196,7 @@ static int nv_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam*
}
if (netif_running(dev)) {
- nv_start_rx(dev);
- nv_start_tx(dev);
+ nv_start_rxtx(dev);
nv_enable_irq(dev);
}
return 0;
@@ -4511,8 +4432,7 @@ static int nv_loopback_test(struct net_device *dev)
pci_push(base);
/* restart rx engine */
- nv_start_rx(dev);
- nv_start_tx(dev);
+ nv_start_rxtx(dev);
/* setup packet for tx */
pkt_len = ETH_DATA_LEN;
@@ -4530,7 +4450,7 @@ static int nv_loopback_test(struct net_device *dev)
for (i = 0; i < pkt_len; i++)
pkt_data[i] = (u8)(i & 0xff);
- if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
+ if (!nv_optimized(np)) {
np->tx_ring.orig[0].buf = cpu_to_le32(test_dma_addr);
np->tx_ring.orig[0].flaglen = cpu_to_le32((pkt_len-1) | np->tx_flags | tx_flags_extra);
} else {
@@ -4544,7 +4464,7 @@ static int nv_loopback_test(struct net_device *dev)
msleep(500);
/* check for rx of the packet */
- if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
+ if (!nv_optimized(np)) {
flags = le32_to_cpu(np->rx_ring.orig[0].flaglen);
len = nv_descr_getlength(&np->rx_ring.orig[0], np->desc_ver);
@@ -4590,12 +4510,10 @@ static int nv_loopback_test(struct net_device *dev)
dev_kfree_skb_any(tx_skb);
out:
/* stop engines */
- nv_stop_rx(dev);
- nv_stop_tx(dev);
+ nv_stop_rxtx(dev);
nv_txrx_reset(dev);
/* drain rx queue */
- nv_drain_rx(dev);
- nv_drain_tx(dev);
+ nv_drain_rxtx(dev);
if (netif_running(dev)) {
writel(misc1_flags, base + NvRegMisc1);
@@ -4633,12 +4551,10 @@ static void nv_self_test(struct net_device *dev, struct ethtool_test *test, u64
writel(NVREG_IRQSTAT_MASK, base + NvRegMSIXIrqStatus);
}
/* stop engines */
- nv_stop_rx(dev);
- nv_stop_tx(dev);
+ nv_stop_rxtx(dev);
nv_txrx_reset(dev);
/* drain rx queue */
- nv_drain_rx(dev);
- nv_drain_tx(dev);
+ nv_drain_rxtx(dev);
spin_unlock_irq(&np->lock);
netif_tx_unlock_bh(dev);
}
@@ -4679,8 +4595,7 @@ static void nv_self_test(struct net_device *dev, struct ethtool_test *test, u64
writel(NVREG_TXRXCTL_KICK|np->txrxctl_bits, get_hwbase(dev) + NvRegTxRxControl);
pci_push(base);
/* restart rx engine */
- nv_start_rx(dev);
- nv_start_tx(dev);
+ nv_start_rxtx(dev);
netif_start_queue(dev);
#ifdef CONFIG_FORCEDETH_NAPI
napi_enable(&np->napi);
@@ -4909,8 +4824,7 @@ static int nv_open(struct net_device *dev)
* to init hw */
np->linkspeed = 0;
ret = nv_update_linkspeed(dev);
- nv_start_rx(dev);
- nv_start_tx(dev);
+ nv_start_rxtx(dev);
netif_start_queue(dev);
#ifdef CONFIG_FORCEDETH_NAPI
napi_enable(&np->napi);
@@ -4933,7 +4847,7 @@ static int nv_open(struct net_device *dev)
return 0;
out_drain:
- drain_ring(dev);
+ nv_drain_rxtx(dev);
return ret;
}
@@ -4956,8 +4870,7 @@ static int nv_close(struct net_device *dev)
netif_stop_queue(dev);
spin_lock_irq(&np->lock);
- nv_stop_tx(dev);
- nv_stop_rx(dev);
+ nv_stop_rxtx(dev);
nv_txrx_reset(dev);
/* disable interrupts on the nic or we will lock up */
@@ -4970,7 +4883,7 @@ static int nv_close(struct net_device *dev)
nv_free_irq(dev);
- drain_ring(dev);
+ nv_drain_rxtx(dev);
if (np->wolenabled) {
writel(NVREG_PFF_ALWAYS|NVREG_PFF_MYADDR, base + NvRegPacketFilterFlags);
@@ -5128,7 +5041,7 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
np->rx_ring_size = RX_RING_DEFAULT;
np->tx_ring_size = TX_RING_DEFAULT;
- if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
+ if (!nv_optimized(np)) {
np->rx_ring.orig = pci_alloc_consistent(pci_dev,
sizeof(struct ring_desc) * (np->rx_ring_size + np->tx_ring_size),
&np->ring_addr);
@@ -5150,7 +5063,8 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
dev->open = nv_open;
dev->stop = nv_close;
- if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2)
+
+ if (!nv_optimized(np))
dev->hard_start_xmit = nv_start_xmit;
else
dev->hard_start_xmit = nv_start_xmit_optimized;
^ permalink raw reply related
* [PATCH 0/6] forcedeth interrupt and task overhaul, v2
From: Jeff Garzik @ 2007-10-17 5:52 UTC (permalink / raw)
To: netdev, LKML; +Cc: Manfred Spraul, Ingo Molnar, Ayaz Abdulla
These six changes can be found in the 'fe' branch of
git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/netdev-2.6.git
[netdrvr] forcedeth: internal simplifications; changelog removal
[netdrvr] forcedeth: timer overhaul
[netdrvr] forcedeth: eliminate some duplicate irq handling code
[netdrvr] forcedeth: unconditionally enable NAPI
[netdrvr] forcedeth: use NAPI for TX completion
[netdrvr] interrupt handling overhaul
I rewrote the previous forcedeth patchset, even though the direction and
methods are quite similar to the last patchset.
This hammers out bugs, problems, inconsistencies, mainly with interrupt
handling and locking. This moves the driver towards removing the
disable_irq() usage, but does not actually take that step (yet).
A few changes just went upstream, too:
Ingo Molnar (1):
forcedeth: fix NAPI rx poll function
Jeff Garzik (2):
[netdrvr] forcedeth: improved probe info; dev_printk() cleanups
[netdrvr] forcedeth: remove in-driver copy of net_device_stats
This patchset is diff'd against the latest linux-2.6.git as of an hour
ago or so. It applies on top of the three changes listed immediately
above this paragraph.
At this point, it has been tested on
nVidia Corporation CK804 Ethernet Controller (rev a3)
but I'm definitely interested in other testing, particularly performance
numbers (wire speed? or below?), CPU usage and memory usage info. Most
particularly CPU usage info. And maybe interrupt count with/without
this patchset, too.
If you send feedback, 'dmesg' and 'lspci' output is helpful. The
current forcedeth driver in upstream (0.61) prints out useful diagnostic
information about your chip at bootup.
^ permalink raw reply
* Re: [PATCH 4/4] UDP memory accounting and limitation(take 5): memory limitation
From: Herbert Xu @ 2007-10-17 4:23 UTC (permalink / raw)
To: Hideo AOKI
Cc: Satoshi OSHIMA, David Miller, netdev, Hideo AOKI, Yumiko SUGITA,
Andi Kleen, Evgeniy Polyakov, Stephen Hemminger, yoshfuji
In-Reply-To: <47158A50.90905@redhat.com>
On Wed, Oct 17, 2007 at 12:06:40AM -0400, Hideo AOKI wrote:
>
> Thank you so much for your comments.
>
> I'm going to fix them in next take patch set.
Thank you!
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply
* Re: [PATCH 4/4] UDP memory accounting and limitation(take 5): memory limitation
From: Hideo AOKI @ 2007-10-17 4:06 UTC (permalink / raw)
To: Herbert Xu
Cc: Satoshi OSHIMA, David Miller, netdev, Hideo AOKI, Yumiko SUGITA,
Andi Kleen, Evgeniy Polyakov, Stephen Hemminger, yoshfuji
In-Reply-To: <20071015104306.GA30828@gondor.apana.org.au>
Herbert Xu wrote:
> On Fri, Oct 12, 2007 at 09:11:27PM +0900, Satoshi OSHIMA wrote:
>> +static inline int __ip_check_max_skb_pages(struct sock *sk, int size)
>> +{
>> + switch(sk->sk_protocol) {
>> + case IPPROTO_UDP:
>> + if (sk->sk_prot->sysctl_mem[0] > UDP_MIN_SKB_PAGES)
>> + if (atomic_read(sk->sk_prot->memory_allocated)+size
>> + >= sk->sk_prot->sysctl_mem[0])
>> + return -ENOBUFS;
>> + /* Fall through */
>> + default:
>> + break;
>> + }
>> + return 0;
>> +}
>
> Since you're not doing a per-user limit, you must give each
> socket a minimum even when the total exceeds your threshold.
>
> Otherwise any local user can trivially DoS the whole system.
>
>> +UDP variables:
>> +
>> +udp_mem - INTERGER
>> + Number of pages allowed for queueing by all UDP sockets.
>> + Minimal value is 4096. If 4096 is set, UDP memory will not
>> + be limited.
>> + Default: 4096
>
> This seems to be a rather confusing setup. We don't set a
> minimum for TCP so is this really necessary?
Hello Herbert,
Thank you so much for your comments.
I'm going to fix them in next take patch set.
Regards,
Hideo
--
Hitachi Computer Products (America) Inc.
^ permalink raw reply
* Re: [patch] net: avoid race between netpoll and network fast path
From: David Miller @ 2007-10-17 4:06 UTC (permalink / raw)
To: tina.yang; +Cc: mpm, netdev
In-Reply-To: <47158540.7000709@oracle.com>
From: Tina Yang <tina.yang@oracle.com>
Date: Tue, 16 Oct 2007 20:45:04 -0700
> The current netpoll design and implementation has serveral race issues with the
> network fast path that panics/hangs the system or causes interface timeout/reset
> but the fix is likely to have impact on the overall system performance and could
> involve a large number of drivers. The proposal is to disable the problem code
> for normal operations but only to enable it at the time of crash in case polling
> is necessary. Tests that have been done included the bug fix verification
> as well as regression check on the netlog results in various crash modes.
>
> Signed-off-by: Tina Yang <tina.yang@oracle.com>
This is at best a kludge, and it's the wrong way to approach this problem.
Fix the bug, and fix it right.
If you disable that stretch of code, what you've done is make the
netpoll code hang and/or drop console messages if the TX queue is full
in the driver and the only way to liberate TX space is to call into
->poll().
You haven't shown the precise race that leads to corruption so that someone
so motivated can guide you towards a more correct fix if you are not
capable of implementing it properly on your own.
^ permalink raw reply
* [patch] net: avoid race between netpoll and network fast path
From: Tina Yang @ 2007-10-17 3:45 UTC (permalink / raw)
To: Matt Mackall; +Cc: netdev
The current netpoll design and implementation has serveral race issues with the
network fast path that panics/hangs the system or causes interface timeout/reset
but the fix is likely to have impact on the overall system performance and could
involve a large number of drivers. The proposal is to disable the problem code
for normal operations but only to enable it at the time of crash in case polling
is necessary. Tests that have been done included the bug fix verification
as well as regression check on the netlog results in various crash modes.
Signed-off-by: Tina Yang <tina.yang@oracle.com>
---
--- linux-2.6.23.1/include/linux/kernel.h.orig 2007-10-12 09:43:44.000000000 -0700
+++ linux-2.6.23.1/include/linux/kernel.h 2007-10-15 22:05:27.000000000 -0700
@@ -184,6 +184,8 @@ static inline void console_verbose(void)
console_loglevel = 15;
}
+extern int netpoll_enable;
+
extern void bust_spinlocks(int yes);
extern void wake_up_klogd(void);
extern int oops_in_progress; /* If set, an oops, panic(), BUG() or die() is in progress */
--- linux-2.6.23.1/net/core/netpoll.c.orig 2007-10-12 09:43:44.000000000 -0700
+++ linux-2.6.23.1/net/core/netpoll.c 2007-10-15 22:20:15.000000000 -0700
@@ -150,15 +150,19 @@ static void service_arp_queue(struct net
}
}
+int netpoll_enable;
+EXPORT_SYMBOL(netpoll_enable);
void netpoll_poll(struct netpoll *np)
{
if (!np->dev || !netif_running(np->dev) || !np->dev->poll_controller)
return;
/* Process pending work on NIC */
- np->dev->poll_controller(np->dev);
- if (np->dev->poll)
- poll_napi(np);
+ if (unlikely(netpoll_enable)) {
+ np->dev->poll_controller(np->dev);
+ if (np->dev->poll)
+ poll_napi(np);
+ }
service_arp_queue(np->dev->npinfo);
--- linux-2.6.23.1/kernel/panic.c.orig 2007-10-12 09:43:44.000000000 -0700
+++ linux-2.6.23.1/kernel/panic.c 2007-10-15 22:07:25.000000000 -0700
@@ -66,6 +66,7 @@ NORET_TYPE void panic(const char * fmt,
unsigned long caller = (unsigned long) __builtin_return_address(0);
#endif
+ netpoll_enable = 1;
/*
* It's possible to come here directly from a panic-assertion and not
* have preempt disabled. Some functions called from here want
--- linux-2.6.23.1/arch/x86_64/kernel/traps.c.orig 2007-10-12 09:43:44.000000000 -0700
+++ linux-2.6.23.1/arch/x86_64/kernel/traps.c 2007-10-15 22:06:29.000000000 -0700
@@ -522,6 +522,8 @@ void __kprobes __die(const char * str, s
#endif
printk("\n");
notify_die(DIE_OOPS, str, regs, err, current->thread.trap_no, SIGSEGV);
+ if (kexec_should_crash(current))
+ netpoll_enable = 1;
show_registers(regs);
add_taint(TAINT_DIE);
/* Executive summary in case the oops scrolled away */
--- linux-2.6.23.1/arch/i386/kernel/traps.c.orig 2007-10-12 09:43:44.000000000 -0700
+++ linux-2.6.23.1/arch/i386/kernel/traps.c 2007-10-15 22:06:14.000000000 -0700
@@ -428,6 +428,8 @@ void die(const char * str, struct pt_reg
if (notify_die(DIE_OOPS, str, regs, err,
current->thread.trap_no, SIGSEGV) !=
NOTIFY_STOP) {
+ if (kexec_should_crash(current))
+ netpoll_enable = 1;
show_registers(regs);
/* Executive summary in case the oops scrolled away */
esp = (unsigned long) (®s->esp);
^ permalink raw reply
* Re: Please pull 'fixes-davem' branch of wireless-2.6
From: Michael Wu @ 2007-10-17 3:29 UTC (permalink / raw)
To: John W. Linville
Cc: davem-fT/PcQaiUtIeIZ0/mPfg9Q, netdev-u79uwXL29TY76Z2rM5mHXA,
linux-wireless-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20071017023145.GA2848-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 452 bytes --]
On Tuesday 16 October 2007 22:31:46 John W. Linville wrote:
> Bill Moss (1):
> mac80211: honor IW_SCAN_THIS_ESSID in siwscan ioctl
>
Where did this patch come from? The original patch looked nothing like this,
and this patch changes the behavior of siwscan incorrectly. If the interface
is not STA, IBSS, or AP, scanning should not be permitted. This patch allows
scanning on any interface if a SSID is specified. NACK.
-Michael Wu
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 194 bytes --]
^ permalink raw reply
* Re: [PATCH/RFC] net: Add __napi_sycnhronize() to sync with napi poll
From: Benjamin Herrenschmidt @ 2007-10-17 3:24 UTC (permalink / raw)
To: Jeff Garzik
Cc: Stephen Hemminger, netdev, David S. Miller, Roland Dreier,
linuxppc-dev list
In-Reply-To: <47157EA4.1000500@garzik.org>
\
> > Note: unfortunately, Jeff already picked up the EMAC patch without
> > waiting for this to be sorted out (oops...). So if you agree with
> > this patch, it would be nice to have it go in quickly or maybe via
> > Jeff's tree to avoid breakage ? Not terribly important tho.
>
>
> Sorry, I thought that was the way everybody was headed. With the driver
> broken /anyway/, I just sorta threw it into the pile of fixes.
>
> It's upstream now, so let me know if you want to revert or move forward
> from here...
Let's just move forward. I can always re-fix the driver :-)
Ben.
^ permalink raw reply
* Re: [PATCH/RFC] net: Add __napi_sycnhronize() to sync with napi poll
From: Jeff Garzik @ 2007-10-17 3:16 UTC (permalink / raw)
To: benh
Cc: Stephen Hemminger, netdev, David S. Miller, Roland Dreier,
linuxppc-dev list
In-Reply-To: <1192589522.11899.130.camel@pasglop>
Benjamin Herrenschmidt wrote:
> net: Add __napi_synchronize() to sync with napi poll
>
> The EMAC driver which needs to handle multiple devices with one
> NAPI instance implements its own per-channel disable bit. However,
> when setting such a bit, it needs to synchronize with the poller
> (that is make sure that any pending poller instance has completed,
> or is started late enough to see that disable bit).
>
> This implements a low level __napi_synchronize() function to acheive
> that. The underscores are to emphasis the low level aspect of it and
> to discourage driver writers who don't know what they are doing to
> use it (to please DaveM :-)
>
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> ---
>
> Switched to do a cpu_relax() spin instead and only on SMP. Not that
> we have an smp_mb() in there which synchronize_irq() lacks. I believe
> this is a bug in synchronize_irq() which I'll handle separately.
>
> Note: unfortunately, Jeff already picked up the EMAC patch without
> waiting for this to be sorted out (oops...). So if you agree with
> this patch, it would be nice to have it go in quickly or maybe via
> Jeff's tree to avoid breakage ? Not terribly important tho.
Sorry, I thought that was the way everybody was headed. With the driver
broken /anyway/, I just sorta threw it into the pile of fixes.
It's upstream now, so let me know if you want to revert or move forward
from here...
Jeff
^ permalink raw reply
* Please pull 'fixes-davem' branch of wireless-2.6
From: John W. Linville @ 2007-10-17 2:31 UTC (permalink / raw)
To: davem; +Cc: netdev, linux-wireless
Dave,
A few fixes for 2.6.24. I've included some SSB ones since I wasn't
sure who else should get them.
Thanks,
John
---
As usual, individual patches are available here:
http://www.kernel.org/pub/linux/kernel/people/linville/wireless-2.6/fixes-davem
---
The following changes since commit 65a6ec0d72a07f16719e9b7a96e1c4bae044b591:
Linus Torvalds (1):
Merge branch 'devel' of master.kernel.org:/home/rmk/linux-2.6-arm
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-2.6.git fixes-davem
Bill Moss (1):
mac80211: honor IW_SCAN_THIS_ESSID in siwscan ioctl
Felix Fietkau (1):
ssb: Fix a null pointer check in mipscore init
Ingo Molnar (1):
ssb: fix build failure
Johannes Berg (3):
ieee80211: fix TKIP QoS bug
mac80211: fix set_channel regression
mac80211: reorder association debug output
John W. Linville (2):
mac80211: store channel info in sta_bss_list
mac80211: store SSID in sta_bss_list
drivers/ssb/Kconfig | 4 +-
drivers/ssb/driver_mipscore.c | 3 +-
net/ieee80211/ieee80211_crypt_tkip.c | 2 +-
net/mac80211/ieee80211_ioctl.c | 45 +++++++++++++--------
net/mac80211/ieee80211_sta.c | 71 +++++++++++++++++++++-------------
5 files changed, 77 insertions(+), 48 deletions(-)
diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig
index b4a5e5e..d976660 100644
--- a/drivers/ssb/Kconfig
+++ b/drivers/ssb/Kconfig
@@ -22,7 +22,7 @@ config SSB
config SSB_PCIHOST_POSSIBLE
bool
- depends on SSB && PCI
+ depends on SSB && (PCI = y || PCI = SSB)
default y
config SSB_PCIHOST
@@ -37,7 +37,7 @@ config SSB_PCIHOST
config SSB_PCMCIAHOST_POSSIBLE
bool
- depends on SSB && PCMCIA && EXPERIMENTAL
+ depends on SSB && (PCMCIA = y || PCMCIA = SSB) && EXPERIMENTAL
default y
config SSB_PCMCIAHOST
diff --git a/drivers/ssb/driver_mipscore.c b/drivers/ssb/driver_mipscore.c
index ab8691a..3d3dd32 100644
--- a/drivers/ssb/driver_mipscore.c
+++ b/drivers/ssb/driver_mipscore.c
@@ -173,7 +173,7 @@ u32 ssb_cpu_clock(struct ssb_mipscore *mcore)
void ssb_mipscore_init(struct ssb_mipscore *mcore)
{
- struct ssb_bus *bus = mcore->dev->bus;
+ struct ssb_bus *bus;
struct ssb_device *dev;
unsigned long hz, ns;
unsigned int irq, i;
@@ -183,6 +183,7 @@ void ssb_mipscore_init(struct ssb_mipscore *mcore)
ssb_dprintk(KERN_INFO PFX "Initializing MIPS core...\n");
+ bus = mcore->dev->bus;
hz = ssb_clockspeed(bus);
if (!hz)
hz = 100000000;
diff --git a/net/ieee80211/ieee80211_crypt_tkip.c b/net/ieee80211/ieee80211_crypt_tkip.c
index 6cc54ee..72e6ab6 100644
--- a/net/ieee80211/ieee80211_crypt_tkip.c
+++ b/net/ieee80211/ieee80211_crypt_tkip.c
@@ -586,7 +586,7 @@ static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr)
if (stype & IEEE80211_STYPE_QOS_DATA) {
const struct ieee80211_hdr_3addrqos *qoshdr =
(struct ieee80211_hdr_3addrqos *)skb->data;
- hdr[12] = qoshdr->qos_ctl & cpu_to_le16(IEEE80211_QCTL_TID);
+ hdr[12] = le16_to_cpu(qoshdr->qos_ctl) & IEEE80211_QCTL_TID;
} else
hdr[12] = 0; /* priority */
diff --git a/net/mac80211/ieee80211_ioctl.c b/net/mac80211/ieee80211_ioctl.c
index f0224c2..a57fed7 100644
--- a/net/mac80211/ieee80211_ioctl.c
+++ b/net/mac80211/ieee80211_ioctl.c
@@ -306,9 +306,12 @@ int ieee80211_set_channel(struct ieee80211_local *local, int channel, int freq)
((chan->chan == channel) || (chan->freq == freq))) {
local->oper_channel = chan;
local->oper_hw_mode = mode;
- set++;
+ set = 1;
+ break;
}
}
+ if (set)
+ break;
}
if (set) {
@@ -508,32 +511,40 @@ static int ieee80211_ioctl_giwap(struct net_device *dev,
static int ieee80211_ioctl_siwscan(struct net_device *dev,
struct iw_request_info *info,
- struct iw_point *data, char *extra)
+ union iwreq_data *wrqu, char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct iw_scan_req *req = NULL;
u8 *ssid = NULL;
size_t ssid_len = 0;
if (!netif_running(dev))
return -ENETDOWN;
- switch (sdata->type) {
- case IEEE80211_IF_TYPE_STA:
- case IEEE80211_IF_TYPE_IBSS:
- if (local->scan_flags & IEEE80211_SCAN_MATCH_SSID) {
- ssid = sdata->u.sta.ssid;
- ssid_len = sdata->u.sta.ssid_len;
- }
- break;
- case IEEE80211_IF_TYPE_AP:
- if (local->scan_flags & IEEE80211_SCAN_MATCH_SSID) {
- ssid = sdata->u.ap.ssid;
- ssid_len = sdata->u.ap.ssid_len;
+ if (wrqu->data.length == sizeof(struct iw_scan_req) &&
+ wrqu->data.flags & IW_SCAN_THIS_ESSID) {
+ req = (struct iw_scan_req *)extra;
+ ssid = req->essid;
+ ssid_len = req->essid_len;
+ } else {
+ switch (sdata->type) {
+ case IEEE80211_IF_TYPE_STA:
+ case IEEE80211_IF_TYPE_IBSS:
+ if (local->scan_flags & IEEE80211_SCAN_MATCH_SSID) {
+ ssid = sdata->u.sta.ssid;
+ ssid_len = sdata->u.sta.ssid_len;
+ }
+ break;
+ case IEEE80211_IF_TYPE_AP:
+ if (local->scan_flags & IEEE80211_SCAN_MATCH_SSID) {
+ ssid = sdata->u.ap.ssid;
+ ssid_len = sdata->u.ap.ssid_len;
+ }
+ break;
+ default:
+ return -EOPNOTSUPP;
}
- break;
- default:
- return -EOPNOTSUPP;
}
return ieee80211_sta_req_scan(dev, ssid, ssid_len);
diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c
index 1641e8f..db81aef 100644
--- a/net/mac80211/ieee80211_sta.c
+++ b/net/mac80211/ieee80211_sta.c
@@ -12,7 +12,6 @@
*/
/* TODO:
- * BSS table: use <BSSID,SSID> as the key to support multi-SSID APs
* order BSS list by RSSI(?) ("quality of AP")
* scan result table filtering (by capability (privacy, IBSS/BSS, WPA/RSN IE,
* SSID)
@@ -61,7 +60,8 @@
static void ieee80211_send_probe_req(struct net_device *dev, u8 *dst,
u8 *ssid, size_t ssid_len);
static struct ieee80211_sta_bss *
-ieee80211_rx_bss_get(struct net_device *dev, u8 *bssid);
+ieee80211_rx_bss_get(struct net_device *dev, u8 *bssid, int channel,
+ u8 *ssid, u8 ssid_len);
static void ieee80211_rx_bss_put(struct net_device *dev,
struct ieee80211_sta_bss *bss);
static int ieee80211_sta_find_ibss(struct net_device *dev,
@@ -427,7 +427,9 @@ static void ieee80211_set_associated(struct net_device *dev,
if (sdata->type != IEEE80211_IF_TYPE_STA)
return;
- bss = ieee80211_rx_bss_get(dev, ifsta->bssid);
+ bss = ieee80211_rx_bss_get(dev, ifsta->bssid,
+ local->hw.conf.channel,
+ ifsta->ssid, ifsta->ssid_len);
if (bss) {
if (bss->has_erp_value)
ieee80211_handle_erp_ie(dev, bss->erp_value);
@@ -574,7 +576,8 @@ static void ieee80211_send_assoc(struct net_device *dev,
capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME |
WLAN_CAPABILITY_SHORT_PREAMBLE;
}
- bss = ieee80211_rx_bss_get(dev, ifsta->bssid);
+ bss = ieee80211_rx_bss_get(dev, ifsta->bssid, local->hw.conf.channel,
+ ifsta->ssid, ifsta->ssid_len);
if (bss) {
if (bss->capability & WLAN_CAPABILITY_PRIVACY)
capab |= WLAN_CAPABILITY_PRIVACY;
@@ -722,6 +725,7 @@ static void ieee80211_send_disassoc(struct net_device *dev,
static int ieee80211_privacy_mismatch(struct net_device *dev,
struct ieee80211_if_sta *ifsta)
{
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sta_bss *bss;
int res = 0;
@@ -729,7 +733,8 @@ static int ieee80211_privacy_mismatch(struct net_device *dev,
ifsta->key_management_enabled)
return 0;
- bss = ieee80211_rx_bss_get(dev, ifsta->bssid);
+ bss = ieee80211_rx_bss_get(dev, ifsta->bssid, local->hw.conf.channel,
+ ifsta->ssid, ifsta->ssid_len);
if (!bss)
return 0;
@@ -1203,15 +1208,11 @@ static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev,
capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
- if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
- printk(KERN_DEBUG "%s: invalid aid value %d; bits 15:14 not "
- "set\n", dev->name, aid);
- aid &= ~(BIT(15) | BIT(14));
printk(KERN_DEBUG "%s: RX %sssocResp from %s (capab=0x%x "
"status=%d aid=%d)\n",
dev->name, reassoc ? "Rea" : "A", print_mac(mac, mgmt->sa),
- capab_info, status_code, aid);
+ capab_info, status_code, aid & ~(BIT(15) | BIT(14)));
if (status_code != WLAN_STATUS_SUCCESS) {
printk(KERN_DEBUG "%s: AP denied association (code=%d)\n",
@@ -1223,6 +1224,11 @@ static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev,
return;
}
+ if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
+ printk(KERN_DEBUG "%s: invalid aid value %d; bits 15:14 not "
+ "set\n", dev->name, aid);
+ aid &= ~(BIT(15) | BIT(14));
+
pos = mgmt->u.assoc_resp.variable;
if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems)
== ParseFailed) {
@@ -1241,7 +1247,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev,
* update our stored copy */
if (elems.erp_info && elems.erp_info_len >= 1) {
struct ieee80211_sta_bss *bss
- = ieee80211_rx_bss_get(dev, ifsta->bssid);
+ = ieee80211_rx_bss_get(dev, ifsta->bssid,
+ local->hw.conf.channel,
+ ifsta->ssid, ifsta->ssid_len);
if (bss) {
bss->erp_value = elems.erp_info[0];
bss->has_erp_value = 1;
@@ -1271,7 +1279,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev,
" AP\n", dev->name);
return;
}
- bss = ieee80211_rx_bss_get(dev, ifsta->bssid);
+ bss = ieee80211_rx_bss_get(dev, ifsta->bssid,
+ local->hw.conf.channel,
+ ifsta->ssid, ifsta->ssid_len);
if (bss) {
sta->last_rssi = bss->rssi;
sta->last_signal = bss->signal;
@@ -1347,7 +1357,8 @@ static void __ieee80211_rx_bss_hash_del(struct net_device *dev,
static struct ieee80211_sta_bss *
-ieee80211_rx_bss_add(struct net_device *dev, u8 *bssid)
+ieee80211_rx_bss_add(struct net_device *dev, u8 *bssid, int channel,
+ u8 *ssid, u8 ssid_len)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sta_bss *bss;
@@ -1358,6 +1369,11 @@ ieee80211_rx_bss_add(struct net_device *dev, u8 *bssid)
atomic_inc(&bss->users);
atomic_inc(&bss->users);
memcpy(bss->bssid, bssid, ETH_ALEN);
+ bss->channel = channel;
+ if (ssid && ssid_len <= IEEE80211_MAX_SSID_LEN) {
+ memcpy(bss->ssid, ssid, ssid_len);
+ bss->ssid_len = ssid_len;
+ }
spin_lock_bh(&local->sta_bss_lock);
/* TODO: order by RSSI? */
@@ -1369,7 +1385,8 @@ ieee80211_rx_bss_add(struct net_device *dev, u8 *bssid)
static struct ieee80211_sta_bss *
-ieee80211_rx_bss_get(struct net_device *dev, u8 *bssid)
+ieee80211_rx_bss_get(struct net_device *dev, u8 *bssid, int channel,
+ u8 *ssid, u8 ssid_len)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sta_bss *bss;
@@ -1377,7 +1394,10 @@ ieee80211_rx_bss_get(struct net_device *dev, u8 *bssid)
spin_lock_bh(&local->sta_bss_lock);
bss = local->sta_bss_hash[STA_HASH(bssid)];
while (bss) {
- if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0) {
+ if (!memcmp(bss->bssid, bssid, ETH_ALEN) &&
+ bss->channel == channel &&
+ bss->ssid_len == ssid_len &&
+ (ssid_len == 0 || !memcmp(bss->ssid, ssid, ssid_len))) {
atomic_inc(&bss->users);
break;
}
@@ -1545,9 +1565,11 @@ static void ieee80211_rx_bss_info(struct net_device *dev,
else
channel = rx_status->channel;
- bss = ieee80211_rx_bss_get(dev, mgmt->bssid);
+ bss = ieee80211_rx_bss_get(dev, mgmt->bssid, channel,
+ elems.ssid, elems.ssid_len);
if (!bss) {
- bss = ieee80211_rx_bss_add(dev, mgmt->bssid);
+ bss = ieee80211_rx_bss_add(dev, mgmt->bssid, channel,
+ elems.ssid, elems.ssid_len);
if (!bss)
return;
} else {
@@ -1573,10 +1595,6 @@ static void ieee80211_rx_bss_info(struct net_device *dev,
bss->beacon_int = le16_to_cpu(mgmt->u.beacon.beacon_int);
bss->capability = le16_to_cpu(mgmt->u.beacon.capab_info);
- if (elems.ssid && elems.ssid_len <= IEEE80211_MAX_SSID_LEN) {
- memcpy(bss->ssid, elems.ssid, elems.ssid_len);
- bss->ssid_len = elems.ssid_len;
- }
bss->supp_rates_len = 0;
if (elems.supp_rates) {
@@ -1647,7 +1665,6 @@ static void ieee80211_rx_bss_info(struct net_device *dev,
bss->hw_mode = rx_status->phymode;
- bss->channel = channel;
bss->freq = rx_status->freq;
if (channel != rx_status->channel &&
(bss->hw_mode == MODE_IEEE80211G ||
@@ -2375,7 +2392,7 @@ static int ieee80211_sta_create_ibss(struct net_device *dev,
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sta_bss *bss;
- struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_hw_mode *mode;
u8 bssid[ETH_ALEN], *pos;
int i;
@@ -2398,18 +2415,17 @@ static int ieee80211_sta_create_ibss(struct net_device *dev,
printk(KERN_DEBUG "%s: Creating new IBSS network, BSSID %s\n",
dev->name, print_mac(mac, bssid));
- bss = ieee80211_rx_bss_add(dev, bssid);
+ bss = ieee80211_rx_bss_add(dev, bssid, local->hw.conf.channel,
+ sdata->u.sta.ssid, sdata->u.sta.ssid_len);
if (!bss)
return -ENOMEM;
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
mode = local->oper_hw_mode;
if (local->hw.conf.beacon_int == 0)
local->hw.conf.beacon_int = 100;
bss->beacon_int = local->hw.conf.beacon_int;
bss->hw_mode = local->hw.conf.phymode;
- bss->channel = local->hw.conf.channel;
bss->freq = local->hw.conf.freq;
bss->last_update = jiffies;
bss->capability = WLAN_CAPABILITY_IBSS;
@@ -2469,7 +2485,8 @@ static int ieee80211_sta_find_ibss(struct net_device *dev,
"%s\n", print_mac(mac, bssid), print_mac(mac2, ifsta->bssid));
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
if (found && memcmp(ifsta->bssid, bssid, ETH_ALEN) != 0 &&
- (bss = ieee80211_rx_bss_get(dev, bssid))) {
+ (bss = ieee80211_rx_bss_get(dev, bssid, local->hw.conf.channel,
+ ifsta->ssid, ifsta->ssid_len))) {
printk(KERN_DEBUG "%s: Selected IBSS BSSID %s"
" based on configured SSID\n",
dev->name, print_mac(mac, bssid));
--
John W. Linville
linville@tuxdriver.com
^ permalink raw reply related
* Re: [PATCH/RFC] net: Add __napi_sycnhronize() to sync with napi poll
From: Benjamin Herrenschmidt @ 2007-10-17 2:52 UTC (permalink / raw)
To: Stephen Hemminger
Cc: netdev, David S. Miller, Roland Dreier, linuxppc-dev list
In-Reply-To: <20071016115318.0fc36af3@freepuppy.rosehill>
net: Add __napi_synchronize() to sync with napi poll
The EMAC driver which needs to handle multiple devices with one
NAPI instance implements its own per-channel disable bit. However,
when setting such a bit, it needs to synchronize with the poller
(that is make sure that any pending poller instance has completed,
or is started late enough to see that disable bit).
This implements a low level __napi_synchronize() function to acheive
that. The underscores are to emphasis the low level aspect of it and
to discourage driver writers who don't know what they are doing to
use it (to please DaveM :-)
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
Switched to do a cpu_relax() spin instead and only on SMP. Not that
we have an smp_mb() in there which synchronize_irq() lacks. I believe
this is a bug in synchronize_irq() which I'll handle separately.
Note: unfortunately, Jeff already picked up the EMAC patch without
waiting for this to be sorted out (oops...). So if you agree with
this patch, it would be nice to have it go in quickly or maybe via
Jeff's tree to avoid breakage ? Not terribly important tho.
Index: linux-work/include/linux/netdevice.h
===================================================================
--- linux-work.orig/include/linux/netdevice.h 2007-10-17 12:39:40.000000000 +1000
+++ linux-work/include/linux/netdevice.h 2007-10-17 12:40:59.000000000 +1000
@@ -394,6 +394,23 @@ static inline void napi_disable(struct n
}
/**
+ * __napi_synchronize - synchronize with a concurrent poll
+ * @n: napi context
+ *
+ * Synchronizes with a concurrent poll. Not to be used in normal
+ * drivers, mostly useful if you end up with multiple interfaces
+ * on one NAPI instance.
+ */
+static inline void __napi_synchronize(struct napi_struct *n)
+{
+#ifdef CONFIG_SMP
+ smp_mb();
+ while (test_bit(NAPI_STATE_SCHED, &n->state))
+ cpu_relax();
+#endif /* CONFIG_SMP */
+}
+
+/**
* napi_enable - enable NAPI scheduling
* @n: napi context
*
^ permalink raw reply
* Re: [PATCH/RFC] net: Add __napi_sycnhronize() to sync with napi poll
From: Benjamin Herrenschmidt @ 2007-10-17 2:50 UTC (permalink / raw)
To: Stephen Hemminger
Cc: netdev, David S. Miller, Roland Dreier, linuxppc-dev list
In-Reply-To: <20071016115318.0fc36af3@freepuppy.rosehill>
napi: use non-interruptible sleep in napi_disable
The current napi_disable() uses msleep_interruptible() but doesn't
(and can't) exit in case there's a signal, thus ending up doing a
hot spin without a cpu_relax. Use uninterruptible sleep instead.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
Index: linux-work/include/linux/netdevice.h
===================================================================
--- linux-work.orig/include/linux/netdevice.h 2007-10-17 12:39:16.000000000 +1000
+++ linux-work/include/linux/netdevice.h 2007-10-17 12:45:00.000000000 +1000
@@ -390,7 +390,7 @@ static inline void napi_complete(struct
static inline void napi_disable(struct napi_struct *n)
{
while (test_and_set_bit(NAPI_STATE_SCHED, &n->state))
- msleep_interruptible(1);
+ msleep(1);
}
/**
^ permalink raw reply
* RE: [PATCH] [net] fix gianfar (compile and warning)
From: Li Yang-r58472 @ 2007-10-17 2:38 UTC (permalink / raw)
To: Sebastian Siewior, jeff; +Cc: netdev, Grant Likely
In-Reply-To: <E1IhqCT-0007Wu-Lz@Chamillionaire.breakpoint.cc>
> -----Original Message-----
> From: Sebastian Siewior [mailto:netdev@ml.breakpoint.cc]
> Sent: Wednesday, October 17, 2007 1:25 AM
> To: jeff@garzik.org
> Cc: Li Yang-r58472; netdev@vger.kernel.org; Grant Likely
> Subject: [PATCH] [net] fix gianfar (compile and warning)
>
> Currently it does not compile because a not declared variable is used.
> struct net_device_stats in driver's private struct is also unsued.
> This patch uses (hopefully) the right stats.
>
> Cc: Li Yang <leoli@freescale.com>
> Signed-off-by: Sebastian Siewior <bigeasy@linutronix.de>
I have also submitted a patch to fixed this compile error two days
before, and it has been applied on Jeff's netdev tree. But you can
submit a separate patch to cleanup the struct net_device_stats in
driver's private struct. Thanks.
- Leo
^ permalink raw reply
* Re: [PATCH] net: Fix new EMAC driver for NAPI changes
From: Benjamin Herrenschmidt @ 2007-10-17 1:46 UTC (permalink / raw)
To: Jeff Garzik
Cc: shemminger, netdev, David S. Miller, Roland Dreier,
linuxppc-dev list
In-Reply-To: <47156258.5010800@garzik.org>
On Tue, 2007-10-16 at 21:16 -0400, Jeff Garzik wrote:
> Benjamin Herrenschmidt wrote:
> > net: Fix new EMAC driver for NAPI changes
> >
> > This fixes the new EMAC driver for the NAPI updates. The previous patch
> > by Roland Dreier (already applied) to do that doesn't actually work. This
> > applies on top of it makes it work on my test Ebony machine.
> >
> > This patch depends on "net: Add __napi_sycnhronize() to sync with napi poll"
> > posted previously.
> >
> > Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> > ---
> >
> > The old EMAC driver does things a bit differently (doesn't do useful
> > locking :-) and seems to work with Roland patch. So I'm not going to
> > touch it unless somebody reports me that it has problems
>
> applied
Beware that the depend patch for __napi_synchronize() is still being
discussed :-)
Cheers,
Ben.
^ permalink raw reply
* [git patches] net driver fixes (mostly)
From: Jeff Garzik @ 2007-10-17 1:20 UTC (permalink / raw)
To: Andrew Morton, Linus Torvalds; +Cc: netdev, LKML
Mostly fixes, except a couple things from Stephen H and myself.
Please pull from 'upstream-linus' branch of
master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/netdev-2.6.git upstream-linus
to receive the following updates:
drivers/net/3c59x.c | 8 +-
drivers/net/Kconfig | 10 +
drivers/net/bonding/bond_main.c | 11 +-
drivers/net/bonding/bonding.h | 4 +-
drivers/net/e1000e/ethtool.c | 35 ++-
drivers/net/e1000e/hw.h | 2 +-
drivers/net/forcedeth.c | 168 ++++++++------
drivers/net/gianfar.c | 7 +-
drivers/net/ibm_newemac/mal.c | 25 ++-
drivers/net/skge.c | 485 ++++++++++++++++++++++++++++++---------
drivers/net/skge.h | 17 +-
drivers/net/tokenring/3c359.c | 2 +-
12 files changed, 558 insertions(+), 216 deletions(-)
Adrian Bunk (1):
e1000e: fix error checks
Auke Kok (2):
e1000e: Fix debug printk macro
e1000e: don't poke PHY registers to retreive link status
Benjamin Herrenschmidt (1):
net: Fix new EMAC driver for NAPI changes
Ingo Molnar (1):
forcedeth: fix NAPI rx poll function
Jay Vosburgh (1):
bonding: two small fixes for IPoIB support
Jeff Garzik (2):
[netdrvr] forcedeth: improved probe info; dev_printk() cleanups
[netdrvr] forcedeth: remove in-driver copy of net_device_stats
Li Yang (1):
gianfar: Fix compile regression caused by 09f75cd7
Marcus Meissner (1):
tokenring/3c359.c: fixed array index problem
Steffen Klassert (1):
WOL bugfix for 3c59x.c
Stephen Hemminger (7):
skge: fix ram buffer size calculation
skge: changing MTU while running causes problems
skge: XM PHY handling fixes
skge: internal stats
skge: eeprom support
skge: add a debug interface
skge 1.12
diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c
index 8d3893d..862f472 100644
--- a/drivers/net/3c59x.c
+++ b/drivers/net/3c59x.c
@@ -3118,7 +3118,13 @@ static void acpi_set_WOL(struct net_device *dev)
iowrite16(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);
iowrite16(RxEnable, ioaddr + EL3_CMD);
- pci_enable_wake(VORTEX_PCI(vp), 0, 1);
+ if (pci_enable_wake(VORTEX_PCI(vp), PCI_D3hot, 1)) {
+ printk(KERN_INFO "%s: WOL not supported.\n",
+ pci_name(VORTEX_PCI(vp)));
+
+ vp->enable_wol = 0;
+ return;
+ }
/* Change the power state to D3; RxEnable doesn't take effect. */
pci_set_power_state(VORTEX_PCI(vp), PCI_D3hot);
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 8f99a06..83d52c8 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2173,6 +2173,16 @@ config SKGE
To compile this driver as a module, choose M here: the module
will be called skge. This is recommended.
+config SKGE_DEBUG
+ bool "Debugging interface"
+ depends on SKGE && DEBUG_FS
+ help
+ This option adds the ability to dump driver state for debugging.
+ The file debugfs/skge/ethX displays the state of the internal
+ transmit and receive rings.
+
+ If unsure, say N.
+
config SKY2
tristate "SysKonnect Yukon2 support"
depends on PCI
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index db80f24..6f85cc3 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1263,6 +1263,7 @@ static void bond_setup_by_slave(struct net_device *bond_dev,
struct bonding *bond = bond_dev->priv;
bond_dev->neigh_setup = slave_dev->neigh_setup;
+ bond_dev->header_ops = slave_dev->header_ops;
bond_dev->type = slave_dev->type;
bond_dev->hard_header_len = slave_dev->hard_header_len;
@@ -3351,7 +3352,10 @@ static int bond_slave_netdev_event(unsigned long event, struct net_device *slave
switch (event) {
case NETDEV_UNREGISTER:
if (bond_dev) {
- bond_release(bond_dev, slave_dev);
+ if (bond->setup_by_slave)
+ bond_release_and_destroy(bond_dev, slave_dev);
+ else
+ bond_release(bond_dev, slave_dev);
}
break;
case NETDEV_CHANGE:
@@ -3366,11 +3370,6 @@ static int bond_slave_netdev_event(unsigned long event, struct net_device *slave
* ... Or is it this?
*/
break;
- case NETDEV_GOING_DOWN:
- dprintk("slave %s is going down\n", slave_dev->name);
- if (bond->setup_by_slave)
- bond_release_and_destroy(bond_dev, slave_dev);
- break;
case NETDEV_CHANGEMTU:
/*
* TODO: Should slaves be allowed to
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index a8bbd56..b818060 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -22,8 +22,8 @@
#include "bond_3ad.h"
#include "bond_alb.h"
-#define DRV_VERSION "3.2.0"
-#define DRV_RELDATE "September 13, 2007"
+#define DRV_VERSION "3.2.1"
+#define DRV_RELDATE "October 15, 2007"
#define DRV_NAME "bonding"
#define DRV_DESCRIPTION "Ethernet Channel Bonding Driver"
diff --git a/drivers/net/e1000e/ethtool.c b/drivers/net/e1000e/ethtool.c
index b7a7e2a..0666e62 100644
--- a/drivers/net/e1000e/ethtool.c
+++ b/drivers/net/e1000e/ethtool.c
@@ -110,6 +110,7 @@ static int e1000_get_settings(struct net_device *netdev,
{
struct e1000_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
+ u32 status;
if (hw->media_type == e1000_media_type_copper) {
@@ -147,16 +148,16 @@ static int e1000_get_settings(struct net_device *netdev,
ecmd->transceiver = XCVR_EXTERNAL;
}
- if (er32(STATUS) & E1000_STATUS_LU) {
-
- adapter->hw.mac.ops.get_link_up_info(hw, &adapter->link_speed,
- &adapter->link_duplex);
- ecmd->speed = adapter->link_speed;
-
- /* unfortunately FULL_DUPLEX != DUPLEX_FULL
- * and HALF_DUPLEX != DUPLEX_HALF */
+ status = er32(STATUS);
+ if (status & E1000_STATUS_LU) {
+ if (status & E1000_STATUS_SPEED_1000)
+ ecmd->speed = 1000;
+ else if (status & E1000_STATUS_SPEED_100)
+ ecmd->speed = 100;
+ else
+ ecmd->speed = 10;
- if (adapter->link_duplex == FULL_DUPLEX)
+ if (status & E1000_STATUS_FD)
ecmd->duplex = DUPLEX_FULL;
else
ecmd->duplex = DUPLEX_HALF;
@@ -170,6 +171,16 @@ static int e1000_get_settings(struct net_device *netdev,
return 0;
}
+static u32 e1000_get_link(struct net_device *netdev)
+{
+ struct e1000_adapter *adapter = netdev_priv(netdev);
+ struct e1000_hw *hw = &adapter->hw;
+ u32 status;
+
+ status = er32(STATUS);
+ return (status & E1000_STATUS_LU);
+}
+
static int e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx)
{
struct e1000_mac_info *mac = &adapter->hw.mac;
@@ -1451,11 +1462,11 @@ static int e1000_loopback_test(struct e1000_adapter *adapter, u64 *data)
}
*data = e1000_setup_desc_rings(adapter);
- if (data)
+ if (*data)
goto out;
*data = e1000_setup_loopback_test(adapter);
- if (data)
+ if (*data)
goto err_loopback;
*data = e1000_run_loopback_test(adapter);
@@ -1751,7 +1762,7 @@ static const struct ethtool_ops e1000_ethtool_ops = {
.get_msglevel = e1000_get_msglevel,
.set_msglevel = e1000_set_msglevel,
.nway_reset = e1000_nway_reset,
- .get_link = ethtool_op_get_link,
+ .get_link = e1000_get_link,
.get_eeprom_len = e1000_get_eeprom_len,
.get_eeprom = e1000_get_eeprom,
.set_eeprom = e1000_set_eeprom,
diff --git a/drivers/net/e1000e/hw.h b/drivers/net/e1000e/hw.h
index aa82f1a..6451578 100644
--- a/drivers/net/e1000e/hw.h
+++ b/drivers/net/e1000e/hw.h
@@ -852,7 +852,7 @@ struct e1000_hw {
#ifdef DEBUG
#define hw_dbg(hw, format, arg...) \
- printk(KERN_DEBUG, "%s: " format, e1000e_get_hw_dev_name(hw), ##arg);
+ printk(KERN_DEBUG "%s: " format, e1000e_get_hw_dev_name(hw), ##arg)
#else
static inline int __attribute__ ((format (printf, 2, 3)))
hw_dbg(struct e1000_hw *hw, const char *format, ...)
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index dae30b7..cfbb7aa 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -128,7 +128,7 @@
#else
#define DRIVERNAPI
#endif
-#define FORCEDETH_VERSION "0.60"
+#define FORCEDETH_VERSION "0.61"
#define DRV_NAME "forcedeth"
#include <linux/module.h>
@@ -752,7 +752,6 @@ struct fe_priv {
/* General data:
* Locking: spin_lock(&np->lock); */
- struct net_device_stats stats;
struct nv_ethtool_stats estats;
int in_shutdown;
u32 linkspeed;
@@ -1505,15 +1504,16 @@ static struct net_device_stats *nv_get_stats(struct net_device *dev)
nv_get_hw_stats(dev);
/* copy to net_device stats */
- np->stats.tx_bytes = np->estats.tx_bytes;
- np->stats.tx_fifo_errors = np->estats.tx_fifo_errors;
- np->stats.tx_carrier_errors = np->estats.tx_carrier_errors;
- np->stats.rx_crc_errors = np->estats.rx_crc_errors;
- np->stats.rx_over_errors = np->estats.rx_over_errors;
- np->stats.rx_errors = np->estats.rx_errors_total;
- np->stats.tx_errors = np->estats.tx_errors_total;
- }
- return &np->stats;
+ dev->stats.tx_bytes = np->estats.tx_bytes;
+ dev->stats.tx_fifo_errors = np->estats.tx_fifo_errors;
+ dev->stats.tx_carrier_errors = np->estats.tx_carrier_errors;
+ dev->stats.rx_crc_errors = np->estats.rx_crc_errors;
+ dev->stats.rx_over_errors = np->estats.rx_over_errors;
+ dev->stats.rx_errors = np->estats.rx_errors_total;
+ dev->stats.tx_errors = np->estats.tx_errors_total;
+ }
+
+ return &dev->stats;
}
/*
@@ -1733,7 +1733,7 @@ static void nv_drain_tx(struct net_device *dev)
np->tx_ring.ex[i].buflow = 0;
}
if (nv_release_txskb(dev, &np->tx_skb[i]))
- np->stats.tx_dropped++;
+ dev->stats.tx_dropped++;
}
}
@@ -2049,13 +2049,13 @@ static void nv_tx_done(struct net_device *dev)
if (flags & NV_TX_LASTPACKET) {
if (flags & NV_TX_ERROR) {
if (flags & NV_TX_UNDERFLOW)
- np->stats.tx_fifo_errors++;
+ dev->stats.tx_fifo_errors++;
if (flags & NV_TX_CARRIERLOST)
- np->stats.tx_carrier_errors++;
- np->stats.tx_errors++;
+ dev->stats.tx_carrier_errors++;
+ dev->stats.tx_errors++;
} else {
- np->stats.tx_packets++;
- np->stats.tx_bytes += np->get_tx_ctx->skb->len;
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += np->get_tx_ctx->skb->len;
}
dev_kfree_skb_any(np->get_tx_ctx->skb);
np->get_tx_ctx->skb = NULL;
@@ -2064,13 +2064,13 @@ static void nv_tx_done(struct net_device *dev)
if (flags & NV_TX2_LASTPACKET) {
if (flags & NV_TX2_ERROR) {
if (flags & NV_TX2_UNDERFLOW)
- np->stats.tx_fifo_errors++;
+ dev->stats.tx_fifo_errors++;
if (flags & NV_TX2_CARRIERLOST)
- np->stats.tx_carrier_errors++;
- np->stats.tx_errors++;
+ dev->stats.tx_carrier_errors++;
+ dev->stats.tx_errors++;
} else {
- np->stats.tx_packets++;
- np->stats.tx_bytes += np->get_tx_ctx->skb->len;
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += np->get_tx_ctx->skb->len;
}
dev_kfree_skb_any(np->get_tx_ctx->skb);
np->get_tx_ctx->skb = NULL;
@@ -2107,7 +2107,7 @@ static void nv_tx_done_optimized(struct net_device *dev, int limit)
if (flags & NV_TX2_LASTPACKET) {
if (!(flags & NV_TX2_ERROR))
- np->stats.tx_packets++;
+ dev->stats.tx_packets++;
dev_kfree_skb_any(np->get_tx_ctx->skb);
np->get_tx_ctx->skb = NULL;
}
@@ -2268,13 +2268,13 @@ static int nv_rx_process(struct net_device *dev, int limit)
{
struct fe_priv *np = netdev_priv(dev);
u32 flags;
- u32 rx_processed_cnt = 0;
+ int rx_work = 0;
struct sk_buff *skb;
int len;
while((np->get_rx.orig != np->put_rx.orig) &&
!((flags = le32_to_cpu(np->get_rx.orig->flaglen)) & NV_RX_AVAIL) &&
- (rx_processed_cnt++ < limit)) {
+ (rx_work < limit)) {
dprintk(KERN_DEBUG "%s: nv_rx_process: flags 0x%x.\n",
dev->name, flags);
@@ -2308,7 +2308,7 @@ static int nv_rx_process(struct net_device *dev, int limit)
if (flags & NV_RX_ERROR4) {
len = nv_getlen(dev, skb->data, len);
if (len < 0) {
- np->stats.rx_errors++;
+ dev->stats.rx_errors++;
dev_kfree_skb(skb);
goto next_pkt;
}
@@ -2322,12 +2322,12 @@ static int nv_rx_process(struct net_device *dev, int limit)
/* the rest are hard errors */
else {
if (flags & NV_RX_MISSEDFRAME)
- np->stats.rx_missed_errors++;
+ dev->stats.rx_missed_errors++;
if (flags & NV_RX_CRCERR)
- np->stats.rx_crc_errors++;
+ dev->stats.rx_crc_errors++;
if (flags & NV_RX_OVERFLOW)
- np->stats.rx_over_errors++;
- np->stats.rx_errors++;
+ dev->stats.rx_over_errors++;
+ dev->stats.rx_errors++;
dev_kfree_skb(skb);
goto next_pkt;
}
@@ -2343,7 +2343,7 @@ static int nv_rx_process(struct net_device *dev, int limit)
if (flags & NV_RX2_ERROR4) {
len = nv_getlen(dev, skb->data, len);
if (len < 0) {
- np->stats.rx_errors++;
+ dev->stats.rx_errors++;
dev_kfree_skb(skb);
goto next_pkt;
}
@@ -2357,10 +2357,10 @@ static int nv_rx_process(struct net_device *dev, int limit)
/* the rest are hard errors */
else {
if (flags & NV_RX2_CRCERR)
- np->stats.rx_crc_errors++;
+ dev->stats.rx_crc_errors++;
if (flags & NV_RX2_OVERFLOW)
- np->stats.rx_over_errors++;
- np->stats.rx_errors++;
+ dev->stats.rx_over_errors++;
+ dev->stats.rx_errors++;
dev_kfree_skb(skb);
goto next_pkt;
}
@@ -2389,16 +2389,18 @@ static int nv_rx_process(struct net_device *dev, int limit)
netif_rx(skb);
#endif
dev->last_rx = jiffies;
- np->stats.rx_packets++;
- np->stats.rx_bytes += len;
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += len;
next_pkt:
if (unlikely(np->get_rx.orig++ == np->last_rx.orig))
np->get_rx.orig = np->first_rx.orig;
if (unlikely(np->get_rx_ctx++ == np->last_rx_ctx))
np->get_rx_ctx = np->first_rx_ctx;
+
+ rx_work++;
}
- return rx_processed_cnt;
+ return rx_work;
}
static int nv_rx_process_optimized(struct net_device *dev, int limit)
@@ -2505,8 +2507,8 @@ static int nv_rx_process_optimized(struct net_device *dev, int limit)
}
dev->last_rx = jiffies;
- np->stats.rx_packets++;
- np->stats.rx_bytes += len;
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += len;
} else {
dev_kfree_skb(skb);
}
@@ -3727,7 +3729,7 @@ static void nv_do_stats_poll(unsigned long data)
static void nv_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
struct fe_priv *np = netdev_priv(dev);
- strcpy(info->driver, "forcedeth");
+ strcpy(info->driver, DRV_NAME);
strcpy(info->version, FORCEDETH_VERSION);
strcpy(info->bus_info, pci_name(np->pci_dev));
}
@@ -4991,6 +4993,11 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
u32 phystate_orig = 0, phystate;
int phyinitialized = 0;
DECLARE_MAC_BUF(mac);
+ static int printed_version;
+
+ if (!printed_version++)
+ printk(KERN_INFO "%s: Reverse Engineered nForce ethernet"
+ " driver. Version %s.\n", DRV_NAME, FORCEDETH_VERSION);
dev = alloc_etherdev(sizeof(struct fe_priv));
err = -ENOMEM;
@@ -5014,11 +5021,8 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
np->stats_poll.function = &nv_do_stats_poll; /* timer handler */
err = pci_enable_device(pci_dev);
- if (err) {
- printk(KERN_INFO "forcedeth: pci_enable_dev failed (%d) for device %s\n",
- err, pci_name(pci_dev));
+ if (err)
goto out_free;
- }
pci_set_master(pci_dev);
@@ -5047,8 +5051,8 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
}
}
if (i == DEVICE_COUNT_RESOURCE) {
- printk(KERN_INFO "forcedeth: Couldn't find register window for device %s.\n",
- pci_name(pci_dev));
+ dev_printk(KERN_INFO, &pci_dev->dev,
+ "Couldn't find register window\n");
goto out_relreg;
}
@@ -5061,16 +5065,14 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
np->desc_ver = DESC_VER_3;
np->txrxctl_bits = NVREG_TXRXCTL_DESC_3;
if (dma_64bit) {
- if (pci_set_dma_mask(pci_dev, DMA_39BIT_MASK)) {
- printk(KERN_INFO "forcedeth: 64-bit DMA failed, using 32-bit addressing for device %s.\n",
- pci_name(pci_dev));
- } else {
+ if (pci_set_dma_mask(pci_dev, DMA_39BIT_MASK))
+ dev_printk(KERN_INFO, &pci_dev->dev,
+ "64-bit DMA failed, using 32-bit addressing\n");
+ else
dev->features |= NETIF_F_HIGHDMA;
- printk(KERN_INFO "forcedeth: using HIGHDMA\n");
- }
if (pci_set_consistent_dma_mask(pci_dev, DMA_39BIT_MASK)) {
- printk(KERN_INFO "forcedeth: 64-bit DMA (consistent) failed, using 32-bit ring buffers for device %s.\n",
- pci_name(pci_dev));
+ dev_printk(KERN_INFO, &pci_dev->dev,
+ "64-bit DMA (consistent) failed, using 32-bit ring buffers\n");
}
}
} else if (id->driver_data & DEV_HAS_LARGEDESC) {
@@ -5205,9 +5207,11 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
* Bad mac address. At least one bios sets the mac address
* to 01:23:45:67:89:ab
*/
- printk(KERN_ERR "%s: Invalid Mac address detected: %s\n",
- pci_name(pci_dev), print_mac(mac, dev->dev_addr));
- printk(KERN_ERR "Please complain to your hardware vendor. Switching to a random MAC.\n");
+ dev_printk(KERN_ERR, &pci_dev->dev,
+ "Invalid Mac address detected: %s\n",
+ print_mac(mac, dev->dev_addr));
+ dev_printk(KERN_ERR, &pci_dev->dev,
+ "Please complain to your hardware vendor. Switching to a random MAC.\n");
dev->dev_addr[0] = 0x00;
dev->dev_addr[1] = 0x00;
dev->dev_addr[2] = 0x6c;
@@ -5321,8 +5325,8 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
break;
}
if (i == 33) {
- printk(KERN_INFO "%s: open: Could not find a valid PHY.\n",
- pci_name(pci_dev));
+ dev_printk(KERN_INFO, &pci_dev->dev,
+ "open: Could not find a valid PHY.\n");
goto out_error;
}
@@ -5344,12 +5348,37 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
err = register_netdev(dev);
if (err) {
- printk(KERN_INFO "forcedeth: unable to register netdev: %d\n", err);
+ dev_printk(KERN_INFO, &pci_dev->dev,
+ "unable to register netdev: %d\n", err);
goto out_error;
}
- printk(KERN_INFO "%s: forcedeth.c: subsystem: %05x:%04x bound to %s\n",
- dev->name, pci_dev->subsystem_vendor, pci_dev->subsystem_device,
- pci_name(pci_dev));
+
+ dev_printk(KERN_INFO, &pci_dev->dev, "ifname %s, PHY OUI 0x%x @ %d, "
+ "addr %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
+ dev->name,
+ np->phy_oui,
+ np->phyaddr,
+ dev->dev_addr[0],
+ dev->dev_addr[1],
+ dev->dev_addr[2],
+ dev->dev_addr[3],
+ dev->dev_addr[4],
+ dev->dev_addr[5]);
+
+ dev_printk(KERN_INFO, &pci_dev->dev, "%s%s%s%s%s%s%s%s%s%sdesc-v%u\n",
+ dev->features & NETIF_F_HIGHDMA ? "highdma " : "",
+ dev->features & (NETIF_F_HW_CSUM | NETIF_F_SG) ?
+ "csum " : "",
+ dev->features & (NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX) ?
+ "vlan " : "",
+ id->driver_data & DEV_HAS_POWER_CNTRL ? "pwrctl " : "",
+ id->driver_data & DEV_HAS_MGMT_UNIT ? "mgmt " : "",
+ id->driver_data & DEV_NEED_TIMERIRQ ? "timirq " : "",
+ np->gigabit == PHY_GIGABIT ? "gbit " : "",
+ np->need_linktimer ? "lnktim " : "",
+ np->msi_flags & NV_MSI_CAPABLE ? "msi " : "",
+ np->msi_flags & NV_MSI_X_CAPABLE ? "msi-x " : "",
+ np->desc_ver);
return 0;
@@ -5567,17 +5596,16 @@ static struct pci_device_id pci_tbl[] = {
};
static struct pci_driver driver = {
- .name = "forcedeth",
- .id_table = pci_tbl,
- .probe = nv_probe,
- .remove = __devexit_p(nv_remove),
- .suspend = nv_suspend,
- .resume = nv_resume,
+ .name = DRV_NAME,
+ .id_table = pci_tbl,
+ .probe = nv_probe,
+ .remove = __devexit_p(nv_remove),
+ .suspend = nv_suspend,
+ .resume = nv_resume,
};
static int __init init_nic(void)
{
- printk(KERN_INFO "forcedeth.c: Reverse Engineered nForce ethernet driver. Version %s.\n", FORCEDETH_VERSION);
return pci_register_driver(&driver);
}
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index 558440c..cc288d8 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -1237,8 +1237,6 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu)
* starting over will fix the problem. */
static void gfar_timeout(struct net_device *dev)
{
- struct gfar_private *priv = netdev_priv(dev);
-
dev->stats.tx_errors++;
if (dev->flags & IFF_UP) {
@@ -1344,8 +1342,9 @@ struct sk_buff * gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp)
return skb;
}
-static inline void count_errors(unsigned short status, struct gfar_private *priv)
+static inline void count_errors(unsigned short status, struct net_device *dev)
{
+ struct gfar_private *priv = netdev_priv(dev);
struct net_device_stats *stats = &dev->stats;
struct gfar_extra_stats *estats = &priv->extra_stats;
@@ -1539,7 +1538,7 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
dev->stats.rx_bytes += pkt_len;
} else {
- count_errors(bdp->status, priv);
+ count_errors(bdp->status, dev);
if (skb)
dev_kfree_skb_any(skb);
diff --git a/drivers/net/ibm_newemac/mal.c b/drivers/net/ibm_newemac/mal.c
index 39f4cb6..a680eb0 100644
--- a/drivers/net/ibm_newemac/mal.c
+++ b/drivers/net/ibm_newemac/mal.c
@@ -45,6 +45,8 @@ int __devinit mal_register_commac(struct mal_instance *mal,
return -EBUSY;
}
+ if (list_empty(&mal->list))
+ napi_enable(&mal->napi);
mal->tx_chan_mask |= commac->tx_chan_mask;
mal->rx_chan_mask |= commac->rx_chan_mask;
list_add(&commac->list, &mal->list);
@@ -67,6 +69,8 @@ void __devexit mal_unregister_commac(struct mal_instance *mal,
mal->tx_chan_mask &= ~commac->tx_chan_mask;
mal->rx_chan_mask &= ~commac->rx_chan_mask;
list_del_init(&commac->list);
+ if (list_empty(&mal->list))
+ napi_disable(&mal->napi);
spin_unlock_irqrestore(&mal->lock, flags);
}
@@ -182,7 +186,7 @@ static inline void mal_enable_eob_irq(struct mal_instance *mal)
set_mal_dcrn(mal, MAL_CFG, get_mal_dcrn(mal, MAL_CFG) | MAL_CFG_EOPIE);
}
-/* synchronized by __LINK_STATE_RX_SCHED bit in ndev->state */
+/* synchronized by NAPI state */
static inline void mal_disable_eob_irq(struct mal_instance *mal)
{
// XXX might want to cache MAL_CFG as the DCR read can be slooooow
@@ -317,8 +321,8 @@ void mal_poll_disable(struct mal_instance *mal, struct mal_commac *commac)
while (test_and_set_bit(MAL_COMMAC_POLL_DISABLED, &commac->flags))
msleep(1);
- /* Synchronize with the MAL NAPI poller. */
- napi_disable(&mal->napi);
+ /* Synchronize with the MAL NAPI poller */
+ __napi_synchronize(&mal->napi);
}
void mal_poll_enable(struct mal_instance *mal, struct mal_commac *commac)
@@ -326,7 +330,12 @@ void mal_poll_enable(struct mal_instance *mal, struct mal_commac *commac)
smp_wmb();
clear_bit(MAL_COMMAC_POLL_DISABLED, &commac->flags);
- // XXX might want to kick a poll now...
+ /* Feels better to trigger a poll here to catch up with events that
+ * may have happened on this channel while disabled. It will most
+ * probably be delayed until the next interrupt but that's mostly a
+ * non-issue in the context where this is called.
+ */
+ napi_schedule(&mal->napi);
}
static int mal_poll(struct napi_struct *napi, int budget)
@@ -336,8 +345,7 @@ static int mal_poll(struct napi_struct *napi, int budget)
int received = 0;
unsigned long flags;
- MAL_DBG2(mal, "poll(%d) %d ->" NL, *budget,
- rx_work_limit);
+ MAL_DBG2(mal, "poll(%d)" NL, budget);
again:
/* Process TX skbs */
list_for_each(l, &mal->poll_list) {
@@ -528,11 +536,12 @@ static int __devinit mal_probe(struct of_device *ofdev,
}
INIT_LIST_HEAD(&mal->poll_list);
- mal->napi.weight = CONFIG_IBM_NEW_EMAC_POLL_WEIGHT;
- mal->napi.poll = mal_poll;
INIT_LIST_HEAD(&mal->list);
spin_lock_init(&mal->lock);
+ netif_napi_add(NULL, &mal->napi, mal_poll,
+ CONFIG_IBM_NEW_EMAC_POLL_WEIGHT);
+
/* Load power-on reset defaults */
mal_reset(mal);
diff --git a/drivers/net/skge.c b/drivers/net/skge.c
index 2aae9fe..b9961dc 100644
--- a/drivers/net/skge.c
+++ b/drivers/net/skge.c
@@ -36,13 +36,15 @@
#include <linux/delay.h>
#include <linux/crc32.h>
#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
#include <linux/mii.h>
#include <asm/irq.h>
#include "skge.h"
#define DRV_NAME "skge"
-#define DRV_VERSION "1.11"
+#define DRV_VERSION "1.12"
#define PFX DRV_NAME " "
#define DEFAULT_TX_RING_SIZE 128
@@ -57,7 +59,10 @@
#define TX_WATCHDOG (5 * HZ)
#define NAPI_WEIGHT 64
#define BLINK_MS 250
-#define LINK_HZ (HZ/2)
+#define LINK_HZ HZ
+
+#define SKGE_EEPROM_MAGIC 0x9933aabb
+
MODULE_DESCRIPTION("SysKonnect Gigabit Ethernet driver");
MODULE_AUTHOR("Stephen Hemminger <shemminger@linux-foundation.org>");
@@ -445,15 +450,15 @@ static struct net_device_stats *skge_get_stats(struct net_device *dev)
else
yukon_get_stats(skge, data);
- skge->net_stats.tx_bytes = data[0];
- skge->net_stats.rx_bytes = data[1];
- skge->net_stats.tx_packets = data[2] + data[4] + data[6];
- skge->net_stats.rx_packets = data[3] + data[5] + data[7];
- skge->net_stats.multicast = data[3] + data[5];
- skge->net_stats.collisions = data[10];
- skge->net_stats.tx_aborted_errors = data[12];
+ dev->stats.tx_bytes = data[0];
+ dev->stats.rx_bytes = data[1];
+ dev->stats.tx_packets = data[2] + data[4] + data[6];
+ dev->stats.rx_packets = data[3] + data[5] + data[7];
+ dev->stats.multicast = data[3] + data[5];
+ dev->stats.collisions = data[10];
+ dev->stats.tx_aborted_errors = data[12];
- return &skge->net_stats;
+ return &dev->stats;
}
static void skge_get_strings(struct net_device *dev, u32 stringset, u8 *data)
@@ -798,6 +803,98 @@ static int skge_phys_id(struct net_device *dev, u32 data)
return 0;
}
+static int skge_get_eeprom_len(struct net_device *dev)
+{
+ struct skge_port *skge = netdev_priv(dev);
+ u32 reg2;
+
+ pci_read_config_dword(skge->hw->pdev, PCI_DEV_REG2, ®2);
+ return 1 << ( ((reg2 & PCI_VPD_ROM_SZ) >> 14) + 8);
+}
+
+static u32 skge_vpd_read(struct pci_dev *pdev, int cap, u16 offset)
+{
+ u32 val;
+
+ pci_write_config_word(pdev, cap + PCI_VPD_ADDR, offset);
+
+ do {
+ pci_read_config_word(pdev, cap + PCI_VPD_ADDR, &offset);
+ } while (!(offset & PCI_VPD_ADDR_F));
+
+ pci_read_config_dword(pdev, cap + PCI_VPD_DATA, &val);
+ return val;
+}
+
+static void skge_vpd_write(struct pci_dev *pdev, int cap, u16 offset, u32 val)
+{
+ pci_write_config_dword(pdev, cap + PCI_VPD_DATA, val);
+ pci_write_config_word(pdev, cap + PCI_VPD_ADDR,
+ offset | PCI_VPD_ADDR_F);
+
+ do {
+ pci_read_config_word(pdev, cap + PCI_VPD_ADDR, &offset);
+ } while (offset & PCI_VPD_ADDR_F);
+}
+
+static int skge_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
+ u8 *data)
+{
+ struct skge_port *skge = netdev_priv(dev);
+ struct pci_dev *pdev = skge->hw->pdev;
+ int cap = pci_find_capability(pdev, PCI_CAP_ID_VPD);
+ int length = eeprom->len;
+ u16 offset = eeprom->offset;
+
+ if (!cap)
+ return -EINVAL;
+
+ eeprom->magic = SKGE_EEPROM_MAGIC;
+
+ while (length > 0) {
+ u32 val = skge_vpd_read(pdev, cap, offset);
+ int n = min_t(int, length, sizeof(val));
+
+ memcpy(data, &val, n);
+ length -= n;
+ data += n;
+ offset += n;
+ }
+ return 0;
+}
+
+static int skge_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
+ u8 *data)
+{
+ struct skge_port *skge = netdev_priv(dev);
+ struct pci_dev *pdev = skge->hw->pdev;
+ int cap = pci_find_capability(pdev, PCI_CAP_ID_VPD);
+ int length = eeprom->len;
+ u16 offset = eeprom->offset;
+
+ if (!cap)
+ return -EINVAL;
+
+ if (eeprom->magic != SKGE_EEPROM_MAGIC)
+ return -EINVAL;
+
+ while (length > 0) {
+ u32 val;
+ int n = min_t(int, length, sizeof(val));
+
+ if (n < sizeof(val))
+ val = skge_vpd_read(pdev, cap, offset);
+ memcpy(&val, data, n);
+
+ skge_vpd_write(pdev, cap, offset, val);
+
+ length -= n;
+ data += n;
+ offset += n;
+ }
+ return 0;
+}
+
static const struct ethtool_ops skge_ethtool_ops = {
.get_settings = skge_get_settings,
.set_settings = skge_set_settings,
@@ -810,6 +907,9 @@ static const struct ethtool_ops skge_ethtool_ops = {
.set_msglevel = skge_set_msglevel,
.nway_reset = skge_nway_reset,
.get_link = ethtool_op_get_link,
+ .get_eeprom_len = skge_get_eeprom_len,
+ .get_eeprom = skge_get_eeprom,
+ .set_eeprom = skge_set_eeprom,
.get_ringparam = skge_get_ring_param,
.set_ringparam = skge_set_ring_param,
.get_pauseparam = skge_get_pauseparam,
@@ -995,19 +1095,15 @@ static void xm_link_down(struct skge_hw *hw, int port)
{
struct net_device *dev = hw->dev[port];
struct skge_port *skge = netdev_priv(dev);
- u16 cmd, msk;
+ u16 cmd = xm_read16(hw, port, XM_MMU_CMD);
- if (hw->phy_type == SK_PHY_XMAC) {
- msk = xm_read16(hw, port, XM_IMSK);
- msk |= XM_IS_INP_ASS | XM_IS_LIPA_RC | XM_IS_RX_PAGE | XM_IS_AND;
- xm_write16(hw, port, XM_IMSK, msk);
- }
+ xm_write16(hw, port, XM_IMSK, XM_IMSK_DISABLE);
- cmd = xm_read16(hw, port, XM_MMU_CMD);
cmd &= ~(XM_MMU_ENA_RX | XM_MMU_ENA_TX);
xm_write16(hw, port, XM_MMU_CMD, cmd);
+
/* dummy read to ensure writing */
- (void) xm_read16(hw, port, XM_MMU_CMD);
+ xm_read16(hw, port, XM_MMU_CMD);
if (netif_carrier_ok(dev))
skge_link_down(skge);
@@ -1103,7 +1199,7 @@ static void genesis_reset(struct skge_hw *hw, int port)
/* reset the statistics module */
xm_write32(hw, port, XM_GP_PORT, XM_GP_RES_STAT);
- xm_write16(hw, port, XM_IMSK, 0xffff); /* disable XMAC IRQs */
+ xm_write16(hw, port, XM_IMSK, XM_IMSK_DISABLE);
xm_write32(hw, port, XM_MODE, 0); /* clear Mode Reg */
xm_write16(hw, port, XM_TX_CMD, 0); /* reset TX CMD Reg */
xm_write16(hw, port, XM_RX_CMD, 0); /* reset RX CMD Reg */
@@ -1141,7 +1237,7 @@ static void bcom_check_link(struct skge_hw *hw, int port)
u16 status;
/* read twice because of latch */
- (void) xm_phy_read(hw, port, PHY_BCOM_STAT);
+ xm_phy_read(hw, port, PHY_BCOM_STAT);
status = xm_phy_read(hw, port, PHY_BCOM_STAT);
if ((status & PHY_ST_LSYNC) == 0) {
@@ -1342,7 +1438,7 @@ static void xm_phy_init(struct skge_port *skge)
mod_timer(&skge->link_timer, jiffies + LINK_HZ);
}
-static void xm_check_link(struct net_device *dev)
+static int xm_check_link(struct net_device *dev)
{
struct skge_port *skge = netdev_priv(dev);
struct skge_hw *hw = skge->hw;
@@ -1350,25 +1446,25 @@ static void xm_check_link(struct net_device *dev)
u16 status;
/* read twice because of latch */
- (void) xm_phy_read(hw, port, PHY_XMAC_STAT);
+ xm_phy_read(hw, port, PHY_XMAC_STAT);
status = xm_phy_read(hw, port, PHY_XMAC_STAT);
if ((status & PHY_ST_LSYNC) == 0) {
xm_link_down(hw, port);
- return;
+ return 0;
}
if (skge->autoneg == AUTONEG_ENABLE) {
u16 lpa, res;
if (!(status & PHY_ST_AN_OVER))
- return;
+ return 0;
lpa = xm_phy_read(hw, port, PHY_XMAC_AUNE_LP);
if (lpa & PHY_B_AN_RF) {
printk(KERN_NOTICE PFX "%s: remote fault\n",
dev->name);
- return;
+ return 0;
}
res = xm_phy_read(hw, port, PHY_XMAC_RES_ABI);
@@ -1384,7 +1480,7 @@ static void xm_check_link(struct net_device *dev)
default:
printk(KERN_NOTICE PFX "%s: duplex mismatch\n",
dev->name);
- return;
+ return 0;
}
/* We are using IEEE 802.3z/D5.0 Table 37-4 */
@@ -1408,11 +1504,14 @@ static void xm_check_link(struct net_device *dev)
if (!netif_carrier_ok(dev))
genesis_link_up(skge);
+ return 1;
}
/* Poll to check for link coming up.
+ *
* Since internal PHY is wired to a level triggered pin, can't
- * get an interrupt when carrier is detected.
+ * get an interrupt when carrier is detected, need to poll for
+ * link coming up.
*/
static void xm_link_timer(unsigned long arg)
{
@@ -1420,29 +1519,35 @@ static void xm_link_timer(unsigned long arg)
struct net_device *dev = skge->netdev;
struct skge_hw *hw = skge->hw;
int port = skge->port;
+ int i;
+ unsigned long flags;
if (!netif_running(dev))
return;
- if (netif_carrier_ok(dev)) {
+ spin_lock_irqsave(&hw->phy_lock, flags);
+
+ /*
+ * Verify that the link by checking GPIO register three times.
+ * This pin has the signal from the link_sync pin connected to it.
+ */
+ for (i = 0; i < 3; i++) {
+ if (xm_read16(hw, port, XM_GP_PORT) & XM_GP_INP_ASS)
+ goto link_down;
+ }
+
+ /* Re-enable interrupt to detect link down */
+ if (xm_check_link(dev)) {
+ u16 msk = xm_read16(hw, port, XM_IMSK);
+ msk &= ~XM_IS_INP_ASS;
+ xm_write16(hw, port, XM_IMSK, msk);
xm_read16(hw, port, XM_ISRC);
- if (!(xm_read16(hw, port, XM_ISRC) & XM_IS_INP_ASS))
- goto nochange;
} else {
- if (xm_read32(hw, port, XM_GP_PORT) & XM_GP_INP_ASS)
- goto nochange;
- xm_read16(hw, port, XM_ISRC);
- if (xm_read16(hw, port, XM_ISRC) & XM_IS_INP_ASS)
- goto nochange;
+link_down:
+ mod_timer(&skge->link_timer,
+ round_jiffies(jiffies + LINK_HZ));
}
-
- spin_lock(&hw->phy_lock);
- xm_check_link(dev);
- spin_unlock(&hw->phy_lock);
-
-nochange:
- if (netif_running(dev))
- mod_timer(&skge->link_timer, jiffies + LINK_HZ);
+ spin_unlock_irqrestore(&hw->phy_lock, flags);
}
static void genesis_mac_init(struct skge_hw *hw, int port)
@@ -1679,24 +1784,27 @@ static void genesis_get_stats(struct skge_port *skge, u64 *data)
static void genesis_mac_intr(struct skge_hw *hw, int port)
{
- struct skge_port *skge = netdev_priv(hw->dev[port]);
+ struct net_device *dev = hw->dev[port];
+ struct skge_port *skge = netdev_priv(dev);
u16 status = xm_read16(hw, port, XM_ISRC);
if (netif_msg_intr(skge))
printk(KERN_DEBUG PFX "%s: mac interrupt status 0x%x\n",
- skge->netdev->name, status);
+ dev->name, status);
- if (hw->phy_type == SK_PHY_XMAC &&
- (status & (XM_IS_INP_ASS | XM_IS_LIPA_RC)))
- xm_link_down(hw, port);
+ if (hw->phy_type == SK_PHY_XMAC && (status & XM_IS_INP_ASS)) {
+ xm_link_down(hw, port);
+ mod_timer(&skge->link_timer, jiffies + 1);
+ }
if (status & XM_IS_TXF_UR) {
xm_write32(hw, port, XM_MODE, XM_MD_FTF);
- ++skge->net_stats.tx_fifo_errors;
+ ++dev->stats.tx_fifo_errors;
}
+
if (status & XM_IS_RXF_OV) {
xm_write32(hw, port, XM_MODE, XM_MD_FRF);
- ++skge->net_stats.rx_fifo_errors;
+ ++dev->stats.rx_fifo_errors;
}
}
@@ -1753,11 +1861,12 @@ static void genesis_link_up(struct skge_port *skge)
}
xm_write32(hw, port, XM_MODE, mode);
- msk = XM_DEF_MSK;
- if (hw->phy_type != SK_PHY_XMAC)
- msk |= XM_IS_INP_ASS; /* disable GP0 interrupt bit */
+ /* Turn on detection of Tx underrun, Rx overrun */
+ msk = xm_read16(hw, port, XM_IMSK);
+ msk &= ~(XM_IS_RXF_OV | XM_IS_TXF_UR);
xm_write16(hw, port, XM_IMSK, msk);
+
xm_read16(hw, port, XM_ISRC);
/* get MMU Command Reg. */
@@ -2192,12 +2301,12 @@ static void yukon_mac_intr(struct skge_hw *hw, int port)
dev->name, status);
if (status & GM_IS_RX_FF_OR) {
- ++skge->net_stats.rx_fifo_errors;
+ ++dev->stats.rx_fifo_errors;
skge_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_CLI_RX_FO);
}
if (status & GM_IS_TX_FF_UR) {
- ++skge->net_stats.tx_fifo_errors;
+ ++dev->stats.tx_fifo_errors;
skge_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_CLI_TX_FU);
}
@@ -2403,32 +2512,31 @@ static int skge_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return err;
}
-static void skge_ramset(struct skge_hw *hw, u16 q, u32 start, size_t len)
+/* Assign Ram Buffer allocation to queue */
+static void skge_ramset(struct skge_hw *hw, u16 q, u32 start, u32 space)
{
u32 end;
- start /= 8;
- len /= 8;
- end = start + len - 1;
+ /* convert from K bytes to qwords used for hw register */
+ start *= 1024/8;
+ space *= 1024/8;
+ end = start + space - 1;
skge_write8(hw, RB_ADDR(q, RB_CTRL), RB_RST_CLR);
skge_write32(hw, RB_ADDR(q, RB_START), start);
+ skge_write32(hw, RB_ADDR(q, RB_END), end);
skge_write32(hw, RB_ADDR(q, RB_WP), start);
skge_write32(hw, RB_ADDR(q, RB_RP), start);
- skge_write32(hw, RB_ADDR(q, RB_END), end);
if (q == Q_R1 || q == Q_R2) {
+ u32 tp = space - space/4;
+
/* Set thresholds on receive queue's */
- skge_write32(hw, RB_ADDR(q, RB_RX_UTPP),
- start + (2*len)/3);
- skge_write32(hw, RB_ADDR(q, RB_RX_LTPP),
- start + (len/3));
- } else {
- /* Enable store & forward on Tx queue's because
- * Tx FIFO is only 4K on Genesis and 1K on Yukon
- */
+ skge_write32(hw, RB_ADDR(q, RB_RX_UTPP), tp);
+ skge_write32(hw, RB_ADDR(q, RB_RX_LTPP), space/4);
+ } else if (hw->chip_id != CHIP_ID_GENESIS)
+ /* Genesis Tx Fifo is too small for normal store/forward */
skge_write8(hw, RB_ADDR(q, RB_CTRL), RB_ENA_STFWD);
- }
skge_write8(hw, RB_ADDR(q, RB_CTRL), RB_ENA_OP_MD);
}
@@ -2456,7 +2564,7 @@ static int skge_up(struct net_device *dev)
struct skge_port *skge = netdev_priv(dev);
struct skge_hw *hw = skge->hw;
int port = skge->port;
- u32 chunk, ram_addr;
+ u32 ramaddr, ramsize, rxspace;
size_t rx_size, tx_size;
int err;
@@ -2511,14 +2619,15 @@ static int skge_up(struct net_device *dev)
spin_unlock_bh(&hw->phy_lock);
/* Configure RAMbuffers */
- chunk = hw->ram_size / ((hw->ports + 1)*2);
- ram_addr = hw->ram_offset + 2 * chunk * port;
+ ramsize = (hw->ram_size - hw->ram_offset) / hw->ports;
+ ramaddr = hw->ram_offset + port * ramsize;
+ rxspace = 8 + (2*(ramsize - 16))/3;
- skge_ramset(hw, rxqaddr[port], ram_addr, chunk);
- skge_qset(skge, rxqaddr[port], skge->rx_ring.to_clean);
+ skge_ramset(hw, rxqaddr[port], ramaddr, rxspace);
+ skge_ramset(hw, txqaddr[port], ramaddr + rxspace, ramsize - rxspace);
+ skge_qset(skge, rxqaddr[port], skge->rx_ring.to_clean);
BUG_ON(skge->tx_ring.to_use != skge->tx_ring.to_clean);
- skge_ramset(hw, txqaddr[port], ram_addr+chunk, chunk);
skge_qset(skge, txqaddr[port], skge->tx_ring.to_use);
/* Start receiver BMU */
@@ -2544,6 +2653,15 @@ static int skge_up(struct net_device *dev)
return err;
}
+/* stop receiver */
+static void skge_rx_stop(struct skge_hw *hw, int port)
+{
+ skge_write8(hw, Q_ADDR(rxqaddr[port], Q_CSR), CSR_STOP);
+ skge_write32(hw, RB_ADDR(port ? Q_R2 : Q_R1, RB_CTRL),
+ RB_RST_SET|RB_DIS_OP_MD);
+ skge_write32(hw, Q_ADDR(rxqaddr[port], Q_CSR), CSR_SET_RESET);
+}
+
static int skge_down(struct net_device *dev)
{
struct skge_port *skge = netdev_priv(dev);
@@ -2595,11 +2713,8 @@ static int skge_down(struct net_device *dev)
/* Reset the RAM Buffer async Tx queue */
skge_write8(hw, RB_ADDR(port == 0 ? Q_XA1 : Q_XA2, RB_CTRL), RB_RST_SET);
- /* stop receiver */
- skge_write8(hw, Q_ADDR(rxqaddr[port], Q_CSR), CSR_STOP);
- skge_write32(hw, RB_ADDR(port ? Q_R2 : Q_R1, RB_CTRL),
- RB_RST_SET|RB_DIS_OP_MD);
- skge_write32(hw, Q_ADDR(rxqaddr[port], Q_CSR), CSR_SET_RESET);
+
+ skge_rx_stop(hw, port);
if (hw->chip_id == CHIP_ID_GENESIS) {
skge_write8(hw, SK_REG(port, TX_MFF_CTRL2), MFF_RST_SET);
@@ -2782,7 +2897,11 @@ static void skge_tx_timeout(struct net_device *dev)
static int skge_change_mtu(struct net_device *dev, int new_mtu)
{
+ struct skge_port *skge = netdev_priv(dev);
+ struct skge_hw *hw = skge->hw;
+ int port = skge->port;
int err;
+ u16 ctl, reg;
if (new_mtu < ETH_ZLEN || new_mtu > ETH_JUMBO_MTU)
return -EINVAL;
@@ -2792,13 +2911,40 @@ static int skge_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
- skge_down(dev);
+ skge_write32(hw, B0_IMSK, 0);
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ netif_stop_queue(dev);
+ napi_disable(&skge->napi);
+
+ ctl = gma_read16(hw, port, GM_GP_CTRL);
+ gma_write16(hw, port, GM_GP_CTRL, ctl & ~GM_GPCR_RX_ENA);
+
+ skge_rx_clean(skge);
+ skge_rx_stop(hw, port);
dev->mtu = new_mtu;
- err = skge_up(dev);
+ reg = GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF);
+ if (new_mtu > 1500)
+ reg |= GM_SMOD_JUMBO_ENA;
+ gma_write16(hw, port, GM_SERIAL_MODE, reg);
+
+ skge_write8(hw, RB_ADDR(rxqaddr[port], RB_CTRL), RB_ENA_OP_MD);
+
+ err = skge_rx_fill(dev);
+ wmb();
+ if (!err)
+ skge_write8(hw, Q_ADDR(rxqaddr[port], Q_CSR), CSR_START | CSR_IRQ_CL_F);
+ skge_write32(hw, B0_IMSK, hw->intr_mask);
+
if (err)
dev_close(dev);
+ else {
+ gma_write16(hw, port, GM_GP_CTRL, ctl);
+
+ napi_enable(&skge->napi);
+ netif_wake_queue(dev);
+ }
return err;
}
@@ -2994,18 +3140,18 @@ error:
if (skge->hw->chip_id == CHIP_ID_GENESIS) {
if (status & (XMR_FS_RUNT|XMR_FS_LNG_ERR))
- skge->net_stats.rx_length_errors++;
+ dev->stats.rx_length_errors++;
if (status & XMR_FS_FRA_ERR)
- skge->net_stats.rx_frame_errors++;
+ dev->stats.rx_frame_errors++;
if (status & XMR_FS_FCS_ERR)
- skge->net_stats.rx_crc_errors++;
+ dev->stats.rx_crc_errors++;
} else {
if (status & (GMR_FS_LONG_ERR|GMR_FS_UN_SIZE))
- skge->net_stats.rx_length_errors++;
+ dev->stats.rx_length_errors++;
if (status & GMR_FS_FRAGMENT)
- skge->net_stats.rx_frame_errors++;
+ dev->stats.rx_frame_errors++;
if (status & GMR_FS_CRC_ERR)
- skge->net_stats.rx_crc_errors++;
+ dev->stats.rx_crc_errors++;
}
resubmit:
@@ -3103,10 +3249,7 @@ static void skge_mac_parity(struct skge_hw *hw, int port)
{
struct net_device *dev = hw->dev[port];
- if (dev) {
- struct skge_port *skge = netdev_priv(dev);
- ++skge->net_stats.tx_heartbeat_errors;
- }
+ ++dev->stats.tx_heartbeat_errors;
if (hw->chip_id == CHIP_ID_GENESIS)
skge_write16(hw, SK_REG(port, TX_MFF_CTRL1),
@@ -3259,9 +3402,7 @@ static irqreturn_t skge_intr(int irq, void *dev_id)
skge_write16(hw, B3_PA_CTRL, PA_CLR_TO_TX1);
if (status & IS_PA_TO_RX1) {
- struct skge_port *skge = netdev_priv(hw->dev[0]);
-
- ++skge->net_stats.rx_over_errors;
+ ++hw->dev[0]->stats.rx_over_errors;
skge_write16(hw, B3_PA_CTRL, PA_CLR_TO_RX1);
}
@@ -3278,7 +3419,7 @@ static irqreturn_t skge_intr(int irq, void *dev_id)
}
if (status & IS_PA_TO_RX2) {
- ++skge->net_stats.rx_over_errors;
+ ++hw->dev[1]->stats.rx_over_errors;
skge_write16(hw, B3_PA_CTRL, PA_CLR_TO_RX2);
}
@@ -3450,15 +3591,12 @@ static int skge_reset(struct skge_hw *hw)
if (hw->chip_id == CHIP_ID_GENESIS) {
if (t8 == 3) {
/* special case: 4 x 64k x 36, offset = 0x80000 */
- hw->ram_size = 0x100000;
- hw->ram_offset = 0x80000;
+ hw->ram_size = 1024;
+ hw->ram_offset = 512;
} else
hw->ram_size = t8 * 512;
- }
- else if (t8 == 0)
- hw->ram_size = 0x20000;
- else
- hw->ram_size = t8 * 4096;
+ } else /* Yukon */
+ hw->ram_size = t8 ? t8 * 4 : 128;
hw->intr_mask = IS_HW_ERR;
@@ -3540,6 +3678,145 @@ static int skge_reset(struct skge_hw *hw)
return 0;
}
+
+#ifdef CONFIG_SKGE_DEBUG
+
+static struct dentry *skge_debug;
+
+static int skge_debug_show(struct seq_file *seq, void *v)
+{
+ struct net_device *dev = seq->private;
+ const struct skge_port *skge = netdev_priv(dev);
+ const struct skge_hw *hw = skge->hw;
+ const struct skge_element *e;
+
+ if (!netif_running(dev))
+ return -ENETDOWN;
+
+ seq_printf(seq, "IRQ src=%x mask=%x\n", skge_read32(hw, B0_ISRC),
+ skge_read32(hw, B0_IMSK));
+
+ seq_printf(seq, "Tx Ring: (%d)\n", skge_avail(&skge->tx_ring));
+ for (e = skge->tx_ring.to_clean; e != skge->tx_ring.to_use; e = e->next) {
+ const struct skge_tx_desc *t = e->desc;
+ seq_printf(seq, "%#x dma=%#x%08x %#x csum=%#x/%x/%x\n",
+ t->control, t->dma_hi, t->dma_lo, t->status,
+ t->csum_offs, t->csum_write, t->csum_start);
+ }
+
+ seq_printf(seq, "\nRx Ring: \n");
+ for (e = skge->rx_ring.to_clean; ; e = e->next) {
+ const struct skge_rx_desc *r = e->desc;
+
+ if (r->control & BMU_OWN)
+ break;
+
+ seq_printf(seq, "%#x dma=%#x%08x %#x %#x csum=%#x/%x\n",
+ r->control, r->dma_hi, r->dma_lo, r->status,
+ r->timestamp, r->csum1, r->csum1_start);
+ }
+
+ return 0;
+}
+
+static int skge_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, skge_debug_show, inode->i_private);
+}
+
+static const struct file_operations skge_debug_fops = {
+ .owner = THIS_MODULE,
+ .open = skge_debug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/*
+ * Use network device events to create/remove/rename
+ * debugfs file entries
+ */
+static int skge_device_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = ptr;
+ struct skge_port *skge;
+ struct dentry *d;
+
+ if (dev->open != &skge_up || !skge_debug)
+ goto done;
+
+ skge = netdev_priv(dev);
+ switch(event) {
+ case NETDEV_CHANGENAME:
+ if (skge->debugfs) {
+ d = debugfs_rename(skge_debug, skge->debugfs,
+ skge_debug, dev->name);
+ if (d)
+ skge->debugfs = d;
+ else {
+ pr_info(PFX "%s: rename failed\n", dev->name);
+ debugfs_remove(skge->debugfs);
+ }
+ }
+ break;
+
+ case NETDEV_GOING_DOWN:
+ if (skge->debugfs) {
+ debugfs_remove(skge->debugfs);
+ skge->debugfs = NULL;
+ }
+ break;
+
+ case NETDEV_UP:
+ d = debugfs_create_file(dev->name, S_IRUGO,
+ skge_debug, dev,
+ &skge_debug_fops);
+ if (!d || IS_ERR(d))
+ pr_info(PFX "%s: debugfs create failed\n",
+ dev->name);
+ else
+ skge->debugfs = d;
+ break;
+ }
+
+done:
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block skge_notifier = {
+ .notifier_call = skge_device_event,
+};
+
+
+static __init void skge_debug_init(void)
+{
+ struct dentry *ent;
+
+ ent = debugfs_create_dir("skge", NULL);
+ if (!ent || IS_ERR(ent)) {
+ pr_info(PFX "debugfs create directory failed\n");
+ return;
+ }
+
+ skge_debug = ent;
+ register_netdevice_notifier(&skge_notifier);
+}
+
+static __exit void skge_debug_cleanup(void)
+{
+ if (skge_debug) {
+ unregister_netdevice_notifier(&skge_notifier);
+ debugfs_remove(skge_debug);
+ skge_debug = NULL;
+ }
+}
+
+#else
+#define skge_debug_init()
+#define skge_debug_cleanup()
+#endif
+
/* Initialize network device */
static struct net_device *skge_devinit(struct skge_hw *hw, int port,
int highmem)
@@ -3904,12 +4181,14 @@ static struct pci_driver skge_driver = {
static int __init skge_init_module(void)
{
+ skge_debug_init();
return pci_register_driver(&skge_driver);
}
static void __exit skge_cleanup_module(void)
{
pci_unregister_driver(&skge_driver);
+ skge_debug_cleanup();
}
module_init(skge_init_module);
diff --git a/drivers/net/skge.h b/drivers/net/skge.h
index 1a57bdd..17caccb 100644
--- a/drivers/net/skge.h
+++ b/drivers/net/skge.h
@@ -1,5 +1,5 @@
/*
- * Definitions for the new Marvell Yukon / SysKonenct driver.
+ * Definitions for the new Marvell Yukon / SysKonnect driver.
*/
#ifndef _SKGE_H
#define _SKGE_H
@@ -8,8 +8,10 @@
#define PCI_DEV_REG1 0x40
#define PCI_PHY_COMA 0x8000000
#define PCI_VIO 0x2000000
+
#define PCI_DEV_REG2 0x44
-#define PCI_REV_DESC 0x4
+#define PCI_VPD_ROM_SZ 7L<<14 /* VPD ROM size 0=256, 1=512, ... */
+#define PCI_REV_DESC 1<<2 /* Reverse Descriptor bytes */
#define PCI_STATUS_ERROR_BITS (PCI_STATUS_DETECTED_PARITY | \
PCI_STATUS_SIG_SYSTEM_ERROR | \
@@ -2191,11 +2193,9 @@ enum {
XM_IS_TXF_UR = 1<<2, /* Bit 2: Transmit FIFO Underrun */
XM_IS_TX_COMP = 1<<1, /* Bit 1: Frame Tx Complete */
XM_IS_RX_COMP = 1<<0, /* Bit 0: Frame Rx Complete */
-};
-
-#define XM_DEF_MSK (~(XM_IS_INP_ASS | XM_IS_LIPA_RC | \
- XM_IS_RXF_OV | XM_IS_TXF_UR))
+ XM_IMSK_DISABLE = 0xffff,
+};
/* XM_HW_CFG 16 bit r/w Hardware Config Register */
enum {
@@ -2469,8 +2469,9 @@ struct skge_port {
void *mem; /* PCI memory for rings */
dma_addr_t dma;
unsigned long mem_size;
-
- struct net_device_stats net_stats;
+#ifdef CONFIG_SKGE_DEBUG
+ struct dentry *debugfs;
+#endif
};
diff --git a/drivers/net/tokenring/3c359.c b/drivers/net/tokenring/3c359.c
index 7224d36..5d31519 100644
--- a/drivers/net/tokenring/3c359.c
+++ b/drivers/net/tokenring/3c359.c
@@ -760,7 +760,7 @@ static int xl_open_hw(struct net_device *dev)
if (xl_priv->xl_laa[0]) { /* If using a LAA address */
for (i=10;i<16;i++) {
writel( (MEM_BYTE_WRITE | 0xD0000 | xl_priv->srb) + i, xl_mmio + MMIO_MAC_ACCESS_CMD) ;
- writeb(xl_priv->xl_laa[i],xl_mmio + MMIO_MACDATA) ;
+ writeb(xl_priv->xl_laa[i-10],xl_mmio + MMIO_MACDATA) ;
}
memcpy(dev->dev_addr,xl_priv->xl_laa,dev->addr_len) ;
} else { /* Regular hardware address */
^ permalink raw reply related
* Re: [patch 3/4] WOL bugfix for 3c59x.c
From: Jeff Garzik @ 2007-10-17 1:16 UTC (permalink / raw)
To: akpm; +Cc: netdev, klassert, hcoin
In-Reply-To: <200710162124.l9GLO9t6017971@imap1.linux-foundation.org>
akpm@linux-foundation.org wrote:
> From: Steffen Klassert <klassert@mathematik.tu-chemnitz.de>
>
> Some NICs (3c905B) can not generate PME in power state PCI_D0, while others
> like 3c905C can. Call pci_enable_wake() with PCI_D3hot should give proper
> WOL for 3c905B.
>
> Signed-off-by: Steffen Klassert <klassert@mathematik.tu-chemnitz.de>
> Tested-by: Harry Coin <hcoin@n4comm.com>
> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
> ---
>
> drivers/net/3c59x.c | 8 +++++++-
> 1 file changed, 7 insertions(+), 1 deletion(-)
applied
^ permalink raw reply
* Re: [PATCH 1/7] skge: fix ram buffer size calculation
From: Jeff Garzik @ 2007-10-17 1:16 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: netdev
In-Reply-To: <20071016191733.868358630@linux-foundation.org>
applied 1-7
^ permalink raw reply
* Re: [PATCH] net: Fix new EMAC driver for NAPI changes
From: Jeff Garzik @ 2007-10-17 1:16 UTC (permalink / raw)
To: benh; +Cc: shemminger, netdev, David S. Miller, Roland Dreier,
linuxppc-dev list
In-Reply-To: <1192513250.19073.10.camel@pasglop>
Benjamin Herrenschmidt wrote:
> net: Fix new EMAC driver for NAPI changes
>
> This fixes the new EMAC driver for the NAPI updates. The previous patch
> by Roland Dreier (already applied) to do that doesn't actually work. This
> applies on top of it makes it work on my test Ebony machine.
>
> This patch depends on "net: Add __napi_sycnhronize() to sync with napi poll"
> posted previously.
>
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> ---
>
> The old EMAC driver does things a bit differently (doesn't do useful
> locking :-) and seems to work with Roland patch. So I'm not going to
> touch it unless somebody reports me that it has problems
applied
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox