|
@@ -4462,430 +4462,6 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
|
|
|
return buf;
|
|
|
}
|
|
|
|
|
|
-#if 0
|
|
|
-int btrfs_drop_leaf_ref(struct btrfs_trans_handle *trans,
|
|
|
- struct btrfs_root *root, struct extent_buffer *leaf)
|
|
|
-{
|
|
|
- u64 disk_bytenr;
|
|
|
- u64 num_bytes;
|
|
|
- struct btrfs_key key;
|
|
|
- struct btrfs_file_extent_item *fi;
|
|
|
- u32 nritems;
|
|
|
- int i;
|
|
|
- int ret;
|
|
|
-
|
|
|
- BUG_ON(!btrfs_is_leaf(leaf));
|
|
|
- nritems = btrfs_header_nritems(leaf);
|
|
|
-
|
|
|
- for (i = 0; i < nritems; i++) {
|
|
|
- cond_resched();
|
|
|
- btrfs_item_key_to_cpu(leaf, &key, i);
|
|
|
-
|
|
|
- /* only extents have references, skip everything else */
|
|
|
- if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY)
|
|
|
- continue;
|
|
|
-
|
|
|
- fi = btrfs_item_ptr(leaf, i, struct btrfs_file_extent_item);
|
|
|
-
|
|
|
- /* inline extents live in the btree, they don't have refs */
|
|
|
- if (btrfs_file_extent_type(leaf, fi) ==
|
|
|
- BTRFS_FILE_EXTENT_INLINE)
|
|
|
- continue;
|
|
|
-
|
|
|
- disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
|
|
|
-
|
|
|
- /* holes don't have refs */
|
|
|
- if (disk_bytenr == 0)
|
|
|
- continue;
|
|
|
-
|
|
|
- num_bytes = btrfs_file_extent_disk_num_bytes(leaf, fi);
|
|
|
- ret = btrfs_free_extent(trans, root, disk_bytenr, num_bytes,
|
|
|
- leaf->start, 0, key.objectid, 0);
|
|
|
- BUG_ON(ret);
|
|
|
- }
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static noinline int cache_drop_leaf_ref(struct btrfs_trans_handle *trans,
|
|
|
- struct btrfs_root *root,
|
|
|
- struct btrfs_leaf_ref *ref)
|
|
|
-{
|
|
|
- int i;
|
|
|
- int ret;
|
|
|
- struct btrfs_extent_info *info;
|
|
|
- struct refsort *sorted;
|
|
|
-
|
|
|
- if (ref->nritems == 0)
|
|
|
- return 0;
|
|
|
-
|
|
|
- sorted = kmalloc(sizeof(*sorted) * ref->nritems, GFP_NOFS);
|
|
|
- for (i = 0; i < ref->nritems; i++) {
|
|
|
- sorted[i].bytenr = ref->extents[i].bytenr;
|
|
|
- sorted[i].slot = i;
|
|
|
- }
|
|
|
- sort(sorted, ref->nritems, sizeof(struct refsort), refsort_cmp, NULL);
|
|
|
-
|
|
|
- /*
|
|
|
- * the items in the ref were sorted when the ref was inserted
|
|
|
- * into the ref cache, so this is already in order
|
|
|
- */
|
|
|
- for (i = 0; i < ref->nritems; i++) {
|
|
|
- info = ref->extents + sorted[i].slot;
|
|
|
- ret = btrfs_free_extent(trans, root, info->bytenr,
|
|
|
- info->num_bytes, ref->bytenr,
|
|
|
- ref->owner, ref->generation,
|
|
|
- info->objectid, 0);
|
|
|
-
|
|
|
- atomic_inc(&root->fs_info->throttle_gen);
|
|
|
- wake_up(&root->fs_info->transaction_throttle);
|
|
|
- cond_resched();
|
|
|
-
|
|
|
- BUG_ON(ret);
|
|
|
- info++;
|
|
|
- }
|
|
|
-
|
|
|
- kfree(sorted);
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-static int drop_snap_lookup_refcount(struct btrfs_trans_handle *trans,
|
|
|
- struct btrfs_root *root, u64 start,
|
|
|
- u64 len, u32 *refs)
|
|
|
-{
|
|
|
- int ret;
|
|
|
-
|
|
|
- ret = btrfs_lookup_extent_refs(trans, root, start, len, refs);
|
|
|
- BUG_ON(ret);
|
|
|
-
|
|
|
-#if 0 /* some debugging code in case we see problems here */
|
|
|
- /* if the refs count is one, it won't get increased again. But
|
|
|
- * if the ref count is > 1, someone may be decreasing it at
|
|
|
- * the same time we are.
|
|
|
- */
|
|
|
- if (*refs != 1) {
|
|
|
- struct extent_buffer *eb = NULL;
|
|
|
- eb = btrfs_find_create_tree_block(root, start, len);
|
|
|
- if (eb)
|
|
|
- btrfs_tree_lock(eb);
|
|
|
-
|
|
|
- mutex_lock(&root->fs_info->alloc_mutex);
|
|
|
- ret = lookup_extent_ref(NULL, root, start, len, refs);
|
|
|
- BUG_ON(ret);
|
|
|
- mutex_unlock(&root->fs_info->alloc_mutex);
|
|
|
-
|
|
|
- if (eb) {
|
|
|
- btrfs_tree_unlock(eb);
|
|
|
- free_extent_buffer(eb);
|
|
|
- }
|
|
|
- if (*refs == 1) {
|
|
|
- printk(KERN_ERR "btrfs block %llu went down to one "
|
|
|
- "during drop_snap\n", (unsigned long long)start);
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-#endif
|
|
|
-
|
|
|
- cond_resched();
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/*
|
|
|
- * this is used while deleting old snapshots, and it drops the refs
|
|
|
- * on a whole subtree starting from a level 1 node.
|
|
|
- *
|
|
|
- * The idea is to sort all the leaf pointers, and then drop the
|
|
|
- * ref on all the leaves in order. Most of the time the leaves
|
|
|
- * will have ref cache entries, so no leaf IOs will be required to
|
|
|
- * find the extents they have references on.
|
|
|
- *
|
|
|
- * For each leaf, any references it has are also dropped in order
|
|
|
- *
|
|
|
- * This ends up dropping the references in something close to optimal
|
|
|
- * order for reading and modifying the extent allocation tree.
|
|
|
- */
|
|
|
-static noinline int drop_level_one_refs(struct btrfs_trans_handle *trans,
|
|
|
- struct btrfs_root *root,
|
|
|
- struct btrfs_path *path)
|
|
|
-{
|
|
|
- u64 bytenr;
|
|
|
- u64 root_owner;
|
|
|
- u64 root_gen;
|
|
|
- struct extent_buffer *eb = path->nodes[1];
|
|
|
- struct extent_buffer *leaf;
|
|
|
- struct btrfs_leaf_ref *ref;
|
|
|
- struct refsort *sorted = NULL;
|
|
|
- int nritems = btrfs_header_nritems(eb);
|
|
|
- int ret;
|
|
|
- int i;
|
|
|
- int refi = 0;
|
|
|
- int slot = path->slots[1];
|
|
|
- u32 blocksize = btrfs_level_size(root, 0);
|
|
|
- u32 refs;
|
|
|
-
|
|
|
- if (nritems == 0)
|
|
|
- goto out;
|
|
|
-
|
|
|
- root_owner = btrfs_header_owner(eb);
|
|
|
- root_gen = btrfs_header_generation(eb);
|
|
|
- sorted = kmalloc(sizeof(*sorted) * nritems, GFP_NOFS);
|
|
|
-
|
|
|
- /*
|
|
|
- * step one, sort all the leaf pointers so we don't scribble
|
|
|
- * randomly into the extent allocation tree
|
|
|
- */
|
|
|
- for (i = slot; i < nritems; i++) {
|
|
|
- sorted[refi].bytenr = btrfs_node_blockptr(eb, i);
|
|
|
- sorted[refi].slot = i;
|
|
|
- refi++;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * nritems won't be zero, but if we're picking up drop_snapshot
|
|
|
- * after a crash, slot might be > 0, so double check things
|
|
|
- * just in case.
|
|
|
- */
|
|
|
- if (refi == 0)
|
|
|
- goto out;
|
|
|
-
|
|
|
- sort(sorted, refi, sizeof(struct refsort), refsort_cmp, NULL);
|
|
|
-
|
|
|
- /*
|
|
|
- * the first loop frees everything the leaves point to
|
|
|
- */
|
|
|
- for (i = 0; i < refi; i++) {
|
|
|
- u64 ptr_gen;
|
|
|
-
|
|
|
- bytenr = sorted[i].bytenr;
|
|
|
-
|
|
|
- /*
|
|
|
- * check the reference count on this leaf. If it is > 1
|
|
|
- * we just decrement it below and don't update any
|
|
|
- * of the refs the leaf points to.
|
|
|
- */
|
|
|
- ret = drop_snap_lookup_refcount(trans, root, bytenr,
|
|
|
- blocksize, &refs);
|
|
|
- BUG_ON(ret);
|
|
|
- if (refs != 1)
|
|
|
- continue;
|
|
|
-
|
|
|
- ptr_gen = btrfs_node_ptr_generation(eb, sorted[i].slot);
|
|
|
-
|
|
|
- /*
|
|
|
- * the leaf only had one reference, which means the
|
|
|
- * only thing pointing to this leaf is the snapshot
|
|
|
- * we're deleting. It isn't possible for the reference
|
|
|
- * count to increase again later
|
|
|
- *
|
|
|
- * The reference cache is checked for the leaf,
|
|
|
- * and if found we'll be able to drop any refs held by
|
|
|
- * the leaf without needing to read it in.
|
|
|
- */
|
|
|
- ref = btrfs_lookup_leaf_ref(root, bytenr);
|
|
|
- if (ref && ref->generation != ptr_gen) {
|
|
|
- btrfs_free_leaf_ref(root, ref);
|
|
|
- ref = NULL;
|
|
|
- }
|
|
|
- if (ref) {
|
|
|
- ret = cache_drop_leaf_ref(trans, root, ref);
|
|
|
- BUG_ON(ret);
|
|
|
- btrfs_remove_leaf_ref(root, ref);
|
|
|
- btrfs_free_leaf_ref(root, ref);
|
|
|
- } else {
|
|
|
- /*
|
|
|
- * the leaf wasn't in the reference cache, so
|
|
|
- * we have to read it.
|
|
|
- */
|
|
|
- leaf = read_tree_block(root, bytenr, blocksize,
|
|
|
- ptr_gen);
|
|
|
- ret = btrfs_drop_leaf_ref(trans, root, leaf);
|
|
|
- BUG_ON(ret);
|
|
|
- free_extent_buffer(leaf);
|
|
|
- }
|
|
|
- atomic_inc(&root->fs_info->throttle_gen);
|
|
|
- wake_up(&root->fs_info->transaction_throttle);
|
|
|
- cond_resched();
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * run through the loop again to free the refs on the leaves.
|
|
|
- * This is faster than doing it in the loop above because
|
|
|
- * the leaves are likely to be clustered together. We end up
|
|
|
- * working in nice chunks on the extent allocation tree.
|
|
|
- */
|
|
|
- for (i = 0; i < refi; i++) {
|
|
|
- bytenr = sorted[i].bytenr;
|
|
|
- ret = btrfs_free_extent(trans, root, bytenr,
|
|
|
- blocksize, eb->start,
|
|
|
- root_owner, root_gen, 0, 1);
|
|
|
- BUG_ON(ret);
|
|
|
-
|
|
|
- atomic_inc(&root->fs_info->throttle_gen);
|
|
|
- wake_up(&root->fs_info->transaction_throttle);
|
|
|
- cond_resched();
|
|
|
- }
|
|
|
-out:
|
|
|
- kfree(sorted);
|
|
|
-
|
|
|
- /*
|
|
|
- * update the path to show we've processed the entire level 1
|
|
|
- * node. This will get saved into the root's drop_snapshot_progress
|
|
|
- * field so these drops are not repeated again if this transaction
|
|
|
- * commits.
|
|
|
- */
|
|
|
- path->slots[1] = nritems;
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * helper function for drop_snapshot, this walks down the tree dropping ref
|
|
|
- * counts as it goes.
|
|
|
- */
|
|
|
-static noinline int walk_down_tree(struct btrfs_trans_handle *trans,
|
|
|
- struct btrfs_root *root,
|
|
|
- struct btrfs_path *path, int *level)
|
|
|
-{
|
|
|
- u64 root_owner;
|
|
|
- u64 root_gen;
|
|
|
- u64 bytenr;
|
|
|
- u64 ptr_gen;
|
|
|
- struct extent_buffer *next;
|
|
|
- struct extent_buffer *cur;
|
|
|
- struct extent_buffer *parent;
|
|
|
- u32 blocksize;
|
|
|
- int ret;
|
|
|
- u32 refs;
|
|
|
-
|
|
|
- WARN_ON(*level < 0);
|
|
|
- WARN_ON(*level >= BTRFS_MAX_LEVEL);
|
|
|
- ret = drop_snap_lookup_refcount(trans, root, path->nodes[*level]->start,
|
|
|
- path->nodes[*level]->len, &refs);
|
|
|
- BUG_ON(ret);
|
|
|
- if (refs > 1)
|
|
|
- goto out;
|
|
|
-
|
|
|
- /*
|
|
|
- * walk down to the last node level and free all the leaves
|
|
|
- */
|
|
|
- while (*level >= 0) {
|
|
|
- WARN_ON(*level < 0);
|
|
|
- WARN_ON(*level >= BTRFS_MAX_LEVEL);
|
|
|
- cur = path->nodes[*level];
|
|
|
-
|
|
|
- if (btrfs_header_level(cur) != *level)
|
|
|
- WARN_ON(1);
|
|
|
-
|
|
|
- if (path->slots[*level] >=
|
|
|
- btrfs_header_nritems(cur))
|
|
|
- break;
|
|
|
-
|
|
|
- /* the new code goes down to level 1 and does all the
|
|
|
- * leaves pointed to that node in bulk. So, this check
|
|
|
- * for level 0 will always be false.
|
|
|
- *
|
|
|
- * But, the disk format allows the drop_snapshot_progress
|
|
|
- * field in the root to leave things in a state where
|
|
|
- * a leaf will need cleaning up here. If someone crashes
|
|
|
- * with the old code and then boots with the new code,
|
|
|
- * we might find a leaf here.
|
|
|
- */
|
|
|
- if (*level == 0) {
|
|
|
- ret = btrfs_drop_leaf_ref(trans, root, cur);
|
|
|
- BUG_ON(ret);
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * once we get to level one, process the whole node
|
|
|
- * at once, including everything below it.
|
|
|
- */
|
|
|
- if (*level == 1) {
|
|
|
- ret = drop_level_one_refs(trans, root, path);
|
|
|
- BUG_ON(ret);
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- bytenr = btrfs_node_blockptr(cur, path->slots[*level]);
|
|
|
- ptr_gen = btrfs_node_ptr_generation(cur, path->slots[*level]);
|
|
|
- blocksize = btrfs_level_size(root, *level - 1);
|
|
|
-
|
|
|
- ret = drop_snap_lookup_refcount(trans, root, bytenr,
|
|
|
- blocksize, &refs);
|
|
|
- BUG_ON(ret);
|
|
|
-
|
|
|
- /*
|
|
|
- * if there is more than one reference, we don't need
|
|
|
- * to read that node to drop any references it has. We
|
|
|
- * just drop the ref we hold on that node and move on to the
|
|
|
- * next slot in this level.
|
|
|
- */
|
|
|
- if (refs != 1) {
|
|
|
- parent = path->nodes[*level];
|
|
|
- root_owner = btrfs_header_owner(parent);
|
|
|
- root_gen = btrfs_header_generation(parent);
|
|
|
- path->slots[*level]++;
|
|
|
-
|
|
|
- ret = btrfs_free_extent(trans, root, bytenr,
|
|
|
- blocksize, parent->start,
|
|
|
- root_owner, root_gen,
|
|
|
- *level - 1, 1);
|
|
|
- BUG_ON(ret);
|
|
|
-
|
|
|
- atomic_inc(&root->fs_info->throttle_gen);
|
|
|
- wake_up(&root->fs_info->transaction_throttle);
|
|
|
- cond_resched();
|
|
|
-
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * we need to keep freeing things in the next level down.
|
|
|
- * read the block and loop around to process it
|
|
|
- */
|
|
|
- next = read_tree_block(root, bytenr, blocksize, ptr_gen);
|
|
|
- WARN_ON(*level <= 0);
|
|
|
- if (path->nodes[*level-1])
|
|
|
- free_extent_buffer(path->nodes[*level-1]);
|
|
|
- path->nodes[*level-1] = next;
|
|
|
- *level = btrfs_header_level(next);
|
|
|
- path->slots[*level] = 0;
|
|
|
- cond_resched();
|
|
|
- }
|
|
|
-out:
|
|
|
- WARN_ON(*level < 0);
|
|
|
- WARN_ON(*level >= BTRFS_MAX_LEVEL);
|
|
|
-
|
|
|
- if (path->nodes[*level] == root->node) {
|
|
|
- parent = path->nodes[*level];
|
|
|
- bytenr = path->nodes[*level]->start;
|
|
|
- } else {
|
|
|
- parent = path->nodes[*level + 1];
|
|
|
- bytenr = btrfs_node_blockptr(parent, path->slots[*level + 1]);
|
|
|
- }
|
|
|
-
|
|
|
- blocksize = btrfs_level_size(root, *level);
|
|
|
- root_owner = btrfs_header_owner(parent);
|
|
|
- root_gen = btrfs_header_generation(parent);
|
|
|
-
|
|
|
- /*
|
|
|
- * cleanup and free the reference on the last node
|
|
|
- * we processed
|
|
|
- */
|
|
|
- ret = btrfs_free_extent(trans, root, bytenr, blocksize,
|
|
|
- parent->start, root_owner, root_gen,
|
|
|
- *level, 1);
|
|
|
- free_extent_buffer(path->nodes[*level]);
|
|
|
- path->nodes[*level] = NULL;
|
|
|
-
|
|
|
- *level += 1;
|
|
|
- BUG_ON(ret);
|
|
|
-
|
|
|
- cond_resched();
|
|
|
- return 0;
|
|
|
-}
|
|
|
-#endif
|
|
|
-
|
|
|
struct walk_control {
|
|
|
u64 refs[BTRFS_MAX_LEVEL];
|
|
|
u64 flags[BTRFS_MAX_LEVEL];
|
|
@@ -7129,288 +6705,6 @@ int btrfs_prepare_block_group_relocation(struct btrfs_root *root,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-#if 0
|
|
|
-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;
|
|
|
-
|
|
|
- path->leave_spinning = 1;
|
|
|
- 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;
|
|
|
-}
|
|
|
-
|
|
|
-static noinline 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 root_key;
|
|
|
- u64 objectid = BTRFS_FIRST_FREE_OBJECTID;
|
|
|
- int err = 0;
|
|
|
-
|
|
|
- root_key.objectid = BTRFS_DATA_RELOC_TREE_OBJECTID;
|
|
|
- root_key.type = BTRFS_ROOT_ITEM_KEY;
|
|
|
- root_key.offset = (u64)-1;
|
|
|
- root = btrfs_read_fs_root_no_name(fs_info, &root_key);
|
|
|
- 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);
|
|
|
-
|
|
|
- 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);
|
|
|
- BUG_ON(is_bad_inode(inode));
|
|
|
- } else {
|
|
|
- BUG_ON(1);
|
|
|
- }
|
|
|
- BTRFS_I(inode)->index_cnt = group->key.objectid;
|
|
|
-
|
|
|
- err = btrfs_orphan_add(trans, inode);
|
|
|
-out:
|
|
|
- btrfs_end_transaction(trans, root);
|
|
|
- if (err) {
|
|
|
- if (inode)
|
|
|
- iput(inode);
|
|
|
- inode = ERR_PTR(err);
|
|
|
- }
|
|
|
- return inode;
|
|
|
-}
|
|
|
-
|
|
|
-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;
|
|
|
- struct list_head list;
|
|
|
- size_t offset;
|
|
|
- int ret;
|
|
|
- u64 disk_bytenr;
|
|
|
-
|
|
|
- INIT_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;
|
|
|
-}
|
|
|
-
|
|
|
-int btrfs_relocate_block_group(struct btrfs_root *root, u64 group_start)
|
|
|
-{
|
|
|
- struct btrfs_trans_handle *trans;
|
|
|
- struct btrfs_path *path;
|
|
|
- struct btrfs_fs_info *info = root->fs_info;
|
|
|
- struct extent_buffer *leaf;
|
|
|
- struct inode *reloc_inode;
|
|
|
- struct btrfs_block_group_cache *block_group;
|
|
|
- struct btrfs_key key;
|
|
|
- u64 skipped;
|
|
|
- u64 cur_byte;
|
|
|
- u64 total_found;
|
|
|
- u32 nritems;
|
|
|
- int ret;
|
|
|
- int progress;
|
|
|
- int pass = 0;
|
|
|
-
|
|
|
- root = root->fs_info->extent_root;
|
|
|
-
|
|
|
- block_group = btrfs_lookup_block_group(info, group_start);
|
|
|
- BUG_ON(!block_group);
|
|
|
-
|
|
|
- printk(KERN_INFO "btrfs relocating block group %llu flags %llu\n",
|
|
|
- (unsigned long long)block_group->key.objectid,
|
|
|
- (unsigned long long)block_group->flags);
|
|
|
-
|
|
|
- path = btrfs_alloc_path();
|
|
|
- BUG_ON(!path);
|
|
|
-
|
|
|
- reloc_inode = create_reloc_inode(info, block_group);
|
|
|
- BUG_ON(IS_ERR(reloc_inode));
|
|
|
-
|
|
|
- __alloc_chunk_for_shrink(root, block_group, 1);
|
|
|
- set_block_group_readonly(block_group);
|
|
|
-
|
|
|
- btrfs_start_delalloc_inodes(info->tree_root);
|
|
|
- btrfs_wait_ordered_extents(info->tree_root, 0);
|
|
|
-again:
|
|
|
- skipped = 0;
|
|
|
- total_found = 0;
|
|
|
- progress = 0;
|
|
|
- key.objectid = block_group->key.objectid;
|
|
|
- key.offset = 0;
|
|
|
- key.type = 0;
|
|
|
- cur_byte = key.objectid;
|
|
|
-
|
|
|
- trans = btrfs_start_transaction(info->tree_root, 1);
|
|
|
- btrfs_commit_transaction(trans, info->tree_root);
|
|
|
-
|
|
|
- mutex_lock(&root->fs_info->cleaner_mutex);
|
|
|
- btrfs_clean_old_snapshots(info->tree_root);
|
|
|
- btrfs_remove_leaf_refs(info->tree_root, (u64)-1, 1);
|
|
|
- mutex_unlock(&root->fs_info->cleaner_mutex);
|
|
|
-
|
|
|
- trans = btrfs_start_transaction(info->tree_root, 1);
|
|
|
- btrfs_commit_transaction(trans, info->tree_root);
|
|
|
-
|
|
|
- while (1) {
|
|
|
- ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
|
|
|
- if (ret < 0)
|
|
|
- goto out;
|
|
|
-next:
|
|
|
- leaf = path->nodes[0];
|
|
|
- nritems = btrfs_header_nritems(leaf);
|
|
|
- if (path->slots[0] >= nritems) {
|
|
|
- ret = btrfs_next_leaf(root, path);
|
|
|
- if (ret < 0)
|
|
|
- goto out;
|
|
|
- if (ret == 1) {
|
|
|
- ret = 0;
|
|
|
- break;
|
|
|
- }
|
|
|
- leaf = path->nodes[0];
|
|
|
- nritems = btrfs_header_nritems(leaf);
|
|
|
- }
|
|
|
-
|
|
|
- btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
|
|
|
-
|
|
|
- if (key.objectid >= block_group->key.objectid +
|
|
|
- block_group->key.offset)
|
|
|
- break;
|
|
|
-
|
|
|
- if (progress && need_resched()) {
|
|
|
- btrfs_release_path(root, path);
|
|
|
- cond_resched();
|
|
|
- progress = 0;
|
|
|
- continue;
|
|
|
- }
|
|
|
- progress = 1;
|
|
|
-
|
|
|
- if (btrfs_key_type(&key) != BTRFS_EXTENT_ITEM_KEY ||
|
|
|
- key.objectid + key.offset <= cur_byte) {
|
|
|
- path->slots[0]++;
|
|
|
- goto next;
|
|
|
- }
|
|
|
-
|
|
|
- total_found++;
|
|
|
- cur_byte = key.objectid + key.offset;
|
|
|
- btrfs_release_path(root, path);
|
|
|
-
|
|
|
- __alloc_chunk_for_shrink(root, block_group, 0);
|
|
|
- ret = relocate_one_extent(root, path, &key, block_group,
|
|
|
- reloc_inode, pass);
|
|
|
- BUG_ON(ret < 0);
|
|
|
- if (ret > 0)
|
|
|
- skipped++;
|
|
|
-
|
|
|
- key.objectid = cur_byte;
|
|
|
- key.type = 0;
|
|
|
- key.offset = 0;
|
|
|
- }
|
|
|
-
|
|
|
- btrfs_release_path(root, path);
|
|
|
-
|
|
|
- if (pass == 0) {
|
|
|
- btrfs_wait_ordered_range(reloc_inode, 0, (u64)-1);
|
|
|
- invalidate_mapping_pages(reloc_inode->i_mapping, 0, -1);
|
|
|
- }
|
|
|
-
|
|
|
- if (total_found > 0) {
|
|
|
- printk(KERN_INFO "btrfs found %llu extents in pass %d\n",
|
|
|
- (unsigned long long)total_found, pass);
|
|
|
- pass++;
|
|
|
- if (total_found == skipped && pass > 2) {
|
|
|
- iput(reloc_inode);
|
|
|
- reloc_inode = create_reloc_inode(info, block_group);
|
|
|
- pass = 0;
|
|
|
- }
|
|
|
- goto again;
|
|
|
- }
|
|
|
-
|
|
|
- /* delete reloc_inode */
|
|
|
- iput(reloc_inode);
|
|
|
-
|
|
|
- /* unpin extents in this range */
|
|
|
- trans = btrfs_start_transaction(info->tree_root, 1);
|
|
|
- btrfs_commit_transaction(trans, info->tree_root);
|
|
|
-
|
|
|
- spin_lock(&block_group->lock);
|
|
|
- WARN_ON(block_group->pinned > 0);
|
|
|
- WARN_ON(block_group->reserved > 0);
|
|
|
- WARN_ON(btrfs_block_group_used(&block_group->item) > 0);
|
|
|
- spin_unlock(&block_group->lock);
|
|
|
- btrfs_put_block_group(block_group);
|
|
|
- ret = 0;
|
|
|
-out:
|
|
|
- btrfs_free_path(path);
|
|
|
- return ret;
|
|
|
-}
|
|
|
-#endif
|
|
|
-
|
|
|
/*
|
|
|
* checks to see if its even possible to relocate this block group.
|
|
|
*
|