10a8165d7SJaegeuk Kim /* 2d624c96fSJaegeuk Kim * fs/f2fs/recovery.c 3d624c96fSJaegeuk Kim * 4d624c96fSJaegeuk Kim * Copyright (c) 2012 Samsung Electronics Co., Ltd. 5d624c96fSJaegeuk Kim * http://www.samsung.com/ 6d624c96fSJaegeuk Kim * 7d624c96fSJaegeuk Kim * This program is free software; you can redistribute it and/or modify 8d624c96fSJaegeuk Kim * it under the terms of the GNU General Public License version 2 as 9d624c96fSJaegeuk Kim * published by the Free Software Foundation. 10d624c96fSJaegeuk Kim */ 11d624c96fSJaegeuk Kim #include <linux/fs.h> 12d624c96fSJaegeuk Kim #include <linux/f2fs_fs.h> 13d624c96fSJaegeuk Kim #include "f2fs.h" 14d624c96fSJaegeuk Kim #include "node.h" 15d624c96fSJaegeuk Kim #include "segment.h" 16d624c96fSJaegeuk Kim 17d624c96fSJaegeuk Kim static struct kmem_cache *fsync_entry_slab; 18d624c96fSJaegeuk Kim 19d624c96fSJaegeuk Kim bool space_for_roll_forward(struct f2fs_sb_info *sbi) 20d624c96fSJaegeuk Kim { 21d624c96fSJaegeuk Kim if (sbi->last_valid_block_count + sbi->alloc_valid_block_count 22d624c96fSJaegeuk Kim > sbi->user_block_count) 23d624c96fSJaegeuk Kim return false; 24d624c96fSJaegeuk Kim return true; 25d624c96fSJaegeuk Kim } 26d624c96fSJaegeuk Kim 27d624c96fSJaegeuk Kim static struct fsync_inode_entry *get_fsync_inode(struct list_head *head, 28d624c96fSJaegeuk Kim nid_t ino) 29d624c96fSJaegeuk Kim { 30d624c96fSJaegeuk Kim struct fsync_inode_entry *entry; 31d624c96fSJaegeuk Kim 322d7b822aSChao Yu list_for_each_entry(entry, head, list) 33d624c96fSJaegeuk Kim if (entry->inode->i_ino == ino) 34d624c96fSJaegeuk Kim return entry; 352d7b822aSChao Yu 36d624c96fSJaegeuk Kim return NULL; 37d624c96fSJaegeuk Kim } 38d624c96fSJaegeuk Kim 39d624c96fSJaegeuk Kim static int recover_dentry(struct page *ipage, struct inode *inode) 40d624c96fSJaegeuk Kim { 4158bfaf44SJaegeuk Kim struct f2fs_inode *raw_inode = F2FS_INODE(ipage); 4274d0b917SJaegeuk Kim nid_t pino = le32_to_cpu(raw_inode->i_pino); 436b8213d9SJaegeuk Kim struct f2fs_dir_entry *de; 44b7f7a5e0SAl Viro struct qstr name; 45d624c96fSJaegeuk Kim struct page *page; 466b8213d9SJaegeuk Kim struct inode *dir, *einode; 47d624c96fSJaegeuk Kim int err = 0; 48d624c96fSJaegeuk Kim 4974d0b917SJaegeuk Kim dir = f2fs_iget(inode->i_sb, pino); 50d624c96fSJaegeuk Kim if (IS_ERR(dir)) { 51047184b4SChris Fries err = PTR_ERR(dir); 52d624c96fSJaegeuk Kim goto out; 53d624c96fSJaegeuk Kim } 54ed57c27fSJaegeuk Kim 55ed57c27fSJaegeuk Kim if (is_inode_flag_set(F2FS_I(dir), FI_DIRTY_DIR)) { 56ed57c27fSJaegeuk Kim iput(dir); 57ed57c27fSJaegeuk Kim } else { 585deb8267SJaegeuk Kim add_dirty_dir_inode(dir); 59ed57c27fSJaegeuk Kim set_inode_flag(F2FS_I(dir), FI_DELAY_IPUT); 6074d0b917SJaegeuk Kim } 61d624c96fSJaegeuk Kim 62b7f7a5e0SAl Viro name.len = le32_to_cpu(raw_inode->i_namelen); 63b7f7a5e0SAl Viro name.name = raw_inode->i_name; 64d96b1431SChao Yu 65d96b1431SChao Yu if (unlikely(name.len > F2FS_NAME_LEN)) { 66d96b1431SChao Yu WARN_ON(1); 67d96b1431SChao Yu err = -ENAMETOOLONG; 68d96b1431SChao Yu goto out; 69d96b1431SChao Yu } 706b8213d9SJaegeuk Kim retry: 716b8213d9SJaegeuk Kim de = f2fs_find_entry(dir, &name, &page); 722e5558f4SRuss W. Knize if (de && inode->i_ino == le32_to_cpu(de->ino)) 732e5558f4SRuss W. Knize goto out_unmap_put; 746b8213d9SJaegeuk Kim if (de) { 756b8213d9SJaegeuk Kim einode = f2fs_iget(inode->i_sb, le32_to_cpu(de->ino)); 766b8213d9SJaegeuk Kim if (IS_ERR(einode)) { 776b8213d9SJaegeuk Kim WARN_ON(1); 785c1f9927SChao Yu err = PTR_ERR(einode); 795c1f9927SChao Yu if (err == -ENOENT) 806b8213d9SJaegeuk Kim err = -EEXIST; 812e5558f4SRuss W. Knize goto out_unmap_put; 822e5558f4SRuss W. Knize } 832e5558f4SRuss W. Knize err = acquire_orphan_inode(F2FS_SB(inode->i_sb)); 842e5558f4SRuss W. Knize if (err) { 852e5558f4SRuss W. Knize iput(einode); 862e5558f4SRuss W. Knize goto out_unmap_put; 876b8213d9SJaegeuk Kim } 886b8213d9SJaegeuk Kim f2fs_delete_entry(de, page, einode); 896b8213d9SJaegeuk Kim iput(einode); 906b8213d9SJaegeuk Kim goto retry; 916b8213d9SJaegeuk Kim } 926b8213d9SJaegeuk Kim err = __f2fs_add_link(dir, &name, inode); 932e5558f4SRuss W. Knize goto out; 942e5558f4SRuss W. Knize 952e5558f4SRuss W. Knize out_unmap_put: 962e5558f4SRuss W. Knize kunmap(page); 972e5558f4SRuss W. Knize f2fs_put_page(page, 0); 98d624c96fSJaegeuk Kim out: 996c311ec6SChris Fries f2fs_msg(inode->i_sb, KERN_NOTICE, 1006c311ec6SChris Fries "%s: ino = %x, name = %s, dir = %lx, err = %d", 1016c311ec6SChris Fries __func__, ino_of_node(ipage), raw_inode->i_name, 102f28c06faSDan Carpenter IS_ERR(dir) ? 0 : dir->i_ino, err); 103d624c96fSJaegeuk Kim return err; 104d624c96fSJaegeuk Kim } 105d624c96fSJaegeuk Kim 106d624c96fSJaegeuk Kim static int recover_inode(struct inode *inode, struct page *node_page) 107d624c96fSJaegeuk Kim { 10858bfaf44SJaegeuk Kim struct f2fs_inode *raw_inode = F2FS_INODE(node_page); 109d624c96fSJaegeuk Kim 110f356fe0cSJaegeuk Kim if (!IS_INODE(node_page)) 111f356fe0cSJaegeuk Kim return 0; 112f356fe0cSJaegeuk Kim 11325ca923bSJaegeuk Kim inode->i_mode = le16_to_cpu(raw_inode->i_mode); 114d624c96fSJaegeuk Kim i_size_write(inode, le64_to_cpu(raw_inode->i_size)); 115d624c96fSJaegeuk Kim inode->i_atime.tv_sec = le64_to_cpu(raw_inode->i_mtime); 116d624c96fSJaegeuk Kim inode->i_ctime.tv_sec = le64_to_cpu(raw_inode->i_ctime); 117d624c96fSJaegeuk Kim inode->i_mtime.tv_sec = le64_to_cpu(raw_inode->i_mtime); 118d624c96fSJaegeuk Kim inode->i_atime.tv_nsec = le32_to_cpu(raw_inode->i_mtime_nsec); 119d624c96fSJaegeuk Kim inode->i_ctime.tv_nsec = le32_to_cpu(raw_inode->i_ctime_nsec); 120d624c96fSJaegeuk Kim inode->i_mtime.tv_nsec = le32_to_cpu(raw_inode->i_mtime_nsec); 121d624c96fSJaegeuk Kim 122f356fe0cSJaegeuk Kim if (is_dent_dnode(node_page)) 123d624c96fSJaegeuk Kim return recover_dentry(node_page, inode); 124f356fe0cSJaegeuk Kim 125f356fe0cSJaegeuk Kim f2fs_msg(inode->i_sb, KERN_NOTICE, "recover_inode: ino = %x, name = %s", 126f356fe0cSJaegeuk Kim ino_of_node(node_page), raw_inode->i_name); 127f356fe0cSJaegeuk Kim return 0; 128d624c96fSJaegeuk Kim } 129d624c96fSJaegeuk Kim 130d624c96fSJaegeuk Kim static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) 131d624c96fSJaegeuk Kim { 132d71b5564SJaegeuk Kim unsigned long long cp_ver = cur_cp_version(F2FS_CKPT(sbi)); 133d624c96fSJaegeuk Kim struct curseg_info *curseg; 134d624c96fSJaegeuk Kim struct page *page; 135d624c96fSJaegeuk Kim block_t blkaddr; 136d624c96fSJaegeuk Kim int err = 0; 137d624c96fSJaegeuk Kim 138d624c96fSJaegeuk Kim /* get node pages in the current segment */ 139d624c96fSJaegeuk Kim curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); 140695fd1edSChao Yu blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); 141d624c96fSJaegeuk Kim 142d624c96fSJaegeuk Kim /* read node page */ 143d624c96fSJaegeuk Kim page = alloc_page(GFP_F2FS_ZERO); 144e27dae4dSDan Carpenter if (!page) 145e27dae4dSDan Carpenter return -ENOMEM; 146d624c96fSJaegeuk Kim lock_page(page); 147d624c96fSJaegeuk Kim 148d624c96fSJaegeuk Kim while (1) { 149d624c96fSJaegeuk Kim struct fsync_inode_entry *entry; 150d624c96fSJaegeuk Kim 15193dfe2acSJaegeuk Kim err = f2fs_submit_page_bio(sbi, page, blkaddr, READ_SYNC); 152393ff91fSJaegeuk Kim if (err) 153b9987a27SChao Yu return err; 154d624c96fSJaegeuk Kim 155393ff91fSJaegeuk Kim lock_page(page); 156393ff91fSJaegeuk Kim 1576ead1142SJaegeuk Kim if (cp_ver != cpver_of_node(page)) 158f356fe0cSJaegeuk Kim break; 159d624c96fSJaegeuk Kim 160d624c96fSJaegeuk Kim if (!is_fsync_dnode(page)) 161d624c96fSJaegeuk Kim goto next; 162d624c96fSJaegeuk Kim 163d624c96fSJaegeuk Kim entry = get_fsync_inode(head, ino_of_node(page)); 164d624c96fSJaegeuk Kim if (entry) { 165d624c96fSJaegeuk Kim if (IS_INODE(page) && is_dent_dnode(page)) 166d624c96fSJaegeuk Kim set_inode_flag(F2FS_I(entry->inode), 167d624c96fSJaegeuk Kim FI_INC_LINK); 168d624c96fSJaegeuk Kim } else { 169d624c96fSJaegeuk Kim if (IS_INODE(page) && is_dent_dnode(page)) { 1706ead1142SJaegeuk Kim err = recover_inode_page(sbi, page); 1716ead1142SJaegeuk Kim if (err) 172f356fe0cSJaegeuk Kim break; 173d624c96fSJaegeuk Kim } 174d624c96fSJaegeuk Kim 175d624c96fSJaegeuk Kim /* add this fsync inode to the list */ 176d624c96fSJaegeuk Kim entry = kmem_cache_alloc(fsync_entry_slab, GFP_NOFS); 177d624c96fSJaegeuk Kim if (!entry) { 178d624c96fSJaegeuk Kim err = -ENOMEM; 179f356fe0cSJaegeuk Kim break; 180d624c96fSJaegeuk Kim } 181d624c96fSJaegeuk Kim 182d624c96fSJaegeuk Kim entry->inode = f2fs_iget(sbi->sb, ino_of_node(page)); 183d624c96fSJaegeuk Kim if (IS_ERR(entry->inode)) { 184d624c96fSJaegeuk Kim err = PTR_ERR(entry->inode); 185fd8bb65fSNamjae Jeon kmem_cache_free(fsync_entry_slab, entry); 186f356fe0cSJaegeuk Kim break; 187d624c96fSJaegeuk Kim } 188fd8bb65fSNamjae Jeon list_add_tail(&entry->list, head); 189d624c96fSJaegeuk Kim } 190addbe45bSJaegeuk Kim entry->blkaddr = blkaddr; 191addbe45bSJaegeuk Kim 192d624c96fSJaegeuk Kim err = recover_inode(entry->inode, page); 193f356fe0cSJaegeuk Kim if (err && err != -ENOENT) 194f356fe0cSJaegeuk Kim break; 195d624c96fSJaegeuk Kim next: 196d624c96fSJaegeuk Kim /* check next segment */ 197d624c96fSJaegeuk Kim blkaddr = next_blkaddr_of_node(page); 198d624c96fSJaegeuk Kim } 199b9987a27SChao Yu 200d624c96fSJaegeuk Kim unlock_page(page); 201d624c96fSJaegeuk Kim __free_pages(page, 0); 202b9987a27SChao Yu 203d624c96fSJaegeuk Kim return err; 204d624c96fSJaegeuk Kim } 205d624c96fSJaegeuk Kim 2065ebefc5bSGu Zheng static void destroy_fsync_dnodes(struct list_head *head) 207d624c96fSJaegeuk Kim { 208d8b79b2fSDan Carpenter struct fsync_inode_entry *entry, *tmp; 209d8b79b2fSDan Carpenter 210d8b79b2fSDan Carpenter list_for_each_entry_safe(entry, tmp, head, list) { 211d624c96fSJaegeuk Kim iput(entry->inode); 212d624c96fSJaegeuk Kim list_del(&entry->list); 213d624c96fSJaegeuk Kim kmem_cache_free(fsync_entry_slab, entry); 214d624c96fSJaegeuk Kim } 215d624c96fSJaegeuk Kim } 216d624c96fSJaegeuk Kim 21739cf72cfSJaegeuk Kim static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi, 218b292dcabSJaegeuk Kim block_t blkaddr, struct dnode_of_data *dn) 219d624c96fSJaegeuk Kim { 220d624c96fSJaegeuk Kim struct seg_entry *sentry; 221d624c96fSJaegeuk Kim unsigned int segno = GET_SEGNO(sbi, blkaddr); 222491c0854SJaegeuk Kim unsigned short blkoff = GET_BLKOFF_FROM_SEG0(sbi, blkaddr); 223f6517cfcSJaegeuk Kim struct f2fs_summary_block *sum_node; 224d624c96fSJaegeuk Kim struct f2fs_summary sum; 225f6517cfcSJaegeuk Kim struct page *sum_page, *node_page; 226b292dcabSJaegeuk Kim nid_t ino, nid; 227d624c96fSJaegeuk Kim struct inode *inode; 228de93653fSJaegeuk Kim unsigned int offset; 229d624c96fSJaegeuk Kim block_t bidx; 230d624c96fSJaegeuk Kim int i; 231d624c96fSJaegeuk Kim 232d624c96fSJaegeuk Kim sentry = get_seg_entry(sbi, segno); 233d624c96fSJaegeuk Kim if (!f2fs_test_bit(blkoff, sentry->cur_valid_map)) 23439cf72cfSJaegeuk Kim return 0; 235d624c96fSJaegeuk Kim 236d624c96fSJaegeuk Kim /* Get the previous summary */ 237d624c96fSJaegeuk Kim for (i = CURSEG_WARM_DATA; i <= CURSEG_COLD_DATA; i++) { 238d624c96fSJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, i); 239d624c96fSJaegeuk Kim if (curseg->segno == segno) { 240d624c96fSJaegeuk Kim sum = curseg->sum_blk->entries[blkoff]; 241f6517cfcSJaegeuk Kim goto got_it; 242d624c96fSJaegeuk Kim } 243d624c96fSJaegeuk Kim } 244d624c96fSJaegeuk Kim 245f6517cfcSJaegeuk Kim sum_page = get_sum_page(sbi, segno); 246f6517cfcSJaegeuk Kim sum_node = (struct f2fs_summary_block *)page_address(sum_page); 247f6517cfcSJaegeuk Kim sum = sum_node->entries[blkoff]; 248f6517cfcSJaegeuk Kim f2fs_put_page(sum_page, 1); 249f6517cfcSJaegeuk Kim got_it: 250b292dcabSJaegeuk Kim /* Use the locked dnode page and inode */ 251b292dcabSJaegeuk Kim nid = le32_to_cpu(sum.nid); 252b292dcabSJaegeuk Kim if (dn->inode->i_ino == nid) { 253b292dcabSJaegeuk Kim struct dnode_of_data tdn = *dn; 254b292dcabSJaegeuk Kim tdn.nid = nid; 255b292dcabSJaegeuk Kim tdn.node_page = dn->inode_page; 256060dd67bSJaegeuk Kim tdn.ofs_in_node = le16_to_cpu(sum.ofs_in_node); 257b292dcabSJaegeuk Kim truncate_data_blocks_range(&tdn, 1); 25839cf72cfSJaegeuk Kim return 0; 259b292dcabSJaegeuk Kim } else if (dn->nid == nid) { 260b292dcabSJaegeuk Kim struct dnode_of_data tdn = *dn; 261060dd67bSJaegeuk Kim tdn.ofs_in_node = le16_to_cpu(sum.ofs_in_node); 262b292dcabSJaegeuk Kim truncate_data_blocks_range(&tdn, 1); 26339cf72cfSJaegeuk Kim return 0; 264b292dcabSJaegeuk Kim } 265b292dcabSJaegeuk Kim 266d624c96fSJaegeuk Kim /* Get the node page */ 267b292dcabSJaegeuk Kim node_page = get_node_page(sbi, nid); 26839cf72cfSJaegeuk Kim if (IS_ERR(node_page)) 26939cf72cfSJaegeuk Kim return PTR_ERR(node_page); 270de93653fSJaegeuk Kim 271de93653fSJaegeuk Kim offset = ofs_of_node(node_page); 272d624c96fSJaegeuk Kim ino = ino_of_node(node_page); 273d624c96fSJaegeuk Kim f2fs_put_page(node_page, 1); 274d624c96fSJaegeuk Kim 275d624c96fSJaegeuk Kim /* Deallocate previous index in the node page */ 276d4686d56SJaegeuk Kim inode = f2fs_iget(sbi->sb, ino); 27706025f4dSNamjae Jeon if (IS_ERR(inode)) 27839cf72cfSJaegeuk Kim return PTR_ERR(inode); 27906025f4dSNamjae Jeon 280de93653fSJaegeuk Kim bidx = start_bidx_of_node(offset, F2FS_I(inode)) + 281de93653fSJaegeuk Kim le16_to_cpu(sum.ofs_in_node); 282de93653fSJaegeuk Kim 283d624c96fSJaegeuk Kim truncate_hole(inode, bidx, bidx + 1); 284d624c96fSJaegeuk Kim iput(inode); 28539cf72cfSJaegeuk Kim return 0; 286d624c96fSJaegeuk Kim } 287d624c96fSJaegeuk Kim 2886ead1142SJaegeuk Kim static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, 289d624c96fSJaegeuk Kim struct page *page, block_t blkaddr) 290d624c96fSJaegeuk Kim { 291de93653fSJaegeuk Kim struct f2fs_inode_info *fi = F2FS_I(inode); 292d624c96fSJaegeuk Kim unsigned int start, end; 293d624c96fSJaegeuk Kim struct dnode_of_data dn; 294d624c96fSJaegeuk Kim struct f2fs_summary sum; 295d624c96fSJaegeuk Kim struct node_info ni; 296f356fe0cSJaegeuk Kim int err = 0, recovered = 0; 297d624c96fSJaegeuk Kim 2981e1bb4baSJaegeuk Kim if (recover_inline_data(inode, page)) 2991e1bb4baSJaegeuk Kim goto out; 3001e1bb4baSJaegeuk Kim 301abb2366cSJaegeuk Kim if (recover_xattr_data(inode, page, blkaddr)) 302abb2366cSJaegeuk Kim goto out; 303abb2366cSJaegeuk Kim 304de93653fSJaegeuk Kim start = start_bidx_of_node(ofs_of_node(page), fi); 3056403eb1fSChao Yu end = start + ADDRS_PER_PAGE(page, fi); 306d624c96fSJaegeuk Kim 307e479556bSGu Zheng f2fs_lock_op(sbi); 3081e1bb4baSJaegeuk Kim 309d624c96fSJaegeuk Kim set_new_dnode(&dn, inode, NULL, NULL, 0); 31039936837SJaegeuk Kim 3116ead1142SJaegeuk Kim err = get_dnode_of_data(&dn, start, ALLOC_NODE); 31239936837SJaegeuk Kim if (err) { 313e479556bSGu Zheng f2fs_unlock_op(sbi); 3141e1bb4baSJaegeuk Kim goto out; 31539936837SJaegeuk Kim } 316d624c96fSJaegeuk Kim 3173cb5ad15SJaegeuk Kim f2fs_wait_on_page_writeback(dn.node_page, NODE); 318d624c96fSJaegeuk Kim 319d624c96fSJaegeuk Kim get_node_info(sbi, dn.nid, &ni); 3205d56b671SJaegeuk Kim f2fs_bug_on(ni.ino != ino_of_node(page)); 3215d56b671SJaegeuk Kim f2fs_bug_on(ofs_of_node(dn.node_page) != ofs_of_node(page)); 322d624c96fSJaegeuk Kim 323d624c96fSJaegeuk Kim for (; start < end; start++) { 324d624c96fSJaegeuk Kim block_t src, dest; 325d624c96fSJaegeuk Kim 326d624c96fSJaegeuk Kim src = datablock_addr(dn.node_page, dn.ofs_in_node); 327d624c96fSJaegeuk Kim dest = datablock_addr(page, dn.ofs_in_node); 328d624c96fSJaegeuk Kim 329d624c96fSJaegeuk Kim if (src != dest && dest != NEW_ADDR && dest != NULL_ADDR) { 330d624c96fSJaegeuk Kim if (src == NULL_ADDR) { 3315d56b671SJaegeuk Kim err = reserve_new_block(&dn); 332d624c96fSJaegeuk Kim /* We should not get -ENOSPC */ 3335d56b671SJaegeuk Kim f2fs_bug_on(err); 334d624c96fSJaegeuk Kim } 335d624c96fSJaegeuk Kim 336d624c96fSJaegeuk Kim /* Check the previous node page having this index */ 33739cf72cfSJaegeuk Kim err = check_index_in_prev_nodes(sbi, dest, &dn); 33839cf72cfSJaegeuk Kim if (err) 33939cf72cfSJaegeuk Kim goto err; 340d624c96fSJaegeuk Kim 341d624c96fSJaegeuk Kim set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version); 342d624c96fSJaegeuk Kim 343d624c96fSJaegeuk Kim /* write dummy data page */ 344d624c96fSJaegeuk Kim recover_data_page(sbi, NULL, &sum, src, dest); 345d624c96fSJaegeuk Kim update_extent_cache(dest, &dn); 346f356fe0cSJaegeuk Kim recovered++; 347d624c96fSJaegeuk Kim } 348d624c96fSJaegeuk Kim dn.ofs_in_node++; 349d624c96fSJaegeuk Kim } 350d624c96fSJaegeuk Kim 351d624c96fSJaegeuk Kim /* write node page in place */ 352d624c96fSJaegeuk Kim set_summary(&sum, dn.nid, 0, 0); 353d624c96fSJaegeuk Kim if (IS_INODE(dn.node_page)) 354d624c96fSJaegeuk Kim sync_inode_page(&dn); 355d624c96fSJaegeuk Kim 356d624c96fSJaegeuk Kim copy_node_footer(dn.node_page, page); 357d624c96fSJaegeuk Kim fill_node_footer(dn.node_page, dn.nid, ni.ino, 358d624c96fSJaegeuk Kim ofs_of_node(page), false); 359d624c96fSJaegeuk Kim set_page_dirty(dn.node_page); 360d624c96fSJaegeuk Kim 361d624c96fSJaegeuk Kim recover_node_page(sbi, dn.node_page, &sum, &ni, blkaddr); 36239cf72cfSJaegeuk Kim err: 363d624c96fSJaegeuk Kim f2fs_put_dnode(&dn); 364e479556bSGu Zheng f2fs_unlock_op(sbi); 3651e1bb4baSJaegeuk Kim out: 3666c311ec6SChris Fries f2fs_msg(sbi->sb, KERN_NOTICE, 3676c311ec6SChris Fries "recover_data: ino = %lx, recovered = %d blocks, err = %d", 36839cf72cfSJaegeuk Kim inode->i_ino, recovered, err); 36939cf72cfSJaegeuk Kim return err; 370d624c96fSJaegeuk Kim } 371d624c96fSJaegeuk Kim 3726ead1142SJaegeuk Kim static int recover_data(struct f2fs_sb_info *sbi, 373d624c96fSJaegeuk Kim struct list_head *head, int type) 374d624c96fSJaegeuk Kim { 375d71b5564SJaegeuk Kim unsigned long long cp_ver = cur_cp_version(F2FS_CKPT(sbi)); 376d624c96fSJaegeuk Kim struct curseg_info *curseg; 377d624c96fSJaegeuk Kim struct page *page; 3786ead1142SJaegeuk Kim int err = 0; 379d624c96fSJaegeuk Kim block_t blkaddr; 380d624c96fSJaegeuk Kim 381d624c96fSJaegeuk Kim /* get node pages in the current segment */ 382d624c96fSJaegeuk Kim curseg = CURSEG_I(sbi, type); 383d624c96fSJaegeuk Kim blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); 384d624c96fSJaegeuk Kim 385d624c96fSJaegeuk Kim /* read node page */ 386a0acdfe0SChao Yu page = alloc_page(GFP_F2FS_ZERO); 387e27dae4dSDan Carpenter if (!page) 3886ead1142SJaegeuk Kim return -ENOMEM; 3896ead1142SJaegeuk Kim 390d624c96fSJaegeuk Kim lock_page(page); 391d624c96fSJaegeuk Kim 392d624c96fSJaegeuk Kim while (1) { 393d624c96fSJaegeuk Kim struct fsync_inode_entry *entry; 394d624c96fSJaegeuk Kim 39593dfe2acSJaegeuk Kim err = f2fs_submit_page_bio(sbi, page, blkaddr, READ_SYNC); 3966ead1142SJaegeuk Kim if (err) 397b9987a27SChao Yu return err; 398d624c96fSJaegeuk Kim 399393ff91fSJaegeuk Kim lock_page(page); 400393ff91fSJaegeuk Kim 401d624c96fSJaegeuk Kim if (cp_ver != cpver_of_node(page)) 40245856affSJaegeuk Kim break; 403d624c96fSJaegeuk Kim 404d624c96fSJaegeuk Kim entry = get_fsync_inode(head, ino_of_node(page)); 405d624c96fSJaegeuk Kim if (!entry) 406d624c96fSJaegeuk Kim goto next; 407d624c96fSJaegeuk Kim 4086ead1142SJaegeuk Kim err = do_recover_data(sbi, entry->inode, page, blkaddr); 4096ead1142SJaegeuk Kim if (err) 41045856affSJaegeuk Kim break; 411d624c96fSJaegeuk Kim 412d624c96fSJaegeuk Kim if (entry->blkaddr == blkaddr) { 413d624c96fSJaegeuk Kim iput(entry->inode); 414d624c96fSJaegeuk Kim list_del(&entry->list); 415d624c96fSJaegeuk Kim kmem_cache_free(fsync_entry_slab, entry); 416d624c96fSJaegeuk Kim } 417d624c96fSJaegeuk Kim next: 418d624c96fSJaegeuk Kim /* check next segment */ 419d624c96fSJaegeuk Kim blkaddr = next_blkaddr_of_node(page); 420d624c96fSJaegeuk Kim } 421b9987a27SChao Yu 422d624c96fSJaegeuk Kim unlock_page(page); 423d624c96fSJaegeuk Kim __free_pages(page, 0); 424d624c96fSJaegeuk Kim 4256ead1142SJaegeuk Kim if (!err) 426d624c96fSJaegeuk Kim allocate_new_segments(sbi); 4276ead1142SJaegeuk Kim return err; 428d624c96fSJaegeuk Kim } 429d624c96fSJaegeuk Kim 4306ead1142SJaegeuk Kim int recover_fsync_data(struct f2fs_sb_info *sbi) 431d624c96fSJaegeuk Kim { 432d624c96fSJaegeuk Kim struct list_head inode_list; 4336ead1142SJaegeuk Kim int err; 434aabe5136SHaicheng Li bool need_writecp = false; 435d624c96fSJaegeuk Kim 436d624c96fSJaegeuk Kim fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry", 437e8512d2eSGu Zheng sizeof(struct fsync_inode_entry)); 4386bacf52fSJaegeuk Kim if (!fsync_entry_slab) 4396ead1142SJaegeuk Kim return -ENOMEM; 440d624c96fSJaegeuk Kim 441d624c96fSJaegeuk Kim INIT_LIST_HEAD(&inode_list); 442d624c96fSJaegeuk Kim 443d624c96fSJaegeuk Kim /* step #1: find fsynced inode numbers */ 444aabe5136SHaicheng Li sbi->por_doing = true; 4456ead1142SJaegeuk Kim err = find_fsync_dnodes(sbi, &inode_list); 4466ead1142SJaegeuk Kim if (err) 447d624c96fSJaegeuk Kim goto out; 448d624c96fSJaegeuk Kim 449d624c96fSJaegeuk Kim if (list_empty(&inode_list)) 450d624c96fSJaegeuk Kim goto out; 451d624c96fSJaegeuk Kim 452aabe5136SHaicheng Li need_writecp = true; 453691c6fd2SChao Yu 454d624c96fSJaegeuk Kim /* step #2: recover data */ 4556ead1142SJaegeuk Kim err = recover_data(sbi, &inode_list, CURSEG_WARM_NODE); 4565d56b671SJaegeuk Kim f2fs_bug_on(!list_empty(&inode_list)); 457d624c96fSJaegeuk Kim out: 4585ebefc5bSGu Zheng destroy_fsync_dnodes(&inode_list); 459d624c96fSJaegeuk Kim kmem_cache_destroy(fsync_entry_slab); 460aabe5136SHaicheng Li sbi->por_doing = false; 461691c6fd2SChao Yu if (!err && need_writecp) 46243727527SJaegeuk Kim write_checkpoint(sbi, false); 4636ead1142SJaegeuk Kim return err; 464d624c96fSJaegeuk Kim } 465