Skip to content

Commit 1eafa6c

Browse files
Miao XieJosef Bacik
authored andcommitted
Btrfs: fix repeated delalloc work allocation
btrfs_start_delalloc_inodes() locks the delalloc_inodes list, fetches the first inode, unlocks the list, triggers btrfs_alloc_delalloc_work/ btrfs_queue_worker for this inode, and then it locks the list, checks the head of the list again. But because we don't delete the first inode that it deals with before, it will fetch the same inode. As a result, this function allocates a huge amount of btrfs_delalloc_work structures, and OOM happens. Fix this problem by splice this delalloc list. Reported-by: Alex Lyakas <alex.btrfs@zadarastorage.com> Signed-off-by: Miao Xie <miaox@cn.fujitsu.com> Signed-off-by: Josef Bacik <jbacik@fusionio.com>
1 parent c9f01bf commit 1eafa6c

File tree

1 file changed

+41
-14
lines changed

1 file changed

+41
-14
lines changed

fs/btrfs/inode.c

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7585,41 +7585,61 @@ void btrfs_wait_and_free_delalloc_work(struct btrfs_delalloc_work *work)
75857585
*/
75867586
int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput)
75877587
{
7588-
struct list_head *head = &root->fs_info->delalloc_inodes;
75897588
struct btrfs_inode *binode;
75907589
struct inode *inode;
75917590
struct btrfs_delalloc_work *work, *next;
75927591
struct list_head works;
7592+
struct list_head splice;
75937593
int ret = 0;
75947594

75957595
if (root->fs_info->sb->s_flags & MS_RDONLY)
75967596
return -EROFS;
75977597

75987598
INIT_LIST_HEAD(&works);
7599-
7599+
INIT_LIST_HEAD(&splice);
7600+
again:
76007601
spin_lock(&root->fs_info->delalloc_lock);
7601-
while (!list_empty(head)) {
7602-
binode = list_entry(head->next, struct btrfs_inode,
7602+
list_splice_init(&root->fs_info->delalloc_inodes, &splice);
7603+
while (!list_empty(&splice)) {
7604+
binode = list_entry(splice.next, struct btrfs_inode,
76037605
delalloc_inodes);
7606+
7607+
list_del_init(&binode->delalloc_inodes);
7608+
76047609
inode = igrab(&binode->vfs_inode);
76057610
if (!inode)
7606-
list_del_init(&binode->delalloc_inodes);
7611+
continue;
7612+
7613+
list_add_tail(&binode->delalloc_inodes,
7614+
&root->fs_info->delalloc_inodes);
76077615
spin_unlock(&root->fs_info->delalloc_lock);
7608-
if (inode) {
7609-
work = btrfs_alloc_delalloc_work(inode, 0, delay_iput);
7610-
if (!work) {
7611-
ret = -ENOMEM;
7612-
goto out;
7613-
}
7614-
list_add_tail(&work->list, &works);
7615-
btrfs_queue_worker(&root->fs_info->flush_workers,
7616-
&work->work);
7616+
7617+
work = btrfs_alloc_delalloc_work(inode, 0, delay_iput);
7618+
if (unlikely(!work)) {
7619+
ret = -ENOMEM;
7620+
goto out;
76177621
}
7622+
list_add_tail(&work->list, &works);
7623+
btrfs_queue_worker(&root->fs_info->flush_workers,
7624+
&work->work);
7625+
76187626
cond_resched();
76197627
spin_lock(&root->fs_info->delalloc_lock);
76207628
}
76217629
spin_unlock(&root->fs_info->delalloc_lock);
76227630

7631+
list_for_each_entry_safe(work, next, &works, list) {
7632+
list_del_init(&work->list);
7633+
btrfs_wait_and_free_delalloc_work(work);
7634+
}
7635+
7636+
spin_lock(&root->fs_info->delalloc_lock);
7637+
if (!list_empty(&root->fs_info->delalloc_inodes)) {
7638+
spin_unlock(&root->fs_info->delalloc_lock);
7639+
goto again;
7640+
}
7641+
spin_unlock(&root->fs_info->delalloc_lock);
7642+
76237643
/* the filemap_flush will queue IO into the worker threads, but
76247644
* we have to make sure the IO is actually started and that
76257645
* ordered extents get created before we return
@@ -7632,11 +7652,18 @@ int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput)
76327652
atomic_read(&root->fs_info->async_delalloc_pages) == 0));
76337653
}
76347654
atomic_dec(&root->fs_info->async_submit_draining);
7655+
return 0;
76357656
out:
76367657
list_for_each_entry_safe(work, next, &works, list) {
76377658
list_del_init(&work->list);
76387659
btrfs_wait_and_free_delalloc_work(work);
76397660
}
7661+
7662+
if (!list_empty_careful(&splice)) {
7663+
spin_lock(&root->fs_info->delalloc_lock);
7664+
list_splice_tail(&splice, &root->fs_info->delalloc_inodes);
7665+
spin_unlock(&root->fs_info->delalloc_lock);
7666+
}
76407667
return ret;
76417668
}
76427669

0 commit comments

Comments
 (0)