diff options
Diffstat (limited to 'fs/ext4/ext4_jbd2.c')
| -rw-r--r-- | fs/ext4/ext4_jbd2.c | 179 | 
1 files changed, 167 insertions, 12 deletions
diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c index 6e272ef6ba9..0074e0d23d6 100644 --- a/fs/ext4/ext4_jbd2.c +++ b/fs/ext4/ext4_jbd2.c @@ -6,25 +6,154 @@  #include <trace/events/ext4.h> -int __ext4_journal_get_undo_access(const char *where, unsigned int line, -				   handle_t *handle, struct buffer_head *bh) +/* Just increment the non-pointer handle value */ +static handle_t *ext4_get_nojournal(void)  { -	int err = 0; +	handle_t *handle = current->journal_info; +	unsigned long ref_cnt = (unsigned long)handle; -	if (ext4_handle_valid(handle)) { -		err = jbd2_journal_get_undo_access(handle, bh); -		if (err) -			ext4_journal_abort_handle(where, line, __func__, bh, -						  handle, err); +	BUG_ON(ref_cnt >= EXT4_NOJOURNAL_MAX_REF_COUNT); + +	ref_cnt++; +	handle = (handle_t *)ref_cnt; + +	current->journal_info = handle; +	return handle; +} + + +/* Decrement the non-pointer handle value */ +static void ext4_put_nojournal(handle_t *handle) +{ +	unsigned long ref_cnt = (unsigned long)handle; + +	BUG_ON(ref_cnt == 0); + +	ref_cnt--; +	handle = (handle_t *)ref_cnt; + +	current->journal_info = handle; +} + +/* + * Wrappers for jbd2_journal_start/end. + */ +static int ext4_journal_check_start(struct super_block *sb) +{ +	journal_t *journal; + +	might_sleep(); +	if (sb->s_flags & MS_RDONLY) +		return -EROFS; +	WARN_ON(sb->s_writers.frozen == SB_FREEZE_COMPLETE); +	journal = EXT4_SB(sb)->s_journal; +	/* +	 * Special case here: if the journal has aborted behind our +	 * backs (eg. EIO in the commit thread), then we still need to +	 * take the FS itself readonly cleanly. +	 */ +	if (journal && is_journal_aborted(journal)) { +		ext4_abort(sb, "Detected aborted journal"); +		return -EROFS; +	} +	return 0; +} + +handle_t *__ext4_journal_start_sb(struct super_block *sb, unsigned int line, +				  int type, int blocks, int rsv_blocks) +{ +	journal_t *journal; +	int err; + +	trace_ext4_journal_start(sb, blocks, rsv_blocks, _RET_IP_); +	err = ext4_journal_check_start(sb); +	if (err < 0) +		return ERR_PTR(err); + +	journal = EXT4_SB(sb)->s_journal; +	if (!journal) +		return ext4_get_nojournal(); +	return jbd2__journal_start(journal, blocks, rsv_blocks, GFP_NOFS, +				   type, line); +} + +int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle) +{ +	struct super_block *sb; +	int err; +	int rc; + +	if (!ext4_handle_valid(handle)) { +		ext4_put_nojournal(handle); +		return 0;  	} +	sb = handle->h_transaction->t_journal->j_private; +	err = handle->h_err; +	rc = jbd2_journal_stop(handle); + +	if (!err) +		err = rc; +	if (err) +		__ext4_std_error(sb, where, line, err);  	return err;  } +handle_t *__ext4_journal_start_reserved(handle_t *handle, unsigned int line, +					int type) +{ +	struct super_block *sb; +	int err; + +	if (!ext4_handle_valid(handle)) +		return ext4_get_nojournal(); + +	sb = handle->h_journal->j_private; +	trace_ext4_journal_start_reserved(sb, handle->h_buffer_credits, +					  _RET_IP_); +	err = ext4_journal_check_start(sb); +	if (err < 0) { +		jbd2_journal_free_reserved(handle); +		return ERR_PTR(err); +	} + +	err = jbd2_journal_start_reserved(handle, type, line); +	if (err < 0) +		return ERR_PTR(err); +	return handle; +} + +static void ext4_journal_abort_handle(const char *caller, unsigned int line, +				      const char *err_fn, +				      struct buffer_head *bh, +				      handle_t *handle, int err) +{ +	char nbuf[16]; +	const char *errstr = ext4_decode_error(NULL, err, nbuf); + +	BUG_ON(!ext4_handle_valid(handle)); + +	if (bh) +		BUFFER_TRACE(bh, "abort"); + +	if (!handle->h_err) +		handle->h_err = err; + +	if (is_handle_aborted(handle)) +		return; + +	printk(KERN_ERR "EXT4-fs: %s:%d: aborting transaction: %s in %s\n", +	       caller, line, errstr, err_fn); + +	jbd2_journal_abort_handle(handle); +} +  int __ext4_journal_get_write_access(const char *where, unsigned int line,  				    handle_t *handle, struct buffer_head *bh)  {  	int err = 0; +	might_sleep(); +  	if (ext4_handle_valid(handle)) {  		err = jbd2_journal_get_write_access(handle, bh);  		if (err) @@ -121,11 +250,36 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,  {  	int err = 0; +	might_sleep(); + +	set_buffer_meta(bh); +	set_buffer_prio(bh);  	if (ext4_handle_valid(handle)) {  		err = jbd2_journal_dirty_metadata(handle, bh); -		if (err) -			ext4_journal_abort_handle(where, line, __func__, -						  bh, handle, err); +		/* Errors can only happen if there is a bug */ +		if (WARN_ON_ONCE(err)) { +			ext4_journal_abort_handle(where, line, __func__, bh, +						  handle, err); +			if (inode == NULL) { +				pr_err("EXT4: jbd2_journal_dirty_metadata " +				       "failed: handle type %u started at " +				       "line %u, credits %u/%u, errcode %d", +				       handle->h_type, +				       handle->h_line_no, +				       handle->h_requested_credits, +				       handle->h_buffer_credits, err); +				return err; +			} +			ext4_error_inode(inode, where, line, +					 bh->b_blocknr, +					 "journal_dirty_metadata failed: " +					 "handle type %u started at line %u, " +					 "credits %u/%u, errcode %d", +					 handle->h_type, +					 handle->h_line_no, +					 handle->h_requested_credits, +					 handle->h_buffer_credits, err); +		}  	} else {  		if (inode)  			mark_buffer_dirty_inode(bh, inode); @@ -155,12 +309,13 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line,  	struct buffer_head *bh = EXT4_SB(sb)->s_sbh;  	int err = 0; +	ext4_superblock_csum_set(sb);  	if (ext4_handle_valid(handle)) {  		err = jbd2_journal_dirty_metadata(handle, bh);  		if (err)  			ext4_journal_abort_handle(where, line, __func__,  						  bh, handle, err);  	} else -		sb->s_dirt = 1; +		mark_buffer_dirty(bh);  	return err;  }  | 
