f2fs系列文章truncate
这篇文章讲f2fs文件系统的截断,在调用这个函数之前会设置inode的i_size,这个函数完成在文件中i_size之后的数据的删除。其起始的函数是f2fs_truncate。
f2fs_truncate:检查inode的mode,如果不是REG或者是目录或者是LNK,那么直接返回。然后再调用f2fs_may_inline_data检查文件是否可以以内联的形式存放,如果不行,调用f2fs_convert_inline_inode来将内联的数据转换成正常索引的形式(目前还不知道这个地方有什么用)。接着调用truncate_blocks来进行真正的截断。最后修改inode的修改时间i_mtime,然后将inode设置为dirty。
int f2fs_truncate(struct inode *inode)
{int err;if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)))return 0;trace_f2fs_truncate(inode);if (!f2fs_may_inline_data(inode)) {err = f2fs_convert_inline_inode(inode);if (err)return err;}err = truncate_blocks(inode, i_size_read(inode), true);if (err)return err;inode->i_mtime = inode->i_ctime = current_time(inode);f2fs_mark_inode_dirty_sync(inode);return 0;
}
truncate_blocks:完成真正的所有的截断。首先计算截断位置下一个block的块索引free_from,然后调用get_node_page读取inode对应的f2fs_inode。f2fs_has_inline_data检查是否存放的是内联数据,如果是就调用truncate_inline对内联数据进行truncate操作,然后马上将修改后的f2fs_inode进行set_dirty操作。如果没有内联数据就按照正常索引的形式进行截断:首先通过set_new_dnode和get_dnode_of_data来获取free_from所在的dnode,通过计算得到dnode中大于等于free_from的块地址的个数count。如果dnode中的ofs或者是当前的dnode是f2fs_inode,那么就调用函数truncate_data_blocks_range把当前dnode(这里拥有923个块地址的f2fs_inode姑且也算一个dnode)中索引大于等于free_from的块地址全部删除,上述操作是为了删除部分的块地址来消除dnode中的零头,后面的删除可以以dnode为单位进行删除了。接下来的截断就由truncate_inode_blcoks来完成剩余的block的删除,最后调用truncate_partial_data_page对from所在的block中剩余的部分进行块内的截断。
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);free_from = (pgoff_t)F2FS_BYTES_TO_BLK(from + blocksize - 1);if (free_from >= sbi->max_file_blocks)goto free_partial;if (lock)f2fs_lock_op(sbi);ipage = get_node_page(sbi, inode->i_ino);if (IS_ERR(ipage)) {err = PTR_ERR(ipage);goto out;}if (f2fs_has_inline_data(inode)) {if (truncate_inline_inode(ipage, from))set_page_dirty(ipage);f2fs_put_page(ipage, 1);truncate_page = true;goto out;}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;}count = ADDRS_PER_PAGE(dn.node_page, inode);count -= dn.ofs_in_node;f2fs_bug_on(sbi, count < 0);if (dn.ofs_in_node || IS_INODE(dn.node_page)) {truncate_data_blocks_range(&dn, count);free_from += count;}f2fs_put_dnode(&dn);
free_next:err = truncate_inode_blocks(inode, free_from);
out:if (lock)f2fs_unlock_op(sbi);
free_partial:if (!err)err = truncate_partial_data_page(inode, from, truncate_page);trace_f2fs_truncate_blocks_exit(inode, err);return err;
}
truncate_inline_inode首先检查截断的位置from是否大于MAX_INLINE_DATA,这是最大的内联字节数。如果大于这个就直接返回。否则计算f2fs_inode中的内联数据起始地址,然后将存放内联数据的空间中的from后面的全部置零,也就是删除,最后将f2fs_inode进行set_dirty操作。
bool truncate_inline_inode(struct page *ipage, u64 from)
{void *addr;if (from >= MAX_INLINE_DATA)return false;addr = inline_data_addr(ipage);f2fs_wait_on_page_writeback(ipage, NODE, true);memset(addr + from, 0, MAX_INLINE_DATA - from);set_page_dirty(ipage);return true;
}
truncate_data_blocks_range计算dnode中的ofs的实际地址,然后对dnode中剩余的块地址进行遍历,如果其本身就是NULL_ADDR,那就直接跳过。如果不是,那就首先将其修改为NULL_ADDR,然后将其更新到dnode中,接着调用invalidate_blocks函数修改文件系统元数据sit。如果删除了部分的block,那就更新一下extent。
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;raw_node = F2FS_NODE(dn->node_page);addr = blkaddr_in_node(raw_node) + ofs;for (; count > 0; count--, addr++, dn->ofs_in_node++) {block_t blkaddr = le32_to_cpu(*addr);if (blkaddr == NULL_ADDR)continue;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;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;
}
truncate_partial_data_page:首先判断加入删除的页内偏移为零并且没有该页没有缓存,那么就直接返回没截下来如果缓存了from所在的block,那么就找到该块,如果找到这个块并且是最新的,那么直接跳转到下面进行页内截断,如果不满足,那就直接返回。如果没有缓存,那就通过函数get_lock_data_page来读取from所在block,然后将这个块中from到结束的一段全部置零也就是删除。也就是这个函数完成的是块内数据的删除操作。
static int truncate_partial_data_page(struct inode *inode, u64 from, bool cache_only)
{unsigned offset = from & (PAGE_SIZE - 1);pgoff_t index = from >> PAGE_SHIFT;struct address_space *mapping = inode->i_mapping;struct page *page;if (!offset && !cache_only)return 0;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 0;
truncate_out:f2fs_wait_on_page_writeback(page, DATA, true);zero_user(page, offset, PAGE_SIZE - offset);if (!cache_only || !f2fs_encrypted_inode(inode) || !S_ISREG(inode->i_mode))set_page_dirty(page);f2fs_put_page(page, 1);return 0;
}
truncate_inode_blocks:首先调用get_node_path来确定截断的位置level及offset这些,由于我们需要截断的位置所处的dnode、indnode是不能删除的,所以我们先对其进行处理一下。对于截断位置from在f2fs_inode中属于1级的也就是对应两个dnode,由于在truncate_blocks以及对齐到dnode了,所以直接跳过。如果截断位置from在f2fs_inode中属于2级的也就是对应两个indnode,如果对应offset==0也就是现在的对应的位置在一个全新的indnode,这时不要担心删除截断位置对应的indnode。同理如果截断位置from在f2fs_inode中属于3级的也就是对应dindnode,如果对应offset==0也就是现在的对应的位置在一个全新的indnode,这时不要担心删除截断位置对应的indnode。对于2级和3级,如果在offset!=0的情况下,那么需要删除一定数量的dnode来达到与indnode对齐的目的。这个是通过函数truncate_partial_nodes完成的。解决了这个问题之后,需要进行最后的全部清洗了,这个仍然是分级进行处理,如果是1级也就是对应两个dnode,那就直接调用函数truncate_dnode对dnode中的全部块地址以及dnode本身的删除,然后是循环进入2级的删除。如果是2级也就是对应两个indnode,那么调用truncate_nodes对indnode中的dnode及indnode本身的删除,然后循环进入3级的删除。如果是3级也就是对应dindnode,调用对dindnode中的indnode及indnode本身的删除。在上述删除的过程中,每次循环删除之后,如果offset==0,也就是删除的是一个完整的级别的block,那么此时在f2fs_inode中对应的nid也应该置0了。
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);level = get_node_path(inode, from, offset, noffset);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);switch (level) {case 0:case 1:nofs = noffset[1];break;case 2:nofs = noffset[1];if (!offset[level - 1])goto skip_partial;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;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:err = truncate_nodes(&dn, nofs, offset[1], 2);break;case NODE_DIND_BLOCK: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;
}
truncate_partial_nodes,这个函数只可能是由truncate_inode_blocks来调用,这个函数用来完成截断与indnode的对齐,也就是这个函数调用之后,后面的删除可以以indnode为单位进行删除了。首先调用get_node_page和get_nid来获取截断位置所在的indnode在f2fs_inode的nid或者dindnode中的nid。然后调用ra_node_pages对截断位置及之后的dnode进行与读取。接着对这些dnode进行遍历调用,检查对应的dnode的nid!=0就调用函数truncate_dnode对该dnode进行删除。在完成了这些删除之后,然后检查这次的删除是不是在offset==0,也就是从indnode的开始删除的(但是根据调用的情况,这个是不存在的),如果是这种情况那么将这个indnode本身也删除了。接着更新offset进入下一个全新的indnode。
static int truncate_partial_nodes(struct dnode_of_data *dn,struct f2fs_inode *ri, int *offset, int depth)
{struct page *pages[2];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;for (i = 0; i < idx + 1; i++) {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);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;
}
truncate_dnode:主要完成dnode的数据块地址和本身的删除。首先调用get_node_page读取到该dnode,然后truncate_data_blocks来完成对dnode中的数据块的删除。接着调用truncate_node来删除dnode本身。
static int truncate_dnode(struct dnode_of_data *dn)
{struct page *page;if (dn->nid == 0)return 1;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);dn->node_page = page;dn->ofs_in_node = 0;truncate_data_blocks(dn);truncate_node(dn);return 1;
}
truncate_data_blocks:通过调用truncate_data_blocks_range来完成一个dnode中的所有的数据块地址的删除。
void truncate_data_blocks(struct dnode_of_data *dn)
{truncate_data_blocks_range(dn, ADDRS_PER_BLOCK);
}
truncate_nodes:主要完成node本身的删除。首先调用get_node_info获得nid对应的node_info,接着检查i_blocks。接着调用函数invalidate_blocks修改文件系统元数据sit,将dnode对应的块地址无效掉,然后调用dec_valid_node_count更新有效的node的数量。然后调用set_node_addr函数将node_info中的块地址设置为NULL_ADDR(这个函数有把这个node_indo置为dirty)。接着检查删除的node是否为inode,如果是inode,那么首先调用remove_orphan_inode从孤儿inode中删除(由于文件inode的删除首先会将inode加入到orphaninode中)。然后是调用dec_valid_inode_count更新有效inode数量,接着调用f2fs_inode_synced来解除一些链表的联系(f2fs缓存机制维护了很多的链表)。接着将该node的dirty标志清除,因为删除了没有再同步的需要了,最后调用invalidate_mapping_pages删掉node_mapping中的页缓存。
static void truncate_node(struct dnode_of_data *dn)
{struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);struct node_info ni;get_node_info(sbi, dn->nid, &ni);if (dn->inode->i_blocks == 0) {f2fs_bug_on(sbi, ni.blk_addr != NULL_ADDR);goto invalidate;}f2fs_bug_on(sbi, ni.blk_addr == NULL_ADDR);invalidate_blocks(sbi, ni.blk_addr);dec_valid_node_count(sbi, dn->inode);set_node_addr(sbi, &ni, NULL_ADDR, false);if (dn->nid == dn->inode->i_ino) {remove_orphan_inode(sbi, dn->nid);dec_valid_inode_count(sbi);f2fs_inode_synced(dn->inode);}
invalidate: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);
}
truncate_nodes:这个函数主要完成indnode和dindirect的删除。首先get_node_page读取需要删除的nid所对应的indnode或者dindnode。然后ra_node_pages来对nid下面的dnode或者indnode进行预读。对于删除indnode的情况,对该node中的nid进行遍历,如果nid==0,那么直接跳过;如果nid!=0,那就调用truncate_dnode对该dnode进行删除;接着调用set_nid将indnode中的该位置的nid修改为0。删除dindnode跟上述的删除indnode的情况是差不多的,只是在删除dnode的时候是调用truncate_nodes递归删除掉indnode。最后如果删除的ofs==0,那说明删除的是一个全新的dindnode或indnode。那就调用truncate_node将这个dindnode或者indnode本身也删除。
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) {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;
}
f2fs系列文章truncate相关推荐
- f2fs系列文章fill_super(三)
这篇文章完成f2fs的segment管理结构f2fs_sm_info的创建和恢复. build_segment_manager:首先分配容纳f2fs_sm_info的空间,然后用f2fs_super_ ...
- f2fs系列文章fsck(五)
fsck_verify通过前面的检查结果来修正元数据. 首先是对nid的检查情况进行查看,f2fs_fsck中的nat_area_bitmap从开始的读取f2fs_nat_block中的所有的f2fs ...
- f2fs系列文章fsck(四)
补充一下之前略过的关于direct node.indirect node.dindirect node的检查. fsck_chk_didnode_blk,对NIDS_PER_BLOCK个nid进行遍历 ...
- 数据结构学习系列文章合集
数据结构学习系列文章目录 前言 1.稀疏数组和队列 稀疏数组和二位数组的转换 数组队列的实现 环形队列的介绍与实现 2.链表 单链表的增.删.改.查 总结 前言 学习数据结构记录,作为自己的笔记,同时 ...
- 积少成多 Flash(ActionScript 3.0 Flex 3.0) 系列文章索引
[源码下载] 积少成多 Flash(ActionScript 3.0 & Flex 3.0) 系列文章索引 作者:webabcd Flash 之 ActionScript 3.0 1.积少成 ...
- Scott的ASP.net MVC框架系列文章之四:处理表单数据(2)
前几周我发表了一系列文章介绍我们正在研究的ASP.NET MVC框架.ASP.NET MVC框架为你提供了一种新的开发Web应用程序的途径,这种途径可以让应用程序变得更加层次清晰,而且更加有利于对代码 ...
- Enterprise Library系列文章回顾与总结
Enterprise Library系列文章回顾与总结 自Enterprise Library 1.1 推出以来,Terry写了一系列的关于Enterprise Library的文章,其中得到了很多朋 ...
- 系列文章|OKR与敏捷(三):赋予团队自主权
OKR与敏捷开发的原理有着相似之处,但已经使用敏捷的团队再用OKR感觉会显得多余.这种误解的根源就在于对这两种模式不够了解,运用得当的情况下,OKR和敏捷可以形成强强联合的效果,他们可以创造出以价值为 ...
- 系列文章|OKR与敏捷(二):实现全栈敏捷
OKR与敏捷开发的原理有着相似之处,但已经使用敏捷的团队再用OKR感觉会显得多余.这种误解的根源就在于对这两种模式不够了解,运用得当的情况下,OKR和敏捷可以形成强强联合的效果,他们可以创造出以价值为 ...
最新文章
- 致广大关注《网络规划设计师考试案例梳理、真题透解与强化训练》读者朋友的一封信...
- 关于“中国大妈”的用户画像
- 基于三维激光点云的目标识别与跟踪研究
- c是过程化语言吗数据库,A.数据库语言B.过程化语言C.宿主语言D.数据库管理系统...
- Java基础学习总结(122)——Java八种基本数据类型的包装类及其装箱拆箱详解
- 使用idea导入文件夹作为项目时,在项目中的文件夹可能会被隐藏
- linux activemq修改端口号,linux下 activemq集群配置
- matlab simout,每日学习Matlab(2)
- Akka系统《sixteen》译
- java的swing案例
- 3D建模最常用的是那三款软件?
- Java轻量级缓存Ehcache与SpringBoot整合
- linux zip -e,在 Linux 上压缩文件:zip 命令的各种变体及用法
- 途胜怎样与android手机互联,现代途胜车载蓝牙怎么连接,途胜手机互联映射教程...
- 阿里和腾讯在泰国热战正酣,马云和马化腾要打世界大战?
- html5绘制随机五角星_HTML5 canvas基本绘图之绘制五角星
- 2019java8u201环境变量_CentOS7.4安装jdk1.8.0_201、Tomcat-8.5.38环境
- 智能座舱域控制器功能自动化测试方案
- 火星存在大型地下水系统,火星或曾是一片海洋
- Codeforces 91A-Newspaper Headline
热门文章
- c# 使用Office com组件时遇到的问题
- 计算机图形学 Unity ShaderLab 颜色混合运算相关计算方法
- 安装blockchain-explorer区块浏览器
- android 5.0小米1刷机包,小米1刷机包 V5稳定版V1.2 流畅纯净 精简省电 默认开启未知来源 Android4.1.2...
- 京东开源asyncTool之线程编排
- python迅雷sdk_Python批量创建迅雷任务及创建多个文件
- 奥特曼系列ol如何进老服务器,《奥特曼系列OL》新手攻略
- Android FaceBook登录 分享获取HashKey(密钥散列)的简单方法
- B2C电子商务网站使用Spring发送激活账号的电子邮件
- CSIRO Detects Raw Materials Used in the Ma IoT PLC accessking of the First Stars