Linux Documentation
 help / color / mirror / Atom feed
* Re: [PATCH v6 03/12] PCI: liveupdate: Track incoming preserved PCI devices
From: Samiullah Khawaja @ 2026-06-16 20:09 UTC (permalink / raw)
  To: David Matlack
  Cc: 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, Mike Rapoport, Parav Pandit, Pasha Tatashin,
	Pranjal Shrivastava, Pratyush Yadav, Saeed Mahameed, Shuah Khan,
	Vipin Sharma, William Tu, Yi Liu
In-Reply-To: <20260522202410.3104264-4-dmatlack@google.com>

On Fri, May 22, 2026 at 08:24:01PM +0000, David Matlack wrote:
>During PCI enumeration, the previous kernel might have passed state about
>devices that were preserved across kexec. The PCI core needs to fetch
>this state to identify which devices are "incoming" and require special
>handling.
>
>Add pci_liveupdate_setup_device() which is called during device setup
>to fetch the serialized state (struct pci_ser) from the Live Update
>Orchestrator. The first time this happens, pci_flb_retrieve() will run
>and convert the array of pci_dev_ser structs into an xarray so that it
>can be looked up efficiently.
>
>If a device is found in the xarray, the PCI core stores a pointer to its
>state in dev->liveupdate_incoming and holds a reference to the incoming
>FLB until pci_liveupdate_finish() is called by the driver.
>
>This ensures proper lifecycle management for incoming preserved devices
>and allows the PCI core and drivers to apply specific Live Update
>logic to them in subsequent commits.
>
>Drivers can check if a device is an incoming preserved device (e.g.
>during probe) by calling pci_liveupdate_is_incoming().
>
>CONFIG_64BIT is now required to enable CONFIG_PCI_LIVEUPDATE so that the
>domain and bdf can be guaranteed to fit in an unsigned long and be used
>as the xarray key.
>
>Signed-off-by: David Matlack <dmatlack@google.com>
>---
> MAINTAINERS                    |   1 +
> drivers/pci/Kconfig            |   2 +-
> drivers/pci/liveupdate.c       | 230 ++++++++++++++++++++++++++++++++-
> drivers/pci/liveupdate.h       |   5 +
> drivers/pci/probe.c            |   3 +
> include/linux/pci_liveupdate.h |  13 ++
> 6 files changed, 251 insertions(+), 3 deletions(-)
>

[snip]
>
> static int pci_flb_retrieve(struct liveupdate_flb_op_args *args)
> {
>-	args->obj = phys_to_virt(args->data);
>+	struct pci_ser *ser = phys_to_virt(args->data);
>+	struct pci_flb_incoming *incoming;
>+	int ret = -ENOMEM;
>+	u32 i;
>+
>+	incoming = kmalloc_obj(*incoming);
>+	if (!incoming)
>+		goto err_restore_free;
>+
>+	incoming->ser = ser;
>+	xa_init(&incoming->xa);
>+
>+	for (i = 0; i < incoming->ser->max_nr_devices; i++) {
>+		struct pci_dev_ser *dev_ser = &incoming->ser->devices[i];
>+		unsigned long key;
>+
>+		if (!dev_ser->refcount)
>+			continue;
>+
>+		key = pci_ser_xa_key(dev_ser->domain, dev_ser->bdf);
>+		ret = xa_insert(&incoming->xa, key, dev_ser, GFP_KERNEL);
>+		if (ret)
>+			goto err_xa_destroy;
>+	}
>+
>+	args->obj = incoming;
> 	return 0;
>+
>+err_xa_destroy:
>+	xa_destroy(&incoming->xa);
>+	kfree(incoming);
>+err_restore_free:
>+	kho_restore_free(ser);
>+	return ret;

Hmm.. This is interesting, so the KHO state is freed and it cannot be
reused. I see you already pointed out that we are putting an LUO policy
to say that the retry is not allowed.

But what should be the behaviour of liveupdate in this regard? Let the
system boot in a normal way? This might break other subsystems as they
might depend on PCIe restoring state properly. Also I think some of the
PCIe state, like device-id, BAR addresses, ACLs etc, might be used as
source of truth by other components.

For example, lets say FLB retrieve() of PCIe fails, but succeeds for
VFIO/IOMMU, now VFIO/IOMMU are restoring state of a device that is not
restored/preserved?

Should this be considered fatal?
> }
>
> static void pci_flb_finish(struct liveupdate_flb_op_args *args)
> {
>-	kho_restore_free(args->obj);
>+	struct pci_flb_incoming *incoming = args->obj;
>+
>+	xa_destroy(&incoming->xa);
>+	kho_restore_free(incoming->ser);
>+	kfree(incoming);
> }
>
> static struct liveupdate_flb_ops pci_liveupdate_flb_ops = {
>@@ -270,6 +335,91 @@ void pci_liveupdate_unpreserve(struct pci_dev *dev)
> }
> EXPORT_SYMBOL_GPL(pci_liveupdate_unpreserve);
>
>+static struct pci_flb_incoming *pci_liveupdate_flb_get_incoming(void)
>+{
>+	struct pci_flb_incoming *incoming = NULL;
>+	int ret;
>+
>+	ret = liveupdate_flb_get_incoming(&pci_liveupdate_flb, (void **)&incoming);
>+
>+	/* Live Update is not enabled. */
>+	if (ret == -EOPNOTSUPP)
>+		return NULL;
>+
>+	/* Live Update is enabled, but there is no incoming FLB data. */
>+	if (ret == -ENODATA)
>+		return NULL;
>+
>+	/*
>+	 * Live Update is enabled and there is incoming FLB data, but none of it
>+	 * matches pci_liveupdate_flb.compatible.
>+	 *
>+	 * This could mean that no PCI FLB data was passed by the previous
>+	 * kernel, but it could also mean the previous kernel used a different
>+	 * compatibility string (i.e. a different ABI).
>+	 */
>+	if (ret == -ENOENT) {
>+		pr_info_once("No incoming FLB matched %s\n", pci_liveupdate_flb.compatible);
>+		return NULL;
>+	}
>+
>+	/*
>+	 * There is incoming FLB data that matches pci_liveupdate_flb.compatible
>+	 * but it cannot be retrieved.
>+	 */
>+	if (ret) {
>+		WARN_ONCE(ret, "Failed to retrieve incoming FLB data\n");

I think this should probably be considered fatal as mentioned above or
the caller of this function should get an error so it can fail. I think
retrievel of preserved state should generally not fail unless there is
memory corruption or ABI is incompatible.
>+		return NULL;
>+	}
>+
>+	return incoming;
>+}
>+

[snip]
>+
>+static inline bool pci_liveupdate_is_incoming(struct pci_dev *dev)
>+{
>+	return false;
>+}
> #endif
>
> #endif /* LINUX_PCI_LIVEUPDATE_H */
>-- 
>2.54.0.746.g67dd491aae-goog
>

Sami

^ permalink raw reply

* Re: [swap tier discussion] Re: [PATCH v3 2/4] mm/zswap: Implement proactive writeback
From: Nhat Pham @ 2026-06-16 20:08 UTC (permalink / raw)
  To: Yosry Ahmed
  Cc: YoungJun Park, Shakeel Butt, Hao Jia, Johannes Weiner, mhocko, tj,
	mkoutny, roman.gushchin, akpm, chengming.zhou, muchun.song,
	cgroups, linux-mm, linux-kernel, linux-doc, Hao Jia, chrisl,
	kasong, baoquan.he, joshua.hahnjy
In-Reply-To: <CAO9r8zOD7XaJ0Uo_LLLDTRKbeTOmAwmM3q8q6rUyH3oS-X3Csw@mail.gmail.com>

On Tue, Jun 16, 2026 at 3:54 PM Yosry Ahmed <yosry@kernel.org> wrote:
>
> On Tue, Jun 16, 2026 at 11:33 AM Nhat Pham <nphamcs@gmail.com> wrote:
> >
> > TBH, without vswap, we should not allow setting zswap as its own tier.
> > It's meaningless. Maybe makes it a no-op, and warn users what they're
> > setting is gibberish?
>
> Why? vswap is transparent to the user. Why can't zswap be its own tier?

Without vswap, if you set zswap as its own tier, which phys swap
device should we allocate from for the backing slot? :)

With vswap then it makes sense (and would probably be the "default"
for zswap setup until we enable zswap writeback).

^ permalink raw reply

* Re: [swap tier discussion] Re: [PATCH v3 2/4] mm/zswap: Implement proactive writeback
From: Yosry Ahmed @ 2026-06-16 19:54 UTC (permalink / raw)
  To: Nhat Pham
  Cc: YoungJun Park, Shakeel Butt, Hao Jia, Johannes Weiner, mhocko, tj,
	mkoutny, roman.gushchin, akpm, chengming.zhou, muchun.song,
	cgroups, linux-mm, linux-kernel, linux-doc, Hao Jia, chrisl,
	kasong, baoquan.he, joshua.hahnjy
In-Reply-To: <CAKEwX=Nz9SWcEVQGQjHN8P8OANJY4BG0w+iQOzoNOWuteoVjAg@mail.gmail.com>

On Tue, Jun 16, 2026 at 11:33 AM Nhat Pham <nphamcs@gmail.com> wrote:
>
> On Tue, Jun 16, 2026 at 1:31 PM Yosry Ahmed <yosry@kernel.org> wrote:
> >
> > On Mon, Jun 15, 2026 at 8:08 PM YoungJun Park <youngjun.park@lge.com> wrote:
> > >
> > > ...
> > > > - "zswap tier only": Only zswap is allowed. Fallback to other swap is
> > > >   blocked.
> > > > - "zswap writeback disabled": zswap is allowed, but if zswap_store()
> > > >   fails, pages can still fall back to other swap devices.
> > >
> > > Upon double-checking the code, my previous clarification was wrong.
> > > You are right. Sorry for the confusion. "zswap tier only" is indeed
> > > equivalent to "zswap writeback disabled".
> > > (I'm not sure why I read the code that way...)
> > >
> > > As I initially thought, it might be possible to replace the zswap writeback
> > > control with the tiering mechanism.
> > >
> > > If we need to keep the existing interface, we can integrate or share the
> > > underlying logic (though the specific details need more thought anyway).
> > >
> > > It can be summarized as follows:
> > >
> > > - "zswap tier only" + "zswap writeback disable" -> meaningless (noop)
> > > - "zswap tier only" + "zswap writeback enable" -> meaningless (no writabck backend exist)
> > > - "zswap tier with other tiers" + "zswap writeback disable" -> uses only zswap
> > >   (can be replaced by "zswap tier only". This code could be intergrated, modified or something.)
> > > - "zswap tier with other tiers" + "zswap writeback enable" -> works as is
>
> TBH, without vswap, we should not allow setting zswap as its own tier.
> It's meaningless. Maybe makes it a no-op, and warn users what they're
> setting is gibberish?

Why? vswap is transparent to the user. Why can't zswap be its own tier?

^ permalink raw reply

* Re: [swap tier discussion] Re: [PATCH v3 2/4] mm/zswap: Implement proactive writeback
From: Nhat Pham @ 2026-06-16 18:32 UTC (permalink / raw)
  To: Yosry Ahmed
  Cc: YoungJun Park, Shakeel Butt, Hao Jia, Johannes Weiner, mhocko, tj,
	mkoutny, roman.gushchin, akpm, chengming.zhou, muchun.song,
	cgroups, linux-mm, linux-kernel, linux-doc, Hao Jia, chrisl,
	kasong, baoquan.he, joshua.hahnjy
In-Reply-To: <CAO9r8zMimM8n54BL1viuX3pYzO=wzQU89LhCF1HW0bAv97ZQtg@mail.gmail.com>

On Tue, Jun 16, 2026 at 1:31 PM Yosry Ahmed <yosry@kernel.org> wrote:
>
> On Mon, Jun 15, 2026 at 8:08 PM YoungJun Park <youngjun.park@lge.com> wrote:
> >
> > ...
> > > - "zswap tier only": Only zswap is allowed. Fallback to other swap is
> > >   blocked.
> > > - "zswap writeback disabled": zswap is allowed, but if zswap_store()
> > >   fails, pages can still fall back to other swap devices.
> >
> > Upon double-checking the code, my previous clarification was wrong.
> > You are right. Sorry for the confusion. "zswap tier only" is indeed
> > equivalent to "zswap writeback disabled".
> > (I'm not sure why I read the code that way...)
> >
> > As I initially thought, it might be possible to replace the zswap writeback
> > control with the tiering mechanism.
> >
> > If we need to keep the existing interface, we can integrate or share the
> > underlying logic (though the specific details need more thought anyway).
> >
> > It can be summarized as follows:
> >
> > - "zswap tier only" + "zswap writeback disable" -> meaningless (noop)
> > - "zswap tier only" + "zswap writeback enable" -> meaningless (no writabck backend exist)
> > - "zswap tier with other tiers" + "zswap writeback disable" -> uses only zswap
> >   (can be replaced by "zswap tier only". This code could be intergrated, modified or something.)
> > - "zswap tier with other tiers" + "zswap writeback enable" -> works as is

TBH, without vswap, we should not allow setting zswap as its own tier.
It's meaningless. Maybe makes it a no-op, and warn users what they're
setting is gibberish?


>
> Hmm we might want to somehow disable memory.zswap.writeback if tiering
> is enabled, to avoid having to deal with this. But I am not sure how
> possible this is.

With tiering and without vswap, you still need an interface to
prescribe that a cgroup can:

1. Allocate slots from a certain swap device.

2. Use zswap, backed by those slots.

3. But no IO is allowed to those slots (no writeback or fallback on
zswap failure).

So we still need memory.zswap.writeback, until we get rid of non-vswap
case for zswap.

^ permalink raw reply

* Re: [PATCH] docs: infiniband: correct name of option to enable the ib_uverbs module
From: Jason Gunthorpe @ 2026-06-16 17:48 UTC (permalink / raw)
  To: Ethan Nelson-Moore
  Cc: Shuah Khan, Dongliang Mu, linux-rdma, linux-doc, Leon Romanovsky,
	Jonathan Corbet, Alex Shi, Yanteng Si
In-Reply-To: <20260616002027.67925-1-enelsonmoore@gmail.com>

On Mon, Jun 15, 2026 at 05:20:23PM -0700, Ethan Nelson-Moore wrote:
> The Infiniband documentation states that CONFIG_INFINIBAND_USER_VERBS
> should be used to enable the ib_uverbs module. However, this option was
> renamed to CONFIG_INFINIBAND_USER_ACCESS in commit 17781cd6186c
> ("[PATCH] IB: clean up user access config options"). Update the
> documentation to reflect this.
> 
> Signed-off-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>
> Reviewed-by: Dongliang Mu <dzm91@hust.edu.cn>
> ---
>  Documentation/infiniband/user_verbs.rst                    | 2 +-
>  Documentation/translations/zh_CN/infiniband/user_verbs.rst | 2 +-
>  2 files changed, 2 insertions(+), 2 deletions(-)

It seems simple enough I picked it up

Thanks,
Jason

^ permalink raw reply

* Re: [swap tier discussion] Re: [PATCH v3 2/4] mm/zswap: Implement proactive writeback
From: Yosry Ahmed @ 2026-06-16 17:30 UTC (permalink / raw)
  To: YoungJun Park
  Cc: Shakeel Butt, Hao Jia, Johannes Weiner, mhocko, tj, mkoutny,
	roman.gushchin, Nhat Pham, akpm, chengming.zhou, muchun.song,
	cgroups, linux-mm, linux-kernel, linux-doc, Hao Jia, chrisl,
	kasong, baoquan.he, joshua.hahnjy
In-Reply-To: <ajC+FNpkVpI4pbBz@yjaykim-PowerEdge-T330>

On Mon, Jun 15, 2026 at 8:08 PM YoungJun Park <youngjun.park@lge.com> wrote:
>
> ...
> > - "zswap tier only": Only zswap is allowed. Fallback to other swap is
> >   blocked.
> > - "zswap writeback disabled": zswap is allowed, but if zswap_store()
> >   fails, pages can still fall back to other swap devices.
>
> Upon double-checking the code, my previous clarification was wrong.
> You are right. Sorry for the confusion. "zswap tier only" is indeed
> equivalent to "zswap writeback disabled".
> (I'm not sure why I read the code that way...)
>
> As I initially thought, it might be possible to replace the zswap writeback
> control with the tiering mechanism.
>
> If we need to keep the existing interface, we can integrate or share the
> underlying logic (though the specific details need more thought anyway).
>
> It can be summarized as follows:
>
> - "zswap tier only" + "zswap writeback disable" -> meaningless (noop)
> - "zswap tier only" + "zswap writeback enable" -> meaningless (no writabck backend exist)
> - "zswap tier with other tiers" + "zswap writeback disable" -> uses only zswap
>   (can be replaced by "zswap tier only". This code could be intergrated, modified or something.)
> - "zswap tier with other tiers" + "zswap writeback enable" -> works as is

Hmm we might want to somehow disable memory.zswap.writeback if tiering
is enabled, to avoid having to deal with this. But I am not sure how
possible this is.

If swap tiering is behind a config option, maybe we can disable
memory.zswap.writeback under that config option? But then if distros
start enabling the config option it might break some users. Not sure
what's the right way to do this, but having both interfaces active at
the same time is annoying.

>
> As mentioned in the previous email, the zswap tier on/off control comes as a
> bonus (though, as you pointed out, we may need to discuss if it's actually
> needed).
>
> BR,
> Youngjun

^ permalink raw reply

* Re: [PATCH net-next V3 2/7] netdevsim: Register devlink after device init
From: Mark Bloch @ 2026-06-16 17:29 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko
  Cc: Eric Dumazet, Paolo Abeni, Andrew Lunn, David S. Miller,
	Jonathan Corbet, Shuah Khan, Jiri Pirko, Simon Horman,
	Sunil Goutham, Linu Cherian, Geetha sowjanya, hariprasad,
	Subbaraya Sundeep, Bharat Bhushan, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Ethan Nelson-Moore, linux-doc,
	netdev, linux-rdma
In-Reply-To: <f266dfa5-0c6c-4be0-b73e-b2185dadd6a7@nvidia.com>



On 11/06/2026 20:43, Mark Bloch wrote:
> 
> 
> On 11/06/2026 18:54, Jakub Kicinski wrote:
>> On Thu, 11 Jun 2026 09:02:03 +0300 Mark Bloch wrote:
>>> On 11/06/2026 2:50, Jakub Kicinski wrote:
>>>> On Fri, 5 Jun 2026 21:10:25 +0300 Mark Bloch wrote:  
>>>>> devl_register() makes the devlink instance visible to userspace. A later
>>>>> patch also makes registration the point where devlink core may call
>>>>> eswitch_mode_set() to apply a boot-time default eswitch mode.
>>>>>
>>>>> Move netdevsim registration after all objects (resources, params, regions,
>>>>> traps, debugfs etc) are initialized, and after the initial eswitch mode is
>>>>> set to legacy.
>>>>>
>>>>> Move devl_unregister() to the beginning of nsim_drv_remove(), before those
>>>>> devlink objects are torn down. This keeps devlink register/unregister as
>>>>> the notification barrier and makes the later object teardown paths run
>>>>> after devlink is no longer registered, so they do not emit their own
>>>>> netlink DEL notifications.  
>>>>
>>>> This is going backwards. At some point someone from nVidia thought that
>>>> we can order our way out of locking, so mlx5 is likely ordered this way,
>>>> but this must not be required, or in any way normalized.
>>>> We (syzbot) quickly discovered that it doesn't cover all corner cases.
>>>> devl_lock() is exposed specifically to allow the driver to finish
>>>> whatever init it needs without letting user space invoke callbacks, yet.
>>>> Almost (?) all driver callbacks hold devl_lock(), so maybe the devlink
>>>> instance is "visible" to user space but that should not matter.  
>>>
>>> Let me clarify.
>>>
>>> No locking is changed here, and I don't want to make register/unregister
>>> ordering a substitute for devl_lock().
>>>
>>> The only requirement I have for this series is that devl_register() is called
>>> only once the driver is ready for devlink core to call eswitch_mode_set().
>>> That follows from the earlier direction to have the core apply the default
>>> mode from devl_register() instead of adding an explicit driver call.
>>
>> This is exactly what I'm objecting to. AFAIU we are trading off
>> explicit call to get the default value for an implicit behavior
>> depending on order of calls. We want to optimize for how easy it
>> is to get the API wrong, not for LoC.
> 
> Right, the reason I moved in this direction is that in v1 I had
> the explicit driver call, and Jiri asked to make this transparent
> from devlink core instead.
> 
>>
>> If we don't have a clean way to implement this without driver
>> changes let's add the explicit API to get the default value.
>> If driver doesn't call it schedule a work to go via the callback
>> once devl_lock() is dropped. That way drivers which care can optimize
>> themselves by reading the default value upfront. Drivers which don't 
>> care will work correctly, and there's no API call order trap.
> 
> The workqueue fallback is possible, but I think it makes the semantics
> more complicated.
> 
> We would need to track devlink instances which still need the default
> applied, and the worker would have to skip/remove them once handled.
> 
> More importantly, the worker can race with userspace setting the
> eswitch mode, so we would also need some state to tell whether the user
> already changed the mode. That feels more fragile than an explicit
> driver call.
> 
>>
>> Not ideal, but isn't that best we can do here?
>> I still have flashbacks of the fallout from the call ordering games, 
>> we have too many drivers to keep this straight...
> 
> That's why I started with the explicit call in the first place.
> 
> I can switch back to this model: drivers which support boot time eswitch
> defaults will opt in and call the helper once they are ready. This keeps
> the support explicit per driver and avoids making it depend on where
> devl_register() happens in the init path.
> 
> With that, devlink can tell at register time whether the instance supports
> boot time eswitch defaults. If the user configured a default for an instance
> whose driver did not opt in, devlink can write to dmesg from
> devl_register().
> 
> Not perfect, but at least the user gets a visible failure instead of the
> config being silently ignored.
> 
> Mark

Jakub, Jiri, any thoughts?

I think the explicit helper is the cleanest option here, without any
workqueue fallback inside devlink. It avoids depending on devl_register()
ordering, and makes the support explicit per driver.

Does that sound like an acceptable direction?

Mark

> 
>>
>>> So if the objection is to the commit message wording, I can fix that and drop
>>> the "notification barrier" language.
>>>
>>> For unregister, I can probably leave the old ordering as-is. I moved it only
>>> to mirror the register path, which felt cleaner, but it is not required for
>>> the default-mode change and as the lock is held I see no issue with doing
>>> that.
> 
> 


^ permalink raw reply

* Re: [PATCH v5 6/6] kselftest: alloc_tag: extend the allocinfo ioctl kselftest
From: Abhishek Bapat @ 2026-06-16 16:56 UTC (permalink / raw)
  To: Hao Ge
  Cc: Shuah Khan, Jonathan Corbet, linux-doc, linux-kernel, linux-mm,
	Sourav Panda, Suren Baghdasaryan, Andrew Morton, Kent Overstreet
In-Reply-To: <8e554bce-bd66-4481-bc53-fa4cbaf0c0b9@linux.dev>

On Mon, Jun 15, 2026 at 11:19 PM Hao Ge <hao.ge@linux.dev> wrote:
>
> Hi Abhishek
>
>
> On 2026/6/16 07:04, Abhishek Bapat wrote:
> > 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          | 197 +++++++++++++++++-
> >   1 file changed, 196 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 62d5a488a04d..041fee1a3d74 100644
> > --- a/tools/testing/selftests/alloc_tag/allocinfo_ioctl_test.c
> > +++ b/tools/testing/selftests/alloc_tag/allocinfo_ioctl_test.c
> > @@ -309,11 +309,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));
>
>
> I see. The #include <errno.h> you added in patch 5 is meant for this
> spot, right?
>
> If that's the case, I'd prefer moving the #include <errno.h>
>
> addition into this patch, though this is a trivial detail either way.
>
>
> Thanks
>
> Best Regards
>
> Hao
>
>
Ack, will do.
> > +             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)
> > @@ -327,5 +510,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();
> >   }

^ permalink raw reply

* Re: [PATCH v5 5/6] kselftest: alloc_tag: add kselftest for ioctl interface
From: Abhishek Bapat @ 2026-06-16 16:56 UTC (permalink / raw)
  To: Hao Ge
  Cc: Shuah Khan, Jonathan Corbet, linux-doc, linux-kernel, linux-mm,
	Sourav Panda, Suren Baghdasaryan, Andrew Morton, Kent Overstreet
In-Reply-To: <f5afcb7a-5b38-4c98-ac3e-529631bda4cb@linux.dev>

On Mon, Jun 15, 2026 at 11:01 PM Hao Ge <hao.ge@linux.dev> wrote:
>
> Hi Abhishek
>
>
> On 2026/6/16 07:04, Abhishek Bapat wrote:
> > 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/alloc_tag/Makefile    |   9 +
> >   .../alloc_tag/allocinfo_ioctl_test.c          | 331 ++++++++++++++++++
> >   3 files changed, 341 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/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
>
>
> I think we should replace -I../../../../usr/include with $(KHDR_INCLUDES).
>
>
> > +
> > +include ../lib.mk
> > +
>
>
> We would also introduce an extra field in the parent directory,
>
> allowing our alloc_tag target to be built when running make
>
> within /home/linux/tools/testing/selftests.
>
> like this:
>
> --- 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
>
> Below is the relevant build log:
>
> [root@localhost selftests]# make -j8
>    CC       acct_syscall
>    CC       allocinfo_ioctl_test
>
> Below is the log from running make clean:
>
> [root@localhost selftests]# make clean
> rm -f -r /home/linux/tools/testing/selftests/acct/acct_syscall
> rm -f -r /home/linux/tools/testing/selftests/alloc_tag/allocinfo_ioctl_test
>
>
> (Sorry about this inconvenience. I had limited experience with the
> kselftest framework,
>
> so I reached out to my teammates to sort out the relevant build logic.
>
> It's my fault for not catching this issue in prior reviews
>
> and forcing you to send a revised patch.)
>
>
Before writing this patch, I had discussed this with Suren and he was
of the opinion that this should be done later in a separate patch. So
I'll let Suren answer this further.

> > 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..62d5a488a04d
> > --- /dev/null
> > +++ b/tools/testing/selftests/alloc_tag/allocinfo_ioctl_test.c
> > @@ -0,0 +1,331 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +
> > +/* kselftest for allocinfo ioctl
> > + * allocinfo ioctl retrives allocinfo data through ioctl
> > + * Copyright (C) 2026 Google, Inc.
> > + */
> > +
> > +#include <errno.h>
>
>
> errno.h is unused and may be dropped.
>
>
> > +#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");
> > +                     return IOCTL_FAILURE;
>
> Sashiko pointed out the fd leak in get_filtered_ioctl_entries().
>
> Since we already need to update the patch to fix the missing
>
> TARGETS entry in the top-level Makefile, we might as well fix
>
> this at the same time.
>
>
> Thanks
>
> Best Regards
>
> Hao
>

Ahh I missed this code path interestingly. Ack, I'll remove these
return statements here.

> > +             }
> > +
> > +             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");
> > +                     return IOCTL_FAILURE;
> > +             }
> > +             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");
> > +                     return IOCTL_FAILURE;
> > +             }
> > +
> > +             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);
> > +
> > +     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();
> > +}

^ permalink raw reply

* Re: [PATCH v4 3/3] hwmon: Add documentation for SQ24860
From: Guenter Roeck @ 2026-06-16 16:05 UTC (permalink / raw)
  To: Ziming Zhu
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
	Shuah Khan, linux-hwmon, devicetree, linux-kernel, linux-doc,
	Ziming Zhu
In-Reply-To: <20260612030304.5165-4-zmzhu0630@163.com>

On Fri, Jun 12, 2026 at 11:03:04AM +0800, Ziming Zhu wrote:
> From: Ziming Zhu <ziming.zhu@silergycorp.com>
> 
> Document the supported sysfs attributes for the Silergy SQ24860 PMBus
> hwmon driver.
> 
> Signed-off-by: Ziming Zhu <ziming.zhu@silergycorp.com>

Applied to hwmon-next.

Thanks,
Guenter

^ permalink raw reply

* Re: [PATCH v4 2/3] hwmon: pmbus: Add support for Silergy SQ24860
From: Guenter Roeck @ 2026-06-16 16:04 UTC (permalink / raw)
  To: Ziming Zhu
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
	Shuah Khan, linux-hwmon, devicetree, linux-kernel, linux-doc,
	Ziming Zhu
In-Reply-To: <20260612030304.5165-3-zmzhu0630@163.com>

On Fri, Jun 12, 2026 at 11:03:03AM +0800, Ziming Zhu wrote:
> From: Ziming Zhu <ziming.zhu@silergycorp.com>
> 
> Add PMBus hwmon support for the Silergy SQ24860 eFuse.
> 
> The driver reports input voltage, output voltage, auxiliary voltage,
> input current, input power, and temperature. It also exposes peak,
> average, and minimum history attributes, sample count configuration,
> and maps the manufacturer-specific VIREF register to the generic input
> over-current fault limit attribute.
> 
> The IMON resistor value is read from the silergy,rimon-micro-ohms device
> property and used to configure the input current calibration gain.
> 
> Signed-off-by: Ziming Zhu <ziming.zhu@silergycorp.com>

Applied to hwmon-next.

Thanks,
Guenter

^ permalink raw reply

* Re: [PATCH v4 1/3] dt-bindings: hwmon: pmbus: Add bindings for Silergy SQ24860
From: Guenter Roeck @ 2026-06-16 16:03 UTC (permalink / raw)
  To: Ziming Zhu
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
	Shuah Khan, linux-hwmon, devicetree, linux-kernel, linux-doc,
	Ziming Zhu, Conor Dooley
In-Reply-To: <20260612030304.5165-2-zmzhu0630@163.com>

On Fri, Jun 12, 2026 at 11:03:02AM +0800, Ziming Zhu wrote:
> From: Ziming Zhu <ziming.zhu@silergycorp.com>
> 
> Add devicetree binding documentation for the Silergy SQ24860 eFuse.
> 
> The device is a PMBus hardware monitoring device which reports voltage,
> current, power, and temperature telemetry. The board-specific IMON
> resistor value is described with silergy,rimon-micro-ohms.
> 
> Signed-off-by: Ziming Zhu <ziming.zhu@silergycorp.com>
> 
> Reviewed-by: Conor Dooley <conor.dooley@microchip.com>

Applied to hwmon-next (after dropping the extra blank line above).

Thanks,
Guenter

^ permalink raw reply

* Re: [PATCH 4/7] dt-bindings: adm1275: ROHM BD12790 hot-swap controller
From: Conor Dooley @ 2026-06-16 15:54 UTC (permalink / raw)
  To: Matti Vaittinen
  Cc: Matti Vaittinen, Matti Vaittinen, Guenter Roeck, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet, Shuah Khan,
	Wensheng Wang, Ashish Yadav, Kim Seer Paller, Cedric Encarnacion,
	Chris Packham, Yuxi Wang, Charles Hsu, ChiShih Tsai, linux-hwmon,
	devicetree, linux-kernel, linux-doc
In-Reply-To: <ae80a037fa3b03ef5cfb446bf3e9c44efd4f04ec.1781591132.git.mazziesaccount@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 75 bytes --]

Acked-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: not-applicable

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* Re: [PATCH RFC v5 1/6] dt-bindings: iio: add Open Sensor Fusion device
From: Conor Dooley @ 2026-06-16 15:53 UTC (permalink / raw)
  To: Jinseob Kim
  Cc: Jonathan Cameron, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	David Lechner, Nuno Sá, Andy Shevchenko, Jonathan Corbet,
	Shuah Khan, linux-iio, devicetree, linux-doc, linux-kernel
In-Reply-To: <20260616072242.3942-2-kimjinseob88@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 6337 bytes --]

On Tue, Jun 16, 2026 at 04:22:37PM +0900, Jinseob Kim wrote:
> Add the generic Open Sensor Fusion device binding for a serdev-attached
> IIO sensor aggregation hub, and document the opensensorfusion vendor
> prefix.
> 
> The opensensorfusion,osf compatible describes the generic Open Sensor
> Fusion host interface. OSF GREEN is not the Linux compatible identity.
> Likewise, OSF0 is the current wire magic and a wire-format detail, not
> the Linux driver identity.
> 
> The fixed OSF frame header carries protocol_major and protocol_minor at
> fixed offsets. This driver currently supports protocol_major 0.
> protocol_minor changes are intended to remain backward-compatible within
> that fixed header layout. Incompatible wire-format changes require a new
> protocol_major. If a future device cannot expose compatible version
> discovery through the fixed header layout, it will need a different
> compatible.
> 
> Require vcc-supply so the driver can enable device power before starting
> communication.
> 
> Signed-off-by: Jinseob Kim <kimjinseob88@gmail.com>
> ---
>  .../bindings/iio/opensensorfusion,osf.yaml    | 59 +++++++++++++++++++
>  .../devicetree/bindings/vendor-prefixes.yaml  |  2 +
>  MAINTAINERS                                   | 13 ++++
>  3 files changed, 74 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/opensensorfusion,osf.yaml
> 
> diff --git a/Documentation/devicetree/bindings/iio/opensensorfusion,osf.yaml b/Documentation/devicetree/bindings/iio/opensensorfusion,osf.yaml
> new file mode 100644
> index 000000000..012a07fd6
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/opensensorfusion,osf.yaml
> @@ -0,0 +1,59 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/opensensorfusion,osf.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Open Sensor Fusion Sensor Aggregation Hub
> +
> +maintainers:
> +  - Jinseob Kim <kimjinseob88@gmail.com>
> +
> +description: |
> +  Open Sensor Fusion is a sensor aggregation hub. The hub exposes an OSF
> +  protocol data stream over its host interface and may report capabilities and

s/may report/reports/ because mandatory reporting, even if nothing is
actually there, is a requirement for having a compatible describing the
"bus".

> +  samples for multiple sensor classes. The Linux driver discovers the actual

s/The Linux driver discovers the actual sensor channels/The actual sensor channels are discovered"
Because although maybe only linux will use this it should not be
specific.

> +  sensor channels from OSF capability reports instead of describing those
> +  sensors in Device Tree.
> +
> +  Open Sensor Fusion is not a generic industry standard. Public project

I would drop this first sentence to be honest.

> +  documentation is available at:
> +
> +    https://github.com/opensensorfusion
> +
> +  The compatible describes the generic Open Sensor Fusion host interface. It
> +  is not an OSF GREEN board identity, and it does not encode the OSF0 wire
> +  magic. OSF0, protocol_major, and protocol_minor are wire-protocol details
> +  exchanged in OSF frames.

I think I move this to the first paragraph and would say something like

| This binding documents the generic Open Sensor Fusion host interface.
| 
| Open Sensor Fusion is a sensor aggregation hub. The hub exposes an OSF
| protocol data stream over its host interface and reports capabilities and
| samples for multiple sensor classes. The actual sensor channels are discovered
| at runtime from OSF capability reports instead of describing them in Device
| Tree.
|
| The protocol version is discovered at runtime.

Does that sound about right?

> +
> +allOf:
> +  - $ref: /schemas/serial/serial-peripheral-props.yaml#
> +
> +properties:
> +  compatible:
> +    const: opensensorfusion,osf
> +
> +  vcc-supply:
> +    description:
> +      Regulator supplying power to the Open Sensor Fusion device.
> +
> +required:
> +  - compatible
> +  - vcc-supply
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    vcc_sensor: regulator-0 {
> +        compatible = "regulator-fixed";
> +        regulator-name = "sensor-vcc";
> +    };

Drop this node, the tooling fakes one when running the checks. Only keep
nodes that actually form your device here. Same way you don't need to
actually fill out the serial port.
pw-bot: changes-requested

> +
> +    serial {
> +        sensor {
> +            compatible = "opensensorfusion,osf";
> +            vcc-supply = <&vcc_sensor>;
> +        };
> +    };
> +...
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
> index 28784d66a..88172d4a4 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
> @@ -1237,6 +1237,8 @@ patternProperties:
>      description: OpenPandora GmbH
>    "^openrisc,.*":
>      description: OpenRISC.io
> +  "^opensensorfusion,.*":
> +    description: Open Sensor Fusion
>    "^openwrt,.*":
>      description: OpenWrt
>    "^option,.*":
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c2c6d7927..2ddefc42d 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -20011,6 +20011,19 @@ F:	Documentation/devicetree/
>  F:	arch/*/boot/dts/
>  F:	include/dt-bindings/
>  

> +OPEN SENSOR FUSION IIO DRIVER
> +M:	Jinseob Kim <kimjinseob88@gmail.com>
> +S:	Maintained
> +F:	Documentation/devicetree/bindings/iio/opensensorfusion,osf.yaml
> +F:	Documentation/iio/open-sensor-fusion.rst
> +F:	drivers/iio/opensensorfusion/Kconfig
> +F:	drivers/iio/opensensorfusion/Makefile
> +F:	drivers/iio/opensensorfusion/osf_core.*
> +F:	drivers/iio/opensensorfusion/osf_iio.*
> +F:	drivers/iio/opensensorfusion/osf_protocol.*
> +F:	drivers/iio/opensensorfusion/osf_serdev.c
> +F:	drivers/iio/opensensorfusion/osf_stream.*

At this stage, only add the binding. The rest should be added when the
files are.

Cheers,
Conor.

> +
>  OPENCOMPUTE PTP CLOCK DRIVER
>  M:	Vadim Fedorenko <vadim.fedorenko@linux.dev>
>  L:	netdev@vger.kernel.org
> -- 
> 2.43.0
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* Re: [PATCH 6/7] hwmon: adm1275: Support ROHM BD12790
From: Guenter Roeck @ 2026-06-16 14:15 UTC (permalink / raw)
  To: Matti Vaittinen, Matti Vaittinen, Matti Vaittinen
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
	Shuah Khan, Wensheng Wang, Ashish Yadav, Kim Seer Paller,
	Cedric Encarnacion, Chris Packham, Yuxi Wang, Charles Hsu,
	ChiShih Tsai, linux-hwmon, devicetree, linux-kernel, linux-doc
In-Reply-To: <8ca875d21f2d9a4d53a87b47a5e6efab48266178.1781591132.git.mazziesaccount@gmail.com>

On 6/15/26 23:44, Matti Vaittinen wrote:
> From: Matti Vaittinen <mazziesaccount@gmail.com>
> 
> Add support for ROHM BD12790 hot-swap controller which is largely
> similar to Analog Devices adm1272.
> 
> The BD12790 uses the same selectable 60V/100V voltage ranges and
> 15mV/30mV current-sense ranges as the ADM1272, and the same VRANGE
> (bit 5) and IRANGE (bit 0) layout in PMON_CONFIG. It therefore uses
> a dedicated coefficient table that mirrors adm1272_coefficients, with
> the following differences derived from BD12790 datasheet Table 1 (p.18):
> - power 60V/30mV: m=17560 (vs. 17561)
> - power 100V/30mV: m=10536 (vs. 10535)
> - temperature: b=31880 (vs. 31871, reflecting T[11:0] = 4.2*T + 3188)
> 
> Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
> Assisted-by: GitHub Copilot:claude-sonnet-4.6
> 
> ---
> Originally this patch was AI-generated. I did pretty much re-write the
> probe changes by hand, and also fixed some of the coefficient math
> afterwards :/ But yeah, this one was AI "assisted". :)
> 
>   drivers/hwmon/pmbus/Kconfig   |  4 +--
>   drivers/hwmon/pmbus/adm1275.c | 53 +++++++++++++++++++++++++++++------
>   2 files changed, 47 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
> index b3c27f3b2712..6ebc01e26db3 100644
> --- a/drivers/hwmon/pmbus/Kconfig
> +++ b/drivers/hwmon/pmbus/Kconfig
> @@ -52,8 +52,8 @@ config SENSORS_ADM1275
>   	help
>   	  If you say yes here you get hardware monitoring support for Analog
>   	  Devices ADM1075, ADM1272, ADM1273, ADM1275, ADM1276, ADM1278, ADM1281,
> -	  ADM1293, ADM1294, ROHM BD12780, and SQ24905C Hot-Swap Controller and
> -	  Digital Power Monitors.
> +	  ADM1293, ADM1294, ROHM BD12780, ROHM BD12790, and SQ24905C
> +	  Hot-Swap Controller and Digital Power Monitors.
>   
>   	  This driver can also be built as a module. If so, the module will
>   	  be called adm1275.
> diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
> index 838b8827eb76..9e21dd4083e9 100644
> --- a/drivers/hwmon/pmbus/adm1275.c
> +++ b/drivers/hwmon/pmbus/adm1275.c
> @@ -19,7 +19,7 @@
>   #include "pmbus.h"
>   
>   enum chips { adm1075, adm1272, adm1273, adm1275, adm1276, adm1278, adm1281,
> -	 adm1293, adm1294, bd12780, sq24905c };
> +	 adm1293, adm1294, bd12780, bd12790, sq24905c };
>   
>   #define ADM1275_MFR_STATUS_IOUT_WARN2	BIT(0)
>   #define ADM1293_MFR_STATUS_VAUX_UV_WARN	BIT(5)
> @@ -47,8 +47,8 @@ enum chips { adm1075, adm1272, adm1273, adm1275, adm1276, adm1278, adm1281,
>   #define ADM1278_VOUT_EN			BIT(1)
>   
>   #define ADM1278_PMON_DEFCONFIG		(ADM1278_VOUT_EN | ADM1278_TEMP1_EN | ADM1278_TSFILT)
> -/* The BD12780 data sheets mark TSFILT bit as reserved. */
> -#define BD12780_PMON_DEFCONFIG		(ADM1278_VOUT_EN | ADM1278_TEMP1_EN)
> +/* The BD127x0 data sheets mark TSFILT bit as reserved. */
> +#define BD127X0_PMON_DEFCONFIG		(ADM1278_VOUT_EN | ADM1278_TEMP1_EN)

Please don't use such placeholders. Just use BD12780_PMON_DEFCONFIG
for both chips, similar to how the defines for all other chips
are handled.

Thanks,
Guenter


^ permalink raw reply

* [PATCH 2/4] mm/slub: preserve previous object lifetime in user tracking
From: Pengpeng Hou @ 2026-06-16 14:14 UTC (permalink / raw)
  To: Vlastimil Babka, Andrew Morton, linux-mm
  Cc: Harry Yoo, Hao Li, Christoph Lameter, David Rientjes,
	Roman Gushchin, David Hildenbrand, Lorenzo Stoakes, liam,
	Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Jonathan Corbet,
	Shuah Khan, linux-doc, linux-kernel, Pengpeng Hou
In-Reply-To: <20260616141410.52117-1-pengpeng@iscas.ac.cn>

SLAB_STORE_USER stores one allocation track and one free track for an
object.  When that object is reused, the next allocation overwrites the
allocation track.  If a stale pointer from the previous lifetime is later
freed or otherwise reported, the free/check report can contain the victim
allocation and the stale operation while the previous completed alloc/free
pair has already been overwritten.

Keep one previous completed lifetime in the existing user tracking
metadata.  When an object is allocated and the current allocation/free
tracks both exist, copy that completed lifetime to the previous-lifetime
slots before recording the new allocation.  Clear the current free track
when the new allocation begins so the current lifetime does not continue
to display a free from the old lifetime.

Print the previous object lifetime when it is available.  This is
diagnostic information only; it does not infer semantic ownership or
identify the root cause of a use-after-free.

Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
 mm/slub.c | 66 +++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 55 insertions(+), 11 deletions(-)

diff --git a/mm/slub.c b/mm/slub.c
index 43d4febd5bf2..358f42e92207 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -327,7 +327,13 @@ struct track {
 	unsigned long when;	/* When did the operation occur */
 };
 
-enum track_item { TRACK_ALLOC, TRACK_FREE, TRACK_NR };
+enum track_item {
+	TRACK_ALLOC,
+	TRACK_FREE,
+	TRACK_PREV_ALLOC,
+	TRACK_PREV_FREE,
+	TRACK_NR,
+};
 
 static inline unsigned int user_tracking_size(slab_flags_t flags)
 {
@@ -1080,12 +1086,37 @@ static void set_track_update(struct kmem_cache *s, void *object,
 	p->when = jiffies;
 }
 
-static __always_inline void set_track(struct kmem_cache *s, void *object,
-				      enum track_item alloc, unsigned long addr, gfp_t gfp_flags)
+static bool track_has_record(const struct track *t)
+{
+	return t->addr;
+}
+
+static void clear_track(struct kmem_cache *s, void *object,
+			enum track_item track)
+{
+	memset(get_track(s, object, track), 0, sizeof(struct track));
+}
+
+static void save_previous_lifetime(struct kmem_cache *s, void *object)
+{
+	struct track *alloc = get_track(s, object, TRACK_ALLOC);
+	struct track *free = get_track(s, object, TRACK_FREE);
+
+	if (!track_has_record(alloc) || !track_has_record(free))
+		return;
+
+	*get_track(s, object, TRACK_PREV_ALLOC) = *alloc;
+	*get_track(s, object, TRACK_PREV_FREE) = *free;
+}
+
+static __always_inline void set_alloc_track(struct kmem_cache *s, void *object,
+					    unsigned long addr, gfp_t gfp_flags)
 {
 	depot_stack_handle_t handle = set_track_prepare(gfp_flags);
 
-	set_track_update(s, object, alloc, addr, handle);
+	save_previous_lifetime(s, object);
+	set_track_update(s, object, TRACK_ALLOC, addr, handle);
+	clear_track(s, object, TRACK_FREE);
 }
 
 static void init_tracking(struct kmem_cache *s, void *object)
@@ -1120,11 +1151,22 @@ static void print_track(const char *s, struct track *t, unsigned long pr_time)
 void print_tracking(struct kmem_cache *s, void *object)
 {
 	unsigned long pr_time = jiffies;
+	struct track *prev_alloc;
+	struct track *prev_free;
+
 	if (!(s->flags & SLAB_STORE_USER))
 		return;
 
 	print_track("Allocated", get_track(s, object, TRACK_ALLOC), pr_time);
 	print_track("Freed", get_track(s, object, TRACK_FREE), pr_time);
+
+	prev_alloc = get_track(s, object, TRACK_PREV_ALLOC);
+	prev_free = get_track(s, object, TRACK_PREV_FREE);
+	if (track_has_record(prev_alloc) || track_has_record(prev_free)) {
+		pr_err("Previous object lifetime:\n");
+		print_track("Previously allocated", prev_alloc, pr_time);
+		print_track("Previously freed", prev_free, pr_time);
+	}
 }
 
 static void print_slab_info(const struct slab *slab)
@@ -1371,10 +1413,12 @@ check_bytes_and_report(struct kmem_cache *s, struct slab *slab,
  *
  * [Metadata starts at object + s->inuse]
  *   - A. freelist pointer (if freeptr_outside_object)
- *   - B. alloc tracking (SLAB_STORE_USER)
- *   - C. free tracking (SLAB_STORE_USER)
- *   - D. original request size (SLAB_KMALLOC && SLAB_STORE_USER)
- *   - E. KASAN metadata (if enabled)
+ *   - B. current alloc tracking (SLAB_STORE_USER)
+ *   - C. current free tracking (SLAB_STORE_USER)
+ *   - D. previous alloc tracking (SLAB_STORE_USER)
+ *   - E. previous free tracking (SLAB_STORE_USER)
+ *   - F. original request size (SLAB_KMALLOC && SLAB_STORE_USER)
+ *   - G. KASAN metadata (if enabled)
  *
  * [Mandatory padding] (if CONFIG_SLUB_DEBUG && SLAB_RED_ZONE)
  *   - One mandatory debug word to guarantee a minimum poisoned gap
@@ -2029,8 +2073,8 @@ static inline void slab_pad_check(struct kmem_cache *s, struct slab *slab) {}
 static inline int check_object(struct kmem_cache *s, struct slab *slab,
 			void *object, u8 val) { return 1; }
 static inline depot_stack_handle_t set_track_prepare(gfp_t gfp_flags) { return 0; }
-static inline void set_track(struct kmem_cache *s, void *object,
-			     enum track_item alloc, unsigned long addr, gfp_t gfp_flags) {}
+static inline void set_alloc_track(struct kmem_cache *s, void *object,
+				   unsigned long addr, gfp_t gfp_flags) {}
 static inline void add_full(struct kmem_cache *s, struct kmem_cache_node *n,
 					struct slab *slab) {}
 static inline void remove_full(struct kmem_cache *s, struct kmem_cache_node *n,
@@ -4522,7 +4566,7 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
 
 success:
 	if (kmem_cache_debug_flags(s, SLAB_STORE_USER))
-		set_track(s, object, TRACK_ALLOC, addr, gfpflags);
+		set_alloc_track(s, object, addr, gfpflags);
 
 	return object;
 }
-- 
2.43.0


^ permalink raw reply related

* [PATCH 1/4] mm/slub: factor user tracking metadata size calculation
From: Pengpeng Hou @ 2026-06-16 14:14 UTC (permalink / raw)
  To: Vlastimil Babka, Andrew Morton, linux-mm
  Cc: Harry Yoo, Hao Li, Christoph Lameter, David Rientjes,
	Roman Gushchin, David Hildenbrand, Lorenzo Stoakes, liam,
	Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Jonathan Corbet,
	Shuah Khan, linux-doc, linux-kernel, Pengpeng Hou
In-Reply-To: <20260616141410.52117-1-pengpeng@iscas.ac.cn>

SLAB_STORE_USER currently stores two struct track records in the per-object
debug metadata.  The size is open-coded as 2 * sizeof(struct track) in
several layout and offset calculations.

Add TRACK_NR and a small helper for the user-tracking metadata size.  This
keeps all offset calculations tied to the number of track records and makes
the following extension less error-prone.

No functional change.

Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
 mm/slub.c | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/mm/slub.c b/mm/slub.c
index f87e693aca5d..43d4febd5bf2 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -327,7 +327,15 @@ struct track {
 	unsigned long when;	/* When did the operation occur */
 };
 
-enum track_item { TRACK_ALLOC, TRACK_FREE };
+enum track_item { TRACK_ALLOC, TRACK_FREE, TRACK_NR };
+
+static inline unsigned int user_tracking_size(slab_flags_t flags)
+{
+	if (flags & SLAB_STORE_USER)
+		return TRACK_NR * sizeof(struct track);
+
+	return 0;
+}
 
 #ifdef SLAB_SUPPORTS_SYSFS
 static int sysfs_slab_add(struct kmem_cache *);
@@ -751,7 +759,7 @@ static inline void set_orig_size(struct kmem_cache *s,
 		return;
 
 	p += get_info_end(s);
-	p += sizeof(struct track) * 2;
+	p += user_tracking_size(s->flags);
 
 	*(unsigned long *)p = orig_size;
 }
@@ -767,7 +775,7 @@ static inline unsigned long get_orig_size(struct kmem_cache *s, void *object)
 		return s->object_size;
 
 	p += get_info_end(s);
-	p += sizeof(struct track) * 2;
+	p += user_tracking_size(s->flags);
 
 	return *(unsigned long *)p;
 }
@@ -885,7 +893,7 @@ static unsigned int obj_exts_offset_in_object(struct kmem_cache *s)
 	unsigned int offset = get_info_end(s);
 
 	if (kmem_cache_debug_flags(s, SLAB_STORE_USER))
-		offset += sizeof(struct track) * 2;
+		offset += user_tracking_size(s->flags);
 
 	if (slub_debug_orig_size(s))
 		offset += sizeof(unsigned long);
@@ -1088,7 +1096,7 @@ static void init_tracking(struct kmem_cache *s, void *object)
 		return;
 
 	p = get_track(s, object, TRACK_ALLOC);
-	memset(p, 0, 2*sizeof(struct track));
+	memset(p, 0, user_tracking_size(s->flags));
 }
 
 static void print_track(const char *s, struct track *t, unsigned long pr_time)
@@ -1196,8 +1204,7 @@ static void print_trailer(struct kmem_cache *s, struct slab *slab, u8 *p)
 
 	off = get_info_end(s);
 
-	if (s->flags & SLAB_STORE_USER)
-		off += 2 * sizeof(struct track);
+	off += user_tracking_size(s->flags);
 
 	if (slub_debug_orig_size(s))
 		off += sizeof(unsigned long);
@@ -1401,7 +1408,7 @@ static int check_pad_bytes(struct kmem_cache *s, struct slab *slab, u8 *p)
 
 	if (s->flags & SLAB_STORE_USER) {
 		/* We also have user information there */
-		off += 2 * sizeof(struct track);
+		off += user_tracking_size(s->flags);
 
 		if (s->flags & SLAB_KMALLOC)
 			off += sizeof(unsigned long);
@@ -7820,7 +7827,7 @@ static int calculate_sizes(struct kmem_cache_args *args, struct kmem_cache *s)
 		 * Need to store information about allocs and frees after
 		 * the object.
 		 */
-		size += 2 * sizeof(struct track);
+		size += user_tracking_size(flags);
 
 		/* Save the original kmalloc request size */
 		if (flags & SLAB_KMALLOC)
-- 
2.43.0


^ permalink raw reply related

* [PATCH 4/4] Documentation/mm: document SLUB previous lifetime tracking
From: Pengpeng Hou @ 2026-06-16 14:14 UTC (permalink / raw)
  To: Vlastimil Babka, Andrew Morton, linux-mm
  Cc: Harry Yoo, Hao Li, Christoph Lameter, David Rientjes,
	Roman Gushchin, David Hildenbrand, Lorenzo Stoakes, liam,
	Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Jonathan Corbet,
	Shuah Khan, linux-doc, linux-kernel, Pengpeng Hou
In-Reply-To: <20260616141410.52117-1-pengpeng@iscas.ac.cn>

Document that the U debug option also records one previous completed object
lifetime.  Also document the extra report lines and explicitly state that
the previous lifetime is diagnostic information, not semantic ownership or
root-cause attribution.

Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
 Documentation/admin-guide/mm/slab.rst | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/Documentation/admin-guide/mm/slab.rst b/Documentation/admin-guide/mm/slab.rst
index 14429ab90611..324edf328c34 100644
--- a/Documentation/admin-guide/mm/slab.rst
+++ b/Documentation/admin-guide/mm/slab.rst
@@ -49,7 +49,8 @@ Possible debug options are::
 			Sorry SLAB legacy issues)
 	Z		Red zoning
 	P		Poisoning (object and padding)
-	U		User tracking (free and alloc)
+	U		User tracking (free and alloc, including one previous
+			completed object lifetime)
 	T		Trace (please only use on single slabs)
 	A		Enable failslab filter mark for the cache
 	O		Switch debugging off for caches that would have
@@ -245,9 +246,16 @@ into the syslog:
 	cpu> pid=<pid of the process>
      INFO: Freed in <kernel function> age=<jiffies since free> cpu=<freed by cpu>
 	pid=<pid of the process>
+     INFO: Previous object lifetime:
+     INFO: Previously allocated in <kernel function> age=<jiffies since alloc>
+	cpu=<allocated by cpu> pid=<pid of the process>
+     INFO: Previously freed in <kernel function> age=<jiffies since free>
+	cpu=<freed by cpu> pid=<pid of the process>
 
    (Object allocation / free information is only available if SLAB_STORE_USER is
-   set for the slab. slab_debug sets that option)
+   set for the slab. slab_debug sets that option. The previous object lifetime
+   is diagnostic information for reuse cases and does not identify semantic
+   ownership or the root cause of a use-after-free.)
 
 2. The object contents if an object was involved.
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH 3/4] mm/slub: test previous lifetime tracking
From: Pengpeng Hou @ 2026-06-16 14:14 UTC (permalink / raw)
  To: Vlastimil Babka, Andrew Morton, linux-mm
  Cc: Harry Yoo, Hao Li, Christoph Lameter, David Rientjes,
	Roman Gushchin, David Hildenbrand, Lorenzo Stoakes, liam,
	Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Jonathan Corbet,
	Shuah Khan, linux-doc, linux-kernel, Pengpeng Hou
In-Reply-To: <20260616141410.52117-1-pengpeng@iscas.ac.cn>

Add KUnit coverage for the previous-lifetime tracking state transition.

The test allocates an object from a SLAB_STORE_USER cache, frees it and
allocates again from the same cache.  The immediate reuse case should move
the completed alloc/free lifetime into the previous-lifetime slots before
the new allocation track is recorded.

Expose a small KUnit-only helper so the test can check the internal
tracking state without parsing printk output or intentionally triggering a
real use-after-free.

Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
 lib/tests/slub_kunit.c | 33 +++++++++++++++++++++++++++++++++
 mm/slab.h              | 10 ++++++++++
 mm/slub.c              | 13 +++++++++++++
 3 files changed, 56 insertions(+)

diff --git a/lib/tests/slub_kunit.c b/lib/tests/slub_kunit.c
index fa6d31dbca16..debd28c7f7a8 100644
--- a/lib/tests/slub_kunit.c
+++ b/lib/tests/slub_kunit.c
@@ -160,6 +160,37 @@ static void test_kmalloc_redzone_access(struct kunit *test)
 	kmem_cache_destroy(s);
 }
 
+static void test_store_user_previous_lifetime(struct kunit *test)
+{
+	struct kmem_cache *s;
+	void *p;
+	void *q;
+
+	s = test_kmem_cache_create("TestSlub_prev_lifetime", 64,
+				   SLAB_STORE_USER | SLAB_NO_MERGE);
+	KUNIT_ASSERT_NOT_NULL(test, s);
+
+	p = kmem_cache_alloc(s, GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, p);
+	KUNIT_EXPECT_FALSE(test, slab_test_has_previous_lifetime(s, p));
+
+	kmem_cache_free(s, p);
+
+	q = kmem_cache_alloc(s, GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, q);
+	if (q != p) {
+		KUNIT_FAIL(test, "expected immediate reuse of the freed object");
+		kmem_cache_free(s, q);
+		kmem_cache_destroy(s);
+		return;
+	}
+
+	KUNIT_EXPECT_TRUE(test, slab_test_has_previous_lifetime(s, q));
+
+	kmem_cache_free(s, q);
+	kmem_cache_destroy(s);
+}
+
 struct test_kfree_rcu_struct {
 	struct rcu_head rcu;
 };
@@ -400,6 +431,7 @@ static struct kunit_case test_cases[] = {
 
 	KUNIT_CASE(test_clobber_redzone_free),
 	KUNIT_CASE(test_kmalloc_redzone_access),
+	KUNIT_CASE(test_store_user_previous_lifetime),
 	KUNIT_CASE(test_kfree_rcu),
 	KUNIT_CASE(test_kfree_rcu_wq_destroy),
 	KUNIT_CASE(test_leak_destroy),
@@ -419,3 +451,4 @@ kunit_test_suite(test_suite);
 
 MODULE_DESCRIPTION("Kunit tests for slub allocator");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
diff --git a/mm/slab.h b/mm/slab.h
index 1bf9c3021ae3..0b3813696ca9 100644
--- a/mm/slab.h
+++ b/mm/slab.h
@@ -487,6 +487,16 @@ bool slab_in_kunit_test(void);
 static inline bool slab_in_kunit_test(void) { return false; }
 #endif
 
+#if IS_ENABLED(CONFIG_SLUB_KUNIT_TEST)
+bool slab_test_has_previous_lifetime(struct kmem_cache *s, void *object);
+#else
+static inline bool
+slab_test_has_previous_lifetime(struct kmem_cache *s, void *object)
+{
+	return false;
+}
+#endif
+
 /*
  * slub is about to manipulate internal object metadata.  This memory lies
  * outside the range of the allocated object, so accessing it would normally
diff --git a/mm/slub.c b/mm/slub.c
index 358f42e92207..5e94bd1bed81 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -46,6 +46,7 @@
 #include <linux/prandom.h>
 #include <kunit/test.h>
 #include <kunit/test-bug.h>
+#include <kunit/visibility.h>
 #include <linux/sort.h>
 #include <linux/irq_work.h>
 #include <linux/kprobes.h>
@@ -1169,6 +1170,18 @@ void print_tracking(struct kmem_cache *s, void *object)
 	}
 }
 
+#if IS_ENABLED(CONFIG_SLUB_KUNIT_TEST)
+bool slab_test_has_previous_lifetime(struct kmem_cache *s, void *object)
+{
+	if (!(s->flags & SLAB_STORE_USER))
+		return false;
+
+	return track_has_record(get_track(s, object, TRACK_PREV_ALLOC)) &&
+	       track_has_record(get_track(s, object, TRACK_PREV_FREE));
+}
+EXPORT_SYMBOL_IF_KUNIT(slab_test_has_previous_lifetime);
+#endif
+
 static void print_slab_info(const struct slab *slab)
 {
 	pr_err("Slab 0x%p objects=%u used=%u fp=0x%p flags=%pGp\n",
-- 
2.43.0


^ permalink raw reply related

* [PATCH 0/4] mm/slub: preserve previous object lifetime
From: Pengpeng Hou @ 2026-06-16 14:14 UTC (permalink / raw)
  To: Vlastimil Babka, Andrew Morton, linux-mm
  Cc: Harry Yoo, Hao Li, Christoph Lameter, David Rientjes,
	Roman Gushchin, David Hildenbrand, Lorenzo Stoakes, liam,
	Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Jonathan Corbet,
	Shuah Khan, linux-doc, linux-kernel, Pengpeng Hou

SLAB_STORE_USER currently stores one allocation track and one free track
for an object.  Once the object is reused, the next allocation overwrites
the allocation track.  If a stale pointer from the previous lifetime is
later freed or otherwise reported, the report can show the victim
allocation and the stale operation while the previous completed lifetime
has already been lost.

This series keeps one previous completed lifetime in the existing
SLAB_STORE_USER metadata and prints it when available.  It is intended as
diagnostic information for reuse cases only; it does not infer semantic
ownership or identify the root cause of a use-after-free.

This is the non-RFC version of the earlier RFC.  Based on the feedback,
this version does not add a separate slab_debug=H option.  If a user
enables U, the user tracking metadata records the current allocation/free
tracks and one previous completed allocation/free pair.

Changes since RFC:
- extend the existing U option directly instead of adding H/UH
- add KUnit coverage for the previous-lifetime state transition
- document the extra report lines and the diagnostic-only semantics

Pengpeng Hou (4):
  mm/slub: factor user tracking metadata size calculation
  mm/slub: preserve previous object lifetime in user tracking
  mm/slub: test previous lifetime tracking
  Documentation/mm: document SLUB previous lifetime tracking

 Documentation/admin-guide/mm/slab.rst |  12 ++-
 lib/tests/slub_kunit.c                |  33 +++++++++
 mm/slab.h                             |  10 +++
 mm/slub.c                             | 102 +++++++++++++++++++++-----
 4 files changed, 136 insertions(+), 21 deletions(-)

-- 
2.43.0


^ permalink raw reply

* Re: [PATCH 3/7] hwmon: adm1275: Support ROHM BD12780
From: Guenter Roeck @ 2026-06-16 14:13 UTC (permalink / raw)
  To: Matti Vaittinen, Matti Vaittinen, Matti Vaittinen
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
	Shuah Khan, Wensheng Wang, Ashish Yadav, Kim Seer Paller,
	Cedric Encarnacion, Chris Packham, Yuxi Wang, Charles Hsu,
	ChiShih Tsai, linux-hwmon, devicetree, linux-kernel, linux-doc
In-Reply-To: <c92f1356fbf967dee3130f2eb0da08eb84800d47.1781591132.git.mazziesaccount@gmail.com>

On 6/15/26 23:36, Matti Vaittinen wrote:
> From: Matti Vaittinen <mazziesaccount@gmail.com>
> 
> ROHM BD12780 and BD12780A are hot-swap controllers. They are largely
> similar to Analog Devices ADM1278. Besides the ID registers and some
> added functionality, the BD12780 and BD12780A mark PMON_CONFIG bits
> [15:14] as reserved. Hence TSFILT setting must be omitted on these ICs.
> 
> The BD12780 has 3 pins usable for configuring the I2C address. The
> BD12780A lists the ADDR3-pin as "not connect".
> 
> Support ROHM BD12780 and BD12780A controllers.
> 
> Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
> ---
>   drivers/hwmon/pmbus/Kconfig   |  2 +-
>   drivers/hwmon/pmbus/adm1275.c | 46 +++++++++++++++++++++++++++++------
>   2 files changed, 39 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
> index 8f4bff375ecb..b3c27f3b2712 100644
> --- a/drivers/hwmon/pmbus/Kconfig
> +++ b/drivers/hwmon/pmbus/Kconfig
> @@ -52,7 +52,7 @@ config SENSORS_ADM1275
>   	help
>   	  If you say yes here you get hardware monitoring support for Analog
>   	  Devices ADM1075, ADM1272, ADM1273, ADM1275, ADM1276, ADM1278, ADM1281,
> -	  ADM1293, ADM1294 and SQ24905C Hot-Swap Controller and
> +	  ADM1293, ADM1294, ROHM BD12780, and SQ24905C Hot-Swap Controller and
>   	  Digital Power Monitors.
>   
>   	  This driver can also be built as a module. If so, the module will
> diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
> index bc2a6a07dc3e..838b8827eb76 100644
> --- a/drivers/hwmon/pmbus/adm1275.c
> +++ b/drivers/hwmon/pmbus/adm1275.c
> @@ -19,7 +19,7 @@
>   #include "pmbus.h"
>   
>   enum chips { adm1075, adm1272, adm1273, adm1275, adm1276, adm1278, adm1281,
> -	 adm1293, adm1294, sq24905c };
> +	 adm1293, adm1294, bd12780, sq24905c };
>   
>   #define ADM1275_MFR_STATUS_IOUT_WARN2	BIT(0)
>   #define ADM1293_MFR_STATUS_VAUX_UV_WARN	BIT(5)
> @@ -47,6 +47,8 @@ enum chips { adm1075, adm1272, adm1273, adm1275, adm1276, adm1278, adm1281,
>   #define ADM1278_VOUT_EN			BIT(1)
>   
>   #define ADM1278_PMON_DEFCONFIG		(ADM1278_VOUT_EN | ADM1278_TEMP1_EN | ADM1278_TSFILT)
> +/* The BD12780 data sheets mark TSFILT bit as reserved. */
> +#define BD12780_PMON_DEFCONFIG		(ADM1278_VOUT_EN | ADM1278_TEMP1_EN)
>   
>   #define ADM1293_IRANGE_25		0
>   #define ADM1293_IRANGE_50		BIT(6)
> @@ -487,6 +489,21 @@ static const struct i2c_device_id adm1275_id[] = {
>   	{ "adm1281", adm1281 },
>   	{ "adm1293", adm1293 },
>   	{ "adm1294", adm1294 },
> +	/*
> +	 * The BD12780a is functionally identical to BD12780(*). Even the pmbus ID
> +	 * register contents are same. When instantiated from the DT, it is required
> +	 * to have the bd12780 as a fall-back. We still need the bd12780a ID here,
> +	 * because the i2c_device_id is created from the first compatible, not from
> +	 * the fall-back entry.
> +	 * (*)Until proven to differ. I prefer having own compatible for these
> +	 * variants for that day. Please note that even though the probe is called
> +	 * based on the 'bd12780a' -entry, the ID is picked at probe based on the
> +	 * pmbus register contents and not by DT entry. Thus, if the bd12780 and
> +	 * bd12780a are found to require different handling, then this needs to be
> +	 * changed, or bd12780a is handled as bd12780.
> +	 */
> +	{ "bd12780", bd12780 },
> +	{ "bd12780a", /* driver data unused, see --^ */ },

We don't usually do that. There are various A/B/C variants for many chips,
and we just use the base name unless a difference is warranted. Either this
is needed, and driver data is needed as well, or it isn't. If it is not needed,
it should be dropped.

>   	{ "mc09c", sq24905c },
>   	{ }
>   };
> @@ -494,12 +511,13 @@ MODULE_DEVICE_TABLE(i2c, adm1275_id);
>   
>   /* Enable VOUT & TEMP1 if not enabled (disabled by default) */
>   static int adm1275_enable_vout_temp(struct adm1275_data *data,
> -				    struct i2c_client *client, int config)
> +				    struct i2c_client *client, int config,
> +				    u16 defconfig)
>   {
>   	int ret;
>   
> -	if ((config & ADM1278_PMON_DEFCONFIG) != ADM1278_PMON_DEFCONFIG) {
> -		config |= ADM1278_PMON_DEFCONFIG;
> +	if ((config & defconfig) != defconfig) {
> +		config |= defconfig;
>   		ret = adm1275_write_pmon_config(data, client, config);
>   		if (ret < 0) {
>   			dev_err(&client->dev, "Failed to enable VOUT/TEMP1 monitoring\n");
> @@ -535,7 +553,8 @@ static int adm1275_probe(struct i2c_client *client)
>   		return ret;
>   	}
>   	if ((ret != 3 || strncmp(block_buffer, "ADI", 3)) &&
> -	    (ret != 2 || strncmp(block_buffer, "SY", 2))) {
> +	    (ret != 2 || strncmp(block_buffer, "SY", 2)) &&
> +	    (ret != 4 || strncmp(block_buffer, "ROHM", 4))) {
>   		dev_err(&client->dev, "Unsupported Manufacturer ID\n");
>   		return -ENODEV;
>   	}
> @@ -562,7 +581,7 @@ static int adm1275_probe(struct i2c_client *client)
>   	if (mid->driver_data == adm1272 || mid->driver_data == adm1273 ||
>   	    mid->driver_data == adm1278 || mid->driver_data == adm1281 ||
>   	    mid->driver_data == adm1293 || mid->driver_data == adm1294 ||
> -	    mid->driver_data == sq24905c)
> +	    mid->driver_data == bd12780 || mid->driver_data == sq24905c)
>   		config_read_fn = i2c_smbus_read_word_data;
>   	else
>   		config_read_fn = i2c_smbus_read_byte_data;
> @@ -666,7 +685,8 @@ static int adm1275_probe(struct i2c_client *client)
>   			PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
>   			PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
>   
> -		ret = adm1275_enable_vout_temp(data, client, config);
> +		ret = adm1275_enable_vout_temp(data, client, config,
> +					       ADM1278_PMON_DEFCONFIG);
>   		if (ret)
>   			return ret;
>   
> @@ -712,7 +732,16 @@ static int adm1275_probe(struct i2c_client *client)
>   		break;
>   	case adm1278:
>   	case adm1281:
> +	case bd12780:
>   	case sq24905c:
> +	{
> +		u16 defconfig;
> +
> +		if (data->id == bd12780)
> +			defconfig = BD12780_PMON_DEFCONFIG;
> +		else
> +			defconfig = ADM1278_PMON_DEFCONFIG;
> +

Please add a separate case statement for the new chip
and do not overload existing chip data.

>   		data->have_vout = true;
>   		data->have_pin_max = true;
>   		data->have_temp_max = true;
> @@ -728,13 +757,14 @@ static int adm1275_probe(struct i2c_client *client)
>   			PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
>   			PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
>   
> -		ret = adm1275_enable_vout_temp(data, client, config);
> +		ret = adm1275_enable_vout_temp(data, client, config, defconfig);
>   		if (ret)
>   			return ret;
>   
>   		if (config & ADM1278_VIN_EN)
>   			info->func[0] |= PMBUS_HAVE_VIN;
>   		break;
> +	}
>   	case adm1293:
>   	case adm1294:
>   		data->have_iout_min = true;


^ permalink raw reply

* Re: [PATCH 6/7] hwmon: adm1275: Support ROHM BD12790
From: Guenter Roeck @ 2026-06-16 14:08 UTC (permalink / raw)
  To: Matti Vaittinen, Matti Vaittinen, Matti Vaittinen
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
	Shuah Khan, Wensheng Wang, Ashish Yadav, Kim Seer Paller,
	Cedric Encarnacion, Chris Packham, Yuxi Wang, Charles Hsu,
	ChiShih Tsai, linux-hwmon, devicetree, linux-kernel, linux-doc
In-Reply-To: <8ca875d21f2d9a4d53a87b47a5e6efab48266178.1781591132.git.mazziesaccount@gmail.com>

On 6/15/26 23:44, Matti Vaittinen wrote:
> From: Matti Vaittinen <mazziesaccount@gmail.com>
> 
> Add support for ROHM BD12790 hot-swap controller which is largely
> similar to Analog Devices adm1272.
> 
> The BD12790 uses the same selectable 60V/100V voltage ranges and
> 15mV/30mV current-sense ranges as the ADM1272, and the same VRANGE
> (bit 5) and IRANGE (bit 0) layout in PMON_CONFIG. It therefore uses
> a dedicated coefficient table that mirrors adm1272_coefficients, with
> the following differences derived from BD12790 datasheet Table 1 (p.18):
> - power 60V/30mV: m=17560 (vs. 17561)
> - power 100V/30mV: m=10536 (vs. 10535)
> - temperature: b=31880 (vs. 31871, reflecting T[11:0] = 4.2*T + 3188)
> 
> Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
> Assisted-by: GitHub Copilot:claude-sonnet-4.6
> 
> ---
> Originally this patch was AI-generated. I did pretty much re-write the
> probe changes by hand, and also fixed some of the coefficient math
> afterwards :/ But yeah, this one was AI "assisted". :)
> 
>   drivers/hwmon/pmbus/Kconfig   |  4 +--
>   drivers/hwmon/pmbus/adm1275.c | 53 +++++++++++++++++++++++++++++------
>   2 files changed, 47 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
> index b3c27f3b2712..6ebc01e26db3 100644
> --- a/drivers/hwmon/pmbus/Kconfig
> +++ b/drivers/hwmon/pmbus/Kconfig
> @@ -52,8 +52,8 @@ config SENSORS_ADM1275
>   	help
>   	  If you say yes here you get hardware monitoring support for Analog
>   	  Devices ADM1075, ADM1272, ADM1273, ADM1275, ADM1276, ADM1278, ADM1281,
> -	  ADM1293, ADM1294, ROHM BD12780, and SQ24905C Hot-Swap Controller and
> -	  Digital Power Monitors.
> +	  ADM1293, ADM1294, ROHM BD12780, ROHM BD12790, and SQ24905C
> +	  Hot-Swap Controller and Digital Power Monitors.
>   
>   	  This driver can also be built as a module. If so, the module will
>   	  be called adm1275.
> diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
> index 838b8827eb76..9e21dd4083e9 100644
> --- a/drivers/hwmon/pmbus/adm1275.c
> +++ b/drivers/hwmon/pmbus/adm1275.c
> @@ -19,7 +19,7 @@
>   #include "pmbus.h"
>   
>   enum chips { adm1075, adm1272, adm1273, adm1275, adm1276, adm1278, adm1281,
> -	 adm1293, adm1294, bd12780, sq24905c };
> +	 adm1293, adm1294, bd12780, bd12790, sq24905c };
>   
>   #define ADM1275_MFR_STATUS_IOUT_WARN2	BIT(0)
>   #define ADM1293_MFR_STATUS_VAUX_UV_WARN	BIT(5)
> @@ -47,8 +47,8 @@ enum chips { adm1075, adm1272, adm1273, adm1275, adm1276, adm1278, adm1281,
>   #define ADM1278_VOUT_EN			BIT(1)
>   
>   #define ADM1278_PMON_DEFCONFIG		(ADM1278_VOUT_EN | ADM1278_TEMP1_EN | ADM1278_TSFILT)
> -/* The BD12780 data sheets mark TSFILT bit as reserved. */
> -#define BD12780_PMON_DEFCONFIG		(ADM1278_VOUT_EN | ADM1278_TEMP1_EN)
> +/* The BD127x0 data sheets mark TSFILT bit as reserved. */
> +#define BD127X0_PMON_DEFCONFIG		(ADM1278_VOUT_EN | ADM1278_TEMP1_EN)
>   
>   #define ADM1293_IRANGE_25		0
>   #define ADM1293_IRANGE_50		BIT(6)
> @@ -136,6 +136,30 @@ static const struct coefficients adm1272_coefficients[] = {
>   
>   };
>   
> +/*
> + * BD12790 coefficients derived from preliminary datasheet, Table 1 (p.18)
> + * and the PMBus direct-format relationship X = (Y * 10^(-R) - b) / m.
> + *
> + * Voltage: V[V] = 14.77e-3 * code (60V) / 24.62e-3 * code (100V)
> + *   -> m = 6770, R=-2 / m = 4062, R=-2
> + * Current: code = I[A] * RS * 132802.1 + 2048 (15mV) / * 66401.06 + 2048 (30mV)
> + *   -> m = 1328, b = 2048 * 10^(-R) = 20480, R=-1 / m = 664, same b and R
> + * Power: code = k * RS * PIN, k = 35119.94 / 17559.97 / 21071.44 / 10535.72
> + *   -> m = round(k / 10^(-R)), R=-2 for 60V/15mV, R=-3 for the other three
> + * Temperature: code = 4.2 * T + 3188 -> m = 42, b = 3188 * 10 = 31880, R=-1
> + */
> +static const struct coefficients bd12790_coefficients[] = {
> +	[0] = { 6770, 0, -2 },		/* voltage, vrange 60V */
> +	[1] = { 4062, 0, -2 },		/* voltage, vrange 100V */
> +	[2] = { 1328, 20480, -1 },	/* current, vsense range 15mV */
> +	[3] = { 664, 20480, -1 },	/* current, vsense range 30mV */
> +	[4] = { 3512, 0, -2 },		/* power, vrange 60V, irange 15mV */
> +	[5] = { 21071, 0, -3 },		/* power, vrange 100V, irange 15mV */
> +	[6] = { 17560, 0, -3 },		/* power, vrange 60V, irange 30mV */
> +	[7] = { 10536, 0, -3 },		/* power, vrange 100V, irange 30mV */
> +	[8] = { 42, 31880, -1 },	/* temperature */
> +};
> +
>   static const struct coefficients adm1275_coefficients[] = {
>   	[0] = { 19199, 0, -2 },		/* voltage, vrange set */
>   	[1] = { 6720, 0, -1 },		/* voltage, vrange not set */
> @@ -504,6 +528,7 @@ static const struct i2c_device_id adm1275_id[] = {
>   	 */
>   	{ "bd12780", bd12780 },
>   	{ "bd12780a", /* driver data unused, see --^ */ },
> +	{ "bd12790", bd12790 },
>   	{ "mc09c", sq24905c },
>   	{ }
>   };
> @@ -581,7 +606,8 @@ static int adm1275_probe(struct i2c_client *client)
>   	if (mid->driver_data == adm1272 || mid->driver_data == adm1273 ||
>   	    mid->driver_data == adm1278 || mid->driver_data == adm1281 ||
>   	    mid->driver_data == adm1293 || mid->driver_data == adm1294 ||
> -	    mid->driver_data == bd12780 || mid->driver_data == sq24905c)
> +	    mid->driver_data == bd12780 || mid->driver_data == bd12790 ||
> +	    mid->driver_data == sq24905c)
>   		config_read_fn = i2c_smbus_read_word_data;
>   	else
>   		config_read_fn = i2c_smbus_read_byte_data;
> @@ -655,12 +681,23 @@ static int adm1275_probe(struct i2c_client *client)
>   		break;
>   	case adm1272:
>   	case adm1273:
> +	case bd12790:

Please don't overload the existing case statements.
Just add separate case statements for the new chips.

Thanks,
Guenter

> +	{
> +		u16 defconfig;
> +
>   		data->have_vout = true;
>   		data->have_pin_max = true;
>   		data->have_temp_max = true;
>   		data->have_power_sampling = true;
>   
> -		coefficients = adm1272_coefficients;
> +		if (data->id == bd12790) {
> +			coefficients = bd12790_coefficients;
> +			defconfig = BD127X0_PMON_DEFCONFIG;
> +		} else {
> +			coefficients = adm1272_coefficients;
> +			defconfig = ADM1278_PMON_DEFCONFIG;
> +		}
> +
>   		vindex = (config & ADM1275_VRANGE) ? 1 : 0;
>   		cindex = (config & ADM1272_IRANGE) ? 3 : 2;
>   		/* pindex depends on the combination of the above */
> @@ -685,14 +722,14 @@ static int adm1275_probe(struct i2c_client *client)
>   			PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
>   			PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
>   
> -		ret = adm1275_enable_vout_temp(data, client, config,
> -					       ADM1278_PMON_DEFCONFIG);
> +		ret = adm1275_enable_vout_temp(data, client, config, defconfig);
>   		if (ret)
>   			return ret;
>   
>   		if (config & ADM1278_VIN_EN)
>   			info->func[0] |= PMBUS_HAVE_VIN;
>   		break;
> +	}
>   	case adm1275:
>   		if (device_config & ADM1275_IOUT_WARN2_SELECT)
>   			data->have_oc_fault = true;
> @@ -738,7 +775,7 @@ static int adm1275_probe(struct i2c_client *client)
>   		u16 defconfig;
>   
>   		if (data->id == bd12780)
> -			defconfig = BD12780_PMON_DEFCONFIG;
> +			defconfig = BD127X0_PMON_DEFCONFIG;
>   		else
>   			defconfig = ADM1278_PMON_DEFCONFIG;
>   


^ permalink raw reply

* Re: [PATCH 7/7] hwmon: adm1275: Support module auto-loading
From: Guenter Roeck @ 2026-06-16 14:04 UTC (permalink / raw)
  To: Matti Vaittinen, Matti Vaittinen, Matti Vaittinen
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
	Shuah Khan, Wensheng Wang, Ashish Yadav, Kim Seer Paller,
	Cedric Encarnacion, Chris Packham, Yuxi Wang, Charles Hsu,
	ChiShih Tsai, linux-hwmon, devicetree, linux-kernel, linux-doc
In-Reply-To: <634e76680ed93e58ebeb35db080138b791cb6c27.1781591132.git.mazziesaccount@gmail.com>

On 6/15/26 23:47, Matti Vaittinen wrote:
> From: Matti Vaittinen <mazziesaccount@gmail.com>
> 
> Populating the spi_device_id -table is not enough to make the
> driver module automatically load when device-tree node for the bd12780
> is parsed at boot.
> 
> Adding the of_device_id tables causes the driver module to be
> automatically load at boot. Testing has been done with rather old Debian
> system.
> 
> When inspecting the generated module-aliases with the insmod, following
> entries seem to be the difference:
> 
> alias:          of:N*T*Crohm,bd12780C*
> alias:          of:N*T*Crohm,bd12780
> 
> I suspect these are required for the module loading to work.
> 
> Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
> 
> ---
> 
> I did not add of_device_ids for other supported ICs as I can't verify it
> doesn't cause side-effects. Please let me know if you think those IDs
> should be added as well. I would be glad if I got more educated opinion
> on adding the of-IDs :) (I can squash this to 3/7 and 6/7 in next
> revision, and add own patch for adding of-IDs for other ICs if
> required).
> 

I don't know what those side effects might be. I am much more concerned
about side effects of having some of the devices in adm1275_of_match
and some in adm1275_id. So, yes, please add a patch to provide
adm1275_of_match for all chips supported by the driver.

Thanks,
Guenter

> ---
>   drivers/hwmon/pmbus/adm1275.c | 8 ++++++++
>   1 file changed, 8 insertions(+)
> 
> diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
> index 9e21dd4083e9..c27bb0e49354 100644
> --- a/drivers/hwmon/pmbus/adm1275.c
> +++ b/drivers/hwmon/pmbus/adm1275.c
> @@ -927,9 +927,17 @@ static int adm1275_probe(struct i2c_client *client)
>   	return pmbus_do_probe(client, info);
>   }
>   
> +static const struct of_device_id adm1275_of_match[] = {
> +	{ .compatible = "rohm,bd12780", },
> +	{ .compatible = "rohm,bd12790", },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, adm1275_of_match);
> +
>   static struct i2c_driver adm1275_driver = {
>   	.driver = {
>   		   .name = "adm1275",
> +		   .of_match_table = adm1275_of_match,
>   		   },
>   	.probe = adm1275_probe,
>   	.id_table = adm1275_id,


^ permalink raw reply

* Re: [PATCH v3] arm64: errata: Workaround NVIDIA Olympus device store/load ordering erratum
From: Shanker Donthineni @ 2026-06-16 13:22 UTC (permalink / raw)
  To: Jason Gunthorpe
  Cc: Will Deacon, Catalin Marinas, Vladimir Murzin,
	linux-arm-kernel@lists.infradead.org, Mark Rutland,
	linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org,
	Vikram Sethi, Jason Sequeira
In-Reply-To: <20260612124825.GF1962447@nvidia.com>

Hi Will,

On 6/12/2026 7:48 AM, Jason Gunthorpe wrote:
> On Thu, Jun 11, 2026 at 08:13:48PM -0500, Shanker Donthineni wrote:
>
>> For the scalar MMIO helpers, the workaround promotes the raw writes to
>> store-release on affected CPUs as v1/v2 shown below. For the memcpy-toIO
>> helpers, could you please clarify the specific reason for adding a dmb despite
>> the documented no-ordering contract? Is the concern that some drivers may
>> be relying on ordering across memcpy_toio_*() today even though the API
>> does not guarantee it, and that we should cover those cases defensively?
> I think given how arm implements them today the iocopy's are actually
> the _relaxed variations.. I wonder if this matters to any user?

Following Jason's observation that on arm64 the memcpy_toio() 
/__iowrite{32,64}_copy() helpers are effectively the relaxed 
(write-combining) variants, I'd like to settle one open point before 
posting v4: should the workaround also promote dgh() > dmb on affected 
CPUs (now Olympus core), or leave dgh() as a plain hint?

       
If you'd still prefer the dmb defensively, to cover drivers that may 
rely on ordering across memcpy_toio() today despite the relaxed 
contract, I'm happy to fold it into v4.

     

       
Please let me know how you'd like me to proceed.

     

-Shanker


^ permalink raw reply

* Re: [PATCH v4 0/2] cpufreq: CPPC: add autonomous mode boot parameter support
From: Sumit Gupta @ 2026-06-16 12:52 UTC (permalink / raw)
  To: rafael, viresh.kumar, pierre.gondois, ionela.voinescu,
	zhenglifeng1, zhanjie9, corbet, skhan, rdunlap, mario.limonciello,
	linux-pm, linux-doc, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	mochs, bbasu, sumitg
In-Reply-To: <20260527202550.206828-1-sumitg@nvidia.com>


On 28/05/26 01:55, Sumit Gupta wrote:
> This series adds a kernel boot parameter 'cppc_cpufreq.auto_sel_mode'
> to enable CPPC autonomous performance selection on all CPUs at system
> startup, avoiding per-CPU sysfs scripting at every boot.
>
> When autonomous mode is enabled, the hardware automatically adjusts
> CPU performance based on workload demands using Energy Performance
> Preference (EPP) hints.
>
> Patch 1: Sets CPPC Enable Register for both OS-driven and autonomous
> CPPC control modes. It can be applied independently of patch 2.
>
> Patch 2: Adds the auto_sel_mode boot parameter with three modes:
>    - performance (or 1):         override EPP to performance (0x0)
>    - balance_performance (or 2): override EPP to balance_performance (0x80)
>    - default_epp (or 3):         preserve EPP value programmed by
>                                  BIOS/firmware
>
> Patch 2 depends on Pierre's series [4] ("cpufreq: Set policy->min and
> max as real QoS constraints") so that policy->min/max set during
> cppc_cpufreq_cpu_init() are not overridden by cpufreq_set_policy().
>
> v3[3] -> v4:
> - Add 'balance_performance' mode which sets EPP to 0x80.
> - Add CPPC_EPP_BALANCE_PERFORMANCE_PREF (0x80) constant in cppc_acpi.h.
> - Clean up EPP mode selection with switch + boolean flag in cpu_init.
> - Use local variable for kp->arg in auto_sel_mode_set/get to avoid
>    repeated casts.
>
> Sumit Gupta (2):
>    cpufreq: CPPC: Set CPPC Enable register in cpu_init
>    cpufreq: CPPC: add autonomous mode boot parameter support
>
>   .../admin-guide/kernel-parameters.txt         |  20 +++
>   drivers/cpufreq/cppc_cpufreq.c                | 154 +++++++++++++++++-
>   include/acpi/cppc_acpi.h                      |   1 +
>   3 files changed, 170 insertions(+), 5 deletions(-)
>
> [1] v1: https://lore.kernel.org/lkml/20260317151053.2361475-1-sumitg@nvidia.com/
> [2] v2: https://lore.kernel.org/lkml/20260424201814.230071-1-sumitg@nvidia.com/
> [3] v3: https://lore.kernel.org/lkml/20260515122624.1920637-1-sumitg@nvidia.com/
> [4] https://lore.kernel.org/lkml/20260511135538.522653-1-pierre.gondois@arm.com/
>

Gentle ping on this series.

The dependency it was waiting on, the "cpufreq: Set policy->min and
max as real QoS constraints" series, is now in linux-pm (linux-next).
I rebased on top and verified autonomous mode works as expected, and
it applies cleanly on the current linux-next.

The [1] reference in patch 2/2 points to v2 of that series; the merged
version is v3 [2].

If there are no further comments, please consider acking and queuing
this for the next cycle.

Thanks for your time.

[1] 
https://lore.kernel.org/lkml/20260511135538.522653-1-pierre.gondois@arm.com/
[2] 
https://lore.kernel.org/lkml/20260528090913.2759118-1-pierre.gondois@arm.com/

Regards,
Sumit



^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox