diff options
author | Tejun Heo <htejun@gmail.com> | 2007-09-03 12:32:57 +0900 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-10-12 14:55:39 -0400 |
commit | 05027adccc09401a7e31d5ef51040dc75ab03c22 (patch) | |
tree | 83fbb8e65d26e10e1fd74377bc13fd358a61c13e | |
parent | c728a9149f519cbb9f610962873f4e22ed4a6efd (diff) | |
download | blackbird-op-linux-05027adccc09401a7e31d5ef51040dc75ab03c22.tar.gz blackbird-op-linux-05027adccc09401a7e31d5ef51040dc75ab03c22.zip |
libata: remiplement ata_hpa_resize()
This patch reimplement ata_hpa_resize() such that...
* All HPA related decisions are made inside ata_hpa_resize() proper.
ata_hpa_resize() returns 0 if configuration can proceed, -errno if
device needs to be reset and reconfigured.
* All errors are handled properly. If HPA unlocking isn't requested,
HPA handling is disabled automatically to avoid unnecessary device
detection failure.
* Messages are trimmed. HPA detection message is printed only during
initial configuration. HPA unlocked message is printed only during
initial configuration or unlocking results in different size.
* Instead of using sectors returned in TF of SET_MAX, re-read IDENTIFY
data as that's the value the device is going to use.
* It's called early during ata_dev_configure() as IDENTIFY data might
change after resizing.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
-rw-r--r-- | drivers/ata/libata-core.c | 124 |
1 files changed, 85 insertions, 39 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index c7614bdc0cb9..b01b5a897dcf 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -915,7 +915,6 @@ static int ata_read_native_max_address(struct ata_device *dev, u64 *max_sectors) * ata_set_max_sectors - Set max sectors * @dev: target device * @new_sectors: new max sectors value to set for the device - * @res_sectors: result max sectors * * Set max sectors of @dev to @new_sectors. * @@ -924,8 +923,7 @@ static int ata_read_native_max_address(struct ata_device *dev, u64 *max_sectors) * previous non-volatile SET_MAX) by the drive. -EIO on other * errors. */ -static int ata_set_max_sectors(struct ata_device *dev, u64 new_sectors, - u64 *res_sectors) +static int ata_set_max_sectors(struct ata_device *dev, u64 new_sectors) { unsigned int err_mask; struct ata_taskfile tf; @@ -964,11 +962,6 @@ static int ata_set_max_sectors(struct ata_device *dev, u64 new_sectors, return -EIO; } - if (lba48) - *res_sectors = ata_tf_to_lba48(&tf); - else - *res_sectors = ata_tf_to_lba(&tf); - return 0; } @@ -979,41 +972,93 @@ static int ata_set_max_sectors(struct ata_device *dev, u64 new_sectors, * Read the size of an LBA28 or LBA48 disk with HPA features and resize * it if required to the full size of the media. The caller must check * the drive has the HPA feature set enabled. + * + * RETURNS: + * 0 on success, -errno on failure. */ - -static u64 ata_hpa_resize(struct ata_device *dev) +static int ata_hpa_resize(struct ata_device *dev) { - u64 sectors = dev->n_sectors; - u64 hpa_sectors; + struct ata_eh_context *ehc = &dev->link->eh_context; + int print_info = ehc->i.flags & ATA_EHI_PRINTINFO; + u64 sectors = ata_id_n_sectors(dev->id); + u64 native_sectors; int rc; - rc = ata_read_native_max_address(dev, &hpa_sectors); - if (rc) + /* do we need to do it? */ + if (dev->class != ATA_DEV_ATA || + !ata_id_has_lba(dev->id) || !ata_id_hpa_enabled(dev->id) || + (dev->horkage & ATA_HORKAGE_BROKEN_HPA)) return 0; - if (hpa_sectors > sectors) { - ata_dev_printk(dev, KERN_INFO, - "Host Protected Area detected:\n" - "\tcurrent size: %lld sectors\n" - "\tnative size: %lld sectors\n", - (long long)sectors, (long long)hpa_sectors); - - if (ata_ignore_hpa) { - rc = ata_set_max_sectors(dev, hpa_sectors, &hpa_sectors); - - if (rc == 0) { - ata_dev_printk(dev, KERN_INFO, "native size " - "increased to %lld sectors\n", - (long long)hpa_sectors); - return hpa_sectors; - } + /* read native max address */ + rc = ata_read_native_max_address(dev, &native_sectors); + if (rc) { + /* If HPA isn't going to be unlocked, skip HPA + * resizing from the next try. + */ + if (!ata_ignore_hpa) { + ata_dev_printk(dev, KERN_WARNING, "HPA support seems " + "broken, will skip HPA handling\n"); + dev->horkage |= ATA_HORKAGE_BROKEN_HPA; + + /* we can continue if device aborted the command */ + if (rc == -EACCES) + rc = 0; } - } else if (hpa_sectors < sectors) - ata_dev_printk(dev, KERN_WARNING, "%s 1: hpa sectors (%lld) " - "is smaller than sectors (%lld)\n", __FUNCTION__, - (long long)hpa_sectors, (long long)sectors); - return sectors; + return rc; + } + + /* nothing to do? */ + if (native_sectors <= sectors || !ata_ignore_hpa) { + if (!print_info || native_sectors == sectors) + return 0; + + if (native_sectors > sectors) + ata_dev_printk(dev, KERN_INFO, + "HPA detected: current %llu, native %llu\n", + (unsigned long long)sectors, + (unsigned long long)native_sectors); + else if (native_sectors < sectors) + ata_dev_printk(dev, KERN_WARNING, + "native sectors (%llu) is smaller than " + "sectors (%llu)\n", + (unsigned long long)native_sectors, + (unsigned long long)sectors); + return 0; + } + + /* let's unlock HPA */ + rc = ata_set_max_sectors(dev, native_sectors); + if (rc == -EACCES) { + /* if device aborted the command, skip HPA resizing */ + ata_dev_printk(dev, KERN_WARNING, "device aborted resize " + "(%llu -> %llu), skipping HPA handling\n", + (unsigned long long)sectors, + (unsigned long long)native_sectors); + dev->horkage |= ATA_HORKAGE_BROKEN_HPA; + return 0; + } else if (rc) + return rc; + + /* re-read IDENTIFY data */ + rc = ata_dev_reread_id(dev, 0); + if (rc) { + ata_dev_printk(dev, KERN_ERR, "failed to re-read IDENTIFY " + "data after HPA resizing\n"); + return rc; + } + + if (print_info) { + u64 new_sectors = ata_id_n_sectors(dev->id); + ata_dev_printk(dev, KERN_INFO, + "HPA unlocked: %llu -> %llu, native %llu\n", + (unsigned long long)sectors, + (unsigned long long)new_sectors, + (unsigned long long)native_sectors); + } + + return 0; } /** @@ -1837,6 +1882,11 @@ int ata_dev_configure(struct ata_device *dev) if (rc) return rc; + /* massage HPA, do it early as it might change IDENTIFY data */ + rc = ata_hpa_resize(dev); + if (rc) + return rc; + /* print device capabilities */ if (ata_msg_probe(ap)) ata_dev_printk(dev, KERN_DEBUG, @@ -1904,10 +1954,6 @@ int ata_dev_configure(struct ata_device *dev) dev->flags |= ATA_DFLAG_FLUSH_EXT; } - if (!(dev->horkage & ATA_HORKAGE_BROKEN_HPA) && - ata_id_hpa_enabled(dev->id)) - dev->n_sectors = ata_hpa_resize(dev); - /* config NCQ */ ata_dev_config_ncq(dev, ncq_desc, sizeof(ncq_desc)); |