Patchwork [RFC,03/30] ext4: snapshot hooks - inside JBD hooks

login
register
mail settings
Submitter Amir G.
Date May 9, 2011, 4:41 p.m.
Message ID <1304959308-11122-4-git-send-email-amir73il@users.sourceforge.net>
Download mbox | patch
Permalink /patch/94795/
State Deferred
Delegated to: Theodore Ts'o
Headers show

Comments

Amir G. - May 9, 2011, 4:41 p.m.
From: Amir Goldstein <amir73il@users.sf.net>

Before every metadata buffer write, the journal API is called,
namely, one of the ext4_journal_get_XXX_access() functions.
We use these journal hooks to call the snapshot API, namely
ext4_snapshot_get_XXX_access(), to COW the metadata buffer before
it is modified for the first time.

Signed-off-by: Amir Goldstein <amir73il@users.sf.net>
Signed-off-by: Yongqiang Yang <xiaoqiangnk@gmail.com>
---
 fs/ext4/ext4_jbd2.c   |    9 +++++++--
 fs/ext4/ext4_jbd2.h   |   15 +++++++++++----
 fs/ext4/extents.c     |    3 ++-
 fs/ext4/inode.c       |   22 +++++++++++++++-------
 fs/ext4/move_extent.c |    3 ++-
 5 files changed, 37 insertions(+), 15 deletions(-)
Lukas Czerner - June 6, 2011, 3:53 p.m.
On Mon, 9 May 2011, amir73il@users.sourceforge.net wrote:

> From: Amir Goldstein <amir73il@users.sf.net>
> 
> Before every metadata buffer write, the journal API is called,
> namely, one of the ext4_journal_get_XXX_access() functions.
> We use these journal hooks to call the snapshot API, namely
> ext4_snapshot_get_XXX_access(), to COW the metadata buffer before
> it is modified for the first time.
> 
> Signed-off-by: Amir Goldstein <amir73il@users.sf.net>
> Signed-off-by: Yongqiang Yang <xiaoqiangnk@gmail.com>
> ---
>  fs/ext4/ext4_jbd2.c   |    9 +++++++--
>  fs/ext4/ext4_jbd2.h   |   15 +++++++++++----
>  fs/ext4/extents.c     |    3 ++-
>  fs/ext4/inode.c       |   22 +++++++++++++++-------
>  fs/ext4/move_extent.c |    3 ++-
>  5 files changed, 37 insertions(+), 15 deletions(-)
> 
> diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c
> index 560020d..833969b 100644
> --- a/fs/ext4/ext4_jbd2.c
> +++ b/fs/ext4/ext4_jbd2.c
> @@ -23,13 +23,16 @@ int __ext4_journal_get_undo_access(const char *where, unsigned int line,
>  	return err;
>  }
>  
> -int __ext4_journal_get_write_access(const char *where, unsigned int line,
> -				    handle_t *handle, struct buffer_head *bh)
> +int __ext4_journal_get_write_access_inode(const char *where, unsigned int line,
> +					 handle_t *handle, struct inode *inode,
> +					 struct buffer_head *bh, int exclude)
>  {
>  	int err = 0;
>  
>  	if (ext4_handle_valid(handle)) {
>  		err = jbd2_journal_get_write_access(handle, bh);
> +		if (!err && !exclude)
> +			err = ext4_snapshot_get_write_access(handle, inode, bh);

Agh, this is not defined anywhere again. Actually it is defined only if
snapshot is not configured in. It is quite painful to review when half
of the code is missing really. And also something like this will break
bisecting for all of us. Is not there really the other way ?

Also, could you document the new parameters ?

Anyway:
	if (!err && !exclude && inode)

>  		if (err)
>  			ext4_journal_abort_handle(where, line, __func__, bh,
>  						  handle, err);
> @@ -111,6 +114,8 @@ int __ext4_journal_get_create_access(const char *where, unsigned int line,
>  
>  	if (ext4_handle_valid(handle)) {
>  		err = jbd2_journal_get_create_access(handle, bh);
> +		if (!err)
> +			err = ext4_snapshot_get_create_access(handle, bh);
>  		if (err)
>  			ext4_journal_abort_handle(where, line, __func__,
>  						  bh, handle, err);
> diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h
> index 8ffffb1..75662f7 100644
> --- a/fs/ext4/ext4_jbd2.h
> +++ b/fs/ext4/ext4_jbd2.h
> @@ -132,9 +132,9 @@ void ext4_journal_abort_handle(const char *caller, unsigned int line,
>  int __ext4_journal_get_undo_access(const char *where, unsigned int line,
>  				   handle_t *handle, struct buffer_head *bh);
>  
> -int __ext4_journal_get_write_access(const char *where, unsigned int line,
> -				    handle_t *handle, struct buffer_head *bh);
> -
> +int __ext4_journal_get_write_access_inode(const char *where, unsigned int line,
> +					 handle_t *handle, struct inode *inode,
> +					 struct buffer_head *bh, int exclude);
>  int __ext4_forget(const char *where, unsigned int line, handle_t *handle,
>  		  int is_metadata, struct inode *inode,
>  		  struct buffer_head *bh, ext4_fsblk_t blocknr);
> @@ -151,8 +151,15 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line,
>  
>  #define ext4_journal_get_undo_access(handle, bh) \
>  	__ext4_journal_get_undo_access(__func__, __LINE__, (handle), (bh))
> +#define ext4_journal_get_write_access_exclude(handle, bh) \
> +	__ext4_journal_get_write_access_inode(__func__, __LINE__, \
> +						 (handle), NULL, (bh), 1)
>  #define ext4_journal_get_write_access(handle, bh) \
> -	__ext4_journal_get_write_access(__func__, __LINE__, (handle), (bh))
> +	__ext4_journal_get_write_access_inode(__func__, __LINE__, \
> +						 (handle), NULL, (bh), 0)
> +#define ext4_journal_get_write_access_inode(handle, inode, bh) \
> +	__ext4_journal_get_write_access_inode(__func__, __LINE__, \
> +						(handle), (inode), (bh), 0)

Could you add some comments so everyone knows when to use the _exclude
helper and when not?

>  #define ext4_forget(handle, is_metadata, inode, bh, block_nr) \
>  	__ext4_forget(__func__, __LINE__, (handle), (is_metadata), (inode), \
>  		      (bh), (block_nr))
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 0c3ea93..c8cab3d 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -77,7 +77,8 @@ static int ext4_ext_get_access(handle_t *handle, struct inode *inode,
>  {
>  	if (path->p_bh) {
>  		/* path points to block */
> -		return ext4_journal_get_write_access(handle, path->p_bh);
> +		return ext4_journal_get_write_access_inode(handle,
> +					inode, path->p_bh);
>  	}
>  	/* path points to leaf/index in inode body */
>  	/* we use in-core data, no need to protect them */
> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
> index a597ff1..b848072 100644
> --- a/fs/ext4/inode.c
> +++ b/fs/ext4/inode.c
> @@ -874,7 +874,8 @@ static int ext4_splice_branch(handle_t *handle, struct inode *inode,
>  	 */
>  	if (where->bh) {
>  		BUFFER_TRACE(where->bh, "get_write_access");
> -		err = ext4_journal_get_write_access(handle, where->bh);
> +		err = ext4_journal_get_write_access_inode(handle, inode,
> +							   where->bh);
>  		if (err)
>  			goto err_out;
>  	}
> @@ -4172,7 +4173,8 @@ static int ext4_clear_blocks(handle_t *handle, struct inode *inode,
>  			goto out_err;
>  		if (bh) {
>  			BUFFER_TRACE(bh, "retaking write access");
> -			err = ext4_journal_get_write_access(handle, bh);
> +			err = ext4_journal_get_write_access_inode(handle,
> +								  inode, bh);
>  			if (unlikely(err))
>  				goto out_err;
>  		}
> @@ -4223,7 +4225,8 @@ static void ext4_free_data(handle_t *handle, struct inode *inode,
>  
>  	if (this_bh) {				/* For indirect block */
>  		BUFFER_TRACE(this_bh, "get_write_access");
> -		err = ext4_journal_get_write_access(handle, this_bh);
> +		err = ext4_journal_get_write_access_inode(handle, inode,
> +							   this_bh);
>  		/* Important: if we can't update the indirect pointers
>  		 * to the blocks, we can't free them. */
>  		if (err)
> @@ -4386,8 +4389,8 @@ static void ext4_free_branches(handle_t *handle, struct inode *inode,
>  				 * pointed to by an indirect block: journal it
>  				 */
>  				BUFFER_TRACE(parent_bh, "get_write_access");
> -				if (!ext4_journal_get_write_access(handle,
> -								   parent_bh)){
> +				if (!ext4_journal_get_write_access_inode(
> +					    handle, inode, parent_bh)){
>  					*p = 0;
>  					BUFFER_TRACE(parent_bh,
>  					"call ext4_handle_dirty_metadata");
> @@ -4759,9 +4762,14 @@ has_buffer:
>  
>  int ext4_get_inode_loc(struct inode *inode, struct ext4_iloc *iloc)
>  {
> -	/* We have all inode data except xattrs in memory here. */
> -	return __ext4_get_inode_loc(inode, iloc,
> +	int in_mem = (!EXT4_SNAPSHOTS(inode->i_sb) &&
>  		!ext4_test_inode_state(inode, EXT4_STATE_XATTR));
> +
> +	/*
> +	 * We have all inode's data except xattrs in memory here,
> +	 * but we must always read-in the entire inode block for COW.
> +	 */
> +	return __ext4_get_inode_loc(inode, iloc, in_mem);
>  }
>  
>  void ext4_set_inode_flags(struct inode *inode)
> diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
> index b9f3e78..ad5409a 100644
> --- a/fs/ext4/move_extent.c
> +++ b/fs/ext4/move_extent.c
> @@ -421,7 +421,8 @@ mext_insert_extents(handle_t *handle, struct inode *orig_inode,
>  
>  	if (depth) {
>  		/* Register to journal */
> -		ret = ext4_journal_get_write_access(handle, orig_path->p_bh);
> +		ret = ext4_journal_get_write_access_inode(handle,
> +					orig_inode, orig_path->p_bh);
>  		if (ret)
>  			return ret;
>  	}
>
Amir G. - June 6, 2011, 4:08 p.m.
On Mon, Jun 6, 2011 at 6:53 PM, Lukas Czerner <lczerner@redhat.com> wrote:
> On Mon, 9 May 2011, amir73il@users.sourceforge.net wrote:
>
>> From: Amir Goldstein <amir73il@users.sf.net>
>>
>> Before every metadata buffer write, the journal API is called,
>> namely, one of the ext4_journal_get_XXX_access() functions.
>> We use these journal hooks to call the snapshot API, namely
>> ext4_snapshot_get_XXX_access(), to COW the metadata buffer before
>> it is modified for the first time.
>>
>> Signed-off-by: Amir Goldstein <amir73il@users.sf.net>
>> Signed-off-by: Yongqiang Yang <xiaoqiangnk@gmail.com>
>> ---
>>  fs/ext4/ext4_jbd2.c   |    9 +++++++--
>>  fs/ext4/ext4_jbd2.h   |   15 +++++++++++----
>>  fs/ext4/extents.c     |    3 ++-
>>  fs/ext4/inode.c       |   22 +++++++++++++++-------
>>  fs/ext4/move_extent.c |    3 ++-
>>  5 files changed, 37 insertions(+), 15 deletions(-)
>>
>> diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c
>> index 560020d..833969b 100644
>> --- a/fs/ext4/ext4_jbd2.c
>> +++ b/fs/ext4/ext4_jbd2.c
>> @@ -23,13 +23,16 @@ int __ext4_journal_get_undo_access(const char *where, unsigned int line,
>>       return err;
>>  }
>>
>> -int __ext4_journal_get_write_access(const char *where, unsigned int line,
>> -                                 handle_t *handle, struct buffer_head *bh)
>> +int __ext4_journal_get_write_access_inode(const char *where, unsigned int line,
>> +                                      handle_t *handle, struct inode *inode,
>> +                                      struct buffer_head *bh, int exclude)
>>  {
>>       int err = 0;
>>
>>       if (ext4_handle_valid(handle)) {
>>               err = jbd2_journal_get_write_access(handle, bh);
>> +             if (!err && !exclude)
>> +                     err = ext4_snapshot_get_write_access(handle, inode, bh);
>
> Agh, this is not defined anywhere again. Actually it is defined only if
> snapshot is not configured in. It is quite painful to review when half
> of the code is missing really. And also something like this will break
> bisecting for all of us. Is not there really the other way ?

Hi Lukas,

Thanks for the review :-)
I just have time to one quick answer now.
I will address the rest of your comments later.

As you understand, this patch series was stripped off of snapshot
code, so it only compiles with EXT4_FS_SNAPSHOT not defined.

I have no problem, of course, sending out the full patch series,
but my intention was, as I wrote in the introduction, that the first
review would concentrate on the question:
Will these patches introduce significant changes when snapshot
code is not compiled or if the feature is off.

Would you like me to post the full (40) patches?
If you rather trying out the github online review,
I can also post the patches there.

Thanks,
Amir.

>
> Also, could you document the new parameters ?
>
> Anyway:
>        if (!err && !exclude && inode)
>
>>               if (err)
>>                       ext4_journal_abort_handle(where, line, __func__, bh,
>>                                                 handle, err);
>> @@ -111,6 +114,8 @@ int __ext4_journal_get_create_access(const char *where, unsigned int line,
>>
>>       if (ext4_handle_valid(handle)) {
>>               err = jbd2_journal_get_create_access(handle, bh);
>> +             if (!err)
>> +                     err = ext4_snapshot_get_create_access(handle, bh);
>>               if (err)
>>                       ext4_journal_abort_handle(where, line, __func__,
>>                                                 bh, handle, err);
>> diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h
>> index 8ffffb1..75662f7 100644
>> --- a/fs/ext4/ext4_jbd2.h
>> +++ b/fs/ext4/ext4_jbd2.h
>> @@ -132,9 +132,9 @@ void ext4_journal_abort_handle(const char *caller, unsigned int line,
>>  int __ext4_journal_get_undo_access(const char *where, unsigned int line,
>>                                  handle_t *handle, struct buffer_head *bh);
>>
>> -int __ext4_journal_get_write_access(const char *where, unsigned int line,
>> -                                 handle_t *handle, struct buffer_head *bh);
>> -
>> +int __ext4_journal_get_write_access_inode(const char *where, unsigned int line,
>> +                                      handle_t *handle, struct inode *inode,
>> +                                      struct buffer_head *bh, int exclude);
>>  int __ext4_forget(const char *where, unsigned int line, handle_t *handle,
>>                 int is_metadata, struct inode *inode,
>>                 struct buffer_head *bh, ext4_fsblk_t blocknr);
>> @@ -151,8 +151,15 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line,
>>
>>  #define ext4_journal_get_undo_access(handle, bh) \
>>       __ext4_journal_get_undo_access(__func__, __LINE__, (handle), (bh))
>> +#define ext4_journal_get_write_access_exclude(handle, bh) \
>> +     __ext4_journal_get_write_access_inode(__func__, __LINE__, \
>> +                                              (handle), NULL, (bh), 1)
>>  #define ext4_journal_get_write_access(handle, bh) \
>> -     __ext4_journal_get_write_access(__func__, __LINE__, (handle), (bh))
>> +     __ext4_journal_get_write_access_inode(__func__, __LINE__, \
>> +                                              (handle), NULL, (bh), 0)
>> +#define ext4_journal_get_write_access_inode(handle, inode, bh) \
>> +     __ext4_journal_get_write_access_inode(__func__, __LINE__, \
>> +                                             (handle), (inode), (bh), 0)
>
> Could you add some comments so everyone knows when to use the _exclude
> helper and when not?
>
>>  #define ext4_forget(handle, is_metadata, inode, bh, block_nr) \
>>       __ext4_forget(__func__, __LINE__, (handle), (is_metadata), (inode), \
>>                     (bh), (block_nr))
>> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
>> index 0c3ea93..c8cab3d 100644
>> --- a/fs/ext4/extents.c
>> +++ b/fs/ext4/extents.c
>> @@ -77,7 +77,8 @@ static int ext4_ext_get_access(handle_t *handle, struct inode *inode,
>>  {
>>       if (path->p_bh) {
>>               /* path points to block */
>> -             return ext4_journal_get_write_access(handle, path->p_bh);
>> +             return ext4_journal_get_write_access_inode(handle,
>> +                                     inode, path->p_bh);
>>       }
>>       /* path points to leaf/index in inode body */
>>       /* we use in-core data, no need to protect them */
>> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
>> index a597ff1..b848072 100644
>> --- a/fs/ext4/inode.c
>> +++ b/fs/ext4/inode.c
>> @@ -874,7 +874,8 @@ static int ext4_splice_branch(handle_t *handle, struct inode *inode,
>>        */
>>       if (where->bh) {
>>               BUFFER_TRACE(where->bh, "get_write_access");
>> -             err = ext4_journal_get_write_access(handle, where->bh);
>> +             err = ext4_journal_get_write_access_inode(handle, inode,
>> +                                                        where->bh);
>>               if (err)
>>                       goto err_out;
>>       }
>> @@ -4172,7 +4173,8 @@ static int ext4_clear_blocks(handle_t *handle, struct inode *inode,
>>                       goto out_err;
>>               if (bh) {
>>                       BUFFER_TRACE(bh, "retaking write access");
>> -                     err = ext4_journal_get_write_access(handle, bh);
>> +                     err = ext4_journal_get_write_access_inode(handle,
>> +                                                               inode, bh);
>>                       if (unlikely(err))
>>                               goto out_err;
>>               }
>> @@ -4223,7 +4225,8 @@ static void ext4_free_data(handle_t *handle, struct inode *inode,
>>
>>       if (this_bh) {                          /* For indirect block */
>>               BUFFER_TRACE(this_bh, "get_write_access");
>> -             err = ext4_journal_get_write_access(handle, this_bh);
>> +             err = ext4_journal_get_write_access_inode(handle, inode,
>> +                                                        this_bh);
>>               /* Important: if we can't update the indirect pointers
>>                * to the blocks, we can't free them. */
>>               if (err)
>> @@ -4386,8 +4389,8 @@ static void ext4_free_branches(handle_t *handle, struct inode *inode,
>>                                * pointed to by an indirect block: journal it
>>                                */
>>                               BUFFER_TRACE(parent_bh, "get_write_access");
>> -                             if (!ext4_journal_get_write_access(handle,
>> -                                                                parent_bh)){
>> +                             if (!ext4_journal_get_write_access_inode(
>> +                                         handle, inode, parent_bh)){
>>                                       *p = 0;
>>                                       BUFFER_TRACE(parent_bh,
>>                                       "call ext4_handle_dirty_metadata");
>> @@ -4759,9 +4762,14 @@ has_buffer:
>>
>>  int ext4_get_inode_loc(struct inode *inode, struct ext4_iloc *iloc)
>>  {
>> -     /* We have all inode data except xattrs in memory here. */
>> -     return __ext4_get_inode_loc(inode, iloc,
>> +     int in_mem = (!EXT4_SNAPSHOTS(inode->i_sb) &&
>>               !ext4_test_inode_state(inode, EXT4_STATE_XATTR));
>> +
>> +     /*
>> +      * We have all inode's data except xattrs in memory here,
>> +      * but we must always read-in the entire inode block for COW.
>> +      */
>> +     return __ext4_get_inode_loc(inode, iloc, in_mem);
>>  }
>>
>>  void ext4_set_inode_flags(struct inode *inode)
>> diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
>> index b9f3e78..ad5409a 100644
>> --- a/fs/ext4/move_extent.c
>> +++ b/fs/ext4/move_extent.c
>> @@ -421,7 +421,8 @@ mext_insert_extents(handle_t *handle, struct inode *orig_inode,
>>
>>       if (depth) {
>>               /* Register to journal */
>> -             ret = ext4_journal_get_write_access(handle, orig_path->p_bh);
>> +             ret = ext4_journal_get_write_access_inode(handle,
>> +                                     orig_inode, orig_path->p_bh);
>>               if (ret)
>>                       return ret;
>>       }
>>
>
> --
>
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Amir G. - June 6, 2011, 7:01 p.m.
On Mon, Jun 6, 2011 at 6:53 PM, Lukas Czerner <lczerner@redhat.com> wrote:
> On Mon, 9 May 2011, amir73il@users.sourceforge.net wrote:
>
>> From: Amir Goldstein <amir73il@users.sf.net>
>>
>> Before every metadata buffer write, the journal API is called,
>> namely, one of the ext4_journal_get_XXX_access() functions.
>> We use these journal hooks to call the snapshot API, namely
>> ext4_snapshot_get_XXX_access(), to COW the metadata buffer before
>> it is modified for the first time.
>>
>> Signed-off-by: Amir Goldstein <amir73il@users.sf.net>
>> Signed-off-by: Yongqiang Yang <xiaoqiangnk@gmail.com>
>> ---
>>  fs/ext4/ext4_jbd2.c   |    9 +++++++--
>>  fs/ext4/ext4_jbd2.h   |   15 +++++++++++----
>>  fs/ext4/extents.c     |    3 ++-
>>  fs/ext4/inode.c       |   22 +++++++++++++++-------
>>  fs/ext4/move_extent.c |    3 ++-
>>  5 files changed, 37 insertions(+), 15 deletions(-)
>>
>> diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c
>> index 560020d..833969b 100644
>> --- a/fs/ext4/ext4_jbd2.c
>> +++ b/fs/ext4/ext4_jbd2.c
>> @@ -23,13 +23,16 @@ int __ext4_journal_get_undo_access(const char *where, unsigned int line,
>>       return err;
>>  }
>>
>> -int __ext4_journal_get_write_access(const char *where, unsigned int line,
>> -                                 handle_t *handle, struct buffer_head *bh)
>> +int __ext4_journal_get_write_access_inode(const char *where, unsigned int line,
>> +                                      handle_t *handle, struct inode *inode,
>> +                                      struct buffer_head *bh, int exclude)
>>  {
>>       int err = 0;
>>
>>       if (ext4_handle_valid(handle)) {
>>               err = jbd2_journal_get_write_access(handle, bh);
>> +             if (!err && !exclude)
>> +                     err = ext4_snapshot_get_write_access(handle, inode, bh);
>
> Agh, this is not defined anywhere again. Actually it is defined only if
> snapshot is not configured in. It is quite painful to review when half
> of the code is missing really. And also something like this will break
> bisecting for all of us. Is not there really the other way ?

Oh, sorry for the pain, I was trying to make life easier for reviewers, but that
often results in the opposite outcome...
These 'core' patches are not meant for merging, just for initial review.
In the 'full' patches, ext4_snapshot_get_write_access() is defined for
both configurations.


>
> Also, could you document the new parameters ?
>
> Anyway:
>        if (!err && !exclude && inode)
>

It's documented in the snapshot hooks that inode can be NULL, so in the 'full'
patches that should be clear. sorry :-/
I promise to post the 'full' series soon.

>>               if (err)
>>                       ext4_journal_abort_handle(where, line, __func__, bh,
>>                                                 handle, err);
>> @@ -111,6 +114,8 @@ int __ext4_journal_get_create_access(const char *where, unsigned int line,
>>
>>       if (ext4_handle_valid(handle)) {
>>               err = jbd2_journal_get_create_access(handle, bh);
>> +             if (!err)
>> +                     err = ext4_snapshot_get_create_access(handle, bh);
>>               if (err)
>>                       ext4_journal_abort_handle(where, line, __func__,
>>                                                 bh, handle, err);
>> diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h
>> index 8ffffb1..75662f7 100644
>> --- a/fs/ext4/ext4_jbd2.h
>> +++ b/fs/ext4/ext4_jbd2.h
>> @@ -132,9 +132,9 @@ void ext4_journal_abort_handle(const char *caller, unsigned int line,
>>  int __ext4_journal_get_undo_access(const char *where, unsigned int line,
>>                                  handle_t *handle, struct buffer_head *bh);
>>
>> -int __ext4_journal_get_write_access(const char *where, unsigned int line,
>> -                                 handle_t *handle, struct buffer_head *bh);
>> -
>> +int __ext4_journal_get_write_access_inode(const char *where, unsigned int line,
>> +                                      handle_t *handle, struct inode *inode,
>> +                                      struct buffer_head *bh, int exclude);
>>  int __ext4_forget(const char *where, unsigned int line, handle_t *handle,
>>                 int is_metadata, struct inode *inode,
>>                 struct buffer_head *bh, ext4_fsblk_t blocknr);
>> @@ -151,8 +151,15 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line,
>>
>>  #define ext4_journal_get_undo_access(handle, bh) \
>>       __ext4_journal_get_undo_access(__func__, __LINE__, (handle), (bh))
>> +#define ext4_journal_get_write_access_exclude(handle, bh) \
>> +     __ext4_journal_get_write_access_inode(__func__, __LINE__, \
>> +                                              (handle), NULL, (bh), 1)
>>  #define ext4_journal_get_write_access(handle, bh) \
>> -     __ext4_journal_get_write_access(__func__, __LINE__, (handle), (bh))
>> +     __ext4_journal_get_write_access_inode(__func__, __LINE__, \
>> +                                              (handle), NULL, (bh), 0)
>> +#define ext4_journal_get_write_access_inode(handle, inode, bh) \
>> +     __ext4_journal_get_write_access_inode(__func__, __LINE__, \
>> +                                             (handle), (inode), (bh), 0)
>
> Could you add some comments so everyone knows when to use the _exclude
> helper and when not?

Will do. nobody should use it except for exclude bitmap write access.
I should change the name to get_exclude_bitmap_access to make that
more clear.

>
>>  #define ext4_forget(handle, is_metadata, inode, bh, block_nr) \
>>       __ext4_forget(__func__, __LINE__, (handle), (is_metadata), (inode), \
>>                     (bh), (block_nr))
>> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
>> index 0c3ea93..c8cab3d 100644
>> --- a/fs/ext4/extents.c
>> +++ b/fs/ext4/extents.c
>> @@ -77,7 +77,8 @@ static int ext4_ext_get_access(handle_t *handle, struct inode *inode,
>>  {
>>       if (path->p_bh) {
>>               /* path points to block */
>> -             return ext4_journal_get_write_access(handle, path->p_bh);
>> +             return ext4_journal_get_write_access_inode(handle,
>> +                                     inode, path->p_bh);
>>       }
>>       /* path points to leaf/index in inode body */
>>       /* we use in-core data, no need to protect them */
>> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
>> index a597ff1..b848072 100644
>> --- a/fs/ext4/inode.c
>> +++ b/fs/ext4/inode.c
>> @@ -874,7 +874,8 @@ static int ext4_splice_branch(handle_t *handle, struct inode *inode,
>>        */
>>       if (where->bh) {
>>               BUFFER_TRACE(where->bh, "get_write_access");
>> -             err = ext4_journal_get_write_access(handle, where->bh);
>> +             err = ext4_journal_get_write_access_inode(handle, inode,
>> +                                                        where->bh);
>>               if (err)
>>                       goto err_out;
>>       }
>> @@ -4172,7 +4173,8 @@ static int ext4_clear_blocks(handle_t *handle, struct inode *inode,
>>                       goto out_err;
>>               if (bh) {
>>                       BUFFER_TRACE(bh, "retaking write access");
>> -                     err = ext4_journal_get_write_access(handle, bh);
>> +                     err = ext4_journal_get_write_access_inode(handle,
>> +                                                               inode, bh);
>>                       if (unlikely(err))
>>                               goto out_err;
>>               }
>> @@ -4223,7 +4225,8 @@ static void ext4_free_data(handle_t *handle, struct inode *inode,
>>
>>       if (this_bh) {                          /* For indirect block */
>>               BUFFER_TRACE(this_bh, "get_write_access");
>> -             err = ext4_journal_get_write_access(handle, this_bh);
>> +             err = ext4_journal_get_write_access_inode(handle, inode,
>> +                                                        this_bh);
>>               /* Important: if we can't update the indirect pointers
>>                * to the blocks, we can't free them. */
>>               if (err)
>> @@ -4386,8 +4389,8 @@ static void ext4_free_branches(handle_t *handle, struct inode *inode,
>>                                * pointed to by an indirect block: journal it
>>                                */
>>                               BUFFER_TRACE(parent_bh, "get_write_access");
>> -                             if (!ext4_journal_get_write_access(handle,
>> -                                                                parent_bh)){
>> +                             if (!ext4_journal_get_write_access_inode(
>> +                                         handle, inode, parent_bh)){
>>                                       *p = 0;
>>                                       BUFFER_TRACE(parent_bh,
>>                                       "call ext4_handle_dirty_metadata");
>> @@ -4759,9 +4762,14 @@ has_buffer:
>>
>>  int ext4_get_inode_loc(struct inode *inode, struct ext4_iloc *iloc)
>>  {
>> -     /* We have all inode data except xattrs in memory here. */
>> -     return __ext4_get_inode_loc(inode, iloc,
>> +     int in_mem = (!EXT4_SNAPSHOTS(inode->i_sb) &&
>>               !ext4_test_inode_state(inode, EXT4_STATE_XATTR));
>> +
>> +     /*
>> +      * We have all inode's data except xattrs in memory here,
>> +      * but we must always read-in the entire inode block for COW.
>> +      */
>> +     return __ext4_get_inode_loc(inode, iloc, in_mem);
>>  }
>>
>>  void ext4_set_inode_flags(struct inode *inode)
>> diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
>> index b9f3e78..ad5409a 100644
>> --- a/fs/ext4/move_extent.c
>> +++ b/fs/ext4/move_extent.c
>> @@ -421,7 +421,8 @@ mext_insert_extents(handle_t *handle, struct inode *orig_inode,
>>
>>       if (depth) {
>>               /* Register to journal */
>> -             ret = ext4_journal_get_write_access(handle, orig_path->p_bh);
>> +             ret = ext4_journal_get_write_access_inode(handle,
>> +                                     orig_inode, orig_path->p_bh);
>>               if (ret)
>>                       return ret;
>>       }
>>
>
> --
>
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch

diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c
index 560020d..833969b 100644
--- a/fs/ext4/ext4_jbd2.c
+++ b/fs/ext4/ext4_jbd2.c
@@ -23,13 +23,16 @@  int __ext4_journal_get_undo_access(const char *where, unsigned int line,
 	return err;
 }
 
-int __ext4_journal_get_write_access(const char *where, unsigned int line,
-				    handle_t *handle, struct buffer_head *bh)
+int __ext4_journal_get_write_access_inode(const char *where, unsigned int line,
+					 handle_t *handle, struct inode *inode,
+					 struct buffer_head *bh, int exclude)
 {
 	int err = 0;
 
 	if (ext4_handle_valid(handle)) {
 		err = jbd2_journal_get_write_access(handle, bh);
+		if (!err && !exclude)
+			err = ext4_snapshot_get_write_access(handle, inode, bh);
 		if (err)
 			ext4_journal_abort_handle(where, line, __func__, bh,
 						  handle, err);
@@ -111,6 +114,8 @@  int __ext4_journal_get_create_access(const char *where, unsigned int line,
 
 	if (ext4_handle_valid(handle)) {
 		err = jbd2_journal_get_create_access(handle, bh);
+		if (!err)
+			err = ext4_snapshot_get_create_access(handle, bh);
 		if (err)
 			ext4_journal_abort_handle(where, line, __func__,
 						  bh, handle, err);
diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h
index 8ffffb1..75662f7 100644
--- a/fs/ext4/ext4_jbd2.h
+++ b/fs/ext4/ext4_jbd2.h
@@ -132,9 +132,9 @@  void ext4_journal_abort_handle(const char *caller, unsigned int line,
 int __ext4_journal_get_undo_access(const char *where, unsigned int line,
 				   handle_t *handle, struct buffer_head *bh);
 
-int __ext4_journal_get_write_access(const char *where, unsigned int line,
-				    handle_t *handle, struct buffer_head *bh);
-
+int __ext4_journal_get_write_access_inode(const char *where, unsigned int line,
+					 handle_t *handle, struct inode *inode,
+					 struct buffer_head *bh, int exclude);
 int __ext4_forget(const char *where, unsigned int line, handle_t *handle,
 		  int is_metadata, struct inode *inode,
 		  struct buffer_head *bh, ext4_fsblk_t blocknr);
@@ -151,8 +151,15 @@  int __ext4_handle_dirty_super(const char *where, unsigned int line,
 
 #define ext4_journal_get_undo_access(handle, bh) \
 	__ext4_journal_get_undo_access(__func__, __LINE__, (handle), (bh))
+#define ext4_journal_get_write_access_exclude(handle, bh) \
+	__ext4_journal_get_write_access_inode(__func__, __LINE__, \
+						 (handle), NULL, (bh), 1)
 #define ext4_journal_get_write_access(handle, bh) \
-	__ext4_journal_get_write_access(__func__, __LINE__, (handle), (bh))
+	__ext4_journal_get_write_access_inode(__func__, __LINE__, \
+						 (handle), NULL, (bh), 0)
+#define ext4_journal_get_write_access_inode(handle, inode, bh) \
+	__ext4_journal_get_write_access_inode(__func__, __LINE__, \
+						(handle), (inode), (bh), 0)
 #define ext4_forget(handle, is_metadata, inode, bh, block_nr) \
 	__ext4_forget(__func__, __LINE__, (handle), (is_metadata), (inode), \
 		      (bh), (block_nr))
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 0c3ea93..c8cab3d 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -77,7 +77,8 @@  static int ext4_ext_get_access(handle_t *handle, struct inode *inode,
 {
 	if (path->p_bh) {
 		/* path points to block */
-		return ext4_journal_get_write_access(handle, path->p_bh);
+		return ext4_journal_get_write_access_inode(handle,
+					inode, path->p_bh);
 	}
 	/* path points to leaf/index in inode body */
 	/* we use in-core data, no need to protect them */
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index a597ff1..b848072 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -874,7 +874,8 @@  static int ext4_splice_branch(handle_t *handle, struct inode *inode,
 	 */
 	if (where->bh) {
 		BUFFER_TRACE(where->bh, "get_write_access");
-		err = ext4_journal_get_write_access(handle, where->bh);
+		err = ext4_journal_get_write_access_inode(handle, inode,
+							   where->bh);
 		if (err)
 			goto err_out;
 	}
@@ -4172,7 +4173,8 @@  static int ext4_clear_blocks(handle_t *handle, struct inode *inode,
 			goto out_err;
 		if (bh) {
 			BUFFER_TRACE(bh, "retaking write access");
-			err = ext4_journal_get_write_access(handle, bh);
+			err = ext4_journal_get_write_access_inode(handle,
+								  inode, bh);
 			if (unlikely(err))
 				goto out_err;
 		}
@@ -4223,7 +4225,8 @@  static void ext4_free_data(handle_t *handle, struct inode *inode,
 
 	if (this_bh) {				/* For indirect block */
 		BUFFER_TRACE(this_bh, "get_write_access");
-		err = ext4_journal_get_write_access(handle, this_bh);
+		err = ext4_journal_get_write_access_inode(handle, inode,
+							   this_bh);
 		/* Important: if we can't update the indirect pointers
 		 * to the blocks, we can't free them. */
 		if (err)
@@ -4386,8 +4389,8 @@  static void ext4_free_branches(handle_t *handle, struct inode *inode,
 				 * pointed to by an indirect block: journal it
 				 */
 				BUFFER_TRACE(parent_bh, "get_write_access");
-				if (!ext4_journal_get_write_access(handle,
-								   parent_bh)){
+				if (!ext4_journal_get_write_access_inode(
+					    handle, inode, parent_bh)){
 					*p = 0;
 					BUFFER_TRACE(parent_bh,
 					"call ext4_handle_dirty_metadata");
@@ -4759,9 +4762,14 @@  has_buffer:
 
 int ext4_get_inode_loc(struct inode *inode, struct ext4_iloc *iloc)
 {
-	/* We have all inode data except xattrs in memory here. */
-	return __ext4_get_inode_loc(inode, iloc,
+	int in_mem = (!EXT4_SNAPSHOTS(inode->i_sb) &&
 		!ext4_test_inode_state(inode, EXT4_STATE_XATTR));
+
+	/*
+	 * We have all inode's data except xattrs in memory here,
+	 * but we must always read-in the entire inode block for COW.
+	 */
+	return __ext4_get_inode_loc(inode, iloc, in_mem);
 }
 
 void ext4_set_inode_flags(struct inode *inode)
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
index b9f3e78..ad5409a 100644
--- a/fs/ext4/move_extent.c
+++ b/fs/ext4/move_extent.c
@@ -421,7 +421,8 @@  mext_insert_extents(handle_t *handle, struct inode *orig_inode,
 
 	if (depth) {
 		/* Register to journal */
-		ret = ext4_journal_get_write_access(handle, orig_path->p_bh);
+		ret = ext4_journal_get_write_access_inode(handle,
+					orig_inode, orig_path->p_bh);
 		if (ret)
 			return ret;
 	}