Re: [PATCH 2/6] alloc_tag: add ioctl filters to /proc/allocinfo
From: Abhishek Bapat
Date: Mon May 18 2026 - 19:54:07 EST
On Wed, May 13, 2026 at 11:15 PM Hao Ge <hao.ge@xxxxxxxxx> wrote:
>
> Hi Abhishek
>
> On 2026/5/5 07:36, Abhishek Bapat 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>
> > ---
> > include/uapi/linux/alloc_tag.h | 26 +++++++++++++++-
> > lib/alloc_tag.c | 55 ++++++++++++++++++++++++++++++++--
> > 2 files changed, 77 insertions(+), 4 deletions(-)
> >
> > diff --git a/include/uapi/linux/alloc_tag.h b/include/uapi/linux/alloc_tag.h
> > index e9a5b55fcc7a..0cc9db5298c6 100644
> > --- a/include/uapi/linux/alloc_tag.h
> > +++ b/include/uapi/linux/alloc_tag.h
> > @@ -34,8 +34,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 5c24d2f954d4..7ff936e15e97 100644
> > --- a/lib/alloc_tag.c
> > +++ b/lib/alloc_tag.c
> > @@ -47,6 +47,7 @@ int alloc_tag_ref_offs;
> > struct allocinfo_private {
> > struct codetag_iterator 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 */
> > @@ -156,6 +157,11 @@ static void allocinfo_copy_str(char *dest, const char *src)
> > strscpy(dest, allocinfo_str(src), ALLOCINFO_STR_SIZE);
> > }
> >
> > +static int allocinfo_cmp_str(const char *str, const char *template)
> > +{
> > + return strncmp(allocinfo_str(str), template, ALLOCINFO_STR_SIZE);
> > +}
> > +
> > static void allocinfo_to_params(struct codetag *ct,
> > struct allocinfo_tag_data *data)
> > {
> > @@ -187,26 +193,67 @@ static int allocinfo_ioctl_get_content_id(struct seq_file *m, void __user *arg)
> > return 0;
> > }
> >
> > +static bool matches_filter(struct codetag *ct, struct allocinfo_filter *filter)
> > +{
> > + if (!ct || !filter || !filter->mask)
> > + return true;
> > +
>
> Minor: in matches_filter(), returning true when ct is NULL seems
>
> semantically odd since both callers already check for ct != NULL
>
> before calling this function. Not a real issue though.
>
> > + if ((filter->mask & ALLOCINFO_FILTER_MASK_MODNAME) &&
> > + ct->modname && (allocinfo_cmp_str(ct->modname, filter->fields.modname)))
> > + return false;
> > +
>
> In matches_filter(), when ct->modname is NULL (built-in kernel code),
>
> the modname filter is skipped due to
>
> ct->modname && (allocinfo_cmp_str(...))
>
> This means built-in allocations always pass the modname filter. Since
>
> built-in code doesn't belong to any module, maybe it should not match
>
> when a modname filter is set:
>
> if (filter->mask & ALLOCINFO_FILTER_MASK_MODNAME) {
>
> if (!ct->modname)
>
> return false;
>
> if (allocinfo_cmp_str(ct->modname, filter->fields.modname))
>
> return false;
>
> }
>
> Thanks
>
> Best Regards
>
> Hao
>
Ack, will include in v2.
> > + 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;
> > +}
> > +
> > 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 = (struct allocinfo_private *)m->private;
> > - pos = params.pos;
> > +
> > + skip_count = params.pos;
> >
> > codetag_lock_module_list(alloc_tag_cttype, true);
> >
> > + 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;
> > @@ -240,6 +287,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);
> >