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

📄 inode.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
 */static ext4_fsblk_t ext4_find_goal(struct inode *inode, long block,		Indirect chain[4], Indirect *partial){	struct ext4_block_alloc_info *block_i;	block_i =  EXT4_I(inode)->i_block_alloc_info;	/*	 * try the heuristic for sequential allocation,	 * failing that at least try to get decent locality.	 */	if (block_i && (block == block_i->last_alloc_logical_block + 1)		&& (block_i->last_alloc_physical_block != 0)) {		return block_i->last_alloc_physical_block + 1;	}	return ext4_find_near(inode, partial);}/** *	ext4_blks_to_allocate: Look up the block map and count the number *	of direct blocks need to be allocated for the given branch. * *	@branch: chain of indirect blocks *	@k: number of blocks need for indirect blocks *	@blks: number of data blocks to be mapped. *	@blocks_to_boundary:  the offset in the indirect block * *	return the total number of blocks to be allocate, including the *	direct and indirect blocks. */static int ext4_blks_to_allocate(Indirect *branch, int k, unsigned long blks,		int blocks_to_boundary){	unsigned long count = 0;	/*	 * Simple case, [t,d]Indirect block(s) has not allocated yet	 * then it's clear blocks on that path have not allocated	 */	if (k > 0) {		/* right now we don't handle cross boundary allocation */		if (blks < blocks_to_boundary + 1)			count += blks;		else			count += blocks_to_boundary + 1;		return count;	}	count++;	while (count < blks && count <= blocks_to_boundary &&		le32_to_cpu(*(branch[0].p + count)) == 0) {		count++;	}	return count;}/** *	ext4_alloc_blocks: multiple allocate blocks needed for a branch *	@indirect_blks: the number of blocks need to allocate for indirect *			blocks * *	@new_blocks: on return it will store the new block numbers for *	the indirect blocks(if needed) and the first direct block, *	@blks:	on return it will store the total number of allocated *		direct blocks */static int ext4_alloc_blocks(handle_t *handle, struct inode *inode,			ext4_fsblk_t goal, int indirect_blks, int blks,			ext4_fsblk_t new_blocks[4], int *err){	int target, i;	unsigned long count = 0;	int index = 0;	ext4_fsblk_t current_block = 0;	int ret = 0;	/*	 * Here we try to allocate the requested multiple blocks at once,	 * on a best-effort basis.	 * To build a branch, we should allocate blocks for	 * the indirect blocks(if not allocated yet), and at least	 * the first direct block of this branch.  That's the	 * minimum number of blocks need to allocate(required)	 */	target = blks + indirect_blks;	while (1) {		count = target;		/* allocating blocks for indirect blocks and direct blocks */		current_block = ext4_new_blocks(handle,inode,goal,&count,err);		if (*err)			goto failed_out;		target -= count;		/* allocate blocks for indirect blocks */		while (index < indirect_blks && count) {			new_blocks[index++] = current_block++;			count--;		}		if (count > 0)			break;	}	/* save the new block number for the first direct block */	new_blocks[index] = current_block;	/* total number of blocks allocated for direct blocks */	ret = count;	*err = 0;	return ret;failed_out:	for (i = 0; i <index; i++)		ext4_free_blocks(handle, inode, new_blocks[i], 1);	return ret;}/** *	ext4_alloc_branch - allocate and set up a chain of blocks. *	@inode: owner *	@indirect_blks: number of allocated indirect blocks *	@blks: number of allocated direct blocks *	@offsets: offsets (in the blocks) to store the pointers to next. *	@branch: place to store the chain in. * *	This function allocates 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 ext4_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 ext4_get_block(), except 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 *	ext4_alloc_block() (normally -ENOSPC). Otherwise we set the chain *	as described above and return 0. */static int ext4_alloc_branch(handle_t *handle, struct inode *inode,			int indirect_blks, int *blks, ext4_fsblk_t goal,			int *offsets, Indirect *branch){	int blocksize = inode->i_sb->s_blocksize;	int i, n = 0;	int err = 0;	struct buffer_head *bh;	int num;	ext4_fsblk_t new_blocks[4];	ext4_fsblk_t current_block;	num = ext4_alloc_blocks(handle, inode, goal, indirect_blks,				*blks, new_blocks, &err);	if (err)		return err;	branch[0].key = cpu_to_le32(new_blocks[0]);	/*	 * metadata blocks and data blocks are allocated.	 */	for (n = 1; n <= indirect_blks;  n++) {		/*		 * 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, new_blocks[n-1]);		branch[n].bh = bh;		lock_buffer(bh);		BUFFER_TRACE(bh, "call get_create_access");		err = ext4_journal_get_create_access(handle, bh);		if (err) {			unlock_buffer(bh);			brelse(bh);			goto failed;		}		memset(bh->b_data, 0, blocksize);		branch[n].p = (__le32 *) bh->b_data + offsets[n];		branch[n].key = cpu_to_le32(new_blocks[n]);		*branch[n].p = branch[n].key;		if ( n == indirect_blks) {			current_block = new_blocks[n];			/*			 * End of chain, update the last new metablock of			 * the chain to point to the new allocated			 * data blocks numbers			 */			for (i=1; i < num; i++)				*(branch[n].p + i) = cpu_to_le32(++current_block);		}		BUFFER_TRACE(bh, "marking uptodate");		set_buffer_uptodate(bh);		unlock_buffer(bh);		BUFFER_TRACE(bh, "call ext4_journal_dirty_metadata");		err = ext4_journal_dirty_metadata(handle, bh);		if (err)			goto failed;	}	*blks = num;	return err;failed:	/* Allocation failed, free what we already allocated */	for (i = 1; i <= n ; i++) {		BUFFER_TRACE(branch[i].bh, "call jbd2_journal_forget");		ext4_journal_forget(handle, branch[i].bh);	}	for (i = 0; i <indirect_blks; i++)		ext4_free_blocks(handle, inode, new_blocks[i], 1);	ext4_free_blocks(handle, inode, new_blocks[i], num);	return err;}/** * ext4_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 *	ext4_alloc_branch) * @where: location of missing link * @num:   number of indirect blocks we are adding * @blks:  number of direct blocks we are adding * * This function 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. */static int ext4_splice_branch(handle_t *handle, struct inode *inode,			long block, Indirect *where, int num, int blks){	int i;	int err = 0;	struct ext4_block_alloc_info *block_i;	ext4_fsblk_t current_block;	block_i = EXT4_I(inode)->i_block_alloc_info;	/*	 * 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 = ext4_journal_get_write_access(handle, where->bh);		if (err)			goto err_out;	}	/* That's it */	*where->p = where->key;	/*	 * Update the host buffer_head or inode to point to more just allocated	 * direct blocks blocks	 */	if (num == 0 && blks > 1) {		current_block = le32_to_cpu(where->key) + 1;		for (i = 1; i < blks; i++)			*(where->p + i ) = cpu_to_le32(current_block++);	}	/*	 * update the most recently allocated logical & physical block	 * in i_block_alloc_info, to assist find the proper goal block for next	 * allocation	 */	if (block_i) {		block_i->last_alloc_logical_block = block + blks - 1;		block_i->last_alloc_physical_block =				le32_to_cpu(where[num].key) + blks - 1;	}	/* We are done with atomic stuff, now do the rest of housekeeping */	inode->i_ctime = ext4_current_time(inode);	ext4_mark_inode_dirty(handle, inode);	/* had we spliced it onto indirect block? */	if (where->bh) {		/*		 * 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->ext4_dirty_inode.		 */		jbd_debug(5, "splicing indirect only\n");		BUFFER_TRACE(where->bh, "call ext4_journal_dirty_metadata");		err = ext4_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;err_out:	for (i = 1; i <= num; i++) {		BUFFER_TRACE(where[i].bh, "call jbd2_journal_forget");		ext4_journal_forget(handle, where[i].bh);		ext4_free_blocks(handle,inode,le32_to_cpu(where[i-1].key),1);	}	ext4_free_blocks(handle, inode, le32_to_cpu(where[num].key), blks);	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. * * `handle' can be NULL if create == 0. * * The BKL may not be held on entry here.  Be sure to take it early. * return > 0, # of blocks mapped or allocated. * return = 0, if plain lookup failed. * return < 0, error case. */int ext4_get_blocks_handle(handle_t *handle, struct inode *inode,		sector_t iblock, unsigned long maxblocks,		struct buffer_head *bh_result,		int create, int extend_disksize){	int err = -EIO;	int offsets[4];	Indirect chain[4];	Indirect *partial;	ext4_fsblk_t goal;	int indirect_blks;	int blocks_to_boundary = 0;	int depth;	struct ext4_inode_info *ei = EXT4_I(inode);	int count = 0;	ext4_fsblk_t first_block = 0;	J_ASSERT(!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL));	J_ASSERT(handle != NULL || create == 0);	depth = ext4_block_to_path(inode,iblock,offsets,&blocks_to_boundary);	if (depth == 0)		goto out;	partial = ext4_get_branch(inode, depth, offsets, chain, &err);	/* Simplest case - block found, no allocation needed */	if (!partial) {		first_block = le32_to_cpu(chain[depth - 1].key);		clear_buffer_new(bh_result);		count++;		/*map more blocks*/		while (count < maxblocks && count <= blocks_to_boundary) {			ext4_fsblk_t blk;			if (!verify_chain(chain, partial)) {				/*				 * Indirect block might be removed by				 * truncate while we were reading it.				 * Handling of that case: forget what we've				 * got now. Flag the err as EAGAIN, so it				 * will reread.				 */				err = -EAGAIN;				count = 0;				break;			}			blk = le32_to_cpu(*(chain[depth-1].p + count));			if (blk == first_block + count)				count++;			else				break;		}		if (err != -EAGAIN)			goto got_it;	}	/* Next simple case - plain lookup or failed read of indirect block */	if (!create || err == -EIO)		goto cleanup;	mutex_lock(&ei->truncate_mutex);	/*	 * If the indirect block is missing while we are reading	 * the chain(ext4_get_branch() returns -EAGAIN err), or	 * if the chain has been changed after we grab the semaphore,	 * (either because another process truncated this branch, or	 * another get_block allocated this branch) re-grab the chain to see if	 * the request block has been allocated or not.	 *	 * Since we already block the truncate/other get_block	 * at this point, we will have the current copy of the chain when we	 * splice the branch into the tree.	 */	if (err == -EAGAIN || !verify_chain(chain, partial)) {		while (partial > chain) {			brelse(partial->bh);			partial--;		}		partial = ext4_get_branch(inode, depth, offsets, chain, &err);		if (!partial) {			count++;			mutex_unlock(&ei->truncate_mutex);			if (err)				goto cleanup;			clear_buffer_new(bh_result);			goto got_it;		}	}	/*	 * Okay, we need to do block allocation.  Lazily initialize the block	 * allocation info here if necessary	*/	if (S_ISREG(inode->i_mode) && (!ei->i_block_alloc_info))		ext4_init_block_alloc_info(inode);	goal = ext4_find_goal(inode, iblock, chain, partial);	/* the number of blocks need to allocate for [d,t]indirect blocks */	indirect_blks = (chain + depth) - partial - 1;

⌨️ 快捷键说明

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