From: Kiyoshi Ueda Subject: Block integrity support for DM and MD References: FATE#304345 This patch adds Data integrity support to DM and MD. It has been reworked to support request-based multipathing. Signed-off-by: Kiyoshi Ueda Signed-off-by: Jun'ichi Nomura Cc: Martin K. Petersen Signed-off-by: Hannes Reinecke --- drivers/md/dm-table.c | 37 +++++++++++++++++++++++++++++++++++++ drivers/md/dm.c | 22 ++++++++++++++++++++++ drivers/md/dm.h | 1 + drivers/md/md.c | 34 ++++++++++++++++++++++++++++++++++ fs/bio-integrity.c | 5 +++-- fs/bio.c | 2 +- include/linux/bio.h | 4 ++-- 7 files changed, 100 insertions(+), 5 deletions(-) --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -951,6 +951,43 @@ void dm_table_set_restrictions(struct dm queue_flag_set_unlocked(QUEUE_FLAG_STACKABLE, q); } +void dm_table_set_integrity(struct dm_table *t, struct mapped_device *md) +{ + struct list_head *devices = dm_table_get_devices(t); + struct dm_dev *prev, *cur; + + /* + * Run through all devices to ensure they have matching + * integrity profile + */ + cur = prev = NULL; + + list_for_each_entry(cur, devices, list) { + + if (prev && blk_integrity_compare(prev->bdev->bd_disk, + cur->bdev->bd_disk) < 0) { + printk(KERN_ERR "%s: %s %s Integrity mismatch!\n", + __func__, prev->bdev->bd_disk->disk_name, + cur->bdev->bd_disk->disk_name); + return; + } + prev = cur; + } + + /* Register dm device as being integrity capable */ + if (prev && bdev_get_integrity(prev->bdev)) { + struct gendisk *disk = dm_disk(md); + + if (blk_integrity_register(dm_disk(md), + bdev_get_integrity(prev->bdev))) + printk(KERN_ERR "%s: %s Could not register integrity!\n", + __func__, disk->disk_name); + else + printk(KERN_INFO "Enabling data integrity on %s\n", + disk->disk_name); + } +} + unsigned int dm_table_get_num_targets(struct dm_table *t) { return t->num_targets; --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1022,6 +1022,13 @@ static struct bio *split_bvec(struct bio clone->bi_size = to_bytes(len); clone->bi_io_vec->bv_offset = offset; clone->bi_io_vec->bv_len = clone->bi_size; + clone->bi_flags |= 1 << BIO_CLONED; + + if (bio_integrity(bio)) { + bio_integrity_clone(clone, bio, GFP_NOIO, bs); + bio_integrity_trim(clone, + bio_sector_offset(bio, idx, offset), len); + } return clone; } @@ -1044,6 +1051,14 @@ static struct bio *clone_bio(struct bio clone->bi_size = to_bytes(len); clone->bi_flags &= ~(1 << BIO_SEG_VALID); + if (bio_integrity(bio)) { + bio_integrity_clone(clone, bio, GFP_NOIO, bs); + + if (idx != bio->bi_idx || clone->bi_size < bio->bi_size) + bio_integrity_trim(clone, + bio_sector_offset(bio, idx, 0), len); + } + return clone; } @@ -1365,6 +1380,11 @@ static int clone_request_bios(struct req } __bio_clone(clone_bio, bio); + if (bio_integrity(bio)) + if (bio_integrity_clone(clone_bio, bio, GFP_ATOMIC, + md->bs) < 0) + goto free_and_out; + clone_bio->bi_destructor = dm_bio_destructor; clone_bio->bi_end_io = end_clone_bio; info->rq = clone; @@ -1835,6 +1855,7 @@ static void free_dev(struct mapped_devic mempool_destroy(md->io_pool); if (md->bs) bioset_free(md->bs); + blk_integrity_unregister(md->disk); del_gendisk(md->disk); free_minor(minor); @@ -1910,6 +1931,7 @@ static int __bind(struct mapped_device * write_lock(&md->map_lock); md->map = t; dm_table_set_restrictions(t, q); + dm_table_set_integrity(t, md); if (!(dm_table_get_mode(t) & FMODE_WRITE)) { set_disk_ro(md->disk, 1); } else { --- a/drivers/md/dm.h +++ b/drivers/md/dm.h @@ -51,6 +51,7 @@ void dm_table_event_callback(struct dm_t struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index); struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector); void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q); +void dm_table_set_integrity(struct dm_table *t, struct mapped_device *md); struct list_head *dm_table_get_devices(struct dm_table *t); void dm_table_presuspend_targets(struct dm_table *t); void dm_table_postsuspend_targets(struct dm_table *t); --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -1411,6 +1411,38 @@ static int match_mddev_units(mddev_t *md static LIST_HEAD(pending_raid_disks); +static void md_integrity_check(mdk_rdev_t *rdev, mddev_t *mddev) +{ + struct mdk_personality *pers = mddev->pers; + struct gendisk *disk = mddev->gendisk; + struct blk_integrity *bi_rdev = bdev_get_integrity(rdev->bdev); + struct blk_integrity *bi_mddev = blk_get_integrity(disk); + + /* Data integrity passthrough not supported on RAID 4, 5 and 6 */ + if (pers && pers->level >= 4 && pers->level <= 6) + return; + + /* If rdev is integrity capable, register profile for mddev */ + if (!bi_mddev && bi_rdev) { + if (blk_integrity_register(disk, bi_rdev)) + printk(KERN_ERR "%s: %s Could not register integrity!\n", + __func__, disk->disk_name); + else + printk(KERN_NOTICE "Enabling data integrity on %s\n", + disk->disk_name); + return; + } + + /* Check that mddev and rdev have matching profiles */ + if (blk_integrity_compare(disk, rdev->bdev->bd_disk) < 0) { + printk(KERN_ERR "%s: %s/%s integrity mismatch!\n", __func__, + disk->disk_name, rdev->bdev->bd_disk->disk_name); + printk(KERN_NOTICE "Disabling data integrity on %s\n", + disk->disk_name); + blk_integrity_unregister(disk); + } +} + static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) { char b[BDEVNAME_SIZE]; @@ -1479,6 +1511,7 @@ static int bind_rdev_to_array(mdk_rdev_t } list_add_rcu(&rdev->same_set, &mddev->disks); bd_claim_by_disk(rdev->bdev, rdev->bdev->bd_holder, mddev->gendisk); + md_integrity_check(rdev, mddev); return 0; fail: @@ -3967,6 +4000,7 @@ static int do_md_stop(mddev_t * mddev, i printk(KERN_INFO "md: %s switched to read-only mode.\n", mdname(mddev)); err = 0; + blk_integrity_unregister(disk); md_new_event(mddev); sysfs_notify(&mddev->kobj, NULL, "array_state"); out: --- a/fs/bio-integrity.c +++ b/fs/bio-integrity.c @@ -655,19 +655,20 @@ EXPORT_SYMBOL(bio_integrity_split); * bio_integrity_clone - Callback for cloning bios with integrity metadata * @bio: New bio * @bio_src: Original bio + * @gfp_mask: Memory allocation mask * @bs: bio_set to allocate bip from * * Description: Called to allocate a bip when cloning a bio */ int bio_integrity_clone(struct bio *bio, struct bio *bio_src, - struct bio_set *bs) + gfp_t gfp_mask, struct bio_set *bs) { struct bio_integrity_payload *bip_src = bio_src->bi_integrity; struct bio_integrity_payload *bip; BUG_ON(bip_src == NULL); - bip = bio_integrity_alloc_bioset(bio, GFP_NOIO, bip_src->bip_vcnt, bs); + bip = bio_integrity_alloc_bioset(bio, gfp_mask, bip_src->bip_vcnt, bs); if (bip == NULL) return -EIO; --- a/fs/bio.c +++ b/fs/bio.c @@ -256,7 +256,7 @@ struct bio *bio_clone(struct bio *bio, g if (bio_integrity(bio)) { int ret; - ret = bio_integrity_clone(b, bio, fs_bio_set); + ret = bio_integrity_clone(b, bio, gfp_mask, fs_bio_set); if (ret < 0) return NULL; --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -501,7 +501,7 @@ extern void bio_integrity_endio(struct b extern void bio_integrity_advance(struct bio *, unsigned int); extern void bio_integrity_trim(struct bio *, unsigned int, unsigned int); extern void bio_integrity_split(struct bio *, struct bio_pair *, int); -extern int bio_integrity_clone(struct bio *, struct bio *, struct bio_set *); +extern int bio_integrity_clone(struct bio *, struct bio *, gfp_t, struct bio_set *); extern int bioset_integrity_create(struct bio_set *, int); extern void bioset_integrity_free(struct bio_set *); extern void bio_integrity_init_slab(void); @@ -512,7 +512,7 @@ extern void bio_integrity_init_slab(void #define bioset_integrity_create(a, b) (0) #define bio_integrity_prep(a) (0) #define bio_integrity_enabled(a) (0) -#define bio_integrity_clone(a, b, c) (0) +#define bio_integrity_clone(a, b, c, d) (0) #define bioset_integrity_free(a) do { } while (0) #define bio_integrity_free(a, b) do { } while (0) #define bio_integrity_endio(a, b) do { } while (0)