diff options
Diffstat (limited to 'drivers/scsi/sd.c')
-rw-r--r-- | drivers/scsi/sd.c | 190 |
1 files changed, 124 insertions, 66 deletions
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 0dc95e102e69..b6bb4e0ce0e3 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -115,6 +115,7 @@ 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 void sd_eh_reset(struct scsi_cmnd *); 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); @@ -573,6 +574,7 @@ static struct scsi_driver sd_template = { .uninit_command = sd_uninit_command, .done = sd_done, .eh_action = sd_eh_action, + .eh_reset = sd_eh_reset, }; /* @@ -825,21 +827,32 @@ static int sd_setup_write_zeroes_cmnd(struct scsi_cmnd *cmd) struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); u64 sector = blk_rq_pos(rq) >> (ilog2(sdp->sector_size) - 9); u32 nr_sectors = blk_rq_sectors(rq) >> (ilog2(sdp->sector_size) - 9); + int ret; if (!(rq->cmd_flags & REQ_NOUNMAP)) { switch (sdkp->zeroing_mode) { case SD_ZERO_WS16_UNMAP: - return sd_setup_write_same16_cmnd(cmd, true); + ret = sd_setup_write_same16_cmnd(cmd, true); + goto out; case SD_ZERO_WS10_UNMAP: - return sd_setup_write_same10_cmnd(cmd, true); + ret = sd_setup_write_same10_cmnd(cmd, true); + goto out; } } if (sdp->no_write_same) return BLKPREP_INVALID; + if (sdkp->ws16 || sector > 0xffffffff || nr_sectors > 0xffff) - return sd_setup_write_same16_cmnd(cmd, false); - return sd_setup_write_same10_cmnd(cmd, false); + ret = sd_setup_write_same16_cmnd(cmd, false); + else + ret = sd_setup_write_same10_cmnd(cmd, false); + +out: + if (sd_is_zoned(sdkp) && ret == BLKPREP_OK) + return sd_zbc_write_lock_zone(cmd); + + return ret; } static void sd_config_write_same(struct scsi_disk *sdkp) @@ -888,8 +901,8 @@ out: * sd_setup_write_same_cmnd - write the same data to multiple blocks * @cmd: command to prepare * - * Will issue either WRITE SAME(10) or WRITE SAME(16) depending on - * preference indicated by target device. + * Will set up either WRITE SAME(10) or WRITE SAME(16) depending on + * the preference indicated by the target device. **/ static int sd_setup_write_same_cmnd(struct scsi_cmnd *cmd) { @@ -908,7 +921,7 @@ static int sd_setup_write_same_cmnd(struct scsi_cmnd *cmd) BUG_ON(bio_offset(bio) || bio_iovec(bio).bv_len != sdp->sector_size); if (sd_is_zoned(sdkp)) { - ret = sd_zbc_setup_write_cmnd(cmd); + ret = sd_zbc_write_lock_zone(cmd); if (ret != BLKPREP_OK) return ret; } @@ -946,6 +959,10 @@ static int sd_setup_write_same_cmnd(struct scsi_cmnd *cmd) rq->__data_len = sdp->sector_size; ret = scsi_init_io(cmd); rq->__data_len = nr_bytes; + + if (sd_is_zoned(sdkp) && ret != BLKPREP_OK) + sd_zbc_write_unlock_zone(cmd); + return ret; } @@ -980,7 +997,7 @@ static int sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) unsigned char protect; if (zoned_write) { - ret = sd_zbc_setup_write_cmnd(SCpnt); + ret = sd_zbc_write_lock_zone(SCpnt); if (ret != BLKPREP_OK) return ret; } @@ -1207,7 +1224,7 @@ static int sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) ret = BLKPREP_OK; out: if (zoned_write && ret != BLKPREP_OK) - sd_zbc_cancel_write_cmnd(SCpnt); + sd_zbc_write_unlock_zone(SCpnt); return ret; } @@ -1264,8 +1281,8 @@ static void sd_uninit_command(struct scsi_cmnd *SCpnt) /** * sd_open - open a scsi disk device - * @inode: only i_rdev member may be used - * @filp: only f_mode and f_flags may be used + * @bdev: Block device of the scsi disk to open + * @mode: FMODE_* mask * * Returns 0 if successful. Returns a negated errno value in case * of error. @@ -1341,8 +1358,8 @@ error_out: /** * sd_release - invoked when the (last) close(2) is called on this * scsi disk. - * @inode: only i_rdev member may be used - * @filp: only f_mode and f_flags may be used + * @disk: disk to release + * @mode: FMODE_* mask * * Returns 0. * @@ -1398,8 +1415,8 @@ static int sd_getgeo(struct block_device *bdev, struct hd_geometry *geo) /** * sd_ioctl - process an ioctl - * @inode: only i_rdev/i_bdev members may be used - * @filp: only f_mode and f_flags may be used + * @bdev: target block device + * @mode: FMODE_* mask * @cmd: ioctl command number * @arg: this is third argument given to ioctl(2) system call. * Often contains a pointer. @@ -1565,17 +1582,21 @@ out: return retval; } -static int sd_sync_cache(struct scsi_disk *sdkp) +static int sd_sync_cache(struct scsi_disk *sdkp, struct scsi_sense_hdr *sshdr) { 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; + struct scsi_sense_hdr my_sshdr; if (!scsi_device_online(sdp)) return -ENODEV; + /* caller might not be interested in sense, but we need it */ + if (!sshdr) + sshdr = &my_sshdr; + for (retries = 3; retries > 0; --retries) { unsigned char cmd[10] = { 0 }; @@ -1584,7 +1605,7 @@ static int sd_sync_cache(struct scsi_disk *sdkp) * Leave the rest of the command zero to indicate * flush everything. */ - res = scsi_execute(sdp, cmd, DMA_NONE, NULL, 0, NULL, &sshdr, + res = scsi_execute(sdp, cmd, DMA_NONE, NULL, 0, NULL, sshdr, timeout, SD_MAX_RETRIES, 0, RQF_PM, NULL); if (res == 0) break; @@ -1594,11 +1615,12 @@ static int sd_sync_cache(struct scsi_disk *sdkp) sd_print_result(sdkp, "Synchronize Cache(10) failed", res); if (driver_byte(res) & DRIVER_SENSE) - sd_print_sense_hdr(sdkp, &sshdr); + 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 */ + if (scsi_sense_valid(sshdr) && + (sshdr->asc == 0x3a || /* medium not present */ + sshdr->asc == 0x20)) /* invalid command */ /* this is no error here */ return 0; @@ -1762,6 +1784,26 @@ static const struct block_device_operations sd_fops = { }; /** + * sd_eh_reset - reset error handling callback + * @scmd: sd-issued command that has failed + * + * This function is called by the SCSI midlayer before starting + * SCSI EH. When counting medium access failures we have to be + * careful to register it only only once per device and SCSI EH run; + * there might be several timed out commands which will cause the + * 'max_medium_access_timeouts' counter to trigger after the first + * SCSI EH run already and set the device to offline. + * So this function resets the internal counter before starting SCSI EH. + **/ +static void sd_eh_reset(struct scsi_cmnd *scmd) +{ + struct scsi_disk *sdkp = scsi_disk(scmd->request->rq_disk); + + /* New SCSI EH run, reset gate variable */ + sdkp->ignore_medium_access_errors = false; +} + +/** * sd_eh_action - error handling callback * @scmd: sd-issued command that has failed * @eh_disp: The recovery disposition suggested by the midlayer @@ -1790,7 +1832,10 @@ static int sd_eh_action(struct scsi_cmnd *scmd, int eh_disp) * process of recovering or has it suffered an internal failure * that prevents access to the storage medium. */ - sdkp->medium_access_timed_out++; + if (!sdkp->ignore_medium_access_errors) { + sdkp->medium_access_timed_out++; + sdkp->ignore_medium_access_errors = true; + } /* * If the device keeps failing read/write commands but TEST UNIT @@ -1802,7 +1847,7 @@ static int sd_eh_action(struct scsi_cmnd *scmd, int eh_disp) "Medium access timeout failure. Offlining disk!\n"); scsi_device_set_state(scmd->device, SDEV_OFFLINE); - return FAILED; + return SUCCESS; } return eh_disp; @@ -1810,41 +1855,44 @@ static int sd_eh_action(struct scsi_cmnd *scmd, int eh_disp) static unsigned int sd_completed_bytes(struct scsi_cmnd *scmd) { - u64 start_lba = blk_rq_pos(scmd->request); - u64 end_lba = blk_rq_pos(scmd->request) + (scsi_bufflen(scmd) / 512); - u64 factor = scmd->device->sector_size / 512; - u64 bad_lba; - int info_valid; + struct request *req = scmd->request; + struct scsi_device *sdev = scmd->device; + unsigned int transferred, good_bytes; + u64 start_lba, end_lba, bad_lba; + /* - * resid is optional but mostly filled in. When it's unused, - * its value is zero, so we assume the whole buffer transferred + * Some commands have a payload smaller than the device logical + * block size (e.g. INQUIRY on a 4K disk). */ - unsigned int transferred = scsi_bufflen(scmd) - scsi_get_resid(scmd); - unsigned int good_bytes; - - info_valid = scsi_get_sense_info_fld(scmd->sense_buffer, - SCSI_SENSE_BUFFERSIZE, - &bad_lba); - if (!info_valid) + if (scsi_bufflen(scmd) <= sdev->sector_size) return 0; - if (scsi_bufflen(scmd) <= scmd->device->sector_size) + /* Check if we have a 'bad_lba' information */ + if (!scsi_get_sense_info_fld(scmd->sense_buffer, + SCSI_SENSE_BUFFERSIZE, + &bad_lba)) return 0; - /* be careful ... don't want any overflows */ - do_div(start_lba, factor); - do_div(end_lba, factor); - - /* The bad lba was reported incorrectly, we have no idea where + /* + * If the bad lba was reported incorrectly, we have no idea where * the error is. */ - if (bad_lba < start_lba || bad_lba >= end_lba) + start_lba = sectors_to_logical(sdev, blk_rq_pos(req)); + end_lba = start_lba + bytes_to_logical(sdev, scsi_bufflen(scmd)); + if (bad_lba < start_lba || bad_lba >= end_lba) return 0; - /* This computation should always be done in terms of - * the resolution of the device's medium. + /* + * resid is optional but mostly filled in. When it's unused, + * its value is zero, so we assume the whole buffer transferred + */ + transferred = scsi_bufflen(scmd) - scsi_get_resid(scmd); + + /* This computation should always be done in terms of the + * resolution of the device's medium. */ - good_bytes = (bad_lba - start_lba) * scmd->device->sector_size; + good_bytes = logical_to_bytes(sdev, bad_lba - start_lba); + return min(good_bytes, transferred); } @@ -1866,8 +1914,6 @@ static int sd_done(struct scsi_cmnd *SCpnt) struct request *req = SCpnt->request; int sense_valid = 0; int sense_deferred = 0; - unsigned char op = SCpnt->cmnd[0]; - unsigned char unmap = SCpnt->cmnd[1] & 8; switch (req_op(req)) { case REQ_OP_DISCARD: @@ -1941,26 +1987,27 @@ static int sd_done(struct scsi_cmnd *SCpnt) good_bytes = sd_completed_bytes(SCpnt); break; case ILLEGAL_REQUEST: - if (sshdr.asc == 0x10) /* DIX: Host detected corruption */ + switch (sshdr.asc) { + case 0x10: /* DIX: Host detected corruption */ good_bytes = sd_completed_bytes(SCpnt); - /* INVALID COMMAND OPCODE or INVALID FIELD IN CDB */ - if (sshdr.asc == 0x20 || sshdr.asc == 0x24) { - switch (op) { + break; + case 0x20: /* INVALID COMMAND OPCODE */ + case 0x24: /* INVALID FIELD IN CDB */ + switch (SCpnt->cmnd[0]) { case UNMAP: sd_config_discard(sdkp, SD_LBP_DISABLE); break; case WRITE_SAME_16: case WRITE_SAME: - if (unmap) + if (SCpnt->cmnd[1] & 8) { /* UNMAP */ sd_config_discard(sdkp, SD_LBP_DISABLE); - else { + } else { sdkp->device->no_write_same = 1; sd_config_write_same(sdkp); - - good_bytes = 0; req->__data_len = blk_rq_bytes(req); req->rq_flags |= RQF_QUIET; } + break; } } break; @@ -2798,7 +2845,7 @@ static void sd_read_app_tag_own(struct scsi_disk *sdkp, unsigned char *buffer) /** * sd_read_block_limits - Query disk device for preferred I/O sizes. - * @disk: disk to query + * @sdkp: disk to query */ static void sd_read_block_limits(struct scsi_disk *sdkp) { @@ -2864,7 +2911,7 @@ static void sd_read_block_limits(struct scsi_disk *sdkp) /** * sd_read_block_characteristics - Query block dev. characteristics - * @disk: disk to query + * @sdkp: disk to query */ static void sd_read_block_characteristics(struct scsi_disk *sdkp) { @@ -2912,7 +2959,7 @@ static void sd_read_block_characteristics(struct scsi_disk *sdkp) /** * sd_read_block_provisioning - Query provisioning VPD page - * @disk: disk to query + * @sdkp: disk to query */ static void sd_read_block_provisioning(struct scsi_disk *sdkp) { @@ -3417,7 +3464,7 @@ static void sd_shutdown(struct device *dev) if (sdkp->WCE && sdkp->media_present) { sd_printk(KERN_NOTICE, sdkp, "Synchronizing SCSI cache\n"); - sd_sync_cache(sdkp); + sd_sync_cache(sdkp, NULL); } if (system_state != SYSTEM_RESTART && sdkp->device->manage_start_stop) { @@ -3429,6 +3476,7 @@ static void sd_shutdown(struct device *dev) static int sd_suspend_common(struct device *dev, bool ignore_stop_errors) { struct scsi_disk *sdkp = dev_get_drvdata(dev); + struct scsi_sense_hdr sshdr; int ret = 0; if (!sdkp) /* E.g.: runtime suspend following sd_remove() */ @@ -3436,12 +3484,23 @@ static int sd_suspend_common(struct device *dev, bool ignore_stop_errors) if (sdkp->WCE && sdkp->media_present) { sd_printk(KERN_NOTICE, sdkp, "Synchronizing SCSI cache\n"); - ret = sd_sync_cache(sdkp); + ret = sd_sync_cache(sdkp, &sshdr); + if (ret) { /* ignore OFFLINE device */ if (ret == -ENODEV) - ret = 0; - goto done; + return 0; + + if (!scsi_sense_valid(&sshdr) || + sshdr.sense_key != ILLEGAL_REQUEST) + return ret; + + /* + * sshdr.sense_key == ILLEGAL_REQUEST means this drive + * doesn't support sync. There's not much to do and + * suspend shouldn't fail. + */ + ret = 0; } } @@ -3453,7 +3512,6 @@ static int sd_suspend_common(struct device *dev, bool ignore_stop_errors) ret = 0; } -done: return ret; } |