提交 39c60a09 编写于 作者: J James Bottomley

[SCSI] sd: fix array cache flushing bug causing performance problems

Some arrays synchronize their full non volatile cache when the sd driver sends
a SYNCHRONIZE CACHE command.  Unfortunately, they can have Terrabytes of this
and we send a SYNCHRONIZE CACHE for every barrier if an array reports it has a
writeback cache.  This leads to massive slowdowns on journalled filesystems.

The fix is to allow userspace to turn off the writeback cache setting as a
temporary measure (i.e. without doing the MODE SELECT to write it back to the
device), so even though the device reported it has a writeback cache, the
user, knowing that the cache is non volatile and all they care about is
filesystem correctness, can turn that bit off in the kernel and avoid the
performance ruinous (and safety irrelevant) SYNCHRONIZE CACHE commands.

The way you do this is add a 'temporary' prefix when performing the usual
cache setting operations, so

echo temporary write through > /sys/class/scsi_disk/<disk>/cache_type
Reported-by: NRic Wheeler <rwheeler@redhat.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: NJames Bottomley <JBottomley@Parallels.com>
上级 14b06808
...@@ -142,6 +142,7 @@ sd_store_cache_type(struct device *dev, struct device_attribute *attr, ...@@ -142,6 +142,7 @@ sd_store_cache_type(struct device *dev, struct device_attribute *attr,
char *buffer_data; char *buffer_data;
struct scsi_mode_data data; struct scsi_mode_data data;
struct scsi_sense_hdr sshdr; struct scsi_sense_hdr sshdr;
const char *temp = "temporary ";
int len; int len;
if (sdp->type != TYPE_DISK) if (sdp->type != TYPE_DISK)
...@@ -150,6 +151,13 @@ sd_store_cache_type(struct device *dev, struct device_attribute *attr, ...@@ -150,6 +151,13 @@ sd_store_cache_type(struct device *dev, struct device_attribute *attr,
* it's not worth the risk */ * it's not worth the risk */
return -EINVAL; return -EINVAL;
if (strncmp(buf, temp, sizeof(temp) - 1) == 0) {
buf += sizeof(temp) - 1;
sdkp->cache_override = 1;
} else {
sdkp->cache_override = 0;
}
for (i = 0; i < ARRAY_SIZE(sd_cache_types); i++) { for (i = 0; i < ARRAY_SIZE(sd_cache_types); i++) {
len = strlen(sd_cache_types[i]); len = strlen(sd_cache_types[i]);
if (strncmp(sd_cache_types[i], buf, len) == 0 && if (strncmp(sd_cache_types[i], buf, len) == 0 &&
...@@ -162,6 +170,13 @@ sd_store_cache_type(struct device *dev, struct device_attribute *attr, ...@@ -162,6 +170,13 @@ sd_store_cache_type(struct device *dev, struct device_attribute *attr,
return -EINVAL; return -EINVAL;
rcd = ct & 0x01 ? 1 : 0; rcd = ct & 0x01 ? 1 : 0;
wce = ct & 0x02 ? 1 : 0; wce = ct & 0x02 ? 1 : 0;
if (sdkp->cache_override) {
sdkp->WCE = wce;
sdkp->RCD = rcd;
return count;
}
if (scsi_mode_sense(sdp, 0x08, 8, buffer, sizeof(buffer), SD_TIMEOUT, if (scsi_mode_sense(sdp, 0x08, 8, buffer, sizeof(buffer), SD_TIMEOUT,
SD_MAX_RETRIES, &data, NULL)) SD_MAX_RETRIES, &data, NULL))
return -EINVAL; return -EINVAL;
...@@ -2319,6 +2334,10 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer) ...@@ -2319,6 +2334,10 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer)
int old_rcd = sdkp->RCD; int old_rcd = sdkp->RCD;
int old_dpofua = sdkp->DPOFUA; int old_dpofua = sdkp->DPOFUA;
if (sdkp->cache_override)
return;
first_len = 4; first_len = 4;
if (sdp->skip_ms_page_8) { if (sdp->skip_ms_page_8) {
if (sdp->type == TYPE_RBC) if (sdp->type == TYPE_RBC)
...@@ -2812,6 +2831,7 @@ static void sd_probe_async(void *data, async_cookie_t cookie) ...@@ -2812,6 +2831,7 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
sdkp->capacity = 0; sdkp->capacity = 0;
sdkp->media_present = 1; sdkp->media_present = 1;
sdkp->write_prot = 0; sdkp->write_prot = 0;
sdkp->cache_override = 0;
sdkp->WCE = 0; sdkp->WCE = 0;
sdkp->RCD = 0; sdkp->RCD = 0;
sdkp->ATO = 0; sdkp->ATO = 0;
......
...@@ -73,6 +73,7 @@ struct scsi_disk { ...@@ -73,6 +73,7 @@ struct scsi_disk {
u8 protection_type;/* Data Integrity Field */ u8 protection_type;/* Data Integrity Field */
u8 provisioning_mode; u8 provisioning_mode;
unsigned ATO : 1; /* state of disk ATO bit */ 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 */ unsigned WCE : 1; /* state of disk WCE bit */
unsigned RCD : 1; /* state of disk RCD bit, unused */ unsigned RCD : 1; /* state of disk RCD bit, unused */
unsigned DPOFUA : 1; /* state of disk DPOFUA bit */ unsigned DPOFUA : 1; /* state of disk DPOFUA bit */
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册