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

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

 




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.

> +
> +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
> 

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