Re: [PATCH 08/20] btrfs-progs: qgroups: introduce btrfs_qgroup_query

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 3/8/18 12:54 AM, Qu Wenruo wrote:
> 
> 
> On 2018年03月08日 10:40, jeffm@xxxxxxxx wrote:
>> From: Jeff Mahoney <jeffm@xxxxxxxx>
>>
>> The only mechanism we have in the progs for searching qgroups is to load
>> all of them and filter the results.  This works for qgroup show but
>> to add quota information to 'btrfs subvoluem show' it's pretty wasteful.
>>
>> This patch splits out setting up the search and performing the search so
>> we can search for a single qgroupid more easily.  Since TREE_SEARCH
>> will give results that don't strictly match the search terms, we add
>> a filter to match only the results we care about.
>>
>> Signed-off-by: Jeff Mahoney <jeffm@xxxxxxxx>
>> ---
>>  qgroup.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++---------------
>>  qgroup.h |   7 ++++
>>  2 files changed, 116 insertions(+), 34 deletions(-)
>>
>> diff --git a/qgroup.c b/qgroup.c
>> index 57815718..d076b1de 100644
>> --- a/qgroup.c
>> +++ b/qgroup.c
>> @@ -1165,11 +1165,30 @@ static inline void print_status_flag_warning(u64 flags)
>>  		warning("qgroup data inconsistent, rescan recommended");
>>  }
>>  
>> -static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
>> +static bool key_in_range(const struct btrfs_key *key,
>> +			 const struct btrfs_ioctl_search_key *sk)
>> +{
>> +	if (key->objectid < sk->min_objectid ||
>> +	    key->objectid > sk->max_objectid)
>> +		return false;
>> +
>> +	if (key->type < sk->min_type ||
>> +	    key->type > sk->max_type)
>> +		return false;
>> +
>> +	if (key->offset < sk->min_offset ||
>> +	    key->offset > sk->max_offset)
>> +		return false;
>> +
>> +	return true;
>> +}
> 
> Even with the key_in_range() check here, we are still following the tree
> search slice behavior:
> 
> tree search will still gives us all the items in key range from
> (min_objectid, min_type, min_offset) to
> (max_objectid, max_type, max_offset).
> 
> I don't see much different between the tree search ioctl and this one.

It's fundamentally different.

The one in the kernel has a silly interface.  It should be min_key and
max_key since the components aren't evaluated independently.  It
effectively treats min_key and max_key as 136-bit values and returns
everything between them, inclusive.  That's the slice behavior, as you
call it.

This key_in_range treats each component separately and acts as a filter
on the slice returned from the kernel.  If we request min/max_offset =
259, the caller will not get anything without offset = 259.

I suppose, ultimately, this could also be done using a filter on the
rbtree using the existing interface but that seems even more wasteful.

-Jeff

