LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [ugly patch] Save .15W-.5W by AHCI powersaving
@ 2008-02-25 13:45 Pavel Machek
  2008-02-25 22:26 ` Mark Lord
  0 siblings, 1 reply; 8+ messages in thread
From: Pavel Machek @ 2008-02-25 13:45 UTC (permalink / raw)
  To: kernel list, Linux-pm mailing list, stern; +Cc: linux-ide

[-- Attachment #1: Type: text/plain, Size: 5998 bytes --]

Hi!

This is a patch (very ugly, assumes you have just one disk) to bring
powersaving to AHCI. You need Alan's SCSI autosuspend (attached) patch
as a base.

It saves .5W compared to config with disk spinning, and even .15W
compared to hdparm -y... on my thinkpad x60 anyway.

It is also mandatory first step towards sleepy linux ;-).

								Pavel
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 29e71bd..0197b1f 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -259,8 +260,8 @@ static void ahci_fill_cmd_slot(struct ah
 			       u32 opts);
 #ifdef CONFIG_PM
 static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg);
-static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg);
-static int ahci_pci_device_resume(struct pci_dev *pdev);
+int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg);
+int ahci_pci_device_resume(struct pci_dev *pdev);
 #endif
 
 static struct class_device_attribute *ahci_shost_attrs[] = {
@@ -268,6 +269,35 @@ static struct class_device_attribute *ah
 	NULL
 };
 
+struct pci_dev *my_pdev;
+int autosuspend_enabled;
+
+/* The host and its devices are all idle so we can autosuspend */
+static int autosuspend(struct Scsi_Host *host)
+{
+	if (my_pdev && autosuspend_enabled) {
+		printk("ahci: should autosuspend\n");
+		ahci_pci_device_suspend(my_pdev, PMSG_SUSPEND);
+		return 0;
+	} 
+	printk("ahci: autosuspend disabled\n");
+	return -EINVAL;
+}
+
+/* The host needs to be autoresumed */
+static int autoresume(struct Scsi_Host *host)
+{
+	if (my_pdev && autosuspend_enabled) {
+		printk("ahci: should autoresume\n");
+		ahci_pci_device_resume(my_pdev);
+		return 0;
+	}
+	printk("ahci: autoresume disabled\n");
+	return -EINVAL;
+}
+
+
+
 static struct scsi_host_template ahci_sht = {
 	.module			= THIS_MODULE,
 	.name			= DRV_NAME,
@@ -286,6 +322,8 @@ static struct scsi_host_template ahci_sh
 	.slave_destroy		= ata_scsi_slave_destroy,
 	.bios_param		= ata_std_bios_param,
 	.shost_attrs		= ahci_shost_attrs,
+	.autosuspend		= autosuspend,
+	.autoresume		= autoresume,
 };
 
 static const struct ata_port_operations ahci_ops = {
@@ -2300,8 +2356,12 @@ static int ahci_init_one(struct pci_dev 
 	ahci_print_info(host);
 
 	pci_set_master(pdev);
-	return ata_host_activate(host, pdev->irq, ahci_interrupt, IRQF_SHARED,
+
+	rc = ata_host_activate(host, pdev->irq, ahci_interrupt, IRQF_SHARED,
 				 &ahci_sht);
+	pci_save_state(pdev);
+	my_pdev = pdev;
+	return rc;
 }
 
 static int __init ahci_init(void)
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 4e31071..5c40ac2 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -380,7 +381,7 @@ enum scsi_eh_timer_return ata_scsi_timed
  *	Inherited from SCSI layer (none, can sleep)
  *
  *	RETURNS:
- *	Zero.
+ *	Nothing.
  */
 void ata_scsi_error(struct Scsi_Host *host)
 {
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index c838e65..0edc25e 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -484,6 +484,8 @@ static int scsi_try_host_reset(struct sc
 	if (scsi_autoresume_host(shost) != 0)
 		return FAILED;
 
+	rtn = shost->hostt->eh_host_reset_handler(scmd);
+
 	if (rtn == SUCCESS) {
 		if (!shost->hostt->skip_settle_delay)
 			ssleep(HOST_RESET_SETTLE_TIME);
@@ -1577,7 +1579,11 @@ int scsi_error_handler(void *data)
 		 * what we need to do to get it up and online again (if we can).
 		 * If we fail, we end up taking the thing offline.
 		 */
+#if 0
+		/* libata uses scsi_error_handler to suspend its parts; we deadlock
+		   if we try to autoresume here */
 		autoresume_rc = scsi_autoresume_host(shost);
+#endif
 		if (shost->transportt->eh_strategy_handler)
 			shost->transportt->eh_strategy_handler(shost);
 		else
@@ -1591,8 +1597,10 @@ int scsi_error_handler(void *data)
 		 * which are still online.
 		 */
 		scsi_restart_operations(shost);
+#if 0
 		if (autoresume_rc == 0)
 			scsi_autosuspend_host(shost);
+#endif
 		set_current_state(TASK_INTERRUPTIBLE);
 	}
 	__set_current_state(TASK_RUNNING);
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 233feee..3c598e0 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -67,6 +67,8 @@ #undef SP
 
 static struct kmem_cache *scsi_bidi_sdb_cache;
 
+void scsi_run_queue(struct request_queue *q);
+
 /*
  * Function:	scsi_unprep_request()
  *
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 5393e15..25bebc1 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -340,6 +340,21 @@ static int scsi_bus_uevent(struct device
 	return 0;
 }
 
+static int scsi_bus_remove(struct device *dev)
+{
+	struct device_driver *drv = dev->driver;
+	struct scsi_device *sdev = to_scsi_device(dev);
+	int err = 0;
+
+	/* reset the prep_fn back to the default since the
+	 * driver may have altered it and it's being removed */
+	blk_queue_prep_rq(sdev->request_queue, scsi_prep_fn);
+
+	if (drv && drv->remove)
+		err = drv->remove(dev);
+
+	return 0;
+}
 
 struct bus_type scsi_bus_type = {
         .name		= "scsi",
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 16add9a..9ca12d1 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -1596,6 +1596,8 @@ static int sd_revalidate_disk(struct gen
 	return 0;
 }
 
+struct device *my_scsi_disk; 
+
 /**
  *	sd_probe - called during driver initialization and whenever a
  *	new scsi device is attached to the system. It is called once
@@ -1715,6 +1717,7 @@ static int sd_probe(struct device *dev)
 
 	scsi_use_ULD_pm(sdp, 1);
 	scsi_autosuspend_device(sdp);
+	my_scsi_disk = dev;
 	return 0;
 
  out_suspend:
@@ -1835,6 +1838,8 @@ static int sd_suspend(struct device *dev
 	struct scsi_disk *sdkp = scsi_disk_get_from_dev(dev);
 	int ret = 0;
 
+	printk("sleepy: sd_suspend start\n");
+
 	if (!sdkp)
 		return 0;	/* this can happen */
 



-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #2: delme2 --]
[-- Type: text/plain, Size: 28306 bytes --]

From: Alan Stern <stern@rowland.harvard.edu>

Alan Stern's autosuspend-for-SCSI patches.

---
commit 8b534a6dc550b4115627e35535f07753d5612800
tree a8e2b2eafb680fb8c5c5bcad049d29f23f576b43
parent 9c269a8b0695a4b18d36b918cb14b757f73e6a49
author Pavel <pavel@amd.ucw.cz> Mon, 25 Feb 2008 13:43:18 +0100
committer Pavel <pavel@amd.ucw.cz> Mon, 25 Feb 2008 13:43:18 +0100

 Documentation/scsi/scsi_mid_low_api.txt |   50 ++++++++++++++++++++++++++++
 drivers/scsi/Kconfig                    |   12 +++++++
 drivers/scsi/Makefile                   |    3 +-
 drivers/scsi/hosts.c                    |   20 ++++-------
 drivers/scsi/scsi.c                     |    8 ++++
 drivers/scsi/scsi_error.c               |   36 +++++++++++++-------
 drivers/scsi/scsi_lib.c                 |    7 ++--
 drivers/scsi/scsi_priv.h                |   38 +++++++++++++++++++++
 drivers/scsi/scsi_scan.c                |    9 +++++
 drivers/scsi/scsi_sysfs.c               |   56 ++++---------------------------
 drivers/scsi/sd.c                       |   12 ++++++-
 drivers/scsi/sg.c                       |   10 ++++++
 drivers/usb/storage/scsiglue.c          |   35 +++++++++++++++++--
 drivers/usb/storage/usb.c               |   15 ++++----
 include/scsi/scsi_device.h              |   23 ++++++++++++-
 include/scsi/scsi_host.h                |   24 +++++++++++++
 16 files changed, 264 insertions(+), 94 deletions(-)

diff --git a/Documentation/scsi/scsi_mid_low_api.txt b/Documentation/scsi/scsi_mid_low_api.txt
index a6d5354..98dc005 100644
--- a/Documentation/scsi/scsi_mid_low_api.txt
+++ b/Documentation/scsi/scsi_mid_low_api.txt
@@ -782,6 +782,8 @@ In some cases more detail is given in sc
 The interface functions are listed below in alphabetical order.
 
 Summary:
+   autoresume - perform dynamic (runtime) host resume
+   autosuspend - perform dynamic (runtime) host suspend
    bios_param - fetch head, sector, cylinder info for a disk
    detect - detects HBAs this driver wants to control
    eh_timed_out - notify the host that a command timer expired
@@ -802,6 +804,54 @@ Summary:
 Details:
 
 /**
+ *	autoresume - perform dynamic (runtime) host resume
+ *	@shp: host to resume
+ *
+ *	Resume (return to an operational power level) the specified host.
+ *	Return 0 if the resume was successful, otherwise a negative
+ *	error code.
+ *
+ *	Locks: struct Scsi_Host::pm_mutex held throughout the call.
+ *
+ *	Calling context: process
+ *
+ *	Notes: If the host is not currently suspended, this method does
+ *	need to do anything.
+ *
+ *	Optionally defined in: LLD
+ **/
+    int autoresume(struct Scsi_Host *shp)
+
+
+/**
+ *	autosuspend - perform dynamic (runtime) host suspend
+ *	@shp: host to suspend
+ *
+ *	Suspend (change to a non-operational low-power state) the
+ *	specified host.
+ *	Return 0 if the suspend was successful (or was successfully
+ *	queued, or was successfully ignored), otherwise a negative
+ *	error code.
+ *
+ *	Locks: struct Scsi_Host::pm_mutex held throughout the call.
+ *
+ *	Calling context: process
+ *
+ *	Notes: The suspend need not be carried out immediately (or indeed
+ *	at all); it may be delayed indefinitely.  The real meaning of this
+ *	method call is that all of the host's devices are now idle.  It can
+ *	happen that an autosuspend is quickly followed by an autoresume,
+ *	so it is beneficial if the suspend is delayed by a few seconds.
+ *		A host is assumed to be at full power (resumed) when it is
+ *	first created.  In the absence of errors, the LLD will receive a
+ *	strictly alternating sequence of autosuspend, autoresume,... calls.
+ *
+ *	Optionally defined in: LLD
+ **/
+    int autosuspend(struct Scsi_Host *shp)
+
+
+/**
  *      bios_param - fetch head, sector, cylinder info for a disk
  *      @sdev: pointer to scsi device context (defined in 
  *             include/scsi/scsi_device.h)
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index a7a0813..9bada81 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -57,6 +57,18 @@ config SCSI_PROC_FS
 
 	  If unsure say Y.
 
+config SCSI_DYNAMIC_PM
+	bool "SCSI dynamic Power Management support (EXPERIMENTAL)"
+	depends on SCSI && PM && EXPERIMENTAL
+	---help---
+	  This option enables support for dynamic (or runtime)
+	  power management of SCSI devices and host adapters.
+	  If you say Y here, you can use the sysfs "power/level"
+	  and "power/autosuspend" files to control manual or
+	  automatic suspend/resume of individual SCSI devices.
+
+	  If unsure say N.
+
 comment "SCSI support type (disk, tape, CD-ROM)"
 	depends on SCSI
 
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 925c26b..634a0e4 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -139,7 +139,8 @@ obj-$(CONFIG_SCSI_WAIT_SCAN)	+= scsi_wai
 scsi_mod-y			+= scsi.o hosts.o scsi_ioctl.o constants.o \
 				   scsicam.o scsi_error.o scsi_lib.o
 scsi_mod-$(CONFIG_SCSI_DMA)	+= scsi_lib_dma.o
-scsi_mod-y			+= scsi_scan.o scsi_sysfs.o scsi_devinfo.o
+scsi_mod-y			+= scsi_scan.o scsi_sysfs.o scsi_devinfo.o \
+				   scsi_pm.o
 scsi_mod-$(CONFIG_SCSI_NETLINK)	+= scsi_netlink.o
 scsi_mod-$(CONFIG_SYSCTL)	+= scsi_sysctl.o
 scsi_mod-$(CONFIG_SCSI_PROC_FS)	+= scsi_proc.o
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index 880c78b..250cdab 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -154,24 +154,15 @@ EXPORT_SYMBOL(scsi_host_set_state);
  **/
 void scsi_remove_host(struct Scsi_Host *shost)
 {
-	unsigned long flags;
-	mutex_lock(&shost->scan_mutex);
-	spin_lock_irqsave(shost->host_lock, flags);
-	if (scsi_host_set_state(shost, SHOST_CANCEL))
-		if (scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY)) {
-			spin_unlock_irqrestore(shost->host_lock, flags);
-			mutex_unlock(&shost->scan_mutex);
-			return;
-		}
-	spin_unlock_irqrestore(shost->host_lock, flags);
-	mutex_unlock(&shost->scan_mutex);
+	if (scsi_pm_host_stop(shost))
+		return;
 	scsi_forget_host(shost);
 	scsi_proc_host_rm(shost);
 
-	spin_lock_irqsave(shost->host_lock, flags);
+	spin_lock_irq(shost->host_lock);
 	if (scsi_host_set_state(shost, SHOST_DEL))
 		BUG_ON(scsi_host_set_state(shost, SHOST_DEL_RECOVERY));
-	spin_unlock_irqrestore(shost->host_lock, flags);
+	spin_unlock_irq(shost->host_lock);
 
 	transport_unregister_device(&shost->shost_gendev);
 	class_device_unregister(&shost->shost_classdev);
@@ -236,6 +227,7 @@ int scsi_add_host(struct Scsi_Host *shos
 	if (error)
 		goto out_destroy_host;
 
+	scsi_autosuspend_host(shost);
 	scsi_proc_host_add(shost);
 	return error;
 
@@ -386,6 +378,8 @@ struct Scsi_Host *scsi_host_alloc(struct
 	snprintf(shost->shost_classdev.class_id, BUS_ID_SIZE, "host%d",
 		  shost->host_no);
 
+	scsi_pm_host_initialize(shost);
+
 	shost->ehandler = kthread_run(scsi_error_handler, shost,
 			"scsi_eh_%d", shost->host_no);
 	if (IS_ERR(shost->ehandler)) {
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index fecba05..4fa9db4 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -1133,15 +1133,20 @@ static int __init init_scsi(void)
 	error = scsi_init_sysctl();
 	if (error)
 		goto cleanup_hosts;
-	error = scsi_sysfs_register();
+	error = scsi_init_pm();
 	if (error)
 		goto cleanup_sysctl;
+	error = scsi_sysfs_register();
+	if (error)
+		goto cleanup_pm;
 
 	scsi_netlink_init();
 
 	printk(KERN_NOTICE "SCSI subsystem initialized\n");
 	return 0;
 
+cleanup_pm:
+	scsi_exit_pm();
 cleanup_sysctl:
 	scsi_exit_sysctl();
 cleanup_hosts:
@@ -1161,6 +1166,7 @@ static void __exit exit_scsi(void)
 {
 	scsi_netlink_exit();
 	scsi_sysfs_unregister();
+	scsi_exit_pm();
 	scsi_exit_sysctl();
 	scsi_exit_hosts();
 	scsi_exit_devinfo();
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 045a086..c838e65 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -468,30 +468,32 @@ static void scsi_eh_done(struct scsi_cmn
 
 /**
  * scsi_try_host_reset - ask host adapter to reset itself
- * @scmd:	SCSI cmd to send hsot reset.
+ * @scmd:	SCSI cmd to send host reset.
  */
 static int scsi_try_host_reset(struct scsi_cmnd *scmd)
 {
 	unsigned long flags;
 	int rtn;
+	struct Scsi_Host *shost = scmd->device->host;
 
 	SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Host RST\n",
 					  __FUNCTION__));
 
 	if (!scmd->device->host->hostt->eh_host_reset_handler)
 		return FAILED;
-
-	rtn = scmd->device->host->hostt->eh_host_reset_handler(scmd);
+	if (scsi_autoresume_host(shost) != 0)
+		return FAILED;
 
 	if (rtn == SUCCESS) {
-		if (!scmd->device->host->hostt->skip_settle_delay)
+		if (!shost->hostt->skip_settle_delay)
 			ssleep(HOST_RESET_SETTLE_TIME);
-		spin_lock_irqsave(scmd->device->host->host_lock, flags);
-		scsi_report_bus_reset(scmd->device->host,
+		spin_lock_irqsave(shost->host_lock, flags);
+		scsi_report_bus_reset(shost,
 				      scmd_channel(scmd));
-		spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
+		spin_unlock_irqrestore(shost->host_lock, flags);
 	}
 
+	scsi_autosuspend_host(shost);
 	return rtn;
 }
 
@@ -503,24 +505,28 @@ static int scsi_try_bus_reset(struct scs
 {
 	unsigned long flags;
 	int rtn;
+	struct Scsi_Host *shost = scmd->device->host;
 
 	SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Bus RST\n",
 					  __FUNCTION__));
 
-	if (!scmd->device->host->hostt->eh_bus_reset_handler)
+	if (!shost->hostt->eh_bus_reset_handler)
+		return FAILED;
+	if (scsi_autoresume_host(shost) != 0)
 		return FAILED;
 
-	rtn = scmd->device->host->hostt->eh_bus_reset_handler(scmd);
+	rtn = shost->hostt->eh_bus_reset_handler(scmd);
 
 	if (rtn == SUCCESS) {
-		if (!scmd->device->host->hostt->skip_settle_delay)
+		if (!shost->hostt->skip_settle_delay)
 			ssleep(BUS_RESET_SETTLE_TIME);
-		spin_lock_irqsave(scmd->device->host->host_lock, flags);
-		scsi_report_bus_reset(scmd->device->host,
+		spin_lock_irqsave(shost->host_lock, flags);
+		scsi_report_bus_reset(shost,
 				      scmd_channel(scmd));
-		spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
+		spin_unlock_irqrestore(shost->host_lock, flags);
 	}
 
+	scsi_autosuspend_host(shost);
 	return rtn;
 }
 
@@ -1541,6 +1547,7 @@ static void scsi_unjam_host(struct Scsi_
 int scsi_error_handler(void *data)
 {
 	struct Scsi_Host *shost = data;
+	int autoresume_rc;
 
 	/*
 	 * We use TASK_INTERRUPTIBLE so that the thread is not
@@ -1570,6 +1577,7 @@ int scsi_error_handler(void *data)
 		 * what we need to do to get it up and online again (if we can).
 		 * If we fail, we end up taking the thing offline.
 		 */
+		autoresume_rc = scsi_autoresume_host(shost);
 		if (shost->transportt->eh_strategy_handler)
 			shost->transportt->eh_strategy_handler(shost);
 		else
@@ -1583,6 +1591,8 @@ int scsi_error_handler(void *data)
 		 * which are still online.
 		 */
 		scsi_restart_operations(shost);
+		if (autoresume_rc == 0)
+			scsi_autosuspend_host(shost);
 		set_current_state(TASK_INTERRUPTIBLE);
 	}
 	__set_current_state(TASK_RUNNING);
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 135c1d0..233feee 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -67,8 +67,6 @@ #undef SP
 
 static struct kmem_cache *scsi_bidi_sdb_cache;
 
-static void scsi_run_queue(struct request_queue *q);
-
 /*
  * Function:	scsi_unprep_request()
  *
@@ -461,6 +459,7 @@ void scsi_device_unbusy(struct scsi_devi
 	spin_unlock(shost->host_lock);
 	spin_lock(sdev->request_queue->queue_lock);
 	sdev->device_busy--;
+	scsi_mark_last_busy(sdev);
 	spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
 }
 
@@ -522,7 +521,7 @@ static void scsi_single_lun_run(struct s
  * Notes:	The previous command was completely finished, start
  *		a new one if possible.
  */
-static void scsi_run_queue(struct request_queue *q)
+void scsi_run_queue(struct request_queue *q)
 {
 	struct scsi_device *sdev = q->queuedata;
 	struct Scsi_Host *shost = sdev->host;
@@ -1201,6 +1200,8 @@ int scsi_prep_state_check(struct scsi_de
 			ret = BLKPREP_KILL;
 			break;
 		case SDEV_QUIESCE:
+			ret = scsi_pm_state_check(sdev, req);
+			break;
 		case SDEV_BLOCK:
 			/*
 			 * If the devices is blocked we defer normal commands.
diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h
index 3f34e93..c331de4 100644
--- a/drivers/scsi/scsi_priv.h
+++ b/drivers/scsi/scsi_priv.h
@@ -4,11 +4,13 @@ #define _SCSI_PRIV_H
 #include <linux/device.h>
 
 struct request_queue;
+struct request;
 struct scsi_cmnd;
 struct scsi_device;
 struct scsi_host_template;
 struct Scsi_Host;
 struct scsi_nl_hdr;
+struct workqueue_struct;
 
 
 /*
@@ -67,6 +69,7 @@ int scsi_eh_get_sense(struct list_head *
 extern int scsi_maybe_unblock_host(struct scsi_device *sdev);
 extern void scsi_device_unbusy(struct scsi_device *sdev);
 extern int scsi_queue_insert(struct scsi_cmnd *cmd, int reason);
+extern void scsi_run_queue(struct request_queue *q);
 extern void scsi_next_command(struct scsi_cmnd *cmd);
 extern void scsi_io_completion(struct scsi_cmnd *, unsigned int);
 extern void scsi_run_host_queues(struct Scsi_Host *shost);
@@ -132,6 +135,41 @@ static inline void scsi_netlink_init(voi
 static inline void scsi_netlink_exit(void) {}
 #endif
 
+/* scsi_pm.c */
+extern int scsi_bus_suspend(struct device *, pm_message_t);
+extern int scsi_bus_resume(struct device *);
+extern int scsi_pm_state_check(struct scsi_device *, struct request *);
+extern int scsi_pm_device_stop(struct scsi_device *);
+extern int scsi_pm_host_stop(struct Scsi_Host *);
+#ifdef CONFIG_SCSI_DYNAMIC_PM
+extern void scsi_autosuspend_host(struct Scsi_Host *);
+extern int scsi_autoresume_host(struct Scsi_Host *);
+extern void scsi_pm_host_initialize(struct Scsi_Host *);
+extern void scsi_mark_last_busy(struct scsi_device *);
+extern void scsi_use_ULD_pm(struct scsi_device *, int);
+extern void scsi_autosuspend_device(struct scsi_device *);
+extern int scsi_autoresume_device(struct scsi_device *);
+extern int scsi_pm_create_device_files(struct scsi_device *);
+extern void scsi_pm_device_initialize(struct scsi_device *);
+extern int scsi_init_pm(void);
+extern void scsi_exit_pm(void);
+#else
+static inline void scsi_autosuspend_host(struct Scsi_Host *shost)	{}
+static inline int scsi_autoresume_host(struct Scsi_Host *shost)
+					{ return 0; }
+static inline void scsi_pm_host_initialize(struct Scsi_Host *shost)	{}
+static inline void scsi_mark_last_busy(struct scsi_device *sdev)	{}
+static inline void scsi_use_ULD_pm(struct scsi_device *sdev, int v)	{}
+static inline void scsi_autosuspend_device(struct scsi_device *sdev)	{}
+static inline int scsi_autoresume_device(struct scsi_device *sdev)
+					{ return 0; }
+static inline int scsi_pm_create_device_files(struct scsi_device *sdev)
+					{ return 0; }
+static inline void scsi_pm_device_initialize(struct scsi_device *sdev)	{}
+static inline int scsi_init_pm(void)	{ return 0; }
+static inline void scsi_exit_pm(void)	{}
+#endif /* CONFIG_SCSI_DYNAMIC_PM */
+
 /* 
  * internal scsi timeout functions: for use by mid-layer and transport
  * classes.
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 1dc165a..c48a143 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -297,6 +297,9 @@ static struct scsi_device *scsi_alloc_sd
 	scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
 
 	scsi_sysfs_device_initialize(sdev);
+	scsi_pm_device_initialize(sdev);
+	if (scsi_autoresume_host(shost) != 0)
+		goto out_device_destroy;
 
 	if (shost->hostt->slave_alloc) {
 		ret = shost->hostt->slave_alloc(sdev);
@@ -307,6 +310,7 @@ static struct scsi_device *scsi_alloc_sd
 			 */
 			if (ret == -ENXIO)
 				display_failure_msg = 0;
+			scsi_autosuspend_host(shost);
 			goto out_device_destroy;
 		}
 	}
@@ -922,6 +926,7 @@ static int scsi_add_lun(struct scsi_devi
 static inline void scsi_destroy_sdev(struct scsi_device *sdev)
 {
 	scsi_device_set_state(sdev, SDEV_DEL);
+	scsi_autosuspend_host(sdev->host);
 	if (sdev->host->hostt->slave_destroy)
 		sdev->host->hostt->slave_destroy(sdev);
 	transport_destroy_device(&sdev->sdev_gendev);
@@ -1084,6 +1089,7 @@ static int scsi_probe_and_add_lun(struct
 
 	res = scsi_add_lun(sdev, result, &bflags, shost->async_scan);
 	if (res == SCSI_SCAN_LUN_PRESENT) {
+		scsi_autosuspend_device(sdev);
 		if (bflags & BLIST_KEY) {
 			sdev->lockable = 0;
 			scsi_unlock_floptical(sdev, result);
@@ -1785,6 +1791,8 @@ static void scsi_finish_async_scan(struc
 
 static void do_scsi_scan_host(struct Scsi_Host *shost)
 {
+	if (scsi_autoresume_host(shost) != 0)
+		return;
 	if (shost->hostt->scan_finished) {
 		unsigned long start = jiffies;
 		if (shost->hostt->scan_start)
@@ -1796,6 +1804,7 @@ static void do_scsi_scan_host(struct Scs
 		scsi_scan_host_selected(shost, SCAN_WILD_CARD, SCAN_WILD_CARD,
 				SCAN_WILD_CARD, 0);
 	}
+	scsi_autosuspend_host(shost);
 }
 
 static int do_scan_async(void *_data)
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index ed83cdb..5393e15 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -340,54 +340,6 @@ static int scsi_bus_uevent(struct device
 	return 0;
 }
 
-static int scsi_bus_suspend(struct device * dev, pm_message_t state)
-{
-	struct device_driver *drv = dev->driver;
-	struct scsi_device *sdev = to_scsi_device(dev);
-	int err;
-
-	err = scsi_device_quiesce(sdev);
-	if (err)
-		return err;
-
-	if (drv && drv->suspend) {
-		err = drv->suspend(dev, state);
-		if (err)
-			return err;
-	}
-
-	return 0;
-}
-
-static int scsi_bus_resume(struct device * dev)
-{
-	struct device_driver *drv = dev->driver;
-	struct scsi_device *sdev = to_scsi_device(dev);
-	int err = 0;
-
-	if (drv && drv->resume)
-		err = drv->resume(dev);
-
-	scsi_device_resume(sdev);
-
-	return err;
-}
-
-static int scsi_bus_remove(struct device *dev)
-{
-	struct device_driver *drv = dev->driver;
-	struct scsi_device *sdev = to_scsi_device(dev);
-	int err = 0;
-
-	/* reset the prep_fn back to the default since the
-	 * driver may have altered it and it's being removed */
-	blk_queue_prep_rq(sdev->request_queue, scsi_prep_fn);
-
-	if (drv && drv->remove)
-		err = drv->remove(dev);
-
-	return 0;
-}
 
 struct bus_type scsi_bus_type = {
         .name		= "scsi",
@@ -814,6 +766,12 @@ int scsi_sysfs_add_sdev(struct scsi_devi
 		goto out;
 	}
 
+	error = scsi_pm_create_device_files(sdev);
+	if (error) {
+		__scsi_remove_device(sdev);
+		goto out;
+	}
+
 	error = bsg_register_queue(rq, &sdev->sdev_gendev, NULL);
 
 	if (error)
@@ -854,7 +812,7 @@ void __scsi_remove_device(struct scsi_de
 {
 	struct device *dev = &sdev->sdev_gendev;
 
-	if (scsi_device_set_state(sdev, SDEV_CANCEL) != 0)
+	if (scsi_pm_device_stop(sdev) != 0)
 		return;
 
 	bsg_unregister_queue(sdev->request_queue);
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 37df8bb..16add9a 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -61,6 +61,7 @@ #include <scsi/scsicam.h>
 #include <scsi/sd.h>
 
 #include "scsi_logging.h"
+#include "scsi_priv.h"
 
 MODULE_AUTHOR("Eric Youngdale");
 MODULE_DESCRIPTION("SCSI disk (sd) driver");
@@ -1649,6 +1650,10 @@ static int sd_probe(struct device *dev)
 	if (error)
 		goto out_put;
 
+	error = scsi_autoresume_device(sdp);
+	if (error)
+		goto out_put;
+
 	sdkp->device = sdp;
 	sdkp->driver = &sd_template;
 	sdkp->disk = gd;
@@ -1668,7 +1673,7 @@ static int sd_probe(struct device *dev)
 	strncpy(sdkp->cdev.class_id, sdp->sdev_gendev.bus_id, BUS_ID_SIZE);
 
 	if (class_device_add(&sdkp->cdev))
-		goto out_put;
+		goto out_suspend;
 
 	get_device(&sdp->sdev_gendev);
 
@@ -1708,8 +1713,12 @@ static int sd_probe(struct device *dev)
 	sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n",
 		  sdp->removable ? "removable " : "");
 
+	scsi_use_ULD_pm(sdp, 1);
+	scsi_autosuspend_device(sdp);
 	return 0;
 
+ out_suspend:
+	scsi_autosuspend_device(sdp);
  out_put:
 	put_disk(gd);
  out_free:
@@ -1735,6 +1744,7 @@ static int sd_remove(struct device *dev)
 
 	class_device_del(&sdkp->cdev);
 	del_gendisk(sdkp->disk);
+	scsi_use_ULD_pm(sdkp->device, 0);
 	sd_shutdown(dev);
 
 	mutex_lock(&sd_ref_mutex);
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index e5156aa..27e3669 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -57,6 +57,7 @@ #include <scsi/scsi_driver.h>
 #include <scsi/scsi_ioctl.h>
 #include <scsi/sg.h>
 
+#include "scsi_priv.h"
 #include "scsi_logging.h"
 
 #ifdef CONFIG_SCSI_PROC_FS
@@ -226,6 +227,7 @@ sg_open(struct inode *inode, struct file
 	Sg_fd *sfp;
 	int res;
 	int retval;
+	int autoresume_rc = 1;
 
 	nonseekable_open(inode, filp);
 	SCSI_LOG_TIMEOUT(3, printk("sg_open: dev=%d, flags=0x%x\n", dev, flags));
@@ -241,6 +243,10 @@ sg_open(struct inode *inode, struct file
 	if (retval)
 		return retval;
 
+	retval = autoresume_rc = scsi_autoresume_device(sdp->device);
+	if (retval)
+		goto error_out;
+
 	if (!((flags & O_NONBLOCK) ||
 	      scsi_block_when_processing_errors(sdp->device))) {
 		retval = -ENXIO;
@@ -298,6 +304,8 @@ sg_open(struct inode *inode, struct file
 	return 0;
 
       error_out:
+	if (autoresume_rc == 0)
+		scsi_autosuspend_device(sdp->device);
 	scsi_device_put(sdp->device);
 	return retval;
 }
@@ -313,6 +321,8 @@ sg_release(struct inode *inode, struct f
 		return -ENXIO;
 	SCSI_LOG_TIMEOUT(3, printk("sg_release: %s\n", sdp->disk->disk_name));
 	sg_fasync(-1, filp, 0);	/* remove filp from async notification list */
+	scsi_autosuspend_device(sdp->device);
+
 	if (0 == sg_remove_sfp(sdp, sfp)) {	/* Returns 1 when sdp gone */
 		if (!sdp->detached) {
 			scsi_device_put(sdp->device);
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index 8c1e295..ca954fa 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -299,10 +299,15 @@ static int device_reset(struct scsi_cmnd
 
 	US_DEBUGP("%s called\n", __FUNCTION__);
 
-	/* lock the device pointers and do the reset */
-	mutex_lock(&(us->dev_mutex));
-	result = us->transport_reset(us);
-	mutex_unlock(&us->dev_mutex);
+	result = usb_autopm_get_interface(us->pusb_intf);
+	if (result == 0) {
+
+		/* lock the device pointers and do the reset */
+		mutex_lock(&(us->dev_mutex));
+		result = us->transport_reset(us);
+		mutex_unlock(&us->dev_mutex);
+		usb_autopm_put_interface(us->pusb_intf);
+	}
 
 	return result < 0 ? FAILED : SUCCESS;
 }
@@ -345,6 +350,24 @@ void usb_stor_report_bus_reset(struct us
 	scsi_unlock(host);
 }
 
+/* The host and its devices are all idle so we can autosuspend */
+static int autosuspend(struct Scsi_Host *host)
+{
+	struct us_data *us = host_to_us(host);
+
+	usb_autopm_put_interface(us->pusb_intf);
+	return 0;
+}
+
+/* The host needs to be autoresumed */
+static int autoresume(struct Scsi_Host *host)
+{
+	struct us_data *us = host_to_us(host);
+
+	return usb_autopm_get_interface(us->pusb_intf);
+}
+
+
 /***********************************************************************
  * /proc/scsi/ functions
  ***********************************************************************/
@@ -471,6 +494,10 @@ struct scsi_host_template usb_stor_host_
 	.eh_device_reset_handler =	device_reset,
 	.eh_bus_reset_handler =		bus_reset,
 
+	/* dynamic power management */
+	.autosuspend =			autosuspend,
+	.autoresume =			autoresume,
+
 	/* queue commands only, only one command per LUN */
 	.can_queue =			1,
 	.cmd_per_lun =			1,
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index ac6114e..a574646 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -184,16 +184,14 @@ static int storage_suspend(struct usb_in
 {
 	struct us_data *us = usb_get_intfdata(iface);
 
+	US_DEBUGP("%s\n", __FUNCTION__);
+
 	/* Wait until no command is running */
 	mutex_lock(&us->dev_mutex);
 
-	US_DEBUGP("%s\n", __FUNCTION__);
 	if (us->suspend_resume_hook)
 		(us->suspend_resume_hook)(us, US_SUSPEND);
 
-	/* When runtime PM is working, we'll set a flag to indicate
-	 * whether we should autoresume when a SCSI request arrives. */
-
 	mutex_unlock(&us->dev_mutex);
 	return 0;
 }
@@ -202,13 +200,10 @@ static int storage_resume(struct usb_int
 {
 	struct us_data *us = usb_get_intfdata(iface);
 
-	mutex_lock(&us->dev_mutex);
-
 	US_DEBUGP("%s\n", __FUNCTION__);
 	if (us->suspend_resume_hook)
 		(us->suspend_resume_hook)(us, US_RESUME);
 
-	mutex_unlock(&us->dev_mutex);
 	return 0;
 }
 
@@ -928,6 +923,7 @@ static int usb_stor_scan_thread(void * _
 		/* Should we unbind if no devices were detected? */
 	}
 
+	usb_autopm_put_interface(us->pusb_intf);
 	complete_and_exit(&us->scanning_done, 0);
 }
 
@@ -957,6 +953,9 @@ static int storage_probe(struct usb_inte
 		return -ENOMEM;
 	}
 
+	/* Don't autosuspend until the SCSI core tells us */
+	usb_autopm_get_interface(intf);
+
 	/*
 	 * Allow 16-byte CDBs and thus > 2TB
 	 */
@@ -1017,6 +1016,7 @@ static int storage_probe(struct usb_inte
 		goto BadDevice;
 	}
 
+	usb_autopm_get_interface(intf); /* dropped in the scanning thread */
 	wake_up_process(th);
 
 	return 0;
@@ -1054,6 +1054,7 @@ #endif
 	.pre_reset =	storage_pre_reset,
 	.post_reset =	storage_post_reset,
 	.id_table =	storage_usb_ids,
+	.supports_autosuspend = 1,
 };
 
 static int __init usb_stor_init(void)
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index ab7acbe..1c181c4 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -36,8 +36,9 @@ enum scsi_device_state {
 				 * Only error handler commands allowed */
 	SDEV_DEL,		/* device deleted 
 				 * no commands allowed */
-	SDEV_QUIESCE,		/* Device quiescent.  No block commands
-				 * will be accepted, only specials (which
+	SDEV_QUIESCE,		/* Device quiescent or suspended.
+				 * No block commands will be accepted,
+				 * only specials (which
 				 * originate in the mid-layer) */
 	SDEV_OFFLINE,		/* Device offlined (by error handling or
 				 * user request */
@@ -161,6 +162,24 @@ #define SCSI_DEFAULT_DEVICE_BLOCKED	3
 
 	struct execute_work	ew; /* used to get process context on put */
 
+#ifdef CONFIG_SCSI_DYNAMIC_PM
+	struct mutex pm_mutex;		/* protect PM data & operations */
+	struct work_struct autoresume_work;
+
+	unsigned long last_busy;	/* time of last use */
+	int autosuspend_delay;		/* delay in jiffies */
+	int pm_usage_cnt;		/* usage counter for autosuspend */
+
+	unsigned is_suspended:1;
+	unsigned pm_in_progress:1;	/* performing suspend or resume */
+	unsigned auto_pm:1;		/* doing autosuspend or autoresume */
+	unsigned autosuspend_disabled:1;	/* autosuspend & autoresume */
+	unsigned autoresume_disabled:1;		/*  disabled by the user */
+	unsigned skip_sys_resume:1;	/* skip the next system resume */
+	unsigned use_ULD_pm:1;		/* call the Upper-Level Driver's
+					 *   suspend/resume methods */
+#endif /* CONFIG_SCSI_DYNAMIC_PM */
+
 	enum scsi_device_state sdev_state;
 	unsigned long		sdev_data[0];
 } __attribute__((aligned(sizeof(unsigned long))));
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
index 530ff4c..906c4b5 100644
--- a/include/scsi/scsi_host.h
+++ b/include/scsi/scsi_host.h
@@ -176,6 +176,22 @@ #endif
 	int (* eh_host_reset_handler)(struct scsi_cmnd *);
 
 	/*
+	 * Power management routines.  These are optional; you should
+	 * implement them if you want your LLD to perform dynamic Power
+	 * Management.  The autosuspend method will be called whenever
+	 * all the devices below a host have been suspended (are in an
+	 * idle state), at which time the host adapter can safely be
+	 * autosuspended.  The autoresume method will be called whenever
+	 * a suspended host must be resumed for one of its devices to
+	 * carry out a command.  Both routines are always called in a
+	 * process context with interrupts enabled.
+	 *
+	 * Status: OPTIONAL
+	 */
+	int (* autosuspend)(struct Scsi_Host *);
+	int (* autoresume)(struct Scsi_Host *);
+
+	/*
 	 * Before the mid layer attempts to scan for a new device where none
 	 * currently exists, it will call this entry in your driver.  Should
 	 * your driver need to allocate any structs or perform any other init
@@ -657,6 +673,14 @@ struct Scsi_Host {
 	struct device		shost_gendev;
 	struct class_device	shost_classdev;
 
+#ifdef CONFIG_SCSI_DYNAMIC_PM
+	struct mutex pm_mutex;		/* protect PM data & operations */
+	struct delayed_work autosuspend_work;
+
+	int pm_usage_cnt;		/* usage counter for autosuspend */
+	unsigned is_suspended:1;
+#endif /* CONFIG_SCSI_DYNAMIC_PM */
+
 	/*
 	 * List of hosts per template.
 	 *

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

* Re: [ugly patch] Save .15W-.5W by AHCI powersaving
  2008-02-25 13:45 [ugly patch] Save .15W-.5W by AHCI powersaving Pavel Machek
@ 2008-02-25 22:26 ` Mark Lord
  2008-02-25 22:34   ` Pavel Machek
  2008-02-25 22:42   ` Jeff Garzik
  0 siblings, 2 replies; 8+ messages in thread
