diff options
Diffstat (limited to 'fs/btrfs/extent-tree.c')
| -rw-r--r-- | fs/btrfs/extent-tree.c | 133 | 
1 files changed, 107 insertions, 26 deletions
| diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index acf3ed11cfb6..c4661db2b72a 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -124,7 +124,7 @@ static int block_group_bits(struct btrfs_block_group_cache *cache, u64 bits)  	return (cache->flags & bits) == bits;  } -static void btrfs_get_block_group(struct btrfs_block_group_cache *cache) +void btrfs_get_block_group(struct btrfs_block_group_cache *cache)  {  	atomic_inc(&cache->count);  } @@ -5915,19 +5915,6 @@ static int update_block_group(struct btrfs_trans_handle *trans,  			set_extent_dirty(info->pinned_extents,  					 bytenr, bytenr + num_bytes - 1,  					 GFP_NOFS | __GFP_NOFAIL); -			/* -			 * No longer have used bytes in this block group, queue -			 * it for deletion. -			 */ -			if (old_val == 0) { -				spin_lock(&info->unused_bgs_lock); -				if (list_empty(&cache->bg_list)) { -					btrfs_get_block_group(cache); -					list_add_tail(&cache->bg_list, -						      &info->unused_bgs); -				} -				spin_unlock(&info->unused_bgs_lock); -			}  		}  		spin_lock(&trans->transaction->dirty_bgs_lock); @@ -5939,6 +5926,22 @@ static int update_block_group(struct btrfs_trans_handle *trans,  		}  		spin_unlock(&trans->transaction->dirty_bgs_lock); +		/* +		 * No longer have used bytes in this block group, queue it for +		 * deletion. We do this after adding the block group to the +		 * dirty list to avoid races between cleaner kthread and space +		 * cache writeout. +		 */ +		if (!alloc && old_val == 0) { +			spin_lock(&info->unused_bgs_lock); +			if (list_empty(&cache->bg_list)) { +				btrfs_get_block_group(cache); +				list_add_tail(&cache->bg_list, +					      &info->unused_bgs); +			} +			spin_unlock(&info->unused_bgs_lock); +		} +  		btrfs_put_block_group(cache);  		total -= num_bytes;  		bytenr += num_bytes; @@ -8105,21 +8108,47 @@ reada:  }  /* - * TODO: Modify related function to add related node/leaf to dirty_extent_root, - * for later qgroup accounting. - * - * Current, this function does nothing. + * These may not be seen by the usual inc/dec ref code so we have to + * add them here.   */ +static int record_one_subtree_extent(struct btrfs_trans_handle *trans, +				     struct btrfs_root *root, u64 bytenr, +				     u64 num_bytes) +{ +	struct btrfs_qgroup_extent_record *qrecord; +	struct btrfs_delayed_ref_root *delayed_refs; + +	qrecord = kmalloc(sizeof(*qrecord), GFP_NOFS); +	if (!qrecord) +		return -ENOMEM; + +	qrecord->bytenr = bytenr; +	qrecord->num_bytes = num_bytes; +	qrecord->old_roots = NULL; + +	delayed_refs = &trans->transaction->delayed_refs; +	spin_lock(&delayed_refs->lock); +	if (btrfs_qgroup_insert_dirty_extent(delayed_refs, qrecord)) +		kfree(qrecord); +	spin_unlock(&delayed_refs->lock); + +	return 0; +} +  static int account_leaf_items(struct btrfs_trans_handle *trans,  			      struct btrfs_root *root,  			      struct extent_buffer *eb)  {  	int nr = btrfs_header_nritems(eb); -	int i, extent_type; +	int i, extent_type, ret;  	struct btrfs_key key;  	struct btrfs_file_extent_item *fi;  	u64 bytenr, num_bytes; +	/* We can be called directly from walk_up_proc() */ +	if (!root->fs_info->quota_enabled) +		return 0; +  	for (i = 0; i < nr; i++) {  		btrfs_item_key_to_cpu(eb, &key, i); @@ -8138,6 +8167,10 @@ static int account_leaf_items(struct btrfs_trans_handle *trans,  			continue;  		num_bytes = btrfs_file_extent_disk_num_bytes(eb, fi); + +		ret = record_one_subtree_extent(trans, root, bytenr, num_bytes); +		if (ret) +			return ret;  	}  	return 0;  } @@ -8206,8 +8239,6 @@ static int adjust_slots_upwards(struct btrfs_root *root,  /*   * root_eb is the subtree root and is locked before this function is called. - * TODO: Modify this function to mark all (including complete shared node) - * to dirty_extent_root to allow it get accounted in qgroup.   */  static int account_shared_subtree(struct btrfs_trans_handle *trans,  				  struct btrfs_root *root, @@ -8285,6 +8316,11 @@ walk_down:  			btrfs_tree_read_lock(eb);  			btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK);  			path->locks[level] = BTRFS_READ_LOCK_BLOCKING; + +			ret = record_one_subtree_extent(trans, root, child_bytenr, +							root->nodesize); +			if (ret) +				goto out;  		}  		if (level == 0) { @@ -10256,6 +10292,47 @@ out:  	return ret;  } +struct btrfs_trans_handle * +btrfs_start_trans_remove_block_group(struct btrfs_fs_info *fs_info, +				     const u64 chunk_offset) +{ +	struct extent_map_tree *em_tree = &fs_info->mapping_tree.map_tree; +	struct extent_map *em; +	struct map_lookup *map; +	unsigned int num_items; + +	read_lock(&em_tree->lock); +	em = lookup_extent_mapping(em_tree, chunk_offset, 1); +	read_unlock(&em_tree->lock); +	ASSERT(em && em->start == chunk_offset); + +	/* +	 * We need to reserve 3 + N units from the metadata space info in order +	 * to remove a block group (done at btrfs_remove_chunk() and at +	 * btrfs_remove_block_group()), which are used for: +	 * +	 * 1 unit for adding the free space inode's orphan (located in the tree +	 * of tree roots). +	 * 1 unit for deleting the block group item (located in the extent +	 * tree). +	 * 1 unit for deleting the free space item (located in tree of tree +	 * roots). +	 * N units for deleting N device extent items corresponding to each +	 * stripe (located in the device tree). +	 * +	 * In order to remove a block group we also need to reserve units in the +	 * system space info in order to update the chunk tree (update one or +	 * more device items and remove one chunk item), but this is done at +	 * btrfs_remove_chunk() through a call to check_system_chunk(). +	 */ +	map = (struct map_lookup *)em->bdev; +	num_items = 3 + map->num_stripes; +	free_extent_map(em); + +	return btrfs_start_transaction_fallback_global_rsv(fs_info->extent_root, +							   num_items, 1); +} +  /*   * Process the unused_bgs list and remove any that don't have any allocated   * space inside of them. @@ -10322,8 +10399,8 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)  		 * Want to do this before we do anything else so we can recover  		 * properly if we fail to join the transaction.  		 */ -		/* 1 for btrfs_orphan_reserve_metadata() */ -		trans = btrfs_start_transaction(root, 1); +		trans = btrfs_start_trans_remove_block_group(fs_info, +						     block_group->key.objectid);  		if (IS_ERR(trans)) {  			btrfs_dec_block_group_ro(root, block_group);  			ret = PTR_ERR(trans); @@ -10403,11 +10480,15 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)  		 * until transaction commit to do the actual discard.  		 */  		if (trimming) { -			WARN_ON(!list_empty(&block_group->bg_list)); -			spin_lock(&trans->transaction->deleted_bgs_lock); +			spin_lock(&fs_info->unused_bgs_lock); +			/* +			 * A concurrent scrub might have added us to the list +			 * fs_info->unused_bgs, so use a list_move operation +			 * to add the block group to the deleted_bgs list. +			 */  			list_move(&block_group->bg_list,  				  &trans->transaction->deleted_bgs); -			spin_unlock(&trans->transaction->deleted_bgs_lock); +			spin_unlock(&fs_info->unused_bgs_lock);  			btrfs_get_block_group(block_group);  		}  end_trans: | 
