aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2010-04-04 21:33:13 -0400
committerJeff Garzik <jgarzik@redhat.com>2010-04-06 10:55:33 -0400
commit445d211b0da4e9a6e6d576edff85085c2aaf53df (patch)
tree039ade5b49cb0bbd388babc369f28f541c76ec96 /drivers
parent68b0ddb289220b6d4d865be128939663be34959d (diff)
libata: unlock HPA if device shrunk
Some BIOSes don't configure HPA during boot but do so while resuming. This causes harddrives to shrink during resume making libata detach and reattach them. This can be worked around by unlocking HPA if old size equals native size. Add ATA_DFLAG_UNLOCK_HPA so that HPA unlocking can be controlled per-device and update ata_dev_revalidate() such that it sets ATA_DFLAG_UNLOCK_HPA and fails with -EIO when the above condition is detected. This patch fixes the following bug. https://bugzilla.kernel.org/show_bug.cgi?id=15396 Signed-off-by: Tejun Heo <tj@kernel.org> Reported-by: Oleksandr Yermolenko <yaa.bta@gmail.com> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/ata/libata-core.c74
1 files changed, 45 insertions, 29 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 2ab34dc97f6f..49cffb6094a3 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -1494,6 +1494,7 @@ static int ata_hpa_resize(struct ata_device *dev)
1494{ 1494{
1495 struct ata_eh_context *ehc = &dev->link->eh_context; 1495 struct ata_eh_context *ehc = &dev->link->eh_context;
1496 int print_info = ehc->i.flags & ATA_EHI_PRINTINFO; 1496 int print_info = ehc->i.flags & ATA_EHI_PRINTINFO;
1497 bool unlock_hpa = ata_ignore_hpa || dev->flags & ATA_DFLAG_UNLOCK_HPA;
1497 u64 sectors = ata_id_n_sectors(dev->id); 1498 u64 sectors = ata_id_n_sectors(dev->id);
1498 u64 native_sectors; 1499 u64 native_sectors;
1499 int rc; 1500 int rc;
@@ -1510,7 +1511,7 @@ static int ata_hpa_resize(struct ata_device *dev)
1510 /* If device aborted the command or HPA isn't going to 1511 /* If device aborted the command or HPA isn't going to
1511 * be unlocked, skip HPA resizing. 1512 * be unlocked, skip HPA resizing.
1512 */ 1513 */
1513 if (rc == -EACCES || !ata_ignore_hpa) { 1514 if (rc == -EACCES || !unlock_hpa) {
1514 ata_dev_printk(dev, KERN_WARNING, "HPA support seems " 1515 ata_dev_printk(dev, KERN_WARNING, "HPA support seems "
1515 "broken, skipping HPA handling\n"); 1516 "broken, skipping HPA handling\n");
1516 dev->horkage |= ATA_HORKAGE_BROKEN_HPA; 1517 dev->horkage |= ATA_HORKAGE_BROKEN_HPA;
@@ -1525,7 +1526,7 @@ static int ata_hpa_resize(struct ata_device *dev)
1525 dev->n_native_sectors = native_sectors; 1526 dev->n_native_sectors = native_sectors;
1526 1527
1527 /* nothing to do? */ 1528 /* nothing to do? */
1528 if (native_sectors <= sectors || !ata_ignore_hpa) { 1529 if (native_sectors <= sectors || !unlock_hpa) {
1529 if (!print_info || native_sectors == sectors) 1530 if (!print_info || native_sectors == sectors)
1530 return 0; 1531 return 0;
1531 1532
@@ -4186,36 +4187,51 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class,
4186 goto fail; 4187 goto fail;
4187 4188
4188 /* verify n_sectors hasn't changed */ 4189 /* verify n_sectors hasn't changed */
4189 if (dev->class == ATA_DEV_ATA && n_sectors && 4190 if (dev->class != ATA_DEV_ATA || !n_sectors ||
4190 dev->n_sectors != n_sectors) { 4191 dev->n_sectors == n_sectors)
4191 ata_dev_printk(dev, KERN_WARNING, "n_sectors mismatch " 4192 return 0;
4192 "%llu != %llu\n", 4193
4193 (unsigned long long)n_sectors, 4194 /* n_sectors has changed */
4194 (unsigned long long)dev->n_sectors); 4195 ata_dev_printk(dev, KERN_WARNING, "n_sectors mismatch %llu != %llu\n",
4195 /* 4196 (unsigned long long)n_sectors,
4196 * Something could have caused HPA to be unlocked 4197 (unsigned long long)dev->n_sectors);
4197 * involuntarily. If n_native_sectors hasn't changed 4198
4198 * and the new size matches it, keep the device. 4199 /*
4199 */ 4200 * Something could have caused HPA to be unlocked
4200 if (dev->n_native_sectors == n_native_sectors && 4201 * involuntarily. If n_native_sectors hasn't changed and the
4201 dev->n_sectors > n_sectors && 4202 * new size matches it, keep the device.
4202 dev->n_sectors == n_native_sectors) { 4203 */
4203 ata_dev_printk(dev, KERN_WARNING, 4204 if (dev->n_native_sectors == n_native_sectors &&
4204 "new n_sectors matches native, probably " 4205 dev->n_sectors > n_sectors && dev->n_sectors == n_native_sectors) {
4205 "late HPA unlock, continuing\n"); 4206 ata_dev_printk(dev, KERN_WARNING,
4206 /* keep using the old n_sectors */ 4207 "new n_sectors matches native, probably "
4207 dev->n_sectors = n_sectors; 4208 "late HPA unlock, continuing\n");
4208 } else { 4209 /* keep using the old n_sectors */
4209 /* restore original n_[native]_sectors and fail */ 4210 dev->n_sectors = n_sectors;
4210 dev->n_native_sectors = n_native_sectors; 4211 return 0;
4211 dev->n_sectors = n_sectors;
4212 rc = -ENODEV;
4213 goto fail;
4214 }
4215 } 4212 }
4216 4213
4217 return 0; 4214 /*
4215 * Some BIOSes boot w/o HPA but resume w/ HPA locked. Try
4216 * unlocking HPA in those cases.
4217 *
4218 * https://bugzilla.kernel.org/show_bug.cgi?id=15396
4219 */
4220 if (dev->n_native_sectors == n_native_sectors &&
4221 dev->n_sectors < n_sectors && n_sectors == n_native_sectors &&
4222 !(dev->horkage & ATA_HORKAGE_BROKEN_HPA)) {
4223 ata_dev_printk(dev, KERN_WARNING,
4224 "old n_sectors matches native, probably "
4225 "late HPA lock, will try to unlock HPA\n");
4226 /* try unlocking HPA */
4227 dev->flags |= ATA_DFLAG_UNLOCK_HPA;
4228 rc = -EIO;
4229 } else
4230 rc = -ENODEV;
4218 4231
4232 /* restore original n_[native_]sectors and fail */
4233 dev->n_native_sectors = n_native_sectors;
4234 dev->n_sectors = n_sectors;
4219 fail: 4235 fail:
4220 ata_dev_printk(dev, KERN_ERR, "revalidation failed (errno=%d)\n", rc); 4236 ata_dev_printk(dev, KERN_ERR, "revalidation failed (errno=%d)\n", rc);
4221 return rc; 4237 return rc;