Re: [PATCH v5 2/6] alloc_tag: add ioctl filters to /proc/allocinfo
From: Suren Baghdasaryan
Date: Wed Jun 17 2026 - 12:13:08 EST
On Mon, Jun 15, 2026 at 4:04 PM Abhishek Bapat <abhishekbapat@xxxxxxxxxx> wrote:
>
> Extend the capability of the IOCTL mechanism to filter allocations based
> on tag's module name, function name, file name and line number.
>
> Signed-off-by: Abhishek Bapat <abhishekbapat@xxxxxxxxxx>
> Acked-by: Hao Ge <hao.ge@xxxxxxxxx>
Acked-by: Suren Baghdasaryan <surenb@xxxxxxxxxx>
> ---
> include/uapi/linux/alloc_tag.h | 26 ++++++++++++-
> lib/alloc_tag.c | 68 ++++++++++++++++++++++++++++++++--
> 2 files changed, 89 insertions(+), 5 deletions(-)
>
> diff --git a/include/uapi/linux/alloc_tag.h b/include/uapi/linux/alloc_tag.h
> index 0928e1a48d49..3b11877955b9 100644
> --- a/include/uapi/linux/alloc_tag.h
> +++ b/include/uapi/linux/alloc_tag.h
> @@ -40,8 +40,32 @@ struct allocinfo_tag_data {
> struct allocinfo_counter counter;
> };
>
> +enum {
> + ALLOCINFO_FILTER_MODNAME,
> + ALLOCINFO_FILTER_FUNCTION,
> + ALLOCINFO_FILTER_FILENAME,
> + ALLOCINFO_FILTER_LINENO,
> + __ALLOCINFO_FILTER_LAST = ALLOCINFO_FILTER_LINENO
> +};
> +
> +#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_MASKS \
> + ((1 << (__ALLOCINFO_FILTER_LAST + 1)) - 1)
> +
> +struct allocinfo_filter {
> + __u64 mask; /* bitmask of the filter fields used */
> + struct allocinfo_tag fields;
> +};
> +
> struct allocinfo_get_at {
> - __u64 pos; /* input */
> + /* inputs */
> + __u64 pos;
> + struct allocinfo_filter filter;
> + /* output */
> struct allocinfo_tag_data data;
> };
>
> diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c
> index 82e3b5f32dff..5feb61d9fb92 100644
> --- a/lib/alloc_tag.c
> +++ b/lib/alloc_tag.c
> @@ -49,6 +49,7 @@ struct allocinfo_private {
> struct codetag_iterator iter;
> struct codetag_iterator reported_iter;
> bool print_header;
> + struct allocinfo_filter filter;
> /* ioctl uses a separate iterator not to interfere with reads */
> struct codetag_iterator ioctl_iter;
> bool positioned; /* seq_open_private() sets to 0 */
> @@ -188,6 +189,12 @@ static void allocinfo_copy_str(char *dest, const char *src)
> strscpy_pad(dest, allocinfo_str(src), ALLOCINFO_STR_SIZE);
> }
>
> +/* Compare two strings and only consider the trimmed suffix if s1 is too long */
> +static int allocinfo_cmp_str(const char *str, const char *template)
> +{
> + return strncmp(allocinfo_str(str), template, ALLOCINFO_STR_SIZE);
> +}
> +
> /*
> * Populates the UAPI allocinfo_tag_data structure with active runtime
> * profiling counters extracted from the given kernel codetag.
> @@ -227,6 +234,40 @@ static int allocinfo_ioctl_get_content_id(struct seq_file *m, void __user *arg)
> return 0;
> }
>
> +/*
> + * Verifies whether a given codetag satisfies the active filtering criteria by
> + * matching its characteristics against the specified filter.
> + */
> +static bool matches_filter(struct codetag *ct, struct allocinfo_filter *filter)
> +{
> + if (!filter || !filter->mask)
> + return true;
> +
> + if (filter->mask & ALLOCINFO_FILTER_MASK_MODNAME) {
> + /* user wants to filter by modname but ct->modname is NULL */
> + if (!ct->modname) {
> + /* validate if user was attempting to filter for built-in allocations */
> + if (filter->fields.modname[0] != '\0')
> + return false;
> + } else if (allocinfo_cmp_str(ct->modname, filter->fields.modname))
> + return false;
> + }
> +
> + if ((filter->mask & ALLOCINFO_FILTER_MASK_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))
> + return false;
> +
> + if ((filter->mask & ALLOCINFO_FILTER_MASK_LINENO) &&
> + ct->lineno != filter->fields.lineno)
> + return false;
> +
> + return true;
> +}
> +
> /*
> * Seeks the ioctl iterator to the specified 0-indexed tag position, reads its
> * profiling data and returns it to userspace.
> @@ -235,29 +276,46 @@ static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg)
> {
> struct allocinfo_private *priv;
> struct codetag *ct;
> - __u64 pos;
> struct allocinfo_get_at params = {0};
> + __u64 skip_count;
>
> if (copy_from_user(¶ms, arg, sizeof(params)))
> return -EFAULT;
>
> + if (params.filter.mask & ~ALLOCINFO_FILTER_MASKS)
> + return -EINVAL;
> +
> priv = m->private;
> - pos = params.pos;
>
> mutex_lock(&priv->ioctl_lock);
> codetag_lock_module_list(alloc_tag_cttype);
>
> - if (pos >= codetag_get_count(alloc_tag_cttype)) {
> + if (params.pos >= codetag_get_count(alloc_tag_cttype)) {
> codetag_unlock_module_list(alloc_tag_cttype);
> mutex_unlock(&priv->ioctl_lock);
> return -ENOENT;
> }
>
> + skip_count = params.pos;
> +
> + if (params.filter.mask)
> + priv->filter = params.filter;
> + else
> + priv->filter.mask = 0;
> +
> /* Find the codetag */
> priv->ioctl_iter = codetag_get_ct_iter(alloc_tag_cttype);
> ct = codetag_next_ct(&priv->ioctl_iter);
> - while (ct && pos--)
> +
> + while (ct) {
> + if (matches_filter(ct, &priv->filter)) {
> + if (skip_count == 0)
> + break;
> + skip_count--;
> + }
> ct = codetag_next_ct(&priv->ioctl_iter);
> + }
> +
> if (ct) {
> allocinfo_to_params(ct, ¶ms.data);
> priv->positioned = true;
> @@ -298,6 +356,8 @@ 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))
> + ct = codetag_next_ct(&priv->ioctl_iter);
> if (ct)
> allocinfo_to_params(ct, ¶ms);
>
> --
> 2.54.0.1136.gdb2ca164c4-goog
>