一、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 */
} __packed

nid: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)相关推荐

  1. 博途软件中多重背景块的建立_怎么理解多重背景数据块?

    拿电机说把,如果有4台电机,你不用多重背景,那就要建立4个DB块,如果用多重背景变成,公用一个DB块就好了.多重背景就是让你少建立DB块的. 回答者: 大来 - 毕业实践员&nbsp& ...

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

    nat entry(简称ne)在代码中涉及多个数据结构,先上图. 图1 ne涉及的数据结构关系图 大的原则 1)红色部分是disk layout中的(持久化存储):蓝色部分是内存数据结构. 2)系统中 ...

  3. [20170419]bbed探究数据块.txt

    [20170419]bbed探究数据块.txt --//bbed 是一个瑞士军刀,也是了解oracle内部数据块结构的好工具.我自己开始使用基本是看别人的帖子,对oracle数据块的内部也不是很了解. ...

  4. Oracle数据块损坏的恢复实例

    测试环境:11.2.0.4 1.构建数据块损坏的测试环境 2.有备份:常规恢复坏块 3.无备份:跳过坏块 1.构建数据块损坏的测试环境 1.1 创建测试表 --Create Table t_test ...

  5. ORA-01578: ORACLE 数据块损坏 之奇妙处理 DBV

    下午接到研发人员报告: ORA-01578: ORACLE 数据块损坏 根据网上诸多仁兄的处理方案,具体采用下面检查: ORA-01578: ORACLE 数据块损坏 (文件号 6, 块号 20) O ...

  6. oracle 数据块 修复,案例:Oracle坏块 使用RMAN工具的命令clear标记数据块为corrupt 修复坏块...

    天萃荷净 运维DBA巡检发现数据文件中存在坏块,使用RMAN工具的命令clear标记数据坏块,使用bbed修复坏块 在rman中有隐藏的命令clear,可以标记数据块为corrupt,从而实现数据库坏 ...

  7. 解析oracle的dump文件,读懂数据块dump文件信息

    尊重作者著作,转自http://blog.csdn.net/guoyjoe/article/details/8567076 -------------------------------------- ...

  8. 关于 RMAN 备份 数据块 一致性的讨论

    今天和 杭州恒生 的一个朋友讨论一个RMAN 在备份时数据块一致性的问题. 关于RMAN 的备份原理参考blog: RMAN 系列(一)---- RMAN 体系结构概述 http://blog.csd ...

  9. oracle asm dd命令,ASM来用DD命令模拟数据块损坏

    ASM来用DD命令模拟坏块 会话1:oracle SQL> create tablespace test datafile '+DATA/ora11g23/datafile/test.dbf' ...

最新文章

  1. Spark源码阅读02-Spark核心原理之调度算法
  2. PHP变参函数的实现
  3. 成立快两年,阿里巴巴达摩院都干什么了?
  4. webpack的build的时候时间长处理方案
  5. 【渝粤题库】陕西师范大学201381 国际经济法作业
  6. 【FTP】org.apache.commons.net.ftp.FTPClient实现复杂的上传下载,操作目录,处理编码...
  7. 谈谈在深圳挤公交那些事
  8. Java使用C3P0连接池详解
  9. Python实现基于模块的库存管理系统
  10. Python爬虫——爬取知网论文数据(一)
  11. 什么是移动IP?简述其工作过程
  12. 校园网同时连手机和电脑、用数据线给电脑连网
  13. android手机变windows8,安卓手机如何把手机界面投屏到windows8/10电脑上
  14. python 条件概率_NLTK中的条件概率分布
  15. 医疗IT系统在手术部供配电间的应用
  16. 漫话:什么是平衡(AVL)树?这应该是把AVL树讲的最好的文章了
  17. 软件升级包封装方法以及软件升级方法
  18. 率土之滨服务器进备战区维护多久,率土之滨备战区维护,备战区为什么不拆战法...
  19. 海伦公式和鞋带公式求三角形的面积
  20. 使用API 在zabbix监控系统中查看,创建及删除监控主机

热门文章

  1. IDC报告出炉,ZDNS连续五年DDI领域市场占有率第一
  2. 北京大学软件与微电子学院学习经验文章集78篇和1个专题
  3. CEF3:用CEF3实现最简单的浏览器
  4. 采药问题 c语言程序,采药-题解(C语言代码)
  5. win7 关闭防火墙
  6. 华为“天才少年” DIY 生日礼物:四个月打造能缝葡萄的机械臂!
  7. 黑客瞄准里约奥运会,多种手法可能让你中招
  8. WSL2跟踪 - 18945: 通过localhost直接访问 WSL2 容器中的服务
  9. 随手记_英语_学术写作_英文科技论文
  10. 李航(统计学习方法第四章)