2023-03-24 18:04:18

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH v2 00/29] Convert most of ext4 to folios

On top of next-20230321, this converts most of ext4 to use folios instead
of pages. It does not enable large folios although it fixes some places
that will need to be fixed before they can be enabled for ext4. It does
not convert mballoc to use folios. write_begin() and write_end() still
take a page parameter instead of a folio.

It does convert a lot of code away from the page APIs that we're trying
to remove. It does remove a lot of calls to compound_head(). I'd like
to see it land in 6.4.

v2:
Address all the feedback I received on v1. At least I hope I did.

Matthew Wilcox (Oracle) (29):
fs: Add FGP_WRITEBEGIN
fscrypt: Add some folio helper functions
ext4: Convert ext4_bio_write_page() to use a folio
ext4: Convert ext4_finish_bio() to use folios
ext4: Turn mpage_process_page() into mpage_process_folio()
ext4: Convert mpage_submit_page() to mpage_submit_folio()
ext4: Convert mpage_page_done() to mpage_folio_done()
ext4: Convert ext4_bio_write_page() to ext4_bio_write_folio()
ext4: Convert ext4_readpage_inline() to take a folio
ext4: Convert ext4_convert_inline_data_to_extent() to use a folio
ext4: Convert ext4_try_to_write_inline_data() to use a folio
ext4: Convert ext4_da_convert_inline_data_to_extent() to use a folio
ext4: Convert ext4_da_write_inline_data_begin() to use a folio
ext4: Convert ext4_read_inline_page() to ext4_read_inline_folio()
ext4: Convert ext4_write_inline_data_end() to use a folio
ext4: Convert ext4_write_begin() to use a folio
ext4: Convert ext4_write_end() to use a folio
ext4: Use a folio in ext4_journalled_write_end()
ext4: Convert ext4_journalled_zero_new_buffers() to use a folio
ext4: Convert __ext4_block_zero_page_range() to use a folio
ext4: Convert ext4_page_nomap_can_writeout to
ext4_folio_nomap_can_writeout
ext4: Use a folio in ext4_da_write_begin()
ext4: Convert ext4_mpage_readpages() to work on folios
ext4: Convert ext4_block_write_begin() to take a folio
ext4: Use a folio in ext4_page_mkwrite()
ext4: Use a folio iterator in __read_end_io()
ext4: Convert mext_page_mkuptodate() to take a folio
ext4: Convert pagecache_read() to use a folio
ext4: Use a folio in ext4_read_merkle_tree_page

block/bio.c | 1 +
fs/ext4/ext4.h | 9 +-
fs/ext4/inline.c | 171 ++++++++++----------
fs/ext4/inode.c | 312 +++++++++++++++++++------------------
fs/ext4/move_extent.c | 33 ++--
fs/ext4/page-io.c | 98 ++++++------
fs/ext4/readpage.c | 72 ++++-----
fs/ext4/verity.c | 30 ++--
fs/iomap/buffered-io.c | 2 +-
fs/netfs/buffered_read.c | 3 +-
fs/nfs/file.c | 12 +-
include/linux/fscrypt.h | 21 +++
include/linux/page-flags.h | 5 -
include/linux/pagemap.h | 2 +
mm/folio-compat.c | 4 +-
15 files changed, 387 insertions(+), 388 deletions(-)

--
2.39.2


2023-03-24 18:04:18

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH v2 13/29] ext4: Convert ext4_da_write_inline_data_begin() to use a folio

Saves a number of calls to compound_head().

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/ext4/inline.c | 20 +++++++++-----------
1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 45d74274d822..2fa6c51baef9 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -909,10 +909,9 @@ int ext4_da_write_inline_data_begin(struct address_space *mapping,
{
int ret;
handle_t *handle;
- struct page *page;
+ struct folio *folio;
struct ext4_iloc iloc;
int retries = 0;
- unsigned int flags;

ret = ext4_get_inode_loc(inode, &iloc);
if (ret)
@@ -944,10 +943,9 @@ int ext4_da_write_inline_data_begin(struct address_space *mapping,
* We cannot recurse into the filesystem as the transaction
* is already started.
*/
- flags = memalloc_nofs_save();
- page = grab_cache_page_write_begin(mapping, 0);
- memalloc_nofs_restore(flags);
- if (!page) {
+ folio = __filemap_get_folio(mapping, 0, FGP_WRITEBEGIN | FGP_NOFS,
+ mapping_gfp_mask(mapping));
+ if (!folio) {
ret = -ENOMEM;
goto out_journal;
}
@@ -958,8 +956,8 @@ int ext4_da_write_inline_data_begin(struct address_space *mapping,
goto out_release_page;
}

- if (!PageUptodate(page)) {
- ret = ext4_read_inline_page(inode, page);
+ if (!folio_test_uptodate(folio)) {
+ ret = ext4_read_inline_page(inode, &folio->page);
if (ret < 0)
goto out_release_page;
}
@@ -969,13 +967,13 @@ int ext4_da_write_inline_data_begin(struct address_space *mapping,
goto out_release_page;

up_read(&EXT4_I(inode)->xattr_sem);
- *pagep = page;
+ *pagep = &folio->page;
brelse(iloc.bh);
return 1;
out_release_page:
up_read(&EXT4_I(inode)->xattr_sem);
- unlock_page(page);
- put_page(page);
+ folio_unlock(folio);
+ folio_put(folio);
out_journal:
ext4_journal_stop(handle);
out:
--
2.39.2

2023-03-24 18:04:18

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH v2 20/29] ext4: Convert __ext4_block_zero_page_range() to use a folio

Use folio APIs throughout. Saves many calls to compound_head().

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
Reviewed-by: Ritesh Harjani (IBM) <[email protected]>
---
fs/ext4/inode.c | 27 +++++++++++++++------------
1 file changed, 15 insertions(+), 12 deletions(-)

diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 92418efe1afe..a81540a6e8c6 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3669,23 +3669,26 @@ static int __ext4_block_zero_page_range(handle_t *handle,
ext4_lblk_t iblock;
struct inode *inode = mapping->host;
struct buffer_head *bh;
- struct page *page;
+ struct folio *folio;
int err = 0;

- page = find_or_create_page(mapping, from >> PAGE_SHIFT,
- mapping_gfp_constraint(mapping, ~__GFP_FS));
- if (!page)
+ folio = __filemap_get_folio(mapping, from >> PAGE_SHIFT,
+ FGP_LOCK | FGP_ACCESSED | FGP_CREAT,
+ mapping_gfp_constraint(mapping, ~__GFP_FS));
+ if (!folio)
return -ENOMEM;

blocksize = inode->i_sb->s_blocksize;

iblock = index << (PAGE_SHIFT - inode->i_sb->s_blocksize_bits);

- if (!page_has_buffers(page))
- create_empty_buffers(page, blocksize, 0);
+ bh = folio_buffers(folio);
+ if (!bh) {
+ create_empty_buffers(&folio->page, blocksize, 0);
+ bh = folio_buffers(folio);
+ }

/* Find the buffer that contains "offset" */
- bh = page_buffers(page);
pos = blocksize;
while (offset >= pos) {
bh = bh->b_this_page;
@@ -3707,7 +3710,7 @@ static int __ext4_block_zero_page_range(handle_t *handle,
}

/* Ok, it's mapped. Make sure it's up-to-date */
- if (PageUptodate(page))
+ if (folio_test_uptodate(folio))
set_buffer_uptodate(bh);

if (!buffer_uptodate(bh)) {
@@ -3717,7 +3720,7 @@ static int __ext4_block_zero_page_range(handle_t *handle,
if (fscrypt_inode_uses_fs_layer_crypto(inode)) {
/* We expect the key to be set. */
BUG_ON(!fscrypt_has_encryption_key(inode));
- err = fscrypt_decrypt_pagecache_blocks(page_folio(page),
+ err = fscrypt_decrypt_pagecache_blocks(folio,
blocksize,
bh_offset(bh));
if (err) {
@@ -3733,7 +3736,7 @@ static int __ext4_block_zero_page_range(handle_t *handle,
if (err)
goto unlock;
}
- zero_user(page, offset, length);
+ folio_zero_range(folio, offset, length);
BUFFER_TRACE(bh, "zeroed end of block");

if (ext4_should_journal_data(inode)) {
@@ -3747,8 +3750,8 @@ static int __ext4_block_zero_page_range(handle_t *handle,
}

unlock:
- unlock_page(page);
- put_page(page);
+ folio_unlock(folio);
+ folio_put(folio);
return err;
}

--
2.39.2

2023-03-24 18:04:19

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH v2 19/29] ext4: Convert ext4_journalled_zero_new_buffers() to use a folio

Remove a call to compound_head().

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/ext4/inode.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 172b4ca43981..92418efe1afe 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1359,24 +1359,24 @@ static int ext4_write_end(struct file *file,
*/
static void ext4_journalled_zero_new_buffers(handle_t *handle,
struct inode *inode,
- struct page *page,
+ struct folio *folio,
unsigned from, unsigned to)
{
unsigned int block_start = 0, block_end;
struct buffer_head *head, *bh;

- bh = head = page_buffers(page);
+ bh = head = folio_buffers(folio);
do {
block_end = block_start + bh->b_size;
if (buffer_new(bh)) {
if (block_end > from && block_start < to) {
- if (!PageUptodate(page)) {
+ if (!folio_test_uptodate(folio)) {
unsigned start, size;

start = max(from, block_start);
size = min(to, block_end) - start;

- zero_user(page, start, size);
+ folio_zero_range(folio, start, size);
write_end_fn(handle, inode, bh);
}
clear_buffer_new(bh);
@@ -1413,10 +1413,11 @@ static int ext4_journalled_write_end(struct file *file,

if (unlikely(copied < len) && !folio_test_uptodate(folio)) {
copied = 0;
- ext4_journalled_zero_new_buffers(handle, inode, page, from, to);
+ ext4_journalled_zero_new_buffers(handle, inode, folio,
+ from, to);
} else {
if (unlikely(copied < len))
- ext4_journalled_zero_new_buffers(handle, inode, page,
+ ext4_journalled_zero_new_buffers(handle, inode, folio,
from + copied, to);
ret = ext4_walk_page_buffers(handle, inode,
folio_buffers(folio),
--
2.39.2

2023-03-24 18:04:25

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH v2 23/29] ext4: Convert ext4_mpage_readpages() to work on folios

This definitely doesn't include support for large folios; there
are all kinds of assumptions about the number of buffers attached
to a folio. But it does remove several calls to compound_head().

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/ext4/ext4.h | 2 +-
fs/ext4/inode.c | 7 +++---
fs/ext4/readpage.c | 58 ++++++++++++++++++++++------------------------
3 files changed, 32 insertions(+), 35 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 1de5d838996a..57357ef1659b 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -3647,7 +3647,7 @@ static inline void ext4_set_de_type(struct super_block *sb,

/* readpages.c */
extern int ext4_mpage_readpages(struct inode *inode,
- struct readahead_control *rac, struct page *page);
+ struct readahead_control *rac, struct folio *folio);
extern int __init ext4_init_post_read_processing(void);
extern void ext4_exit_post_read_processing(void);

diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index c88ce6f43c01..116acc5fe00c 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3154,17 +3154,16 @@ static sector_t ext4_bmap(struct address_space *mapping, sector_t block)

static int ext4_read_folio(struct file *file, struct folio *folio)
{
- struct page *page = &folio->page;
int ret = -EAGAIN;
- struct inode *inode = page->mapping->host;
+ struct inode *inode = folio->mapping->host;

- trace_ext4_readpage(page);
+ trace_ext4_readpage(&folio->page);

if (ext4_has_inline_data(inode))
ret = ext4_readpage_inline(inode, folio);

if (ret == -EAGAIN)
- return ext4_mpage_readpages(inode, NULL, page);
+ return ext4_mpage_readpages(inode, NULL, folio);

return ret;
}
diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c
index c61dc8a7c014..fed4ddb652df 100644
--- a/fs/ext4/readpage.c
+++ b/fs/ext4/readpage.c
@@ -218,7 +218,7 @@ static inline loff_t ext4_readpage_limit(struct inode *inode)
}

int ext4_mpage_readpages(struct inode *inode,
- struct readahead_control *rac, struct page *page)
+ struct readahead_control *rac, struct folio *folio)
{
struct bio *bio = NULL;
sector_t last_block_in_bio = 0;
@@ -247,16 +247,15 @@ int ext4_mpage_readpages(struct inode *inode,
int fully_mapped = 1;
unsigned first_hole = blocks_per_page;

- if (rac) {
- page = readahead_page(rac);
- prefetchw(&page->flags);
- }
+ if (rac)
+ folio = readahead_folio(rac);
+ prefetchw(&folio->flags);

- if (page_has_buffers(page))
+ if (folio_buffers(folio))
goto confused;

block_in_file = next_block =
- (sector_t)page->index << (PAGE_SHIFT - blkbits);
+ (sector_t)folio->index << (PAGE_SHIFT - blkbits);
last_block = block_in_file + nr_pages * blocks_per_page;
last_block_in_file = (ext4_readpage_limit(inode) +
blocksize - 1) >> blkbits;
@@ -290,7 +289,7 @@ int ext4_mpage_readpages(struct inode *inode,

/*
* Then do more ext4_map_blocks() calls until we are
- * done with this page.
+ * done with this folio.
*/
while (page_block < blocks_per_page) {
if (block_in_file < last_block) {
@@ -299,10 +298,10 @@ int ext4_mpage_readpages(struct inode *inode,

if (ext4_map_blocks(NULL, inode, &map, 0) < 0) {
set_error_page:
- SetPageError(page);
- zero_user_segment(page, 0,
- PAGE_SIZE);
- unlock_page(page);
+ folio_set_error(folio);
+ folio_zero_segment(folio, 0,
+ folio_size(folio));
+ folio_unlock(folio);
goto next_page;
}
}
@@ -333,22 +332,22 @@ int ext4_mpage_readpages(struct inode *inode,
}
}
if (first_hole != blocks_per_page) {
- zero_user_segment(page, first_hole << blkbits,
- PAGE_SIZE);
+ folio_zero_segment(folio, first_hole << blkbits,
+ folio_size(folio));
if (first_hole == 0) {
- if (ext4_need_verity(inode, page->index) &&
- !fsverity_verify_page(page))
+ if (ext4_need_verity(inode, folio->index) &&
+ !fsverity_verify_page(&folio->page))
goto set_error_page;
- SetPageUptodate(page);
- unlock_page(page);
- goto next_page;
+ folio_mark_uptodate(folio);
+ folio_unlock(folio);
+ continue;
}
} else if (fully_mapped) {
- SetPageMappedToDisk(page);
+ folio_set_mappedtodisk(folio);
}

/*
- * This page will go to BIO. Do we need to send this
+ * This folio will go to BIO. Do we need to send this
* BIO off first?
*/
if (bio && (last_block_in_bio != blocks[0] - 1 ||
@@ -366,7 +365,7 @@ int ext4_mpage_readpages(struct inode *inode,
REQ_OP_READ, GFP_KERNEL);
fscrypt_set_bio_crypt_ctx(bio, inode, next_block,
GFP_KERNEL);
- ext4_set_bio_post_read_ctx(bio, inode, page->index);
+ ext4_set_bio_post_read_ctx(bio, inode, folio->index);
bio->bi_iter.bi_sector = blocks[0] << (blkbits - 9);
bio->bi_end_io = mpage_end_io;
if (rac)
@@ -374,7 +373,7 @@ int ext4_mpage_readpages(struct inode *inode,
}

length = first_hole << blkbits;
- if (bio_add_page(bio, page, length, 0) < length)
+ if (!bio_add_folio(bio, folio, length, 0))
goto submit_and_realloc;

if (((map.m_flags & EXT4_MAP_BOUNDARY) &&
@@ -384,19 +383,18 @@ int ext4_mpage_readpages(struct inode *inode,
bio = NULL;
} else
last_block_in_bio = blocks[blocks_per_page - 1];
- goto next_page;
+ continue;
confused:
if (bio) {
submit_bio(bio);
bio = NULL;
}
- if (!PageUptodate(page))
- block_read_full_folio(page_folio(page), ext4_get_block);
+ if (!folio_test_uptodate(folio))
+ block_read_full_folio(folio, ext4_get_block);
else
- unlock_page(page);
- next_page:
- if (rac)
- put_page(page);
+ folio_unlock(folio);
+next_page:
+ ; /* A label shall be followed by a statement until C23 */
}
if (bio)
submit_bio(bio);
--
2.39.2

2023-03-24 18:04:29

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH v2 24/29] ext4: Convert ext4_block_write_begin() to take a folio

All the callers now have a folio, so pass that in and operate on folios.
Removes four calls to compound_head().

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
Reviewed-by: Ritesh Harjani (IBM) <[email protected]>
---
fs/ext4/inode.c | 42 +++++++++++++++++++++---------------------
1 file changed, 21 insertions(+), 21 deletions(-)

diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 116acc5fe00c..cf2b89a819cb 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1030,12 +1030,12 @@ int do_journal_get_write_access(handle_t *handle, struct inode *inode,
}

#ifdef CONFIG_FS_ENCRYPTION
-static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len,
+static int ext4_block_write_begin(struct folio *folio, loff_t pos, unsigned len,
get_block_t *get_block)
{
unsigned from = pos & (PAGE_SIZE - 1);
unsigned to = from + len;
- struct inode *inode = page->mapping->host;
+ struct inode *inode = folio->mapping->host;
unsigned block_start, block_end;
sector_t block;
int err = 0;
@@ -1045,22 +1045,24 @@ static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len,
int nr_wait = 0;
int i;

- BUG_ON(!PageLocked(page));
+ BUG_ON(!folio_test_locked(folio));
BUG_ON(from > PAGE_SIZE);
BUG_ON(to > PAGE_SIZE);
BUG_ON(from > to);

- if (!page_has_buffers(page))
- create_empty_buffers(page, blocksize, 0);
- head = page_buffers(page);
+ head = folio_buffers(folio);
+ if (!head) {
+ create_empty_buffers(&folio->page, blocksize, 0);
+ head = folio_buffers(folio);
+ }
bbits = ilog2(blocksize);
- block = (sector_t)page->index << (PAGE_SHIFT - bbits);
+ block = (sector_t)folio->index << (PAGE_SHIFT - bbits);

for (bh = head, block_start = 0; bh != head || !block_start;
block++, block_start = block_end, bh = bh->b_this_page) {
block_end = block_start + blocksize;
if (block_end <= from || block_start >= to) {
- if (PageUptodate(page)) {
+ if (folio_test_uptodate(folio)) {
set_buffer_uptodate(bh);
}
continue;
@@ -1073,19 +1075,20 @@ static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len,
if (err)
break;
if (buffer_new(bh)) {
- if (PageUptodate(page)) {
+ if (folio_test_uptodate(folio)) {
clear_buffer_new(bh);
set_buffer_uptodate(bh);
mark_buffer_dirty(bh);
continue;
}
if (block_end > to || block_start < from)
- zero_user_segments(page, to, block_end,
- block_start, from);
+ folio_zero_segments(folio, to,
+ block_end,
+ block_start, from);
continue;
}
}
- if (PageUptodate(page)) {
+ if (folio_test_uptodate(folio)) {
set_buffer_uptodate(bh);
continue;
}
@@ -1105,14 +1108,13 @@ static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len,
err = -EIO;
}
if (unlikely(err)) {
- page_zero_new_buffers(page, from, to);
+ page_zero_new_buffers(&folio->page, from, to);
} else if (fscrypt_inode_uses_fs_layer_crypto(inode)) {
for (i = 0; i < nr_wait; i++) {
int err2;

- err2 = fscrypt_decrypt_pagecache_blocks(page_folio(page),
- blocksize,
- bh_offset(wait[i]));
+ err2 = fscrypt_decrypt_pagecache_blocks(folio,
+ blocksize, bh_offset(wait[i]));
if (err2) {
clear_buffer_uptodate(wait[i]);
err = err2;
@@ -1206,11 +1208,10 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping,

#ifdef CONFIG_FS_ENCRYPTION
if (ext4_should_dioread_nolock(inode))
- ret = ext4_block_write_begin(&folio->page, pos, len,
+ ret = ext4_block_write_begin(folio, pos, len,
ext4_get_block_unwritten);
else
- ret = ext4_block_write_begin(&folio->page, pos, len,
- ext4_get_block);
+ ret = ext4_block_write_begin(folio, pos, len, ext4_get_block);
#else
if (ext4_should_dioread_nolock(inode))
ret = __block_write_begin(&folio->page, pos, len,
@@ -2938,8 +2939,7 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping,
folio_wait_stable(folio);

#ifdef CONFIG_FS_ENCRYPTION
- ret = ext4_block_write_begin(&folio->page, pos, len,
- ext4_da_get_block_prep);
+ ret = ext4_block_write_begin(folio, pos, len, ext4_da_get_block_prep);
#else
ret = __block_write_begin(&folio->page, pos, len, ext4_da_get_block_prep);
#endif
--
2.39.2

2023-03-24 18:04:35

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH v2 14/29] ext4: Convert ext4_read_inline_page() to ext4_read_inline_folio()

All callers now have a folio, so pass it and use it. The folio may
be large, although I doubt we'll want to use a large folio for an
inline file.

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
Reviewed-by: Theodore Ts'o <[email protected]>
---
fs/ext4/inline.c | 27 ++++++++++++++-------------
1 file changed, 14 insertions(+), 13 deletions(-)

diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 2fa6c51baef9..4c819b6c70c1 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -467,16 +467,16 @@ static int ext4_destroy_inline_data_nolock(handle_t *handle,
return error;
}

-static int ext4_read_inline_page(struct inode *inode, struct page *page)
+static int ext4_read_inline_folio(struct inode *inode, struct folio *folio)
{
void *kaddr;
int ret = 0;
size_t len;
struct ext4_iloc iloc;

- BUG_ON(!PageLocked(page));
+ BUG_ON(!folio_test_locked(folio));
BUG_ON(!ext4_has_inline_data(inode));
- BUG_ON(page->index);
+ BUG_ON(folio->index);

if (!EXT4_I(inode)->i_inline_off) {
ext4_warning(inode->i_sb, "inode %lu doesn't have inline data.",
@@ -489,12 +489,13 @@ static int ext4_read_inline_page(struct inode *inode, struct page *page)
goto out;

len = min_t(size_t, ext4_get_inline_size(inode), i_size_read(inode));
- kaddr = kmap_atomic(page);
+ BUG_ON(len > PAGE_SIZE);
+ kaddr = kmap_local_folio(folio, 0);
ret = ext4_read_inline_data(inode, kaddr, len, &iloc);
- flush_dcache_page(page);
- kunmap_atomic(kaddr);
- zero_user_segment(page, len, PAGE_SIZE);
- SetPageUptodate(page);
+ flush_dcache_folio(folio);
+ kunmap_local(kaddr);
+ folio_zero_segment(folio, len, folio_size(folio));
+ folio_mark_uptodate(folio);
brelse(iloc.bh);

out:
@@ -516,7 +517,7 @@ int ext4_readpage_inline(struct inode *inode, struct folio *folio)
* So for all the other pages, just set them uptodate.
*/
if (!folio->index)
- ret = ext4_read_inline_page(inode, &folio->page);
+ ret = ext4_read_inline_folio(inode, folio);
else if (!folio_test_uptodate(folio)) {
folio_zero_segment(folio, 0, folio_size(folio));
folio_mark_uptodate(folio);
@@ -581,7 +582,7 @@ static int ext4_convert_inline_data_to_extent(struct address_space *mapping,
from = 0;
to = ext4_get_inline_size(inode);
if (!folio_test_uptodate(folio)) {
- ret = ext4_read_inline_page(inode, &folio->page);
+ ret = ext4_read_inline_folio(inode, folio);
if (ret < 0)
goto out;
}
@@ -707,7 +708,7 @@ int ext4_try_to_write_inline_data(struct address_space *mapping,
}

if (!folio_test_uptodate(folio)) {
- ret = ext4_read_inline_page(inode, &folio->page);
+ ret = ext4_read_inline_folio(inode, folio);
if (ret < 0) {
folio_unlock(folio);
folio_put(folio);
@@ -864,7 +865,7 @@ static int ext4_da_convert_inline_data_to_extent(struct address_space *mapping,
inline_size = ext4_get_inline_size(inode);

if (!folio_test_uptodate(folio)) {
- ret = ext4_read_inline_page(inode, &folio->page);
+ ret = ext4_read_inline_folio(inode, folio);
if (ret < 0)
goto out;
}
@@ -957,7 +958,7 @@ int ext4_da_write_inline_data_begin(struct address_space *mapping,
}

if (!folio_test_uptodate(folio)) {
- ret = ext4_read_inline_page(inode, &folio->page);
+ ret = ext4_read_inline_folio(inode, folio);
if (ret < 0)
goto out_release_page;
}
--
2.39.2

2023-03-24 18:04:41

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH v2 15/29] ext4: Convert ext4_write_inline_data_end() to use a folio

Convert the incoming page to a folio so that we call compound_head()
only once instead of seven times.

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
Reviewed-by: Theodore Ts'o <[email protected]>
---
fs/ext4/inline.c | 29 +++++++++++++++--------------
1 file changed, 15 insertions(+), 14 deletions(-)

diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 4c819b6c70c1..b9fb1177fff6 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -732,20 +732,21 @@ int ext4_try_to_write_inline_data(struct address_space *mapping,
int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len,
unsigned copied, struct page *page)
{
+ struct folio *folio = page_folio(page);
handle_t *handle = ext4_journal_current_handle();
int no_expand;
void *kaddr;
struct ext4_iloc iloc;
int ret = 0, ret2;

- if (unlikely(copied < len) && !PageUptodate(page))
+ if (unlikely(copied < len) && !folio_test_uptodate(folio))
copied = 0;

if (likely(copied)) {
ret = ext4_get_inode_loc(inode, &iloc);
if (ret) {
- unlock_page(page);
- put_page(page);
+ folio_unlock(folio);
+ folio_put(folio);
ext4_std_error(inode->i_sb, ret);
goto out;
}
@@ -759,30 +760,30 @@ int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len,
*/
(void) ext4_find_inline_data_nolock(inode);

- kaddr = kmap_atomic(page);
+ kaddr = kmap_local_folio(folio, 0);
ext4_write_inline_data(inode, &iloc, kaddr, pos, copied);
- kunmap_atomic(kaddr);
- SetPageUptodate(page);
- /* clear page dirty so that writepages wouldn't work for us. */
- ClearPageDirty(page);
+ kunmap_local(kaddr);
+ folio_mark_uptodate(folio);
+ /* clear dirty flag so that writepages wouldn't work for us. */
+ folio_clear_dirty(folio);

ext4_write_unlock_xattr(inode, &no_expand);
brelse(iloc.bh);

/*
- * It's important to update i_size while still holding page
+ * It's important to update i_size while still holding folio
* lock: page writeout could otherwise come in and zero
* beyond i_size.
*/
ext4_update_inode_size(inode, pos + copied);
}
- unlock_page(page);
- put_page(page);
+ folio_unlock(folio);
+ folio_put(folio);

/*
- * Don't mark the inode dirty under page lock. First, it unnecessarily
- * makes the holding time of page lock longer. Second, it forces lock
- * ordering of page lock and transaction start for journaling
+ * Don't mark the inode dirty under folio lock. First, it unnecessarily
+ * makes the holding time of folio lock longer. Second, it forces lock
+ * ordering of folio lock and transaction start for journaling
* filesystems.
*/
if (likely(copied))
--
2.39.2

2023-03-24 18:05:14

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH v2 26/29] ext4: Use a folio iterator in __read_end_io()

Iterate once per folio, not once per page.

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/ext4/readpage.c | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c
index fed4ddb652df..6f46823fba61 100644
--- a/fs/ext4/readpage.c
+++ b/fs/ext4/readpage.c
@@ -68,18 +68,16 @@ struct bio_post_read_ctx {

static void __read_end_io(struct bio *bio)
{
- struct page *page;
- struct bio_vec *bv;
- struct bvec_iter_all iter_all;
+ struct folio_iter fi;

- bio_for_each_segment_all(bv, bio, iter_all) {
- page = bv->bv_page;
+ bio_for_each_folio_all(fi, bio) {
+ struct folio *folio = fi.folio;

if (bio->bi_status)
- ClearPageUptodate(page);
+ folio_clear_uptodate(folio);
else
- SetPageUptodate(page);
- unlock_page(page);
+ folio_mark_uptodate(folio);
+ folio_unlock(folio);
}
if (bio->bi_private)
mempool_free(bio->bi_private, bio_post_read_ctx_pool);
--
2.39.2

2023-03-24 18:06:15

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH v2 28/29] ext4: Convert pagecache_read() to use a folio

Use the folio API and support folios of arbitrary sizes.

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/ext4/verity.c | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/fs/ext4/verity.c b/fs/ext4/verity.c
index e4da1704438e..afe847c967a4 100644
--- a/fs/ext4/verity.c
+++ b/fs/ext4/verity.c
@@ -42,18 +42,16 @@ static int pagecache_read(struct inode *inode, void *buf, size_t count,
loff_t pos)
{
while (count) {
- size_t n = min_t(size_t, count,
- PAGE_SIZE - offset_in_page(pos));
- struct page *page;
+ struct folio *folio;
+ size_t n;

- page = read_mapping_page(inode->i_mapping, pos >> PAGE_SHIFT,
+ folio = read_mapping_folio(inode->i_mapping, pos >> PAGE_SHIFT,
NULL);
- if (IS_ERR(page))
- return PTR_ERR(page);
-
- memcpy_from_page(buf, page, offset_in_page(pos), n);
+ if (IS_ERR(folio))
+ return PTR_ERR(folio);

- put_page(page);
+ n = memcpy_from_file_folio(buf, folio, pos, count);
+ folio_put(folio);

buf += n;
pos += n;
--
2.39.2

2023-03-24 18:06:18

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH v2 27/29] ext4: Convert mext_page_mkuptodate() to take a folio

Use a folio throughout. Does not support large folios due to
an array sized for MAX_BUF_PER_PAGE, but it does remove a few
calls to compound_head().

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/ext4/move_extent.c | 28 +++++++++++++++-------------
1 file changed, 15 insertions(+), 13 deletions(-)

diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
index a84a794fed56..b5af2fc03b2f 100644
--- a/fs/ext4/move_extent.c
+++ b/fs/ext4/move_extent.c
@@ -168,25 +168,27 @@ mext_folio_double_lock(struct inode *inode1, struct inode *inode2,

/* Force page buffers uptodate w/o dropping page's lock */
static int
-mext_page_mkuptodate(struct page *page, unsigned from, unsigned to)
+mext_page_mkuptodate(struct folio *folio, unsigned from, unsigned to)
{
- struct inode *inode = page->mapping->host;
+ struct inode *inode = folio->mapping->host;
sector_t block;
struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE];
unsigned int blocksize, block_start, block_end;
int i, err, nr = 0, partial = 0;
- BUG_ON(!PageLocked(page));
- BUG_ON(PageWriteback(page));
+ BUG_ON(!folio_test_locked(folio));
+ BUG_ON(folio_test_writeback(folio));

- if (PageUptodate(page))
+ if (folio_test_uptodate(folio))
return 0;

blocksize = i_blocksize(inode);
- if (!page_has_buffers(page))
- create_empty_buffers(page, blocksize, 0);
+ head = folio_buffers(folio);
+ if (!head) {
+ create_empty_buffers(&folio->page, blocksize, 0);
+ head = folio_buffers(folio);
+ }

- head = page_buffers(page);
- block = (sector_t)page->index << (PAGE_SHIFT - inode->i_blkbits);
+ block = (sector_t)folio->index << (PAGE_SHIFT - inode->i_blkbits);
for (bh = head, block_start = 0; bh != head || !block_start;
block++, block_start = block_end, bh = bh->b_this_page) {
block_end = block_start + blocksize;
@@ -200,11 +202,11 @@ mext_page_mkuptodate(struct page *page, unsigned from, unsigned to)
if (!buffer_mapped(bh)) {
err = ext4_get_block(inode, block, bh, 0);
if (err) {
- SetPageError(page);
+ folio_set_error(folio);
return err;
}
if (!buffer_mapped(bh)) {
- zero_user(page, block_start, blocksize);
+ folio_zero_range(folio, block_start, blocksize);
set_buffer_uptodate(bh);
continue;
}
@@ -226,7 +228,7 @@ mext_page_mkuptodate(struct page *page, unsigned from, unsigned to)
}
out:
if (!partial)
- SetPageUptodate(page);
+ folio_mark_uptodate(folio);
return 0;
}

@@ -354,7 +356,7 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
goto unlock_folios;
}
data_copy:
- *err = mext_page_mkuptodate(&folio[0]->page, from, from + replaced_size);
+ *err = mext_page_mkuptodate(folio[0], from, from + replaced_size);
if (*err)
goto unlock_folios;

--
2.39.2

2023-03-24 18:06:33

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH v2 29/29] ext4: Use a folio in ext4_read_merkle_tree_page

This is an implementation of fsverity_operations read_merkle_tree_page,
so it must still return the precise page asked for, but we can use the
folio API to reduce the number of conversions between folios & pages.

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/ext4/verity.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/fs/ext4/verity.c b/fs/ext4/verity.c
index afe847c967a4..3b01247066dd 100644
--- a/fs/ext4/verity.c
+++ b/fs/ext4/verity.c
@@ -361,21 +361,21 @@ static struct page *ext4_read_merkle_tree_page(struct inode *inode,
pgoff_t index,
unsigned long num_ra_pages)
{
- struct page *page;
+ struct folio *folio;

index += ext4_verity_metadata_pos(inode) >> PAGE_SHIFT;

- page = find_get_page_flags(inode->i_mapping, index, FGP_ACCESSED);
- if (!page || !PageUptodate(page)) {
+ folio = __filemap_get_folio(inode->i_mapping, index, FGP_ACCESSED, 0);
+ if (!folio || !folio_test_uptodate(folio)) {
DEFINE_READAHEAD(ractl, NULL, NULL, inode->i_mapping, index);

- if (page)
- put_page(page);
+ if (folio)
+ folio_put(folio);
else if (num_ra_pages > 1)
page_cache_ra_unbounded(&ractl, num_ra_pages, 0);
- page = read_mapping_page(inode->i_mapping, index, NULL);
+ folio = read_mapping_folio(inode->i_mapping, index, NULL);
}
- return page;
+ return folio_file_page(folio, index);
}

static int ext4_write_merkle_tree_block(struct inode *inode, const void *buf,
--
2.39.2

2023-03-24 18:07:41

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH v2 25/29] ext4: Use a folio in ext4_page_mkwrite()

Convert to the folio API, saving a few calls to compound_head().

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/ext4/inode.c | 42 ++++++++++++++++++++----------------------
1 file changed, 20 insertions(+), 22 deletions(-)

diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index cf2b89a819cb..f0ebf211983d 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -6075,7 +6075,7 @@ static int ext4_bh_unmapped(handle_t *handle, struct inode *inode,
vm_fault_t ext4_page_mkwrite(struct vm_fault *vmf)
{
struct vm_area_struct *vma = vmf->vma;
- struct page *page = vmf->page;
+ struct folio *folio = page_folio(vmf->page);
loff_t size;
unsigned long len;
int err;
@@ -6119,19 +6119,18 @@ vm_fault_t ext4_page_mkwrite(struct vm_fault *vmf)
goto out_ret;
}

- lock_page(page);
+ folio_lock(folio);
size = i_size_read(inode);
/* Page got truncated from under us? */
- if (page->mapping != mapping || page_offset(page) > size) {
- unlock_page(page);
+ if (folio->mapping != mapping || folio_pos(folio) > size) {
+ folio_unlock(folio);
ret = VM_FAULT_NOPAGE;
goto out;
}

- if (page->index == size >> PAGE_SHIFT)
- len = size & ~PAGE_MASK;
- else
- len = PAGE_SIZE;
+ len = folio_size(folio);
+ if (folio_pos(folio) + len > size)
+ len = size - folio_pos(folio);
/*
* Return if we have all the buffers mapped. This avoids the need to do
* journal_start/journal_stop which can block and take a long time
@@ -6139,17 +6138,17 @@ vm_fault_t ext4_page_mkwrite(struct vm_fault *vmf)
* This cannot be done for data journalling, as we have to add the
* inode to the transaction's list to writeprotect pages on commit.
*/
- if (page_has_buffers(page)) {
- if (!ext4_walk_page_buffers(NULL, inode, page_buffers(page),
+ if (folio_buffers(folio)) {
+ if (!ext4_walk_page_buffers(NULL, inode, folio_buffers(folio),
0, len, NULL,
ext4_bh_unmapped)) {
/* Wait so that we don't change page under IO */
- wait_for_stable_page(page);
+ folio_wait_stable(folio);
ret = VM_FAULT_LOCKED;
goto out;
}
}
- unlock_page(page);
+ folio_unlock(folio);
/* OK, we need to fill the hole... */
if (ext4_should_dioread_nolock(inode))
get_block = ext4_get_block_unwritten;
@@ -6170,26 +6169,25 @@ vm_fault_t ext4_page_mkwrite(struct vm_fault *vmf)
if (!ext4_should_journal_data(inode)) {
err = block_page_mkwrite(vma, vmf, get_block);
} else {
- lock_page(page);
+ folio_lock(folio);
size = i_size_read(inode);
/* Page got truncated from under us? */
- if (page->mapping != mapping || page_offset(page) > size) {
+ if (folio->mapping != mapping || folio_pos(folio) > size) {
ret = VM_FAULT_NOPAGE;
goto out_error;
}

- if (page->index == size >> PAGE_SHIFT)
- len = size & ~PAGE_MASK;
- else
- len = PAGE_SIZE;
+ len = folio_size(folio);
+ if (folio_pos(folio) + len > size)
+ len = size - folio_pos(folio);

- err = __block_write_begin(page, 0, len, ext4_get_block);
+ err = __block_write_begin(&folio->page, 0, len, ext4_get_block);
if (!err) {
ret = VM_FAULT_SIGBUS;
- if (ext4_journal_page_buffers(handle, page, len))
+ if (ext4_journal_page_buffers(handle, &folio->page, len))
goto out_error;
} else {
- unlock_page(page);
+ folio_unlock(folio);
}
}
ext4_journal_stop(handle);
@@ -6202,7 +6200,7 @@ vm_fault_t ext4_page_mkwrite(struct vm_fault *vmf)
sb_end_pagefault(inode->i_sb);
return ret;
out_error:
- unlock_page(page);
+ folio_unlock(folio);
ext4_journal_stop(handle);
goto out;
}
--
2.39.2

2023-03-24 18:07:41

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH v2 03/29] ext4: Convert ext4_bio_write_page() to use a folio

Remove several calls to compound_head() and the last caller of
set_page_writeback_keepwrite(), so remove the wrapper too.

Also export bio_add_folio() as this is the first caller from a module.

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
Reviewed-by: Ritesh Harjani (IBM) <[email protected]>
Reviewed-by: Theodore Ts'o <[email protected]>
---
block/bio.c | 1 +
fs/ext4/page-io.c | 58 ++++++++++++++++++--------------------
include/linux/page-flags.h | 5 ----
3 files changed, 28 insertions(+), 36 deletions(-)

diff --git a/block/bio.c b/block/bio.c
index fc98c1c723ca..798cc4cf3bd2 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -1159,6 +1159,7 @@ bool bio_add_folio(struct bio *bio, struct folio *folio, size_t len,
return false;
return bio_add_page(bio, &folio->page, len, off) > 0;
}
+EXPORT_SYMBOL(bio_add_folio);

void __bio_release_pages(struct bio *bio, bool mark_dirty)
{
diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c
index 8703fd732abb..7850d2cb2e08 100644
--- a/fs/ext4/page-io.c
+++ b/fs/ext4/page-io.c
@@ -409,12 +409,10 @@ static void io_submit_init_bio(struct ext4_io_submit *io,

static void io_submit_add_bh(struct ext4_io_submit *io,
struct inode *inode,
- struct page *pagecache_page,
- struct page *bounce_page,
+ struct folio *folio,
+ struct folio *io_folio,
struct buffer_head *bh)
{
- int ret;
-
if (io->io_bio && (bh->b_blocknr != io->io_next_block ||
!fscrypt_mergeable_bio_bh(io->io_bio, bh))) {
submit_and_retry:
@@ -422,11 +420,9 @@ static void io_submit_add_bh(struct ext4_io_submit *io,
}
if (io->io_bio == NULL)
io_submit_init_bio(io, bh);
- ret = bio_add_page(io->io_bio, bounce_page ?: pagecache_page,
- bh->b_size, bh_offset(bh));
- if (ret != bh->b_size)
+ if (!bio_add_folio(io->io_bio, io_folio, bh->b_size, bh_offset(bh)))
goto submit_and_retry;
- wbc_account_cgroup_owner(io->io_wbc, pagecache_page, bh->b_size);
+ wbc_account_cgroup_owner(io->io_wbc, &folio->page, bh->b_size);
io->io_next_block++;
}

@@ -434,8 +430,9 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
struct page *page,
int len)
{
- struct page *bounce_page = NULL;
- struct inode *inode = page->mapping->host;
+ struct folio *folio = page_folio(page);
+ struct folio *io_folio = folio;
+ struct inode *inode = folio->mapping->host;
unsigned block_start;
struct buffer_head *bh, *head;
int ret = 0;
@@ -443,30 +440,30 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
struct writeback_control *wbc = io->io_wbc;
bool keep_towrite = false;

- BUG_ON(!PageLocked(page));
- BUG_ON(PageWriteback(page));
+ BUG_ON(!folio_test_locked(folio));
+ BUG_ON(folio_test_writeback(folio));

- ClearPageError(page);
+ folio_clear_error(folio);

/*
* Comments copied from block_write_full_page:
*
- * The page straddles i_size. It must be zeroed out on each and every
+ * The folio straddles i_size. It must be zeroed out on each and every
* writepage invocation because it may be mmapped. "A file is mapped
* in multiples of the page size. For a file that is not a multiple of
* the page size, the remaining memory is zeroed when mapped, and
* writes to that region are not written out to the file."
*/
- if (len < PAGE_SIZE)
- zero_user_segment(page, len, PAGE_SIZE);
+ if (len < folio_size(folio))
+ folio_zero_segment(folio, len, folio_size(folio));
/*
* In the first loop we prepare and mark buffers to submit. We have to
- * mark all buffers in the page before submitting so that
- * end_page_writeback() cannot be called from ext4_end_bio() when IO
+ * mark all buffers in the folio before submitting so that
+ * folio_end_writeback() cannot be called from ext4_end_bio() when IO
* on the first buffer finishes and we are still working on submitting
* the second buffer.
*/
- bh = head = page_buffers(page);
+ bh = head = folio_buffers(folio);
do {
block_start = bh_offset(bh);
if (block_start >= len) {
@@ -481,14 +478,14 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
clear_buffer_dirty(bh);
/*
* Keeping dirty some buffer we cannot write? Make sure
- * to redirty the page and keep TOWRITE tag so that
- * racing WB_SYNC_ALL writeback does not skip the page.
+ * to redirty the folio and keep TOWRITE tag so that
+ * racing WB_SYNC_ALL writeback does not skip the folio.
* This happens e.g. when doing writeout for
* transaction commit.
*/
if (buffer_dirty(bh)) {
- if (!PageDirty(page))
- redirty_page_for_writepage(wbc, page);
+ if (!folio_test_dirty(folio))
+ folio_redirty_for_writepage(wbc, folio);
keep_towrite = true;
}
continue;
@@ -500,11 +497,11 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
nr_to_submit++;
} while ((bh = bh->b_this_page) != head);

- /* Nothing to submit? Just unlock the page... */
+ /* Nothing to submit? Just unlock the folio... */
if (!nr_to_submit)
return 0;

- bh = head = page_buffers(page);
+ bh = head = folio_buffers(folio);

/*
* If any blocks are being written to an encrypted file, encrypt them
@@ -516,6 +513,7 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
if (fscrypt_inode_uses_fs_layer_crypto(inode) && nr_to_submit) {
gfp_t gfp_flags = GFP_NOFS;
unsigned int enc_bytes = round_up(len, i_blocksize(inode));
+ struct page *bounce_page;

/*
* Since bounce page allocation uses a mempool, we can only use
@@ -542,7 +540,7 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
}

printk_ratelimited(KERN_ERR "%s: ret = %d\n", __func__, ret);
- redirty_page_for_writepage(wbc, page);
+ folio_redirty_for_writepage(wbc, folio);
do {
if (buffer_async_write(bh)) {
clear_buffer_async_write(bh);
@@ -553,18 +551,16 @@ int ext4_bio_write_page(struct ext4_io_submit *io,

return ret;
}
+ io_folio = page_folio(bounce_page);
}

- if (keep_towrite)
- set_page_writeback_keepwrite(page);
- else
- set_page_writeback(page);
+ __folio_start_writeback(folio, keep_towrite);

/* Now submit buffers to write */
do {
if (!buffer_async_write(bh))
continue;
- io_submit_add_bh(io, inode, page, bounce_page, bh);
+ io_submit_add_bh(io, inode, folio, io_folio, bh);
} while ((bh = bh->b_this_page) != head);

return 0;
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index 88600a94fa91..1c68d67b832f 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -753,11 +753,6 @@ bool set_page_writeback(struct page *page);
#define folio_start_writeback_keepwrite(folio) \
__folio_start_writeback(folio, true)

-static inline void set_page_writeback_keepwrite(struct page *page)
-{
- folio_start_writeback_keepwrite(page_folio(page));
-}
-
static inline bool test_set_page_writeback(struct page *page)
{
return set_page_writeback(page);
--
2.39.2

2023-03-24 18:07:46

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH v2 02/29] fscrypt: Add some folio helper functions

fscrypt_is_bounce_folio() is the equivalent of fscrypt_is_bounce_page()
and fscrypt_pagecache_folio() is the equivalent of fscrypt_pagecache_page().

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
Reviewed-by: Ritesh Harjani (IBM) <[email protected]>
---
include/linux/fscrypt.h | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)

diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index a69f1302051d..c895b12737a1 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -273,6 +273,16 @@ static inline struct page *fscrypt_pagecache_page(struct page *bounce_page)
return (struct page *)page_private(bounce_page);
}

+static inline bool fscrypt_is_bounce_folio(struct folio *folio)
+{
+ return folio->mapping == NULL;
+}
+
+static inline struct folio *fscrypt_pagecache_folio(struct folio *bounce_folio)
+{
+ return bounce_folio->private;
+}
+
void fscrypt_free_bounce_page(struct page *bounce_page);

/* policy.c */
@@ -446,6 +456,17 @@ static inline struct page *fscrypt_pagecache_page(struct page *bounce_page)
return ERR_PTR(-EINVAL);
}

+static inline bool fscrypt_is_bounce_folio(struct folio *folio)
+{
+ return false;
+}
+
+static inline struct folio *fscrypt_pagecache_folio(struct folio *bounce_folio)
+{
+ WARN_ON_ONCE(1);
+ return ERR_PTR(-EINVAL);
+}
+
static inline void fscrypt_free_bounce_page(struct page *bounce_page)
{
}
--
2.39.2

2023-03-24 22:31:03

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH v2 23/29] ext4: Convert ext4_mpage_readpages() to work on folios

On Fri, Mar 24, 2023 at 06:01:23PM +0000, Matthew Wilcox (Oracle) wrote:
> if (first_hole != blocks_per_page) {
> - zero_user_segment(page, first_hole << blkbits,
> - PAGE_SIZE);
> + folio_zero_segment(folio, first_hole << blkbits,
> + folio_size(folio));
> if (first_hole == 0) {
> - if (ext4_need_verity(inode, page->index) &&
> - !fsverity_verify_page(page))
> + if (ext4_need_verity(inode, folio->index) &&
> + !fsverity_verify_page(&folio->page))
> goto set_error_page;

This can use fsverity_verify_folio().

- Eric

2023-03-26 03:50:09

by Matthew Wilcox

[permalink] [raw]
Subject: Re: [PATCH v2 23/29] ext4: Convert ext4_mpage_readpages() to work on folios

On Fri, Mar 24, 2023 at 03:29:51PM -0700, Eric Biggers wrote:
> On Fri, Mar 24, 2023 at 06:01:23PM +0000, Matthew Wilcox (Oracle) wrote:
> > if (first_hole != blocks_per_page) {
> > - zero_user_segment(page, first_hole << blkbits,
> > - PAGE_SIZE);
> > + folio_zero_segment(folio, first_hole << blkbits,
> > + folio_size(folio));
> > if (first_hole == 0) {
> > - if (ext4_need_verity(inode, page->index) &&
> > - !fsverity_verify_page(page))
> > + if (ext4_need_verity(inode, folio->index) &&
> > + !fsverity_verify_page(&folio->page))
> > goto set_error_page;
>
> This can use fsverity_verify_folio().

Thanks! Ted, let me know if you want a resend with this fixed, or
if you'll do it yourself.

2023-04-15 02:32:02

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH v2 00/29] Convert most of ext4 to folios


On Fri, 24 Mar 2023 18:01:00 +0000, Matthew Wilcox (Oracle) wrote:
> On top of next-20230321, this converts most of ext4 to use folios instead
> of pages. It does not enable large folios although it fixes some places
> that will need to be fixed before they can be enabled for ext4. It does
> not convert mballoc to use folios. write_begin() and write_end() still
> take a page parameter instead of a folio.
>
> It does convert a lot of code away from the page APIs that we're trying
> to remove. It does remove a lot of calls to compound_head(). I'd like
> to see it land in 6.4.
>
> [...]

Applied, thanks!

[01/29] fs: Add FGP_WRITEBEGIN
commit: e999a5c5a19cf3b679f3d93c49ad5f5c04e4806c
[02/29] fscrypt: Add some folio helper functions
commit: c76e14dc13bcf89f3b55fd9dcd036a453a822d79
[03/29] ext4: Convert ext4_bio_write_page() to use a folio
commit: cd57b77197a434709aec0e7fb8b2e6ec8479aa4e
[04/29] ext4: Convert ext4_finish_bio() to use folios
commit: bb64c08bff6a6edbd85786c92a2cb980ed99b29f
[05/29] ext4: Turn mpage_process_page() into mpage_process_folio()
commit: 4da2f6e3c45999e904de1edcd06c8533715cc1b5
[06/29] ext4: Convert mpage_submit_page() to mpage_submit_folio()
commit: 81a0d3e126a0bb4300d1db259d89b839124f2cff
[07/29] ext4: Convert mpage_page_done() to mpage_folio_done()
commit: 33483b3b6ee4328f37c3dcf702ba979e6a00bf8f
[08/29] ext4: Convert ext4_bio_write_page() to ext4_bio_write_folio()
commit: e8d6062c50acbf1aba88ca6adaa1bcda058abeab
[09/29] ext4: Convert ext4_readpage_inline() to take a folio
commit: 3edde93e07954a8860d67be4a2165514a083b6e8
[10/29] ext4: Convert ext4_convert_inline_data_to_extent() to use a folio
commit: 83eba701cf6e582afa92987e34abc0b0dbcb690e
[11/29] ext4: Convert ext4_try_to_write_inline_data() to use a folio
commit: f8f8c89f59f7ab037bfca8797e2cc613a5684f21
[12/29] ext4: Convert ext4_da_convert_inline_data_to_extent() to use a folio
commit: 4ed9b598ac30913987ab46e0069620e6e8af82f0
[13/29] ext4: Convert ext4_da_write_inline_data_begin() to use a folio
commit: 9a9d01f081ea29a5a8afc4504b1bc48daffa5cc1
[14/29] ext4: Convert ext4_read_inline_page() to ext4_read_inline_folio()
commit: 6b87fbe4155007c3ab8e950c72db657f6cd990c6
[15/29] ext4: Convert ext4_write_inline_data_end() to use a folio
commit: 6b90d4130ac8ee9cf2a179a617cfced71a18d252
[16/29] ext4: Convert ext4_write_begin() to use a folio
commit: 4d934a5e6caa6dcdd3fbee7b96fe512a455863b6
[17/29] ext4: Convert ext4_write_end() to use a folio
commit: 64fb31367598188a0a230b81c6f4397fa71fd033
[18/29] ext4: Use a folio in ext4_journalled_write_end()
commit: feb22b77b855a6529675b4e998970ab461c0f446
[19/29] ext4: Convert ext4_journalled_zero_new_buffers() to use a folio
commit: 86324a21627a40f949bf787b55c45b9856523f9d
[20/29] ext4: Convert __ext4_block_zero_page_range() to use a folio
commit: 9d3973de9a3745ea9d38bdfb953a4c4bee81ac2a
[21/29] ext4: Convert ext4_page_nomap_can_writeout to ext4_folio_nomap_can_writeout
commit: 02e4b04c56d03a518b958783900b22f33c6643d6
[22/29] ext4: Use a folio in ext4_da_write_begin()
commit: 0b5a254395dc6db5c38d89e606c0298ed4c9e984
[23/29] ext4: Convert ext4_mpage_readpages() to work on folios
commit: c0be8e6f081b3e966e21f52679b2f809b7df10b8
[24/29] ext4: Convert ext4_block_write_begin() to take a folio
commit: 86b38c273cc68ce7b50649447d8ac0ddf3228026
[25/29] ext4: Use a folio in ext4_page_mkwrite()
commit: 9ea0e45bd2f6cbfba787360f5ba8e18deabb7671
[26/29] ext4: Use a folio iterator in __read_end_io()
commit: f2b229a8c6c2633c35cb7446cfabea5a6f721edc
[27/29] ext4: Convert mext_page_mkuptodate() to take a folio
commit: 3060b6ef05603cf3c05b2b746f739b0169bd75f9
[28/29] ext4: Convert pagecache_read() to use a folio
commit: b23fb762785babc1d6194770c88432da037c8a64
[29/29] ext4: Use a folio in ext4_read_merkle_tree_page
commit: e9ebecf266c6657de5865a02a47c0d6b2460c526

Best regards,
--
Theodore Ts'o <[email protected]>

2023-04-18 06:52:26

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH v2 29/29] ext4: Use a folio in ext4_read_merkle_tree_page

Hi Matthew,

On Fri, Mar 24, 2023 at 06:01:29PM +0000, Matthew Wilcox (Oracle) wrote:
> This is an implementation of fsverity_operations read_merkle_tree_page,
> so it must still return the precise page asked for, but we can use the
> folio API to reduce the number of conversions between folios & pages.
>
> Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
> ---
> fs/ext4/verity.c | 14 +++++++-------
> 1 file changed, 7 insertions(+), 7 deletions(-)
>
> diff --git a/fs/ext4/verity.c b/fs/ext4/verity.c
> index afe847c967a4..3b01247066dd 100644
> --- a/fs/ext4/verity.c
> +++ b/fs/ext4/verity.c
> @@ -361,21 +361,21 @@ static struct page *ext4_read_merkle_tree_page(struct inode *inode,
> pgoff_t index,
> unsigned long num_ra_pages)
> {
> - struct page *page;
> + struct folio *folio;
>
> index += ext4_verity_metadata_pos(inode) >> PAGE_SHIFT;
>
> - page = find_get_page_flags(inode->i_mapping, index, FGP_ACCESSED);
> - if (!page || !PageUptodate(page)) {
> + folio = __filemap_get_folio(inode->i_mapping, index, FGP_ACCESSED, 0);
> + if (!folio || !folio_test_uptodate(folio)) {
> DEFINE_READAHEAD(ractl, NULL, NULL, inode->i_mapping, index);
>
> - if (page)
> - put_page(page);
> + if (folio)
> + folio_put(folio);
> else if (num_ra_pages > 1)
> page_cache_ra_unbounded(&ractl, num_ra_pages, 0);
> - page = read_mapping_page(inode->i_mapping, index, NULL);
> + folio = read_mapping_folio(inode->i_mapping, index, NULL);
> }
> - return page;
> + return folio_file_page(folio, index);

This is not working at all, since it dereferences ERR_PTR(-ENOENT). I think it
needs:

diff --git a/fs/ext4/verity.c b/fs/ext4/verity.c
index 3b01247066dd..dbc655a6c443 100644
--- a/fs/ext4/verity.c
+++ b/fs/ext4/verity.c
@@ -366,15 +366,17 @@ static struct page *ext4_read_merkle_tree_page(struct inode *inode,
index += ext4_verity_metadata_pos(inode) >> PAGE_SHIFT;

folio = __filemap_get_folio(inode->i_mapping, index, FGP_ACCESSED, 0);
- if (!folio || !folio_test_uptodate(folio)) {
+ if (folio == ERR_PTR(-ENOENT) || !folio_test_uptodate(folio)) {
DEFINE_READAHEAD(ractl, NULL, NULL, inode->i_mapping, index);

- if (folio)
+ if (!IS_ERR(folio))
folio_put(folio);
else if (num_ra_pages > 1)
page_cache_ra_unbounded(&ractl, num_ra_pages, 0);
folio = read_mapping_folio(inode->i_mapping, index, NULL);
}
+ if (IS_ERR(folio))
+ return ERR_CAST(folio);
return folio_file_page(folio, index);
}

2023-04-18 13:13:52

by Matthew Wilcox

[permalink] [raw]
Subject: Re: [PATCH v2 29/29] ext4: Use a folio in ext4_read_merkle_tree_page

On Mon, Apr 17, 2023 at 11:50:42PM -0700, Eric Biggers wrote:
> Hi Matthew,
>
> On Fri, Mar 24, 2023 at 06:01:29PM +0000, Matthew Wilcox (Oracle) wrote:
> > This is an implementation of fsverity_operations read_merkle_tree_page,
> > so it must still return the precise page asked for, but we can use the
> > folio API to reduce the number of conversions between folios & pages.
> >
> > Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
> > ---
> > fs/ext4/verity.c | 14 +++++++-------
> > 1 file changed, 7 insertions(+), 7 deletions(-)
> >
> > diff --git a/fs/ext4/verity.c b/fs/ext4/verity.c
> > index afe847c967a4..3b01247066dd 100644
> > --- a/fs/ext4/verity.c
> > +++ b/fs/ext4/verity.c
> > @@ -361,21 +361,21 @@ static struct page *ext4_read_merkle_tree_page(struct inode *inode,
> > pgoff_t index,
> > unsigned long num_ra_pages)
> > {
> > - struct page *page;
> > + struct folio *folio;
> >
> > index += ext4_verity_metadata_pos(inode) >> PAGE_SHIFT;
> >
> > - page = find_get_page_flags(inode->i_mapping, index, FGP_ACCESSED);
> > - if (!page || !PageUptodate(page)) {
> > + folio = __filemap_get_folio(inode->i_mapping, index, FGP_ACCESSED, 0);
> > + if (!folio || !folio_test_uptodate(folio)) {
> > DEFINE_READAHEAD(ractl, NULL, NULL, inode->i_mapping, index);
> >
> > - if (page)
> > - put_page(page);
> > + if (folio)
> > + folio_put(folio);
> > else if (num_ra_pages > 1)
> > page_cache_ra_unbounded(&ractl, num_ra_pages, 0);
> > - page = read_mapping_page(inode->i_mapping, index, NULL);
> > + folio = read_mapping_folio(inode->i_mapping, index, NULL);
> > }
> > - return page;
> > + return folio_file_page(folio, index);
>
> This is not working at all, since it dereferences ERR_PTR(-ENOENT). I think it
> needs:

Argh. Christoph changed the return value of __filemap_get_folio().

> folio = __filemap_get_folio(inode->i_mapping, index, FGP_ACCESSED, 0);
> - if (!folio || !folio_test_uptodate(folio)) {
> + if (folio == ERR_PTR(-ENOENT) || !folio_test_uptodate(folio)) {

This should be "if (IS_ERR(folio) || !folio_test_uptodate(folio)) {"

But we can't carry this change in Ted's tree because it doesn't have
Christoph's change. And we can't carry it in Andrew's tree because it
doesn't have my ext4 change.

> DEFINE_READAHEAD(ractl, NULL, NULL, inode->i_mapping, index);
>
> - if (folio)
> + if (!IS_ERR(folio))
> folio_put(folio);
> else if (num_ra_pages > 1)
> page_cache_ra_unbounded(&ractl, num_ra_pages, 0);
> folio = read_mapping_folio(inode->i_mapping, index, NULL);
> }
> + if (IS_ERR(folio))
> + return ERR_CAST(folio);

return &folio->page;

> return folio_file_page(folio, index);
> }
>