@@ -139,6 +139,15 @@ static const char *sd_cache_types[] = {
"write back, no read (daft)"
};
+/*
+ * wce rcd write_cache read_cache
+ * 0 0 off on
+ * 0 1 off off
+ * 1 0 on on
+ * 1 1 on off
+ */
+static const char * const sd_wce_rcd[] = {"00", "01", "10", "11"};
+
static void sd_set_flush_flag(struct scsi_disk *sdkp)
{
bool wc = false, fua = false;
@@ -287,6 +296,80 @@ cache_type_show(struct device *dev, struct device_attribute *attr, char *buf)
static DEVICE_ATTR_RW(cache_type);
static ssize_t
+wce_rcd_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ct, rcd, wce, sp;
+ struct scsi_disk *sdkp = to_scsi_disk(dev);
+ struct scsi_device *sdp = sdkp->device;
+ char buffer[64];
+ char *buffer_data;
+ struct scsi_mode_data data;
+ struct scsi_sense_hdr sshdr;
+ static const char temp[] = "temp ";
+ int len;
+
+ if (sdp->type != TYPE_DISK && sdp->type != TYPE_ZBC)
+ /* no cache control on RBC devices; theoretically they
+ * can do it, but there's probably so many exceptions
+ * it's not worth the risk
+ */
+ return -EINVAL;
+
+ if (strncmp(buf, temp, sizeof(temp) - 1) == 0) {
+ buf += sizeof(temp) - 1;
+ sdkp->cache_override = 1;
+ } else {
+ sdkp->cache_override = 0;
+ }
+
+ ct = sysfs_match_string(sd_wce_rcd, buf);
+ if (ct < 0)
+ return -EINVAL;
+
+ rcd = ct & 0x01 ? 1 : 0;
+ wce = (ct & 0x02) && !sdkp->write_prot ? 1 : 0;
+
+ if (sdkp->cache_override) {
+ sdkp->WCE = wce;
+ sdkp->RCD = rcd;
+ sd_set_flush_flag(sdkp);
+ return count;
+ }
+
+ if (scsi_mode_sense(sdp, 0x08, 8, buffer, sizeof(buffer), SD_TIMEOUT,
+ SD_MAX_RETRIES, &data, NULL))
+ return -EINVAL;
+ len = min_t(size_t, sizeof(buffer), data.length - data.header_length -
+ data.block_descriptor_length);
+ buffer_data = buffer + data.header_length +
+ data.block_descriptor_length;
+ buffer_data[2] &= ~0x05;
+ buffer_data[2] |= wce << 2 | rcd;
+ sp = buffer_data[0] & 0x80 ? 1 : 0;
+ buffer_data[0] &= ~0x80;
+
+ if (scsi_mode_select(sdp, 1, sp, 8, buffer_data, len, SD_TIMEOUT,
+ SD_MAX_RETRIES, &data, &sshdr)) {
+ if (scsi_sense_valid(&sshdr))
+ sd_print_sense_hdr(sdkp, &sshdr);
+ return -EINVAL;
+ }
+ revalidate_disk(sdkp->disk);
+ return count;
+}
+
+static ssize_t
+wce_rcd_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct scsi_disk *sdkp = to_scsi_disk(dev);
+ int ct = sdkp->RCD + 2*sdkp->WCE;
+
+ return sprintf(buf, "%s\n", sd_wce_rcd[ct]);
+}
+static DEVICE_ATTR_RW(wce_rcd);
+
+static ssize_t
FUA_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct scsi_disk *sdkp = to_scsi_disk(dev);
@@ -524,6 +607,7 @@ static DEVICE_ATTR_RW(max_write_same_blocks);
static struct attribute *sd_disk_attrs[] = {
&dev_attr_cache_type.attr,
+ &dev_attr_wce_rcd.attr,
&dev_attr_FUA.attr,
&dev_attr_allow_restart.attr,
&dev_attr_manage_start_stop.attr,
add user friendly interface wce_rcd to enable/disable write&read cache. The code base on cache_type, but need short input. enable both write and read cache: echo "10" > wce_rcd wce rcd write_cache read_cache 0 0 off on 0 1 off off 1 0 on on 1 1 on off Signed-off-by: weiping zhang <zhangweiping@didichuxing.com> --- drivers/scsi/sd.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+)