[ltp] [PATCH] 2.6.18: Unload disks heads before powering down

Henrique de Moraes Holschuh linux-thinkpad@linux-thinkpad.org
Fri, 29 Sep 2006 12:15:37 -0300


--AqsLC8rIMeq19msA
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

On Sat, 23 Sep 2006, Henrique de Moraes Holschuh wrote:
> Here is a first version of the patch for 2.6.18.

Here's an improved version.  It makes the kernel more verbose on what it is
doing (you will get to know if it flushes caches, or spins down).  It also
band-aid's the reboot issue: disks are not spun down on reboots anymore (at
least not by the *kernel*, the ACPI DSDT or BIOS can still do whatever it
wants).

S3 tested, S4 untested, S5 tested, reboots tested (ata_piix). All other
libata drivers are untested, but ata_piix is the only thing that you'd find
in a ThinkPad, AFAIK.

Original author of these patches: 
Randy Dunlap <randy_d_dunlap@linux.intel.com>

2.6.18 libata EH forward port, and to blame for any bugs:
Henrique de Moraes Holschuh <hmh@hmh.eng.br>.

-- 
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh

--AqsLC8rIMeq19msA
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="01-scsi_scsi-host-shutdown.patch"

Propagate SCSI bus shutdown to SCSI host drivers

From: Randy Dunlap <randy_d_dunlap@linux.intel.com>

Add ability for SCSI drivers to invoke a shutdown method.
This allows drivers to make drives safe for shutdown/poweroff,
for example.  Some drives need this to prevent possible problems.

Signed-off-by: Randy Dunlap <randy_d_dunlap@linux.intel.com>
---
 drivers/scsi/scsi_sysfs.c |   17 +++++++++++++++++
 include/scsi/scsi_host.h  |    1 +
 2 files changed, 18 insertions(+)

Index: 2.6.18/drivers/scsi/scsi_sysfs.c
===================================================================
--- 2.6.18.orig/drivers/scsi/scsi_sysfs.c
+++ 2.6.18/drivers/scsi/scsi_sysfs.c
@@ -303,11 +303,28 @@ static int scsi_bus_resume(struct device
 	return err;
 }
 
+static void scsi_bus_shutdown(struct device * dev)
+{
+	struct scsi_device *sdev = to_scsi_device(dev);
+	struct scsi_host_template *sht = sdev->host->hostt;
+	int err;
+
+	err = scsi_device_quiesce(sdev);
+	if (err)
+		printk(KERN_DEBUG "%s: error (0x%x) during shutdown\n",
+			__FUNCTION__, err);
+
+	if (sht->shutdown)
+		err = sht->shutdown(sdev);
+
+}
+
 struct bus_type scsi_bus_type = {
         .name		= "scsi",
         .match		= scsi_bus_match,
 	.suspend	= scsi_bus_suspend,
 	.resume		= scsi_bus_resume,
+	.shutdown	= scsi_bus_shutdown,
 };
 
 int scsi_sysfs_register(void)
