aboutsummaryrefslogtreecommitdiff
path: root/drivers/scsi/sd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/sd.c')
-rw-r--r--drivers/scsi/sd.c228
1 files changed, 136 insertions, 92 deletions
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index e62d17d41d4..6825eda1114 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -105,11 +105,14 @@ 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_init_command(struct scsi_cmnd *SCpnt);
+static void sd_uninit_command(struct scsi_cmnd *SCpnt);
static int sd_done(struct scsi_cmnd *);
-static int sd_eh_action(struct scsi_cmnd *, unsigned char *, int, int);
+static int sd_eh_action(struct scsi_cmnd *, int);
static void sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer);
static void scsi_disk_release(struct device *cdev);
static void sd_print_sense_hdr(struct scsi_disk *, struct scsi_sense_hdr *);
@@ -484,11 +487,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,
};
@@ -502,6 +505,8 @@ static struct scsi_driver sd_template = {
.pm = &sd_pm_ops,
},
.rescan = sd_rescan,
+ .init_command = sd_init_command,
+ .uninit_command = sd_uninit_command,
.done = sd_done,
.eh_action = sd_eh_action,
};
@@ -736,16 +741,14 @@ static int sd_setup_discard_cmnd(struct scsi_device *sdp, struct request *rq)
goto out;
}
+ rq->completion_data = page;
blk_add_request_payload(rq, page, len);
ret = scsi_setup_blk_pc_cmnd(sdp, rq);
- rq->buffer = page_address(page);
rq->__data_len = nr_bytes;
out:
- if (ret != BLKPREP_OK) {
+ if (ret != BLKPREP_OK)
__free_page(page);
- rq->buffer = NULL;
- }
return ret;
}
@@ -800,7 +803,7 @@ static int sd_setup_write_same_cmnd(struct scsi_device *sdp, struct request *rq)
if (sdkp->device->no_write_same)
return BLKPREP_KILL;
- BUG_ON(bio_offset(bio) || bio_iovec(bio)->bv_len != sdp->sector_size);
+ BUG_ON(bio_offset(bio) || bio_iovec(bio).bv_len != sdp->sector_size);
sector >>= ilog2(sdp->sector_size) - 9;
nr_sectors >>= ilog2(sdp->sector_size) - 9;
@@ -829,7 +832,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;
@@ -837,14 +840,13 @@ static int scsi_setup_flush_cmnd(struct scsi_device *sdp, struct request *rq)
return scsi_setup_blk_pc_cmnd(sdp, rq);
}
-static void sd_unprep_fn(struct request_queue *q, struct request *rq)
+static void sd_uninit_command(struct scsi_cmnd *SCpnt)
{
- struct scsi_cmnd *SCpnt = rq->special;
+ struct request *rq = SCpnt->request;
+
+ if (rq->cmd_flags & REQ_DISCARD)
+ __free_page(rq->completion_data);
- if (rq->cmd_flags & REQ_DISCARD) {
- free_page((unsigned long)rq->buffer);
- rq->buffer = NULL;
- }
if (SCpnt->cmnd != rq->cmd) {
mempool_free(SCpnt->cmnd, sd_cdb_pool);
SCpnt->cmnd = NULL;
@@ -852,18 +854,10 @@ static void sd_unprep_fn(struct request_queue *q, struct request *rq)
}
}
-/**
- * sd_prep_fn - build a scsi (read or write) command from
- * information in the request structure.
- * @SCpnt: pointer to mid-level's per scsi command structure that
- * contains request and into which the scsi command is written
- *
- * Returns 1 if successful and 0 if error (or cannot be done now).
- **/
-static int sd_prep_fn(struct request_queue *q, struct request *rq)
+static int sd_init_command(struct scsi_cmnd *SCpnt)
{
- struct scsi_cmnd *SCpnt;
- struct scsi_device *sdp = q->queuedata;
+ struct request *rq = SCpnt->request;
+ struct scsi_device *sdp = SCpnt->device;
struct gendisk *disk = rq->rq_disk;
struct scsi_disk *sdkp;
sector_t block = blk_rq_pos(rq);
@@ -885,12 +879,6 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
} else if (rq->cmd_flags & REQ_FLUSH) {
ret = scsi_setup_flush_cmnd(sdp, rq);
goto out;
- } else if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
- ret = scsi_setup_blk_pc_cmnd(sdp, rq);
- goto out;
- } else if (rq->cmd_type != REQ_TYPE_FS) {
- ret = BLKPREP_KILL;
- goto out;
}
ret = scsi_setup_fs_cmnd(sdp, rq);
if (ret != BLKPREP_OK)
@@ -902,11 +890,10 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
* is used for a killable error condition */
ret = BLKPREP_KILL;
- SCSI_LOG_HLQUEUE(1, scmd_printk(KERN_INFO, SCpnt,
- "sd_prep_fn: block=%llu, "
- "count=%d\n",
- (unsigned long long)block,
- this_count));
+ SCSI_LOG_HLQUEUE(1,
+ scmd_printk(KERN_INFO, SCpnt,
+ "%s: block=%llu, count=%d\n",
+ __func__, (unsigned long long)block, this_count));
if (!sdp || !scsi_device_online(sdp) ||
block + blk_rq_sectors(rq) > get_capacity(disk)) {
@@ -1002,7 +989,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;
}
@@ -1126,7 +1113,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
*/
ret = BLKPREP_OK;
out:
- return scsi_prep_return(q, rq, ret);
+ return ret;
}
/**
@@ -1433,12 +1420,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 +1436,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) &&
+ (sshdr.asc == 0x3a || /* medium not present */
+ sshdr.asc == 0x20)) /* invalid command */
+ /* 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;
}
@@ -1530,23 +1537,23 @@ static const struct block_device_operations sd_fops = {
/**
* sd_eh_action - error handling callback
* @scmd: sd-issued command that has failed
- * @eh_cmnd: The command that was sent during error handling
- * @eh_cmnd_len: Length of eh_cmnd in bytes
* @eh_disp: The recovery disposition suggested by the midlayer
*
- * This function is called by the SCSI midlayer upon completion of
- * an error handling command (TEST UNIT READY, START STOP UNIT,
- * etc.) The command sent to the device by the error handler is
- * stored in eh_cmnd. The result of sending the eh command is
- * passed in eh_disp.
+ * This function is called by the SCSI midlayer upon completion of an
+ * error test command (currently TEST UNIT READY). The result of sending
+ * the eh command is passed in eh_disp. We're looking for devices that
+ * fail medium access commands but are OK with non access commands like
+ * test unit ready (so wrongly see the device as having a successful
+ * recovery)
**/
-static int sd_eh_action(struct scsi_cmnd *scmd, unsigned char *eh_cmnd,
- int eh_cmnd_len, int eh_disp)
+static int sd_eh_action(struct scsi_cmnd *scmd, int eh_disp)
{
struct scsi_disk *sdkp = scsi_disk(scmd->request->rq_disk);
if (!scsi_device_online(scmd->device) ||
- !scsi_medium_access_command(scmd))
+ !scsi_medium_access_command(scmd) ||
+ host_byte(scmd->result) != DID_TIME_OUT ||
+ eh_disp != SUCCESS)
return eh_disp;
/*
@@ -1556,9 +1563,7 @@ static int sd_eh_action(struct scsi_cmnd *scmd, unsigned char *eh_cmnd,
* process of recovering or has it suffered an internal failure
* that prevents access to the storage medium.
*/
- if (host_byte(scmd->result) == DID_TIME_OUT && eh_disp == SUCCESS &&
- eh_cmnd_len && eh_cmnd[0] == TEST_UNIT_READY)
- sdkp->medium_access_timed_out++;
+ sdkp->medium_access_timed_out++;
/*
* If the device keeps failing read/write commands but TEST UNIT
@@ -1607,7 +1612,7 @@ static unsigned int sd_completed_bytes(struct scsi_cmnd *scmd)
end_lba <<= 1;
} else {
/* be careful ... don't want any overflows */
- u64 factor = scmd->device->sector_size / 512;
+ unsigned int factor = scmd->device->sector_size / 512;
do_div(start_lba, factor);
do_div(end_lba, factor);
}
@@ -1670,12 +1675,12 @@ static int sd_done(struct scsi_cmnd *SCpnt)
sshdr.ascq));
}
#endif
+ sdkp->medium_access_timed_out = 0;
+
if (driver_byte(result) != DRIVER_SENSE &&
(!sense_valid || sense_deferred))
goto out;
- sdkp->medium_access_timed_out = 0;
-
switch (sshdr.sense_key) {
case HARDWARE_ERROR:
case MEDIUM_ERROR:
@@ -2262,7 +2267,7 @@ sd_read_write_protect_flag(struct scsi_disk *sdkp, unsigned char *buffer)
set_disk_ro(sdkp->disk, 0);
if (sdp->skip_ms_page_3f) {
- sd_printk(KERN_NOTICE, sdkp, "Assuming Write Enabled\n");
+ sd_first_printk(KERN_NOTICE, sdkp, "Assuming Write Enabled\n");
return;
}
@@ -2294,7 +2299,7 @@ sd_read_write_protect_flag(struct scsi_disk *sdkp, unsigned char *buffer)
}
if (!scsi_status_is_good(res)) {
- sd_printk(KERN_WARNING, sdkp,
+ sd_first_printk(KERN_WARNING, sdkp,
"Test WP failed, assume Write Enabled\n");
} else {
sdkp->write_prot = ((data.device_specific & 0x80) != 0);
@@ -2362,7 +2367,8 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer)
if (!data.header_length) {
modepage = 6;
first_len = 0;
- sd_printk(KERN_ERR, sdkp, "Missing header in MODE_SENSE response\n");
+ sd_first_printk(KERN_ERR, sdkp,
+ "Missing header in MODE_SENSE response\n");
}
/* that went OK, now ask for the proper length */
@@ -2375,7 +2381,7 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer)
if (len < 3)
goto bad_sense;
else if (len > SD_BUF_SIZE) {
- sd_printk(KERN_NOTICE, sdkp, "Truncating mode parameter "
+ sd_first_printk(KERN_NOTICE, sdkp, "Truncating mode parameter "
"data from %d to %d bytes\n", len, SD_BUF_SIZE);
len = SD_BUF_SIZE;
}
@@ -2398,8 +2404,9 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer)
/* We're interested only in the first 3 bytes.
*/
if (len - offset <= 2) {
- sd_printk(KERN_ERR, sdkp, "Incomplete "
- "mode parameter data\n");
+ sd_first_printk(KERN_ERR, sdkp,
+ "Incomplete mode parameter "
+ "data\n");
goto defaults;
} else {
modepage = page_code;
@@ -2413,14 +2420,15 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer)
else if (!spf && len - offset > 1)
offset += 2 + buffer[offset+1];
else {
- sd_printk(KERN_ERR, sdkp, "Incomplete "
- "mode parameter data\n");
+ sd_first_printk(KERN_ERR, sdkp,
+ "Incomplete mode "
+ "parameter data\n");
goto defaults;
}
}
}
- sd_printk(KERN_ERR, sdkp, "No Caching mode page found\n");
+ sd_first_printk(KERN_ERR, sdkp, "No Caching mode page found\n");
goto defaults;
Page_found:
@@ -2433,8 +2441,11 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer)
}
sdkp->DPOFUA = (data.device_specific & 0x10) != 0;
- if (sdkp->DPOFUA && !sdkp->device->use_10_for_rw) {
- sd_printk(KERN_NOTICE, sdkp,
+ if (sdp->broken_fua) {
+ sd_first_printk(KERN_NOTICE, sdkp, "Disabling FUA\n");
+ sdkp->DPOFUA = 0;
+ } else if (sdkp->DPOFUA && !sdkp->device->use_10_for_rw) {
+ sd_first_printk(KERN_NOTICE, sdkp,
"Uses READ/WRITE(6), disabling FUA\n");
sdkp->DPOFUA = 0;
}
@@ -2456,16 +2467,19 @@ bad_sense:
sshdr.sense_key == ILLEGAL_REQUEST &&
sshdr.asc == 0x24 && sshdr.ascq == 0x0)
/* Invalid field in CDB */
- sd_printk(KERN_NOTICE, sdkp, "Cache data unavailable\n");
+ sd_first_printk(KERN_NOTICE, sdkp, "Cache data unavailable\n");
else
- sd_printk(KERN_ERR, sdkp, "Asking for cache data failed\n");
+ sd_first_printk(KERN_ERR, sdkp,
+ "Asking for cache data failed\n");
defaults:
if (sdp->wce_default_on) {
- sd_printk(KERN_NOTICE, sdkp, "Assuming drive cache: write back\n");
+ sd_first_printk(KERN_NOTICE, sdkp,
+ "Assuming drive cache: write back\n");
sdkp->WCE = 1;
} else {
- sd_printk(KERN_ERR, sdkp, "Assuming drive cache: write through\n");
+ sd_first_printk(KERN_ERR, sdkp,
+ "Assuming drive cache: write through\n");
sdkp->WCE = 0;
}
sdkp->RCD = 0;
@@ -2494,7 +2508,7 @@ static void sd_read_app_tag_own(struct scsi_disk *sdkp, unsigned char *buffer)
if (!scsi_status_is_good(res) || !data.header_length ||
data.length < 6) {
- sd_printk(KERN_WARNING, sdkp,
+ sd_first_printk(KERN_WARNING, sdkp,
"getting Control mode page failed, assume no ATO\n");
if (scsi_sense_valid(&sshdr))
@@ -2506,7 +2520,7 @@ static void sd_read_app_tag_own(struct scsi_disk *sdkp, unsigned char *buffer)
offset = data.header_length + data.block_descriptor_length;
if ((buffer[offset] & 0x3f) != 0x0a) {
- sd_printk(KERN_ERR, sdkp, "ATO Got wrong page\n");
+ sd_first_printk(KERN_ERR, sdkp, "ATO Got wrong page\n");
return;
}
@@ -2638,14 +2652,23 @@ static void sd_read_write_same(struct scsi_disk *sdkp, unsigned char *buffer)
{
struct scsi_device *sdev = sdkp->device;
+ if (sdev->host->no_write_same) {
+ sdev->no_write_same = 1;
+
+ return;
+ }
+
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;
}
@@ -2844,9 +2867,6 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
sd_revalidate_disk(gd);
- blk_queue_prep_rq(sdp->request_queue, sd_prep_fn);
- blk_queue_unprep_rq(sdp->request_queue, sd_unprep_fn);
-
gd->driverfs_dev = &sdp->sdev_gendev;
gd->flags = GENHD_FL_EXT_DEVT;
if (sdp->removable) {
@@ -2854,6 +2874,7 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
gd->events |= DISK_EVENT_MEDIA_CHANGE;
}
+ blk_pm_runtime_init(sdp->request_queue, dev);
add_disk(gd);
if (sdkp->capacity)
sd_dif_config_host(sdkp);
@@ -2862,7 +2883,6 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n",
sdp->removable ? "removable " : "");
- blk_pm_runtime_init(sdp->request_queue, dev);
scsi_autopm_put_device(sdp);
put_device(&sdkp->dev);
}
@@ -2992,9 +3012,8 @@ static int sd_remove(struct device *dev)
devt = disk_devt(sdkp->disk);
scsi_autopm_get_device(sdkp->device);
+ async_synchronize_full_domain(&scsi_sd_pm_domain);
async_synchronize_full_domain(&scsi_sd_probe_domain);
- blk_queue_prep_rq(sdkp->device->request_queue, scsi_prep_fn);
- blk_queue_unprep_rq(sdkp->device->request_queue, NULL);
device_del(&sdkp->dev);
del_gendisk(sdkp->disk);
sd_shutdown(dev);
@@ -3058,9 +3077,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 +3105,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 +3119,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 +3127,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 +3151,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);