public inbox for linux-ext4@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] ext4: mark FC as ineligible using an handle
@ 2024-09-19  9:38 Luis Henriques (SUSE)
  2024-09-19  9:38 ` [PATCH 1/2] ext4: use handle to mark fc as ineligible in __track_dentry_update() Luis Henriques (SUSE)
  2024-09-19  9:38 ` [PATCH 2/2] ext4: mark fc as ineligible using an handle in ext4_xattr_set() Luis Henriques (SUSE)
  0 siblings, 2 replies; 6+ messages in thread
From: Luis Henriques (SUSE) @ 2024-09-19  9:38 UTC (permalink / raw)
  To: Theodore Ts'o, Andreas Dilger, Jan Kara, Harshad Shirwadkar
  Cc: linux-ext4, linux-kernel, Luis Henriques (SUSE)

Hi!

The following patches were suggested by Jan Kara[1] some time ago.  Basically,
they try to address a race that exists in several places, where function
ext4_fc_mark_ineligible() is called with a NULL handle: in these cases, marking
the filesystem as ineligible may effectively happen _after_ the fast-commit is
done.

There are several places where this happen, and the two patches that follow try
to address some of them.  The first patch fixes the calls that exist in
__track_dentry_update().

The second patch _partially_ fixes the call in ext4_xattr_set() -- this is
because it may fail to obtain an handle.  And, to be honest, I'm not sure what
to do in this case: try again to obtain a new handle just for marking the fc,
with a lower value for credits? or leave it as-is, with the NULL?

The other two missing callers are the ioctl for filesystem resize and
ext4_evict_inode().

[1] https://lore.kernel.org/all/20240724101504.e2t4pvgw6td7rrmm@quack3/

Luis Henriques (SUSE) (2):
  ext4: use handle to mark fc as ineligible in __track_dentry_update()
  ext4: mark fc as ineligible using an handle in ext4_xattr_set()

 fs/ext4/fast_commit.c | 19 +++++++++++--------
 fs/ext4/xattr.c       |  5 ++++-
 2 files changed, 15 insertions(+), 9 deletions(-)


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/2] ext4: use handle to mark fc as ineligible in __track_dentry_update()
  2024-09-19  9:38 [PATCH 0/2] ext4: mark FC as ineligible using an handle Luis Henriques (SUSE)
@ 2024-09-19  9:38 ` Luis Henriques (SUSE)
  2024-09-19 21:47   ` Jan Kara
  2024-09-19  9:38 ` [PATCH 2/2] ext4: mark fc as ineligible using an handle in ext4_xattr_set() Luis Henriques (SUSE)
  1 sibling, 1 reply; 6+ messages in thread
From: Luis Henriques (SUSE) @ 2024-09-19  9:38 UTC (permalink / raw)
  To: Theodore Ts'o, Andreas Dilger, Jan Kara, Harshad Shirwadkar
  Cc: linux-ext4, linux-kernel, Luis Henriques (SUSE)

Calling ext4_fc_mark_ineligible() with a NULL handle is racy and may result
in a fast-commit being done before the filesystem is effectively marked as
ineligible.  This patch fixes the calls to this function in
__track_dentry_update() by adding an extra parameter to the callback used in
ext4_fc_track_template().

Suggested-by: Jan Kara <jack@suse.cz>
Signed-off-by: Luis Henriques (SUSE) <luis.henriques@linux.dev>
---
 fs/ext4/fast_commit.c | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c
index 3926a05eceee..c330efd771d1 100644
--- a/fs/ext4/fast_commit.c
+++ b/fs/ext4/fast_commit.c
@@ -372,7 +372,7 @@ void ext4_fc_mark_ineligible(struct super_block *sb, int reason, handle_t *handl
  */
 static int ext4_fc_track_template(
 	handle_t *handle, struct inode *inode,
-	int (*__fc_track_fn)(struct inode *, void *, bool),
+	int (*__fc_track_fn)(handle_t *handle, struct inode *, void *, bool),
 	void *args, int enqueue)
 {
 	bool update = false;
@@ -389,7 +389,7 @@ static int ext4_fc_track_template(
 		ext4_fc_reset_inode(inode);
 		ei->i_sync_tid = tid;
 	}
-	ret = __fc_track_fn(inode, args, update);
+	ret = __fc_track_fn(handle, inode, args, update);
 	mutex_unlock(&ei->i_fc_lock);
 
 	if (!enqueue)
@@ -413,7 +413,8 @@ struct __track_dentry_update_args {
 };
 
 /* __track_fn for directory entry updates. Called with ei->i_fc_lock. */
-static int __track_dentry_update(struct inode *inode, void *arg, bool update)
+static int __track_dentry_update(handle_t *handle, struct inode *inode,
+				 void *arg, bool update)
 {
 	struct ext4_fc_dentry_update *node;
 	struct ext4_inode_info *ei = EXT4_I(inode);
@@ -428,14 +429,14 @@ static int __track_dentry_update(struct inode *inode, void *arg, bool update)
 
 	if (IS_ENCRYPTED(dir)) {
 		ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_ENCRYPTED_FILENAME,
-					NULL);
+					handle);
 		mutex_lock(&ei->i_fc_lock);
 		return -EOPNOTSUPP;
 	}
 
 	node = kmem_cache_alloc(ext4_fc_dentry_cachep, GFP_NOFS);
 	if (!node) {
-		ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_NOMEM, NULL);
+		ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_NOMEM, handle);
 		mutex_lock(&ei->i_fc_lock);
 		return -ENOMEM;
 	}
@@ -447,7 +448,7 @@ static int __track_dentry_update(struct inode *inode, void *arg, bool update)
 		node->fcd_name.name = kmalloc(dentry->d_name.len, GFP_NOFS);
 		if (!node->fcd_name.name) {
 			kmem_cache_free(ext4_fc_dentry_cachep, node);
-			ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_NOMEM, NULL);
+			ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_NOMEM, handle);
 			mutex_lock(&ei->i_fc_lock);
 			return -ENOMEM;
 		}
@@ -569,7 +570,8 @@ void ext4_fc_track_create(handle_t *handle, struct dentry *dentry)
 }
 
 /* __track_fn for inode tracking */
-static int __track_inode(struct inode *inode, void *arg, bool update)
+static int __track_inode(handle_t *handle, struct inode *inode, void *arg,
+			 bool update)
 {
 	if (update)
 		return -EEXIST;
@@ -607,7 +609,8 @@ struct __track_range_args {
 };
 
 /* __track_fn for tracking data updates */
-static int __track_range(struct inode *inode, void *arg, bool update)
+static int __track_range(handle_t *handle, struct inode *inode, void *arg,
+			 bool update)
 {
 	struct ext4_inode_info *ei = EXT4_I(inode);
 	ext4_lblk_t oldstart;

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/2] ext4: mark fc as ineligible using an handle in ext4_xattr_set()
  2024-09-19  9:38 [PATCH 0/2] ext4: mark FC as ineligible using an handle Luis Henriques (SUSE)
  2024-09-19  9:38 ` [PATCH 1/2] ext4: use handle to mark fc as ineligible in __track_dentry_update() Luis Henriques (SUSE)
@ 2024-09-19  9:38 ` Luis Henriques (SUSE)
  2024-09-19 21:47   ` Jan Kara
  1 sibling, 1 reply; 6+ messages in thread
From: Luis Henriques (SUSE) @ 2024-09-19  9:38 UTC (permalink / raw)
  To: Theodore Ts'o, Andreas Dilger, Jan Kara, Harshad Shirwadkar
  Cc: linux-ext4, linux-kernel, Luis Henriques (SUSE)

Calling ext4_fc_mark_ineligible() with a NULL handle is racy and may result
in a fast-commit being done before the filesystem is effectively marked as
ineligible.  This patch reduces the risk of this happening in function
ext4_xattr_set() by using an handle if one is available.

Suggested-by: Jan Kara <jack@suse.cz>
Signed-off-by: Luis Henriques (SUSE) <luis.henriques@linux.dev>
---
 fs/ext4/xattr.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index 46ce2f21fef9..dbe4d11cd332 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -2554,11 +2554,15 @@ ext4_xattr_set(struct inode *inode, int name_index, const char *name,
 	handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits);
 	if (IS_ERR(handle)) {
 		error = PTR_ERR(handle);
+		ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR,
+					NULL);
 	} else {
 		int error2;
 
 		error = ext4_xattr_set_handle(handle, inode, name_index, name,
 					      value, value_len, flags);
+		ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR,
+					handle);
 		error2 = ext4_journal_stop(handle);
 		if (error == -ENOSPC &&
 		    ext4_should_retry_alloc(sb, &retries))
@@ -2566,7 +2570,6 @@ ext4_xattr_set(struct inode *inode, int name_index, const char *name,
 		if (error == 0)
 			error = error2;
 	}
-	ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR, NULL);
 
 	return error;
 }

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 2/2] ext4: mark fc as ineligible using an handle in ext4_xattr_set()
  2024-09-19  9:38 ` [PATCH 2/2] ext4: mark fc as ineligible using an handle in ext4_xattr_set() Luis Henriques (SUSE)
@ 2024-09-19 21:47   ` Jan Kara
  2024-09-20  9:35     ` Luis Henriques
  0 siblings, 1 reply; 6+ messages in thread
From: Jan Kara @ 2024-09-19 21:47 UTC (permalink / raw)
  To: Luis Henriques (SUSE)
  Cc: Theodore Ts'o, Andreas Dilger, Jan Kara, Harshad Shirwadkar,
	linux-ext4, linux-kernel

On Thu 19-09-24 10:38:48, Luis Henriques (SUSE) wrote:
> Calling ext4_fc_mark_ineligible() with a NULL handle is racy and may result
> in a fast-commit being done before the filesystem is effectively marked as
> ineligible.  This patch reduces the risk of this happening in function
> ext4_xattr_set() by using an handle if one is available.
> 
> Suggested-by: Jan Kara <jack@suse.cz>
> Signed-off-by: Luis Henriques (SUSE) <luis.henriques@linux.dev>

One comment below:

> diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
> index 46ce2f21fef9..dbe4d11cd332 100644
> --- a/fs/ext4/xattr.c
> +++ b/fs/ext4/xattr.c
> @@ -2554,11 +2554,15 @@ ext4_xattr_set(struct inode *inode, int name_index, const char *name,
>  	handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits);
>  	if (IS_ERR(handle)) {
>  		error = PTR_ERR(handle);
> +		ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR,
> +					NULL);

So when starting a transaction fails:

a) We have a big problem, the journal is aborted so marking fs ineligible
is moot.

b) We don't set anything and bail with error to userspace so again marking
fs as ineligible is pointless.

So there's no need to do anything in this case.

								Honza

>  	} else {
>  		int error2;
>  
>  		error = ext4_xattr_set_handle(handle, inode, name_index, name,
>  					      value, value_len, flags);
> +		ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR,
> +					handle);
>  		error2 = ext4_journal_stop(handle);
>  		if (error == -ENOSPC &&
>  		    ext4_should_retry_alloc(sb, &retries))
> @@ -2566,7 +2570,6 @@ ext4_xattr_set(struct inode *inode, int name_index, const char *name,
>  		if (error == 0)
>  			error = error2;
>  	}
> -	ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR, NULL);
>  
>  	return error;
>  }
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/2] ext4: use handle to mark fc as ineligible in __track_dentry_update()
  2024-09-19  9:38 ` [PATCH 1/2] ext4: use handle to mark fc as ineligible in __track_dentry_update() Luis Henriques (SUSE)
@ 2024-09-19 21:47   ` Jan Kara
  0 siblings, 0 replies; 6+ messages in thread
From: Jan Kara @ 2024-09-19 21:47 UTC (permalink / raw)
  To: Luis Henriques (SUSE)
  Cc: Theodore Ts'o, Andreas Dilger, Jan Kara, Harshad Shirwadkar,
	linux-ext4, linux-kernel

On Thu 19-09-24 10:38:47, Luis Henriques (SUSE) wrote:
> Calling ext4_fc_mark_ineligible() with a NULL handle is racy and may result
> in a fast-commit being done before the filesystem is effectively marked as
> ineligible.  This patch fixes the calls to this function in
> __track_dentry_update() by adding an extra parameter to the callback used in
> ext4_fc_track_template().
> 
> Suggested-by: Jan Kara <jack@suse.cz>
> Signed-off-by: Luis Henriques (SUSE) <luis.henriques@linux.dev>

This looks good. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/ext4/fast_commit.c | 19 +++++++++++--------
>  1 file changed, 11 insertions(+), 8 deletions(-)
> 
> diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c
> index 3926a05eceee..c330efd771d1 100644
> --- a/fs/ext4/fast_commit.c
> +++ b/fs/ext4/fast_commit.c
> @@ -372,7 +372,7 @@ void ext4_fc_mark_ineligible(struct super_block *sb, int reason, handle_t *handl
>   */
>  static int ext4_fc_track_template(
>  	handle_t *handle, struct inode *inode,
> -	int (*__fc_track_fn)(struct inode *, void *, bool),
> +	int (*__fc_track_fn)(handle_t *handle, struct inode *, void *, bool),
>  	void *args, int enqueue)
>  {
>  	bool update = false;
> @@ -389,7 +389,7 @@ static int ext4_fc_track_template(
>  		ext4_fc_reset_inode(inode);
>  		ei->i_sync_tid = tid;
>  	}
> -	ret = __fc_track_fn(inode, args, update);
> +	ret = __fc_track_fn(handle, inode, args, update);
>  	mutex_unlock(&ei->i_fc_lock);
>  
>  	if (!enqueue)
> @@ -413,7 +413,8 @@ struct __track_dentry_update_args {
>  };
>  
>  /* __track_fn for directory entry updates. Called with ei->i_fc_lock. */
> -static int __track_dentry_update(struct inode *inode, void *arg, bool update)
> +static int __track_dentry_update(handle_t *handle, struct inode *inode,
> +				 void *arg, bool update)
>  {
>  	struct ext4_fc_dentry_update *node;
>  	struct ext4_inode_info *ei = EXT4_I(inode);
> @@ -428,14 +429,14 @@ static int __track_dentry_update(struct inode *inode, void *arg, bool update)
>  
>  	if (IS_ENCRYPTED(dir)) {
>  		ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_ENCRYPTED_FILENAME,
> -					NULL);
> +					handle);
>  		mutex_lock(&ei->i_fc_lock);
>  		return -EOPNOTSUPP;
>  	}
>  
>  	node = kmem_cache_alloc(ext4_fc_dentry_cachep, GFP_NOFS);
>  	if (!node) {
> -		ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_NOMEM, NULL);
> +		ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_NOMEM, handle);
>  		mutex_lock(&ei->i_fc_lock);
>  		return -ENOMEM;
>  	}
> @@ -447,7 +448,7 @@ static int __track_dentry_update(struct inode *inode, void *arg, bool update)
>  		node->fcd_name.name = kmalloc(dentry->d_name.len, GFP_NOFS);
>  		if (!node->fcd_name.name) {
>  			kmem_cache_free(ext4_fc_dentry_cachep, node);
> -			ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_NOMEM, NULL);
> +			ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_NOMEM, handle);
>  			mutex_lock(&ei->i_fc_lock);
>  			return -ENOMEM;
>  		}
> @@ -569,7 +570,8 @@ void ext4_fc_track_create(handle_t *handle, struct dentry *dentry)
>  }
>  
>  /* __track_fn for inode tracking */
> -static int __track_inode(struct inode *inode, void *arg, bool update)
> +static int __track_inode(handle_t *handle, struct inode *inode, void *arg,
> +			 bool update)
>  {
>  	if (update)
>  		return -EEXIST;
> @@ -607,7 +609,8 @@ struct __track_range_args {
>  };
>  
>  /* __track_fn for tracking data updates */
> -static int __track_range(struct inode *inode, void *arg, bool update)
> +static int __track_range(handle_t *handle, struct inode *inode, void *arg,
> +			 bool update)
>  {
>  	struct ext4_inode_info *ei = EXT4_I(inode);
>  	ext4_lblk_t oldstart;
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 2/2] ext4: mark fc as ineligible using an handle in ext4_xattr_set()
  2024-09-19 21:47   ` Jan Kara
@ 2024-09-20  9:35     ` Luis Henriques
  0 siblings, 0 replies; 6+ messages in thread
From: Luis Henriques @ 2024-09-20  9:35 UTC (permalink / raw)
  To: Jan Kara
  Cc: Theodore Ts'o, Andreas Dilger, Harshad Shirwadkar, linux-ext4,
	linux-kernel

On Thu, Sep 19 2024, Jan Kara wrote:

> On Thu 19-09-24 10:38:48, Luis Henriques (SUSE) wrote:
>> Calling ext4_fc_mark_ineligible() with a NULL handle is racy and may result
>> in a fast-commit being done before the filesystem is effectively marked as
>> ineligible.  This patch reduces the risk of this happening in function
>> ext4_xattr_set() by using an handle if one is available.
>> 
>> Suggested-by: Jan Kara <jack@suse.cz>
>> Signed-off-by: Luis Henriques (SUSE) <luis.henriques@linux.dev>
>
> One comment below:
>
>> diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
>> index 46ce2f21fef9..dbe4d11cd332 100644
>> --- a/fs/ext4/xattr.c
>> +++ b/fs/ext4/xattr.c
>> @@ -2554,11 +2554,15 @@ ext4_xattr_set(struct inode *inode, int name_index, const char *name,
>>  	handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits);
>>  	if (IS_ERR(handle)) {
>>  		error = PTR_ERR(handle);
>> +		ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR,
>> +					NULL);
>
> So when starting a transaction fails:
>
> a) We have a big problem, the journal is aborted so marking fs ineligible
> is moot.
>
> b) We don't set anything and bail with error to userspace so again marking
> fs as ineligible is pointless.
>
> So there's no need to do anything in this case.

Ah! I spent a good amount of time trying to understand if there was a
point marking it as ineligible in that case, but couldn't reach a clear
conclusion.  That's why I decided to leave it there.  And, hoping to get
some early feedback, that's also why I decided to send these 2 patches
first, because fixing ext4_evict_inode() will require a bit more re-work.
The fix will have to deal with more error paths, and probably the call to
ext4_journal_start() will need to be moved upper in the function.

And, as always, thanks a lot your review, Jan.

Cheers,
-- 
Luís

> 								Honza
>
>>  	} else {
>>  		int error2;
>>  
>>  		error = ext4_xattr_set_handle(handle, inode, name_index, name,
>>  					      value, value_len, flags);
>> +		ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR,
>> +					handle);
>>  		error2 = ext4_journal_stop(handle);
>>  		if (error == -ENOSPC &&
>>  		    ext4_should_retry_alloc(sb, &retries))
>> @@ -2566,7 +2570,6 @@ ext4_xattr_set(struct inode *inode, int name_index, const char *name,
>>  		if (error == 0)
>>  			error = error2;
>>  	}
>> -	ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR, NULL);
>>  
>>  	return error;
>>  }
> -- 
> Jan Kara <jack@suse.com>
> SUSE Labs, CR


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2024-09-20  9:35 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-09-19  9:38 [PATCH 0/2] ext4: mark FC as ineligible using an handle Luis Henriques (SUSE)
2024-09-19  9:38 ` [PATCH 1/2] ext4: use handle to mark fc as ineligible in __track_dentry_update() Luis Henriques (SUSE)
2024-09-19 21:47   ` Jan Kara
2024-09-19  9:38 ` [PATCH 2/2] ext4: mark fc as ineligible using an handle in ext4_xattr_set() Luis Henriques (SUSE)
2024-09-19 21:47   ` Jan Kara
2024-09-20  9:35     ` Luis Henriques

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox