Currently bio_integrity_copy_user() uses bip_vec array to store two different things: (a) kernel allocated bounce buffer (b) bunch of extra bvecs to pinned user memory. This leads to unwieldy handling at places. The patch cleans this up and uses bip_vec for just kernel allocated meta buffer. The bio_vecs (to pinned user memory) and their count are now stored seprately into bip. Existing memory (of the field bip_work) is reused so that bip does not grow unconditionally. Based on an earlier patch from Keith Busch. Signed-off-by: Kanchan Joshi <joshi.k@xxxxxxxxxxx> --- block/bio-integrity.c | 28 ++++++++++++++++++---------- include/linux/bio.h | 12 +++++++++++- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/block/bio-integrity.c b/block/bio-integrity.c index 2e3e8e04961e..47634b89d27c 100644 --- a/block/bio-integrity.c +++ b/block/bio-integrity.c @@ -105,8 +105,8 @@ static void bio_integrity_unpin_bvec(struct bio_vec *bv, int nr_vecs, static void bio_integrity_uncopy_user(struct bio_integrity_payload *bip) { - unsigned short nr_vecs = bip->bip_max_vcnt - 1; - struct bio_vec *copy = &bip->bip_vec[1]; + unsigned short nr_vecs = bip->copy_vcnt; + struct bio_vec *copy = bip->copy_vec; size_t bytes = bip->bip_iter.bi_size; struct iov_iter iter; int ret; @@ -116,6 +116,7 @@ static void bio_integrity_uncopy_user(struct bio_integrity_payload *bip) WARN_ON_ONCE(ret != bytes); bio_integrity_unpin_bvec(copy, nr_vecs, true); + kfree(copy); } static void bio_integrity_unmap_user(struct bio_integrity_payload *bip) @@ -208,6 +209,7 @@ static int bio_integrity_copy_user(struct bio *bio, struct bio_vec *bvec, unsigned int direction, u32 seed) { bool write = direction == ITER_SOURCE; + struct bio_vec *copy_vec = NULL; struct bio_integrity_payload *bip; struct iov_iter iter; void *buf; @@ -224,7 +226,7 @@ static int bio_integrity_copy_user(struct bio *bio, struct bio_vec *bvec, goto free_buf; } - bip = bio_integrity_alloc(bio, GFP_KERNEL, 1); + bio_integrity_unpin_bvec(bvec, nr_vecs, false); } else { memset(buf, 0, len); @@ -232,19 +234,21 @@ static int bio_integrity_copy_user(struct bio *bio, struct bio_vec *bvec, * We need to preserve the original bvec and the number of vecs * in it for completion handling */ - bip = bio_integrity_alloc(bio, GFP_KERNEL, nr_vecs + 1); + copy_vec = kcalloc(nr_vecs, sizeof(*bvec), GFP_KERNEL); + if (!copy_vec) { + ret = -ENOMEM; + goto free_buf; + } + memcpy(copy_vec, bvec, nr_vecs * sizeof(*bvec)); + } + bip = bio_integrity_alloc(bio, GFP_KERNEL, 1); if (IS_ERR(bip)) { ret = PTR_ERR(bip); - goto free_buf; + goto free_copy; } - if (write) - bio_integrity_unpin_bvec(bvec, nr_vecs, false); - else - memcpy(&bip->bip_vec[1], bvec, nr_vecs * sizeof(*bvec)); - ret = bio_integrity_add_page(bio, virt_to_page(buf), len, offset_in_page(buf)); if (ret != len) { @@ -254,9 +258,13 @@ static int bio_integrity_copy_user(struct bio *bio, struct bio_vec *bvec, bip->bip_flags |= BIP_INTEGRITY_USER | BIP_COPY_USER; bip->bip_iter.bi_sector = seed; + bip->copy_vec = copy_vec; + bip->copy_vcnt = nr_vecs; return 0; free_bip: bio_integrity_free(bio); +free_copy: + kfree(copy_vec); free_buf: kfree(buf); return ret; diff --git a/include/linux/bio.h b/include/linux/bio.h index 9b8a369f44bc..ffbfa43d51af 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -345,7 +345,17 @@ struct bio_integrity_payload { struct bvec_iter bio_iter; /* for rewinding parent bio */ - struct work_struct bip_work; /* I/O completion */ + union { + /* + * bip_work is used only for block-layer sent integrity. + * For user sent integrity, reuse the same memory. + */ + struct work_struct bip_work; /* I/O completion */ + struct { + struct bio_vec *copy_vec; /* pinned user memory */ + unsigned short copy_vcnt; /* # of bio_vecs for above */ + }; + }; struct bio_vec *bip_vec; struct bio_vec bip_inline_vecs[];/* embedded bvec array */ -- 2.25.1