All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2] scsi: sd: Separate zeroout and discard command choices
@ 2017-04-05 11:41 Martin K. Petersen
  2017-04-05 11:41 ` [PATCH 2/2] scsi: sd: Remove LBPRZ dependency for discards Martin K. Petersen
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Martin K. Petersen @ 2017-04-05 11:41 UTC (permalink / raw)
  To: linux-scsi; +Cc: Martin K. Petersen, Christoph Hellwig

Now that zeroout and discards are distinct operations we need to
separate the policy of choosing the appropriate command. Create a
zeroing_mode which can be one of:

write:			Zeroout assist not present, use regular WRITE
writesame:		Allow WRITE SAME(10/16) with a zeroed payload
writesame_16_unmap:	Allow WRITE SAME(16) with UNMAP
writesame_10_unmap:	Allow WRITE SAME(10) with UNMAP

The last two are conditional on the device being thin provisioned with
LBPRZ=1 and LBPWS=1 or LBPWS10=1 respectively.

Whether to set the UNMAP bit or not depends on the REQ_NOUNMAP flag. And
if none of the _unmap variants are supported, regular WRITE SAME will be
used if the device supports it.

The zeroout_mode is exported in sysfs and the detected mode for a given
device can be overridden using the string constants above.

With this change in place we can now issue WRITE SAME(16) with UNMAP set
for block zeroing applications that require hard guarantees and
logical_block_size granularity. And at the same time use the UNMAP
command with the device's preferred granulary and alignment for discard
operations.

Cc: Christoph Hellwig <hch@lst.de>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
---
 drivers/scsi/sd.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
 drivers/scsi/sd.h |  8 ++++++++
 2 files changed, 61 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index bcb0cb020fd2..acf9d17b05d8 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -418,6 +418,46 @@ provisioning_mode_store(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RW(provisioning_mode);
 
+static const char *zeroing_mode[] = {
+	[SD_ZERO_WRITE]		= "write",
+	[SD_ZERO_WS]		= "writesame",
+	[SD_ZERO_WS16_UNMAP]	= "writesame_16_unmap",
+	[SD_ZERO_WS10_UNMAP]	= "writesame_10_unmap",
+};
+
+static ssize_t
+zeroing_mode_show(struct device *dev, struct device_attribute *attr,
+		  char *buf)
+{
+	struct scsi_disk *sdkp = to_scsi_disk(dev);
+
+	return snprintf(buf, 20, "%s\n", zeroing_mode[sdkp->zeroing_mode]);
+}
+
+static ssize_t
+zeroing_mode_store(struct device *dev, struct device_attribute *attr,
+		   const char *buf, size_t count)
+{
+	struct scsi_disk *sdkp = to_scsi_disk(dev);
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	if (!strncmp(buf, zeroing_mode[SD_ZERO_WRITE], 20))
+		sdkp->zeroing_mode = SD_ZERO_WRITE;
+	else if (!strncmp(buf, zeroing_mode[SD_ZERO_WS], 20))
+		sdkp->zeroing_mode = SD_ZERO_WS;
+	else if (!strncmp(buf, zeroing_mode[SD_ZERO_WS16_UNMAP], 20))
+		sdkp->zeroing_mode = SD_ZERO_WS16_UNMAP;
+	else if (!strncmp(buf, zeroing_mode[SD_ZERO_WS10_UNMAP], 20))
+		sdkp->zeroing_mode = SD_ZERO_WS10_UNMAP;
+	else
+		return -EINVAL;
+
+	return count;
+}
+static DEVICE_ATTR_RW(zeroing_mode);
+
 static ssize_t
 max_medium_access_timeouts_show(struct device *dev,
 				struct device_attribute *attr, char *buf)
@@ -496,6 +536,7 @@ static struct attribute *sd_disk_attrs[] = {
 	&dev_attr_app_tag_own.attr,
 	&dev_attr_thin_provisioning.attr,
 	&dev_attr_provisioning_mode.attr,
+	&dev_attr_zeroing_mode.attr,
 	&dev_attr_max_write_same_blocks.attr,
 	&dev_attr_max_medium_access_timeouts.attr,
 	NULL,
@@ -799,10 +840,10 @@ static int sd_setup_write_zeroes_cmnd(struct scsi_cmnd *cmd)
 	u32 nr_sectors = blk_rq_sectors(rq) >> (ilog2(sdp->sector_size) - 9);
 
 	if (!(rq->cmd_flags & REQ_NOUNMAP)) {
-		switch (sdkp->provisioning_mode) {
-		case SD_LBP_WS16:
+		switch (sdkp->zeroing_mode) {
+		case SD_ZERO_WS16_UNMAP:
 			return sd_setup_write_same16_cmnd(cmd, true);
-		case SD_LBP_WS10:
+		case SD_ZERO_WS10_UNMAP:
 			return sd_setup_write_same10_cmnd(cmd, true);
 		}
 	}
@@ -840,6 +881,15 @@ static void sd_config_write_same(struct scsi_disk *sdkp)
 		sdkp->max_ws_blocks = 0;
 	}
 
+	if (sdkp->lbprz && sdkp->lbpws)
+		sdkp->zeroing_mode = SD_ZERO_WS16_UNMAP;
+	else if (sdkp->lbprz && sdkp->lbpws10)
+		sdkp->zeroing_mode = SD_ZERO_WS10_UNMAP;
+	else if (sdkp->max_ws_blocks)
+		sdkp->zeroing_mode = SD_ZERO_WS;
+	else
+		sdkp->zeroing_mode = SD_ZERO_WRITE;
+
 out:
 	blk_queue_max_write_same_sectors(q, sdkp->max_ws_blocks *
 					 (logical_block_size >> 9));
diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h
index 4dac35e96a75..a2c4b5c35379 100644
--- a/drivers/scsi/sd.h
+++ b/drivers/scsi/sd.h
@@ -59,6 +59,13 @@ enum {
 	SD_LBP_DISABLE,		/* Discard disabled due to failed cmd */
 };
 
+enum {
+	SD_ZERO_WRITE = 0,	/* Use WRITE(10/16) command */
+	SD_ZERO_WS,		/* Use WRITE SAME(10/16) command */
+	SD_ZERO_WS16_UNMAP,	/* Use WRITE SAME(16) with UNMAP */
+	SD_ZERO_WS10_UNMAP,	/* Use WRITE SAME(10) with UNMAP */
+};
+
 struct scsi_disk {
 	struct scsi_driver *driver;	/* always &sd_template */
 	struct scsi_device *device;
@@ -89,6 +96,7 @@ struct scsi_disk {
 	u8		write_prot;
 	u8		protection_type;/* Data Integrity Field */
 	u8		provisioning_mode;
+	u8		zeroing_mode;
 	unsigned	ATO : 1;	/* state of disk ATO bit */
 	unsigned	cache_override : 1; /* temp override of WCE,RCD */
 	unsigned	WCE : 1;	/* state of disk WCE bit */
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/2] scsi: sd: Remove LBPRZ dependency for discards
  2017-04-05 11:41 [PATCH 1/2] scsi: sd: Separate zeroout and discard command choices Martin K. Petersen
@ 2017-04-05 11:41 ` Martin K. Petersen
  2017-04-07 18:36   ` Bart Van Assche
  2017-04-07 18:28 ` [PATCH 1/2] scsi: sd: Separate zeroout and discard command choices Bart Van Assche
  2017-04-07 19:59 ` Bart Van Assche
  2 siblings, 1 reply; 7+ messages in thread
From: Martin K. Petersen @ 2017-04-05 11:41 UTC (permalink / raw)
  To: linux-scsi; +Cc: Martin K. Petersen, Christoph Hellwig

Separating discards and zeroout operations allows us to remove the LBPRZ
block zeroing constraints from discards and honor the device preferences
for UNMAP commands.

If supported by the device, we'll also choose UNMAP over one of the
WRITE SAME variants for discards.

Cc: Christoph Hellwig <hch@lst.de>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
---
 drivers/scsi/sd.c | 25 ++++++-------------------
 1 file changed, 6 insertions(+), 19 deletions(-)

diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index acf9d17b05d8..8cf34a8e3eea 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -685,24 +685,11 @@ static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode)
 	unsigned int logical_block_size = sdkp->device->sector_size;
 	unsigned int max_blocks = 0;
 
-	/*
-	 * When LBPRZ is reported, discard alignment and granularity
-	 * must be fixed to the logical block size. Otherwise the block
-	 * layer will drop misaligned portions of the request which can
-	 * lead to data corruption. If LBPRZ is not set, we honor the
-	 * device preference.
-	 */
-	if (sdkp->lbprz) {
-		q->limits.discard_alignment = 0;
-		q->limits.discard_granularity = logical_block_size;
-	} else {
-		q->limits.discard_alignment = sdkp->unmap_alignment *
-			logical_block_size;
-		q->limits.discard_granularity =
-			max(sdkp->physical_block_size,
-			    sdkp->unmap_granularity * logical_block_size);
-	}
-
+	q->limits.discard_alignment =
+		sdkp->unmap_alignment * logical_block_size;
+	q->limits.discard_granularity =
+		max(sdkp->physical_block_size,
+		    sdkp->unmap_granularity * logical_block_size);
 	sdkp->provisioning_mode = mode;
 
 	switch (mode) {
@@ -2842,7 +2829,7 @@ static void sd_read_block_limits(struct scsi_disk *sdkp)
 				sd_config_discard(sdkp, SD_LBP_WS16);
 
 		} else {	/* LBP VPD page tells us what to use */
-			if (sdkp->lbpu && sdkp->max_unmap_blocks && !sdkp->lbprz)
+			if (sdkp->lbpu && sdkp->max_unmap_blocks)
 				sd_config_discard(sdkp, SD_LBP_UNMAP);
 			else if (sdkp->lbpws)
 				sd_config_discard(sdkp, SD_LBP_WS16);
-- 
2.12.0

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] scsi: sd: Separate zeroout and discard command choices
  2017-04-05 11:41 [PATCH 1/2] scsi: sd: Separate zeroout and discard command choices Martin K. Petersen
  2017-04-05 11:41 ` [PATCH 2/2] scsi: sd: Remove LBPRZ dependency for discards Martin K. Petersen
