]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ext4: make regular file's buffered write path support large folios
authorZhang Yi <yi.zhang@huawei.com>
Mon, 12 May 2025 06:33:13 +0000 (14:33 +0800)
committerTheodore Ts'o <tytso@mit.edu>
Tue, 20 May 2025 14:31:12 +0000 (10:31 -0400)
The current buffered write path in ext4 can only allocate and handle
folios of PAGE_SIZE size. To support larger folios, modify
ext4_da_write_begin() and ext4_write_begin() to allocate higher-order
folios, and trim the write length if it exceeds the folio size.
Additionally, in ext4_da_do_write_end(), use offset_in_folio() instead
of PAGE_SIZE.

Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Link: https://patch.msgid.link/20250512063319.3539411-3-yi.zhang@huaweicloud.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
fs/ext4/inode.c

index ca1f7a0dd8f8a4708ca76e8fd70b2528567a6864..fb0de28cefc229c9df3895703a0dce26147adabc 100644 (file)
@@ -1066,7 +1066,7 @@ int ext4_block_write_begin(handle_t *handle, struct folio *folio,
                           loff_t pos, unsigned len,
                           get_block_t *get_block)
 {
-       unsigned from = pos & (PAGE_SIZE - 1);
+       unsigned int from = offset_in_folio(folio, pos);
        unsigned to = from + len;
        struct inode *inode = folio->mapping->host;
        unsigned block_start, block_end;
@@ -1080,8 +1080,7 @@ int ext4_block_write_begin(handle_t *handle, struct folio *folio,
        bool should_journal_data = ext4_should_journal_data(inode);
 
        BUG_ON(!folio_test_locked(folio));
-       BUG_ON(from > PAGE_SIZE);
-       BUG_ON(to > PAGE_SIZE);
+       BUG_ON(to > folio_size(folio));
        BUG_ON(from > to);
 
        head = folio_buffers(folio);
@@ -1191,6 +1190,7 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping,
        struct folio *folio;
        pgoff_t index;
        unsigned from, to;
+       fgf_t fgp = FGP_WRITEBEGIN;
 
        ret = ext4_emergency_state(inode->i_sb);
        if (unlikely(ret))
@@ -1203,8 +1203,6 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping,
         */
        needed_blocks = ext4_writepage_trans_blocks(inode) + 1;
        index = pos >> PAGE_SHIFT;
-       from = pos & (PAGE_SIZE - 1);
-       to = from + len;
 
        if (ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) {
                ret = ext4_try_to_write_inline_data(mapping, inode, pos, len,
@@ -1223,10 +1221,18 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping,
         * the folio (if needed) without using GFP_NOFS.
         */
 retry_grab:
-       folio = __filemap_get_folio(mapping, index, FGP_WRITEBEGIN,
-                                       mapping_gfp_mask(mapping));
+       fgp |= fgf_set_order(len);
+       folio = __filemap_get_folio(mapping, index, fgp,
+                                   mapping_gfp_mask(mapping));
        if (IS_ERR(folio))
                return PTR_ERR(folio);
+
+       if (pos + len > folio_pos(folio) + folio_size(folio))
+               len = folio_pos(folio) + folio_size(folio) - pos;
+
+       from = offset_in_folio(folio, pos);
+       to = from + len;
+
        /*
         * The same as page allocation, we prealloc buffer heads before
         * starting the handle.
@@ -2965,6 +2971,7 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping,
        struct folio *folio;
        pgoff_t index;
        struct inode *inode = mapping->host;
+       fgf_t fgp = FGP_WRITEBEGIN;
 
        ret = ext4_emergency_state(inode->i_sb);
        if (unlikely(ret))
@@ -2990,11 +2997,15 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping,
        }
 
 retry:
-       folio = __filemap_get_folio(mapping, index, FGP_WRITEBEGIN,
-                       mapping_gfp_mask(mapping));
+       fgp |= fgf_set_order(len);
+       folio = __filemap_get_folio(mapping, index, fgp,
+                                   mapping_gfp_mask(mapping));
        if (IS_ERR(folio))
                return PTR_ERR(folio);
 
+       if (pos + len > folio_pos(folio) + folio_size(folio))
+               len = folio_pos(folio) + folio_size(folio) - pos;
+
        ret = ext4_block_write_begin(NULL, folio, pos, len,
                                     ext4_da_get_block_prep);
        if (ret < 0) {
@@ -3083,7 +3094,7 @@ static int ext4_da_do_write_end(struct address_space *mapping,
                unsigned long end;
 
                i_size_write(inode, new_i_size);
-               end = (new_i_size - 1) & (PAGE_SIZE - 1);
+               end = offset_in_folio(folio, new_i_size - 1);
                if (copied && ext4_da_should_update_i_disksize(folio, end)) {
                        ext4_update_i_disksize(inode, new_i_size);
                        disksize_changed = true;