提交 02e500a0 编写于 作者: J Jason Yan 提交者: Xie XiuQi

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: Nluojian <luojian5@huawei.com>
Signed-off-by: NJason Yan <yanaijie@huawei.com>
Reviewed-by: Nzhengbin <zhengbin13@huawei.com>
Signed-off-by: NYang Yingliang <yangyingliang@huawei.com>
上级 1269a2ec
...@@ -974,7 +974,7 @@ void ata_std_end_eh(struct ata_port *ap) ...@@ -974,7 +974,7 @@ void ata_std_end_eh(struct ata_port *ap)
{ {
struct Scsi_Host *host = ap->scsi_host; struct Scsi_Host *host = ap->scsi_host;
host->host_eh_scheduled = 0; atomic_dec(&host->host_eh_scheduled);
} }
EXPORT_SYMBOL(ata_std_end_eh); EXPORT_SYMBOL(ata_std_end_eh);
......
...@@ -492,7 +492,7 @@ int sas_eh_abort_handler(struct scsi_cmnd *cmd) ...@@ -492,7 +492,7 @@ int sas_eh_abort_handler(struct scsi_cmnd *cmd)
spin_lock_irqsave(host->host_lock, flags); spin_lock_irqsave(host->host_lock, flags);
/* We cannot do async aborts for SATA devices */ /* 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); spin_unlock_irqrestore(host->host_lock, flags);
return FAILED; return FAILED;
} }
...@@ -792,7 +792,7 @@ void sas_scsi_recover_host(struct Scsi_Host *shost) ...@@ -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 */ /* check if any new eh work was scheduled during the last run */
spin_lock_irq(&ha->lock); spin_lock_irq(&ha->lock);
if (ha->eh_active == 0) { if (ha->eh_active == 0) {
shost->host_eh_scheduled = 0; atomic_set(&shost->host_eh_scheduled, 0);
retry = false; retry = false;
} }
spin_unlock_irq(&ha->lock); spin_unlock_irq(&ha->lock);
......
...@@ -88,7 +88,7 @@ void scsi_schedule_eh(struct Scsi_Host *shost) ...@@ -88,7 +88,7 @@ void scsi_schedule_eh(struct Scsi_Host *shost)
if (scsi_host_set_state(shost, SHOST_RECOVERY) == 0 || if (scsi_host_set_state(shost, SHOST_RECOVERY) == 0 ||
scsi_host_set_state(shost, SHOST_CANCEL_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); scsi_eh_wakeup(shost);
} }
...@@ -2029,7 +2029,7 @@ static void scsi_restart_operations(struct Scsi_Host *shost) ...@@ -2029,7 +2029,7 @@ static void scsi_restart_operations(struct Scsi_Host *shost)
* pending commands complete. * pending commands complete.
*/ */
spin_lock_irqsave(shost->host_lock, flags); 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)) if (scsi_host_set_state(shost, SHOST_RECOVERY))
WARN_ON(scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY)); WARN_ON(scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY));
spin_unlock_irqrestore(shost->host_lock, flags); spin_unlock_irqrestore(shost->host_lock, flags);
...@@ -2165,7 +2165,8 @@ int scsi_error_handler(void *data) ...@@ -2165,7 +2165,8 @@ int scsi_error_handler(void *data)
if (kthread_should_stop()) if (kthread_should_stop())
break; 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)) { shost->host_failed != scsi_host_busy(shost)) {
SCSI_LOG_ERROR_RECOVERY(1, SCSI_LOG_ERROR_RECOVERY(1,
shost_printk(KERN_INFO, shost, shost_printk(KERN_INFO, shost,
...@@ -2179,7 +2180,8 @@ int scsi_error_handler(void *data) ...@@ -2179,7 +2180,8 @@ int scsi_error_handler(void *data)
SCSI_LOG_ERROR_RECOVERY(1, SCSI_LOG_ERROR_RECOVERY(1,
shost_printk(KERN_INFO, shost, shost_printk(KERN_INFO, shost,
"scsi_eh_%d: waking up %d/%d/%d\n", "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, shost->host_failed,
scsi_host_busy(shost))); scsi_host_busy(shost)));
......
...@@ -348,7 +348,8 @@ static void scsi_dec_host_busy(struct Scsi_Host *shost) ...@@ -348,7 +348,8 @@ static void scsi_dec_host_busy(struct Scsi_Host *shost)
atomic_dec(&shost->host_busy); atomic_dec(&shost->host_busy);
if (unlikely(scsi_host_in_recovery(shost))) { if (unlikely(scsi_host_in_recovery(shost))) {
spin_lock_irqsave(shost->host_lock, flags); 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); scsi_eh_wakeup(shost);
spin_unlock_irqrestore(shost->host_lock, flags); spin_unlock_irqrestore(shost->host_lock, flags);
} }
......
...@@ -561,7 +561,7 @@ struct Scsi_Host { ...@@ -561,7 +561,7 @@ struct Scsi_Host {
unsigned int host_failed; /* commands that failed. unsigned int host_failed; /* commands that failed.
protected by host_lock */ 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. */ unsigned int host_no; /* Used for IOCTL_GET_IDLUN, /proc/scsi et al. */
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册