F2FS源码分析-1.3 [F2FS 元数据布局部分] Checkpoint结构
F2FS源碼分析系列文章
主目錄
一、文件系統(tǒng)布局以及元數(shù)據(jù)結(jié)構(gòu)
二、文件數(shù)據(jù)的存儲(chǔ)以及讀寫(xiě)
三、文件與目錄的創(chuàng)建以及刪除(未完成)
四、垃圾回收機(jī)制
五、數(shù)據(jù)恢復(fù)機(jī)制
六、重要數(shù)據(jù)結(jié)構(gòu)或者函數(shù)的分析
Checkpoint區(qū)域
Checkpoint是維護(hù)F2FS的數(shù)據(jù)一致性的結(jié)構(gòu),它維護(hù)了系統(tǒng)當(dāng)前的狀態(tài),例如segment的分配情況,node的分配情況,以及當(dāng)前的active segment的狀態(tài)等。F2FS在滿(mǎn)足一定的條件的情況下,會(huì)將當(dāng)前系統(tǒng)的分配狀態(tài)寫(xiě)入到Checkpoint中,萬(wàn)一系統(tǒng)出現(xiàn)突然宕機(jī),這個(gè)是F2FS可以從Checkpoint中恢復(fù)到上次回寫(xiě)時(shí)的狀態(tài),以保證數(shù)據(jù)的可恢復(fù)性。F2FS維護(hù)了兩個(gè)Checkpoint結(jié)構(gòu),互為備份,其中一個(gè)是當(dāng)前正在使用的Checkpoint,另外一個(gè)上次回寫(xiě)的穩(wěn)定的Chcekpoint。如果系統(tǒng)出現(xiàn)了宕機(jī),那么當(dāng)前的Checkpoint就會(huì)變得不可信任,進(jìn)而使用備份Checkpoint進(jìn)行恢復(fù)。
Checkpoint在元數(shù)據(jù)區(qū)域的物理結(jié)構(gòu)
根據(jù)上述的結(jié)構(gòu)圖,Checkpoint區(qū)域由幾個(gè)部分構(gòu)成,分別是checkpoint元數(shù)據(jù)區(qū)域(f2fs_checkpoint)、orphan node區(qū)域、active segments區(qū)域。同時(shí)active segments區(qū)域在不同的情況下,會(huì)有不同的形式,目的是減少I(mǎi)O的寫(xiě)入。接下來(lái)分別討論Checkpoint不同的部分。
Checkpoint元數(shù)據(jù)區(qū)域
F2FS使用數(shù)據(jù)結(jié)構(gòu)f2fs_checkpoint表示Checkpoint結(jié)構(gòu),它保存在磁盤(pán)中f2fs_super_block之后區(qū)域中,數(shù)據(jù)結(jié)構(gòu)如下。需要特別注意的是cur_node_segno、cur_node_blkoff、cur_data_segno、cur_data_blkoff這幾個(gè)變量。第一節(jié)提到,F2FS分為了6個(gè)log區(qū)域,分別對(duì)應(yīng)hot node/data、warm node/data、cold node/data。F2FS必須定時(shí)執(zhí)行Checkpoint去記錄當(dāng)前系統(tǒng)的log分配到哪個(gè)位置,否則在系統(tǒng)宕機(jī)的時(shí)候,會(huì)出現(xiàn)數(shù)據(jù)丟失等一致性問(wèn)題,因此cur_xxx_segno以及cur_xxx_blkoff記錄了上次Checkpoint時(shí),系統(tǒng)正在使用的log的segment number,以及分配到這個(gè)segment的哪個(gè)位置。
struct f2fs_checkpoint {__le64 checkpoint_ver; /* CP版本,用于比較新舊版本進(jìn)行恢復(fù) */__le64 user_block_count; /* # of user blocks */__le64 valid_block_count; /* # of valid blocks in main area */__le32 rsvd_segment_count; /* # of reserved segments for gc */__le32 overprov_segment_count; /* # of overprovision segments */__le32 free_segment_count; /* # of free segments in main area *//* information of current node segments */__le32 cur_node_segno[MAX_ACTIVE_NODE_LOGS];__le16 cur_node_blkoff[MAX_ACTIVE_NODE_LOGS];/* information of current data segments */__le32 cur_data_segno[MAX_ACTIVE_DATA_LOGS];__le16 cur_data_blkoff[MAX_ACTIVE_DATA_LOGS];__le32 ckpt_flags; /* Flags : umount and journal_present */__le32 cp_pack_total_block_count; /* total # of one cp pack */__le32 cp_pack_start_sum; /* start block number of data summary */__le32 valid_node_count; /* Total number of valid nodes */__le32 valid_inode_count; /* Total number of valid inodes */__le32 next_free_nid; /* Next free node number */__le32 sit_ver_bitmap_bytesize; /* Default value 64 */__le32 nat_ver_bitmap_bytesize; /* Default value 256 */__le32 checksum_offset; /* checksum offset inside cp block */__le64 elapsed_time; /* mounted time *//* allocation type of current segment */unsigned char alloc_type[MAX_ACTIVE_LOGS];/* SIT and NAT version bitmap */unsigned char sit_nat_version_bitmap[1]; } __packed;Orphan node區(qū)域
這是一個(gè)動(dòng)態(tài)的區(qū)域,如果沒(méi)有orphan node list則不會(huì)占用空間。
Active Segments區(qū)域
Active Segments的定義
Active Segments,又稱(chēng)current segment(CURSEG),即當(dāng)前正在用于進(jìn)行數(shù)據(jù)分配的log對(duì)應(yīng)的segment,如用戶(hù)需要寫(xiě)入8KB數(shù)據(jù),那么就會(huì)從active segments分配兩個(gè)block提供給用戶(hù)寫(xiě)入到磁盤(pán)中。F2FS為了提高數(shù)據(jù)分配的效率,根據(jù)數(shù)據(jù)的特性,一共定義了6個(gè)active segment。如第一章的總體結(jié)構(gòu)這一節(jié)提到的multi-head logging特性所描述,這6個(gè)active segments對(duì)應(yīng)了(how, warm, cold) X (node, data)的數(shù)據(jù)。
Active Segment與恢復(fù)相關(guān)的數(shù)據(jù)結(jié)構(gòu)
CP的主要任務(wù)是維護(hù)數(shù)據(jù)一致性,因此CP的active segment區(qū)域的主要任務(wù)是維護(hù)Active Segment的分配狀態(tài),使系統(tǒng)宕機(jī)時(shí)候可以恢復(fù)正常。維護(hù)active segment需要維護(hù)三種信息,分別是f2fs_checkpoint的信息,以及該segment對(duì)應(yīng)的journal和summary的信息。
-
f2fs_checkpoint中Active Segment信息:從上面給出的f2fs_checkpoint定義,cur_node_segno[MAX_ACTIVE_NODE_LOGS]和cur_data_segno[MAX_ACTIVE_DATA_LOGS]表示node和data當(dāng)前的Active Segment的編號(hào)(segment number, segno),系統(tǒng)可以通過(guò)這個(gè)編號(hào)找到對(duì)應(yīng)的segment。MAX_ACTIVE_NODE_LOGS以及MAX_ACTIVE_NODE_LOGS分別表示data和node有多少種類(lèi)型,F2FS默認(rèn)情況下都等于3,表示、、即HOT、WARM、COLD類(lèi)型數(shù)據(jù)。cur_node_blkoff[MAX_ACTIVE_NODE_LOGS]以及cur_data_blkoff[MAX_ACTIVE_DATA_LOGS]則分別表示當(dāng)前active segment分配到哪一個(gè)block(一個(gè)segment包含了512個(gè)block)。
-
Segment對(duì)應(yīng)的Journal信息:Journal在兩處地方都有出現(xiàn),分別是CP區(qū)域以及SSA區(qū)域。CP區(qū)域的journal主要用來(lái)保存active segment的修改信息,而SSA區(qū)域的則是持久化保存的所有的segment的journal信息。如系統(tǒng)分配出一個(gè)block給用戶(hù),那么就要將這個(gè)block所在的segment的bitmap中標(biāo)記為已分配,防止其他寫(xiě)請(qǐng)求使用。分兩個(gè)區(qū)域存放journal是為了減輕頻繁更新導(dǎo)致的系統(tǒng)性能下降。例如,當(dāng)系統(tǒng)寫(xiě)壓力很大的時(shí)候,bitmap就會(huì)頻繁被更新,如果這個(gè)時(shí)候頻繁將bitmap寫(xiě)入SSA,就會(huì)加重寫(xiě)壓力。因此CP區(qū)域的Journal的作用就是維護(hù)這些經(jīng)常修改的數(shù)據(jù),等待CP被觸發(fā)的時(shí)候才回寫(xiě)到閃存設(shè)備,從而減少寫(xiě)壓力,提高閃存壽命。(journal的實(shí)現(xiàn)參考第六章的journal這一節(jié))
-
Segment對(duì)應(yīng)的Summary信息:summary同樣在CP區(qū)域和SSA區(qū)域有出現(xiàn),它表示的是邏輯地址和物理地址的映射關(guān)系,這個(gè)映射關(guān)系會(huì)使用到GC流程中。summary與segment是一對(duì)一的關(guān)系,一個(gè)summary保存了一個(gè)segment所有的block的物理地址和邏輯地址的映射關(guān)系。summary保存在CP區(qū)域中同樣是出于減少I(mǎi)O的寫(xiě)入。
Checkpoint內(nèi)存管理結(jié)構(gòu)
Checkpoint的內(nèi)存管理結(jié)構(gòu)是struct f2fs_checkpoint本身,因?yàn)镃heckpoint一般只在F2FS啟動(dòng)的時(shí)候被讀取數(shù)據(jù),用于數(shù)據(jù)恢復(fù),而在運(yùn)行過(guò)程中大部分情況都是被寫(xiě),用于記錄恢復(fù)信息。因此,Checkpoint不需要過(guò)于復(fù)雜的內(nèi)存管理結(jié)構(gòu),因此使用struct f2fs_checkpoint本身即可以滿(mǎn)足需求。
另一方面,active segments,即F2FS的log,主要用于系統(tǒng)free block的分配,因此需要特定的管理結(jié)構(gòu)struct curseg_info進(jìn)行管理,它的定義如下:
struct curseg_info {struct mutex curseg_mutex;struct f2fs_summary_block *sum_blk; /* 每一個(gè)segment對(duì)應(yīng)一個(gè)summary block */struct rw_semaphore journal_rwsem;struct f2fs_journal *journal; /*每一個(gè)segment對(duì)應(yīng)一個(gè) info */unsigned char alloc_type;unsigned int segno; /* 當(dāng)前segno */unsigned short next_blkoff; /* 記錄當(dāng)前segment用于分配的下一個(gè)給block號(hào) */unsigned int zone; /* current zone number */unsigned int next_segno; /* 當(dāng)前segno用完以后,下個(gè)即將用來(lái)分配的segno */ };從結(jié)構(gòu)分析可以直到,curseg_info記錄當(dāng)前的segment的分配信息,當(dāng)系統(tǒng)出現(xiàn)宕機(jī)的時(shí)候,可以從CP記錄的curseg_info恢復(fù)當(dāng)上一次CP點(diǎn)的狀態(tài)。
每一種類(lèi)型的active segment就對(duì)應(yīng)一個(gè)struct curseg_info結(jié)構(gòu)。在F2FS中,使用一個(gè)數(shù)組來(lái)表示:
struct f2fs_sm_info {...struct curseg_info *curseg_array; // 默認(rèn)是分配6個(gè)curseg_info,分別對(duì)應(yīng)不同類(lèi)型... }struct f2fs_sm_info是SIT的管理結(jié)構(gòu),它也管理了CP最終的active segment的信息,是一個(gè)跨區(qū)域的管理結(jié)構(gòu)。
struct f2fs_checkpoint通過(guò)get_checkpoint_version函數(shù)從磁盤(pán)讀取出來(lái):
static int get_checkpoint_version(struct f2fs_sb_info *sbi, block_t cp_addr,struct f2fs_checkpoint **cp_block, struct page **cp_page,unsigned long long *version) {unsigned long blk_size = sbi->blocksize;size_t crc_offset = 0;__u32 crc = 0;*cp_page = f2fs_get_meta_page(sbi, cp_addr); // 根據(jù)CP所在的地址cp_addr從磁盤(pán)讀取一個(gè)block*cp_block = (struct f2fs_checkpoint *)page_address(*cp_page); // 直接轉(zhuǎn)換為數(shù)據(jù)結(jié)構(gòu)crc_offset = le32_to_cpu((*cp_block)->checksum_offset);if (crc_offset > (blk_size - sizeof(__le32))) {f2fs_msg(sbi->sb, KERN_WARNING,"invalid crc_offset: %zu", crc_offset);return -EINVAL;}crc = cur_cp_crc(*cp_block);if (!f2fs_crc_valid(sbi, crc, *cp_block, crc_offset)) { // 比較CRC的值,進(jìn)而知道是否成功讀取出來(lái)f2fs_msg(sbi->sb, KERN_WARNING, "invalid crc value");return -EINVAL;}*version = cur_cp_version(*cp_block);return 0; }struct curseg_info則是通過(guò)build_curseg函數(shù)進(jìn)行初始化:
static int build_curseg(struct f2fs_sb_info *sbi) {struct curseg_info *array;int i;array = f2fs_kzalloc(sbi, array_size(NR_CURSEG_TYPE, sizeof(*array)),GFP_KERNEL); // 根據(jù)active segment類(lèi)型的數(shù)目分配空間if (!array)return -ENOMEM;SM_I(sbi)->curseg_array = array; // 賦值到f2fs_sm_info->curseg_arrayfor (i = 0; i < NR_CURSEG_TYPE; i++) { // 為curseg的其他信息分配空間mutex_init(&array[i].curseg_mutex);array[i].sum_blk = f2fs_kzalloc(sbi, PAGE_SIZE, GFP_KERNEL);if (!array[i].sum_blk)return -ENOMEM;init_rwsem(&array[i].journal_rwsem);array[i].journal = f2fs_kzalloc(sbi,sizeof(struct f2fs_journal), GFP_KERNEL);if (!array[i].journal)return -ENOMEM;array[i].segno = NULL_SEGNO;array[i].next_blkoff = 0;}return restore_curseg_summaries(sbi); // 從f2fs_checkpoint恢復(fù)上一個(gè)CP點(diǎn)CURSEG的狀態(tài) }static int restore_curseg_summaries(struct f2fs_sb_info *sbi) {struct f2fs_journal *sit_j = CURSEG_I(sbi, CURSEG_COLD_DATA)->journal;struct f2fs_journal *nat_j = CURSEG_I(sbi, CURSEG_HOT_DATA)->journal;int type = CURSEG_HOT_DATA;int err;...for (; type <= CURSEG_COLD_NODE; type++) { // 按類(lèi)型逐個(gè)恢復(fù)active segment的信息err = read_normal_summaries(sbi, type);if (err)return err;}...return 0; }static int read_normal_summaries(struct f2fs_sb_info *sbi, int type) {struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);struct f2fs_summary_block *sum;struct curseg_info *curseg;struct page *new;unsigned short blk_off;unsigned int segno = 0;block_t blk_addr = 0;...segno = le32_to_cpu(ckpt->cur_data_segno[type]); // 從CP讀取segnoblk_off = le16_to_cpu(ckpt->cur_data_blkoff[type - CURSEG_HOT_DATA]); // 從CP讀取blk_offblk_addr = sum_blk_addr(sbi, NR_CURSEG_DATA_TYPE, type); // 獲取summary block地址 // 讀取&轉(zhuǎn)換結(jié)構(gòu)new = f2fs_get_meta_page(sbi, blk_addr);sum = (struct f2fs_summary_block *)page_address(new);curseg = CURSEG_I(sbi, type); // 根據(jù)type找到對(duì)應(yīng)的cursegmutex_lock(&curseg->curseg_mutex);/* 復(fù)制&恢復(fù)數(shù)據(jù) */down_write(&curseg->journal_rwsem);memcpy(curseg->journal, &sum->journal, SUM_JOURNAL_SIZE);up_write(&curseg->journal_rwsem);memcpy(curseg->sum_blk->entries, sum->entries, SUM_ENTRY_SIZE);memcpy(&curseg->sum_blk->footer, &sum->footer, SUM_FOOTER_SIZE);curseg->next_segno = segno;reset_curseg(sbi, type, 0);curseg->alloc_type = ckpt->alloc_type[type];curseg->next_blkoff = blk_off; // 恢復(fù)上次的分配狀態(tài)mutex_unlock(&curseg->curseg_mutex);f2fs_put_page(new, 1);return 0; }總結(jié)
以上是生活随笔為你收集整理的F2FS源码分析-1.3 [F2FS 元数据布局部分] Checkpoint结构的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 基于微信小程序的电影院买票选座系统
- 下一篇: 无刷电调--BLHELI_S的焊接问题与