@ 2017-04-07 18:28 ` Bart Van Assche
  2017-04-12  1:04   ` Martin K. Petersen
  2017-04-07 19:59 ` Bart Van Assche
  2 siblings, 1 reply; 7+ messages in thread
From: Bart Van Assche @ 2017-04-07 18:28 UTC (permalink / raw)
  To: linux-scsi, martin.petersen; +Cc: hch

On Wed, 2017-04-05 at 07:41 -0400, Martin K. Petersen wrote:
> +static const char *zeroing_mode[] = {
> +	[SD_ZERO_WRITE]		= "write",
> +	[SD_ZERO_WS]		= "writesame",
> +	[SD_ZERO_WS16_UNMAP]	= "writesame_16_unmap",
> +	[SD_ZERO_WS10_UNMAP]	= "writesame_10_unmap",
> +};
> +
> +static ssize_t
> +zeroing_mode_show(struct device *dev, struct device_attribute *attr,
> +		  char *buf)
> +{
> +	struct scsi_disk *sdkp = to_scsi_disk(dev);
> +
> +	return snprintf(buf, 20, "%s\n", zeroing_mode[sdkp->zeroing_mode]);
> +}

Hello Martin,

If anyone would ever add a string to zeroing_mode[] that is longer than 20
characters then zeroing_mode_show() will truncate it. Since all strings in
the zeroing_mode[] array are short, have you considered to use sprintf()
instead? And if you do not want to use sprintf(), how about using
snprintf(buf, PAGE_SIZE, ...)? I'm asking this because I'm no fan of magic
constants.
 
> +static ssize_t
> +zeroing_mode_store(struct device *dev, struct device_attribute *attr,
> +		   const char *buf, size_t count)
> +{
> +	struct scsi_disk *sdkp = to_scsi_disk(dev);
> +
> +	if (!capable(CAP_SYS_ADMIN))
> +		return -EACCES;
> +
> +	if (!strncmp(buf, zeroing_mode[SD_ZERO_WRITE], 20))
> +		sdkp->zeroing_mode = SD_ZERO_WRITE;
> +	else if (!strncmp(buf, zeroing_mode[SD_ZERO_WS], 20))
> +		sdkp->zeroing_mode = SD_ZERO_WS;
> +	else if (!strncmp(buf, zeroing_mode[SD_ZERO_WS16_UNMAP], 20))
> +		sdkp->zeroing_mode = SD_ZERO_WS16_UNMAP;
> +	else if (!strncmp(buf, zeroing_mode[SD_ZERO_WS10_UNMAP], 20))
> +		sdkp->zeroing_mode = SD_ZERO_WS10_UNMAP;
> +	else
> +		return -EINVAL;
> +
> +	return count;
> +}

Since sysfs guarantees that buf is '\0'-terminated, why does the above
function call strncmp() instead of strcmp()?

Can the above chain of if-statements be replaced by a for-loop such that
zeroing_mode_store() won't have to be updated if the zeroing_mode[] array
is modified?

Thanks,

Bart.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 2/2] scsi: sd: Remove LBPRZ dependency for discards
  2017-04-05 11:41 ` [PATCH 2/2] scsi: sd: Remove LBPRZ dependency for discards Martin K. Petersen
