]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - fs/f2fs/node.c
f2fs: combine nat_bits and free_nid_bitmap cache
[karo-tx-linux.git] / fs / f2fs / node.c
index b9078fdb37437bff3e6e46ca4f72f4680cb9109f..481aa8dc79f46f4c156cf67cca665e8160e36e6a 100644 (file)
@@ -245,12 +245,24 @@ bool need_inode_block_update(struct f2fs_sb_info *sbi, nid_t ino)
        return need_update;
 }
 
-static struct nat_entry *grab_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid)
+static struct nat_entry *grab_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid,
+                                                               bool no_fail)
 {
        struct nat_entry *new;
 
-       new = f2fs_kmem_cache_alloc(nat_entry_slab, GFP_NOFS);
-       f2fs_radix_tree_insert(&nm_i->nat_root, nid, new);
+       if (no_fail) {
+               new = f2fs_kmem_cache_alloc(nat_entry_slab, GFP_NOFS);
+               f2fs_radix_tree_insert(&nm_i->nat_root, nid, new);
+       } else {
+               new = kmem_cache_alloc(nat_entry_slab, GFP_NOFS);
+               if (!new)
+                       return NULL;
+               if (radix_tree_insert(&nm_i->nat_root, nid, new)) {
+                       kmem_cache_free(nat_entry_slab, new);
+                       return NULL;
+               }
+       }
+
        memset(new, 0, sizeof(struct nat_entry));
        nat_set_nid(new, nid);
        nat_reset_flag(new);
@@ -267,8 +279,9 @@ static void cache_nat_entry(struct f2fs_sb_info *sbi, nid_t nid,
 
        e = __lookup_nat_cache(nm_i, nid);
        if (!e) {
-               e = grab_nat_entry(nm_i, nid);
-               node_info_from_raw_nat(&e->ni, ne);
+               e = grab_nat_entry(nm_i, nid, false);
+               if (e)
+                       node_info_from_raw_nat(&e->ni, ne);
        } else {
                f2fs_bug_on(sbi, nat_get_ino(e) != le32_to_cpu(ne->ino) ||
                                nat_get_blkaddr(e) !=
@@ -286,7 +299,7 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni,
        down_write(&nm_i->nat_tree_lock);
        e = __lookup_nat_cache(nm_i, ni->nid);
        if (!e) {
-               e = grab_nat_entry(nm_i, ni->nid);
+               e = grab_nat_entry(nm_i, ni->nid, true);
                copy_node_info(&e->ni, ni);
                f2fs_bug_on(sbi, ni->blk_addr == NEW_ADDR);
        } else if (new_blkaddr == NEW_ADDR) {
@@ -958,9 +971,6 @@ int truncate_xattr_node(struct inode *inode, struct page *page)
 
        f2fs_i_xnid_write(inode, 0);
 
-       /* need to do checkpoint during fsync */
-       F2FS_I(inode)->xattr_ver = cur_cp_version(F2FS_CKPT(sbi));
-
        set_new_dnode(&dn, inode, page, npage, nid);
 
        if (page)
@@ -1018,7 +1028,7 @@ struct page *new_node_page(struct dnode_of_data *dn,
                                unsigned int ofs, struct page *ipage)
 {
        struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
-       struct node_info old_ni, new_ni;
+       struct node_info new_ni;
        struct page *page;
        int err;
 
@@ -1033,13 +1043,15 @@ struct page *new_node_page(struct dnode_of_data *dn,
                err = -ENOSPC;
                goto fail;
        }
-
-       get_node_info(sbi, dn->nid, &old_ni);
-
-       /* Reinitialize old_ni with new node page */
-       f2fs_bug_on(sbi, old_ni.blk_addr != NULL_ADDR);
-       new_ni = old_ni;
+#ifdef CONFIG_F2FS_CHECK_FS
+       get_node_info(sbi, dn->nid, &new_ni);
+       f2fs_bug_on(sbi, new_ni.blk_addr != NULL_ADDR);
+#endif
+       new_ni.nid = dn->nid;
        new_ni.ino = dn->inode->i_ino;
+       new_ni.blk_addr = NULL_ADDR;
+       new_ni.flag = 0;
+       new_ni.version = 0;
        set_node_addr(sbi, &new_ni, NEW_ADDR, false);
 
        f2fs_wait_on_page_writeback(page, NODE, true);
@@ -1305,16 +1317,99 @@ continue_unlock:
        return last_page;
 }
 
+static int __write_node_page(struct page *page, bool atomic, bool *submitted,
+                               struct writeback_control *wbc)
+{
+       struct f2fs_sb_info *sbi = F2FS_P_SB(page);
+       nid_t nid;
+       struct node_info ni;
+       struct f2fs_io_info fio = {
+               .sbi = sbi,
+               .type = NODE,
+               .op = REQ_OP_WRITE,
+               .op_flags = wbc_to_write_flags(wbc),
+               .page = page,
+               .encrypted_page = NULL,
+               .submitted = false,
+       };
+
+       trace_f2fs_writepage(page, NODE);
+
+       if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
+               goto redirty_out;
+       if (unlikely(f2fs_cp_error(sbi)))
+               goto redirty_out;
+
+       /* get old block addr of this node page */
+       nid = nid_of_node(page);
+       f2fs_bug_on(sbi, page->index != nid);
+
+       if (wbc->for_reclaim) {
+               if (!down_read_trylock(&sbi->node_write))
+                       goto redirty_out;
+       } else {
+               down_read(&sbi->node_write);
+       }
+
+       get_node_info(sbi, nid, &ni);
+
+       /* This page is already truncated */
+       if (unlikely(ni.blk_addr == NULL_ADDR)) {
+               ClearPageUptodate(page);
+               dec_page_count(sbi, F2FS_DIRTY_NODES);
+               up_read(&sbi->node_write);
+               unlock_page(page);
+               return 0;
+       }
+
+       if (atomic && !test_opt(sbi, NOBARRIER))
+               fio.op_flags |= REQ_PREFLUSH | REQ_FUA;
+
+       set_page_writeback(page);
+       fio.old_blkaddr = ni.blk_addr;
+       write_node_page(nid, &fio);
+       set_node_addr(sbi, &ni, fio.new_blkaddr, is_fsync_dnode(page));
+       dec_page_count(sbi, F2FS_DIRTY_NODES);
+       up_read(&sbi->node_write);
+
+       if (wbc->for_reclaim) {
+               f2fs_submit_merged_bio_cond(sbi, page->mapping->host, 0,
+                                               page->index, NODE, WRITE);
+               submitted = NULL;
+       }
+
+       unlock_page(page);
+
+       if (unlikely(f2fs_cp_error(sbi))) {
+               f2fs_submit_merged_bio(sbi, NODE, WRITE);
+               submitted = NULL;
+       }
+       if (submitted)
+               *submitted = fio.submitted;
+
+       return 0;
+
+redirty_out:
+       redirty_page_for_writepage(wbc, page);
+       return AOP_WRITEPAGE_ACTIVATE;
+}
+
+static int f2fs_write_node_page(struct page *page,
+                               struct writeback_control *wbc)
+{
+       return __write_node_page(page, false, NULL, wbc);
+}
+
 int fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode,
                        struct writeback_control *wbc, bool atomic)
 {
        pgoff_t index, end;
+       pgoff_t last_idx = ULONG_MAX;
        struct pagevec pvec;
        int ret = 0;
        struct page *last_page = NULL;
        bool marked = false;
        nid_t ino = inode->i_ino;
-       int nwritten = 0;
 
        if (atomic) {
                last_page = last_fsync_dnode(sbi, ino);
@@ -1336,6 +1431,7 @@ retry:
 
                for (i = 0; i < nr_pages; i++) {
                        struct page *page = pvec.pages[i];
+                       bool submitted = false;
 
                        if (unlikely(f2fs_cp_error(sbi))) {
                                f2fs_put_page(last_page, 0);
@@ -1384,13 +1480,15 @@ continue_unlock:
                        if (!clear_page_dirty_for_io(page))
                                goto continue_unlock;
 
-                       ret = NODE_MAPPING(sbi)->a_ops->writepage(page, wbc);
+                       ret = __write_node_page(page, atomic &&
+                                               page == last_page,
+                                               &submitted, wbc);
                        if (ret) {
                                unlock_page(page);
                                f2fs_put_page(last_page, 0);
                                break;
-                       } else {
-                               nwritten++;
+                       } else if (submitted) {
+                               last_idx = page->index;
                        }
 
                        if (page == last_page) {
@@ -1416,8 +1514,9 @@ continue_unlock:
                goto retry;
        }
 out:
-       if (nwritten)
-               f2fs_submit_merged_bio_cond(sbi, NULL, NULL, ino, NODE, WRITE);
+       if (last_idx != ULONG_MAX)
+               f2fs_submit_merged_bio_cond(sbi, NULL, ino, last_idx,
+                                                       NODE, WRITE);
        return ret ? -EIO: 0;
 }
 
@@ -1445,6 +1544,7 @@ next_step:
 
                for (i = 0; i < nr_pages; i++) {
                        struct page *page = pvec.pages[i];
+                       bool submitted = false;
 
                        if (unlikely(f2fs_cp_error(sbi))) {
                                pagevec_release(&pvec);
@@ -1498,9 +1598,10 @@ continue_unlock:
                        set_fsync_mark(page, 0);
                        set_dentry_mark(page, 0);
 
-                       if (NODE_MAPPING(sbi)->a_ops->writepage(page, wbc))
+                       ret = __write_node_page(page, false, &submitted, wbc);
+                       if (ret)
                                unlock_page(page);
-                       else
+                       else if (submitted)
                                nwritten++;
 
                        if (--wbc->nr_to_write == 0)
@@ -1564,72 +1665,6 @@ int wait_on_node_pages_writeback(struct f2fs_sb_info *sbi, nid_t ino)
        return ret;
 }
 
-static int f2fs_write_node_page(struct page *page,
-                               struct writeback_control *wbc)
-{
-       struct f2fs_sb_info *sbi = F2FS_P_SB(page);
-       nid_t nid;
-       struct node_info ni;
-       struct f2fs_io_info fio = {
-               .sbi = sbi,
-               .type = NODE,
-               .op = REQ_OP_WRITE,
-               .op_flags = wbc_to_write_flags(wbc),
-               .page = page,
-               .encrypted_page = NULL,
-       };
-
-       trace_f2fs_writepage(page, NODE);
-
-       if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
-               goto redirty_out;
-       if (unlikely(f2fs_cp_error(sbi)))
-               goto redirty_out;
-
-       /* get old block addr of this node page */
-       nid = nid_of_node(page);
-       f2fs_bug_on(sbi, page->index != nid);
-
-       if (wbc->for_reclaim) {
-               if (!down_read_trylock(&sbi->node_write))
-                       goto redirty_out;
-       } else {
-               down_read(&sbi->node_write);
-       }
-
-       get_node_info(sbi, nid, &ni);
-
-       /* This page is already truncated */
-       if (unlikely(ni.blk_addr == NULL_ADDR)) {
-               ClearPageUptodate(page);
-               dec_page_count(sbi, F2FS_DIRTY_NODES);
-               up_read(&sbi->node_write);
-               unlock_page(page);
-               return 0;
-       }
-
-       set_page_writeback(page);
-       fio.old_blkaddr = ni.blk_addr;
-       write_node_page(nid, &fio);
-       set_node_addr(sbi, &ni, fio.new_blkaddr, is_fsync_dnode(page));
-       dec_page_count(sbi, F2FS_DIRTY_NODES);
-       up_read(&sbi->node_write);
-
-       if (wbc->for_reclaim)
-               f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, NODE, WRITE);
-
-       unlock_page(page);
-
-       if (unlikely(f2fs_cp_error(sbi)))
-               f2fs_submit_merged_bio(sbi, NODE, WRITE);
-
-       return 0;
-
-redirty_out:
-       redirty_page_for_writepage(wbc, page);
-       return AOP_WRITEPAGE_ACTIVATE;
-}
-
 static int f2fs_write_node_pages(struct address_space *mapping,
                            struct writeback_control *wbc)
 {
@@ -1727,7 +1762,8 @@ static void __remove_nid_from_list(struct f2fs_sb_info *sbi,
                radix_tree_delete(&nm_i->free_nid_root, i->nid);
 }
 
-static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build)
+/* return if the nid is recognized as free */
+static bool add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build)
 {
        struct f2fs_nm_info *nm_i = NM_I(sbi);
        struct free_nid *i;
@@ -1736,14 +1772,14 @@ static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build)
 
        /* 0 nid should not be used */
        if (unlikely(nid == 0))
-               return 0;
+               return false;
 
        if (build) {
                /* do not add allocated nids */
                ne = __lookup_nat_cache(nm_i, nid);
                if (ne && (!get_nat_flag(ne, IS_CHECKPOINTED) ||
                                nat_get_blkaddr(ne) != NULL_ADDR))
-                       return 0;
+                       return false;
        }
 
        i = f2fs_kmem_cache_alloc(free_nid_slab, GFP_NOFS);
@@ -1752,7 +1788,7 @@ static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build)
 
        if (radix_tree_preload(GFP_NOFS)) {
                kmem_cache_free(free_nid_slab, i);
-               return 0;
+               return true;
        }
 
        spin_lock(&nm_i->nid_list_lock);
@@ -1761,9 +1797,9 @@ static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build)
        radix_tree_preload_end();
        if (err) {
                kmem_cache_free(free_nid_slab, i);
-               return 0;
+               return true;
        }
-       return 1;
+       return true;
 }
 
 static void remove_free_nid(struct f2fs_sb_info *sbi, nid_t nid)
@@ -1784,17 +1820,49 @@ static void remove_free_nid(struct f2fs_sb_info *sbi, nid_t nid)
                kmem_cache_free(free_nid_slab, i);
 }
 
+static void update_free_nid_bitmap(struct f2fs_sb_info *sbi, nid_t nid,
+                       bool set, bool build, bool locked)
+{
+       struct f2fs_nm_info *nm_i = NM_I(sbi);
+       unsigned int nat_ofs = NAT_BLOCK_OFFSET(nid);
+       unsigned int nid_ofs = nid - START_NID(nid);
+
+       if (!test_bit_le(nat_ofs, nm_i->nat_block_bitmap))
+               return;
+
+       if (set)
+               __set_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]);
+       else
+               __clear_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]);
+
+       if (!locked)
+               spin_lock(&nm_i->free_nid_lock);
+       if (set)
+               nm_i->free_nid_count[nat_ofs]++;
+       else if (!build)
+               nm_i->free_nid_count[nat_ofs]--;
+       if (!locked)
+               spin_unlock(&nm_i->free_nid_lock);
+}
+
 static void scan_nat_page(struct f2fs_sb_info *sbi,
                        struct page *nat_page, nid_t start_nid)
 {
        struct f2fs_nm_info *nm_i = NM_I(sbi);
        struct f2fs_nat_block *nat_blk = page_address(nat_page);
        block_t blk_addr;
+       unsigned int nat_ofs = NAT_BLOCK_OFFSET(start_nid);
        int i;
 
+       if (test_bit_le(nat_ofs, nm_i->nat_block_bitmap))
+               return;
+
+       __set_bit_le(nat_ofs, nm_i->nat_block_bitmap);
+
        i = start_nid % NAT_ENTRY_PER_BLOCK;
 
        for (; i < NAT_ENTRY_PER_BLOCK; i++, start_nid++) {
+               bool freed = false;
 
                if (unlikely(start_nid >= nm_i->max_nid))
                        break;
@@ -1802,11 +1870,56 @@ static void scan_nat_page(struct f2fs_sb_info *sbi,
                blk_addr = le32_to_cpu(nat_blk->entries[i].block_addr);
                f2fs_bug_on(sbi, blk_addr == NEW_ADDR);
                if (blk_addr == NULL_ADDR)
-                       add_free_nid(sbi, start_nid, true);
+                       freed = add_free_nid(sbi, start_nid, true);
+               update_free_nid_bitmap(sbi, start_nid, freed, true, false);
+       }
+}
+
+static void scan_free_nid_bits(struct f2fs_sb_info *sbi)
+{
+       struct f2fs_nm_info *nm_i = NM_I(sbi);
+       struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
+       struct f2fs_journal *journal = curseg->journal;
+       unsigned int i, idx;
+
+       down_read(&nm_i->nat_tree_lock);
+
+       for (i = 0; i < nm_i->nat_blocks; i++) {
+               if (!test_bit_le(i, nm_i->nat_block_bitmap))
+                       continue;
+               if (!nm_i->free_nid_count[i])
+                       continue;
+               for (idx = 0; idx < NAT_ENTRY_PER_BLOCK; idx++) {
+                       nid_t nid;
+
+                       if (!test_bit_le(idx, nm_i->free_nid_bitmap[i]))
+                               continue;
+
+                       nid = i * NAT_ENTRY_PER_BLOCK + idx;
+                       add_free_nid(sbi, nid, true);
+
+                       if (nm_i->nid_cnt[FREE_NID_LIST] >= MAX_FREE_NIDS)
+                               goto out;
+               }
+       }
+out:
+       down_read(&curseg->journal_rwsem);
+       for (i = 0; i < nats_in_cursum(journal); i++) {
+               block_t addr;
+               nid_t nid;
+
+               addr = le32_to_cpu(nat_in_journal(journal, i).block_addr);
+               nid = le32_to_cpu(nid_in_journal(journal, i));
+               if (addr == NULL_ADDR)
+                       add_free_nid(sbi, nid, true);
+               else
+                       remove_free_nid(sbi, nid);
        }
+       up_read(&curseg->journal_rwsem);
+       up_read(&nm_i->nat_tree_lock);
 }
 
-static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync)
+static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount)
 {
        struct f2fs_nm_info *nm_i = NM_I(sbi);
        struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
@@ -1821,6 +1934,14 @@ static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync)
        if (!sync && !available_free_memory(sbi, FREE_NIDS))
                return;
 
