This adds optional integrity functions for given bio, they are passsed to bio_integrity_prep and initialized in bio_integrity_payload. The optional integrity generate/verify functions take priority over the ones registered on the block device. It brings flexibility to bio integrity handling. e.g. a network filesystem with integrity support would have integrity generation happen on the clients, and send them over the wire. On the server side once we receive the integrity bits and pass the network layer checksums we would merely pass it on to the block devices have integrity support, so we don't have to calculate the integrity again. Verification shares the same principle: on the server we just copy the integrity bits from the device and send them through the wire, then the verification happens on the clients. Signed-off-by: Li Xi <lixi@xxxxxxx> Signed-off-by: Li Dongyang <dongyangli@xxxxxxx> --- block/bio-integrity.c | 37 ++++++++++++++++++++--------- block/blk-core.c | 2 +- block/blk-mq.c | 2 +- drivers/md/dm-crypt.c | 2 +- drivers/nvdimm/blk.c | 2 +- drivers/nvdimm/btt.c | 2 +- drivers/nvme/host/core.c | 2 +- drivers/target/target_core_iblock.c | 2 +- include/linux/bio.h | 16 +++++++++---- 9 files changed, 45 insertions(+), 22 deletions(-) diff --git a/block/bio-integrity.c b/block/bio-integrity.c index add7c7c85335..1b280784671d 100644 --- a/block/bio-integrity.c +++ b/block/bio-integrity.c @@ -43,6 +43,8 @@ void blk_flush_integrity(void) * @bio: bio to attach integrity metadata to * @gfp_mask: Memory allocation mask * @nr_vecs: Number of integrity metadata scatter-gather elements + * @profile: Optional integrity functions overrides the ones registered + * on the block device * * Description: This function prepares a bio for attaching integrity * metadata. nr_vecs specifies the maximum number of pages containing @@ -50,7 +52,8 @@ void blk_flush_integrity(void) */ struct bio_integrity_payload *bio_integrity_alloc(struct bio *bio, gfp_t gfp_mask, - unsigned int nr_vecs) + unsigned int nr_vecs, + const struct blk_integrity_profile *profile) { struct bio_integrity_payload *bip; struct bio_set *bs = bio->bi_pool; @@ -85,6 +88,7 @@ struct bio_integrity_payload *bio_integrity_alloc(struct bio *bio, } bip->bip_bio = bio; + bip->bip_profile = profile; bio->bi_integrity = bip; bio->bi_opf |= REQ_INTEGRITY; @@ -224,16 +228,20 @@ static blk_status_t bio_integrity_process(struct bio *bio, /** * bio_integrity_prep - Prepare bio for integrity I/O * @bio: bio to prepare + * @profile: Optional integrity functions specific to this bio * * Description: Checks if the bio already has an integrity payload attached. * If it does, the payload has been generated by another kernel subsystem, * and we just pass it through. Otherwise allocates integrity payload. * The bio must have data direction, target device and start sector set priot - * to calling. In the WRITE case, integrity metadata will be generated using - * the block device's integrity function. In the READ case, the buffer - * will be prepared for DMA and a suitable end_io handler set up. + * to calling. The optional integrity functions take priority over the ones + * from the block device if provided. In the WRITE case, integrity metadata + * will be generated using the appropriate integrity function. + * In the READ case, the buffer will be prepared for DMA and a suitable + * end_io handler set up. */ -bool bio_integrity_prep(struct bio *bio) +bool bio_integrity_prep(struct bio *bio, + const struct blk_integrity_profile *profile) { struct bio_integrity_payload *bip; struct blk_integrity *bi = blk_get_integrity(bio->bi_disk); @@ -283,7 +291,7 @@ bool bio_integrity_prep(struct bio *bio) nr_pages = end - start; /* Allocate bio integrity payload and integrity vectors */ - bip = bio_integrity_alloc(bio, GFP_NOIO, nr_pages); + bip = bio_integrity_alloc(bio, GFP_NOIO, nr_pages, profile); if (IS_ERR(bip)) { printk(KERN_ERR "could not allocate data integrity bioset\n"); kfree(buf); @@ -326,8 +334,11 @@ bool bio_integrity_prep(struct bio *bio) /* Auto-generate integrity metadata if this is a write */ if (bio_data_dir(bio) == WRITE) { - bio_integrity_process(bio, &bio->bi_iter, - bi->profile->generate_fn); + integrity_processing_fn *generate_fn = + (bip->bip_profile && bip->bip_profile->generate_fn) ? + bip->bip_profile->generate_fn : + bi->profile->generate_fn; + bio_integrity_process(bio, &bio->bi_iter, generate_fn); } return true; @@ -354,6 +365,10 @@ static void bio_integrity_verify_fn(struct work_struct *work) struct bio *bio = bip->bip_bio; struct blk_integrity *bi = blk_get_integrity(bio->bi_disk); struct bvec_iter iter = bio->bi_iter; + integrity_processing_fn *verify_fn = + (bip->bip_profile && bip->bip_profile->verify_fn) ? + bip->bip_profile->verify_fn : + bi->profile->verify_fn; /* * At the moment verify is called bio's iterator was advanced @@ -361,8 +376,7 @@ static void bio_integrity_verify_fn(struct work_struct *work) * it's original position. */ if (bio_rewind_iter(bio, &iter, iter.bi_done)) { - bio->bi_status = bio_integrity_process(bio, &iter, - bi->profile->verify_fn); + bio->bi_status = bio_integrity_process(bio, &iter, verify_fn); } else { bio->bi_status = BLK_STS_IOERR; } @@ -449,7 +463,8 @@ int bio_integrity_clone(struct bio *bio, struct bio *bio_src, BUG_ON(bip_src == NULL); - bip = bio_integrity_alloc(bio, gfp_mask, bip_src->bip_vcnt); + bip = bio_integrity_alloc(bio, gfp_mask, bip_src->bip_vcnt, + bip_src->bip_profile); if (IS_ERR(bip)) return PTR_ERR(bip); diff --git a/block/blk-core.c b/block/blk-core.c index f84a9b7b6f5a..0bae55729754 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1993,7 +1993,7 @@ static blk_qc_t blk_queue_bio(struct request_queue *q, struct bio *bio) blk_queue_split(q, &bio); - if (!bio_integrity_prep(bio)) + if (!bio_integrity_prep(bio, NULL)) return BLK_QC_T_NONE; if (op_is_flush(bio->bi_opf)) { diff --git a/block/blk-mq.c b/block/blk-mq.c index 654b0dc7e001..c3a829c34ea8 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -1762,7 +1762,7 @@ static blk_qc_t blk_mq_make_request(struct request_queue *q, struct bio *bio) blk_queue_split(q, &bio); - if (!bio_integrity_prep(bio)) + if (!bio_integrity_prep(bio, NULL)) return BLK_QC_T_NONE; if (!is_flush_fua && !blk_queue_nomerges(q) && diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index b61b069c33af..f15e43f18cf0 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -931,7 +931,7 @@ static int dm_crypt_integrity_io_alloc(struct dm_crypt_io *io, struct bio *bio) if (!bio_sectors(bio) || !io->cc->on_disk_tag_size) return 0; - bip = bio_integrity_alloc(bio, GFP_NOIO, 1); + bip = bio_integrity_alloc(bio, GFP_NOIO, 1, NULL); if (IS_ERR(bip)) return PTR_ERR(bip); diff --git a/drivers/nvdimm/blk.c b/drivers/nvdimm/blk.c index 62e9cb167aad..c9069add0544 100644 --- a/drivers/nvdimm/blk.c +++ b/drivers/nvdimm/blk.c @@ -180,7 +180,7 @@ static blk_qc_t nd_blk_make_request(struct request_queue *q, struct bio *bio) int err = 0, rw; bool do_acct; - if (!bio_integrity_prep(bio)) + if (!bio_integrity_prep(bio, NULL)) return BLK_QC_T_NONE; bip = bio_integrity(bio); diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c index 85de8053aa34..0158e85f22fe 100644 --- a/drivers/nvdimm/btt.c +++ b/drivers/nvdimm/btt.c @@ -1448,7 +1448,7 @@ static blk_qc_t btt_make_request(struct request_queue *q, struct bio *bio) int err = 0; bool do_acct; - if (!bio_integrity_prep(bio)) + if (!bio_integrity_prep(bio, NULL)) return BLK_QC_T_NONE; do_acct = nd_iostat_start(bio, &start); diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index bf65501e6ed6..58285f8cb566 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -736,7 +736,7 @@ static void *nvme_add_user_metadata(struct bio *bio, void __user *ubuf, if (write && copy_from_user(buf, ubuf, len)) goto out_free_meta; - bip = bio_integrity_alloc(bio, GFP_KERNEL, 1); + bip = bio_integrity_alloc(bio, GFP_KERNEL, 1, NULL); if (IS_ERR(bip)) { ret = PTR_ERR(bip); goto out_free_meta; diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index ce1321a5cb7b..d424d00a4398 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -650,7 +650,7 @@ iblock_alloc_bip(struct se_cmd *cmd, struct bio *bio) return -ENODEV; } - bip = bio_integrity_alloc(bio, GFP_NOIO, cmd->t_prot_nents); + bip = bio_integrity_alloc(bio, GFP_NOIO, cmd->t_prot_nents, NULL); if (IS_ERR(bip)) { pr_err("Unable to allocate bio_integrity_payload\n"); return PTR_ERR(bip); diff --git a/include/linux/bio.h b/include/linux/bio.h index f08f5fe7bd08..e4449c4f2576 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -355,6 +355,9 @@ struct bio_integrity_payload { struct work_struct bip_work; /* I/O completion */ + /* integrity profile specific to the parent bio */ + const struct blk_integrity_profile *bip_profile; + struct bio_vec *bip_vec; struct bio_vec bip_inline_vecs[0];/* embedded bvec array */ }; @@ -788,9 +791,12 @@ static inline bool bioset_initialized(struct bio_set *bs) for_each_bio(_bio) \ bip_for_each_vec(_bvl, _bio->bi_integrity, _iter) -extern struct bio_integrity_payload *bio_integrity_alloc(struct bio *, gfp_t, unsigned int); +extern struct bio_integrity_payload *bio_integrity_alloc(struct bio *, gfp_t, + unsigned int, + const struct blk_integrity_profile *); extern int bio_integrity_add_page(struct bio *, struct page *, unsigned int, unsigned int); -extern bool bio_integrity_prep(struct bio *); +extern bool bio_integrity_prep(struct bio *, + const struct blk_integrity_profile *); extern void bio_integrity_advance(struct bio *, unsigned int); extern void bio_integrity_trim(struct bio *); extern int bio_integrity_clone(struct bio *, struct bio *, gfp_t); @@ -815,7 +821,8 @@ static inline void bioset_integrity_free (struct bio_set *bs) return; } -static inline bool bio_integrity_prep(struct bio *bio) +static inline bool bio_integrity_prep(struct bio *bio, + const struct blk_integrity_profile *profile) { return true; } @@ -848,7 +855,8 @@ static inline bool bio_integrity_flagged(struct bio *bio, enum bip_flags flag) } static inline void *bio_integrity_alloc(struct bio * bio, gfp_t gfp, - unsigned int nr) + unsigned int nr, + const struct blk_integrity_profile *profile) { return ERR_PTR(-EINVAL); } -- 2.18.0