一些零散的问题,记录一下。
f2fs_flush_nat_entries --> remove_nats_in_journal --> __alloc_nat_entry分配一个nat entry并没有增加nm_i->available_nid计数,后面执行__flush_nat_entry_set -->
if (nat_get_blkaddr(ne) == NULL_ADDR) {
add_free_nid(sbi, nid, false, true);
}会nm_i->available_nids++;
这会导致计数错误,所以在f2fs_flush_nat_entries --> remove_nats_in_journal :
if (!get_nat_flag(ne, IS_DIRTY) &&
le32_to_cpu(raw_ne.block_addr) == NULL_ADDR) {
spin_lock(&nm_i->nid_list_lock);
nm_i->available_nids--;
spin_unlock(&nm_i->nid_list_lock);
}
注:IS_DIRTY是并发流程用到的
thread1:
remove_nats_in_journal -->down_write(&curseg->journal_rwsem) --> __set_nat_cache_dirty --> set_nat_flag(ne, IS_DIRTY, true) --> up_write(&curseg->journal_rwsem)
上面的if (!get_nat_flag(ne, IS_DIRTY)成立,计数减1
thread2:
remove_nats_in_journal --> down_write(&curseg->journal_rwsem) 拿到锁执行后面的代码,由于thread1已经设置了IS_DIRTY,所以这个执行流if (!get_nat_flag(ne, IS_DIRTY)不成立,就不会减计数值了。
static void __set_nat_cache_dirty(struct f2fs_nm_info *nm_i,
struct nat_entry *ne)
{
struct nat_entry_set *head;
bool new_ne = nat_get_blkaddr(ne) == NEW_ADDR;
if (!new_ne)
head = __grab_nat_entry_set(nm_i, ne);
/*
* update entry_cnt in below condition:
* 1. update NEW_ADDR to valid block address;
* 2. update old block address to new one;
*/
if (!new_ne && (get_nat_flag(ne, IS_PREALLOC) ||
!get_nat_flag(ne, IS_DIRTY)))
head->entry_cnt++;
git log搜一下don't track new nat entry in nat set提交记录
理解如下:Nat entry set is used only in checkpoint(), and during checkpoint() we
won't flush new nat entry with unallocated address, so we don't need to
add new nat entry into nat set, then nat_entry_set::entry_cnt can
indicate actual entry count we need to flush in checkpoint().
!new_ne表示不是“unallocated address”,所以需要将这个ne一到nat entry set中。并发执行时,第一个执行流定会执行红字(注意,因为new_ne是false,所以是清除IS_PREALLOC):
if (!new_ne && (get_nat_flag(ne, IS_PREALLOC) ||
!get_nat_flag(ne, IS_DIRTY)))
head->entry_cnt++;
set_nat_flag(ne, IS_PREALLOC, new_ne);
接着set_nat_flag(ne, IS_DIRTY, true);设置IS_DIRTY
第二个执行流执行时,get_nat_flag(ne, IS_PREALLOC)、 !get_nat_flag(ne, IS_DIRTY)都是不成立的就不会增加 head->entry_cnt。
__flush_nat_entry_set把nat set list中的struct nat_entry写入journal
if (to_journal) {
offset = f2fs_lookup_journal_in_cursum(journal,
NAT_JOURNAL, nid, 1);
f2fs_bug_on(sbi, offset < 0);
raw_ne = &nat_in_journal(journal, offset);
nid_in_journal(journal, offset) = cpu_to_le32(nid);
}
……
raw_nat_from_node_info(raw_ne, &ne->ni);
nat_reset_flag(ne);
__clear_nat_cache_dirty(NM_I(sbi), set, ne);
1)umount时:f2fs_flush_nat_entries ---> remove_nats_in_journal
2)curseg空间不足: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);
remove_nats_in_journal(sbi) --> __set_nat_cache_dirty --> list_move_tail(&ne->list, &head->entry_list);写入nat set list
f2fs_flush_nat_entries --->
for (idx = 0; idx < found; idx++)
__adjust_nat_entry_set(setvec[idx], &sets,
MAX_NAT_JENTRIES(journal));
上面__adjust_nat_entry_set代码,根据struct nat_entry_set->entry_cnt排序,链表头小,链表尾大,为什么要这样做?
1)生成nat entry,记录在CURSEG_HOT_DATA curseg的journal中(这是内存中的数据结构)。
2)remove_nats_in_journal:
2.1)如果nat cache中没有(struct f2fs_nm_info->nat_root),__alloc_nat_entry新分配一个nat entry并初始化,__init_nat_entry将新分配的nat entry加入nat cache(struct f2fs_nm_info->nat_root)和clean链表中(struct f2fs_nm_info->nat_entries)。
2.2)__set_nat_cache_dirty将nat entry从clean链表中移至set list链表(struct f2fs_nm_info->entry_list)。
3)remove_nats_in_journal --> __set_nat_cache_dirty把非NEW_ADDR的nat entry加入到set list中(这是内存中的数据结构)。
注1:NEW_ADDR代表什么意思要通俗的写出来
注2:set list作用要写出来
4)f2fs_write_checkpoint --> f2fs_flush_nat_entries --> __flush_nat_entry_set将set list中的nat entry写入curseg journal(如果journal中空间足够),或者写入nat block(如果journal中空间不足)。
5)__flush_nat_entry_set最后代码说明,如果是NULL_ADDR,直接add_free_nid,与bitmap没有关系。freenid、bitmap的来龙去脉要搞清楚。还说明了,nat entry先是存在内存中的,然后刷入journal。
6)f2fs_get_node_info中描述了nat cache、journal、nat block一个简单的关系。
7)对于inline文件,f2fs_file_write_iter --> f2fs_file_write_iter --> f2fs_file_write_iter --> __get_node_page --> __get_node_page --> f2fs_get_node_info --> f2fs_get_meta_page读出nid所属的nat block,存放在page中。在page中找到disk nat entry(代码中用f2fs_nat_entry表示),接着通过cache_nat_entry --> __alloc_nat_entry分配一个新的mem nat entry(代码中用nat_entry表示),cache_nat_entry --> __init_nat_entry --> node_info_from_raw_nat将f2fs_nat_entry信息复制到nat_entry,在__init_nat_entry会将nat_entry加入到nat cache(代码中用struct f2fs_nm_info->nat_root管理)和nat clean list中(代码中用struct f2fs_nm_info->nat_entries管理)。