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 261b27c82e1SChristian Brauner if (!vfsuid_eq(attr.ia_vfsuid, i_uid_into_vfsuid(&init_user_ns, inode))) 262af033b2aSChao Yu attr.ia_valid |= ATTR_UID; 263b27c82e1SChristian Brauner if (!vfsgid_eq(attr.ia_vfsgid, i_gid_into_vfsgid(&init_user_ns, inode))) 264af033b2aSChao Yu attr.ia_valid |= ATTR_GID; 265af033b2aSChao Yu 266af033b2aSChao Yu if (!attr.ia_valid) 267af033b2aSChao Yu return 0; 268af033b2aSChao Yu 26971e7b535SChristian Brauner err = dquot_transfer(&init_user_ns, 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); 324441ac5cbSJaegeuk Kim inode->i_ctime.tv_sec = le64_to_cpu(raw->i_ctime); 325441ac5cbSJaegeuk Kim inode->i_mtime.tv_sec = le64_to_cpu(raw->i_mtime); 3269f0552e0SChao Yu inode->i_atime.tv_nsec = le32_to_cpu(raw->i_atime_nsec); 327441ac5cbSJaegeuk Kim inode->i_ctime.tv_nsec = le32_to_cpu(raw->i_ctime_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 363d40d30c5SJaegeuk Kim static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head, 364d40d30c5SJaegeuk Kim bool check_only) 365d624c96fSJaegeuk Kim { 366d624c96fSJaegeuk Kim struct curseg_info *curseg; 3674c521f49SJaegeuk Kim struct page *page = NULL; 368d624c96fSJaegeuk Kim block_t blkaddr; 369fb0e72c8SChao Yu unsigned int loop_cnt = 0; 370430f163bSChao Yu unsigned int ra_blocks = RECOVERY_MAX_RA_BLOCKS; 37182902c06SChao Yu unsigned int free_blocks = MAIN_SEGS(sbi) * sbi->blocks_per_seg - 372fb0e72c8SChao Yu valid_user_blocks(sbi); 373d624c96fSJaegeuk Kim int err = 0; 374d624c96fSJaegeuk Kim 375d624c96fSJaegeuk Kim /* get node pages in the current segment */ 376d624c96fSJaegeuk Kim curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); 377695fd1edSChao Yu blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); 378d624c96fSJaegeuk Kim 379d624c96fSJaegeuk Kim while (1) { 380d624c96fSJaegeuk Kim struct fsync_inode_entry *entry; 381d624c96fSJaegeuk Kim 382e1da7872SChao Yu if (!f2fs_is_valid_blkaddr(sbi, blkaddr, META_POR)) 3834c521f49SJaegeuk Kim return 0; 384d624c96fSJaegeuk Kim 3854d57b86dSChao Yu page = f2fs_get_tmp_page(sbi, blkaddr); 3867735730dSChao Yu if (IS_ERR(page)) { 3877735730dSChao Yu err = PTR_ERR(page); 3887735730dSChao Yu break; 3897735730dSChao Yu } 390393ff91fSJaegeuk Kim 39198838579SChao Yu if (!is_recoverable_dnode(page)) { 39298838579SChao Yu f2fs_put_page(page, 1); 393f356fe0cSJaegeuk Kim break; 39498838579SChao Yu } 395d624c96fSJaegeuk Kim 396d624c96fSJaegeuk Kim if (!is_fsync_dnode(page)) 397d624c96fSJaegeuk Kim goto next; 398d624c96fSJaegeuk Kim 399d624c96fSJaegeuk Kim entry = get_fsync_inode(head, ino_of_node(page)); 400d47b8715SChao Yu if (!entry) { 4014b2414d0SChao Yu bool quota_inode = false; 4024b2414d0SChao Yu 403d40d30c5SJaegeuk Kim if (!check_only && 404d40d30c5SJaegeuk Kim IS_INODE(page) && is_dent_dnode(page)) { 4054d57b86dSChao Yu err = f2fs_recover_inode_page(sbi, page); 40698838579SChao Yu if (err) { 40798838579SChao Yu f2fs_put_page(page, 1); 408f356fe0cSJaegeuk Kim break; 40998838579SChao Yu } 4104b2414d0SChao Yu quota_inode = true; 411d624c96fSJaegeuk Kim } 412d624c96fSJaegeuk Kim 413441ac5cbSJaegeuk Kim /* 414441ac5cbSJaegeuk Kim * CP | dnode(F) | inode(DF) 415441ac5cbSJaegeuk Kim * For this case, we should not give up now. 416441ac5cbSJaegeuk Kim */ 4174b2414d0SChao Yu entry = add_fsync_inode(sbi, head, ino_of_node(page), 4184b2414d0SChao Yu quota_inode); 419f4702d61SJaegeuk Kim if (IS_ERR(entry)) { 420f4702d61SJaegeuk Kim err = PTR_ERR(entry); 4218fbc418fSJaegeuk Kim if (err == -ENOENT) { 4228fbc418fSJaegeuk Kim err = 0; 423441ac5cbSJaegeuk Kim goto next; 4248fbc418fSJaegeuk Kim } 42598838579SChao Yu f2fs_put_page(page, 1); 426f356fe0cSJaegeuk Kim break; 427d624c96fSJaegeuk Kim } 428d624c96fSJaegeuk Kim } 429addbe45bSJaegeuk Kim entry->blkaddr = blkaddr; 430addbe45bSJaegeuk Kim 431608514deSJaegeuk Kim if (IS_INODE(page) && is_dent_dnode(page)) 432c52e1b10SJaegeuk Kim entry->last_dentry = blkaddr; 433d624c96fSJaegeuk Kim next: 434fb0e72c8SChao Yu /* sanity check in order to detect looped node chain */ 435fb0e72c8SChao Yu if (++loop_cnt >= free_blocks || 436fb0e72c8SChao Yu blkaddr == next_blkaddr_of_node(page)) { 437dcbb4c10SJoe Perches f2fs_notice(sbi, "%s: detect looped node chain, blkaddr:%u, next:%u", 438dcbb4c10SJoe Perches __func__, blkaddr, 439dcbb4c10SJoe Perches next_blkaddr_of_node(page)); 44098838579SChao Yu f2fs_put_page(page, 1); 441fb0e72c8SChao Yu err = -EINVAL; 442fb0e72c8SChao Yu break; 443fb0e72c8SChao Yu } 444fb0e72c8SChao Yu 445430f163bSChao Yu ra_blocks = adjust_por_ra_blocks(sbi, ra_blocks, blkaddr, 446430f163bSChao Yu next_blkaddr_of_node(page)); 447430f163bSChao Yu 448d624c96fSJaegeuk Kim /* check next segment */ 449d624c96fSJaegeuk Kim blkaddr = next_blkaddr_of_node(page); 4504c521f49SJaegeuk Kim f2fs_put_page(page, 1); 451635aee1fSChao Yu 452430f163bSChao Yu f2fs_ra_meta_pages_cond(sbi, blkaddr, ra_blocks); 453d624c96fSJaegeuk Kim } 454d624c96fSJaegeuk Kim return err; 455d624c96fSJaegeuk Kim } 456d624c96fSJaegeuk Kim 45726b5a079SSheng Yong static void destroy_fsync_dnodes(struct list_head *head, int drop) 458d624c96fSJaegeuk Kim { 459d8b79b2fSDan Carpenter struct fsync_inode_entry *entry, *tmp; 460d8b79b2fSDan Carpenter 4613f8ab270SChao Yu list_for_each_entry_safe(entry, tmp, head, list) 46226b5a079SSheng Yong del_fsync_inode(entry, drop); 463d624c96fSJaegeuk Kim } 464d624c96fSJaegeuk Kim 46539cf72cfSJaegeuk Kim static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi, 466b292dcabSJaegeuk Kim block_t blkaddr, struct dnode_of_data *dn) 467d624c96fSJaegeuk Kim { 468d624c96fSJaegeuk Kim struct seg_entry *sentry; 469d624c96fSJaegeuk Kim unsigned int segno = GET_SEGNO(sbi, blkaddr); 470491c0854SJaegeuk Kim unsigned short blkoff = GET_BLKOFF_FROM_SEG0(sbi, blkaddr); 471f6517cfcSJaegeuk Kim struct f2fs_summary_block *sum_node; 472d624c96fSJaegeuk Kim struct f2fs_summary sum; 473f6517cfcSJaegeuk Kim struct page *sum_page, *node_page; 474c9ef4810SJaegeuk Kim struct dnode_of_data tdn = *dn; 475b292dcabSJaegeuk Kim nid_t ino, nid; 476d624c96fSJaegeuk Kim struct inode *inode; 477de93653fSJaegeuk Kim unsigned int offset; 478d624c96fSJaegeuk Kim block_t bidx; 479d624c96fSJaegeuk Kim int i; 480d624c96fSJaegeuk Kim 481d624c96fSJaegeuk Kim sentry = get_seg_entry(sbi, segno); 482d624c96fSJaegeuk Kim if (!f2fs_test_bit(blkoff, sentry->cur_valid_map)) 48339cf72cfSJaegeuk Kim return 0; 484d624c96fSJaegeuk Kim 485d624c96fSJaegeuk Kim /* Get the previous summary */ 486125c9fb1SJaegeuk Kim for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { 487d624c96fSJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, i); 4885f029c04SYi Zhuang 489d624c96fSJaegeuk Kim if (curseg->segno == segno) { 490d624c96fSJaegeuk Kim sum = curseg->sum_blk->entries[blkoff]; 491f6517cfcSJaegeuk Kim goto got_it; 492d624c96fSJaegeuk Kim } 493d624c96fSJaegeuk Kim } 494d624c96fSJaegeuk Kim 4954d57b86dSChao Yu sum_page = f2fs_get_sum_page(sbi, segno); 496edc55aafSJaegeuk Kim if (IS_ERR(sum_page)) 497edc55aafSJaegeuk Kim return PTR_ERR(sum_page); 498f6517cfcSJaegeuk Kim sum_node = (struct f2fs_summary_block *)page_address(sum_page); 499f6517cfcSJaegeuk Kim sum = sum_node->entries[blkoff]; 500f6517cfcSJaegeuk Kim f2fs_put_page(sum_page, 1); 501f6517cfcSJaegeuk Kim got_it: 502b292dcabSJaegeuk Kim /* Use the locked dnode page and inode */ 503b292dcabSJaegeuk Kim nid = le32_to_cpu(sum.nid); 504b292dcabSJaegeuk Kim if (dn->inode->i_ino == nid) { 505b292dcabSJaegeuk Kim tdn.nid = nid; 506c9ef4810SJaegeuk Kim if (!dn->inode_page_locked) 507c9ef4810SJaegeuk Kim lock_page(dn->inode_page); 508b292dcabSJaegeuk Kim tdn.node_page = dn->inode_page; 509060dd67bSJaegeuk Kim tdn.ofs_in_node = le16_to_cpu(sum.ofs_in_node); 510c9ef4810SJaegeuk Kim goto truncate_out; 511b292dcabSJaegeuk Kim } else if (dn->nid == nid) { 512060dd67bSJaegeuk Kim tdn.ofs_in_node = le16_to_cpu(sum.ofs_in_node); 513c9ef4810SJaegeuk Kim goto truncate_out; 514b292dcabSJaegeuk Kim } 515b292dcabSJaegeuk Kim 516d624c96fSJaegeuk Kim /* Get the node page */ 5174d57b86dSChao Yu node_page = f2fs_get_node_page(sbi, nid); 51839cf72cfSJaegeuk Kim if (IS_ERR(node_page)) 51939cf72cfSJaegeuk Kim return PTR_ERR(node_page); 520de93653fSJaegeuk Kim 521de93653fSJaegeuk Kim offset = ofs_of_node(node_page); 522d624c96fSJaegeuk Kim ino = ino_of_node(node_page); 523d624c96fSJaegeuk Kim f2fs_put_page(node_page, 1); 524d624c96fSJaegeuk Kim 52560979115SJaegeuk Kim if (ino != dn->inode->i_ino) { 5264b2414d0SChao Yu int ret; 5274b2414d0SChao Yu 528d624c96fSJaegeuk Kim /* Deallocate previous index in the node page */ 529e8ea9b3dSJaegeuk Kim inode = f2fs_iget_retry(sbi->sb, ino); 53006025f4dSNamjae Jeon if (IS_ERR(inode)) 53139cf72cfSJaegeuk Kim return PTR_ERR(inode); 5324b2414d0SChao Yu 53310a26878SChao Yu ret = f2fs_dquot_initialize(inode); 5344b2414d0SChao Yu if (ret) { 5354b2414d0SChao Yu iput(inode); 5364b2414d0SChao Yu return ret; 5374b2414d0SChao Yu } 53860979115SJaegeuk Kim } else { 53960979115SJaegeuk Kim inode = dn->inode; 54060979115SJaegeuk Kim } 54106025f4dSNamjae Jeon 5424d57b86dSChao Yu bidx = f2fs_start_bidx_of_node(offset, inode) + 5434d57b86dSChao Yu le16_to_cpu(sum.ofs_in_node); 544de93653fSJaegeuk Kim 545c9ef4810SJaegeuk Kim /* 546c9ef4810SJaegeuk Kim * if inode page is locked, unlock temporarily, but its reference 547c9ef4810SJaegeuk Kim * count keeps alive. 548c9ef4810SJaegeuk Kim */ 549c9ef4810SJaegeuk Kim if (ino == dn->inode->i_ino && dn->inode_page_locked) 550c9ef4810SJaegeuk Kim unlock_page(dn->inode_page); 551c9ef4810SJaegeuk Kim 552c9ef4810SJaegeuk Kim set_new_dnode(&tdn, inode, NULL, NULL, 0); 5534d57b86dSChao Yu if (f2fs_get_dnode_of_data(&tdn, bidx, LOOKUP_NODE)) 554c9ef4810SJaegeuk Kim goto out; 555c9ef4810SJaegeuk Kim 556c9ef4810SJaegeuk Kim if (tdn.data_blkaddr == blkaddr) 5574d57b86dSChao Yu f2fs_truncate_data_blocks_range(&tdn, 1); 558c9ef4810SJaegeuk Kim 559c9ef4810SJaegeuk Kim f2fs_put_dnode(&tdn); 560c9ef4810SJaegeuk Kim out: 561c9ef4810SJaegeuk Kim if (ino != dn->inode->i_ino) 562c9ef4810SJaegeuk Kim iput(inode); 563c9ef4810SJaegeuk Kim else if (dn->inode_page_locked) 564c9ef4810SJaegeuk Kim lock_page(dn->inode_page); 565c9ef4810SJaegeuk Kim return 0; 566c9ef4810SJaegeuk Kim 567c9ef4810SJaegeuk Kim truncate_out: 568a2ced1ceSChao Yu if (f2fs_data_blkaddr(&tdn) == blkaddr) 5694d57b86dSChao Yu f2fs_truncate_data_blocks_range(&tdn, 1); 570c9ef4810SJaegeuk Kim if (dn->inode->i_ino == nid && !dn->inode_page_locked) 571c9ef4810SJaegeuk Kim unlock_page(dn->inode_page); 57239cf72cfSJaegeuk Kim return 0; 573d624c96fSJaegeuk Kim } 574d624c96fSJaegeuk Kim 5756ead1142SJaegeuk Kim static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, 576e17d488bSSheng Yong struct page *page) 577d624c96fSJaegeuk Kim { 578d624c96fSJaegeuk Kim struct dnode_of_data dn; 579d624c96fSJaegeuk Kim struct node_info ni; 58081ca7350SChao Yu unsigned int start, end; 581f356fe0cSJaegeuk Kim int err = 0, recovered = 0; 582d624c96fSJaegeuk Kim 5831c35a90eSJaegeuk Kim /* step 1: recover xattr */ 5841c35a90eSJaegeuk Kim if (IS_INODE(page)) { 5859627a7b3SChao Yu err = f2fs_recover_inline_xattr(inode, page); 5869627a7b3SChao Yu if (err) 5879627a7b3SChao Yu goto out; 5881c35a90eSJaegeuk Kim } else if (f2fs_has_xattr_block(ofs_of_node(page))) { 5894d57b86dSChao Yu err = f2fs_recover_xattr_data(inode, page); 590d260081cSChao Yu if (!err) 591d260081cSChao Yu recovered++; 5921c35a90eSJaegeuk Kim goto out; 5931c35a90eSJaegeuk Kim } 59470cfed88SChao Yu 5951c35a90eSJaegeuk Kim /* step 2: recover inline data */ 5969627a7b3SChao Yu err = f2fs_recover_inline_data(inode, page); 5979627a7b3SChao Yu if (err) { 5989627a7b3SChao Yu if (err == 1) 5999627a7b3SChao Yu err = 0; 6001e1bb4baSJaegeuk Kim goto out; 6019627a7b3SChao Yu } 6021e1bb4baSJaegeuk Kim 6031c35a90eSJaegeuk Kim /* step 3: recover data indices */ 6044d57b86dSChao Yu start = f2fs_start_bidx_of_node(ofs_of_node(page), inode); 60581ca7350SChao Yu end = start + ADDRS_PER_PAGE(page, inode); 606d624c96fSJaegeuk Kim 607d624c96fSJaegeuk Kim set_new_dnode(&dn, inode, NULL, NULL, 0); 608e8ea9b3dSJaegeuk Kim retry_dn: 6094d57b86dSChao Yu err = f2fs_get_dnode_of_data(&dn, start, ALLOC_NODE); 610e8ea9b3dSJaegeuk Kim if (err) { 611e8ea9b3dSJaegeuk Kim if (err == -ENOMEM) { 6124034247aSNeilBrown memalloc_retry_wait(GFP_NOFS); 613e8ea9b3dSJaegeuk Kim goto retry_dn; 614e8ea9b3dSJaegeuk Kim } 6151e1bb4baSJaegeuk Kim goto out; 616e8ea9b3dSJaegeuk Kim } 617d624c96fSJaegeuk Kim 618bae0ee7aSChao Yu f2fs_wait_on_page_writeback(dn.node_page, NODE, true, true); 619d624c96fSJaegeuk Kim 620a9419b63SJaegeuk Kim err = f2fs_get_node_info(sbi, dn.nid, &ni, false); 6217735730dSChao Yu if (err) 6227735730dSChao Yu goto err; 6237735730dSChao Yu 6249850cf4aSJaegeuk Kim f2fs_bug_on(sbi, ni.ino != ino_of_node(page)); 62522d61e28SChao Yu 62622d61e28SChao Yu if (ofs_of_node(dn.node_page) != ofs_of_node(page)) { 627dcbb4c10SJoe Perches f2fs_warn(sbi, "Inconsistent ofs_of_node, ino:%lu, ofs:%u, %u", 62822d61e28SChao Yu inode->i_ino, ofs_of_node(dn.node_page), 62922d61e28SChao Yu ofs_of_node(page)); 63010f966bbSChao Yu err = -EFSCORRUPTED; 63122d61e28SChao Yu goto err; 63222d61e28SChao Yu } 633d624c96fSJaegeuk Kim 63412a8343eSChao Yu for (; start < end; start++, dn.ofs_in_node++) { 635d624c96fSJaegeuk Kim block_t src, dest; 636d624c96fSJaegeuk Kim 637a2ced1ceSChao Yu src = f2fs_data_blkaddr(&dn); 638a2ced1ceSChao Yu dest = data_blkaddr(dn.inode, page, dn.ofs_in_node); 639d624c96fSJaegeuk Kim 64093770ab7SChao Yu if (__is_valid_data_blkaddr(src) && 64193770ab7SChao Yu !f2fs_is_valid_blkaddr(sbi, src, META_POR)) { 64210f966bbSChao Yu err = -EFSCORRUPTED; 64393770ab7SChao Yu goto err; 64493770ab7SChao Yu } 64593770ab7SChao Yu 64693770ab7SChao Yu if (__is_valid_data_blkaddr(dest) && 64793770ab7SChao Yu !f2fs_is_valid_blkaddr(sbi, dest, META_POR)) { 64810f966bbSChao Yu err = -EFSCORRUPTED; 64993770ab7SChao Yu goto err; 65093770ab7SChao Yu } 65193770ab7SChao Yu 65212a8343eSChao Yu /* skip recovering if dest is the same as src */ 65312a8343eSChao Yu if (src == dest) 65412a8343eSChao Yu continue; 65512a8343eSChao Yu 65612a8343eSChao Yu /* dest is invalid, just invalidate src block */ 65712a8343eSChao Yu if (dest == NULL_ADDR) { 6584d57b86dSChao Yu f2fs_truncate_data_blocks_range(&dn, 1); 65912a8343eSChao Yu continue; 66012a8343eSChao Yu } 66112a8343eSChao Yu 66226787236SJaegeuk Kim if (!file_keep_isize(inode) && 663dba79f38SChao Yu (i_size_read(inode) <= ((loff_t)start << PAGE_SHIFT))) 664dba79f38SChao Yu f2fs_i_size_write(inode, 665dba79f38SChao Yu (loff_t)(start + 1) << PAGE_SHIFT); 66626de9b11SJaegeuk Kim 66712a8343eSChao Yu /* 66812a8343eSChao Yu * dest is reserved block, invalidate src block 66912a8343eSChao Yu * and then reserve one new block in dnode page. 67012a8343eSChao Yu */ 67112a8343eSChao Yu if (dest == NEW_ADDR) { 6724d57b86dSChao Yu f2fs_truncate_data_blocks_range(&dn, 1); 6734d57b86dSChao Yu f2fs_reserve_new_block(&dn); 67412a8343eSChao Yu continue; 67512a8343eSChao Yu } 67612a8343eSChao Yu 67712a8343eSChao Yu /* dest is valid block, try to recover from src to dest */ 678e1da7872SChao Yu if (f2fs_is_valid_blkaddr(sbi, dest, META_POR)) { 679e03b07d9SJaegeuk Kim 680d624c96fSJaegeuk Kim if (src == NULL_ADDR) { 6814d57b86dSChao Yu err = f2fs_reserve_new_block(&dn); 6827fa750a1SArnd Bergmann while (err && 6837fa750a1SArnd Bergmann IS_ENABLED(CONFIG_F2FS_FAULT_INJECTION)) 6844d57b86dSChao Yu err = f2fs_reserve_new_block(&dn); 685d624c96fSJaegeuk Kim /* We should not get -ENOSPC */ 6869850cf4aSJaegeuk Kim f2fs_bug_on(sbi, err); 6876f3ec995SJaegeuk Kim if (err) 6886f3ec995SJaegeuk Kim goto err; 689d624c96fSJaegeuk Kim } 690e8ea9b3dSJaegeuk Kim retry_prev: 691d624c96fSJaegeuk Kim /* Check the previous node page having this index */ 69239cf72cfSJaegeuk Kim err = check_index_in_prev_nodes(sbi, dest, &dn); 693e8ea9b3dSJaegeuk Kim if (err) { 694e8ea9b3dSJaegeuk Kim if (err == -ENOMEM) { 6954034247aSNeilBrown memalloc_retry_wait(GFP_NOFS); 696e8ea9b3dSJaegeuk Kim goto retry_prev; 697e8ea9b3dSJaegeuk Kim } 69839cf72cfSJaegeuk Kim goto err; 699e8ea9b3dSJaegeuk Kim } 700d624c96fSJaegeuk Kim 701*0ef4ca04SChao Yu if (f2fs_is_valid_blkaddr(sbi, dest, 702*0ef4ca04SChao Yu DATA_GENERIC_ENHANCE_UPDATE)) { 703*0ef4ca04SChao Yu f2fs_err(sbi, "Inconsistent dest blkaddr:%u, ino:%lu, ofs:%u", 704*0ef4ca04SChao Yu dest, inode->i_ino, dn.ofs_in_node); 705*0ef4ca04SChao Yu err = -EFSCORRUPTED; 706*0ef4ca04SChao Yu goto err; 707*0ef4ca04SChao Yu } 708*0ef4ca04SChao Yu 709d624c96fSJaegeuk Kim /* write dummy data page */ 710528e3459SChao Yu f2fs_replace_block(sbi, &dn, src, dest, 71128bc106bSChao Yu ni.version, false, false); 712f356fe0cSJaegeuk Kim recovered++; 713d624c96fSJaegeuk Kim } 714d624c96fSJaegeuk Kim } 715d624c96fSJaegeuk Kim 716d624c96fSJaegeuk Kim copy_node_footer(dn.node_page, page); 717d624c96fSJaegeuk Kim fill_node_footer(dn.node_page, dn.nid, ni.ino, 718d624c96fSJaegeuk Kim ofs_of_node(page), false); 719d624c96fSJaegeuk Kim set_page_dirty(dn.node_page); 72039cf72cfSJaegeuk Kim err: 721d624c96fSJaegeuk Kim f2fs_put_dnode(&dn); 7221e1bb4baSJaegeuk Kim out: 723dcbb4c10SJoe Perches f2fs_notice(sbi, "recover_data: ino = %lx (i_size: %s) recovered = %d, err = %d", 724dcbb4c10SJoe Perches inode->i_ino, file_keep_isize(inode) ? "keep" : "recover", 72526787236SJaegeuk Kim recovered, err); 72639cf72cfSJaegeuk Kim return err; 727d624c96fSJaegeuk Kim } 728d624c96fSJaegeuk Kim 729f61cce5bSChao Yu static int recover_data(struct f2fs_sb_info *sbi, struct list_head *inode_list, 73026b5a079SSheng Yong struct list_head *tmp_inode_list, struct list_head *dir_list) 731d624c96fSJaegeuk Kim { 732d624c96fSJaegeuk Kim struct curseg_info *curseg; 7334c521f49SJaegeuk Kim struct page *page = NULL; 7346ead1142SJaegeuk Kim int err = 0; 735d624c96fSJaegeuk Kim block_t blkaddr; 736430f163bSChao Yu unsigned int ra_blocks = RECOVERY_MAX_RA_BLOCKS; 737d624c96fSJaegeuk Kim 738d624c96fSJaegeuk Kim /* get node pages in the current segment */ 739b7973f23SChao Yu curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); 740d624c96fSJaegeuk Kim blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); 741d624c96fSJaegeuk Kim 742d624c96fSJaegeuk Kim while (1) { 743d624c96fSJaegeuk Kim struct fsync_inode_entry *entry; 744d624c96fSJaegeuk Kim 745e1da7872SChao Yu if (!f2fs_is_valid_blkaddr(sbi, blkaddr, META_POR)) 74645856affSJaegeuk Kim break; 747d624c96fSJaegeuk Kim 7484d57b86dSChao Yu page = f2fs_get_tmp_page(sbi, blkaddr); 7497735730dSChao Yu if (IS_ERR(page)) { 7507735730dSChao Yu err = PTR_ERR(page); 7517735730dSChao Yu break; 7527735730dSChao Yu } 7534c521f49SJaegeuk Kim 754a468f0efSJaegeuk Kim if (!is_recoverable_dnode(page)) { 7554c521f49SJaegeuk Kim f2fs_put_page(page, 1); 7564c521f49SJaegeuk Kim break; 7574c521f49SJaegeuk Kim } 7584c521f49SJaegeuk Kim 759f61cce5bSChao Yu entry = get_fsync_inode(inode_list, ino_of_node(page)); 760d624c96fSJaegeuk Kim if (!entry) 761d624c96fSJaegeuk Kim goto next; 762441ac5cbSJaegeuk Kim /* 763441ac5cbSJaegeuk Kim * inode(x) | CP | inode(x) | dnode(F) 764441ac5cbSJaegeuk Kim * In this case, we can lose the latest inode(x). 765c52e1b10SJaegeuk Kim * So, call recover_inode for the inode update. 766441ac5cbSJaegeuk Kim */ 767af033b2aSChao Yu if (IS_INODE(page)) { 768af033b2aSChao Yu err = recover_inode(entry->inode, page); 76998838579SChao Yu if (err) { 77098838579SChao Yu f2fs_put_page(page, 1); 771af033b2aSChao Yu break; 772af033b2aSChao Yu } 77398838579SChao Yu } 774c52e1b10SJaegeuk Kim if (entry->last_dentry == blkaddr) { 775f61cce5bSChao Yu err = recover_dentry(entry->inode, page, dir_list); 776c52e1b10SJaegeuk Kim if (err) { 777c52e1b10SJaegeuk Kim f2fs_put_page(page, 1); 778c52e1b10SJaegeuk Kim break; 779c52e1b10SJaegeuk Kim } 780c52e1b10SJaegeuk Kim } 781e17d488bSSheng Yong err = do_recover_data(sbi, entry->inode, page); 7824c521f49SJaegeuk Kim if (err) { 7834c521f49SJaegeuk Kim f2fs_put_page(page, 1); 78445856affSJaegeuk Kim break; 7854c521f49SJaegeuk Kim } 786d624c96fSJaegeuk Kim 7873f8ab270SChao Yu if (entry->blkaddr == blkaddr) 78826b5a079SSheng Yong list_move_tail(&entry->list, tmp_inode_list); 789d624c96fSJaegeuk Kim next: 790430f163bSChao Yu ra_blocks = adjust_por_ra_blocks(sbi, ra_blocks, blkaddr, 791430f163bSChao Yu next_blkaddr_of_node(page)); 792430f163bSChao Yu 793d624c96fSJaegeuk Kim /* check next segment */ 794d624c96fSJaegeuk Kim blkaddr = next_blkaddr_of_node(page); 7954c521f49SJaegeuk Kim f2fs_put_page(page, 1); 796430f163bSChao Yu 797430f163bSChao Yu f2fs_ra_meta_pages_cond(sbi, blkaddr, ra_blocks); 798d624c96fSJaegeuk Kim } 7996ead1142SJaegeuk Kim if (!err) 800901d745fSChao Yu f2fs_allocate_new_segments(sbi); 8016ead1142SJaegeuk Kim return err; 802d624c96fSJaegeuk Kim } 803d624c96fSJaegeuk Kim 8044d57b86dSChao Yu int f2fs_recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) 805d624c96fSJaegeuk Kim { 80626b5a079SSheng Yong struct list_head inode_list, tmp_inode_list; 807f61cce5bSChao Yu struct list_head dir_list; 8086ead1142SJaegeuk Kim int err; 8096781eabbSJaegeuk Kim int ret = 0; 8104b2414d0SChao Yu unsigned long s_flags = sbi->sb->s_flags; 811aabe5136SHaicheng Li bool need_writecp = false; 812c426d991SShin'ichiro Kawasaki bool fix_curseg_write_pointer = false; 813ea676733SJaegeuk Kim #ifdef CONFIG_QUOTA 814ea676733SJaegeuk Kim int quota_enabled; 815ea676733SJaegeuk Kim #endif 816d624c96fSJaegeuk Kim 8171751e8a6SLinus Torvalds if (s_flags & SB_RDONLY) { 818dcbb4c10SJoe Perches f2fs_info(sbi, "recover fsync data on readonly fs"); 8191751e8a6SLinus Torvalds sbi->sb->s_flags &= ~SB_RDONLY; 8204b2414d0SChao Yu } 8214b2414d0SChao Yu 8224b2414d0SChao Yu #ifdef CONFIG_QUOTA 8234b2414d0SChao Yu /* Turn on quotas so that they are updated correctly */ 8241751e8a6SLinus Torvalds quota_enabled = f2fs_enable_quota_files(sbi, s_flags & SB_RDONLY); 8254b2414d0SChao Yu #endif 8264b2414d0SChao Yu 827d624c96fSJaegeuk Kim INIT_LIST_HEAD(&inode_list); 82826b5a079SSheng Yong INIT_LIST_HEAD(&tmp_inode_list); 829f61cce5bSChao Yu INIT_LIST_HEAD(&dir_list); 830d624c96fSJaegeuk Kim 83114f4e690SJaegeuk Kim /* prevent checkpoint */ 832e4544b63STim Murray f2fs_down_write(&sbi->cp_global_sem); 83314f4e690SJaegeuk Kim 834315df839SJaegeuk Kim /* step #1: find fsynced inode numbers */ 835d40d30c5SJaegeuk Kim err = find_fsync_dnodes(sbi, &inode_list, check_only); 8366781eabbSJaegeuk Kim if (err || list_empty(&inode_list)) 8374b2414d0SChao Yu goto skip; 838d624c96fSJaegeuk Kim 8396781eabbSJaegeuk Kim if (check_only) { 8406781eabbSJaegeuk Kim ret = 1; 8414b2414d0SChao Yu goto skip; 8426781eabbSJaegeuk Kim } 843d624c96fSJaegeuk Kim 844aabe5136SHaicheng Li need_writecp = true; 845691c6fd2SChao Yu 846d624c96fSJaegeuk Kim /* step #2: recover data */ 84726b5a079SSheng Yong err = recover_data(sbi, &inode_list, &tmp_inode_list, &dir_list); 848b307384eSJaegeuk Kim if (!err) 8499850cf4aSJaegeuk Kim f2fs_bug_on(sbi, !list_empty(&inode_list)); 850c02599f2SChao Yu else 851c02599f2SChao Yu f2fs_bug_on(sbi, sbi->sb->s_flags & SB_ACTIVE); 8524b2414d0SChao Yu skip: 853c426d991SShin'ichiro Kawasaki fix_curseg_write_pointer = !check_only || list_empty(&inode_list); 854c426d991SShin'ichiro Kawasaki 85526b5a079SSheng Yong destroy_fsync_dnodes(&inode_list, err); 85626b5a079SSheng Yong destroy_fsync_dnodes(&tmp_inode_list, err); 857cf2271e7SJaegeuk Kim 8584c521f49SJaegeuk Kim /* truncate meta pages to be used by the recovery */ 8594c521f49SJaegeuk Kim truncate_inode_pages_range(META_MAPPING(sbi), 86009cbfeafSKirill A. Shutemov (loff_t)MAIN_BLKADDR(sbi) << PAGE_SHIFT, -1); 8614c521f49SJaegeuk Kim 862cf2271e7SJaegeuk Kim if (err) { 863cf2271e7SJaegeuk Kim truncate_inode_pages_final(NODE_MAPPING(sbi)); 864cf2271e7SJaegeuk Kim truncate_inode_pages_final(META_MAPPING(sbi)); 86526b5a079SSheng Yong } 866c426d991SShin'ichiro Kawasaki 867c426d991SShin'ichiro Kawasaki /* 868c426d991SShin'ichiro Kawasaki * If fsync data succeeds or there is no fsync data to recover, 869c426d991SShin'ichiro Kawasaki * and the f2fs is not read only, check and fix zoned block devices' 870c426d991SShin'ichiro Kawasaki * write pointer consistency. 871c426d991SShin'ichiro Kawasaki */ 872c426d991SShin'ichiro Kawasaki if (!err && fix_curseg_write_pointer && !f2fs_readonly(sbi->sb) && 873c426d991SShin'ichiro Kawasaki f2fs_sb_has_blkzoned(sbi)) { 874c426d991SShin'ichiro Kawasaki err = f2fs_fix_curseg_write_pointer(sbi); 875c426d991SShin'ichiro Kawasaki ret = err; 876c426d991SShin'ichiro Kawasaki } 877c426d991SShin'ichiro Kawasaki 878c426d991SShin'ichiro Kawasaki if (!err) 879c426d991SShin'ichiro Kawasaki clear_sbi_flag(sbi, SBI_POR_DOING); 880c426d991SShin'ichiro Kawasaki 881e4544b63STim Murray f2fs_up_write(&sbi->cp_global_sem); 882a468f0efSJaegeuk Kim 8839e1e6df4SJaegeuk Kim /* let's drop all the directory inodes for clean checkpoint */ 88426b5a079SSheng Yong destroy_fsync_dnodes(&dir_list, err); 8859e1e6df4SJaegeuk Kim 8861378752bSChao Yu if (need_writecp) { 8871378752bSChao Yu set_sbi_flag(sbi, SBI_IS_RECOVERED); 8881378752bSChao Yu 8891378752bSChao Yu if (!err) { 89075ab4cb8SJaegeuk Kim struct cp_control cpc = { 89110027551SJaegeuk Kim .reason = CP_RECOVERY, 89275ab4cb8SJaegeuk Kim }; 8934d57b86dSChao Yu err = f2fs_write_checkpoint(sbi, &cpc); 894cf2271e7SJaegeuk Kim } 8951378752bSChao Yu } 896f61cce5bSChao Yu 8974b2414d0SChao Yu #ifdef CONFIG_QUOTA 8984b2414d0SChao Yu /* Turn quotas off */ 899ea676733SJaegeuk Kim if (quota_enabled) 9004b2414d0SChao Yu f2fs_quota_off_umount(sbi->sb); 9014b2414d0SChao Yu #endif 9021751e8a6SLinus Torvalds sbi->sb->s_flags = s_flags; /* Restore SB_RDONLY status */ 9034b2414d0SChao Yu 9046781eabbSJaegeuk Kim return ret ? ret : err; 905d624c96fSJaegeuk Kim } 906cad83c96SChao Yu 907cad83c96SChao Yu int __init f2fs_create_recovery_cache(void) 908cad83c96SChao Yu { 909cad83c96SChao Yu fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry", 910cad83c96SChao Yu sizeof(struct fsync_inode_entry)); 911cad83c96SChao Yu if (!fsync_entry_slab) 912cad83c96SChao Yu return -ENOMEM; 913cad83c96SChao Yu return 0; 914cad83c96SChao Yu } 915cad83c96SChao Yu 916cad83c96SChao Yu void f2fs_destroy_recovery_cache(void) 917cad83c96SChao Yu { 918cad83c96SChao Yu kmem_cache_destroy(fsync_entry_slab); 919cad83c96SChao Yu } 920