这篇文章讲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相关推荐

  1. f2fs系列文章fill_super(三)

    这篇文章完成f2fs的segment管理结构f2fs_sm_info的创建和恢复. build_segment_manager:首先分配容纳f2fs_sm_info的空间,然后用f2fs_super_ ...

  2. f2fs系列文章fsck(五)

    fsck_verify通过前面的检查结果来修正元数据. 首先是对nid的检查情况进行查看,f2fs_fsck中的nat_area_bitmap从开始的读取f2fs_nat_block中的所有的f2fs ...

  3. f2fs系列文章fsck(四)

    补充一下之前略过的关于direct node.indirect node.dindirect node的检查. fsck_chk_didnode_blk,对NIDS_PER_BLOCK个nid进行遍历 ...

  4. 数据结构学习系列文章合集

    数据结构学习系列文章目录 前言 1.稀疏数组和队列 稀疏数组和二位数组的转换 数组队列的实现 环形队列的介绍与实现 2.链表 单链表的增.删.改.查 总结 前言 学习数据结构记录,作为自己的笔记,同时 ...

  5. 积少成多 Flash(ActionScript 3.0 Flex 3.0) 系列文章索引

    [源码下载] 积少成多 Flash(ActionScript 3.0 & Flex 3.0) 系列文章索引 作者:webabcd Flash 之 ActionScript 3.0  1.积少成 ...

  6. Scott的ASP.net MVC框架系列文章之四:处理表单数据(2)

    前几周我发表了一系列文章介绍我们正在研究的ASP.NET MVC框架.ASP.NET MVC框架为你提供了一种新的开发Web应用程序的途径,这种途径可以让应用程序变得更加层次清晰,而且更加有利于对代码 ...

  7. Enterprise Library系列文章回顾与总结

    Enterprise Library系列文章回顾与总结 自Enterprise Library 1.1 推出以来,Terry写了一系列的关于Enterprise Library的文章,其中得到了很多朋 ...

  8. 系列文章|OKR与敏捷(三):赋予团队自主权

    OKR与敏捷开发的原理有着相似之处,但已经使用敏捷的团队再用OKR感觉会显得多余.这种误解的根源就在于对这两种模式不够了解,运用得当的情况下,OKR和敏捷可以形成强强联合的效果,他们可以创造出以价值为 ...

  9. 系列文章|OKR与敏捷(二):实现全栈敏捷

    OKR与敏捷开发的原理有着相似之处,但已经使用敏捷的团队再用OKR感觉会显得多余.这种误解的根源就在于对这两种模式不够了解,运用得当的情况下,OKR和敏捷可以形成强强联合的效果,他们可以创造出以价值为 ...

最新文章

  1. 致广大关注《网络规划设计师考试案例梳理、真题透解与强化训练》读者朋友的一封信...
  2. 关于“中国大妈”的用户画像
  3. 基于三维激光点云的目标识别与跟踪研究
  4. c是过程化语言吗数据库,A.数据库语言B.过程化语言C.宿主语言D.数据库管理系统...
  5. Java基础学习总结(122)——Java八种基本数据类型的包装类及其装箱拆箱详解
  6. 使用idea导入文件夹作为项目时,在项目中的文件夹可能会被隐藏
  7. linux activemq修改端口号,linux下 activemq集群配置
  8. matlab simout,每日学习Matlab(2)
  9. Akka系统《sixteen》译
  10. java的swing案例
  11. 3D建模最常用的是那三款软件?
  12. Java轻量级缓存Ehcache与SpringBoot整合
  13. linux zip -e,在 Linux 上压缩文件:zip 命令的各种变体及用法
  14. 途胜怎样与android手机互联,现代途胜车载蓝牙怎么连接,途胜手机互联映射教程...
  15. 阿里和腾讯在泰国热战正酣,马云和马化腾要打世界大战?
  16. html5绘制随机五角星_HTML5 canvas基本绘图之绘制五角星
  17. 2019java8u201环境变量_CentOS7.4安装jdk1.8.0_201、Tomcat-8.5.38环境
  18. 智能座舱域控制器功能自动化测试方案
  19. 火星存在大型地下水系统,火星或曾是一片海洋
  20. Codeforces 91A-Newspaper Headline

热门文章

  1. c# 使用Office com组件时遇到的问题
  2. 计算机图形学 Unity ShaderLab 颜色混合运算相关计算方法
  3. 安装blockchain-explorer区块浏览器
  4. android 5.0小米1刷机包,小米1刷机包 V5稳定版V1.2 流畅纯净 精简省电 默认开启未知来源 Android4.1.2...
  5. 京东开源asyncTool之线程编排
  6. python迅雷sdk_Python批量创建迅雷任务及创建多个文件
  7. 奥特曼系列ol如何进老服务器,《奥特曼系列OL》新手攻略
  8. Android FaceBook登录 分享获取HashKey(密钥散列)的简单方法
  9. B2C电子商务网站使用Spring发送激活账号的电子邮件
  10. CSIRO Detects Raw Materials Used in the Ma IoT PLC accessking of the First Stars