Re: [PATCH v2 2/6] alloc_tag: add ioctl filters to /proc/allocinfo
From: Abhishek Bapat
Date: Thu Jun 04 2026 - 19:56:07 EST
On Sun, May 24, 2026 at 8:00 PM Hao Ge <hao.ge@xxxxxxxxx> wrote:
>
> Hi Abhishek
>
>
> On 2026/5/23 01:45, 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 | 58 ++++++++++++++++++++++++++++++++--
> > 2 files changed, 80 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 3598735b6c93..56c394ef721f 100644
> > --- a/lib/alloc_tag.c
> > +++ b/lib/alloc_tag.c
> > @@ -48,6 +48,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 */
> > @@ -167,6 +168,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)
> > {
> > @@ -198,27 +204,71 @@ 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 (!filter || !filter->mask)
> > + return true;
> > +
> > + if (filter->mask & ALLOCINFO_FILTER_MASK_MODNAME) {
> > + if (!ct->modname)
> > + return false;
> > + if (allocinfo_cmp_str(ct->modname, filter->fields.modname))
> > + return false;
> > + }
> > +
>
> Apologies -- I previously suggested the "!ct->modname ->
>
> return false" approach, but I realized we broke the ability to
>
> filter for built-in allocations. allocinfo_to_params() returns
>
> modname="" for those, so a user would naturally try to filter
>
> by modname="" -- except it never matches because ct->modname is
>
> NULL, not an empty string.
>
> Maybe something like this instead?
>
> if (filter->mask & ALLOCINFO_FILTER_MASK_MODNAME) {
>
> if (ct->modname) {
>
> if (allocinfo_cmp_str(ct->modname, filter->fields.modname))
>
> return false;
>
> } else if (filter->fields.modname[0] != '\0') {
>
> return false;
>
> }
>
> }
>
> That way modname="" matches built-in tags, which lines up with
>
> what the API actually returns.
>
> Thanks
>
> Hao
>
Ah good catch, will use the following logic that's a bit cleaner:
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;
}
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;
> > +}
> > +
> > 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;
> >
> > mutex_lock(&priv->ioctl_lock);
> > 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;
> > @@ -254,6 +304,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);
> >