[PATCH V2 4/4] block: avoid to drop & re-add partitions if partitions aren't changed

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



block ioctl(BLKRRPART) always drops current partitions and adds
partitions again, even though there isn't any change in partitions table.

ioctl(BLKRRPART) is called by systemd-udevd and some disk utilities not
unusually. When it is run, partitions disk node are dropped and added back,
this way may confuse userspace or users, for example, one normal workable
partition device node may disappear any time.

Fix this issue by checking if there is real change in partitions, and only
drop & re-add them when partitions state is really changed.

Cc: Ewan D. Milne <emilne@xxxxxxxxxx>
Signed-off-by: Ming Lei <ming.lei@xxxxxxxxxx>
---
 block/partitions/core.c | 76 ++++++++++++++++++++++++++++++++++++++---
 fs/block_dev.c          | 30 ++++++++--------
 include/linux/genhd.h   |  1 +
 3 files changed, 88 insertions(+), 19 deletions(-)

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




[Index of Archives]     [Linux RAID]     [Linux SCSI]     [Linux ATA RAID]     [IDE]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Device Mapper]

  Powered by Linux