Index: 2.6.18/include/scsi/scsi_host.h
===================================================================
--- 2.6.18.orig/include/scsi/scsi_host.h
+++ 2.6.18/include/scsi/scsi_host.h
@@ -286,6 +286,7 @@ struct scsi_host_template {
 	 */
 	int (*resume)(struct scsi_device *);
 	int (*suspend)(struct scsi_device *, pm_message_t state);
+	int (*shutdown)(struct scsi_device *);
 
 	/*
 	 * Name of proc directory

--AqsLC8rIMeq19msA
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="02-libata_device-shutdown-support.patch"

Add libata device shutdown handler using new EH, v2

This patch is heavily based on a patch by Randy Dunlap
<randy_d_dunlap@linux.intel.com>,  modified to use new libata EH by
Henrique de Moraes Holschuh <hmh@hmh.eng.br>

Instead of adding a new EH operation for shutdown, ATA_EH_SUSPEND is used
to prepare the device for shutdown.

This patch includes a band-aid fix for the lack of information on
system_state (which would require a change to device_shutdown() or a new
function for kernel restarts), as suspending disks on reboot is not
desired due to their spin-down.

Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Cc: Randy Dunlap <randy_d_dunlap@linux.intel.com>

---
 drivers/scsi/libata-core.c |    1 
 drivers/scsi/libata-scsi.c |   64 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/libata.h     |    1 
 3 files changed, 66 insertions(+)

Index: 2.6.18/drivers/scsi/libata-core.c
===================================================================
--- 2.6.18.orig/drivers/scsi/libata-core.c
+++ 2.6.18/drivers/scsi/libata-core.c
@@ -6008,6 +6008,7 @@ EXPORT_SYMBOL_GPL(ata_pci_clear_simplex)
 
 EXPORT_SYMBOL_GPL(ata_scsi_device_suspend);
 EXPORT_SYMBOL_GPL(ata_scsi_device_resume);
+EXPORT_SYMBOL_GPL(ata_scsi_device_shutdown);
 
 EXPORT_SYMBOL_GPL(ata_eng_timeout);
 EXPORT_SYMBOL_GPL(ata_port_schedule_eh);
Index: 2.6.18/drivers/scsi/libata-scsi.c
===================================================================
--- 2.6.18.orig/drivers/scsi/libata-scsi.c
+++ 2.6.18/drivers/scsi/libata-scsi.c
@@ -523,6 +523,70 @@ int ata_scsi_device_resume(struct scsi_d
 }
 
 /**
+ *	ata_scsi_device_shutdown - prepares ATA device associated
+ *	with sdev for shutdown
+ *	@sdev: the SCSI device to resume
+ *
+ *	If the kernel is not restarting, request suspend EH action
+ *	on the ATA device associated with @sdev in order to flush
+ *	its cache and unload heads, and wait for the operation to
+ *	complete.
+ *
+ *	Otherwise, just flush caches directly.
+ *
+ *	LOCKING:
+ *	Kernel thread context (may sleep).
+ *
+ *	RETURNS:
+ *	0.
+ */
+int ata_scsi_device_shutdown(struct scsi_device *sdev)
+{
+	struct ata_port *ap = ata_shost_to_port(sdev->host);
+	struct ata_device *dev = ata_scsi_find_dev(ap, sdev);
+	unsigned long flags;
+	unsigned int action, err_mask;
+
+	if (!dev)
+		return 0;
+
+	/* if @sdev is already detached, nothing to do */
+	if (sdev->sdev_state == SDEV_OFFLINE ||
+	    sdev->sdev_state == SDEV_CANCEL || sdev->sdev_state == SDEV_DEL)
+		return 0;
+
+	/* XXX: fix this when the path from device_shutdown() is fixed to
+	 * let us know what kind of shutdown is being done */
+	if (system_state != SYSTEM_RESTART) {
+		spin_lock_irqsave(ap->lock, flags);
+
+		/* request suspend to flush cache and unload device heads */
+		action = ATA_EH_SUSPEND;
+		ap->eh_info.dev_action[dev->devno] |= action;
+		ap->eh_info.flags |= ATA_EHI_QUIET;
+		ata_port_schedule_eh(ap);
+
+		spin_unlock_irqrestore(ap->lock, flags);
+
+		/* wait for EH to do the job */
+		ata_port_wait_eh(ap);
+	} else {
+		/* try to flush cache */
+		/* XXX: maybe this should be done in EH? And if the drive
+		 * supports UNLOAD IMMEDIATE, maybe that one should be
+		 * issued too? */
+		err_mask = ata_flush_cache(dev);
+		if (err_mask) {
+			ata_dev_printk(dev, KERN_ERR, "failed to flush "
+					"caches (err_mask=0x%x)\n",
+					err_mask);
+		}
+	}
+
+	return 0;
+}
+
+/**
  *	ata_to_sense_error - convert ATA error to SCSI error
  *	@id: ATA device number
  *	@drv_stat: value contained in ATA status register
Index: 2.6.18/include/linux/libata.h
===================================================================
--- 2.6.18.orig/include/linux/libata.h
+++ 2.6.18/include/linux/libata.h
@@ -698,6 +698,7 @@ extern int ata_port_online(struct ata_po
 extern int ata_port_offline(struct ata_port *ap);
 extern int ata_scsi_device_resume(struct scsi_device *);
 extern int ata_scsi_device_suspend(struct scsi_device *, pm_message_t state);
+extern int ata_scsi_device_shutdown(struct scsi_device *);
 extern int ata_host_set_suspend(struct ata_host_set *host_set,
 				pm_message_t mesg);
 extern void ata_host_set_resume(struct ata_host_set *host_set);

--AqsLC8rIMeq19msA
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="03-libata_activate-device-shutdown.patch"

Modify libata drivers to call the new shutdown device path

This patch is heavily based on a patch by
Randy Dunlap <randy_d_dunlap@linux.intel.com>

Only ata_piix is tested.

Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Cc: Randy Dunlap <randy_d_dunlap@linux.intel.com>

---
 drivers/scsi/ahci.c         |    1 +
 drivers/scsi/ata_piix.c     |    1 +
 drivers/scsi/pdc_adma.c     |    1 +
 drivers/scsi/sata_mv.c      |    1 +
 drivers/scsi/sata_nv.c      |    1 +
 drivers/scsi/sata_promise.c |    1 +
 drivers/scsi/sata_qstor.c   |    1 +
 drivers/scsi/sata_sil.c     |    1 +
 drivers/scsi/sata_sil24.c   |    1 +
 drivers/scsi/sata_sis.c     |    1 +
 drivers/scsi/sata_svw.c     |    1 +
 drivers/scsi/sata_sx4.c     |    1 +
 drivers/scsi/sata_uli.c     |    1 +
 drivers/scsi/sata_via.c     |    1 +
 drivers/scsi/sata_vsc.c     |    1 +
 15 files changed, 15 insertions(+)

Index: 2.6.18/drivers/scsi/ata_piix.c
===================================================================
--- 2.6.18.orig/drivers/scsi/ata_piix.c
+++ 2.6.18/drivers/scsi/ata_piix.c
@@ -229,6 +229,7 @@ static struct scsi_host_template piix_sh
 	.bios_param		= ata_std_bios_param,
 	.resume			= ata_scsi_device_resume,
 	.suspend		= ata_scsi_device_suspend,
+	.shutdown		= ata_scsi_device_shutdown,
 };
 
 static const struct ata_port_operations piix_pata_ops = {
Index: 2.6.18/drivers/scsi/sata_sil.c
===================================================================
--- 2.6.18.orig/drivers/scsi/sata_sil.c
+++ 2.6.18/drivers/scsi/sata_sil.c
@@ -183,6 +183,7 @@ static struct scsi_host_template sil_sht
 	.bios_param		= ata_std_bios_param,
 	.suspend		= ata_scsi_device_suspend,
 	.resume			= ata_scsi_device_resume,
+	.shutdown		= ata_scsi_device_shutdown,
 };
 
 static const struct ata_port_operations sil_ops = {
Index: 2.6.18/drivers/scsi/sata_sil24.c
===================================================================
--- 2.6.18.orig/drivers/scsi/sata_sil24.c
+++ 2.6.18/drivers/scsi/sata_sil24.c
@@ -378,6 +378,7 @@ static struct scsi_host_template sil24_s
 	.bios_param		= ata_std_bios_param,
 	.suspend		= ata_scsi_device_suspend,
 	.resume			= ata_scsi_device_resume,
+	.shutdown		= ata_scsi_device_shutdown,
 };
 
 static const struct ata_port_operations sil24_ops = {
Index: 2.6.18/drivers/scsi/ahci.c
===================================================================
--- 2.6.18.orig/drivers/scsi/ahci.c
+++ 2.6.18/drivers/scsi/ahci.c
@@ -231,6 +231,7 @@ static struct scsi_host_template ahci_sh
 	.slave_configure	= ata_scsi_slave_config,
 	.slave_destroy		= ata_scsi_slave_destroy,
 	.bios_param		= ata_std_bios_param,
+	.shutdown		= ata_scsi_device_shutdown,
 };
 
 static const struct ata_port_operations ahci_ops = {
Index: 2.6.18/drivers/scsi/pdc_adma.c
===================================================================
--- 2.6.18.orig/drivers/scsi/pdc_adma.c
+++ 2.6.18/drivers/scsi/pdc_adma.c
@@ -154,6 +154,7 @@ static struct scsi_host_template adma_at
 	.slave_configure	= ata_scsi_slave_config,
 	.slave_destroy		= ata_scsi_slave_destroy,
 	.bios_param		= ata_std_bios_param,
+	.shutdown		= ata_scsi_device_shutdown,
 };
 
 static const struct ata_port_operations adma_ata_ops = {
Index: 2.6.18/drivers/scsi/sata_mv.c
===================================================================
--- 2.6.18.orig/drivers/scsi/sata_mv.c
+++ 2.6.18/drivers/scsi/sata_mv.c
@@ -392,6 +392,7 @@ static struct scsi_host_template mv_sht 
 	.slave_configure	= ata_scsi_slave_config,
 	.slave_destroy		= ata_scsi_slave_destroy,
 	.bios_param		= ata_std_bios_param,
+	.shutdown		= ata_scsi_device_shutdown,
 };
 
 static const struct ata_port_operations mv5_ops = {
Index: 2.6.18/drivers/scsi/sata_nv.c
===================================================================
--- 2.6.18.orig/drivers/scsi/sata_nv.c
+++ 2.6.18/drivers/scsi/sata_nv.c
@@ -170,6 +170,7 @@ static struct scsi_host_template nv_sht 
 	.slave_configure	= ata_scsi_slave_config,
 	.slave_destroy		= ata_scsi_slave_destroy,
 	.bios_param		= ata_std_bios_param,
+	.shutdown		= ata_scsi_device_shutdown,
 };
 
 static const struct ata_port_operations nv_generic_ops = {
Index: 2.6.18/drivers/scsi/sata_promise.c
===================================================================
--- 2.6.18.orig/drivers/scsi/sata_promise.c
+++ 2.6.18/drivers/scsi/sata_promise.c
@@ -123,6 +123,7 @@ static struct scsi_host_template pdc_ata
 	.slave_configure	= ata_scsi_slave_config,
 	.slave_destroy		= ata_scsi_slave_destroy,
 	.bios_param		= ata_std_bios_param,
+	.shutdown		= ata_scsi_device_shutdown,
 };
 
 static const struct ata_port_operations pdc_sata_ops = {
Index: 2.6.18/drivers/scsi/sata_qstor.c
===================================================================
--- 2.6.18.orig/drivers/scsi/sata_qstor.c
+++ 2.6.18/drivers/scsi/sata_qstor.c
@@ -144,6 +144,7 @@ static struct scsi_host_template qs_ata_
 	.slave_configure	= ata_scsi_slave_config,
 	.slave_destroy		= ata_scsi_slave_destroy,
 	.bios_param		= ata_std_bios_param,
+	.shutdown		= ata_scsi_device_shutdown,
 };
 
 static const struct ata_port_operations qs_ata_ops = {
Index: 2.6.18/drivers/scsi/sata_sis.c
===================================================================
--- 2.6.18.orig/drivers/scsi/sata_sis.c
+++ 2.6.18/drivers/scsi/sata_sis.c
@@ -97,6 +97,7 @@ static struct scsi_host_template sis_sht
 	.slave_configure	= ata_scsi_slave_config,
 	.slave_destroy		= ata_scsi_slave_destroy,
 	.bios_param		= ata_std_bios_param,
+	.shutdown		= ata_scsi_device_shutdown,
 };
 
 static const struct ata_port_operations sis_ops = {
Index: 2.6.18/drivers/scsi/sata_svw.c
===================================================================
--- 2.6.18.orig/drivers/scsi/sata_svw.c
+++ 2.6.18/drivers/scsi/sata_svw.c
@@ -303,6 +303,7 @@ static struct scsi_host_template k2_sata
 	.proc_info		= k2_sata_proc_info,
 #endif
 	.bios_param		= ata_std_bios_param,
+	.shutdown		= ata_scsi_device_shutdown,
 };
 
 
Index: 2.6.18/drivers/scsi/sata_sx4.c
===================================================================
--- 2.6.18.orig/drivers/scsi/sata_sx4.c
+++ 2.6.18/drivers/scsi/sata_sx4.c
@@ -193,6 +193,7 @@ static struct scsi_host_template pdc_sat
 	.slave_configure	= ata_scsi_slave_config,
 	.slave_destroy		= ata_scsi_slave_destroy,
 	.bios_param		= ata_std_bios_param,
+	.shutdown		= ata_scsi_device_shutdown,
 };
 
 static const struct ata_port_operations pdc_20621_ops = {
Index: 2.6.18/drivers/scsi/sata_uli.c
===================================================================
--- 2.6.18.orig/drivers/scsi/sata_uli.c
+++ 2.6.18/drivers/scsi/sata_uli.c
@@ -91,6 +91,7 @@ static struct scsi_host_template uli_sht
 	.slave_configure	= ata_scsi_slave_config,
 	.slave_destroy		= ata_scsi_slave_destroy,
 	.bios_param		= ata_std_bios_param,
+	.shutdown		= ata_scsi_device_shutdown,
 };
 
 static const struct ata_port_operations uli_ops = {
Index: 2.6.18/drivers/scsi/sata_via.c
===================================================================
--- 2.6.18.orig/drivers/scsi/sata_via.c
+++ 2.6.18/drivers/scsi/sata_via.c
@@ -107,6 +107,7 @@ static struct scsi_host_template svia_sh
 	.slave_configure	= ata_scsi_slave_config,
 	.slave_destroy		= ata_scsi_slave_destroy,
 	.bios_param		= ata_std_bios_param,
+	.shutdown		= ata_scsi_device_shutdown,
 };
 
 static const struct ata_port_operations vt6420_sata_ops = {
Index: 2.6.18/drivers/scsi/sata_vsc.c
===================================================================
--- 2.6.18.orig/drivers/scsi/sata_vsc.c
+++ 2.6.18/drivers/scsi/sata_vsc.c
@@ -281,6 +281,7 @@ static struct scsi_host_template vsc_sat
 	.slave_configure	= ata_scsi_slave_config,
 	.slave_destroy		= ata_scsi_slave_destroy,
 	.bios_param		= ata_std_bios_param,
+	.shutdown		= ata_scsi_device_shutdown,
 };
 
 

--AqsLC8rIMeq19msA
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="04-libata_log-suspend-and-shutdown.patch"

Log suspend/shutdown activity

Tell the user when we spin down disks on suspend, and when flush caches on
shutdown.  This is useful to track down issues with bandaid stuff in
userspace halt(8), and also double-suspends on suspend-to-disk.

Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
---
---
 drivers/scsi/libata-eh.c   |    4 ++++
 drivers/scsi/libata-scsi.c |    3 +++
 2 files changed, 7 insertions(+)

Index: 2.6.18/drivers/scsi/libata-eh.c
===================================================================
--- 2.6.18.orig/drivers/scsi/libata-eh.c
+++ 2.6.18/drivers/scsi/libata-eh.c
@@ -1717,6 +1717,10 @@ static int ata_eh_suspend(struct ata_por
 		ata_eh_about_to_do(ap, dev, ATA_EH_SUSPEND);
 
 		if (dev->class == ATA_DEV_ATA && !(action & ATA_EH_PM_FREEZE)) {
+			ata_dev_printk(dev, KERN_NOTICE,
+					"flushing cache "
+					"and spinning down...\n");
+
 			/* flush cache */
 			rc = ata_flush_cache(dev);
 			if (rc)
Index: 2.6.18/drivers/scsi/libata-scsi.c
===================================================================
--- 2.6.18.orig/drivers/scsi/libata-scsi.c
+++ 2.6.18/drivers/scsi/libata-scsi.c
@@ -572,6 +572,9 @@ int ata_scsi_device_shutdown(struct scsi
 		ata_port_wait_eh(ap);
 	} else {
 		/* try to flush cache */
+
+		ata_dev_printk(dev, KERN_NOTICE, "flushing caches...\n");
+
 		/* XXX: maybe this should be done in EH? And if the drive
 		 * supports UNLOAD IMMEDIATE, maybe that one should be
 		 * issued too? */

--AqsLC8rIMeq19msA--