Commit 001aac25 authored by James Bottomley's avatar James Bottomley Committed by James Bottomley

[SCSI] sd,sr: add early detection of medium not present

The current scsi_test_unit_ready() is updated to return sense code
information (in struct scsi_sense_hdr).  The sd and sr drivers are
changed to interpret the sense code return asc 0x3a as no media and
adjust the device status accordingly.
Signed-off-by: default avatarJames Bottomley <James.Bottomley@HansenPartnership.com>
parent 4a03d90e
...@@ -244,7 +244,7 @@ int scsi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) ...@@ -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); return scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW);
case SCSI_IOCTL_TEST_UNIT_READY: case SCSI_IOCTL_TEST_UNIT_READY:
return scsi_test_unit_ready(sdev, IOCTL_NORMAL_TIMEOUT, return scsi_test_unit_ready(sdev, IOCTL_NORMAL_TIMEOUT,
NORMAL_RETRIES); NORMAL_RETRIES, NULL);
case SCSI_IOCTL_START_UNIT: case SCSI_IOCTL_START_UNIT:
scsi_cmd[0] = START_STOP; scsi_cmd[0] = START_STOP;
scsi_cmd[1] = 0; scsi_cmd[1] = 0;
......
...@@ -1973,27 +1973,57 @@ scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage, ...@@ -1973,27 +1973,57 @@ scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage,
} }
EXPORT_SYMBOL(scsi_mode_sense); 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 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[] = { char cmd[] = {
TEST_UNIT_READY, 0, 0, 0, 0, 0, TEST_UNIT_READY, 0, 0, 0, 0, 0,
}; };
struct scsi_sense_hdr sshdr; struct scsi_sense_hdr *sshdr;
int result; int result;
result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL, 0, &sshdr, 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); 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 ((driver_byte(result) & DRIVER_SENSE) && sdev->removable) {
if ((scsi_sense_valid(&sshdr)) && if ((scsi_sense_valid(sshdr)) &&
((sshdr.sense_key == UNIT_ATTENTION) || ((sshdr->sense_key == UNIT_ATTENTION) ||
(sshdr.sense_key == NOT_READY))) { (sshdr->sense_key == NOT_READY))) {
sdev->changed = 1; sdev->changed = 1;
result = 0; result = 0;
} }
} }
if (!sshdr_external)
kfree(sshdr);
return result; return result;
} }
EXPORT_SYMBOL(scsi_test_unit_ready); EXPORT_SYMBOL(scsi_test_unit_ready);
......
...@@ -736,6 +736,7 @@ static int sd_media_changed(struct gendisk *disk) ...@@ -736,6 +736,7 @@ static int sd_media_changed(struct gendisk *disk)
{ {
struct scsi_disk *sdkp = scsi_disk(disk); struct scsi_disk *sdkp = scsi_disk(disk);
struct scsi_device *sdp = sdkp->device; struct scsi_device *sdp = sdkp->device;
struct scsi_sense_hdr *sshdr = NULL;
int retval; int retval;
SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_media_changed\n")); 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) ...@@ -766,8 +767,11 @@ static int sd_media_changed(struct gendisk *disk)
*/ */
retval = -ENODEV; retval = -ENODEV;
if (scsi_block_when_processing_errors(sdp)) if (scsi_block_when_processing_errors(sdp)) {
retval = scsi_test_unit_ready(sdp, SD_TIMEOUT, SD_MAX_RETRIES); 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 * Unable to test, unit probably not ready. This usually
...@@ -775,7 +779,9 @@ static int sd_media_changed(struct gendisk *disk) ...@@ -775,7 +779,9 @@ static int sd_media_changed(struct gendisk *disk)
* and we will figure it out later once the drive is * and we will figure it out later once the drive is
* available again. * available again.
*/ */
if (retval) { if (retval || (scsi_sense_valid(sshdr) &&
/* 0x3a is medium not present */
sshdr->asc == 0x3a)) {
set_media_not_present(sdkp); set_media_not_present(sdkp);
retval = 1; retval = 1;
goto out; goto out;
...@@ -794,6 +800,7 @@ out: ...@@ -794,6 +800,7 @@ out:
if (retval != sdkp->previous_state) if (retval != sdkp->previous_state)
sdev_evt_send_simple(sdp, SDEV_EVT_MEDIA_CHANGE, GFP_KERNEL); sdev_evt_send_simple(sdp, SDEV_EVT_MEDIA_CHANGE, GFP_KERNEL);
sdkp->previous_state = retval; sdkp->previous_state = retval;
kfree(sshdr);
return retval; return retval;
} }
......
...@@ -179,18 +179,24 @@ static int sr_media_change(struct cdrom_device_info *cdi, int slot) ...@@ -179,18 +179,24 @@ static int sr_media_change(struct cdrom_device_info *cdi, int slot)
{ {
struct scsi_cd *cd = cdi->handle; struct scsi_cd *cd = cdi->handle;
int retval; int retval;
struct scsi_sense_hdr *sshdr;
if (CDSL_CURRENT != slot) { if (CDSL_CURRENT != slot) {
/* no changer support */ /* no changer support */
return -EINVAL; return -EINVAL;
} }
retval = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES); sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL);
if (retval) { retval = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES,
/* Unable to test, unit probably not ready. This usually sshdr);
* means there is no disc in the drive. Mark as changed, if (retval || (scsi_sense_valid(sshdr) &&
* and we will figure it out later once the drive is /* 0x3a is medium not present */
* available again. */ 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; cd->device->changed = 1;
/* This will force a flush, if called from check_disk_change */ /* This will force a flush, if called from check_disk_change */
retval = 1; retval = 1;
...@@ -213,6 +219,7 @@ out: ...@@ -213,6 +219,7 @@ out:
sdev_evt_send_simple(cd->device, SDEV_EVT_MEDIA_CHANGE, sdev_evt_send_simple(cd->device, SDEV_EVT_MEDIA_CHANGE,
GFP_KERNEL); GFP_KERNEL);
cd->previous_state = retval; cd->previous_state = retval;
kfree(sshdr);
return retval; return retval;
} }
......
...@@ -295,7 +295,7 @@ extern int scsi_mode_select(struct scsi_device *sdev, int pf, int sp, ...@@ -295,7 +295,7 @@ extern int scsi_mode_select(struct scsi_device *sdev, int pf, int sp,
struct scsi_mode_data *data, struct scsi_mode_data *data,
struct scsi_sense_hdr *); struct scsi_sense_hdr *);
extern int scsi_test_unit_ready(struct scsi_device *sdev, int timeout, 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, extern int scsi_device_set_state(struct scsi_device *sdev,
enum scsi_device_state state); enum scsi_device_state state);
extern struct scsi_event *sdev_evt_alloc(enum scsi_device_event evt_type, extern struct scsi_event *sdev_evt_alloc(enum scsi_device_event evt_type,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment