提交 a525890c 编写于 作者: L Linus Torvalds

Merge git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable

* git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable: (23 commits)
  Btrfs: fix extent_buffer leak during tree log replay
  Btrfs: fix oops when btrfs_inherit_iflags called with a NULL dir
  Btrfs: fix -o nodatasum printk spelling
  Btrfs: check duplicate backrefs for both data and metadata
  Btrfs: init worker struct fields before kthread-run
  Btrfs: pin buffers during write_dev_supers
  Btrfs: avoid races between super writeout and device list updates
  Fix btrfs when ACLs are configured out
  Btrfs: fdatasync should skip metadata writeout
  Btrfs: remove crc32c.h and use libcrc32c directly.
  Btrfs: implement FS_IOC_GETFLAGS/SETFLAGS/GETVERSION
  Btrfs: autodetect SSD devices
  Btrfs: add mount -o ssd_spread to spread allocations out
  Btrfs: avoid allocation clusters that are too spread out
  Btrfs: Add mount -o nossd
  Btrfs: avoid IO stalls behind congested devices in a multi-device FS
  Btrfs: don't allow WRITE_SYNC bios to starve out regular writes
  Btrfs: fix metadata dirty throttling limits
  Btrfs: reduce mount -o ssd CPU usage
  Btrfs: balance btree more often
  ...
...@@ -6,5 +6,5 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \ ...@@ -6,5 +6,5 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \
transaction.o inode.o file.o tree-defrag.o \ transaction.o inode.o file.o tree-defrag.o \
extent_map.o sysfs.o struct-funcs.o xattr.o ordered-data.o \ extent_map.o sysfs.o struct-funcs.o xattr.o ordered-data.o \
extent_io.o volumes.o async-thread.o ioctl.o locking.o orphan.o \ extent_io.o volumes.o async-thread.o ioctl.o locking.o orphan.o \
ref-cache.o export.o tree-log.o acl.o free-space-cache.o zlib.o \ export.o tree-log.o acl.o free-space-cache.o zlib.o \
compression.o delayed-ref.o compression.o delayed-ref.o relocation.o
...@@ -351,9 +351,4 @@ int btrfs_init_acl(struct inode *inode, struct inode *dir) ...@@ -351,9 +351,4 @@ int btrfs_init_acl(struct inode *inode, struct inode *dir)
return 0; return 0;
} }
int btrfs_check_acl(struct inode *inode, int mask)
{
return 0;
}
#endif /* CONFIG_FS_POSIX_ACL */ #endif /* CONFIG_FS_POSIX_ACL */
...@@ -294,10 +294,10 @@ int btrfs_start_workers(struct btrfs_workers *workers, int num_workers) ...@@ -294,10 +294,10 @@ int btrfs_start_workers(struct btrfs_workers *workers, int num_workers)
INIT_LIST_HEAD(&worker->worker_list); INIT_LIST_HEAD(&worker->worker_list);
spin_lock_init(&worker->lock); spin_lock_init(&worker->lock);
atomic_set(&worker->num_pending, 0); atomic_set(&worker->num_pending, 0);
worker->workers = workers;
worker->task = kthread_run(worker_loop, worker, worker->task = kthread_run(worker_loop, worker,
"btrfs-%s-%d", workers->name, "btrfs-%s-%d", workers->name,
workers->num_workers + i); workers->num_workers + i);
worker->workers = workers;
if (IS_ERR(worker->task)) { if (IS_ERR(worker->task)) {
kfree(worker); kfree(worker);
ret = PTR_ERR(worker->task); ret = PTR_ERR(worker->task);
......
...@@ -72,6 +72,9 @@ struct btrfs_inode { ...@@ -72,6 +72,9 @@ struct btrfs_inode {
*/ */
struct list_head ordered_operations; struct list_head ordered_operations;
/* node for the red-black tree that links inodes in subvolume root */
struct rb_node rb_node;
/* the space_info for where this inode's data allocations are done */ /* the space_info for where this inode's data allocations are done */
struct btrfs_space_info *space_info; struct btrfs_space_info *space_info;
...@@ -154,5 +157,4 @@ static inline void btrfs_i_size_write(struct inode *inode, u64 size) ...@@ -154,5 +157,4 @@ static inline void btrfs_i_size_write(struct inode *inode, u64 size)
BTRFS_I(inode)->disk_i_size = size; BTRFS_I(inode)->disk_i_size = size;
} }
#endif #endif
...@@ -123,7 +123,7 @@ static int check_compressed_csum(struct inode *inode, ...@@ -123,7 +123,7 @@ static int check_compressed_csum(struct inode *inode,
u32 csum; u32 csum;
u32 *cb_sum = &cb->sums; u32 *cb_sum = &cb->sums;
if (btrfs_test_flag(inode, NODATASUM)) if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)
return 0; return 0;
for (i = 0; i < cb->nr_pages; i++) { for (i = 0; i < cb->nr_pages; i++) {
...@@ -670,7 +670,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio, ...@@ -670,7 +670,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
*/ */
atomic_inc(&cb->pending_bios); atomic_inc(&cb->pending_bios);
if (!btrfs_test_flag(inode, NODATASUM)) { if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)) {
btrfs_lookup_bio_sums(root, inode, comp_bio, btrfs_lookup_bio_sums(root, inode, comp_bio,
sums); sums);
} }
...@@ -697,7 +697,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio, ...@@ -697,7 +697,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
ret = btrfs_bio_wq_end_io(root->fs_info, comp_bio, 0); ret = btrfs_bio_wq_end_io(root->fs_info, comp_bio, 0);
BUG_ON(ret); BUG_ON(ret);
if (!btrfs_test_flag(inode, NODATASUM)) if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM))
btrfs_lookup_bio_sums(root, inode, comp_bio, sums); btrfs_lookup_bio_sums(root, inode, comp_bio, sums);
ret = btrfs_map_bio(root, READ, comp_bio, mirror_num, 0); ret = btrfs_map_bio(root, READ, comp_bio, mirror_num, 0);
......
/*
* Copyright (C) 2008 Oracle. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 021110-1307, USA.
*/
#ifndef __BTRFS_CRC32C__
#define __BTRFS_CRC32C__
#include <linux/crc32c.h>
/*
* this file used to do more for selecting the HW version of crc32c,
* perhaps it will one day again soon.
*/
#define btrfs_crc32c(seed, data, length) crc32c(seed, data, length)
#endif
...@@ -197,14 +197,7 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans, ...@@ -197,14 +197,7 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans,
u32 nritems; u32 nritems;
int ret = 0; int ret = 0;
int level; int level;
struct btrfs_root *new_root; struct btrfs_disk_key disk_key;
new_root = kmalloc(sizeof(*new_root), GFP_NOFS);
if (!new_root)
return -ENOMEM;
memcpy(new_root, root, sizeof(*new_root));
new_root->root_key.objectid = new_root_objectid;
WARN_ON(root->ref_cows && trans->transid != WARN_ON(root->ref_cows && trans->transid !=
root->fs_info->running_transaction->transid); root->fs_info->running_transaction->transid);
...@@ -212,28 +205,37 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans, ...@@ -212,28 +205,37 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans,
level = btrfs_header_level(buf); level = btrfs_header_level(buf);
nritems = btrfs_header_nritems(buf); nritems = btrfs_header_nritems(buf);
if (level == 0)
btrfs_item_key(buf, &disk_key, 0);
else
btrfs_node_key(buf, &disk_key, 0);
cow = btrfs_alloc_free_block(trans, new_root, buf->len, 0, cow = btrfs_alloc_free_block(trans, root, buf->len, 0,
new_root_objectid, trans->transid, new_root_objectid, &disk_key, level,
level, buf->start, 0); buf->start, 0);
if (IS_ERR(cow)) { if (IS_ERR(cow))
kfree(new_root);
return PTR_ERR(cow); return PTR_ERR(cow);
}
copy_extent_buffer(cow, buf, 0, 0, cow->len); copy_extent_buffer(cow, buf, 0, 0, cow->len);
btrfs_set_header_bytenr(cow, cow->start); btrfs_set_header_bytenr(cow, cow->start);
btrfs_set_header_generation(cow, trans->transid); btrfs_set_header_generation(cow, trans->transid);
btrfs_set_header_owner(cow, new_root_objectid); btrfs_set_header_backref_rev(cow, BTRFS_MIXED_BACKREF_REV);
btrfs_clear_header_flag(cow, BTRFS_HEADER_FLAG_WRITTEN); btrfs_clear_header_flag(cow, BTRFS_HEADER_FLAG_WRITTEN |
BTRFS_HEADER_FLAG_RELOC);
if (new_root_objectid == BTRFS_TREE_RELOC_OBJECTID)
btrfs_set_header_flag(cow, BTRFS_HEADER_FLAG_RELOC);
else
btrfs_set_header_owner(cow, new_root_objectid);
write_extent_buffer(cow, root->fs_info->fsid, write_extent_buffer(cow, root->fs_info->fsid,
(unsigned long)btrfs_header_fsid(cow), (unsigned long)btrfs_header_fsid(cow),
BTRFS_FSID_SIZE); BTRFS_FSID_SIZE);
WARN_ON(btrfs_header_generation(buf) > trans->transid); WARN_ON(btrfs_header_generation(buf) > trans->transid);
ret = btrfs_inc_ref(trans, new_root, buf, cow, NULL); if (new_root_objectid == BTRFS_TREE_RELOC_OBJECTID)
kfree(new_root); ret = btrfs_inc_ref(trans, root, cow, 1);
else
ret = btrfs_inc_ref(trans, root, cow, 0);
if (ret) if (ret)
return ret; return ret;
...@@ -243,6 +245,125 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans, ...@@ -243,6 +245,125 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans,
return 0; return 0;
} }
/*
* check if the tree block can be shared by multiple trees
*/
int btrfs_block_can_be_shared(struct btrfs_root *root,
struct extent_buffer *buf)
{
/*
* Tree blocks not in refernece counted trees and tree roots
* are never shared. If a block was allocated after the last
* snapshot and the block was not allocated by tree relocation,
* we know the block is not shared.
*/
if (root->ref_cows &&
buf != root->node && buf != root->commit_root &&
(btrfs_header_generation(buf) <=
btrfs_root_last_snapshot(&root->root_item) ||
btrfs_header_flag(buf, BTRFS_HEADER_FLAG_RELOC)))
return 1;
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
if (root->ref_cows &&
btrfs_header_backref_rev(buf) < BTRFS_MIXED_BACKREF_REV)
return 1;
#endif
return 0;
}
static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct extent_buffer *buf,
struct extent_buffer *cow)
{
u64 refs;
u64 owner;
u64 flags;
u64 new_flags = 0;
int ret;
/*
* Backrefs update rules:
*
* Always use full backrefs for extent pointers in tree block
* allocated by tree relocation.
*
* If a shared tree block is no longer referenced by its owner
* tree (btrfs_header_owner(buf) == root->root_key.objectid),
* use full backrefs for extent pointers in tree block.
*
* If a tree block is been relocating
* (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID),
* use full backrefs for extent pointers in tree block.
* The reason for this is some operations (such as drop tree)
* are only allowed for blocks use full backrefs.
*/
if (btrfs_block_can_be_shared(root, buf)) {
ret = btrfs_lookup_extent_info(trans, root, buf->start,
buf->len, &refs, &flags);
BUG_ON(ret);
BUG_ON(refs == 0);
} else {
refs = 1;
if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID ||
btrfs_header_backref_rev(buf) < BTRFS_MIXED_BACKREF_REV)
flags = BTRFS_BLOCK_FLAG_FULL_BACKREF;
else
flags = 0;
}
owner = btrfs_header_owner(buf);
BUG_ON(owner == BTRFS_TREE_RELOC_OBJECTID &&
!(flags & BTRFS_BLOCK_FLAG_FULL_BACKREF));
if (refs > 1) {
if ((owner == root->root_key.objectid ||
root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) &&
!(flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)) {
ret = btrfs_inc_ref(trans, root, buf, 1);
BUG_ON(ret);
if (root->root_key.objectid ==
BTRFS_TREE_RELOC_OBJECTID) {
ret = btrfs_dec_ref(trans, root, buf, 0);
BUG_ON(ret);
ret = btrfs_inc_ref(trans, root, cow, 1);
BUG_ON(ret);
}
new_flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF;
} else {
if (root->root_key.objectid ==
BTRFS_TREE_RELOC_OBJECTID)
ret = btrfs_inc_ref(trans, root, cow, 1);
else
ret = btrfs_inc_ref(trans, root, cow, 0);
BUG_ON(ret);
}
if (new_flags != 0) {
ret = btrfs_set_disk_extent_flags(trans, root,
buf->start,
buf->len,
new_flags, 0);
BUG_ON(ret);
}
} else {
if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF) {
if (root->root_key.objectid ==
BTRFS_TREE_RELOC_OBJECTID)
ret = btrfs_inc_ref(trans, root, cow, 1);
else
ret = btrfs_inc_ref(trans, root, cow, 0);
BUG_ON(ret);
ret = btrfs_dec_ref(trans, root, buf, 1);
BUG_ON(ret);
}
clean_tree_block(trans, root, buf);
}
return 0;
}
/* /*
* does the dirty work in cow of a single block. The parent block (if * does the dirty work in cow of a single block. The parent block (if
* supplied) is updated to point to the new cow copy. The new buffer is marked * supplied) is updated to point to the new cow copy. The new buffer is marked
...@@ -262,34 +383,39 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans, ...@@ -262,34 +383,39 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
struct extent_buffer **cow_ret, struct extent_buffer **cow_ret,
u64 search_start, u64 empty_size) u64 search_start, u64 empty_size)
{ {
u64 parent_start; struct btrfs_disk_key disk_key;
struct extent_buffer *cow; struct extent_buffer *cow;
u32 nritems;
int ret = 0;
int level; int level;
int unlock_orig = 0; int unlock_orig = 0;
u64 parent_start;
if (*cow_ret == buf) if (*cow_ret == buf)
unlock_orig = 1; unlock_orig = 1;
btrfs_assert_tree_locked(buf); btrfs_assert_tree_locked(buf);
if (parent)
parent_start = parent->start;
else
parent_start = 0;
WARN_ON(root->ref_cows && trans->transid != WARN_ON(root->ref_cows && trans->transid !=
root->fs_info->running_transaction->transid); root->fs_info->running_transaction->transid);
WARN_ON(root->ref_cows && trans->transid != root->last_trans); WARN_ON(root->ref_cows && trans->transid != root->last_trans);
level = btrfs_header_level(buf); level = btrfs_header_level(buf);
nritems = btrfs_header_nritems(buf);
cow = btrfs_alloc_free_block(trans, root, buf->len, if (level == 0)
parent_start, root->root_key.objectid, btrfs_item_key(buf, &disk_key, 0);
trans->transid, level, else
search_start, empty_size); btrfs_node_key(buf, &disk_key, 0);
if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) {
if (parent)
parent_start = parent->start;
else
parent_start = 0;
} else
parent_start = 0;
cow = btrfs_alloc_free_block(trans, root, buf->len, parent_start,
root->root_key.objectid, &disk_key,
level, search_start, empty_size);
if (IS_ERR(cow)) if (IS_ERR(cow))
return PTR_ERR(cow); return PTR_ERR(cow);
...@@ -298,83 +424,53 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans, ...@@ -298,83 +424,53 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
copy_extent_buffer(cow, buf, 0, 0, cow->len); copy_extent_buffer(cow, buf, 0, 0, cow->len);
btrfs_set_header_bytenr(cow, cow->start); btrfs_set_header_bytenr(cow, cow->start);
btrfs_set_header_generation(cow, trans->transid); btrfs_set_header_generation(cow, trans->transid);
btrfs_set_header_owner(cow, root->root_key.objectid); btrfs_set_header_backref_rev(cow, BTRFS_MIXED_BACKREF_REV);
btrfs_clear_header_flag(cow, BTRFS_HEADER_FLAG_WRITTEN); btrfs_clear_header_flag(cow, BTRFS_HEADER_FLAG_WRITTEN |
BTRFS_HEADER_FLAG_RELOC);
if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID)
btrfs_set_header_flag(cow, BTRFS_HEADER_FLAG_RELOC);
else
btrfs_set_header_owner(cow, root->root_key.objectid);
write_extent_buffer(cow, root->fs_info->fsid, write_extent_buffer(cow, root->fs_info->fsid,
(unsigned long)btrfs_header_fsid(cow), (unsigned long)btrfs_header_fsid(cow),
BTRFS_FSID_SIZE); BTRFS_FSID_SIZE);
WARN_ON(btrfs_header_generation(buf) > trans->transid); update_ref_for_cow(trans, root, buf, cow);
if (btrfs_header_generation(buf) != trans->transid) {
u32 nr_extents;
ret = btrfs_inc_ref(trans, root, buf, cow, &nr_extents);
if (ret)
return ret;
ret = btrfs_cache_ref(trans, root, buf, nr_extents);
WARN_ON(ret);
} else if (btrfs_header_owner(buf) == BTRFS_TREE_RELOC_OBJECTID) {
/*
* There are only two places that can drop reference to
* tree blocks owned by living reloc trees, one is here,
* the other place is btrfs_drop_subtree. In both places,
* we check reference count while tree block is locked.
* Furthermore, if reference count is one, it won't get
* increased by someone else.
*/
u32 refs;
ret = btrfs_lookup_extent_ref(trans, root, buf->start,
buf->len, &refs);
BUG_ON(ret);
if (refs == 1) {
ret = btrfs_update_ref(trans, root, buf, cow,
0, nritems);
clean_tree_block(trans, root, buf);
} else {
ret = btrfs_inc_ref(trans, root, buf, cow, NULL);
}
BUG_ON(ret);
} else {
ret = btrfs_update_ref(trans, root, buf, cow, 0, nritems);
if (ret)
return ret;
clean_tree_block(trans, root, buf);
}
if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) {
ret = btrfs_reloc_tree_cache_ref(trans, root, cow, buf->start);
WARN_ON(ret);
}
if (buf == root->node) { if (buf == root->node) {
WARN_ON(parent && parent != buf); WARN_ON(parent && parent != buf);
if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID ||
btrfs_header_backref_rev(buf) < BTRFS_MIXED_BACKREF_REV)
parent_start = buf->start;
else
parent_start = 0;
spin_lock(&root->node_lock); spin_lock(&root->node_lock);
root->node = cow; root->node = cow;
extent_buffer_get(cow); extent_buffer_get(cow);
spin_unlock(&root->node_lock); spin_unlock(&root->node_lock);
if (buf != root->commit_root) { btrfs_free_extent(trans, root, buf->start, buf->len,
btrfs_free_extent(trans, root, buf->start, parent_start, root->root_key.objectid,
buf->len, buf->start, level, 0);
root->root_key.objectid,
btrfs_header_generation(buf),
level, 1);
}
free_extent_buffer(buf); free_extent_buffer(buf);
add_root_to_dirty_list(root); add_root_to_dirty_list(root);
} else { } else {
if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID)
parent_start = parent->start;
else
parent_start = 0;
WARN_ON(trans->transid != btrfs_header_generation(parent));
btrfs_set_node_blockptr(parent, parent_slot, btrfs_set_node_blockptr(parent, parent_slot,
cow->start); cow->start);
WARN_ON(trans->transid == 0);
btrfs_set_node_ptr_generation(parent, parent_slot, btrfs_set_node_ptr_generation(parent, parent_slot,
trans->transid); trans->transid);
btrfs_mark_buffer_dirty(parent); btrfs_mark_buffer_dirty(parent);
WARN_ON(btrfs_header_generation(parent) != trans->transid);
btrfs_free_extent(trans, root, buf->start, buf->len, btrfs_free_extent(trans, root, buf->start, buf->len,
parent_start, btrfs_header_owner(parent), parent_start, root->root_key.objectid,
btrfs_header_generation(parent), level, 1); level, 0);
} }
if (unlock_orig) if (unlock_orig)
btrfs_tree_unlock(buf); btrfs_tree_unlock(buf);
...@@ -384,6 +480,18 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans, ...@@ -384,6 +480,18 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
return 0; return 0;
} }
static inline int should_cow_block(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct extent_buffer *buf)
{
if (btrfs_header_generation(buf) == trans->transid &&
!btrfs_header_flag(buf, BTRFS_HEADER_FLAG_WRITTEN) &&
!(root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID &&
btrfs_header_flag(buf, BTRFS_HEADER_FLAG_RELOC)))
return 0;
return 1;
}
/* /*
* cows a single block, see __btrfs_cow_block for the real work. * cows a single block, see __btrfs_cow_block for the real work.
* This version of it has extra checks so that a block isn't cow'd more than * This version of it has extra checks so that a block isn't cow'd more than
...@@ -411,9 +519,7 @@ noinline int btrfs_cow_block(struct btrfs_trans_handle *trans, ...@@ -411,9 +519,7 @@ noinline int btrfs_cow_block(struct btrfs_trans_handle *trans,
WARN_ON(1); WARN_ON(1);
} }
if (btrfs_header_generation(buf) == trans->transid && if (!should_cow_block(trans, root, buf)) {
btrfs_header_owner(buf) == root->root_key.objectid &&
!btrfs_header_flag(buf, BTRFS_HEADER_FLAG_WRITTEN)) {
*cow_ret = buf; *cow_ret = buf;
return 0; return 0;
} }
...@@ -469,7 +575,7 @@ static int comp_keys(struct btrfs_disk_key *disk, struct btrfs_key *k2) ...@@ -469,7 +575,7 @@ static int comp_keys(struct btrfs_disk_key *disk, struct btrfs_key *k2)
/* /*
* same as comp_keys only with two btrfs_key's * same as comp_keys only with two btrfs_key's
*/ */
static int comp_cpu_keys(struct btrfs_key *k1, struct btrfs_key *k2) int btrfs_comp_cpu_keys(struct btrfs_key *k1, struct btrfs_key *k2)
{ {
if (k1->objectid > k2->objectid) if (k1->objectid > k2->objectid)
return 1; return 1;
...@@ -845,6 +951,12 @@ static int bin_search(struct extent_buffer *eb, struct btrfs_key *key, ...@@ -845,6 +951,12 @@ static int bin_search(struct extent_buffer *eb, struct btrfs_key *key,
return -1; return -1;
} }
int btrfs_bin_search(struct extent_buffer *eb, struct btrfs_key *key,
int level, int *slot)
{
return bin_search(eb, key, level, slot);
}
/* given a node and slot number, this reads the blocks it points to. The /* given a node and slot number, this reads the blocks it points to. The
* extent buffer is returned with a reference taken (but unlocked). * extent buffer is returned with a reference taken (but unlocked).
* NULL is returned on error. * NULL is returned on error.
...@@ -921,13 +1033,6 @@ static noinline int balance_level(struct btrfs_trans_handle *trans, ...@@ -921,13 +1033,6 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
root->node = child; root->node = child;
spin_unlock(&root->node_lock); spin_unlock(&root->node_lock);
ret = btrfs_update_extent_ref(trans, root, child->start,
child->len,
mid->start, child->start,
root->root_key.objectid,
trans->transid, level - 1);
BUG_ON(ret);
add_root_to_dirty_list(root); add_root_to_dirty_list(root);
btrfs_tree_unlock(child); btrfs_tree_unlock(child);
...@@ -938,9 +1043,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans, ...@@ -938,9 +1043,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
/* once for the path */ /* once for the path */
free_extent_buffer(mid); free_extent_buffer(mid);
ret = btrfs_free_extent(trans, root, mid->start, mid->len, ret = btrfs_free_extent(trans, root, mid->start, mid->len,
mid->start, root->root_key.objectid, 0, root->root_key.objectid, level, 1);
btrfs_header_generation(mid),
level, 1);
/* once for the root ptr */ /* once for the root ptr */
free_extent_buffer(mid); free_extent_buffer(mid);
return ret; return ret;
...@@ -949,8 +1052,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans, ...@@ -949,8 +1052,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
BTRFS_NODEPTRS_PER_BLOCK(root) / 4) BTRFS_NODEPTRS_PER_BLOCK(root) / 4)
return 0; return 0;
if (trans->transaction->delayed_refs.flushing && if (btrfs_header_nritems(mid) > 2)
btrfs_header_nritems(mid) > 2)
return 0; return 0;
if (btrfs_header_nritems(mid) < 2) if (btrfs_header_nritems(mid) < 2)
...@@ -998,7 +1100,6 @@ static noinline int balance_level(struct btrfs_trans_handle *trans, ...@@ -998,7 +1100,6 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
ret = wret; ret = wret;
if (btrfs_header_nritems(right) == 0) { if (btrfs_header_nritems(right) == 0) {
u64 bytenr = right->start; u64 bytenr = right->start;
u64 generation = btrfs_header_generation(parent);
u32 blocksize = right->len; u32 blocksize = right->len;
clean_tree_block(trans, root, right); clean_tree_block(trans, root, right);
...@@ -1010,9 +1111,9 @@ static noinline int balance_level(struct btrfs_trans_handle *trans, ...@@ -1010,9 +1111,9 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
if (wret) if (wret)
ret = wret; ret = wret;
wret = btrfs_free_extent(trans, root, bytenr, wret = btrfs_free_extent(trans, root, bytenr,
blocksize, parent->start, blocksize, 0,
btrfs_header_owner(parent), root->root_key.objectid,
generation, level, 1); level, 0);
if (wret) if (wret)
ret = wret; ret = wret;
} else { } else {
...@@ -1047,7 +1148,6 @@ static noinline int balance_level(struct btrfs_trans_handle *trans, ...@@ -1047,7 +1148,6 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
} }
if (btrfs_header_nritems(mid) == 0) { if (btrfs_header_nritems(mid) == 0) {
/* we've managed to empty the middle node, drop it */ /* we've managed to empty the middle node, drop it */
u64 root_gen = btrfs_header_generation(parent);
u64 bytenr = mid->start; u64 bytenr = mid->start;
u32 blocksize = mid->len; u32 blocksize = mid->len;
...@@ -1059,9 +1159,8 @@ static noinline int balance_level(struct btrfs_trans_handle *trans, ...@@ -1059,9 +1159,8 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
if (wret) if (wret)
ret = wret; ret = wret;
wret = btrfs_free_extent(trans, root, bytenr, blocksize, wret = btrfs_free_extent(trans, root, bytenr, blocksize,
parent->start, 0, root->root_key.objectid,
btrfs_header_owner(parent), level, 0);
root_gen, level, 1);
if (wret) if (wret)
ret = wret; ret = wret;
} else { } else {
...@@ -1437,7 +1536,7 @@ noinline void btrfs_unlock_up_safe(struct btrfs_path *path, int level) ...@@ -1437,7 +1536,7 @@ noinline void btrfs_unlock_up_safe(struct btrfs_path *path, int level)
{ {
int i; int i;
if (path->keep_locks || path->lowest_level) if (path->keep_locks)
return; return;
for (i = level; i < BTRFS_MAX_LEVEL; i++) { for (i = level; i < BTRFS_MAX_LEVEL; i++) {
...@@ -1552,7 +1651,7 @@ setup_nodes_for_search(struct btrfs_trans_handle *trans, ...@@ -1552,7 +1651,7 @@ setup_nodes_for_search(struct btrfs_trans_handle *trans,
} }
b = p->nodes[level]; b = p->nodes[level];
} else if (ins_len < 0 && btrfs_header_nritems(b) < } else if (ins_len < 0 && btrfs_header_nritems(b) <
BTRFS_NODEPTRS_PER_BLOCK(root) / 4) { BTRFS_NODEPTRS_PER_BLOCK(root) / 2) {
int sret; int sret;
sret = reada_for_balance(root, p, level); sret = reada_for_balance(root, p, level);
...@@ -1614,10 +1713,17 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -1614,10 +1713,17 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
lowest_unlock = 2; lowest_unlock = 2;
again: again:
if (p->skip_locking) if (p->search_commit_root) {
b = btrfs_root_node(root); b = root->commit_root;
else extent_buffer_get(b);
b = btrfs_lock_root_node(root); if (!p->skip_locking)
btrfs_tree_lock(b);
} else {
if (p->skip_locking)
b = btrfs_root_node(root);
else
b = btrfs_lock_root_node(root);
}
while (b) { while (b) {
level = btrfs_header_level(b); level = btrfs_header_level(b);
...@@ -1638,11 +1744,9 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -1638,11 +1744,9 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
* then we don't want to set the path blocking, * then we don't want to set the path blocking,
* so we test it here * so we test it here
*/ */
if (btrfs_header_generation(b) == trans->transid && if (!should_cow_block(trans, root, b))
btrfs_header_owner(b) == root->root_key.objectid &&
!btrfs_header_flag(b, BTRFS_HEADER_FLAG_WRITTEN)) {
goto cow_done; goto cow_done;
}
btrfs_set_path_blocking(p); btrfs_set_path_blocking(p);
wret = btrfs_cow_block(trans, root, b, wret = btrfs_cow_block(trans, root, b,
...@@ -1764,138 +1868,6 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -1764,138 +1868,6 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
return ret; return ret;
} }
int btrfs_merge_path(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_key *node_keys,
u64 *nodes, int lowest_level)
{
struct extent_buffer *eb;
struct extent_buffer *parent;
struct btrfs_key key;
u64 bytenr;
u64 generation;
u32 blocksize;
int level;
int slot;
int key_match;
int ret;
eb = btrfs_lock_root_node(root);
ret = btrfs_cow_block(trans, root, eb, NULL, 0, &eb);
BUG_ON(ret);
btrfs_set_lock_blocking(eb);
parent = eb;
while (1) {
level = btrfs_header_level(parent);
if (level == 0 || level <= lowest_level)
break;
ret = bin_search(parent, &node_keys[lowest_level], level,
&slot);
if (ret && slot > 0)
slot--;
bytenr = btrfs_node_blockptr(parent, slot);
if (nodes[level - 1] == bytenr)
break;
blocksize = btrfs_level_size(root, level - 1);
generation = btrfs_node_ptr_generation(parent, slot);
btrfs_node_key_to_cpu(eb, &key, slot);
key_match = !memcmp(&key, &node_keys[level - 1], sizeof(key));
if (generation == trans->transid) {
eb = read_tree_block(root, bytenr, blocksize,
generation);
btrfs_tree_lock(eb);
btrfs_set_lock_blocking(eb);
}
/*
* if node keys match and node pointer hasn't been modified
* in the running transaction, we can merge the path. for
* blocks owened by reloc trees, the node pointer check is
* skipped, this is because these blocks are fully controlled
* by the space balance code, no one else can modify them.
*/
if (!nodes[level - 1] || !key_match ||
(generation == trans->transid &&
btrfs_header_owner(eb) != BTRFS_TREE_RELOC_OBJECTID)) {
if (level == 1 || level == lowest_level + 1) {
if (generation == trans->transid) {
btrfs_tree_unlock(eb);
free_extent_buffer(eb);
}
break;
}
if (generation != trans->transid) {
eb = read_tree_block(root, bytenr, blocksize,
generation);
btrfs_tree_lock(eb);
btrfs_set_lock_blocking(eb);
}
ret = btrfs_cow_block(trans, root, eb, parent, slot,
&eb);
BUG_ON(ret);
if (root->root_key.objectid ==
BTRFS_TREE_RELOC_OBJECTID) {
if (!nodes[level - 1]) {
nodes[level - 1] = eb->start;
memcpy(&node_keys[level - 1], &key,
sizeof(node_keys[0]));
} else {
WARN_ON(1);
}
}
btrfs_tree_unlock(parent);
free_extent_buffer(parent);
parent = eb;
continue;
}
btrfs_set_node_blockptr(parent, slot, nodes[level - 1]);
btrfs_set_node_ptr_generation(parent, slot, trans->transid);
btrfs_mark_buffer_dirty(parent);
ret = btrfs_inc_extent_ref(trans, root,
nodes[level - 1],
blocksize, parent->start,
btrfs_header_owner(parent),
btrfs_header_generation(parent),
level - 1);
BUG_ON(ret);
/*
* If the block was created in the running transaction,
* it's possible this is the last reference to it, so we
* should drop the subtree.
*/
if (generation == trans->transid) {
ret = btrfs_drop_subtree(trans, root, eb, parent);
BUG_ON(ret);
btrfs_tree_unlock(eb);
free_extent_buffer(eb);
} else {
ret = btrfs_free_extent(trans, root, bytenr,
blocksize, parent->start,
btrfs_header_owner(parent),
btrfs_header_generation(parent),
level - 1, 1);
BUG_ON(ret);
}
break;
}
btrfs_tree_unlock(parent);
free_extent_buffer(parent);
return 0;
}
/* /*
* adjust the pointers going up the tree, starting at level * adjust the pointers going up the tree, starting at level
* making sure the right key of each node is points to 'key'. * making sure the right key of each node is points to 'key'.
...@@ -2021,9 +1993,6 @@ static int push_node_left(struct btrfs_trans_handle *trans, ...@@ -2021,9 +1993,6 @@ static int push_node_left(struct btrfs_trans_handle *trans,
btrfs_mark_buffer_dirty(src); btrfs_mark_buffer_dirty(src);
btrfs_mark_buffer_dirty(dst); btrfs_mark_buffer_dirty(dst);
ret = btrfs_update_ref(trans, root, src, dst, dst_nritems, push_items);
BUG_ON(ret);
return ret; return ret;
} }
...@@ -2083,9 +2052,6 @@ static int balance_node_right(struct btrfs_trans_handle *trans, ...@@ -2083,9 +2052,6 @@ static int balance_node_right(struct btrfs_trans_handle *trans,
btrfs_mark_buffer_dirty(src); btrfs_mark_buffer_dirty(src);
btrfs_mark_buffer_dirty(dst); btrfs_mark_buffer_dirty(dst);
ret = btrfs_update_ref(trans, root, src, dst, 0, push_items);
BUG_ON(ret);
return ret; return ret;
} }
...@@ -2105,7 +2071,6 @@ static noinline int insert_new_root(struct btrfs_trans_handle *trans, ...@@ -2105,7 +2071,6 @@ static noinline int insert_new_root(struct btrfs_trans_handle *trans,
struct extent_buffer *c; struct extent_buffer *c;
struct extent_buffer *old; struct extent_buffer *old;
struct btrfs_disk_key lower_key; struct btrfs_disk_key lower_key;
int ret;
BUG_ON(path->nodes[level]); BUG_ON(path->nodes[level]);
BUG_ON(path->nodes[level-1] != root->node); BUG_ON(path->nodes[level-1] != root->node);
...@@ -2117,16 +2082,17 @@ static noinline int insert_new_root(struct btrfs_trans_handle *trans, ...@@ -2117,16 +2082,17 @@ static noinline int insert_new_root(struct btrfs_trans_handle *trans,
btrfs_node_key(lower, &lower_key, 0); btrfs_node_key(lower, &lower_key, 0);
c = btrfs_alloc_free_block(trans, root, root->nodesize, 0, c = btrfs_alloc_free_block(trans, root, root->nodesize, 0,
root->root_key.objectid, trans->transid, root->root_key.objectid, &lower_key,
level, root->node->start, 0); level, root->node->start, 0);
if (IS_ERR(c)) if (IS_ERR(c))
return PTR_ERR(c); return PTR_ERR(c);
memset_extent_buffer(c, 0, 0, root->nodesize); memset_extent_buffer(c, 0, 0, sizeof(struct btrfs_header));
btrfs_set_header_nritems(c, 1); btrfs_set_header_nritems(c, 1);
btrfs_set_header_level(c, level); btrfs_set_header_level(c, level);
btrfs_set_header_bytenr(c, c->start); btrfs_set_header_bytenr(c, c->start);
btrfs_set_header_generation(c, trans->transid); btrfs_set_header_generation(c, trans->transid);
btrfs_set_header_backref_rev(c, BTRFS_MIXED_BACKREF_REV);
btrfs_set_header_owner(c, root->root_key.objectid); btrfs_set_header_owner(c, root->root_key.objectid);
write_extent_buffer(c, root->fs_info->fsid, write_extent_buffer(c, root->fs_info->fsid,
...@@ -2151,12 +2117,6 @@ static noinline int insert_new_root(struct btrfs_trans_handle *trans, ...@@ -2151,12 +2117,6 @@ static noinline int insert_new_root(struct btrfs_trans_handle *trans,
root->node = c; root->node = c;
spin_unlock(&root->node_lock); spin_unlock(&root->node_lock);
ret = btrfs_update_extent_ref(trans, root, lower->start,
lower->len, lower->start, c->start,
root->root_key.objectid,
trans->transid, level - 1);
BUG_ON(ret);
/* the super has an extra ref to root->node */ /* the super has an extra ref to root->node */
free_extent_buffer(old); free_extent_buffer(old);
...@@ -2233,7 +2193,7 @@ static noinline int split_node(struct btrfs_trans_handle *trans, ...@@ -2233,7 +2193,7 @@ static noinline int split_node(struct btrfs_trans_handle *trans,
ret = insert_new_root(trans, root, path, level + 1); ret = insert_new_root(trans, root, path, level + 1);
if (ret) if (ret)
return ret; return ret;
} else if (!trans->transaction->delayed_refs.flushing) { } else {
ret = push_nodes_for_insert(trans, root, path, level); ret = push_nodes_for_insert(trans, root, path, level);
c = path->nodes[level]; c = path->nodes[level];
if (!ret && btrfs_header_nritems(c) < if (!ret && btrfs_header_nritems(c) <
...@@ -2244,20 +2204,21 @@ static noinline int split_node(struct btrfs_trans_handle *trans, ...@@ -2244,20 +2204,21 @@ static noinline int split_node(struct btrfs_trans_handle *trans,
} }
c_nritems = btrfs_header_nritems(c); c_nritems = btrfs_header_nritems(c);
mid = (c_nritems + 1) / 2;
btrfs_node_key(c, &disk_key, mid);
split = btrfs_alloc_free_block(trans, root, root->nodesize, split = btrfs_alloc_free_block(trans, root, root->nodesize, 0,
path->nodes[level + 1]->start,
root->root_key.objectid, root->root_key.objectid,
trans->transid, level, c->start, 0); &disk_key, level, c->start, 0);
if (IS_ERR(split)) if (IS_ERR(split))
return PTR_ERR(split); return PTR_ERR(split);
btrfs_set_header_flags(split, btrfs_header_flags(c)); memset_extent_buffer(split, 0, 0, sizeof(struct btrfs_header));
btrfs_set_header_level(split, btrfs_header_level(c)); btrfs_set_header_level(split, btrfs_header_level(c));
btrfs_set_header_bytenr(split, split->start); btrfs_set_header_bytenr(split, split->start);
btrfs_set_header_generation(split, trans->transid); btrfs_set_header_generation(split, trans->transid);
btrfs_set_header_backref_rev(split, BTRFS_MIXED_BACKREF_REV);
btrfs_set_header_owner(split, root->root_key.objectid); btrfs_set_header_owner(split, root->root_key.objectid);
btrfs_set_header_flags(split, 0);
write_extent_buffer(split, root->fs_info->fsid, write_extent_buffer(split, root->fs_info->fsid,
(unsigned long)btrfs_header_fsid(split), (unsigned long)btrfs_header_fsid(split),
BTRFS_FSID_SIZE); BTRFS_FSID_SIZE);
...@@ -2265,7 +2226,6 @@ static noinline int split_node(struct btrfs_trans_handle *trans, ...@@ -2265,7 +2226,6 @@ static noinline int split_node(struct btrfs_trans_handle *trans,
(unsigned long)btrfs_header_chunk_tree_uuid(split), (unsigned long)btrfs_header_chunk_tree_uuid(split),
BTRFS_UUID_SIZE); BTRFS_UUID_SIZE);
mid = (c_nritems + 1) / 2;
copy_extent_buffer(split, c, copy_extent_buffer(split, c,
btrfs_node_key_ptr_offset(0), btrfs_node_key_ptr_offset(0),
...@@ -2278,16 +2238,12 @@ static noinline int split_node(struct btrfs_trans_handle *trans, ...@@ -2278,16 +2238,12 @@ static noinline int split_node(struct btrfs_trans_handle *trans,
btrfs_mark_buffer_dirty(c); btrfs_mark_buffer_dirty(c);
btrfs_mark_buffer_dirty(split); btrfs_mark_buffer_dirty(split);
btrfs_node_key(split, &disk_key, 0);
wret = insert_ptr(trans, root, path, &disk_key, split->start, wret = insert_ptr(trans, root, path, &disk_key, split->start,
path->slots[level + 1] + 1, path->slots[level + 1] + 1,
level + 1); level + 1);
if (wret) if (wret)
ret = wret; ret = wret;
ret = btrfs_update_ref(trans, root, c, split, 0, c_nritems - mid);
BUG_ON(ret);
if (path->slots[level] >= mid) { if (path->slots[level] >= mid) {
path->slots[level] -= mid; path->slots[level] -= mid;
btrfs_tree_unlock(c); btrfs_tree_unlock(c);
...@@ -2360,7 +2316,6 @@ static noinline int __push_leaf_right(struct btrfs_trans_handle *trans, ...@@ -2360,7 +2316,6 @@ static noinline int __push_leaf_right(struct btrfs_trans_handle *trans,
u32 right_nritems; u32 right_nritems;
u32 data_end; u32 data_end;
u32 this_item_size; u32 this_item_size;
int ret;
if (empty) if (empty)
nr = 0; nr = 0;
...@@ -2473,9 +2428,6 @@ static noinline int __push_leaf_right(struct btrfs_trans_handle *trans, ...@@ -2473,9 +2428,6 @@ static noinline int __push_leaf_right(struct btrfs_trans_handle *trans,
btrfs_mark_buffer_dirty(left); btrfs_mark_buffer_dirty(left);
btrfs_mark_buffer_dirty(right); btrfs_mark_buffer_dirty(right);
ret = btrfs_update_ref(trans, root, left, right, 0, push_items);
BUG_ON(ret);
btrfs_item_key(right, &disk_key, 0); btrfs_item_key(right, &disk_key, 0);
btrfs_set_node_key(upper, &disk_key, slot + 1); btrfs_set_node_key(upper, &disk_key, slot + 1);
btrfs_mark_buffer_dirty(upper); btrfs_mark_buffer_dirty(upper);
...@@ -2720,10 +2672,6 @@ static noinline int __push_leaf_left(struct btrfs_trans_handle *trans, ...@@ -2720,10 +2672,6 @@ static noinline int __push_leaf_left(struct btrfs_trans_handle *trans,
if (right_nritems) if (right_nritems)
btrfs_mark_buffer_dirty(right); btrfs_mark_buffer_dirty(right);
ret = btrfs_update_ref(trans, root, right, left,
old_left_nritems, push_items);
BUG_ON(ret);
btrfs_item_key(right, &disk_key, 0); btrfs_item_key(right, &disk_key, 0);
wret = fixup_low_keys(trans, root, path, &disk_key, 1); wret = fixup_low_keys(trans, root, path, &disk_key, 1);
if (wret) if (wret)
...@@ -2880,9 +2828,6 @@ static noinline int copy_for_split(struct btrfs_trans_handle *trans, ...@@ -2880,9 +2828,6 @@ static noinline int copy_for_split(struct btrfs_trans_handle *trans,
btrfs_mark_buffer_dirty(l); btrfs_mark_buffer_dirty(l);
BUG_ON(path->slots[0] != slot); BUG_ON(path->slots[0] != slot);
ret = btrfs_update_ref(trans, root, l, right, 0, nritems);
BUG_ON(ret);
if (mid <= slot) { if (mid <= slot) {
btrfs_tree_unlock(path->nodes[0]); btrfs_tree_unlock(path->nodes[0]);
free_extent_buffer(path->nodes[0]); free_extent_buffer(path->nodes[0]);
...@@ -2911,6 +2856,7 @@ static noinline int split_leaf(struct btrfs_trans_handle *trans, ...@@ -2911,6 +2856,7 @@ static noinline int split_leaf(struct btrfs_trans_handle *trans,
struct btrfs_path *path, int data_size, struct btrfs_path *path, int data_size,
int extend) int extend)
{ {
struct btrfs_disk_key disk_key;
struct extent_buffer *l; struct extent_buffer *l;
u32 nritems; u32 nritems;
int mid; int mid;
...@@ -2918,12 +2864,11 @@ static noinline int split_leaf(struct btrfs_trans_handle *trans, ...@@ -2918,12 +2864,11 @@ static noinline int split_leaf(struct btrfs_trans_handle *trans,
struct extent_buffer *right; struct extent_buffer *right;
int ret = 0; int ret = 0;
int wret; int wret;
int double_split; int split;
int num_doubles = 0; int num_doubles = 0;
/* first try to make some room by pushing left and right */ /* first try to make some room by pushing left and right */
if (data_size && ins_key->type != BTRFS_DIR_ITEM_KEY && if (data_size && ins_key->type != BTRFS_DIR_ITEM_KEY) {
!trans->transaction->delayed_refs.flushing) {
wret = push_leaf_right(trans, root, path, data_size, 0); wret = push_leaf_right(trans, root, path, data_size, 0);
if (wret < 0) if (wret < 0)
return wret; return wret;
...@@ -2945,16 +2890,53 @@ static noinline int split_leaf(struct btrfs_trans_handle *trans, ...@@ -2945,16 +2890,53 @@ static noinline int split_leaf(struct btrfs_trans_handle *trans,
return ret; return ret;
} }
again: again:
double_split = 0; split = 1;
l = path->nodes[0]; l = path->nodes[0];
slot = path->slots[0]; slot = path->slots[0];
nritems = btrfs_header_nritems(l); nritems = btrfs_header_nritems(l);
mid = (nritems + 1) / 2; mid = (nritems + 1) / 2;
right = btrfs_alloc_free_block(trans, root, root->leafsize, if (mid <= slot) {
path->nodes[1]->start, if (nritems == 1 ||
leaf_space_used(l, mid, nritems - mid) + data_size >
BTRFS_LEAF_DATA_SIZE(root)) {
if (slot >= nritems) {
split = 0;
} else {
mid = slot;
if (mid != nritems &&
leaf_space_used(l, mid, nritems - mid) +
data_size > BTRFS_LEAF_DATA_SIZE(root)) {
split = 2;
}
}
}
} else {
if (leaf_space_used(l, 0, mid) + data_size >
BTRFS_LEAF_DATA_SIZE(root)) {
if (!extend && data_size && slot == 0) {
split = 0;
} else if ((extend || !data_size) && slot == 0) {
mid = 1;
} else {
mid = slot;
if (mid != nritems &&
leaf_space_used(l, mid, nritems - mid) +
data_size > BTRFS_LEAF_DATA_SIZE(root)) {
split = 2 ;
}
}
}
}
if (split == 0)
btrfs_cpu_key_to_disk(&disk_key, ins_key);
else
btrfs_item_key(l, &disk_key, mid);
right = btrfs_alloc_free_block(trans, root, root->leafsize, 0,
root->root_key.objectid, root->root_key.objectid,
trans->transid, 0, l->start, 0); &disk_key, 0, l->start, 0);
if (IS_ERR(right)) { if (IS_ERR(right)) {
BUG_ON(1); BUG_ON(1);
return PTR_ERR(right); return PTR_ERR(right);
...@@ -2963,6 +2945,7 @@ static noinline int split_leaf(struct btrfs_trans_handle *trans, ...@@ -2963,6 +2945,7 @@ static noinline int split_leaf(struct btrfs_trans_handle *trans,
memset_extent_buffer(right, 0, 0, sizeof(struct btrfs_header)); memset_extent_buffer(right, 0, 0, sizeof(struct btrfs_header));
btrfs_set_header_bytenr(right, right->start); btrfs_set_header_bytenr(right, right->start);
btrfs_set_header_generation(right, trans->transid); btrfs_set_header_generation(right, trans->transid);
btrfs_set_header_backref_rev(right, BTRFS_MIXED_BACKREF_REV);
btrfs_set_header_owner(right, root->root_key.objectid); btrfs_set_header_owner(right, root->root_key.objectid);
btrfs_set_header_level(right, 0); btrfs_set_header_level(right, 0);
write_extent_buffer(right, root->fs_info->fsid, write_extent_buffer(right, root->fs_info->fsid,
...@@ -2973,79 +2956,47 @@ static noinline int split_leaf(struct btrfs_trans_handle *trans, ...@@ -2973,79 +2956,47 @@ static noinline int split_leaf(struct btrfs_trans_handle *trans,
(unsigned long)btrfs_header_chunk_tree_uuid(right), (unsigned long)btrfs_header_chunk_tree_uuid(right),
BTRFS_UUID_SIZE); BTRFS_UUID_SIZE);
if (mid <= slot) { if (split == 0) {
if (nritems == 1 || if (mid <= slot) {
leaf_space_used(l, mid, nritems - mid) + data_size > btrfs_set_header_nritems(right, 0);
BTRFS_LEAF_DATA_SIZE(root)) { wret = insert_ptr(trans, root, path,
if (slot >= nritems) { &disk_key, right->start,
struct btrfs_disk_key disk_key; path->slots[1] + 1, 1);
if (wret)
btrfs_cpu_key_to_disk(&disk_key, ins_key); ret = wret;
btrfs_set_header_nritems(right, 0);
wret = insert_ptr(trans, root, path,
&disk_key, right->start,
path->slots[1] + 1, 1);
if (wret)
ret = wret;
btrfs_tree_unlock(path->nodes[0]); btrfs_tree_unlock(path->nodes[0]);
free_extent_buffer(path->nodes[0]); free_extent_buffer(path->nodes[0]);
path->nodes[0] = right; path->nodes[0] = right;
path->slots[0] = 0; path->slots[0] = 0;
path->slots[1] += 1; path->slots[1] += 1;
btrfs_mark_buffer_dirty(right); } else {
return ret; btrfs_set_header_nritems(right, 0);
} wret = insert_ptr(trans, root, path,
mid = slot; &disk_key,
if (mid != nritems && right->start,
leaf_space_used(l, mid, nritems - mid) + path->slots[1], 1);
data_size > BTRFS_LEAF_DATA_SIZE(root)) { if (wret)
double_split = 1; ret = wret;
} btrfs_tree_unlock(path->nodes[0]);
} free_extent_buffer(path->nodes[0]);
} else { path->nodes[0] = right;
if (leaf_space_used(l, 0, mid) + data_size > path->slots[0] = 0;
BTRFS_LEAF_DATA_SIZE(root)) { if (path->slots[1] == 0) {
if (!extend && data_size && slot == 0) { wret = fixup_low_keys(trans, root,
struct btrfs_disk_key disk_key; path, &disk_key, 1);
btrfs_cpu_key_to_disk(&disk_key, ins_key);
btrfs_set_header_nritems(right, 0);
wret = insert_ptr(trans, root, path,
&disk_key,
right->start,
path->slots[1], 1);
if (wret) if (wret)
ret = wret; ret = wret;
btrfs_tree_unlock(path->nodes[0]);
free_extent_buffer(path->nodes[0]);
path->nodes[0] = right;
path->slots[0] = 0;
if (path->slots[1] == 0) {
wret = fixup_low_keys(trans, root,
path, &disk_key, 1);
if (wret)
ret = wret;
}
btrfs_mark_buffer_dirty(right);
return ret;
} else if ((extend || !data_size) && slot == 0) {
mid = 1;
} else {
mid = slot;
if (mid != nritems &&
leaf_space_used(l, mid, nritems - mid) +
data_size > BTRFS_LEAF_DATA_SIZE(root)) {
double_split = 1;
}
} }
} }
btrfs_mark_buffer_dirty(right);
return ret;
} }
ret = copy_for_split(trans, root, path, l, right, slot, mid, nritems); ret = copy_for_split(trans, root, path, l, right, slot, mid, nritems);
BUG_ON(ret); BUG_ON(ret);
if (double_split) { if (split == 2) {
BUG_ON(num_doubles != 0); BUG_ON(num_doubles != 0);
num_doubles++; num_doubles++;
goto again; goto again;
...@@ -3447,7 +3398,7 @@ int btrfs_insert_some_items(struct btrfs_trans_handle *trans, ...@@ -3447,7 +3398,7 @@ int btrfs_insert_some_items(struct btrfs_trans_handle *trans,
/* figure out how many keys we can insert in here */ /* figure out how many keys we can insert in here */
total_data = data_size[0]; total_data = data_size[0];
for (i = 1; i < nr; i++) { for (i = 1; i < nr; i++) {
if (comp_cpu_keys(&found_key, cpu_key + i) <= 0) if (btrfs_comp_cpu_keys(&found_key, cpu_key + i) <= 0)
break; break;
total_data += data_size[i]; total_data += data_size[i];
} }
...@@ -3745,9 +3696,7 @@ static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root, ...@@ -3745,9 +3696,7 @@ static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
/* /*
* a helper function to delete the leaf pointed to by path->slots[1] and * a helper function to delete the leaf pointed to by path->slots[1] and
* path->nodes[1]. bytenr is the node block pointer, but since the callers * path->nodes[1].
* already know it, it is faster to have them pass it down than to
* read it out of the node again.
* *
* This deletes the pointer in path->nodes[1] and frees the leaf * This deletes the pointer in path->nodes[1] and frees the leaf
* block extent. zero is returned if it all worked out, < 0 otherwise. * block extent. zero is returned if it all worked out, < 0 otherwise.
...@@ -3755,15 +3704,14 @@ static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root, ...@@ -3755,15 +3704,14 @@ static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
* The path must have already been setup for deleting the leaf, including * The path must have already been setup for deleting the leaf, including
* all the proper balancing. path->nodes[1] must be locked. * all the proper balancing. path->nodes[1] must be locked.
*/ */
noinline int btrfs_del_leaf(struct btrfs_trans_handle *trans, static noinline int btrfs_del_leaf(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct btrfs_path *path, u64 bytenr) struct btrfs_path *path,
struct extent_buffer *leaf)
{ {
int ret; int ret;
u64 root_gen = btrfs_header_generation(path->nodes[1]);
u64 parent_start = path->nodes[1]->start;
u64 parent_owner = btrfs_header_owner(path->nodes[1]);
WARN_ON(btrfs_header_generation(leaf) != trans->transid);
ret = del_ptr(trans, root, path, 1, path->slots[1]); ret = del_ptr(trans, root, path, 1, path->slots[1]);
if (ret) if (ret)
return ret; return ret;
...@@ -3774,10 +3722,8 @@ noinline int btrfs_del_leaf(struct btrfs_trans_handle *trans, ...@@ -3774,10 +3722,8 @@ noinline int btrfs_del_leaf(struct btrfs_trans_handle *trans,
*/ */
btrfs_unlock_up_safe(path, 0); btrfs_unlock_up_safe(path, 0);
ret = btrfs_free_extent(trans, root, bytenr, ret = btrfs_free_extent(trans, root, leaf->start, leaf->len,
btrfs_level_size(root, 0), 0, root->root_key.objectid, 0, 0);
parent_start, parent_owner,
root_gen, 0, 1);
return ret; return ret;
} }
/* /*
...@@ -3845,7 +3791,7 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root, ...@@ -3845,7 +3791,7 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
if (leaf == root->node) { if (leaf == root->node) {
btrfs_set_header_level(leaf, 0); btrfs_set_header_level(leaf, 0);
} else { } else {
ret = btrfs_del_leaf(trans, root, path, leaf->start); ret = btrfs_del_leaf(trans, root, path, leaf);
BUG_ON(ret); BUG_ON(ret);
} }
} else { } else {
...@@ -3861,8 +3807,7 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root, ...@@ -3861,8 +3807,7 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
} }
/* delete the leaf if it is mostly empty */ /* delete the leaf if it is mostly empty */
if (used < BTRFS_LEAF_DATA_SIZE(root) / 4 && if (used < BTRFS_LEAF_DATA_SIZE(root) / 2) {
!trans->transaction->delayed_refs.flushing) {
/* push_leaf_left fixes the path. /* push_leaf_left fixes the path.
* make sure the path still points to our leaf * make sure the path still points to our leaf
* for possible call to del_ptr below * for possible call to del_ptr below
...@@ -3884,8 +3829,7 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root, ...@@ -3884,8 +3829,7 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
if (btrfs_header_nritems(leaf) == 0) { if (btrfs_header_nritems(leaf) == 0) {
path->slots[1] = slot; path->slots[1] = slot;
ret = btrfs_del_leaf(trans, root, path, ret = btrfs_del_leaf(trans, root, path, leaf);
leaf->start);
BUG_ON(ret); BUG_ON(ret);
free_extent_buffer(leaf); free_extent_buffer(leaf);
} else { } else {
......
...@@ -45,6 +45,8 @@ struct btrfs_ordered_sum; ...@@ -45,6 +45,8 @@ struct btrfs_ordered_sum;
#define BTRFS_MAX_LEVEL 8 #define BTRFS_MAX_LEVEL 8
#define BTRFS_COMPAT_EXTENT_TREE_V0
/* /*
* files bigger than this get some pre-flushing when they are added * files bigger than this get some pre-flushing when they are added
* to the ordered operations list. That way we limit the total * to the ordered operations list. That way we limit the total
...@@ -267,7 +269,18 @@ static inline unsigned long btrfs_chunk_item_size(int num_stripes) ...@@ -267,7 +269,18 @@ static inline unsigned long btrfs_chunk_item_size(int num_stripes)
} }
#define BTRFS_FSID_SIZE 16 #define BTRFS_FSID_SIZE 16
#define BTRFS_HEADER_FLAG_WRITTEN (1 << 0) #define BTRFS_HEADER_FLAG_WRITTEN (1ULL << 0)
#define BTRFS_HEADER_FLAG_RELOC (1ULL << 1)
#define BTRFS_SUPER_FLAG_SEEDING (1ULL << 32)
#define BTRFS_SUPER_FLAG_METADUMP (1ULL << 33)
#define BTRFS_BACKREF_REV_MAX 256
#define BTRFS_BACKREF_REV_SHIFT 56
#define BTRFS_BACKREF_REV_MASK (((u64)BTRFS_BACKREF_REV_MAX - 1) << \
BTRFS_BACKREF_REV_SHIFT)
#define BTRFS_OLD_BACKREF_REV 0
#define BTRFS_MIXED_BACKREF_REV 1
/* /*
* every tree block (leaf or node) starts with this header. * every tree block (leaf or node) starts with this header.
...@@ -296,7 +309,6 @@ struct btrfs_header { ...@@ -296,7 +309,6 @@ struct btrfs_header {
sizeof(struct btrfs_item) - \ sizeof(struct btrfs_item) - \
sizeof(struct btrfs_file_extent_item)) sizeof(struct btrfs_file_extent_item))
#define BTRFS_SUPER_FLAG_SEEDING (1ULL << 32)
/* /*
* this is a very generous portion of the super block, giving us * this is a very generous portion of the super block, giving us
...@@ -355,9 +367,12 @@ struct btrfs_super_block { ...@@ -355,9 +367,12 @@ struct btrfs_super_block {
* Compat flags that we support. If any incompat flags are set other than the * Compat flags that we support. If any incompat flags are set other than the
* ones specified below then we will fail to mount * ones specified below then we will fail to mount
*/ */
#define BTRFS_FEATURE_COMPAT_SUPP 0x0 #define BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF (1ULL << 0)
#define BTRFS_FEATURE_COMPAT_RO_SUPP 0x0
#define BTRFS_FEATURE_INCOMPAT_SUPP 0x0 #define BTRFS_FEATURE_COMPAT_SUPP 0ULL
#define BTRFS_FEATURE_COMPAT_RO_SUPP 0ULL
#define BTRFS_FEATURE_INCOMPAT_SUPP \
BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF
/* /*
* A leaf is full of items. offset and size tell us where to find * A leaf is full of items. offset and size tell us where to find
...@@ -421,23 +436,65 @@ struct btrfs_path { ...@@ -421,23 +436,65 @@ struct btrfs_path {
unsigned int keep_locks:1; unsigned int keep_locks:1;
unsigned int skip_locking:1; unsigned int skip_locking:1;
unsigned int leave_spinning:1; unsigned int leave_spinning:1;
unsigned int search_commit_root:1;
}; };
/* /*
* items in the extent btree are used to record the objectid of the * items in the extent btree are used to record the objectid of the
* owner of the block and the number of references * owner of the block and the number of references
*/ */
struct btrfs_extent_item { struct btrfs_extent_item {
__le64 refs;
__le64 generation;
__le64 flags;
} __attribute__ ((__packed__));
struct btrfs_extent_item_v0 {
__le32 refs; __le32 refs;
} __attribute__ ((__packed__)); } __attribute__ ((__packed__));
struct btrfs_extent_ref { #define BTRFS_MAX_EXTENT_ITEM_SIZE(r) ((BTRFS_LEAF_DATA_SIZE(r) >> 4) - \
sizeof(struct btrfs_item))
#define BTRFS_EXTENT_FLAG_DATA (1ULL << 0)
#define BTRFS_EXTENT_FLAG_TREE_BLOCK (1ULL << 1)
/* following flags only apply to tree blocks */
/* use full backrefs for extent pointers in the block */
#define BTRFS_BLOCK_FLAG_FULL_BACKREF (1ULL << 8)
struct btrfs_tree_block_info {
struct btrfs_disk_key key;
u8 level;
} __attribute__ ((__packed__));
struct btrfs_extent_data_ref {
__le64 root;
__le64 objectid;
__le64 offset;
__le32 count;
} __attribute__ ((__packed__));
struct btrfs_shared_data_ref {
__le32 count;
} __attribute__ ((__packed__));
struct btrfs_extent_inline_ref {
u8 type;
u64 offset;
} __attribute__ ((__packed__));
/* old style backrefs item */
struct btrfs_extent_ref_v0 {
__le64 root; __le64 root;
__le64 generation; __le64 generation;
__le64 objectid; __le64 objectid;
__le32 num_refs; __le32 count;
} __attribute__ ((__packed__)); } __attribute__ ((__packed__));
/* dev extents record free space on individual devices. The owner /* dev extents record free space on individual devices. The owner
* field points back to the chunk allocation mapping tree that allocated * field points back to the chunk allocation mapping tree that allocated
* the extent. The chunk tree uuid field is a way to double check the owner * the extent. The chunk tree uuid field is a way to double check the owner
...@@ -695,12 +752,7 @@ struct btrfs_block_group_cache { ...@@ -695,12 +752,7 @@ struct btrfs_block_group_cache {
struct list_head cluster_list; struct list_head cluster_list;
}; };
struct btrfs_leaf_ref_tree { struct reloc_control;
struct rb_root root;
struct list_head list;
spinlock_t lock;
};
struct btrfs_device; struct btrfs_device;
struct btrfs_fs_devices; struct btrfs_fs_devices;
struct btrfs_fs_info { struct btrfs_fs_info {
...@@ -831,18 +883,11 @@ struct btrfs_fs_info { ...@@ -831,18 +883,11 @@ struct btrfs_fs_info {
struct task_struct *cleaner_kthread; struct task_struct *cleaner_kthread;
int thread_pool_size; int thread_pool_size;
/* tree relocation relocated fields */
struct list_head dead_reloc_roots;
struct btrfs_leaf_ref_tree reloc_ref_tree;
struct btrfs_leaf_ref_tree shared_ref_tree;
struct kobject super_kobj; struct kobject super_kobj;
struct completion kobj_unregister; struct completion kobj_unregister;
int do_barriers; int do_barriers;
int closing; int closing;
int log_root_recovering; int log_root_recovering;
atomic_t throttles;
atomic_t throttle_gen;
u64 total_pinned; u64 total_pinned;
...@@ -861,6 +906,8 @@ struct btrfs_fs_info { ...@@ -861,6 +906,8 @@ struct btrfs_fs_info {
*/ */
struct list_head space_info; struct list_head space_info;
struct reloc_control *reloc_ctl;
spinlock_t delalloc_lock; spinlock_t delalloc_lock;
spinlock_t new_trans_lock; spinlock_t new_trans_lock;
u64 delalloc_bytes; u64 delalloc_bytes;
...@@ -891,7 +938,6 @@ struct btrfs_fs_info { ...@@ -891,7 +938,6 @@ struct btrfs_fs_info {
* in ram representation of the tree. extent_root is used for all allocations * in ram representation of the tree. extent_root is used for all allocations
* and for the extent tree extent_root root. * and for the extent tree extent_root root.
*/ */
struct btrfs_dirty_root;
struct btrfs_root { struct btrfs_root {
struct extent_buffer *node; struct extent_buffer *node;
...@@ -899,9 +945,6 @@ struct btrfs_root { ...@@ -899,9 +945,6 @@ struct btrfs_root {
spinlock_t node_lock; spinlock_t node_lock;
struct extent_buffer *commit_root; struct extent_buffer *commit_root;
struct btrfs_leaf_ref_tree *ref_tree;
struct btrfs_leaf_ref_tree ref_tree_struct;
struct btrfs_dirty_root *dirty_root;
struct btrfs_root *log_root; struct btrfs_root *log_root;
struct btrfs_root *reloc_root; struct btrfs_root *reloc_root;
...@@ -952,10 +995,15 @@ struct btrfs_root { ...@@ -952,10 +995,15 @@ struct btrfs_root {
/* the dirty list is only used by non-reference counted roots */ /* the dirty list is only used by non-reference counted roots */
struct list_head dirty_list; struct list_head dirty_list;
struct list_head root_list;
spinlock_t list_lock; spinlock_t list_lock;
struct list_head dead_list;
struct list_head orphan_list; struct list_head orphan_list;
spinlock_t inode_lock;
/* red-black tree that keeps track of in-memory inodes */
struct rb_root inode_tree;
/* /*
* right now this just gets used so that a root has its own devid * right now this just gets used so that a root has its own devid
* for stat. It may be used for more later * for stat. It may be used for more later
...@@ -1017,7 +1065,16 @@ struct btrfs_root { ...@@ -1017,7 +1065,16 @@ struct btrfs_root {
* are used, and how many references there are to each block * are used, and how many references there are to each block
*/ */
#define BTRFS_EXTENT_ITEM_KEY 168 #define BTRFS_EXTENT_ITEM_KEY 168
#define BTRFS_EXTENT_REF_KEY 180
#define BTRFS_TREE_BLOCK_REF_KEY 176
#define BTRFS_EXTENT_DATA_REF_KEY 178
#define BTRFS_EXTENT_REF_V0_KEY 180
#define BTRFS_SHARED_BLOCK_REF_KEY 182
#define BTRFS_SHARED_DATA_REF_KEY 184
/* /*
* block groups give us hints into the extent allocation trees. Which * block groups give us hints into the extent allocation trees. Which
...@@ -1043,6 +1100,8 @@ struct btrfs_root { ...@@ -1043,6 +1100,8 @@ struct btrfs_root {
#define BTRFS_MOUNT_COMPRESS (1 << 5) #define BTRFS_MOUNT_COMPRESS (1 << 5)
#define BTRFS_MOUNT_NOTREELOG (1 << 6) #define BTRFS_MOUNT_NOTREELOG (1 << 6)
#define BTRFS_MOUNT_FLUSHONCOMMIT (1 << 7) #define BTRFS_MOUNT_FLUSHONCOMMIT (1 << 7)
#define BTRFS_MOUNT_SSD_SPREAD (1 << 8)
#define BTRFS_MOUNT_NOSSD (1 << 9)
#define btrfs_clear_opt(o, opt) ((o) &= ~BTRFS_MOUNT_##opt) #define btrfs_clear_opt(o, opt) ((o) &= ~BTRFS_MOUNT_##opt)
#define btrfs_set_opt(o, opt) ((o) |= BTRFS_MOUNT_##opt) #define btrfs_set_opt(o, opt) ((o) |= BTRFS_MOUNT_##opt)
...@@ -1056,12 +1115,14 @@ struct btrfs_root { ...@@ -1056,12 +1115,14 @@ struct btrfs_root {
#define BTRFS_INODE_READONLY (1 << 2) #define BTRFS_INODE_READONLY (1 << 2)
#define BTRFS_INODE_NOCOMPRESS (1 << 3) #define BTRFS_INODE_NOCOMPRESS (1 << 3)
#define BTRFS_INODE_PREALLOC (1 << 4) #define BTRFS_INODE_PREALLOC (1 << 4)
#define btrfs_clear_flag(inode, flag) (BTRFS_I(inode)->flags &= \ #define BTRFS_INODE_SYNC (1 << 5)
~BTRFS_INODE_##flag) #define BTRFS_INODE_IMMUTABLE (1 << 6)
#define btrfs_set_flag(inode, flag) (BTRFS_I(inode)->flags |= \ #define BTRFS_INODE_APPEND (1 << 7)
BTRFS_INODE_##flag) #define BTRFS_INODE_NODUMP (1 << 8)
#define btrfs_test_flag(inode, flag) (BTRFS_I(inode)->flags & \ #define BTRFS_INODE_NOATIME (1 << 9)
BTRFS_INODE_##flag) #define BTRFS_INODE_DIRSYNC (1 << 10)
/* some macros to generate set/get funcs for the struct fields. This /* some macros to generate set/get funcs for the struct fields. This
* assumes there is a lefoo_to_cpu for every type, so lets make a simple * assumes there is a lefoo_to_cpu for every type, so lets make a simple
* one for u8: * one for u8:
...@@ -1317,24 +1378,67 @@ static inline u8 *btrfs_dev_extent_chunk_tree_uuid(struct btrfs_dev_extent *dev) ...@@ -1317,24 +1378,67 @@ static inline u8 *btrfs_dev_extent_chunk_tree_uuid(struct btrfs_dev_extent *dev)
return (u8 *)((unsigned long)dev + ptr); return (u8 *)((unsigned long)dev + ptr);
} }
/* struct btrfs_extent_ref */ BTRFS_SETGET_FUNCS(extent_refs, struct btrfs_extent_item, refs, 64);
BTRFS_SETGET_FUNCS(ref_root, struct btrfs_extent_ref, root, 64); BTRFS_SETGET_FUNCS(extent_generation, struct btrfs_extent_item,
BTRFS_SETGET_FUNCS(ref_generation, struct btrfs_extent_ref, generation, 64); generation, 64);
BTRFS_SETGET_FUNCS(ref_objectid, struct btrfs_extent_ref, objectid, 64); BTRFS_SETGET_FUNCS(extent_flags, struct btrfs_extent_item, flags, 64);
BTRFS_SETGET_FUNCS(ref_num_refs, struct btrfs_extent_ref, num_refs, 32);
BTRFS_SETGET_STACK_FUNCS(stack_ref_root, struct btrfs_extent_ref, root, 64); BTRFS_SETGET_FUNCS(extent_refs_v0, struct btrfs_extent_item_v0, refs, 32);
BTRFS_SETGET_STACK_FUNCS(stack_ref_generation, struct btrfs_extent_ref,
generation, 64);
BTRFS_SETGET_STACK_FUNCS(stack_ref_objectid, struct btrfs_extent_ref, BTRFS_SETGET_FUNCS(tree_block_level, struct btrfs_tree_block_info, level, 8);
objectid, 64);
BTRFS_SETGET_STACK_FUNCS(stack_ref_num_refs, struct btrfs_extent_ref, static inline void btrfs_tree_block_key(struct extent_buffer *eb,
num_refs, 32); struct btrfs_tree_block_info *item,
struct btrfs_disk_key *key)
{
read_eb_member(eb, item, struct btrfs_tree_block_info, key, key);
}
static inline void btrfs_set_tree_block_key(struct extent_buffer *eb,
struct btrfs_tree_block_info *item,
struct btrfs_disk_key *key)
{
write_eb_member(eb, item, struct btrfs_tree_block_info, key, key);
}
BTRFS_SETGET_FUNCS(extent_data_ref_root, struct btrfs_extent_data_ref,
root, 64);
BTRFS_SETGET_FUNCS(extent_data_ref_objectid, struct btrfs_extent_data_ref,
objectid, 64);
BTRFS_SETGET_FUNCS(extent_data_ref_offset, struct btrfs_extent_data_ref,
offset, 64);
BTRFS_SETGET_FUNCS(extent_data_ref_count, struct btrfs_extent_data_ref,
count, 32);
BTRFS_SETGET_FUNCS(shared_data_ref_count, struct btrfs_shared_data_ref,
count, 32);
/* struct btrfs_extent_item */ BTRFS_SETGET_FUNCS(extent_inline_ref_type, struct btrfs_extent_inline_ref,
BTRFS_SETGET_FUNCS(extent_refs, struct btrfs_extent_item, refs, 32); type, 8);
BTRFS_SETGET_STACK_FUNCS(stack_extent_refs, struct btrfs_extent_item, BTRFS_SETGET_FUNCS(extent_inline_ref_offset, struct btrfs_extent_inline_ref,
refs, 32); offset, 64);
static inline u32 btrfs_extent_inline_ref_size(int type)
{
if (type == BTRFS_TREE_BLOCK_REF_KEY ||
type == BTRFS_SHARED_BLOCK_REF_KEY)
return sizeof(struct btrfs_extent_inline_ref);
if (type == BTRFS_SHARED_DATA_REF_KEY)
return sizeof(struct btrfs_shared_data_ref) +
sizeof(struct btrfs_extent_inline_ref);
if (type == BTRFS_EXTENT_DATA_REF_KEY)
return sizeof(struct btrfs_extent_data_ref) +
offsetof(struct btrfs_extent_inline_ref, offset);
BUG();
return 0;
}
BTRFS_SETGET_FUNCS(ref_root_v0, struct btrfs_extent_ref_v0, root, 64);
BTRFS_SETGET_FUNCS(ref_generation_v0, struct btrfs_extent_ref_v0,
generation, 64);
BTRFS_SETGET_FUNCS(ref_objectid_v0, struct btrfs_extent_ref_v0, objectid, 64);
BTRFS_SETGET_FUNCS(ref_count_v0, struct btrfs_extent_ref_v0, count, 32);
/* struct btrfs_node */ /* struct btrfs_node */
BTRFS_SETGET_FUNCS(key_blockptr, struct btrfs_key_ptr, blockptr, 64); BTRFS_SETGET_FUNCS(key_blockptr, struct btrfs_key_ptr, blockptr, 64);
...@@ -1558,6 +1662,21 @@ static inline int btrfs_clear_header_flag(struct extent_buffer *eb, u64 flag) ...@@ -1558,6 +1662,21 @@ static inline int btrfs_clear_header_flag(struct extent_buffer *eb, u64 flag)
return (flags & flag) == flag; return (flags & flag) == flag;
} }
static inline int btrfs_header_backref_rev(struct extent_buffer *eb)
{
u64 flags = btrfs_header_flags(eb);
return flags >> BTRFS_BACKREF_REV_SHIFT;
}
static inline void btrfs_set_header_backref_rev(struct extent_buffer *eb,
int rev)
{
u64 flags = btrfs_header_flags(eb);
flags &= ~BTRFS_BACKREF_REV_MASK;
flags |= (u64)rev << BTRFS_BACKREF_REV_SHIFT;
btrfs_set_header_flags(eb, flags);
}
static inline u8 *btrfs_header_fsid(struct extent_buffer *eb) static inline u8 *btrfs_header_fsid(struct extent_buffer *eb)
{ {
unsigned long ptr = offsetof(struct btrfs_header, fsid); unsigned long ptr = offsetof(struct btrfs_header, fsid);
...@@ -1790,39 +1909,32 @@ int btrfs_update_pinned_extents(struct btrfs_root *root, ...@@ -1790,39 +1909,32 @@ int btrfs_update_pinned_extents(struct btrfs_root *root,
int btrfs_drop_leaf_ref(struct btrfs_trans_handle *trans, int btrfs_drop_leaf_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct extent_buffer *leaf); struct btrfs_root *root, struct extent_buffer *leaf);
int btrfs_cross_ref_exist(struct btrfs_trans_handle *trans, int btrfs_cross_ref_exist(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 objectid, u64 bytenr); struct btrfs_root *root,
u64 objectid, u64 offset, u64 bytenr);
int btrfs_copy_pinned(struct btrfs_root *root, struct extent_io_tree *copy); int btrfs_copy_pinned(struct btrfs_root *root, struct extent_io_tree *copy);
struct btrfs_block_group_cache *btrfs_lookup_block_group( struct btrfs_block_group_cache *btrfs_lookup_block_group(
struct btrfs_fs_info *info, struct btrfs_fs_info *info,
u64 bytenr); u64 bytenr);
void btrfs_put_block_group(struct btrfs_block_group_cache *cache);
u64 btrfs_find_block_group(struct btrfs_root *root, u64 btrfs_find_block_group(struct btrfs_root *root,
u64 search_start, u64 search_hint, int owner); u64 search_start, u64 search_hint, int owner);
struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans, struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root, u32 blocksize,
u32 blocksize, u64 parent, u64 parent, u64 root_objectid,
u64 root_objectid, struct btrfs_disk_key *key, int level,
u64 ref_generation, u64 hint, u64 empty_size);
int level,
u64 hint,
u64 empty_size);
struct extent_buffer *btrfs_init_new_buffer(struct btrfs_trans_handle *trans, struct extent_buffer *btrfs_init_new_buffer(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
u64 bytenr, u32 blocksize, u64 bytenr, u32 blocksize,
int level); int level);
int btrfs_alloc_extent(struct btrfs_trans_handle *trans, int btrfs_alloc_reserved_file_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
u64 num_bytes, u64 parent, u64 min_bytes, u64 root_objectid, u64 owner,
u64 root_objectid, u64 ref_generation, u64 offset, struct btrfs_key *ins);
u64 owner, u64 empty_size, u64 hint_byte, int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans,
u64 search_end, struct btrfs_key *ins, u64 data); struct btrfs_root *root,
int btrfs_alloc_reserved_extent(struct btrfs_trans_handle *trans, u64 root_objectid, u64 owner, u64 offset,
struct btrfs_root *root, u64 parent, struct btrfs_key *ins);
u64 root_objectid, u64 ref_generation,
u64 owner, struct btrfs_key *ins);
int btrfs_alloc_logged_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 parent,
u64 root_objectid, u64 ref_generation,
u64 owner, struct btrfs_key *ins);
int btrfs_reserve_extent(struct btrfs_trans_handle *trans, int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
u64 num_bytes, u64 min_alloc_size, u64 num_bytes, u64 min_alloc_size,
...@@ -1830,18 +1942,18 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans, ...@@ -1830,18 +1942,18 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
u64 search_end, struct btrfs_key *ins, u64 search_end, struct btrfs_key *ins,
u64 data); u64 data);
int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct extent_buffer *orig_buf, struct extent_buffer *buf, struct extent_buffer *buf, int full_backref);
u32 *nr_extents); int btrfs_dec_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
int btrfs_cache_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct extent_buffer *buf, int full_backref);
struct extent_buffer *buf, u32 nr_extents); int btrfs_set_disk_extent_flags(struct btrfs_trans_handle *trans,
int btrfs_update_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct btrfs_root *root, struct extent_buffer *orig_buf, u64 bytenr, u64 num_bytes, u64 flags,
struct extent_buffer *buf, int start_slot, int nr); int is_data);
int btrfs_free_extent(struct btrfs_trans_handle *trans, int btrfs_free_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
u64 bytenr, u64 num_bytes, u64 parent, u64 bytenr, u64 num_bytes, u64 parent,
u64 root_objectid, u64 ref_generation, u64 root_objectid, u64 owner, u64 offset);
u64 owner_objectid, int pin);
int btrfs_free_reserved_extent(struct btrfs_root *root, u64 start, u64 len); int btrfs_free_reserved_extent(struct btrfs_root *root, u64 start, u64 len);
int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
...@@ -1849,13 +1961,8 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, ...@@ -1849,13 +1961,8 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans,
int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
u64 bytenr, u64 num_bytes, u64 parent, u64 bytenr, u64 num_bytes, u64 parent,
u64 root_objectid, u64 ref_generation, u64 root_objectid, u64 owner, u64 offset);
u64 owner_objectid);
int btrfs_update_extent_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 bytenr, u64 num_bytes,
u64 orig_parent, u64 parent,
u64 root_objectid, u64 ref_generation,
u64 owner_objectid);
int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans, int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans,
struct btrfs_root *root); struct btrfs_root *root);
int btrfs_extent_readonly(struct btrfs_root *root, u64 bytenr); int btrfs_extent_readonly(struct btrfs_root *root, u64 bytenr);
...@@ -1867,16 +1974,9 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans, ...@@ -1867,16 +1974,9 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
u64 size); u64 size);
int btrfs_remove_block_group(struct btrfs_trans_handle *trans, int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 group_start); struct btrfs_root *root, u64 group_start);
int btrfs_relocate_block_group(struct btrfs_root *root, u64 group_start); int btrfs_prepare_block_group_relocation(struct btrfs_root *root,
int btrfs_free_reloc_root(struct btrfs_trans_handle *trans, struct btrfs_block_group_cache *group);
struct btrfs_root *root);
int btrfs_drop_dead_reloc_roots(struct btrfs_root *root);
int btrfs_reloc_tree_cache_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct extent_buffer *buf, u64 orig_start);
int btrfs_add_dead_reloc_root(struct btrfs_root *root);
int btrfs_cleanup_reloc_trees(struct btrfs_root *root);
int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len);
u64 btrfs_reduce_alloc_profile(struct btrfs_root *root, u64 flags); u64 btrfs_reduce_alloc_profile(struct btrfs_root *root, u64 flags);
void btrfs_set_inode_space_info(struct btrfs_root *root, struct inode *ionde); void btrfs_set_inode_space_info(struct btrfs_root *root, struct inode *ionde);
void btrfs_clear_space_info_full(struct btrfs_fs_info *info); void btrfs_clear_space_info_full(struct btrfs_fs_info *info);
...@@ -1891,13 +1991,12 @@ void btrfs_delalloc_reserve_space(struct btrfs_root *root, struct inode *inode, ...@@ -1891,13 +1991,12 @@ void btrfs_delalloc_reserve_space(struct btrfs_root *root, struct inode *inode,
void btrfs_delalloc_free_space(struct btrfs_root *root, struct inode *inode, void btrfs_delalloc_free_space(struct btrfs_root *root, struct inode *inode,
u64 bytes); u64 bytes);
/* ctree.c */ /* ctree.c */
int btrfs_bin_search(struct extent_buffer *eb, struct btrfs_key *key,
int level, int *slot);
int btrfs_comp_cpu_keys(struct btrfs_key *k1, struct btrfs_key *k2);
int btrfs_previous_item(struct btrfs_root *root, int btrfs_previous_item(struct btrfs_root *root,
struct btrfs_path *path, u64 min_objectid, struct btrfs_path *path, u64 min_objectid,
int type); int type);
int btrfs_merge_path(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_key *node_keys,
u64 *nodes, int lowest_level);
int btrfs_set_item_key_safe(struct btrfs_trans_handle *trans, int btrfs_set_item_key_safe(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_path *path, struct btrfs_root *root, struct btrfs_path *path,
struct btrfs_key *new_key); struct btrfs_key *new_key);
...@@ -1918,6 +2017,8 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans, ...@@ -1918,6 +2017,8 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct extent_buffer *buf, struct extent_buffer *buf,
struct extent_buffer **cow_ret, u64 new_root_objectid); struct extent_buffer **cow_ret, u64 new_root_objectid);
int btrfs_block_can_be_shared(struct btrfs_root *root,
struct extent_buffer *buf);
int btrfs_extend_item(struct btrfs_trans_handle *trans, struct btrfs_root int btrfs_extend_item(struct btrfs_trans_handle *trans, struct btrfs_root
*root, struct btrfs_path *path, u32 data_size); *root, struct btrfs_path *path, u32 data_size);
int btrfs_truncate_item(struct btrfs_trans_handle *trans, int btrfs_truncate_item(struct btrfs_trans_handle *trans,
...@@ -1944,9 +2045,6 @@ void btrfs_unlock_up_safe(struct btrfs_path *p, int level); ...@@ -1944,9 +2045,6 @@ void btrfs_unlock_up_safe(struct btrfs_path *p, int level);
int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root, int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct btrfs_path *path, int slot, int nr); struct btrfs_path *path, int slot, int nr);
int btrfs_del_leaf(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path, u64 bytenr);
static inline int btrfs_del_item(struct btrfs_trans_handle *trans, static inline int btrfs_del_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct btrfs_path *path) struct btrfs_path *path)
...@@ -2005,8 +2103,9 @@ int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, struct ...@@ -2005,8 +2103,9 @@ int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, struct
btrfs_root_item *item, struct btrfs_key *key); btrfs_root_item *item, struct btrfs_key *key);
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);
struct btrfs_root *latest_root); int btrfs_set_root_node(struct btrfs_root_item *item,
struct extent_buffer *node);
/* dir-item.c */ /* dir-item.c */
int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, int btrfs_insert_dir_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root, const char *name, struct btrfs_root *root, const char *name,
...@@ -2139,7 +2238,6 @@ int btrfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf); ...@@ -2139,7 +2238,6 @@ int btrfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf);
int btrfs_readpage(struct file *file, struct page *page); int btrfs_readpage(struct file *file, struct page *page);
void btrfs_delete_inode(struct inode *inode); void btrfs_delete_inode(struct inode *inode);
void btrfs_put_inode(struct inode *inode); void btrfs_put_inode(struct inode *inode);
void btrfs_read_locked_inode(struct inode *inode);
int btrfs_write_inode(struct inode *inode, int wait); 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);
...@@ -2147,12 +2245,8 @@ void btrfs_destroy_inode(struct inode *inode); ...@@ -2147,12 +2245,8 @@ void btrfs_destroy_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);
struct inode *btrfs_ilookup(struct super_block *s, u64 objectid,
struct btrfs_root *root, int wait);
struct inode *btrfs_iget_locked(struct super_block *s, u64 objectid,
struct btrfs_root *root);
struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location, struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
struct btrfs_root *root, int *is_new); struct btrfs_root *root);
int btrfs_commit_write(struct file *file, struct page *page, int btrfs_commit_write(struct file *file, struct page *page,
unsigned from, unsigned to); unsigned from, unsigned to);
struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page, struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page,
...@@ -2168,6 +2262,8 @@ int btrfs_cont_expand(struct inode *inode, loff_t size); ...@@ -2168,6 +2262,8 @@ int btrfs_cont_expand(struct inode *inode, loff_t size);
/* 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);
void btrfs_update_iflags(struct inode *inode);
void btrfs_inherit_iflags(struct inode *inode, struct inode *dir);
/* file.c */ /* file.c */
int btrfs_sync_file(struct file *file, struct dentry *dentry, int datasync); int btrfs_sync_file(struct file *file, struct dentry *dentry, int datasync);
...@@ -2205,8 +2301,20 @@ int btrfs_parse_options(struct btrfs_root *root, char *options); ...@@ -2205,8 +2301,20 @@ int btrfs_parse_options(struct btrfs_root *root, char *options);
int btrfs_sync_fs(struct super_block *sb, int wait); int btrfs_sync_fs(struct super_block *sb, int wait);
/* acl.c */ /* acl.c */
#ifdef CONFIG_FS_POSIX_ACL
int btrfs_check_acl(struct inode *inode, int mask); int btrfs_check_acl(struct inode *inode, int mask);
#else
#define btrfs_check_acl NULL
#endif
int btrfs_init_acl(struct inode *inode, struct inode *dir); int btrfs_init_acl(struct inode *inode, struct inode *dir);
int btrfs_acl_chmod(struct inode *inode); int btrfs_acl_chmod(struct inode *inode);
/* relocation.c */
int btrfs_relocate_block_group(struct btrfs_root *root, u64 group_start);
int btrfs_init_reloc_root(struct btrfs_trans_handle *trans,
struct btrfs_root *root);
int btrfs_update_reloc_root(struct btrfs_trans_handle *trans,
struct btrfs_root *root);
int btrfs_recover_relocation(struct btrfs_root *root);
int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len);
#endif #endif
...@@ -29,27 +29,87 @@ ...@@ -29,27 +29,87 @@
* add extents in the middle of btrfs_search_slot, and it allows * add extents in the middle of btrfs_search_slot, and it allows
* us to buffer up frequently modified backrefs in an rb tree instead * us to buffer up frequently modified backrefs in an rb tree instead
* of hammering updates on the extent allocation tree. * of hammering updates on the extent allocation tree.
*
* Right now this code is only used for reference counted trees, but
* the long term goal is to get rid of the similar code for delayed
* extent tree modifications.
*/ */
/* /*
* entries in the rb tree are ordered by the byte number of the extent * compare two delayed tree backrefs with same bytenr and type
* and by the byte number of the parent block. */
static int comp_tree_refs(struct btrfs_delayed_tree_ref *ref2,
struct btrfs_delayed_tree_ref *ref1)
{
if (ref1->node.type == BTRFS_TREE_BLOCK_REF_KEY) {
if (ref1->root < ref2->root)
return -1;
if (ref1->root > ref2->root)
return 1;
} else {
if (ref1->parent < ref2->parent)
return -1;
if (ref1->parent > ref2->parent)
return 1;
}
return 0;
}
/*
* compare two delayed data backrefs with same bytenr and type
*/ */
static int comp_entry(struct btrfs_delayed_ref_node *ref, static int comp_data_refs(struct btrfs_delayed_data_ref *ref2,
u64 bytenr, u64 parent) struct btrfs_delayed_data_ref *ref1)
{ {
if (bytenr < ref->bytenr) if (ref1->node.type == BTRFS_EXTENT_DATA_REF_KEY) {
if (ref1->root < ref2->root)
return -1;
if (ref1->root > ref2->root)
return 1;
if (ref1->objectid < ref2->objectid)
return -1;
if (ref1->objectid > ref2->objectid)
return 1;
if (ref1->offset < ref2->offset)
return -1;
if (ref1->offset > ref2->offset)
return 1;
} else {
if (ref1->parent < ref2->parent)
return -1;
if (ref1->parent > ref2->parent)
return 1;
}
return 0;
}
/*
* entries in the rb tree are ordered by the byte number of the extent,
* type of the delayed backrefs and content of delayed backrefs.
*/
static int comp_entry(struct btrfs_delayed_ref_node *ref2,
struct btrfs_delayed_ref_node *ref1)
{
if (ref1->bytenr < ref2->bytenr)
return -1; return -1;
if (bytenr > ref->bytenr) if (ref1->bytenr > ref2->bytenr)
return 1; return 1;
if (parent < ref->parent) if (ref1->is_head && ref2->is_head)
return 0;
if (ref2->is_head)
return -1; return -1;
if (parent > ref->parent) if (ref1->is_head)
return 1; return 1;
if (ref1->type < ref2->type)
return -1;
if (ref1->type > ref2->type)
return 1;
if (ref1->type == BTRFS_TREE_BLOCK_REF_KEY ||
ref1->type == BTRFS_SHARED_BLOCK_REF_KEY) {
return comp_tree_refs(btrfs_delayed_node_to_tree_ref(ref2),
btrfs_delayed_node_to_tree_ref(ref1));
} else if (ref1->type == BTRFS_EXTENT_DATA_REF_KEY ||
ref1->type == BTRFS_SHARED_DATA_REF_KEY) {
return comp_data_refs(btrfs_delayed_node_to_data_ref(ref2),
btrfs_delayed_node_to_data_ref(ref1));
}
BUG();
return 0; return 0;
} }
...@@ -59,20 +119,21 @@ static int comp_entry(struct btrfs_delayed_ref_node *ref, ...@@ -59,20 +119,21 @@ static int comp_entry(struct btrfs_delayed_ref_node *ref,
* inserted. * inserted.
*/ */
static struct btrfs_delayed_ref_node *tree_insert(struct rb_root *root, static struct btrfs_delayed_ref_node *tree_insert(struct rb_root *root,
u64 bytenr, u64 parent,
struct rb_node *node) struct rb_node *node)
{ {
struct rb_node **p = &root->rb_node; struct rb_node **p = &root->rb_node;
struct rb_node *parent_node = NULL; struct rb_node *parent_node = NULL;
struct btrfs_delayed_ref_node *entry; struct btrfs_delayed_ref_node *entry;
struct btrfs_delayed_ref_node *ins;
int cmp; int cmp;
ins = rb_entry(node, struct btrfs_delayed_ref_node, rb_node);
while (*p) { while (*p) {
parent_node = *p; parent_node = *p;
entry = rb_entry(parent_node, struct btrfs_delayed_ref_node, entry = rb_entry(parent_node, struct btrfs_delayed_ref_node,
rb_node); rb_node);
cmp = comp_entry(entry, bytenr, parent); cmp = comp_entry(entry, ins);
if (cmp < 0) if (cmp < 0)
p = &(*p)->rb_left; p = &(*p)->rb_left;
else if (cmp > 0) else if (cmp > 0)
...@@ -81,18 +142,17 @@ static struct btrfs_delayed_ref_node *tree_insert(struct rb_root *root, ...@@ -81,18 +142,17 @@ static struct btrfs_delayed_ref_node *tree_insert(struct rb_root *root,
return entry; return entry;
} }
entry = rb_entry(node, struct btrfs_delayed_ref_node, rb_node);
rb_link_node(node, parent_node, p); rb_link_node(node, parent_node, p);
rb_insert_color(node, root); rb_insert_color(node, root);
return NULL; return NULL;
} }
/* /*
* find an entry based on (bytenr,parent). This returns the delayed * find an head entry based on bytenr. This returns the delayed ref
* ref if it was able to find one, or NULL if nothing was in that spot * head if it was able to find one, or NULL if nothing was in that spot
*/ */
static struct btrfs_delayed_ref_node *tree_search(struct rb_root *root, static struct btrfs_delayed_ref_node *find_ref_head(struct rb_root *root,
u64 bytenr, u64 parent, u64 bytenr,
struct btrfs_delayed_ref_node **last) struct btrfs_delayed_ref_node **last)
{ {
struct rb_node *n = root->rb_node; struct rb_node *n = root->rb_node;
...@@ -105,7 +165,15 @@ static struct btrfs_delayed_ref_node *tree_search(struct rb_root *root, ...@@ -105,7 +165,15 @@ static struct btrfs_delayed_ref_node *tree_search(struct rb_root *root,
if (last) if (last)
*last = entry; *last = entry;
cmp = comp_entry(entry, bytenr, parent); if (bytenr < entry->bytenr)
cmp = -1;
else if (bytenr > entry->bytenr)
cmp = 1;
else if (!btrfs_delayed_ref_is_head(entry))
cmp = 1;
else
cmp = 0;
if (cmp < 0) if (cmp < 0)
n = n->rb_left; n = n->rb_left;
else if (cmp > 0) else if (cmp > 0)
...@@ -154,7 +222,7 @@ int btrfs_find_ref_cluster(struct btrfs_trans_handle *trans, ...@@ -154,7 +222,7 @@ int btrfs_find_ref_cluster(struct btrfs_trans_handle *trans,
node = rb_first(&delayed_refs->root); node = rb_first(&delayed_refs->root);
} else { } else {
ref = NULL; ref = NULL;
tree_search(&delayed_refs->root, start, (u64)-1, &ref); find_ref_head(&delayed_refs->root, start, &ref);
if (ref) { if (ref) {
struct btrfs_delayed_ref_node *tmp; struct btrfs_delayed_ref_node *tmp;
...@@ -234,7 +302,7 @@ int btrfs_delayed_ref_pending(struct btrfs_trans_handle *trans, u64 bytenr) ...@@ -234,7 +302,7 @@ int btrfs_delayed_ref_pending(struct btrfs_trans_handle *trans, u64 bytenr)
delayed_refs = &trans->transaction->delayed_refs; delayed_refs = &trans->transaction->delayed_refs;
spin_lock(&delayed_refs->lock); spin_lock(&delayed_refs->lock);
ref = tree_search(&delayed_refs->root, bytenr, (u64)-1, NULL); ref = find_ref_head(&delayed_refs->root, bytenr, NULL);
if (ref) { if (ref) {
prev_node = rb_prev(&ref->rb_node); prev_node = rb_prev(&ref->rb_node);
if (!prev_node) if (!prev_node)
...@@ -250,25 +318,28 @@ int btrfs_delayed_ref_pending(struct btrfs_trans_handle *trans, u64 bytenr) ...@@ -250,25 +318,28 @@ int btrfs_delayed_ref_pending(struct btrfs_trans_handle *trans, u64 bytenr)
} }
/* /*
* helper function to lookup reference count * helper function to lookup reference count and flags of extent.
* *
* the head node for delayed ref is used to store the sum of all the * the head node for delayed ref is used to store the sum of all the
* reference count modifications queued up in the rbtree. This way you * reference count modifications queued up in the rbtree. the head
* can check to see what the reference count would be if all of the * node may also store the extent flags to set. This way you can check
* delayed refs are processed. * to see what the reference count and extent flags would be if all of
* the delayed refs are not processed.
*/ */
int btrfs_lookup_extent_ref(struct btrfs_trans_handle *trans, int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 bytenr, struct btrfs_root *root, u64 bytenr,
u64 num_bytes, u32 *refs) u64 num_bytes, u64 *refs, u64 *flags)
{ {
struct btrfs_delayed_ref_node *ref; struct btrfs_delayed_ref_node *ref;
struct btrfs_delayed_ref_head *head; struct btrfs_delayed_ref_head *head;
struct btrfs_delayed_ref_root *delayed_refs; struct btrfs_delayed_ref_root *delayed_refs;
struct btrfs_path *path; struct btrfs_path *path;
struct extent_buffer *leaf;
struct btrfs_extent_item *ei; struct btrfs_extent_item *ei;
struct extent_buffer *leaf;
struct btrfs_key key; struct btrfs_key key;
u32 num_refs; u32 item_size;
u64 num_refs;
u64 extent_flags;
int ret; int ret;
path = btrfs_alloc_path(); path = btrfs_alloc_path();
...@@ -287,37 +358,60 @@ int btrfs_lookup_extent_ref(struct btrfs_trans_handle *trans, ...@@ -287,37 +358,60 @@ int btrfs_lookup_extent_ref(struct btrfs_trans_handle *trans,
if (ret == 0) { if (ret == 0) {
leaf = path->nodes[0]; leaf = path->nodes[0];
ei = btrfs_item_ptr(leaf, path->slots[0], item_size = btrfs_item_size_nr(leaf, path->slots[0]);
struct btrfs_extent_item); if (item_size >= sizeof(*ei)) {
num_refs = btrfs_extent_refs(leaf, ei); ei = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_extent_item);
num_refs = btrfs_extent_refs(leaf, ei);
extent_flags = btrfs_extent_flags(leaf, ei);
} else {
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
struct btrfs_extent_item_v0 *ei0;
BUG_ON(item_size != sizeof(*ei0));
ei0 = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_extent_item_v0);
num_refs = btrfs_extent_refs_v0(leaf, ei0);
/* FIXME: this isn't correct for data */
extent_flags = BTRFS_BLOCK_FLAG_FULL_BACKREF;
#else
BUG();
#endif
}
BUG_ON(num_refs == 0);
} else { } else {
num_refs = 0; num_refs = 0;
extent_flags = 0;
ret = 0; ret = 0;
} }
spin_lock(&delayed_refs->lock); spin_lock(&delayed_refs->lock);
ref = tree_search(&delayed_refs->root, bytenr, (u64)-1, NULL); ref = find_ref_head(&delayed_refs->root, bytenr, NULL);
if (ref) { if (ref) {
head = btrfs_delayed_node_to_head(ref); head = btrfs_delayed_node_to_head(ref);
if (mutex_trylock(&head->mutex)) { if (!mutex_trylock(&head->mutex)) {
num_refs += ref->ref_mod; atomic_inc(&ref->refs);
mutex_unlock(&head->mutex); spin_unlock(&delayed_refs->lock);
*refs = num_refs;
goto out;
}
atomic_inc(&ref->refs); btrfs_release_path(root->fs_info->extent_root, path);
spin_unlock(&delayed_refs->lock);
btrfs_release_path(root->fs_info->extent_root, path); mutex_lock(&head->mutex);
mutex_unlock(&head->mutex);
btrfs_put_delayed_ref(ref);
goto again;
}
if (head->extent_op && head->extent_op->update_flags)
extent_flags |= head->extent_op->flags_to_set;
else
BUG_ON(num_refs == 0);
mutex_lock(&head->mutex); num_refs += ref->ref_mod;
mutex_unlock(&head->mutex); mutex_unlock(&head->mutex);
btrfs_put_delayed_ref(ref);
goto again;
} else {
*refs = num_refs;
} }
WARN_ON(num_refs == 0);
if (refs)
*refs = num_refs;
if (flags)
*flags = extent_flags;
out: out:
spin_unlock(&delayed_refs->lock); spin_unlock(&delayed_refs->lock);
btrfs_free_path(path); btrfs_free_path(path);
...@@ -338,16 +432,7 @@ update_existing_ref(struct btrfs_trans_handle *trans, ...@@ -338,16 +432,7 @@ update_existing_ref(struct btrfs_trans_handle *trans,
struct btrfs_delayed_ref_node *existing, struct btrfs_delayed_ref_node *existing,
struct btrfs_delayed_ref_node *update) struct btrfs_delayed_ref_node *update)
{ {
struct btrfs_delayed_ref *existing_ref; if (update->action != existing->action) {
struct btrfs_delayed_ref *ref;
existing_ref = btrfs_delayed_node_to_ref(existing);
ref = btrfs_delayed_node_to_ref(update);
if (ref->pin)
existing_ref->pin = 1;
if (ref->action != existing_ref->action) {
/* /*
* this is effectively undoing either an add or a * this is effectively undoing either an add or a
* drop. We decrement the ref_mod, and if it goes * drop. We decrement the ref_mod, and if it goes
...@@ -363,20 +448,13 @@ update_existing_ref(struct btrfs_trans_handle *trans, ...@@ -363,20 +448,13 @@ update_existing_ref(struct btrfs_trans_handle *trans,
delayed_refs->num_entries--; delayed_refs->num_entries--;
if (trans->delayed_ref_updates) if (trans->delayed_ref_updates)
trans->delayed_ref_updates--; trans->delayed_ref_updates--;
} else {
WARN_ON(existing->type == BTRFS_TREE_BLOCK_REF_KEY ||
existing->type == BTRFS_SHARED_BLOCK_REF_KEY);
} }
} else { } else {
if (existing_ref->action == BTRFS_ADD_DELAYED_REF) { WARN_ON(existing->type == BTRFS_TREE_BLOCK_REF_KEY ||
/* if we're adding refs, make sure all the existing->type == BTRFS_SHARED_BLOCK_REF_KEY);
* details match up. The extent could
* have been totally freed and reallocated
* by a different owner before the delayed
* ref entries were removed.
*/
existing_ref->owner_objectid = ref->owner_objectid;
existing_ref->generation = ref->generation;
existing_ref->root = ref->root;
existing->num_bytes = update->num_bytes;
}
/* /*
* the action on the existing ref matches * the action on the existing ref matches
* the action on the ref we're trying to add. * the action on the ref we're trying to add.
...@@ -401,6 +479,7 @@ update_existing_head_ref(struct btrfs_delayed_ref_node *existing, ...@@ -401,6 +479,7 @@ update_existing_head_ref(struct btrfs_delayed_ref_node *existing,
existing_ref = btrfs_delayed_node_to_head(existing); existing_ref = btrfs_delayed_node_to_head(existing);
ref = btrfs_delayed_node_to_head(update); ref = btrfs_delayed_node_to_head(update);
BUG_ON(existing_ref->is_data != ref->is_data);
if (ref->must_insert_reserved) { if (ref->must_insert_reserved) {
/* if the extent was freed and then /* if the extent was freed and then
...@@ -420,6 +499,24 @@ update_existing_head_ref(struct btrfs_delayed_ref_node *existing, ...@@ -420,6 +499,24 @@ update_existing_head_ref(struct btrfs_delayed_ref_node *existing,
} }
if (ref->extent_op) {
if (!existing_ref->extent_op) {
existing_ref->extent_op = ref->extent_op;
} else {
if (ref->extent_op->update_key) {
memcpy(&existing_ref->extent_op->key,
&ref->extent_op->key,
sizeof(ref->extent_op->key));
existing_ref->extent_op->update_key = 1;
}
if (ref->extent_op->update_flags) {
existing_ref->extent_op->flags_to_set |=
ref->extent_op->flags_to_set;
existing_ref->extent_op->update_flags = 1;
}
kfree(ref->extent_op);
}
}
/* /*
* update the reference mod on the head to reflect this new operation * update the reference mod on the head to reflect this new operation
*/ */
...@@ -427,19 +524,16 @@ update_existing_head_ref(struct btrfs_delayed_ref_node *existing, ...@@ -427,19 +524,16 @@ update_existing_head_ref(struct btrfs_delayed_ref_node *existing,
} }
/* /*
* helper function to actually insert a delayed ref into the rbtree. * helper function to actually insert a head node into the rbtree.
* this does all the dirty work in terms of maintaining the correct * this does all the dirty work in terms of maintaining the correct
* overall modification count in the head node and properly dealing * overall modification count.
* with updating existing nodes as new modifications are queued.
*/ */
static noinline int __btrfs_add_delayed_ref(struct btrfs_trans_handle *trans, static noinline int add_delayed_ref_head(struct btrfs_trans_handle *trans,
struct btrfs_delayed_ref_node *ref, struct btrfs_delayed_ref_node *ref,
u64 bytenr, u64 num_bytes, u64 parent, u64 ref_root, u64 bytenr, u64 num_bytes,
u64 ref_generation, u64 owner_objectid, int action, int action, int is_data)
int pin)
{ {
struct btrfs_delayed_ref_node *existing; struct btrfs_delayed_ref_node *existing;
struct btrfs_delayed_ref *full_ref;
struct btrfs_delayed_ref_head *head_ref = NULL; struct btrfs_delayed_ref_head *head_ref = NULL;
struct btrfs_delayed_ref_root *delayed_refs; struct btrfs_delayed_ref_root *delayed_refs;
int count_mod = 1; int count_mod = 1;
...@@ -449,12 +543,10 @@ static noinline int __btrfs_add_delayed_ref(struct btrfs_trans_handle *trans, ...@@ -449,12 +543,10 @@ static noinline int __btrfs_add_delayed_ref(struct btrfs_trans_handle *trans,
* the head node stores the sum of all the mods, so dropping a ref * the head node stores the sum of all the mods, so dropping a ref
* should drop the sum in the head node by one. * should drop the sum in the head node by one.
*/ */
if (parent == (u64)-1) { if (action == BTRFS_UPDATE_DELAYED_HEAD)
if (action == BTRFS_DROP_DELAYED_REF) count_mod = 0;
count_mod = -1; else if (action == BTRFS_DROP_DELAYED_REF)
else if (action == BTRFS_UPDATE_DELAYED_HEAD) count_mod = -1;
count_mod = 0;
}
/* /*
* BTRFS_ADD_DELAYED_EXTENT means that we need to update * BTRFS_ADD_DELAYED_EXTENT means that we need to update
...@@ -467,57 +559,148 @@ static noinline int __btrfs_add_delayed_ref(struct btrfs_trans_handle *trans, ...@@ -467,57 +559,148 @@ static noinline int __btrfs_add_delayed_ref(struct btrfs_trans_handle *trans,
* Once we record must_insert_reserved, switch the action to * Once we record must_insert_reserved, switch the action to
* BTRFS_ADD_DELAYED_REF because other special casing is not required. * BTRFS_ADD_DELAYED_REF because other special casing is not required.
*/ */
if (action == BTRFS_ADD_DELAYED_EXTENT) { if (action == BTRFS_ADD_DELAYED_EXTENT)
must_insert_reserved = 1; must_insert_reserved = 1;
action = BTRFS_ADD_DELAYED_REF; else
} else {
must_insert_reserved = 0; must_insert_reserved = 0;
}
delayed_refs = &trans->transaction->delayed_refs; delayed_refs = &trans->transaction->delayed_refs;
/* first set the basic ref node struct up */ /* first set the basic ref node struct up */
atomic_set(&ref->refs, 1); atomic_set(&ref->refs, 1);
ref->bytenr = bytenr; ref->bytenr = bytenr;
ref->parent = parent; ref->num_bytes = num_bytes;
ref->ref_mod = count_mod; ref->ref_mod = count_mod;
ref->type = 0;
ref->action = 0;
ref->is_head = 1;
ref->in_tree = 1; ref->in_tree = 1;
head_ref = btrfs_delayed_node_to_head(ref);
head_ref->must_insert_reserved = must_insert_reserved;
head_ref->is_data = is_data;
INIT_LIST_HEAD(&head_ref->cluster);
mutex_init(&head_ref->mutex);
existing = tree_insert(&delayed_refs->root, &ref->rb_node);
if (existing) {
update_existing_head_ref(existing, ref);
/*
* we've updated the existing ref, free the newly
* allocated ref
*/
kfree(ref);
} else {
delayed_refs->num_heads++;
delayed_refs->num_heads_ready++;
delayed_refs->num_entries++;
trans->delayed_ref_updates++;
}
return 0;
}
/*
* helper to insert a delayed tree ref into the rbtree.
*/
static noinline int add_delayed_tree_ref(struct btrfs_trans_handle *trans,
struct btrfs_delayed_ref_node *ref,
u64 bytenr, u64 num_bytes, u64 parent,
u64 ref_root, int level, int action)
{
struct btrfs_delayed_ref_node *existing;
struct btrfs_delayed_tree_ref *full_ref;
struct btrfs_delayed_ref_root *delayed_refs;
if (action == BTRFS_ADD_DELAYED_EXTENT)
action = BTRFS_ADD_DELAYED_REF;
delayed_refs = &trans->transaction->delayed_refs;
/* first set the basic ref node struct up */
atomic_set(&ref->refs, 1);
ref->bytenr = bytenr;
ref->num_bytes = num_bytes; ref->num_bytes = num_bytes;
ref->ref_mod = 1;
ref->action = action;
ref->is_head = 0;
ref->in_tree = 1;
if (btrfs_delayed_ref_is_head(ref)) { full_ref = btrfs_delayed_node_to_tree_ref(ref);
head_ref = btrfs_delayed_node_to_head(ref); if (parent) {
head_ref->must_insert_reserved = must_insert_reserved; full_ref->parent = parent;
INIT_LIST_HEAD(&head_ref->cluster); ref->type = BTRFS_SHARED_BLOCK_REF_KEY;
mutex_init(&head_ref->mutex);
} else { } else {
full_ref = btrfs_delayed_node_to_ref(ref);
full_ref->root = ref_root; full_ref->root = ref_root;
full_ref->generation = ref_generation; ref->type = BTRFS_TREE_BLOCK_REF_KEY;
full_ref->owner_objectid = owner_objectid;
full_ref->pin = pin;
full_ref->action = action;
} }
full_ref->level = level;
existing = tree_insert(&delayed_refs->root, bytenr, existing = tree_insert(&delayed_refs->root, &ref->rb_node);
parent, &ref->rb_node);
if (existing) { if (existing) {
if (btrfs_delayed_ref_is_head(ref)) update_existing_ref(trans, delayed_refs, existing, ref);
update_existing_head_ref(existing, ref); /*
else * we've updated the existing ref, free the newly
update_existing_ref(trans, delayed_refs, existing, ref); * allocated ref
*/
kfree(ref);
} else {
delayed_refs->num_entries++;
trans->delayed_ref_updates++;
}
return 0;
}
/*
* helper to insert a delayed data ref into the rbtree.
*/
static noinline int add_delayed_data_ref(struct btrfs_trans_handle *trans,
struct btrfs_delayed_ref_node *ref,
u64 bytenr, u64 num_bytes, u64 parent,
u64 ref_root, u64 owner, u64 offset,
int action)
{
struct btrfs_delayed_ref_node *existing;
struct btrfs_delayed_data_ref *full_ref;
struct btrfs_delayed_ref_root *delayed_refs;
if (action == BTRFS_ADD_DELAYED_EXTENT)
action = BTRFS_ADD_DELAYED_REF;
delayed_refs = &trans->transaction->delayed_refs;
/* first set the basic ref node struct up */
atomic_set(&ref->refs, 1);
ref->bytenr = bytenr;
ref->num_bytes = num_bytes;
ref->ref_mod = 1;
ref->action = action;
ref->is_head = 0;
ref->in_tree = 1;
full_ref = btrfs_delayed_node_to_data_ref(ref);
if (parent) {
full_ref->parent = parent;
ref->type = BTRFS_SHARED_DATA_REF_KEY;
} else {
full_ref->root = ref_root;
ref->type = BTRFS_EXTENT_DATA_REF_KEY;
}
full_ref->objectid = owner;
full_ref->offset = offset;
existing = tree_insert(&delayed_refs->root, &ref->rb_node);
if (existing) {
update_existing_ref(trans, delayed_refs, existing, ref);
/* /*
* we've updated the existing ref, free the newly * we've updated the existing ref, free the newly
* allocated ref * allocated ref
*/ */
kfree(ref); kfree(ref);
} else { } else {
if (btrfs_delayed_ref_is_head(ref)) {
delayed_refs->num_heads++;
delayed_refs->num_heads_ready++;
}
delayed_refs->num_entries++; delayed_refs->num_entries++;
trans->delayed_ref_updates++; trans->delayed_ref_updates++;
} }
...@@ -525,37 +708,78 @@ static noinline int __btrfs_add_delayed_ref(struct btrfs_trans_handle *trans, ...@@ -525,37 +708,78 @@ static noinline int __btrfs_add_delayed_ref(struct btrfs_trans_handle *trans,
} }
/* /*
* add a delayed ref to the tree. This does all of the accounting required * add a delayed tree ref. This does all of the accounting required
* to make sure the delayed ref is eventually processed before this * to make sure the delayed ref is eventually processed before this
* transaction commits. * transaction commits.
*/ */
int btrfs_add_delayed_ref(struct btrfs_trans_handle *trans, int btrfs_add_delayed_tree_ref(struct btrfs_trans_handle *trans,
u64 bytenr, u64 num_bytes, u64 parent, u64 ref_root, u64 bytenr, u64 num_bytes, u64 parent,
u64 ref_generation, u64 owner_objectid, int action, u64 ref_root, int level, int action,
int pin) struct btrfs_delayed_extent_op *extent_op)
{ {
struct btrfs_delayed_ref *ref; struct btrfs_delayed_tree_ref *ref;
struct btrfs_delayed_ref_head *head_ref; struct btrfs_delayed_ref_head *head_ref;
struct btrfs_delayed_ref_root *delayed_refs; struct btrfs_delayed_ref_root *delayed_refs;
int ret; int ret;
BUG_ON(extent_op && extent_op->is_data);
ref = kmalloc(sizeof(*ref), GFP_NOFS); ref = kmalloc(sizeof(*ref), GFP_NOFS);
if (!ref) if (!ref)
return -ENOMEM; return -ENOMEM;
head_ref = kmalloc(sizeof(*head_ref), GFP_NOFS);
if (!head_ref) {
kfree(ref);
return -ENOMEM;
}
head_ref->extent_op = extent_op;
delayed_refs = &trans->transaction->delayed_refs;
spin_lock(&delayed_refs->lock);
/* /*
* the parent = 0 case comes from cases where we don't actually * insert both the head node and the new ref without dropping
* know the parent yet. It will get updated later via a add/drop * the spin lock
* pair.
*/ */
if (parent == 0) ret = add_delayed_ref_head(trans, &head_ref->node, bytenr, num_bytes,
parent = bytenr; action, 0);
BUG_ON(ret);
ret = add_delayed_tree_ref(trans, &ref->node, bytenr, num_bytes,
parent, ref_root, level, action);
BUG_ON(ret);
spin_unlock(&delayed_refs->lock);
return 0;
}
/*
* add a delayed data ref. it's similar to btrfs_add_delayed_tree_ref.
*/
int btrfs_add_delayed_data_ref(struct btrfs_trans_handle *trans,
u64 bytenr, u64 num_bytes,
u64 parent, u64 ref_root,
u64 owner, u64 offset, int action,
struct btrfs_delayed_extent_op *extent_op)
{
struct btrfs_delayed_data_ref *ref;
struct btrfs_delayed_ref_head *head_ref;
struct btrfs_delayed_ref_root *delayed_refs;
int ret;
BUG_ON(extent_op && !extent_op->is_data);
ref = kmalloc(sizeof(*ref), GFP_NOFS);
if (!ref)
return -ENOMEM;
head_ref = kmalloc(sizeof(*head_ref), GFP_NOFS); head_ref = kmalloc(sizeof(*head_ref), GFP_NOFS);
if (!head_ref) { if (!head_ref) {
kfree(ref); kfree(ref);
return -ENOMEM; return -ENOMEM;
} }
head_ref->extent_op = extent_op;
delayed_refs = &trans->transaction->delayed_refs; delayed_refs = &trans->transaction->delayed_refs;
spin_lock(&delayed_refs->lock); spin_lock(&delayed_refs->lock);
...@@ -563,14 +787,39 @@ int btrfs_add_delayed_ref(struct btrfs_trans_handle *trans, ...@@ -563,14 +787,39 @@ int btrfs_add_delayed_ref(struct btrfs_trans_handle *trans,
* insert both the head node and the new ref without dropping * insert both the head node and the new ref without dropping
* the spin lock * the spin lock
*/ */
ret = __btrfs_add_delayed_ref(trans, &head_ref->node, bytenr, num_bytes, ret = add_delayed_ref_head(trans, &head_ref->node, bytenr, num_bytes,
(u64)-1, 0, 0, 0, action, pin); action, 1);
BUG_ON(ret); BUG_ON(ret);
ret = __btrfs_add_delayed_ref(trans, &ref->node, bytenr, num_bytes, ret = add_delayed_data_ref(trans, &ref->node, bytenr, num_bytes,
parent, ref_root, ref_generation, parent, ref_root, owner, offset, action);
owner_objectid, action, pin); BUG_ON(ret);
spin_unlock(&delayed_refs->lock);
return 0;
}
int btrfs_add_delayed_extent_op(struct btrfs_trans_handle *trans,
u64 bytenr, u64 num_bytes,
struct btrfs_delayed_extent_op *extent_op)
{
struct btrfs_delayed_ref_head *head_ref;
struct btrfs_delayed_ref_root *delayed_refs;
int ret;
head_ref = kmalloc(sizeof(*head_ref), GFP_NOFS);
if (!head_ref)
return -ENOMEM;
head_ref->extent_op = extent_op;
delayed_refs = &trans->transaction->delayed_refs;
spin_lock(&delayed_refs->lock);
ret = add_delayed_ref_head(trans, &head_ref->node, bytenr,
num_bytes, BTRFS_UPDATE_DELAYED_HEAD,
extent_op->is_data);
BUG_ON(ret); BUG_ON(ret);
spin_unlock(&delayed_refs->lock); spin_unlock(&delayed_refs->lock);
return 0; return 0;
} }
...@@ -587,7 +836,7 @@ btrfs_find_delayed_ref_head(struct btrfs_trans_handle *trans, u64 bytenr) ...@@ -587,7 +836,7 @@ btrfs_find_delayed_ref_head(struct btrfs_trans_handle *trans, u64 bytenr)
struct btrfs_delayed_ref_root *delayed_refs; struct btrfs_delayed_ref_root *delayed_refs;
delayed_refs = &trans->transaction->delayed_refs; delayed_refs = &trans->transaction->delayed_refs;
ref = tree_search(&delayed_refs->root, bytenr, (u64)-1, NULL); ref = find_ref_head(&delayed_refs->root, bytenr, NULL);
if (ref) if (ref)
return btrfs_delayed_node_to_head(ref); return btrfs_delayed_node_to_head(ref);
return NULL; return NULL;
...@@ -603,6 +852,7 @@ btrfs_find_delayed_ref_head(struct btrfs_trans_handle *trans, u64 bytenr) ...@@ -603,6 +852,7 @@ btrfs_find_delayed_ref_head(struct btrfs_trans_handle *trans, u64 bytenr)
* *
* It is the same as doing a ref add and delete in two separate calls. * It is the same as doing a ref add and delete in two separate calls.
*/ */
#if 0
int btrfs_update_delayed_ref(struct btrfs_trans_handle *trans, int btrfs_update_delayed_ref(struct btrfs_trans_handle *trans,
u64 bytenr, u64 num_bytes, u64 orig_parent, u64 bytenr, u64 num_bytes, u64 orig_parent,
u64 parent, u64 orig_ref_root, u64 ref_root, u64 parent, u64 orig_ref_root, u64 ref_root,
...@@ -666,3 +916,4 @@ int btrfs_update_delayed_ref(struct btrfs_trans_handle *trans, ...@@ -666,3 +916,4 @@ int btrfs_update_delayed_ref(struct btrfs_trans_handle *trans,
spin_unlock(&delayed_refs->lock); spin_unlock(&delayed_refs->lock);
return 0; return 0;
} }
#endif
...@@ -30,9 +30,6 @@ struct btrfs_delayed_ref_node { ...@@ -30,9 +30,6 @@ struct btrfs_delayed_ref_node {
/* the starting bytenr of the extent */ /* the starting bytenr of the extent */
u64 bytenr; u64 bytenr;
/* the parent our backref will point to */
u64 parent;
/* the size of the extent */ /* the size of the extent */
u64 num_bytes; u64 num_bytes;
...@@ -50,10 +47,21 @@ struct btrfs_delayed_ref_node { ...@@ -50,10 +47,21 @@ struct btrfs_delayed_ref_node {
*/ */
int ref_mod; int ref_mod;
unsigned int action:8;
unsigned int type:8;
/* is this node still in the rbtree? */ /* is this node still in the rbtree? */
unsigned int is_head:1;
unsigned int in_tree:1; unsigned int in_tree:1;
}; };
struct btrfs_delayed_extent_op {
struct btrfs_disk_key key;
u64 flags_to_set;
unsigned int update_key:1;
unsigned int update_flags:1;
unsigned int is_data:1;
};
/* /*
* the head refs are used to hold a lock on a given extent, which allows us * the head refs are used to hold a lock on a given extent, which allows us
* to make sure that only one process is running the delayed refs * to make sure that only one process is running the delayed refs
...@@ -71,6 +79,7 @@ struct btrfs_delayed_ref_head { ...@@ -71,6 +79,7 @@ struct btrfs_delayed_ref_head {
struct list_head cluster; struct list_head cluster;
struct btrfs_delayed_extent_op *extent_op;
/* /*
* when a new extent is allocated, it is just reserved in memory * when a new extent is allocated, it is just reserved in memory
* The actual extent isn't inserted into the extent allocation tree * The actual extent isn't inserted into the extent allocation tree
...@@ -84,27 +93,26 @@ struct btrfs_delayed_ref_head { ...@@ -84,27 +93,26 @@ struct btrfs_delayed_ref_head {
* the free has happened. * the free has happened.
*/ */
unsigned int must_insert_reserved:1; unsigned int must_insert_reserved:1;
unsigned int is_data:1;
}; };
struct btrfs_delayed_ref { struct btrfs_delayed_tree_ref {
struct btrfs_delayed_ref_node node; struct btrfs_delayed_ref_node node;
union {
u64 root;
u64 parent;
};
int level;
};
/* the root objectid our ref will point to */ struct btrfs_delayed_data_ref {
u64 root; struct btrfs_delayed_ref_node node;
union {
/* the generation for the backref */ u64 root;
u64 generation; u64 parent;
};
/* owner_objectid of the backref */ u64 objectid;
u64 owner_objectid; u64 offset;
/* operation done by this entry in the rbtree */
u8 action;
/* if pin == 1, when the extent is freed it will be pinned until
* transaction commit
*/
unsigned int pin:1;
}; };
struct btrfs_delayed_ref_root { struct btrfs_delayed_ref_root {
...@@ -143,17 +151,25 @@ static inline void btrfs_put_delayed_ref(struct btrfs_delayed_ref_node *ref) ...@@ -143,17 +151,25 @@ static inline void btrfs_put_delayed_ref(struct btrfs_delayed_ref_node *ref)
} }
} }
int btrfs_add_delayed_ref(struct btrfs_trans_handle *trans, int btrfs_add_delayed_tree_ref(struct btrfs_trans_handle *trans,
u64 bytenr, u64 num_bytes, u64 parent, u64 ref_root, u64 bytenr, u64 num_bytes, u64 parent,
u64 ref_generation, u64 owner_objectid, int action, u64 ref_root, int level, int action,
int pin); struct btrfs_delayed_extent_op *extent_op);
int btrfs_add_delayed_data_ref(struct btrfs_trans_handle *trans,
u64 bytenr, u64 num_bytes,
u64 parent, u64 ref_root,
u64 owner, u64 offset, int action,
struct btrfs_delayed_extent_op *extent_op);
int btrfs_add_delayed_extent_op(struct btrfs_trans_handle *trans,
u64 bytenr, u64 num_bytes,
struct btrfs_delayed_extent_op *extent_op);
struct btrfs_delayed_ref_head * struct btrfs_delayed_ref_head *
btrfs_find_delayed_ref_head(struct btrfs_trans_handle *trans, u64 bytenr); btrfs_find_delayed_ref_head(struct btrfs_trans_handle *trans, u64 bytenr);
int btrfs_delayed_ref_pending(struct btrfs_trans_handle *trans, u64 bytenr); int btrfs_delayed_ref_pending(struct btrfs_trans_handle *trans, u64 bytenr);
int btrfs_lookup_extent_ref(struct btrfs_trans_handle *trans, int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 bytenr, struct btrfs_root *root, u64 bytenr,
u64 num_bytes, u32 *refs); u64 num_bytes, u64 *refs, u64 *flags);
int btrfs_update_delayed_ref(struct btrfs_trans_handle *trans, int btrfs_update_delayed_ref(struct btrfs_trans_handle *trans,
u64 bytenr, u64 num_bytes, u64 orig_parent, u64 bytenr, u64 num_bytes, u64 orig_parent,
u64 parent, u64 orig_ref_root, u64 ref_root, u64 parent, u64 orig_ref_root, u64 ref_root,
...@@ -169,18 +185,24 @@ int btrfs_find_ref_cluster(struct btrfs_trans_handle *trans, ...@@ -169,18 +185,24 @@ int btrfs_find_ref_cluster(struct btrfs_trans_handle *trans,
*/ */
static int btrfs_delayed_ref_is_head(struct btrfs_delayed_ref_node *node) static int btrfs_delayed_ref_is_head(struct btrfs_delayed_ref_node *node)
{ {
return node->parent == (u64)-1; return node->is_head;
} }
/* /*
* helper functions to cast a node into its container * helper functions to cast a node into its container
*/ */
static inline struct btrfs_delayed_ref * static inline struct btrfs_delayed_tree_ref *
btrfs_delayed_node_to_ref(struct btrfs_delayed_ref_node *node) btrfs_delayed_node_to_tree_ref(struct btrfs_delayed_ref_node *node)
{ {
WARN_ON(btrfs_delayed_ref_is_head(node)); WARN_ON(btrfs_delayed_ref_is_head(node));
return container_of(node, struct btrfs_delayed_ref, node); return container_of(node, struct btrfs_delayed_tree_ref, node);
}
static inline struct btrfs_delayed_data_ref *
btrfs_delayed_node_to_data_ref(struct btrfs_delayed_ref_node *node)
{
WARN_ON(btrfs_delayed_ref_is_head(node));
return container_of(node, struct btrfs_delayed_data_ref, node);
} }
static inline struct btrfs_delayed_ref_head * static inline struct btrfs_delayed_ref_head *
...@@ -188,6 +210,5 @@ btrfs_delayed_node_to_head(struct btrfs_delayed_ref_node *node) ...@@ -188,6 +210,5 @@ btrfs_delayed_node_to_head(struct btrfs_delayed_ref_node *node)
{ {
WARN_ON(!btrfs_delayed_ref_is_head(node)); WARN_ON(!btrfs_delayed_ref_is_head(node));
return container_of(node, struct btrfs_delayed_ref_head, node); return container_of(node, struct btrfs_delayed_ref_head, node);
} }
#endif #endif
...@@ -26,8 +26,8 @@ ...@@ -26,8 +26,8 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/crc32c.h>
#include "compat.h" #include "compat.h"
#include "crc32c.h"
#include "ctree.h" #include "ctree.h"
#include "disk-io.h" #include "disk-io.h"
#include "transaction.h" #include "transaction.h"
...@@ -36,7 +36,6 @@ ...@@ -36,7 +36,6 @@
#include "print-tree.h" #include "print-tree.h"
#include "async-thread.h" #include "async-thread.h"
#include "locking.h" #include "locking.h"
#include "ref-cache.h"
#include "tree-log.h" #include "tree-log.h"
#include "free-space-cache.h" #include "free-space-cache.h"
...@@ -172,7 +171,7 @@ static struct extent_map *btree_get_extent(struct inode *inode, ...@@ -172,7 +171,7 @@ static struct extent_map *btree_get_extent(struct inode *inode,
u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len) u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len)
{ {
return btrfs_crc32c(seed, data, len); return crc32c(seed, data, len);
} }
void btrfs_csum_final(u32 crc, char *result) void btrfs_csum_final(u32 crc, char *result)
...@@ -884,7 +883,6 @@ static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize, ...@@ -884,7 +883,6 @@ static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
{ {
root->node = NULL; root->node = NULL;
root->commit_root = NULL; root->commit_root = NULL;
root->ref_tree = NULL;
root->sectorsize = sectorsize; root->sectorsize = sectorsize;
root->nodesize = nodesize; root->nodesize = nodesize;
root->leafsize = leafsize; root->leafsize = leafsize;
...@@ -899,12 +897,14 @@ static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize, ...@@ -899,12 +897,14 @@ static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
root->last_inode_alloc = 0; root->last_inode_alloc = 0;
root->name = NULL; root->name = NULL;
root->in_sysfs = 0; root->in_sysfs = 0;
root->inode_tree.rb_node = NULL;
INIT_LIST_HEAD(&root->dirty_list); INIT_LIST_HEAD(&root->dirty_list);
INIT_LIST_HEAD(&root->orphan_list); INIT_LIST_HEAD(&root->orphan_list);
INIT_LIST_HEAD(&root->dead_list); INIT_LIST_HEAD(&root->root_list);
spin_lock_init(&root->node_lock); spin_lock_init(&root->node_lock);
spin_lock_init(&root->list_lock); spin_lock_init(&root->list_lock);
spin_lock_init(&root->inode_lock);
mutex_init(&root->objectid_mutex); mutex_init(&root->objectid_mutex);
mutex_init(&root->log_mutex); mutex_init(&root->log_mutex);
init_waitqueue_head(&root->log_writer_wait); init_waitqueue_head(&root->log_writer_wait);
...@@ -918,9 +918,6 @@ static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize, ...@@ -918,9 +918,6 @@ static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
extent_io_tree_init(&root->dirty_log_pages, extent_io_tree_init(&root->dirty_log_pages,
fs_info->btree_inode->i_mapping, GFP_NOFS); fs_info->btree_inode->i_mapping, GFP_NOFS);
btrfs_leaf_ref_tree_init(&root->ref_tree_struct);
root->ref_tree = &root->ref_tree_struct;
memset(&root->root_key, 0, sizeof(root->root_key)); memset(&root->root_key, 0, sizeof(root->root_key));
memset(&root->root_item, 0, sizeof(root->root_item)); memset(&root->root_item, 0, sizeof(root->root_item));
memset(&root->defrag_progress, 0, sizeof(root->defrag_progress)); memset(&root->defrag_progress, 0, sizeof(root->defrag_progress));
...@@ -959,6 +956,7 @@ static int find_and_setup_root(struct btrfs_root *tree_root, ...@@ -959,6 +956,7 @@ static int find_and_setup_root(struct btrfs_root *tree_root,
blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item)); blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item));
root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item), root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item),
blocksize, generation); blocksize, generation);
root->commit_root = btrfs_root_node(root);
BUG_ON(!root->node); BUG_ON(!root->node);
return 0; return 0;
} }
...@@ -1025,20 +1023,19 @@ static struct btrfs_root *alloc_log_tree(struct btrfs_trans_handle *trans, ...@@ -1025,20 +1023,19 @@ static struct btrfs_root *alloc_log_tree(struct btrfs_trans_handle *trans,
*/ */
root->ref_cows = 0; root->ref_cows = 0;
leaf = btrfs_alloc_free_block(trans, root, root->leafsize, leaf = btrfs_alloc_free_block(trans, root, root->leafsize, 0,
0, BTRFS_TREE_LOG_OBJECTID, BTRFS_TREE_LOG_OBJECTID, NULL, 0, 0, 0);
trans->transid, 0, 0, 0);
if (IS_ERR(leaf)) { if (IS_ERR(leaf)) {
kfree(root); kfree(root);
return ERR_CAST(leaf); return ERR_CAST(leaf);
} }
memset_extent_buffer(leaf, 0, 0, sizeof(struct btrfs_header));
btrfs_set_header_bytenr(leaf, leaf->start);
btrfs_set_header_generation(leaf, trans->transid);
btrfs_set_header_backref_rev(leaf, BTRFS_MIXED_BACKREF_REV);
btrfs_set_header_owner(leaf, BTRFS_TREE_LOG_OBJECTID);
root->node = leaf; root->node = leaf;
btrfs_set_header_nritems(root->node, 0);
btrfs_set_header_level(root->node, 0);
btrfs_set_header_bytenr(root->node, root->node->start);
btrfs_set_header_generation(root->node, trans->transid);
btrfs_set_header_owner(root->node, BTRFS_TREE_LOG_OBJECTID);
write_extent_buffer(root->node, root->fs_info->fsid, write_extent_buffer(root->node, root->fs_info->fsid,
(unsigned long)btrfs_header_fsid(root->node), (unsigned long)btrfs_header_fsid(root->node),
...@@ -1081,8 +1078,7 @@ int btrfs_add_log_tree(struct btrfs_trans_handle *trans, ...@@ -1081,8 +1078,7 @@ int btrfs_add_log_tree(struct btrfs_trans_handle *trans,
inode_item->nbytes = cpu_to_le64(root->leafsize); inode_item->nbytes = cpu_to_le64(root->leafsize);
inode_item->mode = cpu_to_le32(S_IFDIR | 0755); inode_item->mode = cpu_to_le32(S_IFDIR | 0755);
btrfs_set_root_bytenr(&log_root->root_item, log_root->node->start); btrfs_set_root_node(&log_root->root_item, log_root->node);
btrfs_set_root_generation(&log_root->root_item, trans->transid);
WARN_ON(root->log_root); WARN_ON(root->log_root);
root->log_root = log_root; root->log_root = log_root;
...@@ -1144,6 +1140,7 @@ struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root, ...@@ -1144,6 +1140,7 @@ struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root,
blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item)); blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item));
root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item), root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item),
blocksize, generation); blocksize, generation);
root->commit_root = btrfs_root_node(root);
BUG_ON(!root->node); BUG_ON(!root->node);
insert: insert:
if (location->objectid != BTRFS_TREE_LOG_OBJECTID) { if (location->objectid != BTRFS_TREE_LOG_OBJECTID) {
...@@ -1210,7 +1207,7 @@ struct btrfs_root *btrfs_read_fs_root_no_name(struct btrfs_fs_info *fs_info, ...@@ -1210,7 +1207,7 @@ struct btrfs_root *btrfs_read_fs_root_no_name(struct btrfs_fs_info *fs_info,
} }
if (!(fs_info->sb->s_flags & MS_RDONLY)) { if (!(fs_info->sb->s_flags & MS_RDONLY)) {
ret = btrfs_find_dead_roots(fs_info->tree_root, ret = btrfs_find_dead_roots(fs_info->tree_root,
root->root_key.objectid, root); root->root_key.objectid);
BUG_ON(ret); BUG_ON(ret);
btrfs_orphan_cleanup(root); btrfs_orphan_cleanup(root);
} }
...@@ -1569,8 +1566,6 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1569,8 +1566,6 @@ struct btrfs_root *open_ctree(struct super_block *sb,
atomic_set(&fs_info->async_delalloc_pages, 0); atomic_set(&fs_info->async_delalloc_pages, 0);
atomic_set(&fs_info->async_submit_draining, 0); atomic_set(&fs_info->async_submit_draining, 0);
atomic_set(&fs_info->nr_async_bios, 0); atomic_set(&fs_info->nr_async_bios, 0);
atomic_set(&fs_info->throttles, 0);
atomic_set(&fs_info->throttle_gen, 0);
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;
...@@ -1598,6 +1593,7 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1598,6 +1593,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
fs_info->btree_inode->i_mapping->a_ops = &btree_aops; fs_info->btree_inode->i_mapping->a_ops = &btree_aops;
fs_info->btree_inode->i_mapping->backing_dev_info = &fs_info->bdi; fs_info->btree_inode->i_mapping->backing_dev_info = &fs_info->bdi;
RB_CLEAR_NODE(&BTRFS_I(fs_info->btree_inode)->rb_node);
extent_io_tree_init(&BTRFS_I(fs_info->btree_inode)->io_tree, extent_io_tree_init(&BTRFS_I(fs_info->btree_inode)->io_tree,
fs_info->btree_inode->i_mapping, fs_info->btree_inode->i_mapping,
GFP_NOFS); GFP_NOFS);
...@@ -1613,10 +1609,6 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1613,10 +1609,6 @@ struct btrfs_root *open_ctree(struct super_block *sb,
fs_info->btree_inode->i_mapping, GFP_NOFS); fs_info->btree_inode->i_mapping, GFP_NOFS);
fs_info->do_barriers = 1; fs_info->do_barriers = 1;
INIT_LIST_HEAD(&fs_info->dead_reloc_roots);
btrfs_leaf_ref_tree_init(&fs_info->reloc_ref_tree);
btrfs_leaf_ref_tree_init(&fs_info->shared_ref_tree);
BTRFS_I(fs_info->btree_inode)->root = tree_root; BTRFS_I(fs_info->btree_inode)->root = tree_root;
memset(&BTRFS_I(fs_info->btree_inode)->location, 0, memset(&BTRFS_I(fs_info->btree_inode)->location, 0,
sizeof(struct btrfs_key)); sizeof(struct btrfs_key));
...@@ -1674,6 +1666,12 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1674,6 +1666,12 @@ struct btrfs_root *open_ctree(struct super_block *sb,
goto fail_iput; goto fail_iput;
} }
features = btrfs_super_incompat_flags(disk_super);
if (!(features & BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF)) {
features |= BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF;
btrfs_set_super_incompat_flags(disk_super, features);
}
features = btrfs_super_compat_ro_flags(disk_super) & features = btrfs_super_compat_ro_flags(disk_super) &
~BTRFS_FEATURE_COMPAT_RO_SUPP; ~BTRFS_FEATURE_COMPAT_RO_SUPP;
if (!(sb->s_flags & MS_RDONLY) && features) { if (!(sb->s_flags & MS_RDONLY) && features) {
...@@ -1771,7 +1769,7 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1771,7 +1769,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
if (ret) { if (ret) {
printk(KERN_WARNING "btrfs: failed to read the system " printk(KERN_WARNING "btrfs: failed to read the system "
"array on %s\n", sb->s_id); "array on %s\n", sb->s_id);
goto fail_sys_array; goto fail_sb_buffer;
} }
blocksize = btrfs_level_size(tree_root, blocksize = btrfs_level_size(tree_root,
...@@ -1785,6 +1783,8 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1785,6 +1783,8 @@ struct btrfs_root *open_ctree(struct super_block *sb,
btrfs_super_chunk_root(disk_super), btrfs_super_chunk_root(disk_super),
blocksize, generation); blocksize, generation);
BUG_ON(!chunk_root->node); BUG_ON(!chunk_root->node);
btrfs_set_root_node(&chunk_root->root_item, chunk_root->node);
chunk_root->commit_root = btrfs_root_node(chunk_root);
read_extent_buffer(chunk_root->node, fs_info->chunk_tree_uuid, read_extent_buffer(chunk_root->node, fs_info->chunk_tree_uuid,
(unsigned long)btrfs_header_chunk_tree_uuid(chunk_root->node), (unsigned long)btrfs_header_chunk_tree_uuid(chunk_root->node),
...@@ -1810,7 +1810,8 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1810,7 +1810,8 @@ struct btrfs_root *open_ctree(struct super_block *sb,
blocksize, generation); blocksize, generation);
if (!tree_root->node) if (!tree_root->node)
goto fail_chunk_root; goto fail_chunk_root;
btrfs_set_root_node(&tree_root->root_item, tree_root->node);
tree_root->commit_root = btrfs_root_node(tree_root);
ret = find_and_setup_root(tree_root, fs_info, ret = find_and_setup_root(tree_root, fs_info,
BTRFS_EXTENT_TREE_OBJECTID, extent_root); BTRFS_EXTENT_TREE_OBJECTID, extent_root);
...@@ -1820,14 +1821,14 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1820,14 +1821,14 @@ struct btrfs_root *open_ctree(struct super_block *sb,
ret = find_and_setup_root(tree_root, fs_info, ret = find_and_setup_root(tree_root, fs_info,
BTRFS_DEV_TREE_OBJECTID, dev_root); BTRFS_DEV_TREE_OBJECTID, dev_root);
dev_root->track_dirty = 1;
if (ret) if (ret)
goto fail_extent_root; goto fail_extent_root;
dev_root->track_dirty = 1;
ret = find_and_setup_root(tree_root, fs_info, ret = find_and_setup_root(tree_root, fs_info,
BTRFS_CSUM_TREE_OBJECTID, csum_root); BTRFS_CSUM_TREE_OBJECTID, csum_root);
if (ret) if (ret)
goto fail_extent_root; goto fail_dev_root;
csum_root->track_dirty = 1; csum_root->track_dirty = 1;
...@@ -1849,6 +1850,14 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1849,6 +1850,14 @@ struct btrfs_root *open_ctree(struct super_block *sb,
if (IS_ERR(fs_info->transaction_kthread)) if (IS_ERR(fs_info->transaction_kthread))
goto fail_cleaner; goto fail_cleaner;
if (!btrfs_test_opt(tree_root, SSD) &&
!btrfs_test_opt(tree_root, NOSSD) &&
!fs_info->fs_devices->rotating) {
printk(KERN_INFO "Btrfs detected SSD devices, enabling SSD "
"mode\n");
btrfs_set_opt(fs_info->mount_opt, SSD);
}
if (btrfs_super_log_root(disk_super) != 0) { if (btrfs_super_log_root(disk_super) != 0) {
u64 bytenr = btrfs_super_log_root(disk_super); u64 bytenr = btrfs_super_log_root(disk_super);
...@@ -1881,7 +1890,7 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1881,7 +1890,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
} }
if (!(sb->s_flags & MS_RDONLY)) { if (!(sb->s_flags & MS_RDONLY)) {
ret = btrfs_cleanup_reloc_trees(tree_root); ret = btrfs_recover_relocation(tree_root);
BUG_ON(ret); BUG_ON(ret);
} }
...@@ -1892,6 +1901,7 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1892,6 +1901,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
fs_info->fs_root = btrfs_read_fs_root_no_name(fs_info, &location); fs_info->fs_root = btrfs_read_fs_root_no_name(fs_info, &location);
if (!fs_info->fs_root) if (!fs_info->fs_root)
goto fail_trans_kthread; goto fail_trans_kthread;
return tree_root; return tree_root;
fail_trans_kthread: fail_trans_kthread:
...@@ -1908,14 +1918,19 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1908,14 +1918,19 @@ struct btrfs_root *open_ctree(struct super_block *sb,
fail_csum_root: fail_csum_root:
free_extent_buffer(csum_root->node); free_extent_buffer(csum_root->node);
free_extent_buffer(csum_root->commit_root);
fail_dev_root:
free_extent_buffer(dev_root->node);
free_extent_buffer(dev_root->commit_root);
fail_extent_root: fail_extent_root:
free_extent_buffer(extent_root->node); free_extent_buffer(extent_root->node);
free_extent_buffer(extent_root->commit_root);
fail_tree_root: fail_tree_root:
free_extent_buffer(tree_root->node); free_extent_buffer(tree_root->node);
free_extent_buffer(tree_root->commit_root);
fail_chunk_root: fail_chunk_root:
free_extent_buffer(chunk_root->node); free_extent_buffer(chunk_root->node);
fail_sys_array: free_extent_buffer(chunk_root->commit_root);
free_extent_buffer(dev_root->node);
fail_sb_buffer: fail_sb_buffer:
btrfs_stop_workers(&fs_info->fixup_workers); btrfs_stop_workers(&fs_info->fixup_workers);
btrfs_stop_workers(&fs_info->delalloc_workers); btrfs_stop_workers(&fs_info->delalloc_workers);
...@@ -2005,6 +2020,17 @@ struct buffer_head *btrfs_read_dev_super(struct block_device *bdev) ...@@ -2005,6 +2020,17 @@ struct buffer_head *btrfs_read_dev_super(struct block_device *bdev)
return latest; return latest;
} }
/*
* this should be called twice, once with wait == 0 and
* once with wait == 1. When wait == 0 is done, all the buffer heads
* we write are pinned.
*
* They are released when wait == 1 is done.
* max_mirrors must be the same for both runs, and it indicates how
* many supers on this one device should be written.
*
* max_mirrors == 0 means to write them all.
*/
static int write_dev_supers(struct btrfs_device *device, static int write_dev_supers(struct btrfs_device *device,
struct btrfs_super_block *sb, struct btrfs_super_block *sb,
int do_barriers, int wait, int max_mirrors) int do_barriers, int wait, int max_mirrors)
...@@ -2040,12 +2066,16 @@ static int write_dev_supers(struct btrfs_device *device, ...@@ -2040,12 +2066,16 @@ static int write_dev_supers(struct btrfs_device *device,
bh = __find_get_block(device->bdev, bytenr / 4096, bh = __find_get_block(device->bdev, bytenr / 4096,
BTRFS_SUPER_INFO_SIZE); BTRFS_SUPER_INFO_SIZE);
BUG_ON(!bh); BUG_ON(!bh);
brelse(bh);
wait_on_buffer(bh); wait_on_buffer(bh);
if (buffer_uptodate(bh)) { if (!buffer_uptodate(bh))
brelse(bh); errors++;
continue;
} /* drop our reference */
brelse(bh);
/* drop the reference from the wait == 0 run */
brelse(bh);
continue;
} else { } else {
btrfs_set_super_bytenr(sb, bytenr); btrfs_set_super_bytenr(sb, bytenr);
...@@ -2056,12 +2086,18 @@ static int write_dev_supers(struct btrfs_device *device, ...@@ -2056,12 +2086,18 @@ static int write_dev_supers(struct btrfs_device *device,
BTRFS_CSUM_SIZE); BTRFS_CSUM_SIZE);
btrfs_csum_final(crc, sb->csum); btrfs_csum_final(crc, sb->csum);
/*
* one reference for us, and we leave it for the
* caller
*/
bh = __getblk(device->bdev, bytenr / 4096, bh = __getblk(device->bdev, bytenr / 4096,
BTRFS_SUPER_INFO_SIZE); BTRFS_SUPER_INFO_SIZE);
memcpy(bh->b_data, sb, BTRFS_SUPER_INFO_SIZE); memcpy(bh->b_data, sb, BTRFS_SUPER_INFO_SIZE);
set_buffer_uptodate(bh); /* one reference for submit_bh */
get_bh(bh); get_bh(bh);
set_buffer_uptodate(bh);
lock_buffer(bh); lock_buffer(bh);
bh->b_end_io = btrfs_end_buffer_write_sync; bh->b_end_io = btrfs_end_buffer_write_sync;
} }
...@@ -2073,6 +2109,7 @@ static int write_dev_supers(struct btrfs_device *device, ...@@ -2073,6 +2109,7 @@ static int write_dev_supers(struct btrfs_device *device,
device->name); device->name);
set_buffer_uptodate(bh); set_buffer_uptodate(bh);
device->barriers = 0; device->barriers = 0;
/* one reference for submit_bh */
get_bh(bh); get_bh(bh);
lock_buffer(bh); lock_buffer(bh);
ret = submit_bh(WRITE_SYNC, bh); ret = submit_bh(WRITE_SYNC, bh);
...@@ -2081,22 +2118,15 @@ static int write_dev_supers(struct btrfs_device *device, ...@@ -2081,22 +2118,15 @@ static int write_dev_supers(struct btrfs_device *device,
ret = submit_bh(WRITE_SYNC, bh); ret = submit_bh(WRITE_SYNC, bh);
} }
if (!ret && wait) { if (ret)
wait_on_buffer(bh);
if (!buffer_uptodate(bh))
errors++;
} else if (ret) {
errors++; errors++;
}
if (wait)
brelse(bh);
} }
return errors < i ? 0 : -1; return errors < i ? 0 : -1;
} }
int write_all_supers(struct btrfs_root *root, int max_mirrors) int write_all_supers(struct btrfs_root *root, int max_mirrors)
{ {
struct list_head *head = &root->fs_info->fs_devices->devices; struct list_head *head;
struct btrfs_device *dev; struct btrfs_device *dev;
struct btrfs_super_block *sb; struct btrfs_super_block *sb;
struct btrfs_dev_item *dev_item; struct btrfs_dev_item *dev_item;
...@@ -2111,6 +2141,9 @@ int write_all_supers(struct btrfs_root *root, int max_mirrors) ...@@ -2111,6 +2141,9 @@ int write_all_supers(struct btrfs_root *root, int max_mirrors)
sb = &root->fs_info->super_for_commit; sb = &root->fs_info->super_for_commit;
dev_item = &sb->dev_item; dev_item = &sb->dev_item;
mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
head = &root->fs_info->fs_devices->devices;
list_for_each_entry(dev, head, dev_list) { list_for_each_entry(dev, head, dev_list) {
if (!dev->bdev) { if (!dev->bdev) {
total_errors++; total_errors++;
...@@ -2154,6 +2187,7 @@ int write_all_supers(struct btrfs_root *root, int max_mirrors) ...@@ -2154,6 +2187,7 @@ int write_all_supers(struct btrfs_root *root, int max_mirrors)
if (ret) if (ret)
total_errors++; total_errors++;
} }
mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
if (total_errors > max_errors) { if (total_errors > max_errors) {
printk(KERN_ERR "btrfs: %d errors while writing supers\n", printk(KERN_ERR "btrfs: %d errors while writing supers\n",
total_errors); total_errors);
...@@ -2173,6 +2207,7 @@ int write_ctree_super(struct btrfs_trans_handle *trans, ...@@ -2173,6 +2207,7 @@ int write_ctree_super(struct btrfs_trans_handle *trans,
int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root) int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root)
{ {
WARN_ON(!RB_EMPTY_ROOT(&root->inode_tree));
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);
if (root->anon_super.s_dev) { if (root->anon_super.s_dev) {
...@@ -2219,10 +2254,12 @@ int btrfs_cleanup_fs_roots(struct btrfs_fs_info *fs_info) ...@@ -2219,10 +2254,12 @@ int btrfs_cleanup_fs_roots(struct btrfs_fs_info *fs_info)
ARRAY_SIZE(gang)); ARRAY_SIZE(gang));
if (!ret) if (!ret)
break; break;
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, ret = btrfs_find_dead_roots(fs_info->tree_root,
root_objectid, gang[i]); root_objectid);
BUG_ON(ret); BUG_ON(ret);
btrfs_orphan_cleanup(gang[i]); btrfs_orphan_cleanup(gang[i]);
} }
...@@ -2278,20 +2315,16 @@ int close_ctree(struct btrfs_root *root) ...@@ -2278,20 +2315,16 @@ int close_ctree(struct btrfs_root *root)
(unsigned long long)fs_info->total_ref_cache_size); (unsigned long long)fs_info->total_ref_cache_size);
} }
if (fs_info->extent_root->node) free_extent_buffer(fs_info->extent_root->node);
free_extent_buffer(fs_info->extent_root->node); free_extent_buffer(fs_info->extent_root->commit_root);
free_extent_buffer(fs_info->tree_root->node);
if (fs_info->tree_root->node) free_extent_buffer(fs_info->tree_root->commit_root);
free_extent_buffer(fs_info->tree_root->node); free_extent_buffer(root->fs_info->chunk_root->node);
free_extent_buffer(root->fs_info->chunk_root->commit_root);
if (root->fs_info->chunk_root->node) free_extent_buffer(root->fs_info->dev_root->node);
free_extent_buffer(root->fs_info->chunk_root->node); free_extent_buffer(root->fs_info->dev_root->commit_root);
free_extent_buffer(root->fs_info->csum_root->node);
if (root->fs_info->dev_root->node) free_extent_buffer(root->fs_info->csum_root->commit_root);
free_extent_buffer(root->fs_info->dev_root->node);
if (root->fs_info->csum_root->node)
free_extent_buffer(root->fs_info->csum_root->node);
btrfs_free_block_groups(root->fs_info); btrfs_free_block_groups(root->fs_info);
...@@ -2373,17 +2406,14 @@ void btrfs_btree_balance_dirty(struct btrfs_root *root, unsigned long nr) ...@@ -2373,17 +2406,14 @@ void btrfs_btree_balance_dirty(struct btrfs_root *root, unsigned long nr)
* looks as though older kernels can get into trouble with * looks as though older kernels can get into trouble with
* this code, they end up stuck in balance_dirty_pages forever * this code, they end up stuck in balance_dirty_pages forever
*/ */
struct extent_io_tree *tree;
u64 num_dirty; u64 num_dirty;
u64 start = 0;
unsigned long thresh = 32 * 1024 * 1024; unsigned long thresh = 32 * 1024 * 1024;
tree = &BTRFS_I(root->fs_info->btree_inode)->io_tree;
if (current->flags & PF_MEMALLOC) if (current->flags & PF_MEMALLOC)
return; return;
num_dirty = count_range_bits(tree, &start, (u64)-1, num_dirty = root->fs_info->dirty_metadata_bytes;
thresh, EXTENT_DIRTY);
if (num_dirty > thresh) { if (num_dirty > thresh) {
balance_dirty_pages_ratelimited_nr( balance_dirty_pages_ratelimited_nr(
root->fs_info->btree_inode->i_mapping, 1); root->fs_info->btree_inode->i_mapping, 1);
......
...@@ -78,7 +78,7 @@ static struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid, ...@@ -78,7 +78,7 @@ static struct dentry *btrfs_get_dentry(struct super_block *sb, u64 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, NULL); inode = btrfs_iget(sb, &key, root);
if (IS_ERR(inode)) if (IS_ERR(inode))
return (void *)inode; return (void *)inode;
...@@ -192,7 +192,7 @@ static struct dentry *btrfs_get_parent(struct dentry *child) ...@@ -192,7 +192,7 @@ static struct dentry *btrfs_get_parent(struct dentry *child)
btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY); btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);
key.offset = 0; key.offset = 0;
return d_obtain_alias(btrfs_iget(root->fs_info->sb, &key, root, NULL)); return d_obtain_alias(btrfs_iget(root->fs_info->sb, &key, root));
} }
const struct export_operations btrfs_export_ops = { const struct export_operations btrfs_export_ops = {
......
...@@ -23,50 +23,39 @@ ...@@ -23,50 +23,39 @@
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include "compat.h" #include "compat.h"
#include "hash.h" #include "hash.h"
#include "crc32c.h"
#include "ctree.h" #include "ctree.h"
#include "disk-io.h" #include "disk-io.h"
#include "print-tree.h" #include "print-tree.h"
#include "transaction.h" #include "transaction.h"
#include "volumes.h" #include "volumes.h"
#include "locking.h" #include "locking.h"
#include "ref-cache.h"
#include "free-space-cache.h" #include "free-space-cache.h"
#define PENDING_EXTENT_INSERT 0
#define PENDING_EXTENT_DELETE 1
#define PENDING_BACKREF_UPDATE 2
struct pending_extent_op {
int type;
u64 bytenr;
u64 num_bytes;
u64 parent;
u64 orig_parent;
u64 generation;
u64 orig_generation;
int level;
struct list_head list;
int del;
};
static int __btrfs_alloc_reserved_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 parent,
u64 root_objectid, u64 ref_generation,
u64 owner, struct btrfs_key *ins,
int ref_mod);
static int update_reserved_extents(struct btrfs_root *root, static int update_reserved_extents(struct btrfs_root *root,
u64 bytenr, u64 num, int reserve); u64 bytenr, u64 num, int reserve);
static int update_block_group(struct btrfs_trans_handle *trans, static int update_block_group(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
u64 bytenr, u64 num_bytes, int alloc, u64 bytenr, u64 num_bytes, int alloc,
int mark_free); int mark_free);
static noinline int __btrfs_free_extent(struct btrfs_trans_handle *trans, static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
u64 bytenr, u64 num_bytes, u64 parent, u64 bytenr, u64 num_bytes, u64 parent,
u64 root_objectid, u64 ref_generation, u64 root_objectid, u64 owner_objectid,
u64 owner_objectid, int pin, u64 owner_offset, int refs_to_drop,
int ref_to_drop); struct btrfs_delayed_extent_op *extra_op);
static void __run_delayed_extent_op(struct btrfs_delayed_extent_op *extent_op,
struct extent_buffer *leaf,
struct btrfs_extent_item *ei);
static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 parent, u64 root_objectid,
u64 flags, u64 owner, u64 offset,
struct btrfs_key *ins, int ref_mod);
static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 parent, u64 root_objectid,
u64 flags, struct btrfs_disk_key *key,
int level, struct btrfs_key *ins);
static int do_chunk_alloc(struct btrfs_trans_handle *trans, static int do_chunk_alloc(struct btrfs_trans_handle *trans,
struct btrfs_root *extent_root, u64 alloc_bytes, struct btrfs_root *extent_root, u64 alloc_bytes,
...@@ -453,348 +442,1071 @@ int btrfs_lookup_extent(struct btrfs_root *root, u64 start, u64 len) ...@@ -453,348 +442,1071 @@ int btrfs_lookup_extent(struct btrfs_root *root, u64 start, u64 len)
* maintenance. This is actually the same as #2, but with a slightly * maintenance. This is actually the same as #2, but with a slightly
* different use case. * different use case.
* *
* There are two kinds of back refs. The implicit back refs is optimized
* for pointers in non-shared tree blocks. For a given pointer in a block,
* back refs of this kind provide information about the block's owner tree
* and the pointer's key. These information allow us to find the block by
* b-tree searching. The full back refs is for pointers in tree blocks not
* referenced by their owner trees. The location of tree block is recorded
* in the back refs. Actually the full back refs is generic, and can be
* used in all cases the implicit back refs is used. The major shortcoming
* of the full back refs is its overhead. Every time a tree block gets
* COWed, we have to update back refs entry for all pointers in it.
*
* For a newly allocated tree block, we use implicit back refs for
* pointers in it. This means most tree related operations only involve
* implicit back refs. For a tree block created in old transaction, the
* only way to drop a reference to it is COW it. So we can detect the
* event that tree block loses its owner tree's reference and do the
* back refs conversion.
*
* When a tree block is COW'd through a tree, there are four cases:
*
* The reference count of the block is one and the tree is the block's
* owner tree. Nothing to do in this case.
*
* The reference count of the block is one and the tree is not the
* block's owner tree. In this case, full back refs is used for pointers
* in the block. Remove these full back refs, add implicit back refs for
* every pointers in the new block.
*
* The reference count of the block is greater than one and the tree is
* the block's owner tree. In this case, implicit back refs is used for
* pointers in the block. Add full back refs for every pointers in the
* block, increase lower level extents' reference counts. The original
* implicit back refs are entailed to the new block.
*
* The reference count of the block is greater than one and the tree is
* not the block's owner tree. Add implicit back refs for every pointer in
* the new block, increase lower level extents' reference count.
*
* Back Reference Key composing:
*
* The key objectid corresponds to the first byte in the extent,
* The key type is used to differentiate between types of back refs.
* There are different meanings of the key offset for different types
* of back refs.
*
* File extents can be referenced by: * File extents can be referenced by:
* *
* - multiple snapshots, subvolumes, or different generations in one subvol * - multiple snapshots, subvolumes, or different generations in one subvol
* - different files inside a single subvolume * - different files inside a single subvolume
* - different offsets inside a file (bookend extents in file.c) * - different offsets inside a file (bookend extents in file.c)
* *
* The extent ref structure has fields for: * The extent ref structure for the implicit back refs has fields for:
* *
* - Objectid of the subvolume root * - Objectid of the subvolume root
* - Generation number of the tree holding the reference
* - objectid of the file holding the reference * - objectid of the file holding the reference
* - number of references holding by parent node (alway 1 for tree blocks) * - original offset in the file
* * - how many bookend extents
* Btree leaf may hold multiple references to a file extent. In most cases,
* these references are from same file and the corresponding offsets inside
* the file are close together.
*
* When a file extent is allocated the fields are filled in:
* (root_key.objectid, trans->transid, inode objectid, 1)
* *
* When a leaf is cow'd new references are added for every file extent found * The key offset for the implicit back refs is hash of the first
* in the leaf. It looks similar to the create case, but trans->transid will * three fields.
* be different when the block is cow'd.
* *
* (root_key.objectid, trans->transid, inode objectid, * The extent ref structure for the full back refs has field for:
* number of references in the leaf)
* *
* When a file extent is removed either during snapshot deletion or * - number of pointers in the tree leaf
* file truncation, we find the corresponding back reference and check
* the following fields:
* *
* (btrfs_header_owner(leaf), btrfs_header_generation(leaf), * The key offset for the implicit back refs is the first byte of
* inode objectid) * the tree leaf
* *
* Btree extents can be referenced by: * When a file extent is allocated, The implicit back refs is used.
* * the fields are filled in:
* - Different subvolumes
* - Different generations of the same subvolume
*
* When a tree block is created, back references are inserted:
* *
* (root->root_key.objectid, trans->transid, level, 1) * (root_key.objectid, inode objectid, offset in file, 1)
* *
* When a tree block is cow'd, new back references are added for all the * When a file extent is removed file truncation, we find the
* blocks it points to. If the tree block isn't in reference counted root, * corresponding implicit back refs and check the following fields:
* the old back references are removed. These new back references are of
* the form (trans->transid will have increased since creation):
* *
* (root->root_key.objectid, trans->transid, level, 1) * (btrfs_header_owner(leaf), inode objectid, offset in file)
* *
* When a backref is in deleting, the following fields are checked: * Btree extents can be referenced by:
* *
* if backref was for a tree root: * - Different subvolumes
* (btrfs_header_owner(itself), btrfs_header_generation(itself), level)
* else
* (btrfs_header_owner(parent), btrfs_header_generation(parent), level)
* *
* Back Reference Key composing: * Both the implicit back refs and the full back refs for tree blocks
* only consist of key. The key offset for the implicit back refs is
* objectid of block's owner tree. The key offset for the full back refs
* is the first byte of parent block.
* *
* The key objectid corresponds to the first byte in the extent, the key * When implicit back refs is used, information about the lowest key and
* type is set to BTRFS_EXTENT_REF_KEY, and the key offset is the first * level of the tree block are required. These information are stored in
* byte of parent extent. If a extent is tree root, the key offset is set * tree block info structure.
* to the key objectid.
*/ */
static noinline int lookup_extent_backref(struct btrfs_trans_handle *trans, #ifdef BTRFS_COMPAT_EXTENT_TREE_V0
struct btrfs_root *root, static int convert_extent_item_v0(struct btrfs_trans_handle *trans,
struct btrfs_path *path, struct btrfs_root *root,
u64 bytenr, u64 parent, struct btrfs_path *path,
u64 ref_root, u64 ref_generation, u64 owner, u32 extra_size)
u64 owner_objectid, int del)
{ {
struct btrfs_extent_item *item;
struct btrfs_extent_item_v0 *ei0;
struct btrfs_extent_ref_v0 *ref0;
struct btrfs_tree_block_info *bi;
struct extent_buffer *leaf;
struct btrfs_key key; struct btrfs_key key;
struct btrfs_extent_ref *ref; struct btrfs_key found_key;
u32 new_size = sizeof(*item);
u64 refs;
int ret;
leaf = path->nodes[0];
BUG_ON(btrfs_item_size_nr(leaf, path->slots[0]) != sizeof(*ei0));
btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
ei0 = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_extent_item_v0);
refs = btrfs_extent_refs_v0(leaf, ei0);
if (owner == (u64)-1) {
while (1) {
if (path->slots[0] >= btrfs_header_nritems(leaf)) {
ret = btrfs_next_leaf(root, path);
if (ret < 0)
return ret;
BUG_ON(ret > 0);
leaf = path->nodes[0];
}
btrfs_item_key_to_cpu(leaf, &found_key,
path->slots[0]);
BUG_ON(key.objectid != found_key.objectid);
if (found_key.type != BTRFS_EXTENT_REF_V0_KEY) {
path->slots[0]++;
continue;
}
ref0 = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_extent_ref_v0);
owner = btrfs_ref_objectid_v0(leaf, ref0);
break;
}
}
btrfs_release_path(root, path);
if (owner < BTRFS_FIRST_FREE_OBJECTID)
new_size += sizeof(*bi);
new_size -= sizeof(*ei0);
ret = btrfs_search_slot(trans, root, &key, path,
new_size + extra_size, 1);
if (ret < 0)
return ret;
BUG_ON(ret);
ret = btrfs_extend_item(trans, root, path, new_size);
BUG_ON(ret);
leaf = path->nodes[0];
item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
btrfs_set_extent_refs(leaf, item, refs);
/* FIXME: get real generation */
btrfs_set_extent_generation(leaf, item, 0);
if (owner < BTRFS_FIRST_FREE_OBJECTID) {
btrfs_set_extent_flags(leaf, item,
BTRFS_EXTENT_FLAG_TREE_BLOCK |
BTRFS_BLOCK_FLAG_FULL_BACKREF);
bi = (struct btrfs_tree_block_info *)(item + 1);
/* FIXME: get first key of the block */
memset_extent_buffer(leaf, 0, (unsigned long)bi, sizeof(*bi));
btrfs_set_tree_block_level(leaf, bi, (int)owner);
} else {
btrfs_set_extent_flags(leaf, item, BTRFS_EXTENT_FLAG_DATA);
}
btrfs_mark_buffer_dirty(leaf);
return 0;
}
#endif
static u64 hash_extent_data_ref(u64 root_objectid, u64 owner, u64 offset)
{
u32 high_crc = ~(u32)0;
u32 low_crc = ~(u32)0;
__le64 lenum;
lenum = cpu_to_le64(root_objectid);
high_crc = crc32c(high_crc, &lenum, sizeof(lenum));
lenum = cpu_to_le64(owner);
low_crc = crc32c(low_crc, &lenum, sizeof(lenum));
lenum = cpu_to_le64(offset);
low_crc = crc32c(low_crc, &lenum, sizeof(lenum));
return ((u64)high_crc << 31) ^ (u64)low_crc;
}
static u64 hash_extent_data_ref_item(struct extent_buffer *leaf,
struct btrfs_extent_data_ref *ref)
{
return hash_extent_data_ref(btrfs_extent_data_ref_root(leaf, ref),
btrfs_extent_data_ref_objectid(leaf, ref),
btrfs_extent_data_ref_offset(leaf, ref));
}
static int match_extent_data_ref(struct extent_buffer *leaf,
struct btrfs_extent_data_ref *ref,
u64 root_objectid, u64 owner, u64 offset)
{
if (btrfs_extent_data_ref_root(leaf, ref) != root_objectid ||
btrfs_extent_data_ref_objectid(leaf, ref) != owner ||
btrfs_extent_data_ref_offset(leaf, ref) != offset)
return 0;
return 1;
}
static noinline int lookup_extent_data_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
u64 bytenr, u64 parent,
u64 root_objectid,
u64 owner, u64 offset)
{
struct btrfs_key key;
struct btrfs_extent_data_ref *ref;
struct extent_buffer *leaf; struct extent_buffer *leaf;
u64 ref_objectid; u32 nritems;
int ret; int ret;
int recow;
int err = -ENOENT;
key.objectid = bytenr; key.objectid = bytenr;
key.type = BTRFS_EXTENT_REF_KEY; if (parent) {
key.offset = parent; key.type = BTRFS_SHARED_DATA_REF_KEY;
key.offset = parent;
} else {
key.type = BTRFS_EXTENT_DATA_REF_KEY;
key.offset = hash_extent_data_ref(root_objectid,
owner, offset);
}
again:
recow = 0;
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
if (ret < 0) {
err = ret;
goto fail;
}
ret = btrfs_search_slot(trans, root, &key, path, del ? -1 : 0, 1); if (parent) {
if (ret < 0) if (!ret)
goto out; return 0;
if (ret > 0) { #ifdef BTRFS_COMPAT_EXTENT_TREE_V0
ret = -ENOENT; key.type = BTRFS_EXTENT_REF_V0_KEY;
goto out; btrfs_release_path(root, path);
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
if (ret < 0) {
err = ret;
goto fail;
}
if (!ret)
return 0;
#endif
goto fail;
} }
leaf = path->nodes[0]; leaf = path->nodes[0];
ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_ref); nritems = btrfs_header_nritems(leaf);
ref_objectid = btrfs_ref_objectid(leaf, ref); while (1) {
if (btrfs_ref_root(leaf, ref) != ref_root || if (path->slots[0] >= nritems) {
btrfs_ref_generation(leaf, ref) != ref_generation || ret = btrfs_next_leaf(root, path);
(ref_objectid != owner_objectid && if (ret < 0)
ref_objectid != BTRFS_MULTIPLE_OBJECTIDS)) { err = ret;
ret = -EIO; if (ret)
WARN_ON(1); goto fail;
goto out;
leaf = path->nodes[0];
nritems = btrfs_header_nritems(leaf);
recow = 1;
}
btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
if (key.objectid != bytenr ||
key.type != BTRFS_EXTENT_DATA_REF_KEY)
goto fail;
ref = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_extent_data_ref);
if (match_extent_data_ref(leaf, ref, root_objectid,
owner, offset)) {
if (recow) {
btrfs_release_path(root, path);
goto again;
}
err = 0;
break;
}
path->slots[0]++;
} }
ret = 0; fail:
out: return err;
return ret;
} }
static noinline int insert_extent_backref(struct btrfs_trans_handle *trans, static noinline int insert_extent_data_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct btrfs_path *path, struct btrfs_path *path,
u64 bytenr, u64 parent, u64 bytenr, u64 parent,
u64 ref_root, u64 ref_generation, u64 root_objectid, u64 owner,
u64 owner_objectid, u64 offset, int refs_to_add)
int refs_to_add)
{ {
struct btrfs_key key; struct btrfs_key key;
struct extent_buffer *leaf; struct extent_buffer *leaf;
struct btrfs_extent_ref *ref; u32 size;
u32 num_refs; u32 num_refs;
int ret; int ret;
key.objectid = bytenr; key.objectid = bytenr;
key.type = BTRFS_EXTENT_REF_KEY; if (parent) {
key.offset = parent; key.type = BTRFS_SHARED_DATA_REF_KEY;
key.offset = parent;
size = sizeof(struct btrfs_shared_data_ref);
} else {
key.type = BTRFS_EXTENT_DATA_REF_KEY;
key.offset = hash_extent_data_ref(root_objectid,
owner, offset);
size = sizeof(struct btrfs_extent_data_ref);
}
ret = btrfs_insert_empty_item(trans, root, path, &key, sizeof(*ref)); ret = btrfs_insert_empty_item(trans, root, path, &key, size);
if (ret == 0) { if (ret && ret != -EEXIST)
leaf = path->nodes[0]; goto fail;
ref = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_extent_ref); leaf = path->nodes[0];
btrfs_set_ref_root(leaf, ref, ref_root); if (parent) {
btrfs_set_ref_generation(leaf, ref, ref_generation); struct btrfs_shared_data_ref *ref;
btrfs_set_ref_objectid(leaf, ref, owner_objectid);
btrfs_set_ref_num_refs(leaf, ref, refs_to_add);
} else if (ret == -EEXIST) {
u64 existing_owner;
BUG_ON(owner_objectid < BTRFS_FIRST_FREE_OBJECTID);
leaf = path->nodes[0];
ref = btrfs_item_ptr(leaf, path->slots[0], ref = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_extent_ref); struct btrfs_shared_data_ref);
if (btrfs_ref_root(leaf, ref) != ref_root || if (ret == 0) {
btrfs_ref_generation(leaf, ref) != ref_generation) { btrfs_set_shared_data_ref_count(leaf, ref, refs_to_add);
ret = -EIO; } else {
WARN_ON(1); num_refs = btrfs_shared_data_ref_count(leaf, ref);
goto out; num_refs += refs_to_add;
btrfs_set_shared_data_ref_count(leaf, ref, num_refs);
} }
} else {
struct btrfs_extent_data_ref *ref;
while (ret == -EEXIST) {
ref = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_extent_data_ref);
if (match_extent_data_ref(leaf, ref, root_objectid,
owner, offset))
break;
btrfs_release_path(root, path);
key.offset++;
ret = btrfs_insert_empty_item(trans, root, path, &key,
size);
if (ret && ret != -EEXIST)
goto fail;
num_refs = btrfs_ref_num_refs(leaf, ref); leaf = path->nodes[0];
BUG_ON(num_refs == 0); }
btrfs_set_ref_num_refs(leaf, ref, num_refs + refs_to_add); ref = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_extent_data_ref);
existing_owner = btrfs_ref_objectid(leaf, ref); if (ret == 0) {
if (existing_owner != owner_objectid && btrfs_set_extent_data_ref_root(leaf, ref,
existing_owner != BTRFS_MULTIPLE_OBJECTIDS) { root_objectid);
btrfs_set_ref_objectid(leaf, ref, btrfs_set_extent_data_ref_objectid(leaf, ref, owner);
BTRFS_MULTIPLE_OBJECTIDS); btrfs_set_extent_data_ref_offset(leaf, ref, offset);
btrfs_set_extent_data_ref_count(leaf, ref, refs_to_add);
} else {
num_refs = btrfs_extent_data_ref_count(leaf, ref);
num_refs += refs_to_add;
btrfs_set_extent_data_ref_count(leaf, ref, num_refs);
} }
ret = 0;
} else {
goto out;
} }
btrfs_unlock_up_safe(path, 1); btrfs_mark_buffer_dirty(leaf);
btrfs_mark_buffer_dirty(path->nodes[0]); ret = 0;
out: fail:
btrfs_release_path(root, path); btrfs_release_path(root, path);
return ret; return ret;
} }
static noinline int remove_extent_backref(struct btrfs_trans_handle *trans, static noinline int remove_extent_data_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct btrfs_path *path, struct btrfs_path *path,
int refs_to_drop) int refs_to_drop)
{ {
struct btrfs_key key;
struct btrfs_extent_data_ref *ref1 = NULL;
struct btrfs_shared_data_ref *ref2 = NULL;
struct extent_buffer *leaf; struct extent_buffer *leaf;
struct btrfs_extent_ref *ref; u32 num_refs = 0;
u32 num_refs;
int ret = 0; int ret = 0;
leaf = path->nodes[0]; leaf = path->nodes[0];
ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_ref); btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
num_refs = btrfs_ref_num_refs(leaf, ref);
if (key.type == BTRFS_EXTENT_DATA_REF_KEY) {
ref1 = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_extent_data_ref);
num_refs = btrfs_extent_data_ref_count(leaf, ref1);
} else if (key.type == BTRFS_SHARED_DATA_REF_KEY) {
ref2 = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_shared_data_ref);
num_refs = btrfs_shared_data_ref_count(leaf, ref2);
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
} else if (key.type == BTRFS_EXTENT_REF_V0_KEY) {
struct btrfs_extent_ref_v0 *ref0;
ref0 = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_extent_ref_v0);
num_refs = btrfs_ref_count_v0(leaf, ref0);
#endif
} else {
BUG();
}
BUG_ON(num_refs < refs_to_drop); BUG_ON(num_refs < refs_to_drop);
num_refs -= refs_to_drop; num_refs -= refs_to_drop;
if (num_refs == 0) { if (num_refs == 0) {
ret = btrfs_del_item(trans, root, path); ret = btrfs_del_item(trans, root, path);
} else { } else {
btrfs_set_ref_num_refs(leaf, ref, num_refs); if (key.type == BTRFS_EXTENT_DATA_REF_KEY)
btrfs_set_extent_data_ref_count(leaf, ref1, num_refs);
else if (key.type == BTRFS_SHARED_DATA_REF_KEY)
btrfs_set_shared_data_ref_count(leaf, ref2, num_refs);
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
else {
struct btrfs_extent_ref_v0 *ref0;
ref0 = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_extent_ref_v0);
btrfs_set_ref_count_v0(leaf, ref0, num_refs);
}
#endif
btrfs_mark_buffer_dirty(leaf); btrfs_mark_buffer_dirty(leaf);
} }
btrfs_release_path(root, path);
return ret; return ret;
} }
#ifdef BIO_RW_DISCARD static noinline u32 extent_data_ref_count(struct btrfs_root *root,
static void btrfs_issue_discard(struct block_device *bdev, struct btrfs_path *path,
u64 start, u64 len) struct btrfs_extent_inline_ref *iref)
{ {
blkdev_issue_discard(bdev, start >> 9, len >> 9, GFP_KERNEL); struct btrfs_key key;
} struct extent_buffer *leaf;
struct btrfs_extent_data_ref *ref1;
struct btrfs_shared_data_ref *ref2;
u32 num_refs = 0;
leaf = path->nodes[0];
btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
if (iref) {
if (btrfs_extent_inline_ref_type(leaf, iref) ==
BTRFS_EXTENT_DATA_REF_KEY) {
ref1 = (struct btrfs_extent_data_ref *)(&iref->offset);
num_refs = btrfs_extent_data_ref_count(leaf, ref1);
} else {
ref2 = (struct btrfs_shared_data_ref *)(iref + 1);
num_refs = btrfs_shared_data_ref_count(leaf, ref2);
}
} else if (key.type == BTRFS_EXTENT_DATA_REF_KEY) {
ref1 = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_extent_data_ref);
num_refs = btrfs_extent_data_ref_count(leaf, ref1);
} else if (key.type == BTRFS_SHARED_DATA_REF_KEY) {
ref2 = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_shared_data_ref);
num_refs = btrfs_shared_data_ref_count(leaf, ref2);
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
} else if (key.type == BTRFS_EXTENT_REF_V0_KEY) {
struct btrfs_extent_ref_v0 *ref0;
ref0 = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_extent_ref_v0);
num_refs = btrfs_ref_count_v0(leaf, ref0);
#endif #endif
} else {
WARN_ON(1);
}
return num_refs;
}
static int btrfs_discard_extent(struct btrfs_root *root, u64 bytenr, static noinline int lookup_tree_block_ref(struct btrfs_trans_handle *trans,
u64 num_bytes) struct btrfs_root *root,
struct btrfs_path *path,
u64 bytenr, u64 parent,
u64 root_objectid)
{ {
#ifdef BIO_RW_DISCARD struct btrfs_key key;
int ret; int ret;
u64 map_length = num_bytes;
struct btrfs_multi_bio *multi = NULL;
/* Tell the block device(s) that the sectors can be discarded */
ret = btrfs_map_block(&root->fs_info->mapping_tree, READ,
bytenr, &map_length, &multi, 0);
if (!ret) {
struct btrfs_bio_stripe *stripe = multi->stripes;
int i;
if (map_length > num_bytes)
map_length = num_bytes;
for (i = 0; i < multi->num_stripes; i++, stripe++) { key.objectid = bytenr;
btrfs_issue_discard(stripe->dev->bdev, if (parent) {
stripe->physical, key.type = BTRFS_SHARED_BLOCK_REF_KEY;
map_length); key.offset = parent;
} } else {
kfree(multi); key.type = BTRFS_TREE_BLOCK_REF_KEY;
key.offset = root_objectid;
} }
return ret; ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
#else if (ret > 0)
return 0; ret = -ENOENT;
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
if (ret == -ENOENT && parent) {
btrfs_release_path(root, path);
key.type = BTRFS_EXTENT_REF_V0_KEY;
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
if (ret > 0)
ret = -ENOENT;
}
#endif #endif
return ret;
} }
static int __btrfs_update_extent_ref(struct btrfs_trans_handle *trans, static noinline int insert_tree_block_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 bytenr, struct btrfs_root *root,
u64 num_bytes, struct btrfs_path *path,
u64 orig_parent, u64 parent, u64 bytenr, u64 parent,
u64 orig_root, u64 ref_root, u64 root_objectid)
u64 orig_generation, u64 ref_generation,
u64 owner_objectid)
{ {
struct btrfs_key key;
int ret; int ret;
int pin = owner_objectid < BTRFS_FIRST_FREE_OBJECTID;
ret = btrfs_update_delayed_ref(trans, bytenr, num_bytes, key.objectid = bytenr;
orig_parent, parent, orig_root, if (parent) {
ref_root, orig_generation, key.type = BTRFS_SHARED_BLOCK_REF_KEY;
ref_generation, owner_objectid, pin); key.offset = parent;
BUG_ON(ret); } else {
key.type = BTRFS_TREE_BLOCK_REF_KEY;
key.offset = root_objectid;
}
ret = btrfs_insert_empty_item(trans, root, path, &key, 0);
btrfs_release_path(root, path);
return ret; return ret;
} }
int btrfs_update_extent_ref(struct btrfs_trans_handle *trans, static inline int extent_ref_type(u64 parent, u64 owner)
struct btrfs_root *root, u64 bytenr,
u64 num_bytes, u64 orig_parent, u64 parent,
u64 ref_root, u64 ref_generation,
u64 owner_objectid)
{ {
int ret; int type;
if (ref_root == BTRFS_TREE_LOG_OBJECTID && if (owner < BTRFS_FIRST_FREE_OBJECTID) {
owner_objectid < BTRFS_FIRST_FREE_OBJECTID) if (parent > 0)
return 0; type = BTRFS_SHARED_BLOCK_REF_KEY;
else
ret = __btrfs_update_extent_ref(trans, root, bytenr, num_bytes, type = BTRFS_TREE_BLOCK_REF_KEY;
orig_parent, parent, ref_root, } else {
ref_root, ref_generation, if (parent > 0)
ref_generation, owner_objectid); type = BTRFS_SHARED_DATA_REF_KEY;
return ret; else
type = BTRFS_EXTENT_DATA_REF_KEY;
}
return type;
} }
static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 bytenr,
u64 num_bytes,
u64 orig_parent, u64 parent,
u64 orig_root, u64 ref_root,
u64 orig_generation, u64 ref_generation,
u64 owner_objectid)
{
int ret;
ret = btrfs_add_delayed_ref(trans, bytenr, num_bytes, parent, ref_root, static int find_next_key(struct btrfs_path *path, struct btrfs_key *key)
ref_generation, owner_objectid,
BTRFS_ADD_DELAYED_REF, 0);
BUG_ON(ret);
return ret;
}
static noinline_for_stack int add_extent_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 bytenr,
u64 num_bytes, u64 parent, u64 ref_root,
u64 ref_generation, u64 owner_objectid,
int refs_to_add)
{ {
struct btrfs_path *path; int level;
int ret; BUG_ON(!path->keep_locks);
struct btrfs_key key; for (level = 0; level < BTRFS_MAX_LEVEL; level++) {
struct extent_buffer *l; if (!path->nodes[level])
struct btrfs_extent_item *item; break;
u32 refs; btrfs_assert_tree_locked(path->nodes[level]);
if (path->slots[level] + 1 >=
btrfs_header_nritems(path->nodes[level]))
continue;
if (level == 0)
btrfs_item_key_to_cpu(path->nodes[level], key,
path->slots[level] + 1);
else
btrfs_node_key_to_cpu(path->nodes[level], key,
path->slots[level] + 1);
return 0;
}
return 1;
}
path = btrfs_alloc_path(); /*
if (!path) * look for inline back ref. if back ref is found, *ref_ret is set
return -ENOMEM; * to the address of inline back ref, and 0 is returned.
*
* if back ref isn't found, *ref_ret is set to the address where it
* should be inserted, and -ENOENT is returned.
*
* if insert is true and there are too many inline back refs, the path
* points to the extent item, and -EAGAIN is returned.
*
* NOTE: inline back refs are ordered in the same way that back ref
* items in the tree are ordered.
*/
static noinline_for_stack
int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
struct btrfs_extent_inline_ref **ref_ret,
u64 bytenr, u64 num_bytes,
u64 parent, u64 root_objectid,
u64 owner, u64 offset, int insert)
{
struct btrfs_key key;
struct extent_buffer *leaf;
struct btrfs_extent_item *ei;
struct btrfs_extent_inline_ref *iref;
u64 flags;
u64 item_size;
unsigned long ptr;
unsigned long end;
int extra_size;
int type;
int want;
int ret;
int err = 0;
path->reada = 1;
path->leave_spinning = 1;
key.objectid = bytenr; key.objectid = bytenr;
key.type = BTRFS_EXTENT_ITEM_KEY; key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = num_bytes; key.offset = num_bytes;
/* first find the extent item and update its reference count */ want = extent_ref_type(parent, owner);
ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, if (insert) {
path, 0, 1); extra_size = btrfs_extent_inline_ref_size(want);
path->keep_locks = 1;
} else
extra_size = -1;
ret = btrfs_search_slot(trans, root, &key, path, extra_size, 1);
if (ret < 0) { if (ret < 0) {
btrfs_set_path_blocking(path); err = ret;
goto out;
}
BUG_ON(ret);
leaf = path->nodes[0];
item_size = btrfs_item_size_nr(leaf, path->slots[0]);
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
if (item_size < sizeof(*ei)) {
if (!insert) {
err = -ENOENT;
goto out;
}
ret = convert_extent_item_v0(trans, root, path, owner,
extra_size);
if (ret < 0) {
err = ret;
goto out;
}
leaf = path->nodes[0];
item_size = btrfs_item_size_nr(leaf, path->slots[0]);
}
#endif
BUG_ON(item_size < sizeof(*ei));
ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
flags = btrfs_extent_flags(leaf, ei);
ptr = (unsigned long)(ei + 1);
end = (unsigned long)ei + item_size;
if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
ptr += sizeof(struct btrfs_tree_block_info);
BUG_ON(ptr > end);
} else {
BUG_ON(!(flags & BTRFS_EXTENT_FLAG_DATA));
}
err = -ENOENT;
while (1) {
if (ptr >= end) {
WARN_ON(ptr > end);
break;
}
iref = (struct btrfs_extent_inline_ref *)ptr;
type = btrfs_extent_inline_ref_type(leaf, iref);
if (want < type)
break;
if (want > type) {
ptr += btrfs_extent_inline_ref_size(type);
continue;
}
if (type == BTRFS_EXTENT_DATA_REF_KEY) {
struct btrfs_extent_data_ref *dref;
dref = (struct btrfs_extent_data_ref *)(&iref->offset);
if (match_extent_data_ref(leaf, dref, root_objectid,
owner, offset)) {
err = 0;
break;
}
if (hash_extent_data_ref_item(leaf, dref) <
hash_extent_data_ref(root_objectid, owner, offset))
break;
} else {
u64 ref_offset;
ref_offset = btrfs_extent_inline_ref_offset(leaf, iref);
if (parent > 0) {
if (parent == ref_offset) {
err = 0;
break;
}
if (ref_offset < parent)
break;
} else {
if (root_objectid == ref_offset) {
err = 0;
break;
}
if (ref_offset < root_objectid)
break;
}
}
ptr += btrfs_extent_inline_ref_size(type);
}
if (err == -ENOENT && insert) {
if (item_size + extra_size >=
BTRFS_MAX_EXTENT_ITEM_SIZE(root)) {
err = -EAGAIN;
goto out;
}
/*
* To add new inline back ref, we have to make sure
* there is no corresponding back ref item.
* For simplicity, we just do not add new inline back
* ref if there is any kind of item for this block
*/
if (find_next_key(path, &key) == 0 && key.objectid == bytenr &&
key.type < BTRFS_BLOCK_GROUP_ITEM_KEY) {
err = -EAGAIN;
goto out;
}
}
*ref_ret = (struct btrfs_extent_inline_ref *)ptr;
out:
if (insert) {
path->keep_locks = 0;
btrfs_unlock_up_safe(path, 1);
}
return err;
}
/*
* helper to add new inline back ref
*/
static noinline_for_stack
int setup_inline_extent_backref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
struct btrfs_extent_inline_ref *iref,
u64 parent, u64 root_objectid,
u64 owner, u64 offset, int refs_to_add,
struct btrfs_delayed_extent_op *extent_op)
{
struct extent_buffer *leaf;
struct btrfs_extent_item *ei;
unsigned long ptr;
unsigned long end;
unsigned long item_offset;
u64 refs;
int size;
int type;
int ret;
leaf = path->nodes[0];
ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
item_offset = (unsigned long)iref - (unsigned long)ei;
type = extent_ref_type(parent, owner);
size = btrfs_extent_inline_ref_size(type);
ret = btrfs_extend_item(trans, root, path, size);
BUG_ON(ret);
ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
refs = btrfs_extent_refs(leaf, ei);
refs += refs_to_add;
btrfs_set_extent_refs(leaf, ei, refs);
if (extent_op)
__run_delayed_extent_op(extent_op, leaf, ei);
ptr = (unsigned long)ei + item_offset;
end = (unsigned long)ei + btrfs_item_size_nr(leaf, path->slots[0]);
if (ptr < end - size)
memmove_extent_buffer(leaf, ptr + size, ptr,
end - size - ptr);
iref = (struct btrfs_extent_inline_ref *)ptr;
btrfs_set_extent_inline_ref_type(leaf, iref, type);
if (type == BTRFS_EXTENT_DATA_REF_KEY) {
struct btrfs_extent_data_ref *dref;
dref = (struct btrfs_extent_data_ref *)(&iref->offset);
btrfs_set_extent_data_ref_root(leaf, dref, root_objectid);
btrfs_set_extent_data_ref_objectid(leaf, dref, owner);
btrfs_set_extent_data_ref_offset(leaf, dref, offset);
btrfs_set_extent_data_ref_count(leaf, dref, refs_to_add);
} else if (type == BTRFS_SHARED_DATA_REF_KEY) {
struct btrfs_shared_data_ref *sref;
sref = (struct btrfs_shared_data_ref *)(iref + 1);
btrfs_set_shared_data_ref_count(leaf, sref, refs_to_add);
btrfs_set_extent_inline_ref_offset(leaf, iref, parent);
} else if (type == BTRFS_SHARED_BLOCK_REF_KEY) {
btrfs_set_extent_inline_ref_offset(leaf, iref, parent);
} else {
btrfs_set_extent_inline_ref_offset(leaf, iref, root_objectid);
}
btrfs_mark_buffer_dirty(leaf);
return 0;
}
static int lookup_extent_backref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
struct btrfs_extent_inline_ref **ref_ret,
u64 bytenr, u64 num_bytes, u64 parent,
u64 root_objectid, u64 owner, u64 offset)
{
int ret;
ret = lookup_inline_extent_backref(trans, root, path, ref_ret,
bytenr, num_bytes, parent,
root_objectid, owner, offset, 0);
if (ret != -ENOENT)
return ret; return ret;
btrfs_release_path(root, path);
*ref_ret = NULL;
if (owner < BTRFS_FIRST_FREE_OBJECTID) {
ret = lookup_tree_block_ref(trans, root, path, bytenr, parent,
root_objectid);
} else {
ret = lookup_extent_data_ref(trans, root, path, bytenr, parent,
root_objectid, owner, offset);
} }
return ret;
}
if (ret > 0) { /*
WARN_ON(1); * helper to update/remove inline back ref
btrfs_free_path(path); */
return -EIO; static noinline_for_stack
int update_inline_extent_backref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
struct btrfs_extent_inline_ref *iref,
int refs_to_mod,
struct btrfs_delayed_extent_op *extent_op)
{
struct extent_buffer *leaf;
struct btrfs_extent_item *ei;
struct btrfs_extent_data_ref *dref = NULL;
struct btrfs_shared_data_ref *sref = NULL;
unsigned long ptr;
unsigned long end;
u32 item_size;
int size;
int type;
int ret;
u64 refs;
leaf = path->nodes[0];
ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
refs = btrfs_extent_refs(leaf, ei);
WARN_ON(refs_to_mod < 0 && refs + refs_to_mod <= 0);
refs += refs_to_mod;
btrfs_set_extent_refs(leaf, ei, refs);
if (extent_op)
__run_delayed_extent_op(extent_op, leaf, ei);
type = btrfs_extent_inline_ref_type(leaf, iref);
if (type == BTRFS_EXTENT_DATA_REF_KEY) {
dref = (struct btrfs_extent_data_ref *)(&iref->offset);
refs = btrfs_extent_data_ref_count(leaf, dref);
} else if (type == BTRFS_SHARED_DATA_REF_KEY) {
sref = (struct btrfs_shared_data_ref *)(iref + 1);
refs = btrfs_shared_data_ref_count(leaf, sref);
} else {
refs = 1;
BUG_ON(refs_to_mod != -1);
} }
l = path->nodes[0];
btrfs_item_key_to_cpu(l, &key, path->slots[0]); BUG_ON(refs_to_mod < 0 && refs < -refs_to_mod);
if (key.objectid != bytenr) { refs += refs_to_mod;
btrfs_print_leaf(root->fs_info->extent_root, path->nodes[0]);
printk(KERN_ERR "btrfs wanted %llu found %llu\n", if (refs > 0) {
(unsigned long long)bytenr, if (type == BTRFS_EXTENT_DATA_REF_KEY)
(unsigned long long)key.objectid); btrfs_set_extent_data_ref_count(leaf, dref, refs);
BUG(); else
btrfs_set_shared_data_ref_count(leaf, sref, refs);
} else {
size = btrfs_extent_inline_ref_size(type);
item_size = btrfs_item_size_nr(leaf, path->slots[0]);
ptr = (unsigned long)iref;
end = (unsigned long)ei + item_size;
if (ptr + size < end)
memmove_extent_buffer(leaf, ptr, ptr + size,
end - ptr - size);
item_size -= size;
ret = btrfs_truncate_item(trans, root, path, item_size, 1);
BUG_ON(ret);
} }
BUG_ON(key.type != BTRFS_EXTENT_ITEM_KEY); btrfs_mark_buffer_dirty(leaf);
return 0;
}
item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item); static noinline_for_stack
int insert_inline_extent_backref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
u64 bytenr, u64 num_bytes, u64 parent,
u64 root_objectid, u64 owner,
u64 offset, int refs_to_add,
struct btrfs_delayed_extent_op *extent_op)
{
struct btrfs_extent_inline_ref *iref;
int ret;
refs = btrfs_extent_refs(l, item); ret = lookup_inline_extent_backref(trans, root, path, &iref,
btrfs_set_extent_refs(l, item, refs + refs_to_add); bytenr, num_bytes, parent,
btrfs_unlock_up_safe(path, 1); root_objectid, owner, offset, 1);
if (ret == 0) {
BUG_ON(owner < BTRFS_FIRST_FREE_OBJECTID);
ret = update_inline_extent_backref(trans, root, path, iref,
refs_to_add, extent_op);
} else if (ret == -ENOENT) {
ret = setup_inline_extent_backref(trans, root, path, iref,
parent, root_objectid,
owner, offset, refs_to_add,
extent_op);
}
return ret;
}
btrfs_mark_buffer_dirty(path->nodes[0]); static int insert_extent_backref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
u64 bytenr, u64 parent, u64 root_objectid,
u64 owner, u64 offset, int refs_to_add)
{
int ret;
if (owner < BTRFS_FIRST_FREE_OBJECTID) {
BUG_ON(refs_to_add != 1);
ret = insert_tree_block_ref(trans, root, path, bytenr,
parent, root_objectid);
} else {
ret = insert_extent_data_ref(trans, root, path, bytenr,
parent, root_objectid,
owner, offset, refs_to_add);
}
return ret;
}
static int remove_extent_backref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
struct btrfs_extent_inline_ref *iref,
int refs_to_drop, int is_data)
{
int ret;
BUG_ON(!is_data && refs_to_drop != 1);
if (iref) {
ret = update_inline_extent_backref(trans, root, path, iref,
-refs_to_drop, NULL);
} else if (is_data) {
ret = remove_extent_data_ref(trans, root, path, refs_to_drop);
} else {
ret = btrfs_del_item(trans, root, path);
}
return ret;
}
#ifdef BIO_RW_DISCARD
static void btrfs_issue_discard(struct block_device *bdev,
u64 start, u64 len)
{
blkdev_issue_discard(bdev, start >> 9, len >> 9, GFP_KERNEL);
}
#endif
static int btrfs_discard_extent(struct btrfs_root *root, u64 bytenr,
u64 num_bytes)
{
#ifdef BIO_RW_DISCARD
int ret;
u64 map_length = num_bytes;
struct btrfs_multi_bio *multi = NULL;
/* Tell the block device(s) that the sectors can be discarded */
ret = btrfs_map_block(&root->fs_info->mapping_tree, READ,
bytenr, &map_length, &multi, 0);
if (!ret) {
struct btrfs_bio_stripe *stripe = multi->stripes;
int i;
if (map_length > num_bytes)
map_length = num_bytes;
for (i = 0; i < multi->num_stripes; i++, stripe++) {
btrfs_issue_discard(stripe->dev->bdev,
stripe->physical,
map_length);
}
kfree(multi);
}
return ret;
#else
return 0;
#endif
}
int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 bytenr, u64 num_bytes, u64 parent,
u64 root_objectid, u64 owner, u64 offset)
{
int ret;
BUG_ON(owner < BTRFS_FIRST_FREE_OBJECTID &&
root_objectid == BTRFS_TREE_LOG_OBJECTID);
if (owner < BTRFS_FIRST_FREE_OBJECTID) {
ret = btrfs_add_delayed_tree_ref(trans, bytenr, num_bytes,
parent, root_objectid, (int)owner,
BTRFS_ADD_DELAYED_REF, NULL);
} else {
ret = btrfs_add_delayed_data_ref(trans, bytenr, num_bytes,
parent, root_objectid, owner, offset,
BTRFS_ADD_DELAYED_REF, NULL);
}
return ret;
}
static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 bytenr, u64 num_bytes,
u64 parent, u64 root_objectid,
u64 owner, u64 offset, int refs_to_add,
struct btrfs_delayed_extent_op *extent_op)
{
struct btrfs_path *path;
struct extent_buffer *leaf;
struct btrfs_extent_item *item;
u64 refs;
int ret;
int err = 0;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
path->reada = 1;
path->leave_spinning = 1;
/* this will setup the path even if it fails to insert the back ref */
ret = insert_inline_extent_backref(trans, root->fs_info->extent_root,
path, bytenr, num_bytes, parent,
root_objectid, owner, offset,
refs_to_add, extent_op);
if (ret == 0)
goto out;
if (ret != -EAGAIN) {
err = ret;
goto out;
}
leaf = path->nodes[0];
item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
refs = btrfs_extent_refs(leaf, item);
btrfs_set_extent_refs(leaf, item, refs + refs_to_add);
if (extent_op)
__run_delayed_extent_op(extent_op, leaf, item);
btrfs_mark_buffer_dirty(leaf);
btrfs_release_path(root->fs_info->extent_root, path); btrfs_release_path(root->fs_info->extent_root, path);
path->reada = 1; path->reada = 1;
...@@ -802,56 +1514,197 @@ static noinline_for_stack int add_extent_ref(struct btrfs_trans_handle *trans, ...@@ -802,56 +1514,197 @@ static noinline_for_stack int add_extent_ref(struct btrfs_trans_handle *trans,
/* now insert the actual backref */ /* now insert the actual backref */
ret = insert_extent_backref(trans, root->fs_info->extent_root, ret = insert_extent_backref(trans, root->fs_info->extent_root,
path, bytenr, parent, path, bytenr, parent, root_objectid,
ref_root, ref_generation, owner, offset, refs_to_add);
owner_objectid, refs_to_add);
BUG_ON(ret); BUG_ON(ret);
out:
btrfs_free_path(path); btrfs_free_path(path);
return 0; return err;
} }
int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, static int run_delayed_data_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
u64 bytenr, u64 num_bytes, u64 parent, struct btrfs_delayed_ref_node *node,
u64 ref_root, u64 ref_generation, struct btrfs_delayed_extent_op *extent_op,
u64 owner_objectid) int insert_reserved)
{ {
int ret = 0;
struct btrfs_delayed_data_ref *ref;
struct btrfs_key ins;
u64 parent = 0;
u64 ref_root = 0;
u64 flags = 0;
ins.objectid = node->bytenr;
ins.offset = node->num_bytes;
ins.type = BTRFS_EXTENT_ITEM_KEY;
ref = btrfs_delayed_node_to_data_ref(node);
if (node->type == BTRFS_SHARED_DATA_REF_KEY)
parent = ref->parent;
else
ref_root = ref->root;
if (node->action == BTRFS_ADD_DELAYED_REF && insert_reserved) {
if (extent_op) {
BUG_ON(extent_op->update_key);
flags |= extent_op->flags_to_set;
}
ret = alloc_reserved_file_extent(trans, root,
parent, ref_root, flags,
ref->objectid, ref->offset,
&ins, node->ref_mod);
update_reserved_extents(root, ins.objectid, ins.offset, 0);
} else if (node->action == BTRFS_ADD_DELAYED_REF) {
ret = __btrfs_inc_extent_ref(trans, root, node->bytenr,
node->num_bytes, parent,
ref_root, ref->objectid,
ref->offset, node->ref_mod,
extent_op);
} else if (node->action == BTRFS_DROP_DELAYED_REF) {
ret = __btrfs_free_extent(trans, root, node->bytenr,
node->num_bytes, parent,
ref_root, ref->objectid,
ref->offset, node->ref_mod,
extent_op);
} else {
BUG();
}
return ret;
}
static void __run_delayed_extent_op(struct btrfs_delayed_extent_op *extent_op,
struct extent_buffer *leaf,
struct btrfs_extent_item *ei)
{
u64 flags = btrfs_extent_flags(leaf, ei);
if (extent_op->update_flags) {
flags |= extent_op->flags_to_set;
btrfs_set_extent_flags(leaf, ei, flags);
}
if (extent_op->update_key) {
struct btrfs_tree_block_info *bi;
BUG_ON(!(flags & BTRFS_EXTENT_FLAG_TREE_BLOCK));
bi = (struct btrfs_tree_block_info *)(ei + 1);
btrfs_set_tree_block_key(leaf, bi, &extent_op->key);
}
}
static int run_delayed_extent_op(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_delayed_ref_node *node,
struct btrfs_delayed_extent_op *extent_op)
{
struct btrfs_key key;
struct btrfs_path *path;
struct btrfs_extent_item *ei;
struct extent_buffer *leaf;
u32 item_size;
int ret; int ret;
if (ref_root == BTRFS_TREE_LOG_OBJECTID && int err = 0;
owner_objectid < BTRFS_FIRST_FREE_OBJECTID)
return 0; path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
key.objectid = node->bytenr;
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = node->num_bytes;
path->reada = 1;
path->leave_spinning = 1;
ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key,
path, 0, 1);
if (ret < 0) {
err = ret;
goto out;
}
if (ret > 0) {
err = -EIO;
goto out;
}
leaf = path->nodes[0];
item_size = btrfs_item_size_nr(leaf, path->slots[0]);
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
if (item_size < sizeof(*ei)) {
ret = convert_extent_item_v0(trans, root->fs_info->extent_root,
path, (u64)-1, 0);
if (ret < 0) {
err = ret;
goto out;
}
leaf = path->nodes[0];
item_size = btrfs_item_size_nr(leaf, path->slots[0]);
}
#endif
BUG_ON(item_size < sizeof(*ei));
ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
__run_delayed_extent_op(extent_op, leaf, ei);
ret = __btrfs_inc_extent_ref(trans, root, bytenr, num_bytes, 0, parent, btrfs_mark_buffer_dirty(leaf);
0, ref_root, 0, ref_generation, out:
owner_objectid); btrfs_free_path(path);
return ret; return err;
} }
static int drop_delayed_ref(struct btrfs_trans_handle *trans, static int run_delayed_tree_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct btrfs_delayed_ref_node *node) struct btrfs_delayed_ref_node *node,
struct btrfs_delayed_extent_op *extent_op,
int insert_reserved)
{ {
int ret = 0; int ret = 0;
struct btrfs_delayed_ref *ref = btrfs_delayed_node_to_ref(node); struct btrfs_delayed_tree_ref *ref;
struct btrfs_key ins;
u64 parent = 0;
u64 ref_root = 0;
BUG_ON(node->ref_mod == 0); ins.objectid = node->bytenr;
ret = __btrfs_free_extent(trans, root, node->bytenr, node->num_bytes, ins.offset = node->num_bytes;
node->parent, ref->root, ref->generation, ins.type = BTRFS_EXTENT_ITEM_KEY;
ref->owner_objectid, ref->pin, node->ref_mod);
ref = btrfs_delayed_node_to_tree_ref(node);
if (node->type == BTRFS_SHARED_BLOCK_REF_KEY)
parent = ref->parent;
else
ref_root = ref->root;
BUG_ON(node->ref_mod != 1);
if (node->action == BTRFS_ADD_DELAYED_REF && insert_reserved) {
BUG_ON(!extent_op || !extent_op->update_flags ||
!extent_op->update_key);
ret = alloc_reserved_tree_block(trans, root,
parent, ref_root,
extent_op->flags_to_set,
&extent_op->key,
ref->level, &ins);
update_reserved_extents(root, ins.objectid, ins.offset, 0);
} else if (node->action == BTRFS_ADD_DELAYED_REF) {
ret = __btrfs_inc_extent_ref(trans, root, node->bytenr,
node->num_bytes, parent, ref_root,
ref->level, 0, 1, extent_op);
} else if (node->action == BTRFS_DROP_DELAYED_REF) {
ret = __btrfs_free_extent(trans, root, node->bytenr,
node->num_bytes, parent, ref_root,
ref->level, 0, 1, extent_op);
} else {
BUG();
}
return ret; return ret;
} }
/* helper function to actually process a single delayed ref entry */ /* helper function to actually process a single delayed ref entry */
static noinline int run_one_delayed_ref(struct btrfs_trans_handle *trans, static int run_one_delayed_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct btrfs_delayed_ref_node *node, struct btrfs_delayed_ref_node *node,
int insert_reserved) struct btrfs_delayed_extent_op *extent_op,
int insert_reserved)
{ {
int ret; int ret;
struct btrfs_delayed_ref *ref; if (btrfs_delayed_ref_is_head(node)) {
if (node->parent == (u64)-1) {
struct btrfs_delayed_ref_head *head; struct btrfs_delayed_ref_head *head;
/* /*
* we've hit the end of the chain and we were supposed * we've hit the end of the chain and we were supposed
...@@ -859,44 +1712,35 @@ static noinline int run_one_delayed_ref(struct btrfs_trans_handle *trans, ...@@ -859,44 +1712,35 @@ static noinline int run_one_delayed_ref(struct btrfs_trans_handle *trans,
* deleted before we ever needed to insert it, so all * deleted before we ever needed to insert it, so all
* we have to do is clean up the accounting * we have to do is clean up the accounting
*/ */
BUG_ON(extent_op);
head = btrfs_delayed_node_to_head(node);
if (insert_reserved) { if (insert_reserved) {
if (head->is_data) {
ret = btrfs_del_csums(trans, root,
node->bytenr,
node->num_bytes);
BUG_ON(ret);
}
btrfs_update_pinned_extents(root, node->bytenr,
node->num_bytes, 1);
update_reserved_extents(root, node->bytenr, update_reserved_extents(root, node->bytenr,
node->num_bytes, 0); node->num_bytes, 0);
} }
head = btrfs_delayed_node_to_head(node);
mutex_unlock(&head->mutex); mutex_unlock(&head->mutex);
return 0; return 0;
} }
ref = btrfs_delayed_node_to_ref(node); if (node->type == BTRFS_TREE_BLOCK_REF_KEY ||
if (ref->action == BTRFS_ADD_DELAYED_REF) { node->type == BTRFS_SHARED_BLOCK_REF_KEY)
if (insert_reserved) { ret = run_delayed_tree_ref(trans, root, node, extent_op,
struct btrfs_key ins; insert_reserved);
else if (node->type == BTRFS_EXTENT_DATA_REF_KEY ||
ins.objectid = node->bytenr; node->type == BTRFS_SHARED_DATA_REF_KEY)
ins.offset = node->num_bytes; ret = run_delayed_data_ref(trans, root, node, extent_op,
ins.type = BTRFS_EXTENT_ITEM_KEY; insert_reserved);
else
/* record the full extent allocation */ BUG();
ret = __btrfs_alloc_reserved_extent(trans, root, return ret;
node->parent, ref->root,
ref->generation, ref->owner_objectid,
&ins, node->ref_mod);
update_reserved_extents(root, node->bytenr,
node->num_bytes, 0);
} else {
/* just add one backref */
ret = add_extent_ref(trans, root, node->bytenr,
node->num_bytes,
node->parent, ref->root, ref->generation,
ref->owner_objectid, node->ref_mod);
}
BUG_ON(ret);
} else if (ref->action == BTRFS_DROP_DELAYED_REF) {
WARN_ON(insert_reserved);
ret = drop_delayed_ref(trans, root, node);
}
return 0;
} }
static noinline struct btrfs_delayed_ref_node * static noinline struct btrfs_delayed_ref_node *
...@@ -919,7 +1763,7 @@ select_delayed_ref(struct btrfs_delayed_ref_head *head) ...@@ -919,7 +1763,7 @@ select_delayed_ref(struct btrfs_delayed_ref_head *head)
rb_node); rb_node);
if (ref->bytenr != head->node.bytenr) if (ref->bytenr != head->node.bytenr)
break; break;
if (btrfs_delayed_node_to_ref(ref)->action == action) if (ref->action == action)
return ref; return ref;
node = rb_prev(node); node = rb_prev(node);
} }
...@@ -937,6 +1781,7 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans, ...@@ -937,6 +1781,7 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans,
struct btrfs_delayed_ref_root *delayed_refs; struct btrfs_delayed_ref_root *delayed_refs;
struct btrfs_delayed_ref_node *ref; struct btrfs_delayed_ref_node *ref;
struct btrfs_delayed_ref_head *locked_ref = NULL; struct btrfs_delayed_ref_head *locked_ref = NULL;
struct btrfs_delayed_extent_op *extent_op;
int ret; int ret;
int count = 0; int count = 0;
int must_insert_reserved = 0; int must_insert_reserved = 0;
...@@ -975,6 +1820,9 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans, ...@@ -975,6 +1820,9 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans,
must_insert_reserved = locked_ref->must_insert_reserved; must_insert_reserved = locked_ref->must_insert_reserved;
locked_ref->must_insert_reserved = 0; locked_ref->must_insert_reserved = 0;
extent_op = locked_ref->extent_op;
locked_ref->extent_op = NULL;
/* /*
* locked_ref is the head node, so we have to go one * locked_ref is the head node, so we have to go one
* node back for any delayed ref updates * node back for any delayed ref updates
...@@ -986,6 +1834,25 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans, ...@@ -986,6 +1834,25 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans,
* so that any accounting fixes can happen * so that any accounting fixes can happen
*/ */
ref = &locked_ref->node; ref = &locked_ref->node;
if (extent_op && must_insert_reserved) {
kfree(extent_op);
extent_op = NULL;
}
if (extent_op) {
spin_unlock(&delayed_refs->lock);
ret = run_delayed_extent_op(trans, root,
ref, extent_op);
BUG_ON(ret);
kfree(extent_op);
cond_resched();
spin_lock(&delayed_refs->lock);
continue;
}
list_del_init(&locked_ref->cluster); list_del_init(&locked_ref->cluster);
locked_ref = NULL; locked_ref = NULL;
} }
...@@ -993,14 +1860,17 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans, ...@@ -993,14 +1860,17 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans,
ref->in_tree = 0; ref->in_tree = 0;
rb_erase(&ref->rb_node, &delayed_refs->root); rb_erase(&ref->rb_node, &delayed_refs->root);
delayed_refs->num_entries--; delayed_refs->num_entries--;
spin_unlock(&delayed_refs->lock); spin_unlock(&delayed_refs->lock);
ret = run_one_delayed_ref(trans, root, ref, ret = run_one_delayed_ref(trans, root, ref, extent_op,
must_insert_reserved); must_insert_reserved);
BUG_ON(ret); BUG_ON(ret);
btrfs_put_delayed_ref(ref);
btrfs_put_delayed_ref(ref);
kfree(extent_op);
count++; count++;
cond_resched(); cond_resched();
spin_lock(&delayed_refs->lock); spin_lock(&delayed_refs->lock);
} }
...@@ -1095,25 +1965,112 @@ int btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, ...@@ -1095,25 +1965,112 @@ int btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,
return 0; return 0;
} }
int btrfs_cross_ref_exist(struct btrfs_trans_handle *trans, int btrfs_set_disk_extent_flags(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 objectid, u64 bytenr) struct btrfs_root *root,
u64 bytenr, u64 num_bytes, u64 flags,
int is_data)
{
struct btrfs_delayed_extent_op *extent_op;
int ret;
extent_op = kmalloc(sizeof(*extent_op), GFP_NOFS);
if (!extent_op)
return -ENOMEM;
extent_op->flags_to_set = flags;
extent_op->update_flags = 1;
extent_op->update_key = 0;
extent_op->is_data = is_data ? 1 : 0;
ret = btrfs_add_delayed_extent_op(trans, bytenr, num_bytes, extent_op);
if (ret)
kfree(extent_op);
return ret;
}
static noinline int check_delayed_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
u64 objectid, u64 offset, u64 bytenr)
{
struct btrfs_delayed_ref_head *head;
struct btrfs_delayed_ref_node *ref;
struct btrfs_delayed_data_ref *data_ref;
struct btrfs_delayed_ref_root *delayed_refs;
struct rb_node *node;
int ret = 0;
ret = -ENOENT;
delayed_refs = &trans->transaction->delayed_refs;
spin_lock(&delayed_refs->lock);
head = btrfs_find_delayed_ref_head(trans, bytenr);
if (!head)
goto out;
if (!mutex_trylock(&head->mutex)) {
atomic_inc(&head->node.refs);
spin_unlock(&delayed_refs->lock);
btrfs_release_path(root->fs_info->extent_root, path);
mutex_lock(&head->mutex);
mutex_unlock(&head->mutex);
btrfs_put_delayed_ref(&head->node);
return -EAGAIN;
}
node = rb_prev(&head->node.rb_node);
if (!node)
goto out_unlock;
ref = rb_entry(node, struct btrfs_delayed_ref_node, rb_node);
if (ref->bytenr != bytenr)
goto out_unlock;
ret = 1;
if (ref->type != BTRFS_EXTENT_DATA_REF_KEY)
goto out_unlock;
data_ref = btrfs_delayed_node_to_data_ref(ref);
node = rb_prev(node);
if (node) {
ref = rb_entry(node, struct btrfs_delayed_ref_node, rb_node);
if (ref->bytenr == bytenr)
goto out_unlock;
}
if (data_ref->root != root->root_key.objectid ||
data_ref->objectid != objectid || data_ref->offset != offset)
goto out_unlock;
ret = 0;
out_unlock:
mutex_unlock(&head->mutex);
out:
spin_unlock(&delayed_refs->lock);
return ret;
}
static noinline int check_committed_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
u64 objectid, u64 offset, u64 bytenr)
{ {
struct btrfs_root *extent_root = root->fs_info->extent_root; struct btrfs_root *extent_root = root->fs_info->extent_root;
struct btrfs_path *path;
struct extent_buffer *leaf; struct extent_buffer *leaf;
struct btrfs_extent_ref *ref_item; struct btrfs_extent_data_ref *ref;
struct btrfs_extent_inline_ref *iref;
struct btrfs_extent_item *ei;
struct btrfs_key key; struct btrfs_key key;
struct btrfs_key found_key; u32 item_size;
u64 ref_root;
u64 last_snapshot;
u32 nritems;
int ret; int ret;
key.objectid = bytenr; key.objectid = bytenr;
key.offset = (u64)-1; key.offset = (u64)-1;
key.type = BTRFS_EXTENT_ITEM_KEY; key.type = BTRFS_EXTENT_ITEM_KEY;
path = btrfs_alloc_path();
ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0); ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0);
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -1125,55 +2082,83 @@ int btrfs_cross_ref_exist(struct btrfs_trans_handle *trans, ...@@ -1125,55 +2082,83 @@ int btrfs_cross_ref_exist(struct btrfs_trans_handle *trans,
path->slots[0]--; path->slots[0]--;
leaf = path->nodes[0]; leaf = path->nodes[0];
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
if (found_key.objectid != bytenr || if (key.objectid != bytenr || key.type != BTRFS_EXTENT_ITEM_KEY)
found_key.type != BTRFS_EXTENT_ITEM_KEY)
goto out; goto out;
last_snapshot = btrfs_root_last_snapshot(&root->root_item); ret = 1;
while (1) { item_size = btrfs_item_size_nr(leaf, path->slots[0]);
leaf = path->nodes[0]; #ifdef BTRFS_COMPAT_EXTENT_TREE_V0
nritems = btrfs_header_nritems(leaf); if (item_size < sizeof(*ei)) {
if (path->slots[0] >= nritems) { WARN_ON(item_size != sizeof(struct btrfs_extent_item_v0));
ret = btrfs_next_leaf(extent_root, path); goto out;
if (ret < 0) }
goto out; #endif
if (ret == 0) ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
continue;
break;
}
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
if (found_key.objectid != bytenr)
break;
if (found_key.type != BTRFS_EXTENT_REF_KEY) { if (item_size != sizeof(*ei) +
path->slots[0]++; btrfs_extent_inline_ref_size(BTRFS_EXTENT_DATA_REF_KEY))
continue; goto out;
}
ref_item = btrfs_item_ptr(leaf, path->slots[0], if (btrfs_extent_generation(leaf, ei) <=
struct btrfs_extent_ref); btrfs_root_last_snapshot(&root->root_item))
ref_root = btrfs_ref_root(leaf, ref_item); goto out;
if ((ref_root != root->root_key.objectid &&
ref_root != BTRFS_TREE_LOG_OBJECTID) || iref = (struct btrfs_extent_inline_ref *)(ei + 1);
objectid != btrfs_ref_objectid(leaf, ref_item)) { if (btrfs_extent_inline_ref_type(leaf, iref) !=
ret = 1; BTRFS_EXTENT_DATA_REF_KEY)
goto out; goto out;
}
if (btrfs_ref_generation(leaf, ref_item) <= last_snapshot) { ref = (struct btrfs_extent_data_ref *)(&iref->offset);
ret = 1; if (btrfs_extent_refs(leaf, ei) !=
btrfs_extent_data_ref_count(leaf, ref) ||
btrfs_extent_data_ref_root(leaf, ref) !=
root->root_key.objectid ||
btrfs_extent_data_ref_objectid(leaf, ref) != objectid ||
btrfs_extent_data_ref_offset(leaf, ref) != offset)
goto out;
ret = 0;
out:
return ret;
}
int btrfs_cross_ref_exist(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 objectid, u64 offset, u64 bytenr)
{
struct btrfs_path *path;
int ret;
int ret2;
path = btrfs_alloc_path();
if (!path)
return -ENOENT;
do {
ret = check_committed_ref(trans, root, path, objectid,
offset, bytenr);
if (ret && ret != -ENOENT)
goto out; goto out;
}
path->slots[0]++; ret2 = check_delayed_ref(trans, root, path, objectid,
offset, bytenr);
} while (ret2 == -EAGAIN);
if (ret2 && ret2 != -ENOENT) {
ret = ret2;
goto out;
} }
ret = 0;
if (ret != -ENOENT || ret2 != -ENOENT)
ret = 0;
out: out:
btrfs_free_path(path); btrfs_free_path(path);
return ret; return ret;
} }
#if 0
int btrfs_cache_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, int btrfs_cache_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct extent_buffer *buf, u32 nr_extents) struct extent_buffer *buf, u32 nr_extents)
{ {
...@@ -1291,191 +2276,49 @@ static int refsort_cmp(const void *a_void, const void *b_void) ...@@ -1291,191 +2276,49 @@ static int refsort_cmp(const void *a_void, const void *b_void)
return 1; return 1;
return 0; return 0;
} }
#endif
static int __btrfs_mod_ref(struct btrfs_trans_handle *trans,
noinline int btrfs_inc_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct extent_buffer *orig_buf, struct extent_buffer *buf,
struct extent_buffer *buf, u32 *nr_extents) int full_backref, int inc)
{ {
u64 bytenr; u64 bytenr;
u64 num_bytes;
u64 parent;
u64 ref_root; u64 ref_root;
u64 orig_root;
u64 ref_generation;
u64 orig_generation;
struct refsort *sorted;
u32 nritems; u32 nritems;
u32 nr_file_extents = 0;
struct btrfs_key key; struct btrfs_key key;
struct btrfs_file_extent_item *fi; struct btrfs_file_extent_item *fi;
int i; int i;
int level; int level;
int ret = 0; int ret = 0;
int faili = 0;
int refi = 0;
int slot;
int (*process_func)(struct btrfs_trans_handle *, struct btrfs_root *, int (*process_func)(struct btrfs_trans_handle *, struct btrfs_root *,
u64, u64, u64, u64, u64, u64, u64, u64, u64); u64, u64, u64, u64, u64, u64);
ref_root = btrfs_header_owner(buf); ref_root = btrfs_header_owner(buf);
ref_generation = btrfs_header_generation(buf);
orig_root = btrfs_header_owner(orig_buf);
orig_generation = btrfs_header_generation(orig_buf);
nritems = btrfs_header_nritems(buf); nritems = btrfs_header_nritems(buf);
level = btrfs_header_level(buf); level = btrfs_header_level(buf);
sorted = kmalloc(sizeof(struct refsort) * nritems, GFP_NOFS); if (!root->ref_cows && level == 0)
BUG_ON(!sorted); return 0;
if (root->ref_cows) {
process_func = __btrfs_inc_extent_ref;
} else {
if (level == 0 &&
root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID)
goto out;
if (level != 0 &&
root->root_key.objectid == BTRFS_TREE_LOG_OBJECTID)
goto out;
process_func = __btrfs_update_extent_ref;
}
/*
* we make two passes through the items. In the first pass we
* only record the byte number and slot. Then we sort based on
* byte number and do the actual work based on the sorted results
*/
for (i = 0; i < nritems; i++) {
cond_resched();
if (level == 0) {
btrfs_item_key_to_cpu(buf, &key, i);
if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY)
continue;
fi = btrfs_item_ptr(buf, i,
struct btrfs_file_extent_item);
if (btrfs_file_extent_type(buf, fi) ==
BTRFS_FILE_EXTENT_INLINE)
continue;
bytenr = btrfs_file_extent_disk_bytenr(buf, fi);
if (bytenr == 0)
continue;
nr_file_extents++;
sorted[refi].bytenr = bytenr;
sorted[refi].slot = i;
refi++;
} else {
bytenr = btrfs_node_blockptr(buf, i);
sorted[refi].bytenr = bytenr;
sorted[refi].slot = i;
refi++;
}
}
/*
* if refi == 0, we didn't actually put anything into the sorted
* array and we're done
*/
if (refi == 0)
goto out;
sort(sorted, refi, sizeof(struct refsort), refsort_cmp, NULL);
for (i = 0; i < refi; i++) {
cond_resched();
slot = sorted[i].slot;
bytenr = sorted[i].bytenr;
if (level == 0) {
btrfs_item_key_to_cpu(buf, &key, slot);
fi = btrfs_item_ptr(buf, slot,
struct btrfs_file_extent_item);
bytenr = btrfs_file_extent_disk_bytenr(buf, fi);
if (bytenr == 0)
continue;
ret = process_func(trans, root, bytenr,
btrfs_file_extent_disk_num_bytes(buf, fi),
orig_buf->start, buf->start,
orig_root, ref_root,
orig_generation, ref_generation,
key.objectid);
if (ret) {
faili = slot;
WARN_ON(1);
goto fail;
}
} else {
ret = process_func(trans, root, bytenr, buf->len,
orig_buf->start, buf->start,
orig_root, ref_root,
orig_generation, ref_generation,
level - 1);
if (ret) {
faili = slot;
WARN_ON(1);
goto fail;
}
}
}
out:
kfree(sorted);
if (nr_extents) {
if (level == 0)
*nr_extents = nr_file_extents;
else
*nr_extents = nritems;
}
return 0;
fail:
kfree(sorted);
WARN_ON(1);
return ret;
}
int btrfs_update_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct extent_buffer *orig_buf,
struct extent_buffer *buf, int start_slot, int nr)
{
u64 bytenr;
u64 ref_root;
u64 orig_root;
u64 ref_generation;
u64 orig_generation;
struct btrfs_key key;
struct btrfs_file_extent_item *fi;
int i;
int ret;
int slot;
int level;
BUG_ON(start_slot < 0);
BUG_ON(start_slot + nr > btrfs_header_nritems(buf));
ref_root = btrfs_header_owner(buf);
ref_generation = btrfs_header_generation(buf);
orig_root = btrfs_header_owner(orig_buf);
orig_generation = btrfs_header_generation(orig_buf);
level = btrfs_header_level(buf);
if (!root->ref_cows) { if (inc)
if (level == 0 && process_func = btrfs_inc_extent_ref;
root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID) else
return 0; process_func = btrfs_free_extent;
if (level != 0 &&
root->root_key.objectid == BTRFS_TREE_LOG_OBJECTID)
return 0;
}
for (i = 0, slot = start_slot; i < nr; i++, slot++) { if (full_backref)
cond_resched(); parent = buf->start;
else
parent = 0;
for (i = 0; i < nritems; i++) {
if (level == 0) { if (level == 0) {
btrfs_item_key_to_cpu(buf, &key, slot); btrfs_item_key_to_cpu(buf, &key, i);
if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY) if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY)
continue; continue;
fi = btrfs_item_ptr(buf, slot, fi = btrfs_item_ptr(buf, i,
struct btrfs_file_extent_item); struct btrfs_file_extent_item);
if (btrfs_file_extent_type(buf, fi) == if (btrfs_file_extent_type(buf, fi) ==
BTRFS_FILE_EXTENT_INLINE) BTRFS_FILE_EXTENT_INLINE)
...@@ -1483,28 +2326,39 @@ int btrfs_update_ref(struct btrfs_trans_handle *trans, ...@@ -1483,28 +2326,39 @@ int btrfs_update_ref(struct btrfs_trans_handle *trans,
bytenr = btrfs_file_extent_disk_bytenr(buf, fi); bytenr = btrfs_file_extent_disk_bytenr(buf, fi);
if (bytenr == 0) if (bytenr == 0)
continue; continue;
ret = __btrfs_update_extent_ref(trans, root, bytenr,
btrfs_file_extent_disk_num_bytes(buf, fi), num_bytes = btrfs_file_extent_disk_num_bytes(buf, fi);
orig_buf->start, buf->start, key.offset -= btrfs_file_extent_offset(buf, fi);
orig_root, ref_root, orig_generation, ret = process_func(trans, root, bytenr, num_bytes,
ref_generation, key.objectid); parent, ref_root, key.objectid,
key.offset);
if (ret) if (ret)
goto fail; goto fail;
} else { } else {
bytenr = btrfs_node_blockptr(buf, slot); bytenr = btrfs_node_blockptr(buf, i);
ret = __btrfs_update_extent_ref(trans, root, bytenr, num_bytes = btrfs_level_size(root, level - 1);
buf->len, orig_buf->start, ret = process_func(trans, root, bytenr, num_bytes,
buf->start, orig_root, ref_root, parent, ref_root, level - 1, 0);
orig_generation, ref_generation,
level - 1);
if (ret) if (ret)
goto fail; goto fail;
} }
} }
return 0; return 0;
fail: fail:
WARN_ON(1); BUG();
return -1; return ret;
}
int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct extent_buffer *buf, int full_backref)
{
return __btrfs_mod_ref(trans, root, buf, full_backref, 1);
}
int btrfs_dec_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct extent_buffer *buf, int full_backref)
{
return __btrfs_mod_ref(trans, root, buf, full_backref, 0);
} }
static int write_one_cache_group(struct btrfs_trans_handle *trans, static int write_one_cache_group(struct btrfs_trans_handle *trans,
...@@ -2007,6 +2861,24 @@ static int update_block_group(struct btrfs_trans_handle *trans, ...@@ -2007,6 +2861,24 @@ static int update_block_group(struct btrfs_trans_handle *trans,
u64 old_val; u64 old_val;
u64 byte_in_group; u64 byte_in_group;
/* block accounting for super block */
spin_lock(&info->delalloc_lock);
old_val = btrfs_super_bytes_used(&info->super_copy);
if (alloc)
old_val += num_bytes;
else
old_val -= num_bytes;
btrfs_set_super_bytes_used(&info->super_copy, old_val);
/* block accounting for root item */
old_val = btrfs_root_used(&root->root_item);
if (alloc)
old_val += num_bytes;
else
old_val -= num_bytes;
btrfs_set_root_used(&root->root_item, old_val);
spin_unlock(&info->delalloc_lock);
while (total) { while (total) {
cache = btrfs_lookup_block_group(info, bytenr); cache = btrfs_lookup_block_group(info, bytenr);
if (!cache) if (!cache)
...@@ -2216,8 +3088,6 @@ static int pin_down_bytes(struct btrfs_trans_handle *trans, ...@@ -2216,8 +3088,6 @@ static int pin_down_bytes(struct btrfs_trans_handle *trans,
u64 header_owner = btrfs_header_owner(buf); u64 header_owner = btrfs_header_owner(buf);
u64 header_transid = btrfs_header_generation(buf); u64 header_transid = btrfs_header_generation(buf);
if (header_owner != BTRFS_TREE_LOG_OBJECTID && if (header_owner != BTRFS_TREE_LOG_OBJECTID &&
header_owner != BTRFS_TREE_RELOC_OBJECTID &&
header_owner != BTRFS_DATA_RELOC_TREE_OBJECTID &&
header_transid == trans->transid && header_transid == trans->transid &&
!btrfs_header_flag(buf, BTRFS_HEADER_FLAG_WRITTEN)) { !btrfs_header_flag(buf, BTRFS_HEADER_FLAG_WRITTEN)) {
*must_clean = buf; *must_clean = buf;
...@@ -2235,63 +3105,77 @@ static int pin_down_bytes(struct btrfs_trans_handle *trans, ...@@ -2235,63 +3105,77 @@ static int pin_down_bytes(struct btrfs_trans_handle *trans,
return 0; return 0;
} }
/*
* remove an extent from the root, returns 0 on success static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
*/ struct btrfs_root *root,
static int __free_extent(struct btrfs_trans_handle *trans, u64 bytenr, u64 num_bytes, u64 parent,
struct btrfs_root *root, u64 root_objectid, u64 owner_objectid,
u64 bytenr, u64 num_bytes, u64 parent, u64 owner_offset, int refs_to_drop,
u64 root_objectid, u64 ref_generation, struct btrfs_delayed_extent_op *extent_op)
u64 owner_objectid, int pin, int mark_free,
int refs_to_drop)
{ {
struct btrfs_path *path;
struct btrfs_key key; struct btrfs_key key;
struct btrfs_path *path;
struct btrfs_fs_info *info = root->fs_info; struct btrfs_fs_info *info = root->fs_info;
struct btrfs_root *extent_root = info->extent_root; struct btrfs_root *extent_root = info->extent_root;
struct extent_buffer *leaf; struct extent_buffer *leaf;
struct btrfs_extent_item *ei;
struct btrfs_extent_inline_ref *iref;
int ret; int ret;
int is_data;
int extent_slot = 0; int extent_slot = 0;
int found_extent = 0; int found_extent = 0;
int num_to_del = 1; int num_to_del = 1;
struct btrfs_extent_item *ei; u32 item_size;
u32 refs; u64 refs;
key.objectid = bytenr;
btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
key.offset = num_bytes;
path = btrfs_alloc_path(); path = btrfs_alloc_path();
if (!path) if (!path)
return -ENOMEM; return -ENOMEM;
path->reada = 1; path->reada = 1;
path->leave_spinning = 1; path->leave_spinning = 1;
ret = lookup_extent_backref(trans, extent_root, path,
bytenr, parent, root_objectid, is_data = owner_objectid >= BTRFS_FIRST_FREE_OBJECTID;
ref_generation, owner_objectid, 1); BUG_ON(!is_data && refs_to_drop != 1);
ret = lookup_extent_backref(trans, extent_root, path, &iref,
bytenr, num_bytes, parent,
root_objectid, owner_objectid,
owner_offset);
if (ret == 0) { if (ret == 0) {
struct btrfs_key found_key;
extent_slot = path->slots[0]; extent_slot = path->slots[0];
while (extent_slot > 0) { while (extent_slot >= 0) {
extent_slot--; btrfs_item_key_to_cpu(path->nodes[0], &key,
btrfs_item_key_to_cpu(path->nodes[0], &found_key,
extent_slot); extent_slot);
if (found_key.objectid != bytenr) if (key.objectid != bytenr)
break; break;
if (found_key.type == BTRFS_EXTENT_ITEM_KEY && if (key.type == BTRFS_EXTENT_ITEM_KEY &&
found_key.offset == num_bytes) { key.offset == num_bytes) {
found_extent = 1; found_extent = 1;
break; break;
} }
if (path->slots[0] - extent_slot > 5) if (path->slots[0] - extent_slot > 5)
break; break;
extent_slot--;
} }
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
item_size = btrfs_item_size_nr(path->nodes[0], extent_slot);
if (found_extent && item_size < sizeof(*ei))
found_extent = 0;
#endif
if (!found_extent) { if (!found_extent) {
BUG_ON(iref);
ret = remove_extent_backref(trans, extent_root, path, ret = remove_extent_backref(trans, extent_root, path,
refs_to_drop); NULL, refs_to_drop,
is_data);
BUG_ON(ret); BUG_ON(ret);
btrfs_release_path(extent_root, path); btrfs_release_path(extent_root, path);
path->leave_spinning = 1; path->leave_spinning = 1;
key.objectid = bytenr;
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = num_bytes;
ret = btrfs_search_slot(trans, extent_root, ret = btrfs_search_slot(trans, extent_root,
&key, path, -1, 1); &key, path, -1, 1);
if (ret) { if (ret) {
...@@ -2307,82 +3191,98 @@ static int __free_extent(struct btrfs_trans_handle *trans, ...@@ -2307,82 +3191,98 @@ static int __free_extent(struct btrfs_trans_handle *trans,
btrfs_print_leaf(extent_root, path->nodes[0]); btrfs_print_leaf(extent_root, path->nodes[0]);
WARN_ON(1); WARN_ON(1);
printk(KERN_ERR "btrfs unable to find ref byte nr %llu " printk(KERN_ERR "btrfs unable to find ref byte nr %llu "
"parent %llu root %llu gen %llu owner %llu\n", "parent %llu root %llu owner %llu offset %llu\n",
(unsigned long long)bytenr, (unsigned long long)bytenr,
(unsigned long long)parent, (unsigned long long)parent,
(unsigned long long)root_objectid, (unsigned long long)root_objectid,
(unsigned long long)ref_generation, (unsigned long long)owner_objectid,
(unsigned long long)owner_objectid); (unsigned long long)owner_offset);
} }
leaf = path->nodes[0]; leaf = path->nodes[0];
item_size = btrfs_item_size_nr(leaf, extent_slot);
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
if (item_size < sizeof(*ei)) {
BUG_ON(found_extent || extent_slot != path->slots[0]);
ret = convert_extent_item_v0(trans, extent_root, path,
owner_objectid, 0);
BUG_ON(ret < 0);
btrfs_release_path(extent_root, path);
path->leave_spinning = 1;
key.objectid = bytenr;
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = num_bytes;
ret = btrfs_search_slot(trans, extent_root, &key, path,
-1, 1);
if (ret) {
printk(KERN_ERR "umm, got %d back from search"
", was looking for %llu\n", ret,
(unsigned long long)bytenr);
btrfs_print_leaf(extent_root, path->nodes[0]);
}
BUG_ON(ret);
extent_slot = path->slots[0];
leaf = path->nodes[0];
item_size = btrfs_item_size_nr(leaf, extent_slot);
}
#endif
BUG_ON(item_size < sizeof(*ei));
ei = btrfs_item_ptr(leaf, extent_slot, ei = btrfs_item_ptr(leaf, extent_slot,
struct btrfs_extent_item); struct btrfs_extent_item);
refs = btrfs_extent_refs(leaf, ei); if (owner_objectid < BTRFS_FIRST_FREE_OBJECTID) {
struct btrfs_tree_block_info *bi;
/* BUG_ON(item_size < sizeof(*ei) + sizeof(*bi));
* we're not allowed to delete the extent item if there bi = (struct btrfs_tree_block_info *)(ei + 1);
* are other delayed ref updates pending WARN_ON(owner_objectid != btrfs_tree_block_level(leaf, bi));
*/ }
refs = btrfs_extent_refs(leaf, ei);
BUG_ON(refs < refs_to_drop); BUG_ON(refs < refs_to_drop);
refs -= refs_to_drop; refs -= refs_to_drop;
btrfs_set_extent_refs(leaf, ei, refs);
btrfs_mark_buffer_dirty(leaf);
if (refs == 0 && found_extent && if (refs > 0) {
path->slots[0] == extent_slot + 1) { if (extent_op)
struct btrfs_extent_ref *ref; __run_delayed_extent_op(extent_op, leaf, ei);
ref = btrfs_item_ptr(leaf, path->slots[0], /*
struct btrfs_extent_ref); * In the case of inline back ref, reference count will
BUG_ON(btrfs_ref_num_refs(leaf, ref) != refs_to_drop); * be updated by remove_extent_backref
/* if the back ref and the extent are next to each other
* they get deleted below in one shot
*/ */
path->slots[0] = extent_slot; if (iref) {
num_to_del = 2; BUG_ON(!found_extent);
} else if (found_extent) { } else {
/* otherwise delete the extent back ref */ btrfs_set_extent_refs(leaf, ei, refs);
ret = remove_extent_backref(trans, extent_root, path, btrfs_mark_buffer_dirty(leaf);
refs_to_drop); }
BUG_ON(ret); if (found_extent) {
/* if refs are 0, we need to setup the path for deletion */ ret = remove_extent_backref(trans, extent_root, path,
if (refs == 0) { iref, refs_to_drop,
btrfs_release_path(extent_root, path); is_data);
path->leave_spinning = 1;
ret = btrfs_search_slot(trans, extent_root, &key, path,
-1, 1);
BUG_ON(ret); BUG_ON(ret);
} }
} } else {
int mark_free = 0;
if (refs == 0) {
u64 super_used;
u64 root_used;
struct extent_buffer *must_clean = NULL; struct extent_buffer *must_clean = NULL;
if (pin) { if (found_extent) {
ret = pin_down_bytes(trans, root, path, BUG_ON(is_data && refs_to_drop !=
bytenr, num_bytes, extent_data_ref_count(root, path, iref));
owner_objectid >= BTRFS_FIRST_FREE_OBJECTID, if (iref) {
&must_clean); BUG_ON(path->slots[0] != extent_slot);
if (ret > 0) } else {
mark_free = 1; BUG_ON(path->slots[0] != extent_slot + 1);
BUG_ON(ret < 0); path->slots[0] = extent_slot;
num_to_del = 2;
}
} }
/* block accounting for super block */ ret = pin_down_bytes(trans, root, path, bytenr,
spin_lock(&info->delalloc_lock); num_bytes, is_data, &must_clean);
super_used = btrfs_super_bytes_used(&info->super_copy); if (ret > 0)
btrfs_set_super_bytes_used(&info->super_copy, mark_free = 1;
super_used - num_bytes); BUG_ON(ret < 0);
/* block accounting for root item */
root_used = btrfs_root_used(&root->root_item);
btrfs_set_root_used(&root->root_item,
root_used - num_bytes);
spin_unlock(&info->delalloc_lock);
/* /*
* it is going to be very rare for someone to be waiting * it is going to be very rare for someone to be waiting
* on the block we're freeing. del_items might need to * on the block we're freeing. del_items might need to
...@@ -2403,7 +3303,7 @@ static int __free_extent(struct btrfs_trans_handle *trans, ...@@ -2403,7 +3303,7 @@ static int __free_extent(struct btrfs_trans_handle *trans,
free_extent_buffer(must_clean); free_extent_buffer(must_clean);
} }
if (owner_objectid >= BTRFS_FIRST_FREE_OBJECTID) { if (is_data) {
ret = btrfs_del_csums(trans, root, bytenr, num_bytes); ret = btrfs_del_csums(trans, root, bytenr, num_bytes);
BUG_ON(ret); BUG_ON(ret);
} else { } else {
...@@ -2420,34 +3320,6 @@ static int __free_extent(struct btrfs_trans_handle *trans, ...@@ -2420,34 +3320,6 @@ static int __free_extent(struct btrfs_trans_handle *trans,
return ret; return ret;
} }
/*
* remove an extent from the root, returns 0 on success
*/
static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 bytenr, u64 num_bytes, u64 parent,
u64 root_objectid, u64 ref_generation,
u64 owner_objectid, int pin,
int refs_to_drop)
{
WARN_ON(num_bytes < root->sectorsize);
/*
* if metadata always pin
* if data pin when any transaction has committed this
*/
if (owner_objectid < BTRFS_FIRST_FREE_OBJECTID ||
ref_generation != trans->transid)
pin = 1;
if (ref_generation != trans->transid)
pin = 1;
return __free_extent(trans, root, bytenr, num_bytes, parent,
root_objectid, ref_generation,
owner_objectid, pin, pin == 0, refs_to_drop);
}
/* /*
* when we free an extent, it is possible (and likely) that we free the last * when we free an extent, it is possible (and likely) that we free the last
* delayed ref for that extent as well. This searches the delayed ref tree for * delayed ref for that extent as well. This searches the delayed ref tree for
...@@ -2479,6 +3351,13 @@ static noinline int check_ref_cleanup(struct btrfs_trans_handle *trans, ...@@ -2479,6 +3351,13 @@ static noinline int check_ref_cleanup(struct btrfs_trans_handle *trans,
if (ref->bytenr == bytenr) if (ref->bytenr == bytenr)
goto out; goto out;
if (head->extent_op) {
if (!head->must_insert_reserved)
goto out;
kfree(head->extent_op);
head->extent_op = NULL;
}
/* /*
* waiting for the lock here would deadlock. If someone else has it * waiting for the lock here would deadlock. If someone else has it
* locked they are already in the process of dropping it anyway * locked they are already in the process of dropping it anyway
...@@ -2507,7 +3386,8 @@ static noinline int check_ref_cleanup(struct btrfs_trans_handle *trans, ...@@ -2507,7 +3386,8 @@ static noinline int check_ref_cleanup(struct btrfs_trans_handle *trans,
spin_unlock(&delayed_refs->lock); spin_unlock(&delayed_refs->lock);
ret = run_one_delayed_ref(trans, root->fs_info->tree_root, ret = run_one_delayed_ref(trans, root->fs_info->tree_root,
&head->node, head->must_insert_reserved); &head->node, head->extent_op,
head->must_insert_reserved);
BUG_ON(ret); BUG_ON(ret);
btrfs_put_delayed_ref(&head->node); btrfs_put_delayed_ref(&head->node);
return 0; return 0;
...@@ -2519,32 +3399,32 @@ static noinline int check_ref_cleanup(struct btrfs_trans_handle *trans, ...@@ -2519,32 +3399,32 @@ static noinline int check_ref_cleanup(struct btrfs_trans_handle *trans,
int btrfs_free_extent(struct btrfs_trans_handle *trans, int btrfs_free_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
u64 bytenr, u64 num_bytes, u64 parent, u64 bytenr, u64 num_bytes, u64 parent,
u64 root_objectid, u64 ref_generation, u64 root_objectid, u64 owner, u64 offset)
u64 owner_objectid, int pin)
{ {
int ret; int ret;
/* /*
* tree log blocks never actually go into the extent allocation * tree log blocks never actually go into the extent allocation
* tree, just update pinning info and exit early. * tree, just update pinning info and exit early.
*
* data extents referenced by the tree log do need to have
* their reference counts bumped.
*/ */
if (root->root_key.objectid == BTRFS_TREE_LOG_OBJECTID && if (root_objectid == BTRFS_TREE_LOG_OBJECTID) {
owner_objectid < BTRFS_FIRST_FREE_OBJECTID) { WARN_ON(owner >= BTRFS_FIRST_FREE_OBJECTID);
/* unlocks the pinned mutex */ /* unlocks the pinned mutex */
btrfs_update_pinned_extents(root, bytenr, num_bytes, 1); btrfs_update_pinned_extents(root, bytenr, num_bytes, 1);
update_reserved_extents(root, bytenr, num_bytes, 0); update_reserved_extents(root, bytenr, num_bytes, 0);
ret = 0; ret = 0;
} else { } else if (owner < BTRFS_FIRST_FREE_OBJECTID) {
ret = btrfs_add_delayed_ref(trans, bytenr, num_bytes, parent, ret = btrfs_add_delayed_tree_ref(trans, bytenr, num_bytes,
root_objectid, ref_generation, parent, root_objectid, (int)owner,
owner_objectid, BTRFS_DROP_DELAYED_REF, NULL);
BTRFS_DROP_DELAYED_REF, 1);
BUG_ON(ret); BUG_ON(ret);
ret = check_ref_cleanup(trans, root, bytenr); ret = check_ref_cleanup(trans, root, bytenr);
BUG_ON(ret); BUG_ON(ret);
} else {
ret = btrfs_add_delayed_data_ref(trans, bytenr, num_bytes,
parent, root_objectid, owner,
offset, BTRFS_DROP_DELAYED_REF, NULL);
BUG_ON(ret);
} }
return ret; return ret;
} }
...@@ -2719,7 +3599,7 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans, ...@@ -2719,7 +3599,7 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans,
last_ptr_loop = 0; last_ptr_loop = 0;
/* allocate a cluster in this block group */ /* allocate a cluster in this block group */
ret = btrfs_find_space_cluster(trans, ret = btrfs_find_space_cluster(trans, root,
block_group, last_ptr, block_group, last_ptr,
offset, num_bytes, offset, num_bytes,
empty_cluster + empty_size); empty_cluster + empty_size);
...@@ -2969,99 +3849,147 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans, ...@@ -2969,99 +3849,147 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
return ret; return ret;
} }
static int __btrfs_alloc_reserved_extent(struct btrfs_trans_handle *trans, static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 parent, struct btrfs_root *root,
u64 root_objectid, u64 ref_generation, u64 parent, u64 root_objectid,
u64 owner, struct btrfs_key *ins, u64 flags, u64 owner, u64 offset,
int ref_mod) struct btrfs_key *ins, int ref_mod)
{ {
int ret; int ret;
u64 super_used; struct btrfs_fs_info *fs_info = root->fs_info;
u64 root_used;
u64 num_bytes = ins->offset;
u32 sizes[2];
struct btrfs_fs_info *info = root->fs_info;
struct btrfs_root *extent_root = info->extent_root;
struct btrfs_extent_item *extent_item; struct btrfs_extent_item *extent_item;
struct btrfs_extent_ref *ref; struct btrfs_extent_inline_ref *iref;
struct btrfs_path *path; struct btrfs_path *path;
struct btrfs_key keys[2]; struct extent_buffer *leaf;
int type;
if (parent == 0) u32 size;
parent = ins->objectid;
/* block accounting for super block */
spin_lock(&info->delalloc_lock);
super_used = btrfs_super_bytes_used(&info->super_copy);
btrfs_set_super_bytes_used(&info->super_copy, super_used + num_bytes);
/* block accounting for root item */ if (parent > 0)
root_used = btrfs_root_used(&root->root_item); type = BTRFS_SHARED_DATA_REF_KEY;
btrfs_set_root_used(&root->root_item, root_used + num_bytes); else
spin_unlock(&info->delalloc_lock); type = BTRFS_EXTENT_DATA_REF_KEY;
memcpy(&keys[0], ins, sizeof(*ins)); size = sizeof(*extent_item) + btrfs_extent_inline_ref_size(type);
keys[1].objectid = ins->objectid;
keys[1].type = BTRFS_EXTENT_REF_KEY;
keys[1].offset = parent;
sizes[0] = sizeof(*extent_item);
sizes[1] = sizeof(*ref);
path = btrfs_alloc_path(); path = btrfs_alloc_path();
BUG_ON(!path); BUG_ON(!path);
path->leave_spinning = 1; path->leave_spinning = 1;
ret = btrfs_insert_empty_items(trans, extent_root, path, keys, ret = btrfs_insert_empty_item(trans, fs_info->extent_root, path,
sizes, 2); ins, size);
BUG_ON(ret); BUG_ON(ret);
extent_item = btrfs_item_ptr(path->nodes[0], path->slots[0], leaf = path->nodes[0];
extent_item = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_extent_item); struct btrfs_extent_item);
btrfs_set_extent_refs(path->nodes[0], extent_item, ref_mod); btrfs_set_extent_refs(leaf, extent_item, ref_mod);
ref = btrfs_item_ptr(path->nodes[0], path->slots[0] + 1, btrfs_set_extent_generation(leaf, extent_item, trans->transid);
struct btrfs_extent_ref); btrfs_set_extent_flags(leaf, extent_item,
flags | BTRFS_EXTENT_FLAG_DATA);
btrfs_set_ref_root(path->nodes[0], ref, root_objectid);
btrfs_set_ref_generation(path->nodes[0], ref, ref_generation); iref = (struct btrfs_extent_inline_ref *)(extent_item + 1);
btrfs_set_ref_objectid(path->nodes[0], ref, owner); btrfs_set_extent_inline_ref_type(leaf, iref, type);
btrfs_set_ref_num_refs(path->nodes[0], ref, ref_mod); if (parent > 0) {
struct btrfs_shared_data_ref *ref;
ref = (struct btrfs_shared_data_ref *)(iref + 1);
btrfs_set_extent_inline_ref_offset(leaf, iref, parent);
btrfs_set_shared_data_ref_count(leaf, ref, ref_mod);
} else {
struct btrfs_extent_data_ref *ref;
ref = (struct btrfs_extent_data_ref *)(&iref->offset);
btrfs_set_extent_data_ref_root(leaf, ref, root_objectid);
btrfs_set_extent_data_ref_objectid(leaf, ref, owner);
btrfs_set_extent_data_ref_offset(leaf, ref, offset);
btrfs_set_extent_data_ref_count(leaf, ref, ref_mod);
}
btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_mark_buffer_dirty(path->nodes[0]);
trans->alloc_exclude_start = 0;
trans->alloc_exclude_nr = 0;
btrfs_free_path(path); btrfs_free_path(path);
if (ret) ret = update_block_group(trans, root, ins->objectid, ins->offset,
goto out; 1, 0);
ret = update_block_group(trans, root, ins->objectid,
ins->offset, 1, 0);
if (ret) { if (ret) {
printk(KERN_ERR "btrfs update block group failed for %llu " printk(KERN_ERR "btrfs update block group failed for %llu "
"%llu\n", (unsigned long long)ins->objectid, "%llu\n", (unsigned long long)ins->objectid,
(unsigned long long)ins->offset); (unsigned long long)ins->offset);
BUG(); BUG();
} }
out:
return ret; return ret;
} }
int btrfs_alloc_reserved_extent(struct btrfs_trans_handle *trans, static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 parent, struct btrfs_root *root,
u64 root_objectid, u64 ref_generation, u64 parent, u64 root_objectid,
u64 owner, struct btrfs_key *ins) u64 flags, struct btrfs_disk_key *key,
int level, struct btrfs_key *ins)
{ {
int ret; int ret;
struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_extent_item *extent_item;
struct btrfs_tree_block_info *block_info;
struct btrfs_extent_inline_ref *iref;
struct btrfs_path *path;
struct extent_buffer *leaf;
u32 size = sizeof(*extent_item) + sizeof(*block_info) + sizeof(*iref);
if (root_objectid == BTRFS_TREE_LOG_OBJECTID) path = btrfs_alloc_path();
return 0; BUG_ON(!path);
ret = btrfs_add_delayed_ref(trans, ins->objectid, path->leave_spinning = 1;
ins->offset, parent, root_objectid, ret = btrfs_insert_empty_item(trans, fs_info->extent_root, path,
ref_generation, owner, ins, size);
BTRFS_ADD_DELAYED_EXTENT, 0);
BUG_ON(ret); BUG_ON(ret);
leaf = path->nodes[0];
extent_item = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_extent_item);
btrfs_set_extent_refs(leaf, extent_item, 1);
btrfs_set_extent_generation(leaf, extent_item, trans->transid);
btrfs_set_extent_flags(leaf, extent_item,
flags | BTRFS_EXTENT_FLAG_TREE_BLOCK);
block_info = (struct btrfs_tree_block_info *)(extent_item + 1);
btrfs_set_tree_block_key(leaf, block_info, key);
btrfs_set_tree_block_level(leaf, block_info, level);
iref = (struct btrfs_extent_inline_ref *)(block_info + 1);
if (parent > 0) {
BUG_ON(!(flags & BTRFS_BLOCK_FLAG_FULL_BACKREF));
btrfs_set_extent_inline_ref_type(leaf, iref,
BTRFS_SHARED_BLOCK_REF_KEY);
btrfs_set_extent_inline_ref_offset(leaf, iref, parent);
} else {
btrfs_set_extent_inline_ref_type(leaf, iref,
BTRFS_TREE_BLOCK_REF_KEY);
btrfs_set_extent_inline_ref_offset(leaf, iref, root_objectid);
}
btrfs_mark_buffer_dirty(leaf);
btrfs_free_path(path);
ret = update_block_group(trans, root, ins->objectid, ins->offset,
1, 0);
if (ret) {
printk(KERN_ERR "btrfs update block group failed for %llu "
"%llu\n", (unsigned long long)ins->objectid,
(unsigned long long)ins->offset);
BUG();
}
return ret;
}
int btrfs_alloc_reserved_file_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 root_objectid, u64 owner,
u64 offset, struct btrfs_key *ins)
{
int ret;
BUG_ON(root_objectid == BTRFS_TREE_LOG_OBJECTID);
ret = btrfs_add_delayed_data_ref(trans, ins->objectid, ins->offset,
0, root_objectid, owner, offset,
BTRFS_ADD_DELAYED_EXTENT, NULL);
return ret; return ret;
} }
...@@ -3070,10 +3998,10 @@ int btrfs_alloc_reserved_extent(struct btrfs_trans_handle *trans, ...@@ -3070,10 +3998,10 @@ int btrfs_alloc_reserved_extent(struct btrfs_trans_handle *trans,
* an extent has been allocated and makes sure to clear the free * an extent has been allocated and makes sure to clear the free
* space cache bits as well * space cache bits as well
*/ */
int btrfs_alloc_logged_extent(struct btrfs_trans_handle *trans, int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 parent, struct btrfs_root *root,
u64 root_objectid, u64 ref_generation, u64 root_objectid, u64 owner, u64 offset,
u64 owner, struct btrfs_key *ins) struct btrfs_key *ins)
{ {
int ret; int ret;
struct btrfs_block_group_cache *block_group; struct btrfs_block_group_cache *block_group;
...@@ -3087,8 +4015,8 @@ int btrfs_alloc_logged_extent(struct btrfs_trans_handle *trans, ...@@ -3087,8 +4015,8 @@ int btrfs_alloc_logged_extent(struct btrfs_trans_handle *trans,
ins->offset); ins->offset);
BUG_ON(ret); BUG_ON(ret);
btrfs_put_block_group(block_group); btrfs_put_block_group(block_group);
ret = __btrfs_alloc_reserved_extent(trans, root, parent, root_objectid, ret = alloc_reserved_file_extent(trans, root, 0, root_objectid,
ref_generation, owner, ins, 1); 0, owner, offset, ins, 1);
return ret; return ret;
} }
...@@ -3099,26 +4027,48 @@ int btrfs_alloc_logged_extent(struct btrfs_trans_handle *trans, ...@@ -3099,26 +4027,48 @@ int btrfs_alloc_logged_extent(struct btrfs_trans_handle *trans,
* *
* returns 0 if everything worked, non-zero otherwise. * returns 0 if everything worked, non-zero otherwise.
*/ */
int btrfs_alloc_extent(struct btrfs_trans_handle *trans, static int alloc_tree_block(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
u64 num_bytes, u64 parent, u64 min_alloc_size, u64 num_bytes, u64 parent, u64 root_objectid,
u64 root_objectid, u64 ref_generation, struct btrfs_disk_key *key, int level,
u64 owner_objectid, u64 empty_size, u64 hint_byte, u64 empty_size, u64 hint_byte, u64 search_end,
u64 search_end, struct btrfs_key *ins, u64 data) struct btrfs_key *ins)
{ {
int ret; int ret;
ret = __btrfs_reserve_extent(trans, root, num_bytes, u64 flags = 0;
min_alloc_size, empty_size, hint_byte,
search_end, ins, data); ret = __btrfs_reserve_extent(trans, root, num_bytes, num_bytes,
empty_size, hint_byte, search_end,
ins, 0);
BUG_ON(ret); BUG_ON(ret);
if (root_objectid == BTRFS_TREE_RELOC_OBJECTID) {
if (parent == 0)
parent = ins->objectid;
flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF;
} else
BUG_ON(parent > 0);
update_reserved_extents(root, ins->objectid, ins->offset, 1);
if (root_objectid != BTRFS_TREE_LOG_OBJECTID) { if (root_objectid != BTRFS_TREE_LOG_OBJECTID) {
ret = btrfs_add_delayed_ref(trans, ins->objectid, struct btrfs_delayed_extent_op *extent_op;
ins->offset, parent, root_objectid, extent_op = kmalloc(sizeof(*extent_op), GFP_NOFS);
ref_generation, owner_objectid, BUG_ON(!extent_op);
BTRFS_ADD_DELAYED_EXTENT, 0); if (key)
memcpy(&extent_op->key, key, sizeof(extent_op->key));
else
memset(&extent_op->key, 0, sizeof(extent_op->key));
extent_op->flags_to_set = flags;
extent_op->update_key = 1;
extent_op->update_flags = 1;
extent_op->is_data = 0;
ret = btrfs_add_delayed_tree_ref(trans, ins->objectid,
ins->offset, parent, root_objectid,
level, BTRFS_ADD_DELAYED_EXTENT,
extent_op);
BUG_ON(ret); BUG_ON(ret);
} }
update_reserved_extents(root, ins->objectid, ins->offset, 1);
return ret; return ret;
} }
...@@ -3157,21 +4107,17 @@ struct extent_buffer *btrfs_init_new_buffer(struct btrfs_trans_handle *trans, ...@@ -3157,21 +4107,17 @@ struct extent_buffer *btrfs_init_new_buffer(struct btrfs_trans_handle *trans,
* returns the tree buffer or NULL. * returns the tree buffer or NULL.
*/ */
struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans, struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root, u32 blocksize,
u32 blocksize, u64 parent, u64 parent, u64 root_objectid,
u64 root_objectid, struct btrfs_disk_key *key, int level,
u64 ref_generation, u64 hint, u64 empty_size)
int level,
u64 hint,
u64 empty_size)
{ {
struct btrfs_key ins; struct btrfs_key ins;
int ret; int ret;
struct extent_buffer *buf; struct extent_buffer *buf;
ret = btrfs_alloc_extent(trans, root, blocksize, parent, blocksize, ret = alloc_tree_block(trans, root, blocksize, parent, root_objectid,
root_objectid, ref_generation, level, key, level, empty_size, hint, (u64)-1, &ins);
empty_size, hint, (u64)-1, &ins, 0);
if (ret) { if (ret) {
BUG_ON(ret > 0); BUG_ON(ret > 0);
return ERR_PTR(ret); return ERR_PTR(ret);
...@@ -3185,32 +4131,19 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans, ...@@ -3185,32 +4131,19 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
int btrfs_drop_leaf_ref(struct btrfs_trans_handle *trans, int btrfs_drop_leaf_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct extent_buffer *leaf) struct btrfs_root *root, struct extent_buffer *leaf)
{ {
u64 leaf_owner; u64 disk_bytenr;
u64 leaf_generation; u64 num_bytes;
struct refsort *sorted;
struct btrfs_key key; struct btrfs_key key;
struct btrfs_file_extent_item *fi; struct btrfs_file_extent_item *fi;
u32 nritems;
int i; int i;
int nritems;
int ret; int ret;
int refi = 0;
int slot;
BUG_ON(!btrfs_is_leaf(leaf)); BUG_ON(!btrfs_is_leaf(leaf));
nritems = btrfs_header_nritems(leaf); nritems = btrfs_header_nritems(leaf);
leaf_owner = btrfs_header_owner(leaf);
leaf_generation = btrfs_header_generation(leaf);
sorted = kmalloc(sizeof(*sorted) * nritems, GFP_NOFS);
/* we do this loop twice. The first time we build a list
* of the extents we have a reference on, then we sort the list
* by bytenr. The second time around we actually do the
* extent freeing.
*/
for (i = 0; i < nritems; i++) { for (i = 0; i < nritems; i++) {
u64 disk_bytenr;
cond_resched(); cond_resched();
btrfs_item_key_to_cpu(leaf, &key, i); btrfs_item_key_to_cpu(leaf, &key, i);
/* only extents have references, skip everything else */ /* only extents have references, skip everything else */
...@@ -3230,45 +4163,16 @@ int btrfs_drop_leaf_ref(struct btrfs_trans_handle *trans, ...@@ -3230,45 +4163,16 @@ int btrfs_drop_leaf_ref(struct btrfs_trans_handle *trans,
if (disk_bytenr == 0) if (disk_bytenr == 0)
continue; continue;
sorted[refi].bytenr = disk_bytenr; num_bytes = btrfs_file_extent_disk_num_bytes(leaf, fi);
sorted[refi].slot = i; ret = btrfs_free_extent(trans, root, disk_bytenr, num_bytes,
refi++; leaf->start, 0, key.objectid, 0);
}
if (refi == 0)
goto out;
sort(sorted, refi, sizeof(struct refsort), refsort_cmp, NULL);
for (i = 0; i < refi; i++) {
u64 disk_bytenr;
disk_bytenr = sorted[i].bytenr;
slot = sorted[i].slot;
cond_resched();
btrfs_item_key_to_cpu(leaf, &key, slot);
if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY)
continue;
fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
ret = btrfs_free_extent(trans, root, disk_bytenr,
btrfs_file_extent_disk_num_bytes(leaf, fi),
leaf->start, leaf_owner, leaf_generation,
key.objectid, 0);
BUG_ON(ret); BUG_ON(ret);
atomic_inc(&root->fs_info->throttle_gen);
wake_up(&root->fs_info->transaction_throttle);
cond_resched();
} }
out:
kfree(sorted);
return 0; return 0;
} }
#if 0
static noinline int cache_drop_leaf_ref(struct btrfs_trans_handle *trans, static noinline int cache_drop_leaf_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct btrfs_leaf_ref *ref) struct btrfs_leaf_ref *ref)
...@@ -3311,13 +4215,14 @@ static noinline int cache_drop_leaf_ref(struct btrfs_trans_handle *trans, ...@@ -3311,13 +4215,14 @@ static noinline int cache_drop_leaf_ref(struct btrfs_trans_handle *trans,
return 0; return 0;
} }
static int drop_snap_lookup_refcount(struct btrfs_trans_handle *trans, static int drop_snap_lookup_refcount(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 start, struct btrfs_root *root, u64 start,
u64 len, u32 *refs) u64 len, u32 *refs)
{ {
int ret; int ret;
ret = btrfs_lookup_extent_ref(trans, root, start, len, refs); ret = btrfs_lookup_extent_refs(trans, root, start, len, refs);
BUG_ON(ret); BUG_ON(ret);
#if 0 /* some debugging code in case we see problems here */ #if 0 /* some debugging code in case we see problems here */
...@@ -3352,6 +4257,7 @@ static int drop_snap_lookup_refcount(struct btrfs_trans_handle *trans, ...@@ -3352,6 +4257,7 @@ static int drop_snap_lookup_refcount(struct btrfs_trans_handle *trans,
return ret; return ret;
} }
/* /*
* this is used while deleting old snapshots, and it drops the refs * this is used while deleting old snapshots, and it drops the refs
* on a whole subtree starting from a level 1 node. * on a whole subtree starting from a level 1 node.
...@@ -3645,32 +4551,36 @@ static noinline int walk_down_tree(struct btrfs_trans_handle *trans, ...@@ -3645,32 +4551,36 @@ static noinline int walk_down_tree(struct btrfs_trans_handle *trans,
cond_resched(); cond_resched();
return 0; return 0;
} }
#endif
/* /*
* helper function for drop_subtree, this function is similar to * helper function for drop_subtree, this function is similar to
* walk_down_tree. The main difference is that it checks reference * walk_down_tree. The main difference is that it checks reference
* counts while tree blocks are locked. * counts while tree blocks are locked.
*/ */
static noinline int walk_down_subtree(struct btrfs_trans_handle *trans, static noinline int walk_down_tree(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct btrfs_path *path, int *level) struct btrfs_path *path, int *level)
{ {
struct extent_buffer *next; struct extent_buffer *next;
struct extent_buffer *cur; struct extent_buffer *cur;
struct extent_buffer *parent; struct extent_buffer *parent;
u64 bytenr; u64 bytenr;
u64 ptr_gen; u64 ptr_gen;
u64 refs;
u64 flags;
u32 blocksize; u32 blocksize;
u32 refs;
int ret; int ret;
cur = path->nodes[*level]; cur = path->nodes[*level];
ret = btrfs_lookup_extent_ref(trans, root, cur->start, cur->len, ret = btrfs_lookup_extent_info(trans, root, cur->start, cur->len,
&refs); &refs, &flags);
BUG_ON(ret); BUG_ON(ret);
if (refs > 1) if (refs > 1)
goto out; goto out;
BUG_ON(!(flags & BTRFS_BLOCK_FLAG_FULL_BACKREF));
while (*level >= 0) { while (*level >= 0) {
cur = path->nodes[*level]; cur = path->nodes[*level];
if (*level == 0) { if (*level == 0) {
...@@ -3692,16 +4602,15 @@ static noinline int walk_down_subtree(struct btrfs_trans_handle *trans, ...@@ -3692,16 +4602,15 @@ static noinline int walk_down_subtree(struct btrfs_trans_handle *trans,
btrfs_tree_lock(next); btrfs_tree_lock(next);
btrfs_set_lock_blocking(next); btrfs_set_lock_blocking(next);
ret = btrfs_lookup_extent_ref(trans, root, bytenr, blocksize, ret = btrfs_lookup_extent_info(trans, root, bytenr, blocksize,
&refs); &refs, &flags);
BUG_ON(ret); BUG_ON(ret);
if (refs > 1) { if (refs > 1) {
parent = path->nodes[*level]; parent = path->nodes[*level];
ret = btrfs_free_extent(trans, root, bytenr, ret = btrfs_free_extent(trans, root, bytenr,
blocksize, parent->start, blocksize, parent->start,
btrfs_header_owner(parent), btrfs_header_owner(parent),
btrfs_header_generation(parent), *level - 1, 0);
*level - 1, 1);
BUG_ON(ret); BUG_ON(ret);
path->slots[*level]++; path->slots[*level]++;
btrfs_tree_unlock(next); btrfs_tree_unlock(next);
...@@ -3709,6 +4618,8 @@ static noinline int walk_down_subtree(struct btrfs_trans_handle *trans, ...@@ -3709,6 +4618,8 @@ static noinline int walk_down_subtree(struct btrfs_trans_handle *trans,
continue; continue;
} }
BUG_ON(!(flags & BTRFS_BLOCK_FLAG_FULL_BACKREF));
*level = btrfs_header_level(next); *level = btrfs_header_level(next);
path->nodes[*level] = next; path->nodes[*level] = next;
path->slots[*level] = 0; path->slots[*level] = 0;
...@@ -3716,13 +4627,15 @@ static noinline int walk_down_subtree(struct btrfs_trans_handle *trans, ...@@ -3716,13 +4627,15 @@ static noinline int walk_down_subtree(struct btrfs_trans_handle *trans,
cond_resched(); cond_resched();
} }
out: out:
parent = path->nodes[*level + 1]; if (path->nodes[*level] == root->node)
parent = path->nodes[*level];
else
parent = path->nodes[*level + 1];
bytenr = path->nodes[*level]->start; bytenr = path->nodes[*level]->start;
blocksize = path->nodes[*level]->len; blocksize = path->nodes[*level]->len;
ret = btrfs_free_extent(trans, root, bytenr, blocksize, ret = btrfs_free_extent(trans, root, bytenr, blocksize, parent->start,
parent->start, btrfs_header_owner(parent), btrfs_header_owner(parent), *level, 0);
btrfs_header_generation(parent), *level, 1);
BUG_ON(ret); BUG_ON(ret);
if (path->locks[*level]) { if (path->locks[*level]) {
...@@ -3746,8 +4659,6 @@ static noinline int walk_up_tree(struct btrfs_trans_handle *trans, ...@@ -3746,8 +4659,6 @@ static noinline int walk_up_tree(struct btrfs_trans_handle *trans,
struct btrfs_path *path, struct btrfs_path *path,
int *level, int max_level) int *level, int max_level)
{ {
u64 root_owner;
u64 root_gen;
struct btrfs_root_item *root_item = &root->root_item; struct btrfs_root_item *root_item = &root->root_item;
int i; int i;
int slot; int slot;
...@@ -3755,24 +4666,22 @@ static noinline int walk_up_tree(struct btrfs_trans_handle *trans, ...@@ -3755,24 +4666,22 @@ static noinline int walk_up_tree(struct btrfs_trans_handle *trans,
for (i = *level; i < max_level && path->nodes[i]; i++) { for (i = *level; i < max_level && path->nodes[i]; i++) {
slot = path->slots[i]; slot = path->slots[i];
if (slot < btrfs_header_nritems(path->nodes[i]) - 1) { if (slot + 1 < btrfs_header_nritems(path->nodes[i])) {
struct extent_buffer *node;
struct btrfs_disk_key disk_key;
/* /*
* there is more work to do in this level. * there is more work to do in this level.
* Update the drop_progress marker to reflect * Update the drop_progress marker to reflect
* the work we've done so far, and then bump * the work we've done so far, and then bump
* the slot number * the slot number
*/ */
node = path->nodes[i];
path->slots[i]++; path->slots[i]++;
*level = i;
WARN_ON(*level == 0); WARN_ON(*level == 0);
btrfs_node_key(node, &disk_key, path->slots[i]); if (max_level == BTRFS_MAX_LEVEL) {
memcpy(&root_item->drop_progress, btrfs_node_key(path->nodes[i],
&disk_key, sizeof(disk_key)); &root_item->drop_progress,
root_item->drop_level = i; path->slots[i]);
root_item->drop_level = i;
}
*level = i;
return 0; return 0;
} else { } else {
struct extent_buffer *parent; struct extent_buffer *parent;
...@@ -3786,22 +4695,20 @@ static noinline int walk_up_tree(struct btrfs_trans_handle *trans, ...@@ -3786,22 +4695,20 @@ static noinline int walk_up_tree(struct btrfs_trans_handle *trans,
else else
parent = path->nodes[*level + 1]; parent = path->nodes[*level + 1];
root_owner = btrfs_header_owner(parent); clean_tree_block(trans, root, path->nodes[i]);
root_gen = btrfs_header_generation(parent);
clean_tree_block(trans, root, path->nodes[*level]);
ret = btrfs_free_extent(trans, root, ret = btrfs_free_extent(trans, root,
path->nodes[*level]->start, path->nodes[i]->start,
path->nodes[*level]->len, path->nodes[i]->len,
parent->start, root_owner, parent->start,
root_gen, *level, 1); btrfs_header_owner(parent),
*level, 0);
BUG_ON(ret); BUG_ON(ret);
if (path->locks[*level]) { if (path->locks[*level]) {
btrfs_tree_unlock(path->nodes[*level]); btrfs_tree_unlock(path->nodes[i]);
path->locks[*level] = 0; path->locks[i] = 0;
} }
free_extent_buffer(path->nodes[*level]); free_extent_buffer(path->nodes[i]);
path->nodes[*level] = NULL; path->nodes[i] = NULL;
*level = i + 1; *level = i + 1;
} }
} }
...@@ -3820,21 +4727,18 @@ int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -3820,21 +4727,18 @@ int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root
int wret; int wret;
int level; int level;
struct btrfs_path *path; struct btrfs_path *path;
int i;
int orig_level;
int update_count; int update_count;
struct btrfs_root_item *root_item = &root->root_item; struct btrfs_root_item *root_item = &root->root_item;
WARN_ON(!mutex_is_locked(&root->fs_info->drop_mutex));
path = btrfs_alloc_path(); path = btrfs_alloc_path();
BUG_ON(!path); BUG_ON(!path);
level = btrfs_header_level(root->node); level = btrfs_header_level(root->node);
orig_level = level;
if (btrfs_disk_key_objectid(&root_item->drop_progress) == 0) { if (btrfs_disk_key_objectid(&root_item->drop_progress) == 0) {
path->nodes[level] = root->node; path->nodes[level] = btrfs_lock_root_node(root);
extent_buffer_get(root->node); btrfs_set_lock_blocking(path->nodes[level]);
path->slots[level] = 0; path->slots[level] = 0;
path->locks[level] = 1;
} else { } else {
struct btrfs_key key; struct btrfs_key key;
struct btrfs_disk_key found_key; struct btrfs_disk_key found_key;
...@@ -3856,12 +4760,7 @@ int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -3856,12 +4760,7 @@ int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root
* unlock our path, this is safe because only this * unlock our path, this is safe because only this
* function is allowed to delete this snapshot * function is allowed to delete this snapshot
*/ */
for (i = 0; i < BTRFS_MAX_LEVEL; i++) { btrfs_unlock_up_safe(path, 0);
if (path->nodes[i] && path->locks[i]) {
path->locks[i] = 0;
btrfs_tree_unlock(path->nodes[i]);
}
}
} }
while (1) { while (1) {
unsigned long update; unsigned long update;
...@@ -3882,8 +4781,6 @@ int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -3882,8 +4781,6 @@ int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root
ret = -EAGAIN; ret = -EAGAIN;
break; break;
} }
atomic_inc(&root->fs_info->throttle_gen);
wake_up(&root->fs_info->transaction_throttle);
for (update_count = 0; update_count < 16; update_count++) { for (update_count = 0; update_count < 16; update_count++) {
update = trans->delayed_ref_updates; update = trans->delayed_ref_updates;
trans->delayed_ref_updates = 0; trans->delayed_ref_updates = 0;
...@@ -3893,12 +4790,6 @@ int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -3893,12 +4790,6 @@ int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root
break; break;
} }
} }
for (i = 0; i <= orig_level; i++) {
if (path->nodes[i]) {
free_extent_buffer(path->nodes[i]);
path->nodes[i] = NULL;
}
}
out: out:
btrfs_free_path(path); btrfs_free_path(path);
return ret; return ret;
...@@ -3931,7 +4822,7 @@ int btrfs_drop_subtree(struct btrfs_trans_handle *trans, ...@@ -3931,7 +4822,7 @@ int btrfs_drop_subtree(struct btrfs_trans_handle *trans,
path->slots[level] = 0; path->slots[level] = 0;
while (1) { while (1) {
wret = walk_down_subtree(trans, root, path, &level); wret = walk_down_tree(trans, root, path, &level);
if (wret < 0) if (wret < 0)
ret = wret; ret = wret;
if (wret != 0) if (wret != 0)
...@@ -3948,6 +4839,7 @@ int btrfs_drop_subtree(struct btrfs_trans_handle *trans, ...@@ -3948,6 +4839,7 @@ int btrfs_drop_subtree(struct btrfs_trans_handle *trans,
return ret; return ret;
} }
#if 0
static unsigned long calc_ra(unsigned long start, unsigned long last, static unsigned long calc_ra(unsigned long start, unsigned long last,
unsigned long nr) unsigned long nr)
{ {
...@@ -5429,6 +6321,7 @@ static noinline int relocate_one_extent(struct btrfs_root *extent_root, ...@@ -5429,6 +6321,7 @@ static noinline int relocate_one_extent(struct btrfs_root *extent_root,
kfree(ref_path); kfree(ref_path);
return ret; return ret;
} }
#endif
static u64 update_block_group_flags(struct btrfs_root *root, u64 flags) static u64 update_block_group_flags(struct btrfs_root *root, u64 flags)
{ {
...@@ -5477,7 +6370,8 @@ static int __alloc_chunk_for_shrink(struct btrfs_root *root, ...@@ -5477,7 +6370,8 @@ static int __alloc_chunk_for_shrink(struct btrfs_root *root,
u64 calc; u64 calc;
spin_lock(&shrink_block_group->lock); spin_lock(&shrink_block_group->lock);
if (btrfs_block_group_used(&shrink_block_group->item) > 0) { if (btrfs_block_group_used(&shrink_block_group->item) +
shrink_block_group->reserved > 0) {
spin_unlock(&shrink_block_group->lock); spin_unlock(&shrink_block_group->lock);
trans = btrfs_start_transaction(root, 1); trans = btrfs_start_transaction(root, 1);
...@@ -5502,6 +6396,17 @@ static int __alloc_chunk_for_shrink(struct btrfs_root *root, ...@@ -5502,6 +6396,17 @@ static int __alloc_chunk_for_shrink(struct btrfs_root *root,
return 0; return 0;
} }
int btrfs_prepare_block_group_relocation(struct btrfs_root *root,
struct btrfs_block_group_cache *group)
{
__alloc_chunk_for_shrink(root, group, 1);
set_block_group_readonly(group);
return 0;
}
#if 0
static int __insert_orphan_inode(struct btrfs_trans_handle *trans, static int __insert_orphan_inode(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
u64 objectid, u64 size) u64 objectid, u64 size)
...@@ -5781,6 +6686,7 @@ int btrfs_relocate_block_group(struct btrfs_root *root, u64 group_start) ...@@ -5781,6 +6686,7 @@ int btrfs_relocate_block_group(struct btrfs_root *root, u64 group_start)
btrfs_free_path(path); btrfs_free_path(path);
return ret; return ret;
} }
#endif
static int find_first_block_group(struct btrfs_root *root, static int find_first_block_group(struct btrfs_root *root,
struct btrfs_path *path, struct btrfs_key *key) struct btrfs_path *path, struct btrfs_key *key)
......
...@@ -476,6 +476,7 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, ...@@ -476,6 +476,7 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
struct extent_state *state; struct extent_state *state;
struct extent_state *prealloc = NULL; struct extent_state *prealloc = NULL;
struct rb_node *node; struct rb_node *node;
u64 last_end;
int err; int err;
int set = 0; int set = 0;
...@@ -498,6 +499,7 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, ...@@ -498,6 +499,7 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
if (state->start > end) if (state->start > end)
goto out; goto out;
WARN_ON(state->end < start); WARN_ON(state->end < start);
last_end = state->end;
/* /*
* | ---- desired range ---- | * | ---- desired range ---- |
...@@ -524,9 +526,11 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, ...@@ -524,9 +526,11 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
if (err) if (err)
goto out; goto out;
if (state->end <= end) { if (state->end <= end) {
start = state->end + 1;
set |= clear_state_bit(tree, state, bits, set |= clear_state_bit(tree, state, bits,
wake, delete); wake, delete);
if (last_end == (u64)-1)
goto out;
start = last_end + 1;
} else { } else {
start = state->start; start = state->start;
} }
...@@ -552,8 +556,10 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, ...@@ -552,8 +556,10 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
goto out; goto out;
} }
start = state->end + 1;
set |= clear_state_bit(tree, state, bits, wake, delete); set |= clear_state_bit(tree, state, bits, wake, delete);
if (last_end == (u64)-1)
goto out;
start = last_end + 1;
goto search_again; goto search_again;
out: out:
...@@ -707,8 +713,10 @@ static int set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, ...@@ -707,8 +713,10 @@ static int set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
goto out; goto out;
} }
set_state_bits(tree, state, bits); set_state_bits(tree, state, bits);
start = state->end + 1;
merge_state(tree, state); merge_state(tree, state);
if (last_end == (u64)-1)
goto out;
start = last_end + 1;
goto search_again; goto search_again;
} }
...@@ -742,8 +750,10 @@ static int set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, ...@@ -742,8 +750,10 @@ static int set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
goto out; goto out;
if (state->end <= end) { if (state->end <= end) {
set_state_bits(tree, state, bits); set_state_bits(tree, state, bits);
start = state->end + 1;
merge_state(tree, state); merge_state(tree, state);
if (last_end == (u64)-1)
goto out;
start = last_end + 1;
} else { } else {
start = state->start; start = state->start;
} }
......
...@@ -291,16 +291,12 @@ noinline int btrfs_drop_extents(struct btrfs_trans_handle *trans, ...@@ -291,16 +291,12 @@ noinline int btrfs_drop_extents(struct btrfs_trans_handle *trans,
{ {
u64 extent_end = 0; u64 extent_end = 0;
u64 search_start = start; u64 search_start = start;
u64 leaf_start;
u64 ram_bytes = 0; u64 ram_bytes = 0;
u64 orig_parent = 0;
u64 disk_bytenr = 0; u64 disk_bytenr = 0;
u64 orig_locked_end = locked_end; u64 orig_locked_end = locked_end;
u8 compression; u8 compression;
u8 encryption; u8 encryption;
u16 other_encoding = 0; u16 other_encoding = 0;
u64 root_gen;
u64 root_owner;
struct extent_buffer *leaf; struct extent_buffer *leaf;
struct btrfs_file_extent_item *extent; struct btrfs_file_extent_item *extent;
struct btrfs_path *path; struct btrfs_path *path;
...@@ -340,9 +336,6 @@ noinline int btrfs_drop_extents(struct btrfs_trans_handle *trans, ...@@ -340,9 +336,6 @@ noinline int btrfs_drop_extents(struct btrfs_trans_handle *trans,
bookend = 0; bookend = 0;
found_extent = 0; found_extent = 0;
found_inline = 0; found_inline = 0;
leaf_start = 0;
root_gen = 0;
root_owner = 0;
compression = 0; compression = 0;
encryption = 0; encryption = 0;
extent = NULL; extent = NULL;
...@@ -417,9 +410,6 @@ noinline int btrfs_drop_extents(struct btrfs_trans_handle *trans, ...@@ -417,9 +410,6 @@ noinline int btrfs_drop_extents(struct btrfs_trans_handle *trans,
if (found_extent) { if (found_extent) {
read_extent_buffer(leaf, &old, (unsigned long)extent, read_extent_buffer(leaf, &old, (unsigned long)extent,
sizeof(old)); sizeof(old));
root_gen = btrfs_header_generation(leaf);
root_owner = btrfs_header_owner(leaf);
leaf_start = leaf->start;
} }
if (end < extent_end && end >= key.offset) { if (end < extent_end && end >= key.offset) {
...@@ -443,14 +433,14 @@ noinline int btrfs_drop_extents(struct btrfs_trans_handle *trans, ...@@ -443,14 +433,14 @@ noinline int btrfs_drop_extents(struct btrfs_trans_handle *trans,
} }
locked_end = extent_end; locked_end = extent_end;
} }
orig_parent = path->nodes[0]->start;
disk_bytenr = le64_to_cpu(old.disk_bytenr); disk_bytenr = le64_to_cpu(old.disk_bytenr);
if (disk_bytenr != 0) { if (disk_bytenr != 0) {
ret = btrfs_inc_extent_ref(trans, root, ret = btrfs_inc_extent_ref(trans, root,
disk_bytenr, disk_bytenr,
le64_to_cpu(old.disk_num_bytes), le64_to_cpu(old.disk_num_bytes), 0,
orig_parent, root->root_key.objectid, root->root_key.objectid,
trans->transid, inode->i_ino); key.objectid, key.offset -
le64_to_cpu(old.offset));
BUG_ON(ret); BUG_ON(ret);
} }
} }
...@@ -568,17 +558,6 @@ noinline int btrfs_drop_extents(struct btrfs_trans_handle *trans, ...@@ -568,17 +558,6 @@ noinline int btrfs_drop_extents(struct btrfs_trans_handle *trans,
btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_mark_buffer_dirty(path->nodes[0]);
btrfs_set_lock_blocking(path->nodes[0]); btrfs_set_lock_blocking(path->nodes[0]);
if (disk_bytenr != 0) {
ret = btrfs_update_extent_ref(trans, root,
disk_bytenr,
le64_to_cpu(old.disk_num_bytes),
orig_parent,
leaf->start,
root->root_key.objectid,
trans->transid, ins.objectid);
BUG_ON(ret);
}
path->leave_spinning = 0; path->leave_spinning = 0;
btrfs_release_path(root, path); btrfs_release_path(root, path);
if (disk_bytenr != 0) if (disk_bytenr != 0)
...@@ -594,8 +573,9 @@ noinline int btrfs_drop_extents(struct btrfs_trans_handle *trans, ...@@ -594,8 +573,9 @@ noinline int btrfs_drop_extents(struct btrfs_trans_handle *trans,
ret = btrfs_free_extent(trans, root, ret = btrfs_free_extent(trans, root,
old_disk_bytenr, old_disk_bytenr,
le64_to_cpu(old.disk_num_bytes), le64_to_cpu(old.disk_num_bytes),
leaf_start, root_owner, 0, root->root_key.objectid,
root_gen, key.objectid, 0); key.objectid, key.offset -
le64_to_cpu(old.offset));
BUG_ON(ret); BUG_ON(ret);
*hint_byte = old_disk_bytenr; *hint_byte = old_disk_bytenr;
} }
...@@ -664,12 +644,11 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans, ...@@ -664,12 +644,11 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
u64 bytenr; u64 bytenr;
u64 num_bytes; u64 num_bytes;
u64 extent_end; u64 extent_end;
u64 extent_offset; u64 orig_offset;
u64 other_start; u64 other_start;
u64 other_end; u64 other_end;
u64 split = start; u64 split = start;
u64 locked_end = end; u64 locked_end = end;
u64 orig_parent;
int extent_type; int extent_type;
int split_end = 1; int split_end = 1;
int ret; int ret;
...@@ -703,7 +682,7 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans, ...@@ -703,7 +682,7 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
bytenr = btrfs_file_extent_disk_bytenr(leaf, fi); bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
num_bytes = btrfs_file_extent_disk_num_bytes(leaf, fi); num_bytes = btrfs_file_extent_disk_num_bytes(leaf, fi);
extent_offset = btrfs_file_extent_offset(leaf, fi); orig_offset = key.offset - btrfs_file_extent_offset(leaf, fi);
if (key.offset == start) if (key.offset == start)
split = end; split = end;
...@@ -711,8 +690,6 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans, ...@@ -711,8 +690,6 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
if (key.offset == start && extent_end == end) { if (key.offset == start && extent_end == end) {
int del_nr = 0; int del_nr = 0;
int del_slot = 0; int del_slot = 0;
u64 leaf_owner = btrfs_header_owner(leaf);
u64 leaf_gen = btrfs_header_generation(leaf);
other_start = end; other_start = end;
other_end = 0; other_end = 0;
if (extent_mergeable(leaf, path->slots[0] + 1, inode->i_ino, if (extent_mergeable(leaf, path->slots[0] + 1, inode->i_ino,
...@@ -721,8 +698,8 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans, ...@@ -721,8 +698,8 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
del_slot = path->slots[0] + 1; del_slot = path->slots[0] + 1;
del_nr++; del_nr++;
ret = btrfs_free_extent(trans, root, bytenr, num_bytes, ret = btrfs_free_extent(trans, root, bytenr, num_bytes,
leaf->start, leaf_owner, 0, root->root_key.objectid,
leaf_gen, inode->i_ino, 0); inode->i_ino, orig_offset);
BUG_ON(ret); BUG_ON(ret);
} }
other_start = 0; other_start = 0;
...@@ -733,8 +710,8 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans, ...@@ -733,8 +710,8 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
del_slot = path->slots[0]; del_slot = path->slots[0];
del_nr++; del_nr++;
ret = btrfs_free_extent(trans, root, bytenr, num_bytes, ret = btrfs_free_extent(trans, root, bytenr, num_bytes,
leaf->start, leaf_owner, 0, root->root_key.objectid,
leaf_gen, inode->i_ino, 0); inode->i_ino, orig_offset);
BUG_ON(ret); BUG_ON(ret);
} }
split_end = 0; split_end = 0;
...@@ -768,13 +745,12 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans, ...@@ -768,13 +745,12 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
locked_end = extent_end; locked_end = extent_end;
} }
btrfs_set_file_extent_num_bytes(leaf, fi, split - key.offset); btrfs_set_file_extent_num_bytes(leaf, fi, split - key.offset);
extent_offset += split - key.offset;
} else { } else {
BUG_ON(key.offset != start); BUG_ON(key.offset != start);
btrfs_set_file_extent_offset(leaf, fi, extent_offset +
split - key.offset);
btrfs_set_file_extent_num_bytes(leaf, fi, extent_end - split);
key.offset = split; key.offset = split;
btrfs_set_file_extent_offset(leaf, fi, key.offset -
orig_offset);
btrfs_set_file_extent_num_bytes(leaf, fi, extent_end - split);
btrfs_set_item_key_safe(trans, root, path, &key); btrfs_set_item_key_safe(trans, root, path, &key);
extent_end = split; extent_end = split;
} }
...@@ -793,7 +769,8 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans, ...@@ -793,7 +769,8 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
struct btrfs_file_extent_item); struct btrfs_file_extent_item);
key.offset = split; key.offset = split;
btrfs_set_item_key_safe(trans, root, path, &key); btrfs_set_item_key_safe(trans, root, path, &key);
btrfs_set_file_extent_offset(leaf, fi, extent_offset); btrfs_set_file_extent_offset(leaf, fi, key.offset -
orig_offset);
btrfs_set_file_extent_num_bytes(leaf, fi, btrfs_set_file_extent_num_bytes(leaf, fi,
other_end - split); other_end - split);
goto done; goto done;
...@@ -815,10 +792,9 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans, ...@@ -815,10 +792,9 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
btrfs_mark_buffer_dirty(leaf); btrfs_mark_buffer_dirty(leaf);
orig_parent = leaf->start; ret = btrfs_inc_extent_ref(trans, root, bytenr, num_bytes, 0,
ret = btrfs_inc_extent_ref(trans, root, bytenr, num_bytes, root->root_key.objectid,
orig_parent, root->root_key.objectid, inode->i_ino, orig_offset);
trans->transid, inode->i_ino);
BUG_ON(ret); BUG_ON(ret);
btrfs_release_path(root, path); btrfs_release_path(root, path);
...@@ -833,20 +809,12 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans, ...@@ -833,20 +809,12 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
btrfs_set_file_extent_type(leaf, fi, extent_type); btrfs_set_file_extent_type(leaf, fi, extent_type);
btrfs_set_file_extent_disk_bytenr(leaf, fi, bytenr); btrfs_set_file_extent_disk_bytenr(leaf, fi, bytenr);
btrfs_set_file_extent_disk_num_bytes(leaf, fi, num_bytes); btrfs_set_file_extent_disk_num_bytes(leaf, fi, num_bytes);
btrfs_set_file_extent_offset(leaf, fi, extent_offset); btrfs_set_file_extent_offset(leaf, fi, key.offset - orig_offset);
btrfs_set_file_extent_num_bytes(leaf, fi, extent_end - key.offset); btrfs_set_file_extent_num_bytes(leaf, fi, extent_end - key.offset);
btrfs_set_file_extent_ram_bytes(leaf, fi, num_bytes); btrfs_set_file_extent_ram_bytes(leaf, fi, num_bytes);
btrfs_set_file_extent_compression(leaf, fi, 0); btrfs_set_file_extent_compression(leaf, fi, 0);
btrfs_set_file_extent_encryption(leaf, fi, 0); btrfs_set_file_extent_encryption(leaf, fi, 0);
btrfs_set_file_extent_other_encoding(leaf, fi, 0); btrfs_set_file_extent_other_encoding(leaf, fi, 0);
if (orig_parent != leaf->start) {
ret = btrfs_update_extent_ref(trans, root, bytenr, num_bytes,
orig_parent, leaf->start,
root->root_key.objectid,
trans->transid, inode->i_ino);
BUG_ON(ret);
}
done: done:
btrfs_mark_buffer_dirty(leaf); btrfs_mark_buffer_dirty(leaf);
...@@ -1189,6 +1157,8 @@ int btrfs_sync_file(struct file *file, struct dentry *dentry, int datasync) ...@@ -1189,6 +1157,8 @@ int btrfs_sync_file(struct file *file, struct dentry *dentry, int datasync)
btrfs_wait_ordered_range(inode, 0, (u64)-1); btrfs_wait_ordered_range(inode, 0, (u64)-1);
root->log_batch++; root->log_batch++;
if (datasync && !(inode->i_state & I_DIRTY_PAGES))
goto out;
/* /*
* ok we haven't committed the transaction yet, lets do a commit * ok we haven't committed the transaction yet, lets do a commit
*/ */
......
...@@ -579,6 +579,7 @@ u64 btrfs_alloc_from_cluster(struct btrfs_block_group_cache *block_group, ...@@ -579,6 +579,7 @@ u64 btrfs_alloc_from_cluster(struct btrfs_block_group_cache *block_group,
* it returns -enospc * it returns -enospc
*/ */
int btrfs_find_space_cluster(struct btrfs_trans_handle *trans, int btrfs_find_space_cluster(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_block_group_cache *block_group, struct btrfs_block_group_cache *block_group,
struct btrfs_free_cluster *cluster, struct btrfs_free_cluster *cluster,
u64 offset, u64 bytes, u64 empty_size) u64 offset, u64 bytes, u64 empty_size)
...@@ -595,7 +596,9 @@ int btrfs_find_space_cluster(struct btrfs_trans_handle *trans, ...@@ -595,7 +596,9 @@ int btrfs_find_space_cluster(struct btrfs_trans_handle *trans,
int ret; int ret;
/* for metadata, allow allocates with more holes */ /* for metadata, allow allocates with more holes */
if (block_group->flags & BTRFS_BLOCK_GROUP_METADATA) { if (btrfs_test_opt(root, SSD_SPREAD)) {
min_bytes = bytes + empty_size;
} else if (block_group->flags & BTRFS_BLOCK_GROUP_METADATA) {
/* /*
* we want to do larger allocations when we are * we want to do larger allocations when we are
* flushing out the delayed refs, it helps prevent * flushing out the delayed refs, it helps prevent
...@@ -645,14 +648,15 @@ int btrfs_find_space_cluster(struct btrfs_trans_handle *trans, ...@@ -645,14 +648,15 @@ int btrfs_find_space_cluster(struct btrfs_trans_handle *trans,
* we haven't filled the empty size and the window is * we haven't filled the empty size and the window is
* very large. reset and try again * very large. reset and try again
*/ */
if (next->offset - window_start > (bytes + empty_size) * 2) { if (next->offset - (last->offset + last->bytes) > 128 * 1024 ||
next->offset - window_start > (bytes + empty_size) * 2) {
entry = next; entry = next;
window_start = entry->offset; window_start = entry->offset;
window_free = entry->bytes; window_free = entry->bytes;
last = entry; last = entry;
max_extent = 0; max_extent = 0;
total_retries++; total_retries++;
if (total_retries % 256 == 0) { if (total_retries % 64 == 0) {
if (min_bytes >= (bytes + empty_size)) { if (min_bytes >= (bytes + empty_size)) {
ret = -ENOSPC; ret = -ENOSPC;
goto out; goto out;
......
...@@ -31,6 +31,7 @@ void btrfs_dump_free_space(struct btrfs_block_group_cache *block_group, ...@@ -31,6 +31,7 @@ void btrfs_dump_free_space(struct btrfs_block_group_cache *block_group,
u64 bytes); u64 bytes);
u64 btrfs_block_group_free_space(struct btrfs_block_group_cache *block_group); u64 btrfs_block_group_free_space(struct btrfs_block_group_cache *block_group);
int btrfs_find_space_cluster(struct btrfs_trans_handle *trans, int btrfs_find_space_cluster(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_block_group_cache *block_group, struct btrfs_block_group_cache *block_group,
struct btrfs_free_cluster *cluster, struct btrfs_free_cluster *cluster,
u64 offset, u64 bytes, u64 empty_size); u64 offset, u64 bytes, u64 empty_size);
......
...@@ -19,9 +19,9 @@ ...@@ -19,9 +19,9 @@
#ifndef __HASH__ #ifndef __HASH__
#define __HASH__ #define __HASH__
#include "crc32c.h" #include <linux/crc32c.h>
static inline u64 btrfs_name_hash(const char *name, int len) static inline u64 btrfs_name_hash(const char *name, int len)
{ {
return btrfs_crc32c((u32)~1, name, len); return crc32c((u32)~1, name, len);
} }
#endif #endif
...@@ -48,7 +48,6 @@ ...@@ -48,7 +48,6 @@
#include "ordered-data.h" #include "ordered-data.h"
#include "xattr.h" #include "xattr.h"
#include "tree-log.h" #include "tree-log.h"
#include "ref-cache.h"
#include "compression.h" #include "compression.h"
#include "locking.h" #include "locking.h"
...@@ -369,7 +368,7 @@ static noinline int compress_file_range(struct inode *inode, ...@@ -369,7 +368,7 @@ static noinline int compress_file_range(struct inode *inode,
* inode has not been flagged as nocompress. This flag can * inode has not been flagged as nocompress. This flag can
* change at any time if we discover bad compression ratios. * change at any time if we discover bad compression ratios.
*/ */
if (!btrfs_test_flag(inode, NOCOMPRESS) && if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS) &&
btrfs_test_opt(root, COMPRESS)) { btrfs_test_opt(root, COMPRESS)) {
WARN_ON(pages); WARN_ON(pages);
pages = kzalloc(sizeof(struct page *) * nr_pages, GFP_NOFS); pages = kzalloc(sizeof(struct page *) * nr_pages, GFP_NOFS);
...@@ -470,7 +469,7 @@ static noinline int compress_file_range(struct inode *inode, ...@@ -470,7 +469,7 @@ static noinline int compress_file_range(struct inode *inode,
nr_pages_ret = 0; nr_pages_ret = 0;
/* flag the file so we don't compress in the future */ /* flag the file so we don't compress in the future */
btrfs_set_flag(inode, NOCOMPRESS); BTRFS_I(inode)->flags |= BTRFS_INODE_NOCOMPRESS;
} }
if (will_compress) { if (will_compress) {
*num_added += 1; *num_added += 1;
...@@ -863,7 +862,7 @@ static int cow_file_range_async(struct inode *inode, struct page *locked_page, ...@@ -863,7 +862,7 @@ static int cow_file_range_async(struct inode *inode, struct page *locked_page,
async_cow->locked_page = locked_page; async_cow->locked_page = locked_page;
async_cow->start = start; async_cow->start = start;
if (btrfs_test_flag(inode, NOCOMPRESS)) if (BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS)
cur_end = end; cur_end = end;
else else
cur_end = min(end, start + 512 * 1024 - 1); cur_end = min(end, start + 512 * 1024 - 1);
...@@ -944,6 +943,7 @@ static noinline int run_delalloc_nocow(struct inode *inode, ...@@ -944,6 +943,7 @@ static noinline int run_delalloc_nocow(struct inode *inode,
u64 cow_start; u64 cow_start;
u64 cur_offset; u64 cur_offset;
u64 extent_end; u64 extent_end;
u64 extent_offset;
u64 disk_bytenr; u64 disk_bytenr;
u64 num_bytes; u64 num_bytes;
int extent_type; int extent_type;
...@@ -1005,6 +1005,7 @@ static noinline int run_delalloc_nocow(struct inode *inode, ...@@ -1005,6 +1005,7 @@ static noinline int run_delalloc_nocow(struct inode *inode,
if (extent_type == BTRFS_FILE_EXTENT_REG || if (extent_type == BTRFS_FILE_EXTENT_REG ||
extent_type == BTRFS_FILE_EXTENT_PREALLOC) { extent_type == BTRFS_FILE_EXTENT_PREALLOC) {
disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi); disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
extent_offset = btrfs_file_extent_offset(leaf, fi);
extent_end = found_key.offset + extent_end = found_key.offset +
btrfs_file_extent_num_bytes(leaf, fi); btrfs_file_extent_num_bytes(leaf, fi);
if (extent_end <= start) { if (extent_end <= start) {
...@@ -1022,9 +1023,10 @@ static noinline int run_delalloc_nocow(struct inode *inode, ...@@ -1022,9 +1023,10 @@ static noinline int run_delalloc_nocow(struct inode *inode,
if (btrfs_extent_readonly(root, disk_bytenr)) if (btrfs_extent_readonly(root, disk_bytenr))
goto out_check; goto out_check;
if (btrfs_cross_ref_exist(trans, root, inode->i_ino, if (btrfs_cross_ref_exist(trans, root, inode->i_ino,
disk_bytenr)) found_key.offset -
extent_offset, disk_bytenr))
goto out_check; goto out_check;
disk_bytenr += btrfs_file_extent_offset(leaf, fi); disk_bytenr += extent_offset;
disk_bytenr += cur_offset - found_key.offset; disk_bytenr += cur_offset - found_key.offset;
num_bytes = min(end + 1, extent_end) - cur_offset; num_bytes = min(end + 1, extent_end) - cur_offset;
/* /*
...@@ -1131,10 +1133,10 @@ static int run_delalloc_range(struct inode *inode, struct page *locked_page, ...@@ -1131,10 +1133,10 @@ static int run_delalloc_range(struct inode *inode, struct page *locked_page,
int ret; int ret;
struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_root *root = BTRFS_I(inode)->root;
if (btrfs_test_flag(inode, NODATACOW)) if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW)
ret = run_delalloc_nocow(inode, locked_page, start, end, ret = run_delalloc_nocow(inode, locked_page, start, end,
page_started, 1, nr_written); page_started, 1, nr_written);
else if (btrfs_test_flag(inode, PREALLOC)) else if (BTRFS_I(inode)->flags & BTRFS_INODE_PREALLOC)
ret = run_delalloc_nocow(inode, locked_page, start, end, ret = run_delalloc_nocow(inode, locked_page, start, end,
page_started, 0, nr_written); page_started, 0, nr_written);
else if (!btrfs_test_opt(root, COMPRESS)) else if (!btrfs_test_opt(root, COMPRESS))
...@@ -1288,7 +1290,7 @@ static int btrfs_submit_bio_hook(struct inode *inode, int rw, struct bio *bio, ...@@ -1288,7 +1290,7 @@ static int btrfs_submit_bio_hook(struct inode *inode, int rw, struct bio *bio,
int ret = 0; int ret = 0;
int skip_sum; int skip_sum;
skip_sum = btrfs_test_flag(inode, NODATASUM); skip_sum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM;
ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0); ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
BUG_ON(ret); BUG_ON(ret);
...@@ -1489,9 +1491,9 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, ...@@ -1489,9 +1491,9 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
ins.objectid = disk_bytenr; ins.objectid = disk_bytenr;
ins.offset = disk_num_bytes; ins.offset = disk_num_bytes;
ins.type = BTRFS_EXTENT_ITEM_KEY; ins.type = BTRFS_EXTENT_ITEM_KEY;
ret = btrfs_alloc_reserved_extent(trans, root, leaf->start, ret = btrfs_alloc_reserved_file_extent(trans, root,
root->root_key.objectid, root->root_key.objectid,
trans->transid, inode->i_ino, &ins); inode->i_ino, file_pos, &ins);
BUG_ON(ret); BUG_ON(ret);
btrfs_free_path(path); btrfs_free_path(path);
...@@ -1788,7 +1790,8 @@ static int btrfs_readpage_end_io_hook(struct page *page, u64 start, u64 end, ...@@ -1788,7 +1790,8 @@ static int btrfs_readpage_end_io_hook(struct page *page, u64 start, u64 end,
ClearPageChecked(page); ClearPageChecked(page);
goto good; goto good;
} }
if (btrfs_test_flag(inode, NODATASUM))
if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)
return 0; return 0;
if (root->root_key.objectid == BTRFS_DATA_RELOC_TREE_OBJECTID && if (root->root_key.objectid == BTRFS_DATA_RELOC_TREE_OBJECTID &&
...@@ -1956,23 +1959,13 @@ void btrfs_orphan_cleanup(struct btrfs_root *root) ...@@ -1956,23 +1959,13 @@ void btrfs_orphan_cleanup(struct btrfs_root *root)
* crossing root thing. we store the inode number in the * crossing root thing. we store the inode number in the
* offset of the orphan item. * offset of the orphan item.
*/ */
inode = btrfs_iget_locked(root->fs_info->sb, found_key.objectid = found_key.offset;
found_key.offset, root); found_key.type = BTRFS_INODE_ITEM_KEY;
if (!inode) found_key.offset = 0;
inode = btrfs_iget(root->fs_info->sb, &found_key, root);
if (IS_ERR(inode))
break; break;
if (inode->i_state & I_NEW) {
BTRFS_I(inode)->root = root;
/* have to set the location manually */
BTRFS_I(inode)->location.objectid = inode->i_ino;
BTRFS_I(inode)->location.type = BTRFS_INODE_ITEM_KEY;
BTRFS_I(inode)->location.offset = 0;
btrfs_read_locked_inode(inode);
unlock_new_inode(inode);
}
/* /*
* add this inode to the orphan list so btrfs_orphan_del does * add this inode to the orphan list so btrfs_orphan_del does
* the proper thing when we hit it * the proper thing when we hit it
...@@ -2069,7 +2062,7 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf, ...@@ -2069,7 +2062,7 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf,
/* /*
* read an inode from the btree into the in-memory inode * read an inode from the btree into the in-memory inode
*/ */
void btrfs_read_locked_inode(struct inode *inode) static void btrfs_read_locked_inode(struct inode *inode)
{ {
struct btrfs_path *path; struct btrfs_path *path;
struct extent_buffer *leaf; struct extent_buffer *leaf;
...@@ -2164,6 +2157,8 @@ void btrfs_read_locked_inode(struct inode *inode) ...@@ -2164,6 +2157,8 @@ void btrfs_read_locked_inode(struct inode *inode)
init_special_inode(inode, inode->i_mode, rdev); init_special_inode(inode, inode->i_mode, rdev);
break; break;
} }
btrfs_update_iflags(inode);
return; return;
make_bad: make_bad:
...@@ -2599,9 +2594,8 @@ noinline int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, ...@@ -2599,9 +2594,8 @@ noinline int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
struct btrfs_file_extent_item *fi; struct btrfs_file_extent_item *fi;
u64 extent_start = 0; u64 extent_start = 0;
u64 extent_num_bytes = 0; u64 extent_num_bytes = 0;
u64 extent_offset = 0;
u64 item_end = 0; u64 item_end = 0;
u64 root_gen = 0;
u64 root_owner = 0;
int found_extent; int found_extent;
int del_item; int del_item;
int pending_del_nr = 0; int pending_del_nr = 0;
...@@ -2716,6 +2710,9 @@ noinline int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, ...@@ -2716,6 +2710,9 @@ noinline int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
extent_num_bytes = extent_num_bytes =
btrfs_file_extent_disk_num_bytes(leaf, btrfs_file_extent_disk_num_bytes(leaf,
fi); fi);
extent_offset = found_key.offset -
btrfs_file_extent_offset(leaf, fi);
/* FIXME blocksize != 4096 */ /* FIXME blocksize != 4096 */
num_dec = btrfs_file_extent_num_bytes(leaf, fi); num_dec = btrfs_file_extent_num_bytes(leaf, fi);
if (extent_start != 0) { if (extent_start != 0) {
...@@ -2723,8 +2720,6 @@ noinline int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, ...@@ -2723,8 +2720,6 @@ noinline int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
if (root->ref_cows) if (root->ref_cows)
inode_sub_bytes(inode, num_dec); inode_sub_bytes(inode, num_dec);
} }
root_gen = btrfs_header_generation(leaf);
root_owner = btrfs_header_owner(leaf);
} }
} else if (extent_type == BTRFS_FILE_EXTENT_INLINE) { } else if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
/* /*
...@@ -2768,12 +2763,12 @@ noinline int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, ...@@ -2768,12 +2763,12 @@ noinline int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
} else { } else {
break; break;
} }
if (found_extent) { if (found_extent && root->ref_cows) {
btrfs_set_path_blocking(path); btrfs_set_path_blocking(path);
ret = btrfs_free_extent(trans, root, extent_start, ret = btrfs_free_extent(trans, root, extent_start,
extent_num_bytes, extent_num_bytes, 0,
leaf->start, root_owner, btrfs_header_owner(leaf),
root_gen, inode->i_ino, 0); inode->i_ino, extent_offset);
BUG_ON(ret); BUG_ON(ret);
} }
next: next:
...@@ -3105,6 +3100,45 @@ static int fixup_tree_root_location(struct btrfs_root *root, ...@@ -3105,6 +3100,45 @@ static int fixup_tree_root_location(struct btrfs_root *root,
return 0; return 0;
} }
static void inode_tree_add(struct inode *inode)
{
struct btrfs_root *root = BTRFS_I(inode)->root;
struct btrfs_inode *entry;
struct rb_node **p = &root->inode_tree.rb_node;
struct rb_node *parent = NULL;
spin_lock(&root->inode_lock);
while (*p) {
parent = *p;
entry = rb_entry(parent, struct btrfs_inode, rb_node);
if (inode->i_ino < entry->vfs_inode.i_ino)
p = &(*p)->rb_left;
else if (inode->i_ino > entry->vfs_inode.i_ino)
p = &(*p)->rb_right;
else {
WARN_ON(!(entry->vfs_inode.i_state &
(I_WILL_FREE | I_FREEING | I_CLEAR)));
break;
}
}
rb_link_node(&BTRFS_I(inode)->rb_node, parent, p);
rb_insert_color(&BTRFS_I(inode)->rb_node, &root->inode_tree);
spin_unlock(&root->inode_lock);
}
static void inode_tree_del(struct inode *inode)
{
struct btrfs_root *root = BTRFS_I(inode)->root;
if (!RB_EMPTY_NODE(&BTRFS_I(inode)->rb_node)) {
spin_lock(&root->inode_lock);
rb_erase(&BTRFS_I(inode)->rb_node, &root->inode_tree);
spin_unlock(&root->inode_lock);
RB_CLEAR_NODE(&BTRFS_I(inode)->rb_node);
}
}
static noinline void init_btrfs_i(struct inode *inode) static noinline void init_btrfs_i(struct inode *inode)
{ {
struct btrfs_inode *bi = BTRFS_I(inode); struct btrfs_inode *bi = BTRFS_I(inode);
...@@ -3130,6 +3164,7 @@ static noinline void init_btrfs_i(struct inode *inode) ...@@ -3130,6 +3164,7 @@ static noinline void init_btrfs_i(struct inode *inode)
inode->i_mapping, GFP_NOFS); inode->i_mapping, GFP_NOFS);
INIT_LIST_HEAD(&BTRFS_I(inode)->delalloc_inodes); INIT_LIST_HEAD(&BTRFS_I(inode)->delalloc_inodes);
INIT_LIST_HEAD(&BTRFS_I(inode)->ordered_operations); INIT_LIST_HEAD(&BTRFS_I(inode)->ordered_operations);
RB_CLEAR_NODE(&BTRFS_I(inode)->rb_node);
btrfs_ordered_inode_tree_init(&BTRFS_I(inode)->ordered_tree); btrfs_ordered_inode_tree_init(&BTRFS_I(inode)->ordered_tree);
mutex_init(&BTRFS_I(inode)->extent_mutex); mutex_init(&BTRFS_I(inode)->extent_mutex);
mutex_init(&BTRFS_I(inode)->log_mutex); mutex_init(&BTRFS_I(inode)->log_mutex);
...@@ -3152,26 +3187,9 @@ static int btrfs_find_actor(struct inode *inode, void *opaque) ...@@ -3152,26 +3187,9 @@ static int btrfs_find_actor(struct inode *inode, void *opaque)
args->root == BTRFS_I(inode)->root; args->root == BTRFS_I(inode)->root;
} }
struct inode *btrfs_ilookup(struct super_block *s, u64 objectid, static struct inode *btrfs_iget_locked(struct super_block *s,
struct btrfs_root *root, int wait) u64 objectid,
{ struct btrfs_root *root)
struct inode *inode;
struct btrfs_iget_args args;
args.ino = objectid;
args.root = root;
if (wait) {
inode = ilookup5(s, objectid, btrfs_find_actor,
(void *)&args);
} else {
inode = ilookup5_nowait(s, objectid, btrfs_find_actor,
(void *)&args);
}
return inode;
}
struct inode *btrfs_iget_locked(struct super_block *s, u64 objectid,
struct btrfs_root *root)
{ {
struct inode *inode; struct inode *inode;
struct btrfs_iget_args args; struct btrfs_iget_args args;
...@@ -3188,24 +3206,21 @@ struct inode *btrfs_iget_locked(struct super_block *s, u64 objectid, ...@@ -3188,24 +3206,21 @@ struct inode *btrfs_iget_locked(struct super_block *s, u64 objectid,
* Returns in *is_new if the inode was read from disk * Returns in *is_new if the inode was read from disk
*/ */
struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location, struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
struct btrfs_root *root, int *is_new) struct btrfs_root *root)
{ {
struct inode *inode; struct inode *inode;
inode = btrfs_iget_locked(s, location->objectid, root); inode = btrfs_iget_locked(s, location->objectid, root);
if (!inode) if (!inode)
return ERR_PTR(-EACCES); return ERR_PTR(-ENOMEM);
if (inode->i_state & I_NEW) { if (inode->i_state & I_NEW) {
BTRFS_I(inode)->root = root; BTRFS_I(inode)->root = root;
memcpy(&BTRFS_I(inode)->location, location, sizeof(*location)); memcpy(&BTRFS_I(inode)->location, location, sizeof(*location));
btrfs_read_locked_inode(inode); btrfs_read_locked_inode(inode);
inode_tree_add(inode);
unlock_new_inode(inode); unlock_new_inode(inode);
if (is_new)
*is_new = 1;
} else {
if (is_new)
*is_new = 0;
} }
return inode; return inode;
...@@ -3218,7 +3233,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry) ...@@ -3218,7 +3233,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
struct btrfs_root *root = bi->root; struct btrfs_root *root = bi->root;
struct btrfs_root *sub_root = root; struct btrfs_root *sub_root = root;
struct btrfs_key location; struct btrfs_key location;
int ret, new; int ret;
if (dentry->d_name.len > BTRFS_NAME_LEN) if (dentry->d_name.len > BTRFS_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG); return ERR_PTR(-ENAMETOOLONG);
...@@ -3236,7 +3251,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry) ...@@ -3236,7 +3251,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
return ERR_PTR(ret); return ERR_PTR(ret);
if (ret > 0) if (ret > 0)
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);
inode = btrfs_iget(dir->i_sb, &location, sub_root, &new); inode = btrfs_iget(dir->i_sb, &location, sub_root);
if (IS_ERR(inode)) if (IS_ERR(inode))
return ERR_CAST(inode); return ERR_CAST(inode);
} }
...@@ -3574,9 +3589,9 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans, ...@@ -3574,9 +3589,9 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
btrfs_find_block_group(root, 0, alloc_hint, owner); btrfs_find_block_group(root, 0, alloc_hint, owner);
if ((mode & S_IFREG)) { if ((mode & S_IFREG)) {
if (btrfs_test_opt(root, NODATASUM)) if (btrfs_test_opt(root, NODATASUM))
btrfs_set_flag(inode, NODATASUM); BTRFS_I(inode)->flags |= BTRFS_INODE_NODATASUM;
if (btrfs_test_opt(root, NODATACOW)) if (btrfs_test_opt(root, NODATACOW))
btrfs_set_flag(inode, NODATACOW); BTRFS_I(inode)->flags |= BTRFS_INODE_NODATACOW;
} }
key[0].objectid = objectid; key[0].objectid = objectid;
...@@ -3630,7 +3645,10 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans, ...@@ -3630,7 +3645,10 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
location->offset = 0; location->offset = 0;
btrfs_set_key_type(location, BTRFS_INODE_ITEM_KEY); btrfs_set_key_type(location, BTRFS_INODE_ITEM_KEY);
btrfs_inherit_iflags(inode, dir);
insert_inode_hash(inode); insert_inode_hash(inode);
inode_tree_add(inode);
return inode; return inode;
fail: fail:
if (dir) if (dir)
...@@ -4683,6 +4701,7 @@ void btrfs_destroy_inode(struct inode *inode) ...@@ -4683,6 +4701,7 @@ void btrfs_destroy_inode(struct inode *inode)
btrfs_put_ordered_extent(ordered); btrfs_put_ordered_extent(ordered);
} }
} }
inode_tree_del(inode);
btrfs_drop_extent_cache(inode, 0, (u64)-1, 0); btrfs_drop_extent_cache(inode, 0, (u64)-1, 0);
kmem_cache_free(btrfs_inode_cachep, BTRFS_I(inode)); kmem_cache_free(btrfs_inode_cachep, BTRFS_I(inode));
} }
...@@ -5061,7 +5080,7 @@ static int prealloc_file_range(struct btrfs_trans_handle *trans, ...@@ -5061,7 +5080,7 @@ static int prealloc_file_range(struct btrfs_trans_handle *trans,
out: out:
if (cur_offset > start) { if (cur_offset > start) {
inode->i_ctime = CURRENT_TIME; inode->i_ctime = CURRENT_TIME;
btrfs_set_flag(inode, PREALLOC); BTRFS_I(inode)->flags |= BTRFS_INODE_PREALLOC;
if (!(mode & FALLOC_FL_KEEP_SIZE) && if (!(mode & FALLOC_FL_KEEP_SIZE) &&
cur_offset > i_size_read(inode)) cur_offset > i_size_read(inode))
btrfs_i_size_write(inode, cur_offset); btrfs_i_size_write(inode, cur_offset);
...@@ -5182,7 +5201,7 @@ static int btrfs_set_page_dirty(struct page *page) ...@@ -5182,7 +5201,7 @@ static int btrfs_set_page_dirty(struct page *page)
static int btrfs_permission(struct inode *inode, int mask) static int btrfs_permission(struct inode *inode, int mask)
{ {
if (btrfs_test_flag(inode, READONLY) && (mask & MAY_WRITE)) if ((BTRFS_I(inode)->flags & BTRFS_INODE_READONLY) && (mask & MAY_WRITE))
return -EACCES; return -EACCES;
return generic_permission(inode, mask, btrfs_check_acl); return generic_permission(inode, mask, btrfs_check_acl);
} }
......
...@@ -50,7 +50,177 @@ ...@@ -50,7 +50,177 @@
#include "volumes.h" #include "volumes.h"
#include "locking.h" #include "locking.h"
/* Mask out flags that are inappropriate for the given type of inode. */
static inline __u32 btrfs_mask_flags(umode_t mode, __u32 flags)
{
if (S_ISDIR(mode))
return flags;
else if (S_ISREG(mode))
return flags & ~FS_DIRSYNC_FL;
else
return flags & (FS_NODUMP_FL | FS_NOATIME_FL);
}
/*
* Export inode flags to the format expected by the FS_IOC_GETFLAGS ioctl.
*/
static unsigned int btrfs_flags_to_ioctl(unsigned int flags)
{
unsigned int iflags = 0;
if (flags & BTRFS_INODE_SYNC)
iflags |= FS_SYNC_FL;
if (flags & BTRFS_INODE_IMMUTABLE)
iflags |= FS_IMMUTABLE_FL;
if (flags & BTRFS_INODE_APPEND)
iflags |= FS_APPEND_FL;
if (flags & BTRFS_INODE_NODUMP)
iflags |= FS_NODUMP_FL;
if (flags & BTRFS_INODE_NOATIME)
iflags |= FS_NOATIME_FL;
if (flags & BTRFS_INODE_DIRSYNC)
iflags |= FS_DIRSYNC_FL;
return iflags;
}
/*
* Update inode->i_flags based on the btrfs internal flags.
*/
void btrfs_update_iflags(struct inode *inode)
{
struct btrfs_inode *ip = BTRFS_I(inode);
inode->i_flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC);
if (ip->flags & BTRFS_INODE_SYNC)
inode->i_flags |= S_SYNC;
if (ip->flags & BTRFS_INODE_IMMUTABLE)
inode->i_flags |= S_IMMUTABLE;
if (ip->flags & BTRFS_INODE_APPEND)
inode->i_flags |= S_APPEND;
if (ip->flags & BTRFS_INODE_NOATIME)
inode->i_flags |= S_NOATIME;
if (ip->flags & BTRFS_INODE_DIRSYNC)
inode->i_flags |= S_DIRSYNC;
}
/*
* Inherit flags from the parent inode.
*
* Unlike extN we don't have any flags we don't want to inherit currently.
*/
void btrfs_inherit_iflags(struct inode *inode, struct inode *dir)
{
unsigned int flags;
if (!dir)
return;
flags = BTRFS_I(dir)->flags;
if (S_ISREG(inode->i_mode))
flags &= ~BTRFS_INODE_DIRSYNC;
else if (!S_ISDIR(inode->i_mode))
flags &= (BTRFS_INODE_NODUMP | BTRFS_INODE_NOATIME);
BTRFS_I(inode)->flags = flags;
btrfs_update_iflags(inode);
}
static int btrfs_ioctl_getflags(struct file *file, void __user *arg)
{
struct btrfs_inode *ip = BTRFS_I(file->f_path.dentry->d_inode);
unsigned int flags = btrfs_flags_to_ioctl(ip->flags);
if (copy_to_user(arg, &flags, sizeof(flags)))
return -EFAULT;
return 0;
}
static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
{
struct inode *inode = file->f_path.dentry->d_inode;
struct btrfs_inode *ip = BTRFS_I(inode);
struct btrfs_root *root = ip->root;
struct btrfs_trans_handle *trans;
unsigned int flags, oldflags;
int ret;
if (copy_from_user(&flags, arg, sizeof(flags)))
return -EFAULT;
if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
FS_NOATIME_FL | FS_NODUMP_FL | \
FS_SYNC_FL | FS_DIRSYNC_FL))
return -EOPNOTSUPP;
if (!is_owner_or_cap(inode))
return -EACCES;
mutex_lock(&inode->i_mutex);
flags = btrfs_mask_flags(inode->i_mode, flags);
oldflags = btrfs_flags_to_ioctl(ip->flags);
if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
if (!capable(CAP_LINUX_IMMUTABLE)) {
ret = -EPERM;
goto out_unlock;
}
}
ret = mnt_want_write(file->f_path.mnt);
if (ret)
goto out_unlock;
if (flags & FS_SYNC_FL)
ip->flags |= BTRFS_INODE_SYNC;
else
ip->flags &= ~BTRFS_INODE_SYNC;
if (flags & FS_IMMUTABLE_FL)
ip->flags |= BTRFS_INODE_IMMUTABLE;
else
ip->flags &= ~BTRFS_INODE_IMMUTABLE;
if (flags & FS_APPEND_FL)
ip->flags |= BTRFS_INODE_APPEND;
else
ip->flags &= ~BTRFS_INODE_APPEND;
if (flags & FS_NODUMP_FL)
ip->flags |= BTRFS_INODE_NODUMP;
else
ip->flags &= ~BTRFS_INODE_NODUMP;
if (flags & FS_NOATIME_FL)
ip->flags |= BTRFS_INODE_NOATIME;
else
ip->flags &= ~BTRFS_INODE_NOATIME;
if (flags & FS_DIRSYNC_FL)
ip->flags |= BTRFS_INODE_DIRSYNC;
else
ip->flags &= ~BTRFS_INODE_DIRSYNC;
trans = btrfs_join_transaction(root, 1);
BUG_ON(!trans);
ret = btrfs_update_inode(trans, root, inode);
BUG_ON(ret);
btrfs_update_iflags(inode);
inode->i_ctime = CURRENT_TIME;
btrfs_end_transaction(trans, root);
mnt_drop_write(file->f_path.mnt);
out_unlock:
mutex_unlock(&inode->i_mutex);
return 0;
}
static int btrfs_ioctl_getversion(struct file *file, int __user *arg)
{
struct inode *inode = file->f_path.dentry->d_inode;
return put_user(inode->i_generation, arg);
}
static noinline int create_subvol(struct btrfs_root *root, static noinline int create_subvol(struct btrfs_root *root,
struct dentry *dentry, struct dentry *dentry,
...@@ -82,22 +252,25 @@ static noinline int create_subvol(struct btrfs_root *root, ...@@ -82,22 +252,25 @@ static noinline int create_subvol(struct btrfs_root *root,
if (ret) if (ret)
goto fail; goto fail;
leaf = btrfs_alloc_free_block(trans, root, root->leafsize, 0, leaf = btrfs_alloc_free_block(trans, root, root->leafsize,
objectid, trans->transid, 0, 0, 0); 0, objectid, NULL, 0, 0, 0);
if (IS_ERR(leaf)) { if (IS_ERR(leaf)) {
ret = PTR_ERR(leaf); ret = PTR_ERR(leaf);
goto fail; goto fail;
} }
btrfs_set_header_nritems(leaf, 0); memset_extent_buffer(leaf, 0, 0, sizeof(struct btrfs_header));
btrfs_set_header_level(leaf, 0);
btrfs_set_header_bytenr(leaf, leaf->start); btrfs_set_header_bytenr(leaf, leaf->start);
btrfs_set_header_generation(leaf, trans->transid); btrfs_set_header_generation(leaf, trans->transid);
btrfs_set_header_backref_rev(leaf, BTRFS_MIXED_BACKREF_REV);
btrfs_set_header_owner(leaf, objectid); btrfs_set_header_owner(leaf, objectid);
write_extent_buffer(leaf, root->fs_info->fsid, write_extent_buffer(leaf, root->fs_info->fsid,
(unsigned long)btrfs_header_fsid(leaf), (unsigned long)btrfs_header_fsid(leaf),
BTRFS_FSID_SIZE); BTRFS_FSID_SIZE);
write_extent_buffer(leaf, root->fs_info->chunk_tree_uuid,
(unsigned long)btrfs_header_chunk_tree_uuid(leaf),
BTRFS_UUID_SIZE);
btrfs_mark_buffer_dirty(leaf); btrfs_mark_buffer_dirty(leaf);
inode_item = &root_item.inode; inode_item = &root_item.inode;
...@@ -125,7 +298,7 @@ static noinline int create_subvol(struct btrfs_root *root, ...@@ -125,7 +298,7 @@ static noinline int create_subvol(struct btrfs_root *root,
btrfs_set_root_dirid(&root_item, new_dirid); btrfs_set_root_dirid(&root_item, new_dirid);
key.objectid = objectid; key.objectid = objectid;
key.offset = 1; key.offset = 0;
btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
ret = btrfs_insert_root(trans, root->fs_info->tree_root, &key, ret = btrfs_insert_root(trans, root->fs_info->tree_root, &key,
&root_item); &root_item);
...@@ -911,10 +1084,10 @@ static long btrfs_ioctl_clone(struct file *file, unsigned long srcfd, ...@@ -911,10 +1084,10 @@ static long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
if (disko) { if (disko) {
inode_add_bytes(inode, datal); inode_add_bytes(inode, datal);
ret = btrfs_inc_extent_ref(trans, root, ret = btrfs_inc_extent_ref(trans, root,
disko, diskl, leaf->start, disko, diskl, 0,
root->root_key.objectid, root->root_key.objectid,
trans->transid, inode->i_ino,
inode->i_ino); new_key.offset - datao);
BUG_ON(ret); BUG_ON(ret);
} }
} else if (type == BTRFS_FILE_EXTENT_INLINE) { } else if (type == BTRFS_FILE_EXTENT_INLINE) {
...@@ -1074,6 +1247,12 @@ long btrfs_ioctl(struct file *file, unsigned int ...@@ -1074,6 +1247,12 @@ long btrfs_ioctl(struct file *file, unsigned int
void __user *argp = (void __user *)arg; void __user *argp = (void __user *)arg;
switch (cmd) { switch (cmd) {
case FS_IOC_GETFLAGS:
return btrfs_ioctl_getflags(file, argp);
case FS_IOC_SETFLAGS:
return btrfs_ioctl_setflags(file, argp);
case FS_IOC_GETVERSION:
return btrfs_ioctl_getversion(file, argp);
case BTRFS_IOC_SNAP_CREATE: case BTRFS_IOC_SNAP_CREATE:
return btrfs_ioctl_snap_create(file, argp, 0); return btrfs_ioctl_snap_create(file, argp, 0);
case BTRFS_IOC_SUBVOL_CREATE: case BTRFS_IOC_SUBVOL_CREATE:
......
...@@ -45,22 +45,132 @@ static void print_dev_item(struct extent_buffer *eb, ...@@ -45,22 +45,132 @@ static void print_dev_item(struct extent_buffer *eb,
(unsigned long long)btrfs_device_total_bytes(eb, dev_item), (unsigned long long)btrfs_device_total_bytes(eb, dev_item),
(unsigned long long)btrfs_device_bytes_used(eb, dev_item)); (unsigned long long)btrfs_device_bytes_used(eb, dev_item));
} }
static void print_extent_data_ref(struct extent_buffer *eb,
struct btrfs_extent_data_ref *ref)
{
printk(KERN_INFO "\t\textent data backref root %llu "
"objectid %llu offset %llu count %u\n",
(unsigned long long)btrfs_extent_data_ref_root(eb, ref),
(unsigned long long)btrfs_extent_data_ref_objectid(eb, ref),
(unsigned long long)btrfs_extent_data_ref_offset(eb, ref),
btrfs_extent_data_ref_count(eb, ref));
}
static void print_extent_item(struct extent_buffer *eb, int slot)
{
struct btrfs_extent_item *ei;
struct btrfs_extent_inline_ref *iref;
struct btrfs_extent_data_ref *dref;
struct btrfs_shared_data_ref *sref;
struct btrfs_disk_key key;
unsigned long end;
unsigned long ptr;
int type;
u32 item_size = btrfs_item_size_nr(eb, slot);
u64 flags;
u64 offset;
if (item_size < sizeof(*ei)) {
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
struct btrfs_extent_item_v0 *ei0;
BUG_ON(item_size != sizeof(*ei0));
ei0 = btrfs_item_ptr(eb, slot, struct btrfs_extent_item_v0);
printk(KERN_INFO "\t\textent refs %u\n",
btrfs_extent_refs_v0(eb, ei0));
return;
#else
BUG();
#endif
}
ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item);
flags = btrfs_extent_flags(eb, ei);
printk(KERN_INFO "\t\textent refs %llu gen %llu flags %llu\n",
(unsigned long long)btrfs_extent_refs(eb, ei),
(unsigned long long)btrfs_extent_generation(eb, ei),
(unsigned long long)flags);
if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
struct btrfs_tree_block_info *info;
info = (struct btrfs_tree_block_info *)(ei + 1);
btrfs_tree_block_key(eb, info, &key);
printk(KERN_INFO "\t\ttree block key (%llu %x %llu) "
"level %d\n",
(unsigned long long)btrfs_disk_key_objectid(&key),
key.type,
(unsigned long long)btrfs_disk_key_offset(&key),
btrfs_tree_block_level(eb, info));
iref = (struct btrfs_extent_inline_ref *)(info + 1);
} else {
iref = (struct btrfs_extent_inline_ref *)(ei + 1);
}
ptr = (unsigned long)iref;
end = (unsigned long)ei + item_size;
while (ptr < end) {
iref = (struct btrfs_extent_inline_ref *)ptr;
type = btrfs_extent_inline_ref_type(eb, iref);
offset = btrfs_extent_inline_ref_offset(eb, iref);
switch (type) {
case BTRFS_TREE_BLOCK_REF_KEY:
printk(KERN_INFO "\t\ttree block backref "
"root %llu\n", (unsigned long long)offset);
break;
case BTRFS_SHARED_BLOCK_REF_KEY:
printk(KERN_INFO "\t\tshared block backref "
"parent %llu\n", (unsigned long long)offset);
break;
case BTRFS_EXTENT_DATA_REF_KEY:
dref = (struct btrfs_extent_data_ref *)(&iref->offset);
print_extent_data_ref(eb, dref);
break;
case BTRFS_SHARED_DATA_REF_KEY:
sref = (struct btrfs_shared_data_ref *)(iref + 1);
printk(KERN_INFO "\t\tshared data backref "
"parent %llu count %u\n",
(unsigned long long)offset,
btrfs_shared_data_ref_count(eb, sref));
break;
default:
BUG();
}
ptr += btrfs_extent_inline_ref_size(type);
}
WARN_ON(ptr > end);
}
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
static void print_extent_ref_v0(struct extent_buffer *eb, int slot)
{
struct btrfs_extent_ref_v0 *ref0;
ref0 = btrfs_item_ptr(eb, slot, struct btrfs_extent_ref_v0);
printk("\t\textent back ref root %llu gen %llu "
"owner %llu num_refs %lu\n",
(unsigned long long)btrfs_ref_root_v0(eb, ref0),
(unsigned long long)btrfs_ref_generation_v0(eb, ref0),
(unsigned long long)btrfs_ref_objectid_v0(eb, ref0),
(unsigned long)btrfs_ref_count_v0(eb, ref0));
}
#endif
void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l) void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
{ {
int i; int i;
u32 type;
u32 nr = btrfs_header_nritems(l); u32 nr = btrfs_header_nritems(l);
struct btrfs_item *item; struct btrfs_item *item;
struct btrfs_extent_item *ei;
struct btrfs_root_item *ri; struct btrfs_root_item *ri;
struct btrfs_dir_item *di; struct btrfs_dir_item *di;
struct btrfs_inode_item *ii; struct btrfs_inode_item *ii;
struct btrfs_block_group_item *bi; struct btrfs_block_group_item *bi;
struct btrfs_file_extent_item *fi; struct btrfs_file_extent_item *fi;
struct btrfs_extent_data_ref *dref;
struct btrfs_shared_data_ref *sref;
struct btrfs_dev_extent *dev_extent;
struct btrfs_key key; struct btrfs_key key;
struct btrfs_key found_key; struct btrfs_key found_key;
struct btrfs_extent_ref *ref;
struct btrfs_dev_extent *dev_extent;
u32 type;
printk(KERN_INFO "leaf %llu total ptrs %d free space %d\n", printk(KERN_INFO "leaf %llu total ptrs %d free space %d\n",
(unsigned long long)btrfs_header_bytenr(l), nr, (unsigned long long)btrfs_header_bytenr(l), nr,
...@@ -100,20 +210,25 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l) ...@@ -100,20 +210,25 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
btrfs_disk_root_refs(l, ri)); btrfs_disk_root_refs(l, ri));
break; break;
case BTRFS_EXTENT_ITEM_KEY: case BTRFS_EXTENT_ITEM_KEY:
ei = btrfs_item_ptr(l, i, struct btrfs_extent_item); print_extent_item(l, i);
printk(KERN_INFO "\t\textent data refs %u\n", break;
btrfs_extent_refs(l, ei)); case BTRFS_TREE_BLOCK_REF_KEY:
break; printk(KERN_INFO "\t\ttree block backref\n");
case BTRFS_EXTENT_REF_KEY: break;
ref = btrfs_item_ptr(l, i, struct btrfs_extent_ref); case BTRFS_SHARED_BLOCK_REF_KEY:
printk(KERN_INFO "\t\textent back ref root %llu " printk(KERN_INFO "\t\tshared block backref\n");
"gen %llu owner %llu num_refs %lu\n", break;
(unsigned long long)btrfs_ref_root(l, ref), case BTRFS_EXTENT_DATA_REF_KEY:
(unsigned long long)btrfs_ref_generation(l, ref), dref = btrfs_item_ptr(l, i,
(unsigned long long)btrfs_ref_objectid(l, ref), struct btrfs_extent_data_ref);
(unsigned long)btrfs_ref_num_refs(l, ref)); print_extent_data_ref(l, dref);
break;
case BTRFS_SHARED_DATA_REF_KEY:
sref = btrfs_item_ptr(l, i,
struct btrfs_shared_data_ref);
printk(KERN_INFO "\t\tshared data backref count %u\n",
btrfs_shared_data_ref_count(l, sref));
break; break;
case BTRFS_EXTENT_DATA_KEY: case BTRFS_EXTENT_DATA_KEY:
fi = btrfs_item_ptr(l, i, fi = btrfs_item_ptr(l, i,
struct btrfs_file_extent_item); struct btrfs_file_extent_item);
...@@ -139,6 +254,12 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l) ...@@ -139,6 +254,12 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
(unsigned long long) (unsigned long long)
btrfs_file_extent_ram_bytes(l, fi)); btrfs_file_extent_ram_bytes(l, fi));
break; break;
case BTRFS_EXTENT_REF_V0_KEY:
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
print_extent_ref_v0(l, i);
#else
BUG();
#endif
case BTRFS_BLOCK_GROUP_ITEM_KEY: case BTRFS_BLOCK_GROUP_ITEM_KEY:
bi = btrfs_item_ptr(l, i, bi = btrfs_item_ptr(l, i,
struct btrfs_block_group_item); struct btrfs_block_group_item);
......
/*
* Copyright (C) 2009 Oracle. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 021110-1307, USA.
*/
#include <linux/sched.h>
#include <linux/pagemap.h>
#include <linux/writeback.h>
#include <linux/blkdev.h>
#include <linux/rbtree.h>
#include "ctree.h"
#include "disk-io.h"
#include "transaction.h"
#include "volumes.h"
#include "locking.h"
#include "btrfs_inode.h"
#include "async-thread.h"
/*
* backref_node, mapping_node and tree_block start with this
*/
struct tree_entry {
struct rb_node rb_node;
u64 bytenr;
};
/*
* present a tree block in the backref cache
*/
struct backref_node {
struct rb_node rb_node;
u64 bytenr;
/* objectid tree block owner */
u64 owner;
/* list of upper level blocks reference this block */
struct list_head upper;
/* list of child blocks in the cache */
struct list_head lower;
/* NULL if this node is not tree root */
struct btrfs_root *root;
/* extent buffer got by COW the block */
struct extent_buffer *eb;
/* level of tree block */
unsigned int level:8;
/* 1 if the block is root of old snapshot */
unsigned int old_root:1;
/* 1 if no child blocks in the cache */
unsigned int lowest:1;
/* is the extent buffer locked */
unsigned int locked:1;
/* has the block been processed */
unsigned int processed:1;
/* have backrefs of this block been checked */
unsigned int checked:1;
};
/*
* present a block pointer in the backref cache
*/
struct backref_edge {
struct list_head list[2];
struct backref_node *node[2];
u64 blockptr;
};
#define LOWER 0
#define UPPER 1
struct backref_cache {
/* red black tree of all backref nodes in the cache */
struct rb_root rb_root;
/* list of backref nodes with no child block in the cache */
struct list_head pending[BTRFS_MAX_LEVEL];
spinlock_t lock;
};
/*
* map address of tree root to tree
*/
struct mapping_node {
struct rb_node rb_node;
u64 bytenr;
void *data;
};
struct mapping_tree {
struct rb_root rb_root;
spinlock_t lock;
};
/*
* present a tree block to process
*/
struct tree_block {
struct rb_node rb_node;
u64 bytenr;
struct btrfs_key key;
unsigned int level:8;
unsigned int key_ready:1;
};
/* inode vector */
#define INODEVEC_SIZE 16
struct inodevec {
struct list_head list;
struct inode *inode[INODEVEC_SIZE];
int nr;
};
struct reloc_control {
/* block group to relocate */
struct btrfs_block_group_cache *block_group;
/* extent tree */
struct btrfs_root *extent_root;
/* inode for moving data */
struct inode *data_inode;
struct btrfs_workers workers;
/* tree blocks have been processed */
struct extent_io_tree processed_blocks;
/* map start of tree root to corresponding reloc tree */
struct mapping_tree reloc_root_tree;
/* list of reloc trees */
struct list_head reloc_roots;
u64 search_start;
u64 extents_found;
u64 extents_skipped;
int stage;
int create_reloc_root;
unsigned int found_file_extent:1;
unsigned int found_old_snapshot:1;
};
/* stages of data relocation */
#define MOVE_DATA_EXTENTS 0
#define UPDATE_DATA_PTRS 1
/*
* merge reloc tree to corresponding fs tree in worker threads
*/
struct async_merge {
struct btrfs_work work;
struct reloc_control *rc;
struct btrfs_root *root;
struct completion *done;
atomic_t *num_pending;
};
static void mapping_tree_init(struct mapping_tree *tree)
{
tree->rb_root.rb_node = NULL;
spin_lock_init(&tree->lock);
}
static void backref_cache_init(struct backref_cache *cache)
{
int i;
cache->rb_root.rb_node = NULL;
for (i = 0; i < BTRFS_MAX_LEVEL; i++)
INIT_LIST_HEAD(&cache->pending[i]);
spin_lock_init(&cache->lock);
}
static void backref_node_init(struct backref_node *node)
{
memset(node, 0, sizeof(*node));
INIT_LIST_HEAD(&node->upper);
INIT_LIST_HEAD(&node->lower);
RB_CLEAR_NODE(&node->rb_node);
}
static struct rb_node *tree_insert(struct rb_root *root, u64 bytenr,
struct rb_node *node)
{
struct rb_node **p = &root->rb_node;
struct rb_node *parent = NULL;
struct tree_entry *entry;
while (*p) {
parent = *p;
entry = rb_entry(parent, struct tree_entry, rb_node);
if (bytenr < entry->bytenr)
p = &(*p)->rb_left;
else if (bytenr > entry->bytenr)
p = &(*p)->rb_right;
else
return parent;
}
rb_link_node(node, parent, p);
rb_insert_color(node, root);
return NULL;
}
static struct rb_node *tree_search(struct rb_root *root, u64 bytenr)
{
struct rb_node *n = root->rb_node;
struct tree_entry *entry;
while (n) {
entry = rb_entry(n, struct tree_entry, rb_node);
if (bytenr < entry->bytenr)
n = n->rb_left;
else if (bytenr > entry->bytenr)
n = n->rb_right;
else
return n;
}
return NULL;
}
/*
* walk up backref nodes until reach node presents tree root
*/
static struct backref_node *walk_up_backref(struct backref_node *node,
struct backref_edge *edges[],
int *index)
{
struct backref_edge *edge;
int idx = *index;
while (!list_empty(&node->upper)) {
edge = list_entry(node->upper.next,
struct backref_edge, list[LOWER]);
edges[idx++] = edge;
node = edge->node[UPPER];
}
*index = idx;
return node;
}
/*
* walk down backref nodes to find start of next reference path
*/
static struct backref_node *walk_down_backref(struct backref_edge *edges[],
int *index)
{
struct backref_edge *edge;
struct backref_node *lower;
int idx = *index;
while (idx > 0) {
edge = edges[idx - 1];
lower = edge->node[LOWER];
if (list_is_last(&edge->list[LOWER], &lower->upper)) {
idx--;
continue;
}
edge = list_entry(edge->list[LOWER].next,
struct backref_edge, list[LOWER]);
edges[idx - 1] = edge;
*index = idx;
return edge->node[UPPER];
}
*index = 0;
return NULL;
}
static void drop_node_buffer(struct backref_node *node)
{
if (node->eb) {
if (node->locked) {
btrfs_tree_unlock(node->eb);
node->locked = 0;
}
free_extent_buffer(node->eb);
node->eb = NULL;
}
}
static void drop_backref_node(struct backref_cache *tree,
struct backref_node *node)
{
BUG_ON(!node->lowest);
BUG_ON(!list_empty(&node->upper));
drop_node_buffer(node);
list_del(&node->lower);
rb_erase(&node->rb_node, &tree->rb_root);
kfree(node);
}
/*
* remove a backref node from the backref cache
*/
static void remove_backref_node(struct backref_cache *cache,
struct backref_node *node)
{
struct backref_node *upper;
struct backref_edge *edge;
if (!node)
return;
BUG_ON(!node->lowest);
while (!list_empty(&node->upper)) {
edge = list_entry(node->upper.next, struct backref_edge,
list[LOWER]);
upper = edge->node[UPPER];
list_del(&edge->list[LOWER]);
list_del(&edge->list[UPPER]);
kfree(edge);
/*
* add the node to pending list if no other
* child block cached.
*/
if (list_empty(&upper->lower)) {
list_add_tail(&upper->lower,
&cache->pending[upper->level]);
upper->lowest = 1;
}
}
drop_backref_node(cache, node);
}
/*
* find reloc tree by address of tree root
*/
static struct btrfs_root *find_reloc_root(struct reloc_control *rc,
u64 bytenr)
{
struct rb_node *rb_node;
struct mapping_node *node;
struct btrfs_root *root = NULL;
spin_lock(&rc->reloc_root_tree.lock);
rb_node = tree_search(&rc->reloc_root_tree.rb_root, bytenr);
if (rb_node) {
node = rb_entry(rb_node, struct mapping_node, rb_node);
root = (struct btrfs_root *)node->data;
}
spin_unlock(&rc->reloc_root_tree.lock);
return root;
}
static int is_cowonly_root(u64 root_objectid)
{
if (root_objectid == BTRFS_ROOT_TREE_OBJECTID ||
root_objectid == BTRFS_EXTENT_TREE_OBJECTID ||
root_objectid == BTRFS_CHUNK_TREE_OBJECTID ||
root_objectid == BTRFS_DEV_TREE_OBJECTID ||
root_objectid == BTRFS_TREE_LOG_OBJECTID ||
root_objectid == BTRFS_CSUM_TREE_OBJECTID)
return 1;
return 0;
}
static struct btrfs_root *read_fs_root(struct btrfs_fs_info *fs_info,
u64 root_objectid)
{
struct btrfs_key key;
key.objectid = root_objectid;
key.type = BTRFS_ROOT_ITEM_KEY;
if (is_cowonly_root(root_objectid))
key.offset = 0;
else
key.offset = (u64)-1;
return btrfs_read_fs_root_no_name(fs_info, &key);
}
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
static noinline_for_stack
struct btrfs_root *find_tree_root(struct reloc_control *rc,
struct extent_buffer *leaf,
struct btrfs_extent_ref_v0 *ref0)
{
struct btrfs_root *root;
u64 root_objectid = btrfs_ref_root_v0(leaf, ref0);
u64 generation = btrfs_ref_generation_v0(leaf, ref0);
BUG_ON(root_objectid == BTRFS_TREE_RELOC_OBJECTID);
root = read_fs_root(rc->extent_root->fs_info, root_objectid);
BUG_ON(IS_ERR(root));
if (root->ref_cows &&
generation != btrfs_root_generation(&root->root_item))
return NULL;
return root;
}
#endif
static noinline_for_stack
int find_inline_backref(struct extent_buffer *leaf, int slot,
unsigned long *ptr, unsigned long *end)
{
struct btrfs_extent_item *ei;
struct btrfs_tree_block_info *bi;
u32 item_size;
item_size = btrfs_item_size_nr(leaf, slot);
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
if (item_size < sizeof(*ei)) {
WARN_ON(item_size != sizeof(struct btrfs_extent_item_v0));
return 1;
}
#endif
ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item);
WARN_ON(!(btrfs_extent_flags(leaf, ei) &
BTRFS_EXTENT_FLAG_TREE_BLOCK));
if (item_size <= sizeof(*ei) + sizeof(*bi)) {
WARN_ON(item_size < sizeof(*ei) + sizeof(*bi));
return 1;
}
bi = (struct btrfs_tree_block_info *)(ei + 1);
*ptr = (unsigned long)(bi + 1);
*end = (unsigned long)ei + item_size;
return 0;
}
/*
* build backref tree for a given tree block. root of the backref tree
* corresponds the tree block, leaves of the backref tree correspond
* roots of b-trees that reference the tree block.
*
* the basic idea of this function is check backrefs of a given block
* to find upper level blocks that refernece the block, and then check
* bakcrefs of these upper level blocks recursively. the recursion stop
* when tree root is reached or backrefs for the block is cached.
*
* NOTE: if we find backrefs for a block are cached, we know backrefs
* for all upper level blocks that directly/indirectly reference the
* block are also cached.
*/
static struct backref_node *build_backref_tree(struct reloc_control *rc,
struct backref_cache *cache,
struct btrfs_key *node_key,
int level, u64 bytenr)
{
struct btrfs_path *path1;
struct btrfs_path *path2;
struct extent_buffer *eb;
struct btrfs_root *root;
struct backref_node *cur;
struct backref_node *upper;
struct backref_node *lower;
struct backref_node *node = NULL;
struct backref_node *exist = NULL;
struct backref_edge *edge;
struct rb_node *rb_node;
struct btrfs_key key;
unsigned long end;
unsigned long ptr;
LIST_HEAD(list);
int ret;
int err = 0;
path1 = btrfs_alloc_path();
path2 = btrfs_alloc_path();
if (!path1 || !path2) {
err = -ENOMEM;
goto out;
}
node = kmalloc(sizeof(*node), GFP_NOFS);
if (!node) {
err = -ENOMEM;
goto out;
}
backref_node_init(node);
node->bytenr = bytenr;
node->owner = 0;
node->level = level;
node->lowest = 1;
cur = node;
again:
end = 0;
ptr = 0;
key.objectid = cur->bytenr;
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = (u64)-1;
path1->search_commit_root = 1;
path1->skip_locking = 1;
ret = btrfs_search_slot(NULL, rc->extent_root, &key, path1,
0, 0);
if (ret < 0) {
err = ret;
goto out;
}
BUG_ON(!ret || !path1->slots[0]);
path1->slots[0]--;
WARN_ON(cur->checked);
if (!list_empty(&cur->upper)) {
/*
* the backref was added previously when processsing
* backref of type BTRFS_TREE_BLOCK_REF_KEY
*/
BUG_ON(!list_is_singular(&cur->upper));
edge = list_entry(cur->upper.next, struct backref_edge,
list[LOWER]);
BUG_ON(!list_empty(&edge->list[UPPER]));
exist = edge->node[UPPER];
/*
* add the upper level block to pending list if we need
* check its backrefs
*/
if (!exist->checked)
list_add_tail(&edge->list[UPPER], &list);
} else {
exist = NULL;
}
while (1) {
cond_resched();
eb = path1->nodes[0];
if (ptr >= end) {
if (path1->slots[0] >= btrfs_header_nritems(eb)) {
ret = btrfs_next_leaf(rc->extent_root, path1);
if (ret < 0) {
err = ret;
goto out;
}
if (ret > 0)
break;
eb = path1->nodes[0];
}
btrfs_item_key_to_cpu(eb, &key, path1->slots[0]);
if (key.objectid != cur->bytenr) {
WARN_ON(exist);
break;
}
if (key.type == BTRFS_EXTENT_ITEM_KEY) {
ret = find_inline_backref(eb, path1->slots[0],
&ptr, &end);
if (ret)
goto next;
}
}
if (ptr < end) {
/* update key for inline back ref */
struct btrfs_extent_inline_ref *iref;
iref = (struct btrfs_extent_inline_ref *)ptr;
key.type = btrfs_extent_inline_ref_type(eb, iref);
key.offset = btrfs_extent_inline_ref_offset(eb, iref);
WARN_ON(key.type != BTRFS_TREE_BLOCK_REF_KEY &&
key.type != BTRFS_SHARED_BLOCK_REF_KEY);
}
if (exist &&
((key.type == BTRFS_TREE_BLOCK_REF_KEY &&
exist->owner == key.offset) ||
(key.type == BTRFS_SHARED_BLOCK_REF_KEY &&
exist->bytenr == key.offset))) {
exist = NULL;
goto next;
}
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
if (key.type == BTRFS_SHARED_BLOCK_REF_KEY ||
key.type == BTRFS_EXTENT_REF_V0_KEY) {
if (key.objectid == key.offset &&
key.type == BTRFS_EXTENT_REF_V0_KEY) {
struct btrfs_extent_ref_v0 *ref0;
ref0 = btrfs_item_ptr(eb, path1->slots[0],
struct btrfs_extent_ref_v0);
root = find_tree_root(rc, eb, ref0);
if (root)
cur->root = root;
else
cur->old_root = 1;
break;
}
#else
BUG_ON(key.type == BTRFS_EXTENT_REF_V0_KEY);
if (key.type == BTRFS_SHARED_BLOCK_REF_KEY) {
#endif
if (key.objectid == key.offset) {
/*
* only root blocks of reloc trees use
* backref of this type.
*/
root = find_reloc_root(rc, cur->bytenr);
BUG_ON(!root);
cur->root = root;
break;
}
edge = kzalloc(sizeof(*edge), GFP_NOFS);
if (!edge) {
err = -ENOMEM;
goto out;
}
rb_node = tree_search(&cache->rb_root, key.offset);
if (!rb_node) {
upper = kmalloc(sizeof(*upper), GFP_NOFS);
if (!upper) {
kfree(edge);
err = -ENOMEM;
goto out;
}
backref_node_init(upper);
upper->bytenr = key.offset;
upper->owner = 0;
upper->level = cur->level + 1;
/*
* backrefs for the upper level block isn't
* cached, add the block to pending list
*/
list_add_tail(&edge->list[UPPER], &list);
} else {
upper = rb_entry(rb_node, struct backref_node,
rb_node);
INIT_LIST_HEAD(&edge->list[UPPER]);
}
list_add(&edge->list[LOWER], &cur->upper);
edge->node[UPPER] = upper;
edge->node[LOWER] = cur;
goto next;
} else if (key.type != BTRFS_TREE_BLOCK_REF_KEY) {
goto next;
}
/* key.type == BTRFS_TREE_BLOCK_REF_KEY */
root = read_fs_root(rc->extent_root->fs_info, key.offset);
if (IS_ERR(root)) {
err = PTR_ERR(root);
goto out;
}
if (btrfs_root_level(&root->root_item) == cur->level) {
/* tree root */
BUG_ON(btrfs_root_bytenr(&root->root_item) !=
cur->bytenr);
cur->root = root;
break;
}
level = cur->level + 1;
/*
* searching the tree to find upper level blocks
* reference the block.
*/
path2->search_commit_root = 1;
path2->skip_locking = 1;
path2->lowest_level = level;
ret = btrfs_search_slot(NULL, root, node_key, path2, 0, 0);
path2->lowest_level = 0;
if (ret < 0) {
err = ret;
goto out;
}
eb = path2->nodes[level];
WARN_ON(btrfs_node_blockptr(eb, path2->slots[level]) !=
cur->bytenr);
lower = cur;
for (; level < BTRFS_MAX_LEVEL; level++) {
if (!path2->nodes[level]) {
BUG_ON(btrfs_root_bytenr(&root->root_item) !=
lower->bytenr);
lower->root = root;
break;
}
edge = kzalloc(sizeof(*edge), GFP_NOFS);
if (!edge) {
err = -ENOMEM;
goto out;
}
eb = path2->nodes[level];
rb_node = tree_search(&cache->rb_root, eb->start);
if (!rb_node) {
upper = kmalloc(sizeof(*upper), GFP_NOFS);
if (!upper) {
kfree(edge);
err = -ENOMEM;
goto out;
}
backref_node_init(upper);
upper->bytenr = eb->start;
upper->owner = btrfs_header_owner(eb);
upper->level = lower->level + 1;
/*
* if we know the block isn't shared
* we can void checking its backrefs.
*/
if (btrfs_block_can_be_shared(root, eb))
upper->checked = 0;
else
upper->checked = 1;
/*
* add the block to pending list if we
* need check its backrefs. only block
* at 'cur->level + 1' is added to the
* tail of pending list. this guarantees
* we check backrefs from lower level
* blocks to upper level blocks.
*/
if (!upper->checked &&
level == cur->level + 1) {
list_add_tail(&edge->list[UPPER],
&list);
} else
INIT_LIST_HEAD(&edge->list[UPPER]);
} else {
upper = rb_entry(rb_node, struct backref_node,
rb_node);
BUG_ON(!upper->checked);
INIT_LIST_HEAD(&edge->list[UPPER]);
}
list_add_tail(&edge->list[LOWER], &lower->upper);
edge->node[UPPER] = upper;
edge->node[LOWER] = lower;
if (rb_node)
break;
lower = upper;
upper = NULL;
}
btrfs_release_path(root, path2);
next:
if (ptr < end) {
ptr += btrfs_extent_inline_ref_size(key.type);
if (ptr >= end) {
WARN_ON(ptr > end);
ptr = 0;
end = 0;
}
}
if (ptr >= end)
path1->slots[0]++;
}
btrfs_release_path(rc->extent_root, path1);
cur->checked = 1;
WARN_ON(exist);
/* the pending list isn't empty, take the first block to process */
if (!list_empty(&list)) {
edge = list_entry(list.next, struct backref_edge, list[UPPER]);
list_del_init(&edge->list[UPPER]);
cur = edge->node[UPPER];
goto again;
}
/*
* everything goes well, connect backref nodes and insert backref nodes
* into the cache.
*/
BUG_ON(!node->checked);
rb_node = tree_insert(&cache->rb_root, node->bytenr, &node->rb_node);
BUG_ON(rb_node);
list_for_each_entry(edge, &node->upper, list[LOWER])
list_add_tail(&edge->list[UPPER], &list);
while (!list_empty(&list)) {
edge = list_entry(list.next, struct backref_edge, list[UPPER]);
list_del_init(&edge->list[UPPER]);
upper = edge->node[UPPER];
if (!RB_EMPTY_NODE(&upper->rb_node)) {
if (upper->lowest) {
list_del_init(&upper->lower);
upper->lowest = 0;
}
list_add_tail(&edge->list[UPPER], &upper->lower);
continue;
}
BUG_ON(!upper->checked);
rb_node = tree_insert(&cache->rb_root, upper->bytenr,
&upper->rb_node);
BUG_ON(rb_node);
list_add_tail(&edge->list[UPPER], &upper->lower);
list_for_each_entry(edge, &upper->upper, list[LOWER])
list_add_tail(&edge->list[UPPER], &list);
}
out:
btrfs_free_path(path1);
btrfs_free_path(path2);
if (err) {
INIT_LIST_HEAD(&list);
upper = node;
while (upper) {
if (RB_EMPTY_NODE(&upper->rb_node)) {
list_splice_tail(&upper->upper, &list);
kfree(upper);
}
if (list_empty(&list))
break;
edge = list_entry(list.next, struct backref_edge,
list[LOWER]);
upper = edge->node[UPPER];
kfree(edge);
}
return ERR_PTR(err);
}
return node;
}
/*
* helper to add 'address of tree root -> reloc tree' mapping
*/
static int __add_reloc_root(struct btrfs_root *root)
{
struct rb_node *rb_node;
struct mapping_node *node;
struct reloc_control *rc = root->fs_info->reloc_ctl;
node = kmalloc(sizeof(*node), GFP_NOFS);
BUG_ON(!node);
node->bytenr = root->node->start;
node->data = root;
spin_lock(&rc->reloc_root_tree.lock);
rb_node = tree_insert(&rc->reloc_root_tree.rb_root,
node->bytenr, &node->rb_node);
spin_unlock(&rc->reloc_root_tree.lock);
BUG_ON(rb_node);
list_add_tail(&root->root_list, &rc->reloc_roots);
return 0;
}
/*
* helper to update/delete the 'address of tree root -> reloc tree'
* mapping
*/
static int __update_reloc_root(struct btrfs_root *root, int del)
{
struct rb_node *rb_node;
struct mapping_node *node = NULL;
struct reloc_control *rc = root->fs_info->reloc_ctl;
spin_lock(&rc->reloc_root_tree.lock);
rb_node = tree_search(&rc->reloc_root_tree.rb_root,
root->commit_root->start);
if (rb_node) {
node = rb_entry(rb_node, struct mapping_node, rb_node);
rb_erase(&node->rb_node, &rc->reloc_root_tree.rb_root);
}
spin_unlock(&rc->reloc_root_tree.lock);
BUG_ON((struct btrfs_root *)node->data != root);
if (!del) {
spin_lock(&rc->reloc_root_tree.lock);
node->bytenr = root->node->start;
rb_node = tree_insert(&rc->reloc_root_tree.rb_root,
node->bytenr, &node->rb_node);
spin_unlock(&rc->reloc_root_tree.lock);
BUG_ON(rb_node);
} else {
list_del_init(&root->root_list);
kfree(node);
}
return 0;
}
/*
* create reloc tree for a given fs tree. reloc tree is just a
* snapshot of the fs tree with special root objectid.
*/
int btrfs_init_reloc_root(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
{
struct btrfs_root *reloc_root;
struct extent_buffer *eb;
struct btrfs_root_item *root_item;
struct btrfs_key root_key;
int ret;
if (root->reloc_root) {
reloc_root = root->reloc_root;
reloc_root->last_trans = trans->transid;
return 0;
}
if (!root->fs_info->reloc_ctl ||
!root->fs_info->reloc_ctl->create_reloc_root ||
root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID)
return 0;
root_item = kmalloc(sizeof(*root_item), GFP_NOFS);
BUG_ON(!root_item);
root_key.objectid = BTRFS_TREE_RELOC_OBJECTID;
root_key.type = BTRFS_ROOT_ITEM_KEY;
root_key.offset = root->root_key.objectid;
ret = btrfs_copy_root(trans, root, root->commit_root, &eb,
BTRFS_TREE_RELOC_OBJECTID);
BUG_ON(ret);
btrfs_set_root_last_snapshot(&root->root_item, trans->transid - 1);
memcpy(root_item, &root->root_item, sizeof(*root_item));
btrfs_set_root_refs(root_item, 1);
btrfs_set_root_bytenr(root_item, eb->start);
btrfs_set_root_level(root_item, btrfs_header_level(eb));
btrfs_set_root_generation(root_item, trans->transid);
memset(&root_item->drop_progress, 0, sizeof(struct btrfs_disk_key));
root_item->drop_level = 0;
btrfs_tree_unlock(eb);
free_extent_buffer(eb);
ret = btrfs_insert_root(trans, root->fs_info->tree_root,
&root_key, root_item);
BUG_ON(ret);
kfree(root_item);
reloc_root = btrfs_read_fs_root_no_radix(root->fs_info->tree_root,
&root_key);
BUG_ON(IS_ERR(reloc_root));
reloc_root->last_trans = trans->transid;
__add_reloc_root(reloc_root);
root->reloc_root = reloc_root;
return 0;
}
/*
* update root item of reloc tree
*/
int btrfs_update_reloc_root(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
{
struct btrfs_root *reloc_root;
struct btrfs_root_item *root_item;
int del = 0;
int ret;
if (!root->reloc_root)
return 0;
reloc_root = root->reloc_root;
root_item = &reloc_root->root_item;
if (btrfs_root_refs(root_item) == 0) {
root->reloc_root = NULL;
del = 1;
}
__update_reloc_root(reloc_root, del);
if (reloc_root->commit_root != reloc_root->node) {
btrfs_set_root_node(root_item, reloc_root->node);
free_extent_buffer(reloc_root->commit_root);
reloc_root->commit_root = btrfs_root_node(reloc_root);
}
ret = btrfs_update_root(trans, root->fs_info->tree_root,
&reloc_root->root_key, root_item);
BUG_ON(ret);
return 0;
}
/*
* helper to find first cached inode with inode number >= objectid
* in a subvolume
*/
static struct inode *find_next_inode(struct btrfs_root *root, u64 objectid)
{
struct rb_node *node;
struct rb_node *prev;
struct btrfs_inode *entry;
struct inode *inode;
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);
inode = igrab(&entry->vfs_inode);
if (inode) {
spin_unlock(&root->inode_lock);
return inode;
}
objectid = entry->vfs_inode.i_ino + 1;
if (cond_resched_lock(&root->inode_lock))
goto again;
node = rb_next(node);
}
spin_unlock(&root->inode_lock);
return NULL;
}
static int in_block_group(u64 bytenr,
struct btrfs_block_group_cache *block_group)
{
if (bytenr >= block_group->key.objectid &&
bytenr < block_group->key.objectid + block_group->key.offset)
return 1;
return 0;
}
/*
* get new location of data
*/
static int get_new_location(struct inode *reloc_inode, u64 *new_bytenr,
u64 bytenr, u64 num_bytes)
{
struct btrfs_root *root = BTRFS_I(reloc_inode)->root;
struct btrfs_path *path;
struct btrfs_file_extent_item *fi;
struct extent_buffer *leaf;
int ret;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
bytenr -= BTRFS_I(reloc_inode)->index_cnt;
ret = btrfs_lookup_file_extent(NULL, root, path, reloc_inode->i_ino,
bytenr, 0);
if (ret < 0)
goto out;
if (ret > 0) {
ret = -ENOENT;
goto out;
}
leaf = path->nodes[0];
fi = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_file_extent_item);
BUG_ON(btrfs_file_extent_offset(leaf, fi) ||
btrfs_file_extent_compression(leaf, fi) ||
btrfs_file_extent_encryption(leaf, fi) ||
btrfs_file_extent_other_encoding(leaf, fi));
if (num_bytes != btrfs_file_extent_disk_num_bytes(leaf, fi)) {
ret = 1;
goto out;
}
if (new_bytenr)
*new_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
ret = 0;
out:
btrfs_free_path(path);
return ret;
}
/*
* update file extent items in the tree leaf to point to
* the new locations.
*/
static int replace_file_extents(struct btrfs_trans_handle *trans,
struct reloc_control *rc,
struct btrfs_root *root,
struct extent_buffer *leaf,
struct list_head *inode_list)
{
struct btrfs_key key;
struct btrfs_file_extent_item *fi;
struct inode *inode = NULL;
struct inodevec *ivec = NULL;
u64 parent;
u64 bytenr;
u64 new_bytenr;
u64 num_bytes;
u64 end;
u32 nritems;
u32 i;
int ret;
int first = 1;
int dirty = 0;
if (rc->stage != UPDATE_DATA_PTRS)
return 0;
/* reloc trees always use full backref */
if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID)
parent = leaf->start;
else
parent = 0;
nritems = btrfs_header_nritems(leaf);
for (i = 0; i < nritems; i++) {
cond_resched();
btrfs_item_key_to_cpu(leaf, &key, i);
if (key.type != BTRFS_EXTENT_DATA_KEY)
continue;
fi = btrfs_item_ptr(leaf, i, struct btrfs_file_extent_item);
if (btrfs_file_extent_type(leaf, fi) ==
BTRFS_FILE_EXTENT_INLINE)
continue;
bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
num_bytes = btrfs_file_extent_disk_num_bytes(leaf, fi);
if (bytenr == 0)
continue;
if (!in_block_group(bytenr, rc->block_group))
continue;
/*
* if we are modifying block in fs tree, wait for readpage
* to complete and drop the extent cache
*/
if (root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID) {
if (!ivec || ivec->nr == INODEVEC_SIZE) {
ivec = kmalloc(sizeof(*ivec), GFP_NOFS);
BUG_ON(!ivec);
ivec->nr = 0;
list_add_tail(&ivec->list, inode_list);
}
if (first) {
inode = find_next_inode(root, key.objectid);
if (inode)
ivec->inode[ivec->nr++] = inode;
first = 0;
} else if (inode && inode->i_ino < key.objectid) {
inode = find_next_inode(root, key.objectid);
if (inode)
ivec->inode[ivec->nr++] = inode;
}
if (inode && inode->i_ino == key.objectid) {
end = key.offset +
btrfs_file_extent_num_bytes(leaf, fi);
WARN_ON(!IS_ALIGNED(key.offset,
root->sectorsize));
WARN_ON(!IS_ALIGNED(end, root->sectorsize));
end--;
ret = try_lock_extent(&BTRFS_I(inode)->io_tree,
key.offset, end,
GFP_NOFS);
if (!ret)
continue;
btrfs_drop_extent_cache(inode, key.offset, end,
1);
unlock_extent(&BTRFS_I(inode)->io_tree,
key.offset, end, GFP_NOFS);
}
}
ret = get_new_location(rc->data_inode, &new_bytenr,
bytenr, num_bytes);
if (ret > 0)
continue;
BUG_ON(ret < 0);
btrfs_set_file_extent_disk_bytenr(leaf, fi, new_bytenr);
dirty = 1;
key.offset -= btrfs_file_extent_offset(leaf, fi);
ret = btrfs_inc_extent_ref(trans, root, new_bytenr,
num_bytes, parent,
btrfs_header_owner(leaf),
key.objectid, key.offset);
BUG_ON(ret);
ret = btrfs_free_extent(trans, root, bytenr, num_bytes,
parent, btrfs_header_owner(leaf),
key.objectid, key.offset);
BUG_ON(ret);
}
if (dirty)
btrfs_mark_buffer_dirty(leaf);
return 0;
}
static noinline_for_stack
int memcmp_node_keys(struct extent_buffer *eb, int slot,
struct btrfs_path *path, int level)
{
struct btrfs_disk_key key1;
struct btrfs_disk_key key2;
btrfs_node_key(eb, &key1, slot);
btrfs_node_key(path->nodes[level], &key2, path->slots[level]);
return memcmp(&key1, &key2, sizeof(key1));
}
/*
* try to replace tree blocks in fs tree with the new blocks
* in reloc tree. tree blocks haven't been modified since the
* reloc tree was create can be replaced.
*
* if a block was replaced, level of the block + 1 is returned.
* if no block got replaced, 0 is returned. if there are other
* errors, a negative error number is returned.
*/
static int replace_path(struct btrfs_trans_handle *trans,
struct btrfs_root *dest, struct btrfs_root *src,
struct btrfs_path *path, struct btrfs_key *next_key,
struct extent_buffer **leaf,
int lowest_level, int max_level)
{
struct extent_buffer *eb;
struct extent_buffer *parent;
struct btrfs_key key;
u64 old_bytenr;
u64 new_bytenr;
u64 old_ptr_gen;
u64 new_ptr_gen;
u64 last_snapshot;
u32 blocksize;
int level;
int ret;
int slot;
BUG_ON(src->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID);
BUG_ON(dest->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID);
BUG_ON(lowest_level > 1 && leaf);
last_snapshot = btrfs_root_last_snapshot(&src->root_item);
slot = path->slots[lowest_level];
btrfs_node_key_to_cpu(path->nodes[lowest_level], &key, slot);
eb = btrfs_lock_root_node(dest);
btrfs_set_lock_blocking(eb);
level = btrfs_header_level(eb);
if (level < lowest_level) {
btrfs_tree_unlock(eb);
free_extent_buffer(eb);
return 0;
}
ret = btrfs_cow_block(trans, dest, eb, NULL, 0, &eb);
BUG_ON(ret);
btrfs_set_lock_blocking(eb);
if (next_key) {
next_key->objectid = (u64)-1;
next_key->type = (u8)-1;
next_key->offset = (u64)-1;
}
parent = eb;
while (1) {
level = btrfs_header_level(parent);
BUG_ON(level < lowest_level);
ret = btrfs_bin_search(parent, &key, level, &slot);
if (ret && slot > 0)
slot--;
if (next_key && slot + 1 < btrfs_header_nritems(parent))
btrfs_node_key_to_cpu(parent, next_key, slot + 1);
old_bytenr = btrfs_node_blockptr(parent, slot);
blocksize = btrfs_level_size(dest, level - 1);
old_ptr_gen = btrfs_node_ptr_generation(parent, slot);
if (level <= max_level) {
eb = path->nodes[level];
new_bytenr = btrfs_node_blockptr(eb,
path->slots[level]);
new_ptr_gen = btrfs_node_ptr_generation(eb,
path->slots[level]);
} else {
new_bytenr = 0;
new_ptr_gen = 0;
}
if (new_bytenr > 0 && new_bytenr == old_bytenr) {
WARN_ON(1);
ret = level;
break;
}
if (new_bytenr == 0 || old_ptr_gen > last_snapshot ||
memcmp_node_keys(parent, slot, path, level)) {
if (level <= lowest_level && !leaf) {
ret = 0;
break;
}
eb = read_tree_block(dest, old_bytenr, blocksize,
old_ptr_gen);
btrfs_tree_lock(eb);
ret = btrfs_cow_block(trans, dest, eb, parent,
slot, &eb);
BUG_ON(ret);
btrfs_set_lock_blocking(eb);
if (level <= lowest_level) {
*leaf = eb;
ret = 0;
break;
}
btrfs_tree_unlock(parent);
free_extent_buffer(parent);
parent = eb;
continue;
}
btrfs_node_key_to_cpu(path->nodes[level], &key,
path->slots[level]);
btrfs_release_path(src, path);
path->lowest_level = level;
ret = btrfs_search_slot(trans, src, &key, path, 0, 1);
path->lowest_level = 0;
BUG_ON(ret);
/*
* swap blocks in fs tree and reloc tree.
*/
btrfs_set_node_blockptr(parent, slot, new_bytenr);
btrfs_set_node_ptr_generation(parent, slot, new_ptr_gen);
btrfs_mark_buffer_dirty(parent);
btrfs_set_node_blockptr(path->nodes[level],
path->slots[level], old_bytenr);
btrfs_set_node_ptr_generation(path->nodes[level],
path->slots[level], old_ptr_gen);
btrfs_mark_buffer_dirty(path->nodes[level]);
ret = btrfs_inc_extent_ref(trans, src, old_bytenr, blocksize,
path->nodes[level]->start,
src->root_key.objectid, level - 1, 0);
BUG_ON(ret);
ret = btrfs_inc_extent_ref(trans, dest, new_bytenr, blocksize,
0, dest->root_key.objectid, level - 1,
0);
BUG_ON(ret);
ret = btrfs_free_extent(trans, src, new_bytenr, blocksize,
path->nodes[level]->start,
src->root_key.objectid, level - 1, 0);
BUG_ON(ret);
ret = btrfs_free_extent(trans, dest, old_bytenr, blocksize,
0, dest->root_key.objectid, level - 1,
0);
BUG_ON(ret);
btrfs_unlock_up_safe(path, 0);
ret = level;
break;
}
btrfs_tree_unlock(parent);
free_extent_buffer(parent);
return ret;
}
/*
* helper to find next relocated block in reloc tree
*/
static noinline_for_stack
int walk_up_reloc_tree(struct btrfs_root *root, struct btrfs_path *path,
int *level)
{
struct extent_buffer *eb;
int i;
u64 last_snapshot;
u32 nritems;
last_snapshot = btrfs_root_last_snapshot(&root->root_item);
for (i = 0; i < *level; i++) {
free_extent_buffer(path->nodes[i]);
path->nodes[i] = NULL;
}
for (i = *level; i < BTRFS_MAX_LEVEL && path->nodes[i]; i++) {
eb = path->nodes[i];
nritems = btrfs_header_nritems(eb);
while (path->slots[i] + 1 < nritems) {
path->slots[i]++;
if (btrfs_node_ptr_generation(eb, path->slots[i]) <=
last_snapshot)
continue;
*level = i;
return 0;
}
free_extent_buffer(path->nodes[i]);
path->nodes[i] = NULL;
}
return 1;
}
/*
* walk down reloc tree to find relocated block of lowest level
*/
static noinline_for_stack
int walk_down_reloc_tree(struct btrfs_root *root, struct btrfs_path *path,
int *level)
{
struct extent_buffer *eb = NULL;
int i;
u64 bytenr;
u64 ptr_gen = 0;
u64 last_snapshot;
u32 blocksize;
u32 nritems;
last_snapshot = btrfs_root_last_snapshot(&root->root_item);
for (i = *level; i > 0; i--) {
eb = path->nodes[i];
nritems = btrfs_header_nritems(eb);
while (path->slots[i] < nritems) {
ptr_gen = btrfs_node_ptr_generation(eb, path->slots[i]);
if (ptr_gen > last_snapshot)
break;
path->slots[i]++;
}
if (path->slots[i] >= nritems) {
if (i == *level)
break;
*level = i + 1;
return 0;
}
if (i == 1) {
*level = i;
return 0;
}
bytenr = btrfs_node_blockptr(eb, path->slots[i]);
blocksize = btrfs_level_size(root, i - 1);
eb = read_tree_block(root, bytenr, blocksize, ptr_gen);
BUG_ON(btrfs_header_level(eb) != i - 1);
path->nodes[i - 1] = eb;
path->slots[i - 1] = 0;
}
return 1;
}
/*
* invalidate extent cache for file extents whose key in range of
* [min_key, max_key)
*/
static int invalidate_extent_cache(struct btrfs_root *root,
struct btrfs_key *min_key,
struct btrfs_key *max_key)
{
struct inode *inode = NULL;
u64 objectid;
u64 start, end;
objectid = min_key->objectid;
while (1) {
cond_resched();
iput(inode);
if (objectid > max_key->objectid)
break;
inode = find_next_inode(root, objectid);
if (!inode)
break;
if (inode->i_ino > max_key->objectid) {
iput(inode);
break;
}
objectid = inode->i_ino + 1;
if (!S_ISREG(inode->i_mode))
continue;
if (unlikely(min_key->objectid == inode->i_ino)) {
if (min_key->type > BTRFS_EXTENT_DATA_KEY)
continue;
if (min_key->type < BTRFS_EXTENT_DATA_KEY)
start = 0;
else {
start = min_key->offset;
WARN_ON(!IS_ALIGNED(start, root->sectorsize));
}
} else {
start = 0;
}
if (unlikely(max_key->objectid == inode->i_ino)) {
if (max_key->type < BTRFS_EXTENT_DATA_KEY)
continue;
if (max_key->type > BTRFS_EXTENT_DATA_KEY) {
end = (u64)-1;
} else {
if (max_key->offset == 0)
continue;
end = max_key->offset;
WARN_ON(!IS_ALIGNED(end, root->sectorsize));
end--;
}
} else {
end = (u64)-1;
}
/* the lock_extent waits for readpage to complete */
lock_extent(&BTRFS_I(inode)->io_tree, start, end, GFP_NOFS);
btrfs_drop_extent_cache(inode, start, end, 1);
unlock_extent(&BTRFS_I(inode)->io_tree, start, end, GFP_NOFS);
}
return 0;
}
static int find_next_key(struct btrfs_path *path, int level,
struct btrfs_key *key)
{
while (level < BTRFS_MAX_LEVEL) {
if (!path->nodes[level])
break;
if (path->slots[level] + 1 <
btrfs_header_nritems(path->nodes[level])) {
btrfs_node_key_to_cpu(path->nodes[level], key,
path->slots[level] + 1);
return 0;
}
level++;
}
return 1;
}
/*
* merge the relocated tree blocks in reloc tree with corresponding
* fs tree.
*/
static noinline_for_stack int merge_reloc_root(struct reloc_control *rc,
struct btrfs_root *root)
{
LIST_HEAD(inode_list);
struct btrfs_key key;
struct btrfs_key next_key;
struct btrfs_trans_handle *trans;
struct btrfs_root *reloc_root;
struct btrfs_root_item *root_item;
struct btrfs_path *path;
struct extent_buffer *leaf = NULL;
unsigned long nr;
int level;
int max_level;
int replaced = 0;
int ret;
int err = 0;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
reloc_root = root->reloc_root;
root_item = &reloc_root->root_item;
if (btrfs_disk_key_objectid(&root_item->drop_progress) == 0) {
level = btrfs_root_level(root_item);
extent_buffer_get(reloc_root->node);
path->nodes[level] = reloc_root->node;
path->slots[level] = 0;
} else {
btrfs_disk_key_to_cpu(&key, &root_item->drop_progress);
level = root_item->drop_level;
BUG_ON(level == 0);
path->lowest_level = level;
ret = btrfs_search_slot(NULL, reloc_root, &key, path, 0, 0);
if (ret < 0) {
btrfs_free_path(path);
return ret;
}
btrfs_node_key_to_cpu(path->nodes[level], &next_key,
path->slots[level]);
WARN_ON(memcmp(&key, &next_key, sizeof(key)));
btrfs_unlock_up_safe(path, 0);
}
if (level == 0 && rc->stage == UPDATE_DATA_PTRS) {
trans = btrfs_start_transaction(root, 1);
leaf = path->nodes[0];
btrfs_item_key_to_cpu(leaf, &key, 0);
btrfs_release_path(reloc_root, path);
ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
if (ret < 0) {
err = ret;
goto out;
}
leaf = path->nodes[0];
btrfs_unlock_up_safe(path, 1);
ret = replace_file_extents(trans, rc, root, leaf,
&inode_list);
if (ret < 0)
err = ret;
goto out;
}
memset(&next_key, 0, sizeof(next_key));
while (1) {
leaf = NULL;
replaced = 0;
trans = btrfs_start_transaction(root, 1);
max_level = level;
ret = walk_down_reloc_tree(reloc_root, path, &level);
if (ret < 0) {
err = ret;
goto out;
}
if (ret > 0)
break;
if (!find_next_key(path, level, &key) &&
btrfs_comp_cpu_keys(&next_key, &key) >= 0) {
ret = 0;
} else if (level == 1 && rc->stage == UPDATE_DATA_PTRS) {
ret = replace_path(trans, root, reloc_root,
path, &next_key, &leaf,
level, max_level);
} else {
ret = replace_path(trans, root, reloc_root,
path, &next_key, NULL,
level, max_level);
}
if (ret < 0) {
err = ret;
goto out;
}
if (ret > 0) {
level = ret;
btrfs_node_key_to_cpu(path->nodes[level], &key,
path->slots[level]);
replaced = 1;
} else if (leaf) {
/*
* no block got replaced, try replacing file extents
*/
btrfs_item_key_to_cpu(leaf, &key, 0);
ret = replace_file_extents(trans, rc, root, leaf,
&inode_list);
btrfs_tree_unlock(leaf);
free_extent_buffer(leaf);
BUG_ON(ret < 0);
}
ret = walk_up_reloc_tree(reloc_root, path, &level);
if (ret > 0)
break;
BUG_ON(level == 0);
/*
* save the merging progress in the drop_progress.
* this is OK since root refs == 1 in this case.
*/
btrfs_node_key(path->nodes[level], &root_item->drop_progress,
path->slots[level]);
root_item->drop_level = level;
nr = trans->blocks_used;
btrfs_end_transaction(trans, root);
btrfs_btree_balance_dirty(root, nr);
if (replaced && rc->stage == UPDATE_DATA_PTRS)
invalidate_extent_cache(root, &key, &next_key);
}
/*
* handle the case only one block in the fs tree need to be
* relocated and the block is tree root.
*/
leaf = btrfs_lock_root_node(root);
ret = btrfs_cow_block(trans, root, leaf, NULL, 0, &leaf);
btrfs_tree_unlock(leaf);
free_extent_buffer(leaf);
if (ret < 0)
err = ret;
out:
btrfs_free_path(path);
if (err == 0) {
memset(&root_item->drop_progress, 0,
sizeof(root_item->drop_progress));
root_item->drop_level = 0;
btrfs_set_root_refs(root_item, 0);
}
nr = trans->blocks_used;
btrfs_end_transaction(trans, root);
btrfs_btree_balance_dirty(root, nr);
/*
* put inodes while we aren't holding the tree locks
*/
while (!list_empty(&inode_list)) {
struct inodevec *ivec;
ivec = list_entry(inode_list.next, struct inodevec, list);
list_del(&ivec->list);
while (ivec->nr > 0) {
ivec->nr--;
iput(ivec->inode[ivec->nr]);
}
kfree(ivec);
}
if (replaced && rc->stage == UPDATE_DATA_PTRS)
invalidate_extent_cache(root, &key, &next_key);
return err;
}
/*
* callback for the work threads.
* this function merges reloc tree with corresponding fs tree,
* and then drops the reloc tree.
*/
static void merge_func(struct btrfs_work *work)
{
struct btrfs_trans_handle *trans;
struct btrfs_root *root;
struct btrfs_root *reloc_root;
struct async_merge *async;
async = container_of(work, struct async_merge, work);
reloc_root = async->root;
if (btrfs_root_refs(&reloc_root->root_item) > 0) {
root = read_fs_root(reloc_root->fs_info,
reloc_root->root_key.offset);
BUG_ON(IS_ERR(root));
BUG_ON(root->reloc_root != reloc_root);
merge_reloc_root(async->rc, root);
trans = btrfs_start_transaction(root, 1);
btrfs_update_reloc_root(trans, root);
btrfs_end_transaction(trans, root);
}
btrfs_drop_dead_root(reloc_root);
if (atomic_dec_and_test(async->num_pending))
complete(async->done);
kfree(async);
}
static int merge_reloc_roots(struct reloc_control *rc)
{
struct async_merge *async;
struct btrfs_root *root;
struct completion done;
atomic_t num_pending;
init_completion(&done);
atomic_set(&num_pending, 1);
while (!list_empty(&rc->reloc_roots)) {
root = list_entry(rc->reloc_roots.next,
struct btrfs_root, root_list);
list_del_init(&root->root_list);
async = kmalloc(sizeof(*async), GFP_NOFS);
BUG_ON(!async);
async->work.func = merge_func;
async->work.flags = 0;
async->rc = rc;
async->root = root;
async->done = &done;
async->num_pending = &num_pending;
atomic_inc(&num_pending);
btrfs_queue_worker(&rc->workers, &async->work);
}
if (!atomic_dec_and_test(&num_pending))
wait_for_completion(&done);
BUG_ON(!RB_EMPTY_ROOT(&rc->reloc_root_tree.rb_root));
return 0;
}
static void free_block_list(struct rb_root *blocks)
{
struct tree_block *block;
struct rb_node *rb_node;
while ((rb_node = rb_first(blocks))) {
block = rb_entry(rb_node, struct tree_block, rb_node);
rb_erase(rb_node, blocks);
kfree(block);
}
}
static int record_reloc_root_in_trans(struct btrfs_trans_handle *trans,
struct btrfs_root *reloc_root)
{
struct btrfs_root *root;
if (reloc_root->last_trans == trans->transid)
return 0;
root = read_fs_root(reloc_root->fs_info, reloc_root->root_key.offset);
BUG_ON(IS_ERR(root));
BUG_ON(root->reloc_root != reloc_root);
return btrfs_record_root_in_trans(trans, root);
}
/*
* select one tree from trees that references the block.
* for blocks in refernce counted trees, we preper reloc tree.
* if no reloc tree found and reloc_only is true, NULL is returned.
*/
static struct btrfs_root *__select_one_root(struct btrfs_trans_handle *trans,
struct backref_node *node,
struct backref_edge *edges[],
int *nr, int reloc_only)
{
struct backref_node *next;
struct btrfs_root *root;
int index;
int loop = 0;
again:
index = 0;
next = node;
while (1) {
cond_resched();
next = walk_up_backref(next, edges, &index);
root = next->root;
if (!root) {
BUG_ON(!node->old_root);
goto skip;
}
/* no other choice for non-refernce counted tree */
if (!root->ref_cows) {
BUG_ON(reloc_only);
break;
}
if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) {
record_reloc_root_in_trans(trans, root);
break;
}
if (loop) {
btrfs_record_root_in_trans(trans, root);
break;
}
if (reloc_only || next != node) {
if (!root->reloc_root)
btrfs_record_root_in_trans(trans, root);
root = root->reloc_root;
/*
* if the reloc tree was created in current
* transation, there is no node in backref tree
* corresponds to the root of the reloc tree.
*/
if (btrfs_root_last_snapshot(&root->root_item) ==
trans->transid - 1)
break;
}
skip:
root = NULL;
next = walk_down_backref(edges, &index);
if (!next || next->level <= node->level)
break;
}
if (!root && !loop && !reloc_only) {
loop = 1;
goto again;
}
if (root)
*nr = index;
else
*nr = 0;
return root;
}
static noinline_for_stack
struct btrfs_root *select_one_root(struct btrfs_trans_handle *trans,
struct backref_node *node)
{
struct backref_edge *edges[BTRFS_MAX_LEVEL - 1];
int nr;
return __select_one_root(trans, node, edges, &nr, 0);
}
static noinline_for_stack
struct btrfs_root *select_reloc_root(struct btrfs_trans_handle *trans,
struct backref_node *node,
struct backref_edge *edges[], int *nr)
{
return __select_one_root(trans, node, edges, nr, 1);
}
static void grab_path_buffers(struct btrfs_path *path,
struct backref_node *node,
struct backref_edge *edges[], int nr)
{
int i = 0;
while (1) {
drop_node_buffer(node);
node->eb = path->nodes[node->level];
BUG_ON(!node->eb);
if (path->locks[node->level])
node->locked = 1;
path->nodes[node->level] = NULL;
path->locks[node->level] = 0;
if (i >= nr)
break;
edges[i]->blockptr = node->eb->start;
node = edges[i]->node[UPPER];
i++;
}
}
/*
* relocate a block tree, and then update pointers in upper level
* blocks that reference the block to point to the new location.
*
* if called by link_to_upper, the block has already been relocated.
* in that case this function just updates pointers.
*/
static int do_relocation(struct btrfs_trans_handle *trans,
struct backref_node *node,
struct btrfs_key *key,
struct btrfs_path *path, int lowest)
{
struct backref_node *upper;
struct backref_edge *edge;
struct backref_edge *edges[BTRFS_MAX_LEVEL - 1];
struct btrfs_root *root;
struct extent_buffer *eb;
u32 blocksize;
u64 bytenr;
u64 generation;
int nr;
int slot;
int ret;
int err = 0;
BUG_ON(lowest && node->eb);
path->lowest_level = node->level + 1;
list_for_each_entry(edge, &node->upper, list[LOWER]) {
cond_resched();
if (node->eb && node->eb->start == edge->blockptr)
continue;
upper = edge->node[UPPER];
root = select_reloc_root(trans, upper, edges, &nr);
if (!root)
continue;
if (upper->eb && !upper->locked)
drop_node_buffer(upper);
if (!upper->eb) {
ret = btrfs_search_slot(trans, root, key, path, 0, 1);
if (ret < 0) {
err = ret;
break;
}
BUG_ON(ret > 0);
slot = path->slots[upper->level];
btrfs_unlock_up_safe(path, upper->level + 1);
grab_path_buffers(path, upper, edges, nr);
btrfs_release_path(NULL, path);
} else {
ret = btrfs_bin_search(upper->eb, key, upper->level,
&slot);
BUG_ON(ret);
}
bytenr = btrfs_node_blockptr(upper->eb, slot);
if (!lowest) {
if (node->eb->start == bytenr) {
btrfs_tree_unlock(upper->eb);
upper->locked = 0;
continue;
}
} else {
BUG_ON(node->bytenr != bytenr);
}
blocksize = btrfs_level_size(root, node->level);
generation = btrfs_node_ptr_generation(upper->eb, slot);
eb = read_tree_block(root, bytenr, blocksize, generation);
btrfs_tree_lock(eb);
btrfs_set_lock_blocking(eb);
if (!node->eb) {
ret = btrfs_cow_block(trans, root, eb, upper->eb,
slot, &eb);
if (ret < 0) {
err = ret;
break;
}
btrfs_set_lock_blocking(eb);
node->eb = eb;
node->locked = 1;
} else {
btrfs_set_node_blockptr(upper->eb, slot,
node->eb->start);
btrfs_set_node_ptr_generation(upper->eb, slot,
trans->transid);
btrfs_mark_buffer_dirty(upper->eb);
ret = btrfs_inc_extent_ref(trans, root,
node->eb->start, blocksize,
upper->eb->start,
btrfs_header_owner(upper->eb),
node->level, 0);
BUG_ON(ret);
ret = btrfs_drop_subtree(trans, root, eb, upper->eb);
BUG_ON(ret);
btrfs_tree_unlock(eb);
free_extent_buffer(eb);
}
if (!lowest) {
btrfs_tree_unlock(upper->eb);
upper->locked = 0;
}
}
path->lowest_level = 0;
return err;
}
static int link_to_upper(struct btrfs_trans_handle *trans,
struct backref_node *node,
struct btrfs_path *path)
{
struct btrfs_key key;
if (!node->eb || list_empty(&node->upper))
return 0;
btrfs_node_key_to_cpu(node->eb, &key, 0);
return do_relocation(trans, node, &key, path, 0);
}
static int finish_pending_nodes(struct btrfs_trans_handle *trans,
struct backref_cache *cache,
struct btrfs_path *path)
{
struct backref_node *node;
int level;
int ret;
int err = 0;
for (level = 0; level < BTRFS_MAX_LEVEL; level++) {
while (!list_empty(&cache->pending[level])) {
node = list_entry(cache->pending[level].next,
struct backref_node, lower);
BUG_ON(node->level != level);
ret = link_to_upper(trans, node, path);
if (ret < 0)
err = ret;
/*
* this remove the node from the pending list and
* may add some other nodes to the level + 1
* pending list
*/
remove_backref_node(cache, node);
}
}
BUG_ON(!RB_EMPTY_ROOT(&cache->rb_root));
return err;
}
static void mark_block_processed(struct reloc_control *rc,
struct backref_node *node)
{
u32 blocksize;
if (node->level == 0 ||
in_block_group(node->bytenr, rc->block_group)) {
blocksize = btrfs_level_size(rc->extent_root, node->level);
set_extent_bits(&rc->processed_blocks, node->bytenr,
node->bytenr + blocksize - 1, EXTENT_DIRTY,
GFP_NOFS);
}
node->processed = 1;
}
/*
* mark a block and all blocks directly/indirectly reference the block
* as processed.
*/
static void update_processed_blocks(struct reloc_control *rc,
struct backref_node *node)
{
struct backref_node *next = node;
struct backref_edge *edge;
struct backref_edge *edges[BTRFS_MAX_LEVEL - 1];
int index = 0;
while (next) {
cond_resched();
while (1) {
if (next->processed)
break;
mark_block_processed(rc, next);
if (list_empty(&next->upper))
break;
edge = list_entry(next->upper.next,
struct backref_edge, list[LOWER]);
edges[index++] = edge;
next = edge->node[UPPER];
}
next = walk_down_backref(edges, &index);
}
}
static int tree_block_processed(u64 bytenr, u32 blocksize,
struct reloc_control *rc)
{
if (test_range_bit(&rc->processed_blocks, bytenr,
bytenr + blocksize - 1, EXTENT_DIRTY, 1))
return 1;
return 0;
}
/*
* check if there are any file extent pointers in the leaf point to
* data require processing
*/
static int check_file_extents(struct reloc_control *rc,
u64 bytenr, u32 blocksize, u64 ptr_gen)
{
struct btrfs_key found_key;
struct btrfs_file_extent_item *fi;
struct extent_buffer *leaf;
u32 nritems;
int i;
int ret = 0;
leaf = read_tree_block(rc->extent_root, bytenr, blocksize, ptr_gen);
nritems = btrfs_header_nritems(leaf);
for (i = 0; i < nritems; i++) {
cond_resched();
btrfs_item_key_to_cpu(leaf, &found_key, i);
if (found_key.type != BTRFS_EXTENT_DATA_KEY)
continue;
fi = btrfs_item_ptr(leaf, i, struct btrfs_file_extent_item);
if (btrfs_file_extent_type(leaf, fi) ==
BTRFS_FILE_EXTENT_INLINE)
continue;
bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
if (bytenr == 0)
continue;
if (in_block_group(bytenr, rc->block_group)) {
ret = 1;
break;
}
}
free_extent_buffer(leaf);
return ret;
}
/*
* scan child blocks of a given block to find blocks require processing
*/
static int add_child_blocks(struct btrfs_trans_handle *trans,
struct reloc_control *rc,
struct backref_node *node,
struct rb_root *blocks)
{
struct tree_block *block;
struct rb_node *rb_node;
u64 bytenr;
u64 ptr_gen;
u32 blocksize;
u32 nritems;
int i;
int err = 0;
nritems = btrfs_header_nritems(node->eb);
blocksize = btrfs_level_size(rc->extent_root, node->level - 1);
for (i = 0; i < nritems; i++) {
cond_resched();
bytenr = btrfs_node_blockptr(node->eb, i);
ptr_gen = btrfs_node_ptr_generation(node->eb, i);
if (ptr_gen == trans->transid)
continue;
if (!in_block_group(bytenr, rc->block_group) &&
(node->level > 1 || rc->stage == MOVE_DATA_EXTENTS))
continue;
if (tree_block_processed(bytenr, blocksize, rc))
continue;
readahead_tree_block(rc->extent_root,
bytenr, blocksize, ptr_gen);
}
for (i = 0; i < nritems; i++) {
cond_resched();
bytenr = btrfs_node_blockptr(node->eb, i);
ptr_gen = btrfs_node_ptr_generation(node->eb, i);
if (ptr_gen == trans->transid)
continue;
if (!in_block_group(bytenr, rc->block_group) &&
(node->level > 1 || rc->stage == MOVE_DATA_EXTENTS))
continue;
if (tree_block_processed(bytenr, blocksize, rc))
continue;
if (!in_block_group(bytenr, rc->block_group) &&
!check_file_extents(rc, bytenr, blocksize, ptr_gen))
continue;
block = kmalloc(sizeof(*block), GFP_NOFS);
if (!block) {
err = -ENOMEM;
break;
}
block->bytenr = bytenr;
btrfs_node_key_to_cpu(node->eb, &block->key, i);
block->level = node->level - 1;
block->key_ready = 1;
rb_node = tree_insert(blocks, block->bytenr, &block->rb_node);
BUG_ON(rb_node);
}
if (err)
free_block_list(blocks);
return err;
}
/*
* find adjacent blocks require processing
*/
static noinline_for_stack
int add_adjacent_blocks(struct btrfs_trans_handle *trans,
struct reloc_control *rc,
struct backref_cache *cache,
struct rb_root *blocks, int level,
struct backref_node **upper)
{
struct backref_node *node;
int ret = 0;
WARN_ON(!list_empty(&cache->pending[level]));
if (list_empty(&cache->pending[level + 1]))
return 1;
node = list_entry(cache->pending[level + 1].next,
struct backref_node, lower);
if (node->eb)
ret = add_child_blocks(trans, rc, node, blocks);
*upper = node;
return ret;
}
static int get_tree_block_key(struct reloc_control *rc,
struct tree_block *block)
{
struct extent_buffer *eb;
BUG_ON(block->key_ready);
eb = read_tree_block(rc->extent_root, block->bytenr,
block->key.objectid, block->key.offset);
WARN_ON(btrfs_header_level(eb) != block->level);
if (block->level == 0)
btrfs_item_key_to_cpu(eb, &block->key, 0);
else
btrfs_node_key_to_cpu(eb, &block->key, 0);
free_extent_buffer(eb);
block->key_ready = 1;
return 0;
}
static int reada_tree_block(struct reloc_control *rc,
struct tree_block *block)
{
BUG_ON(block->key_ready);
readahead_tree_block(rc->extent_root, block->bytenr,
block->key.objectid, block->key.offset);
return 0;
}
/*
* helper function to relocate a tree block
*/
static int relocate_tree_block(struct btrfs_trans_handle *trans,
struct reloc_control *rc,
struct backref_node *node,
struct btrfs_key *key,
struct btrfs_path *path)
{
struct btrfs_root *root;
int ret;
root = select_one_root(trans, node);
if (unlikely(!root)) {
rc->found_old_snapshot = 1;
update_processed_blocks(rc, node);
return 0;
}
if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) {
ret = do_relocation(trans, node, key, path, 1);
if (ret < 0)
goto out;
if (node->level == 0 && rc->stage == UPDATE_DATA_PTRS) {
ret = replace_file_extents(trans, rc, root,
node->eb, NULL);
if (ret < 0)
goto out;
}
drop_node_buffer(node);
} else if (!root->ref_cows) {
path->lowest_level = node->level;
ret = btrfs_search_slot(trans, root, key, path, 0, 1);
btrfs_release_path(root, path);
if (ret < 0)
goto out;
} else if (root != node->root) {
WARN_ON(node->level > 0 || rc->stage != UPDATE_DATA_PTRS);
}
update_processed_blocks(rc, node);
ret = 0;
out:
drop_node_buffer(node);
return ret;
}
/*
* relocate a list of blocks
*/
static noinline_for_stack
int relocate_tree_blocks(struct btrfs_trans_handle *trans,
struct reloc_control *rc, struct rb_root *blocks)
{
struct backref_cache *cache;
struct backref_node *node;
struct btrfs_path *path;
struct tree_block *block;
struct rb_node *rb_node;
int level = -1;
int ret;
int err = 0;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
cache = kmalloc(sizeof(*cache), GFP_NOFS);
if (!cache) {
btrfs_free_path(path);
return -ENOMEM;
}
backref_cache_init(cache);
rb_node = rb_first(blocks);
while (rb_node) {
block = rb_entry(rb_node, struct tree_block, rb_node);
if (level == -1)
level = block->level;
else
BUG_ON(level != block->level);
if (!block->key_ready)
reada_tree_block(rc, block);
rb_node = rb_next(rb_node);
}
rb_node = rb_first(blocks);
while (rb_node) {
block = rb_entry(rb_node, struct tree_block, rb_node);
if (!block->key_ready)
get_tree_block_key(rc, block);
rb_node = rb_next(rb_node);
}
rb_node = rb_first(blocks);
while (rb_node) {
block = rb_entry(rb_node, struct tree_block, rb_node);
node = build_backref_tree(rc, cache, &block->key,
block->level, block->bytenr);
if (IS_ERR(node)) {
err = PTR_ERR(node);
goto out;
}
ret = relocate_tree_block(trans, rc, node, &block->key,
path);
if (ret < 0) {
err = ret;
goto out;
}
remove_backref_node(cache, node);
rb_node = rb_next(rb_node);
}
if (level > 0)
goto out;
free_block_list(blocks);
/*
* now backrefs of some upper level tree blocks have been cached,
* try relocating blocks referenced by these upper level blocks.
*/
while (1) {
struct backref_node *upper = NULL;
if (trans->transaction->in_commit ||
trans->transaction->delayed_refs.flushing)
break;
ret = add_adjacent_blocks(trans, rc, cache, blocks, level,
&upper);
if (ret < 0)
err = ret;
if (ret != 0)
break;
rb_node = rb_first(blocks);
while (rb_node) {
block = rb_entry(rb_node, struct tree_block, rb_node);
if (trans->transaction->in_commit ||
trans->transaction->delayed_refs.flushing)
goto out;
BUG_ON(!block->key_ready);
node = build_backref_tree(rc, cache, &block->key,
level, block->bytenr);
if (IS_ERR(node)) {
err = PTR_ERR(node);
goto out;
}
ret = relocate_tree_block(trans, rc, node,
&block->key, path);
if (ret < 0) {
err = ret;
goto out;
}
remove_backref_node(cache, node);
rb_node = rb_next(rb_node);
}
free_block_list(blocks);
if (upper) {
ret = link_to_upper(trans, upper, path);
if (ret < 0) {
err = ret;
break;
}
remove_backref_node(cache, upper);
}
}
out:
free_block_list(blocks);
ret = finish_pending_nodes(trans, cache, path);
if (ret < 0)
err = ret;
kfree(cache);
btrfs_free_path(path);
return err;
}
static noinline_for_stack
int relocate_inode_pages(struct inode *inode, u64 start, u64 len)
{
u64 page_start;
u64 page_end;
unsigned long i;
unsigned long first_index;
unsigned long last_index;
unsigned int total_read = 0;
unsigned int total_dirty = 0;
struct page *page;
struct file_ra_state *ra;
struct btrfs_ordered_extent *ordered;
struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
int ret = 0;
ra = kzalloc(sizeof(*ra), GFP_NOFS);
if (!ra)
return -ENOMEM;
mutex_lock(&inode->i_mutex);
first_index = start >> PAGE_CACHE_SHIFT;
last_index = (start + len - 1) >> PAGE_CACHE_SHIFT;
/* make sure the dirty trick played by the caller work */
ret = invalidate_inode_pages2_range(inode->i_mapping,
first_index, last_index);
if (ret)
goto out_unlock;
file_ra_state_init(ra, inode->i_mapping);
for (i = first_index ; i <= last_index; i++) {
if (total_read % ra->ra_pages == 0) {
btrfs_force_ra(inode->i_mapping, ra, NULL, i,
min(last_index, ra->ra_pages + i - 1));
}
total_read++;
again:
if (((u64)i << PAGE_CACHE_SHIFT) > i_size_read(inode))
BUG_ON(1);
page = grab_cache_page(inode->i_mapping, i);
if (!page) {
ret = -ENOMEM;
goto out_unlock;
}
if (!PageUptodate(page)) {
btrfs_readpage(NULL, page);
lock_page(page);
if (!PageUptodate(page)) {
unlock_page(page);
page_cache_release(page);
ret = -EIO;
goto out_unlock;
}
}
wait_on_page_writeback(page);
page_start = (u64)page->index << PAGE_CACHE_SHIFT;
page_end = page_start + PAGE_CACHE_SIZE - 1;
lock_extent(io_tree, page_start, page_end, GFP_NOFS);
ordered = btrfs_lookup_ordered_extent(inode, page_start);
if (ordered) {
unlock_extent(io_tree, page_start, page_end, GFP_NOFS);
unlock_page(page);
page_cache_release(page);
btrfs_start_ordered_extent(inode, ordered, 1);
btrfs_put_ordered_extent(ordered);
goto again;
}
set_page_extent_mapped(page);
if (i == first_index)
set_extent_bits(io_tree, page_start, page_end,
EXTENT_BOUNDARY, GFP_NOFS);
btrfs_set_extent_delalloc(inode, page_start, page_end);
set_page_dirty(page);
total_dirty++;
unlock_extent(io_tree, page_start, page_end, GFP_NOFS);
unlock_page(page);
page_cache_release(page);
}
out_unlock:
mutex_unlock(&inode->i_mutex);
kfree(ra);
balance_dirty_pages_ratelimited_nr(inode->i_mapping, total_dirty);
return ret;
}
static noinline_for_stack
int relocate_data_extent(struct inode *inode, struct btrfs_key *extent_key)
{
struct btrfs_root *root = BTRFS_I(inode)->root;
struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
struct extent_map *em;
u64 start = extent_key->objectid - BTRFS_I(inode)->index_cnt;
u64 end = start + extent_key->offset - 1;
em = alloc_extent_map(GFP_NOFS);
em->start = start;
em->len = extent_key->offset;
em->block_len = extent_key->offset;
em->block_start = extent_key->objectid;
em->bdev = root->fs_info->fs_devices->latest_bdev;
set_bit(EXTENT_FLAG_PINNED, &em->flags);
/* setup extent map to cheat btrfs_readpage */
lock_extent(&BTRFS_I(inode)->io_tree, start, end, GFP_NOFS);
while (1) {
int ret;
spin_lock(&em_tree->lock);
ret = add_extent_mapping(em_tree, em);
spin_unlock(&em_tree->lock);
if (ret != -EEXIST) {
free_extent_map(em);
break;
}
btrfs_drop_extent_cache(inode, start, end, 0);
}
unlock_extent(&BTRFS_I(inode)->io_tree, start, end, GFP_NOFS);
return relocate_inode_pages(inode, start, extent_key->offset);
}
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
static int get_ref_objectid_v0(struct reloc_control *rc,
struct btrfs_path *path,
struct btrfs_key *extent_key,
u64 *ref_objectid, int *path_change)
{
struct btrfs_key key;
struct extent_buffer *leaf;
struct btrfs_extent_ref_v0 *ref0;
int ret;
int slot;
leaf = path->nodes[0];
slot = path->slots[0];
while (1) {
if (slot >= btrfs_header_nritems(leaf)) {
ret = btrfs_next_leaf(rc->extent_root, path);
if (ret < 0)
return ret;
BUG_ON(ret > 0);
leaf = path->nodes[0];
slot = path->slots[0];
if (path_change)
*path_change = 1;
}
btrfs_item_key_to_cpu(leaf, &key, slot);
if (key.objectid != extent_key->objectid)
return -ENOENT;
if (key.type != BTRFS_EXTENT_REF_V0_KEY) {
slot++;
continue;
}
ref0 = btrfs_item_ptr(leaf, slot,
struct btrfs_extent_ref_v0);
*ref_objectid = btrfs_ref_objectid_v0(leaf, ref0);
break;
}
return 0;
}
#endif
/*
* helper to add a tree block to the list.
* the major work is getting the generation and level of the block
*/
static int add_tree_block(struct reloc_control *rc,
struct btrfs_key *extent_key,
struct btrfs_path *path,
struct rb_root *blocks)
{
struct extent_buffer *eb;
struct btrfs_extent_item *ei;
struct btrfs_tree_block_info *bi;
struct tree_block *block;
struct rb_node *rb_node;
u32 item_size;
int level = -1;
int generation;
eb = path->nodes[0];
item_size = btrfs_item_size_nr(eb, path->slots[0]);
if (item_size >= sizeof(*ei) + sizeof(*bi)) {
ei = btrfs_item_ptr(eb, path->slots[0],
struct btrfs_extent_item);
bi = (struct btrfs_tree_block_info *)(ei + 1);
generation = btrfs_extent_generation(eb, ei);
level = btrfs_tree_block_level(eb, bi);
} else {
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
u64 ref_owner;
int ret;
BUG_ON(item_size != sizeof(struct btrfs_extent_item_v0));
ret = get_ref_objectid_v0(rc, path, extent_key,
&ref_owner, NULL);
BUG_ON(ref_owner >= BTRFS_MAX_LEVEL);
level = (int)ref_owner;
/* FIXME: get real generation */
generation = 0;
#else
BUG();
#endif
}
btrfs_release_path(rc->extent_root, path);
BUG_ON(level == -1);
block = kmalloc(sizeof(*block), GFP_NOFS);
if (!block)
return -ENOMEM;
block->bytenr = extent_key->objectid;
block->key.objectid = extent_key->offset;
block->key.offset = generation;
block->level = level;
block->key_ready = 0;
rb_node = tree_insert(blocks, block->bytenr, &block->rb_node);
BUG_ON(rb_node);
return 0;
}
/*
* helper to add tree blocks for backref of type BTRFS_SHARED_DATA_REF_KEY
*/
static int __add_tree_block(struct reloc_control *rc,
u64 bytenr, u32 blocksize,
struct rb_root *blocks)
{
struct btrfs_path *path;
struct btrfs_key key;
int ret;
if (tree_block_processed(bytenr, blocksize, rc))
return 0;
if (tree_search(blocks, bytenr))
return 0;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
key.objectid = bytenr;
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = blocksize;
path->search_commit_root = 1;
path->skip_locking = 1;
ret = btrfs_search_slot(NULL, rc->extent_root, &key, path, 0, 0);
if (ret < 0)
goto out;
BUG_ON(ret);
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
ret = add_tree_block(rc, &key, path, blocks);
out:
btrfs_free_path(path);
return ret;
}
/*
* helper to check if the block use full backrefs for pointers in it
*/
static int block_use_full_backref(struct reloc_control *rc,
struct extent_buffer *eb)
{
struct btrfs_path *path;
struct btrfs_extent_item *ei;
struct btrfs_key key;
u64 flags;
int ret;
if (btrfs_header_flag(eb, BTRFS_HEADER_FLAG_RELOC) ||
btrfs_header_backref_rev(eb) < BTRFS_MIXED_BACKREF_REV)
return 1;
path = btrfs_alloc_path();
BUG_ON(!path);
key.objectid = eb->start;
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = eb->len;
path->search_commit_root = 1;
path->skip_locking = 1;
ret = btrfs_search_slot(NULL, rc->extent_root,
&key, path, 0, 0);
BUG_ON(ret);
ei = btrfs_item_ptr(path->nodes[0], path->slots[0],
struct btrfs_extent_item);
flags = btrfs_extent_flags(path->nodes[0], ei);
BUG_ON(!(flags & BTRFS_EXTENT_FLAG_TREE_BLOCK));
if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)
ret = 1;
else
ret = 0;
btrfs_free_path(path);
return ret;
}
/*
* helper to add tree blocks for backref of type BTRFS_EXTENT_DATA_REF_KEY
* this function scans fs tree to find blocks reference the data extent
*/
static int find_data_references(struct reloc_control *rc,
struct btrfs_key *extent_key,
struct extent_buffer *leaf,
struct btrfs_extent_data_ref *ref,
struct rb_root *blocks)
{
struct btrfs_path *path;
struct tree_block *block;
struct btrfs_root *root;
struct btrfs_file_extent_item *fi;
struct rb_node *rb_node;
struct btrfs_key key;
u64 ref_root;
u64 ref_objectid;
u64 ref_offset;
u32 ref_count;
u32 nritems;
int err = 0;
int added = 0;
int counted;
int ret;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
ref_root = btrfs_extent_data_ref_root(leaf, ref);
ref_objectid = btrfs_extent_data_ref_objectid(leaf, ref);
ref_offset = btrfs_extent_data_ref_offset(leaf, ref);
ref_count = btrfs_extent_data_ref_count(leaf, ref);
root = read_fs_root(rc->extent_root->fs_info, ref_root);
if (IS_ERR(root)) {
err = PTR_ERR(root);
goto out;
}
key.objectid = ref_objectid;
key.offset = ref_offset;
key.type = BTRFS_EXTENT_DATA_KEY;
path->search_commit_root = 1;
path->skip_locking = 1;
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
if (ret < 0) {
err = ret;
goto out;
}
leaf = path->nodes[0];
nritems = btrfs_header_nritems(leaf);
/*
* the references in tree blocks that use full backrefs
* are not counted in
*/
if (block_use_full_backref(rc, leaf))
counted = 0;
else
counted = 1;
rb_node = tree_search(blocks, leaf->start);
if (rb_node) {
if (counted)
added = 1;
else
path->slots[0] = nritems;
}
while (ref_count > 0) {
while (path->slots[0] >= nritems) {
ret = btrfs_next_leaf(root, path);
if (ret < 0) {
err = ret;
goto out;
}
if (ret > 0) {
WARN_ON(1);
goto out;
}
leaf = path->nodes[0];
nritems = btrfs_header_nritems(leaf);
added = 0;
if (block_use_full_backref(rc, leaf))
counted = 0;
else
counted = 1;
rb_node = tree_search(blocks, leaf->start);
if (rb_node) {
if (counted)
added = 1;
else
path->slots[0] = nritems;
}
}
btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
if (key.objectid != ref_objectid ||
key.type != BTRFS_EXTENT_DATA_KEY) {
WARN_ON(1);
break;
}
fi = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_file_extent_item);
if (btrfs_file_extent_type(leaf, fi) ==
BTRFS_FILE_EXTENT_INLINE)
goto next;
if (btrfs_file_extent_disk_bytenr(leaf, fi) !=
extent_key->objectid)
goto next;
key.offset -= btrfs_file_extent_offset(leaf, fi);
if (key.offset != ref_offset)
goto next;
if (counted)
ref_count--;
if (added)
goto next;
if (!tree_block_processed(leaf->start, leaf->len, rc)) {
block = kmalloc(sizeof(*block), GFP_NOFS);
if (!block) {
err = -ENOMEM;
break;
}
block->bytenr = leaf->start;
btrfs_item_key_to_cpu(leaf, &block->key, 0);
block->level = 0;
block->key_ready = 1;
rb_node = tree_insert(blocks, block->bytenr,
&block->rb_node);
BUG_ON(rb_node);
}
if (counted)
added = 1;
else
path->slots[0] = nritems;
next:
path->slots[0]++;
}
out:
btrfs_free_path(path);
return err;
}
/*
* hepler to find all tree blocks that reference a given data extent
*/
static noinline_for_stack
int add_data_references(struct reloc_control *rc,
struct btrfs_key *extent_key,
struct btrfs_path *path,
struct rb_root *blocks)
{
struct btrfs_key key;
struct extent_buffer *eb;
struct btrfs_extent_data_ref *dref;
struct btrfs_extent_inline_ref *iref;
unsigned long ptr;
unsigned long end;
u32 blocksize;
int ret;
int err = 0;
ret = get_new_location(rc->data_inode, NULL, extent_key->objectid,
extent_key->offset);
BUG_ON(ret < 0);
if (ret > 0) {
/* the relocated data is fragmented */
rc->extents_skipped++;
btrfs_release_path(rc->extent_root, path);
return 0;
}
blocksize = btrfs_level_size(rc->extent_root, 0);
eb = path->nodes[0];
ptr = btrfs_item_ptr_offset(eb, path->slots[0]);
end = ptr + btrfs_item_size_nr(eb, path->slots[0]);
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
if (ptr + sizeof(struct btrfs_extent_item_v0) == end)
ptr = end;
else
#endif
ptr += sizeof(struct btrfs_extent_item);
while (ptr < end) {
iref = (struct btrfs_extent_inline_ref *)ptr;
key.type = btrfs_extent_inline_ref_type(eb, iref);
if (key.type == BTRFS_SHARED_DATA_REF_KEY) {
key.offset = btrfs_extent_inline_ref_offset(eb, iref);
ret = __add_tree_block(rc, key.offset, blocksize,
blocks);
} else if (key.type == BTRFS_EXTENT_DATA_REF_KEY) {
dref = (struct btrfs_extent_data_ref *)(&iref->offset);
ret = find_data_references(rc, extent_key,
eb, dref, blocks);
} else {
BUG();
}
ptr += btrfs_extent_inline_ref_size(key.type);
}
WARN_ON(ptr > end);
while (1) {
cond_resched();
eb = path->nodes[0];
if (path->slots[0] >= btrfs_header_nritems(eb)) {
ret = btrfs_next_leaf(rc->extent_root, path);
if (ret < 0) {
err = ret;
break;
}
if (ret > 0)
break;
eb = path->nodes[0];
}
btrfs_item_key_to_cpu(eb, &key, path->slots[0]);
if (key.objectid != extent_key->objectid)
break;
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
if (key.type == BTRFS_SHARED_DATA_REF_KEY ||
key.type == BTRFS_EXTENT_REF_V0_KEY) {
#else
BUG_ON(key.type == BTRFS_EXTENT_REF_V0_KEY);
if (key.type == BTRFS_SHARED_DATA_REF_KEY) {
#endif
ret = __add_tree_block(rc, key.offset, blocksize,
blocks);
} else if (key.type == BTRFS_EXTENT_DATA_REF_KEY) {
dref = btrfs_item_ptr(eb, path->slots[0],
struct btrfs_extent_data_ref);
ret = find_data_references(rc, extent_key,
eb, dref, blocks);
} else {
ret = 0;
}
if (ret) {
err = ret;
break;
}
path->slots[0]++;
}
btrfs_release_path(rc->extent_root, path);
if (err)
free_block_list(blocks);
return err;
}
/*
* hepler to find next unprocessed extent
*/
static noinline_for_stack
int find_next_extent(struct btrfs_trans_handle *trans,
struct reloc_control *rc, struct btrfs_path *path)
{
struct btrfs_key key;
struct extent_buffer *leaf;
u64 start, end, last;
int ret;
last = rc->block_group->key.objectid + rc->block_group->key.offset;
while (1) {
cond_resched();
if (rc->search_start >= last) {
ret = 1;
break;
}
key.objectid = rc->search_start;
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = 0;
path->search_commit_root = 1;
path->skip_locking = 1;
ret = btrfs_search_slot(NULL, rc->extent_root, &key, path,
0, 0);
if (ret < 0)
break;
next:
leaf = path->nodes[0];
if (path->slots[0] >= btrfs_header_nritems(leaf)) {
ret = btrfs_next_leaf(rc->extent_root, path);
if (ret != 0)
break;
leaf = path->nodes[0];
}
btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
if (key.objectid >= last) {
ret = 1;
break;
}
if (key.type != BTRFS_EXTENT_ITEM_KEY ||
key.objectid + key.offset <= rc->search_start) {
path->slots[0]++;
goto next;
}
ret = find_first_extent_bit(&rc->processed_blocks,
key.objectid, &start, &end,
EXTENT_DIRTY);
if (ret == 0 && start <= key.objectid) {
btrfs_release_path(rc->extent_root, path);
rc->search_start = end + 1;
} else {
rc->search_start = key.objectid + key.offset;
return 0;
}
}
btrfs_release_path(rc->extent_root, path);
return ret;
}
static void set_reloc_control(struct reloc_control *rc)
{
struct btrfs_fs_info *fs_info = rc->extent_root->fs_info;
mutex_lock(&fs_info->trans_mutex);
fs_info->reloc_ctl = rc;
mutex_unlock(&fs_info->trans_mutex);
}
static void unset_reloc_control(struct reloc_control *rc)
{
struct btrfs_fs_info *fs_info = rc->extent_root->fs_info;
mutex_lock(&fs_info->trans_mutex);
fs_info->reloc_ctl = NULL;
mutex_unlock(&fs_info->trans_mutex);
}
static int check_extent_flags(u64 flags)
{
if ((flags & BTRFS_EXTENT_FLAG_DATA) &&
(flags & BTRFS_EXTENT_FLAG_TREE_BLOCK))
return 1;
if (!(flags & BTRFS_EXTENT_FLAG_DATA) &&
!(flags & BTRFS_EXTENT_FLAG_TREE_BLOCK))
return 1;
if ((flags & BTRFS_EXTENT_FLAG_DATA) &&
(flags & BTRFS_BLOCK_FLAG_FULL_BACKREF))
return 1;
return 0;
}
static noinline_for_stack int relocate_block_group(struct reloc_control *rc)
{
struct rb_root blocks = RB_ROOT;
struct btrfs_key key;
struct btrfs_trans_handle *trans = NULL;
struct btrfs_path *path;
struct btrfs_extent_item *ei;
unsigned long nr;
u64 flags;
u32 item_size;
int ret;
int err = 0;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
rc->search_start = rc->block_group->key.objectid;
clear_extent_bits(&rc->processed_blocks, 0, (u64)-1, EXTENT_DIRTY,
GFP_NOFS);
rc->create_reloc_root = 1;
set_reloc_control(rc);
trans = btrfs_start_transaction(rc->extent_root, 1);
btrfs_commit_transaction(trans, rc->extent_root);
while (1) {
trans = btrfs_start_transaction(rc->extent_root, 1);
ret = find_next_extent(trans, rc, path);
if (ret < 0)
err = ret;
if (ret != 0)
break;
rc->extents_found++;
ei = btrfs_item_ptr(path->nodes[0], path->slots[0],
struct btrfs_extent_item);
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
item_size = btrfs_item_size_nr(path->nodes[0],
path->slots[0]);
if (item_size >= sizeof(*ei)) {
flags = btrfs_extent_flags(path->nodes[0], ei);
ret = check_extent_flags(flags);
BUG_ON(ret);
} else {
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
u64 ref_owner;
int path_change = 0;
BUG_ON(item_size !=
sizeof(struct btrfs_extent_item_v0));
ret = get_ref_objectid_v0(rc, path, &key, &ref_owner,
&path_change);
if (ref_owner < BTRFS_FIRST_FREE_OBJECTID)
flags = BTRFS_EXTENT_FLAG_TREE_BLOCK;
else
flags = BTRFS_EXTENT_FLAG_DATA;
if (path_change) {
btrfs_release_path(rc->extent_root, path);
path->search_commit_root = 1;
path->skip_locking = 1;
ret = btrfs_search_slot(NULL, rc->extent_root,
&key, path, 0, 0);
if (ret < 0) {
err = ret;
break;
}
BUG_ON(ret > 0);
}
#else
BUG();
#endif
}
if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
ret = add_tree_block(rc, &key, path, &blocks);
} else if (rc->stage == UPDATE_DATA_PTRS &&
(flags & BTRFS_EXTENT_FLAG_DATA)) {
ret = add_data_references(rc, &key, path, &blocks);
} else {
btrfs_release_path(rc->extent_root, path);
ret = 0;
}
if (ret < 0) {
err = 0;
break;
}
if (!RB_EMPTY_ROOT(&blocks)) {
ret = relocate_tree_blocks(trans, rc, &blocks);
if (ret < 0) {
err = ret;
break;
}
}
nr = trans->blocks_used;
btrfs_end_transaction_throttle(trans, rc->extent_root);
trans = NULL;
btrfs_btree_balance_dirty(rc->extent_root, nr);
if (rc->stage == MOVE_DATA_EXTENTS &&
(flags & BTRFS_EXTENT_FLAG_DATA)) {
rc->found_file_extent = 1;
ret = relocate_data_extent(rc->data_inode, &key);
if (ret < 0) {
err = ret;
break;
}
}
}
btrfs_free_path(path);
if (trans) {
nr = trans->blocks_used;
btrfs_end_transaction(trans, rc->extent_root);
btrfs_btree_balance_dirty(rc->extent_root, nr);
}
rc->create_reloc_root = 0;
smp_mb();
if (rc->extents_found > 0) {
trans = btrfs_start_transaction(rc->extent_root, 1);
btrfs_commit_transaction(trans, rc->extent_root);
}
merge_reloc_roots(rc);
unset_reloc_control(rc);
/* get rid of pinned extents */
trans = btrfs_start_transaction(rc->extent_root, 1);
btrfs_commit_transaction(trans, rc->extent_root);
return err;
}
static int __insert_orphan_inode(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 objectid, u64 size)
{
struct btrfs_path *path;
struct btrfs_inode_item *item;
struct extent_buffer *leaf;
int ret;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
ret = btrfs_insert_empty_inode(trans, root, path, objectid);
if (ret)
goto out;
leaf = path->nodes[0];
item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_inode_item);
memset_extent_buffer(leaf, 0, (unsigned long)item, sizeof(*item));
btrfs_set_inode_generation(leaf, item, 1);
btrfs_set_inode_size(leaf, item, size);
btrfs_set_inode_mode(leaf, item, S_IFREG | 0600);
btrfs_set_inode_flags(leaf, item, BTRFS_INODE_NOCOMPRESS);
btrfs_mark_buffer_dirty(leaf);
btrfs_release_path(root, path);
out:
btrfs_free_path(path);
return ret;
}
/*
* helper to create inode for data relocation.
* the inode is in data relocation tree and its link count is 0
*/
static struct inode *create_reloc_inode(struct btrfs_fs_info *fs_info,
struct btrfs_block_group_cache *group)
{
struct inode *inode = NULL;
struct btrfs_trans_handle *trans;
struct btrfs_root *root;
struct btrfs_key key;
unsigned long nr;
u64 objectid = BTRFS_FIRST_FREE_OBJECTID;
int err = 0;
root = read_fs_root(fs_info, BTRFS_DATA_RELOC_TREE_OBJECTID);
if (IS_ERR(root))
return ERR_CAST(root);
trans = btrfs_start_transaction(root, 1);
BUG_ON(!trans);
err = btrfs_find_free_objectid(trans, root, objectid, &objectid);
if (err)
goto out;
err = __insert_orphan_inode(trans, root, objectid, group->key.offset);
BUG_ON(err);
err = btrfs_insert_file_extent(trans, root, objectid, 0, 0, 0,
group->key.offset, 0, group->key.offset,
0, 0, 0);
BUG_ON(err);
key.objectid = objectid;
key.type = BTRFS_INODE_ITEM_KEY;
key.offset = 0;
inode = btrfs_iget(root->fs_info->sb, &key, root);
BUG_ON(IS_ERR(inode) || is_bad_inode(inode));
BTRFS_I(inode)->index_cnt = group->key.objectid;
err = btrfs_orphan_add(trans, inode);
out:
nr = trans->blocks_used;
btrfs_end_transaction(trans, root);
btrfs_btree_balance_dirty(root, nr);
if (err) {
if (inode)
iput(inode);
inode = ERR_PTR(err);
}
return inode;
}
/*
* function to relocate all extents in a block group.
*/
int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start)
{
struct btrfs_fs_info *fs_info = extent_root->fs_info;
struct reloc_control *rc;
int ret;
int err = 0;
rc = kzalloc(sizeof(*rc), GFP_NOFS);
if (!rc)
return -ENOMEM;
mapping_tree_init(&rc->reloc_root_tree);
extent_io_tree_init(&rc->processed_blocks, NULL, GFP_NOFS);
INIT_LIST_HEAD(&rc->reloc_roots);
rc->block_group = btrfs_lookup_block_group(fs_info, group_start);
BUG_ON(!rc->block_group);
btrfs_init_workers(&rc->workers, "relocate",
fs_info->thread_pool_size);
rc->extent_root = extent_root;
btrfs_prepare_block_group_relocation(extent_root, rc->block_group);
rc->data_inode = create_reloc_inode(fs_info, rc->block_group);
if (IS_ERR(rc->data_inode)) {
err = PTR_ERR(rc->data_inode);
rc->data_inode = NULL;
goto out;
}
printk(KERN_INFO "btrfs: relocating block group %llu flags %llu\n",
(unsigned long long)rc->block_group->key.objectid,
(unsigned long long)rc->block_group->flags);
btrfs_start_delalloc_inodes(fs_info->tree_root);
btrfs_wait_ordered_extents(fs_info->tree_root, 0);
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_skipped = 0;
ret = relocate_block_group(rc);
if (ret < 0) {
err = ret;
break;
}
if (rc->extents_found == 0)
break;
printk(KERN_INFO "btrfs: found %llu extents\n",
(unsigned long long)rc->extents_found);
if (rc->stage == MOVE_DATA_EXTENTS && rc->found_file_extent) {
btrfs_wait_ordered_range(rc->data_inode, 0, (u64)-1);
invalidate_mapping_pages(rc->data_inode->i_mapping,
0, -1);
rc->stage = UPDATE_DATA_PTRS;
} else if (rc->stage == UPDATE_DATA_PTRS &&
rc->extents_skipped >= rc->extents_found) {
iput(rc->data_inode);
rc->data_inode = create_reloc_inode(fs_info,
rc->block_group);
if (IS_ERR(rc->data_inode)) {
err = PTR_ERR(rc->data_inode);
rc->data_inode = NULL;
break;
}
rc->stage = MOVE_DATA_EXTENTS;
rc->found_file_extent = 0;
}
}
filemap_fdatawrite_range(fs_info->btree_inode->i_mapping,
rc->block_group->key.objectid,
rc->block_group->key.objectid +
rc->block_group->key.offset - 1);
WARN_ON(rc->block_group->pinned > 0);
WARN_ON(rc->block_group->reserved > 0);
WARN_ON(btrfs_block_group_used(&rc->block_group->item) > 0);
out:
iput(rc->data_inode);
btrfs_stop_workers(&rc->workers);
btrfs_put_block_group(rc->block_group);
kfree(rc);
return err;
}
/*
* recover relocation interrupted by system crash.
*
* this function resumes merging reloc trees with corresponding fs trees.
* this is important for keeping the sharing of tree blocks
*/
int btrfs_recover_relocation(struct btrfs_root *root)
{
LIST_HEAD(reloc_roots);
struct btrfs_key key;
struct btrfs_root *fs_root;
struct btrfs_root *reloc_root;
struct btrfs_path *path;
struct extent_buffer *leaf;
struct reloc_control *rc = NULL;
struct btrfs_trans_handle *trans;
int ret;
int err = 0;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
key.objectid = BTRFS_TREE_RELOC_OBJECTID;
key.type = BTRFS_ROOT_ITEM_KEY;
key.offset = (u64)-1;
while (1) {
ret = btrfs_search_slot(NULL, root->fs_info->tree_root, &key,
path, 0, 0);
if (ret < 0) {
err = ret;
goto out;
}
if (ret > 0) {
if (path->slots[0] == 0)
break;
path->slots[0]--;
}
leaf = path->nodes[0];
btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
btrfs_release_path(root->fs_info->tree_root, path);
if (key.objectid != BTRFS_TREE_RELOC_OBJECTID ||
key.type != BTRFS_ROOT_ITEM_KEY)
break;
reloc_root = btrfs_read_fs_root_no_radix(root, &key);
if (IS_ERR(reloc_root)) {
err = PTR_ERR(reloc_root);
goto out;
}
list_add(&reloc_root->root_list, &reloc_roots);
if (btrfs_root_refs(&reloc_root->root_item) > 0) {
fs_root = read_fs_root(root->fs_info,
reloc_root->root_key.offset);
if (IS_ERR(fs_root)) {
err = PTR_ERR(fs_root);
goto out;
}
}
if (key.offset == 0)
break;
key.offset--;
}
btrfs_release_path(root->fs_info->tree_root, path);
if (list_empty(&reloc_roots))
goto out;
rc = kzalloc(sizeof(*rc), GFP_NOFS);
if (!rc) {
err = -ENOMEM;
goto out;
}
mapping_tree_init(&rc->reloc_root_tree);
INIT_LIST_HEAD(&rc->reloc_roots);
btrfs_init_workers(&rc->workers, "relocate",
root->fs_info->thread_pool_size);
rc->extent_root = root->fs_info->extent_root;
set_reloc_control(rc);
while (!list_empty(&reloc_roots)) {
reloc_root = list_entry(reloc_roots.next,
struct btrfs_root, root_list);
list_del(&reloc_root->root_list);
if (btrfs_root_refs(&reloc_root->root_item) == 0) {
list_add_tail(&reloc_root->root_list,
&rc->reloc_roots);
continue;
}
fs_root = read_fs_root(root->fs_info,
reloc_root->root_key.offset);
BUG_ON(IS_ERR(fs_root));
__add_reloc_root(reloc_root);
fs_root->reloc_root = reloc_root;
}
trans = btrfs_start_transaction(rc->extent_root, 1);
btrfs_commit_transaction(trans, rc->extent_root);
merge_reloc_roots(rc);
unset_reloc_control(rc);
trans = btrfs_start_transaction(rc->extent_root, 1);
btrfs_commit_transaction(trans, rc->extent_root);
out:
if (rc) {
btrfs_stop_workers(&rc->workers);
kfree(rc);
}
while (!list_empty(&reloc_roots)) {
reloc_root = list_entry(reloc_roots.next,
struct btrfs_root, root_list);
list_del(&reloc_root->root_list);
free_extent_buffer(reloc_root->node);
free_extent_buffer(reloc_root->commit_root);
kfree(reloc_root);
}
btrfs_free_path(path);
if (err == 0) {
/* cleanup orphan inode in data relocation tree */
fs_root = read_fs_root(root->fs_info,
BTRFS_DATA_RELOC_TREE_OBJECTID);
if (IS_ERR(fs_root))
err = PTR_ERR(fs_root);
}
return err;
}
/*
* helper to add ordered checksum for data relocation.
*
* cloning checksum properly handles the nodatasum extents.
* it also saves CPU time to re-calculate the checksum.
*/
int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len)
{
struct btrfs_ordered_sum *sums;
struct btrfs_sector_sum *sector_sum;
struct btrfs_ordered_extent *ordered;
struct btrfs_root *root = BTRFS_I(inode)->root;
size_t offset;
int ret;
u64 disk_bytenr;
LIST_HEAD(list);
ordered = btrfs_lookup_ordered_extent(inode, file_pos);
BUG_ON(ordered->file_offset != file_pos || ordered->len != len);
disk_bytenr = file_pos + BTRFS_I(inode)->index_cnt;
ret = btrfs_lookup_csums_range(root->fs_info->csum_root, disk_bytenr,
disk_bytenr + len - 1, &list);
while (!list_empty(&list)) {
sums = list_entry(list.next, struct btrfs_ordered_sum, list);
list_del_init(&sums->list);
sector_sum = sums->sums;
sums->bytenr = ordered->start;
offset = 0;
while (offset < sums->len) {
sector_sum->bytenr += ordered->start - disk_bytenr;
sector_sum++;
offset += root->sectorsize;
}
btrfs_add_ordered_sum(inode, ordered, sums);
}
btrfs_put_ordered_extent(ordered);
return 0;
}
...@@ -111,6 +111,15 @@ int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, ...@@ -111,6 +111,15 @@ int btrfs_find_last_root(struct btrfs_root *root, u64 objectid,
return ret; return ret;
} }
int btrfs_set_root_node(struct btrfs_root_item *item,
struct extent_buffer *node)
{
btrfs_set_root_bytenr(item, node->start);
btrfs_set_root_level(item, btrfs_header_level(node));
btrfs_set_root_generation(item, btrfs_header_generation(node));
return 0;
}
/* /*
* copy the data in 'item' into the btree * copy the data in 'item' into the btree
*/ */
...@@ -164,8 +173,7 @@ int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -164,8 +173,7 @@ int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root
* offset lower than the latest root. They need to be queued for deletion to * offset lower than the latest root. They need to be queued for deletion to
* finish what was happening when we crashed. * finish what was happening when we crashed.
*/ */
int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid, int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid)
struct btrfs_root *latest)
{ {
struct btrfs_root *dead_root; struct btrfs_root *dead_root;
struct btrfs_item *item; struct btrfs_item *item;
...@@ -227,10 +235,7 @@ int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid, ...@@ -227,10 +235,7 @@ int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid,
goto err; goto err;
} }
if (objectid == BTRFS_TREE_RELOC_OBJECTID) ret = btrfs_add_dead_root(dead_root);
ret = btrfs_add_dead_reloc_root(dead_root);
else
ret = btrfs_add_dead_root(dead_root, latest);
if (ret) if (ret)
goto err; goto err;
goto again; goto again;
......
...@@ -52,7 +52,6 @@ ...@@ -52,7 +52,6 @@
#include "export.h" #include "export.h"
#include "compression.h" #include "compression.h"
static struct super_operations btrfs_super_ops; static struct super_operations btrfs_super_ops;
static void btrfs_put_super(struct super_block *sb) static void btrfs_put_super(struct super_block *sb)
...@@ -67,8 +66,8 @@ static void btrfs_put_super(struct super_block *sb) ...@@ -67,8 +66,8 @@ static void btrfs_put_super(struct super_block *sb)
enum { enum {
Opt_degraded, Opt_subvol, Opt_device, Opt_nodatasum, Opt_nodatacow, Opt_degraded, Opt_subvol, Opt_device, Opt_nodatasum, Opt_nodatacow,
Opt_max_extent, Opt_max_inline, Opt_alloc_start, Opt_nobarrier, Opt_max_extent, Opt_max_inline, Opt_alloc_start, Opt_nobarrier,
Opt_ssd, Opt_thread_pool, Opt_noacl, Opt_compress, Opt_notreelog, Opt_ssd, Opt_nossd, Opt_ssd_spread, Opt_thread_pool, Opt_noacl,
Opt_ratio, Opt_flushoncommit, Opt_err, Opt_compress, Opt_notreelog, Opt_ratio, Opt_flushoncommit, Opt_err,
}; };
static match_table_t tokens = { static match_table_t tokens = {
...@@ -84,6 +83,8 @@ static match_table_t tokens = { ...@@ -84,6 +83,8 @@ static match_table_t tokens = {
{Opt_thread_pool, "thread_pool=%d"}, {Opt_thread_pool, "thread_pool=%d"},
{Opt_compress, "compress"}, {Opt_compress, "compress"},
{Opt_ssd, "ssd"}, {Opt_ssd, "ssd"},
{Opt_ssd_spread, "ssd_spread"},
{Opt_nossd, "nossd"},
{Opt_noacl, "noacl"}, {Opt_noacl, "noacl"},
{Opt_notreelog, "notreelog"}, {Opt_notreelog, "notreelog"},
{Opt_flushoncommit, "flushoncommit"}, {Opt_flushoncommit, "flushoncommit"},
...@@ -158,7 +159,7 @@ int btrfs_parse_options(struct btrfs_root *root, char *options) ...@@ -158,7 +159,7 @@ int btrfs_parse_options(struct btrfs_root *root, char *options)
*/ */
break; break;
case Opt_nodatasum: case Opt_nodatasum:
printk(KERN_INFO "btrfs: setting nodatacsum\n"); printk(KERN_INFO "btrfs: setting nodatasum\n");
btrfs_set_opt(info->mount_opt, NODATASUM); btrfs_set_opt(info->mount_opt, NODATASUM);
break; break;
case Opt_nodatacow: case Opt_nodatacow:
...@@ -174,6 +175,19 @@ int btrfs_parse_options(struct btrfs_root *root, char *options) ...@@ -174,6 +175,19 @@ int btrfs_parse_options(struct btrfs_root *root, char *options)
printk(KERN_INFO "btrfs: use ssd allocation scheme\n"); printk(KERN_INFO "btrfs: use ssd allocation scheme\n");
btrfs_set_opt(info->mount_opt, SSD); btrfs_set_opt(info->mount_opt, SSD);
break; break;
case Opt_ssd_spread:
printk(KERN_INFO "btrfs: use spread ssd "
"allocation scheme\n");
btrfs_set_opt(info->mount_opt, SSD);
btrfs_set_opt(info->mount_opt, SSD_SPREAD);
break;
case Opt_nossd:
printk(KERN_INFO "btrfs: not using ssd allocation "
"scheme\n");
btrfs_set_opt(info->mount_opt, NOSSD);
btrfs_clear_opt(info->mount_opt, SSD);
btrfs_clear_opt(info->mount_opt, SSD_SPREAD);
break;
case Opt_nobarrier: case Opt_nobarrier:
printk(KERN_INFO "btrfs: turning off barriers\n"); printk(KERN_INFO "btrfs: turning off barriers\n");
btrfs_set_opt(info->mount_opt, NOBARRIER); btrfs_set_opt(info->mount_opt, NOBARRIER);
...@@ -322,7 +336,7 @@ static int btrfs_fill_super(struct super_block *sb, ...@@ -322,7 +336,7 @@ static int btrfs_fill_super(struct super_block *sb,
struct dentry *root_dentry; struct dentry *root_dentry;
struct btrfs_super_block *disk_super; struct btrfs_super_block *disk_super;
struct btrfs_root *tree_root; struct btrfs_root *tree_root;
struct btrfs_inode *bi; struct btrfs_key key;
int err; int err;
sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_maxbytes = MAX_LFS_FILESIZE;
...@@ -341,23 +355,15 @@ static int btrfs_fill_super(struct super_block *sb, ...@@ -341,23 +355,15 @@ static int btrfs_fill_super(struct super_block *sb,
} }
sb->s_fs_info = tree_root; sb->s_fs_info = tree_root;
disk_super = &tree_root->fs_info->super_copy; disk_super = &tree_root->fs_info->super_copy;
inode = btrfs_iget_locked(sb, BTRFS_FIRST_FREE_OBJECTID,
tree_root->fs_info->fs_root);
bi = BTRFS_I(inode);
bi->location.objectid = inode->i_ino;
bi->location.offset = 0;
bi->root = tree_root->fs_info->fs_root;
btrfs_set_key_type(&bi->location, BTRFS_INODE_ITEM_KEY); key.objectid = BTRFS_FIRST_FREE_OBJECTID;
key.type = BTRFS_INODE_ITEM_KEY;
if (!inode) { key.offset = 0;
err = -ENOMEM; inode = btrfs_iget(sb, &key, tree_root->fs_info->fs_root);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
goto fail_close; goto fail_close;
} }
if (inode->i_state & I_NEW) {
btrfs_read_locked_inode(inode);
unlock_new_inode(inode);
}
root_dentry = d_alloc_root(inode); root_dentry = d_alloc_root(inode);
if (!root_dentry) { if (!root_dentry) {
...@@ -433,7 +439,11 @@ static int btrfs_show_options(struct seq_file *seq, struct vfsmount *vfs) ...@@ -433,7 +439,11 @@ static int btrfs_show_options(struct seq_file *seq, struct vfsmount *vfs)
seq_printf(seq, ",thread_pool=%d", info->thread_pool_size); seq_printf(seq, ",thread_pool=%d", info->thread_pool_size);
if (btrfs_test_opt(root, COMPRESS)) if (btrfs_test_opt(root, COMPRESS))
seq_puts(seq, ",compress"); seq_puts(seq, ",compress");
if (btrfs_test_opt(root, SSD)) if (btrfs_test_opt(root, NOSSD))
seq_puts(seq, ",nossd");
if (btrfs_test_opt(root, SSD_SPREAD))
seq_puts(seq, ",ssd_spread");
else if (btrfs_test_opt(root, SSD))
seq_puts(seq, ",ssd"); seq_puts(seq, ",ssd");
if (btrfs_test_opt(root, NOTREELOG)) if (btrfs_test_opt(root, NOTREELOG))
seq_puts(seq, ",notreelog"); seq_puts(seq, ",notreelog");
...@@ -584,7 +594,8 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data) ...@@ -584,7 +594,8 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
if (btrfs_super_log_root(&root->fs_info->super_copy) != 0) if (btrfs_super_log_root(&root->fs_info->super_copy) != 0)
return -EINVAL; return -EINVAL;
ret = btrfs_cleanup_reloc_trees(root); /* recover relocation */
ret = btrfs_recover_relocation(root);
WARN_ON(ret); WARN_ON(ret);
ret = btrfs_cleanup_fs_roots(root->fs_info); ret = btrfs_cleanup_fs_roots(root->fs_info);
......
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
#include "disk-io.h" #include "disk-io.h"
#include "transaction.h" #include "transaction.h"
#include "locking.h" #include "locking.h"
#include "ref-cache.h"
#include "tree-log.h" #include "tree-log.h"
#define BTRFS_ROOT_TRANS_TAG 0 #define BTRFS_ROOT_TRANS_TAG 0
...@@ -94,45 +93,37 @@ static noinline int join_transaction(struct btrfs_root *root) ...@@ -94,45 +93,37 @@ static noinline int join_transaction(struct btrfs_root *root)
* to make sure the old root from before we joined the transaction is deleted * to make sure the old root from before we joined the transaction is deleted
* when the transaction commits * when the transaction commits
*/ */
noinline int btrfs_record_root_in_trans(struct btrfs_root *root) static noinline int record_root_in_trans(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
{ {
struct btrfs_dirty_root *dirty; if (root->ref_cows && root->last_trans < trans->transid) {
u64 running_trans_id = root->fs_info->running_transaction->transid;
if (root->ref_cows && root->last_trans < running_trans_id) {
WARN_ON(root == root->fs_info->extent_root); WARN_ON(root == root->fs_info->extent_root);
if (root->root_item.refs != 0) { WARN_ON(root->root_item.refs == 0);
radix_tree_tag_set(&root->fs_info->fs_roots_radix, WARN_ON(root->commit_root != root->node);
(unsigned long)root->root_key.objectid,
BTRFS_ROOT_TRANS_TAG); radix_tree_tag_set(&root->fs_info->fs_roots_radix,
(unsigned long)root->root_key.objectid,
dirty = kmalloc(sizeof(*dirty), GFP_NOFS); BTRFS_ROOT_TRANS_TAG);
BUG_ON(!dirty); root->last_trans = trans->transid;
dirty->root = kmalloc(sizeof(*dirty->root), GFP_NOFS); btrfs_init_reloc_root(trans, root);
BUG_ON(!dirty->root); }
dirty->latest_root = root; return 0;
INIT_LIST_HEAD(&dirty->list); }
root->commit_root = btrfs_root_node(root);
memcpy(dirty->root, root, sizeof(*root));
spin_lock_init(&dirty->root->node_lock);
spin_lock_init(&dirty->root->list_lock);
mutex_init(&dirty->root->objectid_mutex);
mutex_init(&dirty->root->log_mutex);
INIT_LIST_HEAD(&dirty->root->dead_list);
dirty->root->node = root->commit_root;
dirty->root->commit_root = NULL;
spin_lock(&root->list_lock); int btrfs_record_root_in_trans(struct btrfs_trans_handle *trans,
list_add(&dirty->root->dead_list, &root->dead_list); struct btrfs_root *root)
spin_unlock(&root->list_lock); {
if (!root->ref_cows)
return 0;
root->dirty_root = dirty; mutex_lock(&root->fs_info->trans_mutex);
} else { if (root->last_trans == trans->transid) {
WARN_ON(1); mutex_unlock(&root->fs_info->trans_mutex);
} return 0;
root->last_trans = running_trans_id;
} }
record_root_in_trans(trans, root);
mutex_unlock(&root->fs_info->trans_mutex);
return 0; return 0;
} }
...@@ -181,7 +172,6 @@ static struct btrfs_trans_handle *start_transaction(struct btrfs_root *root, ...@@ -181,7 +172,6 @@ static struct btrfs_trans_handle *start_transaction(struct btrfs_root *root,
ret = join_transaction(root); ret = join_transaction(root);
BUG_ON(ret); BUG_ON(ret);
btrfs_record_root_in_trans(root);
h->transid = root->fs_info->running_transaction->transid; h->transid = root->fs_info->running_transaction->transid;
h->transaction = root->fs_info->running_transaction; h->transaction = root->fs_info->running_transaction;
h->blocks_reserved = num_blocks; h->blocks_reserved = num_blocks;
...@@ -192,6 +182,7 @@ static struct btrfs_trans_handle *start_transaction(struct btrfs_root *root, ...@@ -192,6 +182,7 @@ static struct btrfs_trans_handle *start_transaction(struct btrfs_root *root,
h->delayed_ref_updates = 0; h->delayed_ref_updates = 0;
root->fs_info->running_transaction->use_count++; root->fs_info->running_transaction->use_count++;
record_root_in_trans(h, root);
mutex_unlock(&root->fs_info->trans_mutex); mutex_unlock(&root->fs_info->trans_mutex);
return h; return h;
} }
...@@ -233,6 +224,7 @@ static noinline int wait_for_commit(struct btrfs_root *root, ...@@ -233,6 +224,7 @@ static noinline int wait_for_commit(struct btrfs_root *root,
return 0; return 0;
} }
#if 0
/* /*
* rate limit against the drop_snapshot code. This helps to slow down new * rate limit against the drop_snapshot code. This helps to slow down new
* operations if the drop_snapshot code isn't able to keep up. * operations if the drop_snapshot code isn't able to keep up.
...@@ -273,6 +265,7 @@ static void throttle_on_drops(struct btrfs_root *root) ...@@ -273,6 +265,7 @@ static void throttle_on_drops(struct btrfs_root *root)
goto harder; goto harder;
} }
} }
#endif
void btrfs_throttle(struct btrfs_root *root) void btrfs_throttle(struct btrfs_root *root)
{ {
...@@ -280,7 +273,6 @@ void btrfs_throttle(struct btrfs_root *root) ...@@ -280,7 +273,6 @@ void btrfs_throttle(struct btrfs_root *root)
if (!root->fs_info->open_ioctl_trans) if (!root->fs_info->open_ioctl_trans)
wait_current_trans(root); wait_current_trans(root);
mutex_unlock(&root->fs_info->trans_mutex); mutex_unlock(&root->fs_info->trans_mutex);
throttle_on_drops(root);
} }
static int __btrfs_end_transaction(struct btrfs_trans_handle *trans, static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
...@@ -323,9 +315,6 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans, ...@@ -323,9 +315,6 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
memset(trans, 0, sizeof(*trans)); memset(trans, 0, sizeof(*trans));
kmem_cache_free(btrfs_trans_handle_cachep, trans); kmem_cache_free(btrfs_trans_handle_cachep, trans);
if (throttle)
throttle_on_drops(root);
return 0; return 0;
} }
...@@ -462,12 +451,8 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans, ...@@ -462,12 +451,8 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans,
old_root_bytenr = btrfs_root_bytenr(&root->root_item); old_root_bytenr = btrfs_root_bytenr(&root->root_item);
if (old_root_bytenr == root->node->start) if (old_root_bytenr == root->node->start)
break; break;
btrfs_set_root_bytenr(&root->root_item,
root->node->start);
btrfs_set_root_level(&root->root_item,
btrfs_header_level(root->node));
btrfs_set_root_generation(&root->root_item, trans->transid);
btrfs_set_root_node(&root->root_item, root->node);
ret = btrfs_update_root(trans, tree_root, ret = btrfs_update_root(trans, tree_root,
&root->root_key, &root->root_key,
&root->root_item); &root->root_item);
...@@ -477,14 +462,16 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans, ...@@ -477,14 +462,16 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans,
ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1); ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
BUG_ON(ret); BUG_ON(ret);
} }
free_extent_buffer(root->commit_root);
root->commit_root = btrfs_root_node(root);
return 0; return 0;
} }
/* /*
* update all the cowonly tree roots on disk * update all the cowonly tree roots on disk
*/ */
int btrfs_commit_tree_roots(struct btrfs_trans_handle *trans, static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans,
struct btrfs_root *root) struct btrfs_root *root)
{ {
struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_fs_info *fs_info = root->fs_info;
struct list_head *next; struct list_head *next;
...@@ -520,118 +507,54 @@ int btrfs_commit_tree_roots(struct btrfs_trans_handle *trans, ...@@ -520,118 +507,54 @@ int btrfs_commit_tree_roots(struct btrfs_trans_handle *trans,
* a dirty root struct and adds it into the list of dead roots that need to * a dirty root struct and adds it into the list of dead roots that need to
* be deleted * be deleted
*/ */
int btrfs_add_dead_root(struct btrfs_root *root, struct btrfs_root *latest) int btrfs_add_dead_root(struct btrfs_root *root)
{ {
struct btrfs_dirty_root *dirty;
dirty = kmalloc(sizeof(*dirty), GFP_NOFS);
if (!dirty)
return -ENOMEM;
dirty->root = root;
dirty->latest_root = latest;
mutex_lock(&root->fs_info->trans_mutex); mutex_lock(&root->fs_info->trans_mutex);
list_add(&dirty->list, &latest->fs_info->dead_roots); list_add(&root->root_list, &root->fs_info->dead_roots);
mutex_unlock(&root->fs_info->trans_mutex); mutex_unlock(&root->fs_info->trans_mutex);
return 0; return 0;
} }
/* /*
* at transaction commit time we need to schedule the old roots for * update all the cowonly tree roots on disk
* deletion via btrfs_drop_snapshot. This runs through all the
* reference counted roots that were modified in the current
* transaction and puts them into the drop list
*/ */
static noinline int add_dirty_roots(struct btrfs_trans_handle *trans, static noinline int commit_fs_roots(struct btrfs_trans_handle *trans,
struct radix_tree_root *radix, struct btrfs_root *root)
struct list_head *list)
{ {
struct btrfs_dirty_root *dirty;
struct btrfs_root *gang[8]; struct btrfs_root *gang[8];
struct btrfs_root *root; struct btrfs_fs_info *fs_info = root->fs_info;
int i; int i;
int ret; int ret;
int err = 0; int err = 0;
u32 refs;
while (1) { while (1) {
ret = radix_tree_gang_lookup_tag(radix, (void **)gang, 0, ret = radix_tree_gang_lookup_tag(&fs_info->fs_roots_radix,
(void **)gang, 0,
ARRAY_SIZE(gang), ARRAY_SIZE(gang),
BTRFS_ROOT_TRANS_TAG); BTRFS_ROOT_TRANS_TAG);
if (ret == 0) if (ret == 0)
break; break;
for (i = 0; i < ret; i++) { for (i = 0; i < ret; i++) {
root = gang[i]; root = gang[i];
radix_tree_tag_clear(radix, radix_tree_tag_clear(&fs_info->fs_roots_radix,
(unsigned long)root->root_key.objectid, (unsigned long)root->root_key.objectid,
BTRFS_ROOT_TRANS_TAG); BTRFS_ROOT_TRANS_TAG);
BUG_ON(!root->ref_tree);
dirty = root->dirty_root;
btrfs_free_log(trans, root); btrfs_free_log(trans, root);
btrfs_free_reloc_root(trans, root); btrfs_update_reloc_root(trans, root);
if (root->commit_root == root->node) {
WARN_ON(root->node->start !=
btrfs_root_bytenr(&root->root_item));
free_extent_buffer(root->commit_root);
root->commit_root = NULL;
root->dirty_root = NULL;
spin_lock(&root->list_lock);
list_del_init(&dirty->root->dead_list);
spin_unlock(&root->list_lock);
kfree(dirty->root); if (root->commit_root == root->node)
kfree(dirty);
/* make sure to update the root on disk
* so we get any updates to the block used
* counts
*/
err = btrfs_update_root(trans,
root->fs_info->tree_root,
&root->root_key,
&root->root_item);
continue; continue;
}
memset(&root->root_item.drop_progress, 0, free_extent_buffer(root->commit_root);
sizeof(struct btrfs_disk_key)); root->commit_root = btrfs_root_node(root);
root->root_item.drop_level = 0;
root->commit_root = NULL; btrfs_set_root_node(&root->root_item, root->node);
root->dirty_root = NULL; err = btrfs_update_root(trans, fs_info->tree_root,
root->root_key.offset = root->fs_info->generation;
btrfs_set_root_bytenr(&root->root_item,
root->node->start);
btrfs_set_root_level(&root->root_item,
btrfs_header_level(root->node));
btrfs_set_root_generation(&root->root_item,
root->root_key.offset);
err = btrfs_insert_root(trans, root->fs_info->tree_root,
&root->root_key, &root->root_key,
&root->root_item); &root->root_item);
if (err) if (err)
break; break;
refs = btrfs_root_refs(&dirty->root->root_item);
btrfs_set_root_refs(&dirty->root->root_item, refs - 1);
err = btrfs_update_root(trans, root->fs_info->tree_root,
&dirty->root->root_key,
&dirty->root->root_item);
BUG_ON(err);
if (refs == 1) {
list_add(&dirty->list, list);
} else {
WARN_ON(1);
free_extent_buffer(dirty->root->node);
kfree(dirty->root);
kfree(dirty);
}
} }
} }
return err; return err;
...@@ -688,12 +611,8 @@ static noinline int wait_transaction_pre_flush(struct btrfs_fs_info *info) ...@@ -688,12 +611,8 @@ static noinline int wait_transaction_pre_flush(struct btrfs_fs_info *info)
TASK_UNINTERRUPTIBLE); TASK_UNINTERRUPTIBLE);
mutex_unlock(&info->trans_mutex); mutex_unlock(&info->trans_mutex);
atomic_dec(&info->throttles);
wake_up(&info->transaction_throttle);
schedule(); schedule();
atomic_inc(&info->throttles);
mutex_lock(&info->trans_mutex); mutex_lock(&info->trans_mutex);
finish_wait(&info->transaction_wait, &wait); finish_wait(&info->transaction_wait, &wait);
} }
...@@ -705,111 +624,61 @@ static noinline int wait_transaction_pre_flush(struct btrfs_fs_info *info) ...@@ -705,111 +624,61 @@ static noinline int wait_transaction_pre_flush(struct btrfs_fs_info *info)
* Given a list of roots that need to be deleted, call btrfs_drop_snapshot on * Given a list of roots that need to be deleted, call btrfs_drop_snapshot on
* all of them * all of them
*/ */
static noinline int drop_dirty_roots(struct btrfs_root *tree_root, int btrfs_drop_dead_root(struct btrfs_root *root)
struct list_head *list)
{ {
struct btrfs_dirty_root *dirty;
struct btrfs_trans_handle *trans; struct btrfs_trans_handle *trans;
struct btrfs_root *tree_root = root->fs_info->tree_root;
unsigned long nr; unsigned long nr;
u64 num_bytes; int ret;
u64 bytes_used;
u64 max_useless;
int ret = 0;
int err;
while (!list_empty(list)) {
struct btrfs_root *root;
dirty = list_entry(list->prev, struct btrfs_dirty_root, list);
list_del_init(&dirty->list);
num_bytes = btrfs_root_used(&dirty->root->root_item);
root = dirty->latest_root;
atomic_inc(&root->fs_info->throttles);
while (1) {
/*
* we don't want to jump in and create a bunch of
* delayed refs if the transaction is starting to close
*/
wait_transaction_pre_flush(tree_root->fs_info);
trans = btrfs_start_transaction(tree_root, 1);
/*
* we've joined a transaction, make sure it isn't
* closing right now
*/
if (trans->transaction->delayed_refs.flushing) {
btrfs_end_transaction(trans, tree_root);
continue;
}
mutex_lock(&root->fs_info->drop_mutex);
ret = btrfs_drop_snapshot(trans, dirty->root);
if (ret != -EAGAIN)
break;
mutex_unlock(&root->fs_info->drop_mutex);
err = btrfs_update_root(trans, while (1) {
tree_root, /*
&dirty->root->root_key, * we don't want to jump in and create a bunch of
&dirty->root->root_item); * delayed refs if the transaction is starting to close
if (err) */
ret = err; wait_transaction_pre_flush(tree_root->fs_info);
nr = trans->blocks_used; trans = btrfs_start_transaction(tree_root, 1);
ret = btrfs_end_transaction(trans, tree_root);
BUG_ON(ret);
btrfs_btree_balance_dirty(tree_root, nr); /*
cond_resched(); * we've joined a transaction, make sure it isn't
* closing right now
*/
if (trans->transaction->delayed_refs.flushing) {
btrfs_end_transaction(trans, tree_root);
continue;
} }
BUG_ON(ret);
atomic_dec(&root->fs_info->throttles);
wake_up(&root->fs_info->transaction_throttle);
num_bytes -= btrfs_root_used(&dirty->root->root_item); ret = btrfs_drop_snapshot(trans, root);
bytes_used = btrfs_root_used(&root->root_item); if (ret != -EAGAIN)
if (num_bytes) { break;
mutex_lock(&root->fs_info->trans_mutex);
btrfs_record_root_in_trans(root);
mutex_unlock(&root->fs_info->trans_mutex);
btrfs_set_root_used(&root->root_item,
bytes_used - num_bytes);
}
ret = btrfs_del_root(trans, tree_root, &dirty->root->root_key); ret = btrfs_update_root(trans, tree_root,
if (ret) { &root->root_key,
BUG(); &root->root_item);
if (ret)
break; break;
}
mutex_unlock(&root->fs_info->drop_mutex);
spin_lock(&root->list_lock);
list_del_init(&dirty->root->dead_list);
if (!list_empty(&root->dead_list)) {
struct btrfs_root *oldest;
oldest = list_entry(root->dead_list.prev,
struct btrfs_root, dead_list);
max_useless = oldest->root_key.offset - 1;
} else {
max_useless = root->root_key.offset - 1;
}
spin_unlock(&root->list_lock);
nr = trans->blocks_used; nr = trans->blocks_used;
ret = btrfs_end_transaction(trans, tree_root); ret = btrfs_end_transaction(trans, tree_root);
BUG_ON(ret); BUG_ON(ret);
ret = btrfs_remove_leaf_refs(root, max_useless, 0);
BUG_ON(ret);
free_extent_buffer(dirty->root->node);
kfree(dirty->root);
kfree(dirty);
btrfs_btree_balance_dirty(tree_root, nr); btrfs_btree_balance_dirty(tree_root, nr);
cond_resched(); cond_resched();
} }
BUG_ON(ret);
ret = btrfs_del_root(trans, tree_root, &root->root_key);
BUG_ON(ret);
nr = trans->blocks_used;
ret = btrfs_end_transaction(trans, tree_root);
BUG_ON(ret);
free_extent_buffer(root->node);
free_extent_buffer(root->commit_root);
kfree(root);
btrfs_btree_balance_dirty(tree_root, nr);
return ret; return ret;
} }
...@@ -839,24 +708,23 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, ...@@ -839,24 +708,23 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
if (ret) if (ret)
goto fail; goto fail;
btrfs_record_root_in_trans(root); record_root_in_trans(trans, root);
btrfs_set_root_last_snapshot(&root->root_item, trans->transid); btrfs_set_root_last_snapshot(&root->root_item, trans->transid);
memcpy(new_root_item, &root->root_item, sizeof(*new_root_item)); memcpy(new_root_item, &root->root_item, sizeof(*new_root_item));
key.objectid = objectid; key.objectid = objectid;
key.offset = trans->transid; key.offset = 0;
btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
old = btrfs_lock_root_node(root); old = btrfs_lock_root_node(root);
btrfs_cow_block(trans, root, old, NULL, 0, &old); btrfs_cow_block(trans, root, old, NULL, 0, &old);
btrfs_set_lock_blocking(old);
btrfs_copy_root(trans, root, old, &tmp, objectid); btrfs_copy_root(trans, root, old, &tmp, objectid);
btrfs_tree_unlock(old); btrfs_tree_unlock(old);
free_extent_buffer(old); free_extent_buffer(old);
btrfs_set_root_bytenr(new_root_item, tmp->start); btrfs_set_root_node(new_root_item, tmp);
btrfs_set_root_level(new_root_item, btrfs_header_level(tmp));
btrfs_set_root_generation(new_root_item, trans->transid);
ret = btrfs_insert_root(trans, root->fs_info->tree_root, &key, ret = btrfs_insert_root(trans, root->fs_info->tree_root, &key,
new_root_item); new_root_item);
btrfs_tree_unlock(tmp); btrfs_tree_unlock(tmp);
...@@ -964,6 +832,24 @@ static noinline int finish_pending_snapshots(struct btrfs_trans_handle *trans, ...@@ -964,6 +832,24 @@ static noinline int finish_pending_snapshots(struct btrfs_trans_handle *trans,
return 0; return 0;
} }
static void update_super_roots(struct btrfs_root *root)
{
struct btrfs_root_item *root_item;
struct btrfs_super_block *super;
super = &root->fs_info->super_copy;
root_item = &root->fs_info->chunk_root->root_item;
super->chunk_root = root_item->bytenr;
super->chunk_root_generation = root_item->generation;
super->chunk_root_level = root_item->level;
root_item = &root->fs_info->tree_root->root_item;
super->root = root_item->bytenr;
super->generation = root_item->generation;
super->root_level = root_item->level;
}
int btrfs_commit_transaction(struct btrfs_trans_handle *trans, int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
struct btrfs_root *root) struct btrfs_root *root)
{ {
...@@ -971,8 +857,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, ...@@ -971,8 +857,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
unsigned long timeout = 1; unsigned long timeout = 1;
struct btrfs_transaction *cur_trans; struct btrfs_transaction *cur_trans;
struct btrfs_transaction *prev_trans = NULL; struct btrfs_transaction *prev_trans = NULL;
struct btrfs_root *chunk_root = root->fs_info->chunk_root;
struct list_head dirty_fs_roots;
struct extent_io_tree *pinned_copy; struct extent_io_tree *pinned_copy;
DEFINE_WAIT(wait); DEFINE_WAIT(wait);
int ret; int ret;
...@@ -999,7 +883,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, ...@@ -999,7 +883,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
BUG_ON(ret); BUG_ON(ret);
mutex_lock(&root->fs_info->trans_mutex); mutex_lock(&root->fs_info->trans_mutex);
INIT_LIST_HEAD(&dirty_fs_roots);
if (cur_trans->in_commit) { if (cur_trans->in_commit) {
cur_trans->use_count++; cur_trans->use_count++;
mutex_unlock(&root->fs_info->trans_mutex); mutex_unlock(&root->fs_info->trans_mutex);
...@@ -1105,41 +988,36 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, ...@@ -1105,41 +988,36 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
* with the tree-log code. * with the tree-log code.
*/ */
mutex_lock(&root->fs_info->tree_log_mutex); mutex_lock(&root->fs_info->tree_log_mutex);
/*
* keep tree reloc code from adding new reloc trees
*/
mutex_lock(&root->fs_info->tree_reloc_mutex);
ret = add_dirty_roots(trans, &root->fs_info->fs_roots_radix, ret = commit_fs_roots(trans, root);
&dirty_fs_roots);
BUG_ON(ret); BUG_ON(ret);
/* add_dirty_roots gets rid of all the tree log roots, it is now /* commit_fs_roots gets rid of all the tree log roots, it is now
* safe to free the root of tree log roots * safe to free the root of tree log roots
*/ */
btrfs_free_log_root_tree(trans, root->fs_info); btrfs_free_log_root_tree(trans, root->fs_info);
ret = btrfs_commit_tree_roots(trans, root); ret = commit_cowonly_roots(trans, root);
BUG_ON(ret); BUG_ON(ret);
cur_trans = root->fs_info->running_transaction; cur_trans = root->fs_info->running_transaction;
spin_lock(&root->fs_info->new_trans_lock); spin_lock(&root->fs_info->new_trans_lock);
root->fs_info->running_transaction = NULL; root->fs_info->running_transaction = NULL;
spin_unlock(&root->fs_info->new_trans_lock); spin_unlock(&root->fs_info->new_trans_lock);
btrfs_set_super_generation(&root->fs_info->super_copy,
cur_trans->transid); btrfs_set_root_node(&root->fs_info->tree_root->root_item,
btrfs_set_super_root(&root->fs_info->super_copy, root->fs_info->tree_root->node);
root->fs_info->tree_root->node->start); free_extent_buffer(root->fs_info->tree_root->commit_root);
btrfs_set_super_root_level(&root->fs_info->super_copy, root->fs_info->tree_root->commit_root =
btrfs_header_level(root->fs_info->tree_root->node)); btrfs_root_node(root->fs_info->tree_root);
btrfs_set_super_chunk_root(&root->fs_info->super_copy, btrfs_set_root_node(&root->fs_info->chunk_root->root_item,
chunk_root->node->start); root->fs_info->chunk_root->node);
btrfs_set_super_chunk_root_level(&root->fs_info->super_copy, free_extent_buffer(root->fs_info->chunk_root->commit_root);
btrfs_header_level(chunk_root->node)); root->fs_info->chunk_root->commit_root =
btrfs_set_super_chunk_root_generation(&root->fs_info->super_copy, btrfs_root_node(root->fs_info->chunk_root);
btrfs_header_generation(chunk_root->node));
update_super_roots(root);
if (!root->fs_info->log_root_recovering) { if (!root->fs_info->log_root_recovering) {
btrfs_set_super_log_root(&root->fs_info->super_copy, 0); btrfs_set_super_log_root(&root->fs_info->super_copy, 0);
...@@ -1153,7 +1031,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, ...@@ -1153,7 +1031,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
trans->transaction->blocked = 0; trans->transaction->blocked = 0;
wake_up(&root->fs_info->transaction_throttle);
wake_up(&root->fs_info->transaction_wait); wake_up(&root->fs_info->transaction_wait);
mutex_unlock(&root->fs_info->trans_mutex); mutex_unlock(&root->fs_info->trans_mutex);
...@@ -1170,9 +1047,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, ...@@ -1170,9 +1047,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
btrfs_finish_extent_commit(trans, root, pinned_copy); btrfs_finish_extent_commit(trans, root, pinned_copy);
kfree(pinned_copy); kfree(pinned_copy);
btrfs_drop_dead_reloc_roots(root);
mutex_unlock(&root->fs_info->tree_reloc_mutex);
/* do the directory inserts of any pending snapshot creations */ /* do the directory inserts of any pending snapshot creations */
finish_pending_snapshots(trans, root->fs_info); finish_pending_snapshots(trans, root->fs_info);
...@@ -1186,16 +1060,9 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, ...@@ -1186,16 +1060,9 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
put_transaction(cur_trans); put_transaction(cur_trans);
put_transaction(cur_trans); put_transaction(cur_trans);
list_splice_init(&dirty_fs_roots, &root->fs_info->dead_roots);
if (root->fs_info->closing)
list_splice_init(&root->fs_info->dead_roots, &dirty_fs_roots);
mutex_unlock(&root->fs_info->trans_mutex); mutex_unlock(&root->fs_info->trans_mutex);
kmem_cache_free(btrfs_trans_handle_cachep, trans); kmem_cache_free(btrfs_trans_handle_cachep, trans);
if (root->fs_info->closing)
drop_dirty_roots(root->fs_info->tree_root, &dirty_fs_roots);
return ret; return ret;
} }
...@@ -1204,16 +1071,17 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, ...@@ -1204,16 +1071,17 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
*/ */
int btrfs_clean_old_snapshots(struct btrfs_root *root) int btrfs_clean_old_snapshots(struct btrfs_root *root)
{ {
struct list_head dirty_roots; LIST_HEAD(list);
INIT_LIST_HEAD(&dirty_roots); struct btrfs_fs_info *fs_info = root->fs_info;
again:
mutex_lock(&root->fs_info->trans_mutex); mutex_lock(&fs_info->trans_mutex);
list_splice_init(&root->fs_info->dead_roots, &dirty_roots); list_splice_init(&fs_info->dead_roots, &list);
mutex_unlock(&root->fs_info->trans_mutex); mutex_unlock(&fs_info->trans_mutex);
if (!list_empty(&dirty_roots)) { while (!list_empty(&list)) {
drop_dirty_roots(root, &dirty_roots); root = list_entry(list.next, struct btrfs_root, root_list);
goto again; list_del_init(&root->root_list);
btrfs_drop_dead_root(root);
} }
return 0; return 0;
} }
...@@ -62,12 +62,6 @@ struct btrfs_pending_snapshot { ...@@ -62,12 +62,6 @@ struct btrfs_pending_snapshot {
struct list_head list; struct list_head list;
}; };
struct btrfs_dirty_root {
struct list_head list;
struct btrfs_root *root;
struct btrfs_root *latest_root;
};
static inline void btrfs_set_trans_block_group(struct btrfs_trans_handle *trans, static inline void btrfs_set_trans_block_group(struct btrfs_trans_handle *trans,
struct inode *inode) struct inode *inode)
{ {
...@@ -100,7 +94,8 @@ int btrfs_write_and_wait_transaction(struct btrfs_trans_handle *trans, ...@@ -100,7 +94,8 @@ int btrfs_write_and_wait_transaction(struct btrfs_trans_handle *trans,
int btrfs_commit_tree_roots(struct btrfs_trans_handle *trans, int btrfs_commit_tree_roots(struct btrfs_trans_handle *trans,
struct btrfs_root *root); struct btrfs_root *root);
int btrfs_add_dead_root(struct btrfs_root *root, struct btrfs_root *latest); int btrfs_add_dead_root(struct btrfs_root *root);
int btrfs_drop_dead_root(struct btrfs_root *root);
int btrfs_defrag_root(struct btrfs_root *root, int cacheonly); int btrfs_defrag_root(struct btrfs_root *root, int cacheonly);
int btrfs_clean_old_snapshots(struct btrfs_root *root); int btrfs_clean_old_snapshots(struct btrfs_root *root);
int btrfs_commit_transaction(struct btrfs_trans_handle *trans, int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
...@@ -108,7 +103,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, ...@@ -108,7 +103,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
int btrfs_end_transaction_throttle(struct btrfs_trans_handle *trans, int btrfs_end_transaction_throttle(struct btrfs_trans_handle *trans,
struct btrfs_root *root); struct btrfs_root *root);
void btrfs_throttle(struct btrfs_root *root); void btrfs_throttle(struct btrfs_root *root);
int btrfs_record_root_in_trans(struct btrfs_root *root); int btrfs_record_root_in_trans(struct btrfs_trans_handle *trans,
struct btrfs_root *root);
int btrfs_write_and_wait_marked_extents(struct btrfs_root *root, int btrfs_write_and_wait_marked_extents(struct btrfs_root *root,
struct extent_io_tree *dirty_pages); struct extent_io_tree *dirty_pages);
#endif #endif
...@@ -430,18 +430,16 @@ static noinline int overwrite_item(struct btrfs_trans_handle *trans, ...@@ -430,18 +430,16 @@ static noinline int overwrite_item(struct btrfs_trans_handle *trans,
static noinline struct inode *read_one_inode(struct btrfs_root *root, static noinline struct inode *read_one_inode(struct btrfs_root *root,
u64 objectid) u64 objectid)
{ {
struct btrfs_key key;
struct inode *inode; struct inode *inode;
inode = btrfs_iget_locked(root->fs_info->sb, objectid, root);
if (inode->i_state & I_NEW) {
BTRFS_I(inode)->root = root;
BTRFS_I(inode)->location.objectid = objectid;
BTRFS_I(inode)->location.type = BTRFS_INODE_ITEM_KEY;
BTRFS_I(inode)->location.offset = 0;
btrfs_read_locked_inode(inode);
unlock_new_inode(inode);
} key.objectid = objectid;
if (is_bad_inode(inode)) { key.type = BTRFS_INODE_ITEM_KEY;
key.offset = 0;
inode = btrfs_iget(root->fs_info->sb, &key, root);
if (IS_ERR(inode)) {
inode = NULL;
} else if (is_bad_inode(inode)) {
iput(inode); iput(inode);
inode = NULL; inode = NULL;
} }
...@@ -541,6 +539,7 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans, ...@@ -541,6 +539,7 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
if (found_type == BTRFS_FILE_EXTENT_REG || if (found_type == BTRFS_FILE_EXTENT_REG ||
found_type == BTRFS_FILE_EXTENT_PREALLOC) { found_type == BTRFS_FILE_EXTENT_PREALLOC) {
u64 offset;
unsigned long dest_offset; unsigned long dest_offset;
struct btrfs_key ins; struct btrfs_key ins;
...@@ -555,6 +554,7 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans, ...@@ -555,6 +554,7 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
ins.objectid = btrfs_file_extent_disk_bytenr(eb, item); ins.objectid = btrfs_file_extent_disk_bytenr(eb, item);
ins.offset = btrfs_file_extent_disk_num_bytes(eb, item); ins.offset = btrfs_file_extent_disk_num_bytes(eb, item);
ins.type = BTRFS_EXTENT_ITEM_KEY; ins.type = BTRFS_EXTENT_ITEM_KEY;
offset = key->offset - btrfs_file_extent_offset(eb, item);
if (ins.objectid > 0) { if (ins.objectid > 0) {
u64 csum_start; u64 csum_start;
...@@ -569,19 +569,16 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans, ...@@ -569,19 +569,16 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
if (ret == 0) { if (ret == 0) {
ret = btrfs_inc_extent_ref(trans, root, ret = btrfs_inc_extent_ref(trans, root,
ins.objectid, ins.offset, ins.objectid, ins.offset,
path->nodes[0]->start, 0, root->root_key.objectid,
root->root_key.objectid, key->objectid, offset);
trans->transid, key->objectid);
} else { } else {
/* /*
* insert the extent pointer in the extent * insert the extent pointer in the extent
* allocation tree * allocation tree
*/ */
ret = btrfs_alloc_logged_extent(trans, root, ret = btrfs_alloc_logged_file_extent(trans,
path->nodes[0]->start, root, root->root_key.objectid,
root->root_key.objectid, key->objectid, offset, &ins);
trans->transid, key->objectid,
&ins);
BUG_ON(ret); BUG_ON(ret);
} }
btrfs_release_path(root, path); btrfs_release_path(root, path);
...@@ -1706,9 +1703,6 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans, ...@@ -1706,9 +1703,6 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
btrfs_wait_tree_block_writeback(next); btrfs_wait_tree_block_writeback(next);
btrfs_tree_unlock(next); btrfs_tree_unlock(next);
ret = btrfs_drop_leaf_ref(trans, root, next);
BUG_ON(ret);
WARN_ON(root_owner != WARN_ON(root_owner !=
BTRFS_TREE_LOG_OBJECTID); BTRFS_TREE_LOG_OBJECTID);
ret = btrfs_free_reserved_extent(root, ret = btrfs_free_reserved_extent(root,
...@@ -1753,10 +1747,6 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans, ...@@ -1753,10 +1747,6 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
btrfs_wait_tree_block_writeback(next); btrfs_wait_tree_block_writeback(next);
btrfs_tree_unlock(next); btrfs_tree_unlock(next);
if (*level == 0) {
ret = btrfs_drop_leaf_ref(trans, root, next);
BUG_ON(ret);
}
WARN_ON(root_owner != BTRFS_TREE_LOG_OBJECTID); WARN_ON(root_owner != BTRFS_TREE_LOG_OBJECTID);
ret = btrfs_free_reserved_extent(root, bytenr, blocksize); ret = btrfs_free_reserved_extent(root, bytenr, blocksize);
BUG_ON(ret); BUG_ON(ret);
...@@ -1811,12 +1801,6 @@ static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans, ...@@ -1811,12 +1801,6 @@ static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
btrfs_wait_tree_block_writeback(next); btrfs_wait_tree_block_writeback(next);
btrfs_tree_unlock(next); btrfs_tree_unlock(next);
if (*level == 0) {
ret = btrfs_drop_leaf_ref(trans, root,
next);
BUG_ON(ret);
}
WARN_ON(root_owner != BTRFS_TREE_LOG_OBJECTID); WARN_ON(root_owner != BTRFS_TREE_LOG_OBJECTID);
ret = btrfs_free_reserved_extent(root, ret = btrfs_free_reserved_extent(root,
path->nodes[*level]->start, path->nodes[*level]->start,
...@@ -1884,11 +1868,6 @@ static int walk_log_tree(struct btrfs_trans_handle *trans, ...@@ -1884,11 +1868,6 @@ static int walk_log_tree(struct btrfs_trans_handle *trans,
btrfs_wait_tree_block_writeback(next); btrfs_wait_tree_block_writeback(next);
btrfs_tree_unlock(next); btrfs_tree_unlock(next);
if (orig_level == 0) {
ret = btrfs_drop_leaf_ref(trans, log,
next);
BUG_ON(ret);
}
WARN_ON(log->root_key.objectid != WARN_ON(log->root_key.objectid !=
BTRFS_TREE_LOG_OBJECTID); BTRFS_TREE_LOG_OBJECTID);
ret = btrfs_free_reserved_extent(log, next->start, ret = btrfs_free_reserved_extent(log, next->start,
...@@ -2027,9 +2006,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans, ...@@ -2027,9 +2006,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
ret = btrfs_write_and_wait_marked_extents(log, &log->dirty_log_pages); ret = btrfs_write_and_wait_marked_extents(log, &log->dirty_log_pages);
BUG_ON(ret); BUG_ON(ret);
btrfs_set_root_bytenr(&log->root_item, log->node->start); btrfs_set_root_node(&log->root_item, log->node);
btrfs_set_root_generation(&log->root_item, trans->transid);
btrfs_set_root_level(&log->root_item, btrfs_header_level(log->node));
root->log_batch = 0; root->log_batch = 0;
root->log_transid++; root->log_transid++;
...@@ -2581,7 +2558,7 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, ...@@ -2581,7 +2558,7 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,
ins_keys, ins_sizes, nr); ins_keys, ins_sizes, nr);
BUG_ON(ret); BUG_ON(ret);
for (i = 0; i < nr; i++) { for (i = 0; i < nr; i++, dst_path->slots[0]++) {
dst_offset = btrfs_item_ptr_offset(dst_path->nodes[0], dst_offset = btrfs_item_ptr_offset(dst_path->nodes[0],
dst_path->slots[0]); dst_path->slots[0]);
...@@ -2617,36 +2594,31 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, ...@@ -2617,36 +2594,31 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,
found_type = btrfs_file_extent_type(src, extent); found_type = btrfs_file_extent_type(src, extent);
if (found_type == BTRFS_FILE_EXTENT_REG || if (found_type == BTRFS_FILE_EXTENT_REG ||
found_type == BTRFS_FILE_EXTENT_PREALLOC) { found_type == BTRFS_FILE_EXTENT_PREALLOC) {
u64 ds = btrfs_file_extent_disk_bytenr(src, u64 ds, dl, cs, cl;
extent); ds = btrfs_file_extent_disk_bytenr(src,
u64 dl = btrfs_file_extent_disk_num_bytes(src, extent);
extent); /* ds == 0 is a hole */
u64 cs = btrfs_file_extent_offset(src, extent); if (ds == 0)
u64 cl = btrfs_file_extent_num_bytes(src, continue;
extent);;
dl = btrfs_file_extent_disk_num_bytes(src,
extent);
cs = btrfs_file_extent_offset(src, extent);
cl = btrfs_file_extent_num_bytes(src,
extent);;
if (btrfs_file_extent_compression(src, if (btrfs_file_extent_compression(src,
extent)) { extent)) {
cs = 0; cs = 0;
cl = dl; cl = dl;
} }
/* ds == 0 is a hole */
if (ds != 0) { ret = btrfs_lookup_csums_range(
ret = btrfs_inc_extent_ref(trans, log, log->fs_info->csum_root,
ds, dl, ds + cs, ds + cs + cl - 1,
dst_path->nodes[0]->start, &ordered_sums);
BTRFS_TREE_LOG_OBJECTID, BUG_ON(ret);
trans->transid,
ins_keys[i].objectid);
BUG_ON(ret);
ret = btrfs_lookup_csums_range(
log->fs_info->csum_root,
ds + cs, ds + cs + cl - 1,
&ordered_sums);
BUG_ON(ret);
}
} }
} }
dst_path->slots[0]++;
} }
btrfs_mark_buffer_dirty(dst_path->nodes[0]); btrfs_mark_buffer_dirty(dst_path->nodes[0]);
...@@ -3029,9 +3001,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree) ...@@ -3029,9 +3001,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
BUG_ON(!wc.replay_dest); BUG_ON(!wc.replay_dest);
wc.replay_dest->log_root = log; wc.replay_dest->log_root = log;
mutex_lock(&fs_info->trans_mutex); btrfs_record_root_in_trans(trans, wc.replay_dest);
btrfs_record_root_in_trans(wc.replay_dest);
mutex_unlock(&fs_info->trans_mutex);
ret = walk_log_tree(trans, log, &wc); ret = walk_log_tree(trans, log, &wc);
BUG_ON(ret); BUG_ON(ret);
...@@ -3049,6 +3019,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree) ...@@ -3049,6 +3019,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
key.offset = found_key.offset - 1; key.offset = found_key.offset - 1;
wc.replay_dest->log_root = NULL; wc.replay_dest->log_root = NULL;
free_extent_buffer(log->node); free_extent_buffer(log->node);
free_extent_buffer(log->commit_root);
kfree(log); kfree(log);
if (found_key.offset == 0) if (found_key.offset == 0)
......
...@@ -161,8 +161,10 @@ static noinline int run_scheduled_bios(struct btrfs_device *device) ...@@ -161,8 +161,10 @@ static noinline int run_scheduled_bios(struct btrfs_device *device)
int again = 0; int again = 0;
unsigned long num_run; unsigned long num_run;
unsigned long num_sync_run; unsigned long num_sync_run;
unsigned long batch_run = 0;
unsigned long limit; unsigned long limit;
unsigned long last_waited = 0; unsigned long last_waited = 0;
int force_reg = 0;
bdi = blk_get_backing_dev_info(device->bdev); bdi = blk_get_backing_dev_info(device->bdev);
fs_info = device->dev_root->fs_info; fs_info = device->dev_root->fs_info;
...@@ -176,19 +178,22 @@ static noinline int run_scheduled_bios(struct btrfs_device *device) ...@@ -176,19 +178,22 @@ static noinline int run_scheduled_bios(struct btrfs_device *device)
loop: loop:
spin_lock(&device->io_lock); spin_lock(&device->io_lock);
num_run = 0;
loop_lock: loop_lock:
num_run = 0;
/* take all the bios off the list at once and process them /* take all the bios off the list at once and process them
* later on (without the lock held). But, remember the * later on (without the lock held). But, remember the
* tail and other pointers so the bios can be properly reinserted * tail and other pointers so the bios can be properly reinserted
* into the list if we hit congestion * into the list if we hit congestion
*/ */
if (device->pending_sync_bios.head) if (!force_reg && device->pending_sync_bios.head) {
pending_bios = &device->pending_sync_bios; pending_bios = &device->pending_sync_bios;
else force_reg = 1;
} else {
pending_bios = &device->pending_bios; pending_bios = &device->pending_bios;
force_reg = 0;
}
pending = pending_bios->head; pending = pending_bios->head;
tail = pending_bios->tail; tail = pending_bios->tail;
...@@ -228,10 +233,14 @@ static noinline int run_scheduled_bios(struct btrfs_device *device) ...@@ -228,10 +233,14 @@ static noinline int run_scheduled_bios(struct btrfs_device *device)
while (pending) { while (pending) {
rmb(); rmb();
if (pending_bios != &device->pending_sync_bios && /* we want to work on both lists, but do more bios on the
device->pending_sync_bios.head && * sync list than the regular list
num_run > 16) { */
cond_resched(); if ((num_run > 32 &&
pending_bios != &device->pending_sync_bios &&
device->pending_sync_bios.head) ||
(num_run > 64 && pending_bios == &device->pending_sync_bios &&
device->pending_bios.head)) {
spin_lock(&device->io_lock); spin_lock(&device->io_lock);
requeue_list(pending_bios, pending, tail); requeue_list(pending_bios, pending, tail);
goto loop_lock; goto loop_lock;
...@@ -249,6 +258,8 @@ static noinline int run_scheduled_bios(struct btrfs_device *device) ...@@ -249,6 +258,8 @@ static noinline int run_scheduled_bios(struct btrfs_device *device)
BUG_ON(atomic_read(&cur->bi_cnt) == 0); BUG_ON(atomic_read(&cur->bi_cnt) == 0);
submit_bio(cur->bi_rw, cur); submit_bio(cur->bi_rw, cur);
num_run++; num_run++;
batch_run++;
if (bio_sync(cur)) if (bio_sync(cur))
num_sync_run++; num_sync_run++;
...@@ -265,7 +276,7 @@ static noinline int run_scheduled_bios(struct btrfs_device *device) ...@@ -265,7 +276,7 @@ static noinline int run_scheduled_bios(struct btrfs_device *device)
* is now congested. Back off and let other work structs * is now congested. Back off and let other work structs
* run instead * run instead
*/ */
if (pending && bdi_write_congested(bdi) && num_run > 16 && if (pending && bdi_write_congested(bdi) && batch_run > 32 &&
fs_info->fs_devices->open_devices > 1) { fs_info->fs_devices->open_devices > 1) {
struct io_context *ioc; struct io_context *ioc;
...@@ -366,6 +377,7 @@ static noinline int device_list_add(const char *path, ...@@ -366,6 +377,7 @@ static noinline int device_list_add(const char *path,
memcpy(fs_devices->fsid, disk_super->fsid, BTRFS_FSID_SIZE); memcpy(fs_devices->fsid, disk_super->fsid, BTRFS_FSID_SIZE);
fs_devices->latest_devid = devid; fs_devices->latest_devid = devid;
fs_devices->latest_trans = found_transid; fs_devices->latest_trans = found_transid;
mutex_init(&fs_devices->device_list_mutex);
device = NULL; device = NULL;
} else { } else {
device = __find_device(&fs_devices->devices, devid, device = __find_device(&fs_devices->devices, devid,
...@@ -392,7 +404,11 @@ static noinline int device_list_add(const char *path, ...@@ -392,7 +404,11 @@ static noinline int device_list_add(const char *path,
return -ENOMEM; return -ENOMEM;
} }
INIT_LIST_HEAD(&device->dev_alloc_list); INIT_LIST_HEAD(&device->dev_alloc_list);
mutex_lock(&fs_devices->device_list_mutex);
list_add(&device->dev_list, &fs_devices->devices); list_add(&device->dev_list, &fs_devices->devices);
mutex_unlock(&fs_devices->device_list_mutex);
device->fs_devices = fs_devices; device->fs_devices = fs_devices;
fs_devices->num_devices++; fs_devices->num_devices++;
} }
...@@ -418,10 +434,12 @@ static struct btrfs_fs_devices *clone_fs_devices(struct btrfs_fs_devices *orig) ...@@ -418,10 +434,12 @@ static struct btrfs_fs_devices *clone_fs_devices(struct btrfs_fs_devices *orig)
INIT_LIST_HEAD(&fs_devices->devices); INIT_LIST_HEAD(&fs_devices->devices);
INIT_LIST_HEAD(&fs_devices->alloc_list); INIT_LIST_HEAD(&fs_devices->alloc_list);
INIT_LIST_HEAD(&fs_devices->list); INIT_LIST_HEAD(&fs_devices->list);
mutex_init(&fs_devices->device_list_mutex);
fs_devices->latest_devid = orig->latest_devid; fs_devices->latest_devid = orig->latest_devid;
fs_devices->latest_trans = orig->latest_trans; fs_devices->latest_trans = orig->latest_trans;
memcpy(fs_devices->fsid, orig->fsid, sizeof(fs_devices->fsid)); memcpy(fs_devices->fsid, orig->fsid, sizeof(fs_devices->fsid));
mutex_lock(&orig->device_list_mutex);
list_for_each_entry(orig_dev, &orig->devices, dev_list) { list_for_each_entry(orig_dev, &orig->devices, dev_list) {
device = kzalloc(sizeof(*device), GFP_NOFS); device = kzalloc(sizeof(*device), GFP_NOFS);
if (!device) if (!device)
...@@ -443,8 +461,10 @@ static struct btrfs_fs_devices *clone_fs_devices(struct btrfs_fs_devices *orig) ...@@ -443,8 +461,10 @@ static struct btrfs_fs_devices *clone_fs_devices(struct btrfs_fs_devices *orig)
device->fs_devices = fs_devices; device->fs_devices = fs_devices;
fs_devices->num_devices++; fs_devices->num_devices++;
} }
mutex_unlock(&orig->device_list_mutex);
return fs_devices; return fs_devices;
error: error:
mutex_unlock(&orig->device_list_mutex);
free_fs_devices(fs_devices); free_fs_devices(fs_devices);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
...@@ -455,6 +475,7 @@ int btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices) ...@@ -455,6 +475,7 @@ int btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices)
mutex_lock(&uuid_mutex); mutex_lock(&uuid_mutex);
again: again:
mutex_lock(&fs_devices->device_list_mutex);
list_for_each_entry_safe(device, next, &fs_devices->devices, dev_list) { list_for_each_entry_safe(device, next, &fs_devices->devices, dev_list) {
if (device->in_fs_metadata) if (device->in_fs_metadata)
continue; continue;
...@@ -474,6 +495,7 @@ int btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices) ...@@ -474,6 +495,7 @@ int btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices)
kfree(device->name); kfree(device->name);
kfree(device); kfree(device);
} }
mutex_unlock(&fs_devices->device_list_mutex);
if (fs_devices->seed) { if (fs_devices->seed) {
fs_devices = fs_devices->seed; fs_devices = fs_devices->seed;
...@@ -594,6 +616,9 @@ static int __btrfs_open_devices(struct btrfs_fs_devices *fs_devices, ...@@ -594,6 +616,9 @@ static int __btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
device->in_fs_metadata = 0; device->in_fs_metadata = 0;
device->mode = flags; device->mode = flags;
if (!blk_queue_nonrot(bdev_get_queue(bdev)))
fs_devices->rotating = 1;
fs_devices->open_devices++; fs_devices->open_devices++;
if (device->writeable) { if (device->writeable) {
fs_devices->rw_devices++; fs_devices->rw_devices++;
...@@ -1121,12 +1146,14 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) ...@@ -1121,12 +1146,14 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
device = NULL; device = NULL;
devices = &root->fs_info->fs_devices->devices; devices = &root->fs_info->fs_devices->devices;
mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
list_for_each_entry(tmp, devices, dev_list) { list_for_each_entry(tmp, devices, dev_list) {
if (tmp->in_fs_metadata && !tmp->bdev) { if (tmp->in_fs_metadata && !tmp->bdev) {
device = tmp; device = tmp;
break; break;
} }
} }
mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
bdev = NULL; bdev = NULL;
bh = NULL; bh = NULL;
disk_super = NULL; disk_super = NULL;
...@@ -1181,7 +1208,16 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) ...@@ -1181,7 +1208,16 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
goto error_brelse; goto error_brelse;
device->in_fs_metadata = 0; device->in_fs_metadata = 0;
/*
* the device list mutex makes sure that we don't change
* the device list while someone else is writing out all
* the device supers.
*/
mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
list_del_init(&device->dev_list); list_del_init(&device->dev_list);
mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
device->fs_devices->num_devices--; device->fs_devices->num_devices--;
next_device = list_entry(root->fs_info->fs_devices->devices.next, next_device = list_entry(root->fs_info->fs_devices->devices.next,
...@@ -1275,6 +1311,7 @@ static int btrfs_prepare_sprout(struct btrfs_trans_handle *trans, ...@@ -1275,6 +1311,7 @@ static int btrfs_prepare_sprout(struct btrfs_trans_handle *trans,
seed_devices->opened = 1; seed_devices->opened = 1;
INIT_LIST_HEAD(&seed_devices->devices); INIT_LIST_HEAD(&seed_devices->devices);
INIT_LIST_HEAD(&seed_devices->alloc_list); INIT_LIST_HEAD(&seed_devices->alloc_list);
mutex_init(&seed_devices->device_list_mutex);
list_splice_init(&fs_devices->devices, &seed_devices->devices); list_splice_init(&fs_devices->devices, &seed_devices->devices);
list_splice_init(&fs_devices->alloc_list, &seed_devices->alloc_list); list_splice_init(&fs_devices->alloc_list, &seed_devices->alloc_list);
list_for_each_entry(device, &seed_devices->devices, dev_list) { list_for_each_entry(device, &seed_devices->devices, dev_list) {
...@@ -1400,6 +1437,10 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) ...@@ -1400,6 +1437,10 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
mutex_lock(&root->fs_info->volume_mutex); mutex_lock(&root->fs_info->volume_mutex);
devices = &root->fs_info->fs_devices->devices; devices = &root->fs_info->fs_devices->devices;
/*
* we have the volume lock, so we don't need the extra
* device list mutex while reading the list here.
*/
list_for_each_entry(device, devices, dev_list) { list_for_each_entry(device, devices, dev_list) {
if (device->bdev == bdev) { if (device->bdev == bdev) {
ret = -EEXIST; ret = -EEXIST;
...@@ -1454,6 +1495,12 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) ...@@ -1454,6 +1495,12 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
} }
device->fs_devices = root->fs_info->fs_devices; device->fs_devices = root->fs_info->fs_devices;
/*
* we don't want write_supers to jump in here with our device
* half setup
*/
mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
list_add(&device->dev_list, &root->fs_info->fs_devices->devices); list_add(&device->dev_list, &root->fs_info->fs_devices->devices);
list_add(&device->dev_alloc_list, list_add(&device->dev_alloc_list,
&root->fs_info->fs_devices->alloc_list); &root->fs_info->fs_devices->alloc_list);
...@@ -1462,6 +1509,9 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) ...@@ -1462,6 +1509,9 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
root->fs_info->fs_devices->rw_devices++; root->fs_info->fs_devices->rw_devices++;
root->fs_info->fs_devices->total_rw_bytes += device->total_bytes; root->fs_info->fs_devices->total_rw_bytes += device->total_bytes;
if (!blk_queue_nonrot(bdev_get_queue(bdev)))
root->fs_info->fs_devices->rotating = 1;
total_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy); total_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy);
btrfs_set_super_total_bytes(&root->fs_info->super_copy, btrfs_set_super_total_bytes(&root->fs_info->super_copy,
total_bytes + device->total_bytes); total_bytes + device->total_bytes);
...@@ -1469,6 +1519,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) ...@@ -1469,6 +1519,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
total_bytes = btrfs_super_num_devices(&root->fs_info->super_copy); total_bytes = btrfs_super_num_devices(&root->fs_info->super_copy);
btrfs_set_super_num_devices(&root->fs_info->super_copy, btrfs_set_super_num_devices(&root->fs_info->super_copy,
total_bytes + 1); total_bytes + 1);
mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
if (seeding_dev) { if (seeding_dev) {
ret = init_first_rw_device(trans, root, device); ret = init_first_rw_device(trans, root, device);
...@@ -1671,8 +1722,6 @@ static int btrfs_relocate_chunk(struct btrfs_root *root, ...@@ -1671,8 +1722,6 @@ static int btrfs_relocate_chunk(struct btrfs_root *root,
int ret; int ret;
int i; int i;
printk(KERN_INFO "btrfs relocating chunk %llu\n",
(unsigned long long)chunk_offset);
root = root->fs_info->chunk_root; root = root->fs_info->chunk_root;
extent_root = root->fs_info->extent_root; extent_root = root->fs_info->extent_root;
em_tree = &root->fs_info->mapping_tree.map_tree; em_tree = &root->fs_info->mapping_tree.map_tree;
......
...@@ -96,7 +96,12 @@ struct btrfs_fs_devices { ...@@ -96,7 +96,12 @@ struct btrfs_fs_devices {
u64 rw_devices; u64 rw_devices;
u64 total_rw_bytes; u64 total_rw_bytes;
struct block_device *latest_bdev; struct block_device *latest_bdev;
/* all of the devices in the FS */
/* all of the devices in the FS, protected by a mutex
* so we can safely walk it to write out the supers without
* worrying about add/remove by the multi-device code
*/
struct mutex device_list_mutex;
struct list_head devices; struct list_head devices;
/* devices not currently being allocated */ /* devices not currently being allocated */
...@@ -107,6 +112,11 @@ struct btrfs_fs_devices { ...@@ -107,6 +112,11 @@ struct btrfs_fs_devices {
int seeding; int seeding;
int opened; int opened;
/* set when we find or add a device that doesn't have the
* nonrot flag set
*/
int rotating;
}; };
struct btrfs_bio_stripe { struct btrfs_bio_stripe {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册