aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2010-05-15 14:09:31 -0400
committerJens Axboe <jens.axboe@oracle.com>2010-05-21 14:01:03 -0400
commitb403a98e260f3a8c7c33f58a07c7ae549852170f (patch)
treeb6672b43cc1808632547f2f16c917d33d85e1399
parent1493bf217f7f59a5d9e2095a7dbcec00fb36ca8b (diff)
block: improve automatic native capacity unlocking
Currently, native capacity unlocking is initiated only when a recognized partition extends beyond the end of the disk. However, there are several other unhandled cases where truncated capacity can lead to misdetection of partitions. * Partition table is fully beyond EOD. * Partition table is partially beyond EOD (daisy chained ones). * Recognized partition starts beyond EOD. This patch updates generic partition check code such that all the above three cases are handled too. For the first two, @state tracks whether low level partition check code tried to read beyond EOD during partition scan and triggers native capacity unlocking accordingly. The third is now handled similarly to the original unlocking case. Signed-off-by: Tejun Heo <tj@kernel.org> Cc: Ben Hutchings <ben@decadent.org.uk> Acked-by: David S. Miller <davem@davemloft.net> Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
-rw-r--r--fs/partitions/check.c69
-rw-r--r--fs/partitions/check.h5
2 files changed, 60 insertions, 14 deletions
diff --git a/fs/partitions/check.c b/fs/partitions/check.c
index a19995c6f6af..5dcd4b0c5533 100644
--- a/fs/partitions/check.c
+++ b/fs/partitions/check.c
@@ -161,7 +161,7 @@ check_partition(struct gendisk *hd, struct block_device *bdev)
161 struct parsed_partitions *state; 161 struct parsed_partitions *state;
162 int i, res, err; 162 int i, res, err;
163 163
164 state = kmalloc(sizeof(struct parsed_partitions), GFP_KERNEL); 164 state = kzalloc(sizeof(struct parsed_partitions), GFP_KERNEL);
165 if (!state) 165 if (!state)
166 return NULL; 166 return NULL;
167 167
@@ -187,6 +187,8 @@ check_partition(struct gendisk *hd, struct block_device *bdev)
187 } 187 }
188 if (res > 0) 188 if (res > 0)
189 return state; 189 return state;
190 if (state->access_beyond_eod)
191 err = -ENOSPC;
190 if (err) 192 if (err)
191 /* The partition is unrecognized. So report I/O errors if there were any */ 193 /* The partition is unrecognized. So report I/O errors if there were any */
192 res = err; 194 res = err;
@@ -539,13 +541,34 @@ exit:
539 disk_part_iter_exit(&piter); 541 disk_part_iter_exit(&piter);
540} 542}
541 543
544static bool disk_unlock_native_capacity(struct gendisk *disk)
545{
546 const struct block_device_operations *bdops = disk->fops;
547
548 if (bdops->unlock_native_capacity &&
549 !(disk->flags & GENHD_FL_NATIVE_CAPACITY)) {
550 printk(KERN_CONT "enabling native capacity\n");
551 bdops->unlock_native_capacity(disk);
552 disk->flags |= GENHD_FL_NATIVE_CAPACITY;
553 return true;
554 } else {
555 printk(KERN_CONT "truncated\n");
556 return false;
557 }
558}
559
542int rescan_partitions(struct gendisk *disk, struct block_device *bdev) 560int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
543{ 561{
562 struct parsed_partitions *state = NULL;
544 struct disk_part_iter piter; 563 struct disk_part_iter piter;
545 struct hd_struct *part; 564 struct hd_struct *part;
546 struct parsed_partitions *state;
547 int p, highest, res; 565 int p, highest, res;
548rescan: 566rescan:
567 if (state && !IS_ERR(state)) {
568 kfree(state);
569 state = NULL;
570 }
571
549 if (bdev->bd_part_count) 572 if (bdev->bd_part_count)
550 return -EBUSY; 573 return -EBUSY;
551 res = invalidate_partition(disk, 0); 574 res = invalidate_partition(disk, 0);
@@ -563,8 +586,32 @@ rescan:
563 bdev->bd_invalidated = 0; 586 bdev->bd_invalidated = 0;
564 if (!get_capacity(disk) || !(state = check_partition(disk, bdev))) 587 if (!get_capacity(disk) || !(state = check_partition(disk, bdev)))
565 return 0; 588 return 0;
566 if (IS_ERR(state)) /* I/O error reading the partition table */ 589 if (IS_ERR(state)) {
590 /*
591 * I/O error reading the partition table. If any
592 * partition code tried to read beyond EOD, retry
593 * after unlocking native capacity.
594 */
595 if (PTR_ERR(state) == -ENOSPC) {
596 printk(KERN_WARNING "%s: partition table beyond EOD, ",
597 disk->disk_name);
598 if (disk_unlock_native_capacity(disk))
599 goto rescan;
600 }
567 return -EIO; 601 return -EIO;
602 }
603 /*
604 * If any partition code tried to read beyond EOD, try
605 * unlocking native capacity even if partition table is
606 * sucessfully read as we could be missing some partitions.
607 */
608 if (state->access_beyond_eod) {
609 printk(KERN_WARNING
610 "%s: partition table partially beyond EOD, ",
611 disk->disk_name);
612 if (disk_unlock_native_capacity(disk))
613 goto rescan;
614 }
568 615
569 /* tell userspace that the media / partition table may have changed */ 616 /* tell userspace that the media / partition table may have changed */
570 kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE); 617 kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE);
@@ -590,25 +637,20 @@ rescan:
590 from = state->parts[p].from; 637 from = state->parts[p].from;
591 if (from >= get_capacity(disk)) { 638 if (from >= get_capacity(disk)) {
592 printk(KERN_WARNING 639 printk(KERN_WARNING
593 "%s: p%d ignored, start %llu is behind the end of the disk\n", 640 "%s: p%d start %llu is beyond EOD, ",
594 disk->disk_name, p, (unsigned long long) from); 641 disk->disk_name, p, (unsigned long long) from);
642 if (disk_unlock_native_capacity(disk))
643 goto rescan;
595 continue; 644 continue;
596 } 645 }
597 646
598 if (from + size > get_capacity(disk)) { 647 if (from + size > get_capacity(disk)) {
599 const struct block_device_operations *bdops = disk->fops;
600
601 printk(KERN_WARNING 648 printk(KERN_WARNING
602 "%s: p%d size %llu exceeds device capacity, ", 649 "%s: p%d size %llu extends beyond EOD, ",
603 disk->disk_name, p, (unsigned long long) size); 650 disk->disk_name, p, (unsigned long long) size);
604 651
605 if (bdops->unlock_native_capacity && 652 if (disk_unlock_native_capacity(disk)) {
606 (disk->flags & GENHD_FL_NATIVE_CAPACITY) == 0) {
607 printk(KERN_CONT "enabling native capacity\n");
608 bdops->unlock_native_capacity(disk);
609 disk->flags |= GENHD_FL_NATIVE_CAPACITY;
610 /* free state and restart */ 653 /* free state and restart */
611 kfree(state);
612 goto rescan; 654 goto rescan;
613 } else { 655 } else {
614 /* 656 /*
@@ -617,7 +659,6 @@ rescan:
617 * we limit them to the end of the disk to avoid 659 * we limit them to the end of the disk to avoid
618 * creating invalid block devices 660 * creating invalid block devices
619 */ 661 */
620 printk(KERN_CONT "limited to end of disk\n");
621 size = get_capacity(disk) - from; 662 size = get_capacity(disk) - from;
622 } 663 }
623 } 664 }
diff --git a/fs/partitions/check.h b/fs/partitions/check.h
index 4b31a97775be..52f8bd399396 100644
--- a/fs/partitions/check.h
+++ b/fs/partitions/check.h
@@ -15,11 +15,16 @@ struct parsed_partitions {
15 } parts[DISK_MAX_PARTS]; 15 } parts[DISK_MAX_PARTS];
16 int next; 16 int next;
17 int limit; 17 int limit;
18 bool access_beyond_eod;
18}; 19};
19 20
20static inline void *read_part_sector(struct parsed_partitions *state, 21static inline void *read_part_sector(struct parsed_partitions *state,
21 sector_t n, Sector *p) 22 sector_t n, Sector *p)
22{ 23{
24 if (n >= get_capacity(state->bdev->bd_disk)) {
25 state->access_beyond_eod = true;
26 return NULL;
27 }
23 return read_dev_sector(state->bdev, n, p); 28 return read_dev_sector(state->bdev, n, p);
24} 29}
25 30