在
ramfs系统中write过程浅析中已经大致分析了vfs中写入的过程,jffs2大致跟这个过程差不多,略有差异的是
generic_file_buffered_write函数中的a_ops->prepare_write和a_ops->commit_writ,这两个函数各文件系统有差异。在jffs2文件系统中如此定义
- .readpage = jffs2_readpage,
- .prepare_write =jffs2_prepare_write,
- .commit_write = jffs2_commit_write
.readpage = jffs2_readpage,
.prepare_write =jffs2_prepare_write,
.commit_write = jffs2_commit_write
jffs2_prepart_write这个函数主要是写入前准备。
commit_write这个函数才是真的数据写入。
- static int jffs2_prepare_write (struct file *filp, struct page *pg,
- unsigned start, unsigned end)
- {
- struct inode *inode = pg->mapping->host; //对应的inode
- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); //得到这个inode信息
- uint32_t pageofs = pg->index << PAGE_CACHE_SHIFT; //页偏移量,在第几页*4k
- if (pageofs > inode->i_size) { //超过原来尺寸,有新数据写入
- ret = jffs2_reserve_space(c, sizeof(ri), &alloc_len,
- ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); //分配空间,并生成一个jffs2_raw_node_ref
- //下面开始给inode赋值
- ……
- ri.data_crc = cpu_to_je32(0);
- fn = jffs2_write_dnode(c, f, &ri, NULL, 0, ALLOC_NORMAL); //写进去了,但是data是NULL的
- if (IS_ERR(fn)) {
- ret = PTR_ERR(fn);
- jffs2_complete_reservation(c);
- up(&f->sem);
- return ret;
- }
- ret = jffs2_add_full_dnode_to_inode(c, f, fn); //插入一个frag到f中,
- if (f->metadata) { //将原来数据过期
- jffs2_mark_node_obsolete(c, f->metadata->raw);
- jffs2_free_full_dnode(f->metadata);
- f->metadata = NULL;
- }
- jffs2_complete_reservation(c);
- inode->i_size = pageofs;
- }
- }
static int jffs2_prepare_write (struct file *filp, struct page *pg,
unsigned start, unsigned end)
{
struct inode *inode = pg->mapping->host; //对应的inode
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); //得到这个inode信息
uint32_t pageofs = pg->index << PAGE_CACHE_SHIFT; //页偏移量,在第几页*4k
if (pageofs > inode->i_size) { //超过原来尺寸,有新数据写入
ret = jffs2_reserve_space(c, sizeof(ri), &alloc_len,
ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); //分配空间,并生成一个jffs2_raw_node_ref
//下面开始给inode赋值
……
ri.data_crc = cpu_to_je32(0);
fn = jffs2_write_dnode(c, f, &ri, NULL, 0, ALLOC_NORMAL); //写进去了,但是data是NULL的
if (IS_ERR(fn)) {
ret = PTR_ERR(fn);
jffs2_complete_reservation(c);
up(&f->sem);
return ret;
}
ret = jffs2_add_full_dnode_to_inode(c, f, fn); //插入一个frag到f中,
if (f->metadata) { //将原来数据过期
jffs2_mark_node_obsolete(c, f->metadata->raw);
jffs2_free_full_dnode(f->metadata);
f->metadata = NULL;
}
jffs2_complete_reservation(c);
inode->i_size = pageofs;
}
}
一般情况下,如果写入的偏移(ofs)超过节点的大小(i_size),这个函数才会起作用,分配新的空间,且写入data为null的节点,生成fn并加到f->fragment的红黑树上。fn上有node的物理地址信息,数据的ofs和size。
- static int jffs2_commit_write (struct file *filp, struct page *pg,
- unsigned start, unsigned end)
- {
- ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + aligned_start,
- (pg->index << PAGE_CACHE_SHIFT) + aligned_start,
- end - aligned_start, &writtenlen);
- //入口参数:超级块,inode_info,ri,写入数据的位置,偏移,长度,返回值
- //这个偏移很关键,它跟物理地址没关系,就一个page内的偏移
- //将数据mapping(内存)上的数据写入flash
- }
static int jffs2_commit_write (struct file *filp, struct page *pg,
unsigned start, unsigned end)
{
ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + aligned_start,
(pg->index << PAGE_CACHE_SHIFT) + aligned_start,
end - aligned_start, &writtenlen);
//入口参数:超级块,inode_info,ri,写入数据的位置,偏移,长度,返回值
//这个偏移很关键,它跟物理地址没关系,就一个page内的偏移
//将数据mapping(内存)上的数据写入flash
}
其他校验的代码去掉后,剩下本人觉得最重要的代码jffs2_write_inode_range
- /* The OS-specific code fills in the metadata in the jffs2_raw_inode for us, so that
- we don't have to go digging in struct inode or its equivalent. It should set:
- mode, uid, gid, (starting)isize, atime, ctime, mtime */
- int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
- struct jffs2_raw_inode *ri, unsigned char *buf,
- uint32_t offset, uint32_t writelen, uint32_t *retlen)
- {
- while(writelen) { //按照长度写完
- retry:
- ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN,
- &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
- datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1)));//存在超出一页的情况
- cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen);//跨区
- comprtype = jffs2_compress(c, f, buf, &comprbuf, &datalen, &cdatalen); //压缩数据
- //写进去的数据是压缩过的
- ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
- fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, ALLOC_NORETRY);
- //入口参数:sb_info,inode_info,ri,data,datalen,mode;返回full_dnode
- //fn->size=dsize
- //fn->raw是节点信息,里面有物理地址信息
- //fn->ofs=ri.offset
- jffs2_free_comprbuf(comprbuf, buf); //释放压缩后的数据
- ret = jffs2_add_full_dnode_to_inode(c, f, fn);
- if (f->metadata) {//将原来的数据过期掉
- jffs2_mark_node_obsolete(c, f->metadata->raw);
- jffs2_free_full_dnode(f->metadata);
- f->metadata = NULL;
- }
- jffs2_complete_reservation(c);
- }
- }
/* The OS-specific code fills in the metadata in the jffs2_raw_inode for us, so that
we don't have to go digging in struct inode or its equivalent. It should set:
mode, uid, gid, (starting)isize, atime, ctime, mtime */
int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
struct jffs2_raw_inode *ri, unsigned char *buf,
uint32_t offset, uint32_t writelen, uint32_t *retlen)
{
while(writelen) { //按照长度写完
retry:
ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN,
&alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1)));//存在超出一页的情况
cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen);//跨区
comprtype = jffs2_compress(c, f, buf, &comprbuf, &datalen, &cdatalen); //压缩数据
//写进去的数据是压缩过的
ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, ALLOC_NORETRY);
//入口参数:sb_info,inode_info,ri,data,datalen,mode;返回full_dnode
//fn->size=dsize
//fn->raw是节点信息,里面有物理地址信息
//fn->ofs=ri.offset
jffs2_free_comprbuf(comprbuf, buf); //释放压缩后的数据
ret = jffs2_add_full_dnode_to_inode(c, f, fn);
if (f->metadata) {//将原来的数据过期掉
jffs2_mark_node_obsolete(c, f->metadata->raw);
jffs2_free_full_dnode(f->metadata);
f->metadata = NULL;
}
jffs2_complete_reservation(c);
}
}
这个函数是将需要写入的数据压缩,后写到通过jffs2_write_dnode将数据写到flash中。并将写入的node信息fn,挂到f->fragment树上