libata: accept late unlocking of HPA

On certain configurations, HPA isn't or can't be unlocked during
probing but it somehow ends up unlocked afterwards.  In the following
thread, the problem can be reliably reproduced after resuming from
STR.  The BIOS turns on HPA during boot but forgets to do it during
resume.

  http://thread.gmane.org/gmane.linux.kernel/858310

This patch updates libata revalidation such that it considers native
n_sectors.  If the device size has increased to match native
n_sectors, it's assumed that HPA has been unlocked involuntarily and
the device is recognized as the same one.  This should be fairly safe
while nicely working around the problem.

Signed-off-by: Tejun Heo <tj@kernel.org>
Reported-by: Christof Warlich <christof@warlich.name>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
This commit is contained in:
Tejun Heo 2009-07-15 17:11:41 +09:00 committed by Jeff Garzik
parent 7d084d96fd
commit 5920dadfb4
2 changed files with 24 additions and 7 deletions

View File

@ -1515,6 +1515,7 @@ static int ata_hpa_resize(struct ata_device *dev)
return rc;
}
dev->n_native_sectors = native_sectors;
/* nothing to do? */
if (native_sectors <= sectors || !ata_ignore_hpa) {
@ -4099,6 +4100,7 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class,
unsigned int readid_flags)
{
u64 n_sectors = dev->n_sectors;
u64 n_native_sectors = dev->n_native_sectors;
int rc;
if (!ata_dev_enabled(dev))
@ -4128,16 +4130,30 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class,
/* verify n_sectors hasn't changed */
if (dev->class == ATA_DEV_ATA && n_sectors &&
dev->n_sectors != n_sectors) {
ata_dev_printk(dev, KERN_INFO, "n_sectors mismatch "
ata_dev_printk(dev, KERN_WARNING, "n_sectors mismatch "
"%llu != %llu\n",
(unsigned long long)n_sectors,
(unsigned long long)dev->n_sectors);
/* restore original n_sectors */
dev->n_sectors = n_sectors;
rc = -ENODEV;
goto fail;
/*
* Something could have caused HPA to be unlocked
* involuntarily. If n_native_sectors hasn't changed
* and the new size matches it, keep the device.
*/
if (dev->n_native_sectors == n_native_sectors &&
dev->n_sectors > n_sectors &&
dev->n_sectors == n_native_sectors) {
ata_dev_printk(dev, KERN_WARNING,
"new n_sectors matches native, probably "
"late HPA unlock, continuing\n");
/* keep using the old n_sectors */
dev->n_sectors = n_sectors;
} else {
/* restore original n_[native]_sectors and fail */
dev->n_native_sectors = n_native_sectors;
dev->n_sectors = n_sectors;
rc = -ENODEV;
goto fail;
}
}
return 0;

View File

@ -589,6 +589,7 @@ struct ata_device {
#endif
/* n_sector is CLEAR_BEGIN, read comment above CLEAR_BEGIN */
u64 n_sectors; /* size of device, if ATA */
u64 n_native_sectors; /* native size, if ATA */
unsigned int class; /* ATA_DEV_xxx */
unsigned long unpark_deadline;