f2fs_map_blocks函数主要作用是为写入inode的数据分配内存空间(包括node block,data block),四个参数:
1) struct inode * inode: 操作的inode结构
2) struct f2fs_map_blocks * map: 要写入的block相关信息的参数
3) int create: 如果为true, 表示如果内中不存在相应的block, 则创建
4) int flag: map block 场景
f2fs_map_block结构:
struct f2fs_map_blocks {
block_t m_pblk;
block_t m_lblk; //block起始索引值
unsigned int m_len; //最大block个数
unsigned int m_flags;
pgoff_t *m_next_pgofs; /* point next possible non-hole pgofs */
pgoff_t *m_next_extent; /* point to next possible extent */
int m_seg_type;
};
对于create参数为true来说,主要关注m_lblk与m_len两个参数,如果create不为true,会从extent cache里面查找。
static int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map,
int create, int flag)
{
unsigned int maxblocks = map->m_len; //map中包含的block总数,一共要map多少个blocks
struct dnode_of_data dn;
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
int mode = create ? ALLOC_NODE : LOOKUP_NODE_RA;//ALLOC_NODE mode如果不存在,直接创建
pgoff_t pgofs, end_offset;
int err = 0, ofs = 1;
struct extent_info ei;
bool allocated = false;
map->m_len = 0;//后面每map一个block, m_len会加1
map->m_flags = 0;
/* it only supports block size == page size */
pgofs = (pgoff_t)map->m_lblk;//map的第一个block index,最小的block
if (f2fs_lookup_extent_cache(inode, pgofs, &ei)) {
map->m_pblk = ei.blk + pgofs - ei.fofs;
map->m_len = min((pgoff_t)maxblocks, ei.fofs + ei.len - pgofs);
map->m_flags = F2FS_MAP_MAPPED;
goto out;
}
//如果create为true, 表示需要lock住f2fs相关操作
if (create)
f2fs_lock_op(F2FS_I_SB(inode));
/* When reading holes, we need its node page */
set_new_dnode(&dn, inode, NULL, NULL, 0);
/*
* 得到pgofs起始的map block对应的直接node block,它得到的是pgofs(data block index)对
* 应的data block的直
* 属的(上一层的)direct node_block, 返回的参数包含其在上一层的node page对应的偏移
*/
err = get_dnode_of_data(&dn, pgofs, mode);
if (err) {
if (err == -ENOENT)
err = 0;
goto unlock_out;
}
/*
* 若create为true,如果得到的direct node block,在其ofs_in_node偏移处的data block地址为空,则新申请一个data block作为map block
*/
if (dn.data_blkaddr == NEW_ADDR || dn.data_blkaddr == NULL_ADDR) {
if (create) {
if (unlikely(f2fs_cp_error(sbi))) {
err = -EIO;
goto put_out;
}
err = __allocate_data_block(&dn);//新申请一个data block
if (err)
goto put_out;
allocated = true;
map->m_flags = F2FS_MAP_NEW;
} else {
if (flag != F2FS_GET_BLOCK_FIEMAP ||
dn.data_blkaddr != NEW_ADDR) {
if (flag == F2FS_GET_BLOCK_BMAP)
err = -ENOENT;
goto put_out;
}
/*
* preallocated unwritten block should be mapped
* for fiemap.
*/
if (dn.data_blkaddr == NEW_ADDR)
map->m_flags = F2FS_MAP_UNWRITTEN;
}
}
map->m_flags |= F2FS_MAP_MAPPED;
map->m_pblk = dn.data_blkaddr;//刚刚申请的block address
map->m_len = 1;//已Map block length
//node_page能表示的最大地址个数, 如果inode,则end_offset为873,表示最多873个data block地
//址,是inode去掉meta信息剩余的能表示地址的空间,如果是direct node,则为1018,表示最多能指向
//1018个data block
end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode));
dn.ofs_in_node++; //在此node_page 中的下一个block address的offset
pgofs++;//需要map的下一个地址
get_next://下一个block
if (dn.ofs_in_node >= end_offset) {//如果node block的下一个地址已经超过了end_offset,
//node_block中的最大地址,即此node_block的地址已用
//完,则需要重新查找分配node data block
if (allocated)
sync_inode_page(&dn);
allocated = false;
f2fs_put_dnode(&dn);
set_new_dnode(&dn, inode, NULL, NULL, 0);
err = get_dnode_of_data(&dn, pgofs, mode);
if (err) {
if (err == -ENOENT)
err = 0;
goto unlock_out;
}
end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode));
}
if (maxblocks > map->m_len) {//如果已经map了的长度没有达到maxblocks,即还没有map完
block_t blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node);
if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR) {//如果下一个data block为new或者
//null,重新申请一个
if (create) {
if (unlikely(f2fs_cp_error(sbi))) {
err = -EIO;
goto sync_out;
}
err = __allocate_data_block(&dn);
if (err)
goto sync_out;
allocated = true;
map->m_flags |= F2FS_MAP_NEW;
blkaddr = dn.data_blkaddr;
} else {
/*
* we only merge preallocated unwritten blocks
* for fiemap.
*/
if (flag != F2FS_GET_BLOCK_FIEMAP ||
blkaddr != NEW_ADDR)
goto sync_out;
}
}
/* Give more consecutive addresses for the readahead */
if ((map->m_pblk != NEW_ADDR && //如果新分配的block地址连续,则继续
blkaddr == (map->m_pblk + ofs)) ||
(map->m_pblk == NEW_ADDR &&
blkaddr == NEW_ADDR)) {
ofs++;
dn.ofs_in_node++;
pgofs++;
map->m_len++;
goto get_next;
}
}
sync_out:
if (allocated)
sync_inode_page(&dn);
put_out:
f2fs_put_dnode(&dn);
unlock_out:
if (create)
f2fs_unlock_op(F2FS_I_SB(inode));
out:
trace_f2fs_map_blocks(inode, map, err);
return err;
}