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 */
} __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;
}

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值