sfc: Fix EEH with legacy interrupts.
PCI legacy interrupts are level-triggered, and we cannot mask them up on an isolated device. Instead, disable the IRQ at the controller until we have recovered. Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
This commit is contained in:
parent
3717348840
commit
b28405b0f2
@ -23,6 +23,7 @@
|
|||||||
#include <linux/gfp.h>
|
#include <linux/gfp.h>
|
||||||
#include <linux/cpu_rmap.h>
|
#include <linux/cpu_rmap.h>
|
||||||
#include <linux/aer.h>
|
#include <linux/aer.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
#include "net_driver.h"
|
#include "net_driver.h"
|
||||||
#include "efx.h"
|
#include "efx.h"
|
||||||
#include "nic.h"
|
#include "nic.h"
|
||||||
@ -1427,6 +1428,10 @@ static void efx_start_interrupts(struct efx_nic *efx, bool may_keep_eventq)
|
|||||||
|
|
||||||
BUG_ON(efx->state == STATE_DISABLED);
|
BUG_ON(efx->state == STATE_DISABLED);
|
||||||
|
|
||||||
|
if (efx->eeh_disabled_legacy_irq) {
|
||||||
|
enable_irq(efx->legacy_irq);
|
||||||
|
efx->eeh_disabled_legacy_irq = false;
|
||||||
|
}
|
||||||
if (efx->legacy_irq)
|
if (efx->legacy_irq)
|
||||||
efx->legacy_irq_enabled = true;
|
efx->legacy_irq_enabled = true;
|
||||||
efx_nic_enable_interrupts(efx);
|
efx_nic_enable_interrupts(efx);
|
||||||
@ -2365,7 +2370,7 @@ out:
|
|||||||
* Returns 0 if the recovery mechanisms are unsuccessful.
|
* Returns 0 if the recovery mechanisms are unsuccessful.
|
||||||
* Returns a non-zero value otherwise.
|
* Returns a non-zero value otherwise.
|
||||||
*/
|
*/
|
||||||
static int efx_try_recovery(struct efx_nic *efx)
|
int efx_try_recovery(struct efx_nic *efx)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_EEH
|
#ifdef CONFIG_EEH
|
||||||
/* A PCI error can occur and not be seen by EEH because nothing
|
/* A PCI error can occur and not be seen by EEH because nothing
|
||||||
|
@ -124,6 +124,7 @@ extern const struct ethtool_ops efx_ethtool_ops;
|
|||||||
extern int efx_reset(struct efx_nic *efx, enum reset_type method);
|
extern int efx_reset(struct efx_nic *efx, enum reset_type method);
|
||||||
extern void efx_reset_down(struct efx_nic *efx, enum reset_type method);
|
extern void efx_reset_down(struct efx_nic *efx, enum reset_type method);
|
||||||
extern int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok);
|
extern int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok);
|
||||||
|
extern int efx_try_recovery(struct efx_nic *efx);
|
||||||
|
|
||||||
/* Global */
|
/* Global */
|
||||||
extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type);
|
extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type);
|
||||||
|
@ -788,6 +788,7 @@ struct efx_nic {
|
|||||||
const struct efx_nic_type *type;
|
const struct efx_nic_type *type;
|
||||||
int legacy_irq;
|
int legacy_irq;
|
||||||
bool legacy_irq_enabled;
|
bool legacy_irq_enabled;
|
||||||
|
bool eeh_disabled_legacy_irq;
|
||||||
struct workqueue_struct *workqueue;
|
struct workqueue_struct *workqueue;
|
||||||
char workqueue_name[16];
|
char workqueue_name[16];
|
||||||
struct work_struct reset_work;
|
struct work_struct reset_work;
|
||||||
|
@ -1579,6 +1579,16 @@ static irqreturn_t efx_legacy_interrupt(int irq, void *dev_id)
|
|||||||
efx_readd(efx, ®, FR_BZ_INT_ISR0);
|
efx_readd(efx, ®, FR_BZ_INT_ISR0);
|
||||||
queues = EFX_EXTRACT_DWORD(reg, 0, 31);
|
queues = EFX_EXTRACT_DWORD(reg, 0, 31);
|
||||||
|
|
||||||
|
/* Legacy interrupts are disabled too late by the EEH kernel
|
||||||
|
* code. Disable them earlier.
|
||||||
|
* If an EEH error occurred, the read will have returned all ones.
|
||||||
|
*/
|
||||||
|
if (EFX_DWORD_IS_ALL_ONES(reg) && efx_try_recovery(efx) &&
|
||||||
|
!efx->eeh_disabled_legacy_irq) {
|
||||||
|
disable_irq_nosync(efx->legacy_irq);
|
||||||
|
efx->eeh_disabled_legacy_irq = true;
|
||||||
|
}
|
||||||
|
|
||||||
/* Handle non-event-queue sources */
|
/* Handle non-event-queue sources */
|
||||||
if (queues & (1U << efx->irq_level)) {
|
if (queues & (1U << efx->irq_level)) {
|
||||||
syserr = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_FATAL_INT);
|
syserr = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_FATAL_INT);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user