📄 nodemgmt.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/slab.h>#include <linux/mtd/mtd.h>#include <linux/compiler.h>#include <linux/sched.h> /* For cond_resched() */#include "nodelist.h"#include "debug.h"/** * jffs2_reserve_space - request physical space to write nodes to flash * @c: superblock info * @minsize: Minimum acceptable size of allocation * @len: Returned value of allocation length * @prio: Allocation type - ALLOC_{NORMAL,DELETION} * * Requests a block of physical space on the flash. Returns zero for success * and puts 'len' into the appropriate place, or returns -ENOSPC or other * error if appropriate. Doesn't return len since that's * * If it returns zero, jffs2_reserve_space() also downs the per-filesystem * allocation semaphore, to prevent more than one allocation from being * active at any time. The semaphore is later released by jffs2_commit_allocation() * * jffs2_reserve_space() may trigger garbage collection in order to make room * for the requested allocation. */static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *len, uint32_t sumsize);int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *len, int prio, uint32_t sumsize){ int ret = -EAGAIN; int blocksneeded = c->resv_blocks_write; /* align it */ minsize = PAD(minsize); D1(printk(KERN_DEBUG "jffs2_reserve_space(): Requested 0x%x bytes\n", minsize)); down(&c->alloc_sem); D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n")); spin_lock(&c->erase_completion_lock); /* this needs a little more thought (true <tglx> :)) */ while(ret == -EAGAIN) { while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) { int ret; uint32_t dirty, avail; /* calculate real dirty size * dirty_size contains blocks on erase_pending_list * those blocks are counted in c->nr_erasing_blocks. * If one block is actually erased, it is not longer counted as dirty_space * but it is counted in c->nr_erasing_blocks, so we add it and subtract it * with c->nr_erasing_blocks * c->sector_size again. * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks * This helps us to force gc and pick eventually a clean block to spread the load. * We add unchecked_size here, as we hopefully will find some space to use. * This will affect the sum only once, as gc first finishes checking * of nodes. */ dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size + c->unchecked_size; if (dirty < c->nospc_dirty_size) { if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) { D1(printk(KERN_NOTICE "jffs2_reserve_space(): Low on dirty space to GC, but it's a deletion. Allowing...\n")); break; } D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < nospc_dirty_size 0x%08x, returning -ENOSPC\n", dirty, c->unchecked_size, c->sector_size)); spin_unlock(&c->erase_completion_lock); up(&c->alloc_sem); return -ENOSPC; } /* Calc possibly available space. Possibly available means that we * don't know, if unchecked size contains obsoleted nodes, which could give us some * more usable space. This will affect the sum only once, as gc first finishes checking * of nodes. + Return -ENOSPC, if the maximum possibly available space is less or equal than * blocksneeded * sector_size. * This blocks endless gc looping on a filesystem, which is nearly full, even if * the check above passes. */ avail = c->free_size + c->dirty_size + c->erasing_size + c->unchecked_size; if ( (avail / c->sector_size) <= blocksneeded) { if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) { D1(printk(KERN_NOTICE "jffs2_reserve_space(): Low on possibly available space, but it's a deletion. Allowing...\n")); break; } D1(printk(KERN_DEBUG "max. available size 0x%08x < blocksneeded * sector_size 0x%08x, returning -ENOSPC\n", avail, blocksneeded * c->sector_size)); spin_unlock(&c->erase_completion_lock); up(&c->alloc_sem); return -ENOSPC; } up(&c->alloc_sem); D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, wasted_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n", c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->wasted_size, c->used_size, c->erasing_size, c->bad_size, c->free_size + c->dirty_size + c->wasted_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size)); spin_unlock(&c->erase_completion_lock); ret = jffs2_garbage_collect_pass(c); if (ret) return ret; cond_resched(); if (signal_pending(current)) return -EINTR; down(&c->alloc_sem); spin_lock(&c->erase_completion_lock); } ret = jffs2_do_reserve_space(c, minsize, len, sumsize); if (ret) { D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret)); } } spin_unlock(&c->erase_completion_lock); if (!ret) ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1); if (ret) up(&c->alloc_sem); return ret;}int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *len, uint32_t sumsize){ int ret = -EAGAIN; minsize = PAD(minsize); D1(printk(KERN_DEBUG "jffs2_reserve_space_gc(): Requested 0x%x bytes\n", minsize)); spin_lock(&c->erase_completion_lock); while(ret == -EAGAIN) { ret = jffs2_do_reserve_space(c, minsize, len, sumsize); if (ret) { D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret)); } } spin_unlock(&c->erase_completion_lock); if (!ret) ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1); return ret;}/* Classify nextblock (clean, dirty of verydirty) and force to select an other one */static void jffs2_close_nextblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb){ if (c->nextblock == NULL) { D1(printk(KERN_DEBUG "jffs2_close_nextblock: Erase block at 0x%08x has already been placed in a list\n", jeb->offset)); return; } /* Check, if we have a dirty block now, or if it was dirty already */ if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) { c->dirty_size += jeb->wasted_size; c->wasted_size -= jeb->wasted_size; jeb->dirty_size += jeb->wasted_size; jeb->wasted_size = 0; if (VERYDIRTY(c, jeb->dirty_size)) { D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to very_dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); list_add_tail(&jeb->list, &c->very_dirty_list); } else { D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); list_add_tail(&jeb->list, &c->dirty_list); } } else { D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); list_add_tail(&jeb->list, &c->clean_list); } c->nextblock = NULL;}/* Select a new jeb for nextblock */static int jffs2_find_nextblock(struct jffs2_sb_info *c){ struct list_head *next; /* Take the next block off the 'free' list */ if (list_empty(&c->free_list)) { if (!c->nr_erasing_blocks && !list_empty(&c->erasable_list)) { struct jffs2_eraseblock *ejeb; ejeb = list_entry(c->erasable_list.next, struct jffs2_eraseblock, list); list_move_tail(&ejeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; jffs2_erase_pending_trigger(c); D1(printk(KERN_DEBUG "jffs2_find_nextblock: Triggering erase of erasable block at 0x%08x\n", ejeb->offset)); } if (!c->nr_erasing_blocks && !list_empty(&c->erasable_pending_wbuf_list)) { D1(printk(KERN_DEBUG "jffs2_find_nextblock: Flushing write buffer\n")); /* c->nextblock is NULL, no update to c->nextblock allowed */ spin_unlock(&c->erase_completion_lock); jffs2_flush_wbuf_pad(c); spin_lock(&c->erase_completion_lock); /* Have another go. It'll be on the erasable_list now */ return -EAGAIN; } if (!c->nr_erasing_blocks) { /* Ouch. We're in GC, or we wouldn't have got here. And there's no space left. At all. */ printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n", c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasable_list)?"yes":"no", list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"); return -ENOSPC; } spin_unlock(&c->erase_completion_lock); /* Don't wait for it; just erase one right now */ jffs2_erase_pending_blocks(c, 1); spin_lock(&c->erase_completion_lock); /* An erase may have failed, decreasing the amount of free space available. So we must restart from the beginning */ return -EAGAIN; } next = c->free_list.next; list_del(next); c->nextblock = list_entry(next, struct jffs2_eraseblock, list); c->nr_free_blocks--; jffs2_sum_reset_collected(c->summary); /* reset collected summary */ D1(printk(KERN_DEBUG "jffs2_find_nextblock(): new nextblock = 0x%08x\n", c->nextblock->offset)); return 0;}/* Called with alloc sem _and_ erase_completion_lock */static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *len, uint32_t sumsize){ struct jffs2_eraseblock *jeb = c->nextblock; uint32_t reserved_size; /* for summary information at the end of the jeb */ int ret; restart: reserved_size = 0; if (jffs2_sum_active() && (sumsize != JFFS2_SUMMARY_NOSUM_SIZE)) { /* NOSUM_SIZE means not to generate summary */ if (jeb) { reserved_size = PAD(sumsize + c->summary->sum_size + JFFS2_SUMMARY_FRAME_SIZE); dbg_summary("minsize=%d , jeb->free=%d ," "summary->size=%d , sumsize=%d\n", minsize, jeb->free_size, c->summary->sum_size, sumsize); } /* Is there enough space for writing out the current node, or we have to write out summary information now, close this jeb and select new nextblock? */ if (jeb && (PAD(minsize) + PAD(c->summary->sum_size + sumsize + JFFS2_SUMMARY_FRAME_SIZE) > jeb->free_size)) { /* Has summary been disabled for this jeb? */ if (jffs2_sum_is_disabled(c->summary)) { sumsize = JFFS2_SUMMARY_NOSUM_SIZE; goto restart; } /* Writing out the collected summary information */ dbg_summary("generating summary for 0x%08x.\n", jeb->offset); ret = jffs2_sum_write_sumnode(c); if (ret) return ret; if (jffs2_sum_is_disabled(c->summary)) { /* jffs2_write_sumnode() couldn't write out the summary information diabling summary for this jeb and free the collected information */ sumsize = JFFS2_SUMMARY_NOSUM_SIZE; goto restart; } jffs2_close_nextblock(c, jeb); jeb = NULL; /* keep always valid value in reserved_size */ reserved_size = PAD(sumsize + c->summary->sum_size + JFFS2_SUMMARY_FRAME_SIZE); } } else { if (jeb && minsize > jeb->free_size) { uint32_t waste; /* Skip the end of this block and file it as having some dirty space */ /* If there's a pending write to it, flush now */ if (jffs2_wbuf_dirty(c)) { spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n")); jffs2_flush_wbuf_pad(c); spin_lock(&c->erase_completion_lock); jeb = c->nextblock; goto restart; } spin_unlock(&c->erase_completion_lock); ret = jffs2_prealloc_raw_node_refs(c, jeb, 1); if (ret) return ret; /* Just lock it again and continue. Nothing much can change because we hold c->alloc_sem anyway. In fact, it's not entirely clear why we hold c->erase_completion_lock in the majority of this function... but that's a question for another (more caffeine-rich) day. */ spin_lock(&c->erase_completion_lock); waste = jeb->free_size; jffs2_link_node_ref(c, jeb, (jeb->offset + c->sector_size - waste) | REF_OBSOLETE, waste, NULL); /* FIXME: that made it count as dirty. Convert to wasted */ jeb->dirty_size -= waste; c->dirty_size -= waste; jeb->wasted_size += waste; c->wasted_size += waste; jffs2_close_nextblock(c, jeb); jeb = NULL; } } if (!jeb) { ret = jffs2_find_nextblock(c); if (ret) return ret; jeb = c->nextblock; if (jeb->free_size != c->sector_size - c->cleanmarker_size) { printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size); goto restart; } } /* OK, jeb (==c->nextblock) is now pointing at a block which definitely has enough space */ *len = jeb->free_size - reserved_size; if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size && !jeb->first_node->next_in_ino) { /* Only node in it beforehand was a CLEANMARKER node (we think). So mark it obsolete now that there's going to be another node
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -