@@ -505,6 +505,8 @@ static int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
q->limits.alignment_offset,
(unsigned long long) start << SECTOR_SHIFT);
+ limits->zoned = bdev_zoned_model(bdev);
+
return 0;
}
@@ -720,6 +722,96 @@ static int validate_hardware_logical_block_alignment(struct dm_table *table,
return 0;
}
+/*
+ * Check a devices's table for compatibility between zoned devices used by
+ * the table targets. The zone model may come directly from a target block
+ * device or may have been set by the target using the io_hints method.
+ * Overall, if any of the table device targets is advertized as a zoned
+ * block device, then all targets devices should also be advertized as
+ * using the same model and the devices zone size all equal.
+ */
+static int validate_hardware_zone_model(struct dm_table *table,
+ struct queue_limits *limits)
+{
+ struct dm_target *ti;
+ struct queue_limits ti_limits;
+ unsigned int zone_sectors = limits->chunk_sectors;
+ unsigned int num_targets = dm_table_get_num_targets(table);
+ int zone_model = -1;
+ unsigned int i = 0;
+
+ if (!num_targets)
+ return 0;
+
+ /*
+ * Check each entry in the table in turn.
+ */
+ while (i < num_targets) {
+
+ ti = dm_table_get_target(table, i);
+
+ /* Get the target device limits */
+ blk_set_stacking_limits(&ti_limits);
+ if (ti->type->iterate_devices)
+ ti->type->iterate_devices(ti, dm_set_device_limits,
+ &ti_limits);
+
+ /*
+ * Let the target driver change the hardware limits, and
+ * in particular the zone model if needed.
+ */
+ if (ti->type->io_hints)
+ ti->type->io_hints(ti, &ti_limits);
+
+ /* Check zone model compatibility */
+ if (zone_model == -1)
+ zone_model = ti_limits.zoned;
+ if (ti_limits.zoned != zone_model) {
+ zone_model = -1;
+ break;
+ }
+
+ if (zone_model != BLK_ZONED_NONE) {
+ /* Check zone size validity and compatibility */
+ if (!zone_sectors ||
+ !is_power_of_2(zone_sectors))
+ break;
+ if (ti_limits.chunk_sectors != zone_sectors) {
+ zone_sectors = ti_limits.chunk_sectors;
+ break;
+ }
+ }
+
+ i++;
+
+ }
+
+ if (i < num_targets) {
+ if (zone_model == -1)
+ DMWARN("%s: table line %u (start sect %llu len %llu) "
+ "has an incompatible zone model",
+ dm_device_name(table->md), i,
+ (unsigned long long) ti->begin,
+ (unsigned long long) ti->len);
+ else
+ DMWARN("%s: table line %u (start sect %llu len %llu) "
+ "has an incompatible zone size %u",
+ dm_device_name(table->md), i,
+ (unsigned long long) ti->begin,
+ (unsigned long long) ti->len,
+ zone_sectors);
+ return -EINVAL;
+ }
+
+ if (zone_model == BLK_ZONED_HA ||
+ zone_model == BLK_ZONED_HM) {
+ limits->zoned = zone_model;
+ limits->chunk_sectors = zone_sectors;
+ }
+
+ return 0;
+}
+
int dm_table_add_target(struct dm_table *t, const char *type,
sector_t start, sector_t len, char *params)
{
@@ -1432,6 +1524,9 @@ int dm_calculate_queue_limits(struct dm_table *table,
(unsigned long long) ti->len);
}
+ if (validate_hardware_zone_model(table, limits))
+ return -EINVAL;
+
return validate_hardware_logical_block_alignment(table, limits);
}