ext2文件系统inode.c分析(一)

2021SC@SDUSC 

        

static int __ext2_write_inode(struct inode *inode, int do_sync);


        ext2_inode_is_fast_symlink检测一个inode是不是一个快速符号链接。快速符号链接是一个ext2文件系统的特性,要求符号链接指向的文件名是一个短的路径名,小于等于60字节,就把它的路径名放在一个inode里,而不用通过一个数据块来进行转换
static inline int ext2_inode_is_fast_symlink(struct inode *inode)
{

     获取inode的info,看其中是否有acl结构,是则将文件的块大小右移9位,即为占有的磁道数目。
    int ea_blocks = EXT2_I(inode)->i_file_acl ?
        (inode->i_sb->s_blocksize >> 9) : 0;

        返回一个与值,即S_ISLNK(inode->i_mode)和符号链接是否占用一个块。

    return (S_ISLNK(inode->i_mode) &&
        inode->i_blocks - ea_blocks == 0);
}

static void ext2_truncate_blocks(struct inode *inode, loff_t offset);

        ext2_write_failed当文件系统写入失败时,就删除对应的页缓存和块。

static void ext2_write_failed(struct address_space *mapping, loff_t to)
{
    struct inode *inode = mapping->host;

    if (to > inode->i_size) {
        truncate_pagecache(inode, inode->i_size);
        ext2_truncate_blocks(inode, inode->i_size);
    }
}

        如果i_nlink为零,则在最后一个iput()时调用该函数。
void ext2_evict_inode(struct inode * inode)
{
    struct ext2_block_alloc_info *rsv;
    int want_delete = 0;

        如果i_nlink为零,且inode不是坏的就设 want_delete为1,并初始化inode,否则删除inode。

    if (!inode->i_nlink && !is_bad_inode(inode)) {
        want_delete = 1;
        dquot_initialize(inode);
    } else {
        dquot_drop(inode);
    }

         truncate_inode_pages_final是内存管理系统的函数,把inode对应的页删除掉。

    truncate_inode_pages_final(&inode->i_data);

        只要want_delete为1,从inode的超级块的开始处修改,设置时间戳,标记inode为脏,然后ext2_write_inode将inode写入硬盘上,最后删除inode的所有信息。

    if (want_delete) {
        sb_start_intwrite(inode->i_sb);
        /* set dtime */
        EXT2_I(inode)->i_dtime    = ktime_get_real_seconds();
        mark_inode_dirty(inode);
        __ext2_write_inode(inode, inode_needs_sync(inode));
        /* truncate to 0 */
        inode->i_size = 0;
        if (inode->i_blocks)
            ext2_truncate_blocks(inode, 0);
        ext2_xattr_delete_inode(inode);
    }

        初始化inode的缓冲区并清理inode。

    invalidate_inode_buffers(inode);
    clear_inode(inode);

        丢弃inode的预留窗口,更新ext2文件系统块分配信息和inode的块分配信息。

    ext2_discard_reservation(inode);
    rsv = EXT2_I(inode)->i_block_alloc_info;
    EXT2_I(inode)->i_block_alloc_info = NULL;
    if (unlikely(rsv))
        kfree(rsv);

        如果want_delete为1,将inode置为空闲状态,结束i_sb的写入。

    if (want_delete) {
        ext2_free_inode(inode);
        sb_end_intwrite(inode->i_sb);
    }
}

        间接指向的结构体,用于ext2的间接块的实现。*p指针,对应映射表中对应下标处的指针; key对应就是物理块号,如果为0就说明还没有映射下一个块;*bh当前间接块的内存缓冲,如果是直接映射,这个指针是NULL。

typedef struct {
    __le32    *p;
    __le32    key;
    struct buffer_head *bh;
} Indirect;

        增加链接,把间接指针指向这个内存缓冲区,v就是15个指针之一的指针的值

static inline void add_chain(Indirect *p, struct buffer_head *bh, __le32 *v)
{
    p->key = *(p->p = v);
    p->bh = bh;
}

        给定两个indirect结构体,检查这两个结构体是不是构成一条有效的映射链。每从设备上读入一个块的时候都要进行检查,因为读入的时候块的内容可能改变,所以要检查映射表里的内容是不是更改了。

static inline int verify_chain(Indirect *from, Indirect *to)
{
        检查数据是否一致,只要有一个不一致,from的值就不会大于to。
    while (from <= to && from->key == *from->p)
        from++;
    return (from > to);
}

         ext2_block_to_path -将块数解析为偏移量数组。为了存储文件数据的位置,ext2使用UNIX文件系统中常见的数据结构——指针树锚定在inode中,数据块位于叶节点,间接块位于中间节点。该函数将块号转换为该树中的路径——返回值是路径长度,inode是我们要查的block所在的文件,i_block是文件内的逻辑块号,offset [n]是指针指向(n+1)第n个节点的偏移量。如果block超出范围(负的或太大的),则输出警告并返回0。

static int ext2_block_to_path(struct inode *inode,
            long i_block, int offsets[4], int *boundary)
{

        ptrs每一个块可以存放的指针的数目,ptrs_bits是其二进制数。direct_blocks是直接块的数目,indirect_blocks是一级间接块指针可以指向的块的数目, double_blocks 是二级间接块指针可以指向的块的数目。
    int ptrs = EXT2_ADDR_PER_BLOCK(inode->i_sb);
    int ptrs_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);
    const long direct_blocks = EXT2_NDIR_BLOCKS,
        indirect_blocks = ptrs,
        double_blocks = (1 << (ptrs_bits * 2));
    int n = 0;
    int final = 0;

        如果块号小于0,报错;如果块号小于直接指针的数目,则这个数据块在直接指针指向的块中,offset[0]就是对应的偏移,n++,12个直接指针的第i_block个就指向我们得到的这个数据块,final指向对应的最后一个块号;如果块号大于12但是小于一级间接块可以指向的块的数目,offset[0]就是一级间接指针,n++,offset[1]的第i_block个就指向我们得到的这个数据块;如果i_block大于一级间接块指向的块数目,小于二级间接块指向的块数目,说明在二级间接指针指向的块中,offset[3]的第i_block个就指向我们得到的这个数据块,以此类推,直到所有条件都不满足,则显示错误,即块号过大。

    if (i_block < 0) {
        ext2_msg(inode->i_sb, KERN_WARNING,
            "warning: %s: block < 0", __func__);
    } else if (i_block < direct_blocks) {
        offsets[n++] = i_block;
        final = direct_blocks;
    } else if ( (i_block -= direct_blocks) < indirect_blocks) {
        offsets[n++] = EXT2_IND_BLOCK;
        offsets[n++] = i_block;
        final = ptrs;
    } else if ((i_block -= indirect_blocks) < double_blocks) {
        offsets[n++] = EXT2_DIND_BLOCK;
        offsets[n++] = i_block >> ptrs_bits;
        offsets[n++] = i_block & (ptrs - 1);
        final = ptrs;
    } else if (((i_block -= double_blocks) >> (ptrs_bits * 2)) < ptrs) {
        offsets[n++] = EXT2_TIND_BLOCK;
        offsets[n++] = i_block >> (ptrs_bits * 2);
        offsets[n++] = (i_block >> ptrs_bits) & (ptrs - 1);
        offsets[n++] = i_block & (ptrs - 1);
        final = ptrs;
    } else {
        ext2_msg(inode->i_sb, KERN_WARNING,
            "warning: %s: block is too big", __func__);
    }

        如果要寻找的块是在块内的最后一个指针指向的,*boundary的值就是1,即被引用的块很可能(在磁盘上)后面跟着一个间接块。
    if (boundary)
        *boundary = final - 1 - (i_block & (ptrs - 1));

    return n;
}

        ext2_get_branch -读取指向数据的间接块链。根据传入的路径偏移值获得对应的chain结构体指向对应块的数据,offset是传入的参数,offset内是每一级的在块内的偏移排名,depth是路径的层数。ext2_get_branch函数和ext2_block_to_path函数完成了从文件内的块号到物理块号的映射。
