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);
.uninit_command = sd_uninit_command,
.done = sd_done,
.eh_action = sd_eh_action,
+ .eh_reset = sd_eh_reset,
};
/*
/**
* sd_setup_discard_cmnd - unmap blocks on thinly provisioned device
- * @sdp: scsi device to operate one
- * @rq: Request to prepare
+ * @cmd: command to prepare
*
- * Will issue either UNMAP or WRITE SAME(16) depending on preference
- * indicated by target device.
+ * Will set up either UNMAP, WRITE SAME(16) or WRITE SAME(10) depending
+ * on the provisioning mode of the target device.
**/
static int sd_setup_discard_cmnd(struct scsi_cmnd *cmd)
{
rq->special_vec.bv_len = len;
rq->rq_flags |= RQF_SPECIAL_PAYLOAD;
- rq->resid_len = len;
+ scsi_req(rq)->resid_len = len;
ret = scsi_init_io(cmd);
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)
{
if (rq->rq_flags & RQF_SPECIAL_PAYLOAD)
__free_page(rq->special_vec.bv_page);
- if (SCpnt->cmnd != rq->cmd) {
+ if (SCpnt->cmnd != scsi_req(rq)->cmd) {
mempool_free(SCpnt->cmnd, sd_cdb_pool);
SCpnt->cmnd = NULL;
SCpnt->cmd_len = 0;
/**
* 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.
/**
* 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.
*
/**
* 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.
{
struct scsi_disk *sdkp = scsi_disk_get(disk);
struct scsi_device *sdp;
- struct scsi_sense_hdr *sshdr = NULL;
int retval;
if (!sdkp)
* by sd_spinup_disk() from sd_revalidate_disk(), which happens whenever
* sd_revalidate() is called.
*/
- retval = -ENODEV;
-
if (scsi_block_when_processing_errors(sdp)) {
- sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL);
+ struct scsi_sense_hdr sshdr = { 0, };
+
retval = scsi_test_unit_ready(sdp, SD_TIMEOUT, SD_MAX_RETRIES,
- sshdr);
- }
+ &sshdr);
- /* failed to execute TUR, assume media not present */
- if (host_byte(retval)) {
- set_media_not_present(sdkp);
- goto out;
- }
+ /* failed to execute TUR, assume media not present */
+ if (host_byte(retval)) {
+ set_media_not_present(sdkp);
+ goto out;
+ }
- if (media_not_present(sdkp, sshdr))
- goto out;
+ if (media_not_present(sdkp, &sshdr))
+ goto out;
+ }
/*
* For removable scsi disk we have to recognise the presence
* Medium present state has changed in either direction.
* Device has indicated UNIT_ATTENTION.
*/
- kfree(sshdr);
retval = sdp->changed ? DISK_EVENT_MEDIA_CHANGE : 0;
sdp->changed = 0;
scsi_disk_put(sdkp);
* Leave the rest of the command zero to indicate
* flush everything.
*/
- res = scsi_execute_req_flags(sdp, cmd, DMA_NONE, NULL, 0,
- &sshdr, timeout, SD_MAX_RETRIES,
- NULL, 0, RQF_PM);
+ res = scsi_execute(sdp, cmd, DMA_NONE, NULL, 0, NULL, &sshdr,
+ timeout, SD_MAX_RETRIES, 0, RQF_PM, NULL);
if (res == 0)
break;
}
.pr_ops = &sd_pr_ops,
};
+/**
+ * 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
* 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
"Medium access timeout failure. Offlining disk!\n");
scsi_device_set_state(scmd->device, SDEV_OFFLINE);
- return FAILED;
+ return SUCCESS;
}
return 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;
-
- if (scmd->request->cmd_type != REQ_TYPE_FS)
+ if (scsi_bufflen(scmd) <= sdev->sector_size)
return 0;
- info_valid = scsi_get_sense_info_fld(scmd->sense_buffer,
- SCSI_SENSE_BUFFERSIZE,
- &bad_lba);
- if (!info_valid)
+ /* Check if we have a 'bad_lba' information */
+ if (!scsi_get_sense_info_fld(scmd->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE,
+ &bad_lba))
return 0;
- if (scsi_bufflen(scmd) <= scmd->device->sector_size)
- 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);
}
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:
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;
/**
* 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)
{
/**
* 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)
{
/**
* 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)
{
* sd_remove - called whenever a scsi disk (previously recognized by
* sd_probe) is detached from the system. It is called (potentially
* multiple times) during sd module unload.
- * @sdp: pointer to mid level scsi device object
+ * @dev: pointer to device object
*
* Note: this function is invoked from the scsi mid-level.
* This function potentially frees up a device name (e.g. /dev/sdc)
if (!scsi_device_online(sdp))
return -ENODEV;
- res = scsi_execute_req_flags(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
- SD_TIMEOUT, SD_MAX_RETRIES, NULL, 0, RQF_PM);
+ res = scsi_execute(sdp, cmd, DMA_NONE, NULL, 0, NULL, &sshdr,
+ SD_TIMEOUT, SD_MAX_RETRIES, 0, RQF_PM, NULL);
if (res) {
sd_print_result(sdkp, "Start/Stop Unit failed", res);
if (driver_byte(res) & DRIVER_SENSE)