Commit e5005b15c91f3362036067bde5210d5c78af2f0d

Authored by Tejun Heo
Committed by Jeff Garzik
1 parent f08dc1ac6b

libata: issue DIPM enable commands with LPM state updated

Low level drivers may behave differently depending on the current
link->lpm_policy.  During ata_eh_set_lpm(), DIPM enable commands are
issued after the successful completion of ap->ops->set_lpm(), which
means that the controller is already in the target state.  This causes
DIPM enable commands to be processed with mismatching controller power
state and link->lpm_policy value.

In ahci, link->lpm_policy is used to ignore certain PHY events if LPM
is enabled; however, as DIPM commands are issued with stale
link->lpm_policy, they sometimes end up triggering these conditions
and get aborted leading to LPM configuration failure.

Fix it by updating link->lpm_policy before issuing DIPM enable
commands.

Signed-off-by: Tejun Heo <tj@kernel.org>
Reported-by: Kyle McMartin <kyle@mcmartin.ca>
Cc: stable@kernel.org
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>

Showing 1 changed file with 14 additions and 3 deletions Side-by-side Diff

drivers/ata/libata-eh.c
... ... @@ -3275,6 +3275,7 @@
3275 3275 struct ata_port *ap = ata_is_host_link(link) ? link->ap : NULL;
3276 3276 struct ata_eh_context *ehc = &link->eh_context;
3277 3277 struct ata_device *dev, *link_dev = NULL, *lpm_dev = NULL;
  3278 + enum ata_lpm_policy old_policy = link->lpm_policy;
3278 3279 unsigned int hints = ATA_LPM_EMPTY | ATA_LPM_HIPM;
3279 3280 unsigned int err_mask;
3280 3281 int rc;
... ... @@ -3338,6 +3339,14 @@
3338 3339 goto fail;
3339 3340 }
3340 3341  
  3342 + /*
  3343 + * Low level driver acked the transition. Issue DIPM command
  3344 + * with the new policy set.
  3345 + */
  3346 + link->lpm_policy = policy;
  3347 + if (ap && ap->slave_link)
  3348 + ap->slave_link->lpm_policy = policy;
  3349 +
3341 3350 /* host config updated, enable DIPM if transitioning to MIN_POWER */
3342 3351 ata_for_each_dev(dev, link, ENABLED) {
3343 3352 if (policy == ATA_LPM_MIN_POWER && ata_id_has_dipm(dev->id)) {
3344 3353  
... ... @@ -3353,12 +3362,14 @@
3353 3362 }
3354 3363 }
3355 3364  
3356   - link->lpm_policy = policy;
3357   - if (ap && ap->slave_link)
3358   - ap->slave_link->lpm_policy = policy;
3359 3365 return 0;
3360 3366  
3361 3367 fail:
  3368 + /* restore the old policy */
  3369 + link->lpm_policy = old_policy;
  3370 + if (ap && ap->slave_link)
  3371 + ap->slave_link->lpm_policy = old_policy;
  3372 +
3362 3373 /* if no device or only one more chance is left, disable LPM */
3363 3374 if (!dev || ehc->tries[dev->devno] <= 2) {
3364 3375 ata_link_printk(link, KERN_WARNING,