1. 程式人生 > >f2fs系列文章fill_super(四)

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);
}