This patch will modify the scsi and ata subsystem to allow
users to set a power management policy for the link.
libata drivers can define a function (enable_pm) that will
perform hardware specific actions to enable whatever power
management policy the user sets up if the driver supports
it. This power management policy will be activated after
all disks have been enumerated and intialized.
The scsi subsystem will create a new sysfs file for each
host in /sys/class/scsi_host called "link_power_management_policy".
This file can have 3 possible values:
Value Meaning
---
min_power User wishes the link to conserve power as much as
possible, even at the cost of some performance
max_performance User wants priority to be on performance, not power
savings
medium_powerUser wants power savings, with less performance cost
than min_power (but less power savings as well).
Signed-off-by: Kristen Carlson Accardi <[EMAIL PROTECTED]>
Index: 2.6-git/drivers/ata/libata-scsi.c
===
--- 2.6-git.orig/drivers/ata/libata-scsi.c
+++ 2.6-git/drivers/ata/libata-scsi.c
@@ -2890,6 +2890,51 @@ void ata_scsi_simulate(struct ata_device
}
}
+int ata_scsi_set_link_pm_policy(struct Scsi_Host *shost,
+ enum scsi_host_link_pm policy)
+{
+ struct ata_port *ap = ata_shost_to_port(shost);
+ int rc = -EINVAL;
+ int i;
+
+ /*
+* make sure no broken devices are on this port,
+* and that all devices support interface power
+* management
+*/
+ for (i = 0; i < ATA_MAX_DEVICES; i++) {
+ struct ata_device *dev = &ap->device[i];
+
+ /* only check drives which exist */
+ if (!ata_dev_enabled(dev))
+ continue;
+
+ /*
+* do we need to handle the case where we've hotplugged
+* a broken drive (since hotplug and ALPM are mutually
+* exclusive) ?
+*
+* If so, if we detect a broken drive on a port with
+* alpm already enabled, then we should reset the policy
+* to off for the entire port.
+*/
+ if ((dev->horkage & ATA_HORKAGE_ALPM) ||
+ !(dev->flags & ATA_DFLAG_IPM)) {
+ ata_dev_printk(dev, KERN_ERR,
+ "Unable to set Link PM policy\n");
+ ap->pm_policy = SHOST_MAX_PERFORMANCE;
+ }
+ }
+
+ if (ap->ops->enable_pm)
+ rc = ap->ops->enable_pm(ap, policy);
+
+ if (!rc)
+ shost->shost_link_pm_policy = ap->pm_policy;
+ return rc;
+}
+EXPORT_SYMBOL_GPL(ata_scsi_set_link_pm_policy);
+
int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht)
{
int i, rc;
@@ -2912,7 +2957,7 @@ int ata_scsi_add_hosts(struct ata_host *
shost->max_lun = 1;
shost->max_channel = 1;
shost->max_cmd_len = 16;
-
+ shost->shost_link_pm_policy = ap->pm_policy;
rc = scsi_add_host(ap->scsi_host, ap->host->dev);
if (rc)
goto err_add;
Index: 2.6-git/drivers/scsi/hosts.c
===
--- 2.6-git.orig/drivers/scsi/hosts.c
+++ 2.6-git/drivers/scsi/hosts.c
@@ -54,6 +54,25 @@ static struct class shost_class = {
};
/**
+ * scsi_host_set_link_pm - Change the link power management policy
+ * @shost: scsi host to change the policy of.
+ * @policy:policy to change to.
+ *
+ * Returns zero if successful or an error if the requested
+ * transition is illegal.
+ **/
+int scsi_host_set_link_pm(struct Scsi_Host *shost,
+ enum scsi_host_link_pm policy)
+{
+ struct scsi_host_template *sht = shost->hostt;
+ if (sht->set_link_pm_policy)
+ sht->set_link_pm_policy(shost, policy);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(scsi_host_set_link_pm);
+
+/**
* scsi_host_set_state - Take the given host through the host
* state model.
* @shost: scsi host to change the state of.
Index: 2.6-git/drivers/scsi/scsi_sysfs.c
===
--- 2.6-git.orig/drivers/scsi/scsi_sysfs.c
+++ 2.6-git/drivers/scsi/scsi_sysfs.c
@@ -189,6 +189,74 @@ show_shost_state(struct class_device *cl
static CLASS_DEVICE_ATTR(state, S_IRUGO | S_IWUSR, show_shost_state,
store_shost_state);
+static const struct {
+ enum scsi_host_link_pm value;
+ char*name;
+} shost_link_pm_policy[] = {
+ { SHOST_NOT_AVAILABLE, "max_performance" },
+ { SHOST_MIN_POWER, "min_power" },
+ { SHOST_MAX_PERFORMANCE, "max_perfo