diff options
author | Zhao Lei <zhaolei@cn.fujitsu.com> | 2015-02-25 21:49:20 -0500 |
---|---|---|
committer | Chris Mason <clm@fb.com> | 2015-04-13 10:27:41 -0400 |
commit | d7c151717a1efe289aec29fb9f94485f64262c0b (patch) | |
tree | 64a17c0b1403fbbb14e474e101a997f117de237c /fs | |
parent | 18d018ad2c899d3d9c503c25125d56046ed7d3ca (diff) |
btrfs: Fix NO_SPACE bug caused by delayed-iput
Steps to reproduce:
while true; do
dd if=/dev/zero of=/btrfs_dir/file count=[fs_size * 75%]
rm /btrfs_dir/file
sync
done
And we'll see dd failed because btrfs return NO_SPACE.
Reason:
Normally, btrfs_commit_transaction() call btrfs_run_delayed_iputs()
in end to free fs space for next write, but sometimes it hadn't
done work on time, because btrfs-cleaner thread get delayed-iputs
from list before, but do iput() after next write.
This is log:
[ 2569.050776] comm=btrfs-cleaner func=btrfs_evict_inode() begin
[ 2569.084280] comm=sync func=btrfs_commit_transaction() call btrfs_run_delayed_iputs()
[ 2569.085418] comm=sync func=btrfs_commit_transaction() done btrfs_run_delayed_iputs()
[ 2569.087554] comm=sync func=btrfs_commit_transaction() end
[ 2569.191081] comm=dd begin
[ 2569.790112] comm=dd func=__btrfs_buffered_write() ret=-28
[ 2569.847479] comm=btrfs-cleaner func=add_pinned_bytes() 0 + 32677888 = 32677888
[ 2569.849530] comm=btrfs-cleaner func=add_pinned_bytes() 32677888 + 23834624 = 56512512
...
[ 2569.903893] comm=btrfs-cleaner func=add_pinned_bytes() 943976448 + 21762048 = 965738496
[ 2569.908270] comm=btrfs-cleaner func=btrfs_evict_inode() end
Fix:
Make btrfs_commit_transaction() wait current running btrfs-cleaner's
delayed-iputs() done in end.
Test:
Use script similar to above(more complex),
before patch:
7 failed in 100 * 20 loop.
after patch:
0 failed in 100 * 20 loop.
Signed-off-by: Zhao Lei <zhaolei@cn.fujitsu.com>
Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/btrfs/ctree.h | 1 | ||||
-rw-r--r-- | fs/btrfs/disk-io.c | 3 | ||||
-rw-r--r-- | fs/btrfs/extent-tree.c | 6 | ||||
-rw-r--r-- | fs/btrfs/inode.c | 4 |
4 files changed, 13 insertions, 1 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 10b6a75ab7e6..d48b22f31182 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h | |||
@@ -1538,6 +1538,7 @@ struct btrfs_fs_info { | |||
1538 | 1538 | ||
1539 | spinlock_t delayed_iput_lock; | 1539 | spinlock_t delayed_iput_lock; |
1540 | struct list_head delayed_iputs; | 1540 | struct list_head delayed_iputs; |
1541 | struct rw_semaphore delayed_iput_sem; | ||
1541 | 1542 | ||
1542 | /* this protects tree_mod_seq_list */ | 1543 | /* this protects tree_mod_seq_list */ |
1543 | spinlock_t tree_mod_seq_lock; | 1544 | spinlock_t tree_mod_seq_lock; |
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 62cd3b63190d..2ef9a4b72d06 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c | |||
@@ -2487,11 +2487,12 @@ int open_ctree(struct super_block *sb, | |||
2487 | spin_lock_init(&fs_info->qgroup_op_lock); | 2487 | spin_lock_init(&fs_info->qgroup_op_lock); |
2488 | spin_lock_init(&fs_info->buffer_lock); | 2488 | spin_lock_init(&fs_info->buffer_lock); |
2489 | spin_lock_init(&fs_info->unused_bgs_lock); | 2489 | spin_lock_init(&fs_info->unused_bgs_lock); |
2490 | mutex_init(&fs_info->unused_bg_unpin_mutex); | ||
2491 | rwlock_init(&fs_info->tree_mod_log_lock); | 2490 | rwlock_init(&fs_info->tree_mod_log_lock); |
2491 | mutex_init(&fs_info->unused_bg_unpin_mutex); | ||
2492 | mutex_init(&fs_info->reloc_mutex); | 2492 | mutex_init(&fs_info->reloc_mutex); |
2493 | mutex_init(&fs_info->delalloc_root_mutex); | 2493 | mutex_init(&fs_info->delalloc_root_mutex); |
2494 | seqlock_init(&fs_info->profiles_lock); | 2494 | seqlock_init(&fs_info->profiles_lock); |
2495 | init_rwsem(&fs_info->delayed_iput_sem); | ||
2495 | 2496 | ||
2496 | init_completion(&fs_info->kobj_unregister); | 2497 | init_completion(&fs_info->kobj_unregister); |
2497 | INIT_LIST_HEAD(&fs_info->dirty_cowonly_roots); | 2498 | INIT_LIST_HEAD(&fs_info->dirty_cowonly_roots); |
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index be4a79a69ed1..46cb1d414912 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
@@ -3950,6 +3950,12 @@ commit_trans: | |||
3950 | ret = btrfs_commit_transaction(trans, root); | 3950 | ret = btrfs_commit_transaction(trans, root); |
3951 | if (ret) | 3951 | if (ret) |
3952 | return ret; | 3952 | return ret; |
3953 | /* | ||
3954 | * make sure that all running delayed iput are | ||
3955 | * done | ||
3956 | */ | ||
3957 | down_write(&root->fs_info->delayed_iput_sem); | ||
3958 | up_write(&root->fs_info->delayed_iput_sem); | ||
3953 | goto again; | 3959 | goto again; |
3954 | } else { | 3960 | } else { |
3955 | btrfs_end_transaction(trans, root); | 3961 | btrfs_end_transaction(trans, root); |
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 141df0ce6f93..6ef97c184c7b 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
@@ -3111,6 +3111,8 @@ void btrfs_run_delayed_iputs(struct btrfs_root *root) | |||
3111 | if (empty) | 3111 | if (empty) |
3112 | return; | 3112 | return; |
3113 | 3113 | ||
3114 | down_read(&fs_info->delayed_iput_sem); | ||
3115 | |||
3114 | spin_lock(&fs_info->delayed_iput_lock); | 3116 | spin_lock(&fs_info->delayed_iput_lock); |
3115 | list_splice_init(&fs_info->delayed_iputs, &list); | 3117 | list_splice_init(&fs_info->delayed_iputs, &list); |
3116 | spin_unlock(&fs_info->delayed_iput_lock); | 3118 | spin_unlock(&fs_info->delayed_iput_lock); |
@@ -3121,6 +3123,8 @@ void btrfs_run_delayed_iputs(struct btrfs_root *root) | |||
3121 | iput(delayed->inode); | 3123 | iput(delayed->inode); |
3122 | kfree(delayed); | 3124 | kfree(delayed); |
3123 | } | 3125 | } |
3126 | |||
3127 | up_read(&root->fs_info->delayed_iput_sem); | ||
3124 | } | 3128 | } |
3125 | 3129 | ||
3126 | /* | 3130 | /* |