diff mbox series

[4/4] ubifs: Convert do_writepage() to take a folio

Message ID 20230605165029.2908304-5-willy@infradead.org (mailing list archive)
State New, archived
Headers show
Series ubifs: Convert writeback to use folios | expand

Commit Message

Matthew Wilcox June 5, 2023, 4:50 p.m. UTC
Replace the call to SetPageError() with a call to mapping_set_error().
Support large folios by using kmap_local_folio() and remapping each time
we cross a page boundary.  Saves a lot of hidden calls to compound_head().

Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
 fs/ubifs/file.c | 53 +++++++++++++++++++++++++++----------------------
 1 file changed, 29 insertions(+), 24 deletions(-)

Comments

kernel test robot June 5, 2023, 7:28 p.m. UTC | #1
Hi Matthew,

kernel test robot noticed the following build warnings:

[auto build test WARNING on rw-ubifs/next]
[also build test WARNING on linus/master v6.4-rc5 next-20230605]
[cannot apply to rw-ubifs/fixes]
[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/ubifs-Convert-from-writepage-to-writepages/20230606-005309
base:   https://git.kernel.org/pub/scm/linux/kernel/git/rw/ubifs.git next
patch link:    https://lore.kernel.org/r/20230605165029.2908304-5-willy%40infradead.org
patch subject: [PATCH 4/4] ubifs: Convert do_writepage() to take a folio
config: alpha-allyesconfig (https://download.01.org/0day-ci/archive/20230606/202306060302.sCFYZsJ5-lkp@intel.com/config)
compiler: alpha-linux-gcc (GCC) 12.3.0
reproduce (this is a W=1 build):
        mkdir -p ~/bin
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/4f6ac0962f61cc07c95177f78cf1f3b03e79d822
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Matthew-Wilcox-Oracle/ubifs-Convert-from-writepage-to-writepages/20230606-005309
        git checkout 4f6ac0962f61cc07c95177f78cf1f3b03e79d822
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.3.0 ~/bin/make.cross W=1 O=build_dir ARCH=alpha olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.3.0 ~/bin/make.cross W=1 O=build_dir ARCH=alpha SHELL=/bin/bash fs/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202306060302.sCFYZsJ5-lkp@intel.com/

All warnings (new ones prefixed by >>):

   fs/ubifs/file.c: In function 'do_writepage':
>> fs/ubifs/file.c:905:22: warning: variable 'i' set but not used [-Wunused-but-set-variable]
     905 |         int err = 0, i, blen;
         |                      ^


vim +/i +905 fs/ubifs/file.c

1e51764a3c2ac0 Artem Bityutskiy        2008-07-14  902  
4f6ac0962f61cc Matthew Wilcox (Oracle  2023-06-05  903) static int do_writepage(struct folio *folio, size_t len)
1e51764a3c2ac0 Artem Bityutskiy        2008-07-14  904  {
1e51764a3c2ac0 Artem Bityutskiy        2008-07-14 @905  	int err = 0, i, blen;
1e51764a3c2ac0 Artem Bityutskiy        2008-07-14  906  	unsigned int block;
1e51764a3c2ac0 Artem Bityutskiy        2008-07-14  907  	void *addr;
4f6ac0962f61cc Matthew Wilcox (Oracle  2023-06-05  908) 	size_t offset = 0;
1e51764a3c2ac0 Artem Bityutskiy        2008-07-14  909  	union ubifs_key key;
4f6ac0962f61cc Matthew Wilcox (Oracle  2023-06-05  910) 	struct inode *inode = folio->mapping->host;
1e51764a3c2ac0 Artem Bityutskiy        2008-07-14  911  	struct ubifs_info *c = inode->i_sb->s_fs_info;
1e51764a3c2ac0 Artem Bityutskiy        2008-07-14  912
Richard Weinberger June 5, 2023, 9:37 p.m. UTC | #2
Matthew,

----- Ursprüngliche Mail -----
> Von: "Matthew Wilcox" <willy@infradead.org>
> -static int do_writepage(struct page *page, int len)
> +static int do_writepage(struct folio *folio, size_t len)
> {
> 	int err = 0, i, blen;
> 	unsigned int block;
> 	void *addr;
> +	size_t offset = 0;
> 	union ubifs_key key;
> -	struct inode *inode = page->mapping->host;
> +	struct inode *inode = folio->mapping->host;
> 	struct ubifs_info *c = inode->i_sb->s_fs_info;
> 
> #ifdef UBIFS_DEBUG
> 	struct ubifs_inode *ui = ubifs_inode(inode);
> 	spin_lock(&ui->ui_lock);
> -	ubifs_assert(c, page->index <= ui->synced_i_size >> PAGE_SHIFT);
> +	ubifs_assert(c, folio->index <= ui->synced_i_size >> PAGE_SHIFT);
> 	spin_unlock(&ui->ui_lock);
> #endif
> 
> -	/* Update radix tree tags */
> -	set_page_writeback(page);
> +	folio_start_writeback(folio);
> 
> -	addr = kmap(page);
> -	block = page->index << UBIFS_BLOCKS_PER_PAGE_SHIFT;
> +	addr = kmap_local_folio(folio, offset);
> +	block = folio->index << UBIFS_BLOCKS_PER_PAGE_SHIFT;
> 	i = 0;
> -	while (len) {
> -		blen = min_t(int, len, UBIFS_BLOCK_SIZE);
> +	for (;;) {

This change will cause a file system corruption.
If len is zero (it can be) then a zero length data node will be written.
The while(len) made sure that upon zero length nothing is written.

Thanks,
//richard
kernel test robot June 5, 2023, 11:29 p.m. UTC | #3
Hi Matthew,

kernel test robot noticed the following build warnings:

[auto build test WARNING on rw-ubifs/next]
[also build test WARNING on linus/master v6.4-rc5 next-20230605]
[cannot apply to rw-ubifs/fixes]
[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/ubifs-Convert-from-writepage-to-writepages/20230606-005309
base:   https://git.kernel.org/pub/scm/linux/kernel/git/rw/ubifs.git next
patch link:    https://lore.kernel.org/r/20230605165029.2908304-5-willy%40infradead.org
patch subject: [PATCH 4/4] ubifs: Convert do_writepage() to take a folio
config: i386-randconfig-i001-20230605 (https://download.01.org/0day-ci/archive/20230606/202306060727.jUL92Co4-lkp@intel.com/config)
compiler: clang version 15.0.7 (https://github.com/llvm/llvm-project.git 8dfdcc7b7bf66834a761bd8de445840ef68e4d1a)
reproduce (this is a W=1 build):
        mkdir -p ~/bin
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/4f6ac0962f61cc07c95177f78cf1f3b03e79d822
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Matthew-Wilcox-Oracle/ubifs-Convert-from-writepage-to-writepages/20230606-005309
        git checkout 4f6ac0962f61cc07c95177f78cf1f3b03e79d822
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang ~/bin/make.cross W=1 O=build_dir ARCH=i386 olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang ~/bin/make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash fs/ubifs/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202306060727.jUL92Co4-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> fs/ubifs/file.c:905:15: warning: variable 'i' set but not used [-Wunused-but-set-variable]
           int err = 0, i, blen;
                        ^
   1 warning generated.


vim +/i +905 fs/ubifs/file.c

1e51764a3c2ac0 Artem Bityutskiy        2008-07-14  902  
4f6ac0962f61cc Matthew Wilcox (Oracle  2023-06-05  903) static int do_writepage(struct folio *folio, size_t len)
1e51764a3c2ac0 Artem Bityutskiy        2008-07-14  904  {
1e51764a3c2ac0 Artem Bityutskiy        2008-07-14 @905  	int err = 0, i, blen;
1e51764a3c2ac0 Artem Bityutskiy        2008-07-14  906  	unsigned int block;
1e51764a3c2ac0 Artem Bityutskiy        2008-07-14  907  	void *addr;
4f6ac0962f61cc Matthew Wilcox (Oracle  2023-06-05  908) 	size_t offset = 0;
1e51764a3c2ac0 Artem Bityutskiy        2008-07-14  909  	union ubifs_key key;
4f6ac0962f61cc Matthew Wilcox (Oracle  2023-06-05  910) 	struct inode *inode = folio->mapping->host;
1e51764a3c2ac0 Artem Bityutskiy        2008-07-14  911  	struct ubifs_info *c = inode->i_sb->s_fs_info;
1e51764a3c2ac0 Artem Bityutskiy        2008-07-14  912
Matthew Wilcox June 6, 2023, 3:22 a.m. UTC | #4
On Mon, Jun 05, 2023 at 11:37:00PM +0200, Richard Weinberger wrote:
> > -	addr = kmap(page);
> > -	block = page->index << UBIFS_BLOCKS_PER_PAGE_SHIFT;
> > +	addr = kmap_local_folio(folio, offset);
> > +	block = folio->index << UBIFS_BLOCKS_PER_PAGE_SHIFT;
> > 	i = 0;
> > -	while (len) {
> > -		blen = min_t(int, len, UBIFS_BLOCK_SIZE);
> > +	for (;;) {
> 
> This change will cause a file system corruption.
> If len is zero (it can be) then a zero length data node will be written.
> The while(len) made sure that upon zero length nothing is written.

I don't see how 'len' can be 0.  len is modified each time around the
loop, and if it's decremented to 0, we break.  So you must be referring
to a case where the caller of do_writepage passes 0.

There are three callers of do_writepage, two in ubifs_writepage():

        int err, len = folio_size(folio);
...
        if (folio_pos(folio) + len < i_size) {
...
                return do_writepage(folio, len);

len is folio_size(), which is not 0.

        len = offset_in_folio(folio, i_size);

Here, we know that len is not 0.  We already tested earlier:
        if (folio_pos(folio) >= i_size) {

so we know that i_size > folio_pos() and i_size < folio_pos() +
folio_size().  Actually, I should make this more explicit:

	len = i_size - folio_pos(folio);

Now it should be clear that len cannot be zero.

The third caller is do_truncation():

        loff_t old_size = inode->i_size, new_size = attr->ia_size;
        int offset = new_size & (UBIFS_BLOCK_SIZE - 1), budgeted = 1;
        if (offset) {
                pgoff_t index = new_size >> PAGE_SHIFT;
                                       offset = offset_in_folio(folio,
                                                        new_size);
                                err = do_writepage(folio, offset);

It's not large-folio-safe, but it's definitely not 0.

Did I miss something?
Richard Weinberger June 6, 2023, 6:13 a.m. UTC | #5
Matthew,

----- Ursprüngliche Mail -----
> Von: "Matthew Wilcox" <willy@infradead.org>
> len is folio_size(), which is not 0.
> 
>        len = offset_in_folio(folio, i_size);

offset_in_folio(folio, i_size) can give 0.

Further it will call do_writepage() with len being 0.
I can actually trigger this case.

By adding the following ubifs_assert() I can catch the write side.
If the file length is a multiple of PAGE_SIZE (4k), it will trigger.

diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index 67cf5138ccc48..dc39ea368ca2b 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -1059,6 +1059,8 @@ static int ubifs_writepage(struct folio *folio, struct writeback_control *wbc,
        folio_zero_segment(folio, offset_in_folio(folio, i_size), len);
        len = offset_in_folio(folio, i_size);
 
+       ubifs_assert(c, len > 0);
+
        if (i_size > synced_i_size) {
                err = inode->i_sb->s_op->write_inode(inode, NULL);
                if (err)

[   44.569110] UBIFS error (ubi0:0 pid 59): ubifs_assert_failed: UBIFS assert failed: len > 0, in fs/ubifs/file.c:1062
[   44.571359] UBIFS warning (ubi0:0 pid 59): ubifs_ro_mode.part.6: switched to read-only mode, error -22
[   44.572998] CPU: 1 PID: 59 Comm: kworker/u8:2 Not tainted 6.4.0-rc5-00004-gd504b815b71c-dirty #19
[   44.574139] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.15.0-0-g2dd4b9b-rebuilt.opensuse.org 04/01/2014
[   44.574141] Workqueue: writeback wb_workfn (flush-ubifs_0_0)
[   44.574148] Call Trace:
[   44.574162]  <TASK>
[   44.574165]  dump_stack_lvl+0x32/0x50
[   44.574172]  ubifs_writepage+0x25a/0x270
[   44.578096]  write_cache_pages+0x132/0x3a0
[   44.578103]  ? __pfx_ubifs_writepage+0x10/0x10
[   44.578107]  ? virtqueue_add_sgs+0x7b/0x90
[   44.578113]  do_writepages+0xd3/0x1a0
[   44.578116]  ? kvm_clock_read+0x14/0x30
[   44.578121]  ? kvm_sched_clock_read+0x5/0x20
[   44.578125]  __writeback_single_inode+0x3c/0x350
[   44.578128]  writeback_sb_inodes+0x1c9/0x460
[   44.578133]  __writeback_inodes_wb+0x5a/0xc0
[   44.582508]  wb_writeback+0x230/0x2c0
[   44.582513]  wb_workfn+0x301/0x430
[   44.582515]  ? kvm_clock_read+0x14/0x30
[   44.582519]  ? kvm_sched_clock_read+0x5/0x20
[   44.582523]  ? sched_clock_cpu+0xd/0x190
[   44.582527]  ? __smp_call_single_queue+0xa1/0x110
[   44.582532]  process_one_work+0x1f3/0x3f0
[   44.582538]  worker_thread+0x25/0x3b0
[   44.586196]  ? __pfx_worker_thread+0x10/0x10
[   44.586201]  kthread+0xde/0x110
[   44.586204]  ? __pfx_kthread+0x10/0x10
[   44.586207]  ret_from_fork+0x2c/0x50
[   44.586212]  </TASK>

Thanks,
//richard
Matthew Wilcox June 6, 2023, 12:32 p.m. UTC | #6
On Tue, Jun 06, 2023 at 08:13:55AM +0200, Richard Weinberger wrote:
> Matthew,
> 
> ----- Ursprüngliche Mail -----
> > Von: "Matthew Wilcox" <willy@infradead.org>
> > len is folio_size(), which is not 0.
> > 
> >        len = offset_in_folio(folio, i_size);
> 
> offset_in_folio(folio, i_size) can give 0.

Oh!  There is a bug, because it shouldn't get here!

        /* Is the folio fully inside i_size? */
        if (folio_pos(folio) + len < i_size) {

should be:

        /* Is the folio fully inside i_size? */
        if (folio_pos(folio) + len <= i_size) {

right?  Consider a file with i_size 4096.  its single-page folio will
have a pos of 0 and a length of 4096.  so it should be written back by
the first call to do_writepage(), not the case where the folio straddles
i_size.
Richard Weinberger June 6, 2023, 1:41 p.m. UTC | #7
----- Ursprüngliche Mail -----
> Von: "Matthew Wilcox" <willy@infradead.org>
> On Tue, Jun 06, 2023 at 08:13:55AM +0200, Richard Weinberger wrote:
>> Matthew,
>> 
>> ----- Ursprüngliche Mail -----
>> > Von: "Matthew Wilcox" <willy@infradead.org>
>> > len is folio_size(), which is not 0.
>> > 
>> >        len = offset_in_folio(folio, i_size);
>> 
>> offset_in_folio(folio, i_size) can give 0.
> 
> Oh!  There is a bug, because it shouldn't get here!
> 
>        /* Is the folio fully inside i_size? */
>        if (folio_pos(folio) + len < i_size) {
> 
> should be:
> 
>        /* Is the folio fully inside i_size? */
>        if (folio_pos(folio) + len <= i_size) {
> 
> right?  Consider a file with i_size 4096.  its single-page folio will
> have a pos of 0 and a length of 4096.  so it should be written back by
> the first call to do_writepage(), not the case where the folio straddles
> i_size.

Indeed.
With that change I agree that do_writepage() cannot get called with zero len.
I'll run more tests, so far all is nice an shiny. :-)

Thanks,
//richard
Zhihao Cheng June 8, 2023, 3:09 p.m. UTC | #8
在 2023/6/6 0:50, Matthew Wilcox (Oracle) 写道:

Hi
> Replace the call to SetPageError() with a call to mapping_set_error().
> Support large folios by using kmap_local_folio() and remapping each time
> we cross a page boundary.  Saves a lot of hidden calls to compound_head().
> 
> Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
> ---
>   fs/ubifs/file.c | 53 +++++++++++++++++++++++++++----------------------
>   1 file changed, 29 insertions(+), 24 deletions(-)
> 
> diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
> index c0e68b3d7582..1b2055d5ec5f 100644
> --- a/fs/ubifs/file.c
> +++ b/fs/ubifs/file.c
> @@ -900,60 +900,65 @@ static int ubifs_read_folio(struct file *file, struct folio *folio)
>   	return 0;
>   }
>   
> -static int do_writepage(struct page *page, int len)
> +static int do_writepage(struct folio *folio, size_t len)
>   {
>   	int err = 0, i, blen;
>   	unsigned int block;
>   	void *addr;
> +	size_t offset = 0;
>   	union ubifs_key key;
> -	struct inode *inode = page->mapping->host;
> +	struct inode *inode = folio->mapping->host;
>   	struct ubifs_info *c = inode->i_sb->s_fs_info;
>   
>   #ifdef UBIFS_DEBUG
>   	struct ubifs_inode *ui = ubifs_inode(inode);
>   	spin_lock(&ui->ui_lock);
> -	ubifs_assert(c, page->index <= ui->synced_i_size >> PAGE_SHIFT);
> +	ubifs_assert(c, folio->index <= ui->synced_i_size >> PAGE_SHIFT);
>   	spin_unlock(&ui->ui_lock);
>   #endif
>   
> -	/* Update radix tree tags */
> -	set_page_writeback(page);
> +	folio_start_writeback(folio);
>   
> -	addr = kmap(page);
> -	block = page->index << UBIFS_BLOCKS_PER_PAGE_SHIFT;
> +	addr = kmap_local_folio(folio, offset);
> +	block = folio->index << UBIFS_BLOCKS_PER_PAGE_SHIFT;
>   	i = 0;
> -	while (len) {
> -		blen = min_t(int, len, UBIFS_BLOCK_SIZE);
> +	for (;;) {
> +		blen = min_t(size_t, len, UBIFS_BLOCK_SIZE);
>   		data_key_init(c, &key, inode->i_ino, block);
>   		err = ubifs_jnl_write_data(c, inode, &key, addr, blen);
>   		if (err)
>   			break;
> -		if (++i >= UBIFS_BLOCKS_PER_PAGE)
> +		len -= blen;
> +		if (!len)
>   			break;
>   		block += 1;
>   		addr += blen;
> -		len -= blen;
> +		if (folio_test_highmem(folio) && !offset_in_page(addr)) {
> +			kunmap_local(addr - blen);
> +			offset += PAGE_SIZE;
> +			addr = kmap_local_folio(folio, offset);

I'm not sure whether it is a problem here, if we have a 64K PAGE_SIZE 
environment, UBIFS_BLOCK_SIZE is 4K. Given len is 64K, after one 
iteration, we might enter into this branch, ubifs has written 4K-size 
data, and offset becomes 64K, ubifs will write from page pos 64K rather 
4K in second iteration?

> +		} >   	}
> +	kunmap_local(addr);
>   	if (err) {
> -		SetPageError(page);
> -		ubifs_err(c, "cannot write page %lu of inode %lu, error %d",
> -			  page->index, inode->i_ino, err);
> +		mapping_set_error(folio->mapping, err);

I rhink we can add mapping_set_error in ubifs_writepage's error 
branch(eg, ->write_inode), just like I comment in patch 1.

> +		ubifs_err(c, "cannot write folio %lu of inode %lu, error %d",
> +			  folio->index, inode->i_ino, err);
>   		ubifs_ro_mode(c, err);
>   	}
>   
> -	ubifs_assert(c, PagePrivate(page));
> -	if (PageChecked(page))
> +	ubifs_assert(c, folio->private != NULL);
> +	if (folio_test_checked(folio))
>   		release_new_page_budget(c);
>   	else
>   		release_existing_page_budget(c);
>   
>   	atomic_long_dec(&c->dirty_pg_cnt);
> -	detach_page_private(page);
> -	ClearPageChecked(page);
> +	folio_detach_private(folio);
> +	folio_clear_checked(folio);
>   
> -	kunmap(page);
> -	unlock_page(page);
> -	end_page_writeback(page);
> +	folio_unlock(folio);
> +	folio_end_writeback(folio);
>   	return err;
>   }
>   
> @@ -1041,7 +1046,7 @@ static int ubifs_writepage(struct folio *folio, struct writeback_control *wbc,
>   			 * with this.
>   			 */
>   		}
> -		return do_writepage(&folio->page, len);
> +		return do_writepage(folio, len);
>   	}
>   
>   	/*
> @@ -1060,7 +1065,7 @@ static int ubifs_writepage(struct folio *folio, struct writeback_control *wbc,
>   			goto out_redirty;
>   	}
>   
> -	return do_writepage(&folio->page, len);
> +	return do_writepage(folio, len);
>   out_redirty:
>   	/*
>   	 * folio_redirty_for_writepage() won't call ubifs_dirty_inode() because
> @@ -1172,7 +1177,7 @@ static int do_truncation(struct ubifs_info *c, struct inode *inode,
>   				if (UBIFS_BLOCKS_PER_PAGE_SHIFT)
>   					offset = offset_in_folio(folio,
>   							new_size);
> -				err = do_writepage(&folio->page, offset);
> +				err = do_writepage(folio, offset);
>   				folio_put(folio);
>   				if (err)
>   					goto out_budg;
>
diff mbox series

Patch

diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index c0e68b3d7582..1b2055d5ec5f 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -900,60 +900,65 @@  static int ubifs_read_folio(struct file *file, struct folio *folio)
 	return 0;
 }
 
-static int do_writepage(struct page *page, int len)
+static int do_writepage(struct folio *folio, size_t len)
 {
 	int err = 0, i, blen;
 	unsigned int block;
 	void *addr;
+	size_t offset = 0;
 	union ubifs_key key;
-	struct inode *inode = page->mapping->host;
+	struct inode *inode = folio->mapping->host;
 	struct ubifs_info *c = inode->i_sb->s_fs_info;
 
 #ifdef UBIFS_DEBUG
 	struct ubifs_inode *ui = ubifs_inode(inode);
 	spin_lock(&ui->ui_lock);
-	ubifs_assert(c, page->index <= ui->synced_i_size >> PAGE_SHIFT);
+	ubifs_assert(c, folio->index <= ui->synced_i_size >> PAGE_SHIFT);
 	spin_unlock(&ui->ui_lock);
 #endif
 
-	/* Update radix tree tags */
-	set_page_writeback(page);
+	folio_start_writeback(folio);
 
-	addr = kmap(page);
-	block = page->index << UBIFS_BLOCKS_PER_PAGE_SHIFT;
+	addr = kmap_local_folio(folio, offset);
+	block = folio->index << UBIFS_BLOCKS_PER_PAGE_SHIFT;
 	i = 0;
-	while (len) {
-		blen = min_t(int, len, UBIFS_BLOCK_SIZE);
+	for (;;) {
+		blen = min_t(size_t, len, UBIFS_BLOCK_SIZE);
 		data_key_init(c, &key, inode->i_ino, block);
 		err = ubifs_jnl_write_data(c, inode, &key, addr, blen);
 		if (err)
 			break;
-		if (++i >= UBIFS_BLOCKS_PER_PAGE)
+		len -= blen;
+		if (!len)
 			break;
 		block += 1;
 		addr += blen;
-		len -= blen;
+		if (folio_test_highmem(folio) && !offset_in_page(addr)) {
+			kunmap_local(addr - blen);
+			offset += PAGE_SIZE;
+			addr = kmap_local_folio(folio, offset);
+		}
 	}
+	kunmap_local(addr);
 	if (err) {
-		SetPageError(page);
-		ubifs_err(c, "cannot write page %lu of inode %lu, error %d",
-			  page->index, inode->i_ino, err);
+		mapping_set_error(folio->mapping, err);
+		ubifs_err(c, "cannot write folio %lu of inode %lu, error %d",
+			  folio->index, inode->i_ino, err);
 		ubifs_ro_mode(c, err);
 	}
 
-	ubifs_assert(c, PagePrivate(page));
-	if (PageChecked(page))
+	ubifs_assert(c, folio->private != NULL);
+	if (folio_test_checked(folio))
 		release_new_page_budget(c);
 	else
 		release_existing_page_budget(c);
 
 	atomic_long_dec(&c->dirty_pg_cnt);
-	detach_page_private(page);
-	ClearPageChecked(page);
+	folio_detach_private(folio);
+	folio_clear_checked(folio);
 
-	kunmap(page);
-	unlock_page(page);
-	end_page_writeback(page);
+	folio_unlock(folio);
+	folio_end_writeback(folio);
 	return err;
 }
 
@@ -1041,7 +1046,7 @@  static int ubifs_writepage(struct folio *folio, struct writeback_control *wbc,
 			 * with this.
 			 */
 		}
-		return do_writepage(&folio->page, len);
+		return do_writepage(folio, len);
 	}
 
 	/*
@@ -1060,7 +1065,7 @@  static int ubifs_writepage(struct folio *folio, struct writeback_control *wbc,
 			goto out_redirty;
 	}
 
-	return do_writepage(&folio->page, len);
+	return do_writepage(folio, len);
 out_redirty:
 	/*
 	 * folio_redirty_for_writepage() won't call ubifs_dirty_inode() because
@@ -1172,7 +1177,7 @@  static int do_truncation(struct ubifs_info *c, struct inode *inode,
 				if (UBIFS_BLOCKS_PER_PAGE_SHIFT)
 					offset = offset_in_folio(folio,
 							new_size);
-				err = do_writepage(&folio->page, offset);
+				err = do_writepage(folio, offset);
 				folio_put(folio);
 				if (err)
 					goto out_budg;