[PATCH] libata-dev: handle DRQ=1 ERR=1 (revised)
authorAlbert Lee <albertcc@tw.ibm.com>
Sat, 1 Apr 2006 09:38:43 +0000 (17:38 +0800)
committerJeff Garzik <jeff@garzik.org>
Sat, 1 Apr 2006 19:29:21 +0000 (14:29 -0500)
Handle DRQ=1 ERR=1 situation. Revised according to what IDE try_to_flush_leftover_data() does.

Changes:
 - For ATA PIO writes and ATAPI devices, just stop the HSM and let EH handle it.
 - For ATA PIO reads, read only one block of junk data and then let EH handle it.

Signed-off-by: Albert Lee <albertcc@tw.ibm.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
drivers/scsi/libata-core.c

index 6fc25f60db97386a5bb99299050972d5807ce64b..da13deccc0e0e2d2aa08aa3a2a394085dbff1c85 100644 (file)
@@ -3640,13 +3640,16 @@ fsm_start:
 
                /* Device should not ask for data transfer (DRQ=1)
                 * when it finds something wrong.
-                * Anyway, we respect DRQ here and let HSM go on
-                * without changing hsm_task_state to HSM_ST_ERR.
+                * We ignore DRQ here and stop the HSM by
+                * changing hsm_task_state to HSM_ST_ERR and
+                * let the EH abort the command or reset the device.
                 */
                if (unlikely(status & (ATA_ERR | ATA_DF))) {
                        printk(KERN_WARNING "ata%d: DRQ=1 with device error, dev_stat 0x%X\n",
                               ap->id, status);
                        qc->err_mask |= AC_ERR_DEV;
+                       ap->hsm_task_state = HSM_ST_ERR;
+                       goto fsm_start;
                }
 
                /* Send the CDB (atapi) or the first data block (ata pio out).
@@ -3693,13 +3696,16 @@ fsm_start:
 
                        /* Device should not ask for data transfer (DRQ=1)
                         * when it finds something wrong.
-                        * Anyway, we respect DRQ here and let HSM go on
-                        * without changing hsm_task_state to HSM_ST_ERR.
+                        * We ignore DRQ here and stop the HSM by
+                        * changing hsm_task_state to HSM_ST_ERR and
+                        * let the EH abort the command or reset the device.
                         */
                        if (unlikely(status & (ATA_ERR | ATA_DF))) {
                                printk(KERN_WARNING "ata%d: DRQ=1 with device error, dev_stat 0x%X\n",
                                       ap->id, status);
                                qc->err_mask |= AC_ERR_DEV;
+                               ap->hsm_task_state = HSM_ST_ERR;
+                               goto fsm_start;
                        }
 
                        atapi_pio_bytes(qc);
@@ -3717,20 +3723,32 @@ fsm_start:
                                goto fsm_start;
                        }
 
-                       /* Some devices may ask for data transfer (DRQ=1)
-                        * alone with ERR=1 for PIO reads.
-                        * We respect DRQ here and let HSM go on without
-                        * changing hsm_task_state to HSM_ST_ERR.
+                       /* For PIO reads, some devices may ask for
+                        * data transfer (DRQ=1) alone with ERR=1.
+                        * We respect DRQ here and transfer one
+                        * block of junk data before changing the
+                        * hsm_task_state to HSM_ST_ERR.
+                        *
+                        * For PIO writes, ERR=1 DRQ=1 doesn't make
+                        * sense since the data block has been
+                        * transferred to the device.
                         */
                        if (unlikely(status & (ATA_ERR | ATA_DF))) {
-                               /* For writes, ERR=1 DRQ=1 doesn't make
-                                * sense since the data block has been
-                                * transferred to the device.
-                                */
-                               WARN_ON(qc->tf.flags & ATA_TFLAG_WRITE);
-
                                /* data might be corrputed */
                                qc->err_mask |= AC_ERR_DEV;
+
+                               if (!(qc->tf.flags & ATA_TFLAG_WRITE)) {
+                                       ata_pio_sectors(qc);
+                                       ata_altstatus(ap);
+                                       status = ata_wait_idle(ap);
+                               }
+
+                               /* ata_pio_sectors() might change the
+                                * state to HSM_ST_LAST. so, the state
+                                * is changed after ata_pio_sectors().
+                                */
+                               ap->hsm_task_state = HSM_ST_ERR;
+                               goto fsm_start;
                        }
 
                        ata_pio_sectors(qc);