@ 2017-04-07 18:36   ` Bart Van Assche
  0 siblings, 0 replies; 7+ messages in thread
From: Bart Van Assche @ 2017-04-07 18:36 UTC (permalink / raw)
  To: linux-scsi, martin.petersen; +Cc: hch

On Wed, 2017-04-05 at 07:41 -0400, Martin K. Petersen wrote:
> Separating discards and zeroout operations allows us to remove the LBPRZ
> block zeroing constraints from discards and honor the device preferences
> for UNMAP commands.
> 
> If supported by the device, we'll also choose UNMAP over one of the
> WRITE SAME variants for discards.

Reviewed-by: Bart Van Assche <bart.vanassche@sandisk.com>

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] scsi: sd: Separate zeroout and discard command choices
  2017-04-05 11:41 [PATCH 1/2] scsi: sd: Separate zeroout and discard command choices Martin K. Petersen
  2017-04-05 11:41 ` [PATCH 2/2] scsi: sd: Remove LBPRZ dependency for discards Martin K. Petersen
  2017-04-07 18:28 ` [PATCH 1/2] scsi: sd: Separate zeroout and discard command choices Bart Van Assche
@ 2017-04-07 19:59 ` Bart Van Assche
  2017-04-10  7:13   ` hch
  2 siblings, 1 reply; 7+ messages in thread
From: Bart Van Assche @ 2017-04-07 19:59 UTC (permalink / raw)
  To: linux-scsi, martin.petersen; +Cc: hch

On Wed, 2017-04-05 at 07:41 -0400, Martin K. Petersen wrote:
> +static ssize_t
> +zeroing_mode_store(struct device *dev, struct device_attribute *attr,
> +		   const char *buf, size_t count)
> +{
> +	struct scsi_disk *sdkp = to_scsi_disk(dev);
> +
> +	if (!capable(CAP_SYS_ADMIN))
> +		return -EACCES;
> +
> +	if (!strncmp(buf, zeroing_mode[SD_ZERO_WRITE], 20))
> +		sdkp->zeroing_mode = SD_ZERO_WRITE;
> +	else if (!strncmp(buf, zeroing_mode[SD_ZERO_WS], 20))
> +		sdkp->zeroing_mode = SD_ZERO_WS;
> +	else if (!strncmp(buf, zeroing_mode[SD_ZERO_WS16_UNMAP], 20))
> +		sdkp->zeroing_mode = SD_ZERO_WS16_UNMAP;
> +	else if (!strncmp(buf, zeroing_mode[SD_ZERO_WS10_UNMAP], 20))
> +		sdkp->zeroing_mode = SD_ZERO_WS10_UNMAP;
> +	else
> +		return -EINVAL;
> +
> +	return count;
> +}

An additional question about this function: if the shell command "echo" is used
without command-line option -n to modify the "zeroing_mode" sysfs attribute then
a newline character will be present in buf. Does the above code handle newline
characters correctly?

Bart.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] scsi: sd: Separate zeroout and discard command choices
  2017-04-07 19:59 ` Bart Van Assche