static Indirect *ext2_get_branch(struct inode *inode,
                 int depth,
                 int *offsets,
                 Indirect chain[4],
                 int *err)
{
    struct super_block *sb = inode->i_sb;
    Indirect *p = chain;
    struct buffer_head *bh;

    *err = 0;
    /* i_data还在,不需要锁 */

        使得间接块结构体指向路径的第一级,从i_data获得15个指针的值,加上offset就得到了偏移。
    add_chain (chain, NULL, EXT2_I(inode)->i_data + *offsets);

        如果key=0,说明逻辑块的指针还没有和数据块产生映射关系。返回p。
    if (!p->key)
        goto no_block;

        否则一层一层遍历,bh是上一层找到的间接块的数据。然后设置读锁,检验建立的这个间接结构体有没有问题,有则说明在我们建立chain的时候,这个chain被修改了,返回p;否则建立下一层的chain结构体,如果下一级没有块了就goto no_block。
    while (--depth) {
        bh = sb_bread(sb, le32_to_cpu(p->key));
        if (!bh)
            goto failure;
        read_lock(&EXT2_I(inode)->i_meta_lock);
        if (!verify_chain(chain, p))
            goto changed;
        add_chain(++p, bh, (__le32*)bh->b_data + *++offsets);
        read_unlock(&EXT2_I(inode)->i_meta_lock);
        if (!p->key)
            goto no_block;
    }
    return NULL;

changed:
    read_unlock(&EXT2_I(inode)->i_meta_lock);
    brelse(bh);
    *err = -EAGAIN;
    goto no_block;
failure:
    *err = -EIO;
no_block:
    return p;
}

        ext2_find_near—找到一个足够大的位置进行分配,分配的规则是,首先寻找这个块所在的指针块,分配其左边附近的块,如果没找到,就去参数ind所在的间接块内找,如果还没有找到,就去inode所在的磁道块组内找一个。在后一种情况下,我们通过调用者的PID为起始块着色,以防止它与同一块组中不同inode的并发分配发生冲突。

static ext2_fsblk_t ext2_find_near(struct inode *inode, Indirect *ind)
{

        首先start指向当前所在的映射块的起点。
    struct ext2_inode_info *ei = EXT2_I(inode);
    __le32 *start = ind->bh ? (__le32 *) ind->bh->b_data : ei->i_data;
    __le32 *p;
    ext2_fsblk_t bg_start;
    ext2_fsblk_t colour;

    /* 尝试找到上一个块 */
    for (p = ind->p - 1; p >= start; p--)
        if (*p)
            return le32_to_cpu(*p);

    /* 没有就找一个间接块 */
    if (ind->bh)
        return ind->bh->b_blocknr;

    /*在一个磁道分区的块组中找一个块,bg_start是块组中的第一个块*/
    bg_start = ext2_group_first_block_no(inode->i_sb, ei->i_block_group);

        /*colour是组内的偏移,用来防止不同用户的操作发生冲突*/
    colour = (current->pid % 16) *
            (EXT2_BLOCKS_PER_GROUP(inode->i_sb) / 16);
    return bg_start + colour;
}

        ext2_find_goal -找到一个优先分配的位置。inode是块的拥有者,block是文件的逻辑块号,partial指向chain中最后一个三元组的指针,goal是返回的目标块号。成功返回0.

static inline ext2_fsblk_t ext2_find_goal(struct inode *inode, long block,
                      Indirect *partial)
{
    struct ext2_block_alloc_info *block_i;

    block_i = EXT2_I(inode)->i_block_alloc_info;

    /*尝试启发式的顺序分配,如果做不到,至少尝试一下适当的本地化*/
    if (block_i && (block == block_i->last_alloc_logical_block + 1)
        && (block_i->last_alloc_physical_block != 0)) {
        return block_i->last_alloc_physical_block + 1;
    }

    return ext2_find_near(inode, partial);
}

        ext2_blks_to_allocate:查找块映射,并计算需要为给定分配的直接块的数量。k:间接块所需的块数,blks:要映射的数据块数量,blocks_to_boundary:间接块的偏移量。
