Re: [PATCH 3/6] alloc_tag: add size-based filtering to ioctl

From: Hao Ge

Date: Thu May 14 2026 - 02:55:08 EST


Hi Abhishek


On 2026/5/5 07:36, Abhishek Bapat wrote:
Extend the allocinfo filtering mechanism to allow users to filter tags
based on the total number of bytes allocated [min_size, max_size]. The
size range is inclusive.

Filtering by size involves retrieving allocinfo per-CPU counters, which
is an expensive operation. Hence, the performance of size-based
filtering will be worse than other filters.

Signed-off-by: Abhishek Bapat <abhishekbapat@xxxxxxxxxx>
---
include/uapi/linux/alloc_tag.h | 8 +++++++-
lib/alloc_tag.c | 15 +++++++++++++++
2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/include/uapi/linux/alloc_tag.h b/include/uapi/linux/alloc_tag.h
index 0cc9db5298c6..229068efd24c 100644
--- a/include/uapi/linux/alloc_tag.h
+++ b/include/uapi/linux/alloc_tag.h
@@ -20,6 +20,8 @@ struct allocinfo_tag {
char function[ALLOCINFO_STR_SIZE];
char filename[ALLOCINFO_STR_SIZE];
__u64 lineno;
+ __u64 min_size;
+ __u64 max_size;
};

allocinfo_tag is used both as a tag identifier in the output data

(allocinfo_tag_data.tag) and as filter criteria

(allocinfo_filter.fields). min_size and max_size are filter

parameters, not tag identity. Also, allocinfo_to_params() does not

fill these fields, so userspace gets zeros in the output, which is

a bit confusing. Might be cleaner to separate filter parameters

from tag identity.

struct allocinfo_counter {
@@ -39,13 +41,17 @@ enum {
ALLOCINFO_FILTER_FUNCTION,
ALLOCINFO_FILTER_FILENAME,
ALLOCINFO_FILTER_LINENO,
- __ALLOCINFO_FILTER_LAST = ALLOCINFO_FILTER_LINENO
+ ALLOCINFO_FILTER_MIN_SIZE,
+ ALLOCINFO_FILTER_MAX_SIZE,
+ __ALLOCINFO_FILTER_LAST = ALLOCINFO_FILTER_MAX_SIZE
};
#define ALLOCINFO_FILTER_MASK_MODNAME (1 << ALLOCINFO_FILTER_MODNAME)
#define ALLOCINFO_FILTER_MASK_FUNCTION (1 << ALLOCINFO_FILTER_FUNCTION)
#define ALLOCINFO_FILTER_MASK_FILENAME (1 << ALLOCINFO_FILTER_FILENAME)
#define ALLOCINFO_FILTER_MASK_LINENO (1 << ALLOCINFO_FILTER_LINENO)
+#define ALLOCINFO_FILTER_MASK_MIN_SIZE (1 << ALLOCINFO_FILTER_MIN_SIZE)
+#define ALLOCINFO_FILTER_MASK_MAX_SIZE (1 << ALLOCINFO_FILTER_MAX_SIZE)
#define ALLOCINFO_FILTER_MASKS \
((1 << (__ALLOCINFO_FILTER_LAST + 1)) - 1)
diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c
index 7ff936e15e97..98a27c302928 100644
--- a/lib/alloc_tag.c
+++ b/lib/alloc_tag.c
@@ -195,6 +195,9 @@ static int allocinfo_ioctl_get_content_id(struct seq_file *m, void __user *arg)
static bool matches_filter(struct codetag *ct, struct allocinfo_filter *filter)
{
+ struct alloc_tag *tag;
+ struct alloc_tag_counters counters;
+
if (!ct || !filter || !filter->mask)
return true;
@@ -214,6 +217,18 @@ static bool matches_filter(struct codetag *ct, struct allocinfo_filter *filter)
ct->lineno != filter->fields.lineno)
return false;
+ if ((filter->mask & ALLOCINFO_FILTER_MASK_MIN_SIZE) ||
+ (filter->mask & ALLOCINFO_FILTER_MASK_MAX_SIZE)) {
+ tag = ct_to_alloc_tag(ct);
+ counters = alloc_tag_read(tag);

alloc_tag_read() is called twice for matching tags

When size filtering is enabled, matches_filter() calls alloc_tag_read()

to check the size, and then allocinfo_to_params() calls it again to

fill the output data:

matches_filter():

    counters = alloc_tag_read(tag);        // 1st read

    if (counters.bytes < min_size)

        return false;

allocinfo_to_params():

    counter = alloc_tag_read(tag);         // 2nd read (same tag)

    data->counter.bytes = counter.bytes;

For matching tags, the same per-CPU counter aggregation is done twice.

On large machines this is not trivial. Would it make sense to cache

the counters from matches_filter() and reuse them in allocinfo_to_params()?


+ if ((filter->mask & ALLOCINFO_FILTER_MASK_MIN_SIZE) &&
+ counters.bytes < filter->fields.min_size)
+ return false;
+ if ((filter->mask & ALLOCINFO_FILTER_MASK_MAX_SIZE) &&
+ counters.bytes > filter->fields.max_size)
+ return false;
+ }
+

No validation for min_size > max_size.

If both MIN_SIZE and MAX_SIZE are set but min_size > max_size,

no records will match and the user gets no indication of the

invalid input. This could be checked alongside the existing

mask validation in allocinfo_ioctl_get_at():

    if (params.filter.mask & ~ALLOCINFO_FILTER_MASKS)

        return -EINVAL;

    +   if ((params.filter.mask & ALLOCINFO_FILTER_MASK_MIN_SIZE) &&

    +       (params.filter.mask & ALLOCINFO_FILTER_MASK_MAX_SIZE) &&

    +       params.filter.fields.min_size > params.filter.fields.max_size)

    +            return -EINVAL;

Thanks

Best Regards

Hao

return true;
}