>> +
>> +static int __qgroups_search(int fd, struct btrfs_ioctl_search_args *args,
>> +			    struct qgroup_lookup *qgroup_lookup)
>>  {
>>  	int ret;
>> -	struct btrfs_ioctl_search_args args;
>> -	struct btrfs_ioctl_search_key *sk = &args.key;
>> +	struct btrfs_ioctl_search_key *sk = &args->key;
>> +	struct btrfs_ioctl_search_key filter_key = args->key;
>>  	struct btrfs_ioctl_search_header *sh;
>>  	unsigned long off = 0;
>>  	unsigned int i;
>> @@ -1180,30 +1199,15 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
>>  	u64 qgroupid;
>>  	u64 qgroupid1;
>>  
>> -	memset(&args, 0, sizeof(args));
>> -
>> -	sk->tree_id = BTRFS_QUOTA_TREE_OBJECTID;
>> -	sk->max_type = BTRFS_QGROUP_RELATION_KEY;
>> -	sk->min_type = BTRFS_QGROUP_STATUS_KEY;
>> -	sk->max_objectid = (u64)-1;
>> -	sk->max_offset = (u64)-1;
>> -	sk->max_transid = (u64)-1;
>> -	sk->nr_items = 4096;
>> -
>>  	qgroup_lookup_init(qgroup_lookup);
>>  
>>  	while (1) {
>> -		ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
>> +		ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, args);
>>  		if (ret < 0) {
>> -			if (errno == ENOENT) {
>> -				error("can't list qgroups: quotas not enabled");
>> +			if (errno == ENOENT)
>>  				ret = -ENOTTY;
>> -			} else {
>> -				error("can't list qgroups: %s",
>> -				       strerror(errno));
>> +			else
>>  				ret = -errno;
>> -			}
>> -
>>  			break;
>>  		}
>>  
>> @@ -1217,37 +1221,46 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
>>  		 * read the root_ref item it contains
>>  		 */
>>  		for (i = 0; i < sk->nr_items; i++) {
>> -			sh = (struct btrfs_ioctl_search_header *)(args.buf +
>> +			struct btrfs_key key;
>> +
>> +			sh = (struct btrfs_ioctl_search_header *)(args->buf +
>>  								  off);
>>  			off += sizeof(*sh);
>>  
>> -			switch (btrfs_search_header_type(sh)) {
>> +			key.objectid = btrfs_search_header_objectid(sh);
>> +			key.type = btrfs_search_header_type(sh);
>> +			key.offset = btrfs_search_header_offset(sh);
>> +
>> +			if (!key_in_range(&key, &filter_key))
>> +				goto next;
> 
> It still looks like that most other qgroup info will get calculated.
> 
>> +
>> +			switch (key.type) {
>>  			case BTRFS_QGROUP_STATUS_KEY:
>>  				si = (struct btrfs_qgroup_status_item *)
>> -				     (args.buf + off);
>> +				     (args->buf + off);
>>  				flags = btrfs_stack_qgroup_status_flags(si);
>>  
>>  				print_status_flag_warning(flags);
>>  				break;
>>  			case BTRFS_QGROUP_INFO_KEY:
>> -				qgroupid = btrfs_search_header_offset(sh);
>> +				qgroupid = key.offset;
>>  				info = (struct btrfs_qgroup_info_item *)
>> -				       (args.buf + off);
>> +				       (args->buf + off);
>>  
>>  				ret = update_qgroup_info(fd, qgroup_lookup,
>>  							 qgroupid, info);
>>  				break;
>>  			case BTRFS_QGROUP_LIMIT_KEY:
>> -				qgroupid = btrfs_search_header_offset(sh);
>> +				qgroupid = key.offset;
>>  				limit = (struct btrfs_qgroup_limit_item *)
>> -					(args.buf + off);
>> +					(args->buf + off);
>>  
>>  				ret = update_qgroup_limit(fd, qgroup_lookup,
>>  							  qgroupid, limit);
>>  				break;
>>  			case BTRFS_QGROUP_RELATION_KEY:
>> -				qgroupid = btrfs_search_header_offset(sh);
>> -				qgroupid1 = btrfs_search_header_objectid(sh);
>> +				qgroupid = key.offset;
>> +				qgroupid1 = key.objectid;
>>  
>>  				if (qgroupid < qgroupid1)
>>  					break;
>> @@ -1262,15 +1275,16 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
>>  			if (ret)
>>  				return ret;
>>  
>> +next:
>>  			off += btrfs_search_header_len(sh);
>>  
>>  			/*
>>  			 * record the mins in sk so we can make sure the
>>  			 * next search doesn't repeat this root
>>  			 */
>> -			sk->min_type = btrfs_search_header_type(sh);
>> -			sk->min_offset = btrfs_search_header_offset(sh);
>> -			sk->min_objectid = btrfs_search_header_objectid(sh);
>> +			sk->min_type = key.type;
>> +			sk->min_offset = key.offset;
>> +			sk->min_objectid = key.objectid;
>>  		}
>>  		sk->nr_items = 4096;
>>  		/*
>> @@ -1286,6 +1300,67 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
>>  	return ret;
>>  }
>>  
>> +static int qgroups_search_all(int fd, struct qgroup_lookup *qgroup_lookup)
>> +{
>> +	struct btrfs_ioctl_search_args args = {
>> +		.key = {
>> +			.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
>> +			.max_type = BTRFS_QGROUP_RELATION_KEY,
>> +			.min_type = BTRFS_QGROUP_STATUS_KEY,
>> +			.max_objectid = (u64)-1,
>> +			.max_offset = (u64)-1,
>> +			.max_transid = (u64)-1,
>> +			.nr_items = 4096,
>> +		},
>> +	};
>> +	int ret;
>> +
>> +	ret = __qgroups_search(fd, &args, qgroup_lookup);
>> +	if (ret == -ENOTTY)
>> +		error("can't list qgroups: quotas not enabled");
>> +	else if (ret < 0)
>> +		error("can't list qgroups: %s", strerror(-ret));
>> +	return ret;
>> +}
>> +
>> +int btrfs_qgroup_query(int fd, u64 qgroupid, struct btrfs_qgroup_stats *stats)
>> +{
>> +	struct btrfs_ioctl_search_args args = {
>> +		.key = {
>> +			.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
>> +			.min_type = BTRFS_QGROUP_INFO_KEY,
>> +			.max_type = BTRFS_QGROUP_LIMIT_KEY,
>> +			.max_objectid = 0,
>> +			.min_offset = qgroupid,
>> +			.max_offset = qgroupid,
> 
> Just keep in mind that such search could include unrelated qgroup info
> into the result, key_in_range() check won't help much due to the limit
> of how tree search works.
> 
>> +			.max_transid = (u64)-1,
>> +			.nr_items = 4096,
>> +		},
>> +	};
>> +	struct qgroup_lookup qgroup_lookup;
>> +	struct btrfs_qgroup *qgroup;
>> +	struct rb_node *n;
>> +	int ret;
>> +
>> +	ret = __qgroups_search(fd, &args, &qgroup_lookup);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = -ENODATA;
>> +	n = rb_first(&qgroup_lookup.root);
> 
> And in worst case, if we're query some qgroup which doesn't exist (maybe
> manually deleted), then due to limit of tree search, @qgroup_lookup
> could contain unrelated qgroup info.
> 
> In that case, rb_first() could gives us some unrelated qgroup.
> 
> For example, we have qgroups info for 5, 258, 259, 1/1, and we're trying
> to query qgroup 257 (deleted manually), then we will get the following
> result from tree search:
> 
> 	item 3 key (0 QGROUP_INFO 0/258) itemoff 16131 itemsize 40
> 	item 4 key (0 QGROUP_INFO 0/259) itemoff 16131 itemsize 40
> 	item 5 key (0 QGROUP_INFO 1/1) itemoff 16091 itemsize 40
> 	item 6 key (0 QGROUP_LIMIT 0/5) itemoff 16051 itemsize 40
> 
> So in @qgroup_lookup, the first item will be qgroup 258 instead of the
> non-exist qgroup 257.
> 
> Either caller needs extra check or we need to do extra check in this
> function.
>
> Thanks,
> Qu
> 
>> +	if (n) {
>> +		qgroup = rb_entry(n, struct btrfs_qgroup, rb_node);
>> +		stats->qgroupid = qgroup->qgroupid;
>> +		stats->info = qgroup->info;
>> +		stats->limit = qgroup->limit;
>> +
>> +		ret = 0;
>> +	}
>> +
>> +	__free_all_qgroups(&qgroup_lookup);
>> +	return ret;
>> +}
>> +
>>  static void print_all_qgroups(struct qgroup_lookup *qgroup_lookup, bool verbose)
>>  {
>>  
>> @@ -1312,7 +1387,7 @@ int btrfs_show_qgroups(int fd,
>>  	struct qgroup_lookup sort_tree;
>>  	int ret;
>>  
>> -	ret = __qgroups_search(fd, &qgroup_lookup);
>> +	ret = qgroups_search_all(fd, &qgroup_lookup);
>>  	if (ret)
>>  		return ret;
>>  	__filter_and_sort_qgroups(&qgroup_lookup, &sort_tree,
>> diff --git a/qgroup.h b/qgroup.h
>> index 5e71349c..688f92b2 100644
>> --- a/qgroup.h
>> +++ b/qgroup.h
>> @@ -87,6 +87,12 @@ struct btrfs_qgroup_info {
>>  	u64 exclusive_compressed;
>>  };
>>  
>> +struct btrfs_qgroup_stats {
>> +	u64 qgroupid;
>> +	struct btrfs_qgroup_info info;
>> +	struct btrfs_qgroup_limit limit;
>> +};
>> +
>>  int btrfs_qgroup_parse_sort_string(const char *opt_arg,
>>  				struct btrfs_qgroup_comparer_set **comps);
>>  int btrfs_show_qgroups(int fd, struct btrfs_qgroup_filter_set *,
>> @@ -105,4 +111,5 @@ int qgroup_inherit_add_group(struct btrfs_qgroup_inherit **inherit, char *arg);
>>  int qgroup_inherit_add_copy(struct btrfs_qgroup_inherit **inherit, char *arg,
>>  			    int type);
>>  
>> +int btrfs_qgroup_query(int fd, u64 qgroupid, struct btrfs_qgroup_stats *stats);
>>  #endif
>>
> 


-- 
Jeff Mahoney
SUSE Labs

Attachment: signature.asc
Description: OpenPGP digital signature


[Index of Archives]     [Linux Filesystem Development]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux