2023-09-19 04:52:14

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 00/26] Finish the create_empty_buffers() transition

Pankaj recently added folio_create_empty_buffers() as the folio
equivalent to create_empty_buffers(). This patch set finishes
the conversion by first converting all remaining filesystems
to call folio_create_empty_buffers(), then renaming it back
to create_empty_buffers(). I took the opportunity to make a few
simplifications like making folio_create_empty_buffers() return the head
buffer and extracting get_nth_bh() from nilfs2.

A few of the patches in this series aren't directly related to
create_empty_buffers(), but I saw them while I was working on this and
thought they'd be easy enough to add to this series. Compile-tested only,
other than ext4.

Matthew Wilcox (Oracle) (26):
buffer: Make folio_create_empty_buffers() return a buffer_head
mpage: Convert map_buffer_to_folio() to folio_create_empty_buffers()
ext4: Convert to folio_create_empty_buffers
buffer: Add get_nth_bh()
gfs2: Convert inode unstuffing to use a folio
gfs2: Convert gfs2_getbuf() to folios
gfs2; Convert gfs2_getjdatabuf to use a folio
gfs2: Convert gfs2_write_buf_to_page() to use a folio
nilfs2: Convert nilfs_mdt_freeze_buffer to use a folio
nilfs2: Convert nilfs_grab_buffer() to use a folio
nilfs2: Convert nilfs_copy_page() to nilfs_copy_folio()
nilfs2: Convert nilfs_mdt_forget_block() to use a folio
nilfs2: Convert nilfs_mdt_get_frozen_buffer to use a folio
nilfs2: Remove nilfs_page_get_nth_block
nilfs2: Convert nilfs_lookup_dirty_data_buffers to use
folio_create_empty_buffers
ntfs: Convert ntfs_read_block() to use a folio
ntfs: Convert ntfs_writepage to use a folio
ntfs: Convert ntfs_prepare_pages_for_non_resident_write() to folios
ntfs3: Convert ntfs_zero_range() to use a folio
ocfs2: Convert ocfs2_map_page_blocks to use a folio
reiserfs: Convert writepage to use a folio
ufs: Add ufs_get_locked_folio and ufs_put_locked_folio
ufs: Use ufs_get_locked_folio() in ufs_alloc_lastblock()
ufs; Convert ufs_change_blocknr() to use folios
ufs: Remove ufs_get_locked_page()
buffer: Remove folio_create_empty_buffers()

fs/buffer.c | 29 ++--
fs/ext4/inode.c | 14 +-
fs/ext4/move_extent.c | 11 +-
fs/gfs2/aops.c | 2 +-
fs/gfs2/bmap.c | 48 ++++---
fs/gfs2/meta_io.c | 61 ++++-----
fs/gfs2/quota.c | 37 +++---
fs/mpage.c | 3 +-
fs/nilfs2/mdt.c | 66 +++++-----
fs/nilfs2/page.c | 76 +++++------
fs/nilfs2/page.h | 11 --
fs/nilfs2/segment.c | 7 +-
fs/ntfs/aops.c | 255 +++++++++++++++++-------------------
fs/ntfs/file.c | 89 ++++++-------
fs/ntfs3/file.c | 31 ++---
fs/ocfs2/aops.c | 19 +--
fs/reiserfs/inode.c | 80 +++++------
fs/ufs/balloc.c | 20 ++-
fs/ufs/inode.c | 25 ++--
fs/ufs/util.c | 34 +++--
fs/ufs/util.h | 10 +-
include/linux/buffer_head.h | 28 +++-
22 files changed, 458 insertions(+), 498 deletions(-)

--
2.40.1


2023-09-19 04:53:13

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 14/26] nilfs2: Remove nilfs_page_get_nth_block

All users have now been converted to get_nth_block().

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/nilfs2/page.h | 6 ------
1 file changed, 6 deletions(-)

diff --git a/fs/nilfs2/page.h b/fs/nilfs2/page.h
index 344d71942d36..d249ea1cefff 100644
--- a/fs/nilfs2/page.h
+++ b/fs/nilfs2/page.h
@@ -52,10 +52,4 @@ unsigned long nilfs_find_uncommitted_extent(struct inode *inode,
#define NILFS_PAGE_BUG(page, m, a...) \
do { nilfs_page_bug(page); BUG(); } while (0)

-static inline struct buffer_head *
-nilfs_page_get_nth_block(struct page *page, unsigned int count)
-{
- return get_nth_bh(page_buffers(page), count);
-}
-
#endif /* _NILFS_PAGE_H */
--
2.40.1

2023-09-19 04:53:51

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 03/26] ext4: Convert to folio_create_empty_buffers

Remove an unnecessary folio->page->folio conversion and take advantage
of the new return value from folio_create_empty_buffers().

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/ext4/inode.c | 14 +++++---------
fs/ext4/move_extent.c | 11 +++++------
2 files changed, 10 insertions(+), 15 deletions(-)

diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 4ce35f1c8b0a..8e431ff2fd95 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1020,10 +1020,8 @@ static int ext4_block_write_begin(struct folio *folio, loff_t pos, unsigned len,
BUG_ON(from > to);

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

@@ -1153,7 +1151,7 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping,
* starting the handle.
*/
if (!folio_buffers(folio))
- create_empty_buffers(&folio->page, inode->i_sb->s_blocksize, 0);
+ folio_create_empty_buffers(folio, inode->i_sb->s_blocksize, 0);

folio_unlock(folio);

@@ -3643,10 +3641,8 @@ static int __ext4_block_zero_page_range(handle_t *handle,
iblock = index << (PAGE_SHIFT - inode->i_sb->s_blocksize_bits);

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

/* Find the buffer that contains "offset" */
pos = blocksize;
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
index 18a9e7c47975..7fe448fb948b 100644
--- a/fs/ext4/move_extent.c
+++ b/fs/ext4/move_extent.c
@@ -183,10 +183,8 @@ mext_page_mkuptodate(struct folio *folio, unsigned from, unsigned to)

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

block = (sector_t)folio->index << (PAGE_SHIFT - inode->i_blkbits);
for (bh = head, block_start = 0; bh != head || !block_start;
@@ -380,9 +378,10 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
}
/* Perform all necessary steps similar write_begin()/write_end()
* but keeping in mind that i_size will not change */
- if (!folio_buffers(folio[0]))
- create_empty_buffers(&folio[0]->page, 1 << orig_inode->i_blkbits, 0);
bh = folio_buffers(folio[0]);
+ if (!bh)
+ bh = folio_create_empty_buffers(folio[0],
+ 1 << orig_inode->i_blkbits, 0);
for (i = 0; i < data_offset_in_page; i++)
bh = bh->b_this_page;
for (i = 0; i < block_len_in_page; i++) {
--
2.40.1

2023-09-19 04:54:27

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 05/26] gfs2: Convert inode unstuffing to use a folio

Use the folio APIs, removing numerous hidden calls to compound_head().
Also remove the stale comment about the page being looked up if it's NULL.

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/gfs2/bmap.c | 48 +++++++++++++++++++++++-------------------------
1 file changed, 23 insertions(+), 25 deletions(-)

diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index ef7017fb6951..247d2c16593c 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -43,53 +43,51 @@ struct metapath {
static int punch_hole(struct gfs2_inode *ip, u64 offset, u64 length);

/**
- * gfs2_unstuffer_page - unstuff a stuffed inode into a block cached by a page
+ * gfs2_unstuffer_folio - unstuff a stuffed inode into a block cached by a folio
* @ip: the inode
* @dibh: the dinode buffer
* @block: the block number that was allocated
- * @page: The (optional) page. This is looked up if @page is NULL
+ * @folio: The folio.
*
* Returns: errno
*/
-
-static int gfs2_unstuffer_page(struct gfs2_inode *ip, struct buffer_head *dibh,
- u64 block, struct page *page)
+static int gfs2_unstuffer_folio(struct gfs2_inode *ip, struct buffer_head *dibh,
+ u64 block, struct folio *folio)
{
struct inode *inode = &ip->i_inode;

- if (!PageUptodate(page)) {
- void *kaddr = kmap(page);
+ if (!folio_test_uptodate(folio)) {
+ void *kaddr = kmap_local_folio(folio, 0);
u64 dsize = i_size_read(inode);

memcpy(kaddr, dibh->b_data + sizeof(struct gfs2_dinode), dsize);
- memset(kaddr + dsize, 0, PAGE_SIZE - dsize);
- kunmap(page);
+ memset(kaddr + dsize, 0, folio_size(folio) - dsize);
+ kunmap_local(kaddr);

- SetPageUptodate(page);
+ folio_mark_uptodate(folio);
}

if (gfs2_is_jdata(ip)) {
- struct buffer_head *bh;
+ struct buffer_head *bh = folio_buffers(folio);

- if (!page_has_buffers(page))
- create_empty_buffers(page, BIT(inode->i_blkbits),
- BIT(BH_Uptodate));
+ if (!bh)
+ bh = folio_create_empty_buffers(folio,
+ BIT(inode->i_blkbits), BIT(BH_Uptodate));

- bh = page_buffers(page);
if (!buffer_mapped(bh))
map_bh(bh, inode->i_sb, block);

set_buffer_uptodate(bh);
gfs2_trans_add_data(ip->i_gl, bh);
} else {
- set_page_dirty(page);
+ folio_mark_dirty(folio);
gfs2_ordered_add_inode(ip);
}

return 0;
}

-static int __gfs2_unstuff_inode(struct gfs2_inode *ip, struct page *page)
+static int __gfs2_unstuff_inode(struct gfs2_inode *ip, struct folio *folio)
{
struct buffer_head *bh, *dibh;
struct gfs2_dinode *di;
@@ -118,7 +116,7 @@ static int __gfs2_unstuff_inode(struct gfs2_inode *ip, struct page *page)
dibh, sizeof(struct gfs2_dinode));
brelse(bh);
} else {
- error = gfs2_unstuffer_page(ip, dibh, block, page);
+ error = gfs2_unstuffer_folio(ip, dibh, block, folio);
if (error)
goto out_brelse;
}
@@ -157,17 +155,17 @@ static int __gfs2_unstuff_inode(struct gfs2_inode *ip, struct page *page)
int gfs2_unstuff_dinode(struct gfs2_inode *ip)
{
struct inode *inode = &ip->i_inode;
- struct page *page;
+ struct folio *folio;
int error;

down_write(&ip->i_rw_mutex);
- page = grab_cache_page(inode->i_mapping, 0);
- error = -ENOMEM;
- if (!page)
+ folio = filemap_grab_folio(inode->i_mapping, 0);
+ error = PTR_ERR(folio);
+ if (IS_ERR(folio))
goto out;
- error = __gfs2_unstuff_inode(ip, page);
- unlock_page(page);
- put_page(page);
+ error = __gfs2_unstuff_inode(ip, folio);
+ folio_unlock(folio);
+ folio_put(folio);
out:
up_write(&ip->i_rw_mutex);
return error;
--
2.40.1

2023-09-19 04:54:45

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 18/26] ntfs: Convert ntfs_prepare_pages_for_non_resident_write() to folios

Convert each element of the pages array to a folio before using it.
This in no way renders the function large-folio safe, but it does remove
a lot of hidden calls to compound_head().

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/ntfs/file.c | 89 +++++++++++++++++++++++---------------------------
1 file changed, 41 insertions(+), 48 deletions(-)

diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c
index cbc545999cfe..099141d20db6 100644
--- a/fs/ntfs/file.c
+++ b/fs/ntfs/file.c
@@ -567,7 +567,7 @@ static int ntfs_prepare_pages_for_non_resident_write(struct page **pages,
LCN lcn;
s64 bh_pos, vcn_len, end, initialized_size;
sector_t lcn_block;
- struct page *page;
+ struct folio *folio;
struct inode *vi;
ntfs_inode *ni, *base_ni = NULL;
ntfs_volume *vol;
@@ -601,20 +601,6 @@ static int ntfs_prepare_pages_for_non_resident_write(struct page **pages,
(long long)pos, bytes);
blocksize = vol->sb->s_blocksize;
blocksize_bits = vol->sb->s_blocksize_bits;
- u = 0;
- do {
- page = pages[u];
- BUG_ON(!page);
- /*
- * create_empty_buffers() will create uptodate/dirty buffers if
- * the page is uptodate/dirty.
- */
- if (!page_has_buffers(page)) {
- create_empty_buffers(page, blocksize, 0);
- if (unlikely(!page_has_buffers(page)))
- return -ENOMEM;
- }
- } while (++u < nr_pages);
rl_write_locked = false;
rl = NULL;
err = 0;
@@ -626,14 +612,21 @@ static int ntfs_prepare_pages_for_non_resident_write(struct page **pages,
end = pos + bytes;
cend = (end + vol->cluster_size - 1) >> vol->cluster_size_bits;
/*
- * Loop over each page and for each page over each buffer. Use goto to
+ * Loop over each buffer in each folio. Use goto to
* reduce indentation.
*/
u = 0;
-do_next_page:
- page = pages[u];
- bh_pos = (s64)page->index << PAGE_SHIFT;
- bh = head = page_buffers(page);
+do_next_folio:
+ folio = page_folio(pages[u]);
+ bh_pos = folio_pos(folio);
+ head = folio_buffers(folio);
+ if (!head)
+ /*
+ * create_empty_buffers() will create uptodate/dirty
+ * buffers if the folio is uptodate/dirty.
+ */
+ head = folio_create_empty_buffers(folio, blocksize, 0);
+ bh = head;
do {
VCN cdelta;
s64 bh_end;
@@ -653,15 +646,15 @@ static int ntfs_prepare_pages_for_non_resident_write(struct page **pages,
if (buffer_uptodate(bh))
continue;
/*
- * The buffer is not uptodate. If the page is uptodate
+ * The buffer is not uptodate. If the folio is uptodate
* set the buffer uptodate and otherwise ignore it.
*/
- if (PageUptodate(page)) {
+ if (folio_test_uptodate(folio)) {
set_buffer_uptodate(bh);
continue;
}
/*
- * Neither the page nor the buffer are uptodate. If
+ * Neither the folio nor the buffer are uptodate. If
* the buffer is only partially being written to, we
* need to read it in before the write, i.e. now.
*/
@@ -679,7 +672,7 @@ static int ntfs_prepare_pages_for_non_resident_write(struct page **pages,
ntfs_submit_bh_for_read(bh);
*wait_bh++ = bh;
} else {
- zero_user(page, bh_offset(bh),
+ folio_zero_range(folio, bh_offset(bh),
blocksize);
set_buffer_uptodate(bh);
}
@@ -706,7 +699,7 @@ static int ntfs_prepare_pages_for_non_resident_write(struct page **pages,
(bh_cofs >> blocksize_bits);
set_buffer_mapped(bh);
/*
- * If the page is uptodate so is the buffer. If the
+ * If the folio is uptodate so is the buffer. If the
* buffer is fully outside the write, we ignore it if
* it was already allocated and we mark it dirty so it
* gets written out if we allocated it. On the other
@@ -714,7 +707,7 @@ static int ntfs_prepare_pages_for_non_resident_write(struct page **pages,
* marking it dirty we set buffer_new so we can do
* error recovery.
*/
- if (PageUptodate(page)) {
+ if (folio_test_uptodate(folio)) {
if (!buffer_uptodate(bh))
set_buffer_uptodate(bh);
if (unlikely(was_hole)) {
@@ -754,7 +747,8 @@ static int ntfs_prepare_pages_for_non_resident_write(struct page **pages,
ntfs_submit_bh_for_read(bh);
*wait_bh++ = bh;
} else {
- zero_user(page, bh_offset(bh),
+ folio_zero_range(folio,
+ bh_offset(bh),
blocksize);
set_buffer_uptodate(bh);
}
@@ -773,7 +767,7 @@ static int ntfs_prepare_pages_for_non_resident_write(struct page **pages,
*/
if (bh_end <= pos || bh_pos >= end) {
if (!buffer_uptodate(bh)) {
- zero_user(page, bh_offset(bh),
+ folio_zero_range(folio, bh_offset(bh),
blocksize);
set_buffer_uptodate(bh);
}
@@ -786,7 +780,7 @@ static int ntfs_prepare_pages_for_non_resident_write(struct page **pages,
u8 *kaddr;
unsigned pofs;

- kaddr = kmap_atomic(page);
+ kaddr = kmap_local_folio(folio, 0);
if (bh_pos < pos) {
pofs = bh_pos & ~PAGE_MASK;
memset(kaddr + pofs, 0, pos - bh_pos);
@@ -795,8 +789,8 @@ static int ntfs_prepare_pages_for_non_resident_write(struct page **pages,
pofs = end & ~PAGE_MASK;
memset(kaddr + pofs, 0, bh_end - end);
}
- kunmap_atomic(kaddr);
- flush_dcache_page(page);
+ kunmap_local(kaddr);
+ flush_dcache_folio(folio);
}
continue;
}
@@ -809,11 +803,12 @@ static int ntfs_prepare_pages_for_non_resident_write(struct page **pages,
initialized_size = ni->allocated_size;
read_unlock_irqrestore(&ni->size_lock, flags);
if (bh_pos > initialized_size) {
- if (PageUptodate(page)) {
+ if (folio_test_uptodate(folio)) {
if (!buffer_uptodate(bh))
set_buffer_uptodate(bh);
} else if (!buffer_uptodate(bh)) {
- zero_user(page, bh_offset(bh), blocksize);
+ folio_zero_range(folio, bh_offset(bh),
+ blocksize);
set_buffer_uptodate(bh);
}
continue;
@@ -927,17 +922,17 @@ static int ntfs_prepare_pages_for_non_resident_write(struct page **pages,
bh->b_blocknr = -1;
/*
* If the buffer is uptodate we skip it. If it
- * is not but the page is uptodate, we can set
- * the buffer uptodate. If the page is not
+ * is not but the folio is uptodate, we can set
+ * the buffer uptodate. If the folio is not
* uptodate, we can clear the buffer and set it
* uptodate. Whether this is worthwhile is
* debatable and this could be removed.
*/
- if (PageUptodate(page)) {
+ if (folio_test_uptodate(folio)) {
if (!buffer_uptodate(bh))
set_buffer_uptodate(bh);
} else if (!buffer_uptodate(bh)) {
- zero_user(page, bh_offset(bh),
+ folio_zero_range(folio, bh_offset(bh),
blocksize);
set_buffer_uptodate(bh);
}
@@ -1167,7 +1162,7 @@ static int ntfs_prepare_pages_for_non_resident_write(struct page **pages,
} while (bh_pos += blocksize, (bh = bh->b_this_page) != head);
/* If there are no errors, do the next page. */
if (likely(!err && ++u < nr_pages))
- goto do_next_page;
+ goto do_next_folio;
/* If there are no errors, release the runlist lock if we took it. */
if (likely(!err)) {
if (unlikely(rl_write_locked)) {
@@ -1185,9 +1180,8 @@ static int ntfs_prepare_pages_for_non_resident_write(struct page **pages,
bh = *--wait_bh;
wait_on_buffer(bh);
if (likely(buffer_uptodate(bh))) {
- page = bh->b_page;
- bh_pos = ((s64)page->index << PAGE_SHIFT) +
- bh_offset(bh);
+ folio = bh->b_folio;
+ bh_pos = folio_pos(folio) + bh_offset(bh);
/*
* If the buffer overflows the initialized size, need
* to zero the overflowing region.
@@ -1197,7 +1191,7 @@ static int ntfs_prepare_pages_for_non_resident_write(struct page **pages,

if (likely(bh_pos < initialized_size))
ofs = initialized_size - bh_pos;
- zero_user_segment(page, bh_offset(bh) + ofs,
+ folio_zero_segment(folio, bh_offset(bh) + ofs,
blocksize);
}
} else /* if (unlikely(!buffer_uptodate(bh))) */
@@ -1324,21 +1318,20 @@ static int ntfs_prepare_pages_for_non_resident_write(struct page **pages,
u = 0;
end = bh_cpos << vol->cluster_size_bits;
do {
- page = pages[u];
- bh = head = page_buffers(page);
+ folio = page_folio(pages[u]);
+ bh = head = folio_buffers(folio);
do {
if (u == nr_pages &&
- ((s64)page->index << PAGE_SHIFT) +
- bh_offset(bh) >= end)
+ folio_pos(folio) + bh_offset(bh) >= end)
break;
if (!buffer_new(bh))
continue;
clear_buffer_new(bh);
if (!buffer_uptodate(bh)) {
- if (PageUptodate(page))
+ if (folio_test_uptodate(folio))
set_buffer_uptodate(bh);
else {
- zero_user(page, bh_offset(bh),
+ folio_zero_range(folio, bh_offset(bh),
blocksize);
set_buffer_uptodate(bh);
}
--
2.40.1

2023-09-19 04:55:05

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 26/26] buffer: Remove folio_create_empty_buffers()

With all users converted, remove the old create_empty_buffers() and
rename folio_create_empty_buffers() to create_empty_buffers().

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/buffer.c | 13 +++----------
fs/ext4/inode.c | 6 +++---
fs/ext4/move_extent.c | 4 ++--
fs/gfs2/aops.c | 2 +-
fs/gfs2/bmap.c | 2 +-
fs/gfs2/meta_io.c | 2 +-
fs/gfs2/quota.c | 2 +-
fs/mpage.c | 2 +-
fs/nilfs2/mdt.c | 2 +-
fs/nilfs2/page.c | 4 ++--
fs/nilfs2/segment.c | 2 +-
fs/ntfs/aops.c | 4 ++--
fs/ntfs/file.c | 2 +-
fs/ntfs3/file.c | 2 +-
fs/ocfs2/aops.c | 2 +-
fs/reiserfs/inode.c | 2 +-
fs/ufs/util.c | 2 +-
include/linux/buffer_head.h | 4 +---
18 files changed, 25 insertions(+), 34 deletions(-)

diff --git a/fs/buffer.c b/fs/buffer.c
index 1b9e691714bd..d483903fa9ff 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -1646,7 +1646,7 @@ EXPORT_SYMBOL(block_invalidate_folio);
* block_dirty_folio() via private_lock. try_to_free_buffers
* is already excluded via the folio lock.
*/
-struct buffer_head *folio_create_empty_buffers(struct folio *folio,
+struct buffer_head *create_empty_buffers(struct folio *folio,
unsigned long blocksize, unsigned long b_state)
{
struct buffer_head *bh, *head, *tail;
@@ -1677,13 +1677,6 @@ struct buffer_head *folio_create_empty_buffers(struct folio *folio,

return head;
}
-EXPORT_SYMBOL(folio_create_empty_buffers);
-
-void create_empty_buffers(struct page *page,
- unsigned long blocksize, unsigned long b_state)
-{
- folio_create_empty_buffers(page_folio(page), blocksize, b_state);
-}
EXPORT_SYMBOL(create_empty_buffers);

/**
@@ -1783,7 +1776,7 @@ static struct buffer_head *folio_create_buffers(struct folio *folio,

bh = folio_buffers(folio);
if (!bh)
- bh = folio_create_empty_buffers(folio,
+ bh = create_empty_buffers(folio,
1 << READ_ONCE(inode->i_blkbits), b_state);
return bh;
}
@@ -2676,7 +2669,7 @@ int block_truncate_page(struct address_space *mapping,

bh = folio_buffers(folio);
if (!bh)
- bh = folio_create_empty_buffers(folio, blocksize, 0);
+ bh = create_empty_buffers(folio, blocksize, 0);

/* Find the buffer that contains "offset" */
offset = offset_in_folio(folio, from);
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 8e431ff2fd95..347fc8986e93 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1021,7 +1021,7 @@ static int ext4_block_write_begin(struct folio *folio, loff_t pos, unsigned len,

head = folio_buffers(folio);
if (!head)
- head = folio_create_empty_buffers(folio, blocksize, 0);
+ head = create_empty_buffers(folio, blocksize, 0);
bbits = ilog2(blocksize);
block = (sector_t)folio->index << (PAGE_SHIFT - bbits);

@@ -1151,7 +1151,7 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping,
* starting the handle.
*/
if (!folio_buffers(folio))
- folio_create_empty_buffers(folio, inode->i_sb->s_blocksize, 0);
+ create_empty_buffers(folio, inode->i_sb->s_blocksize, 0);

folio_unlock(folio);

@@ -3642,7 +3642,7 @@ static int __ext4_block_zero_page_range(handle_t *handle,

bh = folio_buffers(folio);
if (!bh)
- bh = folio_create_empty_buffers(folio, blocksize, 0);
+ bh = create_empty_buffers(folio, blocksize, 0);

/* Find the buffer that contains "offset" */
pos = blocksize;
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
index 7fe448fb948b..3aa57376d9c2 100644
--- a/fs/ext4/move_extent.c
+++ b/fs/ext4/move_extent.c
@@ -184,7 +184,7 @@ mext_page_mkuptodate(struct folio *folio, unsigned from, unsigned to)
blocksize = i_blocksize(inode);
head = folio_buffers(folio);
if (!head)
- head = folio_create_empty_buffers(folio, blocksize, 0);
+ head = create_empty_buffers(folio, blocksize, 0);

block = (sector_t)folio->index << (PAGE_SHIFT - inode->i_blkbits);
for (bh = head, block_start = 0; bh != head || !block_start;
@@ -380,7 +380,7 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
* but keeping in mind that i_size will not change */
bh = folio_buffers(folio[0]);
if (!bh)
- bh = folio_create_empty_buffers(folio[0],
+ bh = create_empty_buffers(folio[0],
1 << orig_inode->i_blkbits, 0);
for (i = 0; i < data_offset_in_page; i++)
bh = bh->b_this_page;
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c
index c26d48355cc2..6b060fc9e260 100644
--- a/fs/gfs2/aops.c
+++ b/fs/gfs2/aops.c
@@ -130,7 +130,7 @@ static int __gfs2_jdata_write_folio(struct folio *folio,
if (folio_test_checked(folio)) {
folio_clear_checked(folio);
if (!folio_buffers(folio)) {
- folio_create_empty_buffers(folio,
+ create_empty_buffers(folio,
inode->i_sb->s_blocksize,
BIT(BH_Dirty)|BIT(BH_Uptodate));
}
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index 247d2c16593c..f1eee3f4704b 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -71,7 +71,7 @@ static int gfs2_unstuffer_folio(struct gfs2_inode *ip, struct buffer_head *dibh,
struct buffer_head *bh = folio_buffers(folio);

if (!bh)
- bh = folio_create_empty_buffers(folio,
+ bh = create_empty_buffers(folio,
BIT(inode->i_blkbits), BIT(BH_Uptodate));

if (!buffer_mapped(bh))
diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c
index b28196015543..681cbf2b4405 100644
--- a/fs/gfs2/meta_io.c
+++ b/fs/gfs2/meta_io.c
@@ -134,7 +134,7 @@ struct buffer_head *gfs2_getbuf(struct gfs2_glock *gl, u64 blkno, int create)
mapping_gfp_mask(mapping) | __GFP_NOFAIL);
bh = folio_buffers(folio);
if (!bh)
- bh = folio_create_empty_buffers(folio,
+ bh = create_empty_buffers(folio,
sdp->sd_sb.sb_bsize, 0);
} else {
folio = __filemap_get_folio(mapping, index,
diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c
index 0ee4865ebdca..bf093f195abb 100644
--- a/fs/gfs2/quota.c
+++ b/fs/gfs2/quota.c
@@ -750,7 +750,7 @@ static int gfs2_write_buf_to_page(struct gfs2_sbd *sdp, unsigned long index,
return PTR_ERR(folio);
bh = folio_buffers(folio);
if (!bh)
- bh = folio_create_empty_buffers(folio, bsize, 0);
+ bh = create_empty_buffers(folio, bsize, 0);

for (;;) {
/* Find the beginning block within the folio */
diff --git a/fs/mpage.c b/fs/mpage.c
index 964a6efe594d..ffb064ed9d04 100644
--- a/fs/mpage.c
+++ b/fs/mpage.c
@@ -119,7 +119,7 @@ static void map_buffer_to_folio(struct folio *folio, struct buffer_head *bh,
folio_mark_uptodate(folio);
return;
}
- head = folio_create_empty_buffers(folio, i_blocksize(inode), 0);
+ head = create_empty_buffers(folio, i_blocksize(inode), 0);
}

page_bh = head;
diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c
index 7b754e6494d7..c97c77a39668 100644
--- a/fs/nilfs2/mdt.c
+++ b/fs/nilfs2/mdt.c
@@ -568,7 +568,7 @@ int nilfs_mdt_freeze_buffer(struct inode *inode, struct buffer_head *bh)

bh_frozen = folio_buffers(folio);
if (!bh_frozen)
- bh_frozen = folio_create_empty_buffers(folio, 1 << blkbits, 0);
+ bh_frozen = create_empty_buffers(folio, 1 << blkbits, 0);

bh_frozen = get_nth_bh(bh_frozen, bh_offset(bh) >> blkbits);

diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c
index 696215d899bf..06b04758f289 100644
--- a/fs/nilfs2/page.c
+++ b/fs/nilfs2/page.c
@@ -34,7 +34,7 @@ static struct buffer_head *__nilfs_get_folio_block(struct folio *folio,
struct buffer_head *bh = folio_buffers(folio);

if (!bh)
- bh = folio_create_empty_buffers(folio, 1 << blkbits, b_state);
+ bh = create_empty_buffers(folio, 1 << blkbits, b_state);

first_block = (unsigned long)index << (PAGE_SHIFT - blkbits);
bh = get_nth_bh(bh, block - first_block);
@@ -204,7 +204,7 @@ static void nilfs_copy_folio(struct folio *dst, struct folio *src,
sbh = folio_buffers(src);
dbh = folio_buffers(dst);
if (!dbh)
- dbh = folio_create_empty_buffers(dst, sbh->b_size, 0);
+ dbh = create_empty_buffers(dst, sbh->b_size, 0);

if (copy_dirty)
mask |= BIT(BH_Dirty);
diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c
index 94388fe83cf8..55e31cc903d1 100644
--- a/fs/nilfs2/segment.c
+++ b/fs/nilfs2/segment.c
@@ -732,7 +732,7 @@ static size_t nilfs_lookup_dirty_data_buffers(struct inode *inode,
}
head = folio_buffers(folio);
if (!head)
- head = folio_create_empty_buffers(folio,
+ head = create_empty_buffers(folio,
i_blocksize(inode), 0);
folio_unlock(folio);

diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c
index c4426992a2ee..71e31e789b29 100644
--- a/fs/ntfs/aops.c
+++ b/fs/ntfs/aops.c
@@ -189,7 +189,7 @@ static int ntfs_read_block(struct folio *folio)

head = folio_buffers(folio);
if (!head)
- head = folio_create_empty_buffers(folio, blocksize, 0);
+ head = create_empty_buffers(folio, blocksize, 0);
bh = head;

/*
@@ -555,7 +555,7 @@ static int ntfs_write_block(struct folio *folio, struct writeback_control *wbc)
head = folio_buffers(folio);
if (!head) {
BUG_ON(!folio_test_uptodate(folio));
- head = folio_create_empty_buffers(folio, blocksize,
+ head = create_empty_buffers(folio, blocksize,
(1 << BH_Uptodate) | (1 << BH_Dirty));
}
bh = head;
diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c
index 099141d20db6..297c0b9db621 100644
--- a/fs/ntfs/file.c
+++ b/fs/ntfs/file.c
@@ -625,7 +625,7 @@ static int ntfs_prepare_pages_for_non_resident_write(struct page **pages,
* create_empty_buffers() will create uptodate/dirty
* buffers if the folio is uptodate/dirty.
*/
- head = folio_create_empty_buffers(folio, blocksize, 0);
+ head = create_empty_buffers(folio, blocksize, 0);
bh = head;
do {
VCN cdelta;
diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c
index a003a69091a2..66fd4ac28395 100644
--- a/fs/ntfs3/file.c
+++ b/fs/ntfs3/file.c
@@ -203,7 +203,7 @@ static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to)

head = folio_buffers(folio);
if (!head)
- head = folio_create_empty_buffers(folio, blocksize, 0);
+ head = create_empty_buffers(folio, blocksize, 0);

bh = head;
bh_off = 0;
diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c
index 95d1e70b4401..a6405dd5df09 100644
--- a/fs/ocfs2/aops.c
+++ b/fs/ocfs2/aops.c
@@ -601,7 +601,7 @@ int ocfs2_map_page_blocks(struct page *page, u64 *p_blkno,

head = folio_buffers(folio);
if (!head)
- head = folio_create_empty_buffers(folio, bsize, 0);
+ head = create_empty_buffers(folio, bsize, 0);

for (bh = head, block_start = 0; bh != head || !block_start;
bh = bh->b_this_page, block_start += bsize) {
diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
index d9737235b8e0..a9075c4843ed 100644
--- a/fs/reiserfs/inode.c
+++ b/fs/reiserfs/inode.c
@@ -2539,7 +2539,7 @@ static int reiserfs_write_full_folio(struct folio *folio,
*/
head = folio_buffers(folio);
if (!head)
- head = folio_create_empty_buffers(folio, s->s_blocksize,
+ head = create_empty_buffers(folio, s->s_blocksize,
(1 << BH_Dirty) | (1 << BH_Uptodate));

/*
diff --git a/fs/ufs/util.c b/fs/ufs/util.c
index d32de30009a0..13ba34e6d64f 100644
--- a/fs/ufs/util.c
+++ b/fs/ufs/util.c
@@ -264,6 +264,6 @@ struct folio *ufs_get_locked_folio(struct address_space *mapping,
}
}
if (!folio_buffers(folio))
- folio_create_empty_buffers(folio, 1 << inode->i_blkbits, 0);
+ create_empty_buffers(folio, 1 << inode->i_blkbits, 0);
return folio;
}
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index 9fc615ee17fd..37a69e8828c0 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -201,9 +201,7 @@ struct buffer_head *folio_alloc_buffers(struct folio *folio, unsigned long size,
gfp_t gfp);
struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size,
bool retry);
-void create_empty_buffers(struct page *, unsigned long,
- unsigned long b_state);
-struct buffer_head *folio_create_empty_buffers(struct folio *folio,
+struct buffer_head *create_empty_buffers(struct folio *folio,
unsigned long blocksize, unsigned long b_state);
void end_buffer_read_sync(struct buffer_head *bh, int uptodate);
void end_buffer_write_sync(struct buffer_head *bh, int uptodate);
--
2.40.1

2023-09-19 05:11:52

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 06/26] gfs2: Convert gfs2_getbuf() to folios

Remove several folio->page->folio conversions. Also use __GFP_NOFAIL
instead of calling yield() and the new get_nth_bh().

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/gfs2/meta_io.c | 39 +++++++++++++++++----------------------
1 file changed, 17 insertions(+), 22 deletions(-)

diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c
index 924361fa510b..f1fac1b45059 100644
--- a/fs/gfs2/meta_io.c
+++ b/fs/gfs2/meta_io.c
@@ -115,7 +115,7 @@ struct buffer_head *gfs2_getbuf(struct gfs2_glock *gl, u64 blkno, int create)
{
struct address_space *mapping = gfs2_glock2aspace(gl);
struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
- struct page *page;
+ struct folio *folio;
struct buffer_head *bh;
unsigned int shift;
unsigned long index;
@@ -129,36 +129,31 @@ struct buffer_head *gfs2_getbuf(struct gfs2_glock *gl, u64 blkno, int create)
bufnum = blkno - (index << shift); /* block buf index within page */

if (create) {
- for (;;) {
- page = grab_cache_page(mapping, index);
- if (page)
- break;
- yield();
- }
- if (!page_has_buffers(page))
- create_empty_buffers(page, sdp->sd_sb.sb_bsize, 0);
+ folio = __filemap_get_folio(mapping, index,
+ FGP_LOCK | FGP_ACCESSED | FGP_CREAT,
+ mapping_gfp_mask(mapping) | __GFP_NOFAIL);
+ bh = folio_buffers(folio);
+ if (!bh)
+ bh = folio_create_empty_buffers(folio,
+ sdp->sd_sb.sb_bsize, 0);
} else {
- page = find_get_page_flags(mapping, index,
- FGP_LOCK|FGP_ACCESSED);
- if (!page)
+ folio = __filemap_get_folio(mapping, index,
+ FGP_LOCK | FGP_ACCESSED, 0);
+ if (IS_ERR(folio))
return NULL;
- if (!page_has_buffers(page)) {
- bh = NULL;
- goto out_unlock;
- }
+ bh = folio_buffers(folio);
}

- /* Locate header for our buffer within our page */
- for (bh = page_buffers(page); bufnum--; bh = bh->b_this_page)
- /* Do nothing */;
- get_bh(bh);
+ if (!bh)
+ goto out_unlock;

+ bh = get_nth_bh(bh, bufnum);
if (!buffer_mapped(bh))
map_bh(bh, sdp->sd_vfs, blkno);

out_unlock:
- unlock_page(page);
- put_page(page);
+ folio_unlock(folio);
+ folio_put(folio);

return bh;
}
--
2.40.1

2023-09-19 06:03:47

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 25/26] ufs: Remove ufs_get_locked_page()

Both callers are now converted to ufs_get_locked_folio().

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/ufs/util.c | 9 ---------
fs/ufs/util.h | 7 -------
2 files changed, 16 deletions(-)

diff --git a/fs/ufs/util.c b/fs/ufs/util.c
index 151b400cb3b6..d32de30009a0 100644
--- a/fs/ufs/util.c
+++ b/fs/ufs/util.c
@@ -229,15 +229,6 @@ ufs_set_inode_dev(struct super_block *sb, struct ufs_inode_info *ufsi, dev_t dev
ufsi->i_u1.i_data[0] = cpu_to_fs32(sb, fs32);
}

-struct page *ufs_get_locked_page(struct address_space *mapping, pgoff_t index)
-{
- struct folio *folio = ufs_get_locked_folio(mapping, index);
-
- if (folio)
- return folio_file_page(folio, index);
- return NULL;
-}
-
/**
* ufs_get_locked_folio() - locate, pin and lock a pagecache folio, if not exist
* read it from disk.
diff --git a/fs/ufs/util.h b/fs/ufs/util.h
index 62542561d150..0ecd2ed792f5 100644
--- a/fs/ufs/util.h
+++ b/fs/ufs/util.h
@@ -273,7 +273,6 @@ extern void _ubh_ubhcpymem_(struct ufs_sb_private_info *, unsigned char *, struc
extern void _ubh_memcpyubh_(struct ufs_sb_private_info *, struct ufs_buffer_head *, unsigned char *, unsigned);

/* This functions works with cache pages*/
-struct page *ufs_get_locked_page(struct address_space *mapping, pgoff_t index);
struct folio *ufs_get_locked_folio(struct address_space *mapping, pgoff_t index);
static inline void ufs_put_locked_folio(struct folio *folio)
{
@@ -281,12 +280,6 @@ static inline void ufs_put_locked_folio(struct folio *folio)
folio_put(folio);
}

-static inline void ufs_put_locked_page(struct page *page)
-{
- ufs_put_locked_folio(page_folio(page));
-}
-
-
/*
* macros and inline function to get important structures from ufs_sb_private_info
*/
--
2.40.1

2023-09-19 06:06:16

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 17/26] ntfs: Convert ntfs_writepage to use a folio

use folio APIs throughout. Saves many hidden calls to compound_head().

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/ntfs/aops.c | 211 +++++++++++++++++++++++--------------------------
1 file changed, 100 insertions(+), 111 deletions(-)

diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c
index d66a9f5ffde9..c4426992a2ee 100644
--- a/fs/ntfs/aops.c
+++ b/fs/ntfs/aops.c
@@ -501,28 +501,29 @@ static int ntfs_read_folio(struct file *file, struct folio *folio)
#ifdef NTFS_RW

/**
- * ntfs_write_block - write a @page to the backing store
- * @page: page cache page to write out
+ * ntfs_write_block - write a @folio to the backing store
+ * @folio: page cache folio to write out
* @wbc: writeback control structure
*
- * This function is for writing pages belonging to non-resident, non-mst
+ * This function is for writing folios belonging to non-resident, non-mst
* protected attributes to their backing store.
*
- * For a page with buffers, map and write the dirty buffers asynchronously
- * under page writeback. For a page without buffers, create buffers for the
- * page, then proceed as above.
+ * For a folio with buffers, map and write the dirty buffers asynchronously
+ * under folio writeback. For a folio without buffers, create buffers for the
+ * folio, then proceed as above.
*
- * If a page doesn't have buffers the page dirty state is definitive. If a page
- * does have buffers, the page dirty state is just a hint, and the buffer dirty
- * state is definitive. (A hint which has rules: dirty buffers against a clean
- * page is illegal. Other combinations are legal and need to be handled. In
- * particular a dirty page containing clean buffers for example.)
+ * If a folio doesn't have buffers the folio dirty state is definitive. If
+ * a folio does have buffers, the folio dirty state is just a hint,
+ * and the buffer dirty state is definitive. (A hint which has rules:
+ * dirty buffers against a clean folio is illegal. Other combinations are
+ * legal and need to be handled. In particular a dirty folio containing
+ * clean buffers for example.)
*
* Return 0 on success and -errno on error.
*
* Based on ntfs_read_block() and __block_write_full_folio().
*/
-static int ntfs_write_block(struct page *page, struct writeback_control *wbc)
+static int ntfs_write_block(struct folio *folio, struct writeback_control *wbc)
{
VCN vcn;
LCN lcn;
@@ -540,41 +541,29 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc)
bool need_end_writeback;
unsigned char blocksize_bits;

- vi = page->mapping->host;
+ vi = folio->mapping->host;
ni = NTFS_I(vi);
vol = ni->vol;

ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index "
- "0x%lx.", ni->mft_no, ni->type, page->index);
+ "0x%lx.", ni->mft_no, ni->type, folio->index);

BUG_ON(!NInoNonResident(ni));
BUG_ON(NInoMstProtected(ni));
blocksize = vol->sb->s_blocksize;
blocksize_bits = vol->sb->s_blocksize_bits;
- if (!page_has_buffers(page)) {
- BUG_ON(!PageUptodate(page));
- create_empty_buffers(page, blocksize,
+ head = folio_buffers(folio);
+ if (!head) {
+ BUG_ON(!folio_test_uptodate(folio));
+ head = folio_create_empty_buffers(folio, blocksize,
(1 << BH_Uptodate) | (1 << BH_Dirty));
- if (unlikely(!page_has_buffers(page))) {
- ntfs_warning(vol->sb, "Error allocating page "
- "buffers. Redirtying page so we try "
- "again later.");
- /*
- * Put the page back on mapping->dirty_pages, but leave
- * its buffers' dirty state as-is.
- */
- redirty_page_for_writepage(wbc, page);
- unlock_page(page);
- return 0;
- }
}
- bh = head = page_buffers(page);
- BUG_ON(!bh);
+ bh = head;

/* NOTE: Different naming scheme to ntfs_read_block()! */

- /* The first block in the page. */
- block = (s64)page->index << (PAGE_SHIFT - blocksize_bits);
+ /* The first block in the folio. */
+ block = (s64)folio->index << (PAGE_SHIFT - blocksize_bits);

read_lock_irqsave(&ni->size_lock, flags);
i_size = i_size_read(vi);
@@ -591,14 +580,14 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc)
* Be very careful. We have no exclusion from block_dirty_folio
* here, and the (potentially unmapped) buffers may become dirty at
* any time. If a buffer becomes dirty here after we've inspected it
- * then we just miss that fact, and the page stays dirty.
+ * then we just miss that fact, and the folio stays dirty.
*
* Buffers outside i_size may be dirtied by block_dirty_folio;
* handle that here by just cleaning them.
*/

/*
- * Loop through all the buffers in the page, mapping all the dirty
+ * Loop through all the buffers in the folio, mapping all the dirty
* buffers to disk addresses and handling any aliases from the
* underlying block device's mapping.
*/
@@ -610,13 +599,13 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc)
if (unlikely(block >= dblock)) {
/*
* Mapped buffers outside i_size will occur, because
- * this page can be outside i_size when there is a
+ * this folio can be outside i_size when there is a
* truncate in progress. The contents of such buffers
* were zeroed by ntfs_writepage().
*
* FIXME: What about the small race window where
* ntfs_writepage() has not done any clearing because
- * the page was within i_size but before we get here,
+ * the folio was within i_size but before we get here,
* vmtruncate() modifies i_size?
*/
clear_buffer_dirty(bh);
@@ -632,38 +621,38 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc)
if (unlikely((block >= iblock) &&
(initialized_size < i_size))) {
/*
- * If this page is fully outside initialized
- * size, zero out all pages between the current
- * initialized size and the current page. Just
+ * If this folio is fully outside initialized
+ * size, zero out all folios between the current
+ * initialized size and the current folio. Just
* use ntfs_read_folio() to do the zeroing
* transparently.
*/
if (block > iblock) {
// TODO:
- // For each page do:
- // - read_cache_page()
- // Again for each page do:
- // - wait_on_page_locked()
- // - Check (PageUptodate(page) &&
- // !PageError(page))
+ // For each folio do:
+ // - read_cache_folio()
+ // Again for each folio do:
+ // - wait_on_folio_locked()
+ // - Check (folio_test_uptodate(folio) &&
+ // !folio_test_error(folio))
// Update initialized size in the attribute and
// in the inode.
- // Again, for each page do:
+ // Again, for each folio do:
// block_dirty_folio();
- // put_page()
+ // folio_put()
// We don't need to wait on the writes.
// Update iblock.
}
/*
- * The current page straddles initialized size. Zero
+ * The current folio straddles initialized size. Zero
* all non-uptodate buffers and set them uptodate (and
* dirty?). Note, there aren't any non-uptodate buffers
- * if the page is uptodate.
- * FIXME: For an uptodate page, the buffers may need to
+ * if the folio is uptodate.
+ * FIXME: For an uptodate folio, the buffers may need to
* be written out because they were not initialized on
* disk before.
*/
- if (!PageUptodate(page)) {
+ if (!folio_test_uptodate(folio)) {
// TODO:
// Zero any non-uptodate buffers up to i_size.
// Set them uptodate and dirty.
@@ -721,14 +710,14 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc)
unsigned long *bpos, *bend;

/* Check if the buffer is zero. */
- kaddr = kmap_atomic(page);
- bpos = (unsigned long *)(kaddr + bh_offset(bh));
- bend = (unsigned long *)((u8*)bpos + blocksize);
+ kaddr = kmap_local_folio(folio, bh_offset(bh));
+ bpos = (unsigned long *)kaddr;
+ bend = (unsigned long *)(kaddr + blocksize);
do {
if (unlikely(*bpos))
break;
} while (likely(++bpos < bend));
- kunmap_atomic(kaddr);
+ kunmap_local(kaddr);
if (bpos == bend) {
/*
* Buffer is zero and sparse, no need to write
@@ -768,7 +757,7 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc)
if (err == -ENOENT || lcn == LCN_ENOENT) {
bh->b_blocknr = -1;
clear_buffer_dirty(bh);
- zero_user(page, bh_offset(bh), blocksize);
+ folio_zero_range(folio, bh_offset(bh), blocksize);
set_buffer_uptodate(bh);
err = 0;
continue;
@@ -795,7 +784,7 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc)
bh = head;

/* Just an optimization, so ->read_folio() is not called later. */
- if (unlikely(!PageUptodate(page))) {
+ if (unlikely(!folio_test_uptodate(folio))) {
int uptodate = 1;
do {
if (!buffer_uptodate(bh)) {
@@ -805,7 +794,7 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc)
}
} while ((bh = bh->b_this_page) != head);
if (uptodate)
- SetPageUptodate(page);
+ folio_mark_uptodate(folio);
}

/* Setup all mapped, dirty buffers for async write i/o. */
@@ -820,7 +809,7 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc)
} else if (unlikely(err)) {
/*
* For the error case. The buffer may have been set
- * dirty during attachment to a dirty page.
+ * dirty during attachment to a dirty folio.
*/
if (err != -ENOMEM)
clear_buffer_dirty(bh);
@@ -833,20 +822,20 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc)
err = 0;
else if (err == -ENOMEM) {
ntfs_warning(vol->sb, "Error allocating memory. "
- "Redirtying page so we try again "
+ "Redirtying folio so we try again "
"later.");
/*
- * Put the page back on mapping->dirty_pages, but
+ * Put the folio back on mapping->dirty_pages, but
* leave its buffer's dirty state as-is.
*/
- redirty_page_for_writepage(wbc, page);
+ folio_redirty_for_writepage(wbc, folio);
err = 0;
} else
- SetPageError(page);
+ folio_set_error(folio);
}

- BUG_ON(PageWriteback(page));
- set_page_writeback(page); /* Keeps try_to_free_buffers() away. */
+ BUG_ON(folio_test_writeback(folio));
+ folio_start_writeback(folio); /* Keeps try_to_free_buffers() away. */

/* Submit the prepared buffers for i/o. */
need_end_writeback = true;
@@ -858,11 +847,11 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc)
}
bh = next;
} while (bh != head);
- unlock_page(page);
+ folio_unlock(folio);

- /* If no i/o was started, need to end_page_writeback(). */
+ /* If no i/o was started, need to end writeback here. */
if (unlikely(need_end_writeback))
- end_page_writeback(page);
+ folio_end_writeback(folio);

ntfs_debug("Done.");
return err;
@@ -1331,8 +1320,9 @@ static int ntfs_write_mst_block(struct page *page,
*/
static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
{
+ struct folio *folio = page_folio(page);
loff_t i_size;
- struct inode *vi = page->mapping->host;
+ struct inode *vi = folio->mapping->host;
ntfs_inode *base_ni = NULL, *ni = NTFS_I(vi);
char *addr;
ntfs_attr_search_ctx *ctx = NULL;
@@ -1341,14 +1331,13 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
int err;

retry_writepage:
- BUG_ON(!PageLocked(page));
+ BUG_ON(!folio_test_locked(folio));
i_size = i_size_read(vi);
- /* Is the page fully outside i_size? (truncate in progress) */
- if (unlikely(page->index >= (i_size + PAGE_SIZE - 1) >>
+ /* Is the folio fully outside i_size? (truncate in progress) */
+ if (unlikely(folio->index >= (i_size + PAGE_SIZE - 1) >>
PAGE_SHIFT)) {
- struct folio *folio = page_folio(page);
/*
- * The page may have dirty, unmapped buffers. Make them
+ * The folio may have dirty, unmapped buffers. Make them
* freeable here, so the page does not leak.
*/
block_invalidate_folio(folio, 0, folio_size(folio));
@@ -1367,7 +1356,7 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
if (ni->type != AT_INDEX_ALLOCATION) {
/* If file is encrypted, deny access, just like NT4. */
if (NInoEncrypted(ni)) {
- unlock_page(page);
+ folio_unlock(folio);
BUG_ON(ni->type != AT_DATA);
ntfs_debug("Denying write access to encrypted file.");
return -EACCES;
@@ -1378,14 +1367,14 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
BUG_ON(ni->name_len);
// TODO: Implement and replace this with
// return ntfs_write_compressed_block(page);
- unlock_page(page);
+ folio_unlock(folio);
ntfs_error(vi->i_sb, "Writing to compressed files is "
"not supported yet. Sorry.");
return -EOPNOTSUPP;
}
// TODO: Implement and remove this check.
if (NInoNonResident(ni) && NInoSparse(ni)) {
- unlock_page(page);
+ folio_unlock(folio);
ntfs_error(vi->i_sb, "Writing to sparse files is not "
"supported yet. Sorry.");
return -EOPNOTSUPP;
@@ -1394,34 +1383,34 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
/* NInoNonResident() == NInoIndexAllocPresent() */
if (NInoNonResident(ni)) {
/* We have to zero every time due to mmap-at-end-of-file. */
- if (page->index >= (i_size >> PAGE_SHIFT)) {
- /* The page straddles i_size. */
- unsigned int ofs = i_size & ~PAGE_MASK;
- zero_user_segment(page, ofs, PAGE_SIZE);
+ if (folio->index >= (i_size >> PAGE_SHIFT)) {
+ /* The folio straddles i_size. */
+ unsigned int ofs = i_size & (folio_size(folio) - 1);
+ folio_zero_segment(folio, ofs, folio_size(folio));
}
/* Handle mst protected attributes. */
if (NInoMstProtected(ni))
return ntfs_write_mst_block(page, wbc);
/* Normal, non-resident data stream. */
- return ntfs_write_block(page, wbc);
+ return ntfs_write_block(folio, wbc);
}
/*
* Attribute is resident, implying it is not compressed, encrypted, or
* mst protected. This also means the attribute is smaller than an mft
- * record and hence smaller than a page, so can simply return error on
- * any pages with index above 0. Note the attribute can actually be
+ * record and hence smaller than a folio, so can simply return error on
+ * any folios with index above 0. Note the attribute can actually be
* marked compressed but if it is resident the actual data is not
* compressed so we are ok to ignore the compressed flag here.
*/
- BUG_ON(page_has_buffers(page));
- BUG_ON(!PageUptodate(page));
- if (unlikely(page->index > 0)) {
- ntfs_error(vi->i_sb, "BUG()! page->index (0x%lx) > 0. "
- "Aborting write.", page->index);
- BUG_ON(PageWriteback(page));
- set_page_writeback(page);
- unlock_page(page);
- end_page_writeback(page);
+ BUG_ON(folio_buffers(folio));
+ BUG_ON(!folio_test_uptodate(folio));
+ if (unlikely(folio->index > 0)) {
+ ntfs_error(vi->i_sb, "BUG()! folio->index (0x%lx) > 0. "
+ "Aborting write.", folio->index);
+ BUG_ON(folio_test_writeback(folio));
+ folio_start_writeback(folio);
+ folio_unlock(folio);
+ folio_end_writeback(folio);
return -EIO;
}
if (!NInoAttr(ni))
@@ -1454,12 +1443,12 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
if (unlikely(err))
goto err_out;
/*
- * Keep the VM happy. This must be done otherwise the radix-tree tag
- * PAGECACHE_TAG_DIRTY remains set even though the page is clean.
+ * Keep the VM happy. This must be done otherwise
+ * PAGECACHE_TAG_DIRTY remains set even though the folio is clean.
*/
- BUG_ON(PageWriteback(page));
- set_page_writeback(page);
- unlock_page(page);
+ BUG_ON(folio_test_writeback(folio));
+ folio_start_writeback(folio);
+ folio_unlock(folio);
attr_len = le32_to_cpu(ctx->attr->data.resident.value_length);
i_size = i_size_read(vi);
if (unlikely(attr_len > i_size)) {
@@ -1474,18 +1463,18 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
/* Shrinking cannot fail. */
BUG_ON(err);
}
- addr = kmap_atomic(page);
- /* Copy the data from the page to the mft record. */
+ addr = kmap_local_folio(folio, 0);
+ /* Copy the data from the folio to the mft record. */
memcpy((u8*)ctx->attr +
le16_to_cpu(ctx->attr->data.resident.value_offset),
addr, attr_len);
- /* Zero out of bounds area in the page cache page. */
- memset(addr + attr_len, 0, PAGE_SIZE - attr_len);
- kunmap_atomic(addr);
- flush_dcache_page(page);
+ /* Zero out of bounds area in the page cache folio. */
+ memset(addr + attr_len, 0, folio_size(folio) - attr_len);
+ kunmap_local(addr);
+ flush_dcache_folio(folio);
flush_dcache_mft_record_page(ctx->ntfs_ino);
- /* We are done with the page. */
- end_page_writeback(page);
+ /* We are done with the folio. */
+ folio_end_writeback(folio);
/* Finally, mark the mft record dirty, so it gets written back. */
mark_mft_record_dirty(ctx->ntfs_ino);
ntfs_attr_put_search_ctx(ctx);
@@ -1496,18 +1485,18 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
ntfs_warning(vi->i_sb, "Error allocating memory. Redirtying "
"page so we try again later.");
/*
- * Put the page back on mapping->dirty_pages, but leave its
+ * Put the folio back on mapping->dirty_pages, but leave its
* buffers' dirty state as-is.
*/
- redirty_page_for_writepage(wbc, page);
+ folio_redirty_for_writepage(wbc, folio);
err = 0;
} else {
ntfs_error(vi->i_sb, "Resident attribute write failed with "
"error %i.", err);
- SetPageError(page);
+ folio_set_error(folio);
NVolSetErrors(ni->vol);
}
- unlock_page(page);
+ folio_unlock(folio);
if (ctx)
ntfs_attr_put_search_ctx(ctx);
if (m)
--
2.40.1

2023-09-19 06:13:57

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 13/26] nilfs2: Convert nilfs_mdt_get_frozen_buffer to use a folio

Remove a number of folio->page->folio conversions.

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

diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c
index 11b7cf4acc92..7b754e6494d7 100644
--- a/fs/nilfs2/mdt.c
+++ b/fs/nilfs2/mdt.c
@@ -592,17 +592,19 @@ nilfs_mdt_get_frozen_buffer(struct inode *inode, struct buffer_head *bh)
{
struct nilfs_shadow_map *shadow = NILFS_MDT(inode)->mi_shadow;
struct buffer_head *bh_frozen = NULL;
- struct page *page;
+ struct folio *folio;
int n;

- page = find_lock_page(shadow->inode->i_mapping, bh->b_folio->index);
- if (page) {
- if (page_has_buffers(page)) {
+ folio = filemap_lock_folio(shadow->inode->i_mapping,
+ bh->b_folio->index);
+ if (!IS_ERR(folio)) {
+ bh_frozen = folio_buffers(folio);
+ if (bh_frozen) {
n = bh_offset(bh) >> inode->i_blkbits;
- bh_frozen = nilfs_page_get_nth_block(page, n);
+ bh_frozen = get_nth_bh(bh_frozen, n);
}
- unlock_page(page);
- put_page(page);
+ folio_unlock(folio);
+ folio_put(folio);
}
return bh_frozen;
}
--
2.40.1

2023-09-19 06:45:37

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 12/26] nilfs2: Convert nilfs_mdt_forget_block() to use a folio

Remove a number of folio->page->folio conversions.

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

diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c
index db2260d6e44d..11b7cf4acc92 100644
--- a/fs/nilfs2/mdt.c
+++ b/fs/nilfs2/mdt.c
@@ -356,30 +356,28 @@ int nilfs_mdt_delete_block(struct inode *inode, unsigned long block)
*/
int nilfs_mdt_forget_block(struct inode *inode, unsigned long block)
{
- pgoff_t index = (pgoff_t)block >>
- (PAGE_SHIFT - inode->i_blkbits);
- struct page *page;
- unsigned long first_block;
+ pgoff_t index = block >> (PAGE_SHIFT - inode->i_blkbits);
+ struct folio *folio;
+ struct buffer_head *bh;
int ret = 0;
int still_dirty;

- page = find_lock_page(inode->i_mapping, index);
- if (!page)
+ folio = filemap_lock_folio(inode->i_mapping, index);
+ if (IS_ERR(folio))
return -ENOENT;

- wait_on_page_writeback(page);
+ folio_wait_writeback(folio);

- first_block = (unsigned long)index <<
- (PAGE_SHIFT - inode->i_blkbits);
- if (page_has_buffers(page)) {
- struct buffer_head *bh;
-
- bh = nilfs_page_get_nth_block(page, block - first_block);
+ bh = folio_buffers(folio);
+ if (bh) {
+ unsigned long first_block = index <<
+ (PAGE_SHIFT - inode->i_blkbits);
+ bh = get_nth_bh(bh, block - first_block);
nilfs_forget_buffer(bh);
}
- still_dirty = PageDirty(page);
- unlock_page(page);
- put_page(page);
+ still_dirty = folio_test_dirty(folio);
+ folio_unlock(folio);
+ folio_put(folio);

if (still_dirty ||
invalidate_inode_pages2_range(inode->i_mapping, index, index) != 0)
--
2.40.1

2023-09-19 06:55:31

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 01/26] buffer: Make folio_create_empty_buffers() return a buffer_head

Almost all callers want to know the first BH that was allocated
for this folio. We already have that handy, so return it.

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/buffer.c | 24 +++++++++++++-----------
include/linux/buffer_head.h | 4 ++--
2 files changed, 15 insertions(+), 13 deletions(-)

diff --git a/fs/buffer.c b/fs/buffer.c
index ad2526dd7cb4..1b9e691714bd 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -1646,8 +1646,8 @@ EXPORT_SYMBOL(block_invalidate_folio);
* block_dirty_folio() via private_lock. try_to_free_buffers
* is already excluded via the folio lock.
*/
-void folio_create_empty_buffers(struct folio *folio, unsigned long blocksize,
- unsigned long b_state)
+struct buffer_head *folio_create_empty_buffers(struct folio *folio,
+ unsigned long blocksize, unsigned long b_state)
{
struct buffer_head *bh, *head, *tail;
gfp_t gfp = GFP_NOFS | __GFP_ACCOUNT | __GFP_NOFAIL;
@@ -1674,6 +1674,8 @@ void folio_create_empty_buffers(struct folio *folio, unsigned long blocksize,
}
folio_attach_private(folio, head);
spin_unlock(&folio->mapping->private_lock);
+
+ return head;
}
EXPORT_SYMBOL(folio_create_empty_buffers);

@@ -1775,13 +1777,15 @@ static struct buffer_head *folio_create_buffers(struct folio *folio,
struct inode *inode,
unsigned int b_state)
{
+ struct buffer_head *bh;
+
BUG_ON(!folio_test_locked(folio));

- if (!folio_buffers(folio))
- folio_create_empty_buffers(folio,
- 1 << READ_ONCE(inode->i_blkbits),
- b_state);
- return folio_buffers(folio);
+ bh = folio_buffers(folio);
+ if (!bh)
+ bh = folio_create_empty_buffers(folio,
+ 1 << READ_ONCE(inode->i_blkbits), b_state);
+ return bh;
}

/*
@@ -2671,10 +2675,8 @@ int block_truncate_page(struct address_space *mapping,
return PTR_ERR(folio);

bh = folio_buffers(folio);
- if (!bh) {
- folio_create_empty_buffers(folio, blocksize, 0);
- bh = folio_buffers(folio);
- }
+ if (!bh)
+ bh = folio_create_empty_buffers(folio, blocksize, 0);

/* Find the buffer that contains "offset" */
offset = offset_in_folio(folio, from);
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index 3dc4720e4773..1001244a8941 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -203,8 +203,8 @@ struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size,
bool retry);
void create_empty_buffers(struct page *, unsigned long,
unsigned long b_state);
-void folio_create_empty_buffers(struct folio *folio, unsigned long blocksize,
- unsigned long b_state);
+struct buffer_head *folio_create_empty_buffers(struct folio *folio,
+ unsigned long blocksize, unsigned long b_state);
void end_buffer_read_sync(struct buffer_head *bh, int uptodate);
void end_buffer_write_sync(struct buffer_head *bh, int uptodate);
void end_buffer_async_write(struct buffer_head *bh, int uptodate);
--
2.40.1

2023-09-19 07:06:08

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 15/26] nilfs2: Convert nilfs_lookup_dirty_data_buffers to use folio_create_empty_buffers

This function was already using a folio, so this update to the new API
removes a single folio->page->folio conversion.

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

diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c
index 7ec16879756e..94388fe83cf8 100644
--- a/fs/nilfs2/segment.c
+++ b/fs/nilfs2/segment.c
@@ -731,10 +731,9 @@ static size_t nilfs_lookup_dirty_data_buffers(struct inode *inode,
continue;
}
head = folio_buffers(folio);
- if (!head) {
- create_empty_buffers(&folio->page, i_blocksize(inode), 0);
- head = folio_buffers(folio);
- }
+ if (!head)
+ head = folio_create_empty_buffers(folio,
+ i_blocksize(inode), 0);
folio_unlock(folio);

bh = head;
--
2.40.1

2023-09-19 07:16:17

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 11/26] nilfs2: Convert nilfs_copy_page() to nilfs_copy_folio()

Both callers already have a folio, so pass it in and use it directly.
Removes a lot of hidden calls to compound_head().

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/nilfs2/page.c | 50 +++++++++++++++++++++++++-----------------------
1 file changed, 26 insertions(+), 24 deletions(-)

diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c
index 1c075bd906c9..696215d899bf 100644
--- a/fs/nilfs2/page.c
+++ b/fs/nilfs2/page.c
@@ -184,30 +184,32 @@ void nilfs_page_bug(struct page *page)
}

/**
- * nilfs_copy_page -- copy the page with buffers
- * @dst: destination page
- * @src: source page
- * @copy_dirty: flag whether to copy dirty states on the page's buffer heads.
+ * nilfs_copy_folio -- copy the folio with buffers
+ * @dst: destination folio
+ * @src: source folio
+ * @copy_dirty: flag whether to copy dirty states on the folio's buffer heads.
*
- * This function is for both data pages and btnode pages. The dirty flag
- * should be treated by caller. The page must not be under i/o.
- * Both src and dst page must be locked
+ * This function is for both data folios and btnode folios. The dirty flag
+ * should be treated by caller. The folio must not be under i/o.
+ * Both src and dst folio must be locked
*/
-static void nilfs_copy_page(struct page *dst, struct page *src, int copy_dirty)
+static void nilfs_copy_folio(struct folio *dst, struct folio *src,
+ bool copy_dirty)
{
struct buffer_head *dbh, *dbufs, *sbh;
unsigned long mask = NILFS_BUFFER_INHERENT_BITS;

- BUG_ON(PageWriteback(dst));
+ BUG_ON(folio_test_writeback(dst));

- sbh = page_buffers(src);
- if (!page_has_buffers(dst))
- create_empty_buffers(dst, sbh->b_size, 0);
+ sbh = folio_buffers(src);
+ dbh = folio_buffers(dst);
+ if (!dbh)
+ dbh = folio_create_empty_buffers(dst, sbh->b_size, 0);

if (copy_dirty)
mask |= BIT(BH_Dirty);

- dbh = dbufs = page_buffers(dst);
+ dbufs = dbh;
do {
lock_buffer(sbh);
lock_buffer(dbh);
@@ -218,16 +220,16 @@ static void nilfs_copy_page(struct page *dst, struct page *src, int copy_dirty)
dbh = dbh->b_this_page;
} while (dbh != dbufs);

- copy_highpage(dst, src);
+ folio_copy(dst, src);

- if (PageUptodate(src) && !PageUptodate(dst))
- SetPageUptodate(dst);
- else if (!PageUptodate(src) && PageUptodate(dst))
- ClearPageUptodate(dst);
- if (PageMappedToDisk(src) && !PageMappedToDisk(dst))
- SetPageMappedToDisk(dst);
- else if (!PageMappedToDisk(src) && PageMappedToDisk(dst))
- ClearPageMappedToDisk(dst);
+ if (folio_test_uptodate(src) && !folio_test_uptodate(dst))
+ folio_mark_uptodate(dst);
+ else if (!folio_test_uptodate(src) && folio_test_uptodate(dst))
+ folio_clear_uptodate(dst);
+ if (folio_test_mappedtodisk(src) && !folio_test_mappedtodisk(dst))
+ folio_set_mappedtodisk(dst);
+ else if (!folio_test_mappedtodisk(src) && folio_test_mappedtodisk(dst))
+ folio_clear_mappedtodisk(dst);

do {
unlock_buffer(sbh);
@@ -269,7 +271,7 @@ int nilfs_copy_dirty_pages(struct address_space *dmap,
NILFS_PAGE_BUG(&folio->page,
"found empty page in dat page cache");

- nilfs_copy_page(&dfolio->page, &folio->page, 1);
+ nilfs_copy_folio(dfolio, folio, true);
filemap_dirty_folio(folio_mapping(dfolio), dfolio);

folio_unlock(dfolio);
@@ -314,7 +316,7 @@ void nilfs_copy_back_pages(struct address_space *dmap,
if (!IS_ERR(dfolio)) {
/* overwrite existing folio in the destination cache */
WARN_ON(folio_test_dirty(dfolio));
- nilfs_copy_page(&dfolio->page, &folio->page, 0);
+ nilfs_copy_folio(dfolio, folio, false);
folio_unlock(dfolio);
folio_put(dfolio);
/* Do we not need to remove folio from smap here? */
--
2.40.1

2023-09-19 07:17:02

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 09/26] nilfs2: Convert nilfs_mdt_freeze_buffer to use a folio

Remove a number of folio->page->folio conversions.

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

diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c
index 19c8158605ed..db2260d6e44d 100644
--- a/fs/nilfs2/mdt.c
+++ b/fs/nilfs2/mdt.c
@@ -560,17 +560,19 @@ int nilfs_mdt_freeze_buffer(struct inode *inode, struct buffer_head *bh)
{
struct nilfs_shadow_map *shadow = NILFS_MDT(inode)->mi_shadow;
struct buffer_head *bh_frozen;
- struct page *page;
+ struct folio *folio;
int blkbits = inode->i_blkbits;

- page = grab_cache_page(shadow->inode->i_mapping, bh->b_folio->index);
- if (!page)
- return -ENOMEM;
+ folio = filemap_grab_folio(shadow->inode->i_mapping,
+ bh->b_folio->index);
+ if (IS_ERR(folio))
+ return PTR_ERR(folio);

- if (!page_has_buffers(page))
- create_empty_buffers(page, 1 << blkbits, 0);
+ bh_frozen = folio_buffers(folio);
+ if (!bh_frozen)
+ bh_frozen = folio_create_empty_buffers(folio, 1 << blkbits, 0);

- bh_frozen = nilfs_page_get_nth_block(page, bh_offset(bh) >> blkbits);
+ bh_frozen = get_nth_bh(bh_frozen, bh_offset(bh) >> blkbits);

if (!buffer_uptodate(bh_frozen))
nilfs_copy_buffer(bh_frozen, bh);
@@ -582,8 +584,8 @@ int nilfs_mdt_freeze_buffer(struct inode *inode, struct buffer_head *bh)
brelse(bh_frozen); /* already frozen */
}

- unlock_page(page);
- put_page(page);
+ folio_unlock(folio);
+ folio_put(folio);
return 0;
}

--
2.40.1

2023-09-19 07:49:04

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 21/26] reiserfs: Convert writepage to use a folio

Convert the incoming page to a folio and then use it throughout the
writeback path. This definitely isn't enough to support large folios, but
I don't expect reiserfs to gain support for those before it is removed.

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

diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
index 86e55d4bb10d..d9737235b8e0 100644
--- a/fs/reiserfs/inode.c
+++ b/fs/reiserfs/inode.c
@@ -2507,10 +2507,10 @@ static int map_block_for_writepage(struct inode *inode,
* start/recovery path as __block_write_full_folio, along with special
* code to handle reiserfs tails.
*/
-static int reiserfs_write_full_page(struct page *page,
+static int reiserfs_write_full_folio(struct folio *folio,
struct writeback_control *wbc)
{
- struct inode *inode = page->mapping->host;
+ struct inode *inode = folio->mapping->host;
unsigned long end_index = inode->i_size >> PAGE_SHIFT;
int error = 0;
unsigned long block;
@@ -2518,7 +2518,7 @@ static int reiserfs_write_full_page(struct page *page,
struct buffer_head *head, *bh;
int partial = 0;
int nr = 0;
- int checked = PageChecked(page);
+ int checked = folio_test_checked(folio);
struct reiserfs_transaction_handle th;
struct super_block *s = inode->i_sb;
int bh_per_page = PAGE_SIZE / s->s_blocksize;
@@ -2526,47 +2526,46 @@ static int reiserfs_write_full_page(struct page *page,

/* no logging allowed when nonblocking or from PF_MEMALLOC */
if (checked && (current->flags & PF_MEMALLOC)) {
- redirty_page_for_writepage(wbc, page);
- unlock_page(page);
+ folio_redirty_for_writepage(wbc, folio);
+ folio_unlock(folio);
return 0;
}

/*
- * The page dirty bit is cleared before writepage is called, which
+ * The folio dirty bit is cleared before writepage is called, which
* means we have to tell create_empty_buffers to make dirty buffers
- * The page really should be up to date at this point, so tossing
+ * The folio really should be up to date at this point, so tossing
* in the BH_Uptodate is just a sanity check.
*/
- if (!page_has_buffers(page)) {
- create_empty_buffers(page, s->s_blocksize,
+ head = folio_buffers(folio);
+ if (!head)
+ head = folio_create_empty_buffers(folio, s->s_blocksize,
(1 << BH_Dirty) | (1 << BH_Uptodate));
- }
- head = page_buffers(page);

/*
- * last page in the file, zero out any contents past the
+ * last folio in the file, zero out any contents past the
* last byte in the file
*/
- if (page->index >= end_index) {
+ if (folio->index >= end_index) {
unsigned last_offset;

last_offset = inode->i_size & (PAGE_SIZE - 1);
- /* no file contents in this page */
- if (page->index >= end_index + 1 || !last_offset) {
- unlock_page(page);
+ /* no file contents in this folio */
+ if (folio->index >= end_index + 1 || !last_offset) {
+ folio_unlock(folio);
return 0;
}
- zero_user_segment(page, last_offset, PAGE_SIZE);
+ folio_zero_segment(folio, last_offset, folio_size(folio));
}
bh = head;
- block = page->index << (PAGE_SHIFT - s->s_blocksize_bits);
+ block = folio->index << (PAGE_SHIFT - s->s_blocksize_bits);
last_block = (i_size_read(inode) - 1) >> inode->i_blkbits;
/* first map all the buffers, logging any direct items we find */
do {
if (block > last_block) {
/*
* This can happen when the block size is less than
- * the page size. The corresponding bytes in the page
+ * the folio size. The corresponding bytes in the folio
* were zero filled above
*/
clear_buffer_dirty(bh);
@@ -2593,7 +2592,7 @@ static int reiserfs_write_full_page(struct page *page,
* blocks we're going to log
*/
if (checked) {
- ClearPageChecked(page);
+ folio_clear_checked(folio);
reiserfs_write_lock(s);
error = journal_begin(&th, s, bh_per_page + 1);
if (error) {
@@ -2602,7 +2601,7 @@ static int reiserfs_write_full_page(struct page *page,
}
reiserfs_update_inode_transaction(inode);
}
- /* now go through and lock any dirty buffers on the page */
+ /* now go through and lock any dirty buffers on the folio */
do {
get_bh(bh);
if (!buffer_mapped(bh))
@@ -2623,7 +2622,7 @@ static int reiserfs_write_full_page(struct page *page,
lock_buffer(bh);
} else {
if (!trylock_buffer(bh)) {
- redirty_page_for_writepage(wbc, page);
+ folio_redirty_for_writepage(wbc, folio);
continue;
}
}
@@ -2640,13 +2639,13 @@ static int reiserfs_write_full_page(struct page *page,
if (error)
goto fail;
}
- BUG_ON(PageWriteback(page));
- set_page_writeback(page);
- unlock_page(page);
+ BUG_ON(folio_test_writeback(folio));
+ folio_start_writeback(folio);
+ folio_unlock(folio);

/*
- * since any buffer might be the only dirty buffer on the page,
- * the first submit_bh can bring the page out of writeback.
+ * since any buffer might be the only dirty buffer on the folio,
+ * the first submit_bh can bring the folio out of writeback.
* be careful with the buffers.
*/
do {
@@ -2663,10 +2662,10 @@ static int reiserfs_write_full_page(struct page *page,
done:
if (nr == 0) {
/*
- * if this page only had a direct item, it is very possible for
+ * if this folio only had a direct item, it is very possible for
* no io to be required without there being an error. Or,
* someone else could have locked them and sent them down the
- * pipe without locking the page
+ * pipe without locking the folio
*/
bh = head;
do {
@@ -2677,18 +2676,18 @@ static int reiserfs_write_full_page(struct page *page,
bh = bh->b_this_page;
} while (bh != head);
if (!partial)
- SetPageUptodate(page);
- end_page_writeback(page);
+ folio_mark_uptodate(folio);
+ folio_end_writeback(folio);
}
return error;

fail:
/*
* catches various errors, we need to make sure any valid dirty blocks
- * get to the media. The page is currently locked and not marked for
+ * get to the media. The folio is currently locked and not marked for
* writeback
*/
- ClearPageUptodate(page);
+ folio_clear_uptodate(folio);
bh = head;
do {
get_bh(bh);
@@ -2698,16 +2697,16 @@ static int reiserfs_write_full_page(struct page *page,
} else {
/*
* clear any dirty bits that might have come from
- * getting attached to a dirty page
+ * getting attached to a dirty folio
*/
clear_buffer_dirty(bh);
}
bh = bh->b_this_page;
} while (bh != head);
- SetPageError(page);
- BUG_ON(PageWriteback(page));
- set_page_writeback(page);
- unlock_page(page);
+ folio_set_error(folio);
+ BUG_ON(folio_test_writeback(folio));
+ folio_start_writeback(folio);
+ folio_unlock(folio);
do {
struct buffer_head *next = bh->b_this_page;
if (buffer_async_write(bh)) {
@@ -2728,9 +2727,10 @@ static int reiserfs_read_folio(struct file *f, struct folio *folio)

static int reiserfs_writepage(struct page *page, struct writeback_control *wbc)
{
- struct inode *inode = page->mapping->host;
+ struct folio *folio = page_folio(page);
+ struct inode *inode = folio->mapping->host;
reiserfs_wait_on_write_block(inode->i_sb);
- return reiserfs_write_full_page(page, wbc);
+ return reiserfs_write_full_folio(folio, wbc);
}

static void reiserfs_truncate_failed_write(struct inode *inode)
--
2.40.1

2023-09-19 08:51:54

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 19/26] ntfs3: Convert ntfs_zero_range() to use a folio

Use the folio API throughout, saving six hidden calls to compound_head().

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

diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c
index 962f12ce6c0a..a003a69091a2 100644
--- a/fs/ntfs3/file.c
+++ b/fs/ntfs3/file.c
@@ -187,7 +187,7 @@ static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to)
struct buffer_head *head, *bh;
u32 bh_next, bh_off, to;
sector_t iblock;
- struct page *page;
+ struct folio *folio;

for (; idx < idx_end; idx += 1, from = 0) {
page_off = (loff_t)idx << PAGE_SHIFT;
@@ -195,16 +195,17 @@ static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to)
PAGE_SIZE;
iblock = page_off >> inode->i_blkbits;

- page = find_or_create_page(mapping, idx,
- mapping_gfp_constraint(mapping,
- ~__GFP_FS));
- if (!page)
- return -ENOMEM;
+ folio = __filemap_get_folio(mapping, idx,
+ FGP_LOCK | FGP_ACCESSED | FGP_CREAT,
+ mapping_gfp_constraint(mapping, ~__GFP_FS));
+ if (IS_ERR(folio))
+ return PTR_ERR(folio);

- if (!page_has_buffers(page))
- create_empty_buffers(page, blocksize, 0);
+ head = folio_buffers(folio);
+ if (!head)
+ head = folio_create_empty_buffers(folio, blocksize, 0);

- bh = head = page_buffers(page);
+ bh = head;
bh_off = 0;
do {
bh_next = bh_off + blocksize;
@@ -220,14 +221,14 @@ static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to)
}

/* 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)) {
err = bh_read(bh, 0);
if (err < 0) {
- unlock_page(page);
- put_page(page);
+ folio_unlock(folio);
+ folio_put(folio);
goto out;
}
}
@@ -237,10 +238,10 @@ static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to)
} while (bh_off = bh_next, iblock += 1,
head != (bh = bh->b_this_page));

- zero_user_segment(page, from, to);
+ folio_zero_segment(folio, from, to);

- unlock_page(page);
- put_page(page);
+ folio_unlock(folio);
+ folio_put(folio);
cond_resched();
}
out:
--
2.40.1

2023-09-19 09:24:18

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 07/26] gfs2; Convert gfs2_getjdatabuf to use a folio

Use the folio APIs, saving four hidden calls to compound_head().

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

diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c
index f1fac1b45059..b28196015543 100644
--- a/fs/gfs2/meta_io.c
+++ b/fs/gfs2/meta_io.c
@@ -400,26 +400,20 @@ static struct buffer_head *gfs2_getjdatabuf(struct gfs2_inode *ip, u64 blkno)
{
struct address_space *mapping = ip->i_inode.i_mapping;
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
- struct page *page;
+ struct folio *folio;
struct buffer_head *bh;
unsigned int shift = PAGE_SHIFT - sdp->sd_sb.sb_bsize_shift;
unsigned long index = blkno >> shift; /* convert block to page */
unsigned int bufnum = blkno - (index << shift);

- page = find_get_page_flags(mapping, index, FGP_LOCK|FGP_ACCESSED);
- if (!page)
- return NULL;
- if (!page_has_buffers(page)) {
- unlock_page(page);
- put_page(page);
+ folio = __filemap_get_folio(mapping, index, FGP_LOCK | FGP_ACCESSED, 0);
+ if (IS_ERR(folio))
return NULL;
- }
- /* Locate header for our buffer within our page */
- for (bh = page_buffers(page); bufnum--; bh = bh->b_this_page)
- /* Do nothing */;
- get_bh(bh);
- unlock_page(page);
- put_page(page);
+ bh = folio_buffers(folio);
+ if (bh)
+ get_nth_bh(bh, bufnum);
+ folio_unlock(folio);
+ folio_put(folio);
return bh;
}

--
2.40.1

2023-09-19 10:31:19

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 02/26] mpage: Convert map_buffer_to_folio() to folio_create_empty_buffers()

Saves a folio->page->folio conversion.

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/mpage.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/fs/mpage.c b/fs/mpage.c
index 242e213ee064..964a6efe594d 100644
--- a/fs/mpage.c
+++ b/fs/mpage.c
@@ -119,8 +119,7 @@ static void map_buffer_to_folio(struct folio *folio, struct buffer_head *bh,
folio_mark_uptodate(folio);
return;
}
- create_empty_buffers(&folio->page, i_blocksize(inode), 0);
- head = folio_buffers(folio);
+ head = folio_create_empty_buffers(folio, i_blocksize(inode), 0);
}

page_bh = head;
--
2.40.1

2023-09-19 10:46:08

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 10/26] nilfs2: Convert nilfs_grab_buffer() to use a folio

Remove a number of folio->page->folio conversions.

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

diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c
index b4e54d079b7d..1c075bd906c9 100644
--- a/fs/nilfs2/page.c
+++ b/fs/nilfs2/page.c
@@ -25,19 +25,19 @@
(BIT(BH_Uptodate) | BIT(BH_Mapped) | BIT(BH_NILFS_Node) | \
BIT(BH_NILFS_Volatile) | BIT(BH_NILFS_Checked))

-static struct buffer_head *
-__nilfs_get_page_block(struct page *page, unsigned long block, pgoff_t index,
- int blkbits, unsigned long b_state)
+static struct buffer_head *__nilfs_get_folio_block(struct folio *folio,
+ unsigned long block, pgoff_t index, int blkbits,
+ unsigned long b_state)

{
unsigned long first_block;
- struct buffer_head *bh;
+ struct buffer_head *bh = folio_buffers(folio);

- if (!page_has_buffers(page))
- create_empty_buffers(page, 1 << blkbits, b_state);
+ if (!bh)
+ bh = folio_create_empty_buffers(folio, 1 << blkbits, b_state);

first_block = (unsigned long)index << (PAGE_SHIFT - blkbits);
- bh = nilfs_page_get_nth_block(page, block - first_block);
+ bh = get_nth_bh(bh, block - first_block);

touch_buffer(bh);
wait_on_buffer(bh);
@@ -51,17 +51,17 @@ struct buffer_head *nilfs_grab_buffer(struct inode *inode,
{
int blkbits = inode->i_blkbits;
pgoff_t index = blkoff >> (PAGE_SHIFT - blkbits);
- struct page *page;
+ struct folio *folio;
struct buffer_head *bh;

- page = grab_cache_page(mapping, index);
- if (unlikely(!page))
+ folio = filemap_grab_folio(mapping, index);
+ if (IS_ERR(folio))
return NULL;

- bh = __nilfs_get_page_block(page, blkoff, index, blkbits, b_state);
+ bh = __nilfs_get_folio_block(folio, blkoff, index, blkbits, b_state);
if (unlikely(!bh)) {
- unlock_page(page);
- put_page(page);
+ folio_unlock(folio);
+ folio_put(folio);
return NULL;
}
return bh;
--
2.40.1

2023-09-19 10:46:11

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 24/26] ufs; Convert ufs_change_blocknr() to use folios

Convert the locked_page argument to a folio, then use folios throughout.
Saves three hidden calls to compound_head().

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

diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c
index 2436e3f82147..53c11be2b2c1 100644
--- a/fs/ufs/balloc.c
+++ b/fs/ufs/balloc.c
@@ -240,6 +240,7 @@ static void ufs_change_blocknr(struct inode *inode, sector_t beg,
unsigned int count, sector_t oldb,
sector_t newb, struct page *locked_page)
{
+ struct folio *folio, *locked_folio = page_folio(locked_page);
const unsigned blks_per_page =
1 << (PAGE_SHIFT - inode->i_blkbits);
const unsigned mask = blks_per_page - 1;
@@ -247,42 +248,39 @@ static void ufs_change_blocknr(struct inode *inode, sector_t beg,
pgoff_t index, cur_index, last_index;
unsigned pos, j, lblock;
sector_t end, i;
- struct page *page;
struct buffer_head *head, *bh;

UFSD("ENTER, ino %lu, count %u, oldb %llu, newb %llu\n",
inode->i_ino, count,
(unsigned long long)oldb, (unsigned long long)newb);

- BUG_ON(!locked_page);
- BUG_ON(!PageLocked(locked_page));
+ BUG_ON(!folio_test_locked(locked_folio));

- cur_index = locked_page->index;
+ cur_index = locked_folio->index;
end = count + beg;
last_index = end >> (PAGE_SHIFT - inode->i_blkbits);
for (i = beg; i < end; i = (i | mask) + 1) {
index = i >> (PAGE_SHIFT - inode->i_blkbits);

if (likely(cur_index != index)) {
- page = ufs_get_locked_page(mapping, index);
- if (!page)/* it was truncated */
+ folio = ufs_get_locked_folio(mapping, index);
+ if (!folio) /* it was truncated */
continue;
- if (IS_ERR(page)) {/* or EIO */
+ if (IS_ERR(folio)) {/* or EIO */
ufs_error(inode->i_sb, __func__,
"read of page %llu failed\n",
(unsigned long long)index);
continue;
}
} else
- page = locked_page;
+ folio = locked_folio;

- head = page_buffers(page);
+ head = folio_buffers(folio);
bh = head;
pos = i & mask;
for (j = 0; j < pos; ++j)
bh = bh->b_this_page;

-
if (unlikely(index == last_index))
lblock = end & mask;
else
@@ -313,7 +311,7 @@ static void ufs_change_blocknr(struct inode *inode, sector_t beg,
} while (bh != head);

if (likely(cur_index != index))
- ufs_put_locked_page(page);
+ ufs_put_locked_folio(folio);
}
UFSD("EXIT\n");
}
--
2.40.1

2023-09-19 10:47:22

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 08/26] gfs2: Convert gfs2_write_buf_to_page() to use a folio

Remove several folio->page->folio conversions.

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/gfs2/quota.c | 37 +++++++++++++++++++------------------
1 file changed, 19 insertions(+), 18 deletions(-)

diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c
index 171b2713d2e5..0ee4865ebdca 100644
--- a/fs/gfs2/quota.c
+++ b/fs/gfs2/quota.c
@@ -736,7 +736,7 @@ static int gfs2_write_buf_to_page(struct gfs2_sbd *sdp, unsigned long index,
struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode);
struct inode *inode = &ip->i_inode;
struct address_space *mapping = inode->i_mapping;
- struct page *page;
+ struct folio *folio;
struct buffer_head *bh;
u64 blk;
unsigned bsize = sdp->sd_sb.sb_bsize, bnum = 0, boff = 0;
@@ -745,15 +745,15 @@ static int gfs2_write_buf_to_page(struct gfs2_sbd *sdp, unsigned long index,
blk = index << (PAGE_SHIFT - sdp->sd_sb.sb_bsize_shift);
boff = off % bsize;

- page = grab_cache_page(mapping, index);
- if (!page)
- return -ENOMEM;
- if (!page_has_buffers(page))
- create_empty_buffers(page, bsize, 0);
+ folio = filemap_grab_folio(mapping, index);
+ if (IS_ERR(folio))
+ return PTR_ERR(folio);
+ bh = folio_buffers(folio);
+ if (!bh)
+ bh = folio_create_empty_buffers(folio, bsize, 0);

- bh = page_buffers(page);
- for(;;) {
- /* Find the beginning block within the page */
+ for (;;) {
+ /* Find the beginning block within the folio */
if (pg_off >= ((bnum * bsize) + bsize)) {
bh = bh->b_this_page;
bnum++;
@@ -766,9 +766,10 @@ static int gfs2_write_buf_to_page(struct gfs2_sbd *sdp, unsigned long index,
goto unlock_out;
/* If it's a newly allocated disk block, zero it */
if (buffer_new(bh))
- zero_user(page, bnum * bsize, bh->b_size);
+ folio_zero_range(folio, bnum * bsize,
+ bh->b_size);
}
- if (PageUptodate(page))
+ if (folio_test_uptodate(folio))
set_buffer_uptodate(bh);
if (bh_read(bh, REQ_META | REQ_PRIO) < 0)
goto unlock_out;
@@ -784,17 +785,17 @@ static int gfs2_write_buf_to_page(struct gfs2_sbd *sdp, unsigned long index,
break;
}

- /* Write to the page, now that we have setup the buffer(s) */
- memcpy_to_page(page, off, buf, bytes);
- flush_dcache_page(page);
- unlock_page(page);
- put_page(page);
+ /* Write to the folio, now that we have setup the buffer(s) */
+ memcpy_to_folio(folio, off, buf, bytes);
+ flush_dcache_folio(folio);
+ folio_unlock(folio);
+ folio_put(folio);

return 0;

unlock_out:
- unlock_page(page);
- put_page(page);
+ folio_unlock(folio);
+ folio_put(folio);
return -EIO;
}

--
2.40.1

2023-09-19 12:15:12

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 20/26] ocfs2: Convert ocfs2_map_page_blocks to use a folio

Convert the page argument to a folio and then use the folio APIs
throughout. Replaces three hidden calls to compound_head() with one
explicit one.

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/ocfs2/aops.c | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c
index 0fdba30740ab..95d1e70b4401 100644
--- a/fs/ocfs2/aops.c
+++ b/fs/ocfs2/aops.c
@@ -568,10 +568,10 @@ static void ocfs2_clear_page_regions(struct page *page,
* read-in the blocks at the tail of our file. Avoid reading them by
* testing i_size against each block offset.
*/
-static int ocfs2_should_read_blk(struct inode *inode, struct page *page,
+static int ocfs2_should_read_blk(struct inode *inode, struct folio *folio,
unsigned int block_start)
{
- u64 offset = page_offset(page) + block_start;
+ u64 offset = folio_pos(folio) + block_start;

if (ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb)))
return 1;
@@ -593,15 +593,16 @@ int ocfs2_map_page_blocks(struct page *page, u64 *p_blkno,
struct inode *inode, unsigned int from,
unsigned int to, int new)
{
+ struct folio *folio = page_folio(page);
int ret = 0;
struct buffer_head *head, *bh, *wait[2], **wait_bh = wait;
unsigned int block_end, block_start;
unsigned int bsize = i_blocksize(inode);

- if (!page_has_buffers(page))
- create_empty_buffers(page, bsize, 0);
+ head = folio_buffers(folio);
+ if (!head)
+ head = folio_create_empty_buffers(folio, bsize, 0);

- head = page_buffers(page);
for (bh = head, block_start = 0; bh != head || !block_start;
bh = bh->b_this_page, block_start += bsize) {
block_end = block_start + bsize;
@@ -613,7 +614,7 @@ int ocfs2_map_page_blocks(struct page *page, u64 *p_blkno,
* they may belong to unallocated clusters.
*/
if (block_start >= to || block_end <= from) {
- if (PageUptodate(page))
+ if (folio_test_uptodate(folio))
set_buffer_uptodate(bh);
continue;
}
@@ -630,11 +631,11 @@ int ocfs2_map_page_blocks(struct page *page, u64 *p_blkno,
clean_bdev_bh_alias(bh);
}

- if (PageUptodate(page)) {
+ if (folio_test_uptodate(folio)) {
set_buffer_uptodate(bh);
} else if (!buffer_uptodate(bh) && !buffer_delay(bh) &&
!buffer_new(bh) &&
- ocfs2_should_read_blk(inode, page, block_start) &&
+ ocfs2_should_read_blk(inode, folio, block_start) &&
(block_start < from || block_end > to)) {
bh_read_nowait(bh, 0);
*wait_bh++=bh;
@@ -668,7 +669,7 @@ int ocfs2_map_page_blocks(struct page *page, u64 *p_blkno,
if (block_start >= to)
break;

- zero_user(page, block_start, bh->b_size);
+ folio_zero_range(folio, block_start, bh->b_size);
set_buffer_uptodate(bh);
mark_buffer_dirty(bh);

--
2.40.1

2023-09-19 13:39:55

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 16/26] ntfs: Convert ntfs_read_block() to use a folio

The caller already has the folio, so pass it in and use the folio API
throughout saving five hidden calls to compound_head().

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/ntfs/aops.c | 44 +++++++++++++++++++-------------------------
1 file changed, 19 insertions(+), 25 deletions(-)

diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c
index 4e158bce4192..d66a9f5ffde9 100644
--- a/fs/ntfs/aops.c
+++ b/fs/ntfs/aops.c
@@ -145,13 +145,12 @@ static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate)
}

/**
- * ntfs_read_block - fill a @page of an address space with data
- * @page: page cache page to fill with data
+ * ntfs_read_block - fill a @folio of an address space with data
+ * @folio: page cache folio to fill with data
*
- * Fill the page @page of the address space belonging to the @page->host inode.
* We read each buffer asynchronously and when all buffers are read in, our io
* completion handler ntfs_end_buffer_read_async(), if required, automatically
- * applies the mst fixups to the page before finally marking it uptodate and
+ * applies the mst fixups to the folio before finally marking it uptodate and
* unlocking it.
*
* We only enforce allocated_size limit because i_size is checked for in
@@ -161,7 +160,7 @@ static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate)
*
* Contains an adapted version of fs/buffer.c::block_read_full_folio().
*/
-static int ntfs_read_block(struct page *page)
+static int ntfs_read_block(struct folio *folio)
{
loff_t i_size;
VCN vcn;
@@ -178,7 +177,7 @@ static int ntfs_read_block(struct page *page)
int i, nr;
unsigned char blocksize_bits;

- vi = page->mapping->host;
+ vi = folio->mapping->host;
ni = NTFS_I(vi);
vol = ni->vol;

@@ -188,15 +187,10 @@ static int ntfs_read_block(struct page *page)
blocksize = vol->sb->s_blocksize;
blocksize_bits = vol->sb->s_blocksize_bits;

- if (!page_has_buffers(page)) {
- create_empty_buffers(page, blocksize, 0);
- if (unlikely(!page_has_buffers(page))) {
- unlock_page(page);
- return -ENOMEM;
- }
- }
- bh = head = page_buffers(page);
- BUG_ON(!bh);
+ head = folio_buffers(folio);
+ if (!head)
+ head = folio_create_empty_buffers(folio, blocksize, 0);
+ bh = head;

/*
* We may be racing with truncate. To avoid some of the problems we
@@ -205,11 +199,11 @@ static int ntfs_read_block(struct page *page)
* may leave some buffers unmapped which are now allocated. This is
* not a problem since these buffers will just get mapped when a write
* occurs. In case of a shrinking truncate, we will detect this later
- * on due to the runlist being incomplete and if the page is being
+ * on due to the runlist being incomplete and if the folio is being
* fully truncated, truncate will throw it away as soon as we unlock
* it so no need to worry what we do with it.
*/
- iblock = (s64)page->index << (PAGE_SHIFT - blocksize_bits);
+ iblock = (s64)folio->index << (PAGE_SHIFT - blocksize_bits);
read_lock_irqsave(&ni->size_lock, flags);
lblock = (ni->allocated_size + blocksize - 1) >> blocksize_bits;
init_size = ni->initialized_size;
@@ -221,7 +215,7 @@ static int ntfs_read_block(struct page *page)
}
zblock = (init_size + blocksize - 1) >> blocksize_bits;

- /* Loop through all the buffers in the page. */
+ /* Loop through all the buffers in the folio. */
rl = NULL;
nr = i = 0;
do {
@@ -299,7 +293,7 @@ static int ntfs_read_block(struct page *page)
if (!err)
err = -EIO;
bh->b_blocknr = -1;
- SetPageError(page);
+ folio_set_error(folio);
ntfs_error(vol->sb, "Failed to read from inode 0x%lx, "
"attribute type 0x%x, vcn 0x%llx, "
"offset 0x%x because its location on "
@@ -312,13 +306,13 @@ static int ntfs_read_block(struct page *page)
/*
* Either iblock was outside lblock limits or
* ntfs_rl_vcn_to_lcn() returned error. Just zero that portion
- * of the page and set the buffer uptodate.
+ * of the folio and set the buffer uptodate.
*/
handle_hole:
bh->b_blocknr = -1UL;
clear_buffer_mapped(bh);
handle_zblock:
- zero_user(page, i * blocksize, blocksize);
+ folio_zero_range(folio, i * blocksize, blocksize);
if (likely(!err))
set_buffer_uptodate(bh);
} while (i++, iblock++, (bh = bh->b_this_page) != head);
@@ -349,11 +343,11 @@ static int ntfs_read_block(struct page *page)
return 0;
}
/* No i/o was scheduled on any of the buffers. */
- if (likely(!PageError(page)))
- SetPageUptodate(page);
+ if (likely(!folio_test_error(folio)))
+ folio_mark_uptodate(folio);
else /* Signal synchronous i/o error. */
nr = -EIO;
- unlock_page(page);
+ folio_unlock(folio);
return nr;
}

@@ -433,7 +427,7 @@ static int ntfs_read_folio(struct file *file, struct folio *folio)
/* NInoNonResident() == NInoIndexAllocPresent() */
if (NInoNonResident(ni)) {
/* Normal, non-resident data stream. */
- return ntfs_read_block(page);
+ return ntfs_read_block(folio);
}
/*
* Attribute is resident, implying it is not compressed or encrypted.
--
2.40.1

2023-09-19 14:08:11

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 04/26] buffer: Add get_nth_bh()

Extract this useful helper from nilfs_page_get_nth_block()

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/nilfs2/page.h | 7 +------
include/linux/buffer_head.h | 22 ++++++++++++++++++++++
2 files changed, 23 insertions(+), 6 deletions(-)

diff --git a/fs/nilfs2/page.h b/fs/nilfs2/page.h
index 21ddcdd4d63e..344d71942d36 100644
--- a/fs/nilfs2/page.h
+++ b/fs/nilfs2/page.h
@@ -55,12 +55,7 @@ unsigned long nilfs_find_uncommitted_extent(struct inode *inode,
static inline struct buffer_head *
nilfs_page_get_nth_block(struct page *page, unsigned int count)
{
- struct buffer_head *bh = page_buffers(page);
-
- while (count-- > 0)
- bh = bh->b_this_page;
- get_bh(bh);
- return bh;
+ return get_nth_bh(page_buffers(page), count);
}

#endif /* _NILFS_PAGE_H */
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index 1001244a8941..9fc615ee17fd 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -457,6 +457,28 @@ __bread(struct block_device *bdev, sector_t block, unsigned size)
return __bread_gfp(bdev, block, size, __GFP_MOVABLE);
}

+/**
+ * get_nth_bh - Get a reference on the n'th buffer after this one.
+ * @bh: The buffer to start counting from.
+ * @count: How many buffers to skip.
+ *
+ * This is primarily useful for finding the nth buffer in a folio; in
+ * that case you pass the head buffer and the byte offset in the folio
+ * divided by the block size. It can be used for other purposes, but
+ * it will wrap at the end of the folio rather than returning NULL or
+ * proceeding to the next folio for you.
+ *
+ * Return: The requested buffer with an elevated refcount.
+ */
+static inline struct buffer_head *get_nth_bh(struct buffer_head *bh,
+ unsigned int count)
+{
+ while (count--)
+ bh = bh->b_this_page;
+ get_bh(bh);
+ return bh;
+}
+
bool block_dirty_folio(struct address_space *mapping, struct folio *folio);

#ifdef CONFIG_BUFFER_HEAD
--
2.40.1

2023-09-19 15:13:17

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 22/26] ufs: Add ufs_get_locked_folio and ufs_put_locked_folio

Convert the _page variants to call them. Saves a few hidden calls to
compound_head().

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
fs/ufs/util.c | 43 +++++++++++++++++++++++++------------------
fs/ufs/util.h | 13 +++++++++----
2 files changed, 34 insertions(+), 22 deletions(-)

diff --git a/fs/ufs/util.c b/fs/ufs/util.c
index 08ddf41eaaad..151b400cb3b6 100644
--- a/fs/ufs/util.c
+++ b/fs/ufs/util.c
@@ -229,43 +229,50 @@ ufs_set_inode_dev(struct super_block *sb, struct ufs_inode_info *ufsi, dev_t dev
ufsi->i_u1.i_data[0] = cpu_to_fs32(sb, fs32);
}

+struct page *ufs_get_locked_page(struct address_space *mapping, pgoff_t index)
+{
+ struct folio *folio = ufs_get_locked_folio(mapping, index);
+
+ if (folio)
+ return folio_file_page(folio, index);
+ return NULL;
+}
+
/**
- * ufs_get_locked_page() - locate, pin and lock a pagecache page, if not exist
+ * ufs_get_locked_folio() - locate, pin and lock a pagecache folio, if not exist
* read it from disk.
* @mapping: the address_space to search
* @index: the page index
*
- * Locates the desired pagecache page, if not exist we'll read it,
+ * Locates the desired pagecache folio, if not exist we'll read it,
* locks it, increments its reference
* count and returns its address.
*
*/
-
-struct page *ufs_get_locked_page(struct address_space *mapping,
+struct folio *ufs_get_locked_folio(struct address_space *mapping,
pgoff_t index)
{
struct inode *inode = mapping->host;
- struct page *page = find_lock_page(mapping, index);
- if (!page) {
- page = read_mapping_page(mapping, index, NULL);
+ struct folio *folio = filemap_lock_folio(mapping, index);
+ if (!folio) {
+ folio = read_mapping_folio(mapping, index, NULL);

- if (IS_ERR(page)) {
- printk(KERN_ERR "ufs_change_blocknr: "
- "read_mapping_page error: ino %lu, index: %lu\n",
+ if (IS_ERR(folio)) {
+ printk(KERN_ERR "ufs_change_blocknr: read_mapping_folio error: ino %lu, index: %lu\n",
mapping->host->i_ino, index);
- return page;
+ return folio;
}

- lock_page(page);
+ folio_lock(folio);

- if (unlikely(page->mapping == NULL)) {
+ if (unlikely(folio->mapping == NULL)) {
/* Truncate got there first */
- unlock_page(page);
- put_page(page);
+ folio_unlock(folio);
+ folio_put(folio);
return NULL;
}
}
- if (!page_has_buffers(page))
- create_empty_buffers(page, 1 << inode->i_blkbits, 0);
- return page;
+ if (!folio_buffers(folio))
+ folio_create_empty_buffers(folio, 1 << inode->i_blkbits, 0);
+ return folio;
}
diff --git a/fs/ufs/util.h b/fs/ufs/util.h
index 89247193d96d..62542561d150 100644
--- a/fs/ufs/util.h
+++ b/fs/ufs/util.h
@@ -273,12 +273,17 @@ extern void _ubh_ubhcpymem_(struct ufs_sb_private_info *, unsigned char *, struc
extern void _ubh_memcpyubh_(struct ufs_sb_private_info *, struct ufs_buffer_head *, unsigned char *, unsigned);

/* This functions works with cache pages*/
-extern struct page *ufs_get_locked_page(struct address_space *mapping,
- pgoff_t index);
+struct page *ufs_get_locked_page(struct address_space *mapping, pgoff_t index);
+struct folio *ufs_get_locked_folio(struct address_space *mapping, pgoff_t index);
+static inline void ufs_put_locked_folio(struct folio *folio)
+{
+ folio_unlock(folio);
+ folio_put(folio);
+}
+
static inline void ufs_put_locked_page(struct page *page)
{
- unlock_page(page);
- put_page(page);
+ ufs_put_locked_folio(page_folio(page));
}


--
2.40.1

2023-09-19 15:40:07

by Matthew Wilcox

[permalink] [raw]
Subject: [PATCH 23/26] ufs: Use ufs_get_locked_folio() in ufs_alloc_lastblock()

Switch to the folio APIs, saving one folio->page->folio conversion.

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

diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c
index 21a4779a2de5..5b289374d4fd 100644
--- a/fs/ufs/inode.c
+++ b/fs/ufs/inode.c
@@ -1057,7 +1057,7 @@ static int ufs_alloc_lastblock(struct inode *inode, loff_t size)
struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
unsigned i, end;
sector_t lastfrag;
- struct page *lastpage;
+ struct folio *folio;
struct buffer_head *bh;
u64 phys64;

@@ -1068,18 +1068,17 @@ static int ufs_alloc_lastblock(struct inode *inode, loff_t size)

lastfrag--;

- lastpage = ufs_get_locked_page(mapping, lastfrag >>
+ folio = ufs_get_locked_folio(mapping, lastfrag >>
(PAGE_SHIFT - inode->i_blkbits));
- if (IS_ERR(lastpage)) {
- err = -EIO;
- goto out;
- }
-
- end = lastfrag & ((1 << (PAGE_SHIFT - inode->i_blkbits)) - 1);
- bh = page_buffers(lastpage);
- for (i = 0; i < end; ++i)
- bh = bh->b_this_page;
+ if (IS_ERR(folio)) {
+ err = -EIO;
+ goto out;
+ }

+ end = lastfrag & ((1 << (PAGE_SHIFT - inode->i_blkbits)) - 1);
+ bh = folio_buffers(folio);
+ for (i = 0; i < end; ++i)
+ bh = bh->b_this_page;

err = ufs_getfrag_block(inode, lastfrag, bh, 1);

@@ -1095,7 +1094,7 @@ static int ufs_alloc_lastblock(struct inode *inode, loff_t size)
*/
set_buffer_uptodate(bh);
mark_buffer_dirty(bh);
- set_page_dirty(lastpage);
+ folio_mark_dirty(folio);
}

if (lastfrag >= UFS_IND_FRAGMENT) {
@@ -1113,7 +1112,7 @@ static int ufs_alloc_lastblock(struct inode *inode, loff_t size)
}
}
out_unlock:
- ufs_put_locked_page(lastpage);
+ ufs_put_locked_folio(folio);
out:
return err;
}
--
2.40.1

2023-09-19 18:13:30

by Matthew Wilcox

[permalink] [raw]
Subject: Re: [PATCH 11/26] nilfs2: Convert nilfs_copy_page() to nilfs_copy_folio()

On Tue, Sep 19, 2023 at 10:01:27PM +0900, Ryusuke Konishi wrote:
> When I tried to test the patchset against 6.6-rc2, I encountered the
> following error during the build:
>
> ERROR: modpost: "folio_copy" [fs/nilfs2/nilfs2.ko] undefined!
>
> It looks like "folio_copy" is not exported to modules.
>
> I'll correct this manually for now and proceed with the review and
> testing, but could you please fix this build issue in some way ?

Thanks! I'll export the symbol. I did build nilfs2 as a module, but I
just did it with "make fs/" which doesn't run modpost. Appreciate the
testing.

2023-09-19 18:19:08

by Ryusuke Konishi

[permalink] [raw]
Subject: Re: [PATCH 11/26] nilfs2: Convert nilfs_copy_page() to nilfs_copy_folio()

Hi,

On Tue, Sep 19, 2023 at 1:56 PM Matthew Wilcox (Oracle) wrote:
>
> Both callers already have a folio, so pass it in and use it directly.
> Removes a lot of hidden calls to compound_head().
>
> Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
> ---
> fs/nilfs2/page.c | 50 +++++++++++++++++++++++++-----------------------
> 1 file changed, 26 insertions(+), 24 deletions(-)
>
> diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c
> index 1c075bd906c9..696215d899bf 100644
> --- a/fs/nilfs2/page.c
> +++ b/fs/nilfs2/page.c
> @@ -184,30 +184,32 @@ void nilfs_page_bug(struct page *page)
> }
>
> /**
> - * nilfs_copy_page -- copy the page with buffers
> - * @dst: destination page
> - * @src: source page
> - * @copy_dirty: flag whether to copy dirty states on the page's buffer heads.
> + * nilfs_copy_folio -- copy the folio with buffers
> + * @dst: destination folio
> + * @src: source folio
> + * @copy_dirty: flag whether to copy dirty states on the folio's buffer heads.
> *
> - * This function is for both data pages and btnode pages. The dirty flag
> - * should be treated by caller. The page must not be under i/o.
> - * Both src and dst page must be locked
> + * This function is for both data folios and btnode folios. The dirty flag
> + * should be treated by caller. The folio must not be under i/o.
> + * Both src and dst folio must be locked
> */
> -static void nilfs_copy_page(struct page *dst, struct page *src, int copy_dirty)
> +static void nilfs_copy_folio(struct folio *dst, struct folio *src,
> + bool copy_dirty)
> {
> struct buffer_head *dbh, *dbufs, *sbh;
> unsigned long mask = NILFS_BUFFER_INHERENT_BITS;
>
> - BUG_ON(PageWriteback(dst));
> + BUG_ON(folio_test_writeback(dst));
>
> - sbh = page_buffers(src);
> - if (!page_has_buffers(dst))
> - create_empty_buffers(dst, sbh->b_size, 0);
> + sbh = folio_buffers(src);
> + dbh = folio_buffers(dst);
> + if (!dbh)
> + dbh = folio_create_empty_buffers(dst, sbh->b_size, 0);
>
> if (copy_dirty)
> mask |= BIT(BH_Dirty);
>
> - dbh = dbufs = page_buffers(dst);
> + dbufs = dbh;
> do {
> lock_buffer(sbh);
> lock_buffer(dbh);
> @@ -218,16 +220,16 @@ static void nilfs_copy_page(struct page *dst, struct page *src, int copy_dirty)
> dbh = dbh->b_this_page;
> } while (dbh != dbufs);
>
> - copy_highpage(dst, src);
> + folio_copy(dst, src);
>
> - if (PageUptodate(src) && !PageUptodate(dst))
> - SetPageUptodate(dst);
> - else if (!PageUptodate(src) && PageUptodate(dst))
> - ClearPageUptodate(dst);
> - if (PageMappedToDisk(src) && !PageMappedToDisk(dst))
> - SetPageMappedToDisk(dst);
> - else if (!PageMappedToDisk(src) && PageMappedToDisk(dst))
> - ClearPageMappedToDisk(dst);
> + if (folio_test_uptodate(src) && !folio_test_uptodate(dst))
> + folio_mark_uptodate(dst);
> + else if (!folio_test_uptodate(src) && folio_test_uptodate(dst))
> + folio_clear_uptodate(dst);
> + if (folio_test_mappedtodisk(src) && !folio_test_mappedtodisk(dst))
> + folio_set_mappedtodisk(dst);
> + else if (!folio_test_mappedtodisk(src) && folio_test_mappedtodisk(dst))
> + folio_clear_mappedtodisk(dst);
>
> do {
> unlock_buffer(sbh);
> @@ -269,7 +271,7 @@ int nilfs_copy_dirty_pages(struct address_space *dmap,
> NILFS_PAGE_BUG(&folio->page,
> "found empty page in dat page cache");
>
> - nilfs_copy_page(&dfolio->page, &folio->page, 1);
> + nilfs_copy_folio(dfolio, folio, true);
> filemap_dirty_folio(folio_mapping(dfolio), dfolio);
>
> folio_unlock(dfolio);
> @@ -314,7 +316,7 @@ void nilfs_copy_back_pages(struct address_space *dmap,
> if (!IS_ERR(dfolio)) {
> /* overwrite existing folio in the destination cache */
> WARN_ON(folio_test_dirty(dfolio));
> - nilfs_copy_page(&dfolio->page, &folio->page, 0);
> + nilfs_copy_folio(dfolio, folio, false);
> folio_unlock(dfolio);
> folio_put(dfolio);
> /* Do we not need to remove folio from smap here? */
> --
> 2.40.1

When I tried to test the patchset against 6.6-rc2, I encountered the
following error during the build:

ERROR: modpost: "folio_copy" [fs/nilfs2/nilfs2.ko] undefined!

It looks like "folio_copy" is not exported to modules.

I'll correct this manually for now and proceed with the review and
testing, but could you please fix this build issue in some way ?

Thanks,
Ryusuke Konishi

2023-09-19 19:06:25

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH 11/26] nilfs2: Convert nilfs_copy_page() to nilfs_copy_folio()

Hi Matthew,

kernel test robot noticed the following build errors:

[auto build test ERROR on next-20230918]
[also build test ERROR on v6.6-rc2]
[cannot apply to konis-nilfs2/upstream gfs2/for-next tytso-ext4/dev linus/master v6.6-rc2 v6.6-rc1 v6.5]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url: https://github.com/intel-lab-lkp/linux/commits/Matthew-Wilcox-Oracle/buffer-Make-folio_create_empty_buffers-return-a-buffer_head/20230919-125330
base: next-20230918
patch link: https://lore.kernel.org/r/20230919045135.3635437-12-willy%40infradead.org
patch subject: [PATCH 11/26] nilfs2: Convert nilfs_copy_page() to nilfs_copy_folio()
config: s390-defconfig (https://download.01.org/0day-ci/archive/20230920/[email protected]/config)
compiler: s390-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20230920/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All errors (new ones prefixed by >>, old ones prefixed by <<):

WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/net/amt.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/net/macvtap.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/net/tap.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/net/ppp/ppp_generic.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/net/ppp/ppp_async.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/net/ppp/bsd_comp.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/net/ppp/ppp_deflate.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/net/ppp/ppp_synctty.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/net/slip/slhc.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/cdrom/cdrom.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/s390/cio/vfio_ccw.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/s390/block/dcssblk.o
WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/s390/net/lcs.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/802/garp.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/act_gate.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/sch_htb.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/sch_hfsc.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/sch_red.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/sch_gred.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/sch_ingress.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/sch_sfq.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/sch_tbf.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/sch_teql.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/sch_prio.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/sch_multiq.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/sch_netem.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/sch_drr.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/sch_plug.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/sch_ets.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/sch_mqprio.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/sch_mqprio_lib.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/sch_choke.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/sch_qfq.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/cls_u32.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/cls_route.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/cls_fw.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sched/cls_basic.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netlink/netlink_diag.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netfilter/nfnetlink_osf.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netfilter/nf_conntrack.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netfilter/nf_conntrack_netlink.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netfilter/nf_conntrack_broadcast.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netfilter/nf_nat.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netfilter/nf_tables.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netfilter/nft_fib.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netfilter/nft_chain_nat.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netfilter/ipvs/ip_vs.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netfilter/ipvs/ip_vs_rr.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netfilter/ipvs/ip_vs_wrr.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netfilter/ipvs/ip_vs_lc.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netfilter/ipvs/ip_vs_wlc.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netfilter/ipvs/ip_vs_lblc.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netfilter/ipvs/ip_vs_lblcr.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netfilter/ipvs/ip_vs_dh.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netfilter/ipvs/ip_vs_sh.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netfilter/ipvs/ip_vs_sed.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netfilter/ipvs/ip_vs_nq.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netfilter/ipvs/ip_vs_twos.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netfilter/ipvs/ip_vs_ftp.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/netfilter/ipvs/ip_vs_pe_sip.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv4/netfilter/nf_defrag_ipv4.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv4/netfilter/nf_reject_ipv4.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv4/netfilter/iptable_nat.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv4/netfilter/iptable_raw.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv4/ip_tunnel.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv4/ipip.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv4/ip_gre.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv4/udp_tunnel.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv4/ip_vti.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv4/ah4.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv4/esp4.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv4/xfrm4_tunnel.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv4/tunnel4.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv4/inet_diag.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv4/tcp_diag.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv4/udp_diag.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/xfrm/xfrm_algo.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/xfrm/xfrm_user.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/unix/unix_diag.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv6/netfilter/ip6table_raw.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv6/netfilter/ip6table_nat.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv6/netfilter/nf_defrag_ipv6.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv6/netfilter/nf_reject_ipv6.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv6/ah6.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv6/esp6.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv6/xfrm6_tunnel.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv6/tunnel6.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv6/mip6.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv6/sit.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/ipv6/ip6_udp_tunnel.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/packet/af_packet_diag.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/8021q/8021q.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/mptcp/mptcp_diag.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/key/af_key.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sunrpc/sunrpc.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sunrpc/auth_gss/auth_rpcgss.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sunrpc/auth_gss/rpcsec_gss_krb5.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/sctp/sctp_diag.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/smc/smc_diag.o
WARNING: modpost: missing MODULE_DESCRIPTION() in net/vmw_vsock/vsock_diag.o
>> ERROR: modpost: "folio_copy" [fs/nilfs2/nilfs2.ko] undefined!

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

2023-09-19 22:36:27

by Andreas Gruenbacher

[permalink] [raw]
Subject: Re: [PATCH 05/26] gfs2: Convert inode unstuffing to use a folio

On Tue, Sep 19, 2023 at 7:00 AM Matthew Wilcox (Oracle)
<[email protected]> wrote:
> Use the folio APIs, removing numerous hidden calls to compound_head().
> Also remove the stale comment about the page being looked up if it's NULL.
>
> Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
> ---
> fs/gfs2/bmap.c | 48 +++++++++++++++++++++++-------------------------
> 1 file changed, 23 insertions(+), 25 deletions(-)
>
> diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
> index ef7017fb6951..247d2c16593c 100644
> --- a/fs/gfs2/bmap.c
> +++ b/fs/gfs2/bmap.c
> @@ -43,53 +43,51 @@ struct metapath {
> static int punch_hole(struct gfs2_inode *ip, u64 offset, u64 length);
>
> /**
> - * gfs2_unstuffer_page - unstuff a stuffed inode into a block cached by a page
> + * gfs2_unstuffer_folio - unstuff a stuffed inode into a block cached by a folio
> * @ip: the inode
> * @dibh: the dinode buffer
> * @block: the block number that was allocated
> - * @page: The (optional) page. This is looked up if @page is NULL
> + * @folio: The folio.
> *
> * Returns: errno
> */
> -
> -static int gfs2_unstuffer_page(struct gfs2_inode *ip, struct buffer_head *dibh,
> - u64 block, struct page *page)
> +static int gfs2_unstuffer_folio(struct gfs2_inode *ip, struct buffer_head *dibh,
> + u64 block, struct folio *folio)
> {
> struct inode *inode = &ip->i_inode;
>
> - if (!PageUptodate(page)) {
> - void *kaddr = kmap(page);
> + if (!folio_test_uptodate(folio)) {
> + void *kaddr = kmap_local_folio(folio, 0);
> u64 dsize = i_size_read(inode);
>
> memcpy(kaddr, dibh->b_data + sizeof(struct gfs2_dinode), dsize);
> - memset(kaddr + dsize, 0, PAGE_SIZE - dsize);
> - kunmap(page);
> + memset(kaddr + dsize, 0, folio_size(folio) - dsize);
> + kunmap_local(kaddr);
>
> - SetPageUptodate(page);
> + folio_mark_uptodate(folio);
> }
>
> if (gfs2_is_jdata(ip)) {
> - struct buffer_head *bh;
> + struct buffer_head *bh = folio_buffers(folio);
>
> - if (!page_has_buffers(page))
> - create_empty_buffers(page, BIT(inode->i_blkbits),
> - BIT(BH_Uptodate));
> + if (!bh)
> + bh = folio_create_empty_buffers(folio,
> + BIT(inode->i_blkbits), BIT(BH_Uptodate));
>
> - bh = page_buffers(page);
> if (!buffer_mapped(bh))
> map_bh(bh, inode->i_sb, block);
>
> set_buffer_uptodate(bh);
> gfs2_trans_add_data(ip->i_gl, bh);
> } else {
> - set_page_dirty(page);
> + folio_mark_dirty(folio);
> gfs2_ordered_add_inode(ip);
> }
>
> return 0;
> }
>
> -static int __gfs2_unstuff_inode(struct gfs2_inode *ip, struct page *page)
> +static int __gfs2_unstuff_inode(struct gfs2_inode *ip, struct folio *folio)
> {
> struct buffer_head *bh, *dibh;
> struct gfs2_dinode *di;
> @@ -118,7 +116,7 @@ static int __gfs2_unstuff_inode(struct gfs2_inode *ip, struct page *page)
> dibh, sizeof(struct gfs2_dinode));
> brelse(bh);
> } else {
> - error = gfs2_unstuffer_page(ip, dibh, block, page);
> + error = gfs2_unstuffer_folio(ip, dibh, block, folio);
> if (error)
> goto out_brelse;
> }
> @@ -157,17 +155,17 @@ static int __gfs2_unstuff_inode(struct gfs2_inode *ip, struct page *page)
> int gfs2_unstuff_dinode(struct gfs2_inode *ip)
> {
> struct inode *inode = &ip->i_inode;
> - struct page *page;
> + struct folio *folio;
> int error;
>
> down_write(&ip->i_rw_mutex);
> - page = grab_cache_page(inode->i_mapping, 0);
> - error = -ENOMEM;
> - if (!page)
> + folio = filemap_grab_folio(inode->i_mapping, 0);
> + error = PTR_ERR(folio);
> + if (IS_ERR(folio))
> goto out;
> - error = __gfs2_unstuff_inode(ip, page);
> - unlock_page(page);
> - put_page(page);
> + error = __gfs2_unstuff_inode(ip, folio);
> + folio_unlock(folio);
> + folio_put(folio);
> out:
> up_write(&ip->i_rw_mutex);
> return error;
> --
> 2.40.1
>

Reviewed-by: Andreas Gruenbacher <[email protected]>

Thanks,
Andreas

2023-09-20 03:56:36

by Andreas Gruenbacher

[permalink] [raw]
Subject: Re: [PATCH 08/26] gfs2: Convert gfs2_write_buf_to_page() to use a folio

On Tue, Sep 19, 2023 at 7:00 AM Matthew Wilcox (Oracle)
<[email protected]> wrote:
> Remove several folio->page->folio conversions.
>
> Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
> ---
> fs/gfs2/quota.c | 37 +++++++++++++++++++------------------
> 1 file changed, 19 insertions(+), 18 deletions(-)
>
> diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c
> index 171b2713d2e5..0ee4865ebdca 100644
> --- a/fs/gfs2/quota.c
> +++ b/fs/gfs2/quota.c
> @@ -736,7 +736,7 @@ static int gfs2_write_buf_to_page(struct gfs2_sbd *sdp, unsigned long index,
> struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode);
> struct inode *inode = &ip->i_inode;
> struct address_space *mapping = inode->i_mapping;
> - struct page *page;
> + struct folio *folio;
> struct buffer_head *bh;
> u64 blk;
> unsigned bsize = sdp->sd_sb.sb_bsize, bnum = 0, boff = 0;
> @@ -745,15 +745,15 @@ static int gfs2_write_buf_to_page(struct gfs2_sbd *sdp, unsigned long index,
> blk = index << (PAGE_SHIFT - sdp->sd_sb.sb_bsize_shift);
> boff = off % bsize;
>
> - page = grab_cache_page(mapping, index);
> - if (!page)
> - return -ENOMEM;
> - if (!page_has_buffers(page))
> - create_empty_buffers(page, bsize, 0);
> + folio = filemap_grab_folio(mapping, index);
> + if (IS_ERR(folio))
> + return PTR_ERR(folio);
> + bh = folio_buffers(folio);
> + if (!bh)
> + bh = folio_create_empty_buffers(folio, bsize, 0);
>
> - bh = page_buffers(page);
> - for(;;) {
> - /* Find the beginning block within the page */
> + for (;;) {
> + /* Find the beginning block within the folio */
> if (pg_off >= ((bnum * bsize) + bsize)) {
> bh = bh->b_this_page;
> bnum++;
> @@ -766,9 +766,10 @@ static int gfs2_write_buf_to_page(struct gfs2_sbd *sdp, unsigned long index,
> goto unlock_out;
> /* If it's a newly allocated disk block, zero it */
> if (buffer_new(bh))
> - zero_user(page, bnum * bsize, bh->b_size);
> + folio_zero_range(folio, bnum * bsize,
> + bh->b_size);
> }
> - if (PageUptodate(page))
> + if (folio_test_uptodate(folio))
> set_buffer_uptodate(bh);
> if (bh_read(bh, REQ_META | REQ_PRIO) < 0)
> goto unlock_out;
> @@ -784,17 +785,17 @@ static int gfs2_write_buf_to_page(struct gfs2_sbd *sdp, unsigned long index,
> break;
> }
>
> - /* Write to the page, now that we have setup the buffer(s) */
> - memcpy_to_page(page, off, buf, bytes);
> - flush_dcache_page(page);
> - unlock_page(page);
> - put_page(page);
> + /* Write to the folio, now that we have setup the buffer(s) */
> + memcpy_to_folio(folio, off, buf, bytes);
> + flush_dcache_folio(folio);
> + folio_unlock(folio);
> + folio_put(folio);
>
> return 0;
>
> unlock_out:
> - unlock_page(page);
> - put_page(page);
> + folio_unlock(folio);
> + folio_put(folio);
> return -EIO;
> }
>
> --
> 2.40.1
>

Reviewed-by: Andreas Gruenbacher <[email protected]>

Thanks,
Andreas

2023-09-20 05:15:42

by Ryusuke Konishi

[permalink] [raw]
Subject: Re: [PATCH 09/26] nilfs2: Convert nilfs_mdt_freeze_buffer to use a folio

On Tue, Sep 19, 2023 at 6:09 PM Matthew Wilcox (Oracle) wrote:
>
> Remove a number of folio->page->folio conversions.
>
> Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
> ---
> fs/nilfs2/mdt.c | 20 +++++++++++---------
> 1 file changed, 11 insertions(+), 9 deletions(-)
>
> diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c
> index 19c8158605ed..db2260d6e44d 100644
> --- a/fs/nilfs2/mdt.c
> +++ b/fs/nilfs2/mdt.c
> @@ -560,17 +560,19 @@ int nilfs_mdt_freeze_buffer(struct inode *inode, struct buffer_head *bh)
> {
> struct nilfs_shadow_map *shadow = NILFS_MDT(inode)->mi_shadow;
> struct buffer_head *bh_frozen;
> - struct page *page;
> + struct folio *folio;
> int blkbits = inode->i_blkbits;
>
> - page = grab_cache_page(shadow->inode->i_mapping, bh->b_folio->index);
> - if (!page)
> - return -ENOMEM;
> + folio = filemap_grab_folio(shadow->inode->i_mapping,
> + bh->b_folio->index);
> + if (IS_ERR(folio))
> + return PTR_ERR(folio);
>
> - if (!page_has_buffers(page))
> - create_empty_buffers(page, 1 << blkbits, 0);
> + bh_frozen = folio_buffers(folio);
> + if (!bh_frozen)
> + bh_frozen = folio_create_empty_buffers(folio, 1 << blkbits, 0);
>
> - bh_frozen = nilfs_page_get_nth_block(page, bh_offset(bh) >> blkbits);
> + bh_frozen = get_nth_bh(bh_frozen, bh_offset(bh) >> blkbits);
>
> if (!buffer_uptodate(bh_frozen))
> nilfs_copy_buffer(bh_frozen, bh);
> @@ -582,8 +584,8 @@ int nilfs_mdt_freeze_buffer(struct inode *inode, struct buffer_head *bh)
> brelse(bh_frozen); /* already frozen */
> }
>
> - unlock_page(page);
> - put_page(page);
> + folio_unlock(folio);
> + folio_put(folio);
> return 0;
> }
>
> --
> 2.40.1
>

Acked-by: Ryusuke Konishi <[email protected]>

Looks good to me.

Thanks,
Ryusuke Konishi

2023-09-20 07:17:59

by Matthew Wilcox

[permalink] [raw]
Subject: Re: [PATCH 07/26] gfs2; Convert gfs2_getjdatabuf to use a folio

On Wed, Sep 20, 2023 at 12:27:08AM +0200, Andreas Gruenbacher wrote:
> Thanks,
>
> but this patch has an unwanted semicolon in the subject.

Thanks. My laptop has a dodgy shift key, so this sometimes happens.
The build quality on HP Spectre laptops has gone downhill in the last
few years.

> > - page = find_get_page_flags(mapping, index, FGP_LOCK|FGP_ACCESSED);
> > - if (!page)
> > - return NULL;
> > - if (!page_has_buffers(page)) {
> > - unlock_page(page);
> > - put_page(page);
> > + folio = __filemap_get_folio(mapping, index, FGP_LOCK | FGP_ACCESSED, 0);
> > + if (IS_ERR(folio))
> > return NULL;
> > - }
> > - /* Locate header for our buffer within our page */
> > - for (bh = page_buffers(page); bufnum--; bh = bh->b_this_page)
> > - /* Do nothing */;
> > - get_bh(bh);
> > - unlock_page(page);
> > - put_page(page);
> > + bh = folio_buffers(folio);
> > + if (bh)
> > + get_nth_bh(bh, bufnum);
>
> And we need this here:
>
> bh = get_nth_bh(bh, bufnum);

Oof. I should make that __must_check so the compiler tells me I'm being
an idiot.

Thanks!

2023-09-20 07:47:08

by Ryusuke Konishi

[permalink] [raw]
Subject: Re: [PATCH 11/26] nilfs2: Convert nilfs_copy_page() to nilfs_copy_folio()

On Tue, Sep 19, 2023 at 1:56 PM Matthew Wilcox (Oracle) wrote:
>
> Both callers already have a folio, so pass it in and use it directly.
> Removes a lot of hidden calls to compound_head().
>
> Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
> ---
> fs/nilfs2/page.c | 50 +++++++++++++++++++++++++-----------------------
> 1 file changed, 26 insertions(+), 24 deletions(-)
>
> diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c
> index 1c075bd906c9..696215d899bf 100644
> --- a/fs/nilfs2/page.c
> +++ b/fs/nilfs2/page.c
> @@ -184,30 +184,32 @@ void nilfs_page_bug(struct page *page)
> }
>
> /**
> - * nilfs_copy_page -- copy the page with buffers
> - * @dst: destination page
> - * @src: source page
> - * @copy_dirty: flag whether to copy dirty states on the page's buffer heads.
> + * nilfs_copy_folio -- copy the folio with buffers
> + * @dst: destination folio
> + * @src: source folio
> + * @copy_dirty: flag whether to copy dirty states on the folio's buffer heads.
> *
> - * This function is for both data pages and btnode pages. The dirty flag
> - * should be treated by caller. The page must not be under i/o.
> - * Both src and dst page must be locked
> + * This function is for both data folios and btnode folios. The dirty flag
> + * should be treated by caller. The folio must not be under i/o.
> + * Both src and dst folio must be locked
> */
> -static void nilfs_copy_page(struct page *dst, struct page *src, int copy_dirty)
> +static void nilfs_copy_folio(struct folio *dst, struct folio *src,
> + bool copy_dirty)
> {
> struct buffer_head *dbh, *dbufs, *sbh;
> unsigned long mask = NILFS_BUFFER_INHERENT_BITS;
>
> - BUG_ON(PageWriteback(dst));
> + BUG_ON(folio_test_writeback(dst));
>
> - sbh = page_buffers(src);
> - if (!page_has_buffers(dst))
> - create_empty_buffers(dst, sbh->b_size, 0);
> + sbh = folio_buffers(src);
> + dbh = folio_buffers(dst);
> + if (!dbh)
> + dbh = folio_create_empty_buffers(dst, sbh->b_size, 0);
>
> if (copy_dirty)
> mask |= BIT(BH_Dirty);
>
> - dbh = dbufs = page_buffers(dst);
> + dbufs = dbh;
> do {
> lock_buffer(sbh);
> lock_buffer(dbh);
> @@ -218,16 +220,16 @@ static void nilfs_copy_page(struct page *dst, struct page *src, int copy_dirty)
> dbh = dbh->b_this_page;
> } while (dbh != dbufs);
>
> - copy_highpage(dst, src);
> + folio_copy(dst, src);
>
> - if (PageUptodate(src) && !PageUptodate(dst))
> - SetPageUptodate(dst);
> - else if (!PageUptodate(src) && PageUptodate(dst))
> - ClearPageUptodate(dst);
> - if (PageMappedToDisk(src) && !PageMappedToDisk(dst))
> - SetPageMappedToDisk(dst);
> - else if (!PageMappedToDisk(src) && PageMappedToDisk(dst))
> - ClearPageMappedToDisk(dst);
> + if (folio_test_uptodate(src) && !folio_test_uptodate(dst))
> + folio_mark_uptodate(dst);
> + else if (!folio_test_uptodate(src) && folio_test_uptodate(dst))
> + folio_clear_uptodate(dst);
> + if (folio_test_mappedtodisk(src) && !folio_test_mappedtodisk(dst))
> + folio_set_mappedtodisk(dst);
> + else if (!folio_test_mappedtodisk(src) && folio_test_mappedtodisk(dst))
> + folio_clear_mappedtodisk(dst);
>
> do {
> unlock_buffer(sbh);
> @@ -269,7 +271,7 @@ int nilfs_copy_dirty_pages(struct address_space *dmap,
> NILFS_PAGE_BUG(&folio->page,
> "found empty page in dat page cache");
>
> - nilfs_copy_page(&dfolio->page, &folio->page, 1);
> + nilfs_copy_folio(dfolio, folio, true);
> filemap_dirty_folio(folio_mapping(dfolio), dfolio);
>
> folio_unlock(dfolio);
> @@ -314,7 +316,7 @@ void nilfs_copy_back_pages(struct address_space *dmap,
> if (!IS_ERR(dfolio)) {
> /* overwrite existing folio in the destination cache */
> WARN_ON(folio_test_dirty(dfolio));
> - nilfs_copy_page(&dfolio->page, &folio->page, 0);
> + nilfs_copy_folio(dfolio, folio, false);
> folio_unlock(dfolio);
> folio_put(dfolio);
> /* Do we not need to remove folio from smap here? */
> --
> 2.40.1
>

Acked-by: Ryusuke Konishi <[email protected]>

Everything else looks fine if the folio_copy symbol is exported.

Thanks,
Ryusuke Konishi

2023-09-20 08:42:51

by Ryusuke Konishi

[permalink] [raw]
Subject: Re: [PATCH 04/26] buffer: Add get_nth_bh()

On Tue, Sep 19, 2023 at 4:20 PM Matthew Wilcox (Oracle) wrote:
>
> Extract this useful helper from nilfs_page_get_nth_block()
>
> Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
> ---
> fs/nilfs2/page.h | 7 +------
> include/linux/buffer_head.h | 22 ++++++++++++++++++++++
> 2 files changed, 23 insertions(+), 6 deletions(-)
>
> diff --git a/fs/nilfs2/page.h b/fs/nilfs2/page.h
> index 21ddcdd4d63e..344d71942d36 100644
> --- a/fs/nilfs2/page.h
> +++ b/fs/nilfs2/page.h
> @@ -55,12 +55,7 @@ unsigned long nilfs_find_uncommitted_extent(struct inode *inode,
> static inline struct buffer_head *
> nilfs_page_get_nth_block(struct page *page, unsigned int count)
> {
> - struct buffer_head *bh = page_buffers(page);
> -
> - while (count-- > 0)
> - bh = bh->b_this_page;
> - get_bh(bh);
> - return bh;
> + return get_nth_bh(page_buffers(page), count);
> }
>
> #endif /* _NILFS_PAGE_H */
> diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
> index 1001244a8941..9fc615ee17fd 100644
> --- a/include/linux/buffer_head.h
> +++ b/include/linux/buffer_head.h
> @@ -457,6 +457,28 @@ __bread(struct block_device *bdev, sector_t block, unsigned size)
> return __bread_gfp(bdev, block, size, __GFP_MOVABLE);
> }
>
> +/**
> + * get_nth_bh - Get a reference on the n'th buffer after this one.
> + * @bh: The buffer to start counting from.
> + * @count: How many buffers to skip.
> + *
> + * This is primarily useful for finding the nth buffer in a folio; in
> + * that case you pass the head buffer and the byte offset in the folio
> + * divided by the block size. It can be used for other purposes, but
> + * it will wrap at the end of the folio rather than returning NULL or
> + * proceeding to the next folio for you.
> + *
> + * Return: The requested buffer with an elevated refcount.
> + */
> +static inline struct buffer_head *get_nth_bh(struct buffer_head *bh,
> + unsigned int count)
> +{
> + while (count--)
> + bh = bh->b_this_page;
> + get_bh(bh);
> + return bh;
> +}
> +
> bool block_dirty_folio(struct address_space *mapping, struct folio *folio);
>
> #ifdef CONFIG_BUFFER_HEAD
> --
> 2.40.1
>

Acked-by: Ryusuke Konishi <[email protected]>

Looks good to me.

Thanks,
Ryusuke Konishi

2023-09-20 09:26:17

by Andreas Gruenbacher

[permalink] [raw]
Subject: Re: [PATCH 07/26] gfs2; Convert gfs2_getjdatabuf to use a folio

Thanks,

but this patch has an unwanted semicolon in the subject.

On Tue, Sep 19, 2023 at 7:00 AM Matthew Wilcox (Oracle)
<[email protected]> wrote:
>
> Use the folio APIs, saving four hidden calls to compound_head().
>
> Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
> ---
> fs/gfs2/meta_io.c | 22 ++++++++--------------
> 1 file changed, 8 insertions(+), 14 deletions(-)
>
> diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c
> index f1fac1b45059..b28196015543 100644
> --- a/fs/gfs2/meta_io.c
> +++ b/fs/gfs2/meta_io.c
> @@ -400,26 +400,20 @@ static struct buffer_head *gfs2_getjdatabuf(struct gfs2_inode *ip, u64 blkno)
> {
> struct address_space *mapping = ip->i_inode.i_mapping;
> struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
> - struct page *page;
> + struct folio *folio;
> struct buffer_head *bh;
> unsigned int shift = PAGE_SHIFT - sdp->sd_sb.sb_bsize_shift;
> unsigned long index = blkno >> shift; /* convert block to page */
> unsigned int bufnum = blkno - (index << shift);
>
> - page = find_get_page_flags(mapping, index, FGP_LOCK|FGP_ACCESSED);
> - if (!page)
> - return NULL;
> - if (!page_has_buffers(page)) {
> - unlock_page(page);
> - put_page(page);
> + folio = __filemap_get_folio(mapping, index, FGP_LOCK | FGP_ACCESSED, 0);
> + if (IS_ERR(folio))
> return NULL;
> - }
> - /* Locate header for our buffer within our page */
> - for (bh = page_buffers(page); bufnum--; bh = bh->b_this_page)
> - /* Do nothing */;
> - get_bh(bh);
> - unlock_page(page);
> - put_page(page);
> + bh = folio_buffers(folio);
> + if (bh)
> + get_nth_bh(bh, bufnum);

And we need this here:

bh = get_nth_bh(bh, bufnum);

> + folio_unlock(folio);
> + folio_put(folio);
> return bh;
> }
>
> --
> 2.40.1
>
>

Reviewed-by: Andreas Gruenbacher <[email protected]>

2023-09-20 11:02:35

by Ryusuke Konishi

[permalink] [raw]
Subject: Re: [PATCH 13/26] nilfs2: Convert nilfs_mdt_get_frozen_buffer to use a folio

On Tue, Sep 19, 2023 at 6:25 PM Matthew Wilcox (Oracle) wrote:
>
> Remove a number of folio->page->folio conversions.
>
> Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
> ---
> fs/nilfs2/mdt.c | 16 +++++++++-------
> 1 file changed, 9 insertions(+), 7 deletions(-)
>
> diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c
> index 11b7cf4acc92..7b754e6494d7 100644
> --- a/fs/nilfs2/mdt.c
> +++ b/fs/nilfs2/mdt.c
> @@ -592,17 +592,19 @@ nilfs_mdt_get_frozen_buffer(struct inode *inode, struct buffer_head *bh)
> {
> struct nilfs_shadow_map *shadow = NILFS_MDT(inode)->mi_shadow;
> struct buffer_head *bh_frozen = NULL;
> - struct page *page;
> + struct folio *folio;
> int n;
>
> - page = find_lock_page(shadow->inode->i_mapping, bh->b_folio->index);
> - if (page) {
> - if (page_has_buffers(page)) {
> + folio = filemap_lock_folio(shadow->inode->i_mapping,
> + bh->b_folio->index);
> + if (!IS_ERR(folio)) {
> + bh_frozen = folio_buffers(folio);
> + if (bh_frozen) {
> n = bh_offset(bh) >> inode->i_blkbits;
> - bh_frozen = nilfs_page_get_nth_block(page, n);
> + bh_frozen = get_nth_bh(bh_frozen, n);
> }
> - unlock_page(page);
> - put_page(page);
> + folio_unlock(folio);
> + folio_put(folio);
> }
> return bh_frozen;
> }
> --
> 2.40.1
>

Acked-by: Ryusuke Konishi <[email protected]>

Looks good to me.

Thanks,
Ryusuke Konishi

2023-09-20 12:03:06

by Ryusuke Konishi

[permalink] [raw]
Subject: Re: [PATCH 14/26] nilfs2: Remove nilfs_page_get_nth_block

On Tue, Sep 19, 2023 at 3:47 PM Matthew Wilcox (Oracle) wrote:
>
> All users have now been converted to get_nth_block().
>
> Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
> ---
> fs/nilfs2/page.h | 6 ------
> 1 file changed, 6 deletions(-)
>
> diff --git a/fs/nilfs2/page.h b/fs/nilfs2/page.h
> index 344d71942d36..d249ea1cefff 100644
> --- a/fs/nilfs2/page.h
> +++ b/fs/nilfs2/page.h
> @@ -52,10 +52,4 @@ unsigned long nilfs_find_uncommitted_extent(struct inode *inode,
> #define NILFS_PAGE_BUG(page, m, a...) \
> do { nilfs_page_bug(page); BUG(); } while (0)
>
> -static inline struct buffer_head *
> -nilfs_page_get_nth_block(struct page *page, unsigned int count)
> -{
> - return get_nth_bh(page_buffers(page), count);
> -}
> -
> #endif /* _NILFS_PAGE_H */
> --
> 2.40.1
>

Acked-by: Ryusuke Konishi <[email protected]>

Thanks,
Ryusuke Konishi

2023-09-20 14:13:23

by Pankaj Raghav

[permalink] [raw]
Subject: Re: [PATCH 02/26] mpage: Convert map_buffer_to_folio() to folio_create_empty_buffers()

On Tue, Sep 19, 2023 at 05:51:11AM +0100, Matthew Wilcox (Oracle) wrote:
> Saves a folio->page->folio conversion.
>
> Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
> ---
> fs/mpage.c | 3 +--
> 1 file changed, 1 insertion(+), 2 deletions(-)
Looks good,
Reviewed-by: Pankaj Raghav <[email protected]>

2023-09-20 15:07:28

by Pankaj Raghav

[permalink] [raw]
Subject: Re: [PATCH 01/26] buffer: Make folio_create_empty_buffers() return a buffer_head

On Tue, Sep 19, 2023 at 05:51:10AM +0100, Matthew Wilcox (Oracle) wrote:
> Almost all callers want to know the first BH that was allocated
> for this folio. We already have that handy, so return it.
>
> Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
> ---
> fs/buffer.c | 24 +++++++++++++-----------
> include/linux/buffer_head.h | 4 ++--
> 2 files changed, 15 insertions(+), 13 deletions(-)
>
Looks good,
Reviewed-by: Pankaj Raghav <[email protected]>

2023-09-20 15:42:09

by Ryusuke Konishi

[permalink] [raw]
Subject: Re: [PATCH 10/26] nilfs2: Convert nilfs_grab_buffer() to use a folio

On Tue, Sep 19, 2023 at 7:20 PM Matthew Wilcox (Oracle) wrote:
>
> Remove a number of folio->page->folio conversions.
>
> Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
> ---
> fs/nilfs2/page.c | 26 +++++++++++++-------------
> 1 file changed, 13 insertions(+), 13 deletions(-)
>
> diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c
> index b4e54d079b7d..1c075bd906c9 100644
> --- a/fs/nilfs2/page.c
> +++ b/fs/nilfs2/page.c
> @@ -25,19 +25,19 @@
> (BIT(BH_Uptodate) | BIT(BH_Mapped) | BIT(BH_NILFS_Node) | \
> BIT(BH_NILFS_Volatile) | BIT(BH_NILFS_Checked))
>
> -static struct buffer_head *
> -__nilfs_get_page_block(struct page *page, unsigned long block, pgoff_t index,
> - int blkbits, unsigned long b_state)
> +static struct buffer_head *__nilfs_get_folio_block(struct folio *folio,
> + unsigned long block, pgoff_t index, int blkbits,
> + unsigned long b_state)
>
> {
> unsigned long first_block;
> - struct buffer_head *bh;
> + struct buffer_head *bh = folio_buffers(folio);
>
> - if (!page_has_buffers(page))
> - create_empty_buffers(page, 1 << blkbits, b_state);
> + if (!bh)
> + bh = folio_create_empty_buffers(folio, 1 << blkbits, b_state);
>
> first_block = (unsigned long)index << (PAGE_SHIFT - blkbits);
> - bh = nilfs_page_get_nth_block(page, block - first_block);
> + bh = get_nth_bh(bh, block - first_block);
>
> touch_buffer(bh);
> wait_on_buffer(bh);
> @@ -51,17 +51,17 @@ struct buffer_head *nilfs_grab_buffer(struct inode *inode,
> {
> int blkbits = inode->i_blkbits;
> pgoff_t index = blkoff >> (PAGE_SHIFT - blkbits);
> - struct page *page;
> + struct folio *folio;
> struct buffer_head *bh;
>
> - page = grab_cache_page(mapping, index);
> - if (unlikely(!page))
> + folio = filemap_grab_folio(mapping, index);
> + if (IS_ERR(folio))
> return NULL;
>
> - bh = __nilfs_get_page_block(page, blkoff, index, blkbits, b_state);
> + bh = __nilfs_get_folio_block(folio, blkoff, index, blkbits, b_state);
> if (unlikely(!bh)) {
> - unlock_page(page);
> - put_page(page);
> + folio_unlock(folio);
> + folio_put(folio);
> return NULL;
> }
> return bh;
> --
> 2.40.1
>

Acked-by: Ryusuke Konishi <[email protected]>

Looks good to me.

Thanks,
Ryusuke Konishi

2023-09-20 17:13:21

by Pankaj Raghav

[permalink] [raw]
Subject: Re: [PATCH 03/26] ext4: Convert to folio_create_empty_buffers

On Tue, Sep 19, 2023 at 05:51:12AM +0100, Matthew Wilcox (Oracle) wrote:
> Remove an unnecessary folio->page->folio conversion and take advantage
> of the new return value from folio_create_empty_buffers().
>
> Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
> ---
> fs/ext4/inode.c | 14 +++++---------
> fs/ext4/move_extent.c | 11 +++++------
> 2 files changed, 10 insertions(+), 15 deletions(-)
>
I had a similar cleanup that I sent a while ago:
https://lore.kernel.org/linux-ext4/[email protected]/

Looks good,
Reviewed-by: Pankaj Raghav <[email protected]>

2023-09-20 18:36:27

by Ryusuke Konishi

[permalink] [raw]
Subject: Re: [PATCH 15/26] nilfs2: Convert nilfs_lookup_dirty_data_buffers to use folio_create_empty_buffers

On Tue, Sep 19, 2023 at 3:47 PM Matthew Wilcox (Oracle) wrote:
>
> This function was already using a folio, so this update to the new API
> removes a single folio->page->folio conversion.
>
> Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
> ---
> fs/nilfs2/segment.c | 7 +++----
> 1 file changed, 3 insertions(+), 4 deletions(-)
>
> diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c
> index 7ec16879756e..94388fe83cf8 100644
> --- a/fs/nilfs2/segment.c
> +++ b/fs/nilfs2/segment.c
> @@ -731,10 +731,9 @@ static size_t nilfs_lookup_dirty_data_buffers(struct inode *inode,
> continue;
> }
> head = folio_buffers(folio);
> - if (!head) {
> - create_empty_buffers(&folio->page, i_blocksize(inode), 0);
> - head = folio_buffers(folio);
> - }
> + if (!head)
> + head = folio_create_empty_buffers(folio,
> + i_blocksize(inode), 0);
> folio_unlock(folio);
>
> bh = head;
> --
> 2.40.1
>

Acked-by: Ryusuke Konishi <[email protected]>


Now, I've seen all the changes to nilfs2 including the last patch.
It's almost a direct conversion throughout, and I don't see any issues
with the nilfs2 part other than the build issue.

With all 26 patches applied, nilfs2 is running without problems in
actual machine tests including stress tests.

Thank you very much for your efforts.

Regards,
Ryusuke Konishi

2023-09-20 22:00:28

by Ryusuke Konishi

[permalink] [raw]
Subject: Re: [PATCH 12/26] nilfs2: Convert nilfs_mdt_forget_block() to use a folio

On Tue, Sep 19, 2023 at 3:04 PM Matthew Wilcox (Oracle) wrote:
>
> Remove a number of folio->page->folio conversions.
>
> Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
> ---
> fs/nilfs2/mdt.c | 30 ++++++++++++++----------------
> 1 file changed, 14 insertions(+), 16 deletions(-)
>
> diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c
> index db2260d6e44d..11b7cf4acc92 100644
> --- a/fs/nilfs2/mdt.c
> +++ b/fs/nilfs2/mdt.c
> @@ -356,30 +356,28 @@ int nilfs_mdt_delete_block(struct inode *inode, unsigned long block)
> */
> int nilfs_mdt_forget_block(struct inode *inode, unsigned long block)
> {
> - pgoff_t index = (pgoff_t)block >>
> - (PAGE_SHIFT - inode->i_blkbits);
> - struct page *page;
> - unsigned long first_block;
> + pgoff_t index = block >> (PAGE_SHIFT - inode->i_blkbits);
> + struct folio *folio;
> + struct buffer_head *bh;
> int ret = 0;
> int still_dirty;
>
> - page = find_lock_page(inode->i_mapping, index);
> - if (!page)
> + folio = filemap_lock_folio(inode->i_mapping, index);
> + if (IS_ERR(folio))
> return -ENOENT;
>
> - wait_on_page_writeback(page);
> + folio_wait_writeback(folio);
>
> - first_block = (unsigned long)index <<
> - (PAGE_SHIFT - inode->i_blkbits);
> - if (page_has_buffers(page)) {
> - struct buffer_head *bh;
> -
> - bh = nilfs_page_get_nth_block(page, block - first_block);
> + bh = folio_buffers(folio);
> + if (bh) {
> + unsigned long first_block = index <<
> + (PAGE_SHIFT - inode->i_blkbits);
> + bh = get_nth_bh(bh, block - first_block);
> nilfs_forget_buffer(bh);
> }
> - still_dirty = PageDirty(page);
> - unlock_page(page);
> - put_page(page);
> + still_dirty = folio_test_dirty(folio);
> + folio_unlock(folio);
> + folio_put(folio);
>
> if (still_dirty ||
> invalidate_inode_pages2_range(inode->i_mapping, index, index) != 0)
> --
> 2.40.1
>

Acked-by: Ryusuke Konishi <[email protected]>

Looks good to me.

Thanks,
Ryusuke Konishi

2023-09-21 11:01:43

by Andreas Gruenbacher

[permalink] [raw]
Subject: Re: [PATCH 06/26] gfs2: Convert gfs2_getbuf() to folios

On Tue, Sep 19, 2023 at 7:00 AM Matthew Wilcox (Oracle)
<[email protected]> wrote:
> Remove several folio->page->folio conversions. Also use __GFP_NOFAIL
> instead of calling yield() and the new get_nth_bh().
>
> Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
> ---
> fs/gfs2/meta_io.c | 39 +++++++++++++++++----------------------
> 1 file changed, 17 insertions(+), 22 deletions(-)
>
> diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c
> index 924361fa510b..f1fac1b45059 100644
> --- a/fs/gfs2/meta_io.c
> +++ b/fs/gfs2/meta_io.c
> @@ -115,7 +115,7 @@ struct buffer_head *gfs2_getbuf(struct gfs2_glock *gl, u64 blkno, int create)
> {
> struct address_space *mapping = gfs2_glock2aspace(gl);
> struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
> - struct page *page;
> + struct folio *folio;
> struct buffer_head *bh;
> unsigned int shift;
> unsigned long index;
> @@ -129,36 +129,31 @@ struct buffer_head *gfs2_getbuf(struct gfs2_glock *gl, u64 blkno, int create)
> bufnum = blkno - (index << shift); /* block buf index within page */
>
> if (create) {
> - for (;;) {
> - page = grab_cache_page(mapping, index);
> - if (page)
> - break;
> - yield();
> - }
> - if (!page_has_buffers(page))
> - create_empty_buffers(page, sdp->sd_sb.sb_bsize, 0);
> + folio = __filemap_get_folio(mapping, index,
> + FGP_LOCK | FGP_ACCESSED | FGP_CREAT,
> + mapping_gfp_mask(mapping) | __GFP_NOFAIL);
> + bh = folio_buffers(folio);
> + if (!bh)
> + bh = folio_create_empty_buffers(folio,
> + sdp->sd_sb.sb_bsize, 0);
> } else {
> - page = find_get_page_flags(mapping, index,
> - FGP_LOCK|FGP_ACCESSED);
> - if (!page)
> + folio = __filemap_get_folio(mapping, index,
> + FGP_LOCK | FGP_ACCESSED, 0);
> + if (IS_ERR(folio))
> return NULL;
> - if (!page_has_buffers(page)) {
> - bh = NULL;
> - goto out_unlock;
> - }
> + bh = folio_buffers(folio);
> }
>
> - /* Locate header for our buffer within our page */
> - for (bh = page_buffers(page); bufnum--; bh = bh->b_this_page)
> - /* Do nothing */;
> - get_bh(bh);
> + if (!bh)
> + goto out_unlock;
>
> + bh = get_nth_bh(bh, bufnum);
> if (!buffer_mapped(bh))
> map_bh(bh, sdp->sd_vfs, blkno);
>
> out_unlock:
> - unlock_page(page);
> - put_page(page);
> + folio_unlock(folio);
> + folio_put(folio);
>
> return bh;
> }
> --
> 2.40.1
>

Reviewed-by: Andreas Gruenbacher <[email protected]>

Thanks,
Andreas