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
