With the support of ->rmap(), it is possible to obtain the superblock on
a mapped device.

Signed-off-by: Shiyang Ruan <ruansy.f...@cn.fujitsu.com>
---
 drivers/md/dm.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 7bac564f3faa..31b0c340b695 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -507,6 +507,66 @@ static int dm_blk_report_zones(struct gendisk *disk, 
sector_t sector,
 #define dm_blk_report_zones            NULL
 #endif /* CONFIG_BLK_DEV_ZONED */
 
+struct corrupted_hit_info {
+       struct block_device *bdev;
+       sector_t offset;
+};
+
+static int dm_blk_corrupted_hit(struct dm_target *ti, struct dm_dev *dev,
+                               sector_t start, sector_t count, void *data)
+{
+       struct corrupted_hit_info *bc = data;
+
+       return bc->bdev == (void *)dev->bdev &&
+                       (start <= bc->offset && bc->offset < start + count);
+}
+
+struct corrupted_do_info {
+       size_t length;
+       void *data;
+};
+
+static int dm_blk_corrupted_do(struct dm_target *ti, struct block_device *bdev,
+                              sector_t disk_sect, void *data)
+{
+       struct corrupted_do_info *bc = data;
+       loff_t disk_off = to_bytes(disk_sect);
+       loff_t bdev_off = to_bytes(disk_sect - get_start_sect(bdev));
+
+       return bd_corrupted_range(bdev, disk_off, bdev_off, bc->length, 
bc->data);
+}
+
+static int dm_blk_corrupted_range(struct gendisk *disk,
+                                 struct block_device *target_bdev,
+                                 loff_t target_offset, size_t len, void *data)
+{
+       struct mapped_device *md = disk->private_data;
+       struct dm_table *map;
+       struct dm_target *ti;
+       sector_t target_sect = to_sector(target_offset);
+       struct corrupted_hit_info hi = {target_bdev, target_sect};
+       struct corrupted_do_info di = {len, data};
+       int srcu_idx, i, rc = -ENODEV;
+
+       map = dm_get_live_table(md, &srcu_idx);
+       if (!map)
+               return rc;
+
+       for (i = 0; i < dm_table_get_num_targets(map); i++) {
+               ti = dm_table_get_target(map, i);
+               if (!(ti->type->iterate_devices && ti->type->rmap))
+                       continue;
+               if (!ti->type->iterate_devices(ti, dm_blk_corrupted_hit, &hi))
+                       continue;
+
+               rc = ti->type->rmap(ti, target_sect, dm_blk_corrupted_do, &di);
+               break;
+       }
+
+       dm_put_live_table(md, srcu_idx);
+       return rc;
+}
+
 static int dm_prepare_ioctl(struct mapped_device *md, int *srcu_idx,
                            struct block_device **bdev)
 {
@@ -3062,6 +3122,7 @@ static const struct block_device_operations dm_blk_dops = 
{
        .getgeo = dm_blk_getgeo,
        .report_zones = dm_blk_report_zones,
        .pr_ops = &dm_pr_ops,
+       .corrupted_range = dm_blk_corrupted_range,
        .owner = THIS_MODULE
 };
 
-- 
2.30.0



Reply via email to