From 02e500a0554a154d500947cefa9e7860c273a47c Mon Sep 17 00:00:00 2001 From: Jason Yan Date: Thu, 28 Mar 2019 11:12:21 +0800 Subject: [PATCH] scsi: ata: Fix a race condition between scsi error handler and ahci interrupt euler inclusion category: bugfix bugzilla: NA CVE: NA --------------------------- interrupt scsi_eh ahci_error_intr =>ata_port_freeze =>__ata_port_freeze =>ahci_freeze (turn IRQ off) =>ata_port_abort =>ata_port_schedule_eh =>shost->host_eh_scheduled++; host_eh_scheduled = 1 scsi_error_handler =>ata_scsi_error =>ata_scsi_port_error_handler =>ahci_error_handler . =>sata_pmp_error_handler . =>ata_eh_thaw_port . =>ahci_thaw (turn IRQ on) ahci_error_intr . =>ata_port_freeze . =>__ata_port_freeze . =>ahci_freeze (turn IRQ off) . =>ata_port_abort . =>ata_port_schedule_eh . =>shost->host_eh_scheduled++; . host_eh_scheduled = 2 . =>ata_std_end_eh =>host->host_eh_scheduled = 0; host_eh_scheduled is 0 and scsi eh thread will not be scheduled again, and the ata port remain freeze and will never be enabled. Reported-by: luojian Signed-off-by: Jason Yan Reviewed-by: zhengbin Signed-off-by: Yang Yingliang --- drivers/ata/libata-eh.c | 2 +- drivers/scsi/libsas/sas_scsi_host.c | 4 ++-- drivers/scsi/scsi_error.c | 10 ++++++---- drivers/scsi/scsi_lib.c | 3 ++- include/scsi/scsi_host.h | 2 +- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 01306c018398..e4a781d9b58f 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -974,7 +974,7 @@ void ata_std_end_eh(struct ata_port *ap) { struct Scsi_Host *host = ap->scsi_host; - host->host_eh_scheduled = 0; + atomic_dec(&host->host_eh_scheduled); } EXPORT_SYMBOL(ata_std_end_eh); diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 33229348dcb6..b2e0d0f21e73 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -492,7 +492,7 @@ int sas_eh_abort_handler(struct scsi_cmnd *cmd) spin_lock_irqsave(host->host_lock, flags); /* We cannot do async aborts for SATA devices */ - if (dev_is_sata(dev) && !host->host_eh_scheduled) { + if (dev_is_sata(dev) && !atomic_read(&host->host_eh_scheduled)) { spin_unlock_irqrestore(host->host_lock, flags); return FAILED; } @@ -792,7 +792,7 @@ void sas_scsi_recover_host(struct Scsi_Host *shost) /* check if any new eh work was scheduled during the last run */ spin_lock_irq(&ha->lock); if (ha->eh_active == 0) { - shost->host_eh_scheduled = 0; + atomic_set(&shost->host_eh_scheduled, 0); retry = false; } spin_unlock_irq(&ha->lock); diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index c736d61b1648..55f12ecaf400 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -88,7 +88,7 @@ void scsi_schedule_eh(struct Scsi_Host *shost) if (scsi_host_set_state(shost, SHOST_RECOVERY) == 0 || scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY) == 0) { - shost->host_eh_scheduled++; + atomic_inc(&shost->host_eh_scheduled); scsi_eh_wakeup(shost); } @@ -2029,7 +2029,7 @@ static void scsi_restart_operations(struct Scsi_Host *shost) * pending commands complete. */ spin_lock_irqsave(shost->host_lock, flags); - if (shost->host_eh_scheduled) + if (atomic_read(&shost->host_eh_scheduled)) if (scsi_host_set_state(shost, SHOST_RECOVERY)) WARN_ON(scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY)); spin_unlock_irqrestore(shost->host_lock, flags); @@ -2165,7 +2165,8 @@ int scsi_error_handler(void *data) if (kthread_should_stop()) break; - if ((shost->host_failed == 0 && shost->host_eh_scheduled == 0) || + if ((shost->host_failed == 0 && + atomic_read(&shost->host_eh_scheduled) == 0) || shost->host_failed != scsi_host_busy(shost)) { SCSI_LOG_ERROR_RECOVERY(1, shost_printk(KERN_INFO, shost, @@ -2179,7 +2180,8 @@ int scsi_error_handler(void *data) SCSI_LOG_ERROR_RECOVERY(1, shost_printk(KERN_INFO, shost, "scsi_eh_%d: waking up %d/%d/%d\n", - shost->host_no, shost->host_eh_scheduled, + shost->host_no, + atomic_read(&shost->host_eh_scheduled), shost->host_failed, scsi_host_busy(shost))); diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 292829da23bd..300e5d6ec67e 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -348,7 +348,8 @@ static void scsi_dec_host_busy(struct Scsi_Host *shost) atomic_dec(&shost->host_busy); if (unlikely(scsi_host_in_recovery(shost))) { spin_lock_irqsave(shost->host_lock, flags); - if (shost->host_failed || shost->host_eh_scheduled) + if (shost->host_failed || + atomic_read(&shost->host_eh_scheduled)) scsi_eh_wakeup(shost); spin_unlock_irqrestore(shost->host_lock, flags); } diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 5ea06d310a25..7c241cbf64a0 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -561,7 +561,7 @@ struct Scsi_Host { unsigned int host_failed; /* commands that failed. protected by host_lock */ - unsigned int host_eh_scheduled; /* EH scheduled without command */ + atomic_t host_eh_scheduled; /* EH scheduled without command */ unsigned int host_no; /* Used for IOCTL_GET_IDLUN, /proc/scsi et al. */ -- GitLab