@ 2017-04-10  7:13   ` hch
  0 siblings, 0 replies; 7+ messages in thread
From: hch @ 2017-04-10  7:13 UTC (permalink / raw)
  To: Bart Van Assche; +Cc: linux-scsi, martin.petersen, hch

On Fri, Apr 07, 2017 at 07:59:08PM +0000, Bart Van Assche wrote:
> On Wed, 2017-04-05 at 07:41 -0400, Martin K. Petersen wrote:
> > +static ssize_t
> > +zeroing_mode_store(struct device *dev, struct device_attribute *attr,
> > +		   const char *buf, size_t count)
> > +{
> > +	struct scsi_disk *sdkp = to_scsi_disk(dev);
> > +
> > +	if (!capable(CAP_SYS_ADMIN))
> > +		return -EACCES;
> > +
> > +	if (!strncmp(buf, zeroing_mode[SD_ZERO_WRITE], 20))
> > +		sdkp->zeroing_mode = SD_ZERO_WRITE;
> > +	else if (!strncmp(buf, zeroing_mode[SD_ZERO_WS], 20))
> > +		sdkp->zeroing_mode = SD_ZERO_WS;
> > +	else if (!strncmp(buf, zeroing_mode[SD_ZERO_WS16_UNMAP], 20))
> > +		sdkp->zeroing_mode = SD_ZERO_WS16_UNMAP;
> > +	else if (!strncmp(buf, zeroing_mode[SD_ZERO_WS10_UNMAP], 20))
> > +		sdkp->zeroing_mode = SD_ZERO_WS10_UNMAP;
> > +	else
> > +		return -EINVAL;
> > +
> > +	return count;
> > +}
> 
> An additional question about this function: if the shell command "echo" is used
> without command-line option -n to modify the "zeroing_mode" sysfs attribute then
> a newline character will be present in buf. Does the above code handle newline
> characters correctly?

It ignores the newlines.  But we have a helper called sysfs_streq
to possible ignore it.  It might be a good idea to move the various
sysfs files in scsi to use it.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] scsi: sd: Separate zeroout and discard command choices
  2017-04-07 18:28 ` [PATCH 1/2] scsi: sd: Separate zeroout and discard command choices Bart Van Assche
