linux-scsi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/1] scsi: retrieve cache mode using ATA_16 if normal routine fails
@ 2011-12-12 11:18 Amit Sahrawat
  2011-12-12 12:51 ` James Bottomley
  0 siblings, 1 reply; 14+ messages in thread
From: Amit Sahrawat @ 2011-12-12 11:18 UTC (permalink / raw)
  To: James E.J. Bottomley
  Cc: Nam-Jae Jeon, linux-scsi, linux-kernel, Amit Sahrawat

It has been observed that a number of USB HDD's do not respond correctly
to SCSI mode sense command(retrieve caching pages) which results in their
Write Cache being discarded by queue requests i.e., WCE if left set to
'0'(disabled).
This results in a number of Filesystem corruptions, when the device
is unplugged abruptly.

So, in order to identify the devices correctly - give it
a last try using ATA_16 after failure from normal routine.
Introduce a mechanism to store write-cache type using /sys/class/
interface, so that the normal code continues to function without errors.

Signed-off-by: Amit Sahrawat <amit.sahrawat83@gmail.com>
Signed-off-by: Nam-Jae Jeon <namjae.jeon@samsung.com>
---
 drivers/scsi/sd.c          |   49 +++++++++++++++++++++++++++++++++++++++++++-
 include/scsi/scsi.h        |   32 ++++++++++++++++++++++++++++
 include/scsi/scsi_device.h |    2 +
 3 files changed, 82 insertions(+), 1 deletions(-)

diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index fa3a591..5227d65 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -124,7 +124,7 @@ static mempool_t *sd_cdb_pool;
 
 static const char *sd_cache_types[] = {
 	"write through", "none", "write back",
-	"write back, no read (daft)"
+	"write back, no read (daft)", "write back(ata cmd)"
 };
 
 static ssize_t
@@ -156,8 +156,15 @@ sd_store_cache_type(struct device *dev, struct device_attribute *attr,
 	}
 	if (ct < 0)
 		return -EINVAL;
+
+	if (ct & 0x04) {
+		sdp->use_ata_cmd = 1;
+		goto revalidate_disk;
+	}
+
 	rcd = ct & 0x01 ? 1 : 0;
 	wce = ct & 0x02 ? 1 : 0;
+
 	if (scsi_mode_sense(sdp, 0x08, 8, buffer, sizeof(buffer), SD_TIMEOUT,
 			    SD_MAX_RETRIES, &data, NULL))
 		return -EINVAL;
@@ -175,6 +182,8 @@ sd_store_cache_type(struct device *dev, struct device_attribute *attr,
 			sd_print_sense_hdr(sdkp, &sshdr);
 		return -EINVAL;
 	}
+
+revalidate_disk:
 	revalidate_disk(sdkp->disk);
 	return count;
 }
@@ -2029,11 +2038,15 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer)
 	int old_wce = sdkp->WCE;
 	int old_rcd = sdkp->RCD;
 	int old_dpofua = sdkp->DPOFUA;
+	unsigned char cdb[ATA_16_LEN] = {0};
 
 	first_len = 4;
 	if (sdp->skip_ms_page_8) {
 		if (sdp->type == TYPE_RBC)
 			goto defaults;
+		else if (sdp->use_ata_cmd) {
+			goto WCE_USING_ATA;
+		}
 		else {
 			if (sdp->skip_ms_page_3f)
 				goto defaults;
@@ -2142,6 +2155,39 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer)
 			sdkp->DPOFUA = 0;
 		}
 
+WCE_USING_ATA:
+		if (!sdp->removable && (sdp->use_ata_cmd && !sdkp->WCE)) {
+			sd_printk(KERN_NOTICE, sdkp, "Try to check write cache "
+				"enable/disable using ATA command\n");
+			cdb[0] = ATA_16;
+			/* Packet command, Programmed I/O -
+			 * PIO - Indicates Data in */
+			cdb[1] = ATA_PROTO_PIO_IN;
+			/* Data read sectors from device */
+			cdb[2] = CDB2_TLEN_NSECT |
+				 CDB2_TLEN_SECTORS | CDB2_TDIR_FROM_DEV;
+			cdb[6] = 0x01; /* No. of Sectors To Read */
+			cdb[13] = ATA_USING_LBA;
+			cdb[14] = ATA_OP_IDENTIFY;
+
+			sdkp->WCE = 0;
+			sdkp->RCD = 0;
+			sdkp->DPOFUA = 0;
+
+			if (!scsi_execute_req(sdp, cdb, DMA_FROM_DEVICE,
+				buffer,	SD_BUF_SIZE, &sshdr, SD_TIMEOUT,
+				SD_MAX_RETRIES, NULL)) {
+				/*
+				 * '6th' Bit in Word 85 Corresponds to
+				 * Write Cache being Enabled/disabled,
+				 * Word 85 represnets the features supported
+				 */
+				if (le16_to_cpu(
+					((unsigned short *)buffer)[85]) & 0x20)
+					sdkp->WCE = 1;
+			}
+		}
+
 		if (sdkp->first_scan || old_wce != sdkp->WCE ||
 		    old_rcd != sdkp->RCD || old_dpofua != sdkp->DPOFUA)
 			sd_printk(KERN_NOTICE, sdkp,
@@ -2515,6 +2561,7 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
 	sdkp->RCD = 0;
 	sdkp->ATO = 0;
 	sdkp->first_scan = 1;
+	sdp->use_ata_cmd = 0;
 
 	sd_revalidate_disk(gd);
 
diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h
index 8001ae4..9e44805 100644
--- a/include/scsi/scsi.h
+++ b/include/scsi/scsi.h
@@ -181,6 +181,38 @@ struct scsi_cmnd;
 #define	ATA_16		      0x85	/* 16-byte pass-thru */
 #define	ATA_12		      0xa1	/* 12-byte pass-thru */
 
+#define ATA_16_LEN     16
+#define ATA_OP_IDENTIFY        0xec /* Command to Identify device*/
+
+#define ATA_LBA48            1
+#define ATA_PROTO_NON_DATA   (3 << 1)
+#define ATA_PROTO_PIO_IN     (4 << 1)
+#define ATA_PROTO_PIO_OUT    (5 << 1)
+#define ATA_PROTO_DMA        (6 << 1)
+
+/*
+ * Some useful ATA register bits
+ */
+enum {
+	ATA_USING_LBA   =       (1 << 6),
+	ATA_STAT_DRQ    =       (1 << 3),
+	ATA_STAT_ERR    =       (1 << 0),
+};
+
+enum {
+	CDB2_TLEN_NODATA     = 0 << 0,
+	CDB2_TLEN_FEAT       = 1 << 0,
+	CDB2_TLEN_NSECT      = 2 << 0,
+
+	CDB2_TLEN_BYTES      = 0 << 2,
+	CDB2_TLEN_SECTORS    = 1 << 2,
+
+	CDB2_TDIR_TO_DEV     = 0 << 3,
+	CDB2_TDIR_FROM_DEV   = 1 << 3,
+
+	CDB2_CHECK_COND      = 1 << 5,
+};
+
 /*
  *	SCSI command lengths
  */
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index 5591ed5..edfa981 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -151,6 +151,8 @@ struct scsi_device {
 	unsigned no_read_disc_info:1;	/* Avoid READ_DISC_INFO cmds */
 	unsigned no_read_capacity_16:1; /* Avoid READ_CAPACITY_16 cmds */
 	unsigned is_visible:1;	/* is the device visible in sysfs */
+	unsigned use_ata_cmd; /* device which do not responed to normal routine,
+			       * try ATA_16 command on them. */
 
 	DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events */
 	struct list_head event_list;	/* asserted events */
-- 
1.7.2.3


^ permalink raw reply related	[flat|nested] 14+ messages in thread
* [PATCH 1/1] scsi: retrieve cache mode using ATA_16 if normal routine fails
@ 2012-02-03 12:59 Amit Sahrawat
  2012-02-05 12:05 ` Sergei Shtylyov
  0 siblings, 1 reply; 14+ messages in thread
