summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/scsi/scsi_ioctl.c2
-rw-r--r--drivers/scsi/scsi_lib.c46
-rw-r--r--drivers/scsi/sd.c13
-rw-r--r--drivers/scsi/sr.c19
-rw-r--r--include/scsi/scsi_device.h2
5 files changed, 63 insertions, 19 deletions
diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c
index 83e14471690..28b19ef2630 100644
--- a/drivers/scsi/scsi_ioctl.c
+++ b/drivers/scsi/scsi_ioctl.c
@@ -244,7 +244,7 @@ int scsi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
return scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW);
case SCSI_IOCTL_TEST_UNIT_READY:
return scsi_test_unit_ready(sdev, IOCTL_NORMAL_TIMEOUT,
- NORMAL_RETRIES);
+ NORMAL_RETRIES, NULL);
case SCSI_IOCTL_START_UNIT:
scsi_cmd[0] = START_STOP;
scsi_cmd[1] = 0;
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index aa17e718666..db52222885b 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -1973,27 +1973,57 @@ scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage,
}
EXPORT_SYMBOL(scsi_mode_sense);
+/**
+ * scsi_test_unit_ready - test if unit is ready
+ * @sdev: scsi device to change the state of.
+ * @timeout: command timeout
+ * @retries: number of retries before failing
+ * @sshdr_external: Optional pointer to struct scsi_sense_hdr for
+ * returning sense. Make sure that this is cleared before passing
+ * in.
+ *
+ * Returns zero if unsuccessful or an error if TUR failed. For
+ * removable media, a return of NOT_READY or UNIT_ATTENTION is
+ * translated to success, with the ->changed flag updated.
+ **/
int
-scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries)
+scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries,
+ struct scsi_sense_hdr *sshdr_external)
{
char cmd[] = {
TEST_UNIT_READY, 0, 0, 0, 0, 0,
};
- struct scsi_sense_hdr sshdr;
+ struct scsi_sense_hdr *sshdr;
int result;
-
- result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL, 0, &sshdr,
- timeout, retries);
+
+ if (!sshdr_external)
+ sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL);
+ else
+ sshdr = sshdr_external;
+
+ /* try to eat the UNIT_ATTENTION if there are enough retries */
+ do {
+ result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL, 0, sshdr,
+ timeout, retries);
+ } while ((driver_byte(result) & DRIVER_SENSE) &&
+ sshdr && sshdr->sense_key == UNIT_ATTENTION &&
+ --retries);
+
+ if (!sshdr)
+ /* could not allocate sense buffer, so can't process it */
+ return result;
if ((driver_byte(result) & DRIVER_SENSE) && sdev->removable) {
- if ((scsi_sense_valid(&sshdr)) &&
- ((sshdr.sense_key == UNIT_ATTENTION) ||
- (sshdr.sense_key == NOT_READY))) {
+ if ((scsi_sense_valid(sshdr)) &&
+ ((sshdr->sense_key == UNIT_ATTENTION) ||
+ (sshdr->sense_key == NOT_READY))) {
sdev->changed = 1;
result = 0;
}
}
+ if (!sshdr_external)
+ kfree(sshdr);
return result;
}
EXPORT_SYMBOL(scsi_test_unit_ready);
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 18343a6acd8..212f6bcfd45 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -736,6 +736,7 @@ static int sd_media_changed(struct gendisk *disk)
{
struct scsi_disk *sdkp = scsi_disk(disk);
struct scsi_device *sdp = sdkp->device;
+ struct scsi_sense_hdr *sshdr = NULL;
int retval;
SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_media_changed\n"));
@@ -766,8 +767,11 @@ static int sd_media_changed(struct gendisk *disk)
*/
retval = -ENODEV;
- if (scsi_block_when_processing_errors(sdp))
- retval = scsi_test_unit_ready(sdp, SD_TIMEOUT, SD_MAX_RETRIES);
+ if (scsi_block_when_processing_errors(sdp)) {
+ sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL);
+ retval = scsi_test_unit_ready(sdp, SD_TIMEOUT, SD_MAX_RETRIES,
+ sshdr);
+ }
/*
* Unable to test, unit probably not ready. This usually
@@ -775,7 +779,9 @@ static int sd_media_changed(struct gendisk *disk)
* and we will figure it out later once the drive is
* available again.
*/
- if (retval) {
+ if (retval || (scsi_sense_valid(sshdr) &&
+ /* 0x3a is medium not present */
+ sshdr->asc == 0x3a)) {
set_media_not_present(sdkp);
retval = 1;
goto out;
@@ -794,6 +800,7 @@ out:
if (retval != sdkp->previous_state)
sdev_evt_send_simple(sdp, SDEV_EVT_MEDIA_CHANGE, GFP_KERNEL);
sdkp->previous_state = retval;
+ kfree(sshdr);
return retval;
}
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index 7702681d93f..896be4ab285 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -179,18 +179,24 @@ static int sr_media_change(struct cdrom_device_info *cdi, int slot)
{
struct scsi_cd *cd = cdi->handle;
int retval;
+ struct scsi_sense_hdr *sshdr;
if (CDSL_CURRENT != slot) {
/* no changer support */
return -EINVAL;
}
- retval = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES);
- if (retval) {
- /* Unable to test, unit probably not ready. This usually
- * means there is no disc in the drive. Mark as changed,
- * and we will figure it out later once the drive is
- * available again. */
+ sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL);
+ retval = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES,
+ sshdr);
+ if (retval || (scsi_sense_valid(sshdr) &&
+ /* 0x3a is medium not present */
+ sshdr->asc == 0x3a)) {
+ /* Media not present or unable to test, unit probably not
+ * ready. This usually means there is no disc in the drive.
+ * Mark as changed, and we will figure it out later once
+ * the drive is available again.
+ */
cd->device->changed = 1;
/* This will force a flush, if called from check_disk_change */
retval = 1;
@@ -213,6 +219,7 @@ out:
sdev_evt_send_simple(cd->device, SDEV_EVT_MEDIA_CHANGE,
GFP_KERNEL);
cd->previous_state = retval;
+ kfree(sshdr);
return retval;
}
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index 45bb12b5417..e0c645ac501 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -295,7 +295,7 @@ extern int scsi_mode_select(struct scsi_device *sdev, int pf, int sp,
struct scsi_mode_data *data,
struct scsi_sense_hdr *);
extern int scsi_test_unit_ready(struct scsi_device *sdev, int timeout,
- int retries);
+ int retries, struct scsi_sense_hdr *sshdr);
extern int scsi_device_set_state(struct scsi_device *sdev,
enum scsi_device_state state);
extern struct scsi_event *sdev_evt_alloc(enum scsi_device_event evt_type,