From: Mark Lord @ 2008-02-25 22:26 UTC (permalink / raw)
  To: Pavel Machek; +Cc: kernel list, Linux-pm mailing list, stern, linux-ide

Pavel Machek wrote:
> Hi!
> 
> This is a patch (very ugly, assumes you have just one disk) to bring
> powersaving to AHCI. You need Alan's SCSI autosuspend (attached) patch
> as a base.
> 
> It saves .5W compared to config with disk spinning, and even .15W
> compared to hdparm -y... on my thinkpad x60 anyway.
..

There was a discussion of this here today.
It makes good use of AHCI-specific features.

Has it been tested with a Port-Multiplier yet?

This is cool enough that we really ought to do a hardware-independent
version, so that all SATA interfaces could benefit.  Especially ata_piix,
but others too.

Cheers

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

* Re: [ugly patch] Save .15W-.5W by AHCI powersaving
  2008-02-25 22:26 ` Mark Lord
@ 2008-02-25 22:34   ` Pavel Machek
  2008-02-26 15:52     ` Mark Lord
  2008-02-25 22:42   ` Jeff Garzik
  1 sibling, 1 reply; 8+ messages in thread
From: Pavel Machek @ 2008-02-25 22:34 UTC (permalink / raw)
  To: Mark Lord; +Cc: kernel list, Linux-pm mailing list, stern, linux-ide

Hi!

>> This is a patch (very ugly, assumes you have just one disk) to bring
>> powersaving to AHCI. You need Alan's SCSI autosuspend (attached) patch
>> as a base.
>>
>> It saves .5W compared to config with disk spinning, and even .15W
>> compared to hdparm -y... on my thinkpad x60 anyway.
> ..
>
> There was a discussion of this here today.

Real-life discussion, or something I could read? :-).

> It makes good use of AHCI-specific features.
>
> Has it been tested with a Port-Multiplier yet?

I do not know what port-multiplier is, sorry. But it was not really
tested. It is not expected to work on any other config than notebook
very similar to mine.

> This is cool enough that we really ought to do a hardware-independent
> version, so that all SATA interfaces could benefit.  Especially ata_piix,
> but others too.

Well, it seems like it is 10 lines per driver once Alan's SCSI
autosuspend patches are in...
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [ugly patch] Save .15W-.5W by AHCI powersaving
  2008-02-25 22:26 ` Mark Lord
  2008-02-25 22:34   ` Pavel Machek
@ 2008-02-25 22:42   ` Jeff Garzik
  2008-02-26 16:21     ` Matthew Garrett
  2008-03-21  7:49     ` [linux-pm] " Oliver Neukum
  1 sibling, 2 replies; 8+ messages in thread
From: Jeff Garzik @ 2008-02-25 22:42 UTC (permalink / raw)
  To: Mark Lord
  Cc: Pavel Machek, kernel list, Linux-pm mailing list, stern, linux-ide

Mark Lord wrote:
> Pavel Machek wrote:
>> This is a patch (very ugly, assumes you have just one disk) to bring
>> powersaving to AHCI. You need Alan's SCSI autosuspend (attached) patch
>> as a base.
>>
>> It saves .5W compared to config with disk spinning, and even .15W
>> compared to hdparm -y... on my thinkpad x60 anyway.
> ..
> 
> There was a discussion of this here today.
> It makes good use of AHCI-specific features.
> 
> Has it been tested with a Port-Multiplier yet?
> 
> This is cool enough that we really ought to do a hardware-independent
> version, so that all SATA interfaces could benefit.  Especially ata_piix,
> but others too.

BTW we can also save power by allowing the user to choose to disable 
hotplugging support.  Then we can power down PHYs that are not in use.

That requires the addition of some policy controls, because it is 
user-specific whether or not to waste power waiting for a plug-in event.

	Jeff




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

* Re: [ugly patch] Save .15W-.5W by AHCI powersaving
  2008-02-25 22:34   ` Pavel Machek
