From d829118705f8213ffeffa4fefa8931dea6b7f016 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 5 Oct 2012 23:35:41 +0100 Subject: sfc: Rework IRQ enable/disable There are many problems with the current efx_stop_interrupts() and efx_start_interrupts(): 1. On Siena, it is unsafe to disable the master IRQ enable bit (DRV_INT_EN_KER) while any IRQ sources are enabled. 2. On EF10 there is no master IRQ enable bit, so we cannot expect to defer IRQs without tearing down event queues. (Though I don't think we will need to keep any event queues around while the device is down, as we do for VFDI on Siena.) 3. synchronize_irq() only waits for a running IRQ handler to finish, not for any propagation through IRQ controllers. Therefore an IRQ may still be received and handled after efx_stop_interrupts() returns. IRQ handlers can then race with channel reallocation. To fix this: a. Introduce a software IRQ enable flag. So long as this is clear, IRQ handlers will only acknowledge IRQs and not touch the channel structures. b. Define a new struct efx_msi_context as the context for MSIs. This is never reallocated and is sufficient to find the software enable flag and the channel structure. It also includes the channel/IRQ name, which was previously separated out as it must also not be reallocated. c. Split efx_{start,stop}_interrupts() into efx_{,soft_}_{enable,disable}_interrupts(). The 'soft' functions don't touch the hardware master enable flag (if it exists) and don't reinitialise or tear down channels with the keep_eventq flag set. Signed-off-by: Ben Hutchings --- drivers/net/ethernet/sfc/net_driver.h | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'drivers/net/ethernet/sfc/net_driver.h') diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index ad89d7d990f5..7c9637203ea0 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -419,6 +419,21 @@ struct efx_channel { struct efx_tx_queue tx_queue[EFX_TXQ_TYPES]; }; +/** + * struct efx_msi_context - Context for each MSI + * @efx: The associated NIC + * @index: Index of the channel/IRQ + * @name: Name of the channel/IRQ + * + * Unlike &struct efx_channel, this is never reallocated and is always + * safe for the IRQ handler to access. + */ +struct efx_msi_context { + struct efx_nic *efx; + unsigned int index; + char name[IFNAMSIZ + 6]; +}; + /** * struct efx_channel_type - distinguishes traffic and extra channels * @handle_no_channel: Handle failure to allocate an extra channel @@ -669,7 +684,6 @@ struct vfdi_status; * @pci_dev: The PCI device * @type: Controller type attributes * @legacy_irq: IRQ number - * @legacy_irq_enabled: Are IRQs enabled on NIC (INT_EN_KER register)? * @workqueue: Workqueue for port reconfigures and the HW monitor. * Work items do not hold and must not acquire RTNL. * @workqueue_name: Name of workqueue @@ -686,7 +700,7 @@ struct vfdi_status; * @tx_queue: TX DMA queues * @rx_queue: RX DMA queues * @channel: Channels - * @channel_name: Names for channels and their IRQs + * @msi_context: Context for each MSI * @extra_channel_types: Types of extra (non-traffic) channels that * should be allocated for this NIC * @rxq_entries: Size of receive queues requested by user. @@ -709,6 +723,8 @@ struct vfdi_status; * @rx_scatter: Scatter mode enabled for receives * @int_error_count: Number of internal errors seen recently * @int_error_expire: Time at which error count will be expired + * @irq_soft_enabled: Are IRQs soft-enabled? If not, IRQ handler will + * acknowledge but do nothing else. * @irq_status: Interrupt status buffer * @irq_zero_count: Number of legacy IRQs seen with queue flags == 0 * @irq_level: IRQ level/index for IRQs not triggered by an event queue @@ -786,7 +802,6 @@ struct efx_nic { unsigned int port_num; const struct efx_nic_type *type; int legacy_irq; - bool legacy_irq_enabled; bool eeh_disabled_legacy_irq; struct workqueue_struct *workqueue; char workqueue_name[16]; @@ -804,7 +819,7 @@ struct efx_nic { unsigned long reset_pending; struct efx_channel *channel[EFX_MAX_CHANNELS]; - char channel_name[EFX_MAX_CHANNELS][IFNAMSIZ + 6]; + struct efx_msi_context msi_context[EFX_MAX_CHANNELS]; const struct efx_channel_type * extra_channel_type[EFX_MAX_EXTRA_CHANNELS]; @@ -835,6 +850,7 @@ struct efx_nic { unsigned int_error_count; unsigned long int_error_expire; + bool irq_soft_enabled; struct efx_buffer irq_status; unsigned irq_zero_count; unsigned irq_level; -- cgit v1.2.3