f2fs系列文章fill_super(四)
這篇文章將講述f2fs的node的管理結構f2fs_nm_info的構建和恢復。
build_node_manager:首先分配f2fs_nm_info的空間,然後呼叫init_node_manager初始化f2fs_nm_info並分配一些點陣圖的空間。最後呼叫build_free_nids讀取一定page中的f2fs_nat_entry對free_nid進行初始化,然後根據crseg_info中的journal來進行最新的更新。
int build_node_manager(struct f2fs_sb_info *sbi) { int err; sbi->nm_info = kzalloc(sizeof(struct f2fs_nm_info), GFP_KERNEL); if (!sbi->nm_info) return -ENOMEM; err = init_node_manager(sbi); if (err) return err; build_free_nids(sbi); return 0; }
init_node_manager:對f2fs_nm_info中的一些欄位的初始化,特別是關於快取的一些欄位,這些欄位以後會專門來講述的,然後分配f2fs_nm_info中的nat_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; nm_i->nat_blkaddr = le32_to_cpu(sb_raw->nat_blkaddr); 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->available_nids = nm_i->max_nid - F2FS_RESERVED_NODE_NUM; nm_i->fcnt = 0; nm_i->nat_cnt = 0; nm_i->ram_thresh = DEF_RAM_THRESHOLD; nm_i->ra_nid_pages = DEF_RA_NID_PAGES; nm_i->dirty_nats_ratio = DEF_DIRTY_NAT_RATIO_THRESHOLD; INIT_RADIX_TREE(&nm_i->free_nid_root, GFP_ATOMIC); INIT_LIST_HEAD(&nm_i->free_nid_list); INIT_RADIX_TREE(&nm_i->nat_root, GFP_NOIO); INIT_RADIX_TREE(&nm_i->nat_set_root, GFP_NOIO); INIT_LIST_HEAD(&nm_i->nat_entries); mutex_init(&nm_i->build_lock); spin_lock_init(&nm_i->free_nid_list_lock); init_rwsem(&nm_i->nat_tree_lock); nm_i->next_scan_nid = le32_to_cpu(sbi->ckpt->next_free_nid); nm_i->bitmap_size = __bitmap_size(sbi, NAT_BITMAP); version_bitmap = __bitmap_ptr(sbi, NAT_BITMAP); if (!version_bitmap) return -EFAULT; nm_i->nat_bitmap = kmemdup(version_bitmap, nm_i->bitmap_size, GFP_KERNEL); if (!nm_i->nat_bitmap) return -ENOMEM; return 0; }
build_free_nids:f2fs_nm_info中的next_scan_nid這個欄位儲存著上次瀏覽到的nid,本次接著上次的進行。首先預讀以nid所在的f2fs_nat_block開始的FREE_NID_PAGES個f2fs_nat_block,然後對這些f2fs_nat_block進行遍歷對每一個f2fs_nat_block,呼叫scan_nat_page遍歷其中的f2fs_nat_entry,如果檢查到nid對應的地址是空的那就加入到free_nid中來,然後nid增加到下一個f2fs_nat_block的起始nid。遍歷完之後更新next_scan_nid。由於f2fs_nat_block可能還不是最新的f2fs_nat_entry,最新的可能存放在curseg_info中,所以對curseg_info中的f2fs_nat_entry進行遍歷,如果檢測到地址為空,那就呼叫add_free_nid將nid加入到free_nid中,否則,將其從free_nid中刪除。
void build_free_nids(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;
int i = 0;
nid_t nid = nm_i->next_scan_nid;
if (nm_i->fcnt >= NAT_ENTRY_PER_BLOCK)
return;
ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nid), FREE_NID_PAGES, META_NAT, true);
down_read(&nm_i->nat_tree_lock);
while (1) {
struct page *page = get_current_nat_page(sbi, nid);
scan_nat_page(sbi, page, nid);
f2fs_put_page(page, 1);
nid += (NAT_ENTRY_PER_BLOCK - (nid % NAT_ENTRY_PER_BLOCK));
if (unlikely(nid >= nm_i->max_nid))
nid = 0;
if (++i >= FREE_NID_PAGES)
break;
}
nm_i->next_scan_nid = nid;
down_read(&curseg->journal_rwsem);
for (i = 0; i < nats_in_cursum(journal); i++) {
block_t addr;
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(nm_i, nid);
}
up_read(&curseg->journal_rwsem);
up_read(&nm_i->nat_tree_lock);
ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nm_i->next_scan_nid), nm_i->ra_nid_pages, META_NAT, false);
}
scan_nat_page:對f2fs_nat_block中從start_nid的f2fs_nat_entry進行遍歷,首先檢查nid不能大於f2fs_nm_info中的max_nid,然後獲取f2fs_nat_entry中的地址,如果為空,那就加入到free_nid中,否則什麼都不做。
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;
int i;
i = start_nid % NAT_ENTRY_PER_BLOCK;
for (; i < NAT_ENTRY_PER_BLOCK; i++, start_nid++) {
if (unlikely(start_nid >= nm_i->max_nid))
break;
blk_addr = le32_to_cpu(nat_blk->entries[i].block_addr);
f2fs_bug_on(sbi, blk_addr == NEW_ADDR);
if (blk_addr == NULL_ADDR) {
if (add_free_nid(sbi, start_nid, true) < 0)
break;
}
}
}
add_free_nid:首先如果nid==0那就直接返回。接著首先從nat_cache中查詢nid的快取的nat_entry,如果從快取中查詢到了nat_entry並且該nat_entry是沒有check_point過的,或者地址不是NULL_ADDR空的,那就說明最新的nat_entry是在使用的,直接返回。如果沒有滿足的話那就分配一個free_nid的空間,然後呼叫radix_tree_insert將free_nid插入到以f2fs_nm_info中的free_nid_root為根的radix tree中,如果該項已經在free nid的radix tree中存在,那麼釋放空間返回就行。接著將free_nid插入到以f2fs_nm_info中的free_nid_list為頭的雙向連結串列中,最後更新當前的f2fs_nm_info中的free nid的數量。
static int 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;
struct nat_entry *ne;
if (!available_free_memory(sbi, FREE_NIDS))
return -1;
if (unlikely(nid == 0))
return 0;
if (build) {
ne = __lookup_nat_cache(nm_i, nid);
if (ne && (!get_nat_flag(ne, IS_CHECKPOINTED) || nat_get_blkaddr(ne) != NULL_ADDR))
return 0;
}
i = f2fs_kmem_cache_alloc(free_nid_slab, GFP_NOFS);
i->nid = nid;
i->state = NID_NEW;
if (radix_tree_preload(GFP_NOFS)) {
kmem_cache_free(free_nid_slab, i);
return 0;
}
spin_lock(&nm_i->free_nid_list_lock);
if (radix_tree_insert(&nm_i->free_nid_root, i->nid, i)) {
spin_unlock(&nm_i->free_nid_list_lock);
radix_tree_preload_end();
kmem_cache_free(free_nid_slab, i);
return 0;
}
list_add_tail(&i->list, &nm_i->free_nid_list);
nm_i->fcnt++;
spin_unlock(&nm_i->free_nid_list_lock);
radix_tree_preload_end();
return 1;
}
remove_free_nid:首先呼叫__lookup_free_nid_list在維護free_nid的radix tree中查詢nid對應的free_nid,如果找到了且state==NID_NEW,那就從radix tree和list中將該nid對應的free_nid進行刪除並更新f2fs_nm_info中的free nid的數量。最後如果刪除了九江相應的free_nid的空間釋放。
static void remove_free_nid(struct f2fs_nm_info *nm_i, nid_t nid)
{
struct free_nid *i;
bool need_free = false;
spin_lock(&nm_i->free_nid_list_lock);
i = __lookup_free_nid_list(nm_i, nid);
if (i && i->state == NID_NEW) {
__del_from_free_nid_list(nm_i, i);
nm_i->fcnt--;
need_free = true;
}
spin_unlock(&nm_i->free_nid_list_lock);
if (need_free)
kmem_cache_free(free_nid_slab, i);
}