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> 114034247aSNeilBrown #include <linux/sched/mm.h> 12d624c96fSJaegeuk Kim #include "f2fs.h" 13d624c96fSJaegeuk Kim #include "node.h" 14d624c96fSJaegeuk Kim #include "segment.h" 15d624c96fSJaegeuk Kim 16441ac5cbSJaegeuk Kim /* 17441ac5cbSJaegeuk Kim * Roll forward recovery scenarios. 18441ac5cbSJaegeuk Kim * 19441ac5cbSJaegeuk Kim * [Term] F: fsync_mark, D: dentry_mark 20441ac5cbSJaegeuk Kim * 21441ac5cbSJaegeuk Kim * 1. inode(x) | CP | inode(x) | dnode(F) 22441ac5cbSJaegeuk Kim * -> Update the latest inode(x). 23441ac5cbSJaegeuk Kim * 24441ac5cbSJaegeuk Kim * 2. inode(x) | CP | inode(F) | dnode(F) 25441ac5cbSJaegeuk Kim * -> No problem. 26441ac5cbSJaegeuk Kim * 27441ac5cbSJaegeuk Kim * 3. inode(x) | CP | dnode(F) | inode(x) 28441ac5cbSJaegeuk Kim * -> Recover to the latest dnode(F), and drop the last inode(x) 29441ac5cbSJaegeuk Kim * 30441ac5cbSJaegeuk Kim * 4. inode(x) | CP | dnode(F) | inode(F) 31441ac5cbSJaegeuk Kim * -> No problem. 32441ac5cbSJaegeuk Kim * 33441ac5cbSJaegeuk Kim * 5. CP | inode(x) | dnode(F) 34441ac5cbSJaegeuk Kim * -> The inode(DF) was missing. Should drop this dnode(F). 35441ac5cbSJaegeuk Kim * 36441ac5cbSJaegeuk Kim * 6. CP | inode(DF) | dnode(F) 37441ac5cbSJaegeuk Kim * -> No problem. 38441ac5cbSJaegeuk Kim * 39441ac5cbSJaegeuk Kim * 7. CP | dnode(F) | inode(DF) 40441ac5cbSJaegeuk Kim * -> If f2fs_iget fails, then goto next to find inode(DF). 41441ac5cbSJaegeuk Kim * 42441ac5cbSJaegeuk Kim * 8. CP | dnode(F) | inode(x) 43441ac5cbSJaegeuk Kim * -> If f2fs_iget fails, then goto next to find inode(DF). 44441ac5cbSJaegeuk Kim * But it will fail due to no inode(DF). 45441ac5cbSJaegeuk Kim */ 46441ac5cbSJaegeuk Kim 47d624c96fSJaegeuk Kim static struct kmem_cache *fsync_entry_slab; 48d624c96fSJaegeuk Kim 495298d4bfSChristoph Hellwig #if IS_ENABLED(CONFIG_UNICODE) 504d9a2bb1SChao Yu extern struct kmem_cache *f2fs_cf_name_slab; 514d9a2bb1SChao Yu #endif 524d9a2bb1SChao Yu 534d57b86dSChao Yu bool f2fs_space_for_roll_forward(struct f2fs_sb_info *sbi) 54d624c96fSJaegeuk Kim { 5541382ec4SJaegeuk Kim s64 nalloc = percpu_counter_sum_positive(&sbi->alloc_valid_block_count); 5641382ec4SJaegeuk Kim 5741382ec4SJaegeuk Kim if (sbi->last_valid_block_count + nalloc > sbi->user_block_count) 58d624c96fSJaegeuk Kim return false; 5947c8ebccSJaegeuk Kim if (NM_I(sbi)->max_rf_node_blocks && 6047c8ebccSJaegeuk Kim percpu_counter_sum_positive(&sbi->rf_node_block_count) >= 6147c8ebccSJaegeuk Kim NM_I(sbi)->max_rf_node_blocks) 6247c8ebccSJaegeuk Kim return false; 63d624c96fSJaegeuk Kim return true; 64d624c96fSJaegeuk Kim } 65d624c96fSJaegeuk Kim 66d624c96fSJaegeuk Kim static struct fsync_inode_entry *get_fsync_inode(struct list_head *head, 67d624c96fSJaegeuk Kim nid_t ino) 68d624c96fSJaegeuk Kim { 69d624c96fSJaegeuk Kim struct fsync_inode_entry *entry; 70d624c96fSJaegeuk Kim 712d7b822aSChao Yu list_for_each_entry(entry, head, list) 72d624c96fSJaegeuk Kim if (entry->inode->i_ino == ino) 73d624c96fSJaegeuk Kim return entry; 742d7b822aSChao Yu 75d624c96fSJaegeuk Kim return NULL; 76d624c96fSJaegeuk Kim } 77d624c96fSJaegeuk Kim 78f4702d61SJaegeuk Kim static struct fsync_inode_entry *add_fsync_inode(struct f2fs_sb_info *sbi, 794b2414d0SChao Yu struct list_head *head, nid_t ino, bool quota_inode) 803f8ab270SChao Yu { 81e8ea9b3dSJaegeuk Kim struct inode *inode; 823f8ab270SChao Yu struct fsync_inode_entry *entry; 834b2414d0SChao Yu int err; 843f8ab270SChao Yu 85e8ea9b3dSJaegeuk Kim inode = f2fs_iget_retry(sbi->sb, ino); 86f4702d61SJaegeuk Kim if (IS_ERR(inode)) 87f4702d61SJaegeuk Kim return ERR_CAST(inode); 88f4702d61SJaegeuk Kim 8910a26878SChao Yu err = f2fs_dquot_initialize(inode); 904b2414d0SChao Yu if (err) 914b2414d0SChao Yu goto err_out; 924b2414d0SChao Yu 934b2414d0SChao Yu if (quota_inode) { 944b2414d0SChao Yu err = dquot_alloc_inode(inode); 954b2414d0SChao Yu if (err) 964b2414d0SChao Yu goto err_out; 974b2414d0SChao Yu } 984b2414d0SChao Yu 9932410577SChao Yu entry = f2fs_kmem_cache_alloc(fsync_entry_slab, 10032410577SChao Yu GFP_F2FS_ZERO, true, NULL); 1013f8ab270SChao Yu entry->inode = inode; 1023f8ab270SChao Yu list_add_tail(&entry->list, head); 1033f8ab270SChao Yu 1043f8ab270SChao Yu return entry; 1054b2414d0SChao Yu err_out: 1064b2414d0SChao Yu iput(inode); 1074b2414d0SChao Yu return ERR_PTR(err); 1083f8ab270SChao Yu } 1093f8ab270SChao Yu 11026b5a079SSheng Yong static void del_fsync_inode(struct fsync_inode_entry *entry, int drop) 1113f8ab270SChao Yu { 11226b5a079SSheng Yong if (drop) { 11326b5a079SSheng Yong /* inode should not be recovered, drop it */ 11426b5a079SSheng Yong f2fs_inode_synced(entry->inode); 11526b5a079SSheng Yong } 1163f8ab270SChao Yu iput(entry->inode); 1173f8ab270SChao Yu list_del(&entry->list); 1183f8ab270SChao Yu kmem_cache_free(fsync_entry_slab, entry); 1193f8ab270SChao Yu } 1203f8ab270SChao Yu 12143c780baSEric Biggers static int init_recovered_filename(const struct inode *dir, 12243c780baSEric Biggers struct f2fs_inode *raw_inode, 12343c780baSEric Biggers struct f2fs_filename *fname, 12443c780baSEric Biggers struct qstr *usr_fname) 12543c780baSEric Biggers { 12643c780baSEric Biggers int err; 12743c780baSEric Biggers 12843c780baSEric Biggers memset(fname, 0, sizeof(*fname)); 12943c780baSEric Biggers fname->disk_name.len = le32_to_cpu(raw_inode->i_namelen); 13043c780baSEric Biggers fname->disk_name.name = raw_inode->i_name; 13143c780baSEric Biggers 13243c780baSEric Biggers if (WARN_ON(fname->disk_name.len > F2FS_NAME_LEN)) 13343c780baSEric Biggers return -ENAMETOOLONG; 13443c780baSEric Biggers 13543c780baSEric Biggers if (!IS_ENCRYPTED(dir)) { 13643c780baSEric Biggers usr_fname->name = fname->disk_name.name; 13743c780baSEric Biggers usr_fname->len = fname->disk_name.len; 13843c780baSEric Biggers fname->usr_fname = usr_fname; 13943c780baSEric Biggers } 14043c780baSEric Biggers 14143c780baSEric Biggers /* Compute the hash of the filename */ 1427ad08a58SDaniel Rosenberg if (IS_ENCRYPTED(dir) && IS_CASEFOLDED(dir)) { 1437ad08a58SDaniel Rosenberg /* 1447ad08a58SDaniel Rosenberg * In this case the hash isn't computable without the key, so it 1457ad08a58SDaniel Rosenberg * was saved on-disk. 1467ad08a58SDaniel Rosenberg */ 1477ad08a58SDaniel Rosenberg if (fname->disk_name.len + sizeof(f2fs_hash_t) > F2FS_NAME_LEN) 1487ad08a58SDaniel Rosenberg return -EINVAL; 1497ad08a58SDaniel Rosenberg fname->hash = get_unaligned((f2fs_hash_t *) 1507ad08a58SDaniel Rosenberg &raw_inode->i_name[fname->disk_name.len]); 1517ad08a58SDaniel Rosenberg } else if (IS_CASEFOLDED(dir)) { 15243c780baSEric Biggers err = f2fs_init_casefolded_name(dir, fname); 15343c780baSEric Biggers if (err) 15443c780baSEric Biggers return err; 15543c780baSEric Biggers f2fs_hash_filename(dir, fname); 1565298d4bfSChristoph Hellwig #if IS_ENABLED(CONFIG_UNICODE) 15743c780baSEric Biggers /* Case-sensitive match is fine for recovery */ 1584d9a2bb1SChao Yu kmem_cache_free(f2fs_cf_name_slab, fname->cf_name.name); 15943c780baSEric Biggers fname->cf_name.name = NULL; 16043c780baSEric Biggers #endif 16143c780baSEric Biggers } else { 16243c780baSEric Biggers f2fs_hash_filename(dir, fname); 16343c780baSEric Biggers } 16443c780baSEric Biggers return 0; 16543c780baSEric Biggers } 16643c780baSEric Biggers 167f61cce5bSChao Yu static int recover_dentry(struct inode *inode, struct page *ipage, 168f61cce5bSChao Yu struct list_head *dir_list) 169d624c96fSJaegeuk Kim { 17058bfaf44SJaegeuk Kim struct f2fs_inode *raw_inode = F2FS_INODE(ipage); 17174d0b917SJaegeuk Kim nid_t pino = le32_to_cpu(raw_inode->i_pino); 1726b8213d9SJaegeuk Kim struct f2fs_dir_entry *de; 17343c780baSEric Biggers struct f2fs_filename fname; 17443c780baSEric Biggers struct qstr usr_fname; 175d624c96fSJaegeuk Kim struct page *page; 1766b8213d9SJaegeuk Kim struct inode *dir, *einode; 177f61cce5bSChao Yu struct fsync_inode_entry *entry; 178d624c96fSJaegeuk Kim int err = 0; 179e7ba108aSShuoran Liu char *name; 180d624c96fSJaegeuk Kim 181f61cce5bSChao Yu entry = get_fsync_inode(dir_list, pino); 182f61cce5bSChao Yu if (!entry) { 1834b2414d0SChao Yu entry = add_fsync_inode(F2FS_I_SB(inode), dir_list, 1844b2414d0SChao Yu pino, false); 185f4702d61SJaegeuk Kim if (IS_ERR(entry)) { 186f4702d61SJaegeuk Kim dir = ERR_CAST(entry); 187f4702d61SJaegeuk Kim err = PTR_ERR(entry); 188f61cce5bSChao Yu goto out; 189e7d55452SJaegeuk Kim } 190f61cce5bSChao Yu } 191f61cce5bSChao Yu 192f61cce5bSChao Yu dir = entry->inode; 19343c780baSEric Biggers err = init_recovered_filename(dir, raw_inode, &fname, &usr_fname); 19443c780baSEric Biggers if (err) 195f61cce5bSChao Yu goto out; 1966b8213d9SJaegeuk Kim retry: 197e7ba108aSShuoran Liu de = __f2fs_find_entry(dir, &fname, &page); 198418f6c27SJaegeuk Kim if (de && inode->i_ino == le32_to_cpu(de->ino)) 199bdbc90faSYunlong Song goto out_put; 200418f6c27SJaegeuk Kim 2016b8213d9SJaegeuk Kim if (de) { 202e8ea9b3dSJaegeuk Kim einode = f2fs_iget_retry(inode->i_sb, le32_to_cpu(de->ino)); 2036b8213d9SJaegeuk Kim if (IS_ERR(einode)) { 2046b8213d9SJaegeuk Kim WARN_ON(1); 2055c1f9927SChao Yu err = PTR_ERR(einode); 2065c1f9927SChao Yu if (err == -ENOENT) 2076b8213d9SJaegeuk Kim err = -EEXIST; 208bdbc90faSYunlong Song goto out_put; 2092e5558f4SRuss W. Knize } 2104b2414d0SChao Yu 21110a26878SChao Yu err = f2fs_dquot_initialize(einode); 2124b2414d0SChao Yu if (err) { 2134b2414d0SChao Yu iput(einode); 214bdbc90faSYunlong Song goto out_put; 2154b2414d0SChao Yu } 2164b2414d0SChao Yu 2174d57b86dSChao Yu err = f2fs_acquire_orphan_inode(F2FS_I_SB(inode)); 2182e5558f4SRuss W. Knize if (err) { 2192e5558f4SRuss W. Knize iput(einode); 220bdbc90faSYunlong Song goto out_put; 2216b8213d9SJaegeuk Kim } 222dbeacf02SChao Yu f2fs_delete_entry(de, page, dir, einode); 2236b8213d9SJaegeuk Kim iput(einode); 2246b8213d9SJaegeuk Kim goto retry; 22591246c21SChao Yu } else if (IS_ERR(page)) { 22691246c21SChao Yu err = PTR_ERR(page); 22791246c21SChao Yu } else { 2284d57b86dSChao Yu err = f2fs_add_dentry(dir, &fname, inode, 22991246c21SChao Yu inode->i_ino, inode->i_mode); 2306b8213d9SJaegeuk Kim } 231e8ea9b3dSJaegeuk Kim if (err == -ENOMEM) 232e8ea9b3dSJaegeuk Kim goto retry; 2332e5558f4SRuss W. Knize goto out; 2342e5558f4SRuss W. Knize 235bdbc90faSYunlong Song out_put: 2362e5558f4SRuss W. Knize f2fs_put_page(page, 0); 237d624c96fSJaegeuk Kim out: 238e7ba108aSShuoran Liu if (file_enc_name(inode)) 239e7ba108aSShuoran Liu name = "<encrypted>"; 240e7ba108aSShuoran Liu else 241e7ba108aSShuoran Liu name = raw_inode->i_name; 242dcbb4c10SJoe Perches f2fs_notice(F2FS_I_SB(inode), "%s: ino = %x, name = %s, dir = %lx, err = %d", 243e7ba108aSShuoran Liu __func__, ino_of_node(ipage), name, 244f28c06faSDan Carpenter IS_ERR(dir) ? 0 : dir->i_ino, err); 245d624c96fSJaegeuk Kim return err; 246d624c96fSJaegeuk Kim } 247d624c96fSJaegeuk Kim 248af033b2aSChao Yu static int recover_quota_data(struct inode *inode, struct page *page) 249af033b2aSChao Yu { 250af033b2aSChao Yu struct f2fs_inode *raw = F2FS_INODE(page); 251af033b2aSChao Yu struct iattr attr; 252af033b2aSChao Yu uid_t i_uid = le32_to_cpu(raw->i_uid); 253af033b2aSChao Yu gid_t i_gid = le32_to_cpu(raw->i_gid); 254af033b2aSChao Yu int err; 255af033b2aSChao Yu 256af033b2aSChao Yu memset(&attr, 0, sizeof(attr)); 257af033b2aSChao Yu 258b27c82e1SChristian Brauner attr.ia_vfsuid = VFSUIDT_INIT(make_kuid(inode->i_sb->s_user_ns, i_uid)); 259b27c82e1SChristian Brauner attr.ia_vfsgid = VFSGIDT_INIT(make_kgid(inode->i_sb->s_user_ns, i_gid)); 260af033b2aSChao Yu 261e67fe633SChristian Brauner if (!vfsuid_eq(attr.ia_vfsuid, i_uid_into_vfsuid(&nop_mnt_idmap, inode))) 262af033b2aSChao Yu attr.ia_valid |= ATTR_UID; 263e67fe633SChristian Brauner if (!vfsgid_eq(attr.ia_vfsgid, i_gid_into_vfsgid(&nop_mnt_idmap, inode))) 264af033b2aSChao Yu attr.ia_valid |= ATTR_GID; 265af033b2aSChao Yu 266af033b2aSChao Yu if (!attr.ia_valid) 267af033b2aSChao Yu return 0; 268af033b2aSChao Yu 269f861646aSChristian Brauner err = dquot_transfer(&nop_mnt_idmap, inode, &attr); 270af033b2aSChao Yu if (err) 271af033b2aSChao Yu set_sbi_flag(F2FS_I_SB(inode), SBI_QUOTA_NEED_REPAIR); 272af033b2aSChao Yu return err; 273af033b2aSChao Yu } 274af033b2aSChao Yu 27537a086f0SJaegeuk Kim static void recover_inline_flags(struct inode *inode, struct f2fs_inode *ri) 27637a086f0SJaegeuk Kim { 27737a086f0SJaegeuk Kim if (ri->i_inline & F2FS_PIN_FILE) 27837a086f0SJaegeuk Kim set_inode_flag(inode, FI_PIN_FILE); 27937a086f0SJaegeuk Kim else 28037a086f0SJaegeuk Kim clear_inode_flag(inode, FI_PIN_FILE); 28137a086f0SJaegeuk Kim if (ri->i_inline & F2FS_DATA_EXIST) 28237a086f0SJaegeuk Kim set_inode_flag(inode, FI_DATA_EXIST); 28337a086f0SJaegeuk Kim else 28437a086f0SJaegeuk Kim clear_inode_flag(inode, FI_DATA_EXIST); 28537a086f0SJaegeuk Kim } 28637a086f0SJaegeuk Kim 287af033b2aSChao Yu static int recover_inode(struct inode *inode, struct page *page) 288441ac5cbSJaegeuk Kim { 289441ac5cbSJaegeuk Kim struct f2fs_inode *raw = F2FS_INODE(page); 290e7d55452SJaegeuk Kim char *name; 291af033b2aSChao Yu int err; 292441ac5cbSJaegeuk Kim 293441ac5cbSJaegeuk Kim inode->i_mode = le16_to_cpu(raw->i_mode); 294af033b2aSChao Yu 295af033b2aSChao Yu err = recover_quota_data(inode, page); 296af033b2aSChao Yu if (err) 297af033b2aSChao Yu return err; 298af033b2aSChao Yu 299dc4cd125SChao Yu i_uid_write(inode, le32_to_cpu(raw->i_uid)); 300dc4cd125SChao Yu i_gid_write(inode, le32_to_cpu(raw->i_gid)); 301f4474aa6SChao Yu 302f4474aa6SChao Yu if (raw->i_inline & F2FS_EXTRA_ATTR) { 3037beb01f7SChao Yu if (f2fs_sb_has_project_quota(F2FS_I_SB(inode)) && 304f4474aa6SChao Yu F2FS_FITS_IN_INODE(raw, le16_to_cpu(raw->i_extra_isize), 305f4474aa6SChao Yu i_projid)) { 306f4474aa6SChao Yu projid_t i_projid; 30778130819SChao Yu kprojid_t kprojid; 308f4474aa6SChao Yu 309f4474aa6SChao Yu i_projid = (projid_t)le32_to_cpu(raw->i_projid); 31078130819SChao Yu kprojid = make_kprojid(&init_user_ns, i_projid); 31178130819SChao Yu 31278130819SChao Yu if (!projid_eq(kprojid, F2FS_I(inode)->i_projid)) { 31378130819SChao Yu err = f2fs_transfer_project_quota(inode, 31478130819SChao Yu kprojid); 31578130819SChao Yu if (err) 31678130819SChao Yu return err; 31778130819SChao Yu F2FS_I(inode)->i_projid = kprojid; 31878130819SChao Yu } 319f4474aa6SChao Yu } 320f4474aa6SChao Yu } 321f4474aa6SChao Yu 322fc9581c8SJaegeuk Kim f2fs_i_size_write(inode, le64_to_cpu(raw->i_size)); 3239f0552e0SChao Yu inode->i_atime.tv_sec = le64_to_cpu(raw->i_atime); 324c62ebd35SJeff Layton inode_set_ctime(inode, le64_to_cpu(raw->i_ctime), 325c62ebd35SJeff Layton le32_to_cpu(raw->i_ctime_nsec)); 326441ac5cbSJaegeuk Kim inode->i_mtime.tv_sec = le64_to_cpu(raw->i_mtime); 3279f0552e0SChao Yu inode->i_atime.tv_nsec = le32_to_cpu(raw->i_atime_nsec); 328441ac5cbSJaegeuk Kim inode->i_mtime.tv_nsec = le32_to_cpu(raw->i_mtime_nsec); 329f356fe0cSJaegeuk Kim 33026787236SJaegeuk Kim F2FS_I(inode)->i_advise = raw->i_advise; 33119c73a69SChao Yu F2FS_I(inode)->i_flags = le32_to_cpu(raw->i_flags); 3320c093b59SChao Yu f2fs_set_inode_flags(inode); 3337de36cf3SChao Yu F2FS_I(inode)->i_gc_failures[GC_FAILURE_PIN] = 3347de36cf3SChao Yu le16_to_cpu(raw->i_gc_failures); 33526787236SJaegeuk Kim 33637a086f0SJaegeuk Kim recover_inline_flags(inode, raw); 33737a086f0SJaegeuk Kim 3384a1728caSChao Yu f2fs_mark_inode_dirty_sync(inode, true); 3394a1728caSChao Yu 340e7d55452SJaegeuk Kim if (file_enc_name(inode)) 341e7d55452SJaegeuk Kim name = "<encrypted>"; 342e7d55452SJaegeuk Kim else 343e7d55452SJaegeuk Kim name = F2FS_INODE(page)->i_name; 344e7d55452SJaegeuk Kim 345dcbb4c10SJoe Perches f2fs_notice(F2FS_I_SB(inode), "recover_inode: ino = %x, name = %s, inline = %x", 34637a086f0SJaegeuk Kim ino_of_node(page), name, raw->i_inline); 347af033b2aSChao Yu return 0; 348d624c96fSJaegeuk Kim } 349d624c96fSJaegeuk Kim 350430f163bSChao Yu static unsigned int adjust_por_ra_blocks(struct f2fs_sb_info *sbi, 351430f163bSChao Yu unsigned int ra_blocks, unsigned int blkaddr, 352430f163bSChao Yu unsigned int next_blkaddr) 353430f163bSChao Yu { 354430f163bSChao Yu if (blkaddr + 1 == next_blkaddr) 355430f163bSChao Yu ra_blocks = min_t(unsigned int, RECOVERY_MAX_RA_BLOCKS, 356430f163bSChao Yu ra_blocks * 2); 357430f163bSChao Yu else if (next_blkaddr % sbi->blocks_per_seg) 358430f163bSChao Yu ra_blocks = max_t(unsigned int, RECOVERY_MIN_RA_BLOCKS, 359430f163bSChao Yu ra_blocks / 2); 360430f163bSChao Yu return ra_blocks; 361430f163bSChao Yu } 362430f163bSChao Yu 36338a4a330SChunhai Guo /* Detect looped node chain with Floyd's cycle detection algorithm. */ 36438a4a330SChunhai Guo static int sanity_check_node_chain(struct f2fs_sb_info *sbi, block_t blkaddr, 36538a4a330SChunhai Guo block_t *blkaddr_fast, bool *is_detecting) 36638a4a330SChunhai Guo { 36738a4a330SChunhai Guo unsigned int ra_blocks = RECOVERY_MAX_RA_BLOCKS; 36838a4a330SChunhai Guo struct page *page = NULL; 36938a4a330SChunhai Guo int i; 37038a4a330SChunhai Guo 37138a4a330SChunhai Guo if (!*is_detecting) 37238a4a330SChunhai Guo return 0; 37338a4a330SChunhai Guo 37438a4a330SChunhai Guo for (i = 0; i < 2; i++) { 37538a4a330SChunhai Guo if (!f2fs_is_valid_blkaddr(sbi, *blkaddr_fast, META_POR)) { 37638a4a330SChunhai Guo *is_detecting = false; 37738a4a330SChunhai Guo return 0; 37838a4a330SChunhai Guo } 37938a4a330SChunhai Guo 38038a4a330SChunhai Guo page = f2fs_get_tmp_page(sbi, *blkaddr_fast); 38138a4a330SChunhai Guo if (IS_ERR(page)) 38238a4a330SChunhai Guo return PTR_ERR(page); 38338a4a330SChunhai Guo 38438a4a330SChunhai Guo if (!is_recoverable_dnode(page)) { 38538a4a330SChunhai Guo f2fs_put_page(page, 1); 38638a4a330SChunhai Guo *is_detecting = false; 38738a4a330SChunhai Guo return 0; 38838a4a330SChunhai Guo } 38938a4a330SChunhai Guo 39038a4a330SChunhai Guo ra_blocks = adjust_por_ra_blocks(sbi, ra_blocks, *blkaddr_fast, 39138a4a330SChunhai Guo next_blkaddr_of_node(page)); 39238a4a330SChunhai Guo 39338a4a330SChunhai Guo *blkaddr_fast = next_blkaddr_of_node(page); 39438a4a330SChunhai Guo f2fs_put_page(page, 1); 39538a4a330SChunhai Guo 39638a4a330SChunhai Guo f2fs_ra_meta_pages_cond(sbi, *blkaddr_fast, ra_blocks); 39738a4a330SChunhai Guo } 39838a4a330SChunhai Guo 39938a4a330SChunhai Guo if (*blkaddr_fast == blkaddr) { 40038a4a330SChunhai Guo f2fs_notice(sbi, "%s: Detect looped node chain on blkaddr:%u." 40138a4a330SChunhai Guo " Run fsck to fix it.", __func__, blkaddr); 40238a4a330SChunhai Guo return -EINVAL; 40338a4a330SChunhai Guo } 40438a4a330SChunhai Guo return 0; 40538a4a330SChunhai Guo } 40638a4a330SChunhai Guo 407d40d30c5SJaegeuk Kim static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head, 408d40d30c5SJaegeuk Kim bool check_only) 409d624c96fSJaegeuk Kim { 410d624c96fSJaegeuk Kim struct curseg_info *curseg; 4114c521f49SJaegeuk Kim struct page *page = NULL; 41238a4a330SChunhai Guo block_t blkaddr, blkaddr_fast; 41338a4a330SChunhai Guo bool is_detecting = true; 414d624c96fSJaegeuk Kim int err = 0; 415d624c96fSJaegeuk Kim 416d624c96fSJaegeuk Kim /* get node pages in the current segment */ 417d624c96fSJaegeuk Kim curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); 418695fd1edSChao Yu blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); 41938a4a330SChunhai Guo blkaddr_fast = blkaddr; 420d624c96fSJaegeuk Kim 421d624c96fSJaegeuk Kim while (1) { 422d624c96fSJaegeuk Kim struct fsync_inode_entry *entry; 423d624c96fSJaegeuk Kim 424e1da7872SChao Yu if (!f2fs_is_valid_blkaddr(sbi, blkaddr, META_POR)) 4254c521f49SJaegeuk Kim return 0; 426d624c96fSJaegeuk Kim 4274d57b86dSChao Yu page = f2fs_get_tmp_page(sbi, blkaddr); 4287735730dSChao Yu if (IS_ERR(page)) { 4297735730dSChao Yu err = PTR_ERR(page); 4307735730dSChao Yu break; 4317735730dSChao Yu } 432393ff91fSJaegeuk Kim 43398838579SChao Yu if (!is_recoverable_dnode(page)) { 43498838579SChao Yu f2fs_put_page(page, 1); 435f356fe0cSJaegeuk Kim break; 43698838579SChao Yu } 437d624c96fSJaegeuk Kim 438d624c96fSJaegeuk Kim if (!is_fsync_dnode(page)) 439d624c96fSJaegeuk Kim goto next; 440d624c96fSJaegeuk Kim 441d624c96fSJaegeuk Kim entry = get_fsync_inode(head, ino_of_node(page)); 442d47b8715SChao Yu if (!entry) { 4434b2414d0SChao Yu bool quota_inode = false; 4444b2414d0SChao Yu 445d40d30c5SJaegeuk Kim if (!check_only && 446d40d30c5SJaegeuk Kim IS_INODE(page) && is_dent_dnode(page)) { 4474d57b86dSChao Yu err = f2fs_recover_inode_page(sbi, page); 44898838579SChao Yu if (err) { 44998838579SChao Yu f2fs_put_page(page, 1); 450f356fe0cSJaegeuk Kim break; 45198838579SChao Yu } 4524b2414d0SChao Yu quota_inode = true; 453d624c96fSJaegeuk Kim } 454d624c96fSJaegeuk Kim 455441ac5cbSJaegeuk Kim /* 456441ac5cbSJaegeuk Kim * CP | dnode(F) | inode(DF) 457441ac5cbSJaegeuk Kim * For this case, we should not give up now. 458441ac5cbSJaegeuk Kim */ 4594b2414d0SChao Yu entry = add_fsync_inode(sbi, head, ino_of_node(page), 4604b2414d0SChao Yu quota_inode); 461f4702d61SJaegeuk Kim if (IS_ERR(entry)) { 462f4702d61SJaegeuk Kim err = PTR_ERR(entry); 4633f8ac7daSColin Ian King if (err == -ENOENT) 464441ac5cbSJaegeuk Kim goto next; 46598838579SChao Yu f2fs_put_page(page, 1); 466f356fe0cSJaegeuk Kim break; 467d624c96fSJaegeuk Kim } 468d624c96fSJaegeuk Kim } 469addbe45bSJaegeuk Kim entry->blkaddr = blkaddr; 470addbe45bSJaegeuk Kim 471608514deSJaegeuk Kim if (IS_INODE(page) && is_dent_dnode(page)) 472c52e1b10SJaegeuk Kim entry->last_dentry = blkaddr; 473d624c96fSJaegeuk Kim next: 474d624c96fSJaegeuk Kim /* check next segment */ 475d624c96fSJaegeuk Kim blkaddr = next_blkaddr_of_node(page); 4764c521f49SJaegeuk Kim f2fs_put_page(page, 1); 477635aee1fSChao Yu 47838a4a330SChunhai Guo err = sanity_check_node_chain(sbi, blkaddr, &blkaddr_fast, 47938a4a330SChunhai Guo &is_detecting); 48038a4a330SChunhai Guo if (err) 48138a4a330SChunhai Guo break; 482d624c96fSJaegeuk Kim } 483d624c96fSJaegeuk Kim return err; 484d624c96fSJaegeuk Kim } 485d624c96fSJaegeuk Kim 48626b5a079SSheng Yong static void destroy_fsync_dnodes(struct list_head *head, int drop) 487d624c96fSJaegeuk Kim { 488d8b79b2fSDan Carpenter struct fsync_inode_entry *entry, *tmp; 489d8b79b2fSDan Carpenter 4903f8ab270SChao Yu list_for_each_entry_safe(entry, tmp, head, list) 49126b5a079SSheng Yong del_fsync_inode(entry, drop); 492d624c96fSJaegeuk Kim } 493d624c96fSJaegeuk Kim 49439cf72cfSJaegeuk Kim static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi, 495b292dcabSJaegeuk Kim block_t blkaddr, struct dnode_of_data *dn) 496d624c96fSJaegeuk Kim { 497d624c96fSJaegeuk Kim struct seg_entry *sentry; 498d624c96fSJaegeuk Kim unsigned int segno = GET_SEGNO(sbi, blkaddr); 499491c0854SJaegeuk Kim unsigned short blkoff = GET_BLKOFF_FROM_SEG0(sbi, blkaddr); 500f6517cfcSJaegeuk Kim struct f2fs_summary_block *sum_node; 501d624c96fSJaegeuk Kim struct f2fs_summary sum; 502f6517cfcSJaegeuk Kim struct page *sum_page, *node_page; 503c9ef4810SJaegeuk Kim struct dnode_of_data tdn = *dn; 504b292dcabSJaegeuk Kim nid_t ino, nid; 505d624c96fSJaegeuk Kim struct inode *inode; 506c6ad7fd1SChao Yu unsigned int offset, ofs_in_node, max_addrs; 507d624c96fSJaegeuk Kim block_t bidx; 508d624c96fSJaegeuk Kim int i; 509d624c96fSJaegeuk Kim 510d624c96fSJaegeuk Kim sentry = get_seg_entry(sbi, segno); 511d624c96fSJaegeuk Kim if (!f2fs_test_bit(blkoff, sentry->cur_valid_map)) 51239cf72cfSJaegeuk Kim return 0; 513d624c96fSJaegeuk Kim 514d624c96fSJaegeuk Kim /* Get the previous summary */ 515125c9fb1SJaegeuk Kim for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { 516d624c96fSJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, i); 5175f029c04SYi Zhuang 518d624c96fSJaegeuk Kim if (curseg->segno == segno) { 519d624c96fSJaegeuk Kim sum = curseg->sum_blk->entries[blkoff]; 520f6517cfcSJaegeuk Kim goto got_it; 521d624c96fSJaegeuk Kim } 522d624c96fSJaegeuk Kim } 523d624c96fSJaegeuk Kim 5244d57b86dSChao Yu sum_page = f2fs_get_sum_page(sbi, segno); 525edc55aafSJaegeuk Kim if (IS_ERR(sum_page)) 526edc55aafSJaegeuk Kim return PTR_ERR(sum_page); 527f6517cfcSJaegeuk Kim sum_node = (struct f2fs_summary_block *)page_address(sum_page); 528f6517cfcSJaegeuk Kim sum = sum_node->entries[blkoff]; 529f6517cfcSJaegeuk Kim f2fs_put_page(sum_page, 1); 530f6517cfcSJaegeuk Kim got_it: 531b292dcabSJaegeuk Kim /* Use the locked dnode page and inode */ 532b292dcabSJaegeuk Kim nid = le32_to_cpu(sum.nid); 533c6ad7fd1SChao Yu ofs_in_node = le16_to_cpu(sum.ofs_in_node); 534c6ad7fd1SChao Yu 535c6ad7fd1SChao Yu max_addrs = ADDRS_PER_PAGE(dn->node_page, dn->inode); 536c6ad7fd1SChao Yu if (ofs_in_node >= max_addrs) { 537c6ad7fd1SChao Yu f2fs_err(sbi, "Inconsistent ofs_in_node:%u in summary, ino:%lu, nid:%u, max:%u", 538c6ad7fd1SChao Yu ofs_in_node, dn->inode->i_ino, nid, max_addrs); 53995fa90c9SChao Yu f2fs_handle_error(sbi, ERROR_INCONSISTENT_SUMMARY); 540c6ad7fd1SChao Yu return -EFSCORRUPTED; 541c6ad7fd1SChao Yu } 542c6ad7fd1SChao Yu 543b292dcabSJaegeuk Kim if (dn->inode->i_ino == nid) { 544b292dcabSJaegeuk Kim tdn.nid = nid; 545c9ef4810SJaegeuk Kim if (!dn->inode_page_locked) 546c9ef4810SJaegeuk Kim lock_page(dn->inode_page); 547b292dcabSJaegeuk Kim tdn.node_page = dn->inode_page; 548c6ad7fd1SChao Yu tdn.ofs_in_node = ofs_in_node; 549c9ef4810SJaegeuk Kim goto truncate_out; 550b292dcabSJaegeuk Kim } else if (dn->nid == nid) { 551c6ad7fd1SChao Yu tdn.ofs_in_node = ofs_in_node; 552c9ef4810SJaegeuk Kim goto truncate_out; 553b292dcabSJaegeuk Kim } 554b292dcabSJaegeuk Kim 555d624c96fSJaegeuk Kim /* Get the node page */ 5564d57b86dSChao Yu node_page = f2fs_get_node_page(sbi, nid); 55739cf72cfSJaegeuk Kim if (IS_ERR(node_page)) 55839cf72cfSJaegeuk Kim return PTR_ERR(node_page); 559de93653fSJaegeuk Kim 560de93653fSJaegeuk Kim offset = ofs_of_node(node_page); 561d624c96fSJaegeuk Kim ino = ino_of_node(node_page); 562d624c96fSJaegeuk Kim f2fs_put_page(node_page, 1); 563d624c96fSJaegeuk Kim 56460979115SJaegeuk Kim if (ino != dn->inode->i_ino) { 5654b2414d0SChao Yu int ret; 5664b2414d0SChao Yu 567d624c96fSJaegeuk Kim /* Deallocate previous index in the node page */ 568e8ea9b3dSJaegeuk Kim inode = f2fs_iget_retry(sbi->sb, ino); 56906025f4dSNamjae Jeon if (IS_ERR(inode)) 57039cf72cfSJaegeuk Kim return PTR_ERR(inode); 5714b2414d0SChao Yu 57210a26878SChao Yu ret = f2fs_dquot_initialize(inode); 5734b2414d0SChao Yu if (ret) { 5744b2414d0SChao Yu iput(inode); 5754b2414d0SChao Yu return ret; 5764b2414d0SChao Yu } 57760979115SJaegeuk Kim } else { 57860979115SJaegeuk Kim inode = dn->inode; 57960979115SJaegeuk Kim } 58006025f4dSNamjae Jeon 5814d57b86dSChao Yu bidx = f2fs_start_bidx_of_node(offset, inode) + 5824d57b86dSChao Yu le16_to_cpu(sum.ofs_in_node); 583de93653fSJaegeuk Kim 584c9ef4810SJaegeuk Kim /* 585c9ef4810SJaegeuk Kim * if inode page is locked, unlock temporarily, but its reference 586c9ef4810SJaegeuk Kim * count keeps alive. 587c9ef4810SJaegeuk Kim */ 588c9ef4810SJaegeuk Kim if (ino == dn->inode->i_ino && dn->inode_page_locked) 589c9ef4810SJaegeuk Kim unlock_page(dn->inode_page); 590c9ef4810SJaegeuk Kim 591c9ef4810SJaegeuk Kim set_new_dnode(&tdn, inode, NULL, NULL, 0); 5924d57b86dSChao Yu if (f2fs_get_dnode_of_data(&tdn, bidx, LOOKUP_NODE)) 593c9ef4810SJaegeuk Kim goto out; 594c9ef4810SJaegeuk Kim 595c9ef4810SJaegeuk Kim if (tdn.data_blkaddr == blkaddr) 5964d57b86dSChao Yu f2fs_truncate_data_blocks_range(&tdn, 1); 597c9ef4810SJaegeuk Kim 598c9ef4810SJaegeuk Kim f2fs_put_dnode(&tdn); 599c9ef4810SJaegeuk Kim out: 600c9ef4810SJaegeuk Kim if (ino != dn->inode->i_ino) 601c9ef4810SJaegeuk Kim iput(inode); 602c9ef4810SJaegeuk Kim else if (dn->inode_page_locked) 603c9ef4810SJaegeuk Kim lock_page(dn->inode_page); 604c9ef4810SJaegeuk Kim return 0; 605c9ef4810SJaegeuk Kim 606c9ef4810SJaegeuk Kim truncate_out: 607a2ced1ceSChao Yu if (f2fs_data_blkaddr(&tdn) == blkaddr) 6084d57b86dSChao Yu f2fs_truncate_data_blocks_range(&tdn, 1); 609c9ef4810SJaegeuk Kim if (dn->inode->i_ino == nid && !dn->inode_page_locked) 610c9ef4810SJaegeuk Kim unlock_page(dn->inode_page); 61139cf72cfSJaegeuk Kim return 0; 612d624c96fSJaegeuk Kim } 613d624c96fSJaegeuk Kim 614*8844b2f8SChao Yu static int f2fs_reserve_new_block_retry(struct dnode_of_data *dn) 615*8844b2f8SChao Yu { 616*8844b2f8SChao Yu int i, err = 0; 617*8844b2f8SChao Yu 618*8844b2f8SChao Yu for (i = DEFAULT_FAILURE_RETRY_COUNT; i > 0; i--) { 619*8844b2f8SChao Yu err = f2fs_reserve_new_block(dn); 620*8844b2f8SChao Yu if (!err) 621*8844b2f8SChao Yu break; 622*8844b2f8SChao Yu } 623*8844b2f8SChao Yu 624*8844b2f8SChao Yu return err; 625*8844b2f8SChao Yu } 626*8844b2f8SChao Yu 6276ead1142SJaegeuk Kim static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, 628e17d488bSSheng Yong struct page *page) 629d624c96fSJaegeuk Kim { 630d624c96fSJaegeuk Kim struct dnode_of_data dn; 631d624c96fSJaegeuk Kim struct node_info ni; 63281ca7350SChao Yu unsigned int start, end; 633f356fe0cSJaegeuk Kim int err = 0, recovered = 0; 634d624c96fSJaegeuk Kim 6351c35a90eSJaegeuk Kim /* step 1: recover xattr */ 6361c35a90eSJaegeuk Kim if (IS_INODE(page)) { 6379627a7b3SChao Yu err = f2fs_recover_inline_xattr(inode, page); 6389627a7b3SChao Yu if (err) 6399627a7b3SChao Yu goto out; 6401c35a90eSJaegeuk Kim } else if (f2fs_has_xattr_block(ofs_of_node(page))) { 6414d57b86dSChao Yu err = f2fs_recover_xattr_data(inode, page); 642d260081cSChao Yu if (!err) 643d260081cSChao Yu recovered++; 6441c35a90eSJaegeuk Kim goto out; 6451c35a90eSJaegeuk Kim } 64670cfed88SChao Yu 6471c35a90eSJaegeuk Kim /* step 2: recover inline data */ 6489627a7b3SChao Yu err = f2fs_recover_inline_data(inode, page); 6499627a7b3SChao Yu if (err) { 6509627a7b3SChao Yu if (err == 1) 6519627a7b3SChao Yu err = 0; 6521e1bb4baSJaegeuk Kim goto out; 6539627a7b3SChao Yu } 6541e1bb4baSJaegeuk Kim 6551c35a90eSJaegeuk Kim /* step 3: recover data indices */ 6564d57b86dSChao Yu start = f2fs_start_bidx_of_node(ofs_of_node(page), inode); 65781ca7350SChao Yu end = start + ADDRS_PER_PAGE(page, inode); 658d624c96fSJaegeuk Kim 659d624c96fSJaegeuk Kim set_new_dnode(&dn, inode, NULL, NULL, 0); 660e8ea9b3dSJaegeuk Kim retry_dn: 6614d57b86dSChao Yu err = f2fs_get_dnode_of_data(&dn, start, ALLOC_NODE); 662e8ea9b3dSJaegeuk Kim if (err) { 663e8ea9b3dSJaegeuk Kim if (err == -ENOMEM) { 6644034247aSNeilBrown memalloc_retry_wait(GFP_NOFS); 665e8ea9b3dSJaegeuk Kim goto retry_dn; 666e8ea9b3dSJaegeuk Kim } 6671e1bb4baSJaegeuk Kim goto out; 668e8ea9b3dSJaegeuk Kim } 669d624c96fSJaegeuk Kim 670bae0ee7aSChao Yu f2fs_wait_on_page_writeback(dn.node_page, NODE, true, true); 671d624c96fSJaegeuk Kim 672a9419b63SJaegeuk Kim err = f2fs_get_node_info(sbi, dn.nid, &ni, false); 6737735730dSChao Yu if (err) 6747735730dSChao Yu goto err; 6757735730dSChao Yu 6769850cf4aSJaegeuk Kim f2fs_bug_on(sbi, ni.ino != ino_of_node(page)); 67722d61e28SChao Yu 67822d61e28SChao Yu if (ofs_of_node(dn.node_page) != ofs_of_node(page)) { 679dcbb4c10SJoe Perches f2fs_warn(sbi, "Inconsistent ofs_of_node, ino:%lu, ofs:%u, %u", 68022d61e28SChao Yu inode->i_ino, ofs_of_node(dn.node_page), 68122d61e28SChao Yu ofs_of_node(page)); 68210f966bbSChao Yu err = -EFSCORRUPTED; 68395fa90c9SChao Yu f2fs_handle_error(sbi, ERROR_INCONSISTENT_FOOTER); 68422d61e28SChao Yu goto err; 68522d61e28SChao Yu } 686d624c96fSJaegeuk Kim 68712a8343eSChao Yu for (; start < end; start++, dn.ofs_in_node++) { 688d624c96fSJaegeuk Kim block_t src, dest; 689d624c96fSJaegeuk Kim 690a2ced1ceSChao Yu src = f2fs_data_blkaddr(&dn); 691a2ced1ceSChao Yu dest = data_blkaddr(dn.inode, page, dn.ofs_in_node); 692d624c96fSJaegeuk Kim 69393770ab7SChao Yu if (__is_valid_data_blkaddr(src) && 69493770ab7SChao Yu !f2fs_is_valid_blkaddr(sbi, src, META_POR)) { 69510f966bbSChao Yu err = -EFSCORRUPTED; 69695fa90c9SChao Yu f2fs_handle_error(sbi, ERROR_INVALID_BLKADDR); 69793770ab7SChao Yu goto err; 69893770ab7SChao Yu } 69993770ab7SChao Yu 70093770ab7SChao Yu if (__is_valid_data_blkaddr(dest) && 70193770ab7SChao Yu !f2fs_is_valid_blkaddr(sbi, dest, META_POR)) { 70210f966bbSChao Yu err = -EFSCORRUPTED; 70395fa90c9SChao Yu f2fs_handle_error(sbi, ERROR_INVALID_BLKADDR); 70493770ab7SChao Yu goto err; 70593770ab7SChao Yu } 70693770ab7SChao Yu 70712a8343eSChao Yu /* skip recovering if dest is the same as src */ 70812a8343eSChao Yu if (src == dest) 70912a8343eSChao Yu continue; 71012a8343eSChao Yu 71112a8343eSChao Yu /* dest is invalid, just invalidate src block */ 71212a8343eSChao Yu if (dest == NULL_ADDR) { 7134d57b86dSChao Yu f2fs_truncate_data_blocks_range(&dn, 1); 71412a8343eSChao Yu continue; 71512a8343eSChao Yu } 71612a8343eSChao Yu 71726787236SJaegeuk Kim if (!file_keep_isize(inode) && 718dba79f38SChao Yu (i_size_read(inode) <= ((loff_t)start << PAGE_SHIFT))) 719dba79f38SChao Yu f2fs_i_size_write(inode, 720dba79f38SChao Yu (loff_t)(start + 1) << PAGE_SHIFT); 72126de9b11SJaegeuk Kim 72212a8343eSChao Yu /* 72312a8343eSChao Yu * dest is reserved block, invalidate src block 72412a8343eSChao Yu * and then reserve one new block in dnode page. 72512a8343eSChao Yu */ 72612a8343eSChao Yu if (dest == NEW_ADDR) { 7274d57b86dSChao Yu f2fs_truncate_data_blocks_range(&dn, 1); 728*8844b2f8SChao Yu 729*8844b2f8SChao Yu err = f2fs_reserve_new_block_retry(&dn); 7302a7b12d4SChao Yu if (err) 7312a7b12d4SChao Yu goto err; 73212a8343eSChao Yu continue; 73312a8343eSChao Yu } 73412a8343eSChao Yu 73512a8343eSChao Yu /* dest is valid block, try to recover from src to dest */ 736e1da7872SChao Yu if (f2fs_is_valid_blkaddr(sbi, dest, META_POR)) { 737d624c96fSJaegeuk Kim if (src == NULL_ADDR) { 738*8844b2f8SChao Yu err = f2fs_reserve_new_block_retry(&dn); 7396f3ec995SJaegeuk Kim if (err) 7406f3ec995SJaegeuk Kim goto err; 741d624c96fSJaegeuk Kim } 742e8ea9b3dSJaegeuk Kim retry_prev: 743d624c96fSJaegeuk Kim /* Check the previous node page having this index */ 74439cf72cfSJaegeuk Kim err = check_index_in_prev_nodes(sbi, dest, &dn); 745e8ea9b3dSJaegeuk Kim if (err) { 746e8ea9b3dSJaegeuk Kim if (err == -ENOMEM) { 7474034247aSNeilBrown memalloc_retry_wait(GFP_NOFS); 748e8ea9b3dSJaegeuk Kim goto retry_prev; 749e8ea9b3dSJaegeuk Kim } 75039cf72cfSJaegeuk Kim goto err; 751e8ea9b3dSJaegeuk Kim } 752d624c96fSJaegeuk Kim 7530ef4ca04SChao Yu if (f2fs_is_valid_blkaddr(sbi, dest, 7540ef4ca04SChao Yu DATA_GENERIC_ENHANCE_UPDATE)) { 7550ef4ca04SChao Yu f2fs_err(sbi, "Inconsistent dest blkaddr:%u, ino:%lu, ofs:%u", 7560ef4ca04SChao Yu dest, inode->i_ino, dn.ofs_in_node); 7570ef4ca04SChao Yu err = -EFSCORRUPTED; 75895fa90c9SChao Yu f2fs_handle_error(sbi, 75995fa90c9SChao Yu ERROR_INVALID_BLKADDR); 7600ef4ca04SChao Yu goto err; 7610ef4ca04SChao Yu } 7620ef4ca04SChao Yu 763d624c96fSJaegeuk Kim /* write dummy data page */ 764528e3459SChao Yu f2fs_replace_block(sbi, &dn, src, dest, 76528bc106bSChao Yu ni.version, false, false); 766f356fe0cSJaegeuk Kim recovered++; 767d624c96fSJaegeuk Kim } 768d624c96fSJaegeuk Kim } 769d624c96fSJaegeuk Kim 770d624c96fSJaegeuk Kim copy_node_footer(dn.node_page, page); 771d624c96fSJaegeuk Kim fill_node_footer(dn.node_page, dn.nid, ni.ino, 772d624c96fSJaegeuk Kim ofs_of_node(page), false); 773d624c96fSJaegeuk Kim set_page_dirty(dn.node_page); 77439cf72cfSJaegeuk Kim err: 775d624c96fSJaegeuk Kim f2fs_put_dnode(&dn); 7761e1bb4baSJaegeuk Kim out: 777dcbb4c10SJoe Perches f2fs_notice(sbi, "recover_data: ino = %lx (i_size: %s) recovered = %d, err = %d", 778dcbb4c10SJoe Perches inode->i_ino, file_keep_isize(inode) ? "keep" : "recover", 77926787236SJaegeuk Kim recovered, err); 78039cf72cfSJaegeuk Kim return err; 781d624c96fSJaegeuk Kim } 782d624c96fSJaegeuk Kim 783f61cce5bSChao Yu static int recover_data(struct f2fs_sb_info *sbi, struct list_head *inode_list, 78426b5a079SSheng Yong struct list_head *tmp_inode_list, struct list_head *dir_list) 785d624c96fSJaegeuk Kim { 786d624c96fSJaegeuk Kim struct curseg_info *curseg; 7874c521f49SJaegeuk Kim struct page *page = NULL; 7886ead1142SJaegeuk Kim int err = 0; 789d624c96fSJaegeuk Kim block_t blkaddr; 790430f163bSChao Yu unsigned int ra_blocks = RECOVERY_MAX_RA_BLOCKS; 791d624c96fSJaegeuk Kim 792d624c96fSJaegeuk Kim /* get node pages in the current segment */ 793b7973f23SChao Yu curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); 794d624c96fSJaegeuk Kim blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); 795d624c96fSJaegeuk Kim 796d624c96fSJaegeuk Kim while (1) { 797d624c96fSJaegeuk Kim struct fsync_inode_entry *entry; 798d624c96fSJaegeuk Kim 799e1da7872SChao Yu if (!f2fs_is_valid_blkaddr(sbi, blkaddr, META_POR)) 80045856affSJaegeuk Kim break; 801d624c96fSJaegeuk Kim 8024d57b86dSChao Yu page = f2fs_get_tmp_page(sbi, blkaddr); 8037735730dSChao Yu if (IS_ERR(page)) { 8047735730dSChao Yu err = PTR_ERR(page); 8057735730dSChao Yu break; 8067735730dSChao Yu } 8074c521f49SJaegeuk Kim 808a468f0efSJaegeuk Kim if (!is_recoverable_dnode(page)) { 8094c521f49SJaegeuk Kim f2fs_put_page(page, 1); 8104c521f49SJaegeuk Kim break; 8114c521f49SJaegeuk Kim } 8124c521f49SJaegeuk Kim 813f61cce5bSChao Yu entry = get_fsync_inode(inode_list, ino_of_node(page)); 814d624c96fSJaegeuk Kim if (!entry) 815d624c96fSJaegeuk Kim goto next; 816441ac5cbSJaegeuk Kim /* 817441ac5cbSJaegeuk Kim * inode(x) | CP | inode(x) | dnode(F) 818441ac5cbSJaegeuk Kim * In this case, we can lose the latest inode(x). 819c52e1b10SJaegeuk Kim * So, call recover_inode for the inode update. 820441ac5cbSJaegeuk Kim */ 821af033b2aSChao Yu if (IS_INODE(page)) { 822af033b2aSChao Yu err = recover_inode(entry->inode, page); 82398838579SChao Yu if (err) { 82498838579SChao Yu f2fs_put_page(page, 1); 825af033b2aSChao Yu break; 826af033b2aSChao Yu } 82798838579SChao Yu } 828c52e1b10SJaegeuk Kim if (entry->last_dentry == blkaddr) { 829f61cce5bSChao Yu err = recover_dentry(entry->inode, page, dir_list); 830c52e1b10SJaegeuk Kim if (err) { 831c52e1b10SJaegeuk Kim f2fs_put_page(page, 1); 832c52e1b10SJaegeuk Kim break; 833c52e1b10SJaegeuk Kim } 834c52e1b10SJaegeuk Kim } 835e17d488bSSheng Yong err = do_recover_data(sbi, entry->inode, page); 8364c521f49SJaegeuk Kim if (err) { 8374c521f49SJaegeuk Kim f2fs_put_page(page, 1); 83845856affSJaegeuk Kim break; 8394c521f49SJaegeuk Kim } 840d624c96fSJaegeuk Kim 8413f8ab270SChao Yu if (entry->blkaddr == blkaddr) 84226b5a079SSheng Yong list_move_tail(&entry->list, tmp_inode_list); 843d624c96fSJaegeuk Kim next: 844430f163bSChao Yu ra_blocks = adjust_por_ra_blocks(sbi, ra_blocks, blkaddr, 845430f163bSChao Yu next_blkaddr_of_node(page)); 846430f163bSChao Yu 847d624c96fSJaegeuk Kim /* check next segment */ 848d624c96fSJaegeuk Kim blkaddr = next_blkaddr_of_node(page); 8494c521f49SJaegeuk Kim f2fs_put_page(page, 1); 850430f163bSChao Yu 851430f163bSChao Yu f2fs_ra_meta_pages_cond(sbi, blkaddr, ra_blocks); 852d624c96fSJaegeuk Kim } 8536ead1142SJaegeuk Kim if (!err) 854901d745fSChao Yu f2fs_allocate_new_segments(sbi); 8556ead1142SJaegeuk Kim return err; 856d624c96fSJaegeuk Kim } 857d624c96fSJaegeuk Kim 8584d57b86dSChao Yu int f2fs_recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) 859d624c96fSJaegeuk Kim { 86026b5a079SSheng Yong struct list_head inode_list, tmp_inode_list; 861f61cce5bSChao Yu struct list_head dir_list; 8626ead1142SJaegeuk Kim int err; 8636781eabbSJaegeuk Kim int ret = 0; 8644b2414d0SChao Yu unsigned long s_flags = sbi->sb->s_flags; 865aabe5136SHaicheng Li bool need_writecp = false; 866c426d991SShin'ichiro Kawasaki bool fix_curseg_write_pointer = false; 867d624c96fSJaegeuk Kim 868e1bb7d3dSChao Yu if (is_sbi_flag_set(sbi, SBI_IS_WRITABLE)) 869dcbb4c10SJoe Perches f2fs_info(sbi, "recover fsync data on readonly fs"); 8704b2414d0SChao Yu 871d624c96fSJaegeuk Kim INIT_LIST_HEAD(&inode_list); 87226b5a079SSheng Yong INIT_LIST_HEAD(&tmp_inode_list); 873f61cce5bSChao Yu INIT_LIST_HEAD(&dir_list); 874d624c96fSJaegeuk Kim 87514f4e690SJaegeuk Kim /* prevent checkpoint */ 876e4544b63STim Murray f2fs_down_write(&sbi->cp_global_sem); 87714f4e690SJaegeuk Kim 878315df839SJaegeuk Kim /* step #1: find fsynced inode numbers */ 879d40d30c5SJaegeuk Kim err = find_fsync_dnodes(sbi, &inode_list, check_only); 8806781eabbSJaegeuk Kim if (err || list_empty(&inode_list)) 8814b2414d0SChao Yu goto skip; 882d624c96fSJaegeuk Kim 8836781eabbSJaegeuk Kim if (check_only) { 8846781eabbSJaegeuk Kim ret = 1; 8854b2414d0SChao Yu goto skip; 8866781eabbSJaegeuk Kim } 887d624c96fSJaegeuk Kim 888aabe5136SHaicheng Li need_writecp = true; 889691c6fd2SChao Yu 890d624c96fSJaegeuk Kim /* step #2: recover data */ 89126b5a079SSheng Yong err = recover_data(sbi, &inode_list, &tmp_inode_list, &dir_list); 892b307384eSJaegeuk Kim if (!err) 8939850cf4aSJaegeuk Kim f2fs_bug_on(sbi, !list_empty(&inode_list)); 894c02599f2SChao Yu else 895c02599f2SChao Yu f2fs_bug_on(sbi, sbi->sb->s_flags & SB_ACTIVE); 8964b2414d0SChao Yu skip: 897c426d991SShin'ichiro Kawasaki fix_curseg_write_pointer = !check_only || list_empty(&inode_list); 898c426d991SShin'ichiro Kawasaki 89926b5a079SSheng Yong destroy_fsync_dnodes(&inode_list, err); 90026b5a079SSheng Yong destroy_fsync_dnodes(&tmp_inode_list, err); 901cf2271e7SJaegeuk Kim 9024c521f49SJaegeuk Kim /* truncate meta pages to be used by the recovery */ 9034c521f49SJaegeuk Kim truncate_inode_pages_range(META_MAPPING(sbi), 90409cbfeafSKirill A. Shutemov (loff_t)MAIN_BLKADDR(sbi) << PAGE_SHIFT, -1); 9054c521f49SJaegeuk Kim 906cf2271e7SJaegeuk Kim if (err) { 907cf2271e7SJaegeuk Kim truncate_inode_pages_final(NODE_MAPPING(sbi)); 908cf2271e7SJaegeuk Kim truncate_inode_pages_final(META_MAPPING(sbi)); 90926b5a079SSheng Yong } 910c426d991SShin'ichiro Kawasaki 911c426d991SShin'ichiro Kawasaki /* 912c426d991SShin'ichiro Kawasaki * If fsync data succeeds or there is no fsync data to recover, 913c426d991SShin'ichiro Kawasaki * and the f2fs is not read only, check and fix zoned block devices' 914c426d991SShin'ichiro Kawasaki * write pointer consistency. 915c426d991SShin'ichiro Kawasaki */ 916c426d991SShin'ichiro Kawasaki if (!err && fix_curseg_write_pointer && !f2fs_readonly(sbi->sb) && 917c426d991SShin'ichiro Kawasaki f2fs_sb_has_blkzoned(sbi)) { 918c426d991SShin'ichiro Kawasaki err = f2fs_fix_curseg_write_pointer(sbi); 9198b981e78SJaegeuk Kim if (!err) 9208b981e78SJaegeuk Kim err = f2fs_check_write_pointer(sbi); 921c426d991SShin'ichiro Kawasaki ret = err; 922c426d991SShin'ichiro Kawasaki } 923c426d991SShin'ichiro Kawasaki 924c426d991SShin'ichiro Kawasaki if (!err) 925c426d991SShin'ichiro Kawasaki clear_sbi_flag(sbi, SBI_POR_DOING); 926c426d991SShin'ichiro Kawasaki 927e4544b63STim Murray f2fs_up_write(&sbi->cp_global_sem); 928a468f0efSJaegeuk Kim 9299e1e6df4SJaegeuk Kim /* let's drop all the directory inodes for clean checkpoint */ 93026b5a079SSheng Yong destroy_fsync_dnodes(&dir_list, err); 9319e1e6df4SJaegeuk Kim 9321378752bSChao Yu if (need_writecp) { 9331378752bSChao Yu set_sbi_flag(sbi, SBI_IS_RECOVERED); 9341378752bSChao Yu 9351378752bSChao Yu if (!err) { 93675ab4cb8SJaegeuk Kim struct cp_control cpc = { 93710027551SJaegeuk Kim .reason = CP_RECOVERY, 93875ab4cb8SJaegeuk Kim }; 939eb61c2ccSChao Yu stat_inc_cp_call_count(sbi, TOTAL_CALL); 9404d57b86dSChao Yu err = f2fs_write_checkpoint(sbi, &cpc); 941cf2271e7SJaegeuk Kim } 9421378752bSChao Yu } 943f61cce5bSChao Yu 9441751e8a6SLinus Torvalds sbi->sb->s_flags = s_flags; /* Restore SB_RDONLY status */ 9454b2414d0SChao Yu 9466781eabbSJaegeuk Kim return ret ? ret : err; 947d624c96fSJaegeuk Kim } 948cad83c96SChao Yu 949cad83c96SChao Yu int __init f2fs_create_recovery_cache(void) 950cad83c96SChao Yu { 951cad83c96SChao Yu fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry", 952cad83c96SChao Yu sizeof(struct fsync_inode_entry)); 953870af777SYangtao Li return fsync_entry_slab ? 0 : -ENOMEM; 954cad83c96SChao Yu } 955cad83c96SChao Yu 956cad83c96SChao Yu void f2fs_destroy_recovery_cache(void) 957cad83c96SChao Yu { 958cad83c96SChao Yu kmem_cache_destroy(fsync_entry_slab); 959cad83c96SChao Yu } 960