F2FS的删除文件操作流程

F2FS的删除文件操作流程

一般文件组织结构

F2FS中的block被分成data block 和 node block两种,一般情况下,data block 记录了文件的具体数据,node block记录文件的索引方式。

node block包括f2fs_inodedirect_nodeindirect_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_nodeindirect_node

具体情况如下图所示:f2fs索引结构及索引路径

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);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值