From: Wang Jianchao <hidden> Date: 2021-07-24 07:42:58
Hi all
This is the version 3 patch set that attempts to get discard out of the jbd2
commit kthread. When the user delete a lot data and cause discard flooding,
the jbd2 commit kthread can be blocked for very long time and then all of
the metadata operations are blocked due to no journal space.
The xfstest with following parameters,
MODULAR=0
TEST_DIR=/mnt/test
TEST_DEV=/dev/nbd37p1
SCRATCH_MNT=/mnt/scratch
SCRATCH_DEV=/dev/nbd37p2
MOUNT_OPTIONS="-o discard"
has passed. The result is consistent w/ or w/o this patch set.
There are 5 patches,
Patch 1 ~ 3, there are no functional changes in them, but just some preparation
for following patches
Patch 4 introduces a async kworker to do discard in fstrim fation which implements
the core idea of this patch set.
Patch 5 let the fallocate retry when err is ENOSPC. This fix the generic/371
Any comments are welcome ;)
V2 -> V3
- Get rid of the per block group rb tree which carries freed entry. It is not neccesary
because we have done aggregation when wait for journal commit. Just use a list
to carry the free entries.
V1 -> V2
- free the blocks back to mb buddy after commit and then do ftrim fashion discard
fs/ext4/ext4.h | 2 +
fs/ext4/extents.c | 6 ++-
fs/ext4/mballoc.c | 223 ++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
3 files changed, 151 insertions(+), 80 deletions(-)
From: Wang Jianchao <hidden> Date: 2021-07-24 07:43:01
From: Wang Jianchao <redacted>
Get rid of the 'group' parameter of ext4_trim_extent as we can get
it from the 'e4b'.
Reviewed-by: Andreas Dilger <redacted>
Signed-off-by: Wang Jianchao <redacted>
---
fs/ext4/mballoc.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
From: Wang Jianchao <hidden> Date: 2021-07-24 07:43:05
From: Wang Jianchao <redacted>
There is no functional change in this patch but just split the
codes, which serachs free block and does trim, into a new function
ext4_try_to_trim_range. This is preparing for the following async
backgroup discard.
Reviewed-by: Andreas Dilger <redacted>
Signed-off-by: Wang Jianchao <redacted>
---
fs/ext4/mballoc.c | 102 ++++++++++++++++++++++++++--------------------
1 file changed, 57 insertions(+), 45 deletions(-)
@@ -6254,57 +6300,23 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group,ret,group);returnret;}-bitmap=e4b.bd_bitmap;ext4_lock_group(sb,group);-if(EXT4_MB_GRP_WAS_TRIMMED(e4b.bd_info)&&-minblocks>=atomic_read(&EXT4_SB(sb)->s_last_trim_minblks))-gotoout;--start=(e4b.bd_info->bb_first_free>start)?-e4b.bd_info->bb_first_free:start;-while(start<=max){-start=mb_find_next_zero_bit(bitmap,max+1,start);-if(start>max)-break;-next=mb_find_next_bit(bitmap,max+1,start);--if((next-start)>=minblocks){-ret=ext4_trim_extent(sb,start,next-start,&e4b);-if(ret&&ret!=-EOPNOTSUPP)-break;-ret=0;-count+=next-start;-}-free_count+=next-start;-start=next+1;--if(fatal_signal_pending(current)){-count=-ERESTARTSYS;-break;-}--if(need_resched()){-ext4_unlock_group(sb,group);-cond_resched();-ext4_lock_group(sb,group);-}--if((e4b.bd_info->bb_free-free_count)<minblocks)-break;+if(!EXT4_MB_GRP_WAS_TRIMMED(e4b.bd_info)||+minblocks<atomic_read(&EXT4_SB(sb)->s_last_trim_minblks)){+ret=ext4_try_to_trim_range(sb,&e4b,start,max,minblocks);+if(ret>=0)+EXT4_MB_GRP_SET_TRIMMED(e4b.bd_info);+}else{+ret=0;}-if(!ret){-ret=count;-EXT4_MB_GRP_SET_TRIMMED(e4b.bd_info);-}-out:ext4_unlock_group(sb,group);ext4_mb_unload_buddy(&e4b);ext4_debug("trimmed %d blocks in the group %d\n",-count,group);+ret,group);returnret;}
From: Wang Jianchao <hidden> Date: 2021-07-24 07:43:14
From: Wang Jianchao <redacted>
Right now, discard is issued and waited to be completed in jbd2
commit kthread context after the logs are committed. When large
amount of files are deleted and discard is flooding, jbd2 commit
kthread can be blocked for long time. Then all of the metadata
operations can be blocked to wait the log space.
One case is the page fault path with read mm->mmap_sem held, which
wants to update the file time but has to wait for the log space.
When other threads in the task wants to do mmap, then write mmap_sem
is blocked. Finally all of the following read mmap_sem requirements
are blocked, even the ps command which need to read the /proc/pid/
-cmdline. Our monitor service which needs to read /proc/pid/cmdline
used to be blocked for 5 mins.
This patch frees the blocks back to buddy after commit and then do
discard in a async kworker context in fstrim fashion, namely,
- mark blocks to be discarded as used if they have not been allocated
- do discard
- mark them free
After this, jbd2 commit kthread won't be blocked any more by discard
and we won't get NOSPC even if the discard is slow or throttled.
Link: https://marc.info/?l=linux-kernel&m=162143690731901&w=2
Suggested-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Wang Jianchao <redacted>
---
fs/ext4/ext4.h | 2 +
fs/ext4/mballoc.c | 109 +++++++++++++++++++++++++++++++++++-----------
2 files changed, 86 insertions(+), 25 deletions(-)
@@ -1536,6 +1536,8 @@ struct ext4_sb_info {unsignedints_mb_free_pending;structlist_heads_freed_data_list;/* List of blocks to be freedaftercommitcompleted*/+structlist_heads_discard_list;+structwork_structs_discard_work;structrb_roots_mb_avg_fragment_size_root;rwlock_ts_mb_rb_lock;structlist_head*s_mb_largest_free_orders;
@@ -386,6 +386,7 @@staticstructkmem_cache*ext4_pspace_cachep;staticstructkmem_cache*ext4_ac_cachep;staticstructkmem_cache*ext4_free_data_cachep;+staticstructworkqueue_struct*ext4_discard_wq;/* We create slab caches for groupinfo data structures based on the*superblockblocksize.Therewillbeonepermountedfilesystemfor
From: Wang Jianchao <hidden> Date: 2021-07-24 07:43:18
From: Wang Jianchao <redacted>
The blocks may be waiting for journal commit to be freed back to
mb buddy. Let fallocate wait and retry in that case.
Signed-off-by: Wang Jianchao <redacted>
---
fs/ext4/extents.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
From: Wang Jianchao <redacted>
The blocks may be waiting for journal commit to be freed back to
mb buddy. Let fallocate wait and retry in that case.
Signed-off-by: Wang Jianchao <redacted>
---
fs/ext4/extents.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
-static int ext4_trim_extent(struct super_block *sb, int start, int count,
- ext4_group_t group, struct ext4_buddy *e4b)
+static int ext4_trim_extent(struct super_block *sb,
+ int start, int count, struct ext4_buddy *e4b)
Nit, seems only need to change the second line.
Thanks,
Guoqing
From: Wang Jianchao <hidden> Date: 2021-07-26 07:05:51
On 2021/7/26 11:40 AM, Guoqing Jiang wrote:
Hi,
On 7/24/21 3:41 PM, Wang Jianchao wrote:
quoted
From: Wang Jianchao <redacted>
The blocks may be waiting for journal commit to be freed back to
mb buddy. Let fallocate wait and retry in that case.
Signed-off-by: Wang Jianchao <redacted>
---
fs/ext4/extents.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
@@ -4635,7 +4635,7 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
struct inode *inode = file_inode(file);
loff_t new_size = 0;
unsigned int max_blocks;
- int ret = 0;
+ int ret = 0, retries = 0;
int flags;
ext4_lblk_t lblk;
unsigned int blkbits = inode->i_blkbits;
@@ -4656,6 +4656,7 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
FALLOC_FL_INSERT_RANGE))
return -EOPNOTSUPP;
+retry:
ext4_fc_start_update(inode);
if (mode & FALLOC_FL_PUNCH_HOLE) {
@@ -4722,6 +4723,9 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
Not sure if it is necessary since ext4_alloc_file_blocks already retries allocate.
Yes, this patch should be get rid of.
But it is indeed helpful to fix the xfstest generic/371 which does concurrently write/rm
and fallocate/rm. I'll figure out some other way to improve that
Thanks
Jianchao
From: Jan Kara <jack@suse.cz> Date: 2021-08-04 15:27:17
On Sat 24-07-21 15:41:20, Wang Jianchao wrote:
From: Wang Jianchao <redacted>
Get rid of the 'group' parameter of ext4_trim_extent as we can get
it from the 'e4b'.
Reviewed-by: Andreas Dilger <redacted>
Signed-off-by: Wang Jianchao <redacted>
Looks good. Feel free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
From: Jan Kara <jack@suse.cz> Date: 2021-08-04 15:30:01
On Sat 24-07-21 15:41:21, Wang Jianchao wrote:
From: Wang Jianchao <redacted>
There is no functional change in this patch but just split the
codes, which serachs free block and does trim, into a new function
ext4_try_to_trim_range. This is preparing for the following async
backgroup discard.
Reviewed-by: Andreas Dilger <redacted>
Signed-off-by: Wang Jianchao <redacted>
Looks good. Feel free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
@@ -6254,57 +6300,23 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group,ret,group);returnret;}-bitmap=e4b.bd_bitmap;ext4_lock_group(sb,group);-if(EXT4_MB_GRP_WAS_TRIMMED(e4b.bd_info)&&-minblocks>=atomic_read(&EXT4_SB(sb)->s_last_trim_minblks))-gotoout;--start=(e4b.bd_info->bb_first_free>start)?-e4b.bd_info->bb_first_free:start;-while(start<=max){-start=mb_find_next_zero_bit(bitmap,max+1,start);-if(start>max)-break;-next=mb_find_next_bit(bitmap,max+1,start);--if((next-start)>=minblocks){-ret=ext4_trim_extent(sb,start,next-start,&e4b);-if(ret&&ret!=-EOPNOTSUPP)-break;-ret=0;-count+=next-start;-}-free_count+=next-start;-start=next+1;--if(fatal_signal_pending(current)){-count=-ERESTARTSYS;-break;-}--if(need_resched()){-ext4_unlock_group(sb,group);-cond_resched();-ext4_lock_group(sb,group);-}--if((e4b.bd_info->bb_free-free_count)<minblocks)-break;+if(!EXT4_MB_GRP_WAS_TRIMMED(e4b.bd_info)||+minblocks<atomic_read(&EXT4_SB(sb)->s_last_trim_minblks)){+ret=ext4_try_to_trim_range(sb,&e4b,start,max,minblocks);+if(ret>=0)+EXT4_MB_GRP_SET_TRIMMED(e4b.bd_info);+}else{+ret=0;}-if(!ret){-ret=count;-EXT4_MB_GRP_SET_TRIMMED(e4b.bd_info);-}-out:ext4_unlock_group(sb,group);ext4_mb_unload_buddy(&e4b);ext4_debug("trimmed %d blocks in the group %d\n",-count,group);+ret,group);returnret;}
From: Jan Kara <jack@suse.cz> Date: 2021-08-04 15:32:56
On Sat 24-07-21 15:41:24, Wang Jianchao wrote:
From: Wang Jianchao <redacted>
The blocks may be waiting for journal commit to be freed back to
mb buddy. Let fallocate wait and retry in that case.
Signed-off-by: Wang Jianchao <redacted>
Did you really observe this? Because the retry is already handled in
ext4_alloc_file_blocks() that's used by ext4_fallocate(). So no retry
should be needed there.
Honza
From: Jan Kara <jack@suse.cz> Date: 2021-08-04 15:45:36
On Sat 24-07-21 15:41:23, Wang Jianchao wrote:
From: Wang Jianchao <redacted>
Right now, discard is issued and waited to be completed in jbd2
commit kthread context after the logs are committed. When large
amount of files are deleted and discard is flooding, jbd2 commit
kthread can be blocked for long time. Then all of the metadata
operations can be blocked to wait the log space.
One case is the page fault path with read mm->mmap_sem held, which
wants to update the file time but has to wait for the log space.
When other threads in the task wants to do mmap, then write mmap_sem
is blocked. Finally all of the following read mmap_sem requirements
are blocked, even the ps command which need to read the /proc/pid/
-cmdline. Our monitor service which needs to read /proc/pid/cmdline
used to be blocked for 5 mins.
This patch frees the blocks back to buddy after commit and then do
discard in a async kworker context in fstrim fashion, namely,
- mark blocks to be discarded as used if they have not been allocated
- do discard
- mark them free
After this, jbd2 commit kthread won't be blocked any more by discard
and we won't get NOSPC even if the discard is slow or throttled.
Link: https://marc.info/?l=linux-kernel&m=162143690731901&w=2
Suggested-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Wang Jianchao <redacted>
Looks good to me. Just one small comment below. With that addressed feel
free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
quoted hunk
@@ -3474,6 +3530,14 @@ int ext4_mb_release(struct super_block *sb) struct kmem_cache *cachep = get_groupinfo_cache(sb->s_blocksize_bits); int count;+ if (test_opt(sb, DISCARD)) {+ /*+ * wait the discard work to drain all of ext4_free_data+ */+ queue_work(ext4_discard_wq, &sbi->s_discard_work);
Do we really need to queue the work here? The filesystem should be
quiescent by now, we take care to queue the work whenever we add item to
empty list. So it should be enough to have flush_work() here and then
possibly
WARN_ON_ONCE(!list_empty(&sbi->s_discard_list))
Or am I missing something?
Honza
From: Jan Kara <jack@suse.cz> Date: 2021-08-04 15:46:29
On Wed 04-08-21 17:32:21, Jan Kara wrote:
On Sat 24-07-21 15:41:24, Wang Jianchao wrote:
quoted
From: Wang Jianchao <redacted>
The blocks may be waiting for journal commit to be freed back to
mb buddy. Let fallocate wait and retry in that case.
Signed-off-by: Wang Jianchao <redacted>
Did you really observe this? Because the retry is already handled in
ext4_alloc_file_blocks() that's used by ext4_fallocate(). So no retry
should be needed there.
Oh, I can see you've addressed these already in another reply. I'll comment
there.
Honza
--
Jan Kara [off-list ref]
SUSE Labs, CR
From: Jan Kara <jack@suse.cz> Date: 2021-08-04 15:52:20
On Mon 26-07-21 15:05:41, Wang Jianchao wrote:
On 2021/7/26 11:40 AM, Guoqing Jiang wrote:
quoted
Hi,
On 7/24/21 3:41 PM, Wang Jianchao wrote:
quoted
From: Wang Jianchao <redacted>
The blocks may be waiting for journal commit to be freed back to
mb buddy. Let fallocate wait and retry in that case.
Signed-off-by: Wang Jianchao <redacted>
---
fs/ext4/extents.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
@@ -4635,7 +4635,7 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
struct inode *inode = file_inode(file);
loff_t new_size = 0;
unsigned int max_blocks;
- int ret = 0;
+ int ret = 0, retries = 0;
int flags;
ext4_lblk_t lblk;
unsigned int blkbits = inode->i_blkbits;
@@ -4656,6 +4656,7 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
FALLOC_FL_INSERT_RANGE))
return -EOPNOTSUPP;
+retry:
ext4_fc_start_update(inode);
if (mode & FALLOC_FL_PUNCH_HOLE) {
@@ -4722,6 +4723,9 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
Not sure if it is necessary since ext4_alloc_file_blocks already retries allocate.
Yes, this patch should be get rid of. But it is indeed helpful to fix
the xfstest generic/371 which does concurrently write/rm and
fallocate/rm. I'll figure out some other way to improve that
Note that the retry logic is only a heuristic. It is not guaranteed any
number of retries is enough, we just do three to not give up too easily...
Your patch effectively raised number of retries to 9 so that may have
masked the issue. But I don't think so high number of retries is a sensible
choice because that way it may take too long to return ENOSPC.
Honza
--
Jan Kara [off-list ref]
SUSE Labs, CR
On Sat, Jul 24, 2021 at 03:41:21PM +0800, Wang Jianchao wrote:
quoted hunk
From: Wang Jianchao <redacted>
There is no functional change in this patch but just split the
codes, which serachs free block and does trim, into a new function
ext4_try_to_trim_range. This is preparing for the following async
backgroup discard.
Reviewed-by: Andreas Dilger <redacted>
Signed-off-by: Wang Jianchao <redacted>
---
fs/ext4/mballoc.c | 102 ++++++++++++++++++++++++++--------------------
1 file changed, 57 insertions(+), 45 deletions(-)
@@ -3474,6 +3530,14 @@ int ext4_mb_release(struct super_block *sb)structkmem_cache*cachep=get_groupinfo_cache(sb->s_blocksize_bits);intcount;+if(test_opt(sb,DISCARD)){+/*+*waitthediscardworktodrainallofext4_free_data+*/+queue_work(ext4_discard_wq,&sbi->s_discard_work);+flush_work(&sbi->s_discard_work);
I agree with Jan --- it's not clear to me why the call to queue_work()
is needed. After the flush_work() call returns, if s_discard_work is
still non-empty, there must be something terribly wrong --- are we
missing something?
quoted hunk
@@ -3672,8 +3724,14 @@ int __init ext4_init_mballoc(void) if (ext4_free_data_cachep == NULL) goto out_ac_free;+ ext4_discard_wq = alloc_workqueue("ext4discard", WQ_UNBOUND, 0);+ if (!ext4_discard_wq)+ goto out_free_data;+
Perhaps we should only allocate the workqueue when it's needed ---
e.g., when a file system is mounted or remounted with "-o discard"?
Then in ext4_exit_malloc(), we only free it if ext4_discard_wq is
non-NULL.
This would save a bit of memory on systems that wouldn't need the ext4
discard work queue.
- Ted
From: Wang Jianchao <hidden> Date: 2021-08-26 07:16:10
On 2021/8/4 11:45 PM, Jan Kara wrote:
On Sat 24-07-21 15:41:23, Wang Jianchao wrote:
quoted
From: Wang Jianchao <redacted>
Right now, discard is issued and waited to be completed in jbd2
commit kthread context after the logs are committed. When large
amount of files are deleted and discard is flooding, jbd2 commit
kthread can be blocked for long time. Then all of the metadata
operations can be blocked to wait the log space.
One case is the page fault path with read mm->mmap_sem held, which
wants to update the file time but has to wait for the log space.
When other threads in the task wants to do mmap, then write mmap_sem
is blocked. Finally all of the following read mmap_sem requirements
are blocked, even the ps command which need to read the /proc/pid/
-cmdline. Our monitor service which needs to read /proc/pid/cmdline
used to be blocked for 5 mins.
This patch frees the blocks back to buddy after commit and then do
discard in a async kworker context in fstrim fashion, namely,
- mark blocks to be discarded as used if they have not been allocated
- do discard
- mark them free
After this, jbd2 commit kthread won't be blocked any more by discard
and we won't get NOSPC even if the discard is slow or throttled.
Link: https://marc.info/?l=linux-kernel&m=162143690731901&w=2
Suggested-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Wang Jianchao <redacted>
Looks good to me. Just one small comment below. With that addressed feel
free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
quoted
@@ -3474,6 +3530,14 @@ int ext4_mb_release(struct super_block *sb) struct kmem_cache *cachep = get_groupinfo_cache(sb->s_blocksize_bits); int count;+ if (test_opt(sb, DISCARD)) {+ /*+ * wait the discard work to drain all of ext4_free_data+ */+ queue_work(ext4_discard_wq, &sbi->s_discard_work);
Do we really need to queue the work here? The filesystem should be
quiescent by now, we take care to queue the work whenever we add item to
empty list. So it should be enough to have flush_work() here and then
possibly
WARN_ON_ONCE(!list_empty(&sbi->s_discard_list))
Or am I missing something?
queue_work here is indeed redundant.
Thanks so much for you point out this.
Jianchao
From: Wang Jianchao <hidden> Date: 2021-08-26 07:19:20
On 2021/8/13 1:44 AM, Theodore Ts'o wrote:
On Sat, Jul 24, 2021 at 03:41:21PM +0800, Wang Jianchao wrote:
quoted
From: Wang Jianchao <redacted>
There is no functional change in this patch but just split the
codes, which serachs free block and does trim, into a new function
ext4_try_to_trim_range. This is preparing for the following async
backgroup discard.
Reviewed-by: Andreas Dilger <redacted>
Signed-off-by: Wang Jianchao <redacted>
---
fs/ext4/mballoc.c | 102 ++++++++++++++++++++++++++--------------------
1 file changed, 57 insertions(+), 45 deletions(-)
@@ -3474,6 +3530,14 @@ int ext4_mb_release(struct super_block *sb)structkmem_cache*cachep=get_groupinfo_cache(sb->s_blocksize_bits);intcount;+if(test_opt(sb,DISCARD)){+/*+*waitthediscardworktodrainallofext4_free_data+*/+queue_work(ext4_discard_wq,&sbi->s_discard_work);+flush_work(&sbi->s_discard_work);
I agree with Jan --- it's not clear to me why the call to queue_work()
is needed. After the flush_work() call returns, if s_discard_work is
still non-empty, there must be something terribly wrong --- are we
missing something?
Yes,the queue_work() is redundant.
I will get rid of it in next version.
quoted
@@ -3672,8 +3724,14 @@ int __init ext4_init_mballoc(void) if (ext4_free_data_cachep == NULL) goto out_ac_free;+ ext4_discard_wq = alloc_workqueue("ext4discard", WQ_UNBOUND, 0);+ if (!ext4_discard_wq)+ goto out_free_data;+
Perhaps we should only allocate the workqueue when it's needed ---
e.g., when a file system is mounted or remounted with "-o discard"?
Then in ext4_exit_malloc(), we only free it if ext4_discard_wq is
non-NULL.
This would save a bit of memory on systems that wouldn't need the ext4
discard work queue.
Yes, it make sense to the system with pool memory
Thanks so much
Jianchao
From: Wang Jianchao <hidden> Date: 2021-08-26 08:58:35
On 2021/8/26 3:51 PM, Wang Jianchao wrote:
quoted
quoted
@@ -3672,8 +3724,14 @@ int __init ext4_init_mballoc(void) if (ext4_free_data_cachep == NULL) goto out_ac_free;+ ext4_discard_wq = alloc_workqueue("ext4discard", WQ_UNBOUND, 0);+ if (!ext4_discard_wq)+ goto out_free_data;+
Perhaps we should only allocate the workqueue when it's needed ---
e.g., when a file system is mounted or remounted with "-o discard"?
Then in ext4_exit_malloc(), we only free it if ext4_discard_wq is
non-NULL.
This would save a bit of memory on systems that wouldn't need the ext4
discard work queue.
From: Wang Jianchao <hidden> Date: 2021-08-26 11:42:45
On 2021/8/4 11:52 PM, Jan Kara wrote:
On Mon 26-07-21 15:05:41, Wang Jianchao wrote:
quoted
On 2021/7/26 11:40 AM, Guoqing Jiang wrote:
quoted
Hi,
On 7/24/21 3:41 PM, Wang Jianchao wrote:
quoted
From: Wang Jianchao <redacted>
The blocks may be waiting for journal commit to be freed back to
mb buddy. Let fallocate wait and retry in that case.
Signed-off-by: Wang Jianchao <redacted>
---
fs/ext4/extents.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
@@ -4635,7 +4635,7 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
struct inode *inode = file_inode(file);
loff_t new_size = 0;
unsigned int max_blocks;
- int ret = 0;
+ int ret = 0, retries = 0;
int flags;
ext4_lblk_t lblk;
unsigned int blkbits = inode->i_blkbits;
@@ -4656,6 +4656,7 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
FALLOC_FL_INSERT_RANGE))
return -EOPNOTSUPP;
+retry:
ext4_fc_start_update(inode);
if (mode & FALLOC_FL_PUNCH_HOLE) {
@@ -4722,6 +4723,9 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
Not sure if it is necessary since ext4_alloc_file_blocks already retries allocate.
Yes, this patch should be get rid of. But it is indeed helpful to fix
the xfstest generic/371 which does concurrently write/rm and
fallocate/rm. I'll figure out some other way to improve that
Note that the retry logic is only a heuristic. It is not guaranteed any
number of retries is enough, we just do three to not give up too easily...
Your patch effectively raised number of retries to 9 so that may have
masked the issue. But I don't think so high number of retries is a sensible
choice because that way it may take too long to return ENOSPC.
The failure seems due to the background discard which marks the blocks used
before issue discard.
The test make a 256M filesystem which has 59316 4K blocks.
There are two thread running concurrently,
- write, rm 80M file
- fallocate, rm 80M file
When the fallocate failed, I can observe there was a 80M on-going background trim
We seems to need to add a flush_work(sbi->s_discard_work) into ext4_should_retry_alloc()
Thanks so much
Jianchao