aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/sfc
diff options
context:
space:
mode:
authorBen Hutchings <bhutchings@solarflare.com>2010-12-07 14:24:45 -0500
committerBen Hutchings <bhutchings@solarflare.com>2010-12-07 14:30:19 -0500
commit94dec6a2d20a26a779b63bb584e48db5fb0ddb53 (patch)
tree3911c0883429411e27f22db1e34436d75fbb937b /drivers/net/sfc
parent46bcf14f44d8f31ecfdc8b6708ec15a3b33316d9 (diff)
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 <bhutchings@solarflare.com>
Diffstat (limited to 'drivers/net/sfc')
-rw-r--r--drivers/net/sfc/efx.c12
-rw-r--r--drivers/net/sfc/net_driver.h2
-rw-r--r--drivers/net/sfc/nic.c6
3 files changed, 18 insertions, 2 deletions
diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c
index 05df20e47976..d06cb742164b 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)
335 335
336 /* Disable interrupts and wait for ISRs to complete */ 336 /* Disable interrupts and wait for ISRs to complete */
337 efx_nic_disable_interrupts(efx); 337 efx_nic_disable_interrupts(efx);
338 if (efx->legacy_irq) 338 if (efx->legacy_irq) {
339 synchronize_irq(efx->legacy_irq); 339 synchronize_irq(efx->legacy_irq);
340 efx->legacy_irq_enabled = false;
341 }
340 if (channel->irq) 342 if (channel->irq)
341 synchronize_irq(channel->irq); 343 synchronize_irq(channel->irq);
342 344
@@ -351,6 +353,8 @@ void efx_process_channel_now(struct efx_channel *channel)
351 efx_channel_processed(channel); 353 efx_channel_processed(channel);
352 354
353 napi_enable(&channel->napi_str); 355 napi_enable(&channel->napi_str);
356 if (efx->legacy_irq)
357 efx->legacy_irq_enabled = true;
354 efx_nic_enable_interrupts(efx); 358 efx_nic_enable_interrupts(efx);
355} 359}
356 360
@@ -1400,6 +1404,8 @@ static void efx_start_all(struct efx_nic *efx)
1400 efx_start_channel(channel); 1404 efx_start_channel(channel);
1401 } 1405 }
1402 1406
1407 if (efx->legacy_irq)
1408 efx->legacy_irq_enabled = true;
1403 efx_nic_enable_interrupts(efx); 1409 efx_nic_enable_interrupts(efx);
1404 1410
1405 /* Switch to event based MCDI completions after enabling interrupts. 1411 /* Switch to event based MCDI completions after enabling interrupts.
@@ -1460,8 +1466,10 @@ static void efx_stop_all(struct efx_nic *efx)
1460 1466
1461 /* Disable interrupts and wait for ISR to complete */ 1467 /* Disable interrupts and wait for ISR to complete */
1462 efx_nic_disable_interrupts(efx); 1468 efx_nic_disable_interrupts(efx);
1463 if (efx->legacy_irq) 1469 if (efx->legacy_irq) {
1464 synchronize_irq(efx->legacy_irq); 1470 synchronize_irq(efx->legacy_irq);
1471 efx->legacy_irq_enabled = false;
1472 }
1465 efx_for_each_channel(channel, efx) { 1473 efx_for_each_channel(channel, efx) {
1466 if (channel->irq) 1474 if (channel->irq)
1467 synchronize_irq(channel->irq); 1475 synchronize_irq(channel->irq);
diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h
index 0a7e26d73b52..b137c889152b 100644
--- a/drivers/net/sfc/net_driver.h
+++ b/drivers/net/sfc/net_driver.h
@@ -621,6 +621,7 @@ struct efx_filter_state;
621 * @pci_dev: The PCI device 621 * @pci_dev: The PCI device
622 * @type: Controller type attributes 622 * @type: Controller type attributes
623 * @legacy_irq: IRQ number 623 * @legacy_irq: IRQ number
624 * @legacy_irq_enabled: Are IRQs enabled on NIC (INT_EN_KER register)?
624 * @workqueue: Workqueue for port reconfigures and the HW monitor. 625 * @workqueue: Workqueue for port reconfigures and the HW monitor.
625 * Work items do not hold and must not acquire RTNL. 626 * Work items do not hold and must not acquire RTNL.
626 * @workqueue_name: Name of workqueue 627 * @workqueue_name: Name of workqueue
@@ -709,6 +710,7 @@ struct efx_nic {
709 struct pci_dev *pci_dev; 710 struct pci_dev *pci_dev;
710 const struct efx_nic_type *type; 711 const struct efx_nic_type *type;
711 int legacy_irq; 712 int legacy_irq;
713 bool legacy_irq_enabled;
712 struct workqueue_struct *workqueue; 714 struct workqueue_struct *workqueue;
713 char workqueue_name[16]; 715 char workqueue_name[16];
714 struct work_struct reset_work; 716 struct work_struct reset_work;
diff --git a/drivers/net/sfc/nic.c b/drivers/net/sfc/nic.c
index 41c36b9a4244..67cb0c96838c 100644
--- a/drivers/net/sfc/nic.c
+++ b/drivers/net/sfc/nic.c
@@ -1418,6 +1418,12 @@ static irqreturn_t efx_legacy_interrupt(int irq, void *dev_id)
1418 u32 queues; 1418 u32 queues;
1419 int syserr; 1419 int syserr;
1420 1420
1421 /* Could this be ours? If interrupts are disabled then the
1422 * channel state may not be valid.
1423 */
1424 if (!efx->legacy_irq_enabled)
1425 return result;
1426
1421 /* Read the ISR which also ACKs the interrupts */ 1427 /* Read the ISR which also ACKs the interrupts */
1422 efx_readd(efx, &reg, FR_BZ_INT_ISR0); 1428 efx_readd(efx, &reg, FR_BZ_INT_ISR0);
1423 queues = EFX_EXTRACT_DWORD(reg, 0, 31); 1429 queues = EFX_EXTRACT_DWORD(reg, 0, 31);