inode.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,219 行 · 第 1/5 页

C
2,219
字号
		/* Reader: end */		if (!p->key)			goto no_block;	}	return NULL;changed:	brelse(bh);	*err = -EAGAIN;	goto no_block;failure:	*err = -EIO;no_block:	return p;}/** *	ext3_find_near - find a place for allocation with sufficient locality *	@inode: owner *	@ind: descriptor of indirect block. * *	This function returns the prefered place for block allocation. *	It is used when heuristic for sequential allocation fails. *	Rules are: *	  + 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.  * * In the latter case we colour the starting block by the callers PID to * prevent it from clashing with concurrent allocations for a different inode * in the same block group.   The PID is used here so that functionally related * files will be close-by on-disk. * *	Caller must make sure that @ind is valid and will stay that way. */static unsigned long ext3_find_near(struct inode *inode, Indirect *ind){	struct ext3_inode_info *ei = EXT3_I(inode);	__le32 *start = ind->bh ? (__le32*) ind->bh->b_data : ei->i_data;	__le32 *p;	unsigned long bg_start;	unsigned long colour;	/* 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.	 */	bg_start = (ei->i_block_group * EXT3_BLOCKS_PER_GROUP(inode->i_sb)) +		le32_to_cpu(EXT3_SB(inode->i_sb)->s_es->s_first_data_block);	colour = (current->pid % 16) *			(EXT3_BLOCKS_PER_GROUP(inode->i_sb) / 16);	return bg_start + colour;}/** *	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){	struct ext3_inode_info *ei = EXT3_I(inode);	/* Writer: ->i_next_alloc* */	if (block == ei->i_next_alloc_block + 1) {		ei->i_next_alloc_block++;		ei->i_next_alloc_goal++;	}	/* 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 == ei->i_next_alloc_block)			*goal = ei->i_next_alloc_goal;		if (!*goal)			*goal = ext3_find_near(inode, partial);		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 = (__le32*) bh->b_data + offsets[n];			*branch[n].p = branch[n].key;			BUFFER_TRACE(bh, "marking uptodate");			set_buffer_uptodate(bh);			unlock_buffer(bh);			BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata");			err = ext3_journal_dirty_metadata(handle, bh);			if (err)				break;			parent = nr;		}	}	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;	struct ext3_inode_info *ei = EXT3_I(inode);	/*	 * 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;	ei->i_next_alloc_block = block;	ei->i_next_alloc_goal = le32_to_cpu(where[num-1].key);	/* 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 intext3_get_block_handle(handle_t *handle, struct inode *inode, sector_t iblock,		struct buffer_head *bh_result, int create, int extend_disksize){	int err = -EIO;	int offsets[4];	Indirect chain[4];	Indirect *partial;	unsigned long goal;	int left;	int boundary = 0;	int depth = ext3_block_to_path(inode, iblock, offsets, &boundary);	struct ext3_inode_info *ei = EXT3_I(inode);	J_ASSERT(handle != NULL || create == 0);	if (depth == 0)		goto out;reread:	partial = ext3_get_branch(inode, depth, offsets, chain, &err);	/* Simplest case - block found, no allocation needed */	if (!partial) {		clear_buffer_new(bh_result);got_it:		map_bh(bh_result, inode->i_sb, le32_to_cpu(chain[depth-1].key));		if (boundary)			set_buffer_boundary(bh_result);		/* 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");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;	goal = 0;	down(&ei->truncate_sem);	if (ext3_find_goal(inode, iblock, chain, partial, &goal) < 0) {		up(&ei->truncate_sem);		goto changed;	}	left = (chain + depth) - partial;	/*	 * Block out ext3_truncate while we alter the tree	 */	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);	/* i_disksize growing is protected by truncate_sem	 * don't forget to protect it if you're about to implement	 * concurrent ext3_get_block() -bzzz */	if (!err && extend_disksize && inode->i_size > ei->i_disksize)		ei->i_disksize = inode->i_size;	up(&ei->truncate_sem);	if (err == -EAGAIN)		goto changed;	if (err)		goto cleanup;	set_buffer_new(bh_result);	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;}static int ext3_get_block(struct inode *inode, sector_t iblock,			struct buffer_head *bh_result, int create){	handle_t *handle = NULL;	int ret;	if (create) {		handle = ext3_journal_current_handle();		J_ASSERT(handle != 0);	}	ret = ext3_get_block_handle(handle, inode, iblock,				bh_result, create, 1);	return ret;}#define DIO_CREDITS (EXT3_RESERVE_TRANS_BLOCKS + 32)static intext3_direct_io_get_blocks(struct inode *inode, sector_t iblock,		unsigned long max_blocks, struct buffer_head *bh_result,		int create){	handle_t *handle = journal_current_handle();	int ret = 0;	if (!handle)		goto get_block;		/* A read */

⌨️ 快捷键说明

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