Merge branch 'pm-runtime'
[linux-2.6-microblaze.git] / drivers / scsi / sd.c
index 5693f6d..e6c4bff 100644 (file)
@@ -105,7 +105,8 @@ static void sd_unlock_native_capacity(struct gendisk *disk);
 static int  sd_probe(struct device *);
 static int  sd_remove(struct device *);
 static void sd_shutdown(struct device *);
-static int sd_suspend(struct device *);
+static int sd_suspend_system(struct device *);
+static int sd_suspend_runtime(struct device *);
 static int sd_resume(struct device *);
 static void sd_rescan(struct device *);
 static int sd_done(struct scsi_cmnd *);
@@ -484,11 +485,11 @@ static struct class sd_disk_class = {
 };
 
 static const struct dev_pm_ops sd_pm_ops = {
-       .suspend                = sd_suspend,
+       .suspend                = sd_suspend_system,
        .resume                 = sd_resume,
-       .poweroff               = sd_suspend,
+       .poweroff               = sd_suspend_system,
        .restore                = sd_resume,
-       .runtime_suspend        = sd_suspend,
+       .runtime_suspend        = sd_suspend_runtime,
        .runtime_resume         = sd_resume,
 };
 
@@ -829,7 +830,7 @@ static int sd_setup_write_same_cmnd(struct scsi_device *sdp, struct request *rq)
 
 static int scsi_setup_flush_cmnd(struct scsi_device *sdp, struct request *rq)
 {
-       rq->timeout = SD_FLUSH_TIMEOUT;
+       rq->timeout *= SD_FLUSH_TIMEOUT_MULTIPLIER;
        rq->retries = SD_MAX_RETRIES;
        rq->cmd[0] = SYNCHRONIZE_CACHE;
        rq->cmd_len = 10;
@@ -1002,7 +1003,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
                SCpnt->cmnd[0] = READ_6;
                SCpnt->sc_data_direction = DMA_FROM_DEVICE;
        } else {
-               scmd_printk(KERN_ERR, SCpnt, "Unknown command %x\n", rq->cmd_flags);
+               scmd_printk(KERN_ERR, SCpnt, "Unknown command %llx\n", (unsigned long long) rq->cmd_flags);
                goto out;
        }
 
@@ -1433,12 +1434,13 @@ static int sd_sync_cache(struct scsi_disk *sdkp)
 {
        int retries, res;
        struct scsi_device *sdp = sdkp->device;
+       const int timeout = sdp->request_queue->rq_timeout
+               * SD_FLUSH_TIMEOUT_MULTIPLIER;
        struct scsi_sense_hdr sshdr;
 
        if (!scsi_device_online(sdp))
                return -ENODEV;
 
-
        for (retries = 3; retries > 0; --retries) {
                unsigned char cmd[10] = { 0 };
 
@@ -1448,20 +1450,39 @@ static int sd_sync_cache(struct scsi_disk *sdkp)
                 * flush everything.
                 */
                res = scsi_execute_req_flags(sdp, cmd, DMA_NONE, NULL, 0,
-                                            &sshdr, SD_FLUSH_TIMEOUT,
-                                            SD_MAX_RETRIES, NULL, REQ_PM);
+                                            &sshdr, timeout, SD_MAX_RETRIES,
+                                            NULL, REQ_PM);
                if (res == 0)
                        break;
        }
 
        if (res) {
                sd_print_result(sdkp, res);
+
                if (driver_byte(res) & DRIVER_SENSE)
                        sd_print_sense_hdr(sdkp, &sshdr);
+               /* we need to evaluate the error return  */
+               if (scsi_sense_valid(&sshdr) &&
+                       /* 0x3a is medium not present */
+                       sshdr.asc == 0x3a)
+                               /* this is no error here */
+                               return 0;
+
+               switch (host_byte(res)) {
+               /* ignore errors due to racing a disconnection */
+               case DID_BAD_TARGET:
+               case DID_NO_CONNECT:
+                       return 0;
+               /* signal the upper layer it might try again */
+               case DID_BUS_BUSY:
+               case DID_IMM_RETRY:
+               case DID_REQUEUE:
+               case DID_SOFT_ERROR:
+                       return -EBUSY;
+               default:
+                       return -EIO;
+               }
        }
