[Patch] SCSI: Retrieve Cache Mode Using SG_ATA_16 if normal routine fails

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



SCSI: Retrieve Cache Mode Using SG_ATA_16 if normal routine fails

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). So, in order to identify the devices correctly - give it
a last try using SG_ATA_16 after failure from normal routine.

Signed-off-by: Amit Sahrawat <amit.sahrawat83@xxxxxxxxx>

diff -Nurp linux-Orig/drivers/scsi/sd.c linux-Updated/drivers/scsi/sd.c
--- linux-Orig/drivers/scsi/sd.c	2011-10-11 11:02:48.000000000 +0530
+++ linux-Updated/drivers/scsi/sd.c	2011-10-11 11:10:09.000000000 +0530
@@ -56,6 +56,7 @@
 #include <asm/uaccess.h>
 #include <asm/unaligned.h>

+#include <scsi/sg.h>
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_dbg.h>
@@ -134,6 +135,58 @@ static const char *sd_cache_types[] = {
 	"write back, no read (daft)"
 };

+/* Relevant Structure and Function to be used to Retrieve
+ * Caching Information from USB HDD - this is picked from
+ * source code of 'hdparm'
+ *
+ *
+ * Definitions and structures for use with SG_IO + ATA_16:
+ * */
+#define SG_ATA_16               0x85
+#define SG_ATA_16_LEN           16
+
+#define ATA_OP_IDENTIFY		0xec
+
+/*
+ * Some useful ATA register bits
+ */
+enum {
+        ATA_USING_LBA           = (1 << 6),
+        ATA_STAT_DRQ            = (1 << 3),
+        ATA_STAT_ERR            = (1 << 0),
+};
+
+struct ata_lba_regs {
+        __u8    feat;
+        __u8    nsect;
+        __u8    lbal;
+        __u8    lbam;
+        __u8    lbah;
+};
+struct ata_tf {
+        __u8                    dev;
+        __u8                    command;
+        __u8                    error;
+        __u8                    status;
+        __u8                    is_lba48;
+        struct ata_lba_regs     lob;
+        struct ata_lba_regs     hob;
+};
+
+__u64 tf_to_lba (struct ata_tf *tf)
+{
+        __u32 lba24, lbah;
+        __u64 lba64;
+
+        lba24 = (tf->lob.lbah << 16) | (tf->lob.lbam << 8) | (tf->lob.lbal);
+        if (tf->is_lba48)
+                lbah = (tf->hob.lbah << 16) | (tf->hob.lbam << 8) |
(tf->hob.lbal);
+        else
+                lbah = (tf->dev & 0x0f);
+        lba64 = (((__u64)lbah) << 24) | (__u64)lba24;
+        return lba64;
+}
+
 static ssize_t
 sd_store_cache_type(struct device *dev, struct device_attribute *attr,
 		    const char *buf, size_t count)
@@ -1839,6 +1892,18 @@ sd_read_cache_type(struct scsi_disk *sdk
 	int old_rcd = sdkp->RCD;
 	int old_dpofua = sdkp->DPOFUA;

+	struct ata_tf tf;
+	struct sg_io_hdr io_hdr;
+	unsigned char cdb[SG_ATA_16_LEN] = {0};
+	unsigned char sb[32] = {0};
+	unsigned char buf[512] = {0};
+	unsigned short wce_word = 0;
+	void *data_cmd = buf;
+
+	memset(cdb, 0, SG_ATA_16_LEN);
+	memset(&tf, 0, sizeof(struct ata_tf));
+	memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
+
        first_len = 4;
        if (sdp->skip_ms_page_8) {
                if (sdp->type == TYPE_RBC)
@@ -1961,7 +2026,6 @@ Page_found:
 				  sdkp->DPOFUA ? "supports DPO and FUA"
 				  : "doesn't support DPO or FUA");

-		return;
 	}

 bad_sense:
@@ -1974,8 +2038,64 @@ bad_sense:
 		sd_printk(KERN_ERR, sdkp, "Asking for cache data failed\n");

 defaults:
-	sd_printk(KERN_ERR, sdkp, "Assuming drive cache: write through\n");
-	sdkp->WCE = 0;
+	if (sdkp->WCE)
+		return;
+	else {
+		sd_printk(KERN_ERR, sdkp, "Normal Routine Failed: Trying ATA_16\n");
+		
+		/* The below are necessary parameters which are to set - in order
+		to make use of ATA_OP_IDENTIFY command */
+		tf.lob.lbal = 0;
+	        tf.lob.lbam = 0;
+	        tf.lob.lbah = 0;
+	        tf.lob.nsect = 1; //Number of Sectors to Read
+	        tf.lob.feat = 0;
+
+		/* Command Descriptor Block For SCSI 	*/
+	        cdb[0] = SG_ATA_16;
+	        cdb[1] = 0x08;
+	        cdb[2] = 0x0e;
+	        cdb[6] = 0x01; //No. of Sectors To Read
+	        cdb[13] = ATA_USING_LBA;
+	        cdb[14] = ATA_OP_IDENTIFY;
+
+	        io_hdr.cmd_len = SG_ATA_16_LEN;
+	        io_hdr.interface_id = SG_INTERFACE_ID_ORIG;
+	        io_hdr.mx_sb_len= sizeof(sb);
+	        io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+	        io_hdr.dxfer_len = sizeof(buf);
+	        io_hdr.dxferp = data_cmd;
+	        io_hdr.cmdp = cdb;
+	        io_hdr.sbp = sb;
+	        io_hdr.pack_id = tf_to_lba(&tf);
+        	io_hdr.timeout = 0;
+
+	        if (!scsi_cmd_ioctl(sdkp->disk->queue, sdkp->disk,
O_RDONLY|O_NONBLOCK, SG_IO, &io_hdr))
+        	{
+#if 0
+#define DUMP_BYTES_BUFFER(x...) printk( x )
+			int i;
+			for (i = 0; i < 32; i++)
+        	                DUMP_BYTES_BUFFER(" %02x", sb[i]);
+	                DUMP_BYTES_BUFFER("\n");
+	                for (i = 0; i < 512; i++)
+        	                DUMP_BYTES_BUFFER(" %02x", buf[i]);
+	                DUMP_BYTES_BUFFER("\n");
+        	        printk(KERN_ERR"82 - [0x%x], 85 -
[0x%x]\n",((unsigned short*)data_cmd)[82],((unsigned
short*)data_cmd)[85]);
+#endif
+			/* '6th' Bit in Word 85 Corresponds to Write Cache being Enabled/disabled*/
+                	wce_word = le16_to_cpu(((unsigned short*)data_cmd)[85]);
+	                if (wce_word & 0x20) {
+        	                sdkp->WCE = 1;
+                		sd_printk(KERN_NOTICE, sdkp, "Write Cache Enabled
after ATA_16 response\n");
+			} else
+				goto write_through;
+	        } else {
+write_through:
+	        	sd_printk(KERN_ERR, sdkp, "Assuming drive cache: write through\n");
+		        sdkp->WCE = 0;
+        	}
+	}
 	sdkp->RCD = 0;
 	sdkp->DPOFUA = 0;
 }
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux