|
|
@@ -430,6 +430,56 @@ static int drop_partitions(struct gendisk *disk, struct block_device *bdev)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static bool part_zone_aligned(struct gendisk *disk,
|
|
|
+ struct block_device *bdev,
|
|
|
+ sector_t from, sector_t size)
|
|
|
+{
|
|
|
+ unsigned int zone_size = bdev_zone_size(bdev);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If this function is called, then the disk is a zoned block device
|
|
|
+ * (host-aware or host-managed). This can be detected even if the
|
|
|
+ * zoned block device support is disabled (CONFIG_BLK_DEV_ZONED not
|
|
|
+ * set). In this case, however, only host-aware devices will be seen
|
|
|
+ * as a block device is not created for host-managed devices. Without
|
|
|
+ * zoned block device support, host-aware drives can still be used as
|
|
|
+ * regular block devices (no zone operation) and their zone size will
|
|
|
+ * be reported as 0. Allow this case.
|
|
|
+ */
|
|
|
+ if (!zone_size)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Check partition start and size alignement. If the drive has a
|
|
|
+ * smaller last runt zone, ignore it and allow the partition to
|
|
|
+ * use it. Check the zone size too: it should be a power of 2 number
|
|
|
+ * of sectors.
|
|
|
+ */
|
|
|
+ if (WARN_ON_ONCE(!is_power_of_2(zone_size))) {
|
|
|
+ u32 rem;
|
|
|
+
|
|
|
+ div_u64_rem(from, zone_size, &rem);
|
|
|
+ if (rem)
|
|
|
+ return false;
|
|
|
+ if ((from + size) < get_capacity(disk)) {
|
|
|
+ div_u64_rem(size, zone_size, &rem);
|
|
|
+ if (rem)
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ if (from & (zone_size - 1))
|
|
|
+ return false;
|
|
|
+ if ((from + size) < get_capacity(disk) &&
|
|
|
+ (size & (zone_size - 1)))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
|
|
|
{
|
|
|
struct parsed_partitions *state = NULL;
|
|
|
@@ -529,6 +579,21 @@ int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /*
|
|
|
+ * On a zoned block device, partitions should be aligned on the
|
|
|
+ * device zone size (i.e. zone boundary crossing not allowed).
|
|
|
+ * Otherwise, resetting the write pointer of the last zone of
|
|
|
+ * one partition may impact the following partition.
|
|
|
+ */
|
|
|
+ if (bdev_is_zoned(bdev) &&
|
|
|
+ !part_zone_aligned(disk, bdev, from, size)) {
|
|
|
+ printk(KERN_WARNING
|
|
|
+ "%s: p%d start %llu+%llu is not zone aligned\n",
|
|
|
+ disk->disk_name, p, (unsigned long long) from,
|
|
|
+ (unsigned long long) size);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
part = add_partition(disk, p, from, size,
|
|
|
state->parts[p].flags,
|
|
|
&state->parts[p].info);
|