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

📄 inode.c

📁 嵌入式系统设计与实例开发实验教材二源码 多线程应用程序设计 串行端口程序设计 AD接口实验 CAN总线通信实验 GPS通信实验 Linux内核移植与编译实验 IC卡读写实验 SD驱动使
💻 C
📖 第 1 页 / 共 5 页
字号:
 *	  + if there is a block to the left of our position - allocate near it. *	  + if pointer will live in indirect block - allocate near that block. *	  + if pointer will live in inode - allocate in the same *	    cylinder group.  *	Caller must make sure that @ind is valid and will stay that way. */static inline unsigned long ext3_find_near(struct inode *inode, Indirect *ind){	u32 *start = ind->bh ? (u32*) ind->bh->b_data : inode->u.ext3_i.i_data;	u32 *p;	/* Try to find previous block */	for (p = ind->p - 1; p >= start; p--)		if (*p)			return le32_to_cpu(*p);	/* No such thing, so let's try location of indirect block */	if (ind->bh)		return ind->bh->b_blocknr;	/*	 * It is going to be refered from inode itself? OK, just put it into	 * the same cylinder group then.	 */	return (inode->u.ext3_i.i_block_group * 		EXT3_BLOCKS_PER_GROUP(inode->i_sb)) +	       le32_to_cpu(inode->i_sb->u.ext3_sb.s_es->s_first_data_block);}/** *	ext3_find_goal - find a prefered place for allocation. *	@inode: owner *	@block:  block we want *	@chain:  chain of indirect blocks *	@partial: pointer to the last triple within a chain *	@goal:	place to store the result. * *	Normally this function find the prefered place for block allocation, *	stores it in *@goal and returns zero. If the branch had been changed *	under us we return -EAGAIN. */static int ext3_find_goal(struct inode *inode, long block, Indirect chain[4],			  Indirect *partial, unsigned long *goal){	/* Writer: ->i_next_alloc* */	if (block == inode->u.ext3_i.i_next_alloc_block + 1) {		inode->u.ext3_i.i_next_alloc_block++;		inode->u.ext3_i.i_next_alloc_goal++;	}#ifdef SEARCH_FROM_ZERO	inode->u.ext3_i.i_next_alloc_block = 0;	inode->u.ext3_i.i_next_alloc_goal = 0;#endif	/* Writer: end */	/* Reader: pointers, ->i_next_alloc* */	if (verify_chain(chain, partial)) {		/*		 * try the heuristic for sequential allocation,		 * failing that at least try to get decent locality.		 */		if (block == inode->u.ext3_i.i_next_alloc_block)			*goal = inode->u.ext3_i.i_next_alloc_goal;		if (!*goal)			*goal = ext3_find_near(inode, partial);#ifdef SEARCH_FROM_ZERO		*goal = 0;#endif		return 0;	}	/* Reader: end */	return -EAGAIN;}/** *	ext3_alloc_branch - allocate and set up a chain of blocks. *	@inode: owner *	@num: depth of the chain (number of blocks to allocate) *	@offsets: offsets (in the blocks) to store the pointers to next. *	@branch: place to store the chain in. * *	This function allocates @num blocks, zeroes out all but the last one, *	links them into chain and (if we are synchronous) writes them to disk. *	In other words, it prepares a branch that can be spliced onto the *	inode. It stores the information about that chain in the branch[], in *	the same format as ext3_get_branch() would do. We are calling it after *	we had read the existing part of chain and partial points to the last *	triple of that (one with zero ->key). Upon the exit we have the same *	picture as after the successful ext3_get_block(), excpet that in one *	place chain is disconnected - *branch->p is still zero (we did not *	set the last link), but branch->key contains the number that should *	be placed into *branch->p to fill that gap. * *	If allocation fails we free all blocks we've allocated (and forget *	their buffer_heads) and return the error value the from failed *	ext3_alloc_block() (normally -ENOSPC). Otherwise we set the chain *	as described above and return 0. */static int ext3_alloc_branch(handle_t *handle, struct inode *inode,			     int num,			     unsigned long goal,			     int *offsets,			     Indirect *branch){	int blocksize = inode->i_sb->s_blocksize;	int n = 0, keys = 0;	int err = 0;	int i;	int parent = ext3_alloc_block(handle, inode, goal, &err);	branch[0].key = cpu_to_le32(parent);	if (parent) {		for (n = 1; n < num; n++) {			struct buffer_head *bh;			/* Allocate the next block */			int nr = ext3_alloc_block(handle, inode, parent, &err);			if (!nr)				break;			branch[n].key = cpu_to_le32(nr);			keys = n+1;						/*			 * Get buffer_head for parent block, zero it out			 * and set the pointer to new one, then send			 * parent to disk.  			 */			bh = sb_getblk(inode->i_sb, parent);			branch[n].bh = bh;			lock_buffer(bh);			BUFFER_TRACE(bh, "call get_create_access");			err = ext3_journal_get_create_access(handle, bh);			if (err) {				unlock_buffer(bh);				brelse(bh);				break;			}			memset(bh->b_data, 0, blocksize);			branch[n].p = (u32*) bh->b_data + offsets[n];			*branch[n].p = branch[n].key;			BUFFER_TRACE(bh, "marking uptodate");			mark_buffer_uptodate(bh, 1);			unlock_buffer(bh);			BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata");			err = ext3_journal_dirty_metadata(handle, bh);			if (err)				break;						parent = nr;		}		if (IS_SYNC(inode))			handle->h_sync = 1;	}	if (n == num)		return 0;	/* Allocation failed, free what we already allocated */	for (i = 1; i < keys; i++) {		BUFFER_TRACE(branch[i].bh, "call journal_forget");		ext3_journal_forget(handle, branch[i].bh);	}	for (i = 0; i < keys; i++)		ext3_free_blocks(handle, inode, le32_to_cpu(branch[i].key), 1);	return err;}/** *	ext3_splice_branch - splice the allocated branch onto inode. *	@inode: owner *	@block: (logical) number of block we are adding *	@chain: chain of indirect blocks (with a missing link - see *		ext3_alloc_branch) *	@where: location of missing link *	@num:   number of blocks we are adding * *	This function verifies that chain (up to the missing link) had not *	changed, fills the missing link and does all housekeeping needed in *	inode (->i_blocks, etc.). In case of success we end up with the full *	chain to new block and return 0. Otherwise (== chain had been changed) *	we free the new blocks (forgetting their buffer_heads, indeed) and *	return -EAGAIN. */static int ext3_splice_branch(handle_t *handle, struct inode *inode, long block,			      Indirect chain[4], Indirect *where, int num){	int i;	int err = 0;	/*	 * If we're splicing into a [td]indirect block (as opposed to the	 * inode) then we need to get write access to the [td]indirect block	 * before the splice.	 */	if (where->bh) {		BUFFER_TRACE(where->bh, "get_write_access");		err = ext3_journal_get_write_access(handle, where->bh);		if (err)			goto err_out;	}	/* Verify that place we are splicing to is still there and vacant */	/* Writer: pointers, ->i_next_alloc* */	if (!verify_chain(chain, where-1) || *where->p)		/* Writer: end */		goto changed;	/* That's it */	*where->p = where->key;	inode->u.ext3_i.i_next_alloc_block = block;	inode->u.ext3_i.i_next_alloc_goal = le32_to_cpu(where[num-1].key);#ifdef SEARCH_FROM_ZERO	inode->u.ext3_i.i_next_alloc_block = 0;	inode->u.ext3_i.i_next_alloc_goal = 0;#endif	/* Writer: end */	/* We are done with atomic stuff, now do the rest of housekeeping */	inode->i_ctime = CURRENT_TIME;	ext3_mark_inode_dirty(handle, inode);	/* had we spliced it onto indirect block? */	if (where->bh) {		/*		 * akpm: If we spliced it onto an indirect block, we haven't		 * altered the inode.  Note however that if it is being spliced		 * onto an indirect block at the very end of the file (the		 * file is growing) then we *will* alter the inode to reflect		 * the new i_size.  But that is not done here - it is done in		 * generic_commit_write->__mark_inode_dirty->ext3_dirty_inode.		 */		jbd_debug(5, "splicing indirect only\n");		BUFFER_TRACE(where->bh, "call ext3_journal_dirty_metadata");		err = ext3_journal_dirty_metadata(handle, where->bh);		if (err) 			goto err_out;	} else {		/*		 * OK, we spliced it into the inode itself on a direct block.		 * Inode was dirtied above.		 */		jbd_debug(5, "splicing direct\n");	}	return err;changed:	/*	 * AKPM: if where[i].bh isn't part of the current updating	 * transaction then we explode nastily.  Test this code path.	 */	jbd_debug(1, "the chain changed: try again\n");	err = -EAGAIN;	err_out:	for (i = 1; i < num; i++) {		BUFFER_TRACE(where[i].bh, "call journal_forget");		ext3_journal_forget(handle, where[i].bh);	}	/* For the normal collision cleanup case, we free up the blocks.	 * On genuine filesystem errors we don't even think about doing	 * that. */	if (err == -EAGAIN)		for (i = 0; i < num; i++)			ext3_free_blocks(handle, inode, 					 le32_to_cpu(where[i].key), 1);	return err;}/* * Allocation strategy is simple: if we have to allocate something, we will * have to go the whole way to leaf. So let's do it before attaching anything * to tree, set linkage between the newborn blocks, write them if sync is * required, recheck the path, free and repeat if check fails, otherwise * set the last missing link (that will protect us from any truncate-generated * removals - all blocks on the path are immune now) and possibly force the * write on the parent block. * That has a nice additional property: no special recovery from the failed * allocations is needed - we simply release blocks and do not touch anything * reachable from inode. * * akpm: `handle' can be NULL if create == 0. * * The BKL may not be held on entry here.  Be sure to take it early. */static int ext3_get_block_handle(handle_t *handle, struct inode *inode, 				 long iblock,				 struct buffer_head *bh_result, int create){	int err = -EIO;	int offsets[4];	Indirect chain[4];	Indirect *partial;	unsigned long goal;	int left;	int depth = ext3_block_to_path(inode, iblock, offsets);	loff_t new_size;	J_ASSERT(handle != NULL || create == 0);	if (depth == 0)		goto out;	lock_kernel();reread:	partial = ext3_get_branch(inode, depth, offsets, chain, &err);	/* Simplest case - block found, no allocation needed */	if (!partial) {		bh_result->b_state &= ~(1UL << BH_New);got_it:		bh_result->b_dev = inode->i_dev;		bh_result->b_blocknr = le32_to_cpu(chain[depth-1].key);		bh_result->b_state |= (1UL << BH_Mapped);		/* Clean up and exit */		partial = chain+depth-1; /* the whole chain */		goto cleanup;	}	/* Next simple case - plain lookup or failed read of indirect block */	if (!create || err == -EIO) {cleanup:		while (partial > chain) {			BUFFER_TRACE(partial->bh, "call brelse");			brelse(partial->bh);			partial--;		}		BUFFER_TRACE(bh_result, "returned");		unlock_kernel();out:		return err;	}	/*	 * Indirect block might be removed by truncate while we were	 * reading it. Handling of that case (forget what we've got and	 * reread) is taken out of the main path.	 */	if (err == -EAGAIN)		goto changed;	if (ext3_find_goal(inode, iblock, chain, partial, &goal) < 0)		goto changed;	left = (chain + depth) - partial;	/*	 * Block out ext3_truncate while we alter the tree	 */	down_read(&inode->u.ext3_i.truncate_sem);	err = ext3_alloc_branch(handle, inode, left, goal,					offsets+(partial-chain), partial);	/* The ext3_splice_branch call will free and forget any buffers	 * on the new chain if there is a failure, but that risks using	 * up transaction credits, especially for bitmaps where the	 * credits cannot be returned.  Can we handle this somehow?  We	 * may need to return -EAGAIN upwards in the worst case.  --sct */	if (!err)		err = ext3_splice_branch(handle, inode, iblock, chain,					 partial, left);	up_read(&inode->u.ext3_i.truncate_sem);	if (err == -EAGAIN)		goto changed;	if (err)		goto cleanup;	new_size = inode->i_size;	/*	 * This is not racy against ext3_truncate's modification of i_disksize	 * because VM/VFS ensures that the file cannot be extended while	 * truncate is in progress.  It is racy between multiple parallel	 * instances of get_block, but we have the BKL.	 */	if (new_size > inode->u.ext3_i.i_disksize)		inode->u.ext3_i.i_disksize = new_size;	bh_result->b_state |= (1UL << BH_New);	goto got_it;changed:	while (partial > chain) {		jbd_debug(1, "buffer chain changed, retrying\n");		BUFFER_TRACE(partial->bh, "brelsing");		brelse(partial->bh);		partial--;	}	goto reread;}/* * The BKL is not held on entry here. */static int ext3_get_block(struct inode *inode, long iblock,			struct buffer_head *bh_result, int create){	handle_t *handle = 0;	int ret;	if (create) {		handle = ext3_journal_current_handle();		J_ASSERT(handle != 0);	}	ret = ext3_get_block_handle(handle, inode, iblock, bh_result, create);	return ret;}/* * `handle' can be NULL if create is zero */struct buffer_head *ext3_getblk(handle_t *handle, struct inode * inode,				long block, int create, int * errp){	struct buffer_head dummy;	int fatal = 0, err;		J_ASSERT(handle != NULL || create == 0);	dummy.b_state = 0;	dummy.b_blocknr = -1000;	buffer_trace_init(&dummy.b_history);	*errp = ext3_get_block_handle(handle, inode, block, &dummy, create);	if (!*errp && buffer_mapped(&dummy)) {		struct buffer_head *bh;		bh = sb_getblk(inode->i_sb, dummy.b_blocknr);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -