Commit 76dda93c authored by Yan, Zheng's avatar Yan, Zheng Committed by Chris Mason

Btrfs: add snapshot/subvolume destroy ioctl

This patch adds snapshot/subvolume destroy ioctl.  A subvolume that isn't being
used and doesn't contains links to other subvolumes can be destroyed.
Signed-off-by: default avatarYan Zheng <zheng.yan@oracle.com>
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent 4df27c4d
...@@ -839,9 +839,7 @@ struct btrfs_fs_info { ...@@ -839,9 +839,7 @@ struct btrfs_fs_info {
struct mutex transaction_kthread_mutex; struct mutex transaction_kthread_mutex;
struct mutex cleaner_mutex; struct mutex cleaner_mutex;
struct mutex chunk_mutex; struct mutex chunk_mutex;
struct mutex drop_mutex;
struct mutex volume_mutex; struct mutex volume_mutex;
struct mutex tree_reloc_mutex;
/* /*
* this protects the ordered operations list only while we are * this protects the ordered operations list only while we are
* processing all of the entries on it. This way we make * processing all of the entries on it. This way we make
...@@ -852,6 +850,10 @@ struct btrfs_fs_info { ...@@ -852,6 +850,10 @@ struct btrfs_fs_info {
struct mutex ordered_operations_mutex; struct mutex ordered_operations_mutex;
struct rw_semaphore extent_commit_sem; struct rw_semaphore extent_commit_sem;
struct rw_semaphore subvol_sem;
struct srcu_struct subvol_srcu;
struct list_head trans_list; struct list_head trans_list;
struct list_head hashers; struct list_head hashers;
struct list_head dead_roots; struct list_head dead_roots;
...@@ -2142,6 +2144,7 @@ int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, struct ...@@ -2142,6 +2144,7 @@ int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, struct
int btrfs_search_root(struct btrfs_root *root, u64 search_start, int btrfs_search_root(struct btrfs_root *root, u64 search_start,
u64 *found_objectid); u64 *found_objectid);
int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid); int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid);
int btrfs_find_orphan_roots(struct btrfs_root *tree_root);
int btrfs_set_root_node(struct btrfs_root_item *item, int btrfs_set_root_node(struct btrfs_root_item *item,
struct extent_buffer *node); struct extent_buffer *node);
/* dir-item.c */ /* dir-item.c */
...@@ -2273,7 +2276,7 @@ int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end); ...@@ -2273,7 +2276,7 @@ int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end);
int btrfs_writepages(struct address_space *mapping, int btrfs_writepages(struct address_space *mapping,
struct writeback_control *wbc); struct writeback_control *wbc);
int btrfs_create_subvol_root(struct btrfs_trans_handle *trans, int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
struct btrfs_root *new_root, struct dentry *dentry, struct btrfs_root *new_root,
u64 new_dirid, u64 alloc_hint); u64 new_dirid, u64 alloc_hint);
int btrfs_merge_bio_hook(struct page *page, unsigned long offset, int btrfs_merge_bio_hook(struct page *page, unsigned long offset,
size_t size, struct bio *bio, unsigned long bio_flags); size_t size, struct bio *bio, unsigned long bio_flags);
...@@ -2289,6 +2292,7 @@ int btrfs_write_inode(struct inode *inode, int wait); ...@@ -2289,6 +2292,7 @@ int btrfs_write_inode(struct inode *inode, int wait);
void btrfs_dirty_inode(struct inode *inode); void btrfs_dirty_inode(struct inode *inode);
struct inode *btrfs_alloc_inode(struct super_block *sb); struct inode *btrfs_alloc_inode(struct super_block *sb);
void btrfs_destroy_inode(struct inode *inode); void btrfs_destroy_inode(struct inode *inode);
void btrfs_drop_inode(struct inode *inode);
int btrfs_init_cachep(void); int btrfs_init_cachep(void);
void btrfs_destroy_cachep(void); void btrfs_destroy_cachep(void);
long btrfs_ioctl_trans_end(struct file *file); long btrfs_ioctl_trans_end(struct file *file);
...@@ -2306,6 +2310,8 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans, struct inode *inode); ...@@ -2306,6 +2310,8 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans, struct inode *inode);
int btrfs_orphan_del(struct btrfs_trans_handle *trans, struct inode *inode); int btrfs_orphan_del(struct btrfs_trans_handle *trans, struct inode *inode);
void btrfs_orphan_cleanup(struct btrfs_root *root); void btrfs_orphan_cleanup(struct btrfs_root *root);
int btrfs_cont_expand(struct inode *inode, loff_t size); int btrfs_cont_expand(struct inode *inode, loff_t size);
int btrfs_invalidate_inodes(struct btrfs_root *root);
extern struct dentry_operations btrfs_dentry_operations;
/* ioctl.c */ /* ioctl.c */
long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg); long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
......
...@@ -1378,8 +1378,10 @@ static int setup_bdi(struct btrfs_fs_info *info, struct backing_dev_info *bdi) ...@@ -1378,8 +1378,10 @@ static int setup_bdi(struct btrfs_fs_info *info, struct backing_dev_info *bdi)
err = bdi_register(bdi, NULL, "btrfs-%d", err = bdi_register(bdi, NULL, "btrfs-%d",
atomic_inc_return(&btrfs_bdi_num)); atomic_inc_return(&btrfs_bdi_num));
if (err) if (err) {
bdi_destroy(bdi);
return err; return err;
}
bdi->ra_pages = default_backing_dev_info.ra_pages; bdi->ra_pages = default_backing_dev_info.ra_pages;
bdi->unplug_io_fn = btrfs_unplug_io_fn; bdi->unplug_io_fn = btrfs_unplug_io_fn;
...@@ -1469,9 +1471,12 @@ static int cleaner_kthread(void *arg) ...@@ -1469,9 +1471,12 @@ static int cleaner_kthread(void *arg)
break; break;
vfs_check_frozen(root->fs_info->sb, SB_FREEZE_WRITE); vfs_check_frozen(root->fs_info->sb, SB_FREEZE_WRITE);
mutex_lock(&root->fs_info->cleaner_mutex);
btrfs_clean_old_snapshots(root); if (!(root->fs_info->sb->s_flags & MS_RDONLY) &&
mutex_unlock(&root->fs_info->cleaner_mutex); mutex_trylock(&root->fs_info->cleaner_mutex)) {
btrfs_clean_old_snapshots(root);
mutex_unlock(&root->fs_info->cleaner_mutex);
}
if (freezing(current)) { if (freezing(current)) {
refrigerator(); refrigerator();
...@@ -1576,7 +1581,26 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1576,7 +1581,26 @@ struct btrfs_root *open_ctree(struct super_block *sb,
err = -ENOMEM; err = -ENOMEM;
goto fail; goto fail;
} }
INIT_RADIX_TREE(&fs_info->fs_roots_radix, GFP_NOFS);
ret = init_srcu_struct(&fs_info->subvol_srcu);
if (ret) {
err = ret;
goto fail;
}
ret = setup_bdi(fs_info, &fs_info->bdi);
if (ret) {
err = ret;
goto fail_srcu;
}
fs_info->btree_inode = new_inode(sb);
if (!fs_info->btree_inode) {
err = -ENOMEM;
goto fail_bdi;
}
INIT_RADIX_TREE(&fs_info->fs_roots_radix, GFP_ATOMIC);
INIT_LIST_HEAD(&fs_info->trans_list); INIT_LIST_HEAD(&fs_info->trans_list);
INIT_LIST_HEAD(&fs_info->dead_roots); INIT_LIST_HEAD(&fs_info->dead_roots);
INIT_LIST_HEAD(&fs_info->hashers); INIT_LIST_HEAD(&fs_info->hashers);
...@@ -1586,6 +1610,7 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1586,6 +1610,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
spin_lock_init(&fs_info->delalloc_lock); spin_lock_init(&fs_info->delalloc_lock);
spin_lock_init(&fs_info->new_trans_lock); spin_lock_init(&fs_info->new_trans_lock);
spin_lock_init(&fs_info->ref_cache_lock); spin_lock_init(&fs_info->ref_cache_lock);
spin_lock_init(&fs_info->fs_roots_radix_lock);
init_completion(&fs_info->kobj_unregister); init_completion(&fs_info->kobj_unregister);
fs_info->tree_root = tree_root; fs_info->tree_root = tree_root;
...@@ -1604,11 +1629,6 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1604,11 +1629,6 @@ struct btrfs_root *open_ctree(struct super_block *sb,
fs_info->sb = sb; fs_info->sb = sb;
fs_info->max_extent = (u64)-1; fs_info->max_extent = (u64)-1;
fs_info->max_inline = 8192 * 1024; fs_info->max_inline = 8192 * 1024;
if (setup_bdi(fs_info, &fs_info->bdi))
goto fail_bdi;
fs_info->btree_inode = new_inode(sb);
fs_info->btree_inode->i_ino = 1;
fs_info->btree_inode->i_nlink = 1;
fs_info->metadata_ratio = 8; fs_info->metadata_ratio = 8;
fs_info->thread_pool_size = min_t(unsigned long, fs_info->thread_pool_size = min_t(unsigned long,
...@@ -1620,6 +1640,8 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1620,6 +1640,8 @@ struct btrfs_root *open_ctree(struct super_block *sb,
sb->s_blocksize = 4096; sb->s_blocksize = 4096;
sb->s_blocksize_bits = blksize_bits(4096); sb->s_blocksize_bits = blksize_bits(4096);
fs_info->btree_inode->i_ino = BTRFS_BTREE_INODE_OBJECTID;
fs_info->btree_inode->i_nlink = 1;
/* /*
* we set the i_size on the btree inode to the max possible int. * we set the i_size on the btree inode to the max possible int.
* the real end of the address space is determined by all of * the real end of the address space is determined by all of
...@@ -1638,6 +1660,11 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1638,6 +1660,11 @@ struct btrfs_root *open_ctree(struct super_block *sb,
BTRFS_I(fs_info->btree_inode)->io_tree.ops = &btree_extent_io_ops; BTRFS_I(fs_info->btree_inode)->io_tree.ops = &btree_extent_io_ops;
BTRFS_I(fs_info->btree_inode)->root = tree_root;
memset(&BTRFS_I(fs_info->btree_inode)->location, 0,
sizeof(struct btrfs_key));
BTRFS_I(fs_info->btree_inode)->dummy_inode = 1;
spin_lock_init(&fs_info->block_group_cache_lock); spin_lock_init(&fs_info->block_group_cache_lock);
fs_info->block_group_cache_tree.rb_node = NULL; fs_info->block_group_cache_tree.rb_node = NULL;
...@@ -1648,21 +1675,16 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1648,21 +1675,16 @@ struct btrfs_root *open_ctree(struct super_block *sb,
fs_info->pinned_extents = &fs_info->freed_extents[0]; fs_info->pinned_extents = &fs_info->freed_extents[0];
fs_info->do_barriers = 1; fs_info->do_barriers = 1;
BTRFS_I(fs_info->btree_inode)->root = tree_root;
memset(&BTRFS_I(fs_info->btree_inode)->location, 0,
sizeof(struct btrfs_key));
insert_inode_hash(fs_info->btree_inode);
mutex_init(&fs_info->trans_mutex); mutex_init(&fs_info->trans_mutex);
mutex_init(&fs_info->ordered_operations_mutex); mutex_init(&fs_info->ordered_operations_mutex);
mutex_init(&fs_info->tree_log_mutex); mutex_init(&fs_info->tree_log_mutex);
mutex_init(&fs_info->drop_mutex);
mutex_init(&fs_info->chunk_mutex); mutex_init(&fs_info->chunk_mutex);
mutex_init(&fs_info->transaction_kthread_mutex); mutex_init(&fs_info->transaction_kthread_mutex);
mutex_init(&fs_info->cleaner_mutex); mutex_init(&fs_info->cleaner_mutex);
mutex_init(&fs_info->volume_mutex); mutex_init(&fs_info->volume_mutex);
mutex_init(&fs_info->tree_reloc_mutex);
init_rwsem(&fs_info->extent_commit_sem); init_rwsem(&fs_info->extent_commit_sem);
init_rwsem(&fs_info->subvol_sem);
btrfs_init_free_cluster(&fs_info->meta_alloc_cluster); btrfs_init_free_cluster(&fs_info->meta_alloc_cluster);
btrfs_init_free_cluster(&fs_info->data_alloc_cluster); btrfs_init_free_cluster(&fs_info->data_alloc_cluster);
...@@ -1941,6 +1963,9 @@ printk("thread pool is %d\n", fs_info->thread_pool_size); ...@@ -1941,6 +1963,9 @@ printk("thread pool is %d\n", fs_info->thread_pool_size);
} }
} }
ret = btrfs_find_orphan_roots(tree_root);
BUG_ON(ret);
if (!(sb->s_flags & MS_RDONLY)) { if (!(sb->s_flags & MS_RDONLY)) {
ret = btrfs_recover_relocation(tree_root); ret = btrfs_recover_relocation(tree_root);
BUG_ON(ret); BUG_ON(ret);
...@@ -2000,6 +2025,8 @@ printk("thread pool is %d\n", fs_info->thread_pool_size); ...@@ -2000,6 +2025,8 @@ printk("thread pool is %d\n", fs_info->thread_pool_size);
btrfs_mapping_tree_free(&fs_info->mapping_tree); btrfs_mapping_tree_free(&fs_info->mapping_tree);
fail_bdi: fail_bdi:
bdi_destroy(&fs_info->bdi); bdi_destroy(&fs_info->bdi);
fail_srcu:
cleanup_srcu_struct(&fs_info->subvol_srcu);
fail: fail:
kfree(extent_root); kfree(extent_root);
kfree(tree_root); kfree(tree_root);
...@@ -2263,6 +2290,10 @@ int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root) ...@@ -2263,6 +2290,10 @@ int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root)
radix_tree_delete(&fs_info->fs_roots_radix, radix_tree_delete(&fs_info->fs_roots_radix,
(unsigned long)root->root_key.objectid); (unsigned long)root->root_key.objectid);
spin_unlock(&fs_info->fs_roots_radix_lock); spin_unlock(&fs_info->fs_roots_radix_lock);
if (btrfs_root_refs(&root->root_item) == 0)
synchronize_srcu(&fs_info->subvol_srcu);
free_fs_root(root); free_fs_root(root);
return 0; return 0;
} }
...@@ -2286,6 +2317,20 @@ static int del_fs_roots(struct btrfs_fs_info *fs_info) ...@@ -2286,6 +2317,20 @@ static int del_fs_roots(struct btrfs_fs_info *fs_info)
struct btrfs_root *gang[8]; struct btrfs_root *gang[8];
int i; int i;
while (!list_empty(&fs_info->dead_roots)) {
gang[0] = list_entry(fs_info->dead_roots.next,
struct btrfs_root, root_list);
list_del(&gang[0]->root_list);
if (gang[0]->in_radix) {
btrfs_free_fs_root(fs_info, gang[0]);
} else {
free_extent_buffer(gang[0]->node);
free_extent_buffer(gang[0]->commit_root);
kfree(gang[0]);
}
}
while (1) { while (1) {
ret = radix_tree_gang_lookup(&fs_info->fs_roots_radix, ret = radix_tree_gang_lookup(&fs_info->fs_roots_radix,
(void **)gang, 0, (void **)gang, 0,
...@@ -2315,9 +2360,6 @@ int btrfs_cleanup_fs_roots(struct btrfs_fs_info *fs_info) ...@@ -2315,9 +2360,6 @@ int btrfs_cleanup_fs_roots(struct btrfs_fs_info *fs_info)
root_objectid = gang[ret - 1]->root_key.objectid + 1; root_objectid = gang[ret - 1]->root_key.objectid + 1;
for (i = 0; i < ret; i++) { for (i = 0; i < ret; i++) {
root_objectid = gang[i]->root_key.objectid; root_objectid = gang[i]->root_key.objectid;
ret = btrfs_find_dead_roots(fs_info->tree_root,
root_objectid);
BUG_ON(ret);
btrfs_orphan_cleanup(gang[i]); btrfs_orphan_cleanup(gang[i]);
} }
root_objectid++; root_objectid++;
...@@ -2405,6 +2447,7 @@ int close_ctree(struct btrfs_root *root) ...@@ -2405,6 +2447,7 @@ int close_ctree(struct btrfs_root *root)
btrfs_mapping_tree_free(&fs_info->mapping_tree); btrfs_mapping_tree_free(&fs_info->mapping_tree);
bdi_destroy(&fs_info->bdi); bdi_destroy(&fs_info->bdi);
cleanup_srcu_struct(&fs_info->subvol_srcu);
kfree(fs_info->extent_root); kfree(fs_info->extent_root);
kfree(fs_info->tree_root); kfree(fs_info->tree_root);
......
...@@ -28,7 +28,7 @@ static int btrfs_encode_fh(struct dentry *dentry, u32 *fh, int *max_len, ...@@ -28,7 +28,7 @@ static int btrfs_encode_fh(struct dentry *dentry, u32 *fh, int *max_len,
len = BTRFS_FID_SIZE_NON_CONNECTABLE; len = BTRFS_FID_SIZE_NON_CONNECTABLE;
type = FILEID_BTRFS_WITHOUT_PARENT; type = FILEID_BTRFS_WITHOUT_PARENT;
fid->objectid = BTRFS_I(inode)->location.objectid; fid->objectid = inode->i_ino;
fid->root_objectid = BTRFS_I(inode)->root->objectid; fid->root_objectid = BTRFS_I(inode)->root->objectid;
fid->gen = inode->i_generation; fid->gen = inode->i_generation;
...@@ -60,34 +60,61 @@ static int btrfs_encode_fh(struct dentry *dentry, u32 *fh, int *max_len, ...@@ -60,34 +60,61 @@ static int btrfs_encode_fh(struct dentry *dentry, u32 *fh, int *max_len,
} }
static struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid, static struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid,
u64 root_objectid, u32 generation) u64 root_objectid, u32 generation,
int check_generation)
{ {
struct btrfs_fs_info *fs_info = btrfs_sb(sb)->fs_info;
struct btrfs_root *root; struct btrfs_root *root;
struct dentry *dentry;
struct inode *inode; struct inode *inode;
struct btrfs_key key; struct btrfs_key key;
int index;
int err = 0;
if (objectid < BTRFS_FIRST_FREE_OBJECTID)
return ERR_PTR(-ESTALE);
key.objectid = root_objectid; key.objectid = root_objectid;
btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
key.offset = (u64)-1; key.offset = (u64)-1;
root = btrfs_read_fs_root_no_name(btrfs_sb(sb)->fs_info, &key); index = srcu_read_lock(&fs_info->subvol_srcu);
if (IS_ERR(root))
return ERR_CAST(root); root = btrfs_read_fs_root_no_name(fs_info, &key);
if (IS_ERR(root)) {
err = PTR_ERR(root);
goto fail;
}
if (btrfs_root_refs(&root->root_item) == 0) {
err = -ENOENT;
goto fail;
}
key.objectid = objectid; key.objectid = objectid;
btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY); btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);
key.offset = 0; key.offset = 0;
inode = btrfs_iget(sb, &key, root); inode = btrfs_iget(sb, &key, root);
if (IS_ERR(inode)) if (IS_ERR(inode)) {
return (void *)inode; err = PTR_ERR(inode);
goto fail;
}
srcu_read_unlock(&fs_info->subvol_srcu, index);
if (generation != inode->i_generation) { if (check_generation && generation != inode->i_generation) {
iput(inode); iput(inode);
return ERR_PTR(-ESTALE); return ERR_PTR(-ESTALE);
} }
return d_obtain_alias(inode); dentry = d_obtain_alias(inode);
if (!IS_ERR(dentry))
dentry->d_op = &btrfs_dentry_operations;
return dentry;
fail:
srcu_read_unlock(&fs_info->subvol_srcu, index);
return ERR_PTR(err);
} }
static struct dentry *btrfs_fh_to_parent(struct super_block *sb, struct fid *fh, static struct dentry *btrfs_fh_to_parent(struct super_block *sb, struct fid *fh,
...@@ -111,7 +138,7 @@ static struct dentry *btrfs_fh_to_parent(struct super_block *sb, struct fid *fh, ...@@ -111,7 +138,7 @@ static struct dentry *btrfs_fh_to_parent(struct super_block *sb, struct fid *fh,
objectid = fid->parent_objectid; objectid = fid->parent_objectid;
generation = fid->parent_gen; generation = fid->parent_gen;
return btrfs_get_dentry(sb, objectid, root_objectid, generation); return btrfs_get_dentry(sb, objectid, root_objectid, generation, 1);
} }
static struct dentry *btrfs_fh_to_dentry(struct super_block *sb, struct fid *fh, static struct dentry *btrfs_fh_to_dentry(struct super_block *sb, struct fid *fh,
...@@ -133,66 +160,76 @@ static struct dentry *btrfs_fh_to_dentry(struct super_block *sb, struct fid *fh, ...@@ -133,66 +160,76 @@ static struct dentry *btrfs_fh_to_dentry(struct super_block *sb, struct fid *fh,
root_objectid = fid->root_objectid; root_objectid = fid->root_objectid;
generation = fid->gen; generation = fid->gen;
return btrfs_get_dentry(sb, objectid, root_objectid, generation); return btrfs_get_dentry(sb, objectid, root_objectid, generation, 1);
} }
static struct dentry *btrfs_get_parent(struct dentry *child) static struct dentry *btrfs_get_parent(struct dentry *child)
{ {
struct inode *dir = child->d_inode; struct inode *dir = child->d_inode;
static struct dentry *dentry;
struct btrfs_root *root = BTRFS_I(dir)->root; struct btrfs_root *root = BTRFS_I(dir)->root;
struct btrfs_key key;
struct btrfs_path *path; struct btrfs_path *path;
struct extent_buffer *leaf; struct extent_buffer *leaf;
int slot; struct btrfs_root_ref *ref;
u64 objectid; struct btrfs_key key;
struct btrfs_key found_key;
int ret; int ret;
path = btrfs_alloc_path(); path = btrfs_alloc_path();
key.objectid = dir->i_ino; if (dir->i_ino == BTRFS_FIRST_FREE_OBJECTID) {
btrfs_set_key_type(&key, BTRFS_INODE_REF_KEY); key.objectid = root->root_key.objectid;
key.offset = (u64)-1; key.type = BTRFS_ROOT_BACKREF_KEY;
key.offset = (u64)-1;
root = root->fs_info->tree_root;
} else {
key.objectid = dir->i_ino;
key.type = BTRFS_INODE_REF_KEY;
key.offset = (u64)-1;
}
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
if (ret < 0) { if (ret < 0)
/* Error */ goto fail;
btrfs_free_path(path);
return ERR_PTR(ret); BUG_ON(ret == 0);
if (path->slots[0] == 0) {
ret = -ENOENT;
goto fail;
} }
path->slots[0]--;
leaf = path->nodes[0]; leaf = path->nodes[0];
slot = path->slots[0];
if (ret) { btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
/* btrfs_search_slot() returns the slot where we'd want to if (found_key.objectid != key.objectid || found_key.type != key.type) {
insert a backref for parent inode #0xFFFFFFFFFFFFFFFF. ret = -ENOENT;
The _real_ backref, telling us what the parent inode goto fail;
_actually_ is, will be in the slot _before_ the one
that btrfs_search_slot() returns. */
if (!slot) {
/* Unless there is _no_ key in the tree before... */
btrfs_free_path(path);
return ERR_PTR(-EIO);
}
slot--;
} }
btrfs_item_key_to_cpu(leaf, &key, slot); if (found_key.type == BTRFS_ROOT_BACKREF_KEY) {
ref = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_root_ref);
key.objectid = btrfs_root_ref_dirid(leaf, ref);
} else {
key.objectid = found_key.offset;
}
btrfs_free_path(path); btrfs_free_path(path);
if (key.objectid != dir->i_ino || key.type != BTRFS_INODE_REF_KEY) if (found_key.type == BTRFS_ROOT_BACKREF_KEY) {
return ERR_PTR(-EINVAL); return btrfs_get_dentry(root->fs_info->sb, key.objectid,
found_key.offset, 0, 0);
objectid = key.offset; }
/* If we are already at the root of a subvol, return the real root */
if (objectid == dir->i_ino)
return dget(dir->i_sb->s_root);
/* Build a new key for the inode item */ key.type = BTRFS_INODE_ITEM_KEY;
key.objectid = objectid;
btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);
key.offset = 0; key.offset = 0;
dentry = d_obtain_alias(btrfs_iget(root->fs_info->sb, &key, root));
return d_obtain_alias(btrfs_iget(root->fs_info->sb, &key, root)); if (!IS_ERR(dentry))
dentry->d_op = &btrfs_dentry_operations;
return dentry;
fail:
btrfs_free_path(path);
return ERR_PTR(ret);
} }
const struct export_operations btrfs_export_ops = { const struct export_operations btrfs_export_ops = {
......
...@@ -5463,9 +5463,24 @@ int btrfs_drop_snapshot(struct btrfs_root *root, int update_ref) ...@@ -5463,9 +5463,24 @@ int btrfs_drop_snapshot(struct btrfs_root *root, int update_ref)
ret = btrfs_del_root(trans, tree_root, &root->root_key); ret = btrfs_del_root(trans, tree_root, &root->root_key);
BUG_ON(ret); BUG_ON(ret);
free_extent_buffer(root->node); if (root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID) {
free_extent_buffer(root->commit_root); ret = btrfs_find_last_root(tree_root, root->root_key.objectid,
kfree(root); NULL, NULL);
BUG_ON(ret < 0);
if (ret > 0) {
ret = btrfs_del_orphan_item(trans, tree_root,
root->root_key.objectid);
BUG_ON(ret);
}
}
if (root->in_radix) {
btrfs_free_fs_root(tree_root->fs_info, root);
} else {
free_extent_buffer(root->node);
free_extent_buffer(root->commit_root);
kfree(root);
}
out: out:
btrfs_end_transaction(trans, tree_root); btrfs_end_transaction(trans, tree_root);
kfree(wc); kfree(wc);
......
...@@ -3089,6 +3089,11 @@ void btrfs_delete_inode(struct inode *inode) ...@@ -3089,6 +3089,11 @@ void btrfs_delete_inode(struct inode *inode)
} }
btrfs_wait_ordered_range(inode, 0, (u64)-1); btrfs_wait_ordered_range(inode, 0, (u64)-1);
if (inode->i_nlink > 0) {
BUG_ON(btrfs_root_refs(&root->root_item) != 0);
goto no_delete;
}
btrfs_i_size_write(inode, 0); btrfs_i_size_write(inode, 0);
trans = btrfs_join_transaction(root, 1); trans = btrfs_join_transaction(root, 1);
...@@ -3225,11 +3230,13 @@ static void inode_tree_add(struct inode *inode) ...@@ -3225,11 +3230,13 @@ static void inode_tree_add(struct inode *inode)
struct btrfs_inode *entry; struct btrfs_inode *entry;
struct rb_node **p; struct rb_node **p;
struct rb_node *parent; struct rb_node *parent;
again: again:
p = &root->inode_tree.rb_node; p = &root->inode_tree.rb_node;
parent = NULL; parent = NULL;
if (hlist_unhashed(&inode->i_hash))
return;
spin_lock(&root->inode_lock); spin_lock(&root->inode_lock);
while (*p) { while (*p) {
parent = *p; parent = *p;
...@@ -3256,13 +3263,87 @@ static void inode_tree_add(struct inode *inode) ...@@ -3256,13 +3263,87 @@ static void inode_tree_add(struct inode *inode)
static void inode_tree_del(struct inode *inode) static void inode_tree_del(struct inode *inode)
{ {
struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_root *root = BTRFS_I(inode)->root;
int empty = 0;
spin_lock(&root->inode_lock); spin_lock(&root->inode_lock);
if (!RB_EMPTY_NODE(&BTRFS_I(inode)->rb_node)) { if (!RB_EMPTY_NODE(&BTRFS_I(inode)->rb_node)) {
rb_erase(&BTRFS_I(inode)->rb_node, &root->inode_tree); rb_erase(&BTRFS_I(inode)->rb_node, &root->inode_tree);
RB_CLEAR_NODE(&BTRFS_I(inode)->rb_node); RB_CLEAR_NODE(&BTRFS_I(inode)->rb_node);
empty = RB_EMPTY_ROOT(&root->inode_tree);
} }
spin_unlock(&root->inode_lock); spin_unlock(&root->inode_lock);
if (empty && btrfs_root_refs(&root->root_item) == 0) {
synchronize_srcu(&root->fs_info->subvol_srcu);
spin_lock(&root->inode_lock);
empty = RB_EMPTY_ROOT(&root->inode_tree);
spin_unlock(&root->inode_lock);
if (empty)
btrfs_add_dead_root(root);
}
}
int btrfs_invalidate_inodes(struct btrfs_root *root)
{
struct rb_node *node;
struct rb_node *prev;
struct btrfs_inode *entry;
struct inode *inode;
u64 objectid = 0;
WARN_ON(btrfs_root_refs(&root->root_item) != 0);
spin_lock(&root->inode_lock);
again:
node = root->inode_tree.rb_node;
prev = NULL;
while (node) {
prev = node;
entry = rb_entry(node, struct btrfs_inode, rb_node);
if (objectid < entry->vfs_inode.i_ino)
node = node->rb_left;
else if (objectid > entry->vfs_inode.i_ino)
node = node->rb_right;
else
break;
}
if (!node) {
while (prev) {
entry = rb_entry(prev, struct btrfs_inode, rb_node);
if (objectid <= entry->vfs_inode.i_ino) {
node = prev;
break;
}
prev = rb_next(prev);
}
}
while (node) {
entry = rb_entry(node, struct btrfs_inode, rb_node);
objectid = entry->vfs_inode.i_ino + 1;
inode = igrab(&entry->vfs_inode);
if (inode) {
spin_unlock(&root->inode_lock);
if (atomic_read(&inode->i_count) > 1)
d_prune_aliases(inode);
/*
* btrfs_drop_inode will remove it from
* the inode cache when its usage count
* hits zero.
*/
iput(inode);
cond_resched();
spin_lock(&root->inode_lock);
goto again;
}
if (cond_resched_lock(&root->inode_lock))
goto again;
node = rb_next(node);
}
spin_unlock(&root->inode_lock);
return 0;
} }
static noinline void init_btrfs_i(struct inode *inode) static noinline void init_btrfs_i(struct inode *inode)
...@@ -3379,8 +3460,11 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry) ...@@ -3379,8 +3460,11 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
struct btrfs_root *root = BTRFS_I(dir)->root; struct btrfs_root *root = BTRFS_I(dir)->root;
struct btrfs_root *sub_root = root; struct btrfs_root *sub_root = root;
struct btrfs_key location; struct btrfs_key location;
int index;
int ret; int ret;
dentry->d_op = &btrfs_dentry_operations;
if (dentry->d_name.len > BTRFS_NAME_LEN) if (dentry->d_name.len > BTRFS_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG); return ERR_PTR(-ENAMETOOLONG);
...@@ -3399,6 +3483,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry) ...@@ -3399,6 +3483,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
BUG_ON(location.type != BTRFS_ROOT_ITEM_KEY); BUG_ON(location.type != BTRFS_ROOT_ITEM_KEY);
index = srcu_read_lock(&root->fs_info->subvol_srcu);
ret = fixup_tree_root_location(root, dir, dentry, ret = fixup_tree_root_location(root, dir, dentry,
&location, &sub_root); &location, &sub_root);
if (ret < 0) { if (ret < 0) {
...@@ -3409,9 +3494,24 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry) ...@@ -3409,9 +3494,24 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
} else { } else {
inode = btrfs_iget(dir->i_sb, &location, sub_root); inode = btrfs_iget(dir->i_sb, &location, sub_root);
} }
srcu_read_unlock(&root->fs_info->subvol_srcu, index);
return inode; return inode;
} }
static int btrfs_dentry_delete(struct dentry *dentry)
{
struct btrfs_root *root;
if (!dentry->d_inode)
return 0;
root = BTRFS_I(dentry->d_inode)->root;
if (btrfs_root_refs(&root->root_item) == 0)
return 1;
return 0;
}
static struct dentry *btrfs_lookup(struct inode *dir, struct dentry *dentry, static struct dentry *btrfs_lookup(struct inode *dir, struct dentry *dentry,
struct nameidata *nd) struct nameidata *nd)
{ {
...@@ -4773,11 +4873,11 @@ static void btrfs_truncate(struct inode *inode) ...@@ -4773,11 +4873,11 @@ static void btrfs_truncate(struct inode *inode)
* create a new subvolume directory/inode (helper for the ioctl). * create a new subvolume directory/inode (helper for the ioctl).
*/ */
int btrfs_create_subvol_root(struct btrfs_trans_handle *trans, int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
struct btrfs_root *new_root, struct dentry *dentry, struct btrfs_root *new_root,
u64 new_dirid, u64 alloc_hint) u64 new_dirid, u64 alloc_hint)
{ {
struct inode *inode; struct inode *inode;
int error; int err;
u64 index = 0; u64 index = 0;
inode = btrfs_new_inode(trans, new_root, NULL, "..", 2, new_dirid, inode = btrfs_new_inode(trans, new_root, NULL, "..", 2, new_dirid,
...@@ -4790,11 +4890,10 @@ int btrfs_create_subvol_root(struct btrfs_trans_handle *trans, ...@@ -4790,11 +4890,10 @@ int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
inode->i_nlink = 1; inode->i_nlink = 1;
btrfs_i_size_write(inode, 0); btrfs_i_size_write(inode, 0);
error = btrfs_update_inode(trans, new_root, inode); err = btrfs_update_inode(trans, new_root, inode);
if (error) BUG_ON(err);
return error;
d_instantiate(dentry, inode); iput(inode);
return 0; return 0;
} }
...@@ -4872,6 +4971,16 @@ void btrfs_destroy_inode(struct inode *inode) ...@@ -4872,6 +4971,16 @@ void btrfs_destroy_inode(struct inode *inode)
kmem_cache_free(btrfs_inode_cachep, BTRFS_I(inode)); kmem_cache_free(btrfs_inode_cachep, BTRFS_I(inode));
} }
void btrfs_drop_inode(struct inode *inode)
{
struct btrfs_root *root = BTRFS_I(inode)->root;
if (inode->i_nlink > 0 && btrfs_root_refs(&root->root_item) == 0)
generic_delete_inode(inode);
else
generic_drop_inode(inode);
}
static void init_once(void *foo) static void init_once(void *foo)
{ {
struct btrfs_inode *ei = (struct btrfs_inode *) foo; struct btrfs_inode *ei = (struct btrfs_inode *) foo;
...@@ -4973,6 +5082,10 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -4973,6 +5082,10 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
old_inode->i_size > BTRFS_ORDERED_OPERATIONS_FLUSH_LIMIT) old_inode->i_size > BTRFS_ORDERED_OPERATIONS_FLUSH_LIMIT)
filemap_flush(old_inode->i_mapping); filemap_flush(old_inode->i_mapping);
/* close the racy window with snapshot create/destroy ioctl */
if (old_inode->i_ino == BTRFS_FIRST_FREE_OBJECTID)
down_read(&root->fs_info->subvol_sem);
trans = btrfs_start_transaction(root, 1); trans = btrfs_start_transaction(root, 1);
if (dest != root) if (dest != root)
...@@ -5062,6 +5175,8 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -5062,6 +5175,8 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
btrfs_end_transaction_throttle(trans, root); btrfs_end_transaction_throttle(trans, root);
if (old_inode->i_ino == BTRFS_FIRST_FREE_OBJECTID)
up_read(&root->fs_info->subvol_sem);
return ret; return ret;
} }
...@@ -5420,6 +5535,7 @@ static struct inode_operations btrfs_dir_ro_inode_operations = { ...@@ -5420,6 +5535,7 @@ static struct inode_operations btrfs_dir_ro_inode_operations = {
.lookup = btrfs_lookup, .lookup = btrfs_lookup,
.permission = btrfs_permission, .permission = btrfs_permission,
}; };
static struct file_operations btrfs_dir_file_operations = { static struct file_operations btrfs_dir_file_operations = {
.llseek = generic_file_llseek, .llseek = generic_file_llseek,
.read = generic_read_dir, .read = generic_read_dir,
...@@ -5506,3 +5622,7 @@ static struct inode_operations btrfs_symlink_inode_operations = { ...@@ -5506,3 +5622,7 @@ static struct inode_operations btrfs_symlink_inode_operations = {
.listxattr = btrfs_listxattr, .listxattr = btrfs_listxattr,
.removexattr = btrfs_removexattr, .removexattr = btrfs_removexattr,
}; };
struct dentry_operations btrfs_dentry_operations = {
.d_delete = btrfs_dentry_delete,
};
This diff is collapsed.
...@@ -65,5 +65,6 @@ struct btrfs_ioctl_clone_range_args { ...@@ -65,5 +65,6 @@ struct btrfs_ioctl_clone_range_args {
#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \ #define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \
struct btrfs_ioctl_vol_args) struct btrfs_ioctl_vol_args)
#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
struct btrfs_ioctl_vol_args)
#endif #endif
...@@ -3203,6 +3203,7 @@ static int check_extent_flags(u64 flags) ...@@ -3203,6 +3203,7 @@ static int check_extent_flags(u64 flags)
return 0; return 0;
} }
static noinline_for_stack int relocate_block_group(struct reloc_control *rc) static noinline_for_stack int relocate_block_group(struct reloc_control *rc)
{ {
struct rb_root blocks = RB_ROOT; struct rb_root blocks = RB_ROOT;
...@@ -3220,6 +3221,9 @@ static noinline_for_stack int relocate_block_group(struct reloc_control *rc) ...@@ -3220,6 +3221,9 @@ static noinline_for_stack int relocate_block_group(struct reloc_control *rc)
if (!path) if (!path)
return -ENOMEM; return -ENOMEM;
rc->extents_found = 0;
rc->extents_skipped = 0;
rc->search_start = rc->block_group->key.objectid; rc->search_start = rc->block_group->key.objectid;
clear_extent_bits(&rc->processed_blocks, 0, (u64)-1, EXTENT_DIRTY, clear_extent_bits(&rc->processed_blocks, 0, (u64)-1, EXTENT_DIRTY,
GFP_NOFS); GFP_NOFS);
...@@ -3475,14 +3479,15 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start) ...@@ -3475,14 +3479,15 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start)
btrfs_wait_ordered_extents(fs_info->tree_root, 0); btrfs_wait_ordered_extents(fs_info->tree_root, 0);
while (1) { while (1) {
mutex_lock(&fs_info->cleaner_mutex);
btrfs_clean_old_snapshots(fs_info->tree_root);
mutex_unlock(&fs_info->cleaner_mutex);
rc->extents_found = 0; rc->extents_found = 0;
rc->extents_skipped = 0; rc->extents_skipped = 0;
mutex_lock(&fs_info->cleaner_mutex);
btrfs_clean_old_snapshots(fs_info->tree_root);
ret = relocate_block_group(rc); ret = relocate_block_group(rc);
mutex_unlock(&fs_info->cleaner_mutex);
if (ret < 0) { if (ret < 0) {
err = ret; err = ret;
break; break;
...@@ -3530,6 +3535,26 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start) ...@@ -3530,6 +3535,26 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start)
return err; return err;
} }
static noinline_for_stack int mark_garbage_root(struct btrfs_root *root)
{
struct btrfs_trans_handle *trans;
int ret;
trans = btrfs_start_transaction(root->fs_info->tree_root, 1);
memset(&root->root_item.drop_progress, 0,
sizeof(root->root_item.drop_progress));
root->root_item.drop_level = 0;
btrfs_set_root_refs(&root->root_item, 0);
ret = btrfs_update_root(trans, root->fs_info->tree_root,
&root->root_key, &root->root_item);
BUG_ON(ret);
ret = btrfs_end_transaction(trans, root->fs_info->tree_root);
BUG_ON(ret);
return 0;
}
/* /*
* recover relocation interrupted by system crash. * recover relocation interrupted by system crash.
* *
...@@ -3589,8 +3614,12 @@ int btrfs_recover_relocation(struct btrfs_root *root) ...@@ -3589,8 +3614,12 @@ int btrfs_recover_relocation(struct btrfs_root *root)
fs_root = read_fs_root(root->fs_info, fs_root = read_fs_root(root->fs_info,
reloc_root->root_key.offset); reloc_root->root_key.offset);
if (IS_ERR(fs_root)) { if (IS_ERR(fs_root)) {
err = PTR_ERR(fs_root); ret = PTR_ERR(fs_root);
goto out; if (ret != -ENOENT) {
err = ret;
goto out;
}
mark_garbage_root(reloc_root);
} }
} }
......
...@@ -94,17 +94,23 @@ int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, ...@@ -94,17 +94,23 @@ int btrfs_find_last_root(struct btrfs_root *root, u64 objectid,
goto out; goto out;
BUG_ON(ret == 0); BUG_ON(ret == 0);
if (path->slots[0] == 0) {
ret = 1;
goto out;
}
l = path->nodes[0]; l = path->nodes[0];
BUG_ON(path->slots[0] == 0);
slot = path->slots[0] - 1; slot = path->slots[0] - 1;
btrfs_item_key_to_cpu(l, &found_key, slot); btrfs_item_key_to_cpu(l, &found_key, slot);
if (found_key.objectid != objectid) { if (found_key.objectid != objectid ||
found_key.type != BTRFS_ROOT_ITEM_KEY) {
ret = 1; ret = 1;
goto out; goto out;
} }
read_extent_buffer(l, item, btrfs_item_ptr_offset(l, slot), if (item)
sizeof(*item)); read_extent_buffer(l, item, btrfs_item_ptr_offset(l, slot),
memcpy(key, &found_key, sizeof(found_key)); sizeof(*item));
if (key)
memcpy(key, &found_key, sizeof(found_key));
ret = 0; ret = 0;
out: out:
btrfs_free_path(path); btrfs_free_path(path);
...@@ -249,6 +255,59 @@ int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid) ...@@ -249,6 +255,59 @@ int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid)
return ret; return ret;
} }
int btrfs_find_orphan_roots(struct btrfs_root *tree_root)
{
struct extent_buffer *leaf;
struct btrfs_path *path;
struct btrfs_key key;
int err = 0;
int ret;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
key.objectid = BTRFS_ORPHAN_OBJECTID;
key.type = BTRFS_ORPHAN_ITEM_KEY;
key.offset = 0;
while (1) {
ret = btrfs_search_slot(NULL, tree_root, &key, path, 0, 0);
if (ret < 0) {
err = ret;
break;
}
leaf = path->nodes[0];
if (path->slots[0] >= btrfs_header_nritems(leaf)) {
ret = btrfs_next_leaf(tree_root, path);
if (ret < 0)
err = ret;
if (ret != 0)
break;
leaf = path->nodes[0];
}
btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
btrfs_release_path(tree_root, path);
if (key.objectid != BTRFS_ORPHAN_OBJECTID ||
key.type != BTRFS_ORPHAN_ITEM_KEY)
break;
ret = btrfs_find_dead_roots(tree_root, key.offset);
if (ret) {
err = ret;
break;
}
key.offset++;
}
btrfs_free_path(path);
return err;
}
/* drop the root item for 'key' from 'root' */ /* drop the root item for 'key' from 'root' */
int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct btrfs_key *key) struct btrfs_key *key)
......
...@@ -676,6 +676,7 @@ static int btrfs_unfreeze(struct super_block *sb) ...@@ -676,6 +676,7 @@ static int btrfs_unfreeze(struct super_block *sb)
} }
static struct super_operations btrfs_super_ops = { static struct super_operations btrfs_super_ops = {
.drop_inode = btrfs_drop_inode,
.delete_inode = btrfs_delete_inode, .delete_inode = btrfs_delete_inode,
.put_super = btrfs_put_super, .put_super = btrfs_put_super,
.sync_fs = btrfs_sync_fs, .sync_fs = btrfs_sync_fs,
......
...@@ -104,7 +104,6 @@ static noinline int record_root_in_trans(struct btrfs_trans_handle *trans, ...@@ -104,7 +104,6 @@ static noinline int record_root_in_trans(struct btrfs_trans_handle *trans,
{ {
if (root->ref_cows && root->last_trans < trans->transid) { if (root->ref_cows && root->last_trans < trans->transid) {
WARN_ON(root == root->fs_info->extent_root); WARN_ON(root == root->fs_info->extent_root);
WARN_ON(root->root_item.refs == 0);
WARN_ON(root->commit_root != root->node); WARN_ON(root->commit_root != root->node);
radix_tree_tag_set(&root->fs_info->fs_roots_radix, radix_tree_tag_set(&root->fs_info->fs_roots_radix,
...@@ -1078,8 +1077,13 @@ int btrfs_clean_old_snapshots(struct btrfs_root *root) ...@@ -1078,8 +1077,13 @@ int btrfs_clean_old_snapshots(struct btrfs_root *root)
while (!list_empty(&list)) { while (!list_empty(&list)) {
root = list_entry(list.next, struct btrfs_root, root_list); root = list_entry(list.next, struct btrfs_root, root_list);
list_del_init(&root->root_list); list_del(&root->root_list);
btrfs_drop_snapshot(root, 0);
if (btrfs_header_backref_rev(root->node) <
BTRFS_MIXED_BACKREF_REV)
btrfs_drop_snapshot(root, 0);
else
btrfs_drop_snapshot(root, 1);
} }
return 0; return 0;
} }
...@@ -2841,7 +2841,7 @@ static noinline int check_parent_dirs_for_sync(struct btrfs_trans_handle *trans, ...@@ -2841,7 +2841,7 @@ static noinline int check_parent_dirs_for_sync(struct btrfs_trans_handle *trans,
if (!parent || !parent->d_inode || sb != parent->d_inode->i_sb) if (!parent || !parent->d_inode || sb != parent->d_inode->i_sb)
break; break;
if (parent == sb->s_root) if (IS_ROOT(parent))
break; break;
parent = parent->d_parent; parent = parent->d_parent;
...@@ -2880,6 +2880,12 @@ int btrfs_log_inode_parent(struct btrfs_trans_handle *trans, ...@@ -2880,6 +2880,12 @@ int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
goto end_no_trans; goto end_no_trans;
} }
if (root != BTRFS_I(inode)->root ||
btrfs_root_refs(&root->root_item) == 0) {
ret = 1;
goto end_no_trans;
}
ret = check_parent_dirs_for_sync(trans, inode, parent, ret = check_parent_dirs_for_sync(trans, inode, parent,
sb, last_committed); sb, last_committed);
if (ret) if (ret)
...@@ -2907,12 +2913,15 @@ int btrfs_log_inode_parent(struct btrfs_trans_handle *trans, ...@@ -2907,12 +2913,15 @@ int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
break; break;
inode = parent->d_inode; inode = parent->d_inode;
if (root != BTRFS_I(inode)->root)
break;
if (BTRFS_I(inode)->generation > if (BTRFS_I(inode)->generation >
root->fs_info->last_trans_committed) { root->fs_info->last_trans_committed) {
ret = btrfs_log_inode(trans, root, inode, inode_only); ret = btrfs_log_inode(trans, root, inode, inode_only);
BUG_ON(ret); BUG_ON(ret);
} }
if (parent == sb->s_root) if (IS_ROOT(parent))
break; break;
parent = parent->d_parent; parent = parent->d_parent;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment