diff options
| author | Miao Xie <miaox@cn.fujitsu.com> | 2014-04-23 07:33:36 -0400 |
|---|---|---|
| committer | Chris Mason <clm@fb.com> | 2014-04-24 19:43:33 -0400 |
| commit | 1c70d8fb4dfa95bee491816b2a6767b5ca1080e7 (patch) | |
| tree | c866788e0d25fb2f2d1621b1e2e651319a8114b9 | |
| parent | 28c16cbbc32781224309e50cc99c684f2498bc59 (diff) | |
Btrfs: fix inode caching vs tree log
Currently, with inode cache enabled, we will reuse its inode id immediately
after unlinking file, we may hit something like following:
|->iput inode
|->return inode id into inode cache
|->create dir,fsync
|->power off
An easy way to reproduce this problem is:
mkfs.btrfs -f /dev/sdb
mount /dev/sdb /mnt -o inode_cache,commit=100
dd if=/dev/zero of=/mnt/data bs=1M count=10 oflag=sync
inode_id=`ls -i /mnt/data | awk '{print $1}'`
rm -f /mnt/data
i=1
while [ 1 ]
do
mkdir /mnt/dir_$i
test1=`stat /mnt/dir_$i | grep Inode: | awk '{print $4}'`
if [ $test1 -eq $inode_id ]
then
dd if=/dev/zero of=/mnt/dir_$i/data bs=1M count=1 oflag=sync
echo b > /proc/sysrq-trigger
fi
sleep 1
i=$(($i+1))
done
mount /dev/sdb /mnt
umount /dev/sdb
btrfs check /dev/sdb
We fix this problem by adding unlinked inode's id into pinned tree,
and we can not reuse them until committing transaction.
Cc: stable@vger.kernel.org
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
Signed-off-by: Wang Shilong <wangsl.fnst@cn.fujitsu.com>
Signed-off-by: Chris Mason <clm@fb.com>
| -rw-r--r-- | fs/btrfs/inode-map.c | 18 |
1 files changed, 2 insertions, 16 deletions
diff --git a/fs/btrfs/inode-map.c b/fs/btrfs/inode-map.c index 8ad529e3e67e..86935f5ae291 100644 --- a/fs/btrfs/inode-map.c +++ b/fs/btrfs/inode-map.c | |||
| @@ -209,24 +209,14 @@ again: | |||
| 209 | 209 | ||
| 210 | void btrfs_return_ino(struct btrfs_root *root, u64 objectid) | 210 | void btrfs_return_ino(struct btrfs_root *root, u64 objectid) |
| 211 | { | 211 | { |
| 212 | struct btrfs_free_space_ctl *ctl = root->free_ino_ctl; | ||
| 213 | struct btrfs_free_space_ctl *pinned = root->free_ino_pinned; | 212 | struct btrfs_free_space_ctl *pinned = root->free_ino_pinned; |
| 214 | 213 | ||
| 215 | if (!btrfs_test_opt(root, INODE_MAP_CACHE)) | 214 | if (!btrfs_test_opt(root, INODE_MAP_CACHE)) |
| 216 | return; | 215 | return; |
| 217 | |||
| 218 | again: | 216 | again: |
| 219 | if (root->cached == BTRFS_CACHE_FINISHED) { | 217 | if (root->cached == BTRFS_CACHE_FINISHED) { |
| 220 | __btrfs_add_free_space(ctl, objectid, 1); | 218 | __btrfs_add_free_space(pinned, objectid, 1); |
| 221 | } else { | 219 | } else { |
| 222 | /* | ||
| 223 | * If we are in the process of caching free ino chunks, | ||
| 224 | * to avoid adding the same inode number to the free_ino | ||
| 225 | * tree twice due to cross transaction, we'll leave it | ||
| 226 | * in the pinned tree until a transaction is committed | ||
| 227 | * or the caching work is done. | ||
| 228 | */ | ||
| 229 | |||
| 230 | down_write(&root->fs_info->commit_root_sem); | 220 | down_write(&root->fs_info->commit_root_sem); |
| 231 | spin_lock(&root->cache_lock); | 221 | spin_lock(&root->cache_lock); |
| 232 | if (root->cached == BTRFS_CACHE_FINISHED) { | 222 | if (root->cached == BTRFS_CACHE_FINISHED) { |
| @@ -238,11 +228,7 @@ again: | |||
| 238 | 228 | ||
| 239 | start_caching(root); | 229 | start_caching(root); |
| 240 | 230 | ||
| 241 | if (objectid <= root->cache_progress || | 231 | __btrfs_add_free_space(pinned, objectid, 1); |
| 242 | objectid >= root->highest_objectid) | ||
| 243 | __btrfs_add_free_space(ctl, objectid, 1); | ||
| 244 | else | ||
| 245 | __btrfs_add_free_space(pinned, objectid, 1); | ||
| 246 | 232 | ||
| 247 | up_write(&root->fs_info->commit_root_sem); | 233 | up_write(&root->fs_info->commit_root_sem); |
| 248 | } | 234 | } |
