mtd/fs/jffs2 gc.c,1.110,1.111
David Woodhouse
dwmw2 at infradead.org
Wed Oct 8 20:35:46 EDT 2003
Update of /home/cvs/mtd/fs/jffs2
In directory phoenix.infradead.org:/tmp/cvs-serv32339
Modified Files:
gc.c
Log Message:
Different heuristics for when to expand a data node on GC.
Index: gc.c
===================================================================
RCS file: /home/cvs/mtd/fs/jffs2/gc.c,v
retrieving revision 1.110
retrieving revision 1.111
diff -u -r1.110 -r1.111
--- gc.c 7 Oct 2003 13:12:47 -0000 1.110
+++ gc.c 9 Oct 2003 00:35:43 -0000 1.111
@@ -1053,28 +1053,126 @@
orig_end = end;
- /* If we're looking at the last node in the block we're
- garbage-collecting, we allow ourselves to merge as if the
- block was already erasing. We're likely to be GC'ing a
- partial page, and the next block we GC is likely to have
- the other half of this page right at the beginning, which
- means we'd expand it _then_, as nr_erasing_blocks would have
- increased since we checked, and in doing so would obsolete
- the partial node which we'd have written here. Meaning that
- the GC would churn and churn, and just leave dirty blocks in
- it's wake.
- */
- if(c->nr_free_blocks + c->nr_erasing_blocks > c->resv_blocks_gcmerge - (fn->raw->next_phys?0:1)) {
- /* Shitloads of space */
- /* FIXME: Integrate this properly with GC calculations */
- start &= ~(PAGE_CACHE_SIZE-1);
- end = min_t(uint32_t, start + PAGE_CACHE_SIZE, JFFS2_F_I_SIZE(f));
- D1(printk(KERN_DEBUG "Plenty of free space, so expanding to write from offset 0x%x to 0x%x\n",
- start, end));
- if (end < orig_end) {
- printk(KERN_WARNING "Eep. jffs2_garbage_collect_dnode extended node to write, but it got smaller: start 0x%x, orig_end 0x%x, end 0x%x\n", start, orig_end, end);
- end = orig_end;
+ 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;
+#error this code is untested and may eat your filesystem.
+ 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 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.\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 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 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 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-1) {
+ D1(printk(KERN_DEBUG "Expanding 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.\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 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 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 to cover frag (0x%x-0x%x) in dirty block %08x\n",
+ frag->ofs, frag->ofs+frag->size, jeb->offset));
+ start = frag->ofs;
+ break;
+ }
+ }
+ BUG_ON(end > JFFS2_F_I_SIZE(f));
+ BUG_ON(end < orig_end);
+
+ D1(printk("Expanded dnode to write from (0x%x-0x%x) to (0x%x-0x%x)\n",
+ fn->ofs, orig_end, start, end));
}
/* First, use readpage() to read the appropriate page into the page cache */
More information about the linux-mtd-cvs
mailing list