Skip to content

Commit 9705acd

Browse files
Lukas Czernertytso
authored andcommitted
ext4: fix reservation release on invalidatepage for delalloc fs
On delalloc enabled file system on invalidatepage operation in ext4_da_page_release_reservation() we want to clear the delayed buffer and remove the extent covering the delayed buffer from the extent status tree. However currently there is a bug where on the systems with page size > block size we will always remove extents from the start of the page regardless where the actual delayed buffers are positioned in the page. This leads to the errors like this: EXT4-fs warning (device loop0): ext4_da_release_space:1225: ext4_da_release_space: ino 13, to_free 1 with only 0 reserved data blocks This however can cause data loss on writeback time if the file system is in ENOSPC condition because we're releasing reservation for someones else delayed buffer. Fix this by only removing extents that corresponds to the part of the page we want to invalidate. This problem is reproducible by the following fio receipt (however I was only able to reproduce it with fio-2.1 or older. [global] bs=8k iodepth=1024 iodepth_batch=60 randrepeat=1 size=1m directory=/mnt/test numjobs=20 [job1] ioengine=sync bs=1k direct=1 rw=randread filename=file1:file2 [job2] ioengine=libaio rw=randwrite direct=1 filename=file1:file2 [job3] bs=1k ioengine=posixaio rw=randwrite direct=1 filename=file1:file2 [job5] bs=1k ioengine=sync rw=randread filename=file1:file2 [job7] ioengine=libaio rw=randwrite filename=file1:file2 [job8] ioengine=posixaio rw=randwrite filename=file1:file2 [job10] ioengine=mmap rw=randwrite bs=1k filename=file1:file2 [job11] ioengine=mmap rw=randwrite direct=1 filename=file1:file2 Signed-off-by: Lukas Czerner <[email protected]> Signed-off-by: Theodore Ts'o <[email protected]> Reviewed-by: Jan Kara <[email protected]> Cc: [email protected]
1 parent c45653c commit 9705acd

File tree

1 file changed

+12
-3
lines changed

1 file changed

+12
-3
lines changed

fs/ext4/inode.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1330,7 +1330,7 @@ static void ext4_da_page_release_reservation(struct page *page,
13301330
unsigned int offset,
13311331
unsigned int length)
13321332
{
1333-
int to_release = 0;
1333+
int to_release = 0, contiguous_blks = 0;
13341334
struct buffer_head *head, *bh;
13351335
unsigned int curr_off = 0;
13361336
struct inode *inode = page->mapping->host;
@@ -1351,14 +1351,23 @@ static void ext4_da_page_release_reservation(struct page *page,
13511351

13521352
if ((offset <= curr_off) && (buffer_delay(bh))) {
13531353
to_release++;
1354+
contiguous_blks++;
13541355
clear_buffer_delay(bh);
1356+
} else if (contiguous_blks) {
1357+
lblk = page->index <<
1358+
(PAGE_CACHE_SHIFT - inode->i_blkbits);
1359+
lblk += (curr_off >> inode->i_blkbits) -
1360+
contiguous_blks;
1361+
ext4_es_remove_extent(inode, lblk, contiguous_blks);
1362+
contiguous_blks = 0;
13551363
}
13561364
curr_off = next_off;
13571365
} while ((bh = bh->b_this_page) != head);
13581366

1359-
if (to_release) {
1367+
if (contiguous_blks) {
13601368
lblk = page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits);
1361-
ext4_es_remove_extent(inode, lblk, to_release);
1369+
lblk += (curr_off >> inode->i_blkbits) - contiguous_blks;
1370+
ext4_es_remove_extent(inode, lblk, contiguous_blks);
13621371
}
13631372

13641373
/* If we have released all the blocks belonging to a cluster, then we

0 commit comments

Comments
 (0)