|
@@ -79,6 +79,8 @@ enum {
|
|
|
*/
|
|
|
ATA_EH_PRERESET_TIMEOUT = 10000,
|
|
|
ATA_EH_FASTDRAIN_INTERVAL = 3000,
|
|
|
+
|
|
|
+ ATA_EH_UA_TRIES = 5,
|
|
|
};
|
|
|
|
|
|
/* The following table determines how we sequence resets. Each entry
|
|
@@ -1356,6 +1358,37 @@ static int ata_eh_read_log_10h(struct ata_device *dev,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * atapi_eh_tur - perform ATAPI TEST_UNIT_READY
|
|
|
+ * @dev: target ATAPI device
|
|
|
+ * @r_sense_key: out parameter for sense_key
|
|
|
+ *
|
|
|
+ * Perform ATAPI TEST_UNIT_READY.
|
|
|
+ *
|
|
|
+ * LOCKING:
|
|
|
+ * EH context (may sleep).
|
|
|
+ *
|
|
|
+ * RETURNS:
|
|
|
+ * 0 on success, AC_ERR_* mask on failure.
|
|
|
+ */
|
|
|
+static unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key)
|
|
|
+{
|
|
|
+ u8 cdb[ATAPI_CDB_LEN] = { TEST_UNIT_READY, 0, 0, 0, 0, 0 };
|
|
|
+ struct ata_taskfile tf;
|
|
|
+ unsigned int err_mask;
|
|
|
+
|
|
|
+ ata_tf_init(dev, &tf);
|
|
|
+
|
|
|
+ tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
|
|
|
+ tf.command = ATA_CMD_PACKET;
|
|
|
+ tf.protocol = ATAPI_PROT_NODATA;
|
|
|
+
|
|
|
+ err_mask = ata_exec_internal(dev, &tf, cdb, DMA_NONE, NULL, 0, 0);
|
|
|
+ if (err_mask == AC_ERR_DEV)
|
|
|
+ *r_sense_key = tf.feature >> 4;
|
|
|
+ return err_mask;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* atapi_eh_request_sense - perform ATAPI REQUEST_SENSE
|
|
|
* @dev: device to perform REQUEST_SENSE to
|
|
@@ -2774,6 +2807,53 @@ int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * atapi_eh_clear_ua - Clear ATAPI UNIT ATTENTION after reset
|
|
|
+ * @dev: ATAPI device to clear UA for
|
|
|
+ *
|
|
|
+ * Resets and other operations can make an ATAPI device raise
|
|
|
+ * UNIT ATTENTION which causes the next operation to fail. This
|
|
|
+ * function clears UA.
|
|
|
+ *
|
|
|
+ * LOCKING:
|
|
|
+ * EH context (may sleep).
|
|
|
+ *
|
|
|
+ * RETURNS:
|
|
|
+ * 0 on success, -errno on failure.
|
|
|
+ */
|
|
|
+static int atapi_eh_clear_ua(struct ata_device *dev)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < ATA_EH_UA_TRIES; i++) {
|
|
|
+ u8 sense_buffer[SCSI_SENSE_BUFFERSIZE];
|
|
|
+ u8 sense_key = 0;
|
|
|
+ unsigned int err_mask;
|
|
|
+
|
|
|
+ err_mask = atapi_eh_tur(dev, &sense_key);
|
|
|
+ if (err_mask != 0 && err_mask != AC_ERR_DEV) {
|
|
|
+ ata_dev_printk(dev, KERN_WARNING, "TEST_UNIT_READY "
|
|
|
+ "failed (err_mask=0x%x)\n", err_mask);
|
|
|
+ return -EIO;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!err_mask || sense_key != UNIT_ATTENTION)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ err_mask = atapi_eh_request_sense(dev, sense_buffer, sense_key);
|
|
|
+ if (err_mask) {
|
|
|
+ ata_dev_printk(dev, KERN_WARNING, "failed to clear "
|
|
|
+ "UNIT ATTENTION (err_mask=0x%x)\n", err_mask);
|
|
|
+ return -EIO;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ata_dev_printk(dev, KERN_WARNING,
|
|
|
+ "UNIT ATTENTION persists after %d tries\n", ATA_EH_UA_TRIES);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int ata_link_nr_enabled(struct ata_link *link)
|
|
|
{
|
|
|
struct ata_device *dev;
|
|
@@ -3066,6 +3146,20 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
|
|
|
ehc->i.flags &= ~ATA_EHI_SETMODE;
|
|
|
}
|
|
|
|
|
|
+ /* If reset has been issued, clear UA to avoid
|
|
|
+ * disrupting the current users of the device.
|
|
|
+ */
|
|
|
+ if (ehc->i.flags & ATA_EHI_DID_RESET) {
|
|
|
+ ata_link_for_each_dev(dev, link) {
|
|
|
+ if (dev->class != ATA_DEV_ATAPI)
|
|
|
+ continue;
|
|
|
+ rc = atapi_eh_clear_ua(dev);
|
|
|
+ if (rc)
|
|
|
+ goto dev_fail;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* configure link power saving */
|
|
|
if (ehc->i.action & ATA_EH_LPM)
|
|
|
ata_link_for_each_dev(dev, link)
|
|
|
ata_dev_enable_pm(dev, ap->pm_policy);
|