F2FS的删除文件操作流程
一般文件组织结构
F2FS中的block被分成data block 和 node block两种,一般情况下,data block 记录了文件的具体数据,node block记录文件的索引方式。
node block包括f2fs_inode
,direct_node
和indirect_node
三种。在源码中三者以联合体的方式保存在struct f2fs_node
中:
struct f2fs_node {
/* can be one of three types: inode, direct, and indirect types */
union {
struct f2fs_inode i;
struct direct_node dn;
struct indirect_node in;
};
struct node_footer footer;
} __packed;
direct_node
用一个数组记录最多1018个data block的物理地址。indirect_node
用一个数据记录最多1018个node的nid。
struct direct_node {
__le32 addr[DEF_ADDRS_PER_BLOCK]; /* array of data block address */
} __packed;
struct indirect_node {
__le32 nid[NIDS_PER_BLOCK]; /* array of data block address */
} __packed;
每个文件都有有且只有一个f2fs_inode
,记录了文件的基本索引方式。f2fs_inode
的核心成员是两个数组__le32 i_addr[DEF_ADDRS_PER_INODE]
和 __le32 i_nid[DEF_NIDS_PER_INODE]
,一般情况下分别记录最多923个data block的物理地址和5个node block 的nid(nid是每个node的唯一标识),前者用于直接寻址,后置用于间接寻址。
f2fs_inode
中的数组i_nid[5]
中前两个nid记录的是direct_node的nid,中间两个nid记录的是indirect_node的nid,最后一个记录的也是indirect_node的nid,但是用于这个indirect_node记录的也是indirect_node的nid,也就是用于三级索引。
struct f2fs_inode {
...
union {
...
__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 */
} __packed;
F2FS中一个block默认是4KB,所以文件最大为:
923 * 4KB + 2 * 1018 * 4KB + 2 * 1018 * 1018 * 4KB +1018 * 1018 * 1018 * 4KB ≈ 3.938TB
内联文件组织
当文件很小时,例如只有1Byte数据,按照上面的组织方式需要用一个data block存储数据和一个node block存储inode,即使用了2*4KB的空间存储1Byte数据。
为节省空间,F2FS采用内联的方式存储小文件,即当文件很小时,直接将数据存储在f2fs_inode的__le32 i_addr[DEF_ADDRS_PER_INODE]
部分。保留一个数据成员留作它用,所以这里最大可以存储4B*922=3688B。
寻址方式
当访问文件某个位置的数据时,需要从f2fs_inode开始层层索引,直到获取这个位置对应block的物理地址,这个过程在get_dnode_of_data()
中完成。
get_dnode_of_data()
接收一个block 偏移形参 index,也就是待当问位置位于文件中第几个block,然后调用get_node_path()
计算block偏移对应的索引路径,然后从f2fs_inode
出发,直接寻址或者最多三次间接寻址找到对应的data block地址、记录这个block地址的node。
get_node_path()
计算得到的索引路径由两个数组构成,分别是int offset[4]
和unsigned int noffset[4]
。offset记录路径上每个节点在上一个节点中的偏移量,noffset记录路径上一共使用了多少个direct_node
或 indirect_node
。
具体情况如下图所示:
truncate流程
文件系统中的删除操作由truncate实现,F2FS中的truncate流程基本如下,可以从truncate_blocks()
开始分析。
各个函数的功能如下:
// 将inode对应的文件中from字节之后的数据全部删除, 是f2fs正式开始truncate的位置
int truncate_blocks(struct inode *inode, u64 from, bool lock)
// 如果文件是内联的, 那么调用这个函数删除执行内联的truncate
void truncate_inline_inode(struct inode *inode, struct page *ipage, u64 from)
// 截断direct node中,后count个addr对应的block
int truncate_data_blocks_range(struct dnode_of_data *dn, int count);
// 调用上面的truncate_data_blocks_range() 将整个direct node 包含的block截断
void truncate_data_blocks(struct dnode_of_data *dn);
// 先截断direct node,再截断indirect node 和 dindirect node
int truncate_inode_blocks(struct inode *inode, pgoff_t from);
// 截断direct node
static int truncate_partial_nodes(struct dnode_of_data *dn, struct f2fs_inode *ri, int *offset, int depth);
// 截断indirect node 和 dindirect node
static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs, int ofs, int depth);
// 截断这个node本身
static void truncate_node(struct dnode_of_data *dn);
// 先调用truncate_data_blocks() 截断这个node 里所有的block, 再调用truncate_node() 截断这个node自己
static int truncate_dnode(struct dnode_of_data *dn);
// 截断inode本身
int remove_inode_page(struct inode *inode);
具体实现:
/*
* 将文件中from字节之后的数据都删除
*/
int truncate_blocks(struct inode *inode, u64 from, bool lock)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
unsigned int blocksize = inode->i_sb->s_blocksize;
struct dnode_of_data dn;
pgoff_t free_from;
int count = 0, err = 0;
struct page *ipage;
bool truncate_page = false;
trace_f2fs_truncate_blocks_enter(inode, from);
// from表示的是截断位置在文件中的字节偏移
// 这里计算截断位置的下一个block,也就是第一个要删除的block, free_from表示block偏移
free_from = (pgoff_t)F2FS_BYTES_TO_BLK(from + blocksize - 1);
// 判断free_from 这个block有没有超出范围
if (free_from >= sbi->max_file_blocks)
goto free_partial;
if (lock)
f2fs_lock_op(sbi);
// 获得inode对应的page
ipage = get_node_page(sbi, inode->i_ino);
if (IS_ERR(ipage)) {
err = PTR_ERR(ipage);
goto out;
}
// 如果有内敛数据,就直接调用truncate_inline_inode()来截断内联数据
if (f2fs_has_inline_data(inode)) {
truncate_inline_inode(inode, ipage, from);
f2fs_put_page(ipage, 1);
truncate_page = true;
goto out;
}
// 获取 free_from 的block在文件中的dnode_of_data信息
// 包括这个block的对应的 node 的 nid、对应的node page、数据偏移、物理地址等
set_new_dnode(&dn, inode, ipage, NULL, 0);
err = get_dnode_of_data(&dn, free_from, LOOKUP_NODE_RA);
if (err) {
if (err == -ENOENT)
goto free_next;
goto out;
}
// 计算free_from对应的node中大于free_from的block数量
count = ADDRS_PER_PAGE(dn.node_page, inode);
count -= dn.ofs_in_node;
f2fs_bug_on(sbi, count < 0);
// 如果这是个inode或者数据偏移不是0
// 调用 truncate_data_blocks_range 把 node 中大于 free_from 的 block 全部截断
// 如果是inode, 那么count = 923 - dn.ofs_in_node, 否则 count = 1018 - dn.ofs_in_node
// 也就是说truncate_data_blocks_range() 只截断dn中记录的node的addr 部分,而不会涉及nid 部分
// nid部分由下面的truncate_inode_blocks()负责
if (dn.ofs_in_node || IS_INODE(dn.node_page)) {
truncate_data_blocks_range(&dn, count);
free_from += count;
}
/*
* 以上的操作是截断 free_from 对应的 node 中大于 free_from 的 block
* 以block为单位,也就是 node 中的零头
* 下面以 node 为单位做截断
*/
f2fs_put_dnode(&dn);
free_next:
// 截断 free_from 之后的所有 node 的 block
err = truncate_inode_blocks(inode, free_from);
out:
if (lock)
f2fs_unlock_op(sbi);
free_partial:
/* lastly zero out the first data page */
// 对字节偏移 from 所在 block 进行块内截断
if (!err)
err = truncate_partial_data_page(inode, from, truncate_page);
trace_f2fs_truncate_blocks_exit(inode, err);
return err;
}
/*
* 将ipage中要删除的部分置0
*/
void truncate_inline_inode(struct inode *inode, struct page *ipage, u64 from)
{
void *addr;
// 内联文件有个最大文件大小,先检查字节偏移有没有超出这个范围
if (from >= MAX_INLINE_DATA(inode))
return;
// 计算page中内联数据起始部分在内存中的地址,所以addr是指page里面的某一地址
addr = inline_data_addr(inode, ipage);
f2fs_wait_on_page_writeback(ipage, NODE, true);
// 将字节偏移from之后的部分全部置零, 将page置脏,并且置PageUptodate
memset(addr + from, 0, MAX_INLINE_DATA(inode) - from);
set_page_dirty(ipage);
if (from == 0)
clear_inode_flag(inode, FI_DATA_EXIST);
}
/*
* All the block addresses of data and nodes should be nullified.
*/
int truncate_inode_blocks(struct inode *inode, pgoff_t from)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
int err = 0, cont = 1;
int level, offset[4], noffset[4];
unsigned int nofs = 0;
struct f2fs_inode *ri;
struct dnode_of_data dn;
struct page *page;
trace_f2fs_truncate_inode_blocks_enter(inode, from);
// 先获取block 偏移 from 对应的node的路径,即offset[] 和 nooffset[]
level = get_node_path(inode, from, offset, noffset);
if (level < 0)
return level;
page = get_node_page(sbi, inode->i_ino);
if (IS_ERR(page)) {
trace_f2fs_truncate_inode_blocks_exit(inode, PTR_ERR(page));
return PTR_ERR(page);
}
set_new_dnode(&dn, inode, page, NULL, 0);
unlock_page(page);
ri = F2FS_INODE(page);
// truncate_blocks() 中已经对level = 0时做了处理,所以这里不会命中level = 0
switch (level) {
case 0:
case 1:
nofs = noffset[1];
break;
case 2:
nofs = noffset[1];
if (!offset[level - 1])
goto skip_partial;
// 截断direct node
err = truncate_partial_nodes(&dn, ri, offset, level);
if (err < 0 && err != -ENOENT)
goto fail;
nofs += 1 + NIDS_PER_BLOCK;
break;
case 3:
nofs = 5 + 2 * NIDS_PER_BLOCK;
if (!offset[level - 1])
goto skip_partial;
// 截断direct node
err = truncate_partial_nodes(&dn, ri, offset, level);
if (err < 0 && err != -ENOENT)
goto fail;
break;
default:
BUG();
}
skip_partial:
while (cont) {
dn.nid = le32_to_cpu(ri->i_nid[offset[0] - NODE_DIR1_BLOCK]);
switch (offset[0]) {
case NODE_DIR1_BLOCK:
case NODE_DIR2_BLOCK:
err = truncate_dnode(&dn);
break;
case NODE_IND1_BLOCK:
case NODE_IND2_BLOCK:
// 截断 indirect node 和 dindirect node
err = truncate_nodes(&dn, nofs, offset[1], 2);
break;
case NODE_DIND_BLOCK:
// 截断 indirect node 和 dindirect node
err = truncate_nodes(&dn, nofs, offset[1], 3);
cont = 0;
break;
default:
BUG();
}
if (err < 0 && err != -ENOENT)
goto fail;
if (offset[1] == 0 &&
ri->i_nid[offset[0] - NODE_DIR1_BLOCK]) {
lock_page(page);
BUG_ON(page->mapping != NODE_MAPPING(sbi));
f2fs_wait_on_page_writeback(page, NODE, true);
ri->i_nid[offset[0] - NODE_DIR1_BLOCK] = 0;
set_page_dirty(page);
unlock_page(page);
}
offset[1] = 0;
offset[0]++;
nofs += err;
}
fail:
f2fs_put_page(page, 0);
trace_f2fs_truncate_inode_blocks_exit(inode, err);
return err > 0 ? 0 : err;
}
/*
* 截断 dn 中记录的 node 的后 count 个block
*/
int truncate_data_blocks_range(struct dnode_of_data *dn, int count)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
struct f2fs_node *raw_node;
int nr_free = 0, ofs = dn->ofs_in_node, len = count;
__le32 *addr;
int base = 0;
if (IS_INODE(dn->node_page) && f2fs_has_extra_attr(dn->inode))
base = get_extra_isize(dn->inode);
raw_node = F2FS_NODE(dn->node_page);
addr = blkaddr_in_node(raw_node) + base + ofs;
for (; count > 0; count--, addr++, dn->ofs_in_node++) {
block_t blkaddr = le32_to_cpu(*addr);
if (blkaddr == NULL_ADDR)
continue;
// 将修改的blkaddr 更新到 这个 dn 对应的 node 中去
dn->data_blkaddr = NULL_ADDR;
set_data_blkaddr(dn);
invalidate_blocks(sbi, blkaddr);
if (dn->ofs_in_node == 0 && IS_INODE(dn->node_page))
clear_inode_flag(dn->inode, FI_FIRST_BLOCK_WRITTEN);
nr_free++;
}
if (nr_free) {
pgoff_t fofs;
/*
* once we invalidate valid blkaddr in range [ofs, ofs + count],
* we will invalidate all blkaddr in the whole range.
*/
fofs = start_bidx_of_node(ofs_of_node(dn->node_page),
dn->inode) + ofs;
f2fs_update_extent_cache_range(dn, fofs, 0, len);
dec_valid_block_count(sbi, dn->inode, nr_free);
}
dn->ofs_in_node = ofs;
f2fs_update_time(sbi, REQ_TIME);
trace_f2fs_truncate_data_blocks_range(dn->inode, dn->nid,
dn->ofs_in_node, nr_free);
return nr_free;
}
/*
* 截断 inode 中 from 字节之后的block
*/
static int truncate_partial_data_page(struct inode *inode, u64 from,
bool cache_only)
{
unsigned offset = from & (PAGE_SIZE - 1); // from 字节位置对应在block中的偏移
pgoff_t index = from >> PAGE_SHIFT; // from 字节位置对应的block地址
struct address_space *mapping = inode->i_mapping;
struct page *page;
if (!offset && !cache_only)
return 0;
// 如果缓存了from所在的block,那么找到这个块对应的page
if (cache_only) {
page = find_lock_page(mapping, index);
if (page && PageUptodate(page))
goto truncate_out;
f2fs_put_page(page, 1);
return 0;
}
page = get_lock_data_page(inode, index, true);
if (IS_ERR(page))
return PTR_ERR(page) == -ENOENT ? 0 : PTR_ERR(page);
truncate_out:
// 把from所在block中,位于from后面的内容全部置零
f2fs_wait_on_page_writeback(page, DATA, true);
zero_user(page, offset, PAGE_SIZE - offset);
/* An encrypted inode should have a key and truncate the last page. */
f2fs_bug_on(F2FS_I_SB(inode), cache_only && f2fs_encrypted_inode(inode));
if (!cache_only)
set_page_dirty(page);
f2fs_put_page(page, 1);
return 0;
}
/*
* 截断indirect node 和 dindirect node
* depth = 2 或 3
*/
static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs,
int ofs, int depth)
{
struct dnode_of_data rdn = *dn;
struct page *page;
struct f2fs_node *rn;
nid_t child_nid;
unsigned int child_nofs;
int freed = 0;
int i, ret;
if (dn->nid == 0)
return NIDS_PER_BLOCK + 1;
trace_f2fs_truncate_nodes_enter(dn->inode, dn->nid, dn->data_blkaddr);
page = get_node_page(F2FS_I_SB(dn->inode), dn->nid);
if (IS_ERR(page)) {
trace_f2fs_truncate_nodes_exit(dn->inode, PTR_ERR(page));
return PTR_ERR(page);
}
ra_node_pages(page, ofs, NIDS_PER_BLOCK);
rn = F2FS_NODE(page);
if (depth < 3) {
for (i = ofs; i < NIDS_PER_BLOCK; i++, freed++) {
child_nid = le32_to_cpu(rn->in.nid[i]);
if (child_nid == 0)
continue;
rdn.nid = child_nid;
ret = truncate_dnode(&rdn);
if (ret < 0)
goto out_err;
if (set_nid(page, i, 0, false))
dn->node_changed = true;
}
} else {
child_nofs = nofs + ofs * (NIDS_PER_BLOCK + 1) + 1;
for (i = ofs; i < NIDS_PER_BLOCK; i++) {
child_nid = le32_to_cpu(rn->in.nid[i]);
if (child_nid == 0) {
child_nofs += NIDS_PER_BLOCK + 1;
continue;
}
rdn.nid = child_nid;
ret = truncate_nodes(&rdn, child_nofs, 0, depth - 1);
if (ret == (NIDS_PER_BLOCK + 1)) {
if (set_nid(page, i, 0, false))
dn->node_changed = true;
child_nofs += ret;
} else if (ret < 0 && ret != -ENOENT) {
goto out_err;
}
}
freed = child_nofs;
}
if (!ofs) {
/* remove current indirect node */
dn->node_page = page;
truncate_node(dn);
freed++;
} else {
f2fs_put_page(page, 1);
}
trace_f2fs_truncate_nodes_exit(dn->inode, freed);
return freed;
out_err:
f2fs_put_page(page, 1);
trace_f2fs_truncate_nodes_exit(dn->inode, ret);
return ret;
}
/*
* 这个函数用来完成截断与indirect node的对齐
* 也就是这个函数调用之后,后面的删除可以以indirect node为单位进行删除了
* depth=2或3 截断direct node,从而剩下 indirect node
*/
static int truncate_partial_nodes(struct dnode_of_data *dn, struct f2fs_inode *ri, int *offset, int depth)
{
struct page *pages[2]; // pages数组存储的是指向page的指针
nid_t nid[3];
nid_t child_nid;
int err = 0;
int i;
int idx = depth - 2;
nid[0] = le32_to_cpu(ri->i_nid[offset[0] - NODE_DIR1_BLOCK]);
if (!nid[0])
return 0;
/* get indirect nodes in the path */
// 通过这个循环将node路径找出来,也就是路径上每个node的page 和 nid
for (i = 0; i < idx + 1; i++) {
/* reference count'll be increased */
pages[i] = get_node_page(F2FS_I_SB(dn->inode), nid[i]);
if (IS_ERR(pages[i])) {
err = PTR_ERR(pages[i]);
idx = i - 1;
goto fail;
}
nid[i + 1] = get_nid(pages[i], offset[i + 1], false);
}
ra_node_pages(pages[idx], offset[idx + 1], NIDS_PER_BLOCK);
/* free direct nodes linked to a partial indirect node */
// offset[idx + 1] = offset[depth - 1] 也就是最后一个indirect node的nid
// depth = 2 或 depth =3
// 所以 idx = 0 或 1
for (i = offset[idx + 1]; i < NIDS_PER_BLOCK; i++) {
child_nid = get_nid(pages[idx], i, false);
if (!child_nid)
continue;
dn->nid = child_nid;
err = truncate_dnode(dn);
if (err < 0)
goto fail;
if (set_nid(pages[idx], i, 0, false))
dn->node_changed = true;
}
if (offset[idx + 1] == 0) {
dn->node_page = pages[idx];
dn->nid = nid[idx];
truncate_node(dn);
} else {
f2fs_put_page(pages[idx], 1);
}
offset[idx]++;
offset[idx + 1] = 0;
idx--;
fail:
for (i = idx; i >= 0; i--)
f2fs_put_page(pages[i], 1);
trace_f2fs_truncate_partial_nodes(dn->inode, nid, depth, err);
return err;
}
/*
* 截断dn记录的nid对应的node里存储的所有block 和 这个node本身
*/
static int truncate_dnode(struct dnode_of_data *dn)
{
struct page *page;
if (dn->nid == 0)
return 1;
/* get direct node */
page = get_node_page(F2FS_I_SB(dn->inode), dn->nid);
if (IS_ERR(page) && PTR_ERR(page) == -ENOENT)
return 1;
else if (IS_ERR(page))
return PTR_ERR(page);
/* Make dnode_of_data for parameter */
dn->node_page = page;
dn->ofs_in_node = 0;
truncate_data_blocks(dn);
truncate_node(dn);
return 1;
}
static void truncate_node(struct dnode_of_data *dn)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
struct node_info ni;
// 先获取node_info
get_node_info(sbi, dn->nid, &ni);
f2fs_bug_on(sbi, ni.blk_addr == NULL_ADDR);
/* Deallocate node address */
// 修改文件系统元数据sit,将对应的node置为无效
invalidate_blocks(sbi, ni.blk_addr);
dec_valid_node_count(sbi, dn->inode, dn->nid == dn->inode->i_ino);
set_node_addr(sbi, &ni, NULL_ADDR, false);
// 由于文件inode的删除首先会将inode加入到orphan inode中
/ 所以这里如果是inode,那就从orphan inode中删除这个inode
if (dn->nid == dn->inode->i_ino) {
remove_orphan_inode(sbi, dn->nid);
dec_valid_inode_count(sbi);
f2fs_inode_synced(dn->inode); // 解除这个inode在内存中的一些链表关系
}
clear_node_page_dirty(dn->node_page);
set_sbi_flag(sbi, SBI_IS_DIRTY);
f2fs_put_page(dn->node_page, 1);
// 删除页缓存
invalidate_mapping_pages(NODE_MAPPING(sbi),
dn->node_page->index, dn->node_page->index);
dn->node_page = NULL;
trace_f2fs_truncate_node(dn->inode, dn->nid, ni.blk_addr);
}