btrfs: add support for uncached writes buffered-uncached.6
authorJens Axboe <axboe@kernel.dk>
Sun, 10 Nov 2024 20:44:52 +0000 (13:44 -0700)
committerJens Axboe <axboe@kernel.dk>
Mon, 11 Nov 2024 23:35:20 +0000 (16:35 -0700)
The read side is already covered as btrfs uses the generic filemap
helpers. For writes, just pass in FGP_UNCACHED if uncached IO is being
done, then the folios created should be marked appropriately.

For IO completion, ensure that writing back folios that are uncached
gets punted to one of the btrfs workers, as task context is needed for
that. Add an 'uncached_io' member to struct btrfs_bio to manage that.

Outside of that, call generic_uncached_write() upon successful
completion of a buffered write.

With that, add FOP_UNCACHED to the btrfs file_operations fop_flags
structure, enabling use of RWF_UNCACHED.

Signed-off-by: Jens Axboe <axboe@kernel.dk>
fs/btrfs/bio.c
fs/btrfs/bio.h
fs/btrfs/extent_io.c
fs/btrfs/file.c

index 7e0f9600b80c43647c3fac55ddab013b510c2ad8..253e1a6569341d1e8d7c832474f809a1ed392f34 100644 (file)
@@ -334,7 +334,7 @@ static void btrfs_end_bio_work(struct work_struct *work)
        struct btrfs_bio *bbio = container_of(work, struct btrfs_bio, end_io_work);
 
        /* Metadata reads are checked and repaired by the submitter. */
-       if (is_data_bbio(bbio))
+       if (bio_op(&bbio->bio) == REQ_OP_READ && is_data_bbio(bbio))
                btrfs_check_read_bio(bbio, bbio->bio.bi_private);
        else
                btrfs_bio_end_io(bbio, bbio->bio.bi_status);
@@ -351,7 +351,7 @@ static void btrfs_simple_end_io(struct bio *bio)
        if (bio->bi_status)
                btrfs_log_dev_io_error(bio, dev);
 
-       if (bio_op(bio) == REQ_OP_READ) {
+       if (bio_op(bio) == REQ_OP_READ || bbio->uncached_io) {
                INIT_WORK(&bbio->end_io_work, btrfs_end_bio_work);
                queue_work(btrfs_end_io_wq(fs_info, bio), &bbio->end_io_work);
        } else {
index e2fe16074ad6558cd7e604cd0aab0ac0fae4a68d..39b98326c98f5d4ee6fcccfa277bea4a437ef869 100644 (file)
@@ -82,6 +82,8 @@ struct btrfs_bio {
        /* Save the first error status of split bio. */
        blk_status_t status;
 
+       bool uncached_io;
+
        /*
         * This member must come last, bio_alloc_bioset will allocate enough
         * bytes for entire btrfs_bio but relies on bio being last.
index 872cca54cc6ce41bd0f6f5809b1a5671e6187239..b97b21178ed7c4e1c47738494fc2954865c22ea9 100644 (file)
@@ -760,8 +760,11 @@ static void submit_extent_folio(struct btrfs_bio_ctrl *bio_ctrl,
        ASSERT(bio_ctrl->end_io_func);
 
        if (bio_ctrl->bbio &&
-           !btrfs_bio_is_contig(bio_ctrl, folio, disk_bytenr, pg_offset))
+           !btrfs_bio_is_contig(bio_ctrl, folio, disk_bytenr, pg_offset)) {
+               if (folio_test_uncached(folio))
+                       bio_ctrl->bbio->uncached_io = true;
                submit_one_bio(bio_ctrl);
+       }
 
        do {
                u32 len = size;
@@ -779,6 +782,9 @@ static void submit_extent_folio(struct btrfs_bio_ctrl *bio_ctrl,
                        len = bio_ctrl->len_to_oe_boundary;
                }
 
+               if (folio_test_uncached(folio))
+                       bio_ctrl->bbio->uncached_io = true;
+
                if (!bio_add_folio(&bio_ctrl->bbio->bio, folio, len, pg_offset)) {
                        /* bio full: move on to a new one */
                        submit_one_bio(bio_ctrl);
index 4fb521d91b061276a1c09a366cbbe0bed6459862..a27d194a28e0f63c5c6dd2e646676a96f840fcfe 100644 (file)
@@ -919,7 +919,7 @@ static gfp_t get_prepare_gfp_flags(struct inode *inode, bool nowait)
 static noinline int prepare_pages(struct inode *inode, struct page **pages,
                                  size_t num_pages, loff_t pos,
                                  size_t write_bytes, bool force_uptodate,
-                                 bool nowait)
+                                 bool nowait, bool uncached)
 {
        int i;
        unsigned long index = pos >> PAGE_SHIFT;
@@ -928,6 +928,8 @@ static noinline int prepare_pages(struct inode *inode, struct page **pages,
        int ret = 0;
        int faili;
 
+       if (uncached)
+               fgp_flags |= FGP_UNCACHED;
        for (i = 0; i < num_pages; i++) {
 again:
                pages[i] = pagecache_get_page(inode->i_mapping, index + i,
@@ -1323,7 +1325,8 @@ again:
                 * contents of pages from loop to loop
                 */
                ret = prepare_pages(inode, pages, num_pages,
-                                   pos, write_bytes, force_page_uptodate, false);
+                                   pos, write_bytes, force_page_uptodate,
+                                   false, iocb->ki_flags & IOCB_UNCACHED);
                if (ret) {
                        btrfs_delalloc_release_extents(BTRFS_I(inode),
                                                       reserve_bytes);
@@ -1512,6 +1515,7 @@ ssize_t btrfs_do_write_iter(struct kiocb *iocb, struct iov_iter *from,
        btrfs_set_inode_last_sub_trans(inode);
 
        if (num_sync > 0) {
+               generic_uncached_write(iocb, num_sync);
                num_sync = generic_write_sync(iocb, num_sync);
                if (num_sync < 0)
                        num_written = num_sync;
@@ -3802,7 +3806,7 @@ const struct file_operations btrfs_file_operations = {
        .compat_ioctl   = btrfs_compat_ioctl,
 #endif
        .remap_file_range = btrfs_remap_file_range,
-       .fop_flags      = FOP_BUFFER_RASYNC | FOP_BUFFER_WASYNC,
+       .fop_flags      = FOP_BUFFER_RASYNC | FOP_BUFFER_WASYNC | FOP_UNCACHED,
 };
 
 int btrfs_fdatawrite_range(struct btrfs_inode *inode, loff_t start, loff_t end)