17c1a000dSChao Yu // SPDX-License-Identifier: GPL-2.0 20a8165d7SJaegeuk Kim /* 3d624c96fSJaegeuk Kim * fs/f2fs/recovery.c 4d624c96fSJaegeuk Kim * 5d624c96fSJaegeuk Kim * Copyright (c) 2012 Samsung Electronics Co., Ltd. 6d624c96fSJaegeuk Kim * http://www.samsung.com/ 7d624c96fSJaegeuk Kim */ 8d624c96fSJaegeuk Kim #include <linux/fs.h> 9d624c96fSJaegeuk Kim #include <linux/f2fs_fs.h> 10d624c96fSJaegeuk Kim #include "f2fs.h" 11d624c96fSJaegeuk Kim #include "node.h" 12d624c96fSJaegeuk Kim #include "segment.h" 13d624c96fSJaegeuk Kim 14441ac5cbSJaegeuk Kim /* 15441ac5cbSJaegeuk Kim * Roll forward recovery scenarios. 16441ac5cbSJaegeuk Kim * 17441ac5cbSJaegeuk Kim * [Term] F: fsync_mark, D: dentry_mark 18441ac5cbSJaegeuk Kim * 19441ac5cbSJaegeuk Kim * 1. inode(x) | CP | inode(x) | dnode(F) 20441ac5cbSJaegeuk Kim * -> Update the latest inode(x). 21441ac5cbSJaegeuk Kim * 22441ac5cbSJaegeuk Kim * 2. inode(x) | CP | inode(F) | dnode(F) 23441ac5cbSJaegeuk Kim * -> No problem. 24441ac5cbSJaegeuk Kim * 25441ac5cbSJaegeuk Kim * 3. inode(x) | CP | dnode(F) | inode(x) 26441ac5cbSJaegeuk Kim * -> Recover to the latest dnode(F), and drop the last inode(x) 27441ac5cbSJaegeuk Kim * 28441ac5cbSJaegeuk Kim * 4. inode(x) | CP | dnode(F) | inode(F) 29441ac5cbSJaegeuk Kim * -> No problem. 30441ac5cbSJaegeuk Kim * 31441ac5cbSJaegeuk Kim * 5. CP | inode(x) | dnode(F) 32441ac5cbSJaegeuk Kim * -> The inode(DF) was missing. Should drop this dnode(F). 33441ac5cbSJaegeuk Kim * 34441ac5cbSJaegeuk Kim * 6. CP | inode(DF) | dnode(F) 35441ac5cbSJaegeuk Kim * -> No problem. 36441ac5cbSJaegeuk Kim * 37441ac5cbSJaegeuk Kim * 7. CP | dnode(F) | inode(DF) 38441ac5cbSJaegeuk Kim * -> If f2fs_iget fails, then goto next to find inode(DF). 39441ac5cbSJaegeuk Kim * 40441ac5cbSJaegeuk Kim * 8. CP | dnode(F) | inode(x) 41441ac5cbSJaegeuk Kim * -> If f2fs_iget fails, then goto next to find inode(DF). 42441ac5cbSJaegeuk Kim * But it will fail due to no inode(DF). 43441ac5cbSJaegeuk Kim */ 44441ac5cbSJaegeuk Kim 45d624c96fSJaegeuk Kim static struct kmem_cache *fsync_entry_slab; 46d624c96fSJaegeuk Kim 474d57b86dSChao Yu bool f2fs_space_for_roll_forward(struct f2fs_sb_info *sbi) 48d624c96fSJaegeuk Kim { 4941382ec4SJaegeuk Kim s64 nalloc = percpu_counter_sum_positive(&sbi->alloc_valid_block_count); 5041382ec4SJaegeuk Kim 5141382ec4SJaegeuk Kim if (sbi->last_valid_block_count + nalloc > sbi->user_block_count) 52d624c96fSJaegeuk Kim return false; 53d624c96fSJaegeuk Kim return true; 54d624c96fSJaegeuk Kim } 55d624c96fSJaegeuk Kim 56d624c96fSJaegeuk Kim static struct fsync_inode_entry *get_fsync_inode(struct list_head *head, 57d624c96fSJaegeuk Kim nid_t ino) 58d624c96fSJaegeuk Kim { 59d624c96fSJaegeuk Kim struct fsync_inode_entry *entry; 60d624c96fSJaegeuk Kim 612d7b822aSChao Yu list_for_each_entry(entry, head, list) 62d624c96fSJaegeuk Kim if (entry->inode->i_ino == ino) 63d624c96fSJaegeuk Kim return entry; 642d7b822aSChao Yu 65d624c96fSJaegeuk Kim return NULL; 66d624c96fSJaegeuk Kim } 67d624c96fSJaegeuk Kim 68f4702d61SJaegeuk Kim static struct fsync_inode_entry *add_fsync_inode(struct f2fs_sb_info *sbi, 694b2414d0SChao Yu struct list_head *head, nid_t ino, bool quota_inode) 703f8ab270SChao Yu { 71e8ea9b3dSJaegeuk Kim struct inode *inode; 723f8ab270SChao Yu struct fsync_inode_entry *entry; 734b2414d0SChao Yu int err; 743f8ab270SChao Yu 75e8ea9b3dSJaegeuk Kim inode = f2fs_iget_retry(sbi->sb, ino); 76f4702d61SJaegeuk Kim if (IS_ERR(inode)) 77f4702d61SJaegeuk Kim return ERR_CAST(inode); 78f4702d61SJaegeuk Kim 794b2414d0SChao Yu err = dquot_initialize(inode); 804b2414d0SChao Yu if (err) 814b2414d0SChao Yu goto err_out; 824b2414d0SChao Yu 834b2414d0SChao Yu if (quota_inode) { 844b2414d0SChao Yu err = dquot_alloc_inode(inode); 854b2414d0SChao Yu if (err) 864b2414d0SChao Yu goto err_out; 874b2414d0SChao Yu } 884b2414d0SChao Yu 89e8ea9b3dSJaegeuk Kim entry = f2fs_kmem_cache_alloc(fsync_entry_slab, GFP_F2FS_ZERO); 903f8ab270SChao Yu entry->inode = inode; 913f8ab270SChao Yu list_add_tail(&entry->list, head); 923f8ab270SChao Yu 933f8ab270SChao Yu return entry; 944b2414d0SChao Yu err_out: 954b2414d0SChao Yu iput(inode); 964b2414d0SChao Yu return ERR_PTR(err); 973f8ab270SChao Yu } 983f8ab270SChao Yu 9926b5a079SSheng Yong static void del_fsync_inode(struct fsync_inode_entry *entry, int drop) 1003f8ab270SChao Yu { 10126b5a079SSheng Yong if (drop) { 10226b5a079SSheng Yong /* inode should not be recovered, drop it */ 10326b5a079SSheng Yong f2fs_inode_synced(entry->inode); 10426b5a079SSheng Yong } 1053f8ab270SChao Yu iput(entry->inode); 1063f8ab270SChao Yu list_del(&entry->list); 1073f8ab270SChao Yu kmem_cache_free(fsync_entry_slab, entry); 1083f8ab270SChao Yu } 1093f8ab270SChao Yu 110f61cce5bSChao Yu static int recover_dentry(struct inode *inode, struct page *ipage, 111f61cce5bSChao Yu struct list_head *dir_list) 112d624c96fSJaegeuk Kim { 11358bfaf44SJaegeuk Kim struct f2fs_inode *raw_inode = F2FS_INODE(ipage); 11474d0b917SJaegeuk Kim nid_t pino = le32_to_cpu(raw_inode->i_pino); 1156b8213d9SJaegeuk Kim struct f2fs_dir_entry *de; 116e7ba108aSShuoran Liu struct fscrypt_name fname; 117d624c96fSJaegeuk Kim struct page *page; 1186b8213d9SJaegeuk Kim struct inode *dir, *einode; 119f61cce5bSChao Yu struct fsync_inode_entry *entry; 120d624c96fSJaegeuk Kim int err = 0; 121e7ba108aSShuoran Liu char *name; 122d624c96fSJaegeuk Kim 123f61cce5bSChao Yu entry = get_fsync_inode(dir_list, pino); 124f61cce5bSChao Yu if (!entry) { 1254b2414d0SChao Yu entry = add_fsync_inode(F2FS_I_SB(inode), dir_list, 1264b2414d0SChao Yu pino, false); 127f4702d61SJaegeuk Kim if (IS_ERR(entry)) { 128f4702d61SJaegeuk Kim dir = ERR_CAST(entry); 129f4702d61SJaegeuk Kim err = PTR_ERR(entry); 130f61cce5bSChao Yu goto out; 131e7d55452SJaegeuk Kim } 132f61cce5bSChao Yu } 133f61cce5bSChao Yu 134f61cce5bSChao Yu dir = entry->inode; 135f61cce5bSChao Yu 136e7ba108aSShuoran Liu memset(&fname, 0, sizeof(struct fscrypt_name)); 137e7ba108aSShuoran Liu fname.disk_name.len = le32_to_cpu(raw_inode->i_namelen); 138e7ba108aSShuoran Liu fname.disk_name.name = raw_inode->i_name; 139e7d55452SJaegeuk Kim 140e7ba108aSShuoran Liu if (unlikely(fname.disk_name.len > F2FS_NAME_LEN)) { 141d96b1431SChao Yu WARN_ON(1); 142d96b1431SChao Yu err = -ENAMETOOLONG; 143f61cce5bSChao Yu goto out; 144d96b1431SChao Yu } 1456b8213d9SJaegeuk Kim retry: 146e7ba108aSShuoran Liu de = __f2fs_find_entry(dir, &fname, &page); 147418f6c27SJaegeuk Kim if (de && inode->i_ino == le32_to_cpu(de->ino)) 148bdbc90faSYunlong Song goto out_put; 149418f6c27SJaegeuk Kim 1506b8213d9SJaegeuk Kim if (de) { 151e8ea9b3dSJaegeuk Kim einode = f2fs_iget_retry(inode->i_sb, le32_to_cpu(de->ino)); 1526b8213d9SJaegeuk Kim if (IS_ERR(einode)) { 1536b8213d9SJaegeuk Kim WARN_ON(1); 1545c1f9927SChao Yu err = PTR_ERR(einode); 1555c1f9927SChao Yu if (err == -ENOENT) 1566b8213d9SJaegeuk Kim err = -EEXIST; 157bdbc90faSYunlong Song goto out_put; 1582e5558f4SRuss W. Knize } 1594b2414d0SChao Yu 1604b2414d0SChao Yu err = dquot_initialize(einode); 1614b2414d0SChao Yu if (err) { 1624b2414d0SChao Yu iput(einode); 163bdbc90faSYunlong Song goto out_put; 1644b2414d0SChao Yu } 1654b2414d0SChao Yu 1664d57b86dSChao Yu err = f2fs_acquire_orphan_inode(F2FS_I_SB(inode)); 1672e5558f4SRuss W. Knize if (err) { 1682e5558f4SRuss W. Knize iput(einode); 169bdbc90faSYunlong Song goto out_put; 1706b8213d9SJaegeuk Kim } 171dbeacf02SChao Yu f2fs_delete_entry(de, page, dir, einode); 1726b8213d9SJaegeuk Kim iput(einode); 1736b8213d9SJaegeuk Kim goto retry; 17491246c21SChao Yu } else if (IS_ERR(page)) { 17591246c21SChao Yu err = PTR_ERR(page); 17691246c21SChao Yu } else { 1774d57b86dSChao Yu err = f2fs_add_dentry(dir, &fname, inode, 17891246c21SChao Yu inode->i_ino, inode->i_mode); 1796b8213d9SJaegeuk Kim } 180e8ea9b3dSJaegeuk Kim if (err == -ENOMEM) 181e8ea9b3dSJaegeuk Kim goto retry; 1822e5558f4SRuss W. Knize goto out; 1832e5558f4SRuss W. Knize 184bdbc90faSYunlong Song out_put: 1852e5558f4SRuss W. Knize f2fs_put_page(page, 0); 186d624c96fSJaegeuk Kim out: 187e7ba108aSShuoran Liu if (file_enc_name(inode)) 188e7ba108aSShuoran Liu name = "<encrypted>"; 189e7ba108aSShuoran Liu else 190e7ba108aSShuoran Liu name = raw_inode->i_name; 1916c311ec6SChris Fries f2fs_msg(inode->i_sb, KERN_NOTICE, 1926c311ec6SChris Fries "%s: ino = %x, name = %s, dir = %lx, err = %d", 193e7ba108aSShuoran Liu __func__, ino_of_node(ipage), name, 194f28c06faSDan Carpenter IS_ERR(dir) ? 0 : dir->i_ino, err); 195d624c96fSJaegeuk Kim return err; 196d624c96fSJaegeuk Kim } 197d624c96fSJaegeuk Kim 198af033b2aSChao Yu static int recover_quota_data(struct inode *inode, struct page *page) 199af033b2aSChao Yu { 200af033b2aSChao Yu struct f2fs_inode *raw = F2FS_INODE(page); 201af033b2aSChao Yu struct iattr attr; 202af033b2aSChao Yu uid_t i_uid = le32_to_cpu(raw->i_uid); 203af033b2aSChao Yu gid_t i_gid = le32_to_cpu(raw->i_gid); 204af033b2aSChao Yu int err; 205af033b2aSChao Yu 206af033b2aSChao Yu memset(&attr, 0, sizeof(attr)); 207af033b2aSChao Yu 208af033b2aSChao Yu attr.ia_uid = make_kuid(inode->i_sb->s_user_ns, i_uid); 209af033b2aSChao Yu attr.ia_gid = make_kgid(inode->i_sb->s_user_ns, i_gid); 210af033b2aSChao Yu 211af033b2aSChao Yu if (!uid_eq(attr.ia_uid, inode->i_uid)) 212af033b2aSChao Yu attr.ia_valid |= ATTR_UID; 213af033b2aSChao Yu if (!gid_eq(attr.ia_gid, inode->i_gid)) 214af033b2aSChao Yu attr.ia_valid |= ATTR_GID; 215af033b2aSChao Yu 216af033b2aSChao Yu if (!attr.ia_valid) 217af033b2aSChao Yu return 0; 218af033b2aSChao Yu 219af033b2aSChao Yu err = dquot_transfer(inode, &attr); 220af033b2aSChao Yu if (err) 221af033b2aSChao Yu set_sbi_flag(F2FS_I_SB(inode), SBI_QUOTA_NEED_REPAIR); 222af033b2aSChao Yu return err; 223af033b2aSChao Yu } 224af033b2aSChao Yu 22537a086f0SJaegeuk Kim static void recover_inline_flags(struct inode *inode, struct f2fs_inode *ri) 22637a086f0SJaegeuk Kim { 22737a086f0SJaegeuk Kim if (ri->i_inline & F2FS_PIN_FILE) 22837a086f0SJaegeuk Kim set_inode_flag(inode, FI_PIN_FILE); 22937a086f0SJaegeuk Kim else 23037a086f0SJaegeuk Kim clear_inode_flag(inode, FI_PIN_FILE); 23137a086f0SJaegeuk Kim if (ri->i_inline & F2FS_DATA_EXIST) 23237a086f0SJaegeuk Kim set_inode_flag(inode, FI_DATA_EXIST); 23337a086f0SJaegeuk Kim else 23437a086f0SJaegeuk Kim clear_inode_flag(inode, FI_DATA_EXIST); 23537a086f0SJaegeuk Kim } 23637a086f0SJaegeuk Kim 237af033b2aSChao Yu static int recover_inode(struct inode *inode, struct page *page) 238441ac5cbSJaegeuk Kim { 239441ac5cbSJaegeuk Kim struct f2fs_inode *raw = F2FS_INODE(page); 240e7d55452SJaegeuk Kim char *name; 241af033b2aSChao Yu int err; 242441ac5cbSJaegeuk Kim 243441ac5cbSJaegeuk Kim inode->i_mode = le16_to_cpu(raw->i_mode); 244af033b2aSChao Yu 245af033b2aSChao Yu err = recover_quota_data(inode, page); 246af033b2aSChao Yu if (err) 247af033b2aSChao Yu return err; 248af033b2aSChao Yu 249dc4cd125SChao Yu i_uid_write(inode, le32_to_cpu(raw->i_uid)); 250dc4cd125SChao Yu i_gid_write(inode, le32_to_cpu(raw->i_gid)); 251f4474aa6SChao Yu 252f4474aa6SChao Yu if (raw->i_inline & F2FS_EXTRA_ATTR) { 2537beb01f7SChao Yu if (f2fs_sb_has_project_quota(F2FS_I_SB(inode)) && 254f4474aa6SChao Yu F2FS_FITS_IN_INODE(raw, le16_to_cpu(raw->i_extra_isize), 255f4474aa6SChao Yu i_projid)) { 256f4474aa6SChao Yu projid_t i_projid; 25778130819SChao Yu kprojid_t kprojid; 258f4474aa6SChao Yu 259f4474aa6SChao Yu i_projid = (projid_t)le32_to_cpu(raw->i_projid); 26078130819SChao Yu kprojid = make_kprojid(&init_user_ns, i_projid); 26178130819SChao Yu 26278130819SChao Yu if (!projid_eq(kprojid, F2FS_I(inode)->i_projid)) { 26378130819SChao Yu err = f2fs_transfer_project_quota(inode, 26478130819SChao Yu kprojid); 26578130819SChao Yu if (err) 26678130819SChao Yu return err; 26778130819SChao Yu F2FS_I(inode)->i_projid = kprojid; 26878130819SChao Yu } 269f4474aa6SChao Yu } 270f4474aa6SChao Yu } 271f4474aa6SChao Yu 272fc9581c8SJaegeuk Kim f2fs_i_size_write(inode, le64_to_cpu(raw->i_size)); 2739f0552e0SChao Yu inode->i_atime.tv_sec = le64_to_cpu(raw->i_atime); 274441ac5cbSJaegeuk Kim inode->i_ctime.tv_sec = le64_to_cpu(raw->i_ctime); 275441ac5cbSJaegeuk Kim inode->i_mtime.tv_sec = le64_to_cpu(raw->i_mtime); 2769f0552e0SChao Yu inode->i_atime.tv_nsec = le32_to_cpu(raw->i_atime_nsec); 277441ac5cbSJaegeuk Kim inode->i_ctime.tv_nsec = le32_to_cpu(raw->i_ctime_nsec); 278441ac5cbSJaegeuk Kim inode->i_mtime.tv_nsec = le32_to_cpu(raw->i_mtime_nsec); 279f356fe0cSJaegeuk Kim 28026787236SJaegeuk Kim F2FS_I(inode)->i_advise = raw->i_advise; 28119c73a69SChao Yu F2FS_I(inode)->i_flags = le32_to_cpu(raw->i_flags); 2820c093b59SChao Yu f2fs_set_inode_flags(inode); 2837de36cf3SChao Yu F2FS_I(inode)->i_gc_failures[GC_FAILURE_PIN] = 2847de36cf3SChao Yu le16_to_cpu(raw->i_gc_failures); 28526787236SJaegeuk Kim 28637a086f0SJaegeuk Kim recover_inline_flags(inode, raw); 28737a086f0SJaegeuk Kim 2884a1728caSChao Yu f2fs_mark_inode_dirty_sync(inode, true); 2894a1728caSChao Yu 290e7d55452SJaegeuk Kim if (file_enc_name(inode)) 291e7d55452SJaegeuk Kim name = "<encrypted>"; 292e7d55452SJaegeuk Kim else 293e7d55452SJaegeuk Kim name = F2FS_INODE(page)->i_name; 294e7d55452SJaegeuk Kim 29537a086f0SJaegeuk Kim f2fs_msg(inode->i_sb, KERN_NOTICE, 29637a086f0SJaegeuk Kim "recover_inode: ino = %x, name = %s, inline = %x", 29737a086f0SJaegeuk Kim ino_of_node(page), name, raw->i_inline); 298af033b2aSChao Yu return 0; 299d624c96fSJaegeuk Kim } 300d624c96fSJaegeuk Kim 301d40d30c5SJaegeuk Kim static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head, 302d40d30c5SJaegeuk Kim bool check_only) 303d624c96fSJaegeuk Kim { 304d624c96fSJaegeuk Kim struct curseg_info *curseg; 3054c521f49SJaegeuk Kim struct page *page = NULL; 306d624c96fSJaegeuk Kim block_t blkaddr; 307fb0e72c8SChao Yu unsigned int loop_cnt = 0; 30882902c06SChao Yu unsigned int free_blocks = MAIN_SEGS(sbi) * sbi->blocks_per_seg - 309fb0e72c8SChao Yu valid_user_blocks(sbi); 310d624c96fSJaegeuk Kim int err = 0; 311d624c96fSJaegeuk Kim 312d624c96fSJaegeuk Kim /* get node pages in the current segment */ 313d624c96fSJaegeuk Kim curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); 314695fd1edSChao Yu blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); 315d624c96fSJaegeuk Kim 316d624c96fSJaegeuk Kim while (1) { 317d624c96fSJaegeuk Kim struct fsync_inode_entry *entry; 318d624c96fSJaegeuk Kim 319e1da7872SChao Yu if (!f2fs_is_valid_blkaddr(sbi, blkaddr, META_POR)) 3204c521f49SJaegeuk Kim return 0; 321d624c96fSJaegeuk Kim 3224d57b86dSChao Yu page = f2fs_get_tmp_page(sbi, blkaddr); 3237735730dSChao Yu if (IS_ERR(page)) { 3247735730dSChao Yu err = PTR_ERR(page); 3257735730dSChao Yu break; 3267735730dSChao Yu } 327393ff91fSJaegeuk Kim 32898838579SChao Yu if (!is_recoverable_dnode(page)) { 32998838579SChao Yu f2fs_put_page(page, 1); 330f356fe0cSJaegeuk Kim break; 33198838579SChao Yu } 332d624c96fSJaegeuk Kim 333d624c96fSJaegeuk Kim if (!is_fsync_dnode(page)) 334d624c96fSJaegeuk Kim goto next; 335d624c96fSJaegeuk Kim 336d624c96fSJaegeuk Kim entry = get_fsync_inode(head, ino_of_node(page)); 337d47b8715SChao Yu if (!entry) { 3384b2414d0SChao Yu bool quota_inode = false; 3394b2414d0SChao Yu 340d40d30c5SJaegeuk Kim if (!check_only && 341d40d30c5SJaegeuk Kim IS_INODE(page) && is_dent_dnode(page)) { 3424d57b86dSChao Yu err = f2fs_recover_inode_page(sbi, page); 34398838579SChao Yu if (err) { 34498838579SChao Yu f2fs_put_page(page, 1); 345f356fe0cSJaegeuk Kim break; 34698838579SChao Yu } 3474b2414d0SChao Yu quota_inode = true; 348d624c96fSJaegeuk Kim } 349d624c96fSJaegeuk Kim 350441ac5cbSJaegeuk Kim /* 351441ac5cbSJaegeuk Kim * CP | dnode(F) | inode(DF) 352441ac5cbSJaegeuk Kim * For this case, we should not give up now. 353441ac5cbSJaegeuk Kim */ 3544b2414d0SChao Yu entry = add_fsync_inode(sbi, head, ino_of_node(page), 3554b2414d0SChao Yu quota_inode); 356f4702d61SJaegeuk Kim if (IS_ERR(entry)) { 357f4702d61SJaegeuk Kim err = PTR_ERR(entry); 3588fbc418fSJaegeuk Kim if (err == -ENOENT) { 3598fbc418fSJaegeuk Kim err = 0; 360441ac5cbSJaegeuk Kim goto next; 3618fbc418fSJaegeuk Kim } 36298838579SChao Yu f2fs_put_page(page, 1); 363f356fe0cSJaegeuk Kim break; 364d624c96fSJaegeuk Kim } 365d624c96fSJaegeuk Kim } 366addbe45bSJaegeuk Kim entry->blkaddr = blkaddr; 367addbe45bSJaegeuk Kim 368608514deSJaegeuk Kim if (IS_INODE(page) && is_dent_dnode(page)) 369c52e1b10SJaegeuk Kim entry->last_dentry = blkaddr; 370d624c96fSJaegeuk Kim next: 371fb0e72c8SChao Yu /* sanity check in order to detect looped node chain */ 372fb0e72c8SChao Yu if (++loop_cnt >= free_blocks || 373fb0e72c8SChao Yu blkaddr == next_blkaddr_of_node(page)) { 374fb0e72c8SChao Yu f2fs_msg(sbi->sb, KERN_NOTICE, 375fb0e72c8SChao Yu "%s: detect looped node chain, " 376fb0e72c8SChao Yu "blkaddr:%u, next:%u", 377fb0e72c8SChao Yu __func__, blkaddr, next_blkaddr_of_node(page)); 37898838579SChao Yu f2fs_put_page(page, 1); 379fb0e72c8SChao Yu err = -EINVAL; 380fb0e72c8SChao Yu break; 381fb0e72c8SChao Yu } 382fb0e72c8SChao Yu 383d624c96fSJaegeuk Kim /* check next segment */ 384d624c96fSJaegeuk Kim blkaddr = next_blkaddr_of_node(page); 3854c521f49SJaegeuk Kim f2fs_put_page(page, 1); 386635aee1fSChao Yu 3874d57b86dSChao Yu f2fs_ra_meta_pages_cond(sbi, blkaddr); 388d624c96fSJaegeuk Kim } 389d624c96fSJaegeuk Kim return err; 390d624c96fSJaegeuk Kim } 391d624c96fSJaegeuk Kim 39226b5a079SSheng Yong static void destroy_fsync_dnodes(struct list_head *head, int drop) 393d624c96fSJaegeuk Kim { 394d8b79b2fSDan Carpenter struct fsync_inode_entry *entry, *tmp; 395d8b79b2fSDan Carpenter 3963f8ab270SChao Yu list_for_each_entry_safe(entry, tmp, head, list) 39726b5a079SSheng Yong del_fsync_inode(entry, drop); 398d624c96fSJaegeuk Kim } 399d624c96fSJaegeuk Kim 40039cf72cfSJaegeuk Kim static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi, 401b292dcabSJaegeuk Kim block_t blkaddr, struct dnode_of_data *dn) 402d624c96fSJaegeuk Kim { 403d624c96fSJaegeuk Kim struct seg_entry *sentry; 404d624c96fSJaegeuk Kim unsigned int segno = GET_SEGNO(sbi, blkaddr); 405491c0854SJaegeuk Kim unsigned short blkoff = GET_BLKOFF_FROM_SEG0(sbi, blkaddr); 406f6517cfcSJaegeuk Kim struct f2fs_summary_block *sum_node; 407d624c96fSJaegeuk Kim struct f2fs_summary sum; 408f6517cfcSJaegeuk Kim struct page *sum_page, *node_page; 409c9ef4810SJaegeuk Kim struct dnode_of_data tdn = *dn; 410b292dcabSJaegeuk Kim nid_t ino, nid; 411d624c96fSJaegeuk Kim struct inode *inode; 412de93653fSJaegeuk Kim unsigned int offset; 413d624c96fSJaegeuk Kim block_t bidx; 414d624c96fSJaegeuk Kim int i; 415d624c96fSJaegeuk Kim 416d624c96fSJaegeuk Kim sentry = get_seg_entry(sbi, segno); 417d624c96fSJaegeuk Kim if (!f2fs_test_bit(blkoff, sentry->cur_valid_map)) 41839cf72cfSJaegeuk Kim return 0; 419d624c96fSJaegeuk Kim 420d624c96fSJaegeuk Kim /* Get the previous summary */ 421125c9fb1SJaegeuk Kim for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { 422d624c96fSJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, i); 423d624c96fSJaegeuk Kim if (curseg->segno == segno) { 424d624c96fSJaegeuk Kim sum = curseg->sum_blk->entries[blkoff]; 425f6517cfcSJaegeuk Kim goto got_it; 426d624c96fSJaegeuk Kim } 427d624c96fSJaegeuk Kim } 428d624c96fSJaegeuk Kim 4294d57b86dSChao Yu sum_page = f2fs_get_sum_page(sbi, segno); 430edc55aafSJaegeuk Kim if (IS_ERR(sum_page)) 431edc55aafSJaegeuk Kim return PTR_ERR(sum_page); 432f6517cfcSJaegeuk Kim sum_node = (struct f2fs_summary_block *)page_address(sum_page); 433f6517cfcSJaegeuk Kim sum = sum_node->entries[blkoff]; 434f6517cfcSJaegeuk Kim f2fs_put_page(sum_page, 1); 435f6517cfcSJaegeuk Kim got_it: 436b292dcabSJaegeuk Kim /* Use the locked dnode page and inode */ 437b292dcabSJaegeuk Kim nid = le32_to_cpu(sum.nid); 438b292dcabSJaegeuk Kim if (dn->inode->i_ino == nid) { 439b292dcabSJaegeuk Kim tdn.nid = nid; 440c9ef4810SJaegeuk Kim if (!dn->inode_page_locked) 441c9ef4810SJaegeuk Kim lock_page(dn->inode_page); 442b292dcabSJaegeuk Kim tdn.node_page = dn->inode_page; 443060dd67bSJaegeuk Kim tdn.ofs_in_node = le16_to_cpu(sum.ofs_in_node); 444c9ef4810SJaegeuk Kim goto truncate_out; 445b292dcabSJaegeuk Kim } else if (dn->nid == nid) { 446060dd67bSJaegeuk Kim tdn.ofs_in_node = le16_to_cpu(sum.ofs_in_node); 447c9ef4810SJaegeuk Kim goto truncate_out; 448b292dcabSJaegeuk Kim } 449b292dcabSJaegeuk Kim 450d624c96fSJaegeuk Kim /* Get the node page */ 4514d57b86dSChao Yu node_page = f2fs_get_node_page(sbi, nid); 45239cf72cfSJaegeuk Kim if (IS_ERR(node_page)) 45339cf72cfSJaegeuk Kim return PTR_ERR(node_page); 454de93653fSJaegeuk Kim 455de93653fSJaegeuk Kim offset = ofs_of_node(node_page); 456d624c96fSJaegeuk Kim ino = ino_of_node(node_page); 457d624c96fSJaegeuk Kim f2fs_put_page(node_page, 1); 458d624c96fSJaegeuk Kim 45960979115SJaegeuk Kim if (ino != dn->inode->i_ino) { 4604b2414d0SChao Yu int ret; 4614b2414d0SChao Yu 462d624c96fSJaegeuk Kim /* Deallocate previous index in the node page */ 463e8ea9b3dSJaegeuk Kim inode = f2fs_iget_retry(sbi->sb, ino); 46406025f4dSNamjae Jeon if (IS_ERR(inode)) 46539cf72cfSJaegeuk Kim return PTR_ERR(inode); 4664b2414d0SChao Yu 4674b2414d0SChao Yu ret = dquot_initialize(inode); 4684b2414d0SChao Yu if (ret) { 4694b2414d0SChao Yu iput(inode); 4704b2414d0SChao Yu return ret; 4714b2414d0SChao Yu } 47260979115SJaegeuk Kim } else { 47360979115SJaegeuk Kim inode = dn->inode; 47460979115SJaegeuk Kim } 47506025f4dSNamjae Jeon 4764d57b86dSChao Yu bidx = f2fs_start_bidx_of_node(offset, inode) + 4774d57b86dSChao Yu le16_to_cpu(sum.ofs_in_node); 478de93653fSJaegeuk Kim 479c9ef4810SJaegeuk Kim /* 480c9ef4810SJaegeuk Kim * if inode page is locked, unlock temporarily, but its reference 481c9ef4810SJaegeuk Kim * count keeps alive. 482c9ef4810SJaegeuk Kim */ 483c9ef4810SJaegeuk Kim if (ino == dn->inode->i_ino && dn->inode_page_locked) 484c9ef4810SJaegeuk Kim unlock_page(dn->inode_page); 485c9ef4810SJaegeuk Kim 486c9ef4810SJaegeuk Kim set_new_dnode(&tdn, inode, NULL, NULL, 0); 4874d57b86dSChao Yu if (f2fs_get_dnode_of_data(&tdn, bidx, LOOKUP_NODE)) 488c9ef4810SJaegeuk Kim goto out; 489c9ef4810SJaegeuk Kim 490c9ef4810SJaegeuk Kim if (tdn.data_blkaddr == blkaddr) 4914d57b86dSChao Yu f2fs_truncate_data_blocks_range(&tdn, 1); 492c9ef4810SJaegeuk Kim 493c9ef4810SJaegeuk Kim f2fs_put_dnode(&tdn); 494c9ef4810SJaegeuk Kim out: 495c9ef4810SJaegeuk Kim if (ino != dn->inode->i_ino) 496c9ef4810SJaegeuk Kim iput(inode); 497c9ef4810SJaegeuk Kim else if (dn->inode_page_locked) 498c9ef4810SJaegeuk Kim lock_page(dn->inode_page); 499c9ef4810SJaegeuk Kim return 0; 500c9ef4810SJaegeuk Kim 501c9ef4810SJaegeuk Kim truncate_out: 5027a2af766SChao Yu if (datablock_addr(tdn.inode, tdn.node_page, 5037a2af766SChao Yu tdn.ofs_in_node) == blkaddr) 5044d57b86dSChao Yu f2fs_truncate_data_blocks_range(&tdn, 1); 505c9ef4810SJaegeuk Kim if (dn->inode->i_ino == nid && !dn->inode_page_locked) 506c9ef4810SJaegeuk Kim unlock_page(dn->inode_page); 50739cf72cfSJaegeuk Kim return 0; 508d624c96fSJaegeuk Kim } 509d624c96fSJaegeuk Kim 5106ead1142SJaegeuk Kim static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, 511e17d488bSSheng Yong struct page *page) 512d624c96fSJaegeuk Kim { 513d624c96fSJaegeuk Kim struct dnode_of_data dn; 514d624c96fSJaegeuk Kim struct node_info ni; 51581ca7350SChao Yu unsigned int start, end; 516f356fe0cSJaegeuk Kim int err = 0, recovered = 0; 517d624c96fSJaegeuk Kim 5181c35a90eSJaegeuk Kim /* step 1: recover xattr */ 5191c35a90eSJaegeuk Kim if (IS_INODE(page)) { 5204d57b86dSChao Yu f2fs_recover_inline_xattr(inode, page); 5211c35a90eSJaegeuk Kim } else if (f2fs_has_xattr_block(ofs_of_node(page))) { 5224d57b86dSChao Yu err = f2fs_recover_xattr_data(inode, page); 523d260081cSChao Yu if (!err) 524d260081cSChao Yu recovered++; 5251c35a90eSJaegeuk Kim goto out; 5261c35a90eSJaegeuk Kim } 52770cfed88SChao Yu 5281c35a90eSJaegeuk Kim /* step 2: recover inline data */ 5294d57b86dSChao Yu if (f2fs_recover_inline_data(inode, page)) 5301e1bb4baSJaegeuk Kim goto out; 5311e1bb4baSJaegeuk Kim 5321c35a90eSJaegeuk Kim /* step 3: recover data indices */ 5334d57b86dSChao Yu start = f2fs_start_bidx_of_node(ofs_of_node(page), inode); 53481ca7350SChao Yu end = start + ADDRS_PER_PAGE(page, inode); 535d624c96fSJaegeuk Kim 536d624c96fSJaegeuk Kim set_new_dnode(&dn, inode, NULL, NULL, 0); 537e8ea9b3dSJaegeuk Kim retry_dn: 5384d57b86dSChao Yu err = f2fs_get_dnode_of_data(&dn, start, ALLOC_NODE); 539e8ea9b3dSJaegeuk Kim if (err) { 540e8ea9b3dSJaegeuk Kim if (err == -ENOMEM) { 541e8ea9b3dSJaegeuk Kim congestion_wait(BLK_RW_ASYNC, HZ/50); 542e8ea9b3dSJaegeuk Kim goto retry_dn; 543e8ea9b3dSJaegeuk Kim } 5441e1bb4baSJaegeuk Kim goto out; 545e8ea9b3dSJaegeuk Kim } 546d624c96fSJaegeuk Kim 547bae0ee7aSChao Yu f2fs_wait_on_page_writeback(dn.node_page, NODE, true, true); 548d624c96fSJaegeuk Kim 5497735730dSChao Yu err = f2fs_get_node_info(sbi, dn.nid, &ni); 5507735730dSChao Yu if (err) 5517735730dSChao Yu goto err; 5527735730dSChao Yu 5539850cf4aSJaegeuk Kim f2fs_bug_on(sbi, ni.ino != ino_of_node(page)); 55422d61e28SChao Yu 55522d61e28SChao Yu if (ofs_of_node(dn.node_page) != ofs_of_node(page)) { 55622d61e28SChao Yu f2fs_msg(sbi->sb, KERN_WARNING, 55722d61e28SChao Yu "Inconsistent ofs_of_node, ino:%lu, ofs:%u, %u", 55822d61e28SChao Yu inode->i_ino, ofs_of_node(dn.node_page), 55922d61e28SChao Yu ofs_of_node(page)); 56022d61e28SChao Yu err = -EFAULT; 56122d61e28SChao Yu goto err; 56222d61e28SChao Yu } 563d624c96fSJaegeuk Kim 56412a8343eSChao Yu for (; start < end; start++, dn.ofs_in_node++) { 565d624c96fSJaegeuk Kim block_t src, dest; 566d624c96fSJaegeuk Kim 5677a2af766SChao Yu src = datablock_addr(dn.inode, dn.node_page, dn.ofs_in_node); 5687a2af766SChao Yu dest = datablock_addr(dn.inode, page, dn.ofs_in_node); 569d624c96fSJaegeuk Kim 57012a8343eSChao Yu /* skip recovering if dest is the same as src */ 57112a8343eSChao Yu if (src == dest) 57212a8343eSChao Yu continue; 57312a8343eSChao Yu 57412a8343eSChao Yu /* dest is invalid, just invalidate src block */ 57512a8343eSChao Yu if (dest == NULL_ADDR) { 5764d57b86dSChao Yu f2fs_truncate_data_blocks_range(&dn, 1); 57712a8343eSChao Yu continue; 57812a8343eSChao Yu } 57912a8343eSChao Yu 58026787236SJaegeuk Kim if (!file_keep_isize(inode) && 581dba79f38SChao Yu (i_size_read(inode) <= ((loff_t)start << PAGE_SHIFT))) 582dba79f38SChao Yu f2fs_i_size_write(inode, 583dba79f38SChao Yu (loff_t)(start + 1) << PAGE_SHIFT); 58426de9b11SJaegeuk Kim 58512a8343eSChao Yu /* 58612a8343eSChao Yu * dest is reserved block, invalidate src block 58712a8343eSChao Yu * and then reserve one new block in dnode page. 58812a8343eSChao Yu */ 58912a8343eSChao Yu if (dest == NEW_ADDR) { 5904d57b86dSChao Yu f2fs_truncate_data_blocks_range(&dn, 1); 5914d57b86dSChao Yu f2fs_reserve_new_block(&dn); 59212a8343eSChao Yu continue; 59312a8343eSChao Yu } 59412a8343eSChao Yu 59512a8343eSChao Yu /* dest is valid block, try to recover from src to dest */ 596e1da7872SChao Yu if (f2fs_is_valid_blkaddr(sbi, dest, META_POR)) { 597e03b07d9SJaegeuk Kim 598d624c96fSJaegeuk Kim if (src == NULL_ADDR) { 5994d57b86dSChao Yu err = f2fs_reserve_new_block(&dn); 6007fa750a1SArnd Bergmann while (err && 6017fa750a1SArnd Bergmann IS_ENABLED(CONFIG_F2FS_FAULT_INJECTION)) 6024d57b86dSChao Yu err = f2fs_reserve_new_block(&dn); 603d624c96fSJaegeuk Kim /* We should not get -ENOSPC */ 6049850cf4aSJaegeuk Kim f2fs_bug_on(sbi, err); 6056f3ec995SJaegeuk Kim if (err) 6066f3ec995SJaegeuk Kim goto err; 607d624c96fSJaegeuk Kim } 608e8ea9b3dSJaegeuk Kim retry_prev: 609d624c96fSJaegeuk Kim /* Check the previous node page having this index */ 61039cf72cfSJaegeuk Kim err = check_index_in_prev_nodes(sbi, dest, &dn); 611e8ea9b3dSJaegeuk Kim if (err) { 612e8ea9b3dSJaegeuk Kim if (err == -ENOMEM) { 613e8ea9b3dSJaegeuk Kim congestion_wait(BLK_RW_ASYNC, HZ/50); 614e8ea9b3dSJaegeuk Kim goto retry_prev; 615e8ea9b3dSJaegeuk Kim } 61639cf72cfSJaegeuk Kim goto err; 617e8ea9b3dSJaegeuk Kim } 618d624c96fSJaegeuk Kim 619d624c96fSJaegeuk Kim /* write dummy data page */ 620528e3459SChao Yu f2fs_replace_block(sbi, &dn, src, dest, 62128bc106bSChao Yu ni.version, false, false); 622f356fe0cSJaegeuk Kim recovered++; 623d624c96fSJaegeuk Kim } 624d624c96fSJaegeuk Kim } 625d624c96fSJaegeuk Kim 626d624c96fSJaegeuk Kim copy_node_footer(dn.node_page, page); 627d624c96fSJaegeuk Kim fill_node_footer(dn.node_page, dn.nid, ni.ino, 628d624c96fSJaegeuk Kim ofs_of_node(page), false); 629d624c96fSJaegeuk Kim set_page_dirty(dn.node_page); 63039cf72cfSJaegeuk Kim err: 631d624c96fSJaegeuk Kim f2fs_put_dnode(&dn); 6321e1bb4baSJaegeuk Kim out: 6336c311ec6SChris Fries f2fs_msg(sbi->sb, KERN_NOTICE, 63426787236SJaegeuk Kim "recover_data: ino = %lx (i_size: %s) recovered = %d, err = %d", 63526787236SJaegeuk Kim inode->i_ino, 63626787236SJaegeuk Kim file_keep_isize(inode) ? "keep" : "recover", 63726787236SJaegeuk Kim recovered, err); 63839cf72cfSJaegeuk Kim return err; 639d624c96fSJaegeuk Kim } 640d624c96fSJaegeuk Kim 641f61cce5bSChao Yu static int recover_data(struct f2fs_sb_info *sbi, struct list_head *inode_list, 64226b5a079SSheng Yong struct list_head *tmp_inode_list, struct list_head *dir_list) 643d624c96fSJaegeuk Kim { 644d624c96fSJaegeuk Kim struct curseg_info *curseg; 6454c521f49SJaegeuk Kim struct page *page = NULL; 6466ead1142SJaegeuk Kim int err = 0; 647d624c96fSJaegeuk Kim block_t blkaddr; 648d624c96fSJaegeuk Kim 649d624c96fSJaegeuk Kim /* get node pages in the current segment */ 650b7973f23SChao Yu curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); 651d624c96fSJaegeuk Kim blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); 652d624c96fSJaegeuk Kim 653d624c96fSJaegeuk Kim while (1) { 654d624c96fSJaegeuk Kim struct fsync_inode_entry *entry; 655d624c96fSJaegeuk Kim 656e1da7872SChao Yu if (!f2fs_is_valid_blkaddr(sbi, blkaddr, META_POR)) 65745856affSJaegeuk Kim break; 658d624c96fSJaegeuk Kim 6594d57b86dSChao Yu f2fs_ra_meta_pages_cond(sbi, blkaddr); 660635aee1fSChao Yu 6614d57b86dSChao Yu page = f2fs_get_tmp_page(sbi, blkaddr); 6627735730dSChao Yu if (IS_ERR(page)) { 6637735730dSChao Yu err = PTR_ERR(page); 6647735730dSChao Yu break; 6657735730dSChao Yu } 6664c521f49SJaegeuk Kim 667a468f0efSJaegeuk Kim if (!is_recoverable_dnode(page)) { 6684c521f49SJaegeuk Kim f2fs_put_page(page, 1); 6694c521f49SJaegeuk Kim break; 6704c521f49SJaegeuk Kim } 6714c521f49SJaegeuk Kim 672f61cce5bSChao Yu entry = get_fsync_inode(inode_list, ino_of_node(page)); 673d624c96fSJaegeuk Kim if (!entry) 674d624c96fSJaegeuk Kim goto next; 675441ac5cbSJaegeuk Kim /* 676441ac5cbSJaegeuk Kim * inode(x) | CP | inode(x) | dnode(F) 677441ac5cbSJaegeuk Kim * In this case, we can lose the latest inode(x). 678c52e1b10SJaegeuk Kim * So, call recover_inode for the inode update. 679441ac5cbSJaegeuk Kim */ 680af033b2aSChao Yu if (IS_INODE(page)) { 681af033b2aSChao Yu err = recover_inode(entry->inode, page); 68298838579SChao Yu if (err) { 68398838579SChao Yu f2fs_put_page(page, 1); 684af033b2aSChao Yu break; 685af033b2aSChao Yu } 68698838579SChao Yu } 687c52e1b10SJaegeuk Kim if (entry->last_dentry == blkaddr) { 688f61cce5bSChao Yu err = recover_dentry(entry->inode, page, dir_list); 689c52e1b10SJaegeuk Kim if (err) { 690c52e1b10SJaegeuk Kim f2fs_put_page(page, 1); 691c52e1b10SJaegeuk Kim break; 692c52e1b10SJaegeuk Kim } 693c52e1b10SJaegeuk Kim } 694e17d488bSSheng Yong err = do_recover_data(sbi, entry->inode, page); 6954c521f49SJaegeuk Kim if (err) { 6964c521f49SJaegeuk Kim f2fs_put_page(page, 1); 69745856affSJaegeuk Kim break; 6984c521f49SJaegeuk Kim } 699d624c96fSJaegeuk Kim 7003f8ab270SChao Yu if (entry->blkaddr == blkaddr) 70126b5a079SSheng Yong list_move_tail(&entry->list, tmp_inode_list); 702d624c96fSJaegeuk Kim next: 703d624c96fSJaegeuk Kim /* check next segment */ 704d624c96fSJaegeuk Kim blkaddr = next_blkaddr_of_node(page); 7054c521f49SJaegeuk Kim f2fs_put_page(page, 1); 706d624c96fSJaegeuk Kim } 7076ead1142SJaegeuk Kim if (!err) 7084d57b86dSChao Yu f2fs_allocate_new_segments(sbi); 7096ead1142SJaegeuk Kim return err; 710d624c96fSJaegeuk Kim } 711d624c96fSJaegeuk Kim 7124d57b86dSChao Yu int f2fs_recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) 713d624c96fSJaegeuk Kim { 71426b5a079SSheng Yong struct list_head inode_list, tmp_inode_list; 715f61cce5bSChao Yu struct list_head dir_list; 7166ead1142SJaegeuk Kim int err; 7176781eabbSJaegeuk Kim int ret = 0; 7184b2414d0SChao Yu unsigned long s_flags = sbi->sb->s_flags; 719aabe5136SHaicheng Li bool need_writecp = false; 720ea676733SJaegeuk Kim #ifdef CONFIG_QUOTA 721ea676733SJaegeuk Kim int quota_enabled; 722ea676733SJaegeuk Kim #endif 723d624c96fSJaegeuk Kim 7241751e8a6SLinus Torvalds if (s_flags & SB_RDONLY) { 725e6b0b159SYunlei He f2fs_msg(sbi->sb, KERN_INFO, 726e6b0b159SYunlei He "recover fsync data on readonly fs"); 7271751e8a6SLinus Torvalds sbi->sb->s_flags &= ~SB_RDONLY; 7284b2414d0SChao Yu } 7294b2414d0SChao Yu 7304b2414d0SChao Yu #ifdef CONFIG_QUOTA 7314b2414d0SChao Yu /* Needed for iput() to work correctly and not trash data */ 7321751e8a6SLinus Torvalds sbi->sb->s_flags |= SB_ACTIVE; 7334b2414d0SChao Yu /* Turn on quotas so that they are updated correctly */ 7341751e8a6SLinus Torvalds quota_enabled = f2fs_enable_quota_files(sbi, s_flags & SB_RDONLY); 7354b2414d0SChao Yu #endif 7364b2414d0SChao Yu 737d624c96fSJaegeuk Kim fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry", 738e8512d2eSGu Zheng sizeof(struct fsync_inode_entry)); 7394b2414d0SChao Yu if (!fsync_entry_slab) { 7404b2414d0SChao Yu err = -ENOMEM; 7414b2414d0SChao Yu goto out; 7424b2414d0SChao Yu } 743d624c96fSJaegeuk Kim 744d624c96fSJaegeuk Kim INIT_LIST_HEAD(&inode_list); 74526b5a079SSheng Yong INIT_LIST_HEAD(&tmp_inode_list); 746f61cce5bSChao Yu INIT_LIST_HEAD(&dir_list); 747d624c96fSJaegeuk Kim 74814f4e690SJaegeuk Kim /* prevent checkpoint */ 74914f4e690SJaegeuk Kim mutex_lock(&sbi->cp_mutex); 75014f4e690SJaegeuk Kim 751315df839SJaegeuk Kim /* step #1: find fsynced inode numbers */ 752d40d30c5SJaegeuk Kim err = find_fsync_dnodes(sbi, &inode_list, check_only); 7536781eabbSJaegeuk Kim if (err || list_empty(&inode_list)) 7544b2414d0SChao Yu goto skip; 755d624c96fSJaegeuk Kim 7566781eabbSJaegeuk Kim if (check_only) { 7576781eabbSJaegeuk Kim ret = 1; 7584b2414d0SChao Yu goto skip; 7596781eabbSJaegeuk Kim } 760d624c96fSJaegeuk Kim 761aabe5136SHaicheng Li need_writecp = true; 762691c6fd2SChao Yu 763d624c96fSJaegeuk Kim /* step #2: recover data */ 76426b5a079SSheng Yong err = recover_data(sbi, &inode_list, &tmp_inode_list, &dir_list); 765b307384eSJaegeuk Kim if (!err) 7669850cf4aSJaegeuk Kim f2fs_bug_on(sbi, !list_empty(&inode_list)); 76726b5a079SSheng Yong else { 76826b5a079SSheng Yong /* restore s_flags to let iput() trash data */ 76926b5a079SSheng Yong sbi->sb->s_flags = s_flags; 77026b5a079SSheng Yong } 7714b2414d0SChao Yu skip: 77226b5a079SSheng Yong destroy_fsync_dnodes(&inode_list, err); 77326b5a079SSheng Yong destroy_fsync_dnodes(&tmp_inode_list, err); 774cf2271e7SJaegeuk Kim 7754c521f49SJaegeuk Kim /* truncate meta pages to be used by the recovery */ 7764c521f49SJaegeuk Kim truncate_inode_pages_range(META_MAPPING(sbi), 77709cbfeafSKirill A. Shutemov (loff_t)MAIN_BLKADDR(sbi) << PAGE_SHIFT, -1); 7784c521f49SJaegeuk Kim 779cf2271e7SJaegeuk Kim if (err) { 780cf2271e7SJaegeuk Kim truncate_inode_pages_final(NODE_MAPPING(sbi)); 781cf2271e7SJaegeuk Kim truncate_inode_pages_final(META_MAPPING(sbi)); 78226b5a079SSheng Yong } else { 783caf0047eSChao Yu clear_sbi_flag(sbi, SBI_POR_DOING); 78426b5a079SSheng Yong } 78514f4e690SJaegeuk Kim mutex_unlock(&sbi->cp_mutex); 786a468f0efSJaegeuk Kim 7879e1e6df4SJaegeuk Kim /* let's drop all the directory inodes for clean checkpoint */ 78826b5a079SSheng Yong destroy_fsync_dnodes(&dir_list, err); 7899e1e6df4SJaegeuk Kim 7901378752bSChao Yu if (need_writecp) { 7911378752bSChao Yu set_sbi_flag(sbi, SBI_IS_RECOVERED); 7921378752bSChao Yu 7931378752bSChao Yu if (!err) { 79475ab4cb8SJaegeuk Kim struct cp_control cpc = { 79510027551SJaegeuk Kim .reason = CP_RECOVERY, 79675ab4cb8SJaegeuk Kim }; 7974d57b86dSChao Yu err = f2fs_write_checkpoint(sbi, &cpc); 798cf2271e7SJaegeuk Kim } 7991378752bSChao Yu } 800f61cce5bSChao Yu 801f61cce5bSChao Yu kmem_cache_destroy(fsync_entry_slab); 8024b2414d0SChao Yu out: 8034b2414d0SChao Yu #ifdef CONFIG_QUOTA 8044b2414d0SChao Yu /* Turn quotas off */ 805ea676733SJaegeuk Kim if (quota_enabled) 8064b2414d0SChao Yu f2fs_quota_off_umount(sbi->sb); 8074b2414d0SChao Yu #endif 8081751e8a6SLinus Torvalds sbi->sb->s_flags = s_flags; /* Restore SB_RDONLY status */ 8094b2414d0SChao Yu 8106781eabbSJaegeuk Kim return ret ? ret: err; 811d624c96fSJaegeuk Kim } 812