F2FS nat entry涉及的数据结构(linux 5.18.11)

nat entry(简称ne)在代码中涉及多个数据结构,先上图。

图1 ne涉及的数据结构关系图

大的原则

1)红色部分是disk layout中的(持久化存储);蓝色部分是内存数据结构。

2)系统中所有的ne,缓存在nat cache中(radix tree管理),用于快速检索ne。

3)所有clean状态的ne,通过clean list管理。

4)一个NAT block中的所有ne,归属于同一个set;所有的set通过radix tree管理。引入set的目的是为了在将dirty ne刷入disk时,属于同一个block的呢会被一起写入block,提高性能和存储设备寿命。

5)系统内存压力大时,f2fs_balance_fs会回收clean状态的ne。

6)dirty ne的block add是NEW_ADDR时,说明是刚分配的,checkpoint时不需要写回存储设备,也是出于性能、寿命考虑。

7)journal是能用尽量用的,即使checkpoint后,journal中也会存在一些ne,这些ne还没有写回nat block。这也是出于性能考虑,如果不用journal缓存ne,那么ne就需要写回nat block,这会导致频繁的写disk;而用journal则可以将所有的ne一次性写入checkpoint的cp pack区域。

图1执行路径描述

1)挂载f2fs场景。从disk layout的cp pack区域读入journal,存于struct curseg_info->journal中。

f2fs_fill_super --> f2fs_build_segment_manager --> build_curseg --> restore_curseg_summaries --> read_compacted_summaries 或 read_normal_summaries

cp pack、compatced/normal summaries概念参考待整理文章

2)第一次访问ne,并且disk NAT区域没有该ne场景。在内存中,这些ne是新创建的,需记录在nat cache和clean list中。

比如在文件尾部写新数据;lseek到hole区域,等等。

eg1:在文件尾部写新数据,分配了新的block

vfs_write --> new_sync_write --> call_write_iter --> f2fs_file_write_iter -> f2fs_buffered_write_iter --> generic_perform_write --> f2fs_write_begin --> prepare_write_begin --> f2fs_get_block --> f2fs_reserve_block --> f2fs_get_dnode_of_data --> f2fs_new_node_page --> set_node_addr  --> __init_nat_entry

eg2:seek到文件的空洞区域

vfs_llseek --> f2fs_llseek --> f2fs_seek_block --> f2fs_get_dnode_of_data --> f2fs_new_node_page --> set_node_addr

3)第一次访问ne,这个ne存在于disk NAT区域。在内存中,这些ne是新创建的,需记录在nat cache和clean list中。

比如更新文件数据,,被更新的数据块已经存在与disk上。

vfs_write --> new_sync_write --> call_write_iter --> f2fs_file_write_iter --> f2fs_preallocate_blocks --> f2fs_map_blocks --> f2fs_get_dnode_of_data --> f2fs_new_node_page --> set_node_addr --> __init_nat_entry

4)checkpoint时,journal(内存)中的ne在mount读取后一直没访问过,并且journal(内存)中没有空间容纳脏ne了,journal(内存)中的ne将被nat cache和clean list记录起来

f2fs_write_checkpoint --> f2fs_flush_nat_entries -->

 if (cpc->reason & CP_UMOUNT ||
       !__has_cursum_space(journal,
     nm_i->nat_cnt[DIRTY_NAT], NAT_JOURNAL))
     remove_nats_in_journal(sbi);

系统中所有的ne都会记录在nat cache中。除非被路径10或11回收,否则ne一直作为缓存存在系统中。

5)修改ne的场景,常见于做了set_node_addr操作,修改后的ne被移入dirty list

eg1:序号2中写文件场景。f2fs lfs模式更新文件数据,需新分配了一个block,所以会修改ne的block address,执行set_node_addr。

eg2:在目录中创建了一个文件。

f2fs_create --> f2fs_add_link --> f2fs_do_add_link --> f2fs_add_dentry --> f2fs_add_regular_entry --> f2fs_init_inode_metadata --> f2fs_new_inode_page --> f2fs_new_node_page --> set_node_addr --> __set_nat_cache_dirty

6)checkpoint时,journal(内存)中没有空间容纳dirty list中的ne了,不能放入journal中的ne将被写入page中

