diff --git a/block/bio.c b/block/bio.c index d80d5d26804e3..ba4691905d34c 100644 --- a/block/bio.c +++ b/block/bio.c @@ -1481,11 +1481,41 @@ void bio_iov_iter_unbounce(struct bio *bio, bool is_error, bool mark_dirty) bio_iov_iter_unbounce_read(bio, is_error, mark_dirty); } -static void submit_bio_wait_endio(struct bio *bio) +static void bio_wait_end_io(struct bio *bio) { complete(bio->bi_private); } +/** + * bio_await - call a function on a bio, and wait until it completes + * @bio: the bio which describes the I/O + * @submit: function called to submit the bio + * @priv: private data passed to @submit + * + * Wait for the bio as well as any bio chained off it after executing the + * passed in callback @submit. The wait for the bio is set up before calling + * @submit to ensure that the completion is captured. If @submit is %NULL, + * submit_bio() is used instead to submit the bio. + * + * Note: this overrides the bi_private and bi_end_io fields in the bio. + */ +void bio_await(struct bio *bio, void *priv, + void (*submit)(struct bio *bio, void *priv)) +{ + DECLARE_COMPLETION_ONSTACK_MAP(done, + bio->bi_bdev->bd_disk->lockdep_map); + + bio->bi_private = &done; + bio->bi_end_io = bio_wait_end_io; + bio->bi_opf |= REQ_SYNC; + if (submit) + submit(bio, priv); + else + submit_bio(bio); + blk_wait_io(&done); +} +EXPORT_SYMBOL_GPL(bio_await); + /** * submit_bio_wait - submit a bio, and wait until it completes * @bio: The &struct bio which describes the I/O @@ -1499,19 +1529,30 @@ static void submit_bio_wait_endio(struct bio *bio) */ int submit_bio_wait(struct bio *bio) { - DECLARE_COMPLETION_ONSTACK_MAP(done, - bio->bi_bdev->bd_disk->lockdep_map); - - bio->bi_private = &done; - bio->bi_end_io = submit_bio_wait_endio; - bio->bi_opf |= REQ_SYNC; - submit_bio(bio); - blk_wait_io(&done); - + bio_await(bio, NULL, NULL); return blk_status_to_errno(bio->bi_status); } EXPORT_SYMBOL(submit_bio_wait); +static void bio_endio_cb(struct bio *bio, void *priv) +{ + bio_endio(bio); +} + +/* + * Submit @bio synchronously, or call bio_endio on it if the current process + * is being killed. + */ +int bio_submit_or_kill(struct bio *bio, unsigned int flags) +{ + if ((flags & BLKDEV_ZERO_KILLABLE) && fatal_signal_pending(current)) { + bio_await(bio, NULL, bio_endio_cb); + return -EINTR; + } + + return submit_bio_wait(bio); +} + /** * bdev_rw_virt - synchronously read into / write from kernel mapping * @bdev: block device to access @@ -1542,26 +1583,6 @@ int bdev_rw_virt(struct block_device *bdev, sector_t sector, void *data, } EXPORT_SYMBOL_GPL(bdev_rw_virt); -static void bio_wait_end_io(struct bio *bio) -{ - complete(bio->bi_private); - bio_put(bio); -} - -/* - * bio_await_chain - ends @bio and waits for every chained bio to complete - */ -void bio_await_chain(struct bio *bio) -{ - DECLARE_COMPLETION_ONSTACK_MAP(done, - bio->bi_bdev->bd_disk->lockdep_map); - - bio->bi_private = &done; - bio->bi_end_io = bio_wait_end_io; - bio_endio(bio); - blk_wait_io(&done); -} - void __bio_advance(struct bio *bio, unsigned bytes) { if (bio_integrity(bio)) diff --git a/block/blk-lib.c b/block/blk-lib.c index 3213afc7f0d5d..688bc67cbf733 100644 --- a/block/blk-lib.c +++ b/block/blk-lib.c @@ -155,13 +155,7 @@ static int blkdev_issue_write_zeroes(struct block_device *bdev, sector_t sector, __blkdev_issue_write_zeroes(bdev, sector, nr_sects, gfp, &bio, flags, limit); if (bio) { - if ((flags & BLKDEV_ZERO_KILLABLE) && - fatal_signal_pending(current)) { - bio_await_chain(bio); - blk_finish_plug(&plug); - return -EINTR; - } - ret = submit_bio_wait(bio); + ret = bio_submit_or_kill(bio, flags); bio_put(bio); } blk_finish_plug(&plug); @@ -236,13 +230,7 @@ static int blkdev_issue_zero_pages(struct block_device *bdev, sector_t sector, blk_start_plug(&plug); __blkdev_issue_zero_pages(bdev, sector, nr_sects, gfp, &bio, flags); if (bio) { - if ((flags & BLKDEV_ZERO_KILLABLE) && - fatal_signal_pending(current)) { - bio_await_chain(bio); - blk_finish_plug(&plug); - return -EINTR; - } - ret = submit_bio_wait(bio); + ret = bio_submit_or_kill(bio, flags); bio_put(bio); } blk_finish_plug(&plug); diff --git a/block/blk.h b/block/blk.h index f6053e9dd2aac..c69beafa17861 100644 --- a/block/blk.h +++ b/block/blk.h @@ -55,7 +55,7 @@ bool __blk_freeze_queue_start(struct request_queue *q, struct task_struct *owner); int __bio_queue_enter(struct request_queue *q, struct bio *bio); void submit_bio_noacct_nocheck(struct bio *bio, bool split); -void bio_await_chain(struct bio *bio); +int bio_submit_or_kill(struct bio *bio, unsigned int flags); static inline bool blk_try_enter_queue(struct request_queue *q, bool pm) { diff --git a/block/ioctl.c b/block/ioctl.c index 0b04661ac8098..fc3be0549aa75 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -153,13 +153,7 @@ static int blk_ioctl_discard(struct block_device *bdev, blk_mode_t mode, nr_sects = len >> SECTOR_SHIFT; blk_start_plug(&plug); - while (1) { - if (fatal_signal_pending(current)) { - if (prev) - bio_await_chain(prev); - err = -EINTR; - goto out_unplug; - } + while (!fatal_signal_pending(current)) { bio = blk_alloc_discard_bio(bdev, §or, &nr_sects, GFP_KERNEL); if (!bio) @@ -167,12 +161,11 @@ static int blk_ioctl_discard(struct block_device *bdev, blk_mode_t mode, prev = bio_chain_and_submit(prev, bio); } if (prev) { - err = submit_bio_wait(prev); + err = bio_submit_or_kill(prev, BLKDEV_ZERO_KILLABLE); if (err == -EOPNOTSUPP) err = 0; bio_put(prev); } -out_unplug: blk_finish_plug(&plug); fail: filemap_invalidate_unlock(bdev->bd_mapping); diff --git a/fs/xfs/xfs_zone_gc.c b/fs/xfs/xfs_zone_gc.c index 309f700985243..b2626a482563d 100644 --- a/fs/xfs/xfs_zone_gc.c +++ b/fs/xfs/xfs_zone_gc.c @@ -670,7 +670,6 @@ xfs_zone_gc_start_chunk( struct xfs_inode *ip; struct bio *bio; xfs_daddr_t daddr; - unsigned int len; bool is_seq; if (xfs_is_shutdown(mp)) @@ -685,15 +684,16 @@ xfs_zone_gc_start_chunk( return false; } - len = XFS_FSB_TO_B(mp, irec.rm_blockcount); - bio = bio_alloc_bioset(bdev, - min(howmany(len, XFS_GC_BUF_SIZE) + 1, XFS_GC_NR_BUFS), - REQ_OP_READ, GFP_NOFS, &data->bio_set); - + /* + * Scratch allocation can wrap around to the same buffer again, + * provision an extra bvec for that case. + */ + bio = bio_alloc_bioset(bdev, XFS_GC_NR_BUFS + 1, REQ_OP_READ, GFP_NOFS, + &data->bio_set); chunk = container_of(bio, struct xfs_gc_bio, bio); chunk->ip = ip; chunk->offset = XFS_FSB_TO_B(mp, irec.rm_offset); - chunk->len = len; + chunk->len = XFS_FSB_TO_B(mp, irec.rm_blockcount); chunk->old_startblock = xfs_rgbno_to_rtb(iter->victim_rtg, irec.rm_startblock); chunk->new_daddr = daddr; @@ -707,8 +707,9 @@ xfs_zone_gc_start_chunk( bio->bi_iter.bi_sector = xfs_rtb_to_daddr(mp, chunk->old_startblock); bio->bi_end_io = xfs_zone_gc_end_io; xfs_zone_gc_add_data(chunk); - data->scratch_head = (data->scratch_head + len) % data->scratch_size; - data->scratch_available -= len; + data->scratch_head = + (data->scratch_head + chunk->len) % data->scratch_size; + data->scratch_available -= chunk->len; XFS_STATS_INC(mp, xs_gc_read_calls); @@ -899,9 +900,10 @@ xfs_zone_gc_finish_reset( static void xfs_submit_zone_reset_bio( - struct xfs_rtgroup *rtg, - struct bio *bio) + struct bio *bio, + void *priv) { + struct xfs_rtgroup *rtg = priv; struct xfs_mount *mp = rtg_mount(rtg); trace_xfs_zone_reset(rtg); @@ -933,26 +935,16 @@ xfs_submit_zone_reset_bio( submit_bio(bio); } -static void xfs_bio_wait_endio(struct bio *bio) -{ - complete(bio->bi_private); -} - int xfs_zone_gc_reset_sync( struct xfs_rtgroup *rtg) { - DECLARE_COMPLETION_ONSTACK(done); struct bio bio; int error; bio_init(&bio, rtg_mount(rtg)->m_rtdev_targp->bt_bdev, NULL, 0, REQ_OP_ZONE_RESET | REQ_SYNC); - bio.bi_private = &done; - bio.bi_end_io = xfs_bio_wait_endio; - xfs_submit_zone_reset_bio(rtg, &bio); - wait_for_completion_io(&done); - + bio_await(&bio, rtg, xfs_submit_zone_reset_bio); error = blk_status_to_errno(bio.bi_status); bio_uninit(&bio); return error; @@ -989,7 +981,7 @@ xfs_zone_gc_reset_zones( chunk->data = data; WRITE_ONCE(chunk->state, XFS_GC_BIO_NEW); list_add_tail(&chunk->entry, &data->resetting); - xfs_submit_zone_reset_bio(rtg, bio); + xfs_submit_zone_reset_bio(bio, rtg); } while (next); } diff --git a/include/linux/bio.h b/include/linux/bio.h index 36a3f2275ecd0..3059dcaf6d5ec 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -433,6 +433,8 @@ extern void bio_uninit(struct bio *); void bio_reset(struct bio *bio, struct block_device *bdev, blk_opf_t opf); void bio_reuse(struct bio *bio, blk_opf_t opf); void bio_chain(struct bio *, struct bio *); +void bio_await(struct bio *bio, void *priv, + void (*submit)(struct bio *bio, void *priv)); int __must_check bio_add_page(struct bio *bio, struct page *page, unsigned len, unsigned off);