diff --git a/block/partitions/core.c b/block/partitions/core.c
index 288ca362ccbd..901a00fed3c9 100644
--- a/block/partitions/core.c
+++ b/block/partitions/core.c
@@ -123,7 +123,7 @@ static void free_partitions(struct parsed_partitions *state)
}
static struct parsed_partitions *check_partition(struct gendisk *hd,
- struct block_device *bdev)
+ struct block_device *bdev, bool show_part)
{
struct parsed_partitions *state;
int i, res, err;@@ -159,7 +159,8 @@ static struct parsed_partitions *check_partition(struct gendisk *hd,
}
if (res > 0) {
- printk(KERN_INFO "%s", state->pp_buf);
+ if (show_part)
+ printk(KERN_INFO "%s", state->pp_buf);
free_page((unsigned long)state->pp_buf);
return state;@@ -604,7 +605,8 @@ static bool blk_add_partition(struct gendisk *disk, struct block_device *bdev,
}
static int __blk_check_partitions(struct gendisk *disk,
- struct block_device *bdev, struct parsed_partitions **s)
+ struct block_device *bdev, struct parsed_partitions **s,
+ bool show_part)
{
struct parsed_partitions *state = NULL;
int ret = -EAGAIN;@@ -612,7 +614,7 @@ static int __blk_check_partitions(struct gendisk *disk,
if (!disk_part_scan_enabled(disk))
goto out;
- state = check_partition(disk, bdev);
+ state = check_partition(disk, bdev, show_part);
if (!state)
goto out;
@@ -679,12 +681,76 @@ static int __blk_add_partitions(struct gendisk *disk,
return 0;
}
+static bool partition_changed(struct block_device *part,
+ struct parsed_partitions *state)
+{
+ int p = part->bd_partno;
+
+ if (part->bd_start_sect != state->parts[p].from)
+ return true;
+
+ if (bdev_nr_sectors(part) != state->parts[p].size)
+ return true;
+
+ if (part->bd_part_flags != state->parts[p].flags)
+ return true;
+
+ if (bcmp(part->bd_meta_info, &state->parts[p].info,
+ sizeof(struct partition_meta_info)))
+ return true;
+
+ return false;
+}
+
+static int partition_count(struct parsed_partitions *state)
+{
+ int p;
+ int cnt = 0;
+
+ for (p = 1; p < state->limit; p++) {
+ if (state->parts[p].size || state->parts[p].from)
+ cnt++;
+ }
+ return cnt;
+}
+
+bool blk_partition_changed(struct gendisk *disk, struct block_device *bdev)
+{
+ struct disk_part_iter piter;
+ struct block_device *part;
+ struct parsed_partitions *state = NULL;
+ bool changed = true;
+ int nr_parts = 0;
+
+ if (!get_capacity(disk))
+ goto out;
+
+ if (__blk_check_partitions(disk, bdev, &state, false) != 0 || !state)
+ goto out;
+
+ changed = false;
+ disk_part_iter_init(&piter, disk, 0);
+ while ((part = disk_part_iter_next(&piter))) {
+ if (partition_changed(part, state))
+ changed = true;
+ nr_parts++;
+ }
+ disk_part_iter_exit(&piter);
+
+ if (nr_parts != partition_count(state))
+ changed = true;
+ out:
+ if (state)
+ free_partitions(state);
+ return changed;
+}
+
int blk_add_partitions(struct gendisk *disk, struct block_device *bdev)
{
struct parsed_partitions *state;
int ret;
- ret = __blk_check_partitions(disk, bdev, &state);
+ ret = __blk_check_partitions(disk, bdev, &state, true);
if (ret != 0)
return ret;
if (!state)diff --git a/fs/block_dev.c b/fs/block_dev.c
index ec26179c8062..b279649ba532 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -1234,10 +1234,6 @@ int bdev_disk_changed(struct block_device *bdev, bool invalidate)
clear_bit(GD_NEED_PART_SCAN, &bdev->bd_disk->state);
rescan:
- ret = blk_drop_partitions(bdev);
- if (ret)
- return ret;
-
/*
* Historically we only set the capacity to zero for devices that
* support partitions (independ of actually having partitions created).
@@ -1255,16 +1251,22 @@ int bdev_disk_changed(struct block_device *bdev, bool invalidate)
disk->fops->revalidate_disk(disk);
}
- if (get_capacity(disk)) {
- ret = blk_add_partitions(disk, bdev);
- if (ret == -EAGAIN)
- goto rescan;
- } else if (invalidate) {
- /*
- * Tell userspace that the media / partition table may have
- * changed.
- */
- kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE);
+ if (blk_partition_changed(disk, bdev)) {
+ ret = blk_drop_partitions(bdev);
+ if (ret)
+ return ret;
+
+ if (get_capacity(disk)) {
+ ret = blk_add_partitions(disk, bdev);
+ if (ret == -EAGAIN)
+ goto rescan;
+ } else if (invalidate) {
+ /*
+ * Tell userspace that the media / partition table may have
+ * changed.
+ */
+ kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE);
+ }
}
return ret;diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index f364619092cc..ae9ebfb9a8e1 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -274,6 +274,7 @@ static inline sector_t get_capacity(struct gendisk *disk)
int bdev_disk_changed(struct block_device *bdev, bool invalidate);
int blk_add_partitions(struct gendisk *disk, struct block_device *bdev);
int blk_drop_partitions(struct block_device *bdev);
+bool blk_partition_changed(struct gendisk *disk, struct block_device *bdev);
extern struct gendisk *__alloc_disk_node(int minors, int node_id);
extern void put_disk(struct gendisk *disk);
--
2.29.2