162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * fs/f2fs/recovery.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2012 Samsung Electronics Co., Ltd. 662306a36Sopenharmony_ci * http://www.samsung.com/ 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <asm/unaligned.h> 962306a36Sopenharmony_ci#include <linux/fs.h> 1062306a36Sopenharmony_ci#include <linux/f2fs_fs.h> 1162306a36Sopenharmony_ci#include <linux/sched/mm.h> 1262306a36Sopenharmony_ci#include "f2fs.h" 1362306a36Sopenharmony_ci#include "node.h" 1462306a36Sopenharmony_ci#include "segment.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* 1762306a36Sopenharmony_ci * Roll forward recovery scenarios. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * [Term] F: fsync_mark, D: dentry_mark 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * 1. inode(x) | CP | inode(x) | dnode(F) 2262306a36Sopenharmony_ci * -> Update the latest inode(x). 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * 2. inode(x) | CP | inode(F) | dnode(F) 2562306a36Sopenharmony_ci * -> No problem. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * 3. inode(x) | CP | dnode(F) | inode(x) 2862306a36Sopenharmony_ci * -> Recover to the latest dnode(F), and drop the last inode(x) 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * 4. inode(x) | CP | dnode(F) | inode(F) 3162306a36Sopenharmony_ci * -> No problem. 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * 5. CP | inode(x) | dnode(F) 3462306a36Sopenharmony_ci * -> The inode(DF) was missing. Should drop this dnode(F). 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * 6. CP | inode(DF) | dnode(F) 3762306a36Sopenharmony_ci * -> No problem. 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * 7. CP | dnode(F) | inode(DF) 4062306a36Sopenharmony_ci * -> If f2fs_iget fails, then goto next to find inode(DF). 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * 8. CP | dnode(F) | inode(x) 4362306a36Sopenharmony_ci * -> If f2fs_iget fails, then goto next to find inode(DF). 4462306a36Sopenharmony_ci * But it will fail due to no inode(DF). 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic struct kmem_cache *fsync_entry_slab; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_UNICODE) 5062306a36Sopenharmony_ciextern struct kmem_cache *f2fs_cf_name_slab; 5162306a36Sopenharmony_ci#endif 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cibool f2fs_space_for_roll_forward(struct f2fs_sb_info *sbi) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci s64 nalloc = percpu_counter_sum_positive(&sbi->alloc_valid_block_count); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (sbi->last_valid_block_count + nalloc > sbi->user_block_count) 5862306a36Sopenharmony_ci return false; 5962306a36Sopenharmony_ci if (NM_I(sbi)->max_rf_node_blocks && 6062306a36Sopenharmony_ci percpu_counter_sum_positive(&sbi->rf_node_block_count) >= 6162306a36Sopenharmony_ci NM_I(sbi)->max_rf_node_blocks) 6262306a36Sopenharmony_ci return false; 6362306a36Sopenharmony_ci return true; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic struct fsync_inode_entry *get_fsync_inode(struct list_head *head, 6762306a36Sopenharmony_ci nid_t ino) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct fsync_inode_entry *entry; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci list_for_each_entry(entry, head, list) 7262306a36Sopenharmony_ci if (entry->inode->i_ino == ino) 7362306a36Sopenharmony_ci return entry; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return NULL; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic struct fsync_inode_entry *add_fsync_inode(struct f2fs_sb_info *sbi, 7962306a36Sopenharmony_ci struct list_head *head, nid_t ino, bool quota_inode) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct inode *inode; 8262306a36Sopenharmony_ci struct fsync_inode_entry *entry; 8362306a36Sopenharmony_ci int err; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci inode = f2fs_iget_retry(sbi->sb, ino); 8662306a36Sopenharmony_ci if (IS_ERR(inode)) 8762306a36Sopenharmony_ci return ERR_CAST(inode); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci err = f2fs_dquot_initialize(inode); 9062306a36Sopenharmony_ci if (err) 9162306a36Sopenharmony_ci goto err_out; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (quota_inode) { 9462306a36Sopenharmony_ci err = dquot_alloc_inode(inode); 9562306a36Sopenharmony_ci if (err) 9662306a36Sopenharmony_ci goto err_out; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci entry = f2fs_kmem_cache_alloc(fsync_entry_slab, 10062306a36Sopenharmony_ci GFP_F2FS_ZERO, true, NULL); 10162306a36Sopenharmony_ci entry->inode = inode; 10262306a36Sopenharmony_ci list_add_tail(&entry->list, head); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return entry; 10562306a36Sopenharmony_cierr_out: 10662306a36Sopenharmony_ci iput(inode); 10762306a36Sopenharmony_ci return ERR_PTR(err); 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic void del_fsync_inode(struct fsync_inode_entry *entry, int drop) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci if (drop) { 11362306a36Sopenharmony_ci /* inode should not be recovered, drop it */ 11462306a36Sopenharmony_ci f2fs_inode_synced(entry->inode); 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci iput(entry->inode); 11762306a36Sopenharmony_ci list_del(&entry->list); 11862306a36Sopenharmony_ci kmem_cache_free(fsync_entry_slab, entry); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int init_recovered_filename(const struct inode *dir, 12262306a36Sopenharmony_ci struct f2fs_inode *raw_inode, 12362306a36Sopenharmony_ci struct f2fs_filename *fname, 12462306a36Sopenharmony_ci struct qstr *usr_fname) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci int err; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci memset(fname, 0, sizeof(*fname)); 12962306a36Sopenharmony_ci fname->disk_name.len = le32_to_cpu(raw_inode->i_namelen); 13062306a36Sopenharmony_ci fname->disk_name.name = raw_inode->i_name; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (WARN_ON(fname->disk_name.len > F2FS_NAME_LEN)) 13362306a36Sopenharmony_ci return -ENAMETOOLONG; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (!IS_ENCRYPTED(dir)) { 13662306a36Sopenharmony_ci usr_fname->name = fname->disk_name.name; 13762306a36Sopenharmony_ci usr_fname->len = fname->disk_name.len; 13862306a36Sopenharmony_ci fname->usr_fname = usr_fname; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* Compute the hash of the filename */ 14262306a36Sopenharmony_ci if (IS_ENCRYPTED(dir) && IS_CASEFOLDED(dir)) { 14362306a36Sopenharmony_ci /* 14462306a36Sopenharmony_ci * In this case the hash isn't computable without the key, so it 14562306a36Sopenharmony_ci * was saved on-disk. 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci if (fname->disk_name.len + sizeof(f2fs_hash_t) > F2FS_NAME_LEN) 14862306a36Sopenharmony_ci return -EINVAL; 14962306a36Sopenharmony_ci fname->hash = get_unaligned((f2fs_hash_t *) 15062306a36Sopenharmony_ci &raw_inode->i_name[fname->disk_name.len]); 15162306a36Sopenharmony_ci } else if (IS_CASEFOLDED(dir)) { 15262306a36Sopenharmony_ci err = f2fs_init_casefolded_name(dir, fname); 15362306a36Sopenharmony_ci if (err) 15462306a36Sopenharmony_ci return err; 15562306a36Sopenharmony_ci f2fs_hash_filename(dir, fname); 15662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_UNICODE) 15762306a36Sopenharmony_ci /* Case-sensitive match is fine for recovery */ 15862306a36Sopenharmony_ci kmem_cache_free(f2fs_cf_name_slab, fname->cf_name.name); 15962306a36Sopenharmony_ci fname->cf_name.name = NULL; 16062306a36Sopenharmony_ci#endif 16162306a36Sopenharmony_ci } else { 16262306a36Sopenharmony_ci f2fs_hash_filename(dir, fname); 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic int recover_dentry(struct inode *inode, struct page *ipage, 16862306a36Sopenharmony_ci struct list_head *dir_list) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct f2fs_inode *raw_inode = F2FS_INODE(ipage); 17162306a36Sopenharmony_ci nid_t pino = le32_to_cpu(raw_inode->i_pino); 17262306a36Sopenharmony_ci struct f2fs_dir_entry *de; 17362306a36Sopenharmony_ci struct f2fs_filename fname; 17462306a36Sopenharmony_ci struct qstr usr_fname; 17562306a36Sopenharmony_ci struct page *page; 17662306a36Sopenharmony_ci struct inode *dir, *einode; 17762306a36Sopenharmony_ci struct fsync_inode_entry *entry; 17862306a36Sopenharmony_ci int err = 0; 17962306a36Sopenharmony_ci char *name; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci entry = get_fsync_inode(dir_list, pino); 18262306a36Sopenharmony_ci if (!entry) { 18362306a36Sopenharmony_ci entry = add_fsync_inode(F2FS_I_SB(inode), dir_list, 18462306a36Sopenharmony_ci pino, false); 18562306a36Sopenharmony_ci if (IS_ERR(entry)) { 18662306a36Sopenharmony_ci dir = ERR_CAST(entry); 18762306a36Sopenharmony_ci err = PTR_ERR(entry); 18862306a36Sopenharmony_ci goto out; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci dir = entry->inode; 19362306a36Sopenharmony_ci err = init_recovered_filename(dir, raw_inode, &fname, &usr_fname); 19462306a36Sopenharmony_ci if (err) 19562306a36Sopenharmony_ci goto out; 19662306a36Sopenharmony_ciretry: 19762306a36Sopenharmony_ci de = __f2fs_find_entry(dir, &fname, &page); 19862306a36Sopenharmony_ci if (de && inode->i_ino == le32_to_cpu(de->ino)) 19962306a36Sopenharmony_ci goto out_put; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (de) { 20262306a36Sopenharmony_ci einode = f2fs_iget_retry(inode->i_sb, le32_to_cpu(de->ino)); 20362306a36Sopenharmony_ci if (IS_ERR(einode)) { 20462306a36Sopenharmony_ci WARN_ON(1); 20562306a36Sopenharmony_ci err = PTR_ERR(einode); 20662306a36Sopenharmony_ci if (err == -ENOENT) 20762306a36Sopenharmony_ci err = -EEXIST; 20862306a36Sopenharmony_ci goto out_put; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci err = f2fs_dquot_initialize(einode); 21262306a36Sopenharmony_ci if (err) { 21362306a36Sopenharmony_ci iput(einode); 21462306a36Sopenharmony_ci goto out_put; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci err = f2fs_acquire_orphan_inode(F2FS_I_SB(inode)); 21862306a36Sopenharmony_ci if (err) { 21962306a36Sopenharmony_ci iput(einode); 22062306a36Sopenharmony_ci goto out_put; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci f2fs_delete_entry(de, page, dir, einode); 22362306a36Sopenharmony_ci iput(einode); 22462306a36Sopenharmony_ci goto retry; 22562306a36Sopenharmony_ci } else if (IS_ERR(page)) { 22662306a36Sopenharmony_ci err = PTR_ERR(page); 22762306a36Sopenharmony_ci } else { 22862306a36Sopenharmony_ci err = f2fs_add_dentry(dir, &fname, inode, 22962306a36Sopenharmony_ci inode->i_ino, inode->i_mode); 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci if (err == -ENOMEM) 23262306a36Sopenharmony_ci goto retry; 23362306a36Sopenharmony_ci goto out; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ciout_put: 23662306a36Sopenharmony_ci f2fs_put_page(page, 0); 23762306a36Sopenharmony_ciout: 23862306a36Sopenharmony_ci if (file_enc_name(inode)) 23962306a36Sopenharmony_ci name = "<encrypted>"; 24062306a36Sopenharmony_ci else 24162306a36Sopenharmony_ci name = raw_inode->i_name; 24262306a36Sopenharmony_ci f2fs_notice(F2FS_I_SB(inode), "%s: ino = %x, name = %s, dir = %lx, err = %d", 24362306a36Sopenharmony_ci __func__, ino_of_node(ipage), name, 24462306a36Sopenharmony_ci IS_ERR(dir) ? 0 : dir->i_ino, err); 24562306a36Sopenharmony_ci return err; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic int recover_quota_data(struct inode *inode, struct page *page) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct f2fs_inode *raw = F2FS_INODE(page); 25162306a36Sopenharmony_ci struct iattr attr; 25262306a36Sopenharmony_ci uid_t i_uid = le32_to_cpu(raw->i_uid); 25362306a36Sopenharmony_ci gid_t i_gid = le32_to_cpu(raw->i_gid); 25462306a36Sopenharmony_ci int err; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci memset(&attr, 0, sizeof(attr)); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci attr.ia_vfsuid = VFSUIDT_INIT(make_kuid(inode->i_sb->s_user_ns, i_uid)); 25962306a36Sopenharmony_ci attr.ia_vfsgid = VFSGIDT_INIT(make_kgid(inode->i_sb->s_user_ns, i_gid)); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (!vfsuid_eq(attr.ia_vfsuid, i_uid_into_vfsuid(&nop_mnt_idmap, inode))) 26262306a36Sopenharmony_ci attr.ia_valid |= ATTR_UID; 26362306a36Sopenharmony_ci if (!vfsgid_eq(attr.ia_vfsgid, i_gid_into_vfsgid(&nop_mnt_idmap, inode))) 26462306a36Sopenharmony_ci attr.ia_valid |= ATTR_GID; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (!attr.ia_valid) 26762306a36Sopenharmony_ci return 0; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci err = dquot_transfer(&nop_mnt_idmap, inode, &attr); 27062306a36Sopenharmony_ci if (err) 27162306a36Sopenharmony_ci set_sbi_flag(F2FS_I_SB(inode), SBI_QUOTA_NEED_REPAIR); 27262306a36Sopenharmony_ci return err; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic void recover_inline_flags(struct inode *inode, struct f2fs_inode *ri) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci if (ri->i_inline & F2FS_PIN_FILE) 27862306a36Sopenharmony_ci set_inode_flag(inode, FI_PIN_FILE); 27962306a36Sopenharmony_ci else 28062306a36Sopenharmony_ci clear_inode_flag(inode, FI_PIN_FILE); 28162306a36Sopenharmony_ci if (ri->i_inline & F2FS_DATA_EXIST) 28262306a36Sopenharmony_ci set_inode_flag(inode, FI_DATA_EXIST); 28362306a36Sopenharmony_ci else 28462306a36Sopenharmony_ci clear_inode_flag(inode, FI_DATA_EXIST); 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic int recover_inode(struct inode *inode, struct page *page) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct f2fs_inode *raw = F2FS_INODE(page); 29062306a36Sopenharmony_ci char *name; 29162306a36Sopenharmony_ci int err; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci inode->i_mode = le16_to_cpu(raw->i_mode); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci err = recover_quota_data(inode, page); 29662306a36Sopenharmony_ci if (err) 29762306a36Sopenharmony_ci return err; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci i_uid_write(inode, le32_to_cpu(raw->i_uid)); 30062306a36Sopenharmony_ci i_gid_write(inode, le32_to_cpu(raw->i_gid)); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (raw->i_inline & F2FS_EXTRA_ATTR) { 30362306a36Sopenharmony_ci if (f2fs_sb_has_project_quota(F2FS_I_SB(inode)) && 30462306a36Sopenharmony_ci F2FS_FITS_IN_INODE(raw, le16_to_cpu(raw->i_extra_isize), 30562306a36Sopenharmony_ci i_projid)) { 30662306a36Sopenharmony_ci projid_t i_projid; 30762306a36Sopenharmony_ci kprojid_t kprojid; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci i_projid = (projid_t)le32_to_cpu(raw->i_projid); 31062306a36Sopenharmony_ci kprojid = make_kprojid(&init_user_ns, i_projid); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (!projid_eq(kprojid, F2FS_I(inode)->i_projid)) { 31362306a36Sopenharmony_ci err = f2fs_transfer_project_quota(inode, 31462306a36Sopenharmony_ci kprojid); 31562306a36Sopenharmony_ci if (err) 31662306a36Sopenharmony_ci return err; 31762306a36Sopenharmony_ci F2FS_I(inode)->i_projid = kprojid; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci f2fs_i_size_write(inode, le64_to_cpu(raw->i_size)); 32362306a36Sopenharmony_ci inode->i_atime.tv_sec = le64_to_cpu(raw->i_atime); 32462306a36Sopenharmony_ci inode_set_ctime(inode, le64_to_cpu(raw->i_ctime), 32562306a36Sopenharmony_ci le32_to_cpu(raw->i_ctime_nsec)); 32662306a36Sopenharmony_ci inode->i_mtime.tv_sec = le64_to_cpu(raw->i_mtime); 32762306a36Sopenharmony_ci inode->i_atime.tv_nsec = le32_to_cpu(raw->i_atime_nsec); 32862306a36Sopenharmony_ci inode->i_mtime.tv_nsec = le32_to_cpu(raw->i_mtime_nsec); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci F2FS_I(inode)->i_advise = raw->i_advise; 33162306a36Sopenharmony_ci F2FS_I(inode)->i_flags = le32_to_cpu(raw->i_flags); 33262306a36Sopenharmony_ci f2fs_set_inode_flags(inode); 33362306a36Sopenharmony_ci F2FS_I(inode)->i_gc_failures[GC_FAILURE_PIN] = 33462306a36Sopenharmony_ci le16_to_cpu(raw->i_gc_failures); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci recover_inline_flags(inode, raw); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci f2fs_mark_inode_dirty_sync(inode, true); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (file_enc_name(inode)) 34162306a36Sopenharmony_ci name = "<encrypted>"; 34262306a36Sopenharmony_ci else 34362306a36Sopenharmony_ci name = F2FS_INODE(page)->i_name; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci f2fs_notice(F2FS_I_SB(inode), "recover_inode: ino = %x, name = %s, inline = %x", 34662306a36Sopenharmony_ci ino_of_node(page), name, raw->i_inline); 34762306a36Sopenharmony_ci return 0; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic unsigned int adjust_por_ra_blocks(struct f2fs_sb_info *sbi, 35162306a36Sopenharmony_ci unsigned int ra_blocks, unsigned int blkaddr, 35262306a36Sopenharmony_ci unsigned int next_blkaddr) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci if (blkaddr + 1 == next_blkaddr) 35562306a36Sopenharmony_ci ra_blocks = min_t(unsigned int, RECOVERY_MAX_RA_BLOCKS, 35662306a36Sopenharmony_ci ra_blocks * 2); 35762306a36Sopenharmony_ci else if (next_blkaddr % sbi->blocks_per_seg) 35862306a36Sopenharmony_ci ra_blocks = max_t(unsigned int, RECOVERY_MIN_RA_BLOCKS, 35962306a36Sopenharmony_ci ra_blocks / 2); 36062306a36Sopenharmony_ci return ra_blocks; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci/* Detect looped node chain with Floyd's cycle detection algorithm. */ 36462306a36Sopenharmony_cistatic int sanity_check_node_chain(struct f2fs_sb_info *sbi, block_t blkaddr, 36562306a36Sopenharmony_ci block_t *blkaddr_fast, bool *is_detecting) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci unsigned int ra_blocks = RECOVERY_MAX_RA_BLOCKS; 36862306a36Sopenharmony_ci struct page *page = NULL; 36962306a36Sopenharmony_ci int i; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (!*is_detecting) 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 37562306a36Sopenharmony_ci if (!f2fs_is_valid_blkaddr(sbi, *blkaddr_fast, META_POR)) { 37662306a36Sopenharmony_ci *is_detecting = false; 37762306a36Sopenharmony_ci return 0; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci page = f2fs_get_tmp_page(sbi, *blkaddr_fast); 38162306a36Sopenharmony_ci if (IS_ERR(page)) 38262306a36Sopenharmony_ci return PTR_ERR(page); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (!is_recoverable_dnode(page)) { 38562306a36Sopenharmony_ci f2fs_put_page(page, 1); 38662306a36Sopenharmony_ci *is_detecting = false; 38762306a36Sopenharmony_ci return 0; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci ra_blocks = adjust_por_ra_blocks(sbi, ra_blocks, *blkaddr_fast, 39162306a36Sopenharmony_ci next_blkaddr_of_node(page)); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci *blkaddr_fast = next_blkaddr_of_node(page); 39462306a36Sopenharmony_ci f2fs_put_page(page, 1); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci f2fs_ra_meta_pages_cond(sbi, *blkaddr_fast, ra_blocks); 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (*blkaddr_fast == blkaddr) { 40062306a36Sopenharmony_ci f2fs_notice(sbi, "%s: Detect looped node chain on blkaddr:%u." 40162306a36Sopenharmony_ci " Run fsck to fix it.", __func__, blkaddr); 40262306a36Sopenharmony_ci return -EINVAL; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci return 0; 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head, 40862306a36Sopenharmony_ci bool check_only) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci struct curseg_info *curseg; 41162306a36Sopenharmony_ci struct page *page = NULL; 41262306a36Sopenharmony_ci block_t blkaddr, blkaddr_fast; 41362306a36Sopenharmony_ci bool is_detecting = true; 41462306a36Sopenharmony_ci int err = 0; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* get node pages in the current segment */ 41762306a36Sopenharmony_ci curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); 41862306a36Sopenharmony_ci blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); 41962306a36Sopenharmony_ci blkaddr_fast = blkaddr; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci while (1) { 42262306a36Sopenharmony_ci struct fsync_inode_entry *entry; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (!f2fs_is_valid_blkaddr(sbi, blkaddr, META_POR)) 42562306a36Sopenharmony_ci return 0; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci page = f2fs_get_tmp_page(sbi, blkaddr); 42862306a36Sopenharmony_ci if (IS_ERR(page)) { 42962306a36Sopenharmony_ci err = PTR_ERR(page); 43062306a36Sopenharmony_ci break; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (!is_recoverable_dnode(page)) { 43462306a36Sopenharmony_ci f2fs_put_page(page, 1); 43562306a36Sopenharmony_ci break; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (!is_fsync_dnode(page)) 43962306a36Sopenharmony_ci goto next; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci entry = get_fsync_inode(head, ino_of_node(page)); 44262306a36Sopenharmony_ci if (!entry) { 44362306a36Sopenharmony_ci bool quota_inode = false; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (!check_only && 44662306a36Sopenharmony_ci IS_INODE(page) && is_dent_dnode(page)) { 44762306a36Sopenharmony_ci err = f2fs_recover_inode_page(sbi, page); 44862306a36Sopenharmony_ci if (err) { 44962306a36Sopenharmony_ci f2fs_put_page(page, 1); 45062306a36Sopenharmony_ci break; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci quota_inode = true; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* 45662306a36Sopenharmony_ci * CP | dnode(F) | inode(DF) 45762306a36Sopenharmony_ci * For this case, we should not give up now. 45862306a36Sopenharmony_ci */ 45962306a36Sopenharmony_ci entry = add_fsync_inode(sbi, head, ino_of_node(page), 46062306a36Sopenharmony_ci quota_inode); 46162306a36Sopenharmony_ci if (IS_ERR(entry)) { 46262306a36Sopenharmony_ci err = PTR_ERR(entry); 46362306a36Sopenharmony_ci if (err == -ENOENT) 46462306a36Sopenharmony_ci goto next; 46562306a36Sopenharmony_ci f2fs_put_page(page, 1); 46662306a36Sopenharmony_ci break; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci entry->blkaddr = blkaddr; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (IS_INODE(page) && is_dent_dnode(page)) 47262306a36Sopenharmony_ci entry->last_dentry = blkaddr; 47362306a36Sopenharmony_cinext: 47462306a36Sopenharmony_ci /* check next segment */ 47562306a36Sopenharmony_ci blkaddr = next_blkaddr_of_node(page); 47662306a36Sopenharmony_ci f2fs_put_page(page, 1); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci err = sanity_check_node_chain(sbi, blkaddr, &blkaddr_fast, 47962306a36Sopenharmony_ci &is_detecting); 48062306a36Sopenharmony_ci if (err) 48162306a36Sopenharmony_ci break; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci return err; 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistatic void destroy_fsync_dnodes(struct list_head *head, int drop) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci struct fsync_inode_entry *entry, *tmp; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci list_for_each_entry_safe(entry, tmp, head, list) 49162306a36Sopenharmony_ci del_fsync_inode(entry, drop); 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic int check_index_in_prev_nodes(struct f2fs_sb_info *sbi, 49562306a36Sopenharmony_ci block_t blkaddr, struct dnode_of_data *dn) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci struct seg_entry *sentry; 49862306a36Sopenharmony_ci unsigned int segno = GET_SEGNO(sbi, blkaddr); 49962306a36Sopenharmony_ci unsigned short blkoff = GET_BLKOFF_FROM_SEG0(sbi, blkaddr); 50062306a36Sopenharmony_ci struct f2fs_summary_block *sum_node; 50162306a36Sopenharmony_ci struct f2fs_summary sum; 50262306a36Sopenharmony_ci struct page *sum_page, *node_page; 50362306a36Sopenharmony_ci struct dnode_of_data tdn = *dn; 50462306a36Sopenharmony_ci nid_t ino, nid; 50562306a36Sopenharmony_ci struct inode *inode; 50662306a36Sopenharmony_ci unsigned int offset, ofs_in_node, max_addrs; 50762306a36Sopenharmony_ci block_t bidx; 50862306a36Sopenharmony_ci int i; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci sentry = get_seg_entry(sbi, segno); 51162306a36Sopenharmony_ci if (!f2fs_test_bit(blkoff, sentry->cur_valid_map)) 51262306a36Sopenharmony_ci return 0; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* Get the previous summary */ 51562306a36Sopenharmony_ci for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { 51662306a36Sopenharmony_ci struct curseg_info *curseg = CURSEG_I(sbi, i); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (curseg->segno == segno) { 51962306a36Sopenharmony_ci sum = curseg->sum_blk->entries[blkoff]; 52062306a36Sopenharmony_ci goto got_it; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci sum_page = f2fs_get_sum_page(sbi, segno); 52562306a36Sopenharmony_ci if (IS_ERR(sum_page)) 52662306a36Sopenharmony_ci return PTR_ERR(sum_page); 52762306a36Sopenharmony_ci sum_node = (struct f2fs_summary_block *)page_address(sum_page); 52862306a36Sopenharmony_ci sum = sum_node->entries[blkoff]; 52962306a36Sopenharmony_ci f2fs_put_page(sum_page, 1); 53062306a36Sopenharmony_cigot_it: 53162306a36Sopenharmony_ci /* Use the locked dnode page and inode */ 53262306a36Sopenharmony_ci nid = le32_to_cpu(sum.nid); 53362306a36Sopenharmony_ci ofs_in_node = le16_to_cpu(sum.ofs_in_node); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci max_addrs = ADDRS_PER_PAGE(dn->node_page, dn->inode); 53662306a36Sopenharmony_ci if (ofs_in_node >= max_addrs) { 53762306a36Sopenharmony_ci f2fs_err(sbi, "Inconsistent ofs_in_node:%u in summary, ino:%lu, nid:%u, max:%u", 53862306a36Sopenharmony_ci ofs_in_node, dn->inode->i_ino, nid, max_addrs); 53962306a36Sopenharmony_ci f2fs_handle_error(sbi, ERROR_INCONSISTENT_SUMMARY); 54062306a36Sopenharmony_ci return -EFSCORRUPTED; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (dn->inode->i_ino == nid) { 54462306a36Sopenharmony_ci tdn.nid = nid; 54562306a36Sopenharmony_ci if (!dn->inode_page_locked) 54662306a36Sopenharmony_ci lock_page(dn->inode_page); 54762306a36Sopenharmony_ci tdn.node_page = dn->inode_page; 54862306a36Sopenharmony_ci tdn.ofs_in_node = ofs_in_node; 54962306a36Sopenharmony_ci goto truncate_out; 55062306a36Sopenharmony_ci } else if (dn->nid == nid) { 55162306a36Sopenharmony_ci tdn.ofs_in_node = ofs_in_node; 55262306a36Sopenharmony_ci goto truncate_out; 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* Get the node page */ 55662306a36Sopenharmony_ci node_page = f2fs_get_node_page(sbi, nid); 55762306a36Sopenharmony_ci if (IS_ERR(node_page)) 55862306a36Sopenharmony_ci return PTR_ERR(node_page); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci offset = ofs_of_node(node_page); 56162306a36Sopenharmony_ci ino = ino_of_node(node_page); 56262306a36Sopenharmony_ci f2fs_put_page(node_page, 1); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci if (ino != dn->inode->i_ino) { 56562306a36Sopenharmony_ci int ret; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* Deallocate previous index in the node page */ 56862306a36Sopenharmony_ci inode = f2fs_iget_retry(sbi->sb, ino); 56962306a36Sopenharmony_ci if (IS_ERR(inode)) 57062306a36Sopenharmony_ci return PTR_ERR(inode); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci ret = f2fs_dquot_initialize(inode); 57362306a36Sopenharmony_ci if (ret) { 57462306a36Sopenharmony_ci iput(inode); 57562306a36Sopenharmony_ci return ret; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci } else { 57862306a36Sopenharmony_ci inode = dn->inode; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci bidx = f2fs_start_bidx_of_node(offset, inode) + 58262306a36Sopenharmony_ci le16_to_cpu(sum.ofs_in_node); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* 58562306a36Sopenharmony_ci * if inode page is locked, unlock temporarily, but its reference 58662306a36Sopenharmony_ci * count keeps alive. 58762306a36Sopenharmony_ci */ 58862306a36Sopenharmony_ci if (ino == dn->inode->i_ino && dn->inode_page_locked) 58962306a36Sopenharmony_ci unlock_page(dn->inode_page); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci set_new_dnode(&tdn, inode, NULL, NULL, 0); 59262306a36Sopenharmony_ci if (f2fs_get_dnode_of_data(&tdn, bidx, LOOKUP_NODE)) 59362306a36Sopenharmony_ci goto out; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (tdn.data_blkaddr == blkaddr) 59662306a36Sopenharmony_ci f2fs_truncate_data_blocks_range(&tdn, 1); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci f2fs_put_dnode(&tdn); 59962306a36Sopenharmony_ciout: 60062306a36Sopenharmony_ci if (ino != dn->inode->i_ino) 60162306a36Sopenharmony_ci iput(inode); 60262306a36Sopenharmony_ci else if (dn->inode_page_locked) 60362306a36Sopenharmony_ci lock_page(dn->inode_page); 60462306a36Sopenharmony_ci return 0; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_citruncate_out: 60762306a36Sopenharmony_ci if (f2fs_data_blkaddr(&tdn) == blkaddr) 60862306a36Sopenharmony_ci f2fs_truncate_data_blocks_range(&tdn, 1); 60962306a36Sopenharmony_ci if (dn->inode->i_ino == nid && !dn->inode_page_locked) 61062306a36Sopenharmony_ci unlock_page(dn->inode_page); 61162306a36Sopenharmony_ci return 0; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic int f2fs_reserve_new_block_retry(struct dnode_of_data *dn) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci int i, err = 0; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci for (i = DEFAULT_FAILURE_RETRY_COUNT; i > 0; i--) { 61962306a36Sopenharmony_ci err = f2fs_reserve_new_block(dn); 62062306a36Sopenharmony_ci if (!err) 62162306a36Sopenharmony_ci break; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci return err; 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cistatic int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, 62862306a36Sopenharmony_ci struct page *page) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci struct dnode_of_data dn; 63162306a36Sopenharmony_ci struct node_info ni; 63262306a36Sopenharmony_ci unsigned int start, end; 63362306a36Sopenharmony_ci int err = 0, recovered = 0; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* step 1: recover xattr */ 63662306a36Sopenharmony_ci if (IS_INODE(page)) { 63762306a36Sopenharmony_ci err = f2fs_recover_inline_xattr(inode, page); 63862306a36Sopenharmony_ci if (err) 63962306a36Sopenharmony_ci goto out; 64062306a36Sopenharmony_ci } else if (f2fs_has_xattr_block(ofs_of_node(page))) { 64162306a36Sopenharmony_ci err = f2fs_recover_xattr_data(inode, page); 64262306a36Sopenharmony_ci if (!err) 64362306a36Sopenharmony_ci recovered++; 64462306a36Sopenharmony_ci goto out; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* step 2: recover inline data */ 64862306a36Sopenharmony_ci err = f2fs_recover_inline_data(inode, page); 64962306a36Sopenharmony_ci if (err) { 65062306a36Sopenharmony_ci if (err == 1) 65162306a36Sopenharmony_ci err = 0; 65262306a36Sopenharmony_ci goto out; 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci /* step 3: recover data indices */ 65662306a36Sopenharmony_ci start = f2fs_start_bidx_of_node(ofs_of_node(page), inode); 65762306a36Sopenharmony_ci end = start + ADDRS_PER_PAGE(page, inode); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci set_new_dnode(&dn, inode, NULL, NULL, 0); 66062306a36Sopenharmony_ciretry_dn: 66162306a36Sopenharmony_ci err = f2fs_get_dnode_of_data(&dn, start, ALLOC_NODE); 66262306a36Sopenharmony_ci if (err) { 66362306a36Sopenharmony_ci if (err == -ENOMEM) { 66462306a36Sopenharmony_ci memalloc_retry_wait(GFP_NOFS); 66562306a36Sopenharmony_ci goto retry_dn; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci goto out; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci f2fs_wait_on_page_writeback(dn.node_page, NODE, true, true); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci err = f2fs_get_node_info(sbi, dn.nid, &ni, false); 67362306a36Sopenharmony_ci if (err) 67462306a36Sopenharmony_ci goto err; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci f2fs_bug_on(sbi, ni.ino != ino_of_node(page)); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci if (ofs_of_node(dn.node_page) != ofs_of_node(page)) { 67962306a36Sopenharmony_ci f2fs_warn(sbi, "Inconsistent ofs_of_node, ino:%lu, ofs:%u, %u", 68062306a36Sopenharmony_ci inode->i_ino, ofs_of_node(dn.node_page), 68162306a36Sopenharmony_ci ofs_of_node(page)); 68262306a36Sopenharmony_ci err = -EFSCORRUPTED; 68362306a36Sopenharmony_ci f2fs_handle_error(sbi, ERROR_INCONSISTENT_FOOTER); 68462306a36Sopenharmony_ci goto err; 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci for (; start < end; start++, dn.ofs_in_node++) { 68862306a36Sopenharmony_ci block_t src, dest; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci src = f2fs_data_blkaddr(&dn); 69162306a36Sopenharmony_ci dest = data_blkaddr(dn.inode, page, dn.ofs_in_node); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (__is_valid_data_blkaddr(src) && 69462306a36Sopenharmony_ci !f2fs_is_valid_blkaddr(sbi, src, META_POR)) { 69562306a36Sopenharmony_ci err = -EFSCORRUPTED; 69662306a36Sopenharmony_ci f2fs_handle_error(sbi, ERROR_INVALID_BLKADDR); 69762306a36Sopenharmony_ci goto err; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (__is_valid_data_blkaddr(dest) && 70162306a36Sopenharmony_ci !f2fs_is_valid_blkaddr(sbi, dest, META_POR)) { 70262306a36Sopenharmony_ci err = -EFSCORRUPTED; 70362306a36Sopenharmony_ci f2fs_handle_error(sbi, ERROR_INVALID_BLKADDR); 70462306a36Sopenharmony_ci goto err; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci /* skip recovering if dest is the same as src */ 70862306a36Sopenharmony_ci if (src == dest) 70962306a36Sopenharmony_ci continue; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* dest is invalid, just invalidate src block */ 71262306a36Sopenharmony_ci if (dest == NULL_ADDR) { 71362306a36Sopenharmony_ci f2fs_truncate_data_blocks_range(&dn, 1); 71462306a36Sopenharmony_ci continue; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (!file_keep_isize(inode) && 71862306a36Sopenharmony_ci (i_size_read(inode) <= ((loff_t)start << PAGE_SHIFT))) 71962306a36Sopenharmony_ci f2fs_i_size_write(inode, 72062306a36Sopenharmony_ci (loff_t)(start + 1) << PAGE_SHIFT); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* 72362306a36Sopenharmony_ci * dest is reserved block, invalidate src block 72462306a36Sopenharmony_ci * and then reserve one new block in dnode page. 72562306a36Sopenharmony_ci */ 72662306a36Sopenharmony_ci if (dest == NEW_ADDR) { 72762306a36Sopenharmony_ci f2fs_truncate_data_blocks_range(&dn, 1); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci err = f2fs_reserve_new_block_retry(&dn); 73062306a36Sopenharmony_ci if (err) 73162306a36Sopenharmony_ci goto err; 73262306a36Sopenharmony_ci continue; 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci /* dest is valid block, try to recover from src to dest */ 73662306a36Sopenharmony_ci if (f2fs_is_valid_blkaddr(sbi, dest, META_POR)) { 73762306a36Sopenharmony_ci if (src == NULL_ADDR) { 73862306a36Sopenharmony_ci err = f2fs_reserve_new_block_retry(&dn); 73962306a36Sopenharmony_ci if (err) 74062306a36Sopenharmony_ci goto err; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ciretry_prev: 74362306a36Sopenharmony_ci /* Check the previous node page having this index */ 74462306a36Sopenharmony_ci err = check_index_in_prev_nodes(sbi, dest, &dn); 74562306a36Sopenharmony_ci if (err) { 74662306a36Sopenharmony_ci if (err == -ENOMEM) { 74762306a36Sopenharmony_ci memalloc_retry_wait(GFP_NOFS); 74862306a36Sopenharmony_ci goto retry_prev; 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci goto err; 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci if (f2fs_is_valid_blkaddr(sbi, dest, 75462306a36Sopenharmony_ci DATA_GENERIC_ENHANCE_UPDATE)) { 75562306a36Sopenharmony_ci f2fs_err(sbi, "Inconsistent dest blkaddr:%u, ino:%lu, ofs:%u", 75662306a36Sopenharmony_ci dest, inode->i_ino, dn.ofs_in_node); 75762306a36Sopenharmony_ci err = -EFSCORRUPTED; 75862306a36Sopenharmony_ci f2fs_handle_error(sbi, 75962306a36Sopenharmony_ci ERROR_INVALID_BLKADDR); 76062306a36Sopenharmony_ci goto err; 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* write dummy data page */ 76462306a36Sopenharmony_ci f2fs_replace_block(sbi, &dn, src, dest, 76562306a36Sopenharmony_ci ni.version, false, false); 76662306a36Sopenharmony_ci recovered++; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci copy_node_footer(dn.node_page, page); 77162306a36Sopenharmony_ci fill_node_footer(dn.node_page, dn.nid, ni.ino, 77262306a36Sopenharmony_ci ofs_of_node(page), false); 77362306a36Sopenharmony_ci set_page_dirty(dn.node_page); 77462306a36Sopenharmony_cierr: 77562306a36Sopenharmony_ci f2fs_put_dnode(&dn); 77662306a36Sopenharmony_ciout: 77762306a36Sopenharmony_ci f2fs_notice(sbi, "recover_data: ino = %lx (i_size: %s) recovered = %d, err = %d", 77862306a36Sopenharmony_ci inode->i_ino, file_keep_isize(inode) ? "keep" : "recover", 77962306a36Sopenharmony_ci recovered, err); 78062306a36Sopenharmony_ci return err; 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic int recover_data(struct f2fs_sb_info *sbi, struct list_head *inode_list, 78462306a36Sopenharmony_ci struct list_head *tmp_inode_list, struct list_head *dir_list) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci struct curseg_info *curseg; 78762306a36Sopenharmony_ci struct page *page = NULL; 78862306a36Sopenharmony_ci int err = 0; 78962306a36Sopenharmony_ci block_t blkaddr; 79062306a36Sopenharmony_ci unsigned int ra_blocks = RECOVERY_MAX_RA_BLOCKS; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci /* get node pages in the current segment */ 79362306a36Sopenharmony_ci curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); 79462306a36Sopenharmony_ci blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci while (1) { 79762306a36Sopenharmony_ci struct fsync_inode_entry *entry; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci if (!f2fs_is_valid_blkaddr(sbi, blkaddr, META_POR)) 80062306a36Sopenharmony_ci break; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci page = f2fs_get_tmp_page(sbi, blkaddr); 80362306a36Sopenharmony_ci if (IS_ERR(page)) { 80462306a36Sopenharmony_ci err = PTR_ERR(page); 80562306a36Sopenharmony_ci break; 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (!is_recoverable_dnode(page)) { 80962306a36Sopenharmony_ci f2fs_put_page(page, 1); 81062306a36Sopenharmony_ci break; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci entry = get_fsync_inode(inode_list, ino_of_node(page)); 81462306a36Sopenharmony_ci if (!entry) 81562306a36Sopenharmony_ci goto next; 81662306a36Sopenharmony_ci /* 81762306a36Sopenharmony_ci * inode(x) | CP | inode(x) | dnode(F) 81862306a36Sopenharmony_ci * In this case, we can lose the latest inode(x). 81962306a36Sopenharmony_ci * So, call recover_inode for the inode update. 82062306a36Sopenharmony_ci */ 82162306a36Sopenharmony_ci if (IS_INODE(page)) { 82262306a36Sopenharmony_ci err = recover_inode(entry->inode, page); 82362306a36Sopenharmony_ci if (err) { 82462306a36Sopenharmony_ci f2fs_put_page(page, 1); 82562306a36Sopenharmony_ci break; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci if (entry->last_dentry == blkaddr) { 82962306a36Sopenharmony_ci err = recover_dentry(entry->inode, page, dir_list); 83062306a36Sopenharmony_ci if (err) { 83162306a36Sopenharmony_ci f2fs_put_page(page, 1); 83262306a36Sopenharmony_ci break; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci err = do_recover_data(sbi, entry->inode, page); 83662306a36Sopenharmony_ci if (err) { 83762306a36Sopenharmony_ci f2fs_put_page(page, 1); 83862306a36Sopenharmony_ci break; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci if (entry->blkaddr == blkaddr) 84262306a36Sopenharmony_ci list_move_tail(&entry->list, tmp_inode_list); 84362306a36Sopenharmony_cinext: 84462306a36Sopenharmony_ci ra_blocks = adjust_por_ra_blocks(sbi, ra_blocks, blkaddr, 84562306a36Sopenharmony_ci next_blkaddr_of_node(page)); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci /* check next segment */ 84862306a36Sopenharmony_ci blkaddr = next_blkaddr_of_node(page); 84962306a36Sopenharmony_ci f2fs_put_page(page, 1); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci f2fs_ra_meta_pages_cond(sbi, blkaddr, ra_blocks); 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci if (!err) 85462306a36Sopenharmony_ci f2fs_allocate_new_segments(sbi); 85562306a36Sopenharmony_ci return err; 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ciint f2fs_recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci struct list_head inode_list, tmp_inode_list; 86162306a36Sopenharmony_ci struct list_head dir_list; 86262306a36Sopenharmony_ci int err; 86362306a36Sopenharmony_ci int ret = 0; 86462306a36Sopenharmony_ci unsigned long s_flags = sbi->sb->s_flags; 86562306a36Sopenharmony_ci bool need_writecp = false; 86662306a36Sopenharmony_ci bool fix_curseg_write_pointer = false; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci if (is_sbi_flag_set(sbi, SBI_IS_WRITABLE)) 86962306a36Sopenharmony_ci f2fs_info(sbi, "recover fsync data on readonly fs"); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci INIT_LIST_HEAD(&inode_list); 87262306a36Sopenharmony_ci INIT_LIST_HEAD(&tmp_inode_list); 87362306a36Sopenharmony_ci INIT_LIST_HEAD(&dir_list); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci /* prevent checkpoint */ 87662306a36Sopenharmony_ci f2fs_down_write(&sbi->cp_global_sem); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci /* step #1: find fsynced inode numbers */ 87962306a36Sopenharmony_ci err = find_fsync_dnodes(sbi, &inode_list, check_only); 88062306a36Sopenharmony_ci if (err || list_empty(&inode_list)) 88162306a36Sopenharmony_ci goto skip; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (check_only) { 88462306a36Sopenharmony_ci ret = 1; 88562306a36Sopenharmony_ci goto skip; 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci need_writecp = true; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci /* step #2: recover data */ 89162306a36Sopenharmony_ci err = recover_data(sbi, &inode_list, &tmp_inode_list, &dir_list); 89262306a36Sopenharmony_ci if (!err) 89362306a36Sopenharmony_ci f2fs_bug_on(sbi, !list_empty(&inode_list)); 89462306a36Sopenharmony_ci else 89562306a36Sopenharmony_ci f2fs_bug_on(sbi, sbi->sb->s_flags & SB_ACTIVE); 89662306a36Sopenharmony_ciskip: 89762306a36Sopenharmony_ci fix_curseg_write_pointer = !check_only || list_empty(&inode_list); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci destroy_fsync_dnodes(&inode_list, err); 90062306a36Sopenharmony_ci destroy_fsync_dnodes(&tmp_inode_list, err); 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci /* truncate meta pages to be used by the recovery */ 90362306a36Sopenharmony_ci truncate_inode_pages_range(META_MAPPING(sbi), 90462306a36Sopenharmony_ci (loff_t)MAIN_BLKADDR(sbi) << PAGE_SHIFT, -1); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci if (err) { 90762306a36Sopenharmony_ci truncate_inode_pages_final(NODE_MAPPING(sbi)); 90862306a36Sopenharmony_ci truncate_inode_pages_final(META_MAPPING(sbi)); 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci /* 91262306a36Sopenharmony_ci * If fsync data succeeds or there is no fsync data to recover, 91362306a36Sopenharmony_ci * and the f2fs is not read only, check and fix zoned block devices' 91462306a36Sopenharmony_ci * write pointer consistency. 91562306a36Sopenharmony_ci */ 91662306a36Sopenharmony_ci if (!err && fix_curseg_write_pointer && !f2fs_readonly(sbi->sb) && 91762306a36Sopenharmony_ci f2fs_sb_has_blkzoned(sbi)) { 91862306a36Sopenharmony_ci err = f2fs_fix_curseg_write_pointer(sbi); 91962306a36Sopenharmony_ci if (!err) 92062306a36Sopenharmony_ci err = f2fs_check_write_pointer(sbi); 92162306a36Sopenharmony_ci ret = err; 92262306a36Sopenharmony_ci } 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci if (!err) 92562306a36Sopenharmony_ci clear_sbi_flag(sbi, SBI_POR_DOING); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci f2fs_up_write(&sbi->cp_global_sem); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci /* let's drop all the directory inodes for clean checkpoint */ 93062306a36Sopenharmony_ci destroy_fsync_dnodes(&dir_list, err); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci if (need_writecp) { 93362306a36Sopenharmony_ci set_sbi_flag(sbi, SBI_IS_RECOVERED); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci if (!err) { 93662306a36Sopenharmony_ci struct cp_control cpc = { 93762306a36Sopenharmony_ci .reason = CP_RECOVERY, 93862306a36Sopenharmony_ci }; 93962306a36Sopenharmony_ci stat_inc_cp_call_count(sbi, TOTAL_CALL); 94062306a36Sopenharmony_ci err = f2fs_write_checkpoint(sbi, &cpc); 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci } 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci sbi->sb->s_flags = s_flags; /* Restore SB_RDONLY status */ 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci return ret ? ret : err; 94762306a36Sopenharmony_ci} 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ciint __init f2fs_create_recovery_cache(void) 95062306a36Sopenharmony_ci{ 95162306a36Sopenharmony_ci fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry", 95262306a36Sopenharmony_ci sizeof(struct fsync_inode_entry)); 95362306a36Sopenharmony_ci return fsync_entry_slab ? 0 : -ENOMEM; 95462306a36Sopenharmony_ci} 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_civoid f2fs_destroy_recovery_cache(void) 95762306a36Sopenharmony_ci{ 95862306a36Sopenharmony_ci kmem_cache_destroy(fsync_entry_slab); 95962306a36Sopenharmony_ci} 960