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 */ 87ad08a58SDaniel Rosenberg #include <asm/unaligned.h> 9d624c96fSJaegeuk Kim #include <linux/fs.h> 10d624c96fSJaegeuk Kim #include <linux/f2fs_fs.h> 11d624c96fSJaegeuk Kim #include "f2fs.h" 12d624c96fSJaegeuk Kim #include "node.h" 13d624c96fSJaegeuk Kim #include "segment.h" 14d624c96fSJaegeuk Kim 15441ac5cbSJaegeuk Kim /* 16441ac5cbSJaegeuk Kim * Roll forward recovery scenarios. 17441ac5cbSJaegeuk Kim * 18441ac5cbSJaegeuk Kim * [Term] F: fsync_mark, D: dentry_mark 19441ac5cbSJaegeuk Kim * 20441ac5cbSJaegeuk Kim * 1. inode(x) | CP | inode(x) | dnode(F) 21441ac5cbSJaegeuk Kim * -> Update the latest inode(x). 22441ac5cbSJaegeuk Kim * 23441ac5cbSJaegeuk Kim * 2. inode(x) | CP | inode(F) | dnode(F) 24441ac5cbSJaegeuk Kim * -> No problem. 25441ac5cbSJaegeuk Kim * 26441ac5cbSJaegeuk Kim * 3. inode(x) | CP | dnode(F) | inode(x) 27441ac5cbSJaegeuk Kim * -> Recover to the latest dnode(F), and drop the last inode(x) 28441ac5cbSJaegeuk Kim * 29441ac5cbSJaegeuk Kim * 4. inode(x) | CP | dnode(F) | inode(F) 30441ac5cbSJaegeuk Kim * -> No problem. 31441ac5cbSJaegeuk Kim * 32441ac5cbSJaegeuk Kim * 5. CP | inode(x) | dnode(F) 33441ac5cbSJaegeuk Kim * -> The inode(DF) was missing. Should drop this dnode(F). 34441ac5cbSJaegeuk Kim * 35441ac5cbSJaegeuk Kim * 6. CP | inode(DF) | dnode(F) 36441ac5cbSJaegeuk Kim * -> No problem. 37441ac5cbSJaegeuk Kim * 38441ac5cbSJaegeuk Kim * 7. CP | dnode(F) | inode(DF) 39441ac5cbSJaegeuk Kim * -> If f2fs_iget fails, then goto next to find inode(DF). 40441ac5cbSJaegeuk Kim * 41441ac5cbSJaegeuk Kim * 8. CP | dnode(F) | inode(x) 42441ac5cbSJaegeuk Kim * -> If f2fs_iget fails, then goto next to find inode(DF). 43441ac5cbSJaegeuk Kim * But it will fail due to no inode(DF). 44441ac5cbSJaegeuk Kim */ 45441ac5cbSJaegeuk Kim 46d624c96fSJaegeuk Kim static struct kmem_cache *fsync_entry_slab; 47d624c96fSJaegeuk Kim 484d9a2bb1SChao Yu #ifdef CONFIG_UNICODE 494d9a2bb1SChao Yu extern struct kmem_cache *f2fs_cf_name_slab; 504d9a2bb1SChao Yu #endif 514d9a2bb1SChao Yu 524d57b86dSChao Yu bool f2fs_space_for_roll_forward(struct f2fs_sb_info *sbi) 53d624c96fSJaegeuk Kim { 5441382ec4SJaegeuk Kim s64 nalloc = percpu_counter_sum_positive(&sbi->alloc_valid_block_count); 5541382ec4SJaegeuk Kim 5641382ec4SJaegeuk Kim if (sbi->last_valid_block_count + nalloc > sbi->user_block_count) 57d624c96fSJaegeuk Kim return false; 58d624c96fSJaegeuk Kim return true; 59d624c96fSJaegeuk Kim } 60d624c96fSJaegeuk Kim 61d624c96fSJaegeuk Kim static struct fsync_inode_entry *get_fsync_inode(struct list_head *head, 62d624c96fSJaegeuk Kim nid_t ino) 63d624c96fSJaegeuk Kim { 64d624c96fSJaegeuk Kim struct fsync_inode_entry *entry; 65d624c96fSJaegeuk Kim 662d7b822aSChao Yu list_for_each_entry(entry, head, list) 67d624c96fSJaegeuk Kim if (entry->inode->i_ino == ino) 68d624c96fSJaegeuk Kim return entry; 692d7b822aSChao Yu 70d624c96fSJaegeuk Kim return NULL; 71d624c96fSJaegeuk Kim } 72d624c96fSJaegeuk Kim 73f4702d61SJaegeuk Kim static struct fsync_inode_entry *add_fsync_inode(struct f2fs_sb_info *sbi, 744b2414d0SChao Yu struct list_head *head, nid_t ino, bool quota_inode) 753f8ab270SChao Yu { 76e8ea9b3dSJaegeuk Kim struct inode *inode; 773f8ab270SChao Yu struct fsync_inode_entry *entry; 784b2414d0SChao Yu int err; 793f8ab270SChao Yu 80e8ea9b3dSJaegeuk Kim inode = f2fs_iget_retry(sbi->sb, ino); 81f4702d61SJaegeuk Kim if (IS_ERR(inode)) 82f4702d61SJaegeuk Kim return ERR_CAST(inode); 83f4702d61SJaegeuk Kim 844b2414d0SChao Yu err = dquot_initialize(inode); 854b2414d0SChao Yu if (err) 864b2414d0SChao Yu goto err_out; 874b2414d0SChao Yu 884b2414d0SChao Yu if (quota_inode) { 894b2414d0SChao Yu err = dquot_alloc_inode(inode); 904b2414d0SChao Yu if (err) 914b2414d0SChao Yu goto err_out; 924b2414d0SChao Yu } 934b2414d0SChao Yu 94*32410577SChao Yu entry = f2fs_kmem_cache_alloc(fsync_entry_slab, 95*32410577SChao Yu GFP_F2FS_ZERO, true, NULL); 963f8ab270SChao Yu entry->inode = inode; 973f8ab270SChao Yu list_add_tail(&entry->list, head); 983f8ab270SChao Yu 993f8ab270SChao Yu return entry; 1004b2414d0SChao Yu err_out: 1014b2414d0SChao Yu iput(inode); 1024b2414d0SChao Yu return ERR_PTR(err); 1033f8ab270SChao Yu } 1043f8ab270SChao Yu 10526b5a079SSheng Yong static void del_fsync_inode(struct fsync_inode_entry *entry, int drop) 1063f8ab270SChao Yu { 10726b5a079SSheng Yong if (drop) { 10826b5a079SSheng Yong /* inode should not be recovered, drop it */ 10926b5a079SSheng Yong f2fs_inode_synced(entry->inode); 11026b5a079SSheng Yong } 1113f8ab270SChao Yu iput(entry->inode); 1123f8ab270SChao Yu list_del(&entry->list); 1133f8ab270SChao Yu kmem_cache_free(fsync_entry_slab, entry); 1143f8ab270SChao Yu } 1153f8ab270SChao Yu 11643c780baSEric Biggers static int init_recovered_filename(const struct inode *dir, 11743c780baSEric Biggers struct f2fs_inode *raw_inode, 11843c780baSEric Biggers struct f2fs_filename *fname, 11943c780baSEric Biggers struct qstr *usr_fname) 12043c780baSEric Biggers { 12143c780baSEric Biggers int err; 12243c780baSEric Biggers 12343c780baSEric Biggers memset(fname, 0, sizeof(*fname)); 12443c780baSEric Biggers fname->disk_name.len = le32_to_cpu(raw_inode->i_namelen); 12543c780baSEric Biggers fname->disk_name.name = raw_inode->i_name; 12643c780baSEric Biggers 12743c780baSEric Biggers if (WARN_ON(fname->disk_name.len > F2FS_NAME_LEN)) 12843c780baSEric Biggers return -ENAMETOOLONG; 12943c780baSEric Biggers 13043c780baSEric Biggers if (!IS_ENCRYPTED(dir)) { 13143c780baSEric Biggers usr_fname->name = fname->disk_name.name; 13243c780baSEric Biggers usr_fname->len = fname->disk_name.len; 13343c780baSEric Biggers fname->usr_fname = usr_fname; 13443c780baSEric Biggers } 13543c780baSEric Biggers 13643c780baSEric Biggers /* Compute the hash of the filename */ 1377ad08a58SDaniel Rosenberg if (IS_ENCRYPTED(dir) && IS_CASEFOLDED(dir)) { 1387ad08a58SDaniel Rosenberg /* 1397ad08a58SDaniel Rosenberg * In this case the hash isn't computable without the key, so it 1407ad08a58SDaniel Rosenberg * was saved on-disk. 1417ad08a58SDaniel Rosenberg */ 1427ad08a58SDaniel Rosenberg if (fname->disk_name.len + sizeof(f2fs_hash_t) > F2FS_NAME_LEN) 1437ad08a58SDaniel Rosenberg return -EINVAL; 1447ad08a58SDaniel Rosenberg fname->hash = get_unaligned((f2fs_hash_t *) 1457ad08a58SDaniel Rosenberg &raw_inode->i_name[fname->disk_name.len]); 1467ad08a58SDaniel Rosenberg } else if (IS_CASEFOLDED(dir)) { 14743c780baSEric Biggers err = f2fs_init_casefolded_name(dir, fname); 14843c780baSEric Biggers if (err) 14943c780baSEric Biggers return err; 15043c780baSEric Biggers f2fs_hash_filename(dir, fname); 15143c780baSEric Biggers #ifdef CONFIG_UNICODE 15243c780baSEric Biggers /* Case-sensitive match is fine for recovery */ 1534d9a2bb1SChao Yu kmem_cache_free(f2fs_cf_name_slab, fname->cf_name.name); 15443c780baSEric Biggers fname->cf_name.name = NULL; 15543c780baSEric Biggers #endif 15643c780baSEric Biggers } else { 15743c780baSEric Biggers f2fs_hash_filename(dir, fname); 15843c780baSEric Biggers } 15943c780baSEric Biggers return 0; 16043c780baSEric Biggers } 16143c780baSEric Biggers 162f61cce5bSChao Yu static int recover_dentry(struct inode *inode, struct page *ipage, 163f61cce5bSChao Yu struct list_head *dir_list) 164d624c96fSJaegeuk Kim { 16558bfaf44SJaegeuk Kim struct f2fs_inode *raw_inode = F2FS_INODE(ipage); 16674d0b917SJaegeuk Kim nid_t pino = le32_to_cpu(raw_inode->i_pino); 1676b8213d9SJaegeuk Kim struct f2fs_dir_entry *de; 16843c780baSEric Biggers struct f2fs_filename fname; 16943c780baSEric Biggers struct qstr usr_fname; 170d624c96fSJaegeuk Kim struct page *page; 1716b8213d9SJaegeuk Kim struct inode *dir, *einode; 172f61cce5bSChao Yu struct fsync_inode_entry *entry; 173d624c96fSJaegeuk Kim int err = 0; 174e7ba108aSShuoran Liu char *name; 175d624c96fSJaegeuk Kim 176f61cce5bSChao Yu entry = get_fsync_inode(dir_list, pino); 177f61cce5bSChao Yu if (!entry) { 1784b2414d0SChao Yu entry = add_fsync_inode(F2FS_I_SB(inode), dir_list, 1794b2414d0SChao Yu pino, false); 180f4702d61SJaegeuk Kim if (IS_ERR(entry)) { 181f4702d61SJaegeuk Kim dir = ERR_CAST(entry); 182f4702d61SJaegeuk Kim err = PTR_ERR(entry); 183f61cce5bSChao Yu goto out; 184e7d55452SJaegeuk Kim } 185f61cce5bSChao Yu } 186f61cce5bSChao Yu 187f61cce5bSChao Yu dir = entry->inode; 18843c780baSEric Biggers err = init_recovered_filename(dir, raw_inode, &fname, &usr_fname); 18943c780baSEric Biggers if (err) 190f61cce5bSChao Yu goto out; 1916b8213d9SJaegeuk Kim retry: 192e7ba108aSShuoran Liu de = __f2fs_find_entry(dir, &fname, &page); 193418f6c27SJaegeuk Kim if (de && inode->i_ino == le32_to_cpu(de->ino)) 194bdbc90faSYunlong Song goto out_put; 195418f6c27SJaegeuk Kim 1966b8213d9SJaegeuk Kim if (de) { 197e8ea9b3dSJaegeuk Kim einode = f2fs_iget_retry(inode->i_sb, le32_to_cpu(de->ino)); 1986b8213d9SJaegeuk Kim if (IS_ERR(einode)) { 1996b8213d9SJaegeuk Kim WARN_ON(1); 2005c1f9927SChao Yu err = PTR_ERR(einode); 2015c1f9927SChao Yu if (err == -ENOENT) 2026b8213d9SJaegeuk Kim err = -EEXIST; 203bdbc90faSYunlong Song goto out_put; 2042e5558f4SRuss W. Knize } 2054b2414d0SChao Yu 2064b2414d0SChao Yu err = dquot_initialize(einode); 2074b2414d0SChao Yu if (err) { 2084b2414d0SChao Yu iput(einode); 209bdbc90faSYunlong Song goto out_put; 2104b2414d0SChao Yu } 2114b2414d0SChao Yu 2124d57b86dSChao Yu err = f2fs_acquire_orphan_inode(F2FS_I_SB(inode)); 2132e5558f4SRuss W. Knize if (err) { 2142e5558f4SRuss W. Knize iput(einode); 215bdbc90faSYunlong Song goto out_put; 2166b8213d9SJaegeuk Kim } 217dbeacf02SChao Yu f2fs_delete_entry(de, page, dir, einode); 2186b8213d9SJaegeuk Kim iput(einode); 2196b8213d9SJaegeuk Kim goto retry; 22091246c21SChao Yu } else if (IS_ERR(page)) { 22191246c21SChao Yu err = PTR_ERR(page); 22291246c21SChao Yu } else { 2234d57b86dSChao Yu err = f2fs_add_dentry(dir, &fname, inode, 22491246c21SChao Yu inode->i_ino, inode->i_mode); 2256b8213d9SJaegeuk Kim } 226e8ea9b3dSJaegeuk Kim if (err == -ENOMEM) 227e8ea9b3dSJaegeuk Kim goto retry; 2282e5558f4SRuss W. Knize goto out; 2292e5558f4SRuss W. Knize 230bdbc90faSYunlong Song out_put: 2312e5558f4SRuss W. Knize f2fs_put_page(page, 0); 232d624c96fSJaegeuk Kim out: 233e7ba108aSShuoran Liu if (file_enc_name(inode)) 234e7ba108aSShuoran Liu name = "<encrypted>"; 235e7ba108aSShuoran Liu else 236e7ba108aSShuoran Liu name = raw_inode->i_name; 237dcbb4c10SJoe Perches f2fs_notice(F2FS_I_SB(inode), "%s: ino = %x, name = %s, dir = %lx, err = %d", 238e7ba108aSShuoran Liu __func__, ino_of_node(ipage), name, 239f28c06faSDan Carpenter IS_ERR(dir) ? 0 : dir->i_ino, err); 240d624c96fSJaegeuk Kim return err; 241d624c96fSJaegeuk Kim } 242d624c96fSJaegeuk Kim 243af033b2aSChao Yu static int recover_quota_data(struct inode *inode, struct page *page) 244af033b2aSChao Yu { 245af033b2aSChao Yu struct f2fs_inode *raw = F2FS_INODE(page); 246af033b2aSChao Yu struct iattr attr; 247af033b2aSChao Yu uid_t i_uid = le32_to_cpu(raw->i_uid); 248af033b2aSChao Yu gid_t i_gid = le32_to_cpu(raw->i_gid); 249af033b2aSChao Yu int err; 250af033b2aSChao Yu 251af033b2aSChao Yu memset(&attr, 0, sizeof(attr)); 252af033b2aSChao Yu 253af033b2aSChao Yu attr.ia_uid = make_kuid(inode->i_sb->s_user_ns, i_uid); 254af033b2aSChao Yu attr.ia_gid = make_kgid(inode->i_sb->s_user_ns, i_gid); 255af033b2aSChao Yu 256af033b2aSChao Yu if (!uid_eq(attr.ia_uid, inode->i_uid)) 257af033b2aSChao Yu attr.ia_valid |= ATTR_UID; 258af033b2aSChao Yu if (!gid_eq(attr.ia_gid, inode->i_gid)) 259af033b2aSChao Yu attr.ia_valid |= ATTR_GID; 260af033b2aSChao Yu 261af033b2aSChao Yu if (!attr.ia_valid) 262af033b2aSChao Yu return 0; 263af033b2aSChao Yu 264af033b2aSChao Yu err = dquot_transfer(inode, &attr); 265af033b2aSChao Yu if (err) 266af033b2aSChao Yu set_sbi_flag(F2FS_I_SB(inode), SBI_QUOTA_NEED_REPAIR); 267af033b2aSChao Yu return err; 268af033b2aSChao Yu } 269af033b2aSChao Yu 27037a086f0SJaegeuk Kim static void recover_inline_flags(struct inode *inode, struct f2fs_inode *ri) 27137a086f0SJaegeuk Kim { 27237a086f0SJaegeuk Kim if (ri->i_inline & F2FS_PIN_FILE) 27337a086f0SJaegeuk Kim set_inode_flag(inode, FI_PIN_FILE); 27437a086f0SJaegeuk Kim else 27537a086f0SJaegeuk Kim clear_inode_flag(inode, FI_PIN_FILE); 27637a086f0SJaegeuk Kim if (ri->i_inline & F2FS_DATA_EXIST) 27737a086f0SJaegeuk Kim set_inode_flag(inode, FI_DATA_EXIST); 27837a086f0SJaegeuk Kim else 27937a086f0SJaegeuk Kim clear_inode_flag(inode, FI_DATA_EXIST); 28037a086f0SJaegeuk Kim } 28137a086f0SJaegeuk Kim 282af033b2aSChao Yu static int recover_inode(struct inode *inode, struct page *page) 283441ac5cbSJaegeuk Kim { 284441ac5cbSJaegeuk Kim struct f2fs_inode *raw = F2FS_INODE(page); 285e7d55452SJaegeuk Kim char *name; 286af033b2aSChao Yu int err; 287441ac5cbSJaegeuk Kim 288441ac5cbSJaegeuk Kim inode->i_mode = le16_to_cpu(raw->i_mode); 289af033b2aSChao Yu 290af033b2aSChao Yu err = recover_quota_data(inode, page); 291af033b2aSChao Yu if (err) 292af033b2aSChao Yu return err; 293af033b2aSChao Yu 294dc4cd125SChao Yu i_uid_write(inode, le32_to_cpu(raw->i_uid)); 295dc4cd125SChao Yu i_gid_write(inode, le32_to_cpu(raw->i_gid)); 296f4474aa6SChao Yu 297f4474aa6SChao Yu if (raw->i_inline & F2FS_EXTRA_ATTR) { 2987beb01f7SChao Yu if (f2fs_sb_has_project_quota(F2FS_I_SB(inode)) && 299f4474aa6SChao Yu F2FS_FITS_IN_INODE(raw, le16_to_cpu(raw->i_extra_isize), 300f4474aa6SChao Yu i_projid)) { 301f4474aa6SChao Yu projid_t i_projid; 30278130819SChao Yu kprojid_t kprojid; 303f4474aa6SChao Yu 304f4474aa6SChao Yu i_projid = (projid_t)le32_to_cpu(raw->i_projid); 30578130819SChao Yu kprojid = make_kprojid(&init_user_ns, i_projid); 30678130819SChao Yu 30778130819SChao Yu if (!projid_eq(kprojid, F2FS_I(inode)->i_projid)) { 30878130819SChao Yu err = f2fs_transfer_project_quota(inode, 30978130819SChao Yu kprojid); 31078130819SChao Yu if (err) 31178130819SChao Yu return err; 31278130819SChao Yu F2FS_I(inode)->i_projid = kprojid; 31378130819SChao Yu } 314f4474aa6SChao Yu } 315f4474aa6SChao Yu } 316f4474aa6SChao Yu 317fc9581c8SJaegeuk Kim f2fs_i_size_write(inode, le64_to_cpu(raw->i_size)); 3189f0552e0SChao Yu inode->i_atime.tv_sec = le64_to_cpu(raw->i_atime); 319441ac5cbSJaegeuk Kim inode->i_ctime.tv_sec = le64_to_cpu(raw->i_ctime); 320441ac5cbSJaegeuk Kim inode->i_mtime.tv_sec = le64_to_cpu(raw->i_mtime); 3219f0552e0SChao Yu inode->i_atime.tv_nsec = le32_to_cpu(raw->i_atime_nsec); 322441ac5cbSJaegeuk Kim inode->i_ctime.tv_nsec = le32_to_cpu(raw->i_ctime_nsec); 323441ac5cbSJaegeuk Kim inode->i_mtime.tv_nsec = le32_to_cpu(raw->i_mtime_nsec); 324f356fe0cSJaegeuk Kim 32526787236SJaegeuk Kim F2FS_I(inode)->i_advise = raw->i_advise; 32619c73a69SChao Yu F2FS_I(inode)->i_flags = le32_to_cpu(raw->i_flags); 3270c093b59SChao Yu f2fs_set_inode_flags(inode); 3287de36cf3SChao Yu F2FS_I(inode)->i_gc_failures[GC_FAILURE_PIN] = 3297de36cf3SChao Yu le16_to_cpu(raw->i_gc_failures); 33026787236SJaegeuk Kim 33137a086f0SJaegeuk Kim recover_inline_flags(inode, raw); 33237a086f0SJaegeuk Kim 3334a1728caSChao Yu f2fs_mark_inode_dirty_sync(inode, true); 3344a1728caSChao Yu 335e7d55452SJaegeuk Kim if (file_enc_name(inode)) 336e7d55452SJaegeuk Kim name = "<encrypted>"; 337e7d55452SJaegeuk Kim else 338e7d55452SJaegeuk Kim name = F2FS_INODE(page)->i_name; 339e7d55452SJaegeuk Kim 340dcbb4c10SJoe Perches f2fs_notice(F2FS_I_SB(inode), "recover_inode: ino = %x, name = %s, inline = %x", 34137a086f0SJaegeuk Kim ino_of_node(page), name, raw->i_inline); 342af033b2aSChao Yu return 0; 343d624c96fSJaegeuk Kim } 344d624c96fSJaegeuk Kim 345d40d30c5SJaegeuk Kim static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head, 346d40d30c5SJaegeuk Kim bool check_only) 347d624c96fSJaegeuk Kim { 348d624c96fSJaegeuk Kim struct curseg_info *curseg; 3494c521f49SJaegeuk Kim struct page *page = NULL; 350d624c96fSJaegeuk Kim block_t blkaddr; 351fb0e72c8SChao Yu unsigned int loop_cnt = 0; 35282902c06SChao Yu unsigned int free_blocks = MAIN_SEGS(sbi) * sbi->blocks_per_seg - 353fb0e72c8SChao Yu valid_user_blocks(sbi); 354d624c96fSJaegeuk Kim int err = 0; 355d624c96fSJaegeuk Kim 356d624c96fSJaegeuk Kim /* get node pages in the current segment */ 357d624c96fSJaegeuk Kim curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); 358695fd1edSChao Yu blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); 359d624c96fSJaegeuk Kim 360d624c96fSJaegeuk Kim while (1) { 361d624c96fSJaegeuk Kim struct fsync_inode_entry *entry; 362d624c96fSJaegeuk Kim 363e1da7872SChao Yu if (!f2fs_is_valid_blkaddr(sbi, blkaddr, META_POR)) 3644c521f49SJaegeuk Kim return 0; 365d624c96fSJaegeuk Kim 3664d57b86dSChao Yu page = f2fs_get_tmp_page(sbi, blkaddr); 3677735730dSChao Yu if (IS_ERR(page)) { 3687735730dSChao Yu err = PTR_ERR(page); 3697735730dSChao Yu break; 3707735730dSChao Yu } 371393ff91fSJaegeuk Kim 37298838579SChao Yu if (!is_recoverable_dnode(page)) { 37398838579SChao Yu f2fs_put_page(page, 1); 374f356fe0cSJaegeuk Kim break; 37598838579SChao Yu } 376d624c96fSJaegeuk Kim 377d624c96fSJaegeuk Kim if (!is_fsync_dnode(page)) 378d624c96fSJaegeuk Kim goto next; 379d624c96fSJaegeuk Kim 380d624c96fSJaegeuk Kim entry = get_fsync_inode(head, ino_of_node(page)); 381d47b8715SChao Yu if (!entry) { 3824b2414d0SChao Yu bool quota_inode = false; 3834b2414d0SChao Yu 384d40d30c5SJaegeuk Kim if (!check_only && 385d40d30c5SJaegeuk Kim IS_INODE(page) && is_dent_dnode(page)) { 3864d57b86dSChao Yu err = f2fs_recover_inode_page(sbi, page); 38798838579SChao Yu if (err) { 38898838579SChao Yu f2fs_put_page(page, 1); 389f356fe0cSJaegeuk Kim break; 39098838579SChao Yu } 3914b2414d0SChao Yu quota_inode = true; 392d624c96fSJaegeuk Kim } 393d624c96fSJaegeuk Kim 394441ac5cbSJaegeuk Kim /* 395441ac5cbSJaegeuk Kim * CP | dnode(F) | inode(DF) 396441ac5cbSJaegeuk Kim * For this case, we should not give up now. 397441ac5cbSJaegeuk Kim */ 3984b2414d0SChao Yu entry = add_fsync_inode(sbi, head, ino_of_node(page), 3994b2414d0SChao Yu quota_inode); 400f4702d61SJaegeuk Kim if (IS_ERR(entry)) { 401f4702d61SJaegeuk Kim err = PTR_ERR(entry); 4028fbc418fSJaegeuk Kim if (err == -ENOENT) { 4038fbc418fSJaegeuk Kim err = 0; 404441ac5cbSJaegeuk Kim goto next; 4058fbc418fSJaegeuk Kim } 40698838579SChao Yu f2fs_put_page(page, 1); 407f356fe0cSJaegeuk Kim break; 408d624c96fSJaegeuk Kim } 409d624c96fSJaegeuk Kim } 410addbe45bSJaegeuk Kim entry->blkaddr = blkaddr; 411addbe45bSJaegeuk Kim 412608514deSJaegeuk Kim if (IS_INODE(page) && is_dent_dnode(page)) 413c52e1b10SJaegeuk Kim entry->last_dentry = blkaddr; 414d624c96fSJaegeuk Kim next: 415fb0e72c8SChao Yu /* sanity check in order to detect looped node chain */ 416fb0e72c8SChao Yu if (++loop_cnt >= free_blocks || 417fb0e72c8SChao Yu blkaddr == next_blkaddr_of_node(page)) { 418dcbb4c10SJoe Perches f2fs_notice(sbi, "%s: detect looped node chain, blkaddr:%u, next:%u", 419dcbb4c10SJoe Perches __func__, blkaddr, 420dcbb4c10SJoe Perches next_blkaddr_of_node(page)); 42198838579SChao Yu f2fs_put_page(page, 1); 422fb0e72c8SChao Yu err = -EINVAL; 423fb0e72c8SChao Yu break; 424fb0e72c8SChao Yu } 425fb0e72c8SChao Yu 426d624c96fSJaegeuk Kim /* check next segment */ 427d624c96fSJaegeuk Kim blkaddr = next_blkaddr_of_node(page); 4284c521f49SJaegeuk Kim f2fs_put_page(page, 1); 429635aee1fSChao Yu 4304d57b86dSChao Yu f2fs_ra_meta_pages_cond(sbi, blkaddr); 431d624c96fSJaegeuk Kim } 432d624c96fSJaegeuk Kim return err; 433d624c96fSJaegeuk Kim } 434d624c96fSJaegeuk Kim 43526b5a079SSheng Yong static void destroy_fsync_dnodes(struct list_head *head, int drop) 436d624c96fSJaegeuk Kim { 437d8b79b2fSDan Carpenter struct fsync_inode_entry *entry, *tmp; 438d8b79b2fSDan Carpenter 4393f8ab270SChao Yu list_for_each_entry_safe(entry, tmp, head, list) 44026b5a079SSheng Yong del_fsync_inode(entry, drop); 441d624c96fSJaegeuk Kim } 442d624c96fSJaegeuk Kim 44339cf72cfSJaegeuk Kim static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi, 444b292dcabSJaegeuk Kim block_t blkaddr, struct dnode_of_data *dn) 445d624c96fSJaegeuk Kim { 446d624c96fSJaegeuk Kim struct seg_entry *sentry; 447d624c96fSJaegeuk Kim unsigned int segno = GET_SEGNO(sbi, blkaddr); 448491c0854SJaegeuk Kim unsigned short blkoff = GET_BLKOFF_FROM_SEG0(sbi, blkaddr); 449f6517cfcSJaegeuk Kim struct f2fs_summary_block *sum_node; 450d624c96fSJaegeuk Kim struct f2fs_summary sum; 451f6517cfcSJaegeuk Kim struct page *sum_page, *node_page; 452c9ef4810SJaegeuk Kim struct dnode_of_data tdn = *dn; 453b292dcabSJaegeuk Kim nid_t ino, nid; 454d624c96fSJaegeuk Kim struct inode *inode; 455de93653fSJaegeuk Kim unsigned int offset; 456d624c96fSJaegeuk Kim block_t bidx; 457d624c96fSJaegeuk Kim int i; 458d624c96fSJaegeuk Kim 459d624c96fSJaegeuk Kim sentry = get_seg_entry(sbi, segno); 460d624c96fSJaegeuk Kim if (!f2fs_test_bit(blkoff, sentry->cur_valid_map)) 46139cf72cfSJaegeuk Kim return 0; 462d624c96fSJaegeuk Kim 463d624c96fSJaegeuk Kim /* Get the previous summary */ 464125c9fb1SJaegeuk Kim for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { 465d624c96fSJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, i); 4665f029c04SYi Zhuang 467d624c96fSJaegeuk Kim if (curseg->segno == segno) { 468d624c96fSJaegeuk Kim sum = curseg->sum_blk->entries[blkoff]; 469f6517cfcSJaegeuk Kim goto got_it; 470d624c96fSJaegeuk Kim } 471d624c96fSJaegeuk Kim } 472d624c96fSJaegeuk Kim 4734d57b86dSChao Yu sum_page = f2fs_get_sum_page(sbi, segno); 474edc55aafSJaegeuk Kim if (IS_ERR(sum_page)) 475edc55aafSJaegeuk Kim return PTR_ERR(sum_page); 476f6517cfcSJaegeuk Kim sum_node = (struct f2fs_summary_block *)page_address(sum_page); 477f6517cfcSJaegeuk Kim sum = sum_node->entries[blkoff]; 478f6517cfcSJaegeuk Kim f2fs_put_page(sum_page, 1); 479f6517cfcSJaegeuk Kim got_it: 480b292dcabSJaegeuk Kim /* Use the locked dnode page and inode */ 481b292dcabSJaegeuk Kim nid = le32_to_cpu(sum.nid); 482b292dcabSJaegeuk Kim if (dn->inode->i_ino == nid) { 483b292dcabSJaegeuk Kim tdn.nid = nid; 484c9ef4810SJaegeuk Kim if (!dn->inode_page_locked) 485c9ef4810SJaegeuk Kim lock_page(dn->inode_page); 486b292dcabSJaegeuk Kim tdn.node_page = dn->inode_page; 487060dd67bSJaegeuk Kim tdn.ofs_in_node = le16_to_cpu(sum.ofs_in_node); 488c9ef4810SJaegeuk Kim goto truncate_out; 489b292dcabSJaegeuk Kim } else if (dn->nid == nid) { 490060dd67bSJaegeuk Kim tdn.ofs_in_node = le16_to_cpu(sum.ofs_in_node); 491c9ef4810SJaegeuk Kim goto truncate_out; 492b292dcabSJaegeuk Kim } 493b292dcabSJaegeuk Kim 494d624c96fSJaegeuk Kim /* Get the node page */ 4954d57b86dSChao Yu node_page = f2fs_get_node_page(sbi, nid); 49639cf72cfSJaegeuk Kim if (IS_ERR(node_page)) 49739cf72cfSJaegeuk Kim return PTR_ERR(node_page); 498de93653fSJaegeuk Kim 499de93653fSJaegeuk Kim offset = ofs_of_node(node_page); 500d624c96fSJaegeuk Kim ino = ino_of_node(node_page); 501d624c96fSJaegeuk Kim f2fs_put_page(node_page, 1); 502d624c96fSJaegeuk Kim 50360979115SJaegeuk Kim if (ino != dn->inode->i_ino) { 5044b2414d0SChao Yu int ret; 5054b2414d0SChao Yu 506d624c96fSJaegeuk Kim /* Deallocate previous index in the node page */ 507e8ea9b3dSJaegeuk Kim inode = f2fs_iget_retry(sbi->sb, ino); 50806025f4dSNamjae Jeon if (IS_ERR(inode)) 50939cf72cfSJaegeuk Kim return PTR_ERR(inode); 5104b2414d0SChao Yu 5114b2414d0SChao Yu ret = dquot_initialize(inode); 5124b2414d0SChao Yu if (ret) { 5134b2414d0SChao Yu iput(inode); 5144b2414d0SChao Yu return ret; 5154b2414d0SChao Yu } 51660979115SJaegeuk Kim } else { 51760979115SJaegeuk Kim inode = dn->inode; 51860979115SJaegeuk Kim } 51906025f4dSNamjae Jeon 5204d57b86dSChao Yu bidx = f2fs_start_bidx_of_node(offset, inode) + 5214d57b86dSChao Yu le16_to_cpu(sum.ofs_in_node); 522de93653fSJaegeuk Kim 523c9ef4810SJaegeuk Kim /* 524c9ef4810SJaegeuk Kim * if inode page is locked, unlock temporarily, but its reference 525c9ef4810SJaegeuk Kim * count keeps alive. 526c9ef4810SJaegeuk Kim */ 527c9ef4810SJaegeuk Kim if (ino == dn->inode->i_ino && dn->inode_page_locked) 528c9ef4810SJaegeuk Kim unlock_page(dn->inode_page); 529c9ef4810SJaegeuk Kim 530c9ef4810SJaegeuk Kim set_new_dnode(&tdn, inode, NULL, NULL, 0); 5314d57b86dSChao Yu if (f2fs_get_dnode_of_data(&tdn, bidx, LOOKUP_NODE)) 532c9ef4810SJaegeuk Kim goto out; 533c9ef4810SJaegeuk Kim 534c9ef4810SJaegeuk Kim if (tdn.data_blkaddr == blkaddr) 5354d57b86dSChao Yu f2fs_truncate_data_blocks_range(&tdn, 1); 536c9ef4810SJaegeuk Kim 537c9ef4810SJaegeuk Kim f2fs_put_dnode(&tdn); 538c9ef4810SJaegeuk Kim out: 539c9ef4810SJaegeuk Kim if (ino != dn->inode->i_ino) 540c9ef4810SJaegeuk Kim iput(inode); 541c9ef4810SJaegeuk Kim else if (dn->inode_page_locked) 542c9ef4810SJaegeuk Kim lock_page(dn->inode_page); 543c9ef4810SJaegeuk Kim return 0; 544c9ef4810SJaegeuk Kim 545c9ef4810SJaegeuk Kim truncate_out: 546a2ced1ceSChao Yu if (f2fs_data_blkaddr(&tdn) == blkaddr) 5474d57b86dSChao Yu f2fs_truncate_data_blocks_range(&tdn, 1); 548c9ef4810SJaegeuk Kim if (dn->inode->i_ino == nid && !dn->inode_page_locked) 549c9ef4810SJaegeuk Kim unlock_page(dn->inode_page); 55039cf72cfSJaegeuk Kim return 0; 551d624c96fSJaegeuk Kim } 552d624c96fSJaegeuk Kim 5536ead1142SJaegeuk Kim static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, 554e17d488bSSheng Yong struct page *page) 555d624c96fSJaegeuk Kim { 556d624c96fSJaegeuk Kim struct dnode_of_data dn; 557d624c96fSJaegeuk Kim struct node_info ni; 55881ca7350SChao Yu unsigned int start, end; 559f356fe0cSJaegeuk Kim int err = 0, recovered = 0; 560d624c96fSJaegeuk Kim 5611c35a90eSJaegeuk Kim /* step 1: recover xattr */ 5621c35a90eSJaegeuk Kim if (IS_INODE(page)) { 5639627a7b3SChao Yu err = f2fs_recover_inline_xattr(inode, page); 5649627a7b3SChao Yu if (err) 5659627a7b3SChao Yu goto out; 5661c35a90eSJaegeuk Kim } else if (f2fs_has_xattr_block(ofs_of_node(page))) { 5674d57b86dSChao Yu err = f2fs_recover_xattr_data(inode, page); 568d260081cSChao Yu if (!err) 569d260081cSChao Yu recovered++; 5701c35a90eSJaegeuk Kim goto out; 5711c35a90eSJaegeuk Kim } 57270cfed88SChao Yu 5731c35a90eSJaegeuk Kim /* step 2: recover inline data */ 5749627a7b3SChao Yu err = f2fs_recover_inline_data(inode, page); 5759627a7b3SChao Yu if (err) { 5769627a7b3SChao Yu if (err == 1) 5779627a7b3SChao Yu err = 0; 5781e1bb4baSJaegeuk Kim goto out; 5799627a7b3SChao Yu } 5801e1bb4baSJaegeuk Kim 5811c35a90eSJaegeuk Kim /* step 3: recover data indices */ 5824d57b86dSChao Yu start = f2fs_start_bidx_of_node(ofs_of_node(page), inode); 58381ca7350SChao Yu end = start + ADDRS_PER_PAGE(page, inode); 584d624c96fSJaegeuk Kim 585d624c96fSJaegeuk Kim set_new_dnode(&dn, inode, NULL, NULL, 0); 586e8ea9b3dSJaegeuk Kim retry_dn: 5874d57b86dSChao Yu err = f2fs_get_dnode_of_data(&dn, start, ALLOC_NODE); 588e8ea9b3dSJaegeuk Kim if (err) { 589e8ea9b3dSJaegeuk Kim if (err == -ENOMEM) { 5905df7731fSChao Yu congestion_wait(BLK_RW_ASYNC, DEFAULT_IO_TIMEOUT); 591e8ea9b3dSJaegeuk Kim goto retry_dn; 592e8ea9b3dSJaegeuk Kim } 5931e1bb4baSJaegeuk Kim goto out; 594e8ea9b3dSJaegeuk Kim } 595d624c96fSJaegeuk Kim 596bae0ee7aSChao Yu f2fs_wait_on_page_writeback(dn.node_page, NODE, true, true); 597d624c96fSJaegeuk Kim 5987735730dSChao Yu err = f2fs_get_node_info(sbi, dn.nid, &ni); 5997735730dSChao Yu if (err) 6007735730dSChao Yu goto err; 6017735730dSChao Yu 6029850cf4aSJaegeuk Kim f2fs_bug_on(sbi, ni.ino != ino_of_node(page)); 60322d61e28SChao Yu 60422d61e28SChao Yu if (ofs_of_node(dn.node_page) != ofs_of_node(page)) { 605dcbb4c10SJoe Perches f2fs_warn(sbi, "Inconsistent ofs_of_node, ino:%lu, ofs:%u, %u", 60622d61e28SChao Yu inode->i_ino, ofs_of_node(dn.node_page), 60722d61e28SChao Yu ofs_of_node(page)); 60810f966bbSChao Yu err = -EFSCORRUPTED; 60922d61e28SChao Yu goto err; 61022d61e28SChao Yu } 611d624c96fSJaegeuk Kim 61212a8343eSChao Yu for (; start < end; start++, dn.ofs_in_node++) { 613d624c96fSJaegeuk Kim block_t src, dest; 614d624c96fSJaegeuk Kim 615a2ced1ceSChao Yu src = f2fs_data_blkaddr(&dn); 616a2ced1ceSChao Yu dest = data_blkaddr(dn.inode, page, dn.ofs_in_node); 617d624c96fSJaegeuk Kim 61893770ab7SChao Yu if (__is_valid_data_blkaddr(src) && 61993770ab7SChao Yu !f2fs_is_valid_blkaddr(sbi, src, META_POR)) { 62010f966bbSChao Yu err = -EFSCORRUPTED; 62193770ab7SChao Yu goto err; 62293770ab7SChao Yu } 62393770ab7SChao Yu 62493770ab7SChao Yu if (__is_valid_data_blkaddr(dest) && 62593770ab7SChao Yu !f2fs_is_valid_blkaddr(sbi, dest, META_POR)) { 62610f966bbSChao Yu err = -EFSCORRUPTED; 62793770ab7SChao Yu goto err; 62893770ab7SChao Yu } 62993770ab7SChao Yu 63012a8343eSChao Yu /* skip recovering if dest is the same as src */ 63112a8343eSChao Yu if (src == dest) 63212a8343eSChao Yu continue; 63312a8343eSChao Yu 63412a8343eSChao Yu /* dest is invalid, just invalidate src block */ 63512a8343eSChao Yu if (dest == NULL_ADDR) { 6364d57b86dSChao Yu f2fs_truncate_data_blocks_range(&dn, 1); 63712a8343eSChao Yu continue; 63812a8343eSChao Yu } 63912a8343eSChao Yu 64026787236SJaegeuk Kim if (!file_keep_isize(inode) && 641dba79f38SChao Yu (i_size_read(inode) <= ((loff_t)start << PAGE_SHIFT))) 642dba79f38SChao Yu f2fs_i_size_write(inode, 643dba79f38SChao Yu (loff_t)(start + 1) << PAGE_SHIFT); 64426de9b11SJaegeuk Kim 64512a8343eSChao Yu /* 64612a8343eSChao Yu * dest is reserved block, invalidate src block 64712a8343eSChao Yu * and then reserve one new block in dnode page. 64812a8343eSChao Yu */ 64912a8343eSChao Yu if (dest == NEW_ADDR) { 6504d57b86dSChao Yu f2fs_truncate_data_blocks_range(&dn, 1); 6514d57b86dSChao Yu f2fs_reserve_new_block(&dn); 65212a8343eSChao Yu continue; 65312a8343eSChao Yu } 65412a8343eSChao Yu 65512a8343eSChao Yu /* dest is valid block, try to recover from src to dest */ 656e1da7872SChao Yu if (f2fs_is_valid_blkaddr(sbi, dest, META_POR)) { 657e03b07d9SJaegeuk Kim 658d624c96fSJaegeuk Kim if (src == NULL_ADDR) { 6594d57b86dSChao Yu err = f2fs_reserve_new_block(&dn); 6607fa750a1SArnd Bergmann while (err && 6617fa750a1SArnd Bergmann IS_ENABLED(CONFIG_F2FS_FAULT_INJECTION)) 6624d57b86dSChao Yu err = f2fs_reserve_new_block(&dn); 663d624c96fSJaegeuk Kim /* We should not get -ENOSPC */ 6649850cf4aSJaegeuk Kim f2fs_bug_on(sbi, err); 6656f3ec995SJaegeuk Kim if (err) 6666f3ec995SJaegeuk Kim goto err; 667d624c96fSJaegeuk Kim } 668e8ea9b3dSJaegeuk Kim retry_prev: 669d624c96fSJaegeuk Kim /* Check the previous node page having this index */ 67039cf72cfSJaegeuk Kim err = check_index_in_prev_nodes(sbi, dest, &dn); 671e8ea9b3dSJaegeuk Kim if (err) { 672e8ea9b3dSJaegeuk Kim if (err == -ENOMEM) { 6735df7731fSChao Yu congestion_wait(BLK_RW_ASYNC, 6745df7731fSChao Yu DEFAULT_IO_TIMEOUT); 675e8ea9b3dSJaegeuk Kim goto retry_prev; 676e8ea9b3dSJaegeuk Kim } 67739cf72cfSJaegeuk Kim goto err; 678e8ea9b3dSJaegeuk Kim } 679d624c96fSJaegeuk Kim 680d624c96fSJaegeuk Kim /* write dummy data page */ 681528e3459SChao Yu f2fs_replace_block(sbi, &dn, src, dest, 68228bc106bSChao Yu ni.version, false, false); 683f356fe0cSJaegeuk Kim recovered++; 684d624c96fSJaegeuk Kim } 685d624c96fSJaegeuk Kim } 686d624c96fSJaegeuk Kim 687d624c96fSJaegeuk Kim copy_node_footer(dn.node_page, page); 688d624c96fSJaegeuk Kim fill_node_footer(dn.node_page, dn.nid, ni.ino, 689d624c96fSJaegeuk Kim ofs_of_node(page), false); 690d624c96fSJaegeuk Kim set_page_dirty(dn.node_page); 69139cf72cfSJaegeuk Kim err: 692d624c96fSJaegeuk Kim f2fs_put_dnode(&dn); 6931e1bb4baSJaegeuk Kim out: 694dcbb4c10SJoe Perches f2fs_notice(sbi, "recover_data: ino = %lx (i_size: %s) recovered = %d, err = %d", 695dcbb4c10SJoe Perches inode->i_ino, file_keep_isize(inode) ? "keep" : "recover", 69626787236SJaegeuk Kim recovered, err); 69739cf72cfSJaegeuk Kim return err; 698d624c96fSJaegeuk Kim } 699d624c96fSJaegeuk Kim 700f61cce5bSChao Yu static int recover_data(struct f2fs_sb_info *sbi, struct list_head *inode_list, 70126b5a079SSheng Yong struct list_head *tmp_inode_list, struct list_head *dir_list) 702d624c96fSJaegeuk Kim { 703d624c96fSJaegeuk Kim struct curseg_info *curseg; 7044c521f49SJaegeuk Kim struct page *page = NULL; 7056ead1142SJaegeuk Kim int err = 0; 706d624c96fSJaegeuk Kim block_t blkaddr; 707d624c96fSJaegeuk Kim 708d624c96fSJaegeuk Kim /* get node pages in the current segment */ 709b7973f23SChao Yu curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); 710d624c96fSJaegeuk Kim blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); 711d624c96fSJaegeuk Kim 712d624c96fSJaegeuk Kim while (1) { 713d624c96fSJaegeuk Kim struct fsync_inode_entry *entry; 714d624c96fSJaegeuk Kim 715e1da7872SChao Yu if (!f2fs_is_valid_blkaddr(sbi, blkaddr, META_POR)) 71645856affSJaegeuk Kim break; 717d624c96fSJaegeuk Kim 7184d57b86dSChao Yu f2fs_ra_meta_pages_cond(sbi, blkaddr); 719635aee1fSChao Yu 7204d57b86dSChao Yu page = f2fs_get_tmp_page(sbi, blkaddr); 7217735730dSChao Yu if (IS_ERR(page)) { 7227735730dSChao Yu err = PTR_ERR(page); 7237735730dSChao Yu break; 7247735730dSChao Yu } 7254c521f49SJaegeuk Kim 726a468f0efSJaegeuk Kim if (!is_recoverable_dnode(page)) { 7274c521f49SJaegeuk Kim f2fs_put_page(page, 1); 7284c521f49SJaegeuk Kim break; 7294c521f49SJaegeuk Kim } 7304c521f49SJaegeuk Kim 731f61cce5bSChao Yu entry = get_fsync_inode(inode_list, ino_of_node(page)); 732d624c96fSJaegeuk Kim if (!entry) 733d624c96fSJaegeuk Kim goto next; 734441ac5cbSJaegeuk Kim /* 735441ac5cbSJaegeuk Kim * inode(x) | CP | inode(x) | dnode(F) 736441ac5cbSJaegeuk Kim * In this case, we can lose the latest inode(x). 737c52e1b10SJaegeuk Kim * So, call recover_inode for the inode update. 738441ac5cbSJaegeuk Kim */ 739af033b2aSChao Yu if (IS_INODE(page)) { 740af033b2aSChao Yu err = recover_inode(entry->inode, page); 74198838579SChao Yu if (err) { 74298838579SChao Yu f2fs_put_page(page, 1); 743af033b2aSChao Yu break; 744af033b2aSChao Yu } 74598838579SChao Yu } 746c52e1b10SJaegeuk Kim if (entry->last_dentry == blkaddr) { 747f61cce5bSChao Yu err = recover_dentry(entry->inode, page, dir_list); 748c52e1b10SJaegeuk Kim if (err) { 749c52e1b10SJaegeuk Kim f2fs_put_page(page, 1); 750c52e1b10SJaegeuk Kim break; 751c52e1b10SJaegeuk Kim } 752c52e1b10SJaegeuk Kim } 753e17d488bSSheng Yong err = do_recover_data(sbi, entry->inode, page); 7544c521f49SJaegeuk Kim if (err) { 7554c521f49SJaegeuk Kim f2fs_put_page(page, 1); 75645856affSJaegeuk Kim break; 7574c521f49SJaegeuk Kim } 758d624c96fSJaegeuk Kim 7593f8ab270SChao Yu if (entry->blkaddr == blkaddr) 76026b5a079SSheng Yong list_move_tail(&entry->list, tmp_inode_list); 761d624c96fSJaegeuk Kim next: 762d624c96fSJaegeuk Kim /* check next segment */ 763d624c96fSJaegeuk Kim blkaddr = next_blkaddr_of_node(page); 7644c521f49SJaegeuk Kim f2fs_put_page(page, 1); 765d624c96fSJaegeuk Kim } 7666ead1142SJaegeuk Kim if (!err) 767901d745fSChao Yu f2fs_allocate_new_segments(sbi); 7686ead1142SJaegeuk Kim return err; 769d624c96fSJaegeuk Kim } 770d624c96fSJaegeuk Kim 7714d57b86dSChao Yu int f2fs_recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) 772d624c96fSJaegeuk Kim { 77326b5a079SSheng Yong struct list_head inode_list, tmp_inode_list; 774f61cce5bSChao Yu struct list_head dir_list; 7756ead1142SJaegeuk Kim int err; 7766781eabbSJaegeuk Kim int ret = 0; 7774b2414d0SChao Yu unsigned long s_flags = sbi->sb->s_flags; 778aabe5136SHaicheng Li bool need_writecp = false; 779c426d991SShin'ichiro Kawasaki bool fix_curseg_write_pointer = false; 780ea676733SJaegeuk Kim #ifdef CONFIG_QUOTA 781ea676733SJaegeuk Kim int quota_enabled; 782ea676733SJaegeuk Kim #endif 783d624c96fSJaegeuk Kim 7841751e8a6SLinus Torvalds if (s_flags & SB_RDONLY) { 785dcbb4c10SJoe Perches f2fs_info(sbi, "recover fsync data on readonly fs"); 7861751e8a6SLinus Torvalds sbi->sb->s_flags &= ~SB_RDONLY; 7874b2414d0SChao Yu } 7884b2414d0SChao Yu 7894b2414d0SChao Yu #ifdef CONFIG_QUOTA 7904b2414d0SChao Yu /* Needed for iput() to work correctly and not trash data */ 7911751e8a6SLinus Torvalds sbi->sb->s_flags |= SB_ACTIVE; 7924b2414d0SChao Yu /* Turn on quotas so that they are updated correctly */ 7931751e8a6SLinus Torvalds quota_enabled = f2fs_enable_quota_files(sbi, s_flags & SB_RDONLY); 7944b2414d0SChao Yu #endif 7954b2414d0SChao Yu 796d624c96fSJaegeuk Kim INIT_LIST_HEAD(&inode_list); 79726b5a079SSheng Yong INIT_LIST_HEAD(&tmp_inode_list); 798f61cce5bSChao Yu INIT_LIST_HEAD(&dir_list); 799d624c96fSJaegeuk Kim 80014f4e690SJaegeuk Kim /* prevent checkpoint */ 8018769918bSSahitya Tummala down_write(&sbi->cp_global_sem); 80214f4e690SJaegeuk Kim 803315df839SJaegeuk Kim /* step #1: find fsynced inode numbers */ 804d40d30c5SJaegeuk Kim err = find_fsync_dnodes(sbi, &inode_list, check_only); 8056781eabbSJaegeuk Kim if (err || list_empty(&inode_list)) 8064b2414d0SChao Yu goto skip; 807d624c96fSJaegeuk Kim 8086781eabbSJaegeuk Kim if (check_only) { 8096781eabbSJaegeuk Kim ret = 1; 8104b2414d0SChao Yu goto skip; 8116781eabbSJaegeuk Kim } 812d624c96fSJaegeuk Kim 813aabe5136SHaicheng Li need_writecp = true; 814691c6fd2SChao Yu 815d624c96fSJaegeuk Kim /* step #2: recover data */ 81626b5a079SSheng Yong err = recover_data(sbi, &inode_list, &tmp_inode_list, &dir_list); 817b307384eSJaegeuk Kim if (!err) 8189850cf4aSJaegeuk Kim f2fs_bug_on(sbi, !list_empty(&inode_list)); 81926b5a079SSheng Yong else { 82026b5a079SSheng Yong /* restore s_flags to let iput() trash data */ 82126b5a079SSheng Yong sbi->sb->s_flags = s_flags; 82226b5a079SSheng Yong } 8234b2414d0SChao Yu skip: 824c426d991SShin'ichiro Kawasaki fix_curseg_write_pointer = !check_only || list_empty(&inode_list); 825c426d991SShin'ichiro Kawasaki 82626b5a079SSheng Yong destroy_fsync_dnodes(&inode_list, err); 82726b5a079SSheng Yong destroy_fsync_dnodes(&tmp_inode_list, err); 828cf2271e7SJaegeuk Kim 8294c521f49SJaegeuk Kim /* truncate meta pages to be used by the recovery */ 8304c521f49SJaegeuk Kim truncate_inode_pages_range(META_MAPPING(sbi), 83109cbfeafSKirill A. Shutemov (loff_t)MAIN_BLKADDR(sbi) << PAGE_SHIFT, -1); 8324c521f49SJaegeuk Kim 833cf2271e7SJaegeuk Kim if (err) { 834cf2271e7SJaegeuk Kim truncate_inode_pages_final(NODE_MAPPING(sbi)); 835cf2271e7SJaegeuk Kim truncate_inode_pages_final(META_MAPPING(sbi)); 83626b5a079SSheng Yong } 837c426d991SShin'ichiro Kawasaki 838c426d991SShin'ichiro Kawasaki /* 839c426d991SShin'ichiro Kawasaki * If fsync data succeeds or there is no fsync data to recover, 840c426d991SShin'ichiro Kawasaki * and the f2fs is not read only, check and fix zoned block devices' 841c426d991SShin'ichiro Kawasaki * write pointer consistency. 842c426d991SShin'ichiro Kawasaki */ 843c426d991SShin'ichiro Kawasaki if (!err && fix_curseg_write_pointer && !f2fs_readonly(sbi->sb) && 844c426d991SShin'ichiro Kawasaki f2fs_sb_has_blkzoned(sbi)) { 845c426d991SShin'ichiro Kawasaki err = f2fs_fix_curseg_write_pointer(sbi); 846c426d991SShin'ichiro Kawasaki ret = err; 847c426d991SShin'ichiro Kawasaki } 848c426d991SShin'ichiro Kawasaki 849c426d991SShin'ichiro Kawasaki if (!err) 850c426d991SShin'ichiro Kawasaki clear_sbi_flag(sbi, SBI_POR_DOING); 851c426d991SShin'ichiro Kawasaki 8528769918bSSahitya Tummala up_write(&sbi->cp_global_sem); 853a468f0efSJaegeuk Kim 8549e1e6df4SJaegeuk Kim /* let's drop all the directory inodes for clean checkpoint */ 85526b5a079SSheng Yong destroy_fsync_dnodes(&dir_list, err); 8569e1e6df4SJaegeuk Kim 8571378752bSChao Yu if (need_writecp) { 8581378752bSChao Yu set_sbi_flag(sbi, SBI_IS_RECOVERED); 8591378752bSChao Yu 8601378752bSChao Yu if (!err) { 86175ab4cb8SJaegeuk Kim struct cp_control cpc = { 86210027551SJaegeuk Kim .reason = CP_RECOVERY, 86375ab4cb8SJaegeuk Kim }; 8644d57b86dSChao Yu err = f2fs_write_checkpoint(sbi, &cpc); 865cf2271e7SJaegeuk Kim } 8661378752bSChao Yu } 867f61cce5bSChao Yu 8684b2414d0SChao Yu #ifdef CONFIG_QUOTA 8694b2414d0SChao Yu /* Turn quotas off */ 870ea676733SJaegeuk Kim if (quota_enabled) 8714b2414d0SChao Yu f2fs_quota_off_umount(sbi->sb); 8724b2414d0SChao Yu #endif 8731751e8a6SLinus Torvalds sbi->sb->s_flags = s_flags; /* Restore SB_RDONLY status */ 8744b2414d0SChao Yu 8756781eabbSJaegeuk Kim return ret ? ret : err; 876d624c96fSJaegeuk Kim } 877cad83c96SChao Yu 878cad83c96SChao Yu int __init f2fs_create_recovery_cache(void) 879cad83c96SChao Yu { 880cad83c96SChao Yu fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry", 881cad83c96SChao Yu sizeof(struct fsync_inode_entry)); 882cad83c96SChao Yu if (!fsync_entry_slab) 883cad83c96SChao Yu return -ENOMEM; 884cad83c96SChao Yu return 0; 885cad83c96SChao Yu } 886cad83c96SChao Yu 887cad83c96SChao Yu void f2fs_destroy_recovery_cache(void) 888cad83c96SChao Yu { 889cad83c96SChao Yu kmem_cache_destroy(fsync_entry_slab); 890cad83c96SChao Yu } 891