Skip to content

Commit

Permalink
Btrfs: fix the same inode id problem when doing auto defragment
Browse files Browse the repository at this point in the history
Two files in the different subvolumes may have the same inode id, so
The rb-tree which is used to manage the defragment object must take it
into account. This patch fix this problem.

Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
  • Loading branch information
Miao Xie authored and Josef Bacik committed May 30, 2012
1 parent 2adcac1 commit 762f226
Showing 1 changed file with 39 additions and 10 deletions.
49 changes: 39 additions & 10 deletions fs/btrfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,21 @@ struct inode_defrag {
int cycled;
};

static int __compare_inode_defrag(struct inode_defrag *defrag1,
struct inode_defrag *defrag2)
{
if (defrag1->root > defrag2->root)
return 1;
else if (defrag1->root < defrag2->root)
return -1;
else if (defrag1->ino > defrag2->ino)
return 1;
else if (defrag1->ino < defrag2->ino)
return -1;
else
return 0;
}

/* pop a record for an inode into the defrag tree. The lock
* must be held already
*
Expand All @@ -81,15 +96,17 @@ static void __btrfs_add_inode_defrag(struct inode *inode,
struct inode_defrag *entry;
struct rb_node **p;
struct rb_node *parent = NULL;
int ret;

p = &root->fs_info->defrag_inodes.rb_node;
while (*p) {
parent = *p;
entry = rb_entry(parent, struct inode_defrag, rb_node);

if (defrag->ino < entry->ino)
ret = __compare_inode_defrag(defrag, entry);
if (ret < 0)
p = &parent->rb_left;
else if (defrag->ino > entry->ino)
else if (ret > 0)
p = &parent->rb_right;
else {
/* if we're reinserting an entry for
Expand Down Expand Up @@ -159,28 +176,35 @@ int btrfs_add_inode_defrag(struct btrfs_trans_handle *trans,
/*
* must be called with the defrag_inodes lock held
*/
struct inode_defrag *btrfs_find_defrag_inode(struct btrfs_fs_info *info, u64 ino,
struct inode_defrag *btrfs_find_defrag_inode(struct btrfs_fs_info *info,
u64 root, u64 ino,
struct rb_node **next)
{
struct inode_defrag *entry = NULL;
struct inode_defrag tmp;
struct rb_node *p;
struct rb_node *parent = NULL;
int ret;

tmp.ino = ino;
tmp.root = root;

p = info->defrag_inodes.rb_node;
while (p) {
parent = p;
entry = rb_entry(parent, struct inode_defrag, rb_node);

if (ino < entry->ino)
ret = __compare_inode_defrag(&tmp, entry);
if (ret < 0)
p = parent->rb_left;
else if (ino > entry->ino)
else if (ret > 0)
p = parent->rb_right;
else
return entry;
}

if (next) {
while (parent && ino > entry->ino) {
while (parent && __compare_inode_defrag(&tmp, entry) > 0) {
parent = rb_next(parent);
entry = rb_entry(parent, struct inode_defrag, rb_node);
}
Expand All @@ -202,6 +226,7 @@ int btrfs_run_defrag_inodes(struct btrfs_fs_info *fs_info)
struct btrfs_key key;
struct btrfs_ioctl_defrag_range_args range;
u64 first_ino = 0;
u64 root_objectid = 0;
int num_defrag;
int defrag_batch = 1024;

Expand All @@ -214,11 +239,14 @@ int btrfs_run_defrag_inodes(struct btrfs_fs_info *fs_info)
n = NULL;

/* find an inode to defrag */
defrag = btrfs_find_defrag_inode(fs_info, first_ino, &n);
defrag = btrfs_find_defrag_inode(fs_info, root_objectid,
first_ino, &n);
if (!defrag) {
if (n)
defrag = rb_entry(n, struct inode_defrag, rb_node);
else if (first_ino) {
if (n) {
defrag = rb_entry(n, struct inode_defrag,
rb_node);
} else if (root_objectid || first_ino) {
root_objectid = 0;
first_ino = 0;
continue;
} else {
Expand All @@ -228,6 +256,7 @@ int btrfs_run_defrag_inodes(struct btrfs_fs_info *fs_info)

/* remove it from the rbtree */
first_ino = defrag->ino + 1;
root_objectid = defrag->root;
rb_erase(&defrag->rb_node, &fs_info->defrag_inodes);

if (btrfs_fs_closing(fs_info))
Expand Down

0 comments on commit 762f226

Please sign in to comment.