All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC] libata-core: do something about flush cache
@ 2008-02-20 21:45 Alan Cox
  0 siblings, 0 replies; only message in thread
From: Alan Cox @ 2008-02-20 21:45 UTC (permalink / raw)
  To: jeff, linux-ide

The IDE layers assume a failed flush cache is a simple error, but it is
actually far more complicated. The spec says that an error is returned by
flush cache if a sector cannot be committed to disc. In that case the
failed sector is dropped from cache and the command reports the bad
sector. It does *not* automatically flush other sectors. Instead we must
re-issue the flush.

Signed-off-by: Alan Cox <alan@redhat.com>

diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla-2.6.25-rc2-mm1/drivers/ata/libata-core.c linux-2.6.25-rc2-mm1/drivers/ata/libata-core.c
--- linux.vanilla-2.6.25-rc2-mm1/drivers/ata/libata-core.c	2008-02-19 11:03:26.000000000 +0000
+++ linux-2.6.25-rc2-mm1/drivers/ata/libata-core.c	2008-02-20 11:56:25.000000000 +0000
@@ -6477,10 +6477,30 @@
 	return 0;
 }
 
+static void ata_flush_cache_error(u8 cmd, struct ata_device *dev,
+						struct ata_taskfile *tf)
+{
+	char *df = "";
+	u64 lba;
+
+	if (tf->command & ATA_DF)
+		df = " (device fault)";
+
+	if (cmd == ATA_CMD_FLUSH)
+		lba = ata_tf_to_lba(tf);
+	else
+		lba = ata_tf_to_lba48(tf);
+	
+	ata_dev_printk(dev, KERN_WARNING, "flush of sector %lld failed%s.\n",
+				lba, df);
+}
+
 int ata_flush_cache(struct ata_device *dev)
 {
 	unsigned int err_mask;
 	u8 cmd;
+	struct ata_taskfile tf;
+	int i;
 
 	if (!ata_try_flush_cache(dev))
 		return 0;
@@ -6490,17 +6510,32 @@
 	else
 		cmd = ATA_CMD_FLUSH;
 
-	/* This is wrong. On a failed flush we get back the LBA of the lost
-	   sector and we should (assuming it wasn't aborted as unknown) issue
-	   a further flush command to continue the writeback until it
-	   does not error */
-	err_mask = ata_do_simple_cmd(dev, cmd);
-	if (err_mask) {
-		ata_dev_printk(dev, KERN_ERR, "failed to flush cache\n");
-		return -EIO;
-	}
+	/* 20 failed flush sectors and we abandon hope. This is a heuristic
+	   we can twiddle later */
+	for (i = 0; i < 20; i++) {
+		ata_tf_init(dev, &tf);
+		tf.command = cmd;
+		tf.flags |= ATA_TFLAG_DEVICE;
+		tf.protocol = ATA_PROT_NODATA;
 
-	return 0;
+		err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0);
+
+		/* Flush completed without further errors */
+		if (err_mask == 0)
+			return 0;
+		/* Non device errors cannot be fixed by a retry at this
+		   level of the system */
+		if (err_mask != AC_ERR_DEV) {
+			ata_dev_printk(dev, KERN_ERR, "cache flush failed.\n");
+			return -EIO;
+		}
+		/* On a media error the bad block is reported and the flush
+		   stops. It will continue from after this sector when we
+		   reissue the command */
+		ata_flush_cache_error(cmd, dev, &tf);
+	}
+	ata_dev_printk(dev, KERN_ERR, "cache flush repeatedly failed.\n");
+	return -EIO;
 }
 
 #ifdef CONFIG_PM

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2008-02-20 21:54 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-02-20 21:45 [PATCH RFC] libata-core: do something about flush cache Alan Cox

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.