+
+truncate_racing:
+ vmtruncate(inode, inode->i_size);
+ balance_dirty_pages_ratelimited_nr(inode->i_mapping,
+ total_read);
+ goto out_unlock;
+}
+
+/*
+ * The back references tell us which tree holds a ref on a block,
+ * but it is possible for the tree root field in the reference to
+ * reflect the original root before a snapshot was made. In this
+ * case we should search through all the children of a given root
+ * to find potential holders of references on a block.
+ *
+ * Instead, we do something a little less fancy and just search
+ * all the roots for a given key/block combination.
+ */
+static int find_root_for_ref(struct btrfs_root *root,
+ struct btrfs_path *path,
+ struct btrfs_key *key0,
+ int level,
+ int file_key,
+ struct btrfs_root **found_root,
+ u64 bytenr)
+{
+ struct btrfs_key root_location;
+ struct btrfs_root *cur_root = *found_root;
+ struct btrfs_file_extent_item *file_extent;
+ u64 root_search_start = BTRFS_FS_TREE_OBJECTID;
+ u64 found_bytenr;
+ int ret;
+
+ root_location.offset = (u64)-1;
+ root_location.type = BTRFS_ROOT_ITEM_KEY;
+ path->lowest_level = level;
+ path->reada = 0;
+ while(1) {
+ ret = btrfs_search_slot(NULL, cur_root, key0, path, 0, 0);
+ found_bytenr = 0;
+ if (ret == 0 && file_key) {
+ struct extent_buffer *leaf = path->nodes[0];
+ file_extent = btrfs_item_ptr(leaf, path->slots[0],
+ struct btrfs_file_extent_item);
+ if (btrfs_file_extent_type(leaf, file_extent) ==
+ BTRFS_FILE_EXTENT_REG) {
+ found_bytenr =
+ btrfs_file_extent_disk_bytenr(leaf,
+ file_extent);
+ }
+ } else if (!file_key) {
+ if (path->nodes[level])
+ found_bytenr = path->nodes[level]->start;
+ }
+
+ btrfs_release_path(cur_root, path);
+
+ if (found_bytenr == bytenr) {
+ *found_root = cur_root;
+ ret = 0;
+ goto out;
+ }
+ ret = btrfs_search_root(root->fs_info->tree_root,
+ root_search_start, &root_search_start);
+ if (ret)
+ break;
+
+ root_location.objectid = root_search_start;
+ cur_root = btrfs_read_fs_root_no_name(root->fs_info,
+ &root_location);
+ if (!cur_root) {
+ ret = 1;
+ break;
+ }
+ }
+out:
+ path->lowest_level = 0;
+ return ret;
+}
+
+/*
+ * note, this releases the path
+ */
+static int noinline relocate_one_reference(struct btrfs_root *extent_root,
+ struct btrfs_path *path,
+ struct btrfs_key *extent_key,
+ u64 *last_file_objectid,
+ u64 *last_file_offset,
+ u64 *last_file_root,
+ u64 last_extent)
+{
+ struct inode *inode;
+ struct btrfs_root *found_root;
+ struct btrfs_key root_location;
+ struct btrfs_key found_key;
+ struct btrfs_extent_ref *ref;
+ u64 ref_root;
+ u64 ref_gen;
+ u64 ref_objectid;
+ u64 ref_offset;
+ int ret;
+ int level;
+
+ WARN_ON(!mutex_is_locked(&extent_root->fs_info->alloc_mutex));
+
+ ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ struct btrfs_extent_ref);
+ ref_root = btrfs_ref_root(path->nodes[0], ref);
+ ref_gen = btrfs_ref_generation(path->nodes[0], ref);
+ ref_objectid = btrfs_ref_objectid(path->nodes[0], ref);
+ ref_offset = btrfs_ref_offset(path->nodes[0], ref);
+ btrfs_release_path(extent_root, path);
+
+ root_location.objectid = ref_root;
+ if (ref_gen == 0)
+ root_location.offset = 0;
+ else
+ root_location.offset = (u64)-1;
+ root_location.type = BTRFS_ROOT_ITEM_KEY;
+
+ found_root = btrfs_read_fs_root_no_name(extent_root->fs_info,
+ &root_location);
+ BUG_ON(!found_root);
+ mutex_unlock(&extent_root->fs_info->alloc_mutex);
+
+ if (ref_objectid >= BTRFS_FIRST_FREE_OBJECTID) {
+ found_key.objectid = ref_objectid;
+ found_key.type = BTRFS_EXTENT_DATA_KEY;
+ found_key.offset = ref_offset;
+ level = 0;
+
+ if (last_extent == extent_key->objectid &&
+ *last_file_objectid == ref_objectid &&
+ *last_file_offset == ref_offset &&
+ *last_file_root == ref_root)
+ goto out;
+
+ ret = find_root_for_ref(extent_root, path, &found_key,
+ level, 1, &found_root,
+ extent_key->objectid);
+
+ if (ret)
+ goto out;
+
+ if (last_extent == extent_key->objectid &&
+ *last_file_objectid == ref_objectid &&
+ *last_file_offset == ref_offset &&
+ *last_file_root == ref_root)
+ goto out;
+
+ inode = btrfs_iget_locked(extent_root->fs_info->sb,
+ ref_objectid, found_root);
+ if (inode->i_state & I_NEW) {
+ /* the inode and parent dir are two different roots */
+ BTRFS_I(inode)->root = found_root;
+ BTRFS_I(inode)->location.objectid = ref_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);
+
+ }
+ /* this can happen if the reference is not against
+ * the latest version of the tree root
+ */
+ if (is_bad_inode(inode))
+ goto out;
+
+ *last_file_objectid = inode->i_ino;
+ *last_file_root = found_root->root_key.objectid;
+ *last_file_offset = ref_offset;
+
+ relocate_inode_pages(inode, ref_offset, extent_key->offset);
+ iput(inode);
+ } else {
+ struct btrfs_trans_handle *trans;
+ struct extent_buffer *eb;
+ int needs_lock = 0;
+
+ eb = read_tree_block(found_root, extent_key->objectid,
+ extent_key->offset, 0);
+ btrfs_tree_lock(eb);
+ level = btrfs_header_level(eb);
+
+ if (level == 0)
+ btrfs_item_key_to_cpu(eb, &found_key, 0);
+ else
+ btrfs_node_key_to_cpu(eb, &found_key, 0);
+
+ btrfs_tree_unlock(eb);
+ free_extent_buffer(eb);
+
+ ret = find_root_for_ref(extent_root, path, &found_key,
+ level, 0, &found_root,
+ extent_key->objectid);
+
+ if (ret)
+ goto out;
+
+ /*
+ * right here almost anything could happen to our key,
+ * but that's ok. The cow below will either relocate it
+ * or someone else will have relocated it. Either way,
+ * it is in a different spot than it was before and
+ * we're happy.
+ */
+
+ trans = btrfs_start_transaction(found_root, 1);
+
+ if (found_root == extent_root->fs_info->extent_root ||
+ found_root == extent_root->fs_info->chunk_root ||
+ found_root == extent_root->fs_info->dev_root) {
+ needs_lock = 1;
+ mutex_lock(&extent_root->fs_info->alloc_mutex);
+ }
+
+ path->lowest_level = level;
+ path->reada = 2;
+ ret = btrfs_search_slot(trans, found_root, &found_key, path,
+ 0, 1);
+ path->lowest_level = 0;
+ btrfs_release_path(found_root, path);
+
+ if (found_root == found_root->fs_info->extent_root)
+ btrfs_extent_post_op(trans, found_root);
+ if (needs_lock)
+ mutex_unlock(&extent_root->fs_info->alloc_mutex);
+
+ btrfs_end_transaction(trans, found_root);
+
+ }
+out:
+ mutex_lock(&extent_root->fs_info->alloc_mutex);
+ return 0;
+}
+
+static int noinline del_extent_zero(struct btrfs_root *extent_root,
+ struct btrfs_path *path,
+ struct btrfs_key *extent_key)
+{
+ int ret;
+ struct btrfs_trans_handle *trans;
+
+ trans = btrfs_start_transaction(extent_root, 1);
+ ret = btrfs_search_slot(trans, extent_root, extent_key, path, -1, 1);
+ if (ret > 0) {
+ ret = -EIO;
+ goto out;
+ }
+ if (ret < 0)
+ goto out;
+ ret = btrfs_del_item(trans, extent_root, path);
+out:
+ btrfs_end_transaction(trans, extent_root);
+ return ret;
+}
+
+static int noinline relocate_one_extent(struct btrfs_root *extent_root,
+ struct btrfs_path *path,
+ struct btrfs_key *extent_key)
+{
+ struct btrfs_key key;
+ struct btrfs_key found_key;
+ struct extent_buffer *leaf;
+ u64 last_file_objectid = 0;
+ u64 last_file_root = 0;
+ u64 last_file_offset = (u64)-1;
+ u64 last_extent = 0;
+ u32 nritems;
+ u32 item_size;
+ int ret = 0;
+
+ if (extent_key->objectid == 0) {
+ ret = del_extent_zero(extent_root, path, extent_key);
+ goto out;
+ }
+ key.objectid = extent_key->objectid;
+ key.type = BTRFS_EXTENT_REF_KEY;
+ key.offset = 0;
+
+ while(1) {
+ ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0);
+
+ if (ret < 0)
+ goto out;
+
+ ret = 0;
+ leaf = path->nodes[0];
+ nritems = btrfs_header_nritems(leaf);
+ if (path->slots[0] == nritems) {
+ ret = btrfs_next_leaf(extent_root, path);
+ if (ret > 0) {
+ ret = 0;
+ goto out;
+ }
+ if (ret < 0)
+ goto out;
+ leaf = path->nodes[0];
+ }
+
+ btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
+ if (found_key.objectid != extent_key->objectid) {
+ break;
+ }
+
+ if (found_key.type != BTRFS_EXTENT_REF_KEY) {
+ break;
+ }
+
+ key.offset = found_key.offset + 1;
+ item_size = btrfs_item_size_nr(leaf, path->slots[0]);
+
+ ret = relocate_one_reference(extent_root, path, extent_key,
+ &last_file_objectid,
+ &last_file_offset,
+ &last_file_root, last_extent);
+ if (ret)
+ goto out;
+ last_extent = extent_key->objectid;
+ }
+ ret = 0;
+out:
+ btrfs_release_path(extent_root, path);
+ return ret;
+}
+
+static u64 update_block_group_flags(struct btrfs_root *root, u64 flags)
+{
+ u64 num_devices;
+ u64 stripped = BTRFS_BLOCK_GROUP_RAID0 |
+ BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_RAID10;
+
+ num_devices = root->fs_info->fs_devices->num_devices;
+ if (num_devices == 1) {
+ stripped |= BTRFS_BLOCK_GROUP_DUP;
+ stripped = flags & ~stripped;
+
+ /* turn raid0 into single device chunks */
+ if (flags & BTRFS_BLOCK_GROUP_RAID0)
+ return stripped;
+
+ /* turn mirroring into duplication */
+ if (flags & (BTRFS_BLOCK_GROUP_RAID1 |
+ BTRFS_BLOCK_GROUP_RAID10))
+ return stripped | BTRFS_BLOCK_GROUP_DUP;
+ return flags;
+ } else {
+ /* they already had raid on here, just return */
+ if (flags & stripped)
+ return flags;
+
+ stripped |= BTRFS_BLOCK_GROUP_DUP;
+ stripped = flags & ~stripped;
+
+ /* switch duplicated blocks with raid1 */
+ if (flags & BTRFS_BLOCK_GROUP_DUP)
+ return stripped | BTRFS_BLOCK_GROUP_RAID1;
+
+ /* turn single device chunks into raid0 */
+ return stripped | BTRFS_BLOCK_GROUP_RAID0;
+ }
+ return flags;
+}
+
+int __alloc_chunk_for_shrink(struct btrfs_root *root,
+ struct btrfs_block_group_cache *shrink_block_group,
+ int force)
+{
+ struct btrfs_trans_handle *trans;
+ u64 new_alloc_flags;
+ u64 calc;
+
+ if (btrfs_block_group_used(&shrink_block_group->item) > 0) {
+
+ mutex_unlock(&root->fs_info->alloc_mutex);
+ trans = btrfs_start_transaction(root, 1);
+ mutex_lock(&root->fs_info->alloc_mutex);
+
+ new_alloc_flags = update_block_group_flags(root,
+ shrink_block_group->flags);
+ if (new_alloc_flags != shrink_block_group->flags) {
+ calc =
+ btrfs_block_group_used(&shrink_block_group->item);
+ } else {
+ calc = shrink_block_group->key.offset;
+ }
+ do_chunk_alloc(trans, root->fs_info->extent_root,
+ calc + 2 * 1024 * 1024, new_alloc_flags, force);
+
+ mutex_unlock(&root->fs_info->alloc_mutex);
+ btrfs_end_transaction(trans, root);
+ mutex_lock(&root->fs_info->alloc_mutex);
+ }
+ return 0;
+}
+
+int btrfs_shrink_extent_tree(struct btrfs_root *root, u64 shrink_start)
+{
+ struct btrfs_trans_handle *trans;
+ struct btrfs_root *tree_root = root->fs_info->tree_root;
+ struct btrfs_path *path;
+ u64 cur_byte;
+ u64 total_found;
+ u64 shrink_last_byte;
+ struct btrfs_block_group_cache *shrink_block_group;
+ struct btrfs_fs_info *info = root->fs_info;
+ struct btrfs_key key;
+ struct btrfs_key found_key;
+ struct extent_buffer *leaf;
+ u32 nritems;
+ int ret;
+ int progress;
+
+ mutex_lock(&root->fs_info->alloc_mutex);
+ shrink_block_group = btrfs_lookup_block_group(root->fs_info,
+ shrink_start);
+ BUG_ON(!shrink_block_group);
+
+ shrink_last_byte = shrink_block_group->key.objectid +
+ shrink_block_group->key.offset;
+
+ shrink_block_group->space_info->total_bytes -=
+ shrink_block_group->key.offset;
+ path = btrfs_alloc_path();
+ root = root->fs_info->extent_root;
+ path->reada = 2;
+
+ printk("btrfs relocating block group %llu flags %llu\n",
+ (unsigned long long)shrink_start,
+ (unsigned long long)shrink_block_group->flags);
+
+ __alloc_chunk_for_shrink(root, shrink_block_group, 1);
+
+again:
+
+ shrink_block_group->ro = 1;
+
+ total_found = 0;
+ progress = 0;
+ key.objectid = shrink_start;
+ key.offset = 0;
+ key.type = 0;
+ cur_byte = key.objectid;
+
+ ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ if (ret < 0)
+ goto out;
+
+ ret = btrfs_previous_item(root, path, 0, BTRFS_EXTENT_ITEM_KEY);
+ if (ret < 0)
+ goto out;
+
+ if (ret == 0) {
+ leaf = path->nodes[0];
+ btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
+ if (found_key.objectid + found_key.offset > shrink_start &&
+ found_key.objectid < shrink_last_byte) {
+ cur_byte = found_key.objectid;
+ key.objectid = cur_byte;
+ }
+ }
+ btrfs_release_path(root, path);
+
+ 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, &found_key, path->slots[0]);
+
+ if (found_key.objectid >= shrink_last_byte)
+ break;
+
+ if (progress && need_resched()) {
+ memcpy(&key, &found_key, sizeof(key));
+ cond_resched();
+ btrfs_release_path(root, path);
+ btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ progress = 0;
+ goto next;
+ }
+ progress = 1;
+
+ if (btrfs_key_type(&found_key) != BTRFS_EXTENT_ITEM_KEY ||
+ found_key.objectid + found_key.offset <= cur_byte) {
+ memcpy(&key, &found_key, sizeof(key));
+ key.offset++;
+ path->slots[0]++;
+ goto next;
+ }
+
+ total_found++;
+ cur_byte = found_key.objectid + found_key.offset;
+ key.objectid = cur_byte;
+ btrfs_release_path(root, path);
+ ret = relocate_one_extent(root, path, &found_key);
+ __alloc_chunk_for_shrink(root, shrink_block_group, 0);
+ }
+
+ btrfs_release_path(root, path);
+
+ if (total_found > 0) {
+ printk("btrfs relocate found %llu last extent was %llu\n",
+ (unsigned long long)total_found,
+ (unsigned long long)found_key.objectid);
+ mutex_unlock(&root->fs_info->alloc_mutex);
+ trans = btrfs_start_transaction(tree_root, 1);
+ btrfs_commit_transaction(trans, tree_root);
+
+ btrfs_clean_old_snapshots(tree_root);
+
+ trans = btrfs_start_transaction(tree_root, 1);
+ btrfs_commit_transaction(trans, tree_root);
+ mutex_lock(&root->fs_info->alloc_mutex);
+ goto again;
+ }
+
+ /*
+ * we've freed all the extents, now remove the block
+ * group item from the tree
+ */
+ mutex_unlock(&root->fs_info->alloc_mutex);
+
+ trans = btrfs_start_transaction(root, 1);
+ mutex_lock(&root->fs_info->alloc_mutex);
+ memcpy(&key, &shrink_block_group->key, sizeof(key));
+
+ ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
+ if (ret > 0)
+ ret = -EIO;
+ if (ret < 0)
+ goto out;
+
+ clear_extent_bits(&info->block_group_cache, key.objectid,
+ key.objectid + key.offset - 1,
+ (unsigned int)-1, GFP_NOFS);
+
+
+ clear_extent_bits(&info->free_space_cache,
+ key.objectid, key.objectid + key.offset - 1,
+ (unsigned int)-1, GFP_NOFS);
+
+ memset(shrink_block_group, 0, sizeof(*shrink_block_group));
+ kfree(shrink_block_group);
+
+ btrfs_del_item(trans, root, path);
+ btrfs_release_path(root, path);
+ mutex_unlock(&root->fs_info->alloc_mutex);
+ btrfs_commit_transaction(trans, root);
+
+ mutex_lock(&root->fs_info->alloc_mutex);
+
+ /* the code to unpin extents might set a few bits in the free
+ * space cache for this range again
+ */
+ clear_extent_bits(&info->free_space_cache,
+ key.objectid, key.objectid + key.offset - 1,
+ (unsigned int)-1, GFP_NOFS);
+out:
+ btrfs_free_path(path);
+ mutex_unlock(&root->fs_info->alloc_mutex);
+ return ret;
+}
+
+int find_first_block_group(struct btrfs_root *root, struct btrfs_path *path,
+ struct btrfs_key *key)
+{
+ int ret = 0;
+ struct btrfs_key found_key;
+ struct extent_buffer *leaf;
+ int slot;
+
+ ret = btrfs_search_slot(NULL, root, key, path, 0, 0);
+ if (ret < 0)
+ goto out;
+
+ while(1) {
+ slot = path->slots[0];
+ leaf = path->nodes[0];
+ if (slot >= btrfs_header_nritems(leaf)) {
+ ret = btrfs_next_leaf(root, path);
+ if (ret == 0)
+ continue;
+ if (ret < 0)
+ goto out;
+ break;
+ }
+ btrfs_item_key_to_cpu(leaf, &found_key, slot);
+
+ if (found_key.objectid >= key->objectid &&
+ found_key.type == BTRFS_BLOCK_GROUP_ITEM_KEY) {
+ ret = 0;
+ goto out;
+ }
+ path->slots[0]++;
+ }
+ ret = -ENOENT;
+out:
+ return ret;