static int
ext2_blks_to_allocate(Indirect * branch, int k, unsigned long blks,
        int blocks_to_boundary)
{
    unsigned long count = 0;

    /*简单的情况,[t,d]间接块(s)还没有分配,那么显然它在该路径上都是还没有分配的块*/
    if (k > 0) {
        /* 现在不越界分配*/
        if (blks < blocks_to_boundary + 1)
            count += blks;
        else
            count += blocks_to_boundary + 1;
        return count;
    }

    count++;
    while (count < blks && count <= blocks_to_boundary
        && le32_to_cpu(*(branch[0].p + count)) == 0) {
        count++;
    }
    return count;
}

        

        ext2_alloc_blocks:一个映射链需要多个分配块,indirect_blks:需要为非直接块分配的块数量,blks:需要为直接块分配的块数量,new_blocks:在返回时存储的间接块(如果需要)和第一个直接块的新块号。

static int ext2_alloc_blocks(struct inode *inode,
            ext2_fsblk_t goal, int indirect_blks, int blks,
            ext2_fsblk_t new_blocks[4], int *err)
{
    int target, i;
    unsigned long count = 0;
    int index = 0;
    ext2_fsblk_t current_block = 0;
    int ret = 0;

    /*这里我们在最大的努力的基础上尝试一次分配请求多个块,为了构建一个分支,如果还没有分配,我们应该为间接块分配必须满足的块数。 */
    target = blks + indirect_blks;

    while (1) {
        count = target;
        /*为间接块和直接块分配块 */
        current_block = ext2_new_blocks(inode,goal,&count,err);
        if (*err)
            goto failed_out;

        target -= count;
        /*为间接块分配块*/
        while (index < indirect_blks && count) {
            new_blocks[index++] = current_block++;
            count--;
        }

        if (count > 0)
            break;
    }

    /*为第一个直接块保存新块号*/
    new_blocks[index] = current_block;

    /*分配给直接块的块总数 */
    ret = count;
    *err = 0;
    return ret;
failed_out:
    for (i = 0; i         ext2_free_blocks(inode, new_blocks[i], 1);
    if (index)
        mark_inode_dirty(inode);
    return ret;
}

        ext2_alloc_branch—分配并建立一个区块链。inode是文件的主人,num是要建立的chain的层数,表示还有几层需要建立,offset是要存放的偏移地址的后一个,branch是要存放的chain的地址。这个函数分配num个块,除了最后一个都会初始化为0,将它们链接到chain中,然后写入磁盘。换句话说,它准备了一个可以拼接到inode上的branch。如果分配失败,我们释放所有已经分配的块,并返回失败的ext2_alloc_block()(通常是-ENOSPC)的错误值。

static int ext2_alloc_branch(struct inode *inode,
            int indirect_blks, int *blks, ext2_fsblk_t goal,
            int *offsets, Indirect *branch)
{
    int blocksize = inode->i_sb->s_blocksize;
    int i, n = 0;
    int err = 0;
    struct buffer_head *bh;
    int num;
    ext2_fsblk_t new_blocks[4];
    ext2_fsblk_t current_block;

        /*在goal附近分配一个块*/

    num = ext2_alloc_blocks(inode, goal, indirect_blks,
                *blks, new_blocks, &err);
    if (err)
        return err;

        /*建立开始的第一个的索引key,块号*/

    branch[0].key = cpu_to_le32(new_blocks[0]);
    /*分配元数据块和数据块*/
    for (n = 1; n <= indirect_blks;  n++) {
        /* 为父块获取buffer_head,将其归零并将指针设置为新的,然后将父块发送到磁盘。*/
        bh = sb_getblk(inode->i_sb, new_blocks[n-1]);
        if (unlikely(!bh)) {
            err = -ENOMEM;
            goto failed;
        }

/*当前路径链的间接块缓冲区,memset()初始化缓冲区为0,建立对应的索引key,p执行间接块上的offset对应的偏移地址*/
        branch[n].bh = bh;
        lock_buffer(bh);
        memset(bh->b_data, 0, blocksize);
        branch[n].p = (__le32 *) bh->b_data + offsets[n];
        branch[n].key = cpu_to_le32(new_blocks[n]);
        *branch[n].p = branch[n].key;
        if ( n == indirect_blks) {
            current_block = new_blocks[n];
            /*链的结束,更新链的最后一个新元锁指向新分配的数据块编号*/
            for (i=1; i < num; i++)
                *(branch[n].p + i) = cpu_to_le32(++current_block);
        }

        /*更新缓冲区,标记为脏*/
        set_buffer_uptodate(bh);
        unlock_buffer(bh);
        mark_buffer_dirty_inode(bh, inode);  

        /* 我们在这里使用IS_SYNC(inode)同步bh*/
        if (S_ISDIR(inode->i_mode) && IS_DIRSYNC(inode))
            sync_dirty_buffer(bh);
    }
    *blks = num;
    return err;

failed:

/* 分配失败,就释放我们之前分配的块,bforget函数是内存管理系统的释放buffer的函数,然后释放这间接块所占用的数据块*/
    for (i = 1; i < n; i++)
        bforget(branch[i].bh);
    for (i = 0; i < indirect_blks; i++)
        ext2_free_blocks(inode, new_blocks[i], 1);
    ext2_free_blocks(inode, new_blocks[i], num);
    return err;
}

        ext2_splice_branch—将已分配的branch拼接到inode上。inode是属于的文件inode,block是我们要添加的块的数量,where是缺失的节点位置,num是我们正在添加的间接块的数量,blks是我们正在添加的直接块的数量。这个函数填充了缺少的链接,并在inode中执行所有需要的内处理(->i_blocks,等等)。在成功的情况下,我们以完整的链到新块并返回0结束。
static void ext2_splice_branch(struct inode *inode,
            long block, Indirect *where, int num, int blks)
{
    int i;
    struct ext2_block_alloc_info *block_i;
    ext2_fsblk_t current_block;

    block_i = EXT2_I(inode)->i_block_alloc_info;

     /*断的那一点的p指向key*/

    *where->p = where->key;

    /* 更新本地buffer_head或inode,以指向更多刚刚分配的直接块*/
    if (num == 0 && blks > 1) {
        current_block = le32_to_cpu(where->key) + 1;
        for (i = 1; i < blks; i++)
            *(where->p + i ) = cpu_to_le32(current_block++);
    }

    /*更新i_block_alloc_info中最近分配的逻辑和物理块,以帮助找到合适的目标块进行下一次分配*/
    if (block_i) {
        block_i->last_alloc_logical_block = block + blks - 1;
        block_i->last_alloc_physical_block =
                le32_to_cpu(where[num].key) + blks - 1;
    }

    /* 上述是原子事务*/

    /*检查是否把它链接到间接块上*/
    if (where->bh)
        mark_buffer_dirty_inode(where->bh, inode);

    inode->i_ctime = current_time(inode);
    mark_inode_dirty(inode);
}

        根据文件的逻辑块号得到文件的物理块号,iblock是块在文件内的逻辑块号,参数create标识是否需要创建一个新的块。分配策略很简单,遍历整个树的叶节点,在将其附加到树之前设置新生块之间的链接,如果需要同步就写入它们,重新检查路径,如果检查失败就释放并重来一遍,否则,设置最后一个缺失的链接。
static int ext2_get_blocks(struct inode *inode,
               sector_t iblock, unsigned long maxblocks,
               u32 *bno, bool *new, bool *boundary,
               int create)
{
    int err;
    int offsets[4];
    Indirect chain[4];
    Indirect *partial;
    ext2_fsblk_t goal;
    int indirect_blks;
    int blocks_to_boundary = 0;
    int depth;
    struct ext2_inode_info *ei = EXT2_I(inode);
    int count = 0;
    ext2_fsblk_t first_block = 0;

    BUG_ON(maxblocks == 0);

/*根据文件的inode和逻辑块号,得到这个文件的存储地址,返回路径长度,offset是偏移数组*/

    depth = ext2_block_to_path(inode,iblock,offsets,&blocks_to_boundary);

    if (depth == 0)
        return -EIO;

/*根据传入的offset得到对应的chain数组*/

    partial = ext2_get_branch(inode, depth, offsets, chain, &err);
    /*最简单的情况-找到了块,不需要分配*/
    if (!partial) {
        first_block = le32_to_cpu(chain[depth - 1].key);
        count++;
        /*建立映射关系,映射更多的块*/
        while (count < maxblocks && count <= blocks_to_boundary) {
            ext2_fsblk_t blk;

            if (!verify_chain(chain, chain + depth - 1)) {
                /*当我们读取间接块时,可能会被截断删除。这种情况的处理方法:忘记我们现在看到的,重新读取 */
                err = -EAGAIN;
                count = 0;
                partial = chain + depth - 1;
                break;
            }
            blk = le32_to_cpu(*(chain[depth-1].p + count));
            if (blk == first_block + count)
                count++;
            else
                break;
        }
        if (err != -EAGAIN)
            goto got_it;
    }

    /*第二个简单情况-间接块的普通查找或失败读取。如果参数要求不创建块或者是读取失败的话,就直接清理 */
    if (!create || err == -EIO)
        goto cleanup;

    mutex_lock(&ei->truncate_mutex);
    /*若读取时发现如果间接块缺失,或者我们获取信号量后chain的链接改变了,重新获取chain。*/
    if (err == -EAGAIN || !verify_chain(chain, partial)) {
        while (partial > chain) {
            brelse(partial->bh);
            partial--;
        }
        partial = ext2_get_branch(inode, depth, offsets, chain, &err);
        if (!partial) {
            count++;
            mutex_unlock(&ei->truncate_mutex);
            goto got_it;
        }

        if (err) {
            mutex_unlock(&ei->truncate_mutex);
            goto cleanup;
        }
    }

    /*当我们需要做块分配,在这里惰性地初始化块分配信息*/
    if (S_ISREG(inode->i_mode) && (!ei->i_block_alloc_info))
        ext2_init_block_alloc_info(inode);

    goal = ext2_find_goal(inode, iblock, partial);

    /*需要分配给[d,t]间接块的块数量*/
    indirect_blks = (chain + depth) - partial - 1;
    /*接下来查找间接映射,计算分配给这个分支的直接块的总数 */
    count = ext2_blks_to_allocate(partial, indirect_blks,
                    maxblocks, blocks_to_boundary);
    /*当我们改变树时,阻止ext2_truncate*/
    err = ext2_alloc_branch(inode, indirect_blks, &count, goal,
                offsets + (partial - chain), partial);

    if (err) {
        mutex_unlock(&ei->truncate_mutex);
        goto cleanup;
    }

    if (IS_DAX(inode)) {
        /*我们必须在归零之前解除块映射,这样写回时就不能用块设备页缓存中的陈旧数据覆盖零*/
        clean_bdev_aliases(inode->i_sb->s_bdev,
                   le32_to_cpu(chain[depth-1].key),
                   count);
        /*block必须在我们把它放入树之前被初始化,这样在它被初始化之前就不会被其他线程发现*/
        err = sb_issue_zeroout(inode->i_sb,
                le32_to_cpu(chain[depth-1].key), count,
                GFP_NOFS);
        if (err) {
            mutex_unlock(&ei->truncate_mutex);
            goto cleanup;
        }
    }
    *new = true;

        /*把刚刚分配的树枝粘结到树上*/

    ext2_splice_branch(inode, iblock, partial, indirect_blks, count);
    mutex_unlock(&ei->truncate_mutex);
got_it:
    if (count > blocks_to_boundary)
        *boundary = true;
    err = count;
    /* Clean up and exit */
    partial = chain + depth - 1;    /* the whole chain */
cleanup:
    while (partial > chain) {
        brelse(partial->bh);
        partial--;
    }
    if (err > 0)
        *bno = le32_to_cpu(chain[depth-1].key);
    return err;
}

int ext2_get_block(struct inode *inode, sector_t iblock,
        struct buffer_head *bh_result, int create)
{
    unsigned max_blocks = bh_result->b_size >> inode->i_blkbits;
    bool new = false, boundary = false;
    u32 bno;
    int ret;

    ret = ext2_get_blocks(inode, iblock, max_blocks, &bno, &new, &boundary,
            create);
    if (ret <= 0)
        return ret;

        /*建立bh_result缓冲区和inode所在文件系统和对应的数据块的映射关系*/

    map_bh(bh_result, inode->i_sb, bno);
    bh_result->b_size = (ret << inode->i_blkbits);

/*如果有边界或分配了新块,设置缓冲区*/
    if (new)
        set_buffer_new(bh_result);
    if (boundary)
        set_buffer_boundary(bh_result);
    return 0;

}

#ifdef CONFIG_FS_DAX

        io设备的读写操作。
static int ext2_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
        unsigned flags, struct iomap *iomap, struct iomap *srcmap)
{
    unsigned int blkbits = inode->i_blkbits;
    unsigned long first_block = offset >> blkbits;
    unsigned long max_blocks = (length + (1 << blkbits) - 1) >> blkbits;
    struct ext2_sb_info *sbi = EXT2_SB(inode->i_sb);
    bool new = false, boundary = false;
    u32 bno;
    int ret;

    ret = ext2_get_blocks(inode, first_block, max_blocks,
            &bno, &new, &boundary, flags & IOMAP_WRITE);
    if (ret < 0)
        return ret;

    iomap->flags = 0;
    iomap->bdev = inode->i_sb->s_bdev;
    iomap->offset = (u64)first_block << blkbits;
    iomap->dax_dev = sbi->s_daxdev;

    if (ret == 0) {
        iomap->type = IOMAP_HOLE;
        iomap->addr = IOMAP_NULL_ADDR;
        iomap->length = 1 << blkbits;
    } else {
        iomap->type = IOMAP_MAPPED;
        iomap->addr = (u64)bno << blkbits;
        iomap->length = (u64)ret << blkbits;
        iomap->flags |= IOMAP_F_MERGED;
    }

    if (new)
        iomap->flags |= IOMAP_F_NEW;
    return 0;
}

static int
ext2_iomap_end(struct inode *inode, loff_t offset, loff_t length,
        ssize_t written, unsigned flags, struct iomap *iomap)
{
    if (iomap->type == IOMAP_MAPPED &&
        written < length &&
        (flags & IOMAP_WRITE))
        ext2_write_failed(inode->i_mapping, offset + length);
    return 0;
}

const struct iomap_ops ext2_iomap_ops = {
    .iomap_begin        = ext2_iomap_begin,
    .iomap_end        = ext2_iomap_end,
};
#else
/*为!CONFIG_FS_DAX情况定义空操作*/
const struct iomap_ops ext2_iomap_ops;
#endif /* CONFIG_FS_DAX */

int ext2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
        u64 start, u64 len)
{
    return generic_block_fiemap(inode, fieinfo, start, len,
                    ext2_get_block);
}

/*地址空间的操作函数,写入一个page*/

static int ext2_writepage(struct page *page, struct writeback_control *wbc)
{
    return block_write_full_page(page, ext2_get_block, wbc);
}

/*读取一个页到内存*/

static int ext2_readpage(struct file *file, struct page *page)
{
    return mpage_readpage(page, ext2_get_block);
}

static void ext2_readahead(struct readahead_control *rac)
{
    mpage_readahead(rac, ext2_get_block);
}

        函数 ext2_write_begin 是文档系统写数据的重点实现,它实现了分配页缓存、分配磁盘空间和建立页缓存与磁盘块的关系。

