diff options
Diffstat (limited to 'fs/ext4/inode.c')
| -rw-r--r-- | fs/ext4/inode.c | 198 | 
1 files changed, 150 insertions, 48 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 3c600f02673f..c774bdc22759 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -892,7 +892,7 @@ static int ext4_dio_get_block_unwritten_async(struct inode *inode,  /*   * Get block function for non-AIO DIO writes when we create unwritten extent if   * blocks are not allocated yet. The extent will be converted to written - * after IO is complete from ext4_ext_direct_IO() function. + * after IO is complete by ext4_direct_IO_write().   */  static int ext4_dio_get_block_unwritten_sync(struct inode *inode,  		sector_t iblock, struct buffer_head *bh_result,	int create) @@ -907,7 +907,7 @@ static int ext4_dio_get_block_unwritten_sync(struct inode *inode,  	/*  	 * Mark inode as having pending DIO writes to unwritten extents. -	 * ext4_ext_direct_IO() checks this flag and converts extents to +	 * ext4_direct_IO_write() checks this flag and converts extents to  	 * written.  	 */  	if (!ret && buffer_unwritten(bh_result)) @@ -1015,6 +1015,50 @@ struct buffer_head *ext4_bread(handle_t *handle, struct inode *inode,  	return ERR_PTR(-EIO);  } +/* Read a contiguous batch of blocks. */ +int ext4_bread_batch(struct inode *inode, ext4_lblk_t block, int bh_count, +		     bool wait, struct buffer_head **bhs) +{ +	int i, err; + +	for (i = 0; i < bh_count; i++) { +		bhs[i] = ext4_getblk(NULL, inode, block + i, 0 /* map_flags */); +		if (IS_ERR(bhs[i])) { +			err = PTR_ERR(bhs[i]); +			bh_count = i; +			goto out_brelse; +		} +	} + +	for (i = 0; i < bh_count; i++) +		/* Note that NULL bhs[i] is valid because of holes. */ +		if (bhs[i] && !buffer_uptodate(bhs[i])) +			ll_rw_block(REQ_OP_READ, REQ_META | REQ_PRIO, 1, +				    &bhs[i]); + +	if (!wait) +		return 0; + +	for (i = 0; i < bh_count; i++) +		if (bhs[i]) +			wait_on_buffer(bhs[i]); + +	for (i = 0; i < bh_count; i++) { +		if (bhs[i] && !buffer_uptodate(bhs[i])) { +			err = -EIO; +			goto out_brelse; +		} +	} +	return 0; + +out_brelse: +	for (i = 0; i < bh_count; i++) { +		brelse(bhs[i]); +		bhs[i] = NULL; +	} +	return err; +} +  int ext4_walk_page_buffers(handle_t *handle,  			   struct buffer_head *head,  			   unsigned from, @@ -5658,22 +5702,16 @@ ext4_reserve_inode_write(handle_t *handle, struct inode *inode,  	return err;  } -/* - * Expand an inode by new_extra_isize bytes. - * Returns 0 on success or negative error number on failure. - */ -static int ext4_expand_extra_isize(struct inode *inode, -				   unsigned int new_extra_isize, -				   struct ext4_iloc iloc, -				   handle_t *handle) +static int __ext4_expand_extra_isize(struct inode *inode, +				     unsigned int new_extra_isize, +				     struct ext4_iloc *iloc, +				     handle_t *handle, int *no_expand)  {  	struct ext4_inode *raw_inode;  	struct ext4_xattr_ibody_header *header; +	int error; -	if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) -		return 0; - -	raw_inode = ext4_raw_inode(&iloc); +	raw_inode = ext4_raw_inode(iloc);  	header = IHDR(inode, raw_inode); @@ -5688,8 +5726,98 @@ static int ext4_expand_extra_isize(struct inode *inode,  	}  	/* try to expand with EAs present */ -	return ext4_expand_extra_isize_ea(inode, new_extra_isize, -					  raw_inode, handle); +	error = ext4_expand_extra_isize_ea(inode, new_extra_isize, +					   raw_inode, handle); +	if (error) { +		/* +		 * Inode size expansion failed; don't try again +		 */ +		*no_expand = 1; +	} + +	return error; +} + +/* + * Expand an inode by new_extra_isize bytes. + * Returns 0 on success or negative error number on failure. + */ +static int ext4_try_to_expand_extra_isize(struct inode *inode, +					  unsigned int new_extra_isize, +					  struct ext4_iloc iloc, +					  handle_t *handle) +{ +	int no_expand; +	int error; + +	if (ext4_test_inode_state(inode, EXT4_STATE_NO_EXPAND)) +		return -EOVERFLOW; + +	/* +	 * In nojournal mode, we can immediately attempt to expand +	 * the inode.  When journaled, we first need to obtain extra +	 * buffer credits since we may write into the EA block +	 * with this same handle. If journal_extend fails, then it will +	 * only result in a minor loss of functionality for that inode. +	 * If this is felt to be critical, then e2fsck should be run to +	 * force a large enough s_min_extra_isize. +	 */ +	if (ext4_handle_valid(handle) && +	    jbd2_journal_extend(handle, +				EXT4_DATA_TRANS_BLOCKS(inode->i_sb)) != 0) +		return -ENOSPC; + +	if (ext4_write_trylock_xattr(inode, &no_expand) == 0) +		return -EBUSY; + +	error = __ext4_expand_extra_isize(inode, new_extra_isize, &iloc, +					  handle, &no_expand); +	ext4_write_unlock_xattr(inode, &no_expand); + +	return error; +} + +int ext4_expand_extra_isize(struct inode *inode, +			    unsigned int new_extra_isize, +			    struct ext4_iloc *iloc) +{ +	handle_t *handle; +	int no_expand; +	int error, rc; + +	if (ext4_test_inode_state(inode, EXT4_STATE_NO_EXPAND)) { +		brelse(iloc->bh); +		return -EOVERFLOW; +	} + +	handle = ext4_journal_start(inode, EXT4_HT_INODE, +				    EXT4_DATA_TRANS_BLOCKS(inode->i_sb)); +	if (IS_ERR(handle)) { +		error = PTR_ERR(handle); +		brelse(iloc->bh); +		return error; +	} + +	ext4_write_lock_xattr(inode, &no_expand); + +	BUFFER_TRACE(iloc.bh, "get_write_access"); +	error = ext4_journal_get_write_access(handle, iloc->bh); +	if (error) { +		brelse(iloc->bh); +		goto out_stop; +	} + +	error = __ext4_expand_extra_isize(inode, new_extra_isize, iloc, +					  handle, &no_expand); + +	rc = ext4_mark_iloc_dirty(handle, inode, iloc); +	if (!error) +		error = rc; + +	ext4_write_unlock_xattr(inode, &no_expand); +out_stop: +	ext4_journal_stop(handle); +	return error;  }  /* @@ -5709,44 +5837,18 @@ int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode)  {  	struct ext4_iloc iloc;  	struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); -	static unsigned int mnt_count; -	int err, ret; +	int err;  	might_sleep();  	trace_ext4_mark_inode_dirty(inode, _RET_IP_);  	err = ext4_reserve_inode_write(handle, inode, &iloc);  	if (err)  		return err; -	if (EXT4_I(inode)->i_extra_isize < sbi->s_want_extra_isize && -	    !ext4_test_inode_state(inode, EXT4_STATE_NO_EXPAND)) { -		/* -		 * In nojournal mode, we can immediately attempt to expand -		 * the inode.  When journaled, we first need to obtain extra -		 * buffer credits since we may write into the EA block -		 * with this same handle. If journal_extend fails, then it will -		 * only result in a minor loss of functionality for that inode. -		 * If this is felt to be critical, then e2fsck should be run to -		 * force a large enough s_min_extra_isize. -		 */ -		if (!ext4_handle_valid(handle) || -		    jbd2_journal_extend(handle, -			     EXT4_DATA_TRANS_BLOCKS(inode->i_sb)) == 0) { -			ret = ext4_expand_extra_isize(inode, -						      sbi->s_want_extra_isize, -						      iloc, handle); -			if (ret) { -				if (mnt_count != -					le16_to_cpu(sbi->s_es->s_mnt_count)) { -					ext4_warning(inode->i_sb, -					"Unable to expand inode %lu. Delete" -					" some EAs or run e2fsck.", -					inode->i_ino); -					mnt_count = -					  le16_to_cpu(sbi->s_es->s_mnt_count); -				} -			} -		} -	} + +	if (EXT4_I(inode)->i_extra_isize < sbi->s_want_extra_isize) +		ext4_try_to_expand_extra_isize(inode, sbi->s_want_extra_isize, +					       iloc, handle); +  	return ext4_mark_iloc_dirty(handle, inode, &iloc);  }  |