f2fs_write_checkpoint --> f2fs_flush_nat_entries --> __flush_nat_entry_set --> 

  if ((cpc->reason & CP_UMOUNT) ||
        !__has_cursum_space(journal, set->entry_cnt, NAT_JOURNAL))
        to_journal = false;

    if (to_journal) {
        down_write(&curseg->journal_rwsem);
    } else {
        /* 获取ne所在的nat block,读入page */
        page = get_next_nat_page(sbi, start_nid);
        if (IS_ERR(page))
            return PTR_ERR(page);

        nat_blk = page_address(page);
        f2fs_bug_on(sbi, !nat_blk);
    }
    ……
    /* 更新ne信息到page */
    raw_ne = &nat_blk->entries[nid - start_nid];
    raw_nat_from_node_info(raw_ne, &ne->ni);
    ……

7)checkpoint时,journal(内存)中还有空间容纳dirty list中的ne,dirty list中的ne移至dirty list

f2fs_write_checkpoint --> f2fs_flush_nat_entries --> __flush_nat_entry_set --> 

/* flush dirty nats in nat entry set */
	list_for_each_entry_safe(ne, cur, &set->entry_list, list) {
		struct f2fs_nat_entry *raw_ne;
		nid_t nid = nat_get_nid(ne);
		int offset;

		f2fs_bug_on(sbi, nat_get_blkaddr(ne) == NEW_ADDR);

		if (to_journal) {
            /* 
             * 如果journal中有ne,则返回ne所在的位置偏移 
             * 如果journal中没有ne,则返回journal下一个空闲位置
             */
			offset = f2fs_lookup_journal_in_cursum(journal,
							NAT_JOURNAL, nid, 1);
			f2fs_bug_on(sbi, offset < 0);
            /* 获取journal中的ne */
			raw_ne = &nat_in_journal(journal, offset);
			nid_in_journal(journal, offset) = cpu_to_le32(nid);
		}else {
			raw_ne = &nat_blk->entries[nid - start_nid];
		}
        /* 更新journal中ne信息 */
		raw_nat_from_node_info(raw_ne, &ne->ni);
		nat_reset_flag(ne);
		__clear_nat_cache_dirty(NM_I(sbi), set, ne);

8)checkpoint时,journal中的ne写入cp pack区域(即持久化存储)

f2fs_write_checkpoint --> do_checkpoint --> f2fs_write_data_summaries --> write_compacted_summaries 或 write_normal_summaries

mount f2fs时,路径1读取compatcted/normal summaries到内存中。

9)脏元数据页写入nat block(即持久化存储)

worker_thread --> process_one_work --> wb_workfn --> wb_do_writeback --> wb_check_start_all或wb_check_background_flush --> wb_writeback --> __writeback_inodes_wb --> writeback_sb_inodes --> __writeback_single_inode --> do_writepages --> f2fs_write_meta_pages

kworker线程将脏页数据写入storage device。在大多数情况下,kworker线程周期性回写脏页数据,但也可能因系统脏页过多而主动回写。

10)系统内存压力大时,回收clean状态的ne

f2fs_balance_fs --> f2fs_balance_fs_bg --> f2fs_try_to_free_nats --> __del_from_nat_cache --> __free_nat_entry

只要内存中生成ne,都会缓存在nat cache中(最大数量10万个,见宏定义DEF_NAT_CACHE_THRESHOLD),系统内存压力大的时候,需要回收clean状态的ne,避免f2fs对系统内存的影响。checkpoint或者f2fs_write_node_pages都会触发f2fs_balance_fs_bg,然后根据系统内存使用情况决定是否回收ne。

void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi, bool from_bg)
{
	if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
		return;

	/* try to shrink extent cache when there is no enough memory */
	if (!f2fs_available_free_memory(sbi, EXTENT_CACHE))
		f2fs_shrink_extent_tree(sbi, EXTENT_CACHE_SHRINK_NUMBER);

	/* check the # of cached NAT entries */
	if (!f2fs_available_free_memory(sbi, NAT_ENTRIES))
		f2fs_try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK);

11)umount时回收ne

f2fs_put_super --> f2fs_destroy_node_manager --> __del_from_nat_cache -->  __free_nat_entry

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值