Apply various fixes for HPA handling from Tejun Heo

SCSI/libata: Disable HPA if it overlaps a partition (Closes: #572618)
partitions: Rescan partition tables after HPA is disabled
libata: Disable HPA if it is only enabled after suspend

svn path=/dists/sid/linux-2.6/; revision=15682
This commit is contained in:
Ben Hutchings 2010-05-14 22:37:47 +00:00
parent 6ebfd76559
commit 426e0050f9
7 changed files with 456 additions and 0 deletions

3
debian/changelog vendored
View File

@ -29,6 +29,9 @@ linux-2.6 (2.6.32-13) UNRELEASED; urgency=low
(Closes: #580710; works-around: #581173)
* rtl8192su: Add IDs for several more devices (Closes: #580740)
* Add drm and sfc changes from stable 2.6.33.4
* SCSI/libata: Disable HPA if it overlaps a partition (Closes: #572618)
* partitions: Rescan partition tables after HPA is disabled
* libata: Disable HPA if it is only enabled after suspend
[ Aurelien Jarno ]
* mips/swarm: fix boot from IDE based media (Sebastian Andrzej Siewior)

View File

@ -0,0 +1,95 @@
From: Tejun Heo <tj@kernel.org>
To: jeff@garzik.org, linux-ide@vger.kernel.org, jens.axboe@oracle.com, linux-scsi@vger.kernel.org, James.Bottomley@suse.de, linux-kernel@vger.kernel.org
Cc: ben@decadent.org.uk, Tejun Heo <tj@kernel.org>
Date: Thu, 13 May 2010 17:56:44 +0200
Subject: [PATCH 2/4] SCSI: implement sd_set_capacity()
Implement sd_set_capacity() method which calls into
hostt->set_capacity() if implemented. This will be invoked by block
layer if partitions extend beyond the end of the device and can be
used to implement, for example, on-demand ATA host protected area
unlocking.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Ben Hutchings <ben@decadent.org.uk>
---
drivers/scsi/sd.c | 26 ++++++++++++++++++++++++++
include/scsi/scsi_host.h | 11 +++++++++++
2 files changed, 37 insertions(+), 0 deletions(-)
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 8b827f3..59d5e8f 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -97,6 +97,8 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_RBC);
#endif
static int sd_revalidate_disk(struct gendisk *);
+static unsigned long long sd_set_capacity(struct gendisk *disk,
+ unsigned long long new_capacity);
static int sd_probe(struct device *);
static int sd_remove(struct device *);
static void sd_shutdown(struct device *);
@@ -1100,6 +1102,7 @@ static const struct block_device_operations sd_fops = {
#endif
.media_changed = sd_media_changed,
.revalidate_disk = sd_revalidate_disk,
+ .set_capacity = sd_set_capacity,
};
static unsigned int sd_completed_bytes(struct scsi_cmnd *scmd)
@@ -2101,6 +2104,29 @@ static int sd_revalidate_disk(struct gendisk *disk)
}
/**
+ * sd_set_capacity - set disk capacity
+ * @disk: struct gendisk to set capacity for
+ * @new_capacity: new target capacity
+ *
+ * Block layer calls this function if it detects that partitions
+ * on @disk reach beyond the end of the device. If the SCSI host
+ * implements set_capacity method, it's invoked to give it a
+ * chance to adjust the device capacity.
+ *
+ * CONTEXT:
+ * Defined by block layer. Might sleep.
+ */
+static unsigned long long sd_set_capacity(struct gendisk *disk,
+ unsigned long long new_capacity)
+{
+ struct scsi_device *sdev = scsi_disk(disk)->device;
+
+ if (sdev->host->hostt->set_capacity)
+ return sdev->host->hostt->set_capacity(sdev, new_capacity);
+ return 0;
+}
+
+/**
* sd_format_disk_name - format disk name
* @prefix: name prefix - ie. "sd" for SCSI disks
* @index: index of the disk to format name for
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
index c50a97f..31dba89 100644
--- a/include/scsi/scsi_host.h
+++ b/include/scsi/scsi_host.h
@@ -327,6 +327,17 @@ struct scsi_host_template {
sector_t, int []);
/*
+ * This function is called when one or more partitions on the
+ * device reach beyond the end of the device. This function
+ * should return the new capacity if disk was successfully
+ * enlarged. Return values smaller than the current capacity
+ * are ignored.
+ *
+ * Status: OPTIONAL
+ */
+ sector_t (*set_capacity)(struct scsi_device *, sector_t);
+
+ /*
* Can be used to export driver statistics and other infos to the
* world outside the kernel ie. userspace and it also provides an
* interface to feed the driver with information.
--
1.6.4.2

View File

@ -0,0 +1,51 @@
From: Tejun Heo <tj@kernel.org>
To: jeff@garzik.org, linux-ide@vger.kernel.org, jens.axboe@oracle.com, linux-scsi@vger.kernel.org, James.Bottomley@suse.de, linux-kernel@vger.kernel.org
Cc: ben@decadent.org.uk, Tejun Heo <tj@kernel.org>
Date: Thu, 13 May 2010 17:56:43 +0200
Subject: [PATCH 1/4] block: restart partition scan after resizing a device
Device resize via ->set_capacity() can reveal new partitions (e.g. in
chained partition table formats such as dos extended parts). Restart
partition scan from the beginning after resizing a device.
Signed-off-by: Tejun Heo <tj@kernel.org>
Reported-by: Ben Hutchings <ben@decadent.org.uk>
---
fs/partitions/check.c | 7 ++++---
1 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/fs/partitions/check.c b/fs/partitions/check.c
index e238ab2..f80a58d 100644
--- a/fs/partitions/check.c
+++ b/fs/partitions/check.c
@@ -550,7 +550,7 @@ int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
res = invalidate_partition(disk, 0);
if (res)
return res;
-
+rescan:
disk_part_iter_init(&piter, disk, DISK_PITER_INCL_EMPTY);
while ((part = disk_part_iter_next(&piter)))
delete_partition(disk, part->partno);
@@ -581,7 +581,7 @@ int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
/* add partitions */
for (p = 1; p < state->limit; p++) {
sector_t size, from;
-try_scan:
+
size = state->parts[p].size;
if (!size)
continue;
@@ -612,7 +612,8 @@ try_scan:
check_disk_size_change(disk, bdev);
bdev->bd_invalidated = 0;
}
- goto try_scan;
+ kfree(state);
+ goto rescan;
} else {
/*
* we can not ignore partitions of broken tables
--
1.6.4.2

View File

@ -0,0 +1,114 @@
From: Tejun Heo <tj@kernel.org>
To: jeff@garzik.org, linux-ide@vger.kernel.org, jens.axboe@oracle.com, linux-scsi@vger.kernel.org, James.Bottomley@suse.de, linux-kernel@vger.kernel.org
Cc: ben@decadent.org.uk, Tejun Heo <tj@kernel.org>
Date: Thu, 13 May 2010 17:56:46 +0200
Subject: [PATCH 4/4] libata: implement on-demand HPA unlocking
Implement ata_scsi_set_capacity() which will be called through SCSI
layer when block layer notices that partitions on a device extend
beyond the end of the device. ata_scsi_set_capacity() requests EH to
unlock HPA, waits for completion and returns the current device
capacity.
This allows libata to unlock HPA on demand instead of having to decide
whether to unlock upfront. Unlocking on demand is safer than
unlocking by upfront because some BIOSes write private data to the
area beyond HPA limit. This was suggested by Ben Hutchings.
Signed-off-by: Tejun Heo <tj@kernel.org>
Suggested-by: Ben Hutchings <ben@decadent.org.uk>
---
drivers/ata/libata-core.c | 1 +
drivers/ata/libata-scsi.c | 42 ++++++++++++++++++++++++++++++++++++++++++
include/linux/libata.h | 3 +++
3 files changed, 46 insertions(+), 0 deletions(-)
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 9d6e92d..56badb4 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -6797,6 +6797,7 @@ EXPORT_SYMBOL_GPL(ata_dummy_port_info);
EXPORT_SYMBOL_GPL(ata_link_next);
EXPORT_SYMBOL_GPL(ata_dev_next);
EXPORT_SYMBOL_GPL(ata_std_bios_param);
+EXPORT_SYMBOL_GPL(ata_scsi_set_capacity);
EXPORT_SYMBOL_GPL(ata_host_init);
EXPORT_SYMBOL_GPL(ata_host_alloc);
EXPORT_SYMBOL_GPL(ata_host_alloc_pinfo);
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 0088cde..2523943 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -415,6 +415,48 @@ int ata_std_bios_param(struct scsi_device *sdev, struct block_device *bdev,
}
/**
+ * ata_scsi_set_capacity - adjust device capacity
+ * @sdev: SCSI device to adjust device capacity for
+ * @new_capacity: new target capacity
+ *
+ * This function is called if a partition on @sdev extends beyond
+ * the end of the device. It requests EH to unlock HPA and
+ * returns the possibly adjusted capacity.
+ *
+ * LOCKING:
+ * Defined by the SCSI layer. Might sleep.
+ *
+ * RETURNS:
+ * New capacity if adjusted successfully. 0 if device is not
+ * found.
+ */
+sector_t ata_scsi_set_capacity(struct scsi_device *sdev, sector_t new_capacity)
+{
+ struct ata_port *ap = ata_shost_to_port(sdev->host);
+ sector_t capacity = 0;
+ struct ata_device *dev;
+ unsigned long flags;
+
+ spin_lock_irqsave(ap->lock, flags);
+
+ dev = ata_scsi_find_dev(ap, sdev);
+ if (dev && dev->n_sectors < new_capacity &&
+ dev->n_sectors < dev->n_native_sectors) {
+ struct ata_eh_info *ehi = &dev->link->eh_info;
+
+ dev->flags |= ATA_DFLAG_UNLOCK_HPA;
+ ehi->action |= ATA_EH_RESET;
+ ata_port_schedule_eh(ap);
+ spin_unlock_irqrestore(ap->lock, flags);
+ ata_port_wait_eh(ap);
+ capacity = dev->n_sectors;
+ } else
+ spin_unlock_irqrestore(ap->lock, flags);
+
+ return capacity;
+}
+
+/**
* ata_get_identity - Handler for HDIO_GET_IDENTITY ioctl
* @ap: target port
* @sdev: SCSI device to get identify data for
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 242eb26..1082956 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -1027,6 +1027,8 @@ extern void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd,
extern int ata_std_bios_param(struct scsi_device *sdev,
struct block_device *bdev,
sector_t capacity, int geom[]);
+extern sector_t ata_scsi_set_capacity(struct scsi_device *sdev,
+ sector_t new_capacity);
extern int ata_scsi_slave_config(struct scsi_device *sdev);
extern void ata_scsi_slave_destroy(struct scsi_device *sdev);
extern int ata_scsi_change_queue_depth(struct scsi_device *sdev,
@@ -1181,6 +1183,7 @@ extern struct device_attribute *ata_common_sdev_attrs[];
.slave_configure = ata_scsi_slave_config, \
.slave_destroy = ata_scsi_slave_destroy, \
.bios_param = ata_std_bios_param, \
+ .set_capacity = ata_scsi_set_capacity, \
.sdev_attrs = ata_common_sdev_attrs
#define ATA_NCQ_SHT(drv_name) \
--
1.6.4.2

View File

@ -0,0 +1,151 @@
From 445d211b0da4e9a6e6d576edff85085c2aaf53df Mon Sep 17 00:00:00 2001
From: Tejun Heo <tj@kernel.org>
Date: Mon, 5 Apr 2010 10:33:13 +0900
Subject: [PATCH] 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>
---
drivers/ata/libata-core.c | 74 +++++++++++++++++++++++++++-----------------
include/linux/libata.h | 1 +
2 files changed, 46 insertions(+), 29 deletions(-)
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 2ab34dc..49cffb6 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)
{
struct ata_eh_context *ehc = &dev->link->eh_context;
int print_info = ehc->i.flags & ATA_EHI_PRINTINFO;
+ bool unlock_hpa = ata_ignore_hpa || dev->flags & ATA_DFLAG_UNLOCK_HPA;
u64 sectors = ata_id_n_sectors(dev->id);
u64 native_sectors;
int rc;
@@ -1510,7 +1511,7 @@ static int ata_hpa_resize(struct ata_device *dev)
/* If device aborted the command or HPA isn't going to
* be unlocked, skip HPA resizing.
*/
- if (rc == -EACCES || !ata_ignore_hpa) {
+ if (rc == -EACCES || !unlock_hpa) {
ata_dev_printk(dev, KERN_WARNING, "HPA support seems "
"broken, skipping HPA handling\n");
dev->horkage |= ATA_HORKAGE_BROKEN_HPA;
@@ -1525,7 +1526,7 @@ static int ata_hpa_resize(struct ata_device *dev)
dev->n_native_sectors = native_sectors;
/* nothing to do? */
- if (native_sectors <= sectors || !ata_ignore_hpa) {
+ if (native_sectors <= sectors || !unlock_hpa) {
if (!print_info || native_sectors == sectors)
return 0;
@@ -4186,36 +4187,51 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class,
goto fail;
/* verify n_sectors hasn't changed */
- if (dev->class == ATA_DEV_ATA && n_sectors &&
- dev->n_sectors != n_sectors) {
- ata_dev_printk(dev, KERN_WARNING, "n_sectors mismatch "
- "%llu != %llu\n",
- (unsigned long long)n_sectors,
- (unsigned long long)dev->n_sectors);
- /*
- * 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;
- }
+ if (dev->class != ATA_DEV_ATA || !n_sectors ||
+ dev->n_sectors == n_sectors)
+ return 0;
+
+ /* n_sectors has changed */
+ ata_dev_printk(dev, KERN_WARNING, "n_sectors mismatch %llu != %llu\n",
+ (unsigned long long)n_sectors,
+ (unsigned long long)dev->n_sectors);
+
+ /*
+ * 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;
+ return 0;
}
- return 0;
+ /*
+ * Some BIOSes boot w/o HPA but resume w/ HPA locked. Try
+ * unlocking HPA in those cases.
+ *
+ * https://bugzilla.kernel.org/show_bug.cgi?id=15396
+ */
+ if (dev->n_native_sectors == n_native_sectors &&
+ dev->n_sectors < n_sectors && n_sectors == n_native_sectors &&
+ !(dev->horkage & ATA_HORKAGE_BROKEN_HPA)) {
+ ata_dev_printk(dev, KERN_WARNING,
+ "old n_sectors matches native, probably "
+ "late HPA lock, will try to unlock HPA\n");
+ /* try unlocking HPA */
+ dev->flags |= ATA_DFLAG_UNLOCK_HPA;
+ rc = -EIO;
+ } else
+ rc = -ENODEV;
+ /* restore original n_[native_]sectors and fail */
+ dev->n_native_sectors = n_native_sectors;
+ dev->n_sectors = n_sectors;
fail:
ata_dev_printk(dev, KERN_ERR, "revalidation failed (errno=%d)\n", rc);
return rc;
diff --git a/include/linux/libata.h b/include/linux/libata.h
index f8ea71e..b2f2003 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -146,6 +146,7 @@ enum {
ATA_DFLAG_SLEEPING = (1 << 15), /* device is sleeping */
ATA_DFLAG_DUBIOUS_XFER = (1 << 16), /* data transfer not verified */
ATA_DFLAG_NO_UNLOAD = (1 << 17), /* device doesn't support unload */
+ ATA_DFLAG_UNLOCK_HPA = (1 << 18), /* unlock HPA */
ATA_DFLAG_INIT_MASK = (1 << 24) - 1,
ATA_DFLAG_DETACH = (1 << 24),
--
1.7.1

View File

@ -0,0 +1,36 @@
From: Tejun Heo <tj@kernel.org>
To: jeff@garzik.org, linux-ide@vger.kernel.org, jens.axboe@oracle.com, linux-scsi@vger.kernel.org, James.Bottomley@suse.de, linux-kernel@vger.kernel.org
Cc: ben@decadent.org.uk, Tejun Heo <tj@kernel.org>
Date: Thu, 13 May 2010 17:56:45 +0200
Subject: [PATCH 3/4] libata: use the enlarged capacity after late HPA unlock
After late HPA unlock, libata kept using the original capacity
ignoring the new larger native capacity. Enlarging device on the fly
doesn't cause any harm. Use the larger native capacity instead. This
will enable on-demand HPA unlocking.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Ben Hutchings <ben@decadent.org.uk>
---
drivers/ata/libata-core.c | 5 ++---
1 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 86f405b..9d6e92d 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -4212,9 +4212,8 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class,
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;
+ "late HPA unlock, n_sectors updated\n");
+ /* use the larger n_sectors */
return 0;
}
--
1.6.4.2

View File

@ -23,3 +23,9 @@
+ bugfix/all/stable/2.6.33.4.patch
+ bugfix/all/drm-i915-Stop-trying-to-use-ACPI-lid-status-to-deter-2.patch
+ features/arm/guruplug.patch
+ bugfix/all/block-restart-partition-scan-after-resizing.patch
- debian/sd-libata-set-capacity-abi-changes.patch
+ bugfix/all/SCSI-implement-sd_set_capacity.patch
+ bugfix/all/libata-unlock-HPA-if-device-shrunk.patch
+ bugfix/all/libata-use-enlarged-capacity-after-late-HPA-unlock.patch
+ bugfix/all/libata-implement-on-demand-HPA-unlocking.patch