@ 2008-02-26 15:52     ` Mark Lord
  2008-02-26 21:49       ` Pavel Machek
  0 siblings, 1 reply; 8+ messages in thread
From: Mark Lord @ 2008-02-26 15:52 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Mark Lord, kernel list, Linux-pm mailing list, stern, linux-ide

Pavel Machek wrote:
> Hi!
> 
>>> This is a patch (very ugly, assumes you have just one disk) to bring
>>> powersaving to AHCI. You need Alan's SCSI autosuspend (attached) patch
>>> as a base.
>>>
>>> It saves .5W compared to config with disk spinning, and even .15W
>>> compared to hdparm -y... on my thinkpad x60 anyway.
>> ..
>>
>> There was a discussion of this here today.
> 
> Real-life discussion, or something I could read? :-).
> 
>> It makes good use of AHCI-specific features.
>>
>> Has it been tested with a Port-Multiplier yet?
> 
> I do not know what port-multiplier is, sorry. But it was not really
> tested. It is not expected to work on any other config than notebook
> very similar to mine.
> 
>> This is cool enough that we really ought to do a hardware-independent
>> version, so that all SATA interfaces could benefit.  Especially ata_piix,
>> but others too.
> 
> Well, it seems like it is 10 lines per driver once Alan's SCSI
> autosuspend patches are in...
..

Cool (literally)!

I think I might have gotten your patch confused in my mind
with another AHCI patch, which uses features of the chip itself
to automatically negotiate/change link power status on the fly
(no s/w needed, other than to turn it on).

That one is very ACPI specific, though.



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

* Re: [ugly patch] Save .15W-.5W by AHCI powersaving
  2008-02-25 22:42   ` Jeff Garzik
@ 2008-02-26 16:21     ` Matthew Garrett
  2008-03-21  7:49     ` [linux-pm] " Oliver Neukum
  1 sibling, 0 replies; 8+ messages in thread
From: Matthew Garrett @ 2008-02-26 16:21 UTC (permalink / raw)
  To: Jeff Garzik
  Cc: Mark Lord, Pavel Machek, kernel list, Linux-pm mailing list,
	stern, linux-ide

On Mon, Feb 25, 2008 at 05:42:58PM -0500, Jeff Garzik wrote:

> BTW we can also save power by allowing the user to choose to disable 
> hotplugging support.  Then we can power down PHYs that are not in use.
> 
> That requires the addition of some policy controls, because it is 
> user-specific whether or not to waste power waiting for a plug-in event.

For AHCI, if you've enabled link power management then you've already 
disabled hotplug. We might as well power down unused phys in that case. 
Note that laptop bays still seem to tend to use platform-specific 
hotplug notification, even when they're sata - we'll get the hotplug 
notify for them even if the phy's powered down, so that case also needs 
to be handled.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

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

* Re: [ugly patch] Save .15W-.5W by AHCI powersaving
  2008-02-26 15:52     ` Mark Lord
@ 2008-02-26 21:49       ` Pavel Machek
  0 siblings, 0 replies; 8+ messages in thread
From: Pavel Machek @ 2008-02-26 21:49 UTC (permalink / raw)
  To: Mark Lord; +Cc: Mark Lord, kernel list, Linux-pm mailing list, stern, linux-ide

On Tue 2008-02-26 10:52:36, Mark Lord wrote:
> Pavel Machek wrote:
>> Hi!
>>
>>>> This is a patch (very ugly, assumes you have just one disk) to bring
>>>> powersaving to AHCI. You need Alan's SCSI autosuspend (attached) patch
>>>> as a base.
>>>>
>>>> It saves .5W compared to config with disk spinning, and even .15W
>>>> compared to hdparm -y... on my thinkpad x60 anyway.
>>> ..
>>>
>>> There was a discussion of this here today.
>>
>> Real-life discussion, or something I could read? :-).
>>
>>> It makes good use of AHCI-specific features.
>>>
>>> Has it been tested with a Port-Multiplier yet?
>>
>> I do not know what port-multiplier is, sorry. But it was not really
>> tested. It is not expected to work on any other config than notebook
>> very similar to mine.
>>
>>> This is cool enough that we really ought to do a hardware-independent
>>> version, so that all SATA interfaces could benefit.  Especially ata_piix,
>>> but others too.
>>
>> Well, it seems like it is 10 lines per driver once Alan's SCSI
>> autosuspend patches are in...
> ..
>
> Cool (literally)!
>
> I think I might have gotten your patch confused in my mind
> with another AHCI patch, which uses features of the chip itself
> to automatically negotiate/change link power status on the fly
> (no s/w needed, other than to turn it on).
>
> That one is very ACPI specific, though.

Yep, I seen that, too. I'm just afraid to check, maybe AHCI powerdown
does not save that much power when links in powersave already...
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [linux-pm] [ugly patch] Save .15W-.5W by AHCI powersaving
  2008-02-25 22:42   ` Jeff Garzik
  2008-02-26 16:21     ` Matthew Garrett
@ 2008-03-21  7:49     ` Oliver Neukum
  1 sibling, 0 replies; 8+ messages in thread
From: Oliver Neukum @ 2008-03-21  7:49 UTC (permalink / raw)
  To: linux-pm
  Cc: Jeff Garzik, Mark Lord, Linux-pm mailing list, kernel list, linux-ide

Am Montag, 25. Februar 2008 23:42:58 schrieb Jeff Garzik:
> > This is cool enough that we really ought to do a hardware-independent
> > version, so that all SATA interfaces could benefit.  Especially ata_piix,
> > but others too.
> 
> BTW we can also save power by allowing the user to choose to disable 
> hotplugging support.  Then we can power down PHYs that are not in use.

Can't we power them up periodically to check for new hardware?

	Regards
		Oliver


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

end of thread, other threads:[~2008-03-21 13:20 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-02-25 13:45 [ugly patch] Save .15W-.5W by AHCI powersaving Pavel Machek
2008-02-25 22:26 ` Mark Lord
2008-02-25 22:34   ` Pavel Machek
2008-02-26 15:52     ` Mark Lord
2008-02-26 21:49       ` Pavel Machek
2008-02-25 22:42   ` Jeff Garzik
2008-02-26 16:21     ` Matthew Garrett
2008-03-21  7:49     ` [linux-pm] " Oliver Neukum

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).