static int
ext2_write_begin(struct file *file, struct address_space *mapping,
        loff_t pos, unsigned len, unsigned flags,
        struct page **pagep, void **fsdata)
{
    int ret;

    ret = block_write_begin(mapping, pos, len, flags, pagep,
                ext2_get_block);
    if (ret < 0)
        ext2_write_failed(mapping, pos + len);
    return ret;
}

static int ext2_write_end(struct file *file, struct address_space *mapping,
            loff_t pos, unsigned len, unsigned copied,
            struct page *page, void *fsdata)
{
    int ret;

    ret = generic_write_end(file, mapping, pos, len, copied, page, fsdata);
    if (ret < len)
        ext2_write_failed(mapping, pos + len);
    return ret;
}

static int
ext2_nobh_write_begin(struct file *file, struct address_space *mapping,
        loff_t pos, unsigned len, unsigned flags,
        struct page **pagep, void **fsdata)
{
    int ret;

    ret = nobh_write_begin(mapping, pos, len, flags, pagep, fsdata,
                   ext2_get_block);
    if (ret < 0)
        ext2_write_failed(mapping, pos + len);
    return ret;
}

/*无缓冲区模式的写入页*/

static int ext2_nobh_writepage(struct page *page,
            struct writeback_control *wbc)
{
    return nobh_writepage(page, ext2_get_block, wbc);
}

/*ext2的块的映射*/

static sector_t ext2_bmap(struct address_space *mapping, sector_t block)
{
    return generic_block_bmap(mapping,block,ext2_get_block);
}

/*块设备直接IO操作*/

static ssize_t
ext2_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
{
    struct file *file = iocb->ki_filp;
    struct address_space *mapping = file->f_mapping;
    struct inode *inode = mapping->host;
    size_t count = iov_iter_count(iter);
    loff_t offset = iocb->ki_pos;
    ssize_t ret;

    ret = blockdev_direct_IO(iocb, inode, iter, ext2_get_block);
    if (ret < 0 && iov_iter_rw(iter) == WRITE)
        ext2_write_failed(mapping, offset + count);
    return ret;
}

static int
ext2_writepages(struct address_space *mapping, struct writeback_control *wbc)
{
    return mpage_writepages(mapping, wbc, ext2_get_block);
}

static int
ext2_dax_writepages(struct address_space *mapping, struct writeback_control *wbc)
{
    struct ext2_sb_info *sbi = EXT2_SB(mapping->host->i_sb);

    return dax_writeback_mapping_range(mapping, sbi->s_daxdev, wbc);
}

/*地址空间操作函数*/

const struct address_space_operations ext2_aops = {
    .set_page_dirty        = __set_page_dirty_buffers,
    .readpage        = ext2_readpage,
    .readahead        = ext2_readahead,
    .writepage        = ext2_writepage,
    .write_begin        = ext2_write_begin,
    .write_end        = ext2_write_end,
    .bmap            = ext2_bmap,
    .direct_IO        = ext2_direct_IO,
    .writepages        = ext2_writepages,
    .migratepage        = buffer_migrate_page,
    .is_partially_uptodate    = block_is_partially_uptodate,
    .error_remove_page    = generic_error_remove_page,
};

/*没有缓冲区的地址空间集合*/

const struct address_space_operations ext2_nobh_aops = {
    .set_page_dirty        = __set_page_dirty_buffers,
    .readpage        = ext2_readpage,
    .readahead        = ext2_readahead,
    .writepage        = ext2_nobh_writepage,
    .write_begin        = ext2_nobh_write_begin,
    .write_end        = nobh_write_end,
    .bmap            = ext2_bmap,
    .direct_IO        = ext2_direct_IO,
    .writepages        = ext2_writepages,
    .migratepage        = buffer_migrate_page,
    .error_remove_page    = generic_error_remove_page,
};

static const struct address_space_operations ext2_dax_aops = {
    .writepages        = ext2_dax_writepages,
    .direct_IO        = noop_direct_IO,
    .set_page_dirty        = __set_page_dirty_no_writeback,
    .invalidatepage        = noop_invalidatepage,
};

/*它应该是一个库函数,搜索第一个非零单词或使用zero_page的memcmp*/
static inline int all_zeroes(__le32 *p, __le32 *q)
{
    while (p < q)
        if (*p++)
            return 0;
    return 1;
}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部