-
-       if (res)
-               return -EIO;
        return 0;
 }
 
@@ -2639,13 +2660,16 @@ static void sd_read_write_same(struct scsi_disk *sdkp, unsigned char *buffer)
        struct scsi_device *sdev = sdkp->device;
 
        if (scsi_report_opcode(sdev, buffer, SD_BUF_SIZE, INQUIRY) < 0) {
+               /* too large values might cause issues with arcmsr */
+               int vpd_buf_len = 64;
+
                sdev->no_report_opcodes = 1;
 
                /* Disable WRITE SAME if REPORT SUPPORTED OPERATION
                 * CODES is unsupported and the device has an ATA
                 * Information VPD page (SAT).
                 */
-               if (!scsi_get_vpd_page(sdev, 0x89, buffer, SD_BUF_SIZE))
+               if (!scsi_get_vpd_page(sdev, 0x89, buffer, vpd_buf_len))
                        sdev->no_write_same = 1;
        }
 
@@ -3058,9 +3082,17 @@ static int sd_start_stop_device(struct scsi_disk *sdkp, int start)
                sd_print_result(sdkp, res);
                if (driver_byte(res) & DRIVER_SENSE)
                        sd_print_sense_hdr(sdkp, &sshdr);
+               if (scsi_sense_valid(&sshdr) &&
+                       /* 0x3a is medium not present */
+                       sshdr.asc == 0x3a)
+                       res = 0;
        }
 
-       return res;
+       /* SCSI error codes must not go to the generic layer */
+       if (res)
+               return -EIO;
+
+       return 0;
 }
 
 /*
@@ -3078,7 +3110,7 @@ static void sd_shutdown(struct device *dev)
        if (pm_runtime_suspended(dev))
                goto exit;
 
-       if (sdkp->WCE) {
+       if (sdkp->WCE && sdkp->media_present) {
                sd_printk(KERN_NOTICE, sdkp, "Synchronizing SCSI cache\n");
                sd_sync_cache(sdkp);
        }
@@ -3092,7 +3124,7 @@ exit:
        scsi_disk_put(sdkp);
 }
 
-static int sd_suspend(struct device *dev)
+static int sd_suspend_common(struct device *dev, bool ignore_stop_errors)
 {
        struct scsi_disk *sdkp = scsi_disk_get_from_dev(dev);
        int ret = 0;
@@ -3100,16 +3132,23 @@ static int sd_suspend(struct device *dev)
        if (!sdkp)
                return 0;       /* this can happen */
 
-       if (sdkp->WCE) {
+       if (sdkp->WCE && sdkp->media_present) {
                sd_printk(KERN_NOTICE, sdkp, "Synchronizing SCSI cache\n");
                ret = sd_sync_cache(sdkp);
-               if (ret)
+               if (ret) {
+                       /* ignore OFFLINE device */
+                       if (ret == -ENODEV)
+                               ret = 0;
                        goto done;
+               }
        }
 
        if (sdkp->device->manage_start_stop) {
                sd_printk(KERN_NOTICE, sdkp, "Stopping disk\n");
+               /* an error is not worth aborting a system sleep */
                ret = sd_start_stop_device(sdkp, 0);
+               if (ignore_stop_errors)
+                       ret = 0;
        }
 
 done:
@@ -3117,6 +3156,16 @@ done:
        return ret;
 }
 
+static int sd_suspend_system(struct device *dev)
+{
+       return sd_suspend_common(dev, true);
+}
+
+static int sd_suspend_runtime(struct device *dev)
+{
+       return sd_suspend_common(dev, false);
+}
+
 static int sd_resume(struct device *dev)
 {
        struct scsi_disk *sdkp = scsi_disk_get_from_dev(dev);