From 94dec6a2d20a26a779b63bb584e48db5fb0ddb53 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 7 Dec 2010 19:24:45 +0000 Subject: sfc: Fix crash in legacy onterrupt handler during ring reallocation If we are using a legacy interrupt, our IRQ may be shared and our interrupt handler may be called even though interrupts are disabled on the NIC. When we change ring sizes, we reallocate the event queue and the interrupt handler may use an invalid pointer when called for another device's interrupt. Maintain a legacy_irq_enabled flag and test that at the top of the interrupt handler. Note that this problem results from the need to work around broken INT_ISR0 reads, and does not affect the legacy interrupt handler for Falcon A1. Signed-off-by: Ben Hutchings --- drivers/net/sfc/efx.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/net/sfc/efx.c') diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index 05df20e4797..d06cb742164 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c @@ -335,8 +335,10 @@ void efx_process_channel_now(struct efx_channel *channel) /* Disable interrupts and wait for ISRs to complete */ efx_nic_disable_interrupts(efx); - if (efx->legacy_irq) + if (efx->legacy_irq) { synchronize_irq(efx->legacy_irq); + efx->legacy_irq_enabled = false; + } if (channel->irq) synchronize_irq(channel->irq); @@ -351,6 +353,8 @@ void efx_process_channel_now(struct efx_channel *channel) efx_channel_processed(channel); napi_enable(&channel->napi_str); + if (efx->legacy_irq) + efx->legacy_irq_enabled = true; efx_nic_enable_interrupts(efx); } @@ -1400,6 +1404,8 @@ static void efx_start_all(struct efx_nic *efx) efx_start_channel(channel); } + if (efx->legacy_irq) + efx->legacy_irq_enabled = true; efx_nic_enable_interrupts(efx); /* Switch to event based MCDI completions after enabling interrupts. @@ -1460,8 +1466,10 @@ static void efx_stop_all(struct efx_nic *efx) /* Disable interrupts and wait for ISR to complete */ efx_nic_disable_interrupts(efx); - if (efx->legacy_irq) + if (efx->legacy_irq) { synchronize_irq(efx->legacy_irq); + efx->legacy_irq_enabled = false; + } efx_for_each_channel(channel, efx) { if (channel->irq) synchronize_irq(channel->irq); -- cgit v1.2.3-18-g5258 From e8f149924a3111015d16dfbbb4816cfc75ba53cd Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 7 Dec 2010 19:47:34 +0000 Subject: sfc: Fix NAPI list corruption during ring reallocation Call netif_napi_{add,del}() on the NAPI contexts in the new and old channels, respectively. Since efx_init_napi() cannot fail, make its return type void. Signed-off-by: Ben Hutchings --- drivers/net/sfc/efx.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) (limited to 'drivers/net/sfc/efx.c') diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index d06cb742164..fb83cdd9464 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c @@ -197,7 +197,9 @@ MODULE_PARM_DESC(debug, "Bitmapped debugging message enable value"); static void efx_remove_channels(struct efx_nic *efx); static void efx_remove_port(struct efx_nic *efx); +static void efx_init_napi(struct efx_nic *efx); static void efx_fini_napi(struct efx_nic *efx); +static void efx_fini_napi_channel(struct efx_channel *channel); static void efx_fini_struct(struct efx_nic *efx); static void efx_start_all(struct efx_nic *efx); static void efx_stop_all(struct efx_nic *efx); @@ -430,6 +432,7 @@ efx_alloc_channel(struct efx_nic *efx, int i, struct efx_channel *old_channel) *channel = *old_channel; + channel->napi_dev = NULL; memset(&channel->eventq, 0, sizeof(channel->eventq)); rx_queue = &channel->rx_queue; @@ -740,9 +743,13 @@ efx_realloc_channels(struct efx_nic *efx, u32 rxq_entries, u32 txq_entries) if (rc) goto rollback; + efx_init_napi(efx); + /* Destroy old channels */ - for (i = 0; i < efx->n_channels; i++) + for (i = 0; i < efx->n_channels; i++) { + efx_fini_napi_channel(other_channel[i]); efx_remove_channel(other_channel[i]); + } out: /* Free unused channel structures */ for (i = 0; i < efx->n_channels; i++) @@ -1601,7 +1608,7 @@ static int efx_ioctl(struct net_device *net_dev, struct ifreq *ifr, int cmd) * **************************************************************************/ -static int efx_init_napi(struct efx_nic *efx) +static void efx_init_napi(struct efx_nic *efx) { struct efx_channel *channel; @@ -1610,18 +1617,21 @@ static int efx_init_napi(struct efx_nic *efx) netif_napi_add(channel->napi_dev, &channel->napi_str, efx_poll, napi_weight); } - return 0; +} + +static void efx_fini_napi_channel(struct efx_channel *channel) +{ + if (channel->napi_dev) + netif_napi_del(&channel->napi_str); + channel->napi_dev = NULL; } static void efx_fini_napi(struct efx_nic *efx) { struct efx_channel *channel; - efx_for_each_channel(channel, efx) { - if (channel->napi_dev) - netif_napi_del(&channel->napi_str); - channel->napi_dev = NULL; - } + efx_for_each_channel(channel, efx) + efx_fini_napi_channel(channel); } /************************************************************************** @@ -2343,9 +2353,7 @@ static int efx_pci_probe_main(struct efx_nic *efx) if (rc) goto fail1; - rc = efx_init_napi(efx); - if (rc) - goto fail2; + efx_init_napi(efx); rc = efx->type->init(efx); if (rc) { @@ -2376,7 +2384,6 @@ static int efx_pci_probe_main(struct efx_nic *efx) efx->type->fini(efx); fail3: efx_fini_napi(efx); - fail2: efx_remove_all(efx); fail1: return rc; -- cgit v1.2.3-18-g5258