Commit 05027adc authored by Tejun Heo's avatar Tejun Heo Committed by Jeff Garzik

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: default avatarTejun Heo <htejun@gmail.com>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent c728a914
...@@ -915,7 +915,6 @@ static int ata_read_native_max_address(struct ata_device *dev, u64 *max_sectors) ...@@ -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 * ata_set_max_sectors - Set max sectors
* @dev: target device * @dev: target device
* @new_sectors: new max sectors value to set for the 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. * 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) ...@@ -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 * previous non-volatile SET_MAX) by the drive. -EIO on other
* errors. * errors.
*/ */
static int ata_set_max_sectors(struct ata_device *dev, u64 new_sectors, static int ata_set_max_sectors(struct ata_device *dev, u64 new_sectors)
u64 *res_sectors)
{ {
unsigned int err_mask; unsigned int err_mask;
struct ata_taskfile tf; struct ata_taskfile tf;
...@@ -964,11 +962,6 @@ static int ata_set_max_sectors(struct ata_device *dev, u64 new_sectors, ...@@ -964,11 +962,6 @@ static int ata_set_max_sectors(struct ata_device *dev, u64 new_sectors,
return -EIO; return -EIO;
} }
if (lba48)
*res_sectors = ata_tf_to_lba48(&tf);
else
*res_sectors = ata_tf_to_lba(&tf);
return 0; return 0;
} }
...@@ -979,41 +972,93 @@ static int ata_set_max_sectors(struct ata_device *dev, u64 new_sectors, ...@@ -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 * 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 * it if required to the full size of the media. The caller must check
* the drive has the HPA feature set enabled. * the drive has the HPA feature set enabled.
*
* RETURNS:
* 0 on success, -errno on failure.
*/ */
static int ata_hpa_resize(struct ata_device *dev)
static u64 ata_hpa_resize(struct ata_device *dev)
{ {
u64 sectors = dev->n_sectors; struct ata_eh_context *ehc = &dev->link->eh_context;
u64 hpa_sectors; int print_info = ehc->i.flags & ATA_EHI_PRINTINFO;
u64 sectors = ata_id_n_sectors(dev->id);
u64 native_sectors;
int rc; int rc;
rc = ata_read_native_max_address(dev, &hpa_sectors); /* do we need to do it? */
if (rc) 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; return 0;
if (hpa_sectors > sectors) { /* read native max address */
ata_dev_printk(dev, KERN_INFO, rc = ata_read_native_max_address(dev, &native_sectors);
"Host Protected Area detected:\n" if (rc) {
"\tcurrent size: %lld sectors\n" /* If HPA isn't going to be unlocked, skip HPA
"\tnative size: %lld sectors\n", * resizing from the next try.
(long long)sectors, (long long)hpa_sectors); */
if (!ata_ignore_hpa) {
if (ata_ignore_hpa) { ata_dev_printk(dev, KERN_WARNING, "HPA support seems "
rc = ata_set_max_sectors(dev, hpa_sectors, &hpa_sectors); "broken, will skip HPA handling\n");
dev->horkage |= ATA_HORKAGE_BROKEN_HPA;
if (rc == 0) {
ata_dev_printk(dev, KERN_INFO, "native size " /* we can continue if device aborted the command */
"increased to %lld sectors\n", if (rc == -EACCES)
(long long)hpa_sectors); rc = 0;
return hpa_sectors;
}
} }
} 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) ...@@ -1837,6 +1882,11 @@ int ata_dev_configure(struct ata_device *dev)
if (rc) if (rc)
return 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 */ /* print device capabilities */
if (ata_msg_probe(ap)) if (ata_msg_probe(ap))
ata_dev_printk(dev, KERN_DEBUG, ata_dev_printk(dev, KERN_DEBUG,
...@@ -1904,10 +1954,6 @@ int ata_dev_configure(struct ata_device *dev) ...@@ -1904,10 +1954,6 @@ int ata_dev_configure(struct ata_device *dev)
dev->flags |= ATA_DFLAG_FLUSH_EXT; 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 */ /* config NCQ */
ata_dev_config_ncq(dev, ncq_desc, sizeof(ncq_desc)); ata_dev_config_ncq(dev, ncq_desc, sizeof(ncq_desc));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment