diff options
author | James Bottomley <James.Bottomley@SteelEye.com> | 2007-12-02 12:10:40 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-01-11 19:22:50 -0500 |
commit | 001aac257cf8adbe90cdcba6e07f8d12dfc8fa6b (patch) | |
tree | 0eb6294049245e05f47fdb76e3f878c78c015d88 | |
parent | 4a03d90e35bc5273d27301fa669d4b2103196f94 (diff) |
[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: James Bottomley <James.Bottomley@HansenPartnership.com>
-rw-r--r-- | drivers/scsi/scsi_ioctl.c | 2 | ||||
-rw-r--r-- | drivers/scsi/scsi_lib.c | 46 | ||||
-rw-r--r-- | drivers/scsi/sd.c | 13 | ||||
-rw-r--r-- | drivers/scsi/sr.c | 19 | ||||
-rw-r--r-- | include/scsi/scsi_device.h | 2 |
5 files changed, 63 insertions, 19 deletions
diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c index 83e144716901..28b19ef26309 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) | |||
244 | return scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW); | 244 | return scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW); |
245 | case SCSI_IOCTL_TEST_UNIT_READY: | 245 | case SCSI_IOCTL_TEST_UNIT_READY: |
246 | return scsi_test_unit_ready(sdev, IOCTL_NORMAL_TIMEOUT, | 246 | return scsi_test_unit_ready(sdev, IOCTL_NORMAL_TIMEOUT, |
247 | NORMAL_RETRIES); | 247 | NORMAL_RETRIES, NULL); |
248 | case SCSI_IOCTL_START_UNIT: | 248 | case SCSI_IOCTL_START_UNIT: |
249 | scsi_cmd[0] = START_STOP; | 249 | scsi_cmd[0] = START_STOP; |
250 | scsi_cmd[1] = 0; | 250 | scsi_cmd[1] = 0; |
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index aa17e718666e..db52222885b7 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, | |||
1973 | } | 1973 | } |
1974 | EXPORT_SYMBOL(scsi_mode_sense); | 1974 | EXPORT_SYMBOL(scsi_mode_sense); |
1975 | 1975 | ||
1976 | /** | ||
1977 | * scsi_test_unit_ready - test if unit is ready | ||
1978 | * @sdev: scsi device to change the state of. | ||
1979 | * @timeout: command timeout | ||
1980 | * @retries: number of retries before failing | ||
1981 | * @sshdr_external: Optional pointer to struct scsi_sense_hdr for | ||
1982 | * returning sense. Make sure that this is cleared before passing | ||
1983 | * in. | ||
1984 | * | ||
1985 | * Returns zero if unsuccessful or an error if TUR failed. For | ||
1986 | * removable media, a return of NOT_READY or UNIT_ATTENTION is | ||
1987 | * translated to success, with the ->changed flag updated. | ||
1988 | **/ | ||
1976 | int | 1989 | int |
1977 | scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries) | 1990 | scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries, |
1991 | struct scsi_sense_hdr *sshdr_external) | ||
1978 | { | 1992 | { |
1979 | char cmd[] = { | 1993 | char cmd[] = { |
1980 | TEST_UNIT_READY, 0, 0, 0, 0, 0, | 1994 | TEST_UNIT_READY, 0, 0, 0, 0, 0, |
1981 | }; | 1995 | }; |
1982 | struct scsi_sense_hdr sshdr; | 1996 | struct scsi_sense_hdr *sshdr; |
1983 | int result; | 1997 | int result; |
1984 | 1998 | ||
1985 | result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL, 0, &sshdr, | 1999 | if (!sshdr_external) |
1986 | timeout, retries); | 2000 | sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL); |
2001 | else | ||
2002 | sshdr = sshdr_external; | ||
2003 | |||
2004 | /* try to eat the UNIT_ATTENTION if there are enough retries */ | ||
2005 | do { | ||
2006 | result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL, 0, sshdr, | ||
2007 | timeout, retries); | ||
2008 | } while ((driver_byte(result) & DRIVER_SENSE) && | ||
2009 | sshdr && sshdr->sense_key == UNIT_ATTENTION && | ||
2010 | --retries); | ||
2011 | |||
2012 | if (!sshdr) | ||
2013 | /* could not allocate sense buffer, so can't process it */ | ||
2014 | return result; | ||
1987 | 2015 | ||
1988 | if ((driver_byte(result) & DRIVER_SENSE) && sdev->removable) { | 2016 | if ((driver_byte(result) & DRIVER_SENSE) && sdev->removable) { |
1989 | 2017 | ||
1990 | if ((scsi_sense_valid(&sshdr)) && | 2018 | if ((scsi_sense_valid(sshdr)) && |
1991 | ((sshdr.sense_key == UNIT_ATTENTION) || | 2019 | ((sshdr->sense_key == UNIT_ATTENTION) || |
1992 | (sshdr.sense_key == NOT_READY))) { | 2020 | (sshdr->sense_key == NOT_READY))) { |
1993 | sdev->changed = 1; | 2021 | sdev->changed = 1; |
1994 | result = 0; | 2022 | result = 0; |
1995 | } | 2023 | } |
1996 | } | 2024 | } |
2025 | if (!sshdr_external) | ||
2026 | kfree(sshdr); | ||
1997 | return result; | 2027 | return result; |
1998 | } | 2028 | } |
1999 | EXPORT_SYMBOL(scsi_test_unit_ready); | 2029 | EXPORT_SYMBOL(scsi_test_unit_ready); |
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 18343a6acd8e..212f6bcfd457 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c | |||
@@ -736,6 +736,7 @@ static int sd_media_changed(struct gendisk *disk) | |||
736 | { | 736 | { |
737 | struct scsi_disk *sdkp = scsi_disk(disk); | 737 | struct scsi_disk *sdkp = scsi_disk(disk); |
738 | struct scsi_device *sdp = sdkp->device; | 738 | struct scsi_device *sdp = sdkp->device; |
739 | struct scsi_sense_hdr *sshdr = NULL; | ||
739 | int retval; | 740 | int retval; |
740 | 741 | ||
741 | SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_media_changed\n")); | 742 | 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 | */ | 767 | */ |
767 | retval = -ENODEV; | 768 | retval = -ENODEV; |
768 | 769 | ||
769 | if (scsi_block_when_processing_errors(sdp)) | 770 | if (scsi_block_when_processing_errors(sdp)) { |
770 | retval = scsi_test_unit_ready(sdp, SD_TIMEOUT, SD_MAX_RETRIES); | 771 | sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL); |
772 | retval = scsi_test_unit_ready(sdp, SD_TIMEOUT, SD_MAX_RETRIES, | ||
773 | sshdr); | ||
774 | } | ||
771 | 775 | ||
772 | /* | 776 | /* |
773 | * Unable to test, unit probably not ready. This usually | 777 | * Unable to test, unit probably not ready. This usually |
@@ -775,7 +779,9 @@ static int sd_media_changed(struct gendisk *disk) | |||
775 | * and we will figure it out later once the drive is | 779 | * and we will figure it out later once the drive is |
776 | * available again. | 780 | * available again. |
777 | */ | 781 | */ |
778 | if (retval) { | 782 | if (retval || (scsi_sense_valid(sshdr) && |
783 | /* 0x3a is medium not present */ | ||
784 | sshdr->asc == 0x3a)) { | ||
779 | set_media_not_present(sdkp); | 785 | set_media_not_present(sdkp); |
780 | retval = 1; | 786 | retval = 1; |
781 | goto out; | 787 | goto out; |
@@ -794,6 +800,7 @@ out: | |||
794 | if (retval != sdkp->previous_state) | 800 | if (retval != sdkp->previous_state) |
795 | sdev_evt_send_simple(sdp, SDEV_EVT_MEDIA_CHANGE, GFP_KERNEL); | 801 | sdev_evt_send_simple(sdp, SDEV_EVT_MEDIA_CHANGE, GFP_KERNEL); |
796 | sdkp->previous_state = retval; | 802 | sdkp->previous_state = retval; |
803 | kfree(sshdr); | ||
797 | return retval; | 804 | return retval; |
798 | } | 805 | } |
799 | 806 | ||
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 7702681d93f9..896be4ab285d 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) | |||
179 | { | 179 | { |
180 | struct scsi_cd *cd = cdi->handle; | 180 | struct scsi_cd *cd = cdi->handle; |
181 | int retval; | 181 | int retval; |
182 | struct scsi_sense_hdr *sshdr; | ||
182 | 183 | ||
183 | if (CDSL_CURRENT != slot) { | 184 | if (CDSL_CURRENT != slot) { |
184 | /* no changer support */ | 185 | /* no changer support */ |
185 | return -EINVAL; | 186 | return -EINVAL; |
186 | } | 187 | } |
187 | 188 | ||
188 | retval = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES); | 189 | sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL); |
189 | if (retval) { | 190 | retval = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, |
190 | /* Unable to test, unit probably not ready. This usually | 191 | sshdr); |
191 | * means there is no disc in the drive. Mark as changed, | 192 | if (retval || (scsi_sense_valid(sshdr) && |
192 | * and we will figure it out later once the drive is | 193 | /* 0x3a is medium not present */ |
193 | * available again. */ | 194 | sshdr->asc == 0x3a)) { |
195 | /* Media not present or unable to test, unit probably not | ||
196 | * ready. This usually means there is no disc in the drive. | ||
197 | * Mark as changed, and we will figure it out later once | ||
198 | * the drive is available again. | ||
199 | */ | ||
194 | cd->device->changed = 1; | 200 | cd->device->changed = 1; |
195 | /* This will force a flush, if called from check_disk_change */ | 201 | /* This will force a flush, if called from check_disk_change */ |
196 | retval = 1; | 202 | retval = 1; |
@@ -213,6 +219,7 @@ out: | |||
213 | sdev_evt_send_simple(cd->device, SDEV_EVT_MEDIA_CHANGE, | 219 | sdev_evt_send_simple(cd->device, SDEV_EVT_MEDIA_CHANGE, |
214 | GFP_KERNEL); | 220 | GFP_KERNEL); |
215 | cd->previous_state = retval; | 221 | cd->previous_state = retval; |
222 | kfree(sshdr); | ||
216 | 223 | ||
217 | return retval; | 224 | return retval; |
218 | } | 225 | } |
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 45bb12b54175..e0c645ac5014 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, | |||
295 | struct scsi_mode_data *data, | 295 | struct scsi_mode_data *data, |
296 | struct scsi_sense_hdr *); | 296 | struct scsi_sense_hdr *); |
297 | extern int scsi_test_unit_ready(struct scsi_device *sdev, int timeout, | 297 | extern int scsi_test_unit_ready(struct scsi_device *sdev, int timeout, |
298 | int retries); | 298 | int retries, struct scsi_sense_hdr *sshdr); |
299 | extern int scsi_device_set_state(struct scsi_device *sdev, | 299 | extern int scsi_device_set_state(struct scsi_device *sdev, |
300 | enum scsi_device_state state); | 300 | enum scsi_device_state state); |
301 | extern struct scsi_event *sdev_evt_alloc(enum scsi_device_event evt_type, | 301 | extern struct scsi_event *sdev_evt_alloc(enum scsi_device_event evt_type, |