+       if (!mount) {
+               /* try to find free nids in free_nid_bitmap */
+               scan_free_nid_bits(sbi);
+
+               if (nm_i->nid_cnt[FREE_NID_LIST])
+                       return;
+       }
+
        /* readahead nat pages to be scanned */
        ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nid), FREE_NID_PAGES,
                                                        META_NAT, true);
@@ -1863,10 +1984,10 @@ static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync)
                                        nm_i->ra_nid_pages, META_NAT, false);
 }
 
-void build_free_nids(struct f2fs_sb_info *sbi, bool sync)
+void build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount)
 {
        mutex_lock(&NM_I(sbi)->build_lock);
-       __build_free_nids(sbi, sync);
+       __build_free_nids(sbi, sync, mount);
        mutex_unlock(&NM_I(sbi)->build_lock);
 }
 
@@ -1881,8 +2002,10 @@ bool alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid)
        struct free_nid *i = NULL;
 retry:
 #ifdef CONFIG_F2FS_FAULT_INJECTION
-       if (time_to_inject(sbi, FAULT_ALLOC_NID))
+       if (time_to_inject(sbi, FAULT_ALLOC_NID)) {
+               f2fs_show_injection_info(FAULT_ALLOC_NID);
                return false;
+       }
 #endif
        spin_lock(&nm_i->nid_list_lock);
 
