F2FS数据块寻址(linux5.18.11)
一、node block與data block
node block:存儲元數據(比如ino、尋址數據塊用到的一些信息等等)的block。node block有三種類型:inode block、direct node block、indirect node block。node block在內存中以struct f2fs_node表示。
data block:存儲文件數據的block。
F2FS的每個分區disk layout簡化后如下圖所示,Main area區按照section劃分成多個單元,每個section又包含多個segment(默認情況下1個section含1個segment)。
如果segment存放的是尋址信息,那么就是node segment;如果segment中存放數據信息,那么就是data segment。1個segment含有512個block,所以segment大小為2M。node segment中的block叫做node block;data segment中的block叫做data block。
f2fs node: node block的內存表現形式。
/* 4k大小,內容為node block數據。union部分4072個字節,footer占24個字節 */ struct f2fs_node {/* can be one of three types: inode, direct, and indirect types *//* union占4072字節 */union {struct f2fs_inode i;struct direct_node dn;struct indirect_node in;};/* 24字節 */struct node_footer footer; } __packed;struct node_footer是f2fs_node的尾部信息,用來描述這個node的性質。
struct node_footer {__le32 nid; /* node id */__le32 ino; /* inode number */__le32 flag; /* include cold/fsync/dentry marks and offset */__le64 cp_ver; /* checkpoint version */__le32 next_blkaddr; /* next node page block address */ } __packed;如果node_footer->nid == node_footer->ino,那么這個node是inode類型,即struct f2fs_inode類型的node。
direct node:即struct direct_node+struct node_footer,其內容來自于direct node block。struct direct_node默認有DEF_ADDRS_PER_BLOCK個(即1018)entry,每個entry的值是一個block address,entry值對應的block存儲的是文件數據。
struct direct_node {__le32 addr[DEF_ADDRS_PER_BLOCK]; /* array of data block address */ } __packed;indirect node:即struct indirect_node+struct node_footer,其內容來自于indirect node block。struct direct_node默認有NIDS_PER_BLOCK個(即1018)entry,每個entry的值是一個nid。
struct indirect_node {__le32 nid[NIDS_PER_BLOCK]; /* array of data block address */ } __packednid:node id,是一個32位的無符號整數,每個node block對應一個獨一的nid。
NAT:Node Address Table,是struct f2fs_nat_entry的集合,每個entry記錄了node block的nid、block address。根據nid就能找到node block的地址。
struct f2fs_nat_entry {__u8 version; /* latest version of cached nat entry */__le32 ino; /* inode number */__le32 block_addr; /* block address */ } __packed二、node block與struct f2fs_node
f2fs_get_node_page或f2fs_get_node_page_ra從device上讀取node block,存至page中,page強制類型轉換成struct f2fs_node即得到f2fs node。代碼示例check_index_in_prev_nodes -> f2fs_get_node_page -> F2FS_NODE(node_page):
static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi,block_t blkaddr, struct dnode_of_data *dn) {……/* Get the node page *//* * 如果nid對應的block數據不在緩存中,就從存儲器件上* 讀取block,數據放在node_page。即把4k的block數據* dump到了4k的node_page中。*/node_page = f2fs_get_node_page(sbi, nid);if (IS_ERR(node_page))return PTR_ERR(node_page);offset = ofs_of_node(node_page);…… }static inline unsigned int ofs_of_node(struct page *node_page) {/* node_page強制轉換成struct f2fs_node指針 */struct f2fs_node *rn = F2FS_NODE(node_page);unsigned flag = le32_to_cpu(rn->footer.flag);return flag >> OFFSET_BIT_SHIFT; }static inline struct f2fs_node *F2FS_NODE(struct page *page) {return (struct f2fs_node *)page_address(page); }三、文件數據塊管理
本節限定條件:不討論inline文件、inline xattr及壓縮文件數據塊組織方式,只討論普通文件的數據塊組織方式。
f2fs_inode->i_addr記錄了文件的前DEF_ADDRS_PER_INODE(即923)個數據塊的地址。如果文件數據塊超過923個,則需用到f2fs_inode->i_nid[DEF_NIDS_PER_INODE]尋址數據塊。DEF_NIDS_PER_INODE值為5,所以inode中最多可以記錄5個nid,nid經過NAT轉換可對應到5個node block。
struct f2fs_inode {__le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */__le32 i_nid[DEF_NIDS_PER_INODE]; /* direct(2), indirect(2),double_indirect(1) node id */ }f2fs_inode->i_nid[0]、f2fs_inode->i_nid[1],nid值經過NAT轉換得到direct node block address。
f2fs_inode->i_nid[2]、f2fs_inode->i_nid[3],nid值經NAT轉換后得到indirect node block address。
f2fs_inode->i_nid[4],nid值經NAT轉換后得到(double)indirect node block address。(f2fs只有struct indirect_node數據結構,沒有double_XXX數據結構。i_nid[4]得到的也是indirect node block address,只不過這里的indirect node block address描述的block中存儲的地址值依然是indirect node block address,double是人為邏輯上的概念)。
direct node block存儲1018個data block address。
圖2 F2FS數據塊尋址示意圖
總結尋址文件位置pos對應的數據塊步驟:
1)計算pos在第幾個4K數據塊上,記作offset
2)如果offse小于923,那么pos對應的數據塊地址為:struct f2fs_inode->i_addr[offset]
3)如果offset大于923,則須多次尋址才能找到最終的數據塊。get_node_path代碼將上圖4列藍色圖,從左到右分別稱作level 0~level 3,算出block在各level中所涉及的node block中的偏移,就能從inode找到最終的數據塊了。
在3)中如果level中的block是inode或者indirect類型的node block,由于這些node block中存儲的是nid,所以需通過NAT找到nid的struct f2fs_nat_entry,進而找到node block的block address。這樣從inode開始,一級一級地找下去,最終可以找到pos對應的data block address。整個尋址過程見第五部分的f2fs_get_dnode_of_data函數。
四、get_node_path
根據邏輯塊號尋址數據塊時,從圖2中從最左邊level 0開始,往level4尋址,每級level涉及一個node block,更具體一點,會涉及node block中的一個entry,get_node_path函數用于記錄node block的一些信息:
1)涉及node block中哪個entry,即在某個node block中的偏移量,存放在出參@offset[4]。
2)涉及哪個邏輯node block,即node block在本文件中的邏輯偏移量,存放在出參@noffset[4]。注意,不是node block的nid。邏輯node block號見圖2中紅色編號。@noffset[4]用于新分配一個node時,記錄node的邏輯偏移,見第五部分f2fs_get_dnode_of_data代碼分析。
對于尋址文件數據庫塊,有1)的信息就足夠了。比如尋址圖2中紅色data block:
inode + offset[0] ==> level1 node block
level1 node block + offset[1] ==> level2 node block
level2 node block + offset[2] ==> level3 node block
level3 node block + offset[3] ==> 紅色data?block
代碼分析:
int get_node_path(struct inode *inode, long block,?int offset[4], unsigned int noffset[4])
@inode:文件的inode
@block:邏輯block號。get_node_path將找到該邏輯block的data block address。
@offset:出參,記錄node block中的entry偏移量。
@noffset:出參,記錄node block的邏輯偏移量。
/* 注釋部分對照圖2理解 */ static int get_node_path(struct inode *inode, long block,int offset[4], unsigned int noffset[4]) {/* 圖2中,inode類型的node block中含多少個data block addr,默認923個 */const long direct_index = ADDRS_PER_INODE(inode);/* 圖2中,direct node block中含多少個data block addr,默認1018個 */const long direct_blks = ADDRS_PER_BLOCK(inode);/* 圖2中,indirect node block中含多少個nid,默認1018個 */const long dptrs_per_blk = NIDS_PER_BLOCK;/* 圖2中,一個indirect node block含多少個data block addr */const long indirect_blks = ADDRS_PER_BLOCK(inode) * NIDS_PER_BLOCK;/* 圖2中,一個double indirect node block含多少個data block addr */const long dindirect_blks = indirect_blks * NIDS_PER_BLOCK;int n = 0;int level = 0;/* level 0中的node就是inode node,是文件的第一個node,所以level 0中的邏輯node恒為0 */noffset[0] = 0;/* 邏輯塊號小于inode node中的data block addr個數,可直接從inode中找到data block addr */if (block < direct_index) {/* level 0中node block中entry的偏移量 */offset[n] = block;goto got;}block -= direct_index;if (block < direct_blks) {/* level 0 中node block中entry的偏移量 */offset[n++] = NODE_DIR1_BLOCK;/* level 1涉及第1個node(前面有node 0,見圖2紅字) */noffset[n] = 1;/* level 1中node block中entry的偏移量 */offset[n] = block;level = 1;goto got;}block -= direct_blks;if (block < direct_blks) {/* level 0中node block中entry的偏移量 */offset[n++] = NODE_DIR2_BLOCK;/* level 1涉及第2個node(前面有node 0,node 1,見圖2紅字) */noffset[n] = 2;/* level 1中node block中entry的偏移量 */offset[n] = block;level = 1;goto got;}block -= direct_blks;if (block < indirect_blks) {/* level 0中node block中entry的偏移量 */offset[n++] = NODE_IND1_BLOCK;/* level 1涉及第3個node(前面有node 0,node 1,node2,見圖2紅字)*/noffset[n] = 3;/* level 1中node block中entry的偏移量 */offset[n++] = block / direct_blks;/** level 2涉及的邏輯node號* 4代表前面的node0~node3(對照圖2理解)** offset[n-1]表示level 1中涉及的node block中偏移幾個entry,每個entry對應一個node,* 所以offset[n-1]就表示level 2中有幾個node。* 所以4 + offset[n - 1就是level 2中的node的邏輯號。*/noffset[n] = 4 + offset[n - 1];/* level 2中node block中entry的偏移量 */offset[n] = block % direct_blks;level = 2;goto got;}block -= indirect_blks;if (block < indirect_blks) {/* level 0中node block中entry的偏移量 */offset[n++] = NODE_IND2_BLOCK;/** level 1涉及的邏輯node號* 4代表前面的node0~node3(對照圖2理解)* dptrs_per_blk代表node3含有的node數量(對照圖2理解)* 4 + dptrs_per_blk 表示在level 1的node前面一共有多少個node*/noffset[n] = 4 + dptrs_per_blk;/** level 1中node block中entry的偏移量* level 1中node block每個entry含有direct_blks個node,* 所以block / direct_blks就表示node block中entry的偏移量*/offset[n++] = block / direct_blks;/** level 2中涉及的邏輯node號* 5代表前面的node0、node1、node2、node3、node1023(對照圖2理解)* dptrs_per_blk代表node3含有的node數量(對照圖2理解)* offset[n - 1]代表node1023中偏移幾個entry,每個entry對應一個direct node,* 所以偏移量就代表了node數量。** 所以5 + dptrs_per_blk + offset[n - 1]就表示在level 2中涉及* 的node前面一共有多少個node*/noffset[n] = 5 + dptrs_per_blk + offset[n - 1];/* levle 2中node block中entry的偏移 */offset[n] = block % direct_blks;level = 2;goto got;}block -= indirect_blks;if (block < dindirect_blks) {/* level 0中node block中entry的偏移量 */offset[n++] = NODE_DIND_BLOCK;/** level 1涉及的邏輯node號* 5代表前面的node0、node1、node2、node3、node1023(對照圖2理解)* (dptrs_per_blk * 2)代表node3、node1023含有的node數量(對照圖2理解)*/noffset[n] = 5 + (dptrs_per_blk * 2);/* level 1中node block中entry的偏移量 */offset[n++] = block / indirect_blks;/** level 2涉及的邏輯node號* 6代表前面的node0、node1、node2、node3、node1023、node2043(對照圖2理解)* (dptrs_per_blk * 2)代表node3、node1023含有的node數量(對照圖2理解)* * offset[n - 1]代表node2043中偏移幾個entry,每個entry又是一個indirect node,* 每個indirect node含有dptrs_per_blk個node,算上indirect node本身,* 所有一個entry一共有[offset[n - 1]*(dptrs_per_blk + 1)]* 個node, 這里的+1就是指indirect node本身*/noffset[n] = 6 + (dptrs_per_blk * 2) +offset[n - 1] * (dptrs_per_blk + 1);/* levle 2涉及的邏輯node號(對照圖2理解)*/offset[n++] = (block / direct_blks) % dptrs_per_blk;/** level 3涉及的邏輯node號** 7代表前面的node0、node1、node2、node3、node1023、* node2043以及level 2中的一個node(對照圖2理解)** (dptrs_per_blk * 2)代表node3、node1023含有的node數量(對照圖2理解)** offset[n - 2]表示level 1中node中entry的偏移量,每個entry又是一個indirect node,* 每個indirect node含有dptrs_per_blk個node,算上jindirect node本身,* 所有一個entry一共有[offset[n - 2]*(dptrs_per_blk + 1)]* 個node, 這里的+1就是指indirect node本身** offset[n - 1]表示level 2中node中entry的偏移量(這個node就是數字7中的一個node),* 每個entry對應一個direct node,所以這個偏移量就代表了node數量。*/noffset[n] = 7 + (dptrs_per_blk * 2) +offset[n - 2] * (dptrs_per_blk + 1) +offset[n - 1];/* levle 3中node block中entry的偏移 */offset[n] = block % direct_blks;level = 3;goto got;} else {return -E2BIG;} got:return level; }五、f2fs_get_dnode_of_data
int f2fs_get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode);
@dn 出參,存放與@index相關的direct node的信息
@index,入參,邏輯塊號
@mode,入參,查找node或者分配node
函數功能:
根據@index找到對應的direct node(可能是已有的,也可能是新分配的),node信息放在@dn。
int f2fs_get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode) {struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);/* 記錄level 0 ~ level 3涉及的node block對應的page */struct page *npage[4];struct page *parent = NULL;/* 記錄level 0 ~ level 3涉及的node中entry的偏移 */int offset[4];/* 記錄level 0 ~ level 3中邏輯node號 */unsigned int noffset[4];nid_t nids[4];int level, i = 0;int err = 0;/* 見第四節分析 */level = get_node_path(dn->inode, index, offset, noffset);if (level < 0)return level;/* inode類型的node,ino就是nid */nids[0] = dn->inode->i_ino;npage[0] = dn->inode_page;if (!npage[0]) {/* 根據nid找到node block的page */npage[0] = f2fs_get_node_page(sbi, nids[0]);if (IS_ERR(npage[0]))return PTR_ERR(npage[0]);}/* if inline_data is set, should not report any block indices */if (f2fs_has_inline_data(dn->inode) && index) {err = -ENOENT;f2fs_put_page(npage[0], 1);goto release_out;}parent = npage[0];/* 找到level 0中node記錄的nid值,這個nid值就是下一級level 2涉及的node block的nid */if (level != 0)nids[1] = get_nid(parent, offset[0], true);dn->inode_page = npage[0];dn->inode_page_locked = true;/* get indirect or direct nodes */for (i = 1; i <= level; i++) {bool done = false;if (!nids[i] && mode == ALLOC_NODE) {/* alloc new node *//** level i中涉及的node的entry的表項值為空,* 需要從free_nid_list中獲取一個可用的nid值寫入entry*/if (!f2fs_alloc_nid(sbi, &(nids[i]))) {err = -ENOSPC;goto release_pages;}dn->nid = nids[i];/** noffset[i]是邏輯node號,新分配一個page,* 并根據邏輯node號加入sbi->node_inode->i_mapping*/npage[i] = f2fs_new_node_page(dn, noffset[i]);if (IS_ERR(npage[i])) {f2fs_alloc_nid_failed(sbi, nids[i]);err = PTR_ERR(npage[i]);goto release_pages;}/* @parent:node中entry值為空的node* @offset[i - 1]:parent中entry值為空的位置* @nids[i]:新得到的nid值* i == 1說明level 0的node中的表項值為空,level 0中的node是inode類型的。* set_nid:將nids[i]寫入parent的offset[i - 1]表項*/set_nid(parent, offset[i - 1], nids[i], i == 1);f2fs_alloc_nid_done(sbi, nids[i]);done = true;} else if (mode == LOOKUP_NODE_RA && i == level && level > 1) {npage[i] = f2fs_get_node_page_ra(parent, offset[i - 1]);if (IS_ERR(npage[i])) {err = PTR_ERR(npage[i]);goto release_pages;}done = true;}if (i == 1) {dn->inode_page_locked = false;unlock_page(parent);} else {f2fs_put_page(parent, 1);}if (!done) {/** @nids[i]:當前level涉及的node的邏輯node號* 獲取當前level的node page*/npage[i] = f2fs_get_node_page(sbi, nids[i]);if (IS_ERR(npage[i])) {err = PTR_ERR(npage[i]);f2fs_put_page(npage[0], 0);goto release_out;}}if (i < level) {parent = npage[i];/** @parent:當前level的node page* @offset[i]:當前level中node的entry偏移* get_nid:獲取當前level的node page的entry的值,即下一級node的nid值*/nids[i + 1] = get_nid(parent, offset[i], false);}}/** 記錄最終找到的direct node的信息,放在出參@dn返回* @dn->nid:direct node的nid值(注意,不是邏輯node號)* @dn->ofs_in_node:direct node中entry的偏移量* @dn->node_page:direct node的page* @dn->data_blkaddr:direct node中偏移@dn->ofs_in_node的entry表項值,這個是數數據塊地址,可能是有效的地址,也可能是一個空地址NULL_ADDR*/dn->nid = nids[level];dn->ofs_in_node = offset[level];dn->node_page = npage[level];dn->data_blkaddr = f2fs_data_blkaddr(dn);if (is_inode_flag_set(dn->inode, FI_COMPRESSED_FILE) &&f2fs_sb_has_readonly(sbi)) {unsigned int c_len = f2fs_cluster_blocks_are_contiguous(dn);block_t blkaddr;if (!c_len)goto out;blkaddr = f2fs_data_blkaddr(dn);if (blkaddr == COMPRESS_ADDR)blkaddr = data_blkaddr(dn->inode, dn->node_page,dn->ofs_in_node + 1);f2fs_update_extent_tree_range_compressed(dn->inode,index, blkaddr,F2FS_I(dn->inode)->i_cluster_size,c_len);} out:return 0;release_pages:f2fs_put_page(parent, 1);if (i > 1)f2fs_put_page(npage[0], 0); release_out:dn->inode_page = NULL;dn->node_page = NULL;if (err == -ENOENT) {dn->cur_level = i;dn->max_level = level;dn->ofs_in_node = offset[level];}return err; }總結
以上是生活随笔為你收集整理的F2FS数据块寻址(linux5.18.11)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PS——文字工具
- 下一篇: 安全:金融信息化的命脉