diff mbox

[2/2] scsi: retrieve cache mode using ATA_16 cmd if normal routine fails

Message ID 1329375141-6944-1-git-send-email-amit.sahrawat83@gmail.com
State Not Applicable
Headers show

Commit Message

Amit Sahrawat Feb. 16, 2012, 6:52 a.m. UTC
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 cmd 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 |   39 +++++++++++++++++++++++++++++++++++++++
 drivers/scsi/sd.c         |   18 ++++++++++++++++++
 include/linux/libata.h    |    1 +
 3 files changed, 58 insertions(+), 0 deletions(-)

Comments

James Bottomley Feb. 16, 2012, 1:28 p.m. UTC | #1
On Thu, 2012-02-16 at 12:22 +0530, Amit Sahrawat wrote:

> diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
> index c691fb5..3ca507a 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,15 @@ 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
> +

I already said before, this is a non starter: we can't randomly send
SCSI encapsulated ATA commands to disks from sd.  What we can do is give
you a WCE variable you can change on the fly in sysfs via whatever
mechanism is most appropriate (but this would have to be a mechanism
external to sd.c).

Alternatively, you can co-opt the USB quirks handling to set it for you.

James


--
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Namjae Jeon Feb. 16, 2012, 2:07 p.m. UTC | #2
2012/2/16 James Bottomley <James.Bottomley@hansenpartnership.com>:
> On Thu, 2012-02-16 at 12:22 +0530, Amit Sahrawat wrote:
>
>> diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
>> index c691fb5..3ca507a 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,15 @@ 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
>> +
>
> I already said before, this is a non starter: we can't randomly send
> SCSI encapsulated ATA commands to disks from sd.  What we can do is give
> you a WCE variable you can change on the fly in sysfs via whatever
> mechanism is most appropriate (but this would have to be a mechanism
> external to sd.c).
Oops.. it is mistake. we will make patch v2 included setting via sysfs.
Thanks for your reply.
>
> Alternatively, you can co-opt the USB quirks handling to set it for you.
>
> James
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ide" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Amit Sahrawat March 6, 2012, 5:38 p.m. UTC | #3
Hi James,

Few queries relating to solution for this long pending problem.
As per your suggestions - it is clear - we cannot have this as part of
default routine to retrieve WCE using ATA - as for some USB HDD - it
will result in halting their operations.
So,
1) USB quirks -> i think this will only be a workaround for few USB
hard disks - wherein we can their add their entries in the quirks list
and control this WC. So, this wont be a solution which can be used as
part of a long term solution.
2) As part of 'sysfs' interface - there is already an array for
cache_types in sd.c - Can we make an entry in this array for "write
back(ata cmd)" and make corresponding changes - to reflect this when
we do " echo "write back(ata cmd) >
/sys/class/scsi_disk/<>/cache_type".
So, these changes won't be part of default routine - but this will
still need minor changes in sd.c - will those changes be acceptable?
sd_store_cache_type()->revalidate_disk()->sd_revalidate_disk()->sd_read_cache_type()

Please share your opinion to bring out a solution to this.

Thanks & Regards,
Amit Sahrawat

On Thu, Feb 16, 2012 at 6:58 PM, James Bottomley
<James.Bottomley@hansenpartnership.com> wrote:
> On Thu, 2012-02-16 at 12:22 +0530, Amit Sahrawat wrote:
>
>> diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
>> index c691fb5..3ca507a 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,15 @@ 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
>> +
>
> I already said before, this is a non starter: we can't randomly send
> SCSI encapsulated ATA commands to disks from sd.  What we can do is give
> you a WCE variable you can change on the fly in sysfs via whatever
> mechanism is most appropriate (but this would have to be a mechanism
> external to sd.c).
>
> Alternatively, you can co-opt the USB quirks handling to set it for you.
>
> James
>
>
--
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 508a60b..218df4f 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -561,6 +561,45 @@  error:
 	return rc;
 }
 
+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;
+	int cmd_result;
+	sensebuf = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_NOIO);
+	if (!sensebuf)
+		return rc;
+
+	argbuf = kzalloc(ATA_SECT_SIZE, GFP_KERNEL);
+	if (!argbuf)
+		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_CMD_ID_ATA;
+
+	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
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index c691fb5..3ca507a 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,15 @@  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..20c5c33 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -990,6 +990,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);