⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nodemgmt.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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 + -