]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
f2fs: fix to recover cold bit of inode block during POR
authorChao Yu <yuchao0@huawei.com>
Wed, 3 Oct 2018 14:32:44 +0000 (22:32 +0800)
committerJuerg Haefliger <juergh@canonical.com>
Wed, 24 Jul 2019 01:58:20 +0000 (19:58 -0600)
BugLink: https://bugs.launchpad.net/bugs/1836802
commit ef2a007134b4eaa39264c885999f296577bc87d2 upstream.

Testcase to reproduce this bug:
1. mkfs.f2fs /dev/sdd
2. mount -t f2fs /dev/sdd /mnt/f2fs
3. touch /mnt/f2fs/file
4. sync
5. chattr +A /mnt/f2fs/file
6. xfs_io -f /mnt/f2fs/file -c "fsync"
7. godown /mnt/f2fs
8. umount /mnt/f2fs
9. mount -t f2fs /dev/sdd /mnt/f2fs
10. chattr -A /mnt/f2fs/file
11. xfs_io -f /mnt/f2fs/file -c "fsync"
12. umount /mnt/f2fs
13. mount -t f2fs /dev/sdd /mnt/f2fs
14. lsattr /mnt/f2fs/file

-----------------N- /mnt/f2fs/file

But actually, we expect the corrct result is:

-------A---------N- /mnt/f2fs/file

The reason is in step 9) we missed to recover cold bit flag in inode
block, so later, in fsync, we will skip write inode block due to below
condition check, result in lossing data in another SPOR.

f2fs_fsync_node_pages()
if (!IS_DNODE(page) || !is_cold_node(page))
continue;

Note that, I guess that some non-dir inode has already lost cold bit
during POR, so in order to reenable recovery for those inode, let's
try to recover cold bit in f2fs_iget() to save more fsynced data.

Fixes: c56675750d7c ("f2fs: remove unneeded set_cold_node()")
Cc: <stable@vger.kernel.org> 4.17+
Signed-off-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Andrea Righi <andrea.righi@canonical.com>
Signed-off-by: Khalid Elmously <khalid.elmously@canonical.com>
fs/f2fs/inode.c
fs/f2fs/node.c
fs/f2fs/node.h

index b198e370616b83005c8187d7e3fe747026d7f6a7..6ed22ebd9344cf68e5f517abc094eb9a9fa418bd 100644 (file)
@@ -281,6 +281,12 @@ static int do_read_inode(struct inode *inode)
        if (f2fs_has_inline_data(inode) && !f2fs_exist_data(inode))
                __recover_inline_status(inode, node_page);
 
+       /* try to recover cold bit for non-dir inode */
+       if (!S_ISDIR(inode->i_mode) && !is_cold_node(node_page)) {
+               set_cold_node(inode, node_page);
+               set_page_dirty(node_page);
+       }
+
        /* get rdev by using inline_info */
        __get_inode_rdev(inode, ri);
 
index c7828be3f1e5db39484ff369d91853e4071463ec..4d42b2f73784cb0c7ee485873cc2280d20a43232 100644 (file)
@@ -2305,6 +2305,7 @@ retry:
        if (!PageUptodate(ipage))
                SetPageUptodate(ipage);
        fill_node_footer(ipage, ino, ino, 0, true);
+       set_cold_node(NULL, ipage);
 
        src = F2FS_INODE(page);
        dst = F2FS_INODE(ipage);
index 0ee3e5ff49a30b68d4b3040efa79642d989b19e4..206ad72e9c25837e9de8a5a77726faaf09daac1c 100644 (file)
@@ -423,7 +423,7 @@ static inline void set_cold_node(struct inode *inode, struct page *page)
        struct f2fs_node *rn = F2FS_NODE(page);
        unsigned int flag = le32_to_cpu(rn->footer.flag);
 
-       if (S_ISDIR(inode->i_mode))
+       if (inode && S_ISDIR(inode->i_mode))
                flag &= ~(0x1 << COLD_BIT_SHIFT);
        else
                flag |= (0x1 << COLD_BIT_SHIFT);