Split out bio_should_redirty_pages and bio_queue_for_redirty helpers and let the callers use their normal bio release path for the non-redirty case. Note that this changes the refcounting for the redirty case as bio_queue_for_redirty takes a reference now, which allows the caller to unconditionally drop its reference instead of special casing this path. Signed-off-by: Christoph Hellwig <hch@xxxxxx> --- block/bio.c | 54 ++++++++++++++++++++++++++------------------ block/fops.c | 18 +++++++-------- fs/direct-io.c | 11 ++++----- fs/iomap/direct-io.c | 9 ++++---- include/linux/bio.h | 3 ++- 5 files changed, 51 insertions(+), 44 deletions(-) diff --git a/block/bio.c b/block/bio.c index d3154d8beed72..692e53561e4e0 100644 --- a/block/bio.c +++ b/block/bio.c @@ -1399,7 +1399,7 @@ void bio_free_pages(struct bio *bio) EXPORT_SYMBOL(bio_free_pages); /* - * bio_set_pages_dirty() and bio_check_pages_dirty() are support functions + * bio_set_pages_dirty() and bio_queue_for_redirty() are support functions * for performing direct-IO in BIOs. * * The problem is that we cannot run set_page_dirty() from interrupt context @@ -1438,17 +1438,6 @@ void bio_set_pages_dirty(struct bio *bio) } } -/* - * bio_check_pages_dirty() will check that all the BIO's pages are still dirty. - * If they are, then fine. If, however, some pages are clean then they must - * have been written out during the direct-IO read. So we take another ref on - * the BIO and re-dirty the pages in process context. - * - * It is expected that bio_check_pages_dirty() will wholly own the BIO from - * here on. It will run one put_page() against each page and will run one - * bio_put() against the BIO. - */ - static void bio_dirty_fn(struct work_struct *work); static DECLARE_WORK(bio_dirty_work, bio_dirty_fn); @@ -1475,25 +1464,46 @@ static void bio_dirty_fn(struct work_struct *work) } } -void bio_check_pages_dirty(struct bio *bio) +/** + * bio_should_redirty_pages - check that all the BIO's pages are still dirty + * @bio: BIO to check + * + * Check if all pages in a direct read are still dirty. If they aren't the + * caller must call bio_queue_for_redirty() to redirty all pages that have been + * marked clean. + */ +bool bio_should_redirty_pages(struct bio *bio) { - struct bio_vec *bvec; - unsigned long flags; struct bvec_iter_all iter_all; + struct bio_vec *bvec; - bio_for_each_segment_all(bvec, bio, iter_all) { + bio_for_each_segment_all(bvec, bio, iter_all) if (!PageDirty(bvec->bv_page) && !PageCompound(bvec->bv_page)) - goto defer; - } + return true; + return false; +} + +/** + * bio_queue_for_redirty - queue up a bio to ensure all pages are marked dirty + * + * If some pages are clean after a direct read has completed, then they must + * have been written out during the direct read. Take another ref on the BIO + * and re-dirty the pages in process context. + * + * It is expected that bio_queue_for_redirty() owns the pages in the BIO from + * here on. It will run one put_page() against each page one finished. + */ +void bio_queue_for_redirty(struct bio *bio) +{ + unsigned long flags; + + bio_get(bio); - bio_release_pages(bio, false); - bio_put(bio); - return; -defer: spin_lock_irqsave(&bio_dirty_lock, flags); bio->bi_private = bio_dirty_list; bio_dirty_list = bio; spin_unlock_irqrestore(&bio_dirty_lock, flags); + schedule_work(&bio_dirty_work); } diff --git a/block/fops.c b/block/fops.c index b90742595317e..164b47f836ae6 100644 --- a/block/fops.c +++ b/block/fops.c @@ -159,12 +159,11 @@ static void blkdev_bio_end_io(struct bio *bio) } } - if (should_dirty) { - bio_check_pages_dirty(bio); - } else { + if (should_dirty && bio_should_redirty_pages(bio)) + bio_queue_for_redirty(bio); + else bio_release_pages(bio, false); - bio_put(bio); - } + bio_put(bio); } static ssize_t __blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter, @@ -283,12 +282,11 @@ static void blkdev_bio_end_io_async(struct bio *bio) iocb->ki_complete(iocb, ret); - if (dio->flags & DIO_SHOULD_DIRTY) { - bio_check_pages_dirty(bio); - } else { + if ((dio->flags & DIO_SHOULD_DIRTY) && bio_should_redirty_pages(bio)) + bio_queue_for_redirty(bio); + else bio_release_pages(bio, false); - bio_put(bio); - } + bio_put(bio); } static ssize_t __blkdev_direct_IO_async(struct kiocb *iocb, diff --git a/fs/direct-io.c b/fs/direct-io.c index f669163d5860f..8d7fe8e15509b 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -410,7 +410,7 @@ dio_bio_alloc(struct dio *dio, struct dio_submit *sdio, /* * In the AIO read case we speculatively dirty the pages before starting IO. * During IO completion, any of these pages which happen to have been written - * back will be redirtied by bio_check_pages_dirty(). + * back will be redirtied by bio_queue_for_redirty(). * * bios hold a dio reference between submit_bio and ->end_io. */ @@ -504,12 +504,11 @@ static blk_status_t dio_bio_complete(struct dio *dio, struct bio *bio) dio->io_error = -EIO; } - if (dio->is_async && should_dirty) { - bio_check_pages_dirty(bio); /* transfers ownership */ - } else { + if (dio->is_async && should_dirty && bio_should_redirty_pages(bio)) + bio_queue_for_redirty(bio); /* transfers ownership */ + else bio_release_pages(bio, should_dirty); - bio_put(bio); - } + bio_put(bio); return err; } diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c index 4eb559a16c9ed..1699b3d4895c1 100644 --- a/fs/iomap/direct-io.c +++ b/fs/iomap/direct-io.c @@ -179,12 +179,11 @@ void iomap_dio_bio_end_io(struct bio *bio) } } - if (should_dirty) { - bio_check_pages_dirty(bio); - } else { + if (should_dirty && bio_should_redirty_pages(bio)) + bio_queue_for_redirty(bio); + else bio_release_pages(bio, false); - bio_put(bio); - } + bio_put(bio); } EXPORT_SYMBOL_GPL(iomap_dio_bio_end_io); diff --git a/include/linux/bio.h b/include/linux/bio.h index ca22b06700a94..f1e6d13c2c0da 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -473,7 +473,8 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter); void bio_iov_bvec_set(struct bio *bio, struct iov_iter *iter); void __bio_release_pages(struct bio *bio, bool mark_dirty); extern void bio_set_pages_dirty(struct bio *bio); -extern void bio_check_pages_dirty(struct bio *bio); +bool bio_should_redirty_pages(struct bio *bio); +void bio_queue_for_redirty(struct bio *bio); extern void bio_copy_data_iter(struct bio *dst, struct bvec_iter *dst_iter, struct bio *src, struct bvec_iter *src_iter); -- 2.30.2