f2fs系列文章truncate
? ? 這篇文章講f2fs文件系統(tǒng)的截斷,在調(diào)用這個函數(shù)之前會設(shè)置inode的i_size,這個函數(shù)完成在文件中i_size之后的數(shù)據(jù)的刪除。其起始的函數(shù)是f2fs_truncate。
????f2fs_truncate:檢查inode的mode,如果不是REG或者是目錄或者是LNK,那么直接返回。然后再調(diào)用f2fs_may_inline_data檢查文件是否可以以內(nèi)聯(lián)的形式存放,如果不行,調(diào)用f2fs_convert_inline_inode來將內(nèi)聯(lián)的數(shù)據(jù)轉(zhuǎn)換成正常索引的形式(目前還不知道這個地方有什么用)。接著調(diào)用truncate_blocks來進(jìn)行真正的截斷。最后修改inode的修改時間i_mtime,然后將inode設(shè)置為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,然后調(diào)用get_node_page讀取inode對應(yīng)的f2fs_inode。f2fs_has_inline_data檢查是否存放的是內(nèi)聯(lián)數(shù)據(jù),如果是就調(diào)用truncate_inline對內(nèi)聯(lián)數(shù)據(jù)進(jìn)行truncate操作,然后馬上將修改后的f2fs_inode進(jìn)行set_dirty操作。如果沒有內(nèi)聯(lián)數(shù)據(jù)就按照正常索引的形式進(jìn)行截斷:首先通過set_new_dnode和get_dnode_of_data來獲取free_from所在的dnode,通過計算得到dnode中大于等于free_from的塊地址的個數(shù)count。如果dnode中的ofs或者是當(dāng)前的dnode是f2fs_inode,那么就調(diào)用函數(shù)truncate_data_blocks_range把當(dāng)前dnode(這里擁有923個塊地址的f2fs_inode姑且也算一個dnode)中索引大于等于free_from的塊地址全部刪除,上述操作是為了刪除部分的塊地址來消除dnode中的零頭,后面的刪除可以以dnode為單位進(jìn)行刪除了。接下來的截斷就由truncate_inode_blcoks來完成剩余的block的刪除,最后調(diào)用truncate_partial_data_page對from所在的block中剩余的部分進(jìn)行塊內(nèi)的截斷。
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,這是最大的內(nèi)聯(lián)字節(jié)數(shù)。如果大于這個就直接返回。否則計算f2fs_inode中的內(nèi)聯(lián)數(shù)據(jù)起始地址,然后將存放內(nèi)聯(lián)數(shù)據(jù)的空間中的from后面的全部置零,也就是刪除,最后將f2fs_inode進(jìn)行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中剩余的塊地址進(jìn)行遍歷,如果其本身就是NULL_ADDR,那就直接跳過。如果不是,那就首先將其修改為NULL_ADDR,然后將其更新到dnode中,接著調(diào)用invalidate_blocks函數(shù)修改文件系統(tǒng)元數(shù)據(jù)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:首先判斷加入刪除的頁內(nèi)偏移為零并且沒有該頁沒有緩存,那么就直接返回沒截下來如果緩存了from所在的block,那么就找到該塊,如果找到這個塊并且是最新的,那么直接跳轉(zhuǎn)到下面進(jìn)行頁內(nèi)截斷,如果不滿足,那就直接返回。如果沒有緩存,那就通過函數(shù)get_lock_data_page來讀取from所在block,然后將這個塊中from到結(jié)束的一段全部置零也就是刪除。也就是這個函數(shù)完成的是塊內(nèi)數(shù)據(jù)的刪除操作。
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:首先調(diào)用get_node_path來確定截斷的位置level及offset這些,由于我們需要截斷的位置所處的dnode、indnode是不能刪除的,所以我們先對其進(jìn)行處理一下。對于截斷位置from在f2fs_inode中屬于1級的也就是對應(yīng)兩個dnode,由于在truncate_blocks以及對齊到dnode了,所以直接跳過。如果截斷位置from在f2fs_inode中屬于2級的也就是對應(yīng)兩個indnode,如果對應(yīng)offset==0也就是現(xiàn)在的對應(yīng)的位置在一個全新的indnode,這時不要擔(dān)心刪除截斷位置對應(yīng)的indnode。同理如果截斷位置from在f2fs_inode中屬于3級的也就是對應(yīng)dindnode,如果對應(yīng)offset==0也就是現(xiàn)在的對應(yīng)的位置在一個全新的indnode,這時不要擔(dān)心刪除截斷位置對應(yīng)的indnode。對于2級和3級,如果在offset!=0的情況下,那么需要刪除一定數(shù)量的dnode來達(dá)到與indnode對齊的目的。這個是通過函數(shù)truncate_partial_nodes完成的。解決了這個問題之后,需要進(jìn)行最后的全部清洗了,這個仍然是分級進(jìn)行處理,如果是1級也就是對應(yīng)兩個dnode,那就直接調(diào)用函數(shù)truncate_dnode對dnode中的全部塊地址以及dnode本身的刪除,然后是循環(huán)進(jìn)入2級的刪除。如果是2級也就是對應(yīng)兩個indnode,那么調(diào)用truncate_nodes對indnode中的dnode及indnode本身的刪除,然后循環(huán)進(jìn)入3級的刪除。如果是3級也就是對應(yīng)dindnode,調(diào)用對dindnode中的indnode及indnode本身的刪除。在上述刪除的過程中,每次循環(huán)刪除之后,如果offset==0,也就是刪除的是一個完整的級別的block,那么此時在f2fs_inode中對應(yīng)的nid也應(yīng)該置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,這個函數(shù)只可能是由truncate_inode_blocks來調(diào)用,這個函數(shù)用來完成截斷與indnode的對齊,也就是這個函數(shù)調(diào)用之后,后面的刪除可以以indnode為單位進(jìn)行刪除了。首先調(diào)用get_node_page和get_nid來獲取截斷位置所在的indnode在f2fs_inode的nid或者dindnode中的nid。然后調(diào)用ra_node_pages對截斷位置及之后的dnode進(jìn)行與讀取。接著對這些dnode進(jìn)行遍歷調(diào)用,檢查對應(yīng)的dnode的nid!=0就調(diào)用函數(shù)truncate_dnode對該dnode進(jìn)行刪除。在完成了這些刪除之后,然后檢查這次的刪除是不是在offset==0,也就是從indnode的開始刪除的(但是根據(jù)調(diào)用的情況,這個是不存在的),如果是這種情況那么將這個indnode本身也刪除了。接著更新offset進(jìn)入下一個全新的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的數(shù)據(jù)塊地址和本身的刪除。首先調(diào)用get_node_page讀取到該dnode,然后truncate_data_blocks來完成對dnode中的數(shù)據(jù)塊的刪除。接著調(diào)用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:通過調(diào)用truncate_data_blocks_range來完成一個dnode中的所有的數(shù)據(jù)塊地址的刪除。
?
void truncate_data_blocks(struct dnode_of_data *dn) {truncate_data_blocks_range(dn, ADDRS_PER_BLOCK); }????truncate_nodes:主要完成node本身的刪除。首先調(diào)用get_node_info獲得nid對應(yīng)的node_info,接著檢查i_blocks。接著調(diào)用函數(shù)invalidate_blocks修改文件系統(tǒng)元數(shù)據(jù)sit,將dnode對應(yīng)的塊地址無效掉,然后調(diào)用dec_valid_node_count更新有效的node的數(shù)量。然后調(diào)用set_node_addr函數(shù)將node_info中的塊地址設(shè)置為NULL_ADDR(這個函數(shù)有把這個node_indo置為dirty)。接著檢查刪除的node是否為inode,如果是inode,那么首先調(diào)用remove_orphan_inode從孤兒inode中刪除(由于文件inode的刪除首先會將inode加入到orphaninode中)。然后是調(diào)用dec_valid_inode_count更新有效inode數(shù)量,接著調(diào)用f2fs_inode_synced來解除一些鏈表的聯(lián)系(f2fs緩存機制維護(hù)了很多的鏈表)。接著將該node的dirty標(biāo)志清除,因為刪除了沒有再同步的需要了,最后調(diào)用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:這個函數(shù)主要完成indnode和dindirect的刪除。首先get_node_page讀取需要刪除的nid所對應(yīng)的indnode或者dindnode。然后ra_node_pages來對nid下面的dnode或者indnode進(jìn)行預(yù)讀。對于刪除indnode的情況,對該node中的nid進(jìn)行遍歷,如果nid==0,那么直接跳過;如果nid!=0,那就調(diào)用truncate_dnode對該dnode進(jìn)行刪除;接著調(diào)用set_nid將indnode中的該位置的nid修改為0。刪除dindnode跟上述的刪除indnode的情況是差不多的,只是在刪除dnode的時候是調(diào)用truncate_nodes遞歸刪除掉indnode。最后如果刪除的ofs==0,那說明刪除的是一個全新的dindnode或indnode。那就調(diào)用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; }?
總結(jié)
以上是生活随笔為你收集整理的f2fs系列文章truncate的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: liferay Dynamic Quer
- 下一篇: 书法拓片matlab,拓墨书法作品(拓片