ubifs: ubifs_writepage: Mark page dirty after writing inode failed
authorZhihao Cheng <chengzhihao1@huawei.com>
Wed, 1 Jun 2022 02:59:59 +0000 (10:59 +0800)
committerRichard Weinberger <richard@nod.at>
Thu, 2 Feb 2023 20:13:47 +0000 (21:13 +0100)
There are two states for ubifs writing pages:
1. Dirty, Private
2. Not Dirty, Not Private

There is a third possibility which maybe related to [1] that page is
private but not dirty caused by following process:

          PA
lock(page)
ubifs_write_end
  attach_page_private // set Private
    __set_page_dirty_nobuffers // set Dirty
unlock(page)

write_cache_pages
  lock(page)
  clear_page_dirty_for_io(page) // clear Dirty
  ubifs_writepage
    write_inode
    // fail, goto out, following codes are not executed
    // do_writepage
    //   set_page_writeback  // set Writeback
    //   detach_page_private // clear Private
    //   end_page_writeback  // clear Writeback
    out:
    unlock(page) // Private, Not Dirty

                                       PB
ksys_fadvise64_64
  generic_fadvise
     invalidate_inode_page
     // page is neither Dirty nor Writeback
       invalidate_complete_page
       // page_has_private is true
 try_to_release_page
   ubifs_releasepage
     ubifs_assert(c, 0) !!!

Then we may get following assertion failed:
  UBIFS error (ubi0:0 pid 1492): ubifs_assert_failed [ubifs]:
  UBIFS assert failed: 0, in fs/ubifs/file.c:1499
  UBIFS warning (ubi0:0 pid 1492): ubifs_ro_mode [ubifs]:
  switched to read-only mode, error -22
  CPU: 2 PID: 1492 Comm: aa Not tainted 5.16.0-rc2-00012-g7bb767dee0ba-dirty
  Call Trace:
    dump_stack+0x13/0x1b
    ubifs_ro_mode+0x54/0x60 [ubifs]
    ubifs_assert_failed+0x4b/0x80 [ubifs]
    ubifs_releasepage+0x7e/0x1e0 [ubifs]
    try_to_release_page+0x57/0xe0
    invalidate_inode_page+0xfb/0x130
    invalidate_mapping_pagevec+0x12/0x20
    generic_fadvise+0x303/0x3c0
    vfs_fadvise+0x35/0x40
    ksys_fadvise64_64+0x4c/0xb0

Jump [2] to find a reproducer.

[1] https://linux-mtd.infradead.narkive.com/NQoBeT1u/patch-rfc-ubifs-fix-assert-failed-in-ubifs-set-page-dirty
[2] https://bugzilla.kernel.org/show_bug.cgi?id=215357

Fixes: 1e51764a3c2ac0 ("UBIFS: add new flash file system")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
fs/ubifs/file.c

index f2353dd..1f42926 100644 (file)
@@ -1032,7 +1032,7 @@ static int ubifs_writepage(struct page *page, struct writeback_control *wbc)
                if (page->index >= synced_i_size >> PAGE_SHIFT) {
                        err = inode->i_sb->s_op->write_inode(inode, NULL);
                        if (err)
-                               goto out_unlock;
+                               goto out_redirty;
                        /*
                         * The inode has been written, but the write-buffer has
                         * not been synchronized, so in case of an unclean
@@ -1060,11 +1060,17 @@ static int ubifs_writepage(struct page *page, struct writeback_control *wbc)
        if (i_size > synced_i_size) {
                err = inode->i_sb->s_op->write_inode(inode, NULL);
                if (err)
-                       goto out_unlock;
+                       goto out_redirty;
        }
 
        return do_writepage(page, len);
-
+out_redirty:
+       /*
+        * redirty_page_for_writepage() won't call ubifs_dirty_inode() because
+        * it passes I_DIRTY_PAGES flag while calling __mark_inode_dirty(), so
+        * there is no need to do space budget for dirty inode.
+        */
+       redirty_page_for_writepage(wbc, page);
 out_unlock:
        unlock_page(page);
        return err;