diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-14 12:25:38 +0900 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-14 12:25:38 +0900 |
commit | 0d522ee7499e4abe7189b2f1728e838959b8ddde (patch) | |
tree | 094f60e8bdb0083b58a43a0decbca59443a66e39 /drivers/scsi/sd.c | |
parent | 5eea9be8b2478dbc2f9fb1cd6228a90e8dba804e (diff) | |
parent | 323f6226a816f0b01514d25fba5529e0e68636c3 (diff) |
Merge tag 'scsi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
Pull first round of SCSI updates from James Bottomley:
"This patch set is driver updates for qla4xxx, scsi_debug, pm80xx,
fcoe/libfc, eas2r, lpfc, be2iscsi and megaraid_sas plus some assorted
bug fixes and cleanups"
* tag 'scsi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi: (106 commits)
[SCSI] scsi_error: Escalate to LUN reset if abort fails
[SCSI] Add 'eh_deadline' to limit SCSI EH runtime
[SCSI] remove check for 'resetting'
[SCSI] dc395: Move 'last_reset' into internal host structure
[SCSI] tmscsim: Move 'last_reset' into host structure
[SCSI] advansys: Remove 'last_reset' references
[SCSI] dpt_i2o: return SCSI_MLQUEUE_HOST_BUSY when in reset
[SCSI] dpt_i2o: Remove DPTI_STATE_IOCTL
[SCSI] megaraid_sas: Fix synchronization problem between sysPD IO path and AEN path
[SCSI] lpfc: Fix typo on NULL assignment
[SCSI] scsi_dh_alua: ALUA handler attach should succeed while TPG is transitioning
[SCSI] scsi_dh_alua: ALUA check sense should retry device internal reset unit attention
[SCSI] esas2r: Cleanup snprinf formatting of firmware version
[SCSI] esas2r: Remove superfluous mask of pcie_cap_reg
[SCSI] esas2r: Fixes for big-endian platforms
[SCSI] esas2r: Directly call kernel functions for atomic bit operations
[SCSI] lpfc 8.3.43: Update lpfc version to driver version 8.3.43
[SCSI] lpfc 8.3.43: Fixed not processing task management IOCB response status
[SCSI] lpfc 8.3.43: Fixed spinlock hang.
[SCSI] lpfc 8.3.43: Fixed invalid Total_Data_Placed value received for els and ct command responses
...
Diffstat (limited to 'drivers/scsi/sd.c')
-rw-r--r-- | drivers/scsi/sd.c | 83 |
1 files changed, 66 insertions, 17 deletions
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 7fe4faaa149b..e6c4bff04339 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -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; @@ -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); |