Commit 5db44863 authored by Martin K. Petersen's avatar Martin K. Petersen Committed by James Bottomley

[SCSI] sd: Implement support for WRITE SAME

Implement support for WRITE SAME(10) and WRITE SAME(16) in the SCSI disk
driver.

 - We set the default maximum to 0xFFFF because there are several
   devices out there that only support two-byte block counts even with
   WRITE SAME(16). We only enable transfers bigger than 0xFFFF if the
   device explicitly reports MAXIMUM WRITE SAME LENGTH in the BLOCK
   LIMITS VPD.

 - max_write_same_blocks can be overriden per-device basis in sysfs.

 - The UNMAP discovery heuristics remain unchanged but the discard
   limits are tweaked to match the "real" WRITE SAME commands.

 - In the error handling logic we now distinguish between WRITE SAME
   with and without UNMAP set.

The discovery process heuristics are:

 - If the device reports a SCSI level of SPC-3 or greater we'll issue
   READ SUPPORTED OPERATION CODES to find out whether WRITE SAME(16) is
   supported. If that's the case we will use it.

 - If the device supports the block limits VPD and reports a MAXIMUM
   WRITE SAME LENGTH bigger than 0xFFFF we will use WRITE SAME(16).

 - Otherwise we will use WRITE SAME(10) unless the target LBA is beyond
   0xFFFFFFFF or the block count exceeds 0xFFFF.

 - no_write_same is set for ATA, FireWire and USB.
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
Reviewed-by: default avatarMike Snitzer <snitzer@redhat.com>
Reviewed-by: default avatarJeff Garzik <jgarzik@redhat.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent 26e85fcd
...@@ -1053,6 +1053,7 @@ static void ata_scsi_sdev_config(struct scsi_device *sdev) ...@@ -1053,6 +1053,7 @@ static void ata_scsi_sdev_config(struct scsi_device *sdev)
sdev->use_10_for_rw = 1; sdev->use_10_for_rw = 1;
sdev->use_10_for_ms = 1; sdev->use_10_for_ms = 1;
sdev->no_report_opcodes = 1; sdev->no_report_opcodes = 1;
sdev->no_write_same = 1;
/* Schedule policy is determined by ->qc_defer() callback and /* Schedule policy is determined by ->qc_defer() callback and
* it needs to see every deferred qc. Set dev_blocked to 1 to * it needs to see every deferred qc. Set dev_blocked to 1 to
......
...@@ -1547,6 +1547,7 @@ static int sbp2_scsi_slave_configure(struct scsi_device *sdev) ...@@ -1547,6 +1547,7 @@ static int sbp2_scsi_slave_configure(struct scsi_device *sdev)
sdev->use_10_for_rw = 1; sdev->use_10_for_rw = 1;
sdev->no_report_opcodes = 1; sdev->no_report_opcodes = 1;
sdev->no_write_same = 1;
if (sbp2_param_exclusive_login) if (sbp2_param_exclusive_login)
sdev->manage_start_stop = 1; sdev->manage_start_stop = 1;
......
...@@ -900,11 +900,23 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) ...@@ -900,11 +900,23 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
action = ACTION_FAIL; action = ACTION_FAIL;
error = -EILSEQ; error = -EILSEQ;
/* INVALID COMMAND OPCODE or INVALID FIELD IN CDB */ /* INVALID COMMAND OPCODE or INVALID FIELD IN CDB */
} else if ((sshdr.asc == 0x20 || sshdr.asc == 0x24) && } else if (sshdr.asc == 0x20 || sshdr.asc == 0x24) {
(cmd->cmnd[0] == UNMAP || switch (cmd->cmnd[0]) {
cmd->cmnd[0] == WRITE_SAME_16 || case UNMAP:
cmd->cmnd[0] == WRITE_SAME)) {
description = "Discard failure"; description = "Discard failure";
break;
case WRITE_SAME:
case WRITE_SAME_16:
if (cmd->cmnd[1] & 0x8)
description = "Discard failure";
else
description =
"Write same failure";
break;
default:
description = "Invalid command failure";
break;
}
action = ACTION_FAIL; action = ACTION_FAIL;
error = -EREMOTEIO; error = -EREMOTEIO;
} else } else
......
...@@ -99,6 +99,7 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_RBC); ...@@ -99,6 +99,7 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_RBC);
#endif #endif
static void sd_config_discard(struct scsi_disk *, unsigned int); static void sd_config_discard(struct scsi_disk *, unsigned int);
static void sd_config_write_same(struct scsi_disk *);
static int sd_revalidate_disk(struct gendisk *); static int sd_revalidate_disk(struct gendisk *);
static void sd_unlock_native_capacity(struct gendisk *disk); static void sd_unlock_native_capacity(struct gendisk *disk);
static int sd_probe(struct device *); static int sd_probe(struct device *);
...@@ -395,6 +396,45 @@ sd_store_max_medium_access_timeouts(struct device *dev, ...@@ -395,6 +396,45 @@ sd_store_max_medium_access_timeouts(struct device *dev,
return err ? err : count; return err ? err : count;
} }
static ssize_t
sd_show_write_same_blocks(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct scsi_disk *sdkp = to_scsi_disk(dev);
return snprintf(buf, 20, "%u\n", sdkp->max_ws_blocks);
}
static ssize_t
sd_store_write_same_blocks(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct scsi_disk *sdkp = to_scsi_disk(dev);
struct scsi_device *sdp = sdkp->device;
unsigned long max;
int err;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (sdp->type != TYPE_DISK)
return -EINVAL;
err = kstrtoul(buf, 10, &max);
if (err)
return err;
if (max == 0)
sdp->no_write_same = 1;
else if (max <= SD_MAX_WS16_BLOCKS)
sdkp->max_ws_blocks = max;
sd_config_write_same(sdkp);
return count;
}
static struct device_attribute sd_disk_attrs[] = { static struct device_attribute sd_disk_attrs[] = {
__ATTR(cache_type, S_IRUGO|S_IWUSR, sd_show_cache_type, __ATTR(cache_type, S_IRUGO|S_IWUSR, sd_show_cache_type,
sd_store_cache_type), sd_store_cache_type),
...@@ -410,6 +450,8 @@ static struct device_attribute sd_disk_attrs[] = { ...@@ -410,6 +450,8 @@ static struct device_attribute sd_disk_attrs[] = {
__ATTR(thin_provisioning, S_IRUGO, sd_show_thin_provisioning, NULL), __ATTR(thin_provisioning, S_IRUGO, sd_show_thin_provisioning, NULL),
__ATTR(provisioning_mode, S_IRUGO|S_IWUSR, sd_show_provisioning_mode, __ATTR(provisioning_mode, S_IRUGO|S_IWUSR, sd_show_provisioning_mode,
sd_store_provisioning_mode), sd_store_provisioning_mode),
__ATTR(max_write_same_blocks, S_IRUGO|S_IWUSR,
sd_show_write_same_blocks, sd_store_write_same_blocks),
__ATTR(max_medium_access_timeouts, S_IRUGO|S_IWUSR, __ATTR(max_medium_access_timeouts, S_IRUGO|S_IWUSR,
sd_show_max_medium_access_timeouts, sd_show_max_medium_access_timeouts,
sd_store_max_medium_access_timeouts), sd_store_max_medium_access_timeouts),
...@@ -561,19 +603,23 @@ static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode) ...@@ -561,19 +603,23 @@ static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode)
return; return;
case SD_LBP_UNMAP: case SD_LBP_UNMAP:
max_blocks = min_not_zero(sdkp->max_unmap_blocks, 0xffffffff); max_blocks = min_not_zero(sdkp->max_unmap_blocks,
(u32)SD_MAX_WS16_BLOCKS);
break; break;
case SD_LBP_WS16: case SD_LBP_WS16:
max_blocks = min_not_zero(sdkp->max_ws_blocks, 0xffffffff); max_blocks = min_not_zero(sdkp->max_ws_blocks,
(u32)SD_MAX_WS16_BLOCKS);
break; break;
case SD_LBP_WS10: case SD_LBP_WS10:
max_blocks = min_not_zero(sdkp->max_ws_blocks, (u32)0xffff); max_blocks = min_not_zero(sdkp->max_ws_blocks,
(u32)SD_MAX_WS10_BLOCKS);
break; break;
case SD_LBP_ZERO: case SD_LBP_ZERO:
max_blocks = min_not_zero(sdkp->max_ws_blocks, (u32)0xffff); max_blocks = min_not_zero(sdkp->max_ws_blocks,
(u32)SD_MAX_WS10_BLOCKS);
q->limits.discard_zeroes_data = 1; q->limits.discard_zeroes_data = 1;
break; break;
} }
...@@ -667,6 +713,83 @@ static int sd_setup_discard_cmnd(struct scsi_device *sdp, struct request *rq) ...@@ -667,6 +713,83 @@ static int sd_setup_discard_cmnd(struct scsi_device *sdp, struct request *rq)
return ret; return ret;
} }
static void sd_config_write_same(struct scsi_disk *sdkp)
{
struct request_queue *q = sdkp->disk->queue;
unsigned int logical_block_size = sdkp->device->sector_size;
unsigned int blocks = 0;
if (sdkp->device->no_write_same) {
sdkp->max_ws_blocks = 0;
goto out;
}
/* Some devices can not handle block counts above 0xffff despite
* supporting WRITE SAME(16). Consequently we default to 64k
* blocks per I/O unless the device explicitly advertises a
* bigger limit.
*/
if (sdkp->max_ws_blocks == 0)
sdkp->max_ws_blocks = SD_MAX_WS10_BLOCKS;
if (sdkp->ws16 || sdkp->max_ws_blocks > SD_MAX_WS10_BLOCKS)
blocks = min_not_zero(sdkp->max_ws_blocks,
(u32)SD_MAX_WS16_BLOCKS);
else
blocks = min_not_zero(sdkp->max_ws_blocks,
(u32)SD_MAX_WS10_BLOCKS);
out:
blk_queue_max_write_same_sectors(q, blocks * (logical_block_size >> 9));
}
/**
* sd_setup_write_same_cmnd - write the same data to multiple blocks
* @sdp: scsi device to operate one
* @rq: Request to prepare
*
* Will issue either WRITE SAME(10) or WRITE SAME(16) depending on
* preference indicated by target device.
**/
static int sd_setup_write_same_cmnd(struct scsi_device *sdp, struct request *rq)
{
struct scsi_disk *sdkp = scsi_disk(rq->rq_disk);
struct bio *bio = rq->bio;
sector_t sector = blk_rq_pos(rq);
unsigned int nr_sectors = blk_rq_sectors(rq);
unsigned int nr_bytes = blk_rq_bytes(rq);
int ret;
if (sdkp->device->no_write_same)
return BLKPREP_KILL;
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;
rq->__data_len = sdp->sector_size;
rq->timeout = SD_WRITE_SAME_TIMEOUT;
memset(rq->cmd, 0, rq->cmd_len);
if (sdkp->ws16 || sector > 0xffffffff || nr_sectors > 0xffff) {
rq->cmd_len = 16;
rq->cmd[0] = WRITE_SAME_16;
put_unaligned_be64(sector, &rq->cmd[2]);
put_unaligned_be32(nr_sectors, &rq->cmd[10]);
} else {
rq->cmd_len = 10;
rq->cmd[0] = WRITE_SAME;
put_unaligned_be32(sector, &rq->cmd[2]);
put_unaligned_be16(nr_sectors, &rq->cmd[7]);
}
ret = scsi_setup_blk_pc_cmnd(sdp, rq);
rq->__data_len = nr_bytes;
return ret;
}
static int scsi_setup_flush_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;
...@@ -712,6 +835,9 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) ...@@ -712,6 +835,9 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
if (rq->cmd_flags & REQ_DISCARD) { if (rq->cmd_flags & REQ_DISCARD) {
ret = sd_setup_discard_cmnd(sdp, rq); ret = sd_setup_discard_cmnd(sdp, rq);
goto out; goto out;
} else if (rq->cmd_flags & REQ_WRITE_SAME) {
ret = sd_setup_write_same_cmnd(sdp, rq);
goto out;
} else if (rq->cmd_flags & REQ_FLUSH) { } else if (rq->cmd_flags & REQ_FLUSH) {
ret = scsi_setup_flush_cmnd(sdp, rq); ret = scsi_setup_flush_cmnd(sdp, rq);
goto out; goto out;
...@@ -1484,8 +1610,9 @@ static int sd_done(struct scsi_cmnd *SCpnt) ...@@ -1484,8 +1610,9 @@ static int sd_done(struct scsi_cmnd *SCpnt)
int sense_valid = 0; int sense_valid = 0;
int sense_deferred = 0; int sense_deferred = 0;
unsigned char op = SCpnt->cmnd[0]; unsigned char op = SCpnt->cmnd[0];
unsigned char unmap = SCpnt->cmnd[1] & 8;
if (req->cmd_flags & REQ_DISCARD) { if (req->cmd_flags & REQ_DISCARD || req->cmd_flags & REQ_WRITE_SAME) {
if (!result) { if (!result) {
good_bytes = blk_rq_bytes(req); good_bytes = blk_rq_bytes(req);
scsi_set_resid(SCpnt, 0); scsi_set_resid(SCpnt, 0);
...@@ -1542,10 +1669,26 @@ static int sd_done(struct scsi_cmnd *SCpnt) ...@@ -1542,10 +1669,26 @@ static int sd_done(struct scsi_cmnd *SCpnt)
if (sshdr.asc == 0x10) /* DIX: Host detected corruption */ if (sshdr.asc == 0x10) /* DIX: Host detected corruption */
good_bytes = sd_completed_bytes(SCpnt); good_bytes = sd_completed_bytes(SCpnt);
/* INVALID COMMAND OPCODE or INVALID FIELD IN CDB */ /* INVALID COMMAND OPCODE or INVALID FIELD IN CDB */
if ((sshdr.asc == 0x20 || sshdr.asc == 0x24) && if (sshdr.asc == 0x20 || sshdr.asc == 0x24) {
(op == UNMAP || op == WRITE_SAME_16 || op == WRITE_SAME)) switch (op) {
case UNMAP:
sd_config_discard(sdkp, SD_LBP_DISABLE); sd_config_discard(sdkp, SD_LBP_DISABLE);
break; break;
case WRITE_SAME_16:
case WRITE_SAME:
if (unmap)
sd_config_discard(sdkp, SD_LBP_DISABLE);
else {
sdkp->device->no_write_same = 1;
sd_config_write_same(sdkp);
good_bytes = 0;
req->__data_len = blk_rq_bytes(req);
req->cmd_flags |= REQ_QUIET;
}
}
}
break;
default: default:
break; break;
} }
...@@ -2380,9 +2523,7 @@ static void sd_read_block_limits(struct scsi_disk *sdkp) ...@@ -2380,9 +2523,7 @@ static void sd_read_block_limits(struct scsi_disk *sdkp)
if (buffer[3] == 0x3c) { if (buffer[3] == 0x3c) {
unsigned int lba_count, desc_count; unsigned int lba_count, desc_count;
sdkp->max_ws_blocks = sdkp->max_ws_blocks = (u32)get_unaligned_be64(&buffer[36]);
(u32) min_not_zero(get_unaligned_be64(&buffer[36]),
(u64)0xffffffff);
if (!sdkp->lbpme) if (!sdkp->lbpme)
goto out; goto out;
...@@ -2475,6 +2616,13 @@ static void sd_read_block_provisioning(struct scsi_disk *sdkp) ...@@ -2475,6 +2616,13 @@ static void sd_read_block_provisioning(struct scsi_disk *sdkp)
kfree(buffer); kfree(buffer);
} }
static void sd_read_write_same(struct scsi_disk *sdkp, unsigned char *buffer)
{
if (scsi_report_opcode(sdkp->device, buffer, SD_BUF_SIZE,
WRITE_SAME_16))
sdkp->ws16 = 1;
}
static int sd_try_extended_inquiry(struct scsi_device *sdp) static int sd_try_extended_inquiry(struct scsi_device *sdp)
{ {
/* /*
...@@ -2534,6 +2682,7 @@ static int sd_revalidate_disk(struct gendisk *disk) ...@@ -2534,6 +2682,7 @@ static int sd_revalidate_disk(struct gendisk *disk)
sd_read_write_protect_flag(sdkp, buffer); sd_read_write_protect_flag(sdkp, buffer);
sd_read_cache_type(sdkp, buffer); sd_read_cache_type(sdkp, buffer);
sd_read_app_tag_own(sdkp, buffer); sd_read_app_tag_own(sdkp, buffer);
sd_read_write_same(sdkp, buffer);
} }
sdkp->first_scan = 0; sdkp->first_scan = 0;
...@@ -2551,6 +2700,7 @@ static int sd_revalidate_disk(struct gendisk *disk) ...@@ -2551,6 +2700,7 @@ static int sd_revalidate_disk(struct gendisk *disk)
blk_queue_flush(sdkp->disk->queue, flush); blk_queue_flush(sdkp->disk->queue, flush);
set_capacity(disk, sdkp->capacity); set_capacity(disk, sdkp->capacity);
sd_config_write_same(sdkp);
kfree(buffer); kfree(buffer);
out: out:
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#define SD_TIMEOUT (30 * HZ) #define SD_TIMEOUT (30 * HZ)
#define SD_MOD_TIMEOUT (75 * HZ) #define SD_MOD_TIMEOUT (75 * HZ)
#define SD_FLUSH_TIMEOUT (60 * HZ) #define SD_FLUSH_TIMEOUT (60 * HZ)
#define SD_WRITE_SAME_TIMEOUT (120 * HZ)
/* /*
* Number of allowed retries * Number of allowed retries
...@@ -38,6 +39,11 @@ enum { ...@@ -38,6 +39,11 @@ enum {
SD_MEMPOOL_SIZE = 2, /* CDB pool size */ SD_MEMPOOL_SIZE = 2, /* CDB pool size */
}; };
enum {
SD_MAX_WS10_BLOCKS = 0xffff,
SD_MAX_WS16_BLOCKS = 0x7fffff,
};
enum { enum {
SD_LBP_FULL = 0, /* Full logical block provisioning */ SD_LBP_FULL = 0, /* Full logical block provisioning */
SD_LBP_UNMAP, /* Use UNMAP command */ SD_LBP_UNMAP, /* Use UNMAP command */
...@@ -77,6 +83,7 @@ struct scsi_disk { ...@@ -77,6 +83,7 @@ struct scsi_disk {
unsigned lbpws : 1; unsigned lbpws : 1;
unsigned lbpws10 : 1; unsigned lbpws10 : 1;
unsigned lbpvpd : 1; unsigned lbpvpd : 1;
unsigned ws16 : 1;
}; };
#define to_scsi_disk(obj) container_of(obj,struct scsi_disk,dev) #define to_scsi_disk(obj) container_of(obj,struct scsi_disk,dev)
......
...@@ -189,6 +189,9 @@ static int slave_configure(struct scsi_device *sdev) ...@@ -189,6 +189,9 @@ static int slave_configure(struct scsi_device *sdev)
/* Do not attempt to use REPORT SUPPORTED OPERATION CODES */ /* Do not attempt to use REPORT SUPPORTED OPERATION CODES */
sdev->no_report_opcodes = 1; sdev->no_report_opcodes = 1;
/* Do not attempt to use WRITE SAME */
sdev->no_write_same = 1;
/* Some disks return the total number of blocks in response /* Some disks return the total number of blocks in response
* to READ CAPACITY rather than the highest block number. * to READ CAPACITY rather than the highest block number.
* If this device makes that mistake, tell the sd driver. */ * If this device makes that mistake, tell the sd driver. */
......
...@@ -136,6 +136,7 @@ struct scsi_device { ...@@ -136,6 +136,7 @@ struct scsi_device {
unsigned use_10_for_rw:1; /* first try 10-byte read / write */ unsigned use_10_for_rw:1; /* first try 10-byte read / write */
unsigned use_10_for_ms:1; /* first try 10-byte mode sense/select */ unsigned use_10_for_ms:1; /* first try 10-byte mode sense/select */
unsigned no_report_opcodes:1; /* no REPORT SUPPORTED OPERATION CODES */ unsigned no_report_opcodes:1; /* no REPORT SUPPORTED OPERATION CODES */
unsigned no_write_same:1; /* no WRITE SAME command */
unsigned skip_ms_page_8:1; /* do not use MODE SENSE page 0x08 */ unsigned skip_ms_page_8:1; /* do not use MODE SENSE page 0x08 */
unsigned skip_ms_page_3f:1; /* do not use MODE SENSE page 0x3f */ unsigned skip_ms_page_3f:1; /* do not use MODE SENSE page 0x3f */
unsigned skip_vpd_pages:1; /* do not read VPD pages */ unsigned skip_vpd_pages:1; /* do not read VPD pages */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment