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

📄 inode.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
};static const struct address_space_operations ext4_writeback_aops = {	.readpage	= ext4_readpage,	.readpages	= ext4_readpages,	.writepage	= ext4_writeback_writepage,	.sync_page	= block_sync_page,	.write_begin	= ext4_write_begin,	.write_end	= ext4_writeback_write_end,	.bmap		= ext4_bmap,	.invalidatepage	= ext4_invalidatepage,	.releasepage	= ext4_releasepage,	.direct_IO	= ext4_direct_IO,	.migratepage	= buffer_migrate_page,};static const struct address_space_operations ext4_journalled_aops = {	.readpage	= ext4_readpage,	.readpages	= ext4_readpages,	.writepage	= ext4_journalled_writepage,	.sync_page	= block_sync_page,	.write_begin	= ext4_write_begin,	.write_end	= ext4_journalled_write_end,	.set_page_dirty	= ext4_journalled_set_page_dirty,	.bmap		= ext4_bmap,	.invalidatepage	= ext4_invalidatepage,	.releasepage	= ext4_releasepage,};void ext4_set_aops(struct inode *inode){	if (ext4_should_order_data(inode))		inode->i_mapping->a_ops = &ext4_ordered_aops;	else if (ext4_should_writeback_data(inode))		inode->i_mapping->a_ops = &ext4_writeback_aops;	else		inode->i_mapping->a_ops = &ext4_journalled_aops;}/* * ext4_block_truncate_page() zeroes out a mapping from file offset `from' * up to the end of the block which corresponds to `from'. * This required during truncate. We need to physically zero the tail end * of that block so it doesn't yield old data if the file is later grown. */int ext4_block_truncate_page(handle_t *handle, struct page *page,		struct address_space *mapping, loff_t from){	ext4_fsblk_t index = from >> PAGE_CACHE_SHIFT;	unsigned offset = from & (PAGE_CACHE_SIZE-1);	unsigned blocksize, iblock, length, pos;	struct inode *inode = mapping->host;	struct buffer_head *bh;	int err = 0;	blocksize = inode->i_sb->s_blocksize;	length = blocksize - (offset & (blocksize - 1));	iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits);	/*	 * For "nobh" option,  we can only work if we don't need to	 * read-in the page - otherwise we create buffers to do the IO.	 */	if (!page_has_buffers(page) && test_opt(inode->i_sb, NOBH) &&	     ext4_should_writeback_data(inode) && PageUptodate(page)) {		zero_user_page(page, offset, length, KM_USER0);		set_page_dirty(page);		goto unlock;	}	if (!page_has_buffers(page))		create_empty_buffers(page, blocksize, 0);	/* Find the buffer that contains "offset" */	bh = page_buffers(page);	pos = blocksize;	while (offset >= pos) {		bh = bh->b_this_page;		iblock++;		pos += blocksize;	}	err = 0;	if (buffer_freed(bh)) {		BUFFER_TRACE(bh, "freed: skip");		goto unlock;	}	if (!buffer_mapped(bh)) {		BUFFER_TRACE(bh, "unmapped");		ext4_get_block(inode, iblock, bh, 0);		/* unmapped? It's a hole - nothing to do */		if (!buffer_mapped(bh)) {			BUFFER_TRACE(bh, "still unmapped");			goto unlock;		}	}	/* Ok, it's mapped. Make sure it's up-to-date */	if (PageUptodate(page))		set_buffer_uptodate(bh);	if (!buffer_uptodate(bh)) {		err = -EIO;		ll_rw_block(READ, 1, &bh);		wait_on_buffer(bh);		/* Uhhuh. Read error. Complain and punt. */		if (!buffer_uptodate(bh))			goto unlock;	}	if (ext4_should_journal_data(inode)) {		BUFFER_TRACE(bh, "get write access");		err = ext4_journal_get_write_access(handle, bh);		if (err)			goto unlock;	}	zero_user_page(page, offset, length, KM_USER0);	BUFFER_TRACE(bh, "zeroed end of block");	err = 0;	if (ext4_should_journal_data(inode)) {		err = ext4_journal_dirty_metadata(handle, bh);	} else {		if (ext4_should_order_data(inode))			err = ext4_journal_dirty_data(handle, bh);		mark_buffer_dirty(bh);	}unlock:	unlock_page(page);	page_cache_release(page);	return err;}/* * Probably it should be a library function... search for first non-zero word * or memcmp with zero_page, whatever is better for particular architecture. * Linus? */static inline int all_zeroes(__le32 *p, __le32 *q){	while (p < q)		if (*p++)			return 0;	return 1;}/** *	ext4_find_shared - find the indirect blocks for partial truncation. *	@inode:	  inode in question *	@depth:	  depth of the affected branch *	@offsets: offsets of pointers in that branch (see ext4_block_to_path) *	@chain:	  place to store the pointers to partial indirect blocks *	@top:	  place to the (detached) top of branch * *	This is a helper function used by ext4_truncate(). * *	When we do truncate() we may have to clean the ends of several *	indirect blocks but leave the blocks themselves alive. Block is *	partially truncated if some data below the new i_size is refered *	from it (and it is on the path to the first completely truncated *	data block, indeed).  We have to free the top of that path along *	with everything to the right of the path. Since no allocation *	past the truncation point is possible until ext4_truncate() *	finishes, we may safely do the latter, but top of branch may *	require special attention - pageout below the truncation point *	might try to populate it. * *	We atomically detach the top of branch from the tree, store the *	block number of its root in *@top, pointers to buffer_heads of *	partially truncated blocks - in @chain[].bh and pointers to *	their last elements that should not be removed - in *	@chain[].p. Return value is the pointer to last filled element *	of @chain. * *	The work left to caller to do the actual freeing of subtrees: *		a) free the subtree starting from *@top *		b) free the subtrees whose roots are stored in *			(@chain[i].p+1 .. end of @chain[i].bh->b_data) *		c) free the subtrees growing from the inode past the @chain[0]. *			(no partially truncated stuff there).  */static Indirect *ext4_find_shared(struct inode *inode, int depth,			int offsets[4], Indirect chain[4], __le32 *top){	Indirect *partial, *p;	int k, err;	*top = 0;	/* Make k index the deepest non-null offest + 1 */	for (k = depth; k > 1 && !offsets[k-1]; k--)		;	partial = ext4_get_branch(inode, k, offsets, chain, &err);	/* Writer: pointers */	if (!partial)		partial = chain + k-1;	/*	 * If the branch acquired continuation since we've looked at it -	 * fine, it should all survive and (new) top doesn't belong to us.	 */	if (!partial->key && *partial->p)		/* Writer: end */		goto no_top;	for (p=partial; p>chain && all_zeroes((__le32*)p->bh->b_data,p->p); p--)		;	/*	 * OK, we've found the last block that must survive. The rest of our	 * branch should be detached before unlocking. However, if that rest	 * of branch is all ours and does not grow immediately from the inode	 * it's easier to cheat and just decrement partial->p.	 */	if (p == chain + k - 1 && p > chain) {		p->p--;	} else {		*top = *p->p;		/* Nope, don't do this in ext4.  Must leave the tree intact */#if 0		*p->p = 0;#endif	}	/* Writer: end */	while(partial > p) {		brelse(partial->bh);		partial--;	}no_top:	return partial;}/* * Zero a number of block pointers in either an inode or an indirect block. * If we restart the transaction we must again get write access to the * indirect block for further modification. * * We release `count' blocks on disk, but (last - first) may be greater * than `count' because there can be holes in there. */static void ext4_clear_blocks(handle_t *handle, struct inode *inode,		struct buffer_head *bh, ext4_fsblk_t block_to_free,		unsigned long count, __le32 *first, __le32 *last){	__le32 *p;	if (try_to_extend_transaction(handle, inode)) {		if (bh) {			BUFFER_TRACE(bh, "call ext4_journal_dirty_metadata");			ext4_journal_dirty_metadata(handle, bh);		}		ext4_mark_inode_dirty(handle, inode);		ext4_journal_test_restart(handle, inode);		if (bh) {			BUFFER_TRACE(bh, "retaking write access");			ext4_journal_get_write_access(handle, bh);		}	}	/*	 * Any buffers which are on the journal will be in memory. We find	 * them on the hash table so jbd2_journal_revoke() will run jbd2_journal_forget()	 * on them.  We've already detached each block from the file, so	 * bforget() in jbd2_journal_forget() should be safe.	 *	 * AKPM: turn on bforget in jbd2_journal_forget()!!!	 */	for (p = first; p < last; p++) {		u32 nr = le32_to_cpu(*p);		if (nr) {			struct buffer_head *bh;			*p = 0;			bh = sb_find_get_block(inode->i_sb, nr);			ext4_forget(handle, 0, inode, bh, nr);		}	}	ext4_free_blocks(handle, inode, block_to_free, count);}/** * ext4_free_data - free a list of data blocks * @handle:	handle for this transaction * @inode:	inode we are dealing with * @this_bh:	indirect buffer_head which contains *@first and *@last * @first:	array of block numbers * @last:	points immediately past the end of array * * We are freeing all blocks refered from that array (numbers are stored as * little-endian 32-bit) and updating @inode->i_blocks appropriately. * * We accumulate contiguous runs of blocks to free.  Conveniently, if these * blocks are contiguous then releasing them at one time will only affect one * or two bitmap blocks (+ group descriptor(s) and superblock) and we won't * actually use a lot of journal space. * * @this_bh will be %NULL if @first and @last point into the inode's direct * block pointers. */static void ext4_free_data(handle_t *handle, struct inode *inode,			   struct buffer_head *this_bh,			   __le32 *first, __le32 *last){	ext4_fsblk_t block_to_free = 0;    /* Starting block # of a run */	unsigned long count = 0;	    /* Number of blocks in the run */	__le32 *block_to_free_p = NULL;	    /* Pointer into inode/ind					       corresponding to					       block_to_free */	ext4_fsblk_t nr;		    /* Current block # */	__le32 *p;			    /* Pointer into inode/ind					       for current block */	int err;	if (this_bh) {				/* For indirect block */		BUFFER_TRACE(this_bh, "get_write_access");		err = ext4_journal_get_write_access(handle, this_bh);		/* Important: if we can't update the indirect pointers		 * to the blocks, we can't free them. */		if (err)			return;	}	for (p = first; p < last; p++) {		nr = le32_to_cpu(*p);		if (nr) {			/* accumulate blocks to free if they're contiguous */			if (count == 0) {				block_to_free = nr;				block_to_free_p = p;				count = 1;			} else if (nr == block_to_free + count) {				count++;			} else {				ext4_clear_blocks(handle, inode, this_bh,						  block_to_free,						  count, block_to_free_p, p);				block_to_free = nr;				block_to_free_p = p;				count = 1;			}		}	}	if (count > 0)		ext4_clear_blocks(handle, inode, this_bh, block_to_free,				  count, block_to_free_p, p);	if (this_bh) {		BUFFER_TRACE(this_bh, "call ext4_journal_dirty_metadata");		ext4_journal_dirty_metadata(handle, this_bh);	}}/** *	ext4_free_branches - free an array of branches *	@handle: JBD handle for this transaction *	@inode:	inode we are dealing with *	@parent_bh: the buffer_head which contains *@first and *@last *	@first:	array of block numbers *	@last:	pointer immediately past the end of array *	@depth:	depth of the branches to free * *	We are freeing all blocks refered from these branches (numbers are *	stored as little-endian 32-bit) and updating @inode->i_blocks *	appropriately. */static void ext4_free_branches(handle_t *handle, struct inode *inode,			       struct buffer_head *parent_bh,			       __le32 *first, __le32 *last, int depth){	ext4_fsblk_t nr;	__le32 *p;	if (is_handle_aborted(handle))		return;	if (depth--) {		struct buffer_head *bh;		int addr_per_block = EXT4_ADDR_PER_BLOCK(inode->i_sb);		p = last;		while (--p >= first) {			nr = le32_to_cpu(*p);			if (!nr)				continue;		/* A hole */			/* Go read the buffer for the next level down */			bh = sb_bread(inode->i_sb, nr);			/*			 * A read failure? Report error and clear slot			 * (should be rare).			 */			if (!bh) {				ext4_error(inode->i_sb, "ext4_free_branches",					   "Read failure, inode=%lu, block=%llu",					   inode->i_ino, nr);				continue;			}			/* This zaps the entire block.  Bottom up. */			BUFFER_TRACE(bh, "free child branches");			ext4_free_branches(handle, inode, bh,					   (__le32*)bh->b_data,					   (__le32*)bh->b_data + addr_per_block,					   depth);			/*			 * We've probably journalled the indirect block several			 * times during the truncate.  But it's no longer			 * needed and we now drop it from the transaction via			 * jbd2_journal_revoke().			 *			 * That's easy if it's exclusively part of this			 * transaction.  But if it's part of the committing			 * transaction then jbd2_journal_forget() will simply			 * brelse() it.  That means that if the underlying			 * block is reallocated in ext4_get_block(),			 * unmap_underlying_metadata() will find this block			 * and will try to get rid of it.  damn, damn.			 *			 * If this block has already been committed to the			 * journal, a revoke record will be written.  And			 * revoke records must be emitted *before* clearing			 * this block's bit in the bitmaps.			 */			ext4_forget(handle, 1, inode, bh, bh->b_blocknr);			/*			 * Everything below this this pointer has been			 * released.  Now let this top-of-subtree go.			 *			 * We want the freeing of this indirect block to be			 * atomic in the journal with the updating of the			 * bitmap block which owns it.  So make some room in			 * the journal.			 *			 * We zero the parent pointer *after* freeing its			 * pointee in the bitmaps, so if extend_transaction()			 * for some reason fails to put the bitmap changes and			 * the release into the same transaction, recovery			 * will merely complain about releasing a free block,			 * rather than leaking blocks.			 */			if (is_handle_ab

⌨️ 快捷键说明

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