From: Amit Sahrawat @ 2012-02-03 12:59 UTC (permalink / raw)
  To: Jeff Garzik, James E.J. Bottomley
  Cc: Nam-Jae Jeon, Amit Sahrawat, linux-ide, linux-scsi, linux-kernel,
	Amit Sahrawat

It has been observed that a number of USB HDD's do not respond correctly
to SCSI mode sense command(retrieve caching pages) which results in their
Write Cache being discarded by queue requests i.e., WCE if left set to
'0'(disabled).
This results in a number of Filesystem corruptions, when the device
is unplugged abruptly.

So, in order to identify the devices correctly - give it
a last try using ATA_16 after failure from normal routine.

Signed-off-by: Amit Sahrawat <a.sahrawat@samsung.com>
Signed-off-by: Namjae Jeon <namjae.jeon@samsung.com>

---
 drivers/ata/libata-scsi.c |   51 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/sd.c         |   17 +++++++++++++++
 include/linux/libata.h    |    3 ++
 3 files changed, 71 insertions(+), 0 deletions(-)

diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 508a60b..d5b00e6 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -562,6 +562,57 @@ error:
 }
 
 /**
+ *      ata_get_cachestatus - Handler for to get WriteCache Status
+ *                      using ATA_16 scsi command
+ *      @scsidev: Device to which we are issuing command
+ *
+ *      LOCKING:
+ *      Defined by the SCSI layer.  We don't really care.
+ *
+ *      RETURNS:
+ *      '0' for Write Cache Disabled and on any error.
+ *      '1' for Write Cache enabled
+ */
+int ata_get_cachestatus(struct scsi_device *scsidev)
+{
+	int rc = 0;
+	u8 scsi_cmd[MAX_COMMAND_SIZE] = {0};
+	u8 *sensebuf = NULL, *argbuf = NULL;
+	enum dma_data_direction data_dir;
+
+	sensebuf = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_NOIO);
+	if (!sensebuf)
+		return rc;
+
+	argbuf = kmalloc(ATA_SECT_SIZE, GFP_KERNEL);
+	if (argbuf == NULL)
+		goto error;
+
+	scsi_cmd[0] = ATA_16;
+	scsi_cmd[1]  = (4 << 1); /* PIO Data-in */
+	scsi_cmd[2]  = 0x0e;     /* no off.line or cc, read from dev,
+				block count in sector count field */
+	data_dir = DMA_FROM_DEVICE;
+	scsi_cmd[14] = ATA_IDENTIFY_DEV;
+
+	if (!scsi_execute(scsidev, scsi_cmd, data_dir, argbuf, ATA_SECT_SIZE,
+				sensebuf, (10*HZ), 5, 0, NULL)) {
+		/*
+		 * '6th' Bit in Word 85 Corresponds to Write Cache
+		 * being Enabled/disabled, Word 85 represnets the
+		 * features supported
+		 */
+		if (le16_to_cpu(((unsigned short *)argbuf)[85]) & 0x20)
+			rc = 1;
+	}
+
+error:
+	kfree(sensebuf);
+	kfree(argbuf);
+	return rc;
+}
+
+/**
  *	ata_task_ioctl - Handler for HDIO_DRIVE_TASK ioctl
  *	@scsidev: Device to which we are issuing command
  *	@arg: User provided data for issuing command
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index c691fb5..a6b887d 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -50,6 +50,11 @@
 #include <linux/string_helpers.h>
 #include <linux/async.h>
 #include <linux/slab.h>
+
+#ifdef CONFIG_ATA
+#include <linux/libata.h>
+#endif
+
 #include <linux/pm_runtime.h>
 #include <asm/uaccess.h>
 #include <asm/unaligned.h>
@@ -2129,7 +2134,11 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer)
 		if (modepage == 0x3F) {
 			sd_printk(KERN_ERR, sdkp, "No Caching mode page "
 				  "present\n");
+#ifdef CONFIG_ATA
+			goto WCE_USING_ATA;
+#else
 			goto defaults;
+#endif
 		} else if ((buffer[offset] & 0x3f) != modepage) {
 			sd_printk(KERN_ERR, sdkp, "Got wrong page\n");
 			goto defaults;
@@ -2149,6 +2158,14 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer)
 				  "Uses READ/WRITE(6), disabling FUA\n");
 			sdkp->DPOFUA = 0;
 		}
+#ifdef CONFIG_ATA
+WCE_USING_ATA:
+		if (!sdp->removable && !sdkp->WCE) {
+			sd_printk(KERN_NOTICE, sdkp, "Try to check write cache "
+				"enable/disable using ATA command\n");
+			sdkp->WCE = ata_get_cachestatus(sdp);
+		}
+#endif
 
 		if (sdkp->first_scan || old_wce != sdkp->WCE ||
 		    old_rcd != sdkp->RCD || old_dpofua != sdkp->DPOFUA)
diff --git a/include/linux/libata.h b/include/linux/libata.h
index cafc09a..33fc73f 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -84,6 +84,8 @@
 	}							\
 })
 
+#define ATA_IDENTIFY_DEV	0xEC
+
 /* NEW: debug levels */
 #define HAVE_LIBATA_MSG 1
 
@@ -990,6 +992,7 @@ extern void ata_host_init(struct ata_host *, struct device *,
 			  unsigned long, struct ata_port_operations *);
 extern int ata_scsi_detect(struct scsi_host_template *sht);
 extern int ata_scsi_ioctl(struct scsi_device *dev, int cmd, void __user *arg);
+extern int ata_get_cachestatus(struct scsi_device *scsidev);
 extern int ata_scsi_queuecmd(struct Scsi_Host *h, struct scsi_cmnd *cmd);
 extern int ata_sas_scsi_ioctl(struct ata_port *ap, struct scsi_device *dev,
 			    int cmd, void __user *arg);
-- 
1.7.2.3


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

end of thread, other threads:[~2012-02-06  5:40 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-12-12 11:18 [PATCH 1/1] scsi: retrieve cache mode using ATA_16 if normal routine fails Amit Sahrawat
2011-12-12 12:51 ` James Bottomley
2011-12-13  0:20   ` Namjae Jeon
     [not found]     ` <CADDb1s2SOK5sC3N0OOdkBrPuDKc2d2A4z4yso4jYs=1rbxNmkA@mail.gmail.com>
2011-12-13  4:56       ` Amit Sahrawat
2011-12-13  8:53     ` James Bottomley
2011-12-13 12:15       ` Amit Sahrawat
2011-12-13 20:38       ` Jeff Garzik
2011-12-14  3:44         ` Amit Sahrawat
2011-12-14  7:39           ` James Bottomley
2011-12-15  0:25             ` Namjae Jeon
2012-01-27  5:20               ` Amit Sahrawat
  -- strict thread matches above, loose matches on Subject: below --
2012-02-03 12:59 Amit Sahrawat
2012-02-05 12:05 ` Sergei Shtylyov
2012-02-06  5:40   ` Amit Sahrawat

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).