Commit e5005b15c91f3362036067bde5210d5c78af2f0d
Committed by
Jeff Garzik
1 parent
f08dc1ac6b
Exists in
master
and in
7 other branches
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, |