block: stop calling blk_queue_bounce for passthrough requests

Instead of overloading the passthrough fast path with the deprecated
block layer bounce buffering let the users that combine an old
undermaintained driver with a highmem system pay the price by always
falling back to copies in that case.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Acked-by: Martin K. Petersen <martin.petersen@oracle.com>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Link: https://lore.kernel.org/r/20210331073001.46776-9-hch@lst.de
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Christoph Hellwig 2021-03-31 09:30:01 +02:00 committed by Jens Axboe
parent 9bb33f24ab
commit 393bb12e00
5 changed files with 36 additions and 99 deletions

View File

@ -123,7 +123,6 @@ static int bio_uncopy_user(struct bio *bio)
bio_free_pages(bio); bio_free_pages(bio);
} }
kfree(bmd); kfree(bmd);
bio_put(bio);
return ret; return ret;
} }
@ -132,7 +131,7 @@ static int bio_copy_user_iov(struct request *rq, struct rq_map_data *map_data,
{ {
struct bio_map_data *bmd; struct bio_map_data *bmd;
struct page *page; struct page *page;
struct bio *bio, *bounce_bio; struct bio *bio;
int i = 0, ret; int i = 0, ret;
int nr_pages; int nr_pages;
unsigned int len = iter->count; unsigned int len = iter->count;
@ -218,16 +217,9 @@ static int bio_copy_user_iov(struct request *rq, struct rq_map_data *map_data,
bio->bi_private = bmd; bio->bi_private = bmd;
bounce_bio = bio; ret = blk_rq_append_bio(rq, bio);
ret = blk_rq_append_bio(rq, &bounce_bio);
if (ret) if (ret)
goto cleanup; goto cleanup;
/*
* We link the bounce buffer in and could have to traverse it later, so
* we have to get a ref to prevent it from being freed
*/
bio_get(bounce_bio);
return 0; return 0;
cleanup: cleanup:
if (!map_data) if (!map_data)
@ -242,7 +234,7 @@ static int bio_map_user_iov(struct request *rq, struct iov_iter *iter,
gfp_t gfp_mask) gfp_t gfp_mask)
{ {
unsigned int max_sectors = queue_max_hw_sectors(rq->q); unsigned int max_sectors = queue_max_hw_sectors(rq->q);
struct bio *bio, *bounce_bio; struct bio *bio;
int ret; int ret;
int j; int j;
@ -304,49 +296,17 @@ static int bio_map_user_iov(struct request *rq, struct iov_iter *iter,
break; break;
} }
/* ret = blk_rq_append_bio(rq, bio);
* Subtle: if we end up needing to bounce a bio, it would normally
* disappear when its bi_end_io is run. However, we need the original
* bio for the unmap, so grab an extra reference to it
*/
bio_get(bio);
bounce_bio = bio;
ret = blk_rq_append_bio(rq, &bounce_bio);
if (ret) if (ret)
goto out_put_orig; goto out_unmap;
/*
* We link the bounce buffer in and could have to traverse it
* later, so we have to get a ref to prevent it from being freed
*/
bio_get(bounce_bio);
return 0; return 0;
out_put_orig:
bio_put(bio);
out_unmap: out_unmap:
bio_release_pages(bio, false); bio_release_pages(bio, false);
bio_put(bio); bio_put(bio);
return ret; return ret;
} }
/**
* bio_unmap_user - unmap a bio
* @bio: the bio being unmapped
*
* Unmap a bio previously mapped by bio_map_user_iov(). Must be called from
* process context.
*
* bio_unmap_user() may sleep.
*/
static void bio_unmap_user(struct bio *bio)
{
bio_release_pages(bio, bio_data_dir(bio) == READ);
bio_put(bio);
bio_put(bio);
}
static void bio_invalidate_vmalloc_pages(struct bio *bio) static void bio_invalidate_vmalloc_pages(struct bio *bio)
{ {
#ifdef ARCH_HAS_FLUSH_KERNEL_DCACHE_PAGE #ifdef ARCH_HAS_FLUSH_KERNEL_DCACHE_PAGE
@ -519,33 +479,27 @@ cleanup:
* Append a bio to a passthrough request. Only works if the bio can be merged * Append a bio to a passthrough request. Only works if the bio can be merged
* into the request based on the driver constraints. * into the request based on the driver constraints.
*/ */
int blk_rq_append_bio(struct request *rq, struct bio **bio) int blk_rq_append_bio(struct request *rq, struct bio *bio)
{ {
struct bio *orig_bio = *bio;
struct bvec_iter iter; struct bvec_iter iter;
struct bio_vec bv; struct bio_vec bv;
unsigned int nr_segs = 0; unsigned int nr_segs = 0;
blk_queue_bounce(rq->q, bio); if (WARN_ON_ONCE(rq->q->limits.bounce != BLK_BOUNCE_NONE))
return -EINVAL;
bio_for_each_bvec(bv, *bio, iter) bio_for_each_bvec(bv, bio, iter)
nr_segs++; nr_segs++;
if (!rq->bio) { if (!rq->bio) {
blk_rq_bio_prep(rq, *bio, nr_segs); blk_rq_bio_prep(rq, bio, nr_segs);
} else { } else {
if (!ll_back_merge_fn(rq, *bio, nr_segs)) { if (!ll_back_merge_fn(rq, bio, nr_segs))
if (orig_bio != *bio) {
bio_put(*bio);
*bio = orig_bio;
}
return -EINVAL; return -EINVAL;
} rq->biotail->bi_next = bio;
rq->biotail = bio;
rq->biotail->bi_next = *bio; rq->__data_len += (bio)->bi_iter.bi_size;
rq->biotail = *bio; bio_crypt_free_ctx(bio);
rq->__data_len += (*bio)->bi_iter.bi_size;
bio_crypt_free_ctx(*bio);
} }
return 0; return 0;
@ -566,12 +520,6 @@ EXPORT_SYMBOL(blk_rq_append_bio);
* *
* A matching blk_rq_unmap_user() must be issued at the end of I/O, while * A matching blk_rq_unmap_user() must be issued at the end of I/O, while
* still in process context. * still in process context.
*
* Note: The mapped bio may need to be bounced through blk_queue_bounce()
* before being submitted to the device, as pages mapped may be out of
* reach. It's the callers responsibility to make sure this happens. The
* original bio must be passed back in to blk_rq_unmap_user() for proper
* unmapping.
*/ */
int blk_rq_map_user_iov(struct request_queue *q, struct request *rq, int blk_rq_map_user_iov(struct request_queue *q, struct request *rq,
struct rq_map_data *map_data, struct rq_map_data *map_data,
@ -588,6 +536,8 @@ int blk_rq_map_user_iov(struct request_queue *q, struct request *rq,
if (map_data) if (map_data)
copy = true; copy = true;
else if (blk_queue_may_bounce(q))
copy = true;
else if (iov_iter_alignment(iter) & align) else if (iov_iter_alignment(iter) & align)
copy = true; copy = true;
else if (queue_virt_boundary(q)) else if (queue_virt_boundary(q))
@ -641,25 +591,21 @@ EXPORT_SYMBOL(blk_rq_map_user);
*/ */
int blk_rq_unmap_user(struct bio *bio) int blk_rq_unmap_user(struct bio *bio)
{ {
struct bio *mapped_bio; struct bio *next_bio;
int ret = 0, ret2; int ret = 0, ret2;
while (bio) { while (bio) {
mapped_bio = bio;
if (unlikely(bio_flagged(bio, BIO_BOUNCED)))
mapped_bio = bio->bi_private;
if (bio->bi_private) { if (bio->bi_private) {
ret2 = bio_uncopy_user(mapped_bio); ret2 = bio_uncopy_user(bio);
if (ret2 && !ret) if (ret2 && !ret)
ret = ret2; ret = ret2;
} else { } else {
bio_unmap_user(mapped_bio); bio_release_pages(bio, bio_data_dir(bio) == READ);
} }
mapped_bio = bio; next_bio = bio;
bio = bio->bi_next; bio = bio->bi_next;
bio_put(mapped_bio); bio_put(next_bio);
} }
return ret; return ret;
@ -684,7 +630,7 @@ int blk_rq_map_kern(struct request_queue *q, struct request *rq, void *kbuf,
{ {
int reading = rq_data_dir(rq) == READ; int reading = rq_data_dir(rq) == READ;
unsigned long addr = (unsigned long) kbuf; unsigned long addr = (unsigned long) kbuf;
struct bio *bio, *orig_bio; struct bio *bio;
int ret; int ret;
if (len > (queue_max_hw_sectors(q) << 9)) if (len > (queue_max_hw_sectors(q) << 9))
@ -692,7 +638,8 @@ int blk_rq_map_kern(struct request_queue *q, struct request *rq, void *kbuf,
if (!len || !kbuf) if (!len || !kbuf)
return -EINVAL; return -EINVAL;
if (!blk_rq_aligned(q, addr, len) || object_is_on_stack(kbuf)) if (!blk_rq_aligned(q, addr, len) || object_is_on_stack(kbuf) ||
blk_queue_may_bounce(q))
bio = bio_copy_kern(q, kbuf, len, gfp_mask, reading); bio = bio_copy_kern(q, kbuf, len, gfp_mask, reading);
else else
bio = bio_map_kern(q, kbuf, len, gfp_mask); bio = bio_map_kern(q, kbuf, len, gfp_mask);
@ -703,14 +650,9 @@ int blk_rq_map_kern(struct request_queue *q, struct request *rq, void *kbuf,
bio->bi_opf &= ~REQ_OP_MASK; bio->bi_opf &= ~REQ_OP_MASK;
bio->bi_opf |= req_op(rq); bio->bi_opf |= req_op(rq);
orig_bio = bio; ret = blk_rq_append_bio(rq, bio);
ret = blk_rq_append_bio(rq, &bio); if (unlikely(ret))
if (unlikely(ret)) { bio_put(bio);
/* request is too big */ return ret;
bio_put(orig_bio);
return ret;
}
return 0;
} }
EXPORT_SYMBOL(blk_rq_map_kern); EXPORT_SYMBOL(blk_rq_map_kern);

View File

@ -180,12 +180,8 @@ static struct bio *bounce_clone_bio(struct bio *bio_src)
* asking for trouble and would force extra work on * asking for trouble and would force extra work on
* __bio_clone_fast() anyways. * __bio_clone_fast() anyways.
*/ */
if (bio_is_passthrough(bio_src)) bio = bio_alloc_bioset(GFP_NOIO, bio_segments(bio_src),
bio = bio_kmalloc(GFP_NOIO | __GFP_NOFAIL, &bounce_bio_set);
bio_segments(bio_src));
else
bio = bio_alloc_bioset(GFP_NOIO, bio_segments(bio_src),
&bounce_bio_set);
bio->bi_bdev = bio_src->bi_bdev; bio->bi_bdev = bio_src->bi_bdev;
if (bio_flagged(bio_src, BIO_REMAPPED)) if (bio_flagged(bio_src, BIO_REMAPPED))
bio_set_flag(bio, BIO_REMAPPED); bio_set_flag(bio, BIO_REMAPPED);
@ -245,8 +241,7 @@ void __blk_queue_bounce(struct request_queue *q, struct bio **bio_orig)
if (!bounce) if (!bounce)
return; return;
if (!bio_is_passthrough(*bio_orig) && if (sectors < bio_sectors(*bio_orig)) {
sectors < bio_sectors(*bio_orig)) {
bio = bio_split(*bio_orig, sectors, GFP_NOIO, &bounce_bio_split); bio = bio_split(*bio_orig, sectors, GFP_NOIO, &bounce_bio_split);
bio_chain(bio, *bio_orig); bio_chain(bio, *bio_orig);
submit_bio_noacct(*bio_orig); submit_bio_noacct(*bio_orig);

View File

@ -660,7 +660,7 @@ static struct request *nvme_nvm_alloc_request(struct request_queue *q,
rq->cmd_flags &= ~REQ_FAILFAST_DRIVER; rq->cmd_flags &= ~REQ_FAILFAST_DRIVER;
if (rqd->bio) if (rqd->bio)
blk_rq_append_bio(rq, &rqd->bio); blk_rq_append_bio(rq, rqd->bio);
else else
rq->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, IOPRIO_NORM); rq->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, IOPRIO_NORM);

View File

@ -911,7 +911,7 @@ new_bio:
" %d i: %d bio: %p, allocating another" " %d i: %d bio: %p, allocating another"
" bio\n", bio->bi_vcnt, i, bio); " bio\n", bio->bi_vcnt, i, bio);
rc = blk_rq_append_bio(req, &bio); rc = blk_rq_append_bio(req, bio);
if (rc) { if (rc) {
pr_err("pSCSI: failed to append bio\n"); pr_err("pSCSI: failed to append bio\n");
goto fail; goto fail;
@ -930,7 +930,7 @@ new_bio:
} }
if (bio) { if (bio) {
rc = blk_rq_append_bio(req, &bio); rc = blk_rq_append_bio(req, bio);
if (rc) { if (rc) {
pr_err("pSCSI: failed to append bio\n"); pr_err("pSCSI: failed to append bio\n");
goto fail; goto fail;

View File

@ -909,7 +909,7 @@ extern int blk_rq_prep_clone(struct request *rq, struct request *rq_src,
extern void blk_rq_unprep_clone(struct request *rq); extern void blk_rq_unprep_clone(struct request *rq);
extern blk_status_t blk_insert_cloned_request(struct request_queue *q, extern blk_status_t blk_insert_cloned_request(struct request_queue *q,
struct request *rq); struct request *rq);
extern int blk_rq_append_bio(struct request *rq, struct bio **bio); int blk_rq_append_bio(struct request *rq, struct bio *bio);
extern void blk_queue_split(struct bio **); extern void blk_queue_split(struct bio **);
extern int scsi_verify_blk_ioctl(struct block_device *, unsigned int); extern int scsi_verify_blk_ioctl(struct block_device *, unsigned int);
extern int scsi_cmd_blk_ioctl(struct block_device *, fmode_t, extern int scsi_cmd_blk_ioctl(struct block_device *, fmode_t,