Factor out a helper from __blkdev_issue_discard that chews off as much as possible from a discard range and allocates a bio for it. Signed-off-by: Christoph Hellwig <hch@xxxxxx> --- block/blk-lib.c | 58 ++++++++++++++++++++++++++------------------- include/linux/bio.h | 3 +++ 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/block/blk-lib.c b/block/blk-lib.c index 50923508a32466..fd97f4dd34e7f4 100644 --- a/block/blk-lib.c +++ b/block/blk-lib.c @@ -55,36 +55,46 @@ static void await_bio_chain(struct bio *bio) blk_wait_io(&done); } +struct bio *blk_alloc_discard_bio(struct block_device *bdev, + sector_t *sector, sector_t *nr_sects, gfp_t gfp_mask) +{ + sector_t bio_sects = min(*nr_sects, bio_discard_limit(bdev, *sector)); + struct bio *bio; + + if (WARN_ON_ONCE(!(gfp_mask & __GFP_RECLAIM))) + return NULL; + if (!bio_sects) + return NULL; + + bio = bio_alloc(bdev, 0, REQ_OP_DISCARD, gfp_mask); + bio->bi_iter.bi_sector = *sector; + bio->bi_iter.bi_size = bio_sects << SECTOR_SHIFT; + *sector += bio_sects; + *nr_sects -= bio_sects; + /* + * We can loop for a long time in here if someone does full device + * discards (like mkfs). Be nice and allow us to schedule out to avoid + * softlocking if preempt is disabled. + */ + cond_resched(); + return bio; +} + int __blkdev_issue_discard(struct block_device *bdev, sector_t sector, sector_t nr_sects, gfp_t gfp_mask, struct bio **biop) { - struct bio *bio = *biop; - - while (nr_sects) { - sector_t req_sects = - min(nr_sects, bio_discard_limit(bdev, sector)); + struct bio *bio; - bio = blk_next_bio(bio, bdev, 0, REQ_OP_DISCARD, gfp_mask); - bio->bi_iter.bi_sector = sector; - bio->bi_iter.bi_size = req_sects << 9; - sector += req_sects; - nr_sects -= req_sects; - - /* - * We can loop for a long time in here, if someone does - * full device discards (like mkfs). Be nice and allow - * us to schedule out to avoid softlocking if preempt - * is disabled. - */ - cond_resched(); - if (fatal_signal_pending(current)) { - await_bio_chain(bio); - return -EINTR; - } + while (!fatal_signal_pending(current)) { + bio = blk_alloc_discard_bio(bdev, §or, &nr_sects, gfp_mask); + if (!bio) + return 0; + *biop = bio_chain_and_submit(*biop, bio); } - *biop = bio; - return 0; + if (*biop) + await_bio_chain(*biop); + return -EINTR; } EXPORT_SYMBOL(__blkdev_issue_discard); diff --git a/include/linux/bio.h b/include/linux/bio.h index 643d61b7cb82f7..74138c815657fd 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -826,4 +826,7 @@ struct bio *blk_next_bio(struct bio *bio, struct block_device *bdev, unsigned int nr_pages, blk_opf_t opf, gfp_t gfp); struct bio *bio_chain_and_submit(struct bio *prev, struct bio *new); +struct bio *blk_alloc_discard_bio(struct block_device *bdev, + sector_t *sector, sector_t *nr_sects, gfp_t gfp_mask); + #endif /* __LINUX_BIO_H */ -- 2.39.2