📄 gc.c
字号:
retlen, rawlen, ref_offset(raw));
continue;
}
if (je16_to_cpu(rd->nodetype) != JFFS2_NODETYPE_DIRENT)
continue;
/* If the name CRC doesn't match, skip */
if (je32_to_cpu(rd->name_crc) != name_crc)
continue;
/* If the name length doesn't match, or it's another deletion dirent, skip */
if (rd->nsize != name_len || !je32_to_cpu(rd->ino))
continue;
/* OK, check the actual name now */
if (memcmp(rd->name, fd->name, name_len))
continue;
/* OK. The name really does match. There really is still an older node on
the flash which our deletion dirent obsoletes. So we have to write out
a new deletion dirent to replace it */
up(&c->erase_free_sem);
D1(printk(KERN_DEBUG "Deletion dirent at %08x still obsoletes real dirent \"%s\" at %08x for ino #%u\n",
ref_offset(fd->raw), fd->name, ref_offset(raw), je32_to_cpu(rd->ino)));
kfree(rd);
return jffs2_garbage_collect_dirent(c, jeb, f, fd);
}
up(&c->erase_free_sem);
kfree(rd);
}
/* No need for it any more. Just mark it obsolete and remove it from the list */
while (*fdp) {
if ((*fdp) == fd) {
found = 1;
*fdp = fd->next;
break;
}
fdp = &(*fdp)->next;
}
if (!found) {
printk(KERN_WARNING "Deletion dirent \"%s\" not found in list for ino #%u\n", fd->name, f->inocache->ino);
}
jffs2_mark_node_obsolete(c, fd->raw);
jffs2_free_full_dirent(fd);
return 0;
}
static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
uint32_t start, uint32_t end)
{
struct jffs2_raw_inode ri;
struct jffs2_node_frag *frag;
struct jffs2_full_dnode *new_fn;
uint32_t alloclen, phys_ofs;
int ret;
D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n",
f->inocache->ino, start, end));
memset(&ri, 0, sizeof(ri));
if(fn->frags > 1) {
size_t readlen;
uint32_t crc;
/* It's partially obsoleted by a later write. So we have to
write it out again with the _same_ version as before */
ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(ri), &readlen, (char *)&ri);
if (readlen != sizeof(ri) || ret) {
printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %zd. Data will be lost by writing new hole node\n", ret, readlen);
goto fill;
}
if (je16_to_cpu(ri.nodetype) != JFFS2_NODETYPE_INODE) {
printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had node type 0x%04x instead of JFFS2_NODETYPE_INODE(0x%04x)\n",
ref_offset(fn->raw),
je16_to_cpu(ri.nodetype), JFFS2_NODETYPE_INODE);
return -EIO;
}
if (je32_to_cpu(ri.totlen) != sizeof(ri)) {
printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%zx\n",
ref_offset(fn->raw),
je32_to_cpu(ri.totlen), sizeof(ri));
return -EIO;
}
crc = crc32(0, &ri, sizeof(ri)-8);
if (crc != je32_to_cpu(ri.node_crc)) {
printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had CRC 0x%08x which doesn't match calculated CRC 0x%08x\n",
ref_offset(fn->raw),
je32_to_cpu(ri.node_crc), crc);
/* FIXME: We could possibly deal with this by writing new holes for each frag */
printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n",
start, end, f->inocache->ino);
goto fill;
}
if (ri.compr != JFFS2_COMPR_ZERO) {
printk(KERN_WARNING "jffs2_garbage_collect_hole: Node 0x%08x wasn't a hole node!\n", ref_offset(fn->raw));
printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n",
start, end, f->inocache->ino);
goto fill;
}
} else {
fill:
ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
ri.totlen = cpu_to_je32(sizeof(ri));
ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
ri.ino = cpu_to_je32(f->inocache->ino);
ri.version = cpu_to_je32(++f->highest_version);
ri.offset = cpu_to_je32(start);
ri.dsize = cpu_to_je32(end - start);
ri.csize = cpu_to_je32(0);
ri.compr = JFFS2_COMPR_ZERO;
}
ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
ri.data_crc = cpu_to_je32(0);
ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
ret = jffs2_reserve_space_gc(c, sizeof(ri), &phys_ofs, &alloclen);
if (ret) {
printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_hole failed: %d\n",
sizeof(ri), ret);
return ret;
}
new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_GC);
if (IS_ERR(new_fn)) {
printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn));
return PTR_ERR(new_fn);
}
if (je32_to_cpu(ri.version) == f->highest_version) {
jffs2_add_full_dnode_to_inode(c, f, new_fn);
if (f->metadata) {
jffs2_mark_node_obsolete(c, f->metadata->raw);
jffs2_free_full_dnode(f->metadata);
f->metadata = NULL;
}
return 0;
}
/*
* We should only get here in the case where the node we are
* replacing had more than one frag, so we kept the same version
* number as before. (Except in case of error -- see 'goto fill;'
* above.)
*/
D1(if(unlikely(fn->frags <= 1)) {
printk(KERN_WARNING "jffs2_garbage_collect_hole: Replacing fn with %d frag(s) but new ver %d != highest_version %d of ino #%d\n",
fn->frags, je32_to_cpu(ri.version), f->highest_version,
je32_to_cpu(ri.ino));
});
/* This is a partially-overlapped hole node. Mark it REF_NORMAL not REF_PRISTINE */
mark_ref_normal(new_fn->raw);
for (frag = jffs2_lookup_node_frag(&f->fragtree, fn->ofs);
frag; frag = frag_next(frag)) {
if (frag->ofs > fn->size + fn->ofs)
break;
if (frag->node == fn) {
frag->node = new_fn;
new_fn->frags++;
fn->frags--;
}
}
if (fn->frags) {
printk(KERN_WARNING "jffs2_garbage_collect_hole: Old node still has frags!\n");
BUG();
}
if (!new_fn->frags) {
printk(KERN_WARNING "jffs2_garbage_collect_hole: New node has no frags!\n");
BUG();
}
jffs2_mark_node_obsolete(c, fn->raw);
jffs2_free_full_dnode(fn);
return 0;
}
static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
uint32_t start, uint32_t end)
{
struct jffs2_full_dnode *new_fn;
struct jffs2_raw_inode ri;
uint32_t alloclen, phys_ofs, offset, orig_end, orig_start;
int ret = 0;
unsigned char *comprbuf = NULL, *writebuf;
unsigned long pg;
unsigned char *pg_ptr;
memset(&ri, 0, sizeof(ri));
D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%u from offset 0x%x to 0x%x\n",
f->inocache->ino, start, end));
orig_end = end;
orig_start = start;
if (c->nr_free_blocks + c->nr_erasing_blocks > c->resv_blocks_gcmerge) {
/* Attempt to do some merging. But only expand to cover logically
adjacent frags if the block containing them is already considered
to be dirty. Otherwise we end up with GC just going round in
circles dirtying the nodes it already wrote out, especially
on NAND where we have small eraseblocks and hence a much higher
chance of nodes having to be split to cross boundaries. */
struct jffs2_node_frag *frag;
uint32_t min, max;
min = start & ~(PAGE_CACHE_SIZE-1);
max = min + PAGE_CACHE_SIZE;
frag = jffs2_lookup_node_frag(&f->fragtree, start);
/* BUG_ON(!frag) but that'll happen anyway... */
BUG_ON(frag->ofs != start);
/* First grow down... */
while((frag = frag_prev(frag)) && frag->ofs >= min) {
/* If the previous frag doesn't even reach the beginning, there's
excessive fragmentation. Just merge. */
if (frag->ofs > min) {
D1(printk(KERN_DEBUG "Expanding down to cover partial frag (0x%x-0x%x)\n",
frag->ofs, frag->ofs+frag->size));
start = frag->ofs;
continue;
}
/* OK. This frag holds the first byte of the page. */
if (!frag->node || !frag->node->raw) {
D1(printk(KERN_DEBUG "First frag in page is hole (0x%x-0x%x). Not expanding down.\n",
frag->ofs, frag->ofs+frag->size));
break;
} else {
/* OK, it's a frag which extends to the beginning of the page. Does it live
in a block which is still considered clean? If so, don't obsolete it.
If not, cover it anyway. */
struct jffs2_raw_node_ref *raw = frag->node->raw;
struct jffs2_eraseblock *jeb;
jeb = &c->blocks[raw->flash_offset / c->sector_size];
if (jeb == c->gcblock) {
D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in gcblock at %08x\n",
frag->ofs, frag->ofs+frag->size, ref_offset(raw)));
start = frag->ofs;
break;
}
if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) {
D1(printk(KERN_DEBUG "Not expanding down to cover frag (0x%x-0x%x) in clean block %08x\n",
frag->ofs, frag->ofs+frag->size, jeb->offset));
break;
}
D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in dirty block %08x\n",
frag->ofs, frag->ofs+frag->size, jeb->offset));
start = frag->ofs;
break;
}
}
/* ... then up */
/* Find last frag which is actually part of the node we're to GC. */
frag = jffs2_lookup_node_frag(&f->fragtree, end-1);
while((frag = frag_next(frag)) && frag->ofs+frag->size <= max) {
/* If the previous frag doesn't even reach the beginning, there's lots
of fragmentation. Just merge. */
if (frag->ofs+frag->size < max) {
D1(printk(KERN_DEBUG "Expanding up to cover partial frag (0x%x-0x%x)\n",
frag->ofs, frag->ofs+frag->size));
end = frag->ofs + frag->size;
continue;
}
if (!frag->node || !frag->node->raw) {
D1(printk(KERN_DEBUG "Last frag in page is hole (0x%x-0x%x). Not expanding up.\n",
frag->ofs, frag->ofs+frag->size));
break;
} else {
/* OK, it's a frag which extends to the beginning of the page. Does it live
in a block which is still considered clean? If so, don't obsolete it.
If not, cover it anyway. */
struct jffs2_raw_node_ref *raw = frag->node->raw;
struct jffs2_eraseblock *jeb;
jeb = &c->blocks[raw->flash_offset / c->sector_size];
if (jeb == c->gcblock) {
D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in gcblock at %08x\n",
frag->ofs, frag->ofs+frag->size, ref_offset(raw)));
end = frag->ofs + frag->size;
break;
}
if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) {
D1(printk(KERN_DEBUG "Not expanding up to cover frag (0x%x-0x%x) in clean block %08x\n",
frag->ofs, frag->ofs+frag->size, jeb->offset));
break;
}
D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in dirty block %08x\n",
frag->ofs, frag->ofs+frag->size, jeb->offset));
end = frag->ofs + frag->size;
break;
}
}
D1(printk(KERN_DEBUG "Expanded dnode to write from (0x%x-0x%x) to (0x%x-0x%x)\n",
orig_start, orig_end, start, end));
BUG_ON(end > JFFS2_F_I_SIZE(f));
BUG_ON(end < orig_end);
BUG_ON(start > orig_start);
}
/* First, use readpage() to read the appropriate page into the page cache */
/* Q: What happens if we actually try to GC the _same_ page for which commit_write()
* triggered garbage collection in the first place?
* A: I _think_ it's OK. read_cache_page shouldn't deadlock, we'll write out the
* page OK. We'll actually write it out again in commit_write, which is a little
* suboptimal, but at least we're correct.
*/
pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg);
if (IS_ERR(pg_ptr)) {
printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg_ptr));
return PTR_ERR(pg_ptr);
}
offset = start;
while(offset < orig_end) {
uint32_t datalen;
uint32_t cdatalen;
char comprtype = JFFS2_COMPR_NONE;
ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen);
if (ret) {
printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dnode failed: %d\n",
sizeof(ri)+ JFFS2_MIN_DATA_LEN, ret);
break;
}
cdatalen = min_t(uint32_t, alloclen - sizeof(ri), end - offset);
datalen = end - offset;
writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1));
comprtype = jffs2_compress(c, f, writebuf, &comprbuf, &datalen, &cdatalen);
ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
ri.totlen = cpu_to_je32(sizeof(ri) + cdatalen);
ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
ri.ino = cpu_to_je32(f->inocache->ino);
ri.version = cpu_to_je32(++f->highest_version);
ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
ri.offset = cpu_to_je32(offset);
ri.csize = cpu_to_je32(cdatalen);
ri.dsize = cpu_to_je32(datalen);
ri.compr = comprtype;
ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, phys_ofs, ALLOC_GC);
jffs2_free_comprbuf(comprbuf, writebuf);
if (IS_ERR(new_fn)) {
printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
ret = PTR_ERR(new_fn);
break;
}
ret = jffs2_add_full_dnode_to_inode(c, f, new_fn);
offset += datalen;
if (f->metadata) {
jffs2_mark_node_obsolete(c, f->metadata->raw);
jffs2_free_full_dnode(f->metadata);
f->metadata = NULL;
}
}
jffs2_gc_release_page(c, pg_ptr, &pg);
return ret;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -