Commit 98dcc294 authored by Martin K. Petersen's avatar Martin K. Petersen Committed by Greg Kroah-Hartman

SCSI: sd: Update WRITE SAME heuristics

commit 66c28f97 upstream.

SATA drives located behind a SAS controller would incorrectly receive
WRITE SAME commands. Tweak the heuristics so that:

 - If REPORT SUPPORTED OPERATION CODES is provided we will use that to
   choose between WRITE SAME(16), WRITE SAME(10) and disabled. This also
   fixes an issue with the old code which would issue WRITE SAME(10)
   despite the command not being whitelisted in REPORT SUPPORTED
   OPERATION CODES.

 - If REPORT SUPPORTED OPERATION CODES is not provided we will fall back
   to WRITE SAME(10) unless the device has an ATA Information VPD page.
   The assumption is that a SATL which is smart enough to implement
   WRITE SAME would also provide REPORT SUPPORTED OPERATION CODES.

To facilitate the new heuristics scsi_report_opcode() has been modified
to so we can distinguish between "operation not supported" and "RSOC not
supported".
Reported-by: default avatarH. Peter Anvin <hpa@zytor.com>
Tested-by: default avatarBernd Schubert <bernd.schubert@itwm.fraunhofer.de>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 64ea2992
...@@ -1070,8 +1070,8 @@ EXPORT_SYMBOL_GPL(scsi_get_vpd_page); ...@@ -1070,8 +1070,8 @@ EXPORT_SYMBOL_GPL(scsi_get_vpd_page);
* @opcode: opcode for command to look up * @opcode: opcode for command to look up
* *
* Uses the REPORT SUPPORTED OPERATION CODES to look up the given * Uses the REPORT SUPPORTED OPERATION CODES to look up the given
* opcode. Returns 0 if RSOC fails or if the command opcode is * opcode. Returns -EINVAL if RSOC fails, 0 if the command opcode is
* unsupported. Returns 1 if the device claims to support the command. * unsupported and 1 if the device claims to support the command.
*/ */
int scsi_report_opcode(struct scsi_device *sdev, unsigned char *buffer, int scsi_report_opcode(struct scsi_device *sdev, unsigned char *buffer,
unsigned int len, unsigned char opcode) unsigned int len, unsigned char opcode)
...@@ -1081,7 +1081,7 @@ int scsi_report_opcode(struct scsi_device *sdev, unsigned char *buffer, ...@@ -1081,7 +1081,7 @@ int scsi_report_opcode(struct scsi_device *sdev, unsigned char *buffer,
int result; int result;
if (sdev->no_report_opcodes || sdev->scsi_level < SCSI_SPC_3) if (sdev->no_report_opcodes || sdev->scsi_level < SCSI_SPC_3)
return 0; return -EINVAL;
memset(cmd, 0, 16); memset(cmd, 0, 16);
cmd[0] = MAINTENANCE_IN; cmd[0] = MAINTENANCE_IN;
...@@ -1097,7 +1097,7 @@ int scsi_report_opcode(struct scsi_device *sdev, unsigned char *buffer, ...@@ -1097,7 +1097,7 @@ int scsi_report_opcode(struct scsi_device *sdev, unsigned char *buffer,
if (result && scsi_sense_valid(&sshdr) && if (result && scsi_sense_valid(&sshdr) &&
sshdr.sense_key == ILLEGAL_REQUEST && sshdr.sense_key == ILLEGAL_REQUEST &&
(sshdr.asc == 0x20 || sshdr.asc == 0x24) && sshdr.ascq == 0x00) (sshdr.asc == 0x20 || sshdr.asc == 0x24) && sshdr.ascq == 0x00)
return 0; return -EINVAL;
if ((buffer[1] & 3) == 3) /* Command supported */ if ((buffer[1] & 3) == 3) /* Command supported */
return 1; return 1;
......
...@@ -442,8 +442,10 @@ sd_store_write_same_blocks(struct device *dev, struct device_attribute *attr, ...@@ -442,8 +442,10 @@ sd_store_write_same_blocks(struct device *dev, struct device_attribute *attr,
if (max == 0) if (max == 0)
sdp->no_write_same = 1; sdp->no_write_same = 1;
else if (max <= SD_MAX_WS16_BLOCKS) else if (max <= SD_MAX_WS16_BLOCKS) {
sdp->no_write_same = 0;
sdkp->max_ws_blocks = max; sdkp->max_ws_blocks = max;
}
sd_config_write_same(sdkp); sd_config_write_same(sdkp);
...@@ -740,7 +742,6 @@ static void sd_config_write_same(struct scsi_disk *sdkp) ...@@ -740,7 +742,6 @@ static void sd_config_write_same(struct scsi_disk *sdkp)
{ {
struct request_queue *q = sdkp->disk->queue; struct request_queue *q = sdkp->disk->queue;
unsigned int logical_block_size = sdkp->device->sector_size; unsigned int logical_block_size = sdkp->device->sector_size;
unsigned int blocks = 0;
if (sdkp->device->no_write_same) { if (sdkp->device->no_write_same) {
sdkp->max_ws_blocks = 0; sdkp->max_ws_blocks = 0;
...@@ -752,18 +753,20 @@ static void sd_config_write_same(struct scsi_disk *sdkp) ...@@ -752,18 +753,20 @@ static void sd_config_write_same(struct scsi_disk *sdkp)
* blocks per I/O unless the device explicitly advertises a * blocks per I/O unless the device explicitly advertises a
* bigger limit. * bigger limit.
*/ */
if (sdkp->max_ws_blocks == 0) if (sdkp->max_ws_blocks > SD_MAX_WS10_BLOCKS)
sdkp->max_ws_blocks = SD_MAX_WS10_BLOCKS; sdkp->max_ws_blocks = min_not_zero(sdkp->max_ws_blocks,
(u32)SD_MAX_WS16_BLOCKS);
if (sdkp->ws16 || sdkp->max_ws_blocks > SD_MAX_WS10_BLOCKS) else if (sdkp->ws16 || sdkp->ws10 || sdkp->device->no_report_opcodes)
blocks = min_not_zero(sdkp->max_ws_blocks, sdkp->max_ws_blocks = min_not_zero(sdkp->max_ws_blocks,
(u32)SD_MAX_WS16_BLOCKS); (u32)SD_MAX_WS10_BLOCKS);
else else {
blocks = min_not_zero(sdkp->max_ws_blocks, sdkp->device->no_write_same = 1;
(u32)SD_MAX_WS10_BLOCKS); sdkp->max_ws_blocks = 0;
}
out: out:
blk_queue_max_write_same_sectors(q, blocks * (logical_block_size >> 9)); blk_queue_max_write_same_sectors(q, sdkp->max_ws_blocks *
(logical_block_size >> 9));
} }
/** /**
...@@ -2635,9 +2638,24 @@ static void sd_read_block_provisioning(struct scsi_disk *sdkp) ...@@ -2635,9 +2638,24 @@ static void sd_read_block_provisioning(struct scsi_disk *sdkp)
static void sd_read_write_same(struct scsi_disk *sdkp, unsigned char *buffer) static void sd_read_write_same(struct scsi_disk *sdkp, unsigned char *buffer)
{ {
if (scsi_report_opcode(sdkp->device, buffer, SD_BUF_SIZE, struct scsi_device *sdev = sdkp->device;
WRITE_SAME_16))
if (scsi_report_opcode(sdev, buffer, SD_BUF_SIZE, INQUIRY) < 0) {
sdev->no_report_opcodes = 1;
/* Disable WRITE SAME if REPORT SUPPORTED OPERATION
* CODES is unsupported and the device has an ATA
* Information VPD page (SAT).
*/
if (!scsi_get_vpd_page(sdev, 0x89, buffer, SD_BUF_SIZE))
sdev->no_write_same = 1;
}
if (scsi_report_opcode(sdev, buffer, SD_BUF_SIZE, WRITE_SAME_16) == 1)
sdkp->ws16 = 1; sdkp->ws16 = 1;
if (scsi_report_opcode(sdev, buffer, SD_BUF_SIZE, WRITE_SAME) == 1)
sdkp->ws10 = 1;
} }
static int sd_try_extended_inquiry(struct scsi_device *sdp) static int sd_try_extended_inquiry(struct scsi_device *sdp)
......
...@@ -84,6 +84,7 @@ struct scsi_disk { ...@@ -84,6 +84,7 @@ struct scsi_disk {
unsigned lbpws : 1; unsigned lbpws : 1;
unsigned lbpws10 : 1; unsigned lbpws10 : 1;
unsigned lbpvpd : 1; unsigned lbpvpd : 1;
unsigned ws10 : 1;
unsigned ws16 : 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)
......
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