@ 2017-04-12  1:04   ` Martin K. Petersen
  0 siblings, 0 replies; 7+ messages in thread
From: Martin K. Petersen @ 2017-04-12  1:04 UTC (permalink / raw)
  To: Bart Van Assche; +Cc: linux-scsi, martin.petersen, hch

Bart Van Assche <Bart.VanAssche@sandisk.com> writes:

Bart,

> characters then zeroing_mode_show() will truncate it. Since all
> strings in the zeroing_mode[] array are short, have you considered to
> use sprintf() instead? And if you do not want to use sprintf(), how
> about using snprintf(buf, PAGE_SIZE, ...)? I'm asking this because I'm
> no fan of magic constants.

Yeah, this was just a copy and paste from the provisioning code.

> Since sysfs guarantees that buf is '\0'-terminated, why does the above
> function call strncmp() instead of strcmp()?
>
> Can the above chain of if-statements be replaced by a for-loop such that
> zeroing_mode_store() won't have to be updated if the zeroing_mode[] array
> is modified?

I have a patch that converts sd.c to sysfs_match_string(). That's much
cleaner but will have to wait until the latter gets merged.

-- 
Martin K. Petersen	Oracle Linux Engineering

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2017-04-12  1:04 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-04-05 11:41 [PATCH 1/2] scsi: sd: Separate zeroout and discard command choices Martin K. Petersen
2017-04-05 11:41 ` [PATCH 2/2] scsi: sd: Remove LBPRZ dependency for discards Martin K. Petersen
2017-04-07 18:36   ` Bart Van Assche
2017-04-07 18:28 ` [PATCH 1/2] scsi: sd: Separate zeroout and discard command choices Bart Van Assche
2017-04-12  1:04   ` Martin K. Petersen
2017-04-07 19:59 ` Bart Van Assche
2017-04-10  7:13   ` hch

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.