]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - drivers/scsi/libata-core.c
[PATCH] libata-eh-fw: update ata_exec_internal() for new EH
[mirror_ubuntu-zesty-kernel.git] / drivers / scsi / libata-core.c
index 63857a90ac280a5150e980e52043c4d93c3d3556..4def48ed6f46c8ff4d7f7472bfc156d20e73f4d9 100644 (file)
@@ -984,6 +984,7 @@ unsigned ata_exec_internal(struct ata_device *dev,
        DECLARE_COMPLETION(wait);
        unsigned long flags;
        unsigned int err_mask;
+       int rc;
 
        spin_lock_irqsave(&ap->host_set->lock, flags);
 
@@ -1036,20 +1037,25 @@ unsigned ata_exec_internal(struct ata_device *dev,
 
        spin_unlock_irqrestore(&ap->host_set->lock, flags);
 
-       if (!wait_for_completion_timeout(&wait, ATA_TMOUT_INTERNAL)) {
-               ata_port_flush_task(ap);
+       rc = wait_for_completion_timeout(&wait, ATA_TMOUT_INTERNAL);
+
+       ata_port_flush_task(ap);
 
+       if (!rc) {
                spin_lock_irqsave(&ap->host_set->lock, flags);
 
                /* We're racing with irq here.  If we lose, the
                 * following test prevents us from completing the qc
-                * again.  If completion irq occurs after here but
-                * before the caller cleans up, it will result in a
-                * spurious interrupt.  We can live with that.
+                * twice.  If we win, the port is frozen and will be
+                * cleaned up by ->post_internal_cmd().
                 */
                if (qc->flags & ATA_QCFLAG_ACTIVE) {
-                       qc->err_mask = AC_ERR_TIMEOUT;
-                       ata_qc_complete(qc);
+                       qc->err_mask |= AC_ERR_TIMEOUT;
+
+                       if (ap->ops->error_handler)
+                               ata_port_freeze(ap);
+                       else
+                               ata_qc_complete(qc);
 
                        ata_dev_printk(dev, KERN_WARNING,
                                       "qc timeout (cmd 0x%x)\n", command);
@@ -1058,6 +1064,16 @@ unsigned ata_exec_internal(struct ata_device *dev,
                spin_unlock_irqrestore(&ap->host_set->lock, flags);
        }
 
+       /* do post_internal_cmd */
+       if (ap->ops->post_internal_cmd)
+               ap->ops->post_internal_cmd(qc);
+
+       if (qc->flags & ATA_QCFLAG_FAILED && !qc->err_mask) {
+               ata_dev_printk(dev, KERN_WARNING, "zero err_mask for failed "
+                              "internal command, assuming AC_ERR_OTHER\n");
+               qc->err_mask |= AC_ERR_OTHER;
+       }
+
        /* finish up */
        spin_lock_irqsave(&ap->host_set->lock, flags);
 
@@ -3430,16 +3446,31 @@ skip_map:
  *     LOCKING:
  *     None.  (grabs host lock)
  */
-
 void ata_poll_qc_complete(struct ata_queued_cmd *qc)
 {
        struct ata_port *ap = qc->ap;
        unsigned long flags;
 
        spin_lock_irqsave(&ap->host_set->lock, flags);
-       ap->flags &= ~ATA_FLAG_NOINTR;
-       ata_irq_on(ap);
-       ata_qc_complete(qc);
+
+       if (ap->ops->error_handler) {
+               /* EH might have kicked in while host_set lock is released */
+               qc = ata_qc_from_tag(ap, qc->tag);
+               if (qc) {
+                       if (!(qc->err_mask & AC_ERR_HSM)) {
+                               ap->flags &= ~ATA_FLAG_NOINTR;
+                               ata_irq_on(ap);
+                               ata_qc_complete(qc);
+                       } else
+                               ata_port_freeze(ap);
+               }
+       } else {
+               /* old EH */
+               ap->flags &= ~ATA_FLAG_NOINTR;
+               ata_irq_on(ap);
+               ata_qc_complete(qc);
+       }
+
        spin_unlock_irqrestore(&ap->host_set->lock, flags);
 }