📄 gc.c
字号:
/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * */#include <linux/kernel.h>#include <linux/mtd/mtd.h>#include <linux/slab.h>#include <linux/pagemap.h>#include <linux/crc32.h>#include <linux/compiler.h>#include <linux/stat.h>#include "nodelist.h"#include "compr.h"static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_raw_node_ref *raw);static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dnode *fd);static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dirent *fd);static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dirent *fd);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);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);static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f);/* Called with erase_completion_lock held */static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c){ struct jffs2_eraseblock *ret; struct list_head *nextlist = NULL; int n = jiffies % 128; /* Pick an eraseblock to garbage collect next. This is where we'll put the clever wear-levelling algorithms. Eventually. */ /* We possibly want to favour the dirtier blocks more when the number of free blocks is low. */again: if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > c->resv_blocks_gcbad) { D1(printk(KERN_DEBUG "Picking block from bad_used_list to GC next\n")); nextlist = &c->bad_used_list; } else if (n < 50 && !list_empty(&c->erasable_list)) { /* Note that most of them will have gone directly to be erased. So don't favour the erasable_list _too_ much. */ D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next\n")); nextlist = &c->erasable_list; } else if (n < 110 && !list_empty(&c->very_dirty_list)) { /* Most of the time, pick one off the very_dirty list */ D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next\n")); nextlist = &c->very_dirty_list; } else if (n < 126 && !list_empty(&c->dirty_list)) { D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next\n")); nextlist = &c->dirty_list; } else if (!list_empty(&c->clean_list)) { D1(printk(KERN_DEBUG "Picking block from clean_list to GC next\n")); nextlist = &c->clean_list; } else if (!list_empty(&c->dirty_list)) { D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next (clean_list was empty)\n")); nextlist = &c->dirty_list; } else if (!list_empty(&c->very_dirty_list)) { D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next (clean_list and dirty_list were empty)\n")); nextlist = &c->very_dirty_list; } else if (!list_empty(&c->erasable_list)) { D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next (clean_list and {very_,}dirty_list were empty)\n")); nextlist = &c->erasable_list; } else if (!list_empty(&c->erasable_pending_wbuf_list)) { /* There are blocks are wating for the wbuf sync */ D1(printk(KERN_DEBUG "Synching wbuf in order to reuse erasable_pending_wbuf_list blocks\n")); spin_unlock(&c->erase_completion_lock); jffs2_flush_wbuf_pad(c); spin_lock(&c->erase_completion_lock); goto again; } else { /* Eep. All were empty */ D1(printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n")); return NULL; } ret = list_entry(nextlist->next, struct jffs2_eraseblock, list); list_del(&ret->list); c->gcblock = ret; ret->gc_node = ret->first_node; if (!ret->gc_node) { printk(KERN_WARNING "Eep. ret->gc_node for block at 0x%08x is NULL\n", ret->offset); BUG(); } /* Have we accidentally picked a clean block with wasted space ? */ if (ret->wasted_size) { D1(printk(KERN_DEBUG "Converting wasted_size %08x to dirty_size\n", ret->wasted_size)); ret->dirty_size += ret->wasted_size; c->wasted_size -= ret->wasted_size; c->dirty_size += ret->wasted_size; ret->wasted_size = 0; } return ret;}/* jffs2_garbage_collect_pass * Make a single attempt to progress GC. Move one node, and possibly * start erasing one eraseblock. */int jffs2_garbage_collect_pass(struct jffs2_sb_info *c){ struct jffs2_inode_info *f; struct jffs2_inode_cache *ic; struct jffs2_eraseblock *jeb; struct jffs2_raw_node_ref *raw; uint32_t gcblock_dirty; int ret = 0, inum, nlink; int xattr = 0; if (down_interruptible(&c->alloc_sem)) return -EINTR; for (;;) { spin_lock(&c->erase_completion_lock); if (!c->unchecked_size) break; /* We can't start doing GC yet. We haven't finished checking the node CRCs etc. Do it now. */ /* checked_ino is protected by the alloc_sem */ if (c->checked_ino > c->highest_ino && xattr) { printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n", c->unchecked_size); jffs2_dbg_dump_block_lists_nolock(c); spin_unlock(&c->erase_completion_lock); up(&c->alloc_sem); return -ENOSPC; } spin_unlock(&c->erase_completion_lock); if (!xattr) xattr = jffs2_verify_xattr(c); spin_lock(&c->inocache_lock); ic = jffs2_get_ino_cache(c, c->checked_ino++); if (!ic) { spin_unlock(&c->inocache_lock); continue; } if (!ic->nlink) { D1(printk(KERN_DEBUG "Skipping check of ino #%d with nlink zero\n", ic->ino)); spin_unlock(&c->inocache_lock); jffs2_xattr_delete_inode(c, ic); continue; } switch(ic->state) { case INO_STATE_CHECKEDABSENT: case INO_STATE_PRESENT: D1(printk(KERN_DEBUG "Skipping ino #%u already checked\n", ic->ino)); spin_unlock(&c->inocache_lock); continue; case INO_STATE_GC: case INO_STATE_CHECKING: printk(KERN_WARNING "Inode #%u is in state %d during CRC check phase!\n", ic->ino, ic->state); spin_unlock(&c->inocache_lock); BUG(); case INO_STATE_READING: /* We need to wait for it to finish, lest we move on and trigger the BUG() above while we haven't yet finished checking all its nodes */ D1(printk(KERN_DEBUG "Waiting for ino #%u to finish reading\n", ic->ino)); /* We need to come back again for the _same_ inode. We've made no progress in this case, but that should be OK */ c->checked_ino--; up(&c->alloc_sem); sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); return 0; default: BUG(); case INO_STATE_UNCHECKED: ; } ic->state = INO_STATE_CHECKING; spin_unlock(&c->inocache_lock); D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%u\n", ic->ino)); ret = jffs2_do_crccheck_inode(c, ic); if (ret) printk(KERN_WARNING "Returned error for crccheck of ino #%u. Expect badness...\n", ic->ino); jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT); up(&c->alloc_sem); return ret; } /* First, work out which block we're garbage-collecting */ jeb = c->gcblock; if (!jeb) jeb = jffs2_find_gc_block(c); if (!jeb) { D1 (printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n")); spin_unlock(&c->erase_completion_lock); up(&c->alloc_sem); return -EIO; } D1(printk(KERN_DEBUG "GC from block %08x, used_size %08x, dirty_size %08x, free_size %08x\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size)); D1(if (c->nextblock) printk(KERN_DEBUG "Nextblock at %08x, used_size %08x, dirty_size %08x, wasted_size %08x, free_size %08x\n", c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->free_size)); if (!jeb->used_size) { up(&c->alloc_sem); goto eraseit; } raw = jeb->gc_node; gcblock_dirty = jeb->dirty_size; while(ref_obsolete(raw)) { D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw))); raw = ref_next(raw); if (unlikely(!raw)) { printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n"); printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size); jeb->gc_node = raw; spin_unlock(&c->erase_completion_lock); up(&c->alloc_sem); BUG(); } } jeb->gc_node = raw; D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", ref_offset(raw))); if (!raw->next_in_ino) { /* Inode-less node. Clean marker, snapshot or something like that */ spin_unlock(&c->erase_completion_lock); if (ref_flags(raw) == REF_PRISTINE) { /* It's an unknown node with JFFS2_FEATURE_RWCOMPAT_COPY */ jffs2_garbage_collect_pristine(c, NULL, raw); } else { /* Just mark it obsolete */ jffs2_mark_node_obsolete(c, raw); } up(&c->alloc_sem); goto eraseit_lock; } ic = jffs2_raw_ref_to_ic(raw);#ifdef CONFIG_JFFS2_FS_XATTR /* When 'ic' refers xattr_datum/xattr_ref, this node is GCed as xattr. * We can decide whether this node is inode or xattr by ic->class. */ if (ic->class == RAWNODE_CLASS_XATTR_DATUM || ic->class == RAWNODE_CLASS_XATTR_REF) { spin_unlock(&c->erase_completion_lock); if (ic->class == RAWNODE_CLASS_XATTR_DATUM) { ret = jffs2_garbage_collect_xattr_datum(c, (struct jffs2_xattr_datum *)ic, raw); } else { ret = jffs2_garbage_collect_xattr_ref(c, (struct jffs2_xattr_ref *)ic, raw); } goto test_gcnode; }#endif /* We need to hold the inocache. Either the erase_completion_lock or the inocache_lock are sufficient; we trade down since the inocache_lock causes less contention. */ spin_lock(&c->inocache_lock); spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), ic->ino)); /* Three possibilities: 1. Inode is already in-core. We must iget it and do proper updating to its fragtree, etc. 2. Inode is not in-core, node is REF_PRISTINE. We lock the inocache to prevent a read_inode(), copy the node intact. 3. Inode is not in-core, node is not pristine. We must iget() and take the slow path. */ switch(ic->state) { case INO_STATE_CHECKEDABSENT: /* It's been checked, but it's not currently in-core. We can just copy any pristine nodes, but have to prevent anyone else from doing read_inode() while we're at it, so we set the state accordingly */ if (ref_flags(raw) == REF_PRISTINE) ic->state = INO_STATE_GC; else { D1(printk(KERN_DEBUG "Ino #%u is absent but node not REF_PRISTINE. Reading.\n", ic->ino)); } break; case INO_STATE_PRESENT: /* It's in-core. GC must iget() it. */ break; case INO_STATE_UNCHECKED: case INO_STATE_CHECKING: case INO_STATE_GC: /* Should never happen. We should have finished checking by the time we actually start doing any GC, and since we're holding the alloc_sem, no other garbage collection can happen. */ printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n", ic->ino, ic->state); up(&c->alloc_sem); spin_unlock(&c->inocache_lock); BUG(); case INO_STATE_READING: /* Someone's currently trying to read it. We must wait for them to finish and then go through the full iget() route to do the GC. However, sometimes read_inode() needs to get the alloc_sem() (for marking nodes invalid) so we must drop the alloc_sem before sleeping. */ up(&c->alloc_sem); D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n", ic->ino, ic->state)); sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); /* And because we dropped the alloc_sem we must start again from the beginning. Ponder chance of livelock here -- we're returning success without actually making any progress. Q: What are the chances that the inode is back in INO_STATE_READING again by the time we next enter this function? And that this happens enough times to cause a real delay? A: Small enough that I don't care :) */ return 0; } /* OK. Now if the inode is in state INO_STATE_GC, we are going to copy the node intact, and we don't have to muck about with the fragtree etc. because we know it's not in-core. If it _was_ in-core, we go through all the iget() crap anyway */ if (ic->state == INO_STATE_GC) { spin_unlock(&c->inocache_lock); ret = jffs2_garbage_collect_pristine(c, ic, raw); spin_lock(&c->inocache_lock); ic->state = INO_STATE_CHECKEDABSENT; wake_up(&c->inocache_wq); if (ret != -EBADFD) { spin_unlock(&c->inocache_lock); goto test_gcnode; } /* Fall through if it wanted us to, with inocache_lock held */ } /* Prevent the fairly unlikely race where the gcblock is entirely obsoleted by the final close of a file which had the only valid nodes in the block, followed by erasure, followed by freeing of the ic because the erased block(s) held _all_ the nodes of that inode.... never been seen but it's vaguely possible. */ inum = ic->ino; nlink = ic->nlink; spin_unlock(&c->inocache_lock); f = jffs2_gc_fetch_inode(c, inum, nlink); if (IS_ERR(f)) { ret = PTR_ERR(f); goto release_sem; } if (!f) { ret = 0; goto release_sem; } ret = jffs2_garbage_collect_live(c, jeb, raw, f); jffs2_gc_release_inode(c, f); test_gcnode: if (jeb->dirty_size == gcblock_dirty && !ref_obsolete(jeb->gc_node)) { /* Eep. This really should never happen. GC is broken */ printk(KERN_ERR "Error garbage collecting node at %08x!\n", ref_offset(jeb->gc_node)); ret = -ENOSPC; } release_sem: up(&c->alloc_sem); eraseit_lock: /* If we've finished this block, start it erasing */ spin_lock(&c->erase_completion_lock); eraseit: if (c->gcblock && !c->gcblock->used_size) { D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset)); /* We're GC'ing an empty block? */ list_add_tail(&c->gcblock->list, &c->erase_pending_list); c->gcblock = NULL; c->nr_erasing_blocks++; jffs2_erase_pending_trigger(c); } spin_unlock(&c->erase_completion_lock);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -