Re: [PATCH v3 3/6] alloc_tag: add size-based filtering to ioctl
From: Hao Ge
Date: Mon Jun 08 2026 - 00:10:39 EST
Hi Abhishek
Thanks for the new version.
On 2026/6/6 07:36, Abhishek Bapat wrote:
Extend the allocinfo filtering mechanism to allow users to filter tagsIt seems an extra } was accidentally added here
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 | 68 +++++++++++++++++++++++++++-------
2 files changed, 62 insertions(+), 14 deletions(-)
diff --git a/include/uapi/linux/alloc_tag.h b/include/uapi/linux/alloc_tag.h
index cffb0c46e0b1..0e648192df4d 100644
--- a/include/uapi/linux/alloc_tag.h
+++ b/include/uapi/linux/alloc_tag.h
@@ -39,13 +39,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)
@@ -53,6 +57,8 @@ enum {
struct allocinfo_filter {
__u64 mask; /* bitmask of the filter fields used */
struct allocinfo_tag fields;
+ __u64 min_size;
+ __u64 max_size;
};
struct allocinfo_get_at {
diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c
index 93bc976ac505..ddc6946f56ab 100644
--- a/lib/alloc_tag.c
+++ b/lib/alloc_tag.c
@@ -191,15 +191,26 @@ static int allocinfo_cmp_str(const char *str, const char *template)
return strncmp(allocinfo_str(str), template, ALLOCINFO_STR_SIZE);
}
+/* Fetch the per-CPU counters */
+static inline struct alloc_tag_counters allocinfo_prefetch_counters(struct codetag *ct)
+{
+ return alloc_tag_read(ct_to_alloc_tag(ct));
+}
+
/*
* Populates the UAPI allocinfo_tag_data structure with active runtime
* profiling counters extracted from the given kernel codetag.
*/
static void allocinfo_to_params(struct codetag *ct,
- struct allocinfo_tag_data *data)
+ struct allocinfo_tag_data *data,
+ struct alloc_tag_counters *counters)
{
- struct alloc_tag *tag = ct_to_alloc_tag(ct);
- struct alloc_tag_counters counter = alloc_tag_read(tag);
+ struct alloc_tag_counters local_counters;
+
+ if (!counters) {
+ local_counters = allocinfo_prefetch_counters(ct);
+ counters = &local_counters;
+ }
if (ct->modname)
allocinfo_copy_str(data->tag.modname, ct->modname);
@@ -208,9 +219,9 @@ static void allocinfo_to_params(struct codetag *ct,
allocinfo_copy_str(data->tag.function, ct->function);
allocinfo_copy_str(data->tag.filename, ct->filename);
data->tag.lineno = ct->lineno;
- data->counter.bytes = counter.bytes;
- data->counter.calls = counter.calls;
- data->counter.accurate = !alloc_tag_is_inaccurate(tag);
+ data->counter.bytes = counters->bytes;
+ data->counter.calls = counters->calls;
+ data->counter.accurate = !alloc_tag_is_inaccurate(ct_to_alloc_tag(ct));
}
/*
@@ -234,7 +245,9 @@ static int allocinfo_ioctl_get_content_id(struct seq_file *m, void __user *arg)
* Verifies whether a given codetag satisfies the active filtering criteria by
* matching it's characteristics against the specified filter.
*/
-static bool matches_filter(struct codetag *ct, struct allocinfo_filter *filter)
+static bool matches_filter(struct codetag *ct, struct allocinfo_filter *filter,
+ struct alloc_tag_counters *counters,
+ bool *fetched_counters)
{
if (!filter || !filter->mask)
return true;
@@ -247,20 +260,34 @@ static bool matches_filter(struct codetag *ct, struct allocinfo_filter *filter)
return false;
} else if (allocinfo_cmp_str(ct->modname, filter->fields.modname))
return false;
+ }
}This should be fixed in the second patch; it's unrelated to current functionality.
if ((filter->mask & ALLOCINFO_FILTER_MASK_FUNCTION) &&
- ct->function && (allocinfo_cmp_str(ct->function, filter->fields.function)))
+ ct->function && allocinfo_cmp_str(ct->function, filter->fields.function))
return false;
if ((filter->mask & ALLOCINFO_FILTER_MASK_FILENAME) &&
- ct->filename && (allocinfo_cmp_str(ct->filename, filter->fields.filename)))
+ ct->filename && allocinfo_cmp_str(ct->filename, filter->fields.filename))
return false;
Ditto
Thanks
Best Regards
Hao
if ((filter->mask & ALLOCINFO_FILTER_MASK_LINENO) &&
ct->lineno != filter->fields.lineno)
return false;
+ if (filter->mask & (ALLOCINFO_FILTER_MASK_MIN_SIZE | ALLOCINFO_FILTER_MASK_MAX_SIZE)) {
+ if (!*fetched_counters) {
+ *counters = allocinfo_prefetch_counters(ct);
+ *fetched_counters = true;
+ }
+ if ((filter->mask & ALLOCINFO_FILTER_MASK_MIN_SIZE) &&
+ counters->bytes < filter->min_size)
+ return false;
+ if ((filter->mask & ALLOCINFO_FILTER_MASK_MAX_SIZE) &&
+ counters->bytes > filter->max_size)
+ return false;
+ }
+
return true;
}
@@ -274,6 +301,8 @@ static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg)
struct codetag *ct;
struct allocinfo_get_at params = {0};
__u64 skip_count;
+ struct alloc_tag_counters counters;
+ bool fetched_counters;
if (copy_from_user(¶ms, arg, sizeof(params)))
return -EFAULT;
@@ -281,6 +310,11 @@ static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg)
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.min_size > params.filter.max_size)
+ return -EINVAL;
+
priv = m->private;
mutex_lock(&priv->ioctl_lock);
@@ -304,7 +338,8 @@ static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg)
ct = codetag_next_ct(&priv->ioctl_iter);
while (ct) {
- if (matches_filter(ct, &priv->filter)) {
+ fetched_counters = false;
+ if (matches_filter(ct, &priv->filter, &counters, &fetched_counters)) {
if (skip_count == 0)
break;
skip_count--;
@@ -313,7 +348,7 @@ static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg)
}
if (ct) {
- allocinfo_to_params(ct, ¶ms.data);
+ allocinfo_to_params(ct, ¶ms.data, fetched_counters ? &counters : NULL);
priv->positioned = true;
}
@@ -339,6 +374,8 @@ static int allocinfo_ioctl_get_next(struct seq_file *m, void __user *arg)
struct codetag *ct;
struct allocinfo_tag_data params;
int ret = 0;
+ struct alloc_tag_counters counters;
+ bool fetched_counters;
memset(¶ms, 0, sizeof(params));
priv = m->private;
@@ -352,10 +389,15 @@ static int allocinfo_ioctl_get_next(struct seq_file *m, void __user *arg)
}
ct = codetag_next_ct(&priv->ioctl_iter);
- while (ct && !matches_filter(ct, &priv->filter))
+ while (ct) {
+ fetched_counters = false;
+ if (matches_filter(ct, &priv->filter, &counters, &fetched_counters))
+ break;
ct = codetag_next_ct(&priv->ioctl_iter);
+ }
+
if (ct)
- allocinfo_to_params(ct, ¶ms);
+ allocinfo_to_params(ct, ¶ms, fetched_counters ? &counters : NULL);
if (!ct) {
priv->positioned = false;