@@ -1902,13 +2025,16 @@ retry:
                i->state = NID_ALLOC;
                __insert_nid_to_list(sbi, i, ALLOC_NID_LIST, false);
                nm_i->available_nids--;
+
+               update_free_nid_bitmap(sbi, *nid, false, false, false);
+
                spin_unlock(&nm_i->nid_list_lock);
                return true;
        }
        spin_unlock(&nm_i->nid_list_lock);
 
        /* Let's scan nat pages and its caches to get free nids */
-       build_free_nids(sbi, true);
+       build_free_nids(sbi, true, false);
        goto retry;
 }
 
@@ -1956,6 +2082,8 @@ void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid)
 
        nm_i->available_nids++;
 
+       update_free_nid_bitmap(sbi, nid, true, false, false);
+
        spin_unlock(&nm_i->nid_list_lock);
 
        if (need_free)
@@ -2018,18 +2146,18 @@ update_inode:
        f2fs_put_page(ipage, 1);
 }
 
-void recover_xattr_data(struct inode *inode, struct page *page, block_t blkaddr)
+int recover_xattr_data(struct inode *inode, struct page *page, block_t blkaddr)
 {
        struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
        nid_t prev_xnid = F2FS_I(inode)->i_xattr_nid;
        nid_t new_xnid = nid_of_node(page);
        struct node_info ni;
+       struct page *xpage;
 
-       /* 1: invalidate the previous xattr nid */
        if (!prev_xnid)
                goto recover_xnid;
 
-       /* Deallocate node address */
+       /* 1: invalidate the previous xattr nid */
        get_node_info(sbi, prev_xnid, &ni);
        f2fs_bug_on(sbi, ni.blk_addr == NULL_ADDR);
        invalidate_blocks(sbi, ni.blk_addr);
@@ -2037,19 +2165,27 @@ void recover_xattr_data(struct inode *inode, struct page *page, block_t blkaddr)
        set_node_addr(sbi, &ni, NULL_ADDR, false);
 
 recover_xnid:
-       /* 2: allocate new xattr nid */
+       /* 2: update xattr nid in inode */
+       remove_free_nid(sbi, new_xnid);
+       f2fs_i_xnid_write(inode, new_xnid);
        if (unlikely(!inc_valid_node_count(sbi, inode)))
                f2fs_bug_on(sbi, 1);
+       update_inode_page(inode);
+
+       /* 3: update and set xattr node page dirty */
+       xpage = grab_cache_page(NODE_MAPPING(sbi), new_xnid);
+       if (!xpage)
+               return -ENOMEM;
+
+       memcpy(F2FS_NODE(xpage), F2FS_NODE(page), PAGE_SIZE);
 
-       remove_free_nid(sbi, new_xnid);
        get_node_info(sbi, new_xnid, &ni);
        ni.ino = inode->i_ino;
        set_node_addr(sbi, &ni, NEW_ADDR, false);
-       f2fs_i_xnid_write(inode, new_xnid);
+       set_page_dirty(xpage);
+       f2fs_put_page(xpage, 1);
 
-       /* 3: update xattr blkaddr */
-       refresh_sit_entry(sbi, NEW_ADDR, blkaddr);
-       set_node_addr(sbi, &ni, blkaddr, false);
+       return 0;
 }
 
 int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page)
@@ -2152,7 +2288,7 @@ static void remove_nats_in_journal(struct f2fs_sb_info *sbi)
 
                ne = __lookup_nat_cache(nm_i, nid);
                if (!ne) {
-                       ne = grab_nat_entry(nm_i, nid);
+                       ne = grab_nat_entry(nm_i, nid, true);
                        node_info_from_raw_nat(&ne->ni, &raw_ne);
                }
 
@@ -2192,8 +2328,39 @@ add_out:
        list_add_tail(&nes->set_list, head);
 }
 
+static void __update_nat_bits(struct f2fs_sb_info *sbi, nid_t start_nid,
+                                               struct page *page)
+{
+       struct f2fs_nm_info *nm_i = NM_I(sbi);
+       unsigned int nat_index = start_nid / NAT_ENTRY_PER_BLOCK;
+       struct f2fs_nat_block *nat_blk = page_address(page);
+       int valid = 0;
+       int i;
+
+       if (!enabled_nat_bits(sbi, NULL))
+               return;
+
+       for (i = 0; i < NAT_ENTRY_PER_BLOCK; i++) {
+               if (start_nid == 0 && i == 0)
+                       valid++;
+               if (nat_blk->entries[i].block_addr)
+                       valid++;
+       }
+       if (valid == 0) {
+               __set_bit_le(nat_index, nm_i->empty_nat_bits);
+               __clear_bit_le(nat_index, nm_i->full_nat_bits);
+               return;
+       }
+
+       __clear_bit_le(nat_index, nm_i->empty_nat_bits);
+       if (valid == NAT_ENTRY_PER_BLOCK)
+               __set_bit_le(nat_index, nm_i->full_nat_bits);
+       else
+               __clear_bit_le(nat_index, nm_i->full_nat_bits);
+}
+
 static void __flush_nat_entry_set(struct f2fs_sb_info *sbi,
-                                       struct nat_entry_set *set)
+               struct nat_entry_set *set, struct cp_control *cpc)
 {
        struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
        struct f2fs_journal *journal = curseg->journal;
@@ -2208,7 +2375,8 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi,
         * #1, flush nat entries to journal in current hot data summary block.
         * #2, flush nat entries to nat page.
         */
-       if (!__has_cursum_space(journal, set->entry_cnt, NAT_JOURNAL))
+       if (enabled_nat_bits(sbi, cpc) ||
+               !__has_cursum_space(journal, set->entry_cnt, NAT_JOURNAL))
                to_journal = false;
 
        if (to_journal) {
@@ -2244,14 +2412,21 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi,
                        add_free_nid(sbi, nid, false);
                        spin_lock(&NM_I(sbi)->nid_list_lock);
                        NM_I(sbi)->available_nids++;
+                       update_free_nid_bitmap(sbi, nid, true, false, false);
+                       spin_unlock(&NM_I(sbi)->nid_list_lock);
+               } else {
+                       spin_lock(&NM_I(sbi)->nid_list_lock);
+                       update_free_nid_bitmap(sbi, nid, false, false, false);
                        spin_unlock(&NM_I(sbi)->nid_list_lock);
                }
        }
 
