OSDN Git Service

iomap: cleanup up iomap_dio_bio_end_io()
authorJens Axboe <axboe@kernel.dk>
Wed, 19 Jul 2023 15:34:27 +0000 (09:34 -0600)
committerJens Axboe <axboe@kernel.dk>
Tue, 1 Aug 2023 23:32:17 +0000 (17:32 -0600)
Make the logic a bit easier to follow:

1) Add a release_bio out path, as everybody needs to touch that, and
   have our bio ref check jump there if it's non-zero.
2) Add a kiocb local variable.
3) Add comments for each of the three conditions (sync, inline, or
   async workqueue punt).

No functional changes in this patch.

Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
fs/iomap/direct-io.c

index ea3b868..0ce60e8 100644 (file)
@@ -152,27 +152,43 @@ void iomap_dio_bio_end_io(struct bio *bio)
 {
        struct iomap_dio *dio = bio->bi_private;
        bool should_dirty = (dio->flags & IOMAP_DIO_DIRTY);
+       struct kiocb *iocb = dio->iocb;
 
        if (bio->bi_status)
                iomap_dio_set_error(dio, blk_status_to_errno(bio->bi_status));
+       if (!atomic_dec_and_test(&dio->ref))
+               goto release_bio;
 
-       if (atomic_dec_and_test(&dio->ref)) {
-               if (dio->wait_for_completion) {
-                       struct task_struct *waiter = dio->submit.waiter;
-                       WRITE_ONCE(dio->submit.waiter, NULL);
-                       blk_wake_io_task(waiter);
-               } else if (dio->flags & IOMAP_DIO_WRITE) {
-                       struct inode *inode = file_inode(dio->iocb->ki_filp);
-
-                       WRITE_ONCE(dio->iocb->private, NULL);
-                       INIT_WORK(&dio->aio.work, iomap_dio_complete_work);
-                       queue_work(inode->i_sb->s_dio_done_wq, &dio->aio.work);
-               } else {
-                       WRITE_ONCE(dio->iocb->private, NULL);
-                       iomap_dio_complete_work(&dio->aio.work);
-               }
+       /*
+        * Synchronous dio, task itself will handle any completion work
+        * that needs after IO. All we need to do is wake the task.
+        */
+       if (dio->wait_for_completion) {
+               struct task_struct *waiter = dio->submit.waiter;
+
+               WRITE_ONCE(dio->submit.waiter, NULL);
+               blk_wake_io_task(waiter);
+               goto release_bio;
+       }
+
+       /* Read completion can always complete inline. */
+       if (!(dio->flags & IOMAP_DIO_WRITE)) {
+               WRITE_ONCE(iocb->private, NULL);
+               iomap_dio_complete_work(&dio->aio.work);
+               goto release_bio;
        }
 
+       /*
+        * Async DIO completion that requires filesystem level completion work
+        * gets punted to a work queue to complete as the operation may require
+        * more IO to be issued to finalise filesystem metadata changes or
+        * guarantee data integrity.
+        */
+       WRITE_ONCE(iocb->private, NULL);
+       INIT_WORK(&dio->aio.work, iomap_dio_complete_work);
+       queue_work(file_inode(iocb->ki_filp)->i_sb->s_dio_done_wq,
+                       &dio->aio.work);
+release_bio:
        if (should_dirty) {
                bio_check_pages_dirty(bio);
        } else {