@@ -1079,6 +1079,28 @@ struct bd_holder_disk {
int refcnt;
};
+static int bd_disk_holder_corrupted_range(struct block_device *bdev, loff_t off,
+ size_t len, void *data)
+{
+ struct bd_holder_disk *holder;
+ struct gendisk *disk;
+ int rc = 0;
+
+ if (list_empty(&(bdev->bd_holder_disks)))
+ return -ENODEV;
+
+ list_for_each_entry(holder, &bdev->bd_holder_disks, list) {
+ disk = holder->disk;
+ if (disk->fops->corrupted_range) {
+ rc = disk->fops->corrupted_range(disk, bdev, off,
+ len, data);
+ if (rc != -ENODEV)
+ break;
+ }
+ }
+ return rc;
+}
+
static struct bd_holder_disk *bd_find_holder_disk(struct block_device *bdev,
struct gendisk *disk)
{
@@ -1212,7 +1234,30 @@ void bd_unlink_disk_holder(struct block_device *bdev, struct gendisk *disk)
mutex_unlock(&bdev->bd_mutex);
}
EXPORT_SYMBOL_GPL(bd_unlink_disk_holder);
-#endif
+#else
+static inline int bd_disk_holder_corrupted_range(struct block_device *bdev,
+ loff_t off, size_t len, void *data)
+{
+ return -EOPNOTSUPP;
+}
+#endif /* CONFIG_SYSFS */
+
+int bd_corrupted_range(struct block_device *bdev, loff_t disk_off,
+ loff_t bdev_off, size_t len, void *data)
+{
+ struct super_block *sb = get_super(bdev);
+ int rc = -EOPNOTSUPP;
+
+ if (!sb) {
+ rc = bd_disk_holder_corrupted_range(bdev, disk_off, len, data);
+ return rc;
+ } else if (sb->s_op->corrupted_range)
+ rc = sb->s_op->corrupted_range(sb, bdev, bdev_off, len, data);
+ drop_super(sb);
+
+ return rc;
+}
+EXPORT_SYMBOL(bd_corrupted_range);
static void __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part);
@@ -314,6 +314,8 @@ void unregister_blkdev(unsigned int major, const char *name);
bool bdev_check_media_change(struct block_device *bdev);
int __invalidate_device(struct block_device *bdev, bool kill_dirty);
void set_capacity(struct gendisk *disk, sector_t size);
+int bd_corrupted_range(struct block_device *bdev, loff_t disk_off,
+ loff_t bdev_off, size_t len, void *data);
/* for drivers/char/raw.c: */
int blkdev_ioctl(struct block_device *, fmode_t, unsigned, unsigned long);
As is show in the call tree: ... pgmap->ops->memory_failure() gendisk->fops->corrupted_range() sb->s_ops->currupted_range() ... currupted_range() is called from disk(pmem) to superblock(filesystem). Thus, we need to introduce a helper function to obtain the superblock from a given disk. Normally, a filesystem can be created by mkfs directly on a block device or a parted disk. We can obtain the superblock by calling get_super(). But get_super() is not suitable for mapped device, such as LVM. To obtain MD's superblock, we can iterate bd_holder_disks of a block device to find out the MD that contains it. By this way, MD is able to call corrupted_range() from its target block device. Signed-off-by: Shiyang Ruan <ruansy.fnst@cn.fujitsu.com> --- fs/block_dev.c | 47 ++++++++++++++++++++++++++++++++++++++++++- include/linux/genhd.h | 2 ++ 2 files changed, 48 insertions(+), 1 deletion(-)