btrfs: don't unconditionally call folio_start_writeback in subpage
[linux-2.6-microblaze.git] / fs / btrfs / extent_io.c
index e6230a6..a0ffd41 100644 (file)
@@ -184,22 +184,23 @@ static void process_one_page(struct btrfs_fs_info *fs_info,
                             struct page *page, struct page *locked_page,
                             unsigned long page_ops, u64 start, u64 end)
 {
+       struct folio *folio = page_folio(page);
        u32 len;
 
        ASSERT(end + 1 - start != 0 && end + 1 - start < U32_MAX);
        len = end + 1 - start;
 
        if (page_ops & PAGE_SET_ORDERED)
-               btrfs_page_clamp_set_ordered(fs_info, page, start, len);
+               btrfs_folio_clamp_set_ordered(fs_info, folio, start, len);
        if (page_ops & PAGE_START_WRITEBACK) {
-               btrfs_page_clamp_clear_dirty(fs_info, page, start, len);
-               btrfs_page_clamp_set_writeback(fs_info, page, start, len);
+               btrfs_folio_clamp_clear_dirty(fs_info, folio, start, len);
+               btrfs_folio_clamp_set_writeback(fs_info, folio, start, len);
        }
        if (page_ops & PAGE_END_WRITEBACK)
-               btrfs_page_clamp_clear_writeback(fs_info, page, start, len);
+               btrfs_folio_clamp_clear_writeback(fs_info, folio, start, len);
 
        if (page != locked_page && (page_ops & PAGE_UNLOCK))
-               btrfs_page_end_writer_lock(fs_info, page, start, len);
+               btrfs_folio_end_writer_lock(fs_info, folio, start, len);
 }
 
 static void __process_pages_contig(struct address_space *mapping,
@@ -271,19 +272,20 @@ static noinline int lock_delalloc_pages(struct inode *inode,
                        goto out;
 
                for (i = 0; i < found_folios; i++) {
-                       struct page *page = &fbatch.folios[i]->page;
+                       struct folio *folio = fbatch.folios[i];
+                       struct page *page = folio_page(folio, 0);
                        u32 len = end + 1 - start;
 
                        if (page == locked_page)
                                continue;
 
-                       if (btrfs_page_start_writer_lock(fs_info, page, start,
-                                                        len))
+                       if (btrfs_folio_start_writer_lock(fs_info, folio, start,
+                                                         len))
                                goto out;
 
                        if (!PageDirty(page) || page->mapping != mapping) {
-                               btrfs_page_end_writer_lock(fs_info, page, start,
-                                                          len);
+                               btrfs_folio_end_writer_lock(fs_info, folio, start,
+                                                           len);
                                goto out;
                        }
 
@@ -432,60 +434,65 @@ static bool btrfs_verify_page(struct page *page, u64 start)
 static void end_page_read(struct page *page, bool uptodate, u64 start, u32 len)
 {
        struct btrfs_fs_info *fs_info = btrfs_sb(page->mapping->host->i_sb);
+       struct folio *folio = page_folio(page);
 
        ASSERT(page_offset(page) <= start &&
               start + len <= page_offset(page) + PAGE_SIZE);
 
        if (uptodate && btrfs_verify_page(page, start))
-               btrfs_page_set_uptodate(fs_info, page, start, len);
+               btrfs_folio_set_uptodate(fs_info, folio, start, len);
        else
-               btrfs_page_clear_uptodate(fs_info, page, start, len);
+               btrfs_folio_clear_uptodate(fs_info, folio, start, len);
 
-       if (!btrfs_is_subpage(fs_info, page))
+       if (!btrfs_is_subpage(fs_info, page->mapping))
                unlock_page(page);
        else
-               btrfs_subpage_end_reader(fs_info, page, start, len);
+               btrfs_subpage_end_reader(fs_info, folio, start, len);
 }
 
 /*
- * after a writepage IO is done, we need to:
- * clear the uptodate bits on error
- * clear the writeback bits in the extent tree for this IO
- * end_page_writeback if the page has no more pending IO
+ * After a write IO is done, we need to:
+ *
+ * - clear the uptodate bits on error
+ * - clear the writeback bits in the extent tree for the range
+ * - filio_end_writeback()  if there is no more pending io for the folio
  *
  * Scheduling is not allowed, so the extent state tree is expected
  * to have one and only one object corresponding to this IO.
  */
-static void end_bio_extent_writepage(struct btrfs_bio *bbio)
+static void end_bbio_data_write(struct btrfs_bio *bbio)
 {
        struct bio *bio = &bbio->bio;
        int error = blk_status_to_errno(bio->bi_status);
-       struct bio_vec *bvec;
-       struct bvec_iter_all iter_all;
+       struct folio_iter fi;
 
        ASSERT(!bio_flagged(bio, BIO_CLONED));
-       bio_for_each_segment_all(bvec, bio, iter_all) {
-               struct page *page = bvec->bv_page;
-               struct inode *inode = page->mapping->host;
+       bio_for_each_folio_all(fi, bio) {
+               struct folio *folio = fi.folio;
+               struct inode *inode = folio->mapping->host;
                struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
                const u32 sectorsize = fs_info->sectorsize;
-               u64 start = page_offset(page) + bvec->bv_offset;
-               u32 len = bvec->bv_len;
+               u64 start = folio_pos(folio) + fi.offset;
+               u32 len = fi.length;
+
+               /* Only order 0 (single page) folios are allowed for data. */
+               ASSERT(folio_order(folio) == 0);
 
                /* Our read/write should always be sector aligned. */
-               if (!IS_ALIGNED(bvec->bv_offset, sectorsize))
+               if (!IS_ALIGNED(fi.offset, sectorsize))
                        btrfs_err(fs_info,
-               "partial page write in btrfs with offset %u and length %u",
-                                 bvec->bv_offset, bvec->bv_len);
-               else if (!IS_ALIGNED(bvec->bv_len, sectorsize))
+               "partial page write in btrfs with offset %zu and length %zu",
+                                 fi.offset, fi.length);
+               else if (!IS_ALIGNED(fi.length, sectorsize))
                        btrfs_info(fs_info,
-               "incomplete page write with offset %u and length %u",
-                                  bvec->bv_offset, bvec->bv_len);
+               "incomplete page write with offset %zu and length %zu",
+                                  fi.offset, fi.length);
 
-               btrfs_finish_ordered_extent(bbio->ordered, page, start, len, !error);
+               btrfs_finish_ordered_extent(bbio->ordered,
+                               folio_page(folio, 0), start, len, !error);
                if (error)
-                       mapping_set_error(page->mapping, error);
-               btrfs_page_clear_writeback(fs_info, page, start, len);
+                       mapping_set_error(folio->mapping, error);
+               btrfs_folio_clear_writeback(fs_info, folio, start, len);
        }
 
        bio_put(bio);
@@ -562,98 +569,102 @@ update:
 
 static void begin_page_read(struct btrfs_fs_info *fs_info, struct page *page)
 {
-       ASSERT(PageLocked(page));
-       if (!btrfs_is_subpage(fs_info, page))
+       struct folio *folio = page_folio(page);
+
+       ASSERT(folio_test_locked(folio));
+       if (!btrfs_is_subpage(fs_info, folio->mapping))
                return;
 
-       ASSERT(PagePrivate(page));
-       btrfs_subpage_start_reader(fs_info, page, page_offset(page), PAGE_SIZE);
+       ASSERT(folio_test_private(folio));
+       btrfs_subpage_start_reader(fs_info, folio, page_offset(page), PAGE_SIZE);
 }
 
 /*
- * after a readpage IO is done, we need to:
- * clear the uptodate bits on error
- * set the uptodate bits if things worked
- * set the page up to date if all extents in the tree are uptodate
- * clear the lock bit in the extent tree
- * unlock the page if there are no other extents locked for it
+ * After a data read IO is done, we need to:
+ *
+ * - clear the uptodate bits on error
+ * - set the uptodate bits if things worked
+ * - set the folio up to date if all extents in the tree are uptodate
+ * - clear the lock bit in the extent tree
+ * - unlock the folio if there are no other extents locked for it
  *
  * Scheduling is not allowed, so the extent state tree is expected
  * to have one and only one object corresponding to this IO.
  */
-static void end_bio_extent_readpage(struct btrfs_bio *bbio)
+static void end_bbio_data_read(struct btrfs_bio *bbio)
 {
        struct bio *bio = &bbio->bio;
-       struct bio_vec *bvec;
        struct processed_extent processed = { 0 };
+       struct folio_iter fi;
        /*
         * The offset to the beginning of a bio, since one bio can never be
         * larger than UINT_MAX, u32 here is enough.
         */
        u32 bio_offset = 0;
-       struct bvec_iter_all iter_all;
 
        ASSERT(!bio_flagged(bio, BIO_CLONED));
-       bio_for_each_segment_all(bvec, bio, iter_all) {
+       bio_for_each_folio_all(fi, &bbio->bio) {
                bool uptodate = !bio->bi_status;
-               struct page *page = bvec->bv_page;
-               struct inode *inode = page->mapping->host;
+               struct folio *folio = fi.folio;
+               struct inode *inode = folio->mapping->host;
                struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
                const u32 sectorsize = fs_info->sectorsize;
                u64 start;
                u64 end;
                u32 len;
 
+               /* For now only order 0 folios are supported for data. */
+               ASSERT(folio_order(folio) == 0);
                btrfs_debug(fs_info,
-                       "end_bio_extent_readpage: bi_sector=%llu, err=%d, mirror=%u",
-                       bio->bi_iter.bi_sector, bio->bi_status,
+                       "%s: bi_sector=%llu, err=%d, mirror=%u",
+                       __func__, bio->bi_iter.bi_sector, bio->bi_status,
                        bbio->mirror_num);
 
                /*
                 * We always issue full-sector reads, but if some block in a
-                * page fails to read, blk_update_request() will advance
+                * folio fails to read, blk_update_request() will advance
                 * bv_offset and adjust bv_len to compensate.  Print a warning
                 * for unaligned offsets, and an error if they don't add up to
                 * a full sector.
                 */
-               if (!IS_ALIGNED(bvec->bv_offset, sectorsize))
+               if (!IS_ALIGNED(fi.offset, sectorsize))
                        btrfs_err(fs_info,
-               "partial page read in btrfs with offset %u and length %u",
-                                 bvec->bv_offset, bvec->bv_len);
-               else if (!IS_ALIGNED(bvec->bv_offset + bvec->bv_len,
-                                    sectorsize))
+               "partial page read in btrfs with offset %zu and length %zu",
+                                 fi.offset, fi.length);
+               else if (!IS_ALIGNED(fi.offset + fi.length, sectorsize))
                        btrfs_info(fs_info,
-               "incomplete page read with offset %u and length %u",
-                                  bvec->bv_offset, bvec->bv_len);
+               "incomplete page read with offset %zu and length %zu",
+                                  fi.offset, fi.length);
 
-               start = page_offset(page) + bvec->bv_offset;
-               end = start + bvec->bv_len - 1;
-               len = bvec->bv_len;
+               start = folio_pos(folio) + fi.offset;
+               end = start + fi.length - 1;
+               len = fi.length;
 
                if (likely(uptodate)) {
                        loff_t i_size = i_size_read(inode);
-                       pgoff_t end_index = i_size >> PAGE_SHIFT;
+                       pgoff_t end_index = i_size >> folio_shift(folio);
 
                        /*
                         * Zero out the remaining part if this range straddles
                         * i_size.
                         *
-                        * Here we should only zero the range inside the bvec,
+                        * Here we should only zero the range inside the folio,
                         * not touch anything else.
                         *
                         * NOTE: i_size is exclusive while end is inclusive.
                         */
-                       if (page->index == end_index && i_size <= end) {
-                               u32 zero_start = max(offset_in_page(i_size),
-                                                    offset_in_page(start));
+                       if (folio_index(folio) == end_index && i_size <= end) {
+                               u32 zero_start = max(offset_in_folio(folio, i_size),
+                                                    offset_in_folio(folio, start));
+                               u32 zero_len = offset_in_folio(folio, end) + 1 -
+                                              zero_start;
 
-                               zero_user_segment(page, zero_start,
-                                                 offset_in_page(end) + 1);
+                               folio_zero_range(folio, zero_start, zero_len);
                        }
                }
 
                /* Update page status and unlock. */
-               end_page_read(page, uptodate, start, len);
+               end_page_read(folio_page(folio, 0), uptodate, start, len);
                endio_readpage_release_extent(&processed, BTRFS_I(inode),
                                              start, end, uptodate);
 
@@ -672,19 +683,22 @@ static void end_bio_extent_readpage(struct btrfs_bio *bbio)
  * @nr_pages:   number of pages to allocate
  * @page_array: the array to fill with pages; any existing non-null entries in
  *             the array will be skipped
+ * @extra_gfp: the extra GFP flags for the allocation.
  *
  * Return: 0        if all pages were able to be allocated;
  *         -ENOMEM  otherwise, the partially allocated pages would be freed and
  *                  the array slots zeroed
  */
-int btrfs_alloc_page_array(unsigned int nr_pages, struct page **page_array)
+int btrfs_alloc_page_array(unsigned int nr_pages, struct page **page_array,
+                          gfp_t extra_gfp)
 {
        unsigned int allocated;
 
        for (allocated = 0; allocated < nr_pages;) {
                unsigned int last = allocated;
 
-               allocated = alloc_pages_bulk_array(GFP_NOFS, nr_pages, page_array);
+               allocated = alloc_pages_bulk_array(GFP_NOFS | extra_gfp,
+                                                  nr_pages, page_array);
 
                if (allocated == nr_pages)
                        return 0;
@@ -707,6 +721,26 @@ int btrfs_alloc_page_array(unsigned int nr_pages, struct page **page_array)
        return 0;
 }
 
+/*
+ * Populate needed folios for the extent buffer.
+ *
+ * For now, the folios populated are always in order 0 (aka, single page).
+ */
+static int alloc_eb_folio_array(struct extent_buffer *eb, gfp_t extra_gfp)
+{
+       struct page *page_array[INLINE_EXTENT_BUFFER_PAGES] = { 0 };
+       int num_pages = num_extent_pages(eb);
+       int ret;
+
+       ret = btrfs_alloc_page_array(num_pages, page_array, extra_gfp);
+       if (ret < 0)
+               return ret;
+
+       for (int i = 0; i < num_pages; i++)
+               eb->folios[i] = page_folio(page_array[i]);
+       return 0;
+}
+
 static bool btrfs_bio_is_contig(struct btrfs_bio_ctrl *bio_ctrl,
                                struct page *page, u64 disk_bytenr,
                                unsigned int pg_offset)
@@ -861,9 +895,9 @@ static void submit_extent_page(struct btrfs_bio_ctrl *bio_ctrl,
        } while (size);
 }
 
-static int attach_extent_buffer_page(struct extent_buffer *eb,
-                                    struct page *page,
-                                    struct btrfs_subpage *prealloc)
+static int attach_extent_buffer_folio(struct extent_buffer *eb,
+                                     struct folio *folio,
+                                     struct btrfs_subpage *prealloc)
 {
        struct btrfs_fs_info *fs_info = eb->fs_info;
        int ret = 0;
@@ -874,65 +908,66 @@ static int attach_extent_buffer_page(struct extent_buffer *eb,
         * For cloned or dummy extent buffers, their pages are not mapped and
         * will not race with any other ebs.
         */
-       if (page->mapping)
-               lockdep_assert_held(&page->mapping->private_lock);
+       if (folio->mapping)
+               lockdep_assert_held(&folio->mapping->private_lock);
 
        if (fs_info->nodesize >= PAGE_SIZE) {
-               if (!PagePrivate(page))
-                       attach_page_private(page, eb);
+               if (!folio_test_private(folio))
+                       folio_attach_private(folio, eb);
                else
-                       WARN_ON(page->private != (unsigned long)eb);
+                       WARN_ON(folio_get_private(folio) != eb);
                return 0;
        }
 
        /* Already mapped, just free prealloc */
-       if (PagePrivate(page)) {
+       if (folio_test_private(folio)) {
                btrfs_free_subpage(prealloc);
                return 0;
        }
 
        if (prealloc)
                /* Has preallocated memory for subpage */
-               attach_page_private(page, prealloc);
+               folio_attach_private(folio, prealloc);
        else
                /* Do new allocation to attach subpage */
-               ret = btrfs_attach_subpage(fs_info, page,
-                                          BTRFS_SUBPAGE_METADATA);
+               ret = btrfs_attach_subpage(fs_info, folio, BTRFS_SUBPAGE_METADATA);
        return ret;
 }
 
 int set_page_extent_mapped(struct page *page)
 {
+       struct folio *folio = page_folio(page);
        struct btrfs_fs_info *fs_info;
 
        ASSERT(page->mapping);
 
-       if (PagePrivate(page))
+       if (folio_test_private(folio))
                return 0;
 
        fs_info = btrfs_sb(page->mapping->host->i_sb);
 
-       if (btrfs_is_subpage(fs_info, page))
-               return btrfs_attach_subpage(fs_info, page, BTRFS_SUBPAGE_DATA);
+       if (btrfs_is_subpage(fs_info, page->mapping))
+               return btrfs_attach_subpage(fs_info, folio, BTRFS_SUBPAGE_DATA);
 
-       attach_page_private(page, (void *)EXTENT_PAGE_PRIVATE);
+       folio_attach_private(folio, (void *)EXTENT_FOLIO_PRIVATE);
        return 0;
 }
 
 void clear_page_extent_mapped(struct page *page)
 {
+       struct folio *folio = page_folio(page);
        struct btrfs_fs_info *fs_info;
 
        ASSERT(page->mapping);
 
-       if (!PagePrivate(page))
+       if (!folio_test_private(folio))
                return;
 
        fs_info = btrfs_sb(page->mapping->host->i_sb);
-       if (btrfs_is_subpage(fs_info, page))
-               return btrfs_detach_subpage(fs_info, page);
+       if (btrfs_is_subpage(fs_info, page->mapping))
+               return btrfs_detach_subpage(fs_info, folio);
 
-       detach_page_private(page);
+       folio_detach_private(folio);
 }
 
 static struct extent_map *
@@ -1001,7 +1036,7 @@ static int btrfs_do_readpage(struct page *page, struct extent_map **em_cached,
                        memzero_page(page, zero_offset, iosize);
                }
        }
-       bio_ctrl->end_io_func = end_bio_extent_readpage;
+       bio_ctrl->end_io_func = end_bbio_data_read;
        begin_page_read(fs_info, page);
        while (cur <= end) {
                enum btrfs_compression_type compress_type = BTRFS_COMPRESS_NONE;
@@ -1027,8 +1062,7 @@ static int btrfs_do_readpage(struct page *page, struct extent_map **em_cached,
                BUG_ON(extent_map_end(em) <= cur);
                BUG_ON(end < cur);
 
-               if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags))
-                       compress_type = em->compress_type;
+               compress_type = extent_map_compression(em);
 
                iosize = min(extent_map_end(em) - cur, end - cur + 1);
                iosize = ALIGN(iosize, blocksize);
@@ -1037,7 +1071,7 @@ static int btrfs_do_readpage(struct page *page, struct extent_map **em_cached,
                else
                        disk_bytenr = em->block_start + extent_offset;
                block_start = em->block_start;
-               if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags))
+               if (em->flags & EXTENT_FLAG_PREALLOC)
                        block_start = EXTENT_MAP_HOLE;
 
                /*
@@ -1074,7 +1108,7 @@ static int btrfs_do_readpage(struct page *page, struct extent_map **em_cached,
                 * is a corner case so we prioritize correctness over
                 * non-optimal behavior (submitting 2 bios for the same extent).
                 */
-               if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags) &&
+               if (compress_type != BTRFS_COMPRESS_NONE &&
                    prev_em_start && *prev_em_start != (u64)-1 &&
                    *prev_em_start != em->start)
                        force_bio_submit = true;
@@ -1240,7 +1274,8 @@ static noinline_for_stack int writepage_delalloc(struct btrfs_inode *inode,
 static void find_next_dirty_byte(struct btrfs_fs_info *fs_info,
                                 struct page *page, u64 *start, u64 *end)
 {
-       struct btrfs_subpage *subpage = (struct btrfs_subpage *)page->private;
+       struct folio *folio = page_folio(page);
+       struct btrfs_subpage *subpage = folio_get_private(folio);
        struct btrfs_subpage_info *spi = fs_info->subpage_info;
        u64 orig_start = *start;
        /* Declare as unsigned long so we can use bitmap ops */
@@ -1252,7 +1287,7 @@ static void find_next_dirty_byte(struct btrfs_fs_info *fs_info,
         * For regular sector size == page size case, since one page only
         * contains one sector, we return the page offset directly.
         */
-       if (!btrfs_is_subpage(fs_info, page)) {
+       if (!btrfs_is_subpage(fs_info, page->mapping)) {
                *start = page_offset(page);
                *end = page_offset(page) + PAGE_SIZE;
                return;
@@ -1305,7 +1340,7 @@ static noinline_for_stack int __extent_writepage_io(struct btrfs_inode *inode,
                return 1;
        }
 
-       bio_ctrl->end_io_func = end_bio_extent_writepage;
+       bio_ctrl->end_io_func = end_bbio_data_write;
        while (cur <= end) {
                u32 len = end - cur + 1;
                u64 disk_bytenr;
@@ -1325,7 +1360,7 @@ static noinline_for_stack int __extent_writepage_io(struct btrfs_inode *inode,
                         * writeback the sectors with subpage dirty bits,
                         * causing writeback without ordered extent.
                         */
-                       btrfs_page_clear_dirty(fs_info, page, cur, len);
+                       btrfs_folio_clear_dirty(fs_info, page_folio(page), cur, len);
                        break;
                }
 
@@ -1352,7 +1387,7 @@ static noinline_for_stack int __extent_writepage_io(struct btrfs_inode *inode,
                block_start = em->block_start;
                disk_bytenr = em->block_start + extent_offset;
 
-               ASSERT(!test_bit(EXTENT_FLAG_COMPRESSED, &em->flags));
+               ASSERT(!extent_map_is_compressed(em));
                ASSERT(block_start != EXTENT_MAP_HOLE);
                ASSERT(block_start != EXTENT_MAP_INLINE);
 
@@ -1377,7 +1412,7 @@ static noinline_for_stack int __extent_writepage_io(struct btrfs_inode *inode,
                 * So clear subpage dirty bit here so next time we won't submit
                 * page for range already written to disk.
                 */
-               btrfs_page_clear_dirty(fs_info, page, cur, iosize);
+               btrfs_folio_clear_dirty(fs_info, page_folio(page), cur, iosize);
 
                submit_extent_page(bio_ctrl, disk_bytenr, page, iosize,
                                   cur - page_offset(page));
@@ -1385,7 +1420,7 @@ static noinline_for_stack int __extent_writepage_io(struct btrfs_inode *inode,
                nr++;
        }
 
-       btrfs_page_assert_not_dirty(fs_info, page);
+       btrfs_folio_assert_not_dirty(fs_info, page_folio(page));
        *nr_ret = nr;
        return 0;
 
@@ -1607,24 +1642,23 @@ static struct extent_buffer *find_extent_buffer_nolock(
        return NULL;
 }
 
-static void extent_buffer_write_end_io(struct btrfs_bio *bbio)
+static void end_bbio_meta_write(struct btrfs_bio *bbio)
 {
        struct extent_buffer *eb = bbio->private;
        struct btrfs_fs_info *fs_info = eb->fs_info;
        bool uptodate = !bbio->bio.bi_status;
-       struct bvec_iter_all iter_all;
-       struct bio_vec *bvec;
+       struct folio_iter fi;
        u32 bio_offset = 0;
 
        if (!uptodate)
                set_btree_ioerr(eb);
 
-       bio_for_each_segment_all(bvec, &bbio->bio, iter_all) {
+       bio_for_each_folio_all(fi, &bbio->bio) {
                u64 start = eb->start + bio_offset;
-               struct page *page = bvec->bv_page;
-               u32 len = bvec->bv_len;
+               struct folio *folio = fi.folio;
+               u32 len = fi.length;
 
-               btrfs_page_clear_writeback(fs_info, page, start, len);
+               btrfs_folio_clear_writeback(fs_info, folio, start, len);
                bio_offset += len;
        }
 
@@ -1673,36 +1707,44 @@ static noinline_for_stack void write_one_eb(struct extent_buffer *eb,
 
        bbio = btrfs_bio_alloc(INLINE_EXTENT_BUFFER_PAGES,
                               REQ_OP_WRITE | REQ_META | wbc_to_write_flags(wbc),
-                              eb->fs_info, extent_buffer_write_end_io, eb);
+                              eb->fs_info, end_bbio_meta_write, eb);
        bbio->bio.bi_iter.bi_sector = eb->start >> SECTOR_SHIFT;
        bio_set_dev(&bbio->bio, fs_info->fs_devices->latest_dev->bdev);
        wbc_init_bio(wbc, &bbio->bio);
        bbio->inode = BTRFS_I(eb->fs_info->btree_inode);
        bbio->file_offset = eb->start;
        if (fs_info->nodesize < PAGE_SIZE) {
-               struct page *p = eb->pages[0];
+               struct folio *folio = eb->folios[0];
+               bool ret;
 
-               lock_page(p);
-               btrfs_subpage_set_writeback(fs_info, p, eb->start, eb->len);
-               if (btrfs_subpage_clear_and_test_dirty(fs_info, p, eb->start,
+               folio_lock(folio);
+               btrfs_subpage_set_writeback(fs_info, folio, eb->start, eb->len);
+               if (btrfs_subpage_clear_and_test_dirty(fs_info, folio, eb->start,
                                                       eb->len)) {
-                       clear_page_dirty_for_io(p);
+                       folio_clear_dirty_for_io(folio);
                        wbc->nr_to_write--;
                }
-               __bio_add_page(&bbio->bio, p, eb->len, eb->start - page_offset(p));
-               wbc_account_cgroup_owner(wbc, p, eb->len);
-               unlock_page(p);
+               ret = bio_add_folio(&bbio->bio, folio, eb->len,
+                                   eb->start - folio_pos(folio));
+               ASSERT(ret);
+               wbc_account_cgroup_owner(wbc, folio_page(folio, 0), eb->len);
+               folio_unlock(folio);
        } else {
-               for (int i = 0; i < num_extent_pages(eb); i++) {
-                       struct page *p = eb->pages[i];
-
-                       lock_page(p);
-                       clear_page_dirty_for_io(p);
-                       set_page_writeback(p);
-                       __bio_add_page(&bbio->bio, p, PAGE_SIZE, 0);
-                       wbc_account_cgroup_owner(wbc, p, PAGE_SIZE);
-                       wbc->nr_to_write--;
-                       unlock_page(p);
+               int num_folios = num_extent_folios(eb);
+
+               for (int i = 0; i < num_folios; i++) {
+                       struct folio *folio = eb->folios[i];
+                       bool ret;
+
+                       folio_lock(folio);
+                       folio_clear_dirty_for_io(folio);
+                       folio_start_writeback(folio);
+                       ret = bio_add_folio(&bbio->bio, folio, folio_size(folio), 0);
+                       ASSERT(ret);
+                       wbc_account_cgroup_owner(wbc, folio_page(folio, 0),
+                                                folio_size(folio));
+                       wbc->nr_to_write -= folio_nr_pages(folio);
+                       folio_unlock(folio);
                }
        }
        btrfs_submit_bio(bbio, 0);
@@ -1725,6 +1767,7 @@ static noinline_for_stack void write_one_eb(struct extent_buffer *eb,
 static int submit_eb_subpage(struct page *page, struct writeback_control *wbc)
 {
        struct btrfs_fs_info *fs_info = btrfs_sb(page->mapping->host->i_sb);
+       struct folio *folio = page_folio(page);
        int submitted = 0;
        u64 page_start = page_offset(page);
        int bit_start = 0;
@@ -1732,7 +1775,7 @@ static int submit_eb_subpage(struct page *page, struct writeback_control *wbc)
 
        /* Lock and write each dirty extent buffers in the range */
        while (bit_start < fs_info->subpage_info->bitmap_nr_bits) {
-               struct btrfs_subpage *subpage = (struct btrfs_subpage *)page->private;
+               struct btrfs_subpage *subpage = folio_get_private(folio);
                struct extent_buffer *eb;
                unsigned long flags;
                u64 start;
@@ -1742,7 +1785,7 @@ static int submit_eb_subpage(struct page *page, struct writeback_control *wbc)
                 * in the meantime.
                 */
                spin_lock(&page->mapping->private_lock);
-               if (!PagePrivate(page)) {
+               if (!folio_test_private(folio)) {
                        spin_unlock(&page->mapping->private_lock);
                        break;
                }
@@ -1807,22 +1850,23 @@ static int submit_eb_page(struct page *page, struct btrfs_eb_write_context *ctx)
 {
        struct writeback_control *wbc = ctx->wbc;
        struct address_space *mapping = page->mapping;
+       struct folio *folio = page_folio(page);
        struct extent_buffer *eb;
        int ret;
 
-       if (!PagePrivate(page))
+       if (!folio_test_private(folio))
                return 0;
 
        if (btrfs_sb(page->mapping->host->i_sb)->nodesize < PAGE_SIZE)
                return submit_eb_subpage(page, wbc);
 
        spin_lock(&mapping->private_lock);
-       if (!PagePrivate(page)) {
+       if (!folio_test_private(folio)) {
                spin_unlock(&mapping->private_lock);
                return 0;
        }
 
-       eb = (struct extent_buffer *)page->private;
+       eb = folio_get_private(folio);
 
        /*
         * Shouldn't happen and normally this would be a BUG_ON but no point
@@ -2201,7 +2245,7 @@ void extent_write_locked_range(struct inode *inode, struct page *locked_page,
                                                       cur, cur_len, !ret);
                        mapping_set_error(page->mapping, ret);
                }
-               btrfs_page_unlock_writer(fs_info, page, cur, cur_len);
+               btrfs_folio_unlock_writer(fs_info, page_folio(page), cur, cur_len);
                if (ret < 0)
                        found_error = true;
 next_page:
@@ -2302,7 +2346,8 @@ static int try_release_extent_state(struct extent_io_tree *tree,
                ret = 0;
        } else {
                u32 clear_bits = ~(EXTENT_LOCKED | EXTENT_NODATASUM |
-                                  EXTENT_DELALLOC_NEW | EXTENT_CTLBITS);
+                                  EXTENT_DELALLOC_NEW | EXTENT_CTLBITS |
+                                  EXTENT_QGROUP_RESERVED);
 
                /*
                 * At this point we can safely clear everything except the
@@ -2351,7 +2396,7 @@ int try_release_extent_mapping(struct page *page, gfp_t mask)
                                write_unlock(&map->lock);
                                break;
                        }
-                       if (test_bit(EXTENT_FLAG_PINNED, &em->flags) ||
+                       if ((em->flags & EXTENT_FLAG_PINNED) ||
                            em->start != start) {
                                write_unlock(&map->lock);
                                free_extent_map(em);
@@ -2368,7 +2413,7 @@ int try_release_extent_mapping(struct page *page, gfp_t mask)
                         * extra reference on the em.
                         */
                        if (list_empty(&em->list) ||
-                           test_bit(EXTENT_FLAG_LOGGING, &em->flags))
+                           (em->flags & EXTENT_FLAG_LOGGING))
                                goto remove_em;
                        /*
                         * If it's in the list of modified extents, remove it
@@ -3057,14 +3102,14 @@ static int extent_buffer_under_io(const struct extent_buffer *eb)
                test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags));
 }
 
-static bool page_range_has_eb(struct btrfs_fs_info *fs_info, struct page *page)
+static bool folio_range_has_eb(struct btrfs_fs_info *fs_info, struct folio *folio)
 {
        struct btrfs_subpage *subpage;
 
-       lockdep_assert_held(&page->mapping->private_lock);
+       lockdep_assert_held(&folio->mapping->private_lock);
 
-       if (PagePrivate(page)) {
-               subpage = (struct btrfs_subpage *)page->private;
+       if (folio_test_private(folio)) {
+               subpage = folio_get_private(folio);
                if (atomic_read(&subpage->eb_refs))
                        return true;
                /*
@@ -3077,21 +3122,21 @@ static bool page_range_has_eb(struct btrfs_fs_info *fs_info, struct page *page)
        return false;
 }
 
-static void detach_extent_buffer_page(struct extent_buffer *eb, struct page *page)
+static void detach_extent_buffer_folio(struct extent_buffer *eb, struct folio *folio)
 {
        struct btrfs_fs_info *fs_info = eb->fs_info;
        const bool mapped = !test_bit(EXTENT_BUFFER_UNMAPPED, &eb->bflags);
 
        /*
-        * For mapped eb, we're going to change the page private, which should
+        * For mapped eb, we're going to change the folio private, which should
         * be done under the private_lock.
         */
        if (mapped)
-               spin_lock(&page->mapping->private_lock);
+               spin_lock(&folio->mapping->private_lock);
 
-       if (!PagePrivate(page)) {
+       if (!folio_test_private(folio)) {
                if (mapped)
-                       spin_unlock(&page->mapping->private_lock);
+                       spin_unlock(&folio->mapping->private_lock);
                return;
        }
 
@@ -3100,66 +3145,58 @@ static void detach_extent_buffer_page(struct extent_buffer *eb, struct page *pag
                 * We do this since we'll remove the pages after we've
                 * removed the eb from the radix tree, so we could race
                 * and have this page now attached to the new eb.  So
-                * only clear page_private if it's still connected to
+                * only clear folio if it's still connected to
                 * this eb.
                 */
-               if (PagePrivate(page) &&
-                   page->private == (unsigned long)eb) {
+               if (folio_test_private(folio) && folio_get_private(folio) == eb) {
                        BUG_ON(test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags));
-                       BUG_ON(PageDirty(page));
-                       BUG_ON(PageWriteback(page));
-                       /*
-                        * We need to make sure we haven't be attached
-                        * to a new eb.
-                        */
-                       detach_page_private(page);
+                       BUG_ON(folio_test_dirty(folio));
+                       BUG_ON(folio_test_writeback(folio));
+                       /* We need to make sure we haven't be attached to a new eb. */
+                       folio_detach_private(folio);
                }
                if (mapped)
-                       spin_unlock(&page->mapping->private_lock);
+                       spin_unlock(&folio->mapping->private_lock);
                return;
        }
 
        /*
-        * For subpage, we can have dummy eb with page private.  In this case,
-        * we can directly detach the private as such page is only attached to
-        * one dummy eb, no sharing.
+        * For subpage, we can have dummy eb with folio private attached.  In
+        * this case, we can directly detach the private as such folio is only
+        * attached to one dummy eb, no sharing.
         */
        if (!mapped) {
-               btrfs_detach_subpage(fs_info, page);
+               btrfs_detach_subpage(fs_info, folio);
                return;
        }
 
-       btrfs_page_dec_eb_refs(fs_info, page);
+       btrfs_folio_dec_eb_refs(fs_info, folio);
 
        /*
-        * We can only detach the page private if there are no other ebs in the
+        * We can only detach the folio private if there are no other ebs in the
         * page range and no unfinished IO.
         */
-       if (!page_range_has_eb(fs_info, page))
-               btrfs_detach_subpage(fs_info, page);
+       if (!folio_range_has_eb(fs_info, folio))
+               btrfs_detach_subpage(fs_info, folio);
 
-       spin_unlock(&page->mapping->private_lock);
+       spin_unlock(&folio->mapping->private_lock);
 }
 
 /* Release all pages attached to the extent buffer */
 static void btrfs_release_extent_buffer_pages(struct extent_buffer *eb)
 {
-       int i;
-       int num_pages;
-
        ASSERT(!extent_buffer_under_io(eb));
 
-       num_pages = num_extent_pages(eb);
-       for (i = 0; i < num_pages; i++) {
-               struct page *page = eb->pages[i];
+       for (int i = 0; i < INLINE_EXTENT_BUFFER_PAGES; i++) {
+               struct folio *folio = eb->folios[i];
 
-               if (!page)
+               if (!folio)
                        continue;
 
-               detach_extent_buffer_page(eb, page);
+               detach_extent_buffer_folio(eb, folio);
 
-               /* One for when we allocated the page */
-               put_page(page);
+               /* One for when we allocated the folio. */
+               folio_put(folio);
        }
 }
 
@@ -3197,9 +3234,8 @@ __alloc_extent_buffer(struct btrfs_fs_info *fs_info, u64 start,
 
 struct extent_buffer *btrfs_clone_extent_buffer(const struct extent_buffer *src)
 {
-       int i;
        struct extent_buffer *new;
-       int num_pages = num_extent_pages(src);
+       int num_folios = num_extent_folios(src);
        int ret;
 
        new = __alloc_extent_buffer(src->fs_info, src->start, src->len);
@@ -3213,22 +3249,22 @@ struct extent_buffer *btrfs_clone_extent_buffer(const struct extent_buffer *src)
         */
        set_bit(EXTENT_BUFFER_UNMAPPED, &new->bflags);
 
-       ret = btrfs_alloc_page_array(num_pages, new->pages);
+       ret = alloc_eb_folio_array(new, 0);
        if (ret) {
                btrfs_release_extent_buffer(new);
                return NULL;
        }
 
-       for (i = 0; i < num_pages; i++) {
+       for (int i = 0; i < num_folios; i++) {
+               struct folio *folio = new->folios[i];
                int ret;
-               struct page *p = new->pages[i];
 
-               ret = attach_extent_buffer_page(new, p, NULL);
+               ret = attach_extent_buffer_folio(new, folio, NULL);
                if (ret < 0) {
                        btrfs_release_extent_buffer(new);
                        return NULL;
                }
-               WARN_ON(PageDirty(p));
+               WARN_ON(folio_test_dirty(folio));
        }
        copy_extent_buffer_full(new, src);
        set_extent_buffer_uptodate(new);
@@ -3240,23 +3276,20 @@ struct extent_buffer *__alloc_dummy_extent_buffer(struct btrfs_fs_info *fs_info,
                                                  u64 start, unsigned long len)
 {
        struct extent_buffer *eb;
-       int num_pages;
-       int i;
+       int num_folios = 0;
        int ret;
 
        eb = __alloc_extent_buffer(fs_info, start, len);
        if (!eb)
                return NULL;
 
-       num_pages = num_extent_pages(eb);
-       ret = btrfs_alloc_page_array(num_pages, eb->pages);
+       ret = alloc_eb_folio_array(eb, 0);
        if (ret)
                goto err;
 
-       for (i = 0; i < num_pages; i++) {
-               struct page *p = eb->pages[i];
-
-               ret = attach_extent_buffer_page(eb, p, NULL);
+       num_folios = num_extent_folios(eb);
+       for (int i = 0; i < num_folios; i++) {
+               ret = attach_extent_buffer_folio(eb, eb->folios[i], NULL);
                if (ret < 0)
                        goto err;
        }
@@ -3267,10 +3300,10 @@ struct extent_buffer *__alloc_dummy_extent_buffer(struct btrfs_fs_info *fs_info,
 
        return eb;
 err:
-       for (i = 0; i < num_pages; i++) {
-               if (eb->pages[i]) {
-                       detach_extent_buffer_page(eb, eb->pages[i]);
-                       __free_page(eb->pages[i]);
+       for (int i = 0; i < num_folios; i++) {
+               if (eb->folios[i]) {
+                       detach_extent_buffer_folio(eb, eb->folios[i]);
+                       __folio_put(eb->folios[i]);
                }
        }
        __free_extent_buffer(eb);
@@ -3319,20 +3352,14 @@ static void check_buffer_tree_ref(struct extent_buffer *eb)
        spin_unlock(&eb->refs_lock);
 }
 
-static void mark_extent_buffer_accessed(struct extent_buffer *eb,
-               struct page *accessed)
+static void mark_extent_buffer_accessed(struct extent_buffer *eb)
 {
-       int num_pages, i;
+       int num_folios= num_extent_folios(eb);
 
        check_buffer_tree_ref(eb);
 
-       num_pages = num_extent_pages(eb);
-       for (i = 0; i < num_pages; i++) {
-               struct page *p = eb->pages[i];
-
-               if (p != accessed)
-                       mark_page_accessed(p);
-       }
+       for (int i = 0; i < num_folios; i++)
+               folio_mark_accessed(eb->folios[i]);
 }
 
 struct extent_buffer *find_extent_buffer(struct btrfs_fs_info *fs_info,
@@ -3360,7 +3387,7 @@ struct extent_buffer *find_extent_buffer(struct btrfs_fs_info *fs_info,
                spin_lock(&eb->refs_lock);
                spin_unlock(&eb->refs_lock);
        }
-       mark_extent_buffer_accessed(eb, NULL);
+       mark_extent_buffer_accessed(eb);
        return eb;
 }
 
@@ -3409,6 +3436,7 @@ free_eb:
 static struct extent_buffer *grab_extent_buffer(
                struct btrfs_fs_info *fs_info, struct page *page)
 {
+       struct folio *folio = page_folio(page);
        struct extent_buffer *exists;
 
        /*
@@ -3420,21 +3448,21 @@ static struct extent_buffer *grab_extent_buffer(
                return NULL;
 
        /* Page not yet attached to an extent buffer */
-       if (!PagePrivate(page))
+       if (!folio_test_private(folio))
                return NULL;
 
        /*
         * We could have already allocated an eb for this page and attached one
         * so lets see if we can get a ref on the existing eb, and if we can we
         * know it's good and we can just return that one, else we know we can
-        * just overwrite page->private.
+        * just overwrite folio private.
         */
-       exists = (struct extent_buffer *)page->private;
+       exists = folio_get_private(folio);
        if (atomic_inc_not_zero(&exists->refs))
                return exists;
 
        WARN_ON(PageDirty(page));
-       detach_page_private(page);
+       folio_detach_private(folio);
        return NULL;
 }
 
@@ -3468,19 +3496,88 @@ static int check_eb_alignment(struct btrfs_fs_info *fs_info, u64 start)
        return 0;
 }
 
+
+/*
+ * Return 0 if eb->folios[i] is attached to btree inode successfully.
+ * Return >0 if there is already another extent buffer for the range,
+ * and @found_eb_ret would be updated.
+ * Return -EAGAIN if the filemap has an existing folio but with different size
+ * than @eb.
+ * The caller needs to free the existing folios and retry using the same order.
+ */
+static int attach_eb_folio_to_filemap(struct extent_buffer *eb, int i,
+                                     struct extent_buffer **found_eb_ret)
+{
+
+       struct btrfs_fs_info *fs_info = eb->fs_info;
+       struct address_space *mapping = fs_info->btree_inode->i_mapping;
+       const unsigned long index = eb->start >> PAGE_SHIFT;
+       struct folio *existing_folio;
+       int ret;
+
+       ASSERT(found_eb_ret);
+
+       /* Caller should ensure the folio exists. */
+       ASSERT(eb->folios[i]);
+
+retry:
+       ret = filemap_add_folio(mapping, eb->folios[i], index + i,
+                               GFP_NOFS | __GFP_NOFAIL);
+       if (!ret)
+               return 0;
+
+       existing_folio = filemap_lock_folio(mapping, index + i);
+       /* The page cache only exists for a very short time, just retry. */
+       if (IS_ERR(existing_folio))
+               goto retry;
+
+       /* For now, we should only have single-page folios for btree inode. */
+       ASSERT(folio_nr_pages(existing_folio) == 1);
+
+       if (folio_size(existing_folio) != folio_size(eb->folios[0])) {
+               folio_unlock(existing_folio);
+               folio_put(existing_folio);
+               return -EAGAIN;
+       }
+
+       if (fs_info->nodesize < PAGE_SIZE) {
+               /*
+                * We're going to reuse the existing page, can drop our page
+                * and subpage structure now.
+                */
+               __free_page(folio_page(eb->folios[i], 0));
+               eb->folios[i] = existing_folio;
+       } else {
+               struct extent_buffer *existing_eb;
+
+               existing_eb = grab_extent_buffer(fs_info,
+                                                folio_page(existing_folio, 0));
+               if (existing_eb) {
+                       /* The extent buffer still exists, we can use it directly. */
+                       *found_eb_ret = existing_eb;
+                       folio_unlock(existing_folio);
+                       folio_put(existing_folio);
+                       return 1;
+               }
+               /* The extent buffer no longer exists, we can reuse the folio. */
+               __free_page(folio_page(eb->folios[i], 0));
+               eb->folios[i] = existing_folio;
+       }
+       return 0;
+}
+
 struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info,
                                          u64 start, u64 owner_root, int level)
 {
        unsigned long len = fs_info->nodesize;
-       int num_pages;
-       int i;
-       unsigned long index = start >> PAGE_SHIFT;
+       int num_folios;
+       int attached = 0;
        struct extent_buffer *eb;
-       struct extent_buffer *exists = NULL;
-       struct page *p;
+       struct extent_buffer *existing_eb = NULL;
        struct address_space *mapping = fs_info->btree_inode->i_mapping;
        struct btrfs_subpage *prealloc = NULL;
        u64 lockdep_owner = owner_root;
+       bool page_contig = true;
        int uptodate = 1;
        int ret;
 
@@ -3515,10 +3612,8 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info,
 
        btrfs_set_buffer_lockdep_class(lockdep_owner, eb, level);
 
-       num_pages = num_extent_pages(eb);
-
        /*
-        * Preallocate page->private for subpage case, so that we won't
+        * Preallocate folio private for subpage case, so that we won't
         * allocate memory with private_lock nor page lock hold.
         *
         * The memory will be freed by attach_extent_buffer_page() or freed
@@ -3527,47 +3622,89 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info,
        if (fs_info->nodesize < PAGE_SIZE) {
                prealloc = btrfs_alloc_subpage(fs_info, BTRFS_SUBPAGE_METADATA);
                if (IS_ERR(prealloc)) {
-                       exists = ERR_CAST(prealloc);
-                       goto free_eb;
+                       ret = PTR_ERR(prealloc);
+                       goto out;
                }
        }
 
-       for (i = 0; i < num_pages; i++, index++) {
-               p = find_or_create_page(mapping, index, GFP_NOFS|__GFP_NOFAIL);
-               if (!p) {
-                       exists = ERR_PTR(-ENOMEM);
-                       btrfs_free_subpage(prealloc);
-                       goto free_eb;
+reallocate:
+       /* Allocate all pages first. */
+       ret = alloc_eb_folio_array(eb, __GFP_NOFAIL);
+       if (ret < 0) {
+               btrfs_free_subpage(prealloc);
+               goto out;
+       }
+
+       num_folios = num_extent_folios(eb);
+       /* Attach all pages to the filemap. */
+       for (int i = 0; i < num_folios; i++) {
+               struct folio *folio;
+
+               ret = attach_eb_folio_to_filemap(eb, i, &existing_eb);
+               if (ret > 0) {
+                       ASSERT(existing_eb);
+                       goto out;
                }
 
-               spin_lock(&mapping->private_lock);
-               exists = grab_extent_buffer(fs_info, p);
-               if (exists) {
-                       spin_unlock(&mapping->private_lock);
-                       unlock_page(p);
-                       put_page(p);
-                       mark_extent_buffer_accessed(exists, p);
-                       btrfs_free_subpage(prealloc);
-                       goto free_eb;
+               /*
+                * TODO: Special handling for a corner case where the order of
+                * folios mismatch between the new eb and filemap.
+                *
+                * This happens when:
+                *
+                * - the new eb is using higher order folio
+                *
+                * - the filemap is still using 0-order folios for the range
+                *   This can happen at the previous eb allocation, and we don't
+                *   have higher order folio for the call.
+                *
+                * - the existing eb has already been freed
+                *
+                * In this case, we have to free the existing folios first, and
+                * re-allocate using the same order.
+                * Thankfully this is not going to happen yet, as we're still
+                * using 0-order folios.
+                */
+               if (unlikely(ret == -EAGAIN)) {
+                       ASSERT(0);
+                       goto reallocate;
                }
+               attached++;
+
+               /*
+                * Only after attach_eb_folio_to_filemap(), eb->folios[] is
+                * reliable, as we may choose to reuse the existing page cache
+                * and free the allocated page.
+                */
+               folio = eb->folios[i];
+               spin_lock(&mapping->private_lock);
                /* Should not fail, as we have preallocated the memory */
-               ret = attach_extent_buffer_page(eb, p, prealloc);
+               ret = attach_extent_buffer_folio(eb, folio, prealloc);
                ASSERT(!ret);
                /*
                 * To inform we have extra eb under allocation, so that
-                * detach_extent_buffer_page() won't release the page private
+                * detach_extent_buffer_page() won't release the folio private
                 * when the eb hasn't yet been inserted into radix tree.
                 *
                 * The ref will be decreased when the eb released the page, in
                 * detach_extent_buffer_page().
                 * Thus needs no special handling in error path.
                 */
-               btrfs_page_inc_eb_refs(fs_info, p);
+               btrfs_folio_inc_eb_refs(fs_info, folio);
                spin_unlock(&mapping->private_lock);
 
-               WARN_ON(btrfs_page_test_dirty(fs_info, p, eb->start, eb->len));
-               eb->pages[i] = p;
-               if (!btrfs_page_test_uptodate(fs_info, p, eb->start, eb->len))
+               WARN_ON(btrfs_folio_test_dirty(fs_info, folio, eb->start, eb->len));
+
+               /*
+                * Check if the current page is physically contiguous with previous eb
+                * page.
+                * At this stage, either we allocated a large folio, thus @i
+                * would only be 0, or we fall back to per-page allocation.
+                */
+               if (i && folio_page(eb->folios[i - 1], 0) + 1 != folio_page(folio, 0))
+                       page_contig = false;
+
+               if (!btrfs_folio_test_uptodate(fs_info, folio, eb->start, eb->len))
                        uptodate = 0;
 
                /*
@@ -3580,12 +3717,13 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info,
        }
        if (uptodate)
                set_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags);
+       /* All pages are physically contiguous, can skip cross page handling. */
+       if (page_contig)
+               eb->addr = folio_address(eb->folios[0]) + offset_in_page(eb->start);
 again:
        ret = radix_tree_preload(GFP_NOFS);
-       if (ret) {
-               exists = ERR_PTR(ret);
-               goto free_eb;
-       }
+       if (ret)
+               goto out;
 
        spin_lock(&fs_info->buffer_lock);
        ret = radix_tree_insert(&fs_info->buffer_radix,
@@ -3593,9 +3731,10 @@ again:
        spin_unlock(&fs_info->buffer_lock);
        radix_tree_preload_end();
        if (ret == -EEXIST) {
-               exists = find_extent_buffer(fs_info, start);
-               if (exists)
-                       goto free_eb;
+               ret = 0;
+               existing_eb = find_extent_buffer(fs_info, start);
+               if (existing_eb)
+                       goto out;
                else
                        goto again;
        }
@@ -3608,19 +3747,46 @@ again:
         * btree_release_folio will correctly detect that a page belongs to a
         * live buffer and won't free them prematurely.
         */
-       for (i = 0; i < num_pages; i++)
-               unlock_page(eb->pages[i]);
+       for (int i = 0; i < num_folios; i++)
+               unlock_page(folio_page(eb->folios[i], 0));
        return eb;
 
-free_eb:
+out:
        WARN_ON(!atomic_dec_and_test(&eb->refs));
-       for (i = 0; i < num_pages; i++) {
-               if (eb->pages[i])
-                       unlock_page(eb->pages[i]);
+
+       /*
+        * Any attached folios need to be detached before we unlock them.  This
+        * is because when we're inserting our new folios into the mapping, and
+        * then attaching our eb to that folio.  If we fail to insert our folio
+        * we'll lookup the folio for that index, and grab that EB.  We do not
+        * want that to grab this eb, as we're getting ready to free it.  So we
+        * have to detach it first and then unlock it.
+        *
+        * We have to drop our reference and NULL it out here because in the
+        * subpage case detaching does a btrfs_folio_dec_eb_refs() for our eb.
+        * Below when we call btrfs_release_extent_buffer() we will call
+        * detach_extent_buffer_folio() on our remaining pages in the !subpage
+        * case.  If we left eb->folios[i] populated in the subpage case we'd
+        * double put our reference and be super sad.
+        */
+       for (int i = 0; i < attached; i++) {
+               ASSERT(eb->folios[i]);
+               detach_extent_buffer_folio(eb, eb->folios[i]);
+               unlock_page(folio_page(eb->folios[i], 0));
+               folio_put(eb->folios[i]);
+               eb->folios[i] = NULL;
        }
+       /*
+        * Now all pages of that extent buffer is unmapped, set UNMAPPED flag,
+        * so it can be cleaned up without utlizing page->mapping.
+        */
+       set_bit(EXTENT_BUFFER_UNMAPPED, &eb->bflags);
 
        btrfs_release_extent_buffer(eb);
-       return exists;
+       if (ret < 0)
+               return ERR_PTR(ret);
+       ASSERT(existing_eb);
+       return existing_eb;
 }
 
 static inline void btrfs_release_extent_buffer_rcu(struct rcu_head *head)
@@ -3712,31 +3878,30 @@ void free_extent_buffer_stale(struct extent_buffer *eb)
        release_extent_buffer(eb);
 }
 
-static void btree_clear_page_dirty(struct page *page)
+static void btree_clear_folio_dirty(struct folio *folio)
 {
-       ASSERT(PageDirty(page));
-       ASSERT(PageLocked(page));
-       clear_page_dirty_for_io(page);
-       xa_lock_irq(&page->mapping->i_pages);
-       if (!PageDirty(page))
-               __xa_clear_mark(&page->mapping->i_pages,
-                               page_index(page), PAGECACHE_TAG_DIRTY);
-       xa_unlock_irq(&page->mapping->i_pages);
+       ASSERT(folio_test_dirty(folio));
+       ASSERT(folio_test_locked(folio));
+       folio_clear_dirty_for_io(folio);
+       xa_lock_irq(&folio->mapping->i_pages);
+       if (!folio_test_dirty(folio))
+               __xa_clear_mark(&folio->mapping->i_pages,
+                               folio_index(folio), PAGECACHE_TAG_DIRTY);
+       xa_unlock_irq(&folio->mapping->i_pages);
 }
 
 static void clear_subpage_extent_buffer_dirty(const struct extent_buffer *eb)
 {
        struct btrfs_fs_info *fs_info = eb->fs_info;
-       struct page *page = eb->pages[0];
+       struct folio *folio = eb->folios[0];
        bool last;
 
-       /* btree_clear_page_dirty() needs page locked */
-       lock_page(page);
-       last = btrfs_subpage_clear_and_test_dirty(fs_info, page, eb->start,
-                                                 eb->len);
+       /* btree_clear_folio_dirty() needs page locked. */
+       folio_lock(folio);
+       last = btrfs_subpage_clear_and_test_dirty(fs_info, folio, eb->start, eb->len);
        if (last)
-               btree_clear_page_dirty(page);
-       unlock_page(page);
+               btree_clear_folio_dirty(folio);
+       folio_unlock(folio);
        WARN_ON(atomic_read(&eb->refs) == 0);
 }
 
@@ -3744,15 +3909,27 @@ void btrfs_clear_buffer_dirty(struct btrfs_trans_handle *trans,
                              struct extent_buffer *eb)
 {
        struct btrfs_fs_info *fs_info = eb->fs_info;
-       int i;
-       int num_pages;
-       struct page *page;
+       int num_folios;
 
        btrfs_assert_tree_write_locked(eb);
 
        if (trans && btrfs_header_generation(eb) != trans->transid)
                return;
 
+       /*
+        * Instead of clearing the dirty flag off of the buffer, mark it as
+        * EXTENT_BUFFER_ZONED_ZEROOUT. This allows us to preserve
+        * write-ordering in zoned mode, without the need to later re-dirty
+        * the extent_buffer.
+        *
+        * The actual zeroout of the buffer will happen later in
+        * btree_csum_one_bio.
+        */
+       if (btrfs_is_zoned(fs_info)) {
+               set_bit(EXTENT_BUFFER_ZONED_ZEROOUT, &eb->bflags);
+               return;
+       }
+
        if (!test_and_clear_bit(EXTENT_BUFFER_DIRTY, &eb->bflags))
                return;
 
@@ -3762,30 +3939,29 @@ void btrfs_clear_buffer_dirty(struct btrfs_trans_handle *trans,
        if (eb->fs_info->nodesize < PAGE_SIZE)
                return clear_subpage_extent_buffer_dirty(eb);
 
-       num_pages = num_extent_pages(eb);
+       num_folios = num_extent_folios(eb);
+       for (int i = 0; i < num_folios; i++) {
+               struct folio *folio = eb->folios[i];
 
-       for (i = 0; i < num_pages; i++) {
-               page = eb->pages[i];
-               if (!PageDirty(page))
+               if (!folio_test_dirty(folio))
                        continue;
-               lock_page(page);
-               btree_clear_page_dirty(page);
-               unlock_page(page);
+               folio_lock(folio);
+               btree_clear_folio_dirty(folio);
+               folio_unlock(folio);
        }
        WARN_ON(atomic_read(&eb->refs) == 0);
 }
 
 void set_extent_buffer_dirty(struct extent_buffer *eb)
 {
-       int i;
-       int num_pages;
+       int num_folios;
        bool was_dirty;
 
        check_buffer_tree_ref(eb);
 
        was_dirty = test_and_set_bit(EXTENT_BUFFER_DIRTY, &eb->bflags);
 
-       num_pages = num_extent_pages(eb);
+       num_folios = num_extent_folios(eb);
        WARN_ON(atomic_read(&eb->refs) == 0);
        WARN_ON(!test_bit(EXTENT_BUFFER_TREE_REF, &eb->bflags));
 
@@ -3804,34 +3980,32 @@ void set_extent_buffer_dirty(struct extent_buffer *eb)
                 * the above race.
                 */
                if (subpage)
-                       lock_page(eb->pages[0]);
-               for (i = 0; i < num_pages; i++)
-                       btrfs_page_set_dirty(eb->fs_info, eb->pages[i],
-                                            eb->start, eb->len);
+                       lock_page(folio_page(eb->folios[0], 0));
+               for (int i = 0; i < num_folios; i++)
+                       btrfs_folio_set_dirty(eb->fs_info, eb->folios[i],
+                                             eb->start, eb->len);
                if (subpage)
-                       unlock_page(eb->pages[0]);
+                       unlock_page(folio_page(eb->folios[0], 0));
                percpu_counter_add_batch(&eb->fs_info->dirty_metadata_bytes,
                                         eb->len,
                                         eb->fs_info->dirty_metadata_batch);
        }
 #ifdef CONFIG_BTRFS_DEBUG
-       for (i = 0; i < num_pages; i++)
-               ASSERT(PageDirty(eb->pages[i]));
+       for (int i = 0; i < num_folios; i++)
+               ASSERT(folio_test_dirty(eb->folios[i]));
 #endif
 }
 
 void clear_extent_buffer_uptodate(struct extent_buffer *eb)
 {
        struct btrfs_fs_info *fs_info = eb->fs_info;
-       struct page *page;
-       int num_pages;
-       int i;
+       int num_folios = num_extent_folios(eb);
 
        clear_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags);
-       num_pages = num_extent_pages(eb);
-       for (i = 0; i < num_pages; i++) {
-               page = eb->pages[i];
-               if (!page)
+       for (int i = 0; i < num_folios; i++) {
+               struct folio *folio = eb->folios[i];
+
+               if (!folio)
                        continue;
 
                /*
@@ -3839,44 +4013,40 @@ void clear_extent_buffer_uptodate(struct extent_buffer *eb)
                 * btrfs_is_subpage() can not handle cloned/dummy metadata.
                 */
                if (fs_info->nodesize >= PAGE_SIZE)
-                       ClearPageUptodate(page);
+                       folio_clear_uptodate(folio);
                else
-                       btrfs_subpage_clear_uptodate(fs_info, page, eb->start,
-                                                    eb->len);
+                       btrfs_subpage_clear_uptodate(fs_info, folio,
+                                                    eb->start, eb->len);
        }
 }
 
 void set_extent_buffer_uptodate(struct extent_buffer *eb)
 {
        struct btrfs_fs_info *fs_info = eb->fs_info;
-       struct page *page;
-       int num_pages;
-       int i;
+       int num_folios = num_extent_folios(eb);
 
        set_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags);
-       num_pages = num_extent_pages(eb);
-       for (i = 0; i < num_pages; i++) {
-               page = eb->pages[i];
+       for (int i = 0; i < num_folios; i++) {
+               struct folio *folio = eb->folios[i];
 
                /*
                 * This is special handling for metadata subpage, as regular
                 * btrfs_is_subpage() can not handle cloned/dummy metadata.
                 */
                if (fs_info->nodesize >= PAGE_SIZE)
-                       SetPageUptodate(page);
+                       folio_mark_uptodate(folio);
                else
-                       btrfs_subpage_set_uptodate(fs_info, page, eb->start,
-                                                  eb->len);
+                       btrfs_subpage_set_uptodate(fs_info, folio,
+                                                  eb->start, eb->len);
        }
 }
 
-static void extent_buffer_read_end_io(struct btrfs_bio *bbio)
+static void end_bbio_meta_read(struct btrfs_bio *bbio)
 {
        struct extent_buffer *eb = bbio->private;
        struct btrfs_fs_info *fs_info = eb->fs_info;
        bool uptodate = !bbio->bio.bi_status;
-       struct bvec_iter_all iter_all;
-       struct bio_vec *bvec;
+       struct folio_iter fi;
        u32 bio_offset = 0;
 
        eb->read_mirror = bbio->mirror_num;
@@ -3892,15 +4062,15 @@ static void extent_buffer_read_end_io(struct btrfs_bio *bbio)
                set_bit(EXTENT_BUFFER_READ_ERR, &eb->bflags);
        }
 
-       bio_for_each_segment_all(bvec, &bbio->bio, iter_all) {
+       bio_for_each_folio_all(fi, &bbio->bio) {
+               struct folio *folio = fi.folio;
                u64 start = eb->start + bio_offset;
-               struct page *page = bvec->bv_page;
-               u32 len = bvec->bv_len;
+               u32 len = fi.length;
 
                if (uptodate)
-                       btrfs_page_set_uptodate(fs_info, page, start, len);
+                       btrfs_folio_set_uptodate(fs_info, folio, start, len);
                else
-                       btrfs_page_clear_uptodate(fs_info, page, start, len);
+                       btrfs_folio_clear_uptodate(fs_info, folio, start, len);
 
                bio_offset += len;
        }
@@ -3916,8 +4086,8 @@ static void extent_buffer_read_end_io(struct btrfs_bio *bbio)
 int read_extent_buffer_pages(struct extent_buffer *eb, int wait, int mirror_num,
                             struct btrfs_tree_parent_check *check)
 {
-       int num_pages = num_extent_pages(eb), i;
        struct btrfs_bio *bbio;
+       bool ret;
 
        if (test_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags))
                return 0;
@@ -3941,17 +4111,24 @@ int read_extent_buffer_pages(struct extent_buffer *eb, int wait, int mirror_num,
 
        bbio = btrfs_bio_alloc(INLINE_EXTENT_BUFFER_PAGES,
                               REQ_OP_READ | REQ_META, eb->fs_info,
-                              extent_buffer_read_end_io, eb);
+                              end_bbio_meta_read, eb);
        bbio->bio.bi_iter.bi_sector = eb->start >> SECTOR_SHIFT;
        bbio->inode = BTRFS_I(eb->fs_info->btree_inode);
        bbio->file_offset = eb->start;
        memcpy(&bbio->parent_check, check, sizeof(*check));
        if (eb->fs_info->nodesize < PAGE_SIZE) {
-               __bio_add_page(&bbio->bio, eb->pages[0], eb->len,
-                              eb->start - page_offset(eb->pages[0]));
+               ret = bio_add_folio(&bbio->bio, eb->folios[0], eb->len,
+                                   eb->start - folio_pos(eb->folios[0]));
+               ASSERT(ret);
        } else {
-               for (i = 0; i < num_pages; i++)
-                       __bio_add_page(&bbio->bio, eb->pages[i], PAGE_SIZE, 0);
+               int num_folios = num_extent_folios(eb);
+
+               for (int i = 0; i < num_folios; i++) {
+                       struct folio *folio = eb->folios[i];
+
+                       ret = bio_add_folio(&bbio->bio, folio, folio_size(folio), 0);
+                       ASSERT(ret);
+               }
        }
        btrfs_submit_bio(bbio, mirror_num);
 
@@ -3998,29 +4175,33 @@ static inline int check_eb_range(const struct extent_buffer *eb,
 void read_extent_buffer(const struct extent_buffer *eb, void *dstv,
                        unsigned long start, unsigned long len)
 {
+       const int unit_size = folio_size(eb->folios[0]);
        size_t cur;
        size_t offset;
-       struct page *page;
-       char *kaddr;
        char *dst = (char *)dstv;
-       unsigned long i = get_eb_page_index(start);
+       unsigned long i = get_eb_folio_index(eb, start);
 
        if (check_eb_range(eb, start, len)) {
                /*
                 * Invalid range hit, reset the memory, so callers won't get
-                * some random garbage for their uninitialzed memory.
+                * some random garbage for their uninitialized memory.
                 */
                memset(dstv, 0, len);
                return;
        }
 
-       offset = get_eb_offset_in_page(eb, start);
+       if (eb->addr) {
+               memcpy(dstv, eb->addr + start, len);
+               return;
+       }
+
+       offset = get_eb_offset_in_folio(eb, start);
 
        while (len > 0) {
-               page = eb->pages[i];
+               char *kaddr;
 
-               cur = min(len, (PAGE_SIZE - offset));
-               kaddr = page_address(page);
+               cur = min(len, unit_size - offset);
+               kaddr = folio_address(eb->folios[i]);
                memcpy(dst, kaddr + offset, cur);
 
                dst += cur;
@@ -4034,24 +4215,29 @@ int read_extent_buffer_to_user_nofault(const struct extent_buffer *eb,
                                       void __user *dstv,
                                       unsigned long start, unsigned long len)
 {
+       const int unit_size = folio_size(eb->folios[0]);
        size_t cur;
        size_t offset;
-       struct page *page;
-       char *kaddr;
        char __user *dst = (char __user *)dstv;
-       unsigned long i = get_eb_page_index(start);
+       unsigned long i = get_eb_folio_index(eb, start);
        int ret = 0;
 
        WARN_ON(start > eb->len);
        WARN_ON(start + len > eb->start + eb->len);
 
-       offset = get_eb_offset_in_page(eb, start);
+       if (eb->addr) {
+               if (copy_to_user_nofault(dstv, eb->addr + start, len))
+                       ret = -EFAULT;
+               return ret;
+       }
+
+       offset = get_eb_offset_in_folio(eb, start);
 
        while (len > 0) {
-               page = eb->pages[i];
+               char *kaddr;
 
-               cur = min(len, (PAGE_SIZE - offset));
-               kaddr = page_address(page);
+               cur = min(len, unit_size - offset);
+               kaddr = folio_address(eb->folios[i]);
                if (copy_to_user_nofault(dst, kaddr + offset, cur)) {
                        ret = -EFAULT;
                        break;
@@ -4069,25 +4255,25 @@ int read_extent_buffer_to_user_nofault(const struct extent_buffer *eb,
 int memcmp_extent_buffer(const struct extent_buffer *eb, const void *ptrv,
                         unsigned long start, unsigned long len)
 {
+       const int unit_size = folio_size(eb->folios[0]);
        size_t cur;
        size_t offset;
-       struct page *page;
        char *kaddr;
        char *ptr = (char *)ptrv;
-       unsigned long i = get_eb_page_index(start);
+       unsigned long i = get_eb_folio_index(eb, start);
        int ret = 0;
 
        if (check_eb_range(eb, start, len))
                return -EINVAL;
 
-       offset = get_eb_offset_in_page(eb, start);
+       if (eb->addr)
+               return memcmp(ptrv, eb->addr + start, len);
 
-       while (len > 0) {
-               page = eb->pages[i];
+       offset = get_eb_offset_in_folio(eb, start);
 
-               cur = min(len, (PAGE_SIZE - offset));
-
-               kaddr = page_address(page);
+       while (len > 0) {
+               cur = min(len, unit_size - offset);
+               kaddr = folio_address(eb->folios[i]);
                ret = memcmp(ptr, kaddr + offset, cur);
                if (ret)
                        break;
@@ -4106,10 +4292,12 @@ int memcmp_extent_buffer(const struct extent_buffer *eb, const void *ptrv,
  * For regular sector size == PAGE_SIZE case, check if @page is uptodate.
  * For subpage case, check if the range covered by the eb has EXTENT_UPTODATE.
  */
-static void assert_eb_page_uptodate(const struct extent_buffer *eb,
-                                   struct page *page)
+static void assert_eb_folio_uptodate(const struct extent_buffer *eb, int i)
 {
        struct btrfs_fs_info *fs_info = eb->fs_info;
+       struct folio *folio = eb->folios[i];
+
+       ASSERT(folio);
 
        /*
         * If we are using the commit root we could potentially clear a page
@@ -4123,11 +4311,14 @@ static void assert_eb_page_uptodate(const struct extent_buffer *eb,
                return;
 
        if (fs_info->nodesize < PAGE_SIZE) {
-               if (WARN_ON(!btrfs_subpage_test_uptodate(fs_info, page,
+               struct folio *folio = eb->folios[0];
+
+               ASSERT(i == 0);
+               if (WARN_ON(!btrfs_subpage_test_uptodate(fs_info, folio,
                                                         eb->start, eb->len)))
-                       btrfs_subpage_dump_bitmap(fs_info, page, eb->start, eb->len);
+                       btrfs_subpage_dump_bitmap(fs_info, folio, eb->start, eb->len);
        } else {
-               WARN_ON(!PageUptodate(page));
+               WARN_ON(!folio_test_uptodate(folio));
        }
 }
 
@@ -4135,29 +4326,34 @@ static void __write_extent_buffer(const struct extent_buffer *eb,
                                  const void *srcv, unsigned long start,
                                  unsigned long len, bool use_memmove)
 {
+       const int unit_size = folio_size(eb->folios[0]);
        size_t cur;
        size_t offset;
-       struct page *page;
        char *kaddr;
        char *src = (char *)srcv;
-       unsigned long i = get_eb_page_index(start);
+       unsigned long i = get_eb_folio_index(eb, start);
        /* For unmapped (dummy) ebs, no need to check their uptodate status. */
        const bool check_uptodate = !test_bit(EXTENT_BUFFER_UNMAPPED, &eb->bflags);
 
-       WARN_ON(test_bit(EXTENT_BUFFER_NO_CHECK, &eb->bflags));
-
        if (check_eb_range(eb, start, len))
                return;
 
-       offset = get_eb_offset_in_page(eb, start);
+       if (eb->addr) {
+               if (use_memmove)
+                       memmove(eb->addr + start, srcv, len);
+               else
+                       memcpy(eb->addr + start, srcv, len);
+               return;
+       }
+
+       offset = get_eb_offset_in_folio(eb, start);
 
        while (len > 0) {
-               page = eb->pages[i];
                if (check_uptodate)
-                       assert_eb_page_uptodate(eb, page);
+                       assert_eb_folio_uptodate(eb, i);
 
-               cur = min(len, PAGE_SIZE - offset);
-               kaddr = page_address(page);
+               cur = min(len, unit_size - offset);
+               kaddr = folio_address(eb->folios[i]);
                if (use_memmove)
                        memmove(kaddr + offset, src, cur);
                else
@@ -4179,16 +4375,21 @@ void write_extent_buffer(const struct extent_buffer *eb, const void *srcv,
 static void memset_extent_buffer(const struct extent_buffer *eb, int c,
                                 unsigned long start, unsigned long len)
 {
+       const int unit_size = folio_size(eb->folios[0]);
        unsigned long cur = start;
 
+       if (eb->addr) {
+               memset(eb->addr + start, c, len);
+               return;
+       }
+
        while (cur < start + len) {
-               unsigned long index = get_eb_page_index(cur);
-               unsigned int offset = get_eb_offset_in_page(eb, cur);
-               unsigned int cur_len = min(start + len - cur, PAGE_SIZE - offset);
-               struct page *page = eb->pages[index];
+               unsigned long index = get_eb_folio_index(eb, cur);
+               unsigned int offset = get_eb_offset_in_folio(eb, cur);
+               unsigned int cur_len = min(start + len - cur, unit_size - offset);
 
-               assert_eb_page_uptodate(eb, page);
-               memset(page_address(page) + offset, c, cur_len);
+               assert_eb_folio_uptodate(eb, index);
+               memset(folio_address(eb->folios[index]) + offset, c, cur_len);
 
                cur += cur_len;
        }
@@ -4205,15 +4406,16 @@ void memzero_extent_buffer(const struct extent_buffer *eb, unsigned long start,
 void copy_extent_buffer_full(const struct extent_buffer *dst,
                             const struct extent_buffer *src)
 {
+       const int unit_size = folio_size(src->folios[0]);
        unsigned long cur = 0;
 
        ASSERT(dst->len == src->len);
 
        while (cur < src->len) {
-               unsigned long index = get_eb_page_index(cur);
-               unsigned long offset = get_eb_offset_in_page(src, cur);
-               unsigned long cur_len = min(src->len, PAGE_SIZE - offset);
-               void *addr = page_address(src->pages[index]) + offset;
+               unsigned long index = get_eb_folio_index(src, cur);
+               unsigned long offset = get_eb_offset_in_folio(src, cur);
+               unsigned long cur_len = min(src->len, unit_size - offset);
+               void *addr = folio_address(src->folios[index]) + offset;
 
                write_extent_buffer(dst, addr, cur, cur_len);
 
@@ -4226,12 +4428,12 @@ void copy_extent_buffer(const struct extent_buffer *dst,
                        unsigned long dst_offset, unsigned long src_offset,
                        unsigned long len)
 {
+       const int unit_size = folio_size(dst->folios[0]);
        u64 dst_len = dst->len;
        size_t cur;
        size_t offset;
-       struct page *page;
        char *kaddr;
-       unsigned long i = get_eb_page_index(dst_offset);
+       unsigned long i = get_eb_folio_index(dst, dst_offset);
 
        if (check_eb_range(dst, dst_offset, len) ||
            check_eb_range(src, src_offset, len))
@@ -4239,15 +4441,14 @@ void copy_extent_buffer(const struct extent_buffer *dst,
 
        WARN_ON(src->len != dst_len);
 
-       offset = get_eb_offset_in_page(dst, dst_offset);
+       offset = get_eb_offset_in_folio(dst, dst_offset);
 
        while (len > 0) {
-               page = dst->pages[i];
-               assert_eb_page_uptodate(dst, page);
+               assert_eb_folio_uptodate(dst, i);
 
-               cur = min(len, (unsigned long)(PAGE_SIZE - offset));
+               cur = min(len, (unsigned long)(unit_size - offset));
 
-               kaddr = page_address(page);
+               kaddr = folio_address(dst->folios[i]);
                read_extent_buffer(src, kaddr + offset, src_offset, cur);
 
                src_offset += cur;
@@ -4258,22 +4459,22 @@ void copy_extent_buffer(const struct extent_buffer *dst,
 }
 
 /*
- * Calculate the page and offset of the byte containing the given bit number.
+ * Calculate the folio and offset of the byte containing the given bit number.
  *
  * @eb:           the extent buffer
  * @start:        offset of the bitmap item in the extent buffer
  * @nr:           bit number
- * @page_index:   return index of the page in the extent buffer that contains
+ * @folio_index:  return index of the folio in the extent buffer that contains
  *                the given bit number
- * @page_offset:  return offset into the page given by page_index
+ * @folio_offset: return offset into the folio given by folio_index
  *
  * This helper hides the ugliness of finding the byte in an extent buffer which
  * contains a given bit.
  */
 static inline void eb_bitmap_offset(const struct extent_buffer *eb,
                                    unsigned long start, unsigned long nr,
-                                   unsigned long *page_index,
-                                   size_t *page_offset)
+                                   unsigned long *folio_index,
+                                   size_t *folio_offset)
 {
        size_t byte_offset = BIT_BYTE(nr);
        size_t offset;
@@ -4283,10 +4484,10 @@ static inline void eb_bitmap_offset(const struct extent_buffer *eb,
         * the bitmap item in the extent buffer + the offset of the byte in the
         * bitmap item.
         */
-       offset = start + offset_in_page(eb->start) + byte_offset;
+       offset = start + offset_in_folio(eb->folios[0], eb->start) + byte_offset;
 
-       *page_index = offset >> PAGE_SHIFT;
-       *page_offset = offset_in_page(offset);
+       *folio_index = offset >> folio_shift(eb->folios[0]);
+       *folio_offset = offset_in_folio(eb->folios[0], offset);
 }
 
 /*
@@ -4299,25 +4500,23 @@ static inline void eb_bitmap_offset(const struct extent_buffer *eb,
 int extent_buffer_test_bit(const struct extent_buffer *eb, unsigned long start,
                           unsigned long nr)
 {
-       u8 *kaddr;
-       struct page *page;
        unsigned long i;
        size_t offset;
+       u8 *kaddr;
 
        eb_bitmap_offset(eb, start, nr, &i, &offset);
-       page = eb->pages[i];
-       assert_eb_page_uptodate(eb, page);
-       kaddr = page_address(page);
+       assert_eb_folio_uptodate(eb, i);
+       kaddr = folio_address(eb->folios[i]);
        return 1U & (kaddr[offset] >> (nr & (BITS_PER_BYTE - 1)));
 }
 
 static u8 *extent_buffer_get_byte(const struct extent_buffer *eb, unsigned long bytenr)
 {
-       unsigned long index = get_eb_page_index(bytenr);
+       unsigned long index = get_eb_folio_index(eb, bytenr);
 
        if (check_eb_range(eb, bytenr, 1))
                return NULL;
-       return page_address(eb->pages[index]) + get_eb_offset_in_page(eb, bytenr);
+       return folio_address(eb->folios[index]) + get_eb_offset_in_folio(eb, bytenr);
 }
 
 /*
@@ -4402,19 +4601,30 @@ void memcpy_extent_buffer(const struct extent_buffer *dst,
                          unsigned long dst_offset, unsigned long src_offset,
                          unsigned long len)
 {
+       const int unit_size = folio_size(dst->folios[0]);
        unsigned long cur_off = 0;
 
        if (check_eb_range(dst, dst_offset, len) ||
            check_eb_range(dst, src_offset, len))
                return;
 
+       if (dst->addr) {
+               const bool use_memmove = areas_overlap(src_offset, dst_offset, len);
+
+               if (use_memmove)
+                       memmove(dst->addr + dst_offset, dst->addr + src_offset, len);
+               else
+                       memcpy(dst->addr + dst_offset, dst->addr + src_offset, len);
+               return;
+       }
+
        while (cur_off < len) {
                unsigned long cur_src = cur_off + src_offset;
-               unsigned long pg_index = get_eb_page_index(cur_src);
-               unsigned long pg_off = get_eb_offset_in_page(dst, cur_src);
+               unsigned long folio_index = get_eb_folio_index(dst, cur_src);
+               unsigned long folio_off = get_eb_offset_in_folio(dst, cur_src);
                unsigned long cur_len = min(src_offset + len - cur_src,
-                                           PAGE_SIZE - pg_off);
-               void *src_addr = page_address(dst->pages[pg_index]) + pg_off;
+                                           unit_size - folio_off);
+               void *src_addr = folio_address(dst->folios[folio_index]) + folio_off;
                const bool use_memmove = areas_overlap(src_offset + cur_off,
                                                       dst_offset + cur_off, cur_len);
 
@@ -4440,24 +4650,29 @@ void memmove_extent_buffer(const struct extent_buffer *dst,
                return;
        }
 
+       if (dst->addr) {
+               memmove(dst->addr + dst_offset, dst->addr + src_offset, len);
+               return;
+       }
+
        while (len > 0) {
                unsigned long src_i;
                size_t cur;
-               size_t dst_off_in_page;
-               size_t src_off_in_page;
+               size_t dst_off_in_folio;
+               size_t src_off_in_folio;
                void *src_addr;
                bool use_memmove;
 
-               src_i = get_eb_page_index(src_end);
+               src_i = get_eb_folio_index(dst, src_end);
 
-               dst_off_in_page = get_eb_offset_in_page(dst, dst_end);
-               src_off_in_page = get_eb_offset_in_page(dst, src_end);
+               dst_off_in_folio = get_eb_offset_in_folio(dst, dst_end);
+               src_off_in_folio = get_eb_offset_in_folio(dst, src_end);
 
-               cur = min_t(unsigned long, len, src_off_in_page + 1);
-               cur = min(cur, dst_off_in_page + 1);
+               cur = min_t(unsigned long, len, src_off_in_folio + 1);
+               cur = min(cur, dst_off_in_folio + 1);
 
-               src_addr = page_address(dst->pages[src_i]) + src_off_in_page -
-                                       cur + 1;
+               src_addr = folio_address(dst->folios[src_i]) + src_off_in_folio -
+                                        cur + 1;
                use_memmove = areas_overlap(src_end - cur + 1, dst_end - cur + 1,
                                            cur);
 
@@ -4519,7 +4734,7 @@ static int try_release_subpage_extent_buffer(struct page *page)
                struct extent_buffer *eb = NULL;
 
                /*
-                * Unlike try_release_extent_buffer() which uses page->private
+                * Unlike try_release_extent_buffer() which uses folio private
                 * to grab buffer, for subpage case we rely on radix tree, thus
                 * we need to ensure radix tree consistency.
                 *
@@ -4559,17 +4774,17 @@ static int try_release_subpage_extent_buffer(struct page *page)
 
                /*
                 * Here we don't care about the return value, we will always
-                * check the page private at the end.  And
+                * check the folio private at the end.  And
                 * release_extent_buffer() will release the refs_lock.
                 */
                release_extent_buffer(eb);
        }
        /*
-        * Finally to check if we have cleared page private, as if we have
-        * released all ebs in the page, the page private should be cleared now.
+        * Finally to check if we have cleared folio private, as if we have
+        * released all ebs in the page, the folio private should be cleared now.
         */
        spin_lock(&page->mapping->private_lock);
-       if (!PagePrivate(page))
+       if (!folio_test_private(page_folio(page)))
                ret = 1;
        else
                ret = 0;
@@ -4580,22 +4795,23 @@ static int try_release_subpage_extent_buffer(struct page *page)
 
 int try_release_extent_buffer(struct page *page)
 {
+       struct folio *folio = page_folio(page);
        struct extent_buffer *eb;
 
        if (btrfs_sb(page->mapping->host->i_sb)->nodesize < PAGE_SIZE)
                return try_release_subpage_extent_buffer(page);
 
        /*
-        * We need to make sure nobody is changing page->private, as we rely on
-        * page->private as the pointer to extent buffer.
+        * We need to make sure nobody is changing folio private, as we rely on
+        * folio private as the pointer to extent buffer.
         */
        spin_lock(&page->mapping->private_lock);
-       if (!PagePrivate(page)) {
+       if (!folio_test_private(folio)) {
                spin_unlock(&page->mapping->private_lock);
                return 1;
        }
 
-       eb = (struct extent_buffer *)page->private;
+       eb = folio_get_private(folio);
        BUG_ON(!eb);
 
        /*