Skip to content

Commit 80fa46d

Browse files
committed
ext4: limit the number of retries after discarding preallocations blocks
This patch avoids threads live-locking for hours when a large number threads are competing over the last few free extents as they blocks getting added and removed from preallocation pools. From our bug reporter: A reliable way for triggering this has multiple writers continuously write() to files when the filesystem is full, while small amounts of space are freed (e.g. by truncating a large file -1MiB at a time). In the local filesystem, this can be done by simply not checking the return code of write (0) and/or the error (ENOSPACE) that is set. Over NFS with an async mount, even clients with proper error checking will behave this way since the linux NFS client implementation will not propagate the server errors [the write syscalls immediately return success] until the file handle is closed. This leads to a situation where NFS clients send a continuous stream of WRITE rpcs which result in ERRNOSPACE -- but since the client isn't seeing this, the stream of writes continues at maximum network speed. When some space does appear, multiple writers will all attempt to claim it for their current write. For NFS, we may see dozens to hundreds of threads that do this. The real-world scenario of this is database backup tooling (in particular, github.com/mdkent/percona-xtrabackup) which may write large files (>1TiB) to NFS for safe keeping. Some temporary files are written, rewound, and read back -- all before closing the file handle (the temp file is actually unlinked, to trigger automatic deletion on close/crash.) An application like this operating on an async NFS mount will not see an error code until TiB have been written/read. The lockup was observed when running this database backup on large filesystems (64 TiB in this case) with a high number of block groups and no free space. Fragmentation is generally not a factor in this filesystem (~thousands of large files, mostly contiguous except for the parts written while the filesystem is at capacity.) Signed-off-by: Theodore Ts'o <tytso@mit.edu> Cc: stable@kernel.org
1 parent 29a5b8a commit 80fa46d

File tree

1 file changed

+3
-1
lines changed

1 file changed

+3
-1
lines changed

fs/ext4/mballoc.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5533,6 +5533,7 @@ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle,
55335533
ext4_fsblk_t block = 0;
55345534
unsigned int inquota = 0;
55355535
unsigned int reserv_clstrs = 0;
5536+
int retries = 0;
55365537
u64 seq;
55375538

55385539
might_sleep();
@@ -5635,7 +5636,8 @@ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle,
56355636
ar->len = ac->ac_b_ex.fe_len;
56365637
}
56375638
} else {
5638-
if (ext4_mb_discard_preallocations_should_retry(sb, ac, &seq))
5639+
if (++retries < 3 &&
5640+
ext4_mb_discard_preallocations_should_retry(sb, ac, &seq))
56395641
goto repeat;
56405642
/*
56415643
* If block allocation fails then the pa allocated above

0 commit comments

Comments
 (0)