Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -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
+1 −0
Original line number Original line Diff line number Diff line
@@ -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;
+17 −5
Original line number Original line Diff line number Diff line
@@ -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
+161 −11
Original line number Original line Diff line number Diff line
@@ -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,
	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[] = {
	__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)
		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)
	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)
	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)
	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)
		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)
	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)
	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)
		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)
	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:
+7 −0
Original line number Original line Diff line number Diff line
@@ -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 {
	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 {
	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)


Loading