diff --git a/block/bio-integrity.c b/block/bio-integrity.c index e79eaf047794..2b5fb35ca777 100644 --- a/block/bio-integrity.c +++ b/block/bio-integrity.c @@ -403,6 +403,23 @@ int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter) if (unlikely(ret < 0)) goto free_bvec; + /* Handle partial pinning. This can happen when pin_user_pages_fast() + * returns fewer pages than requested + */ + if (user_backed_iter(iter) && unlikely(ret != bytes)) { + if (0 < ret) { + int npinned = DIV_ROUND_UP(offset + ret, PAGE_SIZE); + int i; + + for (i = 0; i < npinned; i++) + unpin_user_page(pages[i]); + } + if (pages != stack_pages) + kvfree(pages); + ret = -EFAULT; + goto free_bvec; + } + nr_bvecs = bvec_from_pages(bvec, pages, nr_vecs, bytes, offset, &is_p2p); if (pages != stack_pages)