* Re: [PATCH v5 0/8] unwind, arm64: add sframe unwinder for kernel
From: Mark Rutland @ 2026-04-29 17:18 UTC (permalink / raw)
To: Dylan Hatch
Cc: Roman Gushchin, Weinan Liu, Will Deacon, Josh Poimboeuf,
Indu Bhagat, Peter Zijlstra, Steven Rostedt, Catalin Marinas,
Jiri Kosina, Jens Remus, Prasanna Kumar T S M, Puranjay Mohan,
Song Liu, joe.lawrence, linux-toolchains, linux-kernel,
live-patching, linux-arm-kernel, Randy Dunlap
In-Reply-To: <20260428183643.3796063-1-dylanbhatch@google.com>
Hi Dylan,
On Tue, Apr 28, 2026 at 06:36:35PM +0000, Dylan Hatch wrote:
> Implement a generic kernel sframe-based [1] unwinder. The main goal is
> to improve reliable stacktrace on arm64 by unwinding across exception
> boundaries.
Thanks for this!
Just as a holding reply: I'm going over the series now, and I have some
partially-written comments that I'll try to finish up and get out
tomorrow.
Mark.
> On x86, the ORC unwinder provides reliable stacktrace through similar
> methodology, but arm64 lacks the necessary support from objtool to
> create ORC unwind tables.
>
> Currently, there's already a sframe unwinder proposed for userspace: [2].
> To maintain common definitions and algorithms for sframe lookup, a
> substantial portion of this patch series aims to refactor the sframe
> lookup code to support both kernel and userspace sframe sections.
>
> Currently, only GNU Binutils support sframe. This series relies on the
> Sframe V3 format, which is supported in binutils 2.46.
>
> These patches are based on Steven Rostedt's sframe/core branch [3],
> which is and aggregation of existing work done for x86 sframe userspace
> unwind, and contains [2]. This branch is, in turn, based on Linux
> v7.0-rc3. This full series (applied to the sframe/core branch) is
> available on github: [4].
>
> Ref:
> [1]: https://sourceware.org/binutils/docs/sframe-spec.html
> [2]: https://lore.kernel.org/lkml/20260127150554.2760964-1-jremus@linux.ibm.com/
> [3]: https://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace.git/log/?h=sframe/core
> [4]: https://github.com/dylanbhatch/linux/tree/sframe-v5
>
> Changes since v4:
> - (Jens) Fix some minor nits.
> - Handle .init.text and .exit.text in function address validation.
>
> Changes since v3:
>
> - (Jens) Clean up patch summaries.
> - (Jens) Rename SFRAME_LOOKUP -> UNWIND_SFRAME_LOOKUP to fit existing
> naming convention.
> - (Randy) Correct typo errors in new config options.
> - (Jens) Move unwind types to a new unwind_types.h to match their
> usage.
> - (Jens) Update KERNEL_[COPY|GET] to use label-based error handling
> like their userspace counterparts.
> - (Jens) Rename SFRAME_UNWINDER -> HAVE_UNWIND_KERNEL_SFRAME and
> ARCH_SUPPORTS_SFRAME_UNWINDER -> ARCH_SUPPORTS_UNWIND_KERNEL_SFRAME
> to match existing naming convention.
> - (Jens) Move HAVE_UNWIND_KERNEL_SFRAME config option to arch/Kconfig.
> - (Jens) Rename/move extern definitions of __[start|end]_sframe into
> include/asm-generic/sections.h.
> - (Jens) Fix up CFI annotations at kernel entry.
> - (Jens) Fix error path for unsorted FDE lookup.
> - (Jens) Zero-out module sframe_section before init.
> - (Jens) For SFRAME_VALIDATION, use an arch-specific function-address
> validation helper so that .rodata.text can be correctly handled on
> arm64 vmlinux.
> - (Jens) Fixup and better comment kernel stacktrace code.
>
> Changes since v2:
>
> The biggest change from v2 is the switch from adding a dedicated,
> in-kernel sframe-lookup library, to refactoring/using the existing
> library developed by Josh, Jens, and Steve. Consequently, this series
> now depends on Sframe V3, though this upgrade would likely have been
> necessary anyway. Below is a full accounting of the changes since v2.
>
> - (Josh) Add stricter reliability checks during unwind.
> - (Puranjay, Indu, Jens) Update to use a common sframe library with
> userpace unwind, thus resolving the need to support
> SFRAME_F_FDE_FUNC_START_PCREL, added in binutils 2.45.
> - (Jens) Add check for sframe V3, thus resolving the prior need for V2
> and SFRAME_F_FDE_FUNC_START_PCREL support.
> - (Will) Add ARCH_SUPPORTS_SFRAME_UNWINDER, remove SFRAME_UNWIND_TABLE
> - (Indu) add support for unsorted FDE tables, allowing for module
> sframe lookups.
> - (Mark) Prefer frame-pointer unwind when possible, for better
> performance.
> - Simplify compile-time logic, adding stubbs when necessary.
> - Add support for in-kernel SFRAME_VALIDATION.
> - Rebase onto core/sframe (with v7.0-rc3 base)
>
> Dylan Hatch (7):
> sframe: Allow kernelspace sframe sections
> arm64, unwind: build kernel with sframe V3 info
> sframe: Provide PC lookup for vmlinux .sframe section
> sframe: Allow unsorted FDEs
> arm64/module, sframe: Add sframe support for modules
> sframe: Introduce in-kernel SFRAME_VALIDATION
> unwind: arm64: Use sframe to unwind interrupt frames
>
> Weinan Liu (1):
> arm64: entry: add unwind info for various kernel entries
>
> MAINTAINERS | 3 +-
> Makefile | 8 +
> arch/Kconfig | 27 +-
> arch/arm64/Kconfig | 1 +
> arch/arm64/include/asm/module.h | 6 +
> arch/arm64/include/asm/sections.h | 1 +
> arch/arm64/include/asm/stacktrace/common.h | 6 +
> arch/arm64/include/asm/unwind_sframe.h | 55 +++
> arch/arm64/kernel/entry.S | 23 +
> arch/arm64/kernel/module.c | 8 +
> arch/arm64/kernel/setup.c | 2 +
> arch/arm64/kernel/stacktrace.c | 246 ++++++++++-
> arch/arm64/kernel/vdso/Makefile | 2 +-
> arch/arm64/kernel/vmlinux.lds.S | 2 +
> .../{unwind_user_sframe.h => unwind_sframe.h} | 6 +-
> arch/x86/include/asm/unwind_user.h | 12 +-
> include/asm-generic/sections.h | 4 +
> include/asm-generic/vmlinux.lds.h | 15 +
> include/linux/sframe.h | 67 ++-
> include/linux/unwind_types.h | 46 ++
> include/linux/unwind_user_types.h | 41 --
> kernel/unwind/Makefile | 2 +-
> kernel/unwind/sframe.c | 410 ++++++++++++++----
> kernel/unwind/user.c | 41 +-
> 24 files changed, 827 insertions(+), 207 deletions(-)
> create mode 100644 arch/arm64/include/asm/unwind_sframe.h
> rename arch/x86/include/asm/{unwind_user_sframe.h => unwind_sframe.h} (50%)
> create mode 100644 include/linux/unwind_types.h
>
> --
> 2.54.0.545.g6539524ca2-goog
>
^ permalink raw reply
* Re: [PATCH v21 05/13] mfd: psci-mfd: Add PSCI MFD driver for cpuidle-psci-domain cell
From: Shivendra Pratap @ 2026-04-29 17:16 UTC (permalink / raw)
To: Pankaj Patil, Sebastian Reichel, Mark Rutland, Lorenzo Pieralisi,
Rafael J. Wysocki, Daniel Lezcano, Christian Loehle, Ulf Hansson,
Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Arnd Bergmann, Souvik Chakravarty,
Andy Yan, Matthias Brugger, John Stultz, Moritz Fischer,
Bartosz Golaszewski, Sudeep Holla
Cc: linux-pm, linux-kernel, linux-arm-msm, linux-arm-kernel,
devicetree, Florian Fainelli, Krzysztof Kozlowski,
Dmitry Baryshkov, Mukesh Ojha, Andre Draszik, Greg Kroah-Hartman,
Kathiravan Thirumoorthy, Srinivas Kandagatla
In-Reply-To: <e494ba66-3f8d-4d71-a82e-b37f5b34d45d@oss.qualcomm.com>
On 29-04-2026 20:10, Pankaj Patil wrote:
> On 4/27/2026 11:04 PM, Shivendra Pratap wrote:
>> PSCI has multiple kernel consumers, such as cpuidle-psci-domain.
>> Currently, both the PSCI core driver and cpuidle-psci-domain bind
>> directly to the same PSCI node "arm,psci-1.0". Additional consumers, if
>> introduced, would also need to bind in the same way, leading to several
>> drivers attached to a single device node.
>>
>> Introduce a PSCI MFD driver that binds to "arm,psci-1.0" and registers
>> PSCI child cells. As the first user, register cpuidle-psci-domain as a
>> child cell.
>>
>> Update cpuidle-psci-domain to probe as an MFD child and use the parent
>> PSCI node for power-domain traversal.
>>
>> Signed-off-by: Shivendra Pratap <shivendra.pratap@oss.qualcomm.com>
>> ---
>> MAINTAINERS | 1 +
>> drivers/cpuidle/Kconfig.arm | 1 +
>> drivers/cpuidle/cpuidle-psci-domain.c | 9 +-------
>> drivers/mfd/Kconfig | 10 ++++++++
>> drivers/mfd/Makefile | 2 ++
>> drivers/mfd/psci-mfd.c | 43 +++++++++++++++++++++++++++++++++++
>> 6 files changed, 58 insertions(+), 8 deletions(-)
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index c871acf2179ce16c80b404027e6b969c7787a6bb..333b01fa00b8fbd15e6f31a6b9af47600411624e 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -21239,6 +21239,7 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
>> S: Maintained
>> F: Documentation/devicetree/bindings/arm/psci.yaml
>> F: drivers/firmware/psci/
>> +F: drivers/mfd/psci-mfd.c
>> F: include/linux/psci.h
>> F: include/uapi/linux/psci.h
>>
>> diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm
>> index a1ee475d180dacab245510674514811aec337ad3..1a1316d11e0a2e4f61801586229f5104e2435865 100644
>> --- a/drivers/cpuidle/Kconfig.arm
>> +++ b/drivers/cpuidle/Kconfig.arm
>> @@ -36,6 +36,7 @@ config ARM_PSCI_CPUIDLE_DOMAIN
>> bool "PSCI CPU idle Domain"
>> depends on ARM_PSCI_CPUIDLE
>> depends on PM_GENERIC_DOMAINS_OF
>> + depends on MFD_PSCI
>
> Should this be select MFD_PSCI?
> We're seeing bootup issues on Glymur when MFD_PSCI is not y
sure. Let me check on this. We may need to make a change at "config
MFD_PSCI" --
config MFD_PSCI
bool "PSCI MFD for psci child cells"
depends on ARM_PSCI_FW
+ default y if ARM_PSCI_CPUIDLE
--
thanks,
Shivendra
^ permalink raw reply
* Re: [DONOTAPPLY RFC PATCH v2 0/4] WiFi support for samsung,coreprimevelte
From: Brian Norris @ 2026-04-29 17:15 UTC (permalink / raw)
To: Karel Balej
Cc: Francesco Dolcini, Johannes Berg, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Duje Mihanović,
Andrew Lunn, Gregory Clement, Sebastian Hesselbarth, Ulf Hansson,
Frank Li, linux-wireless, devicetree, linux-kernel,
linux-arm-kernel, linux-mmc, ~postmarketos/upstreaming,
phone-devel, Jeff Chen, Peng Fan, david
In-Reply-To: <DI5L100Q1RKO.1A68EJIPWYSRC@matfyz.cz>
Hi,
On Wed, Apr 29, 2026 at 12:55:23PM +0200, Karel Balej wrote:
> Francesco Dolcini, 2025-11-28T18:05:37+01:00:
> > On Thu, Nov 27, 2025 at 04:29:12PM +0100, Karel Balej wrote:
> >> To reiterate, the firmware is generally available but is not part of
> >> linux-firmware and the entire process of upstreaming the chipset support is
> >> stuck on that.
> >
> > I'll try to see if any of my contact in NXP Wi-Fi group is able to help. Give
> > me a few days.
>
> so I was in a long conversation over the past months with Jeff from NXP
> who was very helpful and tried to arrange for the upstreaming of the
> firmware. Unfortunately however, his efforts were ultimately rejected by
> the internal management.
:(
> We were directed to try to reach out to NXP via the customer support
> page which yielded nothing. The current situation is thus the same as a
> few months ago, summarized in my quote above.
>
> Brian, what are the options here now? Would it be possible to make an
> exception and accept the patches without the firmware being in
> linux-firmware? This is an old device with no mainstream audience so I
> expect everyone who will want to use it will be able to supply the
> firmware themselves
I'm not really in the business of writing rules here nor their
exceptions. I was just pointing you at the existing rules, and how this
seemed to violate them. Since the websites have moved around a bit since
the last time, here's the page I referenced:
https://wireless.docs.kernel.org/en/latest/en/developers/documentation/submittingpatches.html#new-driver
I suppose maybe those rules are there so that we don't get drivers with
*no* legally-usable firmware, since it's about "New Drivers". This is
not a New Driver, so maybe that part doesn't have to apply.
I suppose I'll leave it up to Johannes on this part, and may be willing
to retract my NAK [1].
> and it would be great to not have to keep the
> patches in a fork, especially when trying to build on top of them
> further (such as to fix the driver-firmware incompatibilities discussed
> in one of the patches of this series).
Patch 3 is a different story. At the moment, it's definitely not
acceptable. But I tried to provide hints about how you can write proper
FW compatibility logic. I'm still not optimistic that'll be easy and
maintainable, and we still reserve the right to reject patches if they
make things unmaintainable.
(Marvell clearly didn't do any real work here on maintaining good FW
compatibility.)
Brian
> Francesco, would you perhaps still be able to help in any way?
>
> Thank you, kind regards,
> K. B.
[1] I think you're referring to this:
https://lore.kernel.org/all/ZUQN4Ua8byy-Fsy8@google.com/
Re: [PATCH 0/2] net: mwifiex: add support for the SD8777 chipset
^ permalink raw reply
* Re: [PATCH 2/2] ASoC: dt-bindings: drop redundant wakeup-source definitions
From: Bui Duc Phuc @ 2026-04-29 17:12 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Lee Jones, Mark Brown, Liam Girdwood, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner, Joseph Chen,
Chris Zhong, Zhang Qing, David Rau, Animesh Agarwal, devicetree,
linux-sound, linux-rockchip, linux-arm-kernel, linux-kernel
In-Reply-To: <ce219aa2-0bb1-4a74-b40d-bd753f1a22dd@kernel.org>
Hi,
> Yes. And the ABI. You cannot have ABI which has an incompatible
> implementation. IOW, when implementation contradicts the ABI, something
> is wrong.
>
> The question of course if read_bool() is here incompatible. From the
> actual code point of view, it is compatible, but how it is documented
> and how it is intended to use: it is not compatible.
>
> Also if future schema-kernel-ABI checker gets implemented, the tool
> might report here a mistake for that reason. read_bool() means property
> is bool.
>
>
> > If the hardware supports wakeup functionality,
> > referencing the core schema is sufficient. Hardware description should
> > not be constrained by the current driver implementation
> > ( e.g. the use of device_property_read_bool() ).
> > Bindings should remain stable and generic, while drivers can evolve over time.
> So you claim that bindings can define property as integer, but drivers
> can evolve and for example read it as string?
I see your point regarding the ABI semantics and the intended use of
read_bool().
My understanding is that the I2C core currently uses of_property_read_bool()
for wakeup-source as a presence check, even though it has no way to
determine in advance whether a specific device will define the property as
a boolean or a phandle-array in its DTS.
From a behavioral point of view, switching to of_property_present() would
not change anything, but it would better reflect that the driver only checks
for the existence of the property without assuming its type.
If the expectation is to strictly follow the binding types, then
of_property_present()
seems more appropriate here.
I can prepare a patch accordingly and send it to the I2C maintainers for
review and feedback.
> >
> > Re-defining the type locally duplicates the core definition. If the
> > core schema evolves,
>
> There is no re-definition here. This is choice of subset of types.
The flow is: core schema → YAML binding (ABI) → DTS (actual usage).
From a high-level perspective, the binding may appear to redefine the
property type by narrowing its scope, while the DTS selects one valid
representation from the permitted set.
> Where is Rob's suggestion to do such cleanups for EXISTING code? I only
> see that new code should come like that.
>
> Anyway, your commit msg is for me incorrect because it misses all this
> points I made. Whether the schema code is correct, I'll defer to Rob,
> although I still claim the same I claimed before at v2 or v3 of your
> previous work - this should have defined type.
You are right that Rob did not explicitly request cleanups to existing code.
I included them to improve consistency while working on the new parts,
but for now, I will stop modifying the existing code and wait for
Rob's feedback.
Since there are differing perspectives, I would appreciate a consensus
on the preferred
approach before I send out the next version to avoid redundant rework.
Best regards,
Phuc
^ permalink raw reply
* [PATCH 11/11] arm64: percpu: use local percpu for this_cpu_*() APIs
From: Yang Shi @ 2026-04-29 17:04 UTC (permalink / raw)
To: cl, dennis, tj, urezki, catalin.marinas, will, ryan.roberts,
david, akpm, hca, gor, agordeev
Cc: yang, linux-mm, linux-arm-kernel, linux-kernel
In-Reply-To: <20260429170758.3018959-1-yang@os.amperecomputing.com>
Use local percpu address for this_cpu_*() APIs. Because the percpu
variable is mapped to the same virtual address, their address can be
calculated by using __per_cpu_local_off which has same value for all
CPUs. So preempt_disable/preempt_enable is not needed anymore. This
optimization can improve the performance for this_cpu_*() operations.
Kernel build test on AmpereOne (160 cores) with default Fedora kernel
config in a memcg roughly showed 13% - 15% sys time improvement.
Signed-off-by: Yang Shi <yang@os.amperecomputing.com>
---
arch/arm64/include/asm/percpu.h | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/arch/arm64/include/asm/percpu.h b/arch/arm64/include/asm/percpu.h
index b57b2bb00967..15db56f981de 100644
--- a/arch/arm64/include/asm/percpu.h
+++ b/arch/arm64/include/asm/percpu.h
@@ -12,6 +12,7 @@
#include <asm/stack_pointer.h>
#include <asm/sysreg.h>
+extern unsigned long __per_cpu_local_off;
static inline void set_my_cpu_offset(unsigned long off)
{
asm volatile(ALTERNATIVE("msr tpidr_el1, %0",
@@ -153,19 +154,21 @@ PERCPU_RET_OP(add, add, ldadd)
* disabled.
*/
+#define local_cpu_ptr(ptr) \
+({ \
+ __verify_pcpu_ptr(ptr); \
+ SHIFT_PERCPU_PTR(ptr, __per_cpu_local_off); \
+})
+
#define _pcp_protect(op, pcp, ...) \
({ \
- preempt_disable_notrace(); \
- op(raw_cpu_ptr(&(pcp)), __VA_ARGS__); \
- preempt_enable_notrace(); \
+ op(local_cpu_ptr(&(pcp)), __VA_ARGS__); \
})
#define _pcp_protect_return(op, pcp, args...) \
({ \
typeof(pcp) __retval; \
- preempt_disable_notrace(); \
- __retval = (typeof(pcp))op(raw_cpu_ptr(&(pcp)), ##args); \
- preempt_enable_notrace(); \
+ __retval = (typeof(pcp))op(local_cpu_ptr(&(pcp)), ##args); \
__retval; \
})
@@ -251,7 +254,7 @@ PERCPU_RET_OP(add, add, ldadd)
old__ = o; \
new__ = n; \
preempt_disable_notrace(); \
- ptr__ = raw_cpu_ptr(&(pcp)); \
+ ptr__ = local_cpu_ptr(&(pcp)); \
ret__ = cmpxchg128_local((void *)ptr__, old__, new__); \
preempt_enable_notrace(); \
ret__; \
--
2.47.0
^ permalink raw reply related
* [PATCH 10/11] arm64: kconfig: select HAVE_LOCAL_PER_CPU_MAP
From: Yang Shi @ 2026-04-29 17:04 UTC (permalink / raw)
To: cl, dennis, tj, urezki, catalin.marinas, will, ryan.roberts,
david, akpm, hca, gor, agordeev
Cc: yang, linux-mm, linux-arm-kernel, linux-kernel
In-Reply-To: <20260429170758.3018959-1-yang@os.amperecomputing.com>
ARM64 supports local percpu map, so select HAVE_LOCAL_PER_CPU_MAP
by default.
Signed-off-by: Yang Shi <yang@os.amperecomputing.com>
---
arch/arm64/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 0e12e531a5b2..1094154c1c45 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1526,6 +1526,7 @@ config NUMA
select OF_NUMA
select HAVE_SETUP_PER_CPU_AREA
select NEED_PER_CPU_PAGE_FIRST_CHUNK
+ select HAVE_LOCAL_PER_CPU_MAP
select USE_PERCPU_NUMA_NODE_ID
help
Enable NUMA (Non-Uniform Memory Access) support.
--
2.47.0
^ permalink raw reply related
* [PATCH 09/11] mm: percpu: allocate and free local percpu vm area
From: Yang Shi @ 2026-04-29 17:04 UTC (permalink / raw)
To: cl, dennis, tj, urezki, catalin.marinas, will, ryan.roberts,
david, akpm, hca, gor, agordeev
Cc: yang, linux-mm, linux-arm-kernel, linux-kernel
In-Reply-To: <20260429170758.3018959-1-yang@os.amperecomputing.com>
Allocate local percpu vm area. The delta between the allocated addr
(chunk local base) and pcpu_local_base must be same with the delta
between chunk base and pcpu_base_addr. Each CPU's local percpu area
will be mapped to its own page table. This section of page table is not
shared between CPUs.
And free local percpu vm area. Also unmap from percpu page table.
Signed-off-by: Yang Shi <yang@os.amperecomputing.com>
---
include/linux/vmalloc.h | 3 ++
mm/percpu-internal.h | 1 +
mm/percpu-vm.c | 91 +++++++++++++++++++++++++++++++++++++++++
mm/vmalloc.c | 69 ++++++++++++++++++++++++++++---
4 files changed, 159 insertions(+), 5 deletions(-)
diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h
index 3b02c0c6b371..4b53992a063c 100644
--- a/include/linux/vmalloc.h
+++ b/include/linux/vmalloc.h
@@ -311,6 +311,9 @@ struct vm_struct **pcpu_get_vm_areas(const unsigned long *offsets,
size_t align);
void pcpu_free_vm_areas(struct vm_struct **vms, int nr_vms);
+struct vm_struct *pcpu_get_local_vm_area(unsigned long hint,
+ int unit_size, size_t align);
+
# else
static inline struct vm_struct **
pcpu_get_vm_areas(const unsigned long *offsets,
diff --git a/mm/percpu-internal.h b/mm/percpu-internal.h
index 64b48b99ac06..2c560e44ee58 100644
--- a/mm/percpu-internal.h
+++ b/mm/percpu-internal.h
@@ -71,6 +71,7 @@ struct pcpu_chunk {
struct pcpu_block_md *md_blocks; /* metadata blocks */
void *data; /* chunk data */
+ void *local_data; /* chunk local vm */
bool immutable; /* no [de]population allowed */
bool isolated; /* isolated from active chunk
slots */
diff --git a/mm/percpu-vm.c b/mm/percpu-vm.c
index 4f5937090590..1e6b8fdcab71 100644
--- a/mm/percpu-vm.c
+++ b/mm/percpu-vm.c
@@ -9,6 +9,7 @@
* This is the default chunk allocator.
*/
#include "internal.h"
+#include "percpu-internal.h"
static struct page *pcpu_chunk_page(struct pcpu_chunk *chunk,
unsigned int cpu, int page_idx)
@@ -130,6 +131,11 @@ static void pcpu_pre_unmap_flush(struct pcpu_chunk *chunk,
flush_cache_vunmap(
pcpu_chunk_addr(chunk, pcpu_low_unit_cpu, page_start),
pcpu_chunk_addr(chunk, pcpu_high_unit_cpu, page_end));
+
+#ifdef CONFIG_HAVE_LOCAL_PER_CPU_MAP
+ flush_cache_vunmap((unsigned long)chunk->local_base + (page_start << PAGE_SHIFT),
+ (unsigned long)chunk->local_base + (page_end << PAGE_SHIFT));
+#endif
}
static void __pcpu_unmap_pages(unsigned long addr, int nr_pages)
@@ -137,6 +143,20 @@ static void __pcpu_unmap_pages(unsigned long addr, int nr_pages)
vunmap_range_noflush(addr, addr + (nr_pages << PAGE_SHIFT));
}
+#ifdef CONFIG_HAVE_LOCAL_PER_CPU_MAP
+static void __pcpu_unmap_pages_local(pgd_t *pgdir, unsigned long virt,
+ int nr_pages)
+{
+ __vunmap_range_noflush(pgdir, virt, virt + (nr_pages << PAGE_SHIFT));
+}
+#else
+static void __pcpu_unmap_pages_local(pgd_t *pgdir, unsigned long virt,
+ int nr_pages)
+{
+ return;
+}
+#endif
+
/**
* pcpu_unmap_pages - unmap pages out of a pcpu_chunk
* @chunk: chunk of interest
@@ -166,6 +186,10 @@ static void pcpu_unmap_pages(struct pcpu_chunk *chunk,
}
__pcpu_unmap_pages(pcpu_chunk_addr(chunk, cpu, page_start),
page_end - page_start);
+
+ __pcpu_unmap_pages_local(percpu_pgd[cpu],
+ (unsigned long)chunk->local_base + (page_start << PAGE_SHIFT),
+ page_end - page_start);
}
}
@@ -188,6 +212,12 @@ static void pcpu_post_unmap_tlb_flush(struct pcpu_chunk *chunk,
flush_tlb_kernel_range(
pcpu_chunk_addr(chunk, pcpu_low_unit_cpu, page_start),
pcpu_chunk_addr(chunk, pcpu_high_unit_cpu, page_end));
+
+#ifdef CONFIG_HAVE_LOCAL_PER_CPU_MAP
+ flush_tlb_kernel_range(
+ (unsigned long)chunk->local_base + (page_start << PAGE_SHIFT),
+ (unsigned long)chunk->local_base + (page_end << PAGE_SHIFT));
+#endif
}
static int __pcpu_map_pages(unsigned long addr, struct page **pages,
@@ -197,6 +227,32 @@ static int __pcpu_map_pages(unsigned long addr, struct page **pages,
PAGE_KERNEL, pages, PAGE_SHIFT, GFP_KERNEL);
}
+#ifdef CONFIG_HAVE_LOCAL_PER_CPU_MAP
+static int __pcpu_map_pages_local(pgd_t *pgdir, unsigned long virt, struct page **pages,
+ int nr_pages)
+{
+ unsigned int i;
+ int err = 0;
+
+ for (i = 0; i < nr_pages; i++) {
+ err = vmap_range_noflush(pgdir, virt, virt + PAGE_SIZE,
+ page_to_phys(pages[i]), PAGE_KERNEL, PAGE_SHIFT);
+ if (err)
+ return err;
+
+ virt += PAGE_SIZE;
+ }
+
+ return err;
+}
+#else
+static int __pcpu_map_pages_local(pgd_t *pgdir, unsigned long virt, struct page **pages,
+ int nr_pages)
+{
+ return 0;
+}
+#endif
+
/**
* pcpu_map_pages - map pages into a pcpu_chunk
* @chunk: chunk of interest
@@ -224,6 +280,13 @@ static int pcpu_map_pages(struct pcpu_chunk *chunk,
if (err < 0)
goto err;
+ err = __pcpu_map_pages_local(percpu_pgd[cpu],
+ (unsigned long)chunk->local_base + (page_start << PAGE_SHIFT),
+ &pages[pcpu_page_idx(cpu, page_start)],
+ page_end - page_start);
+ if (err < 0)
+ goto err;
+
for (i = page_start; i < page_end; i++)
pcpu_set_page_chunk(pages[pcpu_page_idx(cpu, i)],
chunk);
@@ -233,6 +296,9 @@ static int pcpu_map_pages(struct pcpu_chunk *chunk,
for_each_possible_cpu(tcpu) {
__pcpu_unmap_pages(pcpu_chunk_addr(chunk, tcpu, page_start),
page_end - page_start);
+ __pcpu_unmap_pages_local(percpu_pgd[cpu],
+ (unsigned long)chunk->local_base + (page_start << PAGE_SHIFT),
+ page_end - page_start);
if (tcpu == cpu)
break;
}
@@ -258,6 +324,11 @@ static void pcpu_post_map_flush(struct pcpu_chunk *chunk,
flush_cache_vmap(
pcpu_chunk_addr(chunk, pcpu_low_unit_cpu, page_start),
pcpu_chunk_addr(chunk, pcpu_high_unit_cpu, page_end));
+
+#ifdef CONFIG_HAVE_LOCAL_PER_CPU_MAP
+ flush_cache_vmap((unsigned long)chunk->local_base + (page_start << PAGE_SHIFT),
+ (unsigned long)chunk->local_base + (page_end << PAGE_SHIFT));
+#endif
}
/**
@@ -349,6 +420,24 @@ static struct pcpu_chunk *pcpu_create_chunk(gfp_t gfp)
chunk->data = vms;
chunk->base_addr = vms[0]->addr - pcpu_group_offsets[0];
+#ifdef CONFIG_HAVE_LOCAL_PER_CPU_MAP
+ unsigned long delta = (unsigned long)chunk->base_addr - (unsigned long)pcpu_base_addr;
+ unsigned long hint = delta + (unsigned long)pcpu_local_base;
+ struct vm_struct *local_vm = pcpu_get_local_vm_area(hint,
+ pcpu_unit_size, pcpu_atom_size);
+ if (!local_vm) {
+ pcpu_free_vm_areas(vms, pcpu_nr_groups);
+ pcpu_free_chunk(chunk);
+ return NULL;
+ }
+
+ chunk->local_base = local_vm->addr;
+ chunk->local_data = (void *)local_vm;
+#else
+ chunk->local_base = 0;
+ chunk->local_data = NULL;
+#endif
+
pcpu_stats_chunk_alloc();
trace_percpu_create_chunk(chunk->base_addr);
@@ -365,6 +454,8 @@ static void pcpu_destroy_chunk(struct pcpu_chunk *chunk)
if (chunk->data)
pcpu_free_vm_areas(chunk->data, pcpu_nr_groups);
+ if (chunk->local_data)
+ free_vm_area((struct vm_struct *)chunk->local_data);
pcpu_free_chunk(chunk);
}
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 8ef7d9987e18..f224ffec5696 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -4836,17 +4836,21 @@ pvm_find_va_enclose_addr(unsigned long addr)
* in - the VA we start the search(reverse order);
* out - the VA with the highest aligned end address.
* @align: alignment for required highest address
+ * @pcpu: whether request allocation from local percpu area
*
* Returns: determined end address within vmap_area
*/
static unsigned long
-pvm_determine_end_from_reverse(struct vmap_area **va, unsigned long align)
+pvm_determine_end_from_reverse(struct vmap_area **va, unsigned long align, bool pcpu)
{
unsigned long vmalloc_end;
unsigned long addr;
#ifdef CONFIG_HAVE_LOCAL_PER_CPU_MAP
- vmalloc_end = PERCPU_END & ~(align - 1);
+ if (pcpu)
+ vmalloc_end = LOCAL_PERCPU_END & ~(align - 1);
+ else
+ vmalloc_end = PERCPU_END & ~(align - 1);
#else
vmalloc_end = VMALLOC_END & ~(align - 1);
#endif
@@ -4955,7 +4959,7 @@ struct vm_struct **pcpu_get_vm_areas(const unsigned long *offsets,
end = start + sizes[area];
va = pvm_find_va_enclose_addr(vmalloc_end);
- base = pvm_determine_end_from_reverse(&va, align) - end;
+ base = pvm_determine_end_from_reverse(&va, align, false) - end;
while (true) {
/*
@@ -4976,7 +4980,7 @@ struct vm_struct **pcpu_get_vm_areas(const unsigned long *offsets,
* base downwards and then recheck.
*/
if (base + end > va->va_end) {
- base = pvm_determine_end_from_reverse(&va, align) - end;
+ base = pvm_determine_end_from_reverse(&va, align, false) - end;
term_area = area;
continue;
}
@@ -4986,7 +4990,7 @@ struct vm_struct **pcpu_get_vm_areas(const unsigned long *offsets,
*/
if (base + start < va->va_start) {
va = node_to_va(rb_prev(&va->rb_node));
- base = pvm_determine_end_from_reverse(&va, align) - end;
+ base = pvm_determine_end_from_reverse(&va, align, false) - end;
term_area = area;
continue;
}
@@ -5149,6 +5153,61 @@ void pcpu_free_vm_areas(struct vm_struct **vms, int nr_vms)
free_vm_area(vms[i]);
kfree(vms);
}
+
+#ifdef CONFIG_HAVE_LOCAL_PER_CPU_MAP
+/* Find free vm area starts from hint */
+struct vm_struct *pcpu_get_local_vm_area(unsigned long hint,
+ int unit_size, size_t align)
+{
+ struct vmap_area *tmp_va, *va;
+ struct vm_struct *vm;
+ struct vmap_node *vn;
+ unsigned long end;
+ int ret;
+
+ va = kmem_cache_zalloc(vmap_area_cachep, GFP_KERNEL);
+ vm = kzalloc(sizeof(struct vm_struct), GFP_KERNEL);
+ if (!va || !vm)
+ goto err_free;
+
+ spin_lock(&free_vmap_area_lock);
+
+ tmp_va = pvm_find_va_enclose_addr(hint);
+ if (!tmp_va)
+ return NULL;
+
+ end = pvm_determine_end_from_reverse(&tmp_va, align, true);
+
+ if (hint + unit_size > end)
+ return NULL;
+
+ ret = va_clip(&free_vmap_area_root,
+ &free_vmap_area_list, tmp_va, hint, unit_size);
+ if (ret)
+ return NULL;
+
+ va->va_start = hint;
+ va->va_end = hint + unit_size;
+
+ spin_unlock(&free_vmap_area_lock);
+
+ vn = addr_to_node(va->va_start);
+
+ spin_lock(&vn->busy.lock);
+ insert_vmap_area(va, &vn->busy.root, &vn->busy.head);
+ setup_vmalloc_vm(vm, va, VM_ALLOC,
+ pcpu_get_local_vm_area);
+ spin_unlock(&vn->busy.lock);
+
+ return vm;
+
+err_free:
+ kmem_cache_free(vmap_area_cachep, va);
+ kfree(vm);
+
+ return NULL;
+}
+#endif
#endif /* CONFIG_SMP */
#ifdef CONFIG_PRINTK
--
2.47.0
^ permalink raw reply related
* [PATCH 08/11] vmalloc: pass in pgd pointer for vmap{__vunmap}_range_noflush()
From: Yang Shi @ 2026-04-29 17:04 UTC (permalink / raw)
To: cl, dennis, tj, urezki, catalin.marinas, will, ryan.roberts,
david, akpm, hca, gor, agordeev
Cc: yang, linux-mm, linux-arm-kernel, linux-kernel
In-Reply-To: <20260429170758.3018959-1-yang@os.amperecomputing.com>
vmap{__vunmap}_range_noflush() assume manipulate init_mm pgd. The
following patch will map percpu local mapping into percpu page table by
calling them, so the assumption will no longer stand. Make them take
pgd pointer as an parameter.
Also make vmap_range_noflush() non static, it will be called outside
vmalloc in the following patch.
There is no functional change.
Signed-off-by: Yang Shi <yang@os.amperecomputing.com>
---
mm/internal.h | 5 ++++-
mm/kmsan/hooks.c | 14 +++++++-------
mm/vmalloc.c | 25 +++++++++++++------------
3 files changed, 24 insertions(+), 20 deletions(-)
diff --git a/mm/internal.h b/mm/internal.h
index 5a2ddcf68e0b..1e54945f8750 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -1553,10 +1553,13 @@ void clear_vm_uninitialized_flag(struct vm_struct *vm);
int __must_check __vmap_pages_range_noflush(unsigned long addr,
unsigned long end, pgprot_t prot,
struct page **pages, unsigned int page_shift);
+int __must_check vmap_range_noflush(pgd_t *pgdir, unsigned long addr,
+ unsigned long end, phys_addr_t phys_addr,
+ pgprot_t prot, unsigned int max_page_shift);
void vunmap_range_noflush(unsigned long start, unsigned long end);
-void __vunmap_range_noflush(unsigned long start, unsigned long end);
+void __vunmap_range_noflush(pgd_t *pgdir, unsigned long start, unsigned long end);
static inline bool vma_is_single_threaded_private(struct vm_area_struct *vma)
{
diff --git a/mm/kmsan/hooks.c b/mm/kmsan/hooks.c
index 8f22d1f22981..e2a0faf344b9 100644
--- a/mm/kmsan/hooks.c
+++ b/mm/kmsan/hooks.c
@@ -135,8 +135,8 @@ static unsigned long vmalloc_origin(unsigned long addr)
void kmsan_vunmap_range_noflush(unsigned long start, unsigned long end)
{
- __vunmap_range_noflush(vmalloc_shadow(start), vmalloc_shadow(end));
- __vunmap_range_noflush(vmalloc_origin(start), vmalloc_origin(end));
+ __vunmap_range_noflush(init_mm.pgd, vmalloc_shadow(start), vmalloc_shadow(end));
+ __vunmap_range_noflush(init_mm.pgd, vmalloc_origin(start), vmalloc_origin(end));
flush_cache_vmap(vmalloc_shadow(start), vmalloc_shadow(end));
flush_cache_vmap(vmalloc_origin(start), vmalloc_origin(end));
}
@@ -181,7 +181,7 @@ int kmsan_ioremap_page_range(unsigned long start, unsigned long end,
vmalloc_origin(start + off + PAGE_SIZE), prot, &origin,
PAGE_SHIFT);
if (mapped) {
- __vunmap_range_noflush(
+ __vunmap_range_noflush(init_mm.pgd,
vmalloc_shadow(start + off),
vmalloc_shadow(start + off + PAGE_SIZE));
err = mapped;
@@ -203,10 +203,10 @@ int kmsan_ioremap_page_range(unsigned long start, unsigned long end,
__free_pages(shadow, 1);
if (origin)
__free_pages(origin, 1);
- __vunmap_range_noflush(
+ __vunmap_range_noflush(init_mm.pgd,
vmalloc_shadow(start),
vmalloc_shadow(start + clean * PAGE_SIZE));
- __vunmap_range_noflush(
+ __vunmap_range_noflush(init_mm.pgd,
vmalloc_origin(start),
vmalloc_origin(start + clean * PAGE_SIZE));
}
@@ -233,8 +233,8 @@ void kmsan_iounmap_page_range(unsigned long start, unsigned long end)
i++, v_shadow += PAGE_SIZE, v_origin += PAGE_SIZE) {
shadow = kmsan_vmalloc_to_page_or_null((void *)v_shadow);
origin = kmsan_vmalloc_to_page_or_null((void *)v_origin);
- __vunmap_range_noflush(v_shadow, vmalloc_shadow(end));
- __vunmap_range_noflush(v_origin, vmalloc_origin(end));
+ __vunmap_range_noflush(init_mm.pgd, v_shadow, vmalloc_shadow(end));
+ __vunmap_range_noflush(init_mm.pgd, v_origin, vmalloc_origin(end));
if (shadow)
__free_pages(shadow, 1);
if (origin)
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 068a6709062d..8ef7d9987e18 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -295,9 +295,9 @@ static int vmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end,
return err;
}
-static int vmap_range_noflush(unsigned long addr, unsigned long end,
- phys_addr_t phys_addr, pgprot_t prot,
- unsigned int max_page_shift)
+int vmap_range_noflush(pgd_t *pgdir, unsigned long addr, unsigned long end,
+ phys_addr_t phys_addr, pgprot_t prot,
+ unsigned int max_page_shift)
{
pgd_t *pgd;
unsigned long start;
@@ -314,7 +314,7 @@ static int vmap_range_noflush(unsigned long addr, unsigned long end,
BUG_ON(addr >= end);
start = addr;
- pgd = pgd_offset_k(addr);
+ pgd = pgd_offset_pgd(pgdir, addr);
do {
next = pgd_addr_end(addr, end);
err = vmap_p4d_range(pgd, addr, next, phys_addr, prot,
@@ -334,8 +334,8 @@ int vmap_page_range(unsigned long addr, unsigned long end,
{
int err;
- err = vmap_range_noflush(addr, end, phys_addr, pgprot_nx(prot),
- ioremap_max_page_shift);
+ err = vmap_range_noflush(init_mm.pgd, addr, end, phys_addr,
+ pgprot_nx(prot), ioremap_max_page_shift);
flush_cache_vmap(addr, end);
if (!err)
err = kmsan_ioremap_page_range(addr, end, phys_addr, prot,
@@ -478,7 +478,7 @@ static void vunmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end,
*
* This is an internal function only. Do not use outside mm/.
*/
-void __vunmap_range_noflush(unsigned long start, unsigned long end)
+void __vunmap_range_noflush(pgd_t *pgdir, unsigned long start, unsigned long end)
{
unsigned long next;
pgd_t *pgd;
@@ -486,7 +486,7 @@ void __vunmap_range_noflush(unsigned long start, unsigned long end)
pgtbl_mod_mask mask = 0;
BUG_ON(addr >= end);
- pgd = pgd_offset_k(addr);
+ pgd = pgd_offset_pgd(pgdir, addr);
do {
next = pgd_addr_end(addr, end);
if (pgd_bad(*pgd))
@@ -503,7 +503,7 @@ void __vunmap_range_noflush(unsigned long start, unsigned long end)
void vunmap_range_noflush(unsigned long start, unsigned long end)
{
kmsan_vunmap_range_noflush(start, end);
- __vunmap_range_noflush(start, end);
+ __vunmap_range_noflush(init_mm.pgd, start, end);
}
/**
@@ -670,9 +670,10 @@ int __vmap_pages_range_noflush(unsigned long addr, unsigned long end,
for (i = 0; i < nr; i += 1U << (page_shift - PAGE_SHIFT)) {
int err;
- err = vmap_range_noflush(addr, addr + (1UL << page_shift),
- page_to_phys(pages[i]), prot,
- page_shift);
+ err = vmap_range_noflush(init_mm.pgd, addr,
+ addr + (1UL << page_shift),
+ page_to_phys(pages[i]), prot,
+ page_shift);
if (err)
return err;
--
2.47.0
^ permalink raw reply related
* [PATCH 06/11] mm: percpu: set up first chunk and reserve chunk
From: Yang Shi @ 2026-04-29 17:04 UTC (permalink / raw)
To: cl, dennis, tj, urezki, catalin.marinas, will, ryan.roberts,
david, akpm, hca, gor, agordeev
Cc: yang, linux-mm, linux-arm-kernel, linux-kernel
In-Reply-To: <20260429170758.3018959-1-yang@os.amperecomputing.com>
Set up the first chunk and reserve chunk with local percpu map.
Signed-off-by: Yang Shi <yang@os.amperecomputing.com>
---
include/linux/percpu.h | 2 +-
mm/percpu-internal.h | 2 ++
mm/percpu.c | 24 +++++++++++++++---------
3 files changed, 18 insertions(+), 10 deletions(-)
diff --git a/include/linux/percpu.h b/include/linux/percpu.h
index 85bf8dd9f087..dba050f5b548 100644
--- a/include/linux/percpu.h
+++ b/include/linux/percpu.h
@@ -113,7 +113,7 @@ extern struct pcpu_alloc_info * __init pcpu_alloc_alloc_info(int nr_groups,
extern void __init pcpu_free_alloc_info(struct pcpu_alloc_info *ai);
extern void __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai,
- void *base_addr);
+ void *base_addr, void *local_base);
extern int __init pcpu_embed_first_chunk(size_t reserved_size, size_t dyn_size,
size_t atom_size,
diff --git a/mm/percpu-internal.h b/mm/percpu-internal.h
index b33d1f5aba1b..64b48b99ac06 100644
--- a/mm/percpu-internal.h
+++ b/mm/percpu-internal.h
@@ -64,6 +64,8 @@ struct pcpu_chunk {
* chunk_md.
*/
void *base_addr ____cacheline_aligned_in_smp;
+ /* percpu local base address of the chunk */
+ void *local_base;
unsigned long *alloc_map; /* allocation map */
struct pcpu_block_md *md_blocks; /* metadata blocks */
diff --git a/mm/percpu.c b/mm/percpu.c
index 59682b77089c..5148c5ccf9e3 100644
--- a/mm/percpu.c
+++ b/mm/percpu.c
@@ -1349,15 +1349,16 @@ static void pcpu_init_md_blocks(struct pcpu_chunk *chunk)
* Chunk serving the region at @tmp_addr of @map_size.
*/
static struct pcpu_chunk * __init pcpu_alloc_first_chunk(unsigned long tmp_addr,
- int map_size)
+ unsigned long local_tmp, int map_size)
{
struct pcpu_chunk *chunk;
- unsigned long aligned_addr;
+ unsigned long aligned_addr, aligned_local;
int start_offset, offset_bits, region_size, region_bits;
size_t alloc_size;
/* region calculations */
aligned_addr = tmp_addr & PAGE_MASK;
+ aligned_local = local_tmp & PAGE_MASK;
start_offset = tmp_addr - aligned_addr;
region_size = ALIGN(start_offset + map_size, PAGE_SIZE);
@@ -1370,6 +1371,7 @@ static struct pcpu_chunk * __init pcpu_alloc_first_chunk(unsigned long tmp_addr,
INIT_LIST_HEAD(&chunk->list);
chunk->base_addr = (void *)aligned_addr;
+ chunk->local_base = (void *)aligned_local;
chunk->start_offset = start_offset;
chunk->end_offset = region_size - chunk->start_offset - map_size;
@@ -2562,7 +2564,7 @@ static void pcpu_dump_alloc_info(const char *lvl,
* and available for dynamic allocation like any other chunk.
*/
void __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai,
- void *base_addr)
+ void *base_addr, void *local_base)
{
size_t size_sum = ai->static_size + ai->reserved_size + ai->dyn_size;
size_t static_size, dyn_size;
@@ -2572,7 +2574,7 @@ void __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai,
unsigned int cpu;
int *unit_map;
int group, unit, i;
- unsigned long tmp_addr;
+ unsigned long tmp_addr, local_tmp;
size_t alloc_size;
#define PCPU_SETUP_BUG_ON(cond) do { \
@@ -2713,11 +2715,13 @@ void __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai,
* chunk.
*/
tmp_addr = (unsigned long)base_addr + static_size;
+ local_tmp = (unsigned long)local_base + static_size;
if (ai->reserved_size)
- pcpu_reserved_chunk = pcpu_alloc_first_chunk(tmp_addr,
+ pcpu_reserved_chunk = pcpu_alloc_first_chunk(tmp_addr, local_tmp,
ai->reserved_size);
tmp_addr = (unsigned long)base_addr + static_size + ai->reserved_size;
- pcpu_first_chunk = pcpu_alloc_first_chunk(tmp_addr, dyn_size);
+ local_tmp = (unsigned long)local_base + static_size + ai->reserved_size;
+ pcpu_first_chunk = pcpu_alloc_first_chunk(tmp_addr, local_tmp, dyn_size);
pcpu_nr_empty_pop_pages = pcpu_first_chunk->nr_empty_pop_pages;
pcpu_chunk_relocate(pcpu_first_chunk, -1);
@@ -3108,7 +3112,7 @@ int __init pcpu_embed_first_chunk(size_t reserved_size, size_t dyn_size,
PFN_DOWN(size_sum), ai->static_size, ai->reserved_size,
ai->dyn_size, ai->unit_size);
- pcpu_setup_first_chunk(ai, base);
+ pcpu_setup_first_chunk(ai, base, NULL);
goto out_free;
out_free_areas:
@@ -3256,6 +3260,8 @@ int __init pcpu_page_first_chunk(size_t reserved_size, pcpu_fc_cpu_to_node_fn_t
vm.flags = VM_ALLOC;
vm.size = num_possible_cpus() * ai->unit_size;
vm_area_register_early(&vm, PAGE_SIZE);
+
+ pcpu_vm.addr = NULL;
#endif
for (unit = 0; unit < num_possible_cpus(); unit++) {
@@ -3290,7 +3296,7 @@ int __init pcpu_page_first_chunk(size_t reserved_size, pcpu_fc_cpu_to_node_fn_t
unit_pages, psize_str, ai->static_size,
ai->reserved_size, ai->dyn_size);
- pcpu_setup_first_chunk(ai, vm.addr);
+ pcpu_setup_first_chunk(ai, vm.addr, pcpu_vm.addr);
goto out_free_ar;
enomem:
@@ -3372,7 +3378,7 @@ void __init setup_per_cpu_areas(void)
ai->groups[0].nr_units = 1;
ai->groups[0].cpu_map[0] = 0;
- pcpu_setup_first_chunk(ai, fc);
+ pcpu_setup_first_chunk(ai, fc, NULL);
pcpu_free_alloc_info(ai);
}
--
2.47.0
^ permalink raw reply related
* [PATCH 05/11] arm64: mm: map local percpu first chunk
From: Yang Shi @ 2026-04-29 17:04 UTC (permalink / raw)
To: cl, dennis, tj, urezki, catalin.marinas, will, ryan.roberts,
david, akpm, hca, gor, agordeev
Cc: yang, linux-mm, linux-arm-kernel, linux-kernel
In-Reply-To: <20260429170758.3018959-1-yang@os.amperecomputing.com>
Allocate local percpu area and map to percpu page table for the first
chunk.
It doesn't work for PCPU_FC_EMBED because the percpu base adddress may
be in linear mapping space in this case, it will result in returning huge
offset for percpu allocator. So percpu local map just can work with
PCPU_FC_PAGE which allocates percpu variables from vmalloc area or the
dedicated percpu area. So unselect NEED_PER_CPU_EMBED_FIRST_CHUNK if
the architectures support percpu local map.
Signed-off-by: Yang Shi <yang@os.amperecomputing.com>
---
arch/arm64/Kconfig | 1 -
arch/arm64/include/asm/mmu.h | 2 ++
arch/arm64/kernel/smp.c | 25 ++-----------------------
arch/arm64/mm/mmu.c | 18 ++++++++++++++++++
mm/percpu-internal.h | 12 ++++++++++++
mm/percpu.c | 13 +++++++++++++
6 files changed, 47 insertions(+), 24 deletions(-)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index fe60738e5943..0e12e531a5b2 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1525,7 +1525,6 @@ config NUMA
select GENERIC_ARCH_NUMA
select OF_NUMA
select HAVE_SETUP_PER_CPU_AREA
- select NEED_PER_CPU_EMBED_FIRST_CHUNK
select NEED_PER_CPU_PAGE_FIRST_CHUNK
select USE_PERCPU_NUMA_NODE_ID
help
diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h
index 8ed3b5f3cf84..d81e5c483b55 100644
--- a/arch/arm64/include/asm/mmu.h
+++ b/arch/arm64/include/asm/mmu.h
@@ -73,6 +73,8 @@ extern void *fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot);
extern void mark_linear_text_alias_ro(void);
extern int split_kernel_leaf_mapping(unsigned long start, unsigned long end);
extern void linear_map_maybe_split_to_ptes(void);
+extern void map_local_percpu_first_chunk(pgd_t *pgdir, unsigned long virt,
+ struct page **pages, unsigned int nr);
/*
* This check is triggered during the early boot before the cpufeature
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 0cc8f4a9efa7..4caa6ebec12f 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -831,36 +831,15 @@ int early_cpu_to_node(int cpu)
return cpu_to_node_map[cpu];
}
-static int __init pcpu_cpu_distance(unsigned int from, unsigned int to)
-{
- return node_distance(early_cpu_to_node(from), early_cpu_to_node(to));
-}
-
void __init setup_per_cpu_areas(void)
{
unsigned long delta;
unsigned int cpu;
int rc = -EINVAL;
- if (pcpu_chosen_fc != PCPU_FC_PAGE) {
- /*
- * Always reserve area for module percpu variables. That's
- * what the legacy allocator did.
- */
- rc = pcpu_embed_first_chunk(PERCPU_MODULE_RESERVE,
- PERCPU_DYNAMIC_RESERVE, PAGE_SIZE,
- pcpu_cpu_distance,
- early_cpu_to_node);
-#ifdef CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK
- if (rc < 0)
- pr_warn("PERCPU: %s allocator failed (%d), falling back to page size\n",
- pcpu_fc_names[pcpu_chosen_fc], rc);
-#endif
- }
-
#ifdef CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK
- if (rc < 0)
- rc = pcpu_page_first_chunk(PERCPU_MODULE_RESERVE, early_cpu_to_node);
+ /* PCPU page table just can support PCPU_FC_PAGE */
+ rc = pcpu_page_first_chunk(PERCPU_MODULE_RESERVE, early_cpu_to_node);
#endif
if (rc < 0)
panic("Failed to initialize percpu areas (err=%d).", rc);
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index 7708dcc1b6a9..81b662433677 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -1044,6 +1044,24 @@ void __init linear_map_maybe_split_to_ptes(void)
}
}
+void __init map_local_percpu_first_chunk(pgd_t *pgdir, unsigned long virt,
+ struct page **pages, unsigned int nr)
+{
+ int i;
+
+ arch_enter_lazy_mmu_mode();
+
+ for (i = 0; i < nr; i++) {
+ phys_addr_t phys = page_to_phys(pages[i]);
+ __create_pgd_mapping_locked(pgdir, phys, virt, PAGE_SIZE, PAGE_KERNEL,
+ early_pgtable_alloc, NO_EXEC_MAPPINGS);
+
+ virt += PAGE_SIZE;
+ }
+
+ arch_leave_lazy_mmu_mode();
+}
+
/*
* This function can only be used to modify existing table entries,
* without allocating new levels of table. Note that this permits the
diff --git a/mm/percpu-internal.h b/mm/percpu-internal.h
index 4b3d6ec43703..b33d1f5aba1b 100644
--- a/mm/percpu-internal.h
+++ b/mm/percpu-internal.h
@@ -5,6 +5,7 @@
#include <linux/types.h>
#include <linux/percpu.h>
#include <linux/memcontrol.h>
+#include <linux/mmu_context.h>
/*
* pcpu_block_md is the metadata block struct.
@@ -162,6 +163,17 @@ static inline size_t pcpu_obj_full_size(size_t size)
return size * num_possible_cpus() + extra_size;
}
+#ifdef CONFIG_HAVE_LOCAL_PER_CPU_MAP
+extern void __init map_local_percpu_first_chunk(pgd_t *pgdir, unsigned long virt,
+ struct page **pages, unsigned int nr);
+#else
+static inline void __init map_local_percpu_first_chunk(pgd_t *pgdir, unsigned long virt,
+ struct page **pages, unsigned int nr)
+{
+ return;
+}
+#endif
+
#ifdef CONFIG_PERCPU_STATS
#include <linux/spinlock.h>
diff --git a/mm/percpu.c b/mm/percpu.c
index daa2c88e6971..59682b77089c 100644
--- a/mm/percpu.c
+++ b/mm/percpu.c
@@ -3194,6 +3194,7 @@ void __init __weak pcpu_populate_pte(unsigned long addr)
int __init pcpu_page_first_chunk(size_t reserved_size, pcpu_fc_cpu_to_node_fn_t cpu_to_nd_fn)
{
static struct vm_struct vm;
+ static struct vm_struct pcpu_vm;
struct pcpu_alloc_info *ai;
char psize_str[16];
int unit_pages;
@@ -3247,6 +3248,10 @@ int __init pcpu_page_first_chunk(size_t reserved_size, pcpu_fc_cpu_to_node_fn_t
vm.addr = (void *)ALIGN(PERCPU_START, PAGE_SIZE);
vm.size = num_possible_cpus() * ai->unit_size;
vm_area_add_early(&vm);
+
+ pcpu_vm.addr = (void *)ALIGN(LOCAL_PERCPU_START, PAGE_SIZE);
+ pcpu_vm.size = ai->unit_size;
+ vm_area_add_early(&pcpu_vm);
#else
vm.flags = VM_ALLOC;
vm.size = num_possible_cpus() * ai->unit_size;
@@ -3270,6 +3275,14 @@ int __init pcpu_page_first_chunk(size_t reserved_size, pcpu_fc_cpu_to_node_fn_t
/* copy static data */
memcpy((void *)unit_addr, __per_cpu_start, ai->static_size);
+
+ /*
+ * Map percpu data to PERCPU map.
+ *
+ * PCPU_FC_EMBED can't support it.
+ */
+ map_local_percpu_first_chunk(percpu_pgd[unit], (unsigned long)pcpu_vm.addr,
+ &pages[unit * unit_pages], unit_pages);
}
/* we're ready, commit */
--
2.47.0
^ permalink raw reply related
* [PATCH 07/11] arm64: mm: introduce __per_cpu_local_off
From: Yang Shi @ 2026-04-29 17:04 UTC (permalink / raw)
To: cl, dennis, tj, urezki, catalin.marinas, will, ryan.roberts,
david, akpm, hca, gor, agordeev
Cc: yang, linux-mm, linux-arm-kernel, linux-kernel
In-Reply-To: <20260429170758.3018959-1-yang@os.amperecomputing.com>
this_cpu_*() ops will use it to get the local percpu address. It has
the same value for all CPUs.
Also introduce pcpu_local_base, which is the base address of local
percpu map.
Signed-off-by: Yang Shi <yang@os.amperecomputing.com>
---
arch/arm64/kernel/smp.c | 4 ++++
include/linux/percpu.h | 2 ++
mm/percpu.c | 3 +++
3 files changed, 9 insertions(+)
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 4caa6ebec12f..62afabf86ba1 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -825,6 +825,9 @@ extern int cpu_to_node_map[NR_CPUS];
unsigned long __per_cpu_offset[NR_CPUS] __read_mostly;
EXPORT_SYMBOL(__per_cpu_offset);
+/* Used to calculate pcpu local address, the offset is same for all CPUs */
+unsigned long __per_cpu_local_off __read_mostly;
+EXPORT_SYMBOL(__per_cpu_local_off);
int early_cpu_to_node(int cpu)
{
@@ -847,6 +850,7 @@ void __init setup_per_cpu_areas(void)
delta = (unsigned long)pcpu_base_addr - (unsigned long)__per_cpu_start;
for_each_possible_cpu(cpu)
__per_cpu_offset[cpu] = delta + pcpu_unit_offsets[cpu];
+ __per_cpu_local_off = (unsigned long)pcpu_local_base - (unsigned long)__per_cpu_start;
}
static const char *ipi_types[MAX_IPI] __tracepoint_string = {
diff --git a/include/linux/percpu.h b/include/linux/percpu.h
index dba050f5b548..e29ebd265087 100644
--- a/include/linux/percpu.h
+++ b/include/linux/percpu.h
@@ -74,6 +74,8 @@
extern void *pcpu_base_addr;
extern const unsigned long *pcpu_unit_offsets;
+/* percpu local mapping base */
+extern void *pcpu_local_base;
struct pcpu_group_info {
int nr_units; /* aligned # of units */
diff --git a/mm/percpu.c b/mm/percpu.c
index 5148c5ccf9e3..17d0c2b0de5a 100644
--- a/mm/percpu.c
+++ b/mm/percpu.c
@@ -145,6 +145,8 @@ static unsigned int pcpu_high_unit_cpu __ro_after_init;
/* the address of the first chunk which starts with the kernel static area */
void *pcpu_base_addr __ro_after_init;
+/* The address of the first chunk local mapping */
+void *pcpu_local_base __ro_after_init;
static const int *pcpu_unit_map __ro_after_init; /* cpu -> unit */
const unsigned long *pcpu_unit_offsets __ro_after_init; /* cpu -> unit offset */
@@ -3297,6 +3299,7 @@ int __init pcpu_page_first_chunk(size_t reserved_size, pcpu_fc_cpu_to_node_fn_t
ai->reserved_size, ai->dyn_size);
pcpu_setup_first_chunk(ai, vm.addr, pcpu_vm.addr);
+ pcpu_local_base = pcpu_vm.addr;
goto out_free_ar;
enomem:
--
2.47.0
^ permalink raw reply related
* [PATCH 04/11] mm: percpu: prepare to use dedicated percpu area
From: Yang Shi @ 2026-04-29 17:04 UTC (permalink / raw)
To: cl, dennis, tj, urezki, catalin.marinas, will, ryan.roberts,
david, akpm, hca, gor, agordeev
Cc: yang, linux-mm, linux-arm-kernel, linux-kernel
In-Reply-To: <20260429170758.3018959-1-yang@os.amperecomputing.com>
The percpu variables are allocated from vmalloc area by default. The
architectures which support local percpu map will allocate percpu
variables from dedicated percpu area, for example, ARM64.
Introduce a new kernel config, CONFIG_HAVE_LOCAL_PER_CPU_MAP. The
architectures which support local percpu map will need to select it. If
it is enabled, allocate percpu variables from the dedicated percpu area.
Signed-off-by: Yang Shi <yang@os.amperecomputing.com>
---
mm/Kconfig | 3 +++
mm/percpu.c | 6 ++++++
mm/vmalloc.c | 20 +++++++++++++++++---
3 files changed, 26 insertions(+), 3 deletions(-)
diff --git a/mm/Kconfig b/mm/Kconfig
index e8bf1e9e6ad9..ccdf58b63fb8 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -1022,6 +1022,9 @@ config NEED_PER_CPU_PAGE_FIRST_CHUNK
config USE_PERCPU_NUMA_NODE_ID
bool
+config HAVE_LOCAL_PER_CPU_MAP
+ bool
+
config HAVE_SETUP_PER_CPU_AREA
bool
diff --git a/mm/percpu.c b/mm/percpu.c
index b0676b8054ed..daa2c88e6971 100644
--- a/mm/percpu.c
+++ b/mm/percpu.c
@@ -3243,9 +3243,15 @@ int __init pcpu_page_first_chunk(size_t reserved_size, pcpu_fc_cpu_to_node_fn_t
}
/* allocate vm area, map the pages and copy static data */
+#ifdef CONFIG_HAVE_LOCAL_PER_CPU_MAP
+ vm.addr = (void *)ALIGN(PERCPU_START, PAGE_SIZE);
+ vm.size = num_possible_cpus() * ai->unit_size;
+ vm_area_add_early(&vm);
+#else
vm.flags = VM_ALLOC;
vm.size = num_possible_cpus() * ai->unit_size;
vm_area_register_early(&vm, PAGE_SIZE);
+#endif
for (unit = 0; unit < num_possible_cpus(); unit++) {
unsigned long unit_addr =
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index aa08651ec0df..068a6709062d 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -4841,9 +4841,15 @@ pvm_find_va_enclose_addr(unsigned long addr)
static unsigned long
pvm_determine_end_from_reverse(struct vmap_area **va, unsigned long align)
{
- unsigned long vmalloc_end = VMALLOC_END & ~(align - 1);
+ unsigned long vmalloc_end;
unsigned long addr;
+#ifdef CONFIG_HAVE_LOCAL_PER_CPU_MAP
+ vmalloc_end = PERCPU_END & ~(align - 1);
+#else
+ vmalloc_end = VMALLOC_END & ~(align - 1);
+#endif
+
if (likely(*va)) {
list_for_each_entry_from_reverse((*va),
&free_vmap_area_list, list) {
@@ -4884,14 +4890,22 @@ struct vm_struct **pcpu_get_vm_areas(const unsigned long *offsets,
const size_t *sizes, int nr_vms,
size_t align)
{
- const unsigned long vmalloc_start = ALIGN(VMALLOC_START, align);
- const unsigned long vmalloc_end = VMALLOC_END & ~(align - 1);
+ unsigned long vmalloc_start;
+ unsigned long vmalloc_end;
struct vmap_area **vas, *va;
struct vm_struct **vms;
int area, area2, last_area, term_area;
unsigned long base, start, size, end, last_end, orig_start, orig_end;
bool purged = false;
+#ifdef CONFIG_HAVE_LOCAL_PER_CPU_MAP
+ vmalloc_start = ALIGN(PERCPU_START, align);
+ vmalloc_end = PERCPU_END & ~(align - 1);
+#else
+ vmalloc_start = ALIGN(VMALLOC_START, align);
+ vmalloc_end = VMALLOC_END & ~(align - 1);
+#endif
+
/* verify parameters and allocate data structures */
BUG_ON(offset_in_page(align) || !is_power_of_2(align));
for (last_area = 0, area = 0; area < nr_vms; area++) {
--
2.47.0
^ permalink raw reply related
* [PATCH 03/11] arm64: smp: define setup_per_cpu_areas()
From: Yang Shi @ 2026-04-29 17:04 UTC (permalink / raw)
To: cl, dennis, tj, urezki, catalin.marinas, will, ryan.roberts,
david, akpm, hca, gor, agordeev
Cc: yang, linux-mm, linux-arm-kernel, linux-kernel
In-Reply-To: <20260429170758.3018959-1-yang@os.amperecomputing.com>
We need to modify setup_per_cpu_areas() to set up local percpu area for
arm64, the drivers/base/arch_numa.c implementation won't work anymore, so
moved it to the arm64 directory. No functional change.
It looks like riscv is the only user of it after this change.
Signed-off-by: Yang Shi <yang@os.amperecomputing.com>
---
arch/arm64/kernel/smp.c | 49 ++++++++++++++++++++++++++++++++++++++
drivers/base/arch_numa.c | 51 +---------------------------------------
2 files changed, 50 insertions(+), 50 deletions(-)
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 88a82eb56fb3..0cc8f4a9efa7 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -821,6 +821,55 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
}
}
+extern int cpu_to_node_map[NR_CPUS];
+
+unsigned long __per_cpu_offset[NR_CPUS] __read_mostly;
+EXPORT_SYMBOL(__per_cpu_offset);
+
+int early_cpu_to_node(int cpu)
+{
+ return cpu_to_node_map[cpu];
+}
+
+static int __init pcpu_cpu_distance(unsigned int from, unsigned int to)
+{
+ return node_distance(early_cpu_to_node(from), early_cpu_to_node(to));
+}
+
+void __init setup_per_cpu_areas(void)
+{
+ unsigned long delta;
+ unsigned int cpu;
+ int rc = -EINVAL;
+
+ if (pcpu_chosen_fc != PCPU_FC_PAGE) {
+ /*
+ * Always reserve area for module percpu variables. That's
+ * what the legacy allocator did.
+ */
+ rc = pcpu_embed_first_chunk(PERCPU_MODULE_RESERVE,
+ PERCPU_DYNAMIC_RESERVE, PAGE_SIZE,
+ pcpu_cpu_distance,
+ early_cpu_to_node);
+#ifdef CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK
+ if (rc < 0)
+ pr_warn("PERCPU: %s allocator failed (%d), falling back to page size\n",
+ pcpu_fc_names[pcpu_chosen_fc], rc);
+#endif
+ }
+
+#ifdef CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK
+ if (rc < 0)
+ rc = pcpu_page_first_chunk(PERCPU_MODULE_RESERVE, early_cpu_to_node);
+#endif
+ if (rc < 0)
+ panic("Failed to initialize percpu areas (err=%d).", rc);
+
+ delta = (unsigned long)pcpu_base_addr - (unsigned long)__per_cpu_start;
+ for_each_possible_cpu(cpu)
+ __per_cpu_offset[cpu] = delta + pcpu_unit_offsets[cpu];
+}
+
static const char *ipi_types[MAX_IPI] __tracepoint_string = {
[IPI_RESCHEDULE] = "Rescheduling interrupts",
[IPI_CALL_FUNC] = "Function call interrupts",
diff --git a/drivers/base/arch_numa.c b/drivers/base/arch_numa.c
index c99f2ab105e5..b3b91ceed6a9 100644
--- a/drivers/base/arch_numa.c
+++ b/drivers/base/arch_numa.c
@@ -16,7 +16,7 @@
#include <asm/sections.h>
-static int cpu_to_node_map[NR_CPUS] = { [0 ... NR_CPUS-1] = NUMA_NO_NODE };
+int cpu_to_node_map[NR_CPUS] = { [0 ... NR_CPUS-1] = NUMA_NO_NODE };
bool numa_off;
@@ -140,55 +140,6 @@ void __init early_map_cpu_to_node(unsigned int cpu, int nid)
set_cpu_numa_node(cpu, nid);
}
-#ifdef CONFIG_HAVE_SETUP_PER_CPU_AREA
-unsigned long __per_cpu_offset[NR_CPUS] __read_mostly;
-EXPORT_SYMBOL(__per_cpu_offset);
-
-int early_cpu_to_node(int cpu)
-{
- return cpu_to_node_map[cpu];
-}
-
-static int __init pcpu_cpu_distance(unsigned int from, unsigned int to)
-{
- return node_distance(early_cpu_to_node(from), early_cpu_to_node(to));
-}
-
-void __init setup_per_cpu_areas(void)
-{
- unsigned long delta;
- unsigned int cpu;
- int rc = -EINVAL;
-
- if (pcpu_chosen_fc != PCPU_FC_PAGE) {
- /*
- * Always reserve area for module percpu variables. That's
- * what the legacy allocator did.
- */
- rc = pcpu_embed_first_chunk(PERCPU_MODULE_RESERVE,
- PERCPU_DYNAMIC_RESERVE, PAGE_SIZE,
- pcpu_cpu_distance,
- early_cpu_to_node);
-#ifdef CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK
- if (rc < 0)
- pr_warn("PERCPU: %s allocator failed (%d), falling back to page size\n",
- pcpu_fc_names[pcpu_chosen_fc], rc);
-#endif
- }
-
-#ifdef CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK
- if (rc < 0)
- rc = pcpu_page_first_chunk(PERCPU_MODULE_RESERVE, early_cpu_to_node);
-#endif
- if (rc < 0)
- panic("Failed to initialize percpu areas (err=%d).", rc);
-
- delta = (unsigned long)pcpu_base_addr - (unsigned long)__per_cpu_start;
- for_each_possible_cpu(cpu)
- __per_cpu_offset[cpu] = delta + pcpu_unit_offsets[cpu];
-}
-#endif
-
/*
* Initialize NODE_DATA for a node on the local memory
*/
--
2.47.0
^ permalink raw reply related
* [PATCH 02/11] arm64: mm: define percpu virtual space area
From: Yang Shi @ 2026-04-29 17:04 UTC (permalink / raw)
To: cl, dennis, tj, urezki, catalin.marinas, will, ryan.roberts,
david, akpm, hca, gor, agordeev
Cc: yang, linux-mm, linux-arm-kernel, linux-kernel
In-Reply-To: <20260429170758.3018959-1-yang@os.amperecomputing.com>
The percpu allocator returns offset from percpu base address. The
percpu base address is determined by the first chunk which is typically
in the low address of vmalloc space, however percpu varriables are
typically allocated from the high address of vmalloc space. So the offset
could be quite big. It may be the whole size of vmalloc space. To support
local percpu mapping in order to optimize this_cpu_*() ops, the percpu
allocator needs to allocate memory from local percpu area too in the following
patch and the offset to local percpu base address must be same because
the offset returned by percpu allocator must be used to access both
global percpu and local percpu.
We can half vmalloc space to have either half dedicated to local percpu,
but it wastes too much address space.
So carve out dedicated global percpu and local percpu areas. Each area size
is 2 * PGDIR_SIZE. It is 1TB with 4K page size, should be big enough for percpu.
The percpu areas are PGDIR_SIZE aligned in order to just need to sync percpu
page table at pgd level to minimize page table sync overhead.
The kernel virtual address space layout now looks like:
+-----------------+
| Linear mapping |
+-----------------+
| Modules |
+-----------------+
| Vmalloc |
+-----------------+
| Global Percpu |
+-----------------+
| Local Percpu |
+-----------------+
| Vmemap |
+-----------------+
| PCI I/O |
+-----------------+
| Fixed map |
+-----------------+
Signed-off-by: Yang Shi <yang@os.amperecomputing.com>
---
arch/arm64/include/asm/pgtable.h | 21 ++++++++++++++++++---
arch/arm64/mm/mmu.c | 4 ++++
arch/arm64/mm/ptdump.c | 4 ++++
3 files changed, 26 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index 38eec71ec383..9043b976682c 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -18,14 +18,29 @@
* VMALLOC range.
*
* VMALLOC_START: beginning of the kernel vmalloc space
- * VMALLOC_END: extends to the available space below vmemmap
+ * VMALLOC_END: extends to the space below global percpu area
*/
#define VMALLOC_START (MODULES_END)
+#define VMALLOC_END (PERCPU_START - SZ_8M)
+
+/*
+ * PERCPU range
+ *
+ * PERCPU_START: beginning of global percpu area
+ * PERCPU_END: end of global percpu area
+ * LOCAL_PERCPU_START: beginning of local percpu area
+ * LOCAL_PERCPU_END: end of local percpu area, extend to the available
+ * space below vmemap
+ */
+#define PERCPU_SIZE (2 * PGDIR_SIZE)
+#define PERCPU_START (PERCPU_END - PERCPU_SIZE)
+#define PERCPU_END (LOCAL_PERCPU_START)
+#define LOCAL_PERCPU_START (LOCAL_PERCPU_END - PERCPU_SIZE)
#if VA_BITS == VA_BITS_MIN
-#define VMALLOC_END (VMEMMAP_START - SZ_8M)
+#define LOCAL_PERCPU_END (ALIGN_DOWN(VMEMMAP_START, PGDIR_SIZE))
#else
#define VMEMMAP_UNUSED_NPAGES ((_PAGE_OFFSET(vabits_actual) - PAGE_OFFSET) >> PAGE_SHIFT)
-#define VMALLOC_END (VMEMMAP_START + VMEMMAP_UNUSED_NPAGES * sizeof(struct page) - SZ_8M)
+#define LOCAL_PERCPU_END (ALIGN_DOWN((VMEMMAP_START + VMEMMAP_UNUSED_NPAGES * sizeof(struct page)), PGDIR_SIZE))
#endif
#define vmemmap ((struct page *)VMEMMAP_START - (memstart_addr >> PAGE_SHIFT))
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index ed1545baa045..7708dcc1b6a9 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -108,6 +108,10 @@ void arch_sync_kernel_mappings(unsigned long start, unsigned long end)
if (!percpu_pgd_setup_done)
return;
+ /* Don't sync local percpu area page table */
+ if (start >= LOCAL_PERCPU_START && end < LOCAL_PERCPU_END)
+ return;
+
addr = start;
do {
pgd = READ_ONCE(*pgdp);
diff --git a/arch/arm64/mm/ptdump.c b/arch/arm64/mm/ptdump.c
index ab9899ca1e5f..7d5696a48917 100644
--- a/arch/arm64/mm/ptdump.c
+++ b/arch/arm64/mm/ptdump.c
@@ -389,6 +389,10 @@ static int __init ptdump_init(void)
{ MODULES_END, "Modules end" },
{ VMALLOC_START, "vmalloc() area" },
{ VMALLOC_END, "vmalloc() end" },
+ { PERCPU_START, "Global percpu start" },
+ { PERCPU_END, "Global percpu end" },
+ { LOCAL_PERCPU_START, "Local percpu start" },
+ { LOCAL_PERCPU_END, "Local percpu end" },
{ vmemmap_start, "vmemmap start" },
{ VMEMMAP_END, "vmemmap end" },
{ PCI_IO_START, "PCI I/O start" },
--
2.47.0
^ permalink raw reply related
* [PATCH 01/11] arm64: mm: enable percpu kernel page table
From: Yang Shi @ 2026-04-29 17:04 UTC (permalink / raw)
To: cl, dennis, tj, urezki, catalin.marinas, will, ryan.roberts,
david, akpm, hca, gor, agordeev
Cc: yang, linux-mm, linux-arm-kernel, linux-kernel
In-Reply-To: <20260429170758.3018959-1-yang@os.amperecomputing.com>
Currently all cpus share the same kernel page table (swapper_pg_dir),
this patch creates kernel page table for each cpu and each cpu uses its
own kernel page table. The cpu 0 keeps using swapper_pg_dir. All the
kernel page tables share the same content. So we don't have to
duplicate the whole page table for all cpus, we just need to have
different pgd page for each cpu. All kernel page table modification (split,
creation, deletion, etc) actually still happens on swapper_pg_dir, the
modification needs to be synchronized to other cpu's page tables when
the pgd level is modified.
The percpu page table can't be shared across cores, so clear cnp bit
too even though CNP is supported.
Some features may not work with it for now, for example, KPTI.
Signed-off-by: Yang Shi <yang@os.amperecomputing.com>
---
arch/arm64/include/asm/mmu.h | 1 +
arch/arm64/include/asm/mmu_context.h | 6 +++-
arch/arm64/include/asm/pgtable.h | 3 ++
arch/arm64/kernel/setup.c | 3 ++
arch/arm64/kernel/smp.c | 8 +++++
arch/arm64/mm/mmu.c | 53 ++++++++++++++++++++++++++++
6 files changed, 73 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h
index 5e1211c540ab..8ed3b5f3cf84 100644
--- a/arch/arm64/include/asm/mmu.h
+++ b/arch/arm64/include/asm/mmu.h
@@ -63,6 +63,7 @@ static inline bool arm64_kernel_unmapped_at_el0(void)
extern void arm64_memblock_init(void);
extern void paging_init(void);
extern void bootmem_init(void);
+extern void setup_percpu_pgd(void);
extern void create_mapping_noalloc(phys_addr_t phys, unsigned long virt,
phys_addr_t size, pgprot_t prot);
extern void create_pgd_mapping(struct mm_struct *mm, phys_addr_t phys,
diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h
index 803b68758152..0ee900eed612 100644
--- a/arch/arm64/include/asm/mmu_context.h
+++ b/arch/arm64/include/asm/mmu_context.h
@@ -27,6 +27,7 @@
#include <asm/tlbflush.h>
extern bool rodata_full;
+extern pgd_t *percpu_pgd[NR_CPUS];
static inline void contextidr_thread_switch(struct task_struct *next)
{
@@ -138,7 +139,10 @@ void __cpu_replace_ttbr1(pgd_t *pgdp, bool cnp);
static inline void cpu_enable_swapper_cnp(void)
{
- __cpu_replace_ttbr1(lm_alias(swapper_pg_dir), true);
+ unsigned int cpu = smp_processor_id();
+ pgd_t *ttbr1 = percpu_pgd[cpu];
+
+ __cpu_replace_ttbr1(ttbr1, false);
}
static inline void cpu_replace_ttbr1(pgd_t *pgdp)
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index 4dfa42b7d053..38eec71ec383 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -1216,6 +1216,9 @@ p4d_t *p4d_offset_lockless_folded(pgd_t *pgdp, pgd_t pgd, unsigned long addr)
#endif /* CONFIG_PGTABLE_LEVELS > 4 */
+#define ARCH_PAGE_TABLE_SYNC_MASK \
+ (pgtable_l5_enabled() ? PGTBL_PGD_MODIFIED : PGTBL_P4D_MODIFIED)
+
#define pgd_ERROR(e) \
pr_err("%s:%d: bad pgd %016llx.\n", __FILE__, __LINE__, pgd_val(e))
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index 23c05dc7a8f2..6d420ad59af4 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -360,6 +360,9 @@ void __init __no_sanitize_address setup_arch(char **cmdline_p)
smp_init_cpus();
smp_build_mpidr_hash();
+ /* Must be called after smp_init_cpus */
+ setup_percpu_pgd();
+
#ifdef CONFIG_ARM64_SW_TTBR0_PAN
/*
* Make sure init_thread_info.ttbr0 always generates translation
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 1aa324104afb..88a82eb56fb3 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -55,6 +55,8 @@
#include <trace/events/ipi.h>
+extern void idmap_cpu_replace_ttbr1(phys_addr_t pgdir);
+
/*
* as from 2.5, kernels no longer have an init_tasks structure
* so we need some other way of telling a new secondary core
@@ -198,6 +200,12 @@ asmlinkage notrace void secondary_start_kernel(void)
struct mm_struct *mm = &init_mm;
const struct cpu_operations *ops;
unsigned int cpu = smp_processor_id();
+ typedef void (ttbr_replace_func)(phys_addr_t);
+ ttbr_replace_func *replace_ttbr;
+
+ phys_addr_t ttbr1 = phys_to_ttbr(virt_to_phys(percpu_pgd[cpu]));
+ replace_ttbr = (void *)__pa_symbol(idmap_cpu_replace_ttbr1);
+ replace_ttbr(ttbr1);
/*
* All kernel threads share the same mm context; grab a
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index dd85e093ffdb..ed1545baa045 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -67,6 +67,59 @@ long __section(".mmuoff.data.write") __early_cpu_boot_status;
static DEFINE_SPINLOCK(swapper_pgdir_lock);
static DEFINE_MUTEX(fixmap_lock);
+pgd_t *percpu_pgd[NR_CPUS] __ro_after_init;
+bool percpu_pgd_setup_done __ro_after_init = false;
+
+void __init setup_percpu_pgd(void)
+{
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+ void *addr;
+
+ if (cpu == 0) {
+ percpu_pgd[cpu] = swapper_pg_dir;
+ continue;
+ }
+
+ addr = memblock_alloc(PAGE_SIZE, PAGE_SIZE);
+ if (!addr)
+ panic("Can't alloc percpu pgd\n");
+
+ memcpy(addr, (void *)swapper_pg_dir, PAGE_SIZE);
+ percpu_pgd[cpu] = (pgd_t *)addr;
+ }
+
+ dsb(ishst);
+
+ percpu_pgd_setup_done = true;
+}
+
+void arch_sync_kernel_mappings(unsigned long start, unsigned long end)
+{
+ unsigned long addr, next;
+ int cpu;
+ pgd_t *pgdp = pgd_offset_k(start);
+ pgd_t pgd;
+ unsigned int index = pgd_index(start);
+
+ BUG_ON(start > end);
+
+ if (!percpu_pgd_setup_done)
+ return;
+
+ addr = start;
+ do {
+ pgd = READ_ONCE(*pgdp);
+ next = pgd_addr_end(addr, end);
+ for_each_possible_cpu(cpu) {
+ if (cpu == 0)
+ continue;
+ set_pgd(percpu_pgd[cpu] + index, pgd);
+ }
+ } while (pgdp++, index++, addr = next, addr != end);
+}
+
void noinstr set_swapper_pgd(pgd_t *pgdp, pgd_t pgd)
{
pgd_t *fixmap_pgdp;
--
2.47.0
^ permalink raw reply related
* [RFC v1 PATCH 0/11] Optimize this_cpu_*() ops for non-x86 (ARM64 for this series)
From: Yang Shi @ 2026-04-29 17:04 UTC (permalink / raw)
To: cl, dennis, tj, urezki, catalin.marinas, will, ryan.roberts,
david, akpm, hca, gor, agordeev
Cc: yang, linux-mm, linux-arm-kernel, linux-kernel
Introduction
============
This patch series implemented the LSFMM 2026 proposal for optimizing
this_cpu_*() ops on ARM64. For the details of the proposal, Please refer to:
https://lore.kernel.org/linux-mm/CAHbLzkpcN-T8MH6=W3jCxcFj1gVZp8fRqe231yzZT-rV_E_org@mail.gmail.com/
I didn't repeat it in the cover letter because there is no change to the
proposal.
The series is based on 7.1-rc1. It is basically minimum viable patches.
There are still a few hacks in this series and it may break something,
for example, KPTI, SMT machines which shared TLB, etc. But it shoule be
good enough for now to demonstrate the core idea. The main purpose of the
RFC is to gather feedback early, figure out missing parts and risks, and
make sure we are on the right track, as well as hopefully it can help the
discussion for the upcoming LSFMM.
I broke the patches down to arch-dependent and arch-independent parts so that
hopefully the interested persons can do experiments on other architectures,
for example, S390, easier.
A new kernel config is introduced, HAVE_LOCAL_PER_CPU_MAP. The architectures
which can support this feature will select it. Allocating and freeing percpu
local mapping is protected by this config so that others won't pay the cost.
Known Issues
============
1. KPIT
-------
We need determine what CPU we are on, then switch to the right page table.
Currently arm64 kernel fetches tramp_pg_dir via swapper_pg_dir - fixed_offset,
and fetches swapper_pg_dir from ttbr1. But ttbr1 may not hold swapper_pg_dir
anymore except CPU #0. So we need to figure out the other way to handle it.
Switching to tramp_pg_dir should be easy, but the reverse seems harder because
tramp_pg_dir just maps the trampoline vectors.
Maybe we can do two steps switch. Switch to swapper_pg_dir at the first step,
then switch to per cpu page table (for entry) or tramp page table (for exit).
Nobody should call this_cpu_*() at either userspace -> kernel entry stage or
kernel -> userspace exit stage.
2. Shared TLB machines
----------------------
Some machines may share TLB between CPUs, for example, SMT machines may share
TLB between the two hardware threads in one single core.
The per cpu page table just can't work with it. Maybe we need a new
cpufeature to indicate whether per cpu page table is allowed or not. Then
just enable it for not-shared-TLB machines.
Benchmark
=========
The benchmarks are done on 160 core AmpereOne machine. The baseline is
v7.1-rc1 kernel.
1. Kernel Build
---------------
Run kernel build (make -j160) with the default Fedora kernel config in a
memcg.
13% - 18% sys time improvment
3% - 7% wall time improvement
2. stress-ng vm ops
-------------------
stress-ng --vm 160 --vm-bytes 128M --vm-ops 100000000
8.5% improvement
3. stress-ng vm ops + fork
----------------------
stress-ng --mmapfork 160 --mmapfork-bytes 128M --mmapfork-ops 500
15% improvement
Regression test
===============
1. memcg creation
-----------------
Create 10K memcgs. Each memcg creation needs to allocate multiple percpu
variables, for example, percpu refcnt, rstat and objcg percpu refcnt.
Consumed 2112K more virtual memory for percpu “local mapping” and a few
more mega bytes consumed by per cpu page tables.
No noticeable regression was found for elapsed time.
2. fork test
------------
stress-ng --fork 160 --fork-ops 10000000
fork() needs to allocate multiple percpu variables, for example, rss
counters and mm_cid_cpu.
Roughly 1% regression was found. However stress-ng fork test has quites
small address space, the real life workloads typically have much larger
address space and do more complicated works. The stress-ng mmapfork
benchmark saw 15% improvement.
Yang Shi (11):
arm64: mm: enable percpu kernel page table
arm64: mm: define percpu virtual space area
arm64: smp: define setup_per_cpu_areas()
mm: percpu: prepare to use dedicated percpu area
arm64: mm: map local percpu first chunk
mm: percpu: set up first chunk and reserve chunk
arm64: mm: introduce __per_cpu_local_off
vmalloc: pass in pgd pointer for vmap{__vunmap}_range_noflush()
mm: percpu: allocate and free local percpu vm area
arm64: kconfig: select HAVE_LOCAL_PER_CPU_MAP
arm64: percpu: use local percpu for this_cpu_*() APIs
arch/arm64/Kconfig | 2 +-
arch/arm64/include/asm/mmu.h | 3 +++
arch/arm64/include/asm/mmu_context.h | 6 +++++-
arch/arm64/include/asm/percpu.h | 17 ++++++++++-------
arch/arm64/include/asm/pgtable.h | 24 +++++++++++++++++++++---
arch/arm64/kernel/setup.c | 3 +++
arch/arm64/kernel/smp.c | 40 ++++++++++++++++++++++++++++++++++++++++
arch/arm64/mm/mmu.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
arch/arm64/mm/ptdump.c | 4 ++++
drivers/base/arch_numa.c | 51 +--------------------------------------------------
include/linux/percpu.h | 4 +++-
include/linux/vmalloc.h | 3 +++
mm/Kconfig | 3 +++
mm/internal.h | 5 ++++-
mm/kmsan/hooks.c | 14 +++++++-------
mm/percpu-internal.h | 15 +++++++++++++++
mm/percpu-vm.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
mm/percpu.c | 46 +++++++++++++++++++++++++++++++++++++---------
mm/vmalloc.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
19 files changed, 419 insertions(+), 99 deletions(-)
Thanks,
Yang
^ permalink raw reply
* Re: [PATCH v4 02/11] firmware: samsung: acpm: Consolidate transfer initialization helper
From: Tudor Ambarus @ 2026-04-29 17:02 UTC (permalink / raw)
To: Krzysztof Kozlowski, Rafael J. Wysocki, Zhang Rui, Lukasz Luba,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Alim Akhtar,
Bartlomiej Zolnierkiewicz, Kees Cook, Gustavo A. R. Silva,
Peter Griffin, André Draszik, Daniel Lezcano,
Sylwester Nawrocki, Chanwoo Choi, Michael Turquette, Stephen Boyd,
Lee Jones
Cc: willmcvicker, jyescas, shin.son, linux-samsung-soc, linux-kernel,
linux-pm, devicetree, linux-arm-kernel, linux-hardening,
linux-clk
In-Reply-To: <a63d4dc7-b01a-4da2-ac1f-6679c9030db6@kernel.org>
On 4/28/26 12:25 PM, Krzysztof Kozlowski wrote:
> On 23/04/2026 17:22, Tudor Ambarus wrote:
>> acpm_dvfs_init_get_rate_cmd(cmd, clk_id);
>> - acpm_dvfs_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id, true);
>> + acpm_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id, true);
>>
>> ret = acpm_do_xfer(handle, &xfer);
>> if (ret)
>> diff --git a/drivers/firmware/samsung/exynos-acpm-pmic.c b/drivers/firmware/samsung/exynos-acpm-pmic.c
>> index 0c50993cc9a8..f032f2c69685 100644
>> --- a/drivers/firmware/samsung/exynos-acpm-pmic.c
>> +++ b/drivers/firmware/samsung/exynos-acpm-pmic.c
>> @@ -58,16 +58,6 @@ static inline u32 acpm_pmic_get_bulk(u32 data, unsigned int i)
>> return (data >> (ACPM_PMIC_BULK_SHIFT * i)) & ACPM_PMIC_BULK_MASK;
>> }
>>
>> -static void acpm_pmic_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen,
>> - unsigned int acpm_chan_id)
>> -{
>> - xfer->txd = cmd;
>> - xfer->rxd = cmd;
>> - xfer->txcnt = cmdlen;
>> - xfer->rxcnt = cmdlen;
>> - xfer->acpm_chan_id = acpm_chan_id;
>
> The code is not equivalent. RX bits were always cleared. This should be
> explained in the commit msg which would also provide a proof that you
> actually analyzed if it has any impact.
>
Indeed, my commit message is inaccurate because I claimed the
acpm_pmic/dvfs_set_xfer() were identical.
There's no change in functionality: in the original
`acpm_pmic_set_xfer()`, the RX parameters were unconditionally assigned
(`xfer->rxd = cmd;` and `xfer->rxcnt = cmdlen;`). In this patch, all the
updated PMIC call sites now pass `true` for the `response` argument.
This ensures the unified helper takes the `if (response)` branch, which
performs those exact same assignments, preserving the original PMIC
behavior.
Will update the commit message. And revisit the other patches when
splitting the firmware patches into their own set.
Thanks,
ta
^ permalink raw reply
* Re: [PATCH v10 3/6] mfd: max77759: add register bitmasks and modify irq configs for charger
From: Krzysztof Kozlowski @ 2026-04-29 16:59 UTC (permalink / raw)
To: Amit Sunil Dhamne, Lee Jones
Cc: André Draszik, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Greg Kroah-Hartman, Jagan Sridharan, Mark Brown,
Matti Vaittinen, Andrew Morton, Sebastian Reichel,
Heikki Krogerus, Peter Griffin, Tudor Ambarus, Alim Akhtar,
linux-kernel, devicetree, linux-usb, linux-pm, linux-arm-kernel,
linux-samsung-soc, RD Babiera, Kyle Tso
In-Reply-To: <fc767096-f39d-4088-b3ad-cc85bdedf36c@google.com>
On 29/04/2026 02:29, Amit Sunil Dhamne wrote:
> Hi Lee,
>
>
> Thanks for your review.
>
>
> On 4/24/26 1:26 AM, Lee Jones wrote:
>> On Tue, 31 Mar 2026, Amit Sunil Dhamne via B4 Relay wrote:
>>
>>> From: Amit Sunil Dhamne <amitsd@google.com>
>>>
>>> Add register bitmasks for charger function.
>>> In addition split the charger IRQs further such that each bit represents
>>> an IRQ downstream of charger regmap irq chip. In addition populate the
>>> ack_base to offload irq ack to the regmap irq chip framework.
>> Please reword this commit messages.
>>
>> Using 'In addition' twice in such close proximity reads a little awkwardly.
>
> Thanks for pointing it out. Unfortunately, this commit is already part
> of the linux and linux-next so I am not sure if I could fix the commit
> message retrospectively.
I don't understand why you decided to put this with USB patchset. We do
ask not to mix subsystems all the time. You made it unnecessarily
combination of at least three subsystems.
Do not do that.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v13 3/4] gpio: rpmsg: add generic rpmsg GPIO driver
From: Shenwei Wang @ 2026-04-29 16:53 UTC (permalink / raw)
To: Mathieu Poirier
Cc: Andrew Lunn, Padhi, Beleswar, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Frank Li, Sascha Hauer, Shuah Khan,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org,
linux-kernel@vger.kernel.org, Pengutronix Kernel Team,
Fabio Estevam, Peng Fan, devicetree@vger.kernel.org,
linux-remoteproc@vger.kernel.org, imx@lists.linux.dev,
linux-arm-kernel@lists.infradead.org, dl-linux-imx,
Bartosz Golaszewski
In-Reply-To: <afImuoeHolxGgw3H@p14s>
> -----Original Message-----
> From: Mathieu Poirier <mathieu.poirier@linaro.org>
> Sent: Wednesday, April 29, 2026 10:42 AM
> To: Shenwei Wang <shenwei.wang@nxp.com>
> Cc: Andrew Lunn <andrew@lunn.ch>; Padhi, Beleswar <b-padhi@ti.com>; Linus
> Walleij <linusw@kernel.org>; Bartosz Golaszewski <brgl@kernel.org>; Jonathan
> Corbet <corbet@lwn.net>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski
> <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; Bjorn Andersson
> <andersson@kernel.org>; Frank Li <frank.li@nxp.com>; Sascha Hauer
> <s.hauer@pengutronix.de>; Shuah Khan <skhan@linuxfoundation.org>; linux-
> gpio@vger.kernel.org; linux-doc@vger.kernel.org; linux-kernel@vger.kernel.org;
> Pengutronix Kernel Team <kernel@pengutronix.de>; Fabio Estevam
> <festevam@gmail.com>; Peng Fan <peng.fan@nxp.com>;
> devicetree@vger.kernel.org; linux-remoteproc@vger.kernel.org;
> imx@lists.linux.dev; linux-arm-kernel@lists.infradead.org; dl-linux-imx <linux-
> imx@nxp.com>; Bartosz Golaszewski <brgl@bgdev.pl>
> Subject: [EXT] Re: [PATCH v13 3/4] gpio: rpmsg: add generic rpmsg GPIO driver
> On Tue, Apr 28, 2026 at 03:24:59PM +0000, Shenwei Wang wrote:
> >
> >
> > > -----Original Message-----
> > > From: Andrew Lunn <andrew@lunn.ch>
> > > Sent: Monday, April 27, 2026 3:49 PM
> > > To: Shenwei Wang <shenwei.wang@nxp.com>
> > > Cc: Padhi, Beleswar <b-padhi@ti.com>; Linus Walleij
> > > <linusw@kernel.org>; Bartosz Golaszewski <brgl@kernel.org>; Jonathan
> > > Corbet <corbet@lwn.net>; Rob Herring <robh@kernel.org>; Krzysztof
> > > Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>;
> > > Bjorn Andersson <andersson@kernel.org>; Mathieu Poirier
> > > <mathieu.poirier@linaro.org>; Frank Li <frank.li@nxp.com>; Sascha
> > > Hauer <s.hauer@pengutronix.de>; Shuah Khan
> > > <skhan@linuxfoundation.org>; linux-gpio@vger.kernel.org; linux-
> > > doc@vger.kernel.org; linux-kernel@vger.kernel.org; Pengutronix
> > > Kernel Team <kernel@pengutronix.de>; Fabio Estevam
> > > <festevam@gmail.com>; Peng Fan <peng.fan@nxp.com>;
> > > devicetree@vger.kernel.org; linux- remoteproc@vger.kernel.org;
> > > imx@lists.linux.dev; linux-arm- kernel@lists.infradead.org;
> > > dl-linux-imx <linux-imx@nxp.com>; Bartosz Golaszewski
> > > <brgl@bgdev.pl>
> > > Subject: [EXT] Re: [PATCH v13 3/4] gpio: rpmsg: add generic rpmsg
> > > GPIO driver
> > > > > struct virtio_gpio_response {
> > > > > __u8 status;
> > > > > __u8 value;
> > > > > };
> > >
> > > > It is the same message format. Please see the message definition
> > > (GET_DIRECTION) below:
> > >
> > > > + +-----+-----+-----+-----+-----+----+
> > > > + |0x00 |0x01 |0x02 |0x03 |0x04 |0x05|
> > > > + | 1 | 2 |port |line | err | dir|
> > > > + +-----+-----+-----+-----+-----+----+
> > >
> > > Sorry, but i don't see how two u8 vs six u8 are the same message format.
> > >
> >
> > Some changes to the message format are necessary.
> >
> > Virtio uses two communication channels (virtqueues): one for requests and
> replies, and a second one for events.
> > In contrast, rpmsg provides only a single communication channel, so a
> > type field is required to distinguish between different kinds of messages.
> >
> > Since rpmsg replies and events share the same message format, an additional
> line is introduced to handle both cases.
> >
> > Finally, rpmsg supports multiple GPIO controllers, so a port field is added to
> uniquely identify the target controller.
>
> I have commented on this before - RPMSG is already providing multiplexing
> capability by way of endpoints. There is no need for a port field. One endpoint,
> one GPIO controller.
>
You still need a way to let the remote side know which port the endpoint maps to, either
by embedding the port information in the message (the current way), or by sending it
separately.
Shenwei
> >
> > Shenwei
> >
> > > Andrew
^ permalink raw reply
* Re: [PATCH v18 4/8] ring-buffer: Skip invalid sub-buffers when rewinding persistent ring buffer
From: Steven Rostedt @ 2026-04-29 16:39 UTC (permalink / raw)
To: Masami Hiramatsu (Google)
Cc: Catalin Marinas, Will Deacon, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, Ian Rogers, linux-arm-kernel
In-Reply-To: <20260430002023.b19608c27a746284e8f73b80@kernel.org>
On Thu, 30 Apr 2026 00:20:23 +0900
Masami Hiramatsu (Google) <mhiramat@kernel.org> wrote:
> On Tue, 28 Apr 2026 16:21:46 -0400
> Steven Rostedt <rostedt@goodmis.org> wrote:
>
> > On Fri, 24 Apr 2026 15:52:35 +0900
> > "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:
> >
> > > @@ -1892,9 +1895,19 @@ static int rb_validate_buffer(struct buffer_data_page *dpage, int cpu,
> > > * subbuf_size is considered invalid.
> > > */
> > > tail = local_read(&dpage->commit) & ~RB_MISSED_MASK;
> > > - if (tail > meta->subbuf_size - BUF_PAGE_HDR_SIZE)
> > > - return -1;
> > > - return rb_read_data_buffer(dpage, tail, cpu, &ts, &delta);
> > > + if (tail <= meta->subbuf_size - BUF_PAGE_HDR_SIZE)
> > > + ret = rb_read_data_buffer(dpage, tail, cpu, &ts, &delta);
> > > +
> >
> > This code seriously needs comments.
>
> OK, I'll add it, or let code explain clearer?
>
> if (tail <= meta->subbuf_size - BUF_PAGE_HDR_SIZE)
> ret = rb_read_data_buffer(dpage, tail, cpu, &ts, &delta);
> else
> ret = -1;
That's better...
>
> Thanks,
>
The below should have some explanation too. I can figure it out, but it
wasted more brain cycles than I would have liked ;-)
-- Steve
> >
> > > + if (ret < 0 || (prev_ts && prev_ts > ts) || (next_ts && ts > next_ts)) {
> > > + local_set(&bpage->entries, 0);
> > > + local_set(&bpage->page->commit, 0);
> > > + bpage->page->time_stamp = prev_ts ? prev_ts : next_ts;
> > > + ret = -1;
> > > + } else {
> > > + local_set(&bpage->entries, ret);
> > > + }
> > > +
> > > + return ret;
> > > }
> > >
>
>
^ permalink raw reply
* Re: [PATCH v18 3/8] ring-buffer: Skip invalid sub-buffers when validating persistent ring buffer
From: Steven Rostedt @ 2026-04-29 16:32 UTC (permalink / raw)
To: Masami Hiramatsu (Google)
Cc: Catalin Marinas, Will Deacon, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, Ian Rogers, linux-arm-kernel
In-Reply-To: <20260430001559.a2d0489d956c06059e13e58d@kernel.org>
On Thu, 30 Apr 2026 00:15:59 +0900
Masami Hiramatsu (Google) <mhiramat@kernel.org> wrote:
> It should not be a never ending loop (there are other exit conditions),
> but I agreed. What about limiting with nr_subbufs?
>
> diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
> index 5326924615a4..aa89ec96e964 100644
> --- a/kernel/trace/ring_buffer.c
> +++ b/kernel/trace/ring_buffer.c
> @@ -5630,7 +5630,8 @@ __rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer)
> * a case where we will loop three times. There should be no
> * reason to loop four times (that I know of).
> */
> - if (RB_WARN_ON(cpu_buffer, ++nr_loops > 3)) {
> + if (RB_WARN_ON(cpu_buffer,
> + ++nr_loops > (cpu_buffer->ring_meta ? cpu_buffer->nr_subbufs : 3))) {
> reader = NULL;
> goto out;
> }
Yeah, this looks more reasonable.
Thanks,
-- Steve
^ permalink raw reply
* Re: [PATCH 1/1] KVM: arm64: nv: Avoid full shadow s2 unmap
From: Wei-Lin Chang @ 2026-04-29 16:31 UTC (permalink / raw)
To: Marc Zyngier
Cc: linux-arm-kernel, kvmarm, linux-kernel, Oliver Upton, Joey Gouly,
Suzuki K Poulose, Zenghui Yu, Catalin Marinas, Will Deacon
In-Reply-To: <n4v252jehdq4sn2ux26twudmvmfwjltxkvv6sa6rxtwbvqa2en@xp2oaodm7uqc>
On Fri, Apr 24, 2026 at 08:45:24PM +0100, Wei-Lin Chang wrote:
> On Thu, Apr 16, 2026 at 11:50:38AM +0100, Marc Zyngier wrote:
> > On Thu, 16 Apr 2026 00:05:40 +0100,
> > Wei-Lin Chang <weilin.chang@arm.com> wrote:
> > >
> > > On Wed, Apr 15, 2026 at 09:38:55AM +0100, Marc Zyngier wrote:
> >
> > [...]
> >
> > > > > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > > > > index 851f6171751c..a97bd461c1e1 100644
> > > > > --- a/arch/arm64/include/asm/kvm_host.h
> > > > > +++ b/arch/arm64/include/asm/kvm_host.h
> > > > > @@ -217,6 +217,10 @@ struct kvm_s2_mmu {
> > > > > */
> > > > > bool nested_stage2_enabled;
> > > > >
> > > > > + /* canonical IPA to nested IPA range lookup */
> > > > > + struct maple_tree nested_revmap_mt;
> > > > > + bool nested_revmap_broken;
> > > > > +
> > > >
> > > > Consider moving this boolean next to the other ones so that you don't
> > > > create too many holes in the kvm_s2_mmu structure (use pahole to find out).
> > > >
> > > > But I have some misgivings about the way things are structured
> > > > here. Only NV needs a revmap, yet this is present irrelevant of the
> > > > nature of the VM and bloats the data structure a bit.
> > > >
> > > > My naive approach would have been to only keep a pointer to the
> > > > revmap, and make that pointer NULL when the tree is "broken", and
> > > > freed under RCU if the context isn't the correct one.
> > >
> > > Can you explain what you mean by "if the context isn't the correct one"?
> > > If this refers to when selecting a specific kvm_s2_mmu instance for
> > > another context, then IIUC refcnt would already be 0 and there would be
> > > no other user of the tree.
> >
> > Sorry, "context" is an overloaded word. I meant a situation in which
> > you couldn't immediately free the maple-tree because you're holding
> > locks and freeing (hypothetically) requires a sleeping "context". in
> > this case, freeing under RCU, purely as a deferring mechanism, might
> > be useful.
>
> I experimented using RCU to free the tree as a deferring mechanism.
> Here are a few observations:
>
> - At reverse map record time, if maple tree store fails, we have to
> change the maple tree pointer to a NULL, which is an RCU write
> operation. Therefore we need to either take another lock, or use a
> xchg(ptr, NULL) to avoid the lock.
>
> - Because we're holding the read-side mmu_lock, we shouldn't block
> during reverse map record. Therefore we should use call_rcu()
> instead of synchronize_rcu() to free the "broken" tree. This implies
> a pointer to a maple tree in kvm_s2_mmu will not suffice, an
> additional structure with both the maple tree and an rcu_head have
> to be created.
>
> IMO looking at RCU calls mixed with mtree_{, un}lock(), and having a new
> wrapper struct to make this dynamic allocation scheme work is not very
> attractive to me.
>
> Instead, what do you think if I aggregate all strictly NV-related
> fields in kvm_s2_mmu i.e. tlb_vttbr, tlb_vtcr, nested_stage2_enabled,
> shadow_pt_debugfs_dentry, pending_unmap, into a struct maybe called
> kvm_s2_mmu_nested, add a maple tree in it, and have a pointer to this
> struct in kvm_s2_mmu? kvm_s2_mmu_nested can then be allocated only if we
> init a nested s2 mmu.
>
> Do you think this can work and is better than the current approaches?
After a discussion with Marc, we think making the maple tree a pointer
generates too much complexity from the reasons given above. For the
kvm_s2_mmu_nested idea, it creates churn and the same RCU problem
persists if we want to dynamically allocate and free the structure. On
the other hand, if we allocated kvm_s2_mmu_nested at init-time and reuse
the struct, then it's not much difference than just placing the maple
tree in kvm_s2_mmu, the only benefit will be keeping kvm_s2_mmu smaller
for non nested MMUs.
I'll stick with just adding the maple tree instance in kvm_s2_mmu for
the next version.
Thanks,
Wei-Lin Chang
[...]
^ permalink raw reply
* Re: [PATCH v1] pmdomain: ti_sci: re-sync TIFS with genpd on resume
From: Vitor Soares @ 2026-04-29 16:26 UTC (permalink / raw)
To: Vignesh Raghavendra, Nishanth Menon, Tero Kristo,
Santosh Shilimkar, Ulf Hansson
Cc: Vitor Soares, linux-arm-kernel, linux-pm, linux-kernel,
Tomi Valkeinen, Kevin Hilman, vishalm, sebin.francis, d-gole,
Devarsh Thakkar, stable
In-Reply-To: <1fb0739e-b84f-42f1-9c96-88b5cc5866a8@ti.com>
Hi Vignesh
Thank you for the review.
On Wed, 2026-04-29 at 10:03 +0530, Vignesh Raghavendra wrote:
> Hi Vitor
>
> On 27/04/26 13:18, Vitor Soares wrote:
> > From: Vitor Soares <vitor.soares@toradex.com>
> >
> > When a device in a TI SCI power domain is on the wakeup path of a
> > wakeup-capable child, the suspend path skips genpd_sync_power_off().
> > No put_device is sent to TIFS and the domain's genpd status remains
> > ON.
>
> Correction of terminologies: TIFS is Root of trust component and is not
> usually involved in power management, that would be DM (Device Manager)
>
Thank you for the clarification. I will address this on v2. Also, I was thinking
to replace put_device/get_device with ti_sci_pd_power_off/ti_sci_pd_power_on if
that makes more clear the content.
> But to be really sure who is doing what, Could you provide an example
> and the platform on which you see the issue / external abort?
>
This was reproduced on our Toradex Verdin AM62P WB and the driver for our Wi-Fi
module on the SDIO bus calls device_init_wakeup() during the initialization.
After enter in suspend, it show the following error resume path:
[ 41.759341] Internal error: synchronous external abort: 0000000096000010 [#1]
SMP
[ 41.843286] CPU: 0 UID: 0 PID: 933 Comm: rtcwake Tainted: G M O
6.18.21-dirty #3 PREEMPT
[ 41.852762] Tainted: [M]=MACHINE_CHECK, [O]=OOT_MODULE
[ 41.857891] Hardware name: Toradex Verdin AM62P WB on Verdin Development
Board (DT)
[ 41.865537] pstate: 200000c5 (nzCv daIF -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[ 41.872492] pc : regmap_mmio_read32le+0x8/0x20
[ 41.876941] lr : regmap_mmio_read+0x44/0x70
[ 41.881120] sp : ffff800081fdb8e0
[ 41.884428] x29: ffff800081fdb8e0 x28: 0000000000000000 x27: ffffa95bb64aa9c8
[ 41.891563] x26: 0000000000000000 x25: 0000000000000000 x24: 0000000000000000
[ 41.898697] x23: 0000000080000000 x22: ffff000002df5c00 x21: ffff800081fdb9b4
[ 41.905831] x20: 0000000000000100 x19: ffff000001286400 x18: 0000000000000000
[ 41.912965] x17: 2d69696d67722f79 x16: 687020726f662067 x15: ffff00007fb74f40
[ 41.920100] x14: 00000000000002ea x13: 000000000000031f x12: 0000000000000000
[ 41.927234] x11: 00000000000000c0 x10: 00000000000009e0 x9 : ffff800081fdb7a0
[ 41.934368] x8 : ffff00007fb6ce00 x7 : 0000000000000000 x6 : 0000000000000000
[ 41.941502] x5 : ffffa95bb57948d8 x4 : 0000000000000100 x3 : 0000000000000100
[ 41.948636] x2 : ffffa95bb5795034 x1 : 0000000000000100 x0 : ffff80008025d100
[ 41.955770] Call trace:
[ 41.958211] regmap_mmio_read32le+0x8/0x20 (P)
[ 41.962655] _regmap_bus_reg_read+0x70/0xb0
[ 41.966839] _regmap_read+0x64/0xdc
[ 41.970327] _regmap_update_bits+0xf4/0x140
[ 41.974509] regmap_update_bits_base+0x64/0x98
[ 41.978952] sdhci_am654_runtime_resume+0x138/0x208
[ 41.983830] pm_generic_runtime_resume+0x2c/0x44
[ 41.988445] __genpd_runtime_resume+0x30/0x7c
[ 41.992804] genpd_runtime_resume+0xdc/0x2e8
[ 41.997073] pm_runtime_force_resume+0x68/0xf4
[ 42.001517] dpm_run_callback+0x8c/0x14c
[ 42.005439] device_resume+0x11c/0x34c
[ 42.009188] dpm_resume+0x178/0x1f0
[ 42.012673] dpm_resume_end+0x18/0x34
[ 42.016332] suspend_devices_and_enter+0x4a4/0x668
[ 42.021123] pm_suspend+0x170/0x2dc
[ 42.024610] state_store+0x80/0x104
[ 42.028096] kobj_attr_store+0x18/0x2c
[ 42.031845] sysfs_kf_write+0x7c/0x94
[ 42.035508] kernfs_fop_write_iter+0x130/0x1fc
[ 42.039949] vfs_write+0x200/0x370
[ 42.043351] ksys_write+0x6c/0x100
[ 42.046752] __arm64_sys_write+0x1c/0x28
[ 42.050673] invoke_syscall.constprop.0+0x50/0xe4
[ 42.055378] do_el0_svc+0x40/0xc4
[ 42.058691] el0_svc+0x40/0x15c
[ 42.061834] el0t_64_sync_handler+0xa0/0xe4
[ 42.066015] el0t_64_sync+0x198/0x19c
[ 42.069680] Code: aa0603e0 d65f03c0 f9400000 8b214000 (b9400000)
>
> >
> > TIFS powers off the hardware during deep sleep regardless, since it
> > was never informed to keep the domain active. On resume, because the
> > domain's genpd status is ON, no get_device is issued. The driver
> > then accesses registers of a powered-off domain, causing a
> > synchronous external abort (AXI bus error, ESR 0x96000010).
>
> Hmm, if something is wakeup source, I would expect even TIFS/DM not to
> turn if off, else module wakeup wouldn't work.
>
I tested UART as a wakeup source and I couldn't reproduce this issue. My
understanding is that UART has its own TI SCI domain and device_may_wakeup() is
true directly on that domain device, so the set_device_constraint fires
correctly and DM keeps it powered.
Here is my tracking of the issue:
Wi-Fi driver registers as wakeup source:
device_init_wakeup(mmc0:0001)
During suspend/resume.
dpm_suspend()
->genpd_suspend_dev(fa20000.mmc)
->ti_sci_pd_suspend(fa20000.mmc)
->ti_sci_pd_set_wkup_constraint(fa20000.mmc)
device_may_wakeup(fa20000.mmc) = false
set_device_constraint never sent to DM
dpm_suspend_noirq()
->genpd_finish_suspend(fa20000.mmc)
->device_awake_path(fa20000.mmc) = true
->GENPD_FLAG_ACTIVE_WAKEUP = true
genpd status = GENPD_STATE_ON
skip power_off (ti_sci_pd_power_off)
On deep sleep entry, DM powers off fa20000.mmc independently.
It received no set_device_constraint nor ti_sci_pd_power_off.
I attempted to fix this by calling set_device_constraint when
device_wakeup_path() is true but it prevented the system from entering deep
sleep entirely.
[...]
Best regards,
Vitor Soares
^ permalink raw reply
* Re: [PATCH v8 10/58] perf evsel: Add reference count
From: Ian Rogers @ 2026-04-29 16:20 UTC (permalink / raw)
To: Namhyung Kim
Cc: acme, adrian.hunter, alice.mei.rogers, dapeng1.mi, james.clark,
leo.yan, linux-arm-kernel, linux-kernel, linux-perf-users, mingo,
peterz, tmricht
In-Reply-To: <afG0WoBnoxC8VJNx@z2>
On Wed, Apr 29, 2026 at 12:33 AM Namhyung Kim <namhyung@kernel.org> wrote:
>
> On Tue, Apr 28, 2026 at 12:18:15AM -0700, Ian Rogers wrote:
> > As with evlist this a no-op for most of the perf tool. The reference
> > count is set to 1 at allocation, the put will see the 1, decrement it
> > and perform the delete. The purpose for adding the reference count is
> > for the python code. Prior to this change the python code would clone
> > evsels, but this has issues if events are opened, etc. leading to
> > assertion failures. With a reference count the same evsel can be used
> > and the reference count incremented for the python usage. To not
> > change the python evsel API getset functions are added for the evsel
> > members, no set function is provided for size as it doesn't make sense
> > to alter this.
>
> Can we make the reference counting model simpler by just using evlist's
> refcount? Assuming python code generates no evsel from the shared
> evlist, can we just think evsels belong to an evlist and manage their
> lifetime together?
>
> Maybe I talked it to you already.. but I forgot the details. :-p
I agree that would make the lifetime management easier, but we already
exposed evsels as Python objects and the existing cloning approach
makes the code base more complex and divergent.
Thanks,
Ian
> Thanks,
> Namhyung
^ permalink raw reply
* Re: [PATCH v8 09/58] perf evlist: Add reference count
From: Ian Rogers @ 2026-04-29 16:17 UTC (permalink / raw)
To: Namhyung Kim
Cc: acme, adrian.hunter, alice.mei.rogers, dapeng1.mi, james.clark,
leo.yan, linux-arm-kernel, linux-kernel, linux-perf-users, mingo,
peterz, tmricht
In-Reply-To: <afGyYi01XSNIS-A2@z2>
On Wed, Apr 29, 2026 at 12:25 AM Namhyung Kim <namhyung@kernel.org> wrote:
>
> On Tue, Apr 28, 2026 at 12:18:14AM -0700, Ian Rogers wrote:
> > This a no-op for most of the perf tool. The reference count is set to
> > 1 at allocation, the put will see the 1, decrement it and perform the
> > delete. The purpose for adding the reference count is for the python
> > code. Prior to this change the python code would clone evlists, but
> > this has issues if events are opened, etc. This change adds a
> > reference count for the evlists and a later change will add it to
> > evsels. The combination is needed for the python code to operate
> > correctly (not hit asserts in the evsel clone), but the changes are
> > broken apart for the sake of smaller patches.
>
> Can we keep the existing evlist__delete() code? IIUC most parts of the
> code don't need to worry about python. Then do we really need to
> convert them all? Maybe we can convert only the relevant codes.
Are you thinking of evlist__delete being something like:
```
void evlist__delete(struct evlist *evlist)
{
assert(refcount_read(evlist__refcnt(evlist)) == 1);
evlist__put(evlist);
}
```
This would allow the existing delete cases to remain and minimize
churn, but it creates an opportunity for a runtime assertion failure.
I'm not sure what the codebase gains long-term from this as having
both `put` and `delete` feels like an unnecessary choice for the user.
> Also I think it's better to split evlist__put() conversion and python
> code changes.
So the existing Python code embeds an evlist inside a Python object. I
don't see a way to add the reference count and keep the embedding
without inventing a 3rd way of handling Python evlists, which would be
as complex as just using the reference counts. We could remove the
embedded evlist and use evlist__new/delete, but the code wouldn't be
functional.
Thanks,
Ian
> Thanks,
> Namhyung
>
> >
> > Assisted-by: Gemini:gemini-3.1-pro-preview
> > Signed-off-by: Ian Rogers <irogers@google.com>
> > ---
> > v2: Added evlist__put to pyrf_evlist__init in case init is called more
> > than once.
> >
> > I double-checked trace__replay() and confirmed that trace->evlist
> > is not assigned to session->evlist in that function.
> > trace__replay creates a new session and uses its own evlist for
> > processing file events, leaving trace->evlist pointing to the
> > empty list created at startup. Therefore, the
> > evlist__put(trace->evlist) call in trace__exit() is safe and
> > correct to avoid leaking that empty list.
> >
> > v7:
> > - Added pyrf_evlist__new to zero-initialize pevlist->evlist to fix
> > crash on re-initialization.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox