* Re: [PATCH v6 01/12] PCI: liveupdate: Set up FLB handler for the PCI core
From: Pratyush Yadav @ 2026-06-18 16:35 UTC (permalink / raw)
To: Pranjal Shrivastava
Cc: David Matlack, Pasha Tatashin, Mike Rapoport, kexec, linux-doc,
linux-kernel, linux-mm, linux-pci, Adithya Jayachandran,
Alexander Graf, Alex Williamson, Bjorn Helgaas, Chris Li,
David Rientjes, Jacob Pan, Jason Gunthorpe, Jonathan Corbet,
Josh Hilke, Leon Romanovsky, Lukas Wunner, Parav Pandit,
Pratyush Yadav, Saeed Mahameed, Samiullah Khawaja, Shuah Khan,
Vipin Sharma, William Tu, Yi Liu
In-Reply-To: <ajPzC2Xh1NMbfokP@google.com>
On Thu, Jun 18 2026, Pranjal Shrivastava wrote:
> On Mon, Jun 15, 2026 at 10:19:03PM +0000, David Matlack wrote:
>> On 2026-06-12 10:47 AM, Pasha Tatashin wrote:
>> > On 2026-06-12 09:54:44+03:00, Mike Rapoport wrote:
>> > > On Fri, Jun 12, 2026 at 05:15:02AM +0000, Pasha Tatashin wrote:
>> > >
>> > > > On Fri, 22 May 2026 20:23:59 +0000, David Matlack <dmatlack@google.com> wrote:
>> > > >
>> > > > Please add Pratyush, Mike, and myself so we are notified directly of
>> > > > incoming patches, the same as with other areas where the liveupdate/
>> > > > tree is specified.
>> > >
>> > > Or we can add PCI liveupdate files to LIVEUPDATE entry.
>> >
>> > That will not work, as we cannot serve as maintainers for
>> > PCI/VFIO/IOMMU/KVM, etc. David Matlack will be the maintainer for the
>> > PCI components, and we will accept patches once they have been approved
>> > by him.
>> >
>> > The simplification we could do is to create an email alias
>> > for the live-update tree maintainers. This would allow us to use a
>> > single entry instead of listing all three of us individually.
>>
>> We could create a Live Update mailing list for all code that can be CCed
>> on all patches that must be merged through the Live Update tree. I would
>> also be interested in subscribing to that list.
>
> +1. I'd like if there's a specific Live Update mailing list for
> submissions & discussion about the Live Update tree.
We treat kexec@lists.infradead.org as the "live update mailing list". We
considered getting a separate one, but I reckon the traffic is low
enough on kexec@ already that we can re-use it for live update.
So perhaps we just Cc kexec@? Is there anything to be gained by creating
an alias?
--
Regards,
Pratyush Yadav
^ permalink raw reply
* Re: [PATCH v5 3/6] alloc_tag: add size-based filtering to ioctl
From: Abhishek Bapat @ 2026-06-18 16:38 UTC (permalink / raw)
To: Suren Baghdasaryan
Cc: Andrew Morton, Kent Overstreet, Hao Ge, Shuah Khan,
Jonathan Corbet, linux-doc, linux-kernel, linux-mm, Sourav Panda
In-Reply-To: <CAJuCfpFrgKBGFWoca=XuKh1p22vdfE_uSz_nt2Kj4UvnjvSUJQ@mail.gmail.com>
On Wed, Jun 17, 2026 at 4:01 PM Suren Baghdasaryan <surenb@google.com> wrote:
>
> On Wed, Jun 17, 2026 at 3:41 PM Abhishek Bapat <abhishekbapat@google.com> wrote:
> >
> > On Wed, Jun 17, 2026 at 3:35 PM Suren Baghdasaryan <surenb@google.com> wrote:
> > >
> > > On Wed, Jun 17, 2026 at 1:55 PM Abhishek Bapat <abhishekbapat@google.com> wrote:
> > > >
> > > > On Wed, Jun 17, 2026 at 9:29 AM Suren Baghdasaryan <surenb@google.com> wrote:
> > > > >
> > > > > On Mon, Jun 15, 2026 at 4:04 PM Abhishek Bapat <abhishekbapat@google.com> 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@google.com>
> > > > > > Acked-by: Hao Ge <hao.ge@linux.dev>
> > > > > > ---
> > > > > > include/uapi/linux/alloc_tag.h | 8 ++++-
> > > > > > lib/alloc_tag.c | 63 ++++++++++++++++++++++++++++------
> > > > > > 2 files changed, 59 insertions(+), 12 deletions(-)
> > > > > >
> > > > > > diff --git a/include/uapi/linux/alloc_tag.h b/include/uapi/linux/alloc_tag.h
> > > > > > index 3b11877955b9..7f5acbb44c14 100644
> > > > > > --- a/include/uapi/linux/alloc_tag.h
> > > > > > +++ b/include/uapi/linux/alloc_tag.h
> > > > > > @@ -45,13 +45,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)
> > > > > > @@ -59,6 +63,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 5feb61d9fb92..b3d21834b61e 100644
> > > > > > --- a/lib/alloc_tag.c
> > > > > > +++ b/lib/alloc_tag.c
> > > > > > @@ -195,15 +195,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);
> > > > > > @@ -212,9 +223,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));
> > > > > > }
> > > > > >
> > > > > > /*
> > > > > > @@ -238,7 +249,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 its 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;
> > > > > > @@ -265,6 +278,19 @@ 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 | 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;
> > > > > > }
> > > > > >
> > > > > > @@ -278,6 +304,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;
> > > > > > @@ -285,6 +313,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);
> > > > > > @@ -308,7 +341,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)) {
> > > > >
> > > > > Do we really need this "fetched_counters" parameter? Here are the
> > > > > possible cases:
> > > > > 1. If the filter does not include ALLOCINFO_FILTER_MASK_MIN_SIZE |
> > > > > ALLOCINFO_FILTER_MASK_MAX_SIZE then counters would not be fetched.
> > > > > 2. If the filter includes ALLOCINFO_FILTER_MASK_MIN_SIZE |
> > > > > ALLOCINFO_FILTER_MASK_MAX_SIZE and
> > > > > 2.1. matches_filter() returns true then we know counters were fetched
> > > > > because they had to be validated.
> > > > > 2.2. matches_filter() returns false then we don't care if the counters
> > > > > were fetched. We do not report that tag anyway.
> > > > >
> > > > > So, instead of passing fetched_counters to matches_filter() we could do this:
> > > > >
> > > > > bool filter_by_size = (params.filter.mask &
> > > > > (ALLOCINFO_FILTER_MASK_MIN_SIZE | ALLOCINFO_FILTER_MASK_MAX_SIZE)) !=
> > > > > 0;
> > > > > while (ct) {
> > > > > if (matches_filter(ct, &priv->filter, &counters)) {
> > > > > ...
> > > > > }
> > > > > if (ct) {
> > > > > allocinfo_to_params(ct, ¶ms.data, filter_by_size ?
> > > > > &counters : NULL);
> > > > > ...
> > > > > }
> > > > >
> > > > > Wouldn't that work?
> > > > >
> > > >
> > > > While we can deduce whether counters were fetched outside the
> > > > matches_filter function, I think the current implementation is more
> > > > intuitive from a readability perspective. I believe it should be kept
> > > > as is for that reason. If we extract the logic, we'll first have to
> > > > replicate the boolean logic at two places. Second, we'd need to add a
> > > > comment explaining the boolean calculation, and the reader might have
> > > > a higher cognitive load trying to determine which function populates
> > > > the counters. The current implementation makes it easy for the reader
> > > > to deduce the original intention. Let me know what you think.
> > >
> > > Ok, I guess you have a point.
> > >
> > > I was also thinking why we are passing NULL to allocinfo_to_params()
> > > to fetch the counters into a local variable? Why can't we simply call
> > > allocinfo_prefetch_counters() before calling allocinfo_to_params()
> > > when fetched_counters==false? Basically:
> > >
> > > if (!fetched_counters)
> > > counters = allocinfo_prefetch_counters(ct);
> > > allocinfo_to_params(ct, ¶ms.data, &counters);
> > >
> > > This would simplify allocinfo_to_params() because counter will never
> > > be NULL and it would not need local counters.
> > >
> >
> > The only reason I did it that way was to avoid repeating the code at
> > two places i.e. allocinfo_ioctl_get_at and allocinfo_ioctl_get_next.
> > Either way, the per-CPU counters are assimilated only once. I can
> > include this change if you still want me to, but personally I like the
> > way it currently is implemented.
>
> Yeah, I think repeating 2 lines is preferable to passing NULL and
> fetching into a local variable. Please include that change.
>
Ack, I will change this in the next patchset version.
> >
> > > >
> > > > > > if (skip_count == 0)
> > > > > > break;
> > > > > > skip_count--;
> > > > > > @@ -317,7 +351,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;
> > > > > > }
> > > > > >
> > > > > > @@ -343,6 +377,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;
> > > > > > @@ -356,10 +392,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;
> > > > > > --
> > > > > > 2.54.0.1136.gdb2ca164c4-goog
> > > > > >
^ permalink raw reply
* Re: [PATCH v6 00/10] ACPI: APEI: share GHES CPER helpers and add DT FFH provider
From: Borislav Petkov @ 2026-06-18 16:48 UTC (permalink / raw)
To: Ahmed Tiba
Cc: Rafael J. Wysocki, Tony Luck, Hanjun Guo, Mauro Carvalho Chehab,
Shuai Xue, Len Brown, Saket Dumbre, Davidlohr Bueso,
Jonathan Cameron, Dave Jiang, Alison Schofield, Vishal Verma,
Ira Weiny, Dan Williams, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jonathan Corbet, Shuah Khan, linux-kernel,
linux-acpi, acpica-devel, linux-cxl, devicetree, linux-edac,
linux-doc, Dmitry.Lamerov
In-Reply-To: <20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-0-91f725174aa0@arm.com>
On Wed, Jun 17, 2026 at 02:54:38PM +0100, Ahmed Tiba wrote:
> This is v6 of the GHES refactor series. Compared to v5, it addresses
> the latest review comments and tightens the DT CPER provider and
> related helper wiring.
Sashiko has comments:
https://sashiko.dev/#/patchset/20260617-topics-ahmtib01-ras_ffh_arm_internal_review-v6-0-91f725174aa0%40arm.com
--
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquette
^ permalink raw reply
* Re: [PATCH] kselftest docs: remove reference to obsolete/archived wiki
From: Shuah Khan @ 2026-06-18 17:02 UTC (permalink / raw)
To: Brett Sheffield, Rafael Passos, shuah, corbet
Cc: linux-kselftest, workflows, linux-doc, linux-kernel, Shuah Khan
In-Reply-To: <ajOvQKne74gN-7Y2@karahi.librecast.net>
On 6/18/26 02:41, Brett Sheffield wrote:
>> On 6/17/26 19:03, Shuah Khan wrote:
>>> On 6/17/26 17:57, Rafael Passos wrote:
>>>> This link in the docs point to a wiki that is no longer active.
>>>>
>>>> The wiki was moved to archive.kernel.org, and there is a warning:
>>>> "OBSOLETE CONTENT This wiki has been archived and the content is
>>>> no longer updated."
>>>>
>>>> Signed-off-by: Rafael Passos <rafael@rcpassos.me>
>>>> ---
>>>>
>>>> Documentation/dev-tools/kselftest.rst | 5 -----
>>>> 1 file changed, 5 deletions(-)
>>>>
>>>> diff --git a/Documentation/dev-tools/kselftest.rst b/Documentation/dev-tools/kselftest.rst
>>>> index d7bfe320338c..64c0ec7428a2 100644
>>>> --- a/Documentation/dev-tools/kselftest.rst
>>>> +++ b/Documentation/dev-tools/kselftest.rst
>>>> @@ -15,11 +15,6 @@ able to run that test on an older kernel. Hence, it is important to keep
>>>> code that can still test an older kernel and make sure it skips the test
>>>> gracefully on newer releases.
>>>> -You can find additional information on Kselftest framework, how to
>>>> -write new tests using the framework on Kselftest wiki:
>>>> -
>>>> -https://kselftest.wiki.kernel.org/
>>>> -
>>>> On some systems, hot-plug tests could hang forever waiting for cpu and
>>>> memory to be ready to be offlined. A special hot-plug target is created
>>>> to run the full range of hot-plug tests. In default mode, hot-plug tests run
>>>
>>>
>>> Looks good to me.
>>>
>>> Reviewed-by: Shuah Khan <skhan@linuxfoundation.org>
>>
>> Jon,
>>
>> I can take this through kselftest tree as I usually do.
>>
>> thanks,
>> -- Shuah
>
> Hi Shuah, Jon et al,
>
> I've been trying to get the same change merged since August 2025:
>
> https://lore.kernel.org/linux-doc/20250824075007.13901-2-bacs@librecast.net/
>
> resent in January:
>
> https://lore.kernel.org/linux-doc/20260115172817.7120-1-bacs@librecast.net/
>
> It's great that this trivial fix is finally getting merged, but can someone
> explain why this patch was accepted in preference to the one I sent in August?
>
Brett,
My apologies for not taking your patch earlier. Considering the effort
you put in with a re-sending the patch and following up here, it is
only fair for me to take yours instead. Hope it will apply cleanly on
top of kselftest-next
Rafael, I am going to take Brett;s patch instead of yours.
Apologies to both of you for the mix up.
thanks,
-- Shuah
^ permalink raw reply
* Re: [PATCH v3 06/12] fs/resctrl: Initialize the global kernel-mode policy at subsystem init
From: Babu Moger @ 2026-06-18 17:14 UTC (permalink / raw)
To: Reinette Chatre, corbet, tony.luck, Dave.Martin, james.morse,
tglx, bp, dave.hansen
Cc: skhan, x86, mingo, hpa, akpm, rdunlap, pawan.kumar.gupta,
feng.tang, dapeng1.mi, kees, elver, lirongqing, paulmck, bhelgaas,
seanjc, alexandre.chartre, yazen.ghannam, peterz, chang.seok.bae,
kim.phillips, xin, naveen, thomas.lendacky, linux-doc,
linux-kernel, eranian, peternewman, sos-linux-ext-patches
In-Reply-To: <ffa4f5c5-9512-41fc-9354-803a182a85cd@intel.com>
Hi Reinette,
On 6/16/26 18:36, Reinette Chatre wrote:
> Hi Babu,
>
> On 4/30/26 4:24 PM, Babu Moger wrote:
>> kernel_mode feature needs to add the interface that lets user space
>> choose between INHERIT_CTRL_AND_MON, GLOBAL_ASSIGN_CTRL_INHERIT_MON_PER_CPU
>> and GLOBAL_ASSIGN_CTRL_ASSIGN_MON_PER_CPU. Both the generic resctrl
>> code and the architecture layer need a single shared snapshot of the
>> supported and effective policy plus the resource group that backs the
>> global-assign modes; that snapshot is struct resctrl_kmode_cfg.
>
> This does not seem to match implementation since this implementation does
> not actually share struct resctrl_kmode_cfg as described above. Only
> resctrl_arch_get_kmode_support() exchanges this struct between fs and
> arch and as already mentioned that usage looks unnecessary. The other
> arch/fs touch points use either individual members or their properties
> (like closid/rmid).
>
> As described in response to previous patch I think this can be simplified
> while also making it more robust.
>
Ack.
>>
>> Add the file-local resctrl_kcfg and a helper resctrl_kmode_init() that:
>>
>> - Adds kmode and kmode_cur with BIT(INHERIT_CTRL_AND_MON), the
>> universally supported mode and today's behaviour;
>> - points k_rdtgrp at rdtgroup_default so global-assign modes have a
>> valid backing group from boot;
>
> If the default mode is INHERIT_CTRL_AND_MON then should the default group
> not be NULL?
It will be initialized to NULL.
>
>> - calls resctrl_arch_get_kmode_support() so each architecture ORs
>> BIT(<mode>) into kmode for the policies its hardware supports
>> (on x86, AMD PLZA contributes the two global-assign modes).
>>
>> resctrl_kmode_init() runs from resctrl_init() once the default group
>
> resctrl_kmode_init() can be dropped after changes described in response
> to previous patch. Apart from no longer being necessary I also find that
> having the kernel mode fully initialized *before* the hotplug handlers run
> to be simpler.
That means resctrl_set_kmode_support() will be called from the
architecture layer, likely from core.c within get_rdt_alloc_resources().
The resctrl_set_kmode_support() handler would need to initialize both
the default mode and all supported modes.
I see that this is where the hotplug handler gets registered. Therefore,
the modes are already initialized before the hotplug handler is set up.
>
>> has been set up. No user-visible behaviour changes yet; later patches
>
> (drop "later patches ...")
>
Sure.
Thanks
Babi
^ permalink raw reply
* Re: [PATCH v4 00/31] Introduce SCMI Telemetry FS support
From: David Hildenbrand (Arm) @ 2026-06-18 17:22 UTC (permalink / raw)
To: Cristian Marussi, Christian Brauner
Cc: linux-kernel, linux-arm-kernel, arm-scmi, linux-fsdevel,
linux-doc, sudeep.holla, james.quinlan, f.fainelli,
vincent.guittot, etienne.carriere, peng.fan, michal.simek, d-gole,
jic23, elif.topuz, lukasz.luba, philip.radford,
souvik.chakravarty, leitao, kas, puranjay, usama.arif,
kernel-team
In-Reply-To: <ajLVW1eHzbGDm4yn@pluto>
Hi,
asking some clarifying questions that I assume also Christian might want to know.
>>> In a nutshell, the SCMI Telemetry protocol allows an agent to discover at
>>> runtime the set of Telemetry Data Events (DEs) available on a specific
>>> platform and provides the means to configure the set of DEs that a user is
Is the configuration aspect limited to enabling selected events, or is there
more that can be configured?
>>> interested into, while reading them back using the collection method that
>>> is deeemed more suitable for the usecase at hand. (...amongst the various
>>> possible collection methods allowed by SCMI specification)
>>>
>>> Without delving into the gory details of the whole SCMI Telemetry protocol
>>> let's just say that the SCMI platform/server firmware advertises a number
>>> of Telemetry Data Events, each one identified by a 32bit unique ID, and an
>>> SCMI agent/client, like Linux, can discover them and read back at will the
>>> associated data value in a number of ways.
>>> Data collection is mainly intended to happen on demand via shared memory
>>> areas exposed by the platform firmware, discovered dynamically via SCMI
>>> Telemetry and accessed by Linux on-demand, but some DE can also be reported
>>> via SCMI Notifications asynchronous messages or via direct dedicated
>>> FastChannels (another kind of SCMI memory based access): all of this
>>> underlying mechanism is anyway hidden to the user since it is mediated by
>>> the kernel driver which will return the proper data value when queried.
>>>
>>> Anyway, the set of well-known architected DE IDs defined by the spec is
>>> limited to a dozen IDs, which means that the vast majority of DE IDs are
>>> customizable per-platform: as a consequence, though, the same ID, say
>>> '0x1234', could represent completely different things on different systems.
>>>
>>> Precise definitions and semantic of such custom Data Event IDs are out of
>>> the scope of the SCMI Telemetry specification and of this implementation:
>>> they are supposed to be provided using some kind of JSON-like description
>>> file that will have to be consumed by a userspace tool which would be
>>> finally in charge of making sense of the set of available DEs.
You mention json here ... but I assume the data we are getting fed by the
protocol is not in some default format? (e.g., json)
>>>
>>> IOW, in turn, this means that even though the DEs enumerated via SCMI come
>>> with some sort of topological and qualitative description provided by the
>>> protocol (like unit of measurements, name, topology info etc), kernel-wise
>>> we CANNOT be completely sure of "what is what" without being fed-back some
>>> sort of information about the DEs by the afore mentioned userspace tool.
Maybe you have it in some of the patches here, but what does the typical
directory + file structure look like in the current implementation?
Do you have an example?
Also, is everything in that filesystem read-only, or are there some writable
file (IOW, how is stuff configured?).
>>>
>>> For these reasons, currently this series does NOT attempt to register any
>>> of these DEs with any of the usual in-kernel subsystems (like HWMON, IIO,
>>> PERF etc), simply because we cannot be sure which DE is suitable, or even
>>> desirable, for a given subsystem. This also means there are NO in-kernel
>>> users of these Telemetry data events as of now.
Okay, so you really only feed this data to user space, exposing all the data you
have easily available as part of the protocol.
>>>
>>> So, while we do not exclude, for the future, to feed/register some of the
>>> discovered DEs to/with some of the above mentioned Kernel subsystems, as
>>> of now we have ONLY modeled a custom userspace API to make SCMI Telemetry
>>> available to userspace tools.
It's a good question how that could be done, if you need more information about
these events from user space.
>>>
>>> In deciding which kind of interface to expose SCMI Telemetry data to a
>>> user, this new SCMI Telemetry driver aims at satisfying 2 main reqs:
>>>
>>> - exposing an FS-based human-readable interface that can be used to
>>> discover, configure and access our Telemetry data directly also from
>>> the shell without special tools
>>>
>>> - exposing alternative machine-friendly, more-performant, binary
>>> interfaces that can be used to avoid the overhead of multiple accesses
>>> to the VFS and that can be more suitable to access with custom tools
[...]
>>>
>>> Due to the above reasoning, since V1 we opted for a new approach with the
>>> proposed interfaces now based on a full fledged, unified, virtual pseudo
>>> filesystem implemented from scratch, so that we can:
>>>
>>> - expose all the DEs property we like as before with SysFS, but without
>>> any of the constraint imposed by the usage of SysFs or kernfs.
>>>
>>> - easily expose additional alternative views of the same set of DEs
>>> using symlinking capabilities (e.g. alternative topological view)
That sounds reasonable.
[...]
> ...I would not say that this was the kind of feedback I was hoping for,
> but I am NOT gonna argue, given that you shot down already what I thought
> were all my best selling points :P
>
> At this point my understanding is that the way forward must be to use
> a custom tool to configure/extract/translate the raw Telemetry data and
> move up into userspace the whole human readable FS layer via FUSE, if
> really needed.
>
> I suppose that the new kernel/user interface has to be some dedicated char
> device implementing proper fops. (like I did previously in early versions
> of this series and then abandoned...)
>
> Is this you have in mind ? Dedicated character device(s) with enough fops
> to be able to configure/extract Telemetry data with a custom tool ?
I cannot speak for Christian, but I guess you could have some kind of libscmi in
user space that can obtain the information (as you say, probably char device,
not sure which alternatives we have), to expose the data through a nice ABI, to
then either make tools build upon that directly, or have a fuse server in user
space that mimics what you currently do with the file system.
One thing that is not clear to me yet is how stuff would be configured, and how
possibly multiple users of libscmi would possibly interact.
>
> Should/could such a tool live in the kernel tree (tools/) at least for
> ease of development/deployment ?
I think OOT.
--
Cheers,
David
^ permalink raw reply
* Re: [PATCH v4 00/31] Introduce SCMI Telemetry FS support
From: David Hildenbrand (Arm) @ 2026-06-18 17:27 UTC (permalink / raw)
To: Cristian Marussi, Christian Brauner
Cc: linux-kernel, linux-arm-kernel, arm-scmi, linux-fsdevel,
linux-doc, sudeep.holla, james.quinlan, f.fainelli,
vincent.guittot, etienne.carriere, peng.fan, michal.simek, d-gole,
jic23, elif.topuz, lukasz.luba, philip.radford,
souvik.chakravarty, leitao, kas, puranjay, usama.arif,
kernel-team
In-Reply-To: <29a304f0-1e62-418a-b84f-aabdc4c0de8d@kernel.org>
> Maybe you have it in some of the patches here, but what does the typical
> directory + file structure look like in the current implementation?
>
> Do you have an example?
Found it in patch #20! :)
--
Cheers,
David
^ permalink raw reply
* Re: [PATCH v3 06/13] tick/nohz, context_tracking: Prepare for runtime nohz_full updates
From: Thomas Gleixner @ 2026-06-18 17:27 UTC (permalink / raw)
To: Jing Wu, Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
Valentin Schneider, Paul E. McKenney, Frederic Weisbecker,
Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
Uladzislau Rezki, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
Anna-Maria Behnsen, Tejun Heo, Jonathan Corbet, Shuah Khan,
Shuah Khan
Cc: linux-kernel, rcu, cgroups, linux-doc, linux-kselftest, Jing Wu,
Qiliang Yuan
In-Reply-To: <20260618-wujing-dhm-v3-6-28f1a4d83b68@gmail.com>
On Thu, Jun 18 2026 at 11:11, Jing Wu wrote:
> Remove __init from ct_cpu_track_user() and __initdata from the
> initialized flag so context tracking can be activated on CPUs that
> join nohz_full at runtime. Drop the __ro_after_init attribute from
> the context_tracking_key static key, allowing static_branch_dec()
> when a CPU leaves nohz_full.
>
> Add ct_cpu_untrack_user() to reverse ct_cpu_track_user(), decrementing
> the static key and clearing the per-CPU tracking state.
Please do not enumerate WHAT the patch is doing. Explain the context and
the WHY
https://docs.kernel.org/process/maintainer-tip.html#changelog
>
> #include <asm/irq_regs.h>
> @@ -653,11 +654,6 @@ void __init tick_nohz_init(void)
> if (!tick_nohz_full_running)
> return;
>
> - /*
> - * Full dynticks uses IRQ work to drive the tick rescheduling on safe
> - * locking contexts. But then we need IRQ work to raise its own
> - * interrupts to avoid circular dependency on the tick.
> - */
This comment is removed because it's not longer correct? How is this
related to $Subject?
> if (!arch_irq_work_has_interrupt()) {
> pr_warn("NO_HZ: Can't run full dynticks because arch doesn't support IRQ work self-IPIs\n");
> cpumask_clear(tick_nohz_full_mask);
> @@ -676,6 +672,16 @@ void __init tick_nohz_init(void)
> }
> }
>
> + /*
> + * Pre-initialize context tracking for all possible CPUs so
> + * ctx tracking is already active when a CPU is later added to
> + * nohz_full at runtime. The tracking overhead is negligible
> + * because the static key is not incremented yet — only per-CPU
> + * tracking state is set up.
> + */
> + if (IS_ENABLED(CONFIG_CONTEXT_TRACKING_USER_FORCE))
> + context_tracking_init();
Seriously? Care to look where and when context_tracking_init() is invoked?
> for_each_cpu(cpu, tick_nohz_full_mask)
> ct_cpu_track_user(cpu);
>
> @@ -686,6 +692,147 @@ void __init tick_nohz_init(void)
> pr_info("NO_HZ: Full dynticks CPUs: %*pbl.\n",
> cpumask_pr_args(tick_nohz_full_mask));
> }
> +
> +static int tick_nohz_hk_validate(enum hk_type type,
> + const struct cpumask *cur_mask,
> + const struct cpumask *new_mask)
> +{
> + if (!IS_ENABLED(CONFIG_NO_HZ_FULL))
> + return -EOPNOTSUPP;
> + return 0;
> +}
Why is this code even compiled when CONFIG_NO_HZ_FULL is not enabled?
> +
> +static void tick_nohz_hk_apply(enum hk_type type)
> +{
> + static DEFINE_SPINLOCK(tick_nohz_lock);
> + cpumask_var_t nohz_full, added, removed;
> + bool was_running;
> + int cpu;
> +
> + if (!alloc_cpumask_var(&nohz_full, GFP_KERNEL))
> + return;
This looks more than wrong. If this fails then the core code will
happily proceed with the completely wrong state.
> + if (!alloc_cpumask_var(&added, GFP_KERNEL)) {
> + free_cpumask_var(nohz_full);
> + return;
> + }
> + if (!alloc_cpumask_var(&removed, GFP_KERNEL)) {
> + free_cpumask_var(added);
> + free_cpumask_var(nohz_full);
> + return;
> + }
cpumask_var_t __free(free_cpumask_var) a = CPUMASK_VAR_NULL;
cpumask_var_t __free(free_cpumask_var) b = CPUMASK_VAR_NULL;
cpumask_var_t __free(free_cpumask_var) c = CPUMASK_VAR_NULL;
if (!alloc_cpumask_var(&a, GFP_KERNEL))
return -ENOMEM;
....
> +
> + /*
> + * Snapshot the new HK_TYPE_KERNEL_NOISE mask under an RCU read lock.
> + * housekeeping_update_types() completes synchronize_rcu() before
> + * invoking apply(), so the new pointer is stable; however the lockdep
> + * annotation in housekeeping_cpumask() still requires an RCU read-side
> + * critical section for runtime-mutable types.
This comment is explaining the obvious: housekeeping_cpumask_rcu()
> + */
> + rcu_read_lock();
scoped_guard(rcu)
> + cpumask_andnot(nohz_full, cpu_possible_mask,
> + housekeeping_cpumask_rcu(HK_TYPE_KERNEL_NOISE));
> + rcu_read_unlock();
> +
> + /*
> + * When "nohz_full=" was not passed at boot, tick_nohz_full_running is
> + * false and the full dynticks infrastructure (sched_tick_offload_init,
> + * RCU nohz quiescent-state reporting, context-tracking bootstrap) was
> + * never initialised. In that case restrict the update to
> + * tick_nohz_full_mask so the /sys/devices/system/cpu/nohz_full sysfs
> + * attribute reflects DHM-isolated CPUs without enabling tick
> + * suppression, context tracking, or timer migration – all of which
> + * require boot-time setup and would deadlock on the first
> + * synchronize_rcu() call after CPUs are offlined.
What? You tell user space that the CPUs are nohz_full by updating the
mask, which is exposed in sysfs, which is blatantly wrong.
> + */
> + was_running = READ_ONCE(tick_nohz_full_running);
Q: This READ_ONCE() pairs with which WRITE_ONCE()?
A: With none, so it's just voodoo programming.
> + spin_lock(&tick_nohz_lock);
This lock protects against the housekeeping core code invoking the apply
callback multiple times in parallel, right?
If that happens then there are bigger problems than corrupted masks.
> + /*
> + * When nohz_full= was active at boot, compute the delta and update
> + * context tracking for CPUs joining or leaving the nohz_full set.
> + * Skip when !was_running: ct_cpu_track_user() calls
> + * static_branch_inc() which may sleep (jump_label_update on the
> + * 0→1 transition) – illegal inside a spinlock.
If you remove the pointless voodoo lock then this nonsense goes away too.
> + */
> + if (IS_ENABLED(CONFIG_CONTEXT_TRACKING_USER) &&
> + was_running &&
> + cpumask_available(tick_nohz_full_mask)) {
Why is this stuff even invoked when the mask is not available? If it's
not there then NOHZ full is not functional, period.
> + cpumask_andnot(added, nohz_full, tick_nohz_full_mask);
> + cpumask_andnot(removed, tick_nohz_full_mask, nohz_full);
> + for_each_cpu(cpu, added)
> + ct_cpu_track_user(cpu);
> + for_each_cpu(cpu, removed)
> + ct_cpu_untrack_user(cpu);
> + }
> +
> + /*
> + * Update tick_nohz_full_mask unconditionally: this is the snapshot
> + * read by the /sys/devices/system/cpu/nohz_full sysfs attribute and
> + * must reflect the current isolation set even in the DHM runtime case.
> + */
> + if (cpumask_available(tick_nohz_full_mask))
> + cpumask_copy(tick_nohz_full_mask, nohz_full);
Seriously?
> + /*
> + * Only modify tick_nohz_full_running and migrate the global tick when
> + * nohz_full= was set at boot; without boot-time setup, setting
> + * tick_nohz_full_running would suppress ticks on isolated CPUs and
> + * prevent RCU quiescent-state reporting, causing synchronize_rcu()
> + * to stall permanently when a CPU is subsequently offlined.
> + */
> + if (was_running) {
Again, why is any of this invoked when NOHZ full was never enabled and
initialized?
> + tick_nohz_full_running = !cpumask_empty(nohz_full);
Brilliant. When NOHZ full was enabled on the command line, then changing
the mask can disable "running" and that makes it disabled forever. There
is no way to reenable it.
This 'was_running' check is just wrong. What you need is a
'tick_nohz_full_initialized' boolean, which is only true when nohz_full
was setup early on including the mask.
If that's not the case, then none of this code is supposed to run
ever. I.e. the callback is not installed in the first place.
> + /*
> + * Ensure tick_nohz_full_mask is allocated so that tick_nohz_hk_apply()
> + * can update it (and the /sys/devices/system/cpu/nohz_full sysfs
> + * attribute) when CPUs are isolated at runtime via DHM. If "nohz_full="
> + * was passed at boot the mask is already allocated; allocate an empty
> + * one here for the runtime-only case.
What's the runtime only case? The fake exposure in sysfs which is just
misleading the user? Not going to happen. If it's not enabled on the
command line then it's disabled, end of story.
> + */
> + if (!cpumask_available(tick_nohz_full_mask) &&
> + !zalloc_cpumask_var(&tick_nohz_full_mask, GFP_KERNEL))
> + pr_warn("tick/nohz: failed to allocate nohz_full_mask for DHM\n");
ROTFL. If the allocation fails, then the apply callback becomes a
complete noop doing magic cpumask operations for nothing and pretending
to be successful.
Thanks,
tglx
^ permalink raw reply
* [PATCH v6 0/6] alloc_tag: introduce IOCTL-based filtering for MAP
From: Abhishek Bapat @ 2026-06-18 17:36 UTC (permalink / raw)
To: Suren Baghdasaryan, Andrew Morton, Kent Overstreet, Hao Ge
Cc: Shuah Khan, Jonathan Corbet, linux-doc, linux-kernel, linux-mm,
Sourav Panda, Abhishek Bapat
Currently, memory allocation profiling data is primarily exposed through
/proc/allocinfo. While useful for manual inspection, this text-based
interface poses challenges for production monitoring and large-scale
analysis:
1. Userspace must parse large amounts of text to extract specific
fields.
2. To find specific tags, userspace must read the entire dataset,
requiring many context switches and high data copying.
3. The kernel currently aggregates per-CPU counters for every allocation
size, even those the user intends to filter out immediately.
This series introduces a new IOCTL-based binary interface for allocinfo
that supports kernel-side filtering. By allowing the user to specify a
filter mask, we significantly reduce the work performed in-kernel and
the amount of data transferred to userspace. The IOCTL mechanism was
chosen for allocinfo to address the per-CPU counter aggregation
bottleneck. A traditional read() operation must report the total
allocation count and sizes for every code tag in the system. Doing so
requires iterating across all CPUs to sum their per-CPU counters for
thousands of tags, which introduces substantial runtime overhead.
The IOCTL interface allows userspace to push selective filtering
criteria directly into the kernel before the per-CPU counter
aggregation. The kernel aggregates per-CPU counters only for a small
subset of tags that match the filter. This results in significant
performance improvement.
Beyond fast filtered retrieval, the IOCTL foundation allows introducing
a context capture mechanism in the future to capture the context for
specific allocations.
Performance measurements were conducted on an Intel Xeon Platinum 8481C
(224 CPUs) with caches dropped before each run.
The IOCTL mechanism shows a ~20x performance improvement for
filtered queries. The kernel avoids the expensive per-CPU counter
aggregation (alloc_tag_read) for any tags that fail the initial string
or location filters.
Scenario 1: Specific File Filtering (arch/x86/events/rapl.c)
1. Traditional (cat /proc/allocinfo | grep): 22ms (sys)
2. IOCTL Interface: 1ms (sys)
Scenario 2: Compound Filtering (Filename + Size)
1. Traditional: (cat ... | grep | awk): 21ms (sys)
2. IOCTL Interface: 1ms (sys)
Scenario 3: Size-Based Filtering (min_size = 1MB)
1. Traditional: (cat ... | awk): 21ms (sys)
2. IOCTL Interface: 14ms (sys)
v6 changes:
- Patch 1/6: Added comments explaining why last 64 characters are
compared in the filter.
- Patch 3/6: Moved allocinfo_prefetch_counters outside of
allocinfo_to_params
- Patch 5/6: Fixed fd leak in get_filtered_ioctl_entries() function.
Added alloc_tag selftest to the top-level Makefile.
- Patch 6/6: Moved include for errno.h to this patch.
v5 changes:
- Patch 1/6: Added explicit mutex_destroy.
- Patch 5/6: Self-contained file descriptors to avoid wrap-around errors
in retry loops.
- Patch 6/6: Fixed minor issues raised by sashiko in v4.
v4 changes:
- Patch 1/6: Fixed a copyright comment inside
include/uapi/linux/alloc_tag.h
- Patch 3/6: Among other nits, fixed the inadvertent build failure
introduced in v3.
- Patch 4/6: Included a comment stating that the accurate field in
struct allocinfo_tag is only used for filtering.
- Patch 5/6: Modified test to trim prefix and keep suffix for entries
with filenames exceeding the size limit.
- Patch 6/6: Modified test_size_filter such that if content_id changes
between the moment when procfs and ioctl entries are read, both
entries are invalidated and re-fetched. Removed the tags->count == 0
check from test_lineno_filter as it's virtually unreachable.
v3 changes:
- Patch 1/6: Modified Documentation to indicate that map supports
ioctl(). Modified struct allocinfo_count to use
__attribute__((aligned(8))) instead of manual padding. Removed
redundance type-casting. Added comments for static functions in
lib/alloc_tag.c. Introduced a new seq counter for content_id that gets
bumped every time module is loaded / unloaded. Introduced logic to
validate user specified position is not greater than number of
allocation tags and return early if it is. Changed strscpy to
strscpy_pad to not echo arbitrary user data back to the user.
- Patch 2/6: Handled the case where user wants to specifically filter
for built-in modules. Included some comments for static functions.
- Patch 3/6: Modified logic to only fetch per-CPU counters for codetags
that satisfy other filters. Included some comments for static
functions.
v2 changes:
- Patch 1/6: Introduced locking for m->private. Also included the new uapi
header file in MAINTAINERS list.
- Patch 2/6: Handled the case where ALLOCINFO_FILTER_MASK_MODNAME is
passed but ct->modname is NULL.
- Patch 3/6: Moved min_size and max_size outside of struct allocinfo_tag
into struct allocinfo_filter. Added validation that min_size <=
max_size. Prefetched alloc_tag_counters if size based filter masks are
provided to avoid assimilating per-cpu counters twice.
- Patch 5/6: Removed the hardcoded logic to skip the header, instead the
test will skip lines that don't match the format. Also included the
newly added alloc_tag selftests directory in MAINTAINERS list.
Abhishek Bapat (5):
alloc_tag: add ioctl filters to /proc/allocinfo
alloc_tag: add size-based filtering to ioctl
alloc_tag: add accuracy based filtering to ioctl
kselftest: alloc_tag: add kselftest for ioctl interface
kselftest: alloc_tag: extend the allocinfo ioctl kselftest
Suren Baghdasaryan (1):
alloc_tag: add ioctl to /proc/allocinfo
Documentation/mm/allocation-profiling.rst | 5 +
.../userspace-api/ioctl/ioctl-number.rst | 2 +
MAINTAINERS | 2 +
include/linux/codetag.h | 2 +
include/uapi/linux/alloc_tag.h | 99 ++++
lib/alloc_tag.c | 344 +++++++++++-
lib/codetag.c | 18 +
tools/testing/selftests/Makefile | 1 +
tools/testing/selftests/alloc_tag/Makefile | 9 +
.../alloc_tag/allocinfo_ioctl_test.c | 531 ++++++++++++++++++
10 files changed, 1011 insertions(+), 2 deletions(-)
create mode 100644 include/uapi/linux/alloc_tag.h
create mode 100644 tools/testing/selftests/alloc_tag/Makefile
create mode 100644 tools/testing/selftests/alloc_tag/allocinfo_ioctl_test.c
--
2.55.0.rc0.786.g65d90a0328-goog
^ permalink raw reply
* [PATCH v6 1/6] alloc_tag: add ioctl to /proc/allocinfo
From: Abhishek Bapat @ 2026-06-18 17:36 UTC (permalink / raw)
To: Suren Baghdasaryan, Andrew Morton, Kent Overstreet, Hao Ge
Cc: Shuah Khan, Jonathan Corbet, linux-doc, linux-kernel, linux-mm,
Sourav Panda, Abhishek Bapat
In-Reply-To: <cover.1781803482.git.abhishekbapat@google.com>
From: Suren Baghdasaryan <surenb@google.com>
Add the following ioctl commands for /proc/allocinfo file:
ALLOCINFO_IOC_CONTENT_ID - gets content identifier which can be used
to check whether the file content has changed specifically due to module
load/unload. Every time a module is loaded / unloaded, the returned
value will be different. By comparing the identifier value at the
beginning and at the end of the content retrieval operation, users can
validate retrieved information for consistency.
ALLOCINFO_IOC_GET_AT - gets the record at the specified position. This
is the position of a record in /proc/allocinfo.
ALLOCINFO_IOC_GET_NEXT - gets the record next to the last retrieved
one. If no records were previously retrieved, returns the first
record.
Note, function file and module names often have the same prefixes,
therefore when filtering for them, we compare the last 64 characters to
minimize the chances of name collisions.
Signed-off-by: Suren Baghdasaryan <surenb@google.com>
Signed-off-by: Abhishek Bapat <abhishekbapat@google.com>
Acked-by: Hao Ge <hao.ge@linux.dev>
---
Documentation/mm/allocation-profiling.rst | 5 +
.../userspace-api/ioctl/ioctl-number.rst | 2 +
MAINTAINERS | 1 +
include/linux/codetag.h | 2 +
include/uapi/linux/alloc_tag.h | 65 +++++
lib/alloc_tag.c | 238 +++++++++++++++++-
lib/codetag.c | 18 ++
7 files changed, 329 insertions(+), 2 deletions(-)
create mode 100644 include/uapi/linux/alloc_tag.h
diff --git a/Documentation/mm/allocation-profiling.rst b/Documentation/mm/allocation-profiling.rst
index 5389d241176a..c3a28467955f 100644
--- a/Documentation/mm/allocation-profiling.rst
+++ b/Documentation/mm/allocation-profiling.rst
@@ -46,6 +46,11 @@ sysctl:
Runtime info:
/proc/allocinfo
+ Profiling data can be retrieved either by reading `/proc/allocinfo` directly as
+ text or programmatically via `ioctl()` calls defined in `<uapi/linux/alloc_tag.h>`.
+ The ioctl interface supports structured binary data extraction as well as filtering
+ by module name, function, file, line number, accuracy, or allocation size limits.
+
Example output::
root@moria-kvm:~# sort -g /proc/allocinfo|tail|numfmt --to=iec
diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
index 331223761fff..84f6808a8578 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -349,6 +349,8 @@ Code Seq# Include File Comments
<mailto:luzmaximilian@gmail.com>
0xA5 20-2F linux/surface_aggregator/dtx.h Microsoft Surface DTX driver
<mailto:luzmaximilian@gmail.com>
+0xA6 00-0F uapi/linux/alloc_tag.h Memory allocation profiling
+ <mailto:surenb@google.com>
0xAA 00-3F linux/uapi/linux/userfaultfd.h
0xAB 00-1F linux/nbd.h
0xAC 00-1F linux/raw.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 65bd4328fe05..019cc4c285a3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16713,6 +16713,7 @@ S: Maintained
F: Documentation/mm/allocation-profiling.rst
F: include/linux/alloc_tag.h
F: include/linux/pgalloc_tag.h
+F: include/uapi/linux/alloc_tag.h
F: lib/alloc_tag.c
MEMORY CONTROLLER DRIVERS
diff --git a/include/linux/codetag.h b/include/linux/codetag.h
index ddae7484ca45..a25a085c2df1 100644
--- a/include/linux/codetag.h
+++ b/include/linux/codetag.h
@@ -77,6 +77,8 @@ struct codetag_iterator {
void codetag_lock_module_list(struct codetag_type *cttype);
bool codetag_trylock_module_list(struct codetag_type *cttype);
void codetag_unlock_module_list(struct codetag_type *cttype);
+unsigned long codetag_get_content_id(struct codetag_type *cttype);
+unsigned int codetag_get_count(struct codetag_type *cttype);
struct codetag_iterator codetag_get_ct_iter(struct codetag_type *cttype);
struct codetag *codetag_next_ct(struct codetag_iterator *iter);
diff --git a/include/uapi/linux/alloc_tag.h b/include/uapi/linux/alloc_tag.h
new file mode 100644
index 000000000000..ee6a023cbaf4
--- /dev/null
+++ b/include/uapi/linux/alloc_tag.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * alloc_tag IOCTL API definition
+ *
+ * Copyright (C) 2026 Google, LLC. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _UAPI_ALLOC_TAG_H
+#define _UAPI_ALLOC_TAG_H
+
+#include <linux/types.h>
+
+/*
+ * Function, file and module names often have the same prefixes, therefore
+ * when filtering by these criteria, we compare the last 64 characters to
+ * minimize the chances of name collisions
+ */
+#define ALLOCINFO_STR_SIZE 64
+
+struct allocinfo_content_id {
+ __u64 id;
+};
+
+struct allocinfo_tag {
+ /* Longer names are trimmed */
+ char modname[ALLOCINFO_STR_SIZE];
+ char function[ALLOCINFO_STR_SIZE];
+ char filename[ALLOCINFO_STR_SIZE];
+ __u64 lineno;
+};
+
+/* The alignment ensures 32-bit compatible interfaces are not broken */
+struct allocinfo_counter {
+ __u64 bytes;
+ __u64 calls;
+ __u8 accurate;
+} __attribute__((aligned(8)));
+
+struct allocinfo_tag_data {
+ struct allocinfo_tag tag;
+ struct allocinfo_counter counter;
+};
+
+struct allocinfo_get_at {
+ __u64 pos; /* input */
+ struct allocinfo_tag_data data;
+};
+
+#define _ALLOCINFO_IOC_CONTENT_ID 0
+#define _ALLOCINFO_IOC_GET_AT 1
+#define _ALLOCINFO_IOC_GET_NEXT 2
+
+#define ALLOCINFO_IOC_BASE 0xA6
+#define ALLOCINFO_IOC_CONTENT_ID _IOR(ALLOCINFO_IOC_BASE, _ALLOCINFO_IOC_CONTENT_ID, \
+ struct allocinfo_content_id)
+#define ALLOCINFO_IOC_GET_AT _IOWR(ALLOCINFO_IOC_BASE, _ALLOCINFO_IOC_GET_AT, \
+ struct allocinfo_get_at)
+#define ALLOCINFO_IOC_GET_NEXT _IOR(ALLOCINFO_IOC_BASE, _ALLOCINFO_IOC_GET_NEXT, \
+ struct allocinfo_tag_data)
+
+#endif /* _UAPI_ALLOC_TAG_H */
diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c
index d9be1cf5187d..c73195000830 100644
--- a/lib/alloc_tag.c
+++ b/lib/alloc_tag.c
@@ -5,6 +5,7 @@
#include <linux/gfp.h>
#include <linux/kallsyms.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/page_ext.h>
#include <linux/pgalloc_tag.h>
#include <linux/proc_fs.h>
@@ -14,6 +15,7 @@
#include <linux/string_choices.h>
#include <linux/vmalloc.h>
#include <linux/kmemleak.h>
+#include <uapi/linux/alloc_tag.h>
#define ALLOCINFO_FILE_NAME "allocinfo"
#define MODULE_ALLOC_TAG_VMAP_SIZE (100000UL * sizeof(struct alloc_tag))
@@ -47,6 +49,10 @@ struct allocinfo_private {
struct codetag_iterator iter;
struct codetag_iterator reported_iter;
bool print_header;
+ /* ioctl uses a separate iterator not to interfere with reads */
+ struct codetag_iterator ioctl_iter;
+ bool positioned; /* seq_open_private() sets to 0 */
+ struct mutex ioctl_lock;
};
static void *allocinfo_start(struct seq_file *m, loff_t *pos)
@@ -130,6 +136,235 @@ static const struct seq_operations allocinfo_seq_op = {
.show = allocinfo_show,
};
+/*
+ * Initializes seq_file operations and allocates private state when opening
+ * the /proc/allocinfo procfs entry.
+ */
+static int allocinfo_open(struct inode *inode, struct file *file)
+{
+ int ret;
+
+ ret = seq_open_private(file, &allocinfo_seq_op,
+ sizeof(struct allocinfo_private));
+ if (!ret) {
+ struct seq_file *m = file->private_data;
+ struct allocinfo_private *priv = m->private;
+
+ mutex_init(&priv->ioctl_lock);
+ }
+ return ret;
+}
+
+/*
+ * Cleans up the seq_file state and frees up the private state allocated in
+ * allocinfo_open() when closing the /proc/allocinfo file descriptor.
+ */
+static int allocinfo_release(struct inode *inode, struct file *file)
+{
+ struct seq_file *m = file->private_data;
+ struct allocinfo_private *priv = m->private;
+
+ mutex_destroy(&priv->ioctl_lock);
+ return seq_release_private(inode, file);
+}
+
+/*
+ * Returns a pointer to the suffix of a string so that its length fits within
+ * ALLOCINFO_STR_SIZE, preserving the trailing characters.
+ * Function, file and module names often have the same prefixes, therefore
+ * when filtering by these criteria, we compare the last 64 characters to
+ * minimize the chances of name collisions
+ */
+static const char *allocinfo_str(const char *str)
+{
+ size_t len = strlen(str);
+
+ /* Keep an extra space for the trailing NULL. */
+ if (len >= ALLOCINFO_STR_SIZE)
+ str += (len - ALLOCINFO_STR_SIZE) + 1;
+ return str;
+}
+
+/* Copy a string and trim from the beginning if it's too long */
+static void allocinfo_copy_str(char *dest, const char *src)
+{
+ strscpy_pad(dest, allocinfo_str(src), ALLOCINFO_STR_SIZE);
+}
+
+/*
+ * 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 alloc_tag *tag = ct_to_alloc_tag(ct);
+ struct alloc_tag_counters counter = alloc_tag_read(tag);
+
+ if (ct->modname)
+ allocinfo_copy_str(data->tag.modname, ct->modname);
+ else
+ data->tag.modname[0] = '\0';
+ 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);
+}
+
+/*
+ * Retrieves the unique content ID representing the current allocation tag module
+ * layout, allowing userspace to detect if modules were loaded / unloaded.
+ */
+static int allocinfo_ioctl_get_content_id(struct seq_file *m, void __user *arg)
+{
+ struct allocinfo_content_id params;
+
+ codetag_lock_module_list(alloc_tag_cttype);
+ params.id = codetag_get_content_id(alloc_tag_cttype);
+ codetag_unlock_module_list(alloc_tag_cttype);
+ if (copy_to_user(arg, ¶ms, sizeof(params)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * Seeks the ioctl iterator to the specified 0-indexed tag position, reads its
+ * profiling data and returns it to userspace.
+ */
+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};
+
+ if (copy_from_user(¶ms, arg, sizeof(params)))
+ return -EFAULT;
+
+ 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)) {
+ codetag_unlock_module_list(alloc_tag_cttype);
+ mutex_unlock(&priv->ioctl_lock);
+ return -ENOENT;
+ }
+
+ /* Find the codetag */
+ priv->ioctl_iter = codetag_get_ct_iter(alloc_tag_cttype);
+ ct = codetag_next_ct(&priv->ioctl_iter);
+ while (ct && pos--)
+ ct = codetag_next_ct(&priv->ioctl_iter);
+ if (ct) {
+ allocinfo_to_params(ct, ¶ms.data);
+ priv->positioned = true;
+ }
+
+ codetag_unlock_module_list(alloc_tag_cttype);
+ mutex_unlock(&priv->ioctl_lock);
+
+ if (!ct)
+ return -ENOENT;
+
+ if (copy_to_user(arg, ¶ms, sizeof(params)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * Advances the ioctl iterator to the next allocation tag in the sequence and
+ * returns its profiling data to userspace.
+ */
+static int allocinfo_ioctl_get_next(struct seq_file *m, void __user *arg)
+{
+ struct allocinfo_private *priv;
+ struct codetag *ct;
+ struct allocinfo_tag_data params;
+ int ret = 0;
+
+ memset(¶ms, 0, sizeof(params));
+ priv = m->private;
+
+ mutex_lock(&priv->ioctl_lock);
+ codetag_lock_module_list(alloc_tag_cttype);
+
+ if (!priv->positioned) {
+ priv->ioctl_iter = codetag_get_ct_iter(alloc_tag_cttype);
+ priv->positioned = true;
+ }
+
+ ct = codetag_next_ct(&priv->ioctl_iter);
+ if (ct)
+ allocinfo_to_params(ct, ¶ms);
+
+ if (!ct) {
+ priv->positioned = false;
+ ret = -ENOENT;
+ }
+ codetag_unlock_module_list(alloc_tag_cttype);
+ mutex_unlock(&priv->ioctl_lock);
+
+ if (ret == 0) {
+ if (copy_to_user(arg, ¶ms, sizeof(params)))
+ return -EFAULT;
+ }
+ return ret;
+}
+
+/*
+ * Entry point ioctl function for /proc/allocinfo routing requests to fetch the
+ * layout content ID, seek to a specific tag, or read sequential tags.
+ */
+static long allocinfo_ioctl(struct file *file, unsigned int cmd,
+ unsigned long __arg)
+{
+ void __user *arg = (void __user *)__arg;
+ int ret;
+
+ switch (cmd) {
+ case ALLOCINFO_IOC_CONTENT_ID:
+ ret = allocinfo_ioctl_get_content_id(file->private_data, arg);
+ break;
+ case ALLOCINFO_IOC_GET_AT:
+ ret = allocinfo_ioctl_get_at(file->private_data, arg);
+ break;
+ case ALLOCINFO_IOC_GET_NEXT:
+ ret = allocinfo_ioctl_get_next(file->private_data, arg);
+ break;
+ default:
+ ret = -ENOIOCTLCMD;
+ break;
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long allocinfo_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return allocinfo_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+static const struct proc_ops allocinfo_proc_ops = {
+ .proc_open = allocinfo_open,
+ .proc_read_iter = seq_read_iter,
+ .proc_lseek = seq_lseek,
+ .proc_release = allocinfo_release,
+ .proc_ioctl = allocinfo_ioctl,
+#ifdef CONFIG_COMPAT
+ .proc_compat_ioctl = allocinfo_compat_ioctl,
+#endif
+};
+
size_t alloc_tag_top_users(struct codetag_bytes *tags, size_t count, bool can_sleep)
{
struct codetag_iterator iter;
@@ -993,8 +1228,7 @@ static int __init alloc_tag_init(void)
return 0;
}
- if (!proc_create_seq_private(ALLOCINFO_FILE_NAME, 0400, NULL, &allocinfo_seq_op,
- sizeof(struct allocinfo_private), NULL)) {
+ if (!proc_create(ALLOCINFO_FILE_NAME, 0400, NULL, &allocinfo_proc_ops)) {
pr_err("Failed to create %s file\n", ALLOCINFO_FILE_NAME);
shutdown_mem_profiling(false);
return -ENOMEM;
diff --git a/lib/codetag.c b/lib/codetag.c
index 4001a7ea6675..a9cda4c962a3 100644
--- a/lib/codetag.c
+++ b/lib/codetag.c
@@ -19,6 +19,8 @@ struct codetag_type {
struct codetag_type_desc desc;
/* generates unique sequence number for module load */
unsigned long next_mod_seq;
+ /* bumped on every module load and unload */
+ unsigned long content_id;
};
struct codetag_range {
@@ -50,6 +52,20 @@ void codetag_unlock_module_list(struct codetag_type *cttype)
up_read(&cttype->mod_lock);
}
+unsigned long codetag_get_content_id(struct codetag_type *cttype)
+{
+ lockdep_assert_held(&cttype->mod_lock);
+
+ return cttype->content_id;
+}
+
+unsigned int codetag_get_count(struct codetag_type *cttype)
+{
+ lockdep_assert_held(&cttype->mod_lock);
+
+ return cttype->count;
+}
+
struct codetag_iterator codetag_get_ct_iter(struct codetag_type *cttype)
{
struct codetag_iterator iter = {
@@ -204,6 +220,7 @@ static int codetag_module_init(struct codetag_type *cttype, struct module *mod)
down_write(&cttype->mod_lock);
cmod->mod_seq = ++cttype->next_mod_seq;
+ ++cttype->content_id;
mod_id = idr_alloc(&cttype->mod_idr, cmod, 0, 0, GFP_KERNEL);
if (mod_id >= 0) {
if (cttype->desc.module_load) {
@@ -368,6 +385,7 @@ void codetag_unload_module(struct module *mod)
cttype->count -= range_size(cttype, &cmod->range);
idr_remove(&cttype->mod_idr, mod_id);
kfree(cmod);
+ ++cttype->content_id;
}
up_write(&cttype->mod_lock);
if (found && cttype->desc.free_section_mem)
--
2.55.0.rc0.786.g65d90a0328-goog
^ permalink raw reply related
* [PATCH v6 2/6] alloc_tag: add ioctl filters to /proc/allocinfo
From: Abhishek Bapat @ 2026-06-18 17:36 UTC (permalink / raw)
To: Suren Baghdasaryan, Andrew Morton, Kent Overstreet, Hao Ge
Cc: Shuah Khan, Jonathan Corbet, linux-doc, linux-kernel, linux-mm,
Sourav Panda, Abhishek Bapat
In-Reply-To: <cover.1781803482.git.abhishekbapat@google.com>
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@google.com>
Acked-by: Hao Ge <hao.ge@linux.dev>
Acked-by: Suren Baghdasaryan <surenb@google.com>
---
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 ee6a023cbaf4..13e9b5916bf5 100644
--- a/include/uapi/linux/alloc_tag.h
+++ b/include/uapi/linux/alloc_tag.h
@@ -45,8 +45,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 c73195000830..f00d731b81cf 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 */
@@ -191,6 +192,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.
@@ -230,6 +237,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.
@@ -238,29 +279,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;
@@ -301,6 +359,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.55.0.rc0.786.g65d90a0328-goog
^ permalink raw reply related
* [PATCH v6 3/6] alloc_tag: add size-based filtering to ioctl
From: Abhishek Bapat @ 2026-06-18 17:36 UTC (permalink / raw)
To: Suren Baghdasaryan, Andrew Morton, Kent Overstreet, Hao Ge
Cc: Shuah Khan, Jonathan Corbet, linux-doc, linux-kernel, linux-mm,
Sourav Panda, Abhishek Bapat
In-Reply-To: <cover.1781803482.git.abhishekbapat@google.com>
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@google.com>
Acked-by: Hao Ge <hao.ge@linux.dev>
---
include/uapi/linux/alloc_tag.h | 8 ++++-
lib/alloc_tag.c | 64 +++++++++++++++++++++++++++-------
2 files changed, 58 insertions(+), 14 deletions(-)
diff --git a/include/uapi/linux/alloc_tag.h b/include/uapi/linux/alloc_tag.h
index 13e9b5916bf5..0de5fc180790 100644
--- a/include/uapi/linux/alloc_tag.h
+++ b/include/uapi/linux/alloc_tag.h
@@ -50,13 +50,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)
@@ -64,6 +68,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 f00d731b81cf..ad33d63ef7b4 100644
--- a/lib/alloc_tag.c
+++ b/lib/alloc_tag.c
@@ -198,16 +198,20 @@ 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);
-
if (ct->modname)
allocinfo_copy_str(data->tag.modname, ct->modname);
else
@@ -215,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));
}
/*
@@ -241,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 its 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;
@@ -268,6 +274,19 @@ 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 | 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;
}
@@ -281,6 +300,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;
@@ -288,6 +309,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);
@@ -311,7 +337,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--;
@@ -320,7 +347,9 @@ static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg)
}
if (ct) {
- allocinfo_to_params(ct, ¶ms.data);
+ if (!fetched_counters)
+ counters = allocinfo_prefetch_counters(ct);
+ allocinfo_to_params(ct, ¶ms.data, &counters);
priv->positioned = true;
}
@@ -346,6 +375,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;
@@ -359,11 +390,18 @@ 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);
+ }
+ if (ct) {
+ if (!fetched_counters)
+ counters = allocinfo_prefetch_counters(ct);
+ allocinfo_to_params(ct, ¶ms, &counters);
+ }
if (!ct) {
priv->positioned = false;
ret = -ENOENT;
--
2.55.0.rc0.786.g65d90a0328-goog
^ permalink raw reply related
* [PATCH v6 4/6] alloc_tag: add accuracy based filtering to ioctl
From: Abhishek Bapat @ 2026-06-18 17:36 UTC (permalink / raw)
To: Suren Baghdasaryan, Andrew Morton, Kent Overstreet, Hao Ge
Cc: Shuah Khan, Jonathan Corbet, linux-doc, linux-kernel, linux-mm,
Sourav Panda, Abhishek Bapat
In-Reply-To: <cover.1781803482.git.abhishekbapat@google.com>
Extend the allocinfo filtering mechanism to allow users to filter tags
based on their accuracy.
Signed-off-by: Abhishek Bapat <abhishekbapat@google.com>
Acked-by: Hao Ge <hao.ge@linux.dev>
Acked-by: Suren Baghdasaryan <surenb@google.com>
---
include/uapi/linux/alloc_tag.h | 4 ++++
lib/alloc_tag.c | 8 ++++++++
2 files changed, 12 insertions(+)
diff --git a/include/uapi/linux/alloc_tag.h b/include/uapi/linux/alloc_tag.h
index 0de5fc180790..270f693b1822 100644
--- a/include/uapi/linux/alloc_tag.h
+++ b/include/uapi/linux/alloc_tag.h
@@ -31,6 +31,8 @@ struct allocinfo_tag {
char function[ALLOCINFO_STR_SIZE];
char filename[ALLOCINFO_STR_SIZE];
__u64 lineno;
+ /* filter criteria only; see allocinfo_counter.accurate for actual accuracy */
+ __u64 inaccurate;
};
/* The alignment ensures 32-bit compatible interfaces are not broken */
@@ -50,6 +52,7 @@ enum {
ALLOCINFO_FILTER_FUNCTION,
ALLOCINFO_FILTER_FILENAME,
ALLOCINFO_FILTER_LINENO,
+ ALLOCINFO_FILTER_INACCURATE,
ALLOCINFO_FILTER_MIN_SIZE,
ALLOCINFO_FILTER_MAX_SIZE,
__ALLOCINFO_FILTER_LAST = ALLOCINFO_FILTER_MAX_SIZE
@@ -59,6 +62,7 @@ enum {
#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_INACCURATE (1 << ALLOCINFO_FILTER_INACCURATE)
#define ALLOCINFO_FILTER_MASK_MIN_SIZE (1 << ALLOCINFO_FILTER_MIN_SIZE)
#define ALLOCINFO_FILTER_MASK_MAX_SIZE (1 << ALLOCINFO_FILTER_MAX_SIZE)
diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c
index ad33d63ef7b4..32ac0674d8bf 100644
--- a/lib/alloc_tag.c
+++ b/lib/alloc_tag.c
@@ -249,6 +249,8 @@ static bool matches_filter(struct codetag *ct, struct allocinfo_filter *filter,
struct alloc_tag_counters *counters,
bool *fetched_counters)
{
+ bool inaccurate;
+
if (!filter || !filter->mask)
return true;
@@ -274,6 +276,12 @@ static bool matches_filter(struct codetag *ct, struct allocinfo_filter *filter,
ct->lineno != filter->fields.lineno)
return false;
+ if (filter->mask & ALLOCINFO_FILTER_MASK_INACCURATE) {
+ inaccurate = !!(ct->flags & CODETAG_FLAG_INACCURATE);
+ if (inaccurate != !!(filter->fields.inaccurate))
+ return false;
+ }
+
if (filter->mask & (ALLOCINFO_FILTER_MASK_MIN_SIZE | ALLOCINFO_FILTER_MASK_MAX_SIZE)) {
if (!*fetched_counters) {
*counters = allocinfo_prefetch_counters(ct);
--
2.55.0.rc0.786.g65d90a0328-goog
^ permalink raw reply related
* [PATCH v6 5/6] kselftest: alloc_tag: add kselftest for ioctl interface
From: Abhishek Bapat @ 2026-06-18 17:36 UTC (permalink / raw)
To: Suren Baghdasaryan, Andrew Morton, Kent Overstreet, Hao Ge
Cc: Shuah Khan, Jonathan Corbet, linux-doc, linux-kernel, linux-mm,
Sourav Panda, Abhishek Bapat
In-Reply-To: <cover.1781803482.git.abhishekbapat@google.com>
Introduce a kselftest to verify the new IOCTL-based interface for
/proc/allocinfo. The test covers:
1. Validation of the filename filter.
2. Validation of the function filter.
The first test validates the functionality of the filename filter. Using
"mm/memory.c" as the candidate filename filter, it retrieves filtered
entries from both procfs and ioctl and matches the first VEC_MAX_ENTRIES
entries.
The second test validates the functionality of the function filter.
It uses "dup_mm" as the candidate function as we do not expect this
function name to change frequently and hence won't be needing to modify
this test often.
Note that both the tests match line no, function name and file name
fields. Bytes allocated and calls are not matched as those values may
change in the time when the data is being read from procfs and ioctl and
hence can lead to false negatives.
Signed-off-by: Abhishek Bapat <abhishekbapat@google.com>
---
MAINTAINERS | 1 +
tools/testing/selftests/Makefile | 1 +
tools/testing/selftests/alloc_tag/Makefile | 9 +
.../alloc_tag/allocinfo_ioctl_test.c | 335 ++++++++++++++++++
4 files changed, 346 insertions(+)
create mode 100644 tools/testing/selftests/alloc_tag/Makefile
create mode 100644 tools/testing/selftests/alloc_tag/allocinfo_ioctl_test.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 019cc4c285a3..6610dd42e484 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16715,6 +16715,7 @@ F: include/linux/alloc_tag.h
F: include/linux/pgalloc_tag.h
F: include/uapi/linux/alloc_tag.h
F: lib/alloc_tag.c
+F: tools/testing/selftests/alloc_tag/
MEMORY CONTROLLER DRIVERS
M: Krzysztof Kozlowski <krzk@kernel.org>
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 6e59b8f63e41..276a78c64736 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
TARGETS += acct
+TARGETS += alloc_tag
TARGETS += alsa
TARGETS += amd-pstate
TARGETS += arm64
diff --git a/tools/testing/selftests/alloc_tag/Makefile b/tools/testing/selftests/alloc_tag/Makefile
new file mode 100644
index 000000000000..f2b8fc022c3b
--- /dev/null
+++ b/tools/testing/selftests/alloc_tag/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+
+TEST_GEN_PROGS := allocinfo_ioctl_test
+
+CFLAGS += -Wall
+CFLAGS += -I../../../../usr/include
+
+include ../lib.mk
+
diff --git a/tools/testing/selftests/alloc_tag/allocinfo_ioctl_test.c b/tools/testing/selftests/alloc_tag/allocinfo_ioctl_test.c
new file mode 100644
index 000000000000..1ae0291f2245
--- /dev/null
+++ b/tools/testing/selftests/alloc_tag/allocinfo_ioctl_test.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/* kselftest for allocinfo ioctl
+ * allocinfo ioctl retrives allocinfo data through ioctl
+ * Copyright (C) 2026 Google, Inc.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <linux/types.h>
+#include <linux/alloc_tag.h>
+#include "../kselftest.h"
+
+#define MAX_LINE_LEN 512
+#define ALLOCINFO_PROC "/proc/allocinfo"
+
+enum ioctl_ret {
+ IOCTL_SUCCESS = 0,
+ IOCTL_FAILURE = 1,
+ IOCTL_INVALID_DATA = 2,
+};
+
+#define VEC_MAX_ENTRIES 32
+
+struct allocinfo_tag_data_vec {
+ struct allocinfo_tag_data tag[VEC_MAX_ENTRIES];
+ __u64 count;
+};
+
+static inline int __allocinfo_get_content_id(int dev_fd, struct allocinfo_content_id *params)
+{
+ return ioctl(dev_fd, ALLOCINFO_IOC_CONTENT_ID, params);
+}
+
+static inline int __allocinfo_get_at(int dev_fd, struct allocinfo_get_at *params)
+{
+ return ioctl(dev_fd, ALLOCINFO_IOC_GET_AT, params);
+}
+
+static inline int __allocinfo_get_next(int dev_fd, struct allocinfo_tag_data *params)
+{
+ return ioctl(dev_fd, ALLOCINFO_IOC_GET_NEXT, params);
+}
+
+static bool match_entry(const struct allocinfo_tag_data *procfs_entry,
+ const struct allocinfo_tag_data *tag_data,
+ bool match_bytes, bool match_calls, bool match_lineno,
+ bool match_function, bool match_filename)
+{
+ if (match_bytes && tag_data->counter.bytes != procfs_entry->counter.bytes) {
+ ksft_print_msg("size retrieved through ioctl does not match procfs\n");
+ return false;
+ }
+
+ if (match_calls && tag_data->counter.calls != procfs_entry->counter.calls) {
+ ksft_print_msg("call count retrieved through ioctl does not match procfs\n");
+ return false;
+ }
+
+ if (match_lineno && tag_data->tag.lineno != procfs_entry->tag.lineno) {
+ ksft_print_msg("lineno retrieved through ioctl does not match procfs\n");
+ return false;
+ }
+
+ if (match_function &&
+ strncmp(tag_data->tag.function, procfs_entry->tag.function, ALLOCINFO_STR_SIZE)) {
+ ksft_print_msg("function retrieved through ioctl does not match procfs\n");
+ return false;
+ }
+
+ if (match_filename &&
+ strncmp(tag_data->tag.filename, procfs_entry->tag.filename, ALLOCINFO_STR_SIZE)) {
+ ksft_print_msg("filename retrieved through ioctl does not match procfs\n");
+ return false;
+ }
+ return true;
+}
+
+static bool match_entries(const struct allocinfo_tag_data_vec *procfs_entries,
+ const struct allocinfo_tag_data_vec *tags,
+ bool match_bytes, bool match_calls, bool match_lineno,
+ bool match_function, bool match_filename)
+{
+ __u64 i;
+
+ if (procfs_entries->count != tags->count) {
+ ksft_print_msg("Entry count mismatch. ioctl entries: %llu, proc entries: %llu\n",
+ tags->count, procfs_entries->count);
+ return false;
+ }
+ for (i = 0; i < procfs_entries->count; i++) {
+ if (!match_entry(&procfs_entries->tag[i], &tags->tag[i],
+ match_bytes, match_calls, match_lineno,
+ match_function, match_filename)) {
+ ksft_print_msg("%lluth entry does not match.\n", i);
+ return false;
+ }
+ }
+ return true;
+}
+
+static const char *allocinfo_str(const char *str)
+{
+ size_t len = strlen(str);
+
+ if (len >= ALLOCINFO_STR_SIZE)
+ str += (len - ALLOCINFO_STR_SIZE) + 1;
+ return str;
+}
+
+static void allocinfo_copy_str(char *dest, const char *src)
+{
+ strncpy(dest, allocinfo_str(src), ALLOCINFO_STR_SIZE - 1);
+ dest[ALLOCINFO_STR_SIZE - 1] = '\0';
+}
+
+static int get_filtered_procfs_entries(struct allocinfo_tag_data_vec *procfs_entries,
+ const struct allocinfo_filter *filter)
+{
+ FILE *fp = fopen(ALLOCINFO_PROC, "r");
+ char line[MAX_LINE_LEN];
+ int matches;
+ struct allocinfo_tag_data procfs_entry;
+
+ if (!fp) {
+ ksft_print_msg("Failed to open " ALLOCINFO_PROC " for reading\n");
+ return 1;
+ }
+ memset(procfs_entries, 0, sizeof(*procfs_entries));
+ while (fgets(line, sizeof(line), fp) && procfs_entries->count < VEC_MAX_ENTRIES) {
+ char filename[MAX_LINE_LEN];
+ char function[MAX_LINE_LEN];
+
+ memset(&procfs_entry, 0, sizeof(procfs_entry));
+ matches = sscanf(line, "%llu %llu %[^:]:%llu func:%s",
+ &procfs_entry.counter.bytes,
+ &procfs_entry.counter.calls,
+ filename,
+ &procfs_entry.tag.lineno,
+ function);
+
+ if (matches != 5)
+ continue;
+
+ allocinfo_copy_str(procfs_entry.tag.filename, filename);
+ allocinfo_copy_str(procfs_entry.tag.function, function);
+
+ if (filter->mask & ALLOCINFO_FILTER_MASK_FILENAME) {
+ if (strncmp(procfs_entry.tag.filename,
+ filter->fields.filename, ALLOCINFO_STR_SIZE))
+ continue;
+ }
+ if (filter->mask & ALLOCINFO_FILTER_MASK_FUNCTION) {
+ if (strncmp(procfs_entry.tag.function,
+ filter->fields.function, ALLOCINFO_STR_SIZE))
+ continue;
+ }
+ if (filter->mask & ALLOCINFO_FILTER_MASK_LINENO) {
+ if (procfs_entry.tag.lineno != filter->fields.lineno)
+ continue;
+ }
+ if (filter->mask & ALLOCINFO_FILTER_MASK_MIN_SIZE) {
+ if (procfs_entry.counter.bytes < filter->min_size)
+ continue;
+ }
+ if (filter->mask & ALLOCINFO_FILTER_MASK_MAX_SIZE) {
+ if (procfs_entry.counter.bytes > filter->max_size)
+ continue;
+ }
+
+ memcpy(&procfs_entries->tag[procfs_entries->count++], &procfs_entry,
+ sizeof(procfs_entry));
+ }
+ fclose(fp);
+ return 0;
+}
+
+static enum ioctl_ret get_filtered_ioctl_entries(struct allocinfo_tag_data_vec *tags,
+ const struct allocinfo_filter *filter,
+ __u64 start_pos)
+{
+ int fd = open(ALLOCINFO_PROC, O_RDONLY);
+
+ if (fd < 0) {
+ ksft_print_msg("Failed to open " ALLOCINFO_PROC " for IOCTL\n");
+ return IOCTL_FAILURE;
+ }
+
+ struct allocinfo_content_id start_cont_id, end_cont_id;
+ struct allocinfo_get_at get_at_params;
+ const int max_retries = 10;
+ int retry_count = 0;
+ int status;
+
+ /*
+ * __allocinfo_get_content_id may return different values if a kernel module was loaded
+ * between the two calls. If that happens, the data gathered cannot be considered consistent
+ * and hence needs to be fetched again to avoid flakiness.
+ */
+ do {
+ if (__allocinfo_get_content_id(fd, &start_cont_id)) {
+ ksft_print_msg("allocinfo_get_content_id failed\n");
+ status = IOCTL_FAILURE;
+ goto exit;
+ }
+
+ memset(tags, 0, sizeof(*tags));
+ memset(&get_at_params, 0, sizeof(get_at_params));
+ memcpy(&get_at_params.filter, filter, sizeof(*filter));
+ get_at_params.pos = start_pos;
+ if (__allocinfo_get_at(fd, &get_at_params)) {
+ ksft_print_msg("allocinfo_get_at failed\n");
+ status = IOCTL_FAILURE;
+ goto exit;
+ }
+ memcpy(&tags->tag[tags->count++], &get_at_params.data, sizeof(get_at_params.data));
+
+ while (tags->count < VEC_MAX_ENTRIES &&
+ __allocinfo_get_next(fd, &tags->tag[tags->count]) == 0)
+ tags->count++;
+
+ if (__allocinfo_get_content_id(fd, &end_cont_id)) {
+ ksft_print_msg("allocinfo_get_content_id failed\n");
+ status = IOCTL_FAILURE;
+ goto exit;
+ }
+
+ if (start_cont_id.id == end_cont_id.id) {
+ status = IOCTL_SUCCESS;
+ } else {
+ ksft_print_msg("allocinfo_get_content_id mismatch, retrying...\n");
+ status = IOCTL_INVALID_DATA;
+ }
+ } while (status == IOCTL_INVALID_DATA && retry_count++ < max_retries);
+
+exit:
+ close(fd);
+ return status;
+}
+
+static int run_filter_test(const struct allocinfo_filter *filter)
+{
+ struct allocinfo_tag_data_vec *tags = malloc(sizeof(*tags));
+ struct allocinfo_tag_data_vec *procfs_entries = malloc(sizeof(*procfs_entries));
+ int ioctl_status;
+ int ret = KSFT_PASS;
+
+ if (!tags || !procfs_entries) {
+ ksft_print_msg("Memory allocation failed.\n");
+ ret = KSFT_FAIL;
+ goto exit;
+ }
+
+ if (get_filtered_procfs_entries(procfs_entries, filter)) {
+ ksft_print_msg("Error retrieving entries from " ALLOCINFO_PROC "\n");
+ ret = KSFT_SKIP;
+ goto exit;
+ }
+
+ if (procfs_entries->count == 0) {
+ ksft_print_msg("No entries found in " ALLOCINFO_PROC ", skipping test\n");
+ ret = KSFT_SKIP;
+ goto exit;
+ }
+
+ ioctl_status = get_filtered_ioctl_entries(tags, filter, 0);
+ if (ioctl_status == IOCTL_INVALID_DATA) {
+ ksft_print_msg("Trouble retrieving valid IOCTL entries, skipping.\n");
+ ret = KSFT_SKIP;
+ goto exit;
+ }
+ if (ioctl_status == IOCTL_FAILURE) {
+ ksft_print_msg("Error retrieving IOCTL entries.\n");
+ ret = KSFT_FAIL;
+ goto exit;
+ }
+
+ if (!match_entries(procfs_entries, tags, false, false, true, true, true))
+ ret = KSFT_FAIL;
+
+exit:
+ free(tags);
+ free(procfs_entries);
+ return ret;
+}
+
+static int test_filename_filter(void)
+{
+ struct allocinfo_filter filter;
+ const char *target_filename = "mm/memory.c";
+
+ memset(&filter, 0, sizeof(filter));
+ filter.mask |= ALLOCINFO_FILTER_MASK_FILENAME;
+ strncpy(filter.fields.filename, target_filename, ALLOCINFO_STR_SIZE);
+
+ return run_filter_test(&filter);
+}
+
+static int test_function_filter(void)
+{
+ struct allocinfo_filter filter;
+ const char *target_function = "dup_mm";
+
+ memset(&filter, 0, sizeof(filter));
+ filter.mask |= ALLOCINFO_FILTER_MASK_FUNCTION;
+ strncpy(filter.fields.function, target_function, ALLOCINFO_STR_SIZE);
+
+ return run_filter_test(&filter);
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ ksft_set_plan(2);
+
+ ret = test_filename_filter();
+ if (ret == KSFT_SKIP)
+ ksft_test_result_skip("Skipping test_filename_filter\n");
+ else
+ ksft_test_result(ret == KSFT_PASS, "test_filename_filter\n");
+
+ ret = test_function_filter();
+ if (ret == KSFT_SKIP)
+ ksft_test_result_skip("Skipping test_function_filter\n");
+ else
+ ksft_test_result(ret == KSFT_PASS, "test_function_filter\n");
+
+ ksft_finished();
+}
--
2.55.0.rc0.786.g65d90a0328-goog
^ permalink raw reply related
* [PATCH v6 6/6] kselftest: alloc_tag: extend the allocinfo ioctl kselftest
From: Abhishek Bapat @ 2026-06-18 17:36 UTC (permalink / raw)
To: Suren Baghdasaryan, Andrew Morton, Kent Overstreet, Hao Ge
Cc: Shuah Khan, Jonathan Corbet, linux-doc, linux-kernel, linux-mm,
Sourav Panda, Abhishek Bapat
In-Reply-To: <cover.1781803482.git.abhishekbapat@google.com>
Add the following 2 scenarios to the allocinfo ioctl kselftest:
1. Validate size based filtering
2. Validate lineno based filtering
The first test uses "do_init_module" as the candidate function for the
test. This is because the associated site will only allocate memory when
a kernel module is loaded. The return value of get_content_id() changes
every time modules are loaded or unloaded. Hence, as long as
get_content_id() values at the start and the end of the test are the
same, the memory allocated by the do_init_module call site should also
remain the same. Consequently, the test can assume consistency between
the value returned by the ioctl and the procfs resulting in less
flakiness.
Signed-off-by: Abhishek Bapat <abhishekbapat@google.com>
---
.../alloc_tag/allocinfo_ioctl_test.c | 198 +++++++++++++++++-
1 file changed, 197 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/alloc_tag/allocinfo_ioctl_test.c b/tools/testing/selftests/alloc_tag/allocinfo_ioctl_test.c
index 1ae0291f2245..50755a45d3fe 100644
--- a/tools/testing/selftests/alloc_tag/allocinfo_ioctl_test.c
+++ b/tools/testing/selftests/alloc_tag/allocinfo_ioctl_test.c
@@ -5,6 +5,7 @@
* Copyright (C) 2026 Google, Inc.
*/
+#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
@@ -313,11 +314,194 @@ static int test_function_filter(void)
return run_filter_test(&filter);
}
+static int test_size_filter(void)
+{
+ int fd;
+ struct allocinfo_tag_data_vec *tags = malloc(sizeof(*tags));
+ struct allocinfo_tag_data_vec *procfs_entries = malloc(sizeof(*procfs_entries));
+ struct allocinfo_filter filter;
+ int ret = KSFT_PASS;
+ __u64 target_size, i, pos;
+ bool found;
+ const char *target_function = "do_init_module";
+ struct allocinfo_content_id start_cont_id, end_cont_id;
+ int retry = 0;
+ const int max_retries = 10;
+
+ if (!tags || !procfs_entries) {
+ ksft_print_msg("Memory allocation failed.\n");
+ ret = KSFT_FAIL;
+ goto freemem;
+ }
+
+ fd = open(ALLOCINFO_PROC, O_RDONLY);
+ if (fd < 0) {
+ ksft_print_msg("Failed to open " ALLOCINFO_PROC ": %s\n", strerror(errno));
+ ret = KSFT_FAIL;
+ goto freemem;
+ }
+
+ do {
+ found = false;
+ pos = 0;
+
+ if (__allocinfo_get_content_id(fd, &start_cont_id)) {
+ ksft_print_msg("allocinfo_get_content_id failed\n");
+ ret = KSFT_FAIL;
+ goto exit;
+ }
+
+ memset(&filter, 0, sizeof(filter));
+ filter.mask |= ALLOCINFO_FILTER_MASK_FUNCTION;
+ strncpy(filter.fields.function, target_function, ALLOCINFO_STR_SIZE);
+
+ if (get_filtered_procfs_entries(procfs_entries, &filter)) {
+ ksft_print_msg("Error retrieving entries from " ALLOCINFO_PROC "\n");
+ ret = KSFT_FAIL;
+ goto exit;
+ }
+
+ if (procfs_entries->count == 0) {
+ ksft_print_msg("Function %s not found in procfs\n", target_function);
+ ret = KSFT_SKIP;
+ goto exit;
+ }
+
+ target_size = procfs_entries->tag[0].counter.bytes;
+
+ memset(&filter, 0, sizeof(filter));
+ filter.mask |= ALLOCINFO_FILTER_MASK_MIN_SIZE | ALLOCINFO_FILTER_MASK_MAX_SIZE;
+ filter.min_size = target_size;
+ filter.max_size = target_size;
+
+ while (1) {
+ struct allocinfo_get_at get_at_params;
+
+ memset(&get_at_params, 0, sizeof(get_at_params));
+ memcpy(&get_at_params.filter, &filter, sizeof(filter));
+ get_at_params.pos = pos;
+
+ if (__allocinfo_get_at(fd, &get_at_params))
+ break;
+
+ tags->count = 0;
+ memcpy(&tags->tag[tags->count++], &get_at_params.data,
+ sizeof(get_at_params.data));
+
+ while (tags->count < VEC_MAX_ENTRIES &&
+ __allocinfo_get_next(fd, &tags->tag[tags->count]) == 0)
+ tags->count++;
+
+ for (i = 0; i < tags->count; i++) {
+ if (strcmp(tags->tag[i].tag.function, target_function) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found || tags->count < VEC_MAX_ENTRIES)
+ break;
+
+ pos += tags->count;
+ }
+
+ if (__allocinfo_get_content_id(fd, &end_cont_id)) {
+ ksft_print_msg("allocinfo_get_content_id failed\n");
+ ret = KSFT_FAIL;
+ goto exit;
+ }
+
+ if (start_cont_id.id == end_cont_id.id)
+ break;
+
+ ksft_print_msg("Module load detected during size verification, retrying...\n");
+ } while (retry++ < max_retries);
+
+ if (start_cont_id.id == end_cont_id.id && !found) {
+ ksft_print_msg("Entry with function %s not found in IOCTL results\n",
+ target_function);
+ ret = KSFT_FAIL;
+ } else if (start_cont_id.id != end_cont_id.id) {
+ ksft_print_msg("Failed to match content_ids for procfs and IOCTL, skipping...\n");
+ ret = KSFT_SKIP;
+ }
+
+exit:
+ close(fd);
+freemem:
+ free(tags);
+ free(procfs_entries);
+ return ret;
+}
+
+static int test_lineno_filter(void)
+{
+ struct allocinfo_tag_data_vec *tags = malloc(sizeof(*tags));
+ struct allocinfo_tag_data_vec *procfs_entries = malloc(sizeof(*procfs_entries));
+ struct allocinfo_filter filter;
+ enum ioctl_ret ioctl_status;
+ int ret = KSFT_PASS;
+ __u64 target_lineno, i;
+
+ if (!tags || !procfs_entries) {
+ ksft_print_msg("Memory allocation failed.\n");
+ ret = KSFT_FAIL;
+ goto exit;
+ }
+
+ memset(&filter, 0, sizeof(filter));
+
+ if (get_filtered_procfs_entries(procfs_entries, &filter)) {
+ ksft_print_msg("Error retrieving entries from " ALLOCINFO_PROC "\n");
+ ret = KSFT_FAIL;
+ goto exit;
+ }
+ if (procfs_entries->count == 0) {
+ ksft_print_msg("Could not retrieve procfs entries\n");
+ ret = KSFT_SKIP;
+ goto exit;
+ }
+ /*
+ * We depend on the result of procfs entries to create the ioctl_filter. Hence we
+ * cannot recycle the run_filter_test function here.
+ */
+ target_lineno = procfs_entries->tag[0].tag.lineno;
+
+ filter.mask |= ALLOCINFO_FILTER_MASK_LINENO;
+ filter.fields.lineno = target_lineno;
+
+ ioctl_status = get_filtered_ioctl_entries(tags, &filter, 0);
+ if (ioctl_status == IOCTL_INVALID_DATA) {
+ ksft_print_msg("Trouble retrieving valid IOCTL entries, skipping.\n");
+ ret = KSFT_SKIP;
+ goto exit;
+ }
+ if (ioctl_status == IOCTL_FAILURE) {
+ ksft_print_msg("Error retrieving IOCTL entries.\n");
+ ret = KSFT_FAIL;
+ goto exit;
+ }
+
+ for (i = 0; i < tags->count; i++) {
+ if (tags->tag[i].tag.lineno != target_lineno) {
+ ksft_print_msg("IOCTL entry %llu has incorrect lineno %llu.\n",
+ i, tags->tag[i].tag.lineno);
+ ret = KSFT_FAIL;
+ goto exit;
+ }
+ }
+
+exit:
+ free(tags);
+ free(procfs_entries);
+ return ret;
+}
+
int main(int argc, char *argv[])
{
int ret;
- ksft_set_plan(2);
+ ksft_set_plan(4);
ret = test_filename_filter();
if (ret == KSFT_SKIP)
@@ -331,5 +515,17 @@ int main(int argc, char *argv[])
else
ksft_test_result(ret == KSFT_PASS, "test_function_filter\n");
+ ret = test_size_filter();
+ if (ret == KSFT_SKIP)
+ ksft_test_result_skip("Skipping test_size_filter\n");
+ else
+ ksft_test_result(ret == KSFT_PASS, "test_size_filter\n");
+
+ ret = test_lineno_filter();
+ if (ret == KSFT_SKIP)
+ ksft_test_result_skip("Skipping test_lineno_filter\n");
+ else
+ ksft_test_result(ret == KSFT_PASS, "test_lineno_filter\n");
+
ksft_finished();
}
--
2.55.0.rc0.786.g65d90a0328-goog
^ permalink raw reply related
* Re: [PATCH v6 06/16] iio: core: create local __iio_chan_prefix_emit() for reuse
From: Andy Shevchenko @ 2026-06-18 18:14 UTC (permalink / raw)
To: Rodrigo Alencar
Cc: Nuno Sá, rodrigo.alencar, linux-iio, devicetree,
linux-kernel, linux-doc, linux-hardening, Lars-Peter Clausen,
Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Jonathan Corbet, Shuah Khan, Kees Cook,
Gustavo A. R. Silva
In-Reply-To: <x3aijvc4buo7aqbchikuoyyrgiq3afidtkla37h2rg4tvfdbc3@h42qp3estg2s>
On Thu, Jun 18, 2026 at 05:14:19PM +0100, Rodrigo Alencar wrote:
> On 18/06/26 16:06, Nuno Sá wrote:
> > On Thu, Jun 18, 2026 at 02:27:22PM +0100, Rodrigo Alencar via B4 Relay wrote:
...
> > > + dev_attr->attr.name = kasprintf(GFP_KERNEL, "%s%s", prefix, postfix);
> > > + if (!dev_attr->attr.name)
> > > return -ENOMEM;
> >
> > I don't oppose the change. Looks like a nice cleanup.
May I oppose it? I found use scnprintf() is harder to follow in comparison to
nice kasprintf() that takes care for the dynamically allocated buffer.
Also there is a chance to get a name silently cut due to insufficient space.
Besides that this function can't be used (again due to 'c') in kasprintf()-like
wrapper. I do not consider this as a good approach. Have you looked at seq_buf
instead?
> > But bear in mind this very sensible as any subtle mistake means ABI breakage.
Which immediately raises a question of test coverage. Do we have one? If not,
this code must be accompanied with one.
> Yes! I tried to be careful... this is dangerous stuff!
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [PATCH] kselftest docs: remove reference to obsolete/archived wiki
From: Brett Sheffield @ 2026-06-18 18:14 UTC (permalink / raw)
To: Shuah Khan
Cc: Rafael Passos, shuah, corbet, linux-kselftest, workflows,
linux-doc, linux-kernel
In-Reply-To: <1306d609-3375-4f52-8239-b4c2fffb7bec@linuxfoundation.org>
On 2026-06-18 11:02, Shuah Khan wrote:
> My apologies for not taking your patch earlier. Considering the effort
> you put in with a re-sending the patch and following up here, it is
> only fair for me to take yours instead. Hope it will apply cleanly on
> top of kselftest-next
>
> Rafael, I am going to take Brett;s patch instead of yours.
>
> Apologies to both of you for the mix up.
Thanks Shuah & no worries.
--
Brett Sheffield (he/him)
^ permalink raw reply
* Re: [PATCH v3] kconfig: add optional warnings for changed input values
From: Nathan Chancellor @ 2026-06-18 18:48 UTC (permalink / raw)
To: Masahiro Yamada, Nicolas Schier, Pengpeng Hou
Cc: Jonathan Corbet, linux-kbuild, linux-doc, linux-kernel
In-Reply-To: <20260611060000.23858-1-pengpeng@iscas.ac.cn>
On Thu, 11 Jun 2026 14:00:00 +0800, Pengpeng Hou wrote:
> kconfig: add optional warnings for changed input values
Since v2 was nearly there over a month ago and it is a genuine quality
of life improvement hidden behind an off-by-default option, I am going
to take this for my late 7.2 Kbuild pull request.
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/kbuild/linux.git kbuild-next-unstable
Thanks!
[1/1] kconfig: add optional warnings for changed input values
https://git.kernel.org/kbuild/c/645323a7f4e55
Please look out for regression or issue reports or other follow up
comments, as they may result in the patch/series getting dropped or
reverted. Patches applied to an "unstable" branch are accepted pending
wider testing in -next and any post-commit review; they will generally
be moved to the main branch in a week if no issues are found.
Best regards,
--
Cheers,
Nathan
^ permalink raw reply
* Re: [PATCH v3 07/12] fs/resctrl: Add info/kernel_mode for kernel-mode policy introspection
From: Babu Moger @ 2026-06-18 19:16 UTC (permalink / raw)
To: Reinette Chatre, corbet, tony.luck, Dave.Martin, james.morse,
tglx, bp, dave.hansen
Cc: skhan, x86, mingo, hpa, akpm, rdunlap, pawan.kumar.gupta,
feng.tang, dapeng1.mi, kees, elver, lirongqing, paulmck, bhelgaas,
seanjc, alexandre.chartre, yazen.ghannam, peterz, chang.seok.bae,
kim.phillips, xin, naveen, thomas.lendacky, linux-doc,
linux-kernel, eranian, peternewman
In-Reply-To: <2429a51a-92ad-4810-bee9-44bd6fba3443@intel.com>
Hi Reinette,
On 6/16/26 18:38, Reinette Chatre wrote:
> Hi Babu,
>
> How should "introspection" as used in subject be interpreted? This just
> displays the supported and active kernel modes to user space, no?
Yes. Will change it.
>
> On 4/30/26 4:24 PM, Babu Moger wrote:
>> There is no user-visible way today to see which kernel-mode CLOSID/RMID
>> policies the running kernel supports, which one is active, or which
>> resctrl group currently owns the kernel CLOSID/RMID.
>
> Why should there be? This is a new feature being added in this series.
> No need to write this as a bugfix.
>
Sure.
>>
>> Add a read-only top-level sysfs file, info/kernel_mode. It emits one
>> line per mode advertised in resctrl_kcfg.kmode, in stable lowercase
>> spelling derived from enum resctrl_kernel_modes, e.g.:
>
> All these changelogs feel so strange ... as though they are written by
> somebody who simultaneously has no and full knowledge of resctrl.
> These verbatim descriptions of what the code does is not necessary. Please
> start with why the patch is needed.
Sure. My bad. Will re-write it.
>
>>
>> [inherit_ctrl_and_mon:group=//]
>
> This is unexpected. There should be no group associated with this default mode.
> This is how I interpreted our previous discussion ending:
> https://lore.kernel.org/lkml/6709398b-269d-47b5-9b41-084f410bb1a6@amd.com/
Ack.
>
>> global_assign_ctrl_inherit_mon_per_cpu:group=none
>> global_assign_ctrl_assign_mon_per_cpu:group=none
>>
>> The effective policy (resctrl_kcfg.kmode_cur) is wrapped in square
>
> (needs imperative - please check all changelogs)
Sure.
>
>> brackets and its :group= suffix names the resctrl group currently
>> bound to the kernel CLOSID/RMID (resctrl_kcfg.k_rdtgrp), formatted as
>> <ctrl>/<mon>/ with empty components left blank. Inactive modes are
>> reported as :group=none.
>>
>> rdtgroup_mutex is held while printing, matching other info/ show paths.
>
> No need to describe details that can be seen from patch.
ok.
>
>>
>> Signed-off-by: Babu Moger <babu.moger@amd.com>
>> ---
>> v3: New patch to handle the changed interface file info/kernel_mode.
>> Changed the group name to "none" if kmode binding is not done.
>> Reinette suggested "uninitialized". "none" seemed more relevent.
>> ---
>> fs/resctrl/rdtgroup.c | 74 +++++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 74 insertions(+)
>>
>> diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c
>> index a7bfc74897cc..9cdcfa64c4a2 100644
>> --- a/fs/resctrl/rdtgroup.c
>> +++ b/fs/resctrl/rdtgroup.c
>> @@ -988,6 +988,73 @@ static int rdt_last_cmd_status_show(struct kernfs_open_file *of,
>> return 0;
>> }
>>
>> +/* Sysfs lines for info/kernel_mode; indexed by &enum resctrl_kernel_modes */
>> +static const char * const resctrl_mode_str[] = {
>> + [INHERIT_CTRL_AND_MON] = "inherit_ctrl_and_mon",
>> + [GLOBAL_ASSIGN_CTRL_INHERIT_MON_PER_CPU] = "global_assign_ctrl_inherit_mon_per_cpu",
>> + [GLOBAL_ASSIGN_CTRL_ASSIGN_MON_PER_CPU] = "global_assign_ctrl_assign_mon_per_cpu",
>
> Please make alignment consistent.
>
Sure.
>> +};
>> +
>> +static_assert(ARRAY_SIZE(resctrl_mode_str) == RESCTRL_NUM_KERNEL_MODES);
>> +
>> +/**
>> + * resctrl_kernel_mode_show() - Enumerate supported and effective kernel-mode policies
>
> "Enumerate" -> "Display"?
sure.
>
>> + * @of: kernfs open file
>> + * @seq: output seq_file
>> + * @v: unused
>> + *
>> + * Emits one line per mode advertised in resctrl_kcfg.kmode (each mode is one
>> + * BIT(index) per &enum resctrl_kernel_modes). Every line carries a
>
> Above is clear from the code. Please instead describe what this means.
Sure.
>
>> + * ":group=<name>" suffix:
>> + *
>> + * - The effective policy (whose BIT matches resctrl_kcfg.kmode_cur) is
>> + * wrapped in square brackets and <name> is the resctrl group that
>> + * currently owns the kernel CLOSID/RMID (resctrl_kcfg.k_rdtgrp),
>> + * formatted as "<ctrl>/<mon>/". A component is left empty when it
>> + * does not apply: an RDTCTRL_GROUP emits "<ctrl>//", an RDTMON_GROUP
>> + * under the default control group emits "/<mon>/", and an RDTMON_GROUP
>> + * under a named control group emits "<ctrl>/<mon>/".
>> + *
>> + * - Other supported but inactive modes are emitted without brackets and
>> + * <name> is reported as "none".
>> + *
>> + * Context: Called under rdtgroup_mutex like other resctrl sysfs show paths.
>
> This does not look accurate since it is not called with mutex held but instead
> takes the mutex itself. Also no need to refer to what other code does.
ok.
>
>> + */
>> +static int resctrl_kernel_mode_show(struct kernfs_open_file *of,
>> + struct seq_file *seq, void *v)
>> +{
>> + struct rdtgroup *rdtgrp;
>> + const char *ctrl, *mon;
>> + int i;
>> +
>> + mutex_lock(&rdtgroup_mutex);
>> + for (i = 0; i < RESCTRL_NUM_KERNEL_MODES; i++) {
>> + if (!(resctrl_kcfg.kmode & BIT(i)))
>> + continue;
>> +
>> + if (resctrl_kcfg.kmode_cur != BIT(i)) {
>> + seq_printf(seq, "%s:group=none\n",
>> + resctrl_mode_str[i]);
>> + continue;
>> + }
>> +
>> + rdtgrp = resctrl_kcfg.k_rdtgrp;
>> + ctrl = "";
>> + mon = "";
>> + if (rdtgrp->type == RDTMON_GROUP) {
>> + if (rdtgrp->mon.parent != &rdtgroup_default)
>> + ctrl = rdtgrp->mon.parent->kn->name;
>
> Isn't default group's kn->name is initialized correctly via
> rdtgroup_setup_root()->kernfs_create_root()->__kernfs_new_node(root, NULL, "", ...) ?
Yes. that is correct. I will remove the check.
>
>> + mon = rdtgrp->kn->name;
>> + } else {
>> + ctrl = rdtgrp->kn->name;
>> + }
>
> Can the names not just be initialized directly from kn->name?
Yes. I think so. But I need to know if this is a control group or mon
group to make it generic. Let me see if I can optimize this section.
>
>
>> + seq_printf(seq, "[%s:group=%s/%s/]\n",
>> + resctrl_mode_str[i], ctrl, mon);
>
> This is not where I understood our discussion landed. I expected that the display will
> reflect what can/should be assigned in a mode. For example, mode "inherit_ctrl_and_mon"
> does not have an associated resource group and should thus not display one,
Correct.
> "global_assign_ctrl_inherit_mon_per_cpu" can only be assigned a control group and
> should thus not display a monitor group also.
Yes. True. In that case "mon" is empty. It will print correctly. Let me
see if I can optimize this section.
Thanks
Babu
^ permalink raw reply
* Re: htmldocs: Documentation/scheduler/sched-arch.rst:108: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils]
From: Randy Dunlap @ 2026-06-18 19:33 UTC (permalink / raw)
To: Shrikanth Hegde, kernel test robot; +Cc: oe-kbuild-all, linux-doc
In-Reply-To: <f1a4c4c7-9ad8-40f5-b1a9-ba631977dac6@linux.ibm.com>
On 6/17/26 10:19 PM, Shrikanth Hegde wrote:
>
>
> On 6/18/26 10:40 AM, kernel test robot wrote:
>> tree: https://github.com/intel-lab-lkp/linux/commits/Shrikanth-Hegde/sched-debug-Remove-unused-schedstats/20260618-031604
>> head: bcb0c494e4af36dd6306a5a1839a0c03046053af
>> commit: 4c29e4f3ba22adc04fc456620f2c6abf539d76df sched/docs: Document cpu_preferred_mask and Preferred CPU concept
>> date: 10 hours ago
>> compiler: clang version 22.1.8 (https://github.com/llvm/llvm-project ca7933e47d3a3451d81e72ac174dcb5aa28b59d1)
>> docutils: docutils (Docutils 0.21.2, Python 3.13.5, on linux)
>> reproduce: (https://download.01.org/0day-ci/archive/20260618/202606180717.yNM0yb41-lkp@intel.com/reproduce)
>>
>> If you fix the issue in a separate patch/commit (i.e. not just a new version of
>> the same patch/commit), kindly add following tags
>> | Reported-by: kernel test robot <lkp@intel.com>
>> | Closes: https://lore.kernel.org/oe-kbuild-all/202606180717.yNM0yb41-lkp@intel.com/
>>
>> All warnings (new ones prefixed by >>):
>>
>> Checksumming on output with GSO
>> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [docutils]
>> MAINTAINERS:40: WARNING: Inline strong start-string without end-string. [docutils]
>> Documentation/scheduler/sched-arch.rst:107: ERROR: Unexpected indentation. [docutils]
>>>> Documentation/scheduler/sched-arch.rst:108: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils]
>> Documentation/userspace-api/landlock:504: ./security/landlock/errata/abi-4.h:5: ERROR: Unexpected section title.
>>
>>
>> vim +108 Documentation/scheduler/sched-arch.rst
>>
>> 102
>> 103 Notes:
>> 104 1. This feature is available under CONFIG_PREFERRED_CPU
>> 105 2. This feature works for FAIR class only.
>> 106 3. A task pinned, which can't be moved to preferred CPUs will continue
>> 107 to run based on its affinity. But no load balancing happens
>
> is it flagging here due to missing . ?
No, but you could add that anyway.
>> > 108 4. If needed, steal time based governors/arch dependent method
>> 109 could be used to cater to different types of cpu numbers.
>> 110 Arch can do so by implementing its own hooks.
>> 111 5. Decision to use/not use is driven by kernel. Hence it shouldn't
>> 112 break user affinities. One of the main reason why CPU hotplug
>> 113 or Isolated cpuset partitions was not a solution.
>> 114
It wants a blank line between each list item (if the list items are multi-line).
For the list above this one (3 items, all single line), blank lines aren't needed.
[These comments come from testing, not reading specs.]
I made these changes and a couple of others to make the rendered html look
reasonable.
Use (or not).
---
From: Shrikanth Hegde <sshegde@linux.ibm.com>
To: linux-kernel@vger.kernel.org, mingo@kernel.org, peterz@infradead.org, juri.lelli@redhat.com, vincent.guittot@linaro.org, yury.norov@gmail.com, kprateek.nayak@amd.com, iii@linux.ibm.com
Cc: sshegde@linux.ibm.com, tglx@kernel.org, gregkh@linuxfoundation.org, pbonzini@redhat.com, seanjc@google.com, vschneid@redhat.com, huschle@linux.ibm.com, rostedt@goodmis.org, dietmar.eggemann@arm.com, mgorman@suse.de, bsegall@google.com, maddy@linux.ibm.com, srikar@linux.ibm.com, hdanton@sina.com, chleroy@kernel.org, vineeth@bitbyteword.org, frederic@kernel.org, arighi@nvidia.com, pauld@redhat.com, christian.loehle@arm.com, tj@kernel.org, tommaso.cucinotta@gmail.com, maz@kernel.org, rafael@kernel.org
Subject: [PATCH v4 02/20] sched/docs: Document cpu_preferred_mask and Preferred CPU concept
Date: Wed, 17 Jun 2026 23:11:21 +0530
Message-ID: <20260617174139.155540-3-sshegde@linux.ibm.com>
Add documentation for new cpumask called cpu_preferred_mask. This could
help users in understanding what this mask is and the concept behind it.
Document how to enable it and implementation aspects of it.
Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
v3->v4:
- update docs to reflect preferred is subset of active.
Documentation/scheduler/sched-arch.rst | 61 ++++++++++++++++++++++-
1 file changed, 59 insertions(+), 2 deletions(-)
--- linux-next.orig/Documentation/scheduler/sched-arch.rst
+++ linux-next/Documentation/scheduler/sched-arch.rst
@@ -6,7 +6,8 @@ CPU Scheduler implementation hints for a
Context switch
==============
-1. Runqueue locking
+Runqueue locking
+
By default, the switch_to arch function is called with the runqueue
locked. This is usually not a problem unless switch_to may need to
take the runqueue lock. This is usually due to a wake up operation in
@@ -62,11 +63,67 @@ Your cpu_idle routines need to obey the
arch/x86/kernel/process.c has examples of both polling and
sleeping idle functions.
+Preferred CPUs
+==============
+
+In virtualised environments it is possible to overcommit CPU resources.
+i.e sum of virtual CPU(vCPU) of all VM's is greater than number of physical
+CPUs(pCPU). Under such conditions when all or many VM's have high utilization,
+hypervisor won't be able to satisfy the CPU requirement and has to context
+switch within or across VM. i.e hypervisor need to preempt one vCPU to run
+another. This is called vCPU preemption. This is more expensive compared to
+task context switch within a vCPU.
+
+In such cases it is better that combined vCPU ask from all VM is reduced
+by not using some of the vCPUs. vCPUs where workload can be safely
+scheduled which won't increase any contention for pCPU are called as
+"Preferred CPUs".
+
+In most cases preferred CPUs will be same as active CPUs, when there is pCPU
+contention, Preferred CPUs will reduce based on the amount of steal time.
+When the pCPU contention goes away as indicated by steal time, Preferred CPUs
+will become same as active CPUs again. One has to enable the feature by
+writing 1 to /sys/kernel/debug/sched/steal_monitor/enable
+
+One of the design construct is preferred CPUs is always subset of active CPUs.
+With CONFIG_PREFERRED_CPU=n, it is same as active CPUs.
+
+For scheduling decisions such as wakeup, pushing the task etc, needs this
+CPU state info. This is maintained in cpu_preferred_mask.
+
+vCPUs which are not in cpu_preferred_mask should be treated as vCPUs which
+should not be used at this moment provided it doesn't break user affinity.
+This is achieved by:
+
+1. Selecting a preferred CPU at wakeup.
+2. Push the task away from non-preferred CPU at tick.
+3. Only select preferred CPUs for load balance.
+
+/sys/devices/system/cpu/preferred prints the current cpu_preferred_mask in
+cpulist format.
+
+Notes:
+
+1. This feature is available under CONFIG_PREFERRED_CPU
+
+2. This feature works for FAIR class only.
+
+3. A task pinned, which can't be moved to preferred CPUs will continue
+ to run based on its affinity. But no load balancing happens
+
+4. If needed, steal time based governors/arch dependent method
+ could be used to cater to different types of cpu numbers.
+ Arch can do so by implementing its own hooks.
+
+5. Decision to use/not use is driven by kernel. Hence it shouldn't
+ break user affinities. One of the main reason why CPU hotplug
+ or Isolated cpuset partitions was not a solution.
Possible arch/ problems
=======================
Possible arch problems I found (and either tried to fix or didn't):
-sparc - IRQs on at this point(?), change local_irq_save to _disable.
+sparc:
+ - IRQs on at this point(?), change local_irq_save to _disable.
- TODO: needs secondary CPUs to disable preempt (See #1)
^ permalink raw reply
* Re: [PATCH v3 06/13] tick/nohz, context_tracking: Prepare for runtime nohz_full updates
From: Thomas Gleixner @ 2026-06-18 19:49 UTC (permalink / raw)
To: Jing Wu, Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
Valentin Schneider, Paul E. McKenney, Frederic Weisbecker,
Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
Uladzislau Rezki, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
Anna-Maria Behnsen, Tejun Heo, Jonathan Corbet, Shuah Khan,
Shuah Khan
Cc: linux-kernel, rcu, cgroups, linux-doc, linux-kselftest, Jing Wu,
Qiliang Yuan
In-Reply-To: <87ik7fep2j.ffs@fw13>
On Thu, Jun 18 2026 at 19:27, Thomas Gleixner wrote:
> On Thu, Jun 18 2026 at 11:11, Jing Wu wrote:
>> Remove __init from ct_cpu_track_user() and __initdata from the
>> initialized flag so context tracking can be activated on CPUs that
>> join nohz_full at runtime. Drop the __ro_after_init attribute from
>> the context_tracking_key static key, allowing static_branch_dec()
>> when a CPU leaves nohz_full.
>>
>> Add ct_cpu_untrack_user() to reverse ct_cpu_track_user(), decrementing
>> the static key and clearing the per-CPU tracking state.
>
> Please do not enumerate WHAT the patch is doing. Explain the context and
> the WHY
>
> https://docs.kernel.org/process/maintainer-tip.html#changelog
Just for the record. I told your colleague the same thing already....
^ permalink raw reply
* Re: [PATCH v3 08/13] genirq: Add explicit housekeeping callback for managed IRQ migration
From: Thomas Gleixner @ 2026-06-18 20:27 UTC (permalink / raw)
To: Jing Wu, Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
Valentin Schneider, Paul E. McKenney, Frederic Weisbecker,
Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
Uladzislau Rezki, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
Anna-Maria Behnsen, Tejun Heo, Jonathan Corbet, Shuah Khan,
Shuah Khan, Waiman Long
Cc: linux-kernel, rcu, cgroups, linux-doc, linux-kselftest, Jing Wu,
Qiliang Yuan
In-Reply-To: <20260618-wujing-dhm-v3-8-28f1a4d83b68@gmail.com>
On Thu, Jun 18 2026 at 11:11, Jing Wu wrote:
> +
> +/*
> + * Managed IRQ housekeeping callback: iterate all managed IRQs and ask
S/IRQ/interrupt/
> + * the chip to move them off CPUs newly removed from HK_TYPE_MANAGED_IRQ.
Also this doesn't ask the chip to move it.
> + */
> +static void irq_hk_apply(enum hk_type type)
> +{
> + cpumask_var_t hk_mask;
> + struct irq_desc *desc;
> + unsigned int irq;
> +
> + if (!alloc_cpumask_var(&hk_mask, GFP_KERNEL))
> + return;
> +
> + /*
> + * Snapshot the new HK_TYPE_MANAGED_IRQ mask under an RCU read lock
> + * before iterating IRQ descriptors. The lockdep annotation in
> + * housekeeping_cpumask() requires an RCU read-side critical section
> + * for runtime-mutable types.
> + */
> + rcu_read_lock();
> + cpumask_copy(hk_mask, housekeeping_cpumask_rcu(HK_TYPE_MANAGED_IRQ));
> + rcu_read_unlock();
Same comments as in the nohz patch.
> +
> + irq_lock_sparse();
> +
> + for_each_active_irq(irq) {
> + desc = irq_to_desc(irq);
> + if (!desc || !desc->action)
> + continue;
> +
for (unsigned int irq = 0; irq < total_nr_irqs; irq++) {
struct irq_desc *desc;
scoped_guard(rcu)
desc = irq_find_desc_at_or_after(irq);
....
> + /*
> + * Only managed interrupts are selected: they have
> + * IRQF_AFFINITY_MANAGED set, meaning the kernel owns their
> + * affinity. User-controlled IRQs are intentionally skipped.
> + *
> + * When the intersection of the current affinity mask and the
> + * new housekeeping mask is non-empty, re-apply the restricted
> + * affinity to migrate the IRQ away from newly isolated CPUs.
> + * If the intersection is empty (all serving CPUs are now
> + * isolated), the IRQ is left on its current CPU temporarily;
> + * handling that case (IRQ shutdown / re-startup) is left for
> + * a follow-up.
Oh well...
> + */
> + if (irqd_affinity_is_managed(&desc->irq_data)) {
So you set the affinity even on an interrupt which is shutdown?
> + const struct cpumask *mask;
> + struct cpumask *tmp = this_cpu_ptr(&__tmp_mask);
> +
> + raw_spin_lock_irq(&desc->lock);
guard()
> + mask = irq_data_get_affinity_mask(&desc->irq_data);
> + cpumask_and(tmp, mask, hk_mask);
> + if (cpumask_intersects(tmp, cpu_online_mask))
> + irq_do_set_affinity(&desc->irq_data, tmp, false);
That's completely broken. You _cannot_ change the affinity mask of a
managed interrupt. The mask itself is immutable.
The effective affinity can be changed by invoking the affinity setter
with the original unmodified mask. irq_do_set_affinity() already deals
with the housekeeping mask.
Also invoking irq_do_set_affinity() directly here is just wrong. It
breaks interrupts which cannot be moved in process context.
But even if that is fixed, then there is zero coordination with the
affected drivers/subsystems. Managed interrupts are related to device
and block queues and you cannot change one without the other. Neither
can you stop managed interrupts without quiescing the related device
queue. Starting them up requires also to reenable the device queue.
This problem needs to be fixed no matter what. See below.
> +static int irq_hk_validate(enum hk_type type,
> + const struct cpumask *cur_mask,
> + const struct cpumask *new_mask)
> +{
> + if (!IS_ENABLED(CONFIG_SMP))
> + return -EOPNOTSUPP;
> + return 0;
Seriously? Why is this stuff even built when CONFIG_SMP=n?
So these validate callback seem to be just another voodoo container for
no value.
While this series might work for you by some definition of "works", it's
broken beyond repair and it's really annoying that I explained all of it
to the other people who try to solve that very same problem. Of course
you did not read any of that otherwise you would have CC'ed them.
https://lore.kernel.org/lkml/87o6jcb84w.ffs@tglx
Trying to do that without taking the CPUs mostly offline and bringing
them online again is not going to work and there is zero benefit trying
to avoid that. First of all changing the isolation is not a hotpath
operation. Doing it one by one without bringing the CPU completely down
as I outlined in the above linked mail is not much more disruptive than
trying to do all of this on the fly. If you isolate a CPU then the tasks
on that CPU which do not belong to the isolation set need to get off the
CPU anyway. If you unisolate a CPU then it's really not a problem
whether the non-isolated tasks can move on it 10 milliseconds earlier or
later.
If you want to solve all the problems related to NOHZ, managed
interrupts, RCU etc. without the hotplug machinery then you end up
replicating half of it. Don't even try to think about it, that's a
complete waste of time and won't go anywhere.
Fix the few issues which are related to hotplug that I described in the
above linked mail and use the fully correct and tested common code for
your isolation muck. Please coordinate with Waiman or whoever is working
on it at RH right now.
Thanks,
tglx
^ permalink raw reply
* Re: [PATCH v3 10/13] sched: Guard sched_tick_start/stop against uninitialized tick_work_cpu
From: Thomas Gleixner @ 2026-06-18 20:50 UTC (permalink / raw)
To: Jing Wu, Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
Valentin Schneider, Paul E. McKenney, Frederic Weisbecker,
Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
Uladzislau Rezki, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
Anna-Maria Behnsen, Tejun Heo, Jonathan Corbet, Shuah Khan,
Shuah Khan
Cc: linux-kernel, rcu, cgroups, linux-doc, linux-kselftest, Jing Wu,
Qiliang Yuan
In-Reply-To: <20260618-wujing-dhm-v3-10-28f1a4d83b68@gmail.com>
On Thu, Jun 18 2026 at 11:11, Jing Wu wrote:
> sched_tick_start() and sched_tick_stop() are called during CPU hotplug
> for CPUs not in the HK_TYPE_KERNEL_NOISE set. They dereference
> tick_work_cpu, which is allocated by sched_tick_offload_init() and only
> called from housekeeping_init() when nohz_full= is present at boot.
>
> When the DHM subsystem first-enables HK_TYPE_KERNEL_NOISE at runtime via
> housekeeping_update_types(), tick_work_cpu remains NULL because
> sched_tick_offload_init() is __init-only and cannot be re-invoked. A
> subsequent CPU offline/online cycle for an isolated CPU triggers
> WARN_ON_ONCE(!tick_work_cpu) followed by a NULL-pointer dereference in
> per_cpu_ptr(tick_work_cpu, cpu), crashing the kernel.
>
> Since nohz_full= was not active at boot, tick_nohz_full_running remains
> false and the tick-offload infrastructure is never activated; isolated
> CPUs continue to receive their own ticks. Guard both helpers with an
> additional !tick_work_cpu check so they become no-ops in this case.
This is the same fake functionality as with the tick itself. Seriously?
> - if (housekeeping_cpu(cpu, HK_TYPE_KERNEL_NOISE))
> + if (housekeeping_cpu(cpu, HK_TYPE_KERNEL_NOISE) || !tick_work_cpu)
> return;
>
> WARN_ON_ONCE(!tick_work_cpu);
> @@ -5799,7 +5799,7 @@ static void sched_tick_stop(int cpu)
> struct tick_work *twork;
> int os;
>
> - if (housekeeping_cpu(cpu, HK_TYPE_KERNEL_NOISE))
> + if (housekeeping_cpu(cpu, HK_TYPE_KERNEL_NOISE) || !tick_work_cpu)
> return;
>
> WARN_ON_ONCE(!tick_work_cpu);
Brilliant stuff that. Guard against tick_work_cpu == NULL and then keep
the WARN_ON() there, which became completely pointless.
But that's all just mindless tinkering and fixing the symptoms.
If all of this is runtime managed, then all the initialization needs to
be made unconditional. Yes, that wastes a few bytes of memory per CPU if
it's not used, but avoids these completely inconsistent hacks all over
the place and provides a coherent user interface.
Stop trying to duct tape this in. This needs more thoughts than just
sprinkling works a few works for me hacks all over the place.
Thanks,
tglx
^ permalink raw reply
* Re: [PATCH v3 11/13] cgroup/cpuset: Extend isolated partition to trigger kernel-noise isolation
From: Thomas Gleixner @ 2026-06-18 20:55 UTC (permalink / raw)
To: Jing Wu, Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
Valentin Schneider, Paul E. McKenney, Frederic Weisbecker,
Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
Uladzislau Rezki, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
Anna-Maria Behnsen, Tejun Heo, Jonathan Corbet, Shuah Khan,
Shuah Khan
Cc: linux-kernel, rcu, cgroups, linux-doc, linux-kselftest, Jing Wu,
Qiliang Yuan
In-Reply-To: <20260618-wujing-dhm-v3-11-28f1a4d83b68@gmail.com>
On Thu, Jun 18 2026 at 11:11, Jing Wu wrote:
>
> if (update_housekeeping) {
> + static const unsigned long noise_types =
> + BIT(HK_TYPE_KERNEL_NOISE) | BIT(HK_TYPE_MANAGED_IRQ);
> +
> update_housekeeping = false;
> cpumask_copy(isolated_hk_cpus, isolated_cpus);
>
> - /*
> - * housekeeping_update() is now called without holding
> - * cpus_read_lock and cpuset_mutex. Only cpuset_top_mutex
> - * is still being held for mutual exclusion.
> - */
Why are you randomly removing useful comments?
^ permalink raw reply
* Re: [PATCH v3 05/13] cpu/hotplug: Reserve CPUHP states for nohz_full and managed IRQ down-paths
From: Thomas Gleixner @ 2026-06-18 21:01 UTC (permalink / raw)
To: Jing Wu, Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
Valentin Schneider, Paul E. McKenney, Frederic Weisbecker,
Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
Uladzislau Rezki, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
Anna-Maria Behnsen, Tejun Heo, Jonathan Corbet, Shuah Khan,
Shuah Khan
Cc: linux-kernel, rcu, cgroups, linux-doc, linux-kselftest, Jing Wu,
Qiliang Yuan
In-Reply-To: <871pe3de9b.ffs@fw13>
On Thu, Jun 18 2026 at 18:06, Thomas Gleixner wrote:
> On Thu, Jun 18 2026 at 11:11, Jing Wu wrote:
>> Add CPUHP_AP_NO_HZ_FULL_DYING and CPUHP_AP_IRQ_AFFINITY_DYING to the
>> cpuhp_state enum. These dying callbacks are invoked during CPU offline
>> before the tick is stopped, enabling clean tick handover and managed
>> IRQ migration when a CPU transitions between isolated and housekeeping
>> states.
>>
>> The existing CPUHP_AP_IRQ_AFFINITY_ONLINE already handles managed IRQ
>> restoration on CPU online. The new dying callback completes the pair,
>> migrating managed interrupts away from the CPU before it goes down.
>
> What? They are migrated away today already when the CPU goes down unless
> the CPU is the last one in the affinity set of the interrupt. So why do
> you need a new step for something which already exists?
Aside of that these hotplug states are not used at all. So what is this
patch for?
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox