aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ata/libata-scsi.c
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2010-09-01 11:50:06 -0400
committerJeff Garzik <jgarzik@redhat.com>2010-10-21 20:21:04 -0400
commit6b7ae9545ad9875a289f4191c0216b473e313cb9 (patch)
tree216b4db276202d727ba134d256144a6670497180 /drivers/ata/libata-scsi.c
parent1152b2617a6e1943b6b82e07c962950e56f1000c (diff)
libata: reimplement link power management
The current LPM implementation has the following issues. * Operation order isn't well thought-out. e.g. HIPM should be configured after IPM in SControl is properly configured. Not the other way around. * Suspend/resume paths call ata_lpm_enable/disable() which must only be called from EH context directly. Also, ata_lpm_enable/disable() were called whether LPM was in use or not. * Implementation is per-port when it should be per-link. As a result, it can't be used for controllers with slave links or PMP. * LPM state isn't managed consistently. After a link reset for whatever reason including suspend/resume the actual LPM state would be reset leaving ap->lpm_policy inconsistent. * Generic/driver-specific logic boundary isn't clear. Currently, libahci has to mangle stuff which libata EH proper should be handling. This makes the implementation unnecessarily complex and fragile. * Tied to ALPM. Doesn't consider DIPM only cases and doesn't check whether the device allows HIPM. * Error handling isn't implemented. Given the extent of mismatch with the rest of libata, I don't think trying to fix it piecewise makes much sense. This patch reimplements LPM support. * The new implementation is per-link. The target policy is still port-wide (ap->target_lpm_policy) but all the mechanisms and states are per-link and integrate well with the rest of link abstraction and can work with slave and PMP links. * Core EH has proper control of LPM state. LPM state is reconfigured when and only when reconfiguration is necessary. It makes sure that LPM state is reset when probing for new device on the link. Controller agnostic logic is now implemented in libata EH proper and driver implementation only has to deal with controller specifics. * Proper error handling. LPM config failure is attributed to the device on the link and LPM is disabled for the link if it fails repeatedly. * ops->enable/disable_pm() are replaced with single ops->set_lpm() which takes @policy and @hints. This simplifies driver specific implementation. Signed-off-by: Tejun Heo <tj@kernel.org> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers/ata/libata-scsi.c')
-rw-r--r--drivers/ata/libata-scsi.c11
1 files changed, 8 insertions, 3 deletions
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index aa56681f68db..56f6224fd6e6 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -117,6 +117,7 @@ static ssize_t ata_scsi_lpm_store(struct device *dev,
117 struct Scsi_Host *shost = class_to_shost(dev); 117 struct Scsi_Host *shost = class_to_shost(dev);
118 struct ata_port *ap = ata_shost_to_port(shost); 118 struct ata_port *ap = ata_shost_to_port(shost);
119 enum ata_lpm_policy policy; 119 enum ata_lpm_policy policy;
120 unsigned long flags;
120 121
121 /* UNKNOWN is internal state, iterate from MAX_POWER */ 122 /* UNKNOWN is internal state, iterate from MAX_POWER */
122 for (policy = ATA_LPM_MAX_POWER; 123 for (policy = ATA_LPM_MAX_POWER;
@@ -129,7 +130,11 @@ static ssize_t ata_scsi_lpm_store(struct device *dev,
129 if (policy == ARRAY_SIZE(ata_lpm_policy_names)) 130 if (policy == ARRAY_SIZE(ata_lpm_policy_names))
130 return -EINVAL; 131 return -EINVAL;
131 132
132 ata_lpm_schedule(ap, policy); 133 spin_lock_irqsave(ap->lock, flags);
134 ap->target_lpm_policy = policy;
135 ata_port_schedule_eh(ap);
136 spin_unlock_irqrestore(ap->lock, flags);
137
133 return count; 138 return count;
134} 139}
135 140
@@ -139,11 +144,11 @@ static ssize_t ata_scsi_lpm_show(struct device *dev,
139 struct Scsi_Host *shost = class_to_shost(dev); 144 struct Scsi_Host *shost = class_to_shost(dev);
140 struct ata_port *ap = ata_shost_to_port(shost); 145 struct ata_port *ap = ata_shost_to_port(shost);
141 146
142 if (ap->lpm_policy >= ARRAY_SIZE(ata_lpm_policy_names)) 147 if (ap->target_lpm_policy >= ARRAY_SIZE(ata_lpm_policy_names))
143 return -EINVAL; 148 return -EINVAL;
144 149
145 return snprintf(buf, PAGE_SIZE, "%s\n", 150 return snprintf(buf, PAGE_SIZE, "%s\n",
146 ata_lpm_policy_names[ap->lpm_policy]); 151 ata_lpm_policy_names[ap->target_lpm_policy]);
147} 152}
148DEVICE_ATTR(link_power_management_policy, S_IRUGO | S_IWUSR, 153DEVICE_ATTR(link_power_management_policy, S_IRUGO | S_IWUSR,
149 ata_scsi_lpm_show, ata_scsi_lpm_store); 154 ata_scsi_lpm_show, ata_scsi_lpm_store);