Skip to content
Snippets Groups Projects
extent-tree.c 222 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	struct btrfs_key key;
    	int ret = 0;
    
    	list_for_each_entry_safe(block_group, tmp, &trans->new_bgs,
    				 new_bg_list) {
    		list_del_init(&block_group->new_bg_list);
    
    		if (ret)
    			continue;
    
    		spin_lock(&block_group->lock);
    		memcpy(&item, &block_group->item, sizeof(item));
    		memcpy(&key, &block_group->key, sizeof(key));
    		spin_unlock(&block_group->lock);
    
    		ret = btrfs_insert_item(trans, extent_root, &key, &item,
    					sizeof(item));
    		if (ret)
    			btrfs_abort_transaction(trans, extent_root, ret);
    	}
    }
    
    
    int btrfs_make_block_group(struct btrfs_trans_handle *trans,
    			   struct btrfs_root *root, u64 bytes_used,
    
    			   u64 type, u64 chunk_objectid, u64 chunk_offset,
    
    			   u64 size)
    {
    	int ret;
    	struct btrfs_root *extent_root;
    	struct btrfs_block_group_cache *cache;
    
    	extent_root = root->fs_info->extent_root;
    
    
    	root->fs_info->last_trans_log_full_commit = trans->transid;
    
    	cache = kzalloc(sizeof(*cache), GFP_NOFS);
    
    	if (!cache)
    		return -ENOMEM;
    
    	cache->free_space_ctl = kzalloc(sizeof(*cache->free_space_ctl),
    					GFP_NOFS);
    	if (!cache->free_space_ctl) {
    		kfree(cache);
    		return -ENOMEM;
    	}
    
    	cache->key.objectid = chunk_offset;
    
    	cache->key.offset = size;
    
    	cache->key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
    
    	cache->sectorsize = root->sectorsize;
    
    	cache->fs_info = root->fs_info;
    
    David Woodhouse's avatar
    David Woodhouse committed
    	cache->full_stripe_len = btrfs_full_stripe_len(root,
    					       &root->fs_info->mapping_tree,
    					       chunk_offset);
    
    	atomic_set(&cache->count, 1);
    
    	spin_lock_init(&cache->lock);
    
    	INIT_LIST_HEAD(&cache->list);
    
    	INIT_LIST_HEAD(&cache->cluster_list);
    
    	INIT_LIST_HEAD(&cache->new_bg_list);
    
    	btrfs_init_free_space_ctl(cache);
    
    
    	btrfs_set_block_group_used(&cache->item, bytes_used);
    	btrfs_set_block_group_chunk_objectid(&cache->item, chunk_objectid);
    	cache->flags = type;
    	btrfs_set_block_group_flags(&cache->item, type);
    
    
    	cache->last_byte_to_unpin = (u64)-1;
    
    	cache->cached = BTRFS_CACHE_FINISHED;
    
    	exclude_super_stripes(root, cache);
    
    	add_new_free_space(cache, root->fs_info, chunk_offset,
    			   chunk_offset + size);
    
    
    	free_excluded_extents(root, cache);
    
    
    	ret = update_space_info(root->fs_info, cache->flags, size, bytes_used,
    				&cache->space_info);
    
    	BUG_ON(ret); /* -ENOMEM */
    
    	update_global_block_rsv(root->fs_info);
    
    
    	spin_lock(&cache->space_info->lock);
    
    	cache->space_info->bytes_readonly += cache->bytes_super;
    
    	spin_unlock(&cache->space_info->lock);
    
    
    	__link_block_group(cache->space_info, cache);
    
    	ret = btrfs_add_block_group_cache(root->fs_info, cache);
    
    	BUG_ON(ret); /* Logic error */
    
    	list_add_tail(&cache->new_bg_list, &trans->new_bgs);
    
    	set_avail_alloc_bits(extent_root->fs_info, type);
    
    static void clear_avail_alloc_bits(struct btrfs_fs_info *fs_info, u64 flags)
    {
    
    	u64 extra_flags = chunk_to_extended(flags) &
    				BTRFS_EXTENDED_PROFILE_MASK;
    
    	write_seqlock(&fs_info->profiles_lock);
    
    	if (flags & BTRFS_BLOCK_GROUP_DATA)
    		fs_info->avail_data_alloc_bits &= ~extra_flags;
    	if (flags & BTRFS_BLOCK_GROUP_METADATA)
    		fs_info->avail_metadata_alloc_bits &= ~extra_flags;
    	if (flags & BTRFS_BLOCK_GROUP_SYSTEM)
    		fs_info->avail_system_alloc_bits &= ~extra_flags;
    
    	write_sequnlock(&fs_info->profiles_lock);
    
    int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
    			     struct btrfs_root *root, u64 group_start)
    {
    	struct btrfs_path *path;
    	struct btrfs_block_group_cache *block_group;
    
    	struct btrfs_free_cluster *cluster;
    
    	struct btrfs_root *tree_root = root->fs_info->tree_root;
    
    	struct btrfs_key key;
    
    	struct inode *inode;
    
    	int ret;
    
    Josef Bacik's avatar
    Josef Bacik committed
    	int factor;
    
    
    	root = root->fs_info->extent_root;
    
    	block_group = btrfs_lookup_block_group(root->fs_info, group_start);
    	BUG_ON(!block_group);
    
    	BUG_ON(!block_group->ro);
    
    	/*
    	 * Free the reserved super bytes from this block group before
    	 * remove it.
    	 */
    	free_excluded_extents(root, block_group);
    
    
    	memcpy(&key, &block_group->key, sizeof(key));
    
    	index = get_block_group_index(block_group);
    
    Josef Bacik's avatar
    Josef Bacik committed
    	if (block_group->flags & (BTRFS_BLOCK_GROUP_DUP |
    				  BTRFS_BLOCK_GROUP_RAID1 |
    				  BTRFS_BLOCK_GROUP_RAID10))
    		factor = 2;
    	else
    		factor = 1;
    
    	/* make sure this block group isn't part of an allocation cluster */
    	cluster = &root->fs_info->data_alloc_cluster;
    	spin_lock(&cluster->refill_lock);
    	btrfs_return_cluster_to_free_space(block_group, cluster);
    	spin_unlock(&cluster->refill_lock);
    
    	/*
    	 * make sure this block group isn't part of a metadata
    	 * allocation cluster
    	 */
    	cluster = &root->fs_info->meta_alloc_cluster;
    	spin_lock(&cluster->refill_lock);
    	btrfs_return_cluster_to_free_space(block_group, cluster);
    	spin_unlock(&cluster->refill_lock);
    
    
    	path = btrfs_alloc_path();
    
    	if (!path) {
    		ret = -ENOMEM;
    		goto out;
    	}
    
    	inode = lookup_free_space_inode(tree_root, block_group, path);
    
    	if (!IS_ERR(inode)) {
    
    		ret = btrfs_orphan_add(trans, inode);
    
    		if (ret) {
    			btrfs_add_delayed_iput(inode);
    			goto out;
    		}
    
    		clear_nlink(inode);
    		/* One for the block groups ref */
    		spin_lock(&block_group->lock);
    		if (block_group->iref) {
    			block_group->iref = 0;
    			block_group->inode = NULL;
    			spin_unlock(&block_group->lock);
    			iput(inode);
    		} else {
    			spin_unlock(&block_group->lock);
    		}
    		/* One for our lookup ref */
    
    		btrfs_add_delayed_iput(inode);
    
    	}
    
    	key.objectid = BTRFS_FREE_SPACE_OBJECTID;
    	key.offset = block_group->key.objectid;
    	key.type = 0;
    
    	ret = btrfs_search_slot(trans, tree_root, &key, path, -1, 1);
    	if (ret < 0)
    		goto out;
    	if (ret > 0)
    
    		btrfs_release_path(path);
    
    	if (ret == 0) {
    		ret = btrfs_del_item(trans, tree_root, path);
    		if (ret)
    			goto out;
    
    		btrfs_release_path(path);
    
    	spin_lock(&root->fs_info->block_group_cache_lock);
    
    	rb_erase(&block_group->cache_node,
    		 &root->fs_info->block_group_cache_tree);
    
    
    	if (root->fs_info->first_logical_byte == block_group->key.objectid)
    		root->fs_info->first_logical_byte = (u64)-1;
    
    	spin_unlock(&root->fs_info->block_group_cache_lock);
    
    	down_write(&block_group->space_info->groups_sem);
    
    	/*
    	 * we must use list_del_init so people can check to see if they
    	 * are still on the list after taking the semaphore
    	 */
    	list_del_init(&block_group->list);
    
    	if (list_empty(&block_group->space_info->block_groups[index]))
    		clear_avail_alloc_bits(root->fs_info, block_group->flags);
    
    	up_write(&block_group->space_info->groups_sem);
    
    	if (block_group->cached == BTRFS_CACHE_STARTED)
    
    		wait_block_group_cache_done(block_group);
    
    
    	btrfs_remove_free_space_cache(block_group);
    
    
    	spin_lock(&block_group->space_info->lock);
    	block_group->space_info->total_bytes -= block_group->key.offset;
    	block_group->space_info->bytes_readonly -= block_group->key.offset;
    
    Josef Bacik's avatar
    Josef Bacik committed
    	block_group->space_info->disk_total -= block_group->key.offset * factor;
    
    	spin_unlock(&block_group->space_info->lock);
    
    	memcpy(&key, &block_group->key, sizeof(key));
    
    
    	btrfs_clear_space_info_full(root->fs_info);
    
    	btrfs_put_block_group(block_group);
    	btrfs_put_block_group(block_group);
    
    
    	ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
    	if (ret > 0)
    		ret = -EIO;
    	if (ret < 0)
    		goto out;
    
    	ret = btrfs_del_item(trans, root, path);
    out:
    	btrfs_free_path(path);
    	return ret;
    }
    
    int btrfs_init_space_info(struct btrfs_fs_info *fs_info)
    {
    	struct btrfs_space_info *space_info;
    
    	struct btrfs_super_block *disk_super;
    	u64 features;
    	u64 flags;
    	int mixed = 0;
    
    	disk_super = fs_info->super_copy;
    
    	if (!btrfs_super_root(disk_super))
    		return 1;
    
    	features = btrfs_super_incompat_flags(disk_super);
    	if (features & BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS)
    		mixed = 1;
    
    	flags = BTRFS_BLOCK_GROUP_SYSTEM;
    	ret = update_space_info(fs_info, flags, 0, 0, &space_info);
    
    	if (mixed) {
    		flags = BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_DATA;
    		ret = update_space_info(fs_info, flags, 0, 0, &space_info);
    	} else {
    		flags = BTRFS_BLOCK_GROUP_METADATA;
    		ret = update_space_info(fs_info, flags, 0, 0, &space_info);
    		if (ret)
    			goto out;
    
    		flags = BTRFS_BLOCK_GROUP_DATA;
    		ret = update_space_info(fs_info, flags, 0, 0, &space_info);
    	}
    out:
    
    int btrfs_error_unpin_extent_range(struct btrfs_root *root, u64 start, u64 end)
    {
    	return unpin_extent_range(root, start, end);
    }
    
    int btrfs_error_discard_extent(struct btrfs_root *root, u64 bytenr,
    
    			       u64 num_bytes, u64 *actual_bytes)
    
    	return btrfs_discard_extent(root, bytenr, num_bytes, actual_bytes);
    
    
    int btrfs_trim_fs(struct btrfs_root *root, struct fstrim_range *range)
    {
    	struct btrfs_fs_info *fs_info = root->fs_info;
    	struct btrfs_block_group_cache *cache = NULL;
    	u64 group_trimmed;
    	u64 start;
    	u64 end;
    	u64 trimmed = 0;
    
    	u64 total_bytes = btrfs_super_total_bytes(fs_info->super_copy);
    
    	/*
    	 * try to trim all FS space, our block group may start from non-zero.
    	 */
    	if (range->len == total_bytes)
    		cache = btrfs_lookup_first_block_group(fs_info, range->start);
    	else
    		cache = btrfs_lookup_block_group(fs_info, range->start);
    
    
    	while (cache) {
    		if (cache->key.objectid >= (range->start + range->len)) {
    			btrfs_put_block_group(cache);
    			break;
    		}
    
    		start = max(range->start, cache->key.objectid);
    		end = min(range->start + range->len,
    				cache->key.objectid + cache->key.offset);
    
    		if (end - start >= range->minlen) {
    			if (!block_group_cache_done(cache)) {
    
    				ret = cache_block_group(cache, 0);
    
    				if (!ret)
    					wait_block_group_cache_done(cache);
    			}
    			ret = btrfs_trim_block_group(cache,
    						     &group_trimmed,
    						     start,
    						     end,
    						     range->minlen);
    
    			trimmed += group_trimmed;
    			if (ret) {
    				btrfs_put_block_group(cache);
    				break;
    			}
    		}
    
    		cache = next_block_group(fs_info->tree_root, cache);
    	}
    
    	range->len = trimmed;
    	return ret;
    }