Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=001aac257cf8adbe90cdcba6e07f8d12dfc8fa6b
Commit:     001aac257cf8adbe90cdcba6e07f8d12dfc8fa6b
Parent:     4a03d90e35bc5273d27301fa669d4b2103196f94
Author:     James Bottomley <[EMAIL PROTECTED]>
AuthorDate: Sun Dec 2 19:10:40 2007 +0200
Committer:  James Bottomley <[EMAIL PROTECTED]>
CommitDate: Fri Jan 11 18:22:50 2008 -0600

    [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 <[EMAIL PROTECTED]>
---
 drivers/scsi/scsi_ioctl.c  |    2 +-
 drivers/scsi/scsi_lib.c    |   46 ++++++++++++++++++++++++++++++++++++-------
 drivers/scsi/sd.c          |   13 +++++++++--
 drivers/scsi/sr.c          |   19 ++++++++++++-----
 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 83e1447..28b19ef 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 aa17e71..db52222 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 18343a6..212f6bc 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 7702681..896be4a 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 45bb12b..e0c645a 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,
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to