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