diff options
author | Tejun Heo <tj@kernel.org> | 2010-09-01 11:50:06 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@redhat.com> | 2010-10-21 20:21:04 -0400 |
commit | 6b7ae9545ad9875a289f4191c0216b473e313cb9 (patch) | |
tree | 216b4db276202d727ba134d256144a6670497180 /drivers/ata/libata-scsi.c | |
parent | 1152b2617a6e1943b6b82e07c962950e56f1000c (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.c | 11 |
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 | } |
148 | DEVICE_ATTR(link_power_management_policy, S_IRUGO | S_IWUSR, | 153 | DEVICE_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); |