-       if (to_journal)
+       if (to_journal) {
                up_write(&curseg->journal_rwsem);
-       else
+       } else {
+               __update_nat_bits(sbi, start_nid, page);
                f2fs_put_page(page, 1);
+       }
 
        f2fs_bug_on(sbi, set->entry_cnt);
 
@@ -2262,7 +2437,7 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi,
 /*
  * This function is called during the checkpointing process.
  */
-void flush_nat_entries(struct f2fs_sb_info *sbi)
+void flush_nat_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
 {
        struct f2fs_nm_info *nm_i = NM_I(sbi);
        struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
@@ -2283,7 +2458,8 @@ void flush_nat_entries(struct f2fs_sb_info *sbi)
         * entries, remove all entries from journal and merge them
         * into nat entry set.
         */
-       if (!__has_cursum_space(journal, nm_i->dirty_nat_cnt, NAT_JOURNAL))
+       if (enabled_nat_bits(sbi, cpc) ||
+               !__has_cursum_space(journal, nm_i->dirty_nat_cnt, NAT_JOURNAL))
                remove_nats_in_journal(sbi);
 
        while ((found = __gang_lookup_nat_set(nm_i,
@@ -2297,27 +2473,103 @@ void flush_nat_entries(struct f2fs_sb_info *sbi)
 
        /* flush dirty nats in nat entry set */
        list_for_each_entry_safe(set, tmp, &sets, set_list)
-               __flush_nat_entry_set(sbi, set);
+               __flush_nat_entry_set(sbi, set, cpc);
 
        up_write(&nm_i->nat_tree_lock);
 
        f2fs_bug_on(sbi, nm_i->dirty_nat_cnt);
 }
 
+static int __get_nat_bitmaps(struct f2fs_sb_info *sbi)
+{
+       struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+       struct f2fs_nm_info *nm_i = NM_I(sbi);
+       unsigned int nat_bits_bytes = nm_i->nat_blocks / BITS_PER_BYTE;
+       unsigned int i;
+       __u64 cp_ver = cur_cp_version(ckpt);
+       block_t nat_bits_addr;
+
+       if (!enabled_nat_bits(sbi, NULL))
+               return 0;
+
+       nm_i->nat_bits_blocks = F2FS_BYTES_TO_BLK((nat_bits_bytes << 1) + 8 +
+                                               F2FS_BLKSIZE - 1);
+       nm_i->nat_bits = kzalloc(nm_i->nat_bits_blocks << F2FS_BLKSIZE_BITS,
+                                               GFP_KERNEL);
+       if (!nm_i->nat_bits)
+               return -ENOMEM;
+
+       nat_bits_addr = __start_cp_addr(sbi) + sbi->blocks_per_seg -
+                                               nm_i->nat_bits_blocks;
+       for (i = 0; i < nm_i->nat_bits_blocks; i++) {
+               struct page *page = get_meta_page(sbi, nat_bits_addr++);
+
+               memcpy(nm_i->nat_bits + (i << F2FS_BLKSIZE_BITS),
+                                       page_address(page), F2FS_BLKSIZE);
+               f2fs_put_page(page, 1);
+       }
+
+       cp_ver |= (cur_cp_crc(ckpt) << 32);
+       if (cpu_to_le64(cp_ver) != *(__le64 *)nm_i->nat_bits) {
+               disable_nat_bits(sbi, true);
+               return 0;
+       }
+
+       nm_i->full_nat_bits = nm_i->nat_bits + 8;
+       nm_i->empty_nat_bits = nm_i->full_nat_bits + nat_bits_bytes;
+
+       f2fs_msg(sbi->sb, KERN_NOTICE, "Found nat_bits in checkpoint");
+       return 0;
+}
+
+inline void load_free_nid_bitmap(struct f2fs_sb_info *sbi)
+{
+       struct f2fs_nm_info *nm_i = NM_I(sbi);
+       unsigned int i = 0;
+       nid_t nid, last_nid;
+
+       if (!enabled_nat_bits(sbi, NULL))
+               return;
+
+       for (i = 0; i < nm_i->nat_blocks; i++) {
+               i = find_next_bit_le(nm_i->empty_nat_bits, nm_i->nat_blocks, i);
+               if (i >= nm_i->nat_blocks)
+                       break;
+
+               __set_bit_le(i, nm_i->nat_block_bitmap);
+
+               nid = i * NAT_ENTRY_PER_BLOCK;
+               last_nid = (i + 1) * NAT_ENTRY_PER_BLOCK;
+
+               spin_lock(&nm_i->free_nid_lock);
+               for (; nid < last_nid; nid++)
+                       update_free_nid_bitmap(sbi, nid, true, true, true);
+               spin_unlock(&nm_i->free_nid_lock);
+       }
+
+       for (i = 0; i < nm_i->nat_blocks; i++) {
+               i = find_next_bit_le(nm_i->full_nat_bits, nm_i->nat_blocks, i);
+               if (i >= nm_i->nat_blocks)
+                       break;
+
+               __set_bit_le(i, nm_i->nat_block_bitmap);
+       }
+}
+
 static int init_node_manager(struct f2fs_sb_info *sbi)
 {
        struct f2fs_super_block *sb_raw = F2FS_RAW_SUPER(sbi);
        struct f2fs_nm_info *nm_i = NM_I(sbi);
        unsigned char *version_bitmap;
-       unsigned int nat_segs, nat_blocks;
+       unsigned int nat_segs;
+       int err;
 
        nm_i->nat_blkaddr = le32_to_cpu(sb_raw->nat_blkaddr);
 
        /* segment_count_nat includes pair segment so divide to 2. */
        nat_segs = le32_to_cpu(sb_raw->segment_count_nat) >> 1;
-       nat_blocks = nat_segs << le32_to_cpu(sb_raw->log_blocks_per_seg);
-
-       nm_i->max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks;
+       nm_i->nat_blocks = nat_segs << le32_to_cpu(sb_raw->log_blocks_per_seg);
+       nm_i->max_nid = NAT_ENTRY_PER_BLOCK * nm_i->nat_blocks;
 
        /* not used nids: 0, node, meta, (and root counted as valid node) */
        nm_i->available_nids = nm_i->max_nid - sbi->total_valid_node_count -
@@ -2350,6 +2602,42 @@ static int init_node_manager(struct f2fs_sb_info *sbi)
                                        GFP_KERNEL);
        if (!nm_i->nat_bitmap)
                return -ENOMEM;
+
+       err = __get_nat_bitmaps(sbi);
+       if (err)
+               return err;
+
+#ifdef CONFIG_F2FS_CHECK_FS
+       nm_i->nat_bitmap_mir = kmemdup(version_bitmap, nm_i->bitmap_size,
+                                       GFP_KERNEL);
+       if (!nm_i->nat_bitmap_mir)
+               return -ENOMEM;
+#endif
+
+       return 0;
+}
+
+static int init_free_nid_cache(struct f2fs_sb_info *sbi)
+{
+       struct f2fs_nm_info *nm_i = NM_I(sbi);
+
+       nm_i->free_nid_bitmap = f2fs_kvzalloc(nm_i->nat_blocks *
+                                       NAT_ENTRY_BITMAP_SIZE, GFP_KERNEL);
+       if (!nm_i->free_nid_bitmap)
+               return -ENOMEM;
+
+       nm_i->nat_block_bitmap = f2fs_kvzalloc(nm_i->nat_blocks / 8,
+                                                               GFP_KERNEL);
+       if (!nm_i->nat_block_bitmap)
+               return -ENOMEM;
+
+       nm_i->free_nid_count = f2fs_kvzalloc(nm_i->nat_blocks *
+                                       sizeof(unsigned short), GFP_KERNEL);
+       if (!nm_i->free_nid_count)
+               return -ENOMEM;
+
+       spin_lock_init(&nm_i->free_nid_lock);
+
        return 0;
 }
 
@@ -2365,7 +2653,14 @@ int build_node_manager(struct f2fs_sb_info *sbi)
        if (err)
                return err;
 
-       build_free_nids(sbi, true);
+       err = init_free_nid_cache(sbi);
+       if (err)
+               return err;
+
+       /* load free nid status from nat_bits table */
+       load_free_nid_bitmap(sbi);
+
+       build_free_nids(sbi, true, true);
        return 0;
 }
 
@@ -2423,7 +2718,15 @@ void destroy_node_manager(struct f2fs_sb_info *sbi)
        }
        up_write(&nm_i->nat_tree_lock);
 
+       kvfree(nm_i->nat_block_bitmap);
+       kvfree(nm_i->free_nid_bitmap);
+       kvfree(nm_i->free_nid_count);
+
        kfree(nm_i->nat_bitmap);
+       kfree(nm_i->nat_bits);
+#ifdef CONFIG_F2FS_CHECK_FS
+       kfree(nm_i->nat_bitmap_mir);
+#endif
        sbi->nm_info = NULL;
        kfree(nm_i);
 }