Linux Documentation
 help / color / mirror / Atom feed
* [RFC PATCH 2/2] KVM: Documentation: Document KVM_CAP_X86_DISABLE_PMU_SW_ACCOUNTING
From: Luka Absandze @ 2026-07-20 19:22 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini, kvm
  Cc: linux-kernel, linux-doc, Alexander Graf, David Woodhouse,
	Luka Absandze
In-Reply-To: <20260720192221.72912-1-absandze@amazon.de>

Describe the new VM-scoped capability: its parameter (a bitmask of
PERF_COUNT_HW_* event IDs), the value returned by KVM_CHECK_EXTENSION
(the set of events that can be disabled), the accuracy trade-off, and
why an emulated-vPMU host wants it.

Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Luka Absandze <absandze@amazon.de>
---
 Documentation/virt/kvm/api.rst | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index a5f9ee92f43e..231a5ba7567c 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -8949,6 +8949,38 @@ enabled, cmma can't be enabled anymore and pfmfi and the storage key
 interpretation are disabled. If cmma has already been enabled or the
 hpage_2g module parameter is not set to 1, -EINVAL is returned.
 
+7.48 KVM_CAP_X86_DISABLE_PMU_SW_ACCOUNTING
+------------------------------------------
+
+:Architectures: x86
+:Type: vm
+:Parameters: args[0] - bitmask of PERF_COUNT_HW_* event IDs
+:Returns: 0 on success; -EINVAL if args[0] contains an unsupported event or
+          if the VM already has vCPUs.
+
+By default, KVM software-accounts instructions it emulates against a guest
+PMU counter programmed for retired instructions (PERF_COUNT_HW_INSTRUCTIONS)
+or retired branches (PERF_COUNT_HW_BRANCH_INSTRUCTIONS), by walking the vPMU
+counters and requesting a counter reprogram on the next VM-entry. On hosts
+with an emulated vPMU this reprogram is expensive and, under some workloads,
+inflates timer-interrupt handling enough to trigger guest CSD lockups.
+
+This capability lets userspace disable that software accounting for the given
+events. Setting a bit for an event ID makes KVM skip the accounting for that
+event: instructions KVM emulates in host context go uncounted. Hardware-
+executed guest instructions are still counted by the backing perf_event, and
+its overflow/PMI path is unaffected.
+
+The capability only affects the emulated (perf-based) vPMU. A mediated vPMU
+does not incur the reprogram cost and increments the guest counter directly,
+so accounting is never skipped for it regardless of this setting.
+
+This can only be invoked on a VM prior to the creation of vCPUs; attempting to
+set it once any vCPU exists returns -EINVAL.
+
+KVM_CHECK_EXTENSION returns the bitmask of event IDs that can be disabled.
+args[0] must be a subset of that mask.
+
 8. Other capabilities.
 ======================
 
-- 
2.47.3


^ permalink raw reply related

* [RFC PATCH 1/2] KVM: x86/pmu: Add CAP to disable SW accounting of emulated instructions
From: Luka Absandze @ 2026-07-20 19:22 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini, kvm
  Cc: linux-kernel, linux-doc, Alexander Graf, David Woodhouse,
	Luka Absandze
In-Reply-To: <20260720192221.72912-1-absandze@amazon.de>

On a host with an emulated vPMU (guest PMU MSR accesses trap and each
guest counter is backed by a host perf_event), the per-emulated-
instruction PMU accounting added by commit 9cd803d496e7 ("KVM: x86: Update
vPMCs when retiring instructions") is catastrophically expensive.

For every instruction KVM emulates, kvm_skip_emulated_instruction(),
the emulator writeback path, and nested VMLAUNCH/VMRESUME call into
the retired-instruction accounting for PERF_COUNT_HW_INSTRUCTIONS and
PERF_COUNT_HW_BRANCH_INSTRUCTIONS. If the guest has a counter
programmed for retired instructions (0xc0) or retired branches (0xc2)
-- e.g. any guest running a profiler with an instructions-retired
event -- this walks the counters, software-increments the matching
one, and requests a full reprogram (KVM_REQ_PMU). The reprogram is
drained on the vCPU's next VM-entry, where reprogram_counter() runs
the full pmc_pause_counter() + perf_event_period() + perf_event_
enable() sequence: ctx->mutex, a ctx_resched() of the PMU context,
and a burst of serialized PMU-MSR writes.

Because the batched reprogram is serviced on the next VM-entry
regardless of which exit preceded it, ordinary exits -- notably the
guest's 1kHz timer tick -- absorb the cost, inflating timer
interrupts into the hundreds of microseconds. With certain workloads
this can escalate to a CSD lockup in the guest.

Add KVM_CAP_X86_DISABLE_PMU_SW_ACCOUNTING, a VM-scoped capability that
takes a bitmask of PERF_COUNT_HW_* event IDs and makes KVM skip the
software accounting of KVM-emulated instructions for those events.
KVM_CHECK_EXTENSION returns the set of events that can be disabled
(PERF_COUNT_HW_INSTRUCTIONS and PERF_COUNT_HW_BRANCH_INSTRUCTIONS --
the only events the retired-instruction path ever triggers).

The only functional change for an opted-in VM is reduced accuracy: a
guest counting instructions-retired or branches-retired undercounts by
the instructions KVM emulates in host context, i.e. the behavior that
predates the accounting cited above. Hardware-executed guest
instructions continue to be counted by the backing perf_event, and its
overflow/PMI path is unchanged. Default behavior (mask 0) is unchanged.

Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Luka Absandze <absandze@amazon.de>
---
 arch/x86/include/asm/kvm_host.h |  7 +++++++
 arch/x86/kvm/pmu.c              | 21 +++++++++++++++++++++
 arch/x86/kvm/pmu.h              |  9 +++++++++
 arch/x86/kvm/x86.c              | 15 +++++++++++++++
 include/uapi/linux/kvm.h        |  1 +
 5 files changed, 53 insertions(+)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 5f6c1ce9673b..522f66c44e6a 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1560,6 +1560,13 @@ struct kvm_arch {
 	bool enable_pmu;
 	bool created_mediated_pmu;
 
+	/*
+	 * Bitmask of PERF_COUNT_HW_* event IDs for which software accounting
+	 * of KVM-emulated instructions is disabled (KVM_CAP_X86_DISABLE_PMU_
+	 * SW_ACCOUNTING). See kvm_pmu_trigger_event().
+	 */
+	u64 pmu_disable_sw_accounting;
+
 	u32 notify_window;
 	u32 notify_vmexit_flags;
 	/*
diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index dd1c57593f48..fb3f2df068dc 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -1145,14 +1145,35 @@ static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu,
 	srcu_read_unlock(&vcpu->kvm->srcu, idx);
 }
 
+/*
+ * Whether software accounting of KVM-emulated instructions for @perf_hw_id is
+ * disabled via KVM_CAP_X86_DISABLE_PMU_SW_ACCOUNTING. The capability only
+ * targets the emulated (perf-based) vPMU, where the accounting drives an
+ * expensive counter reprogram; a mediated vPMU increments pmc->counter
+ * directly (and may raise a PMI on overflow), so it is never skipped.
+ */
+static bool kvm_pmu_skip_sw_accounting(struct kvm_vcpu *vcpu, u64 perf_hw_id)
+{
+	if (kvm_vcpu_has_mediated_pmu(vcpu))
+		return false;
+
+	return vcpu->kvm->arch.pmu_disable_sw_accounting & BIT_ULL(perf_hw_id);
+}
+
 void kvm_pmu_instruction_retired(struct kvm_vcpu *vcpu)
 {
+	if (kvm_pmu_skip_sw_accounting(vcpu, PERF_COUNT_HW_INSTRUCTIONS))
+		return;
+
 	kvm_pmu_trigger_event(vcpu, vcpu_to_pmu(vcpu)->pmc_counting_instructions);
 }
 EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_pmu_instruction_retired);
 
 void kvm_pmu_branch_retired(struct kvm_vcpu *vcpu)
 {
+	if (kvm_pmu_skip_sw_accounting(vcpu, PERF_COUNT_HW_BRANCH_INSTRUCTIONS))
+		return;
+
 	kvm_pmu_trigger_event(vcpu, vcpu_to_pmu(vcpu)->pmc_counting_branches);
 }
 EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_pmu_branch_retired);
diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h
index a5821d7c87f9..e34b9e814355 100644
--- a/arch/x86/kvm/pmu.h
+++ b/arch/x86/kvm/pmu.h
@@ -23,6 +23,15 @@
 
 #define KVM_FIXED_PMC_BASE_IDX INTEL_PMC_IDX_FIXED
 
+/*
+ * Events for which KVM_CAP_X86_DISABLE_PMU_SW_ACCOUNTING can disable software
+ * accounting of emulated instructions. These are the only events KVM ever
+ * passes to kvm_pmu_trigger_event() (from the retired-instruction path).
+ */
+#define KVM_PMU_SW_ACCOUNTING_VALID_MASK			\
+	(BIT_ULL(PERF_COUNT_HW_INSTRUCTIONS) |			\
+	 BIT_ULL(PERF_COUNT_HW_BRANCH_INSTRUCTIONS))
+
 struct kvm_pmu_ops {
 	struct kvm_pmc *(*rdpmc_ecx_to_pmc)(struct kvm_vcpu *vcpu,
 		unsigned int idx, u64 *mask);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index afcac1042947..cc8a36f28aea 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -4964,6 +4964,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 	case KVM_CAP_DISABLE_QUIRKS2:
 		r = kvm_caps.supported_quirks;
 		break;
+	case KVM_CAP_X86_DISABLE_PMU_SW_ACCOUNTING:
+		r = KVM_PMU_SW_ACCOUNTING_VALID_MASK;
+		break;
 	case KVM_CAP_X86_NOTIFY_VMEXIT:
 		r = kvm_caps.has_notify_vmexit;
 		break;
@@ -6730,6 +6733,18 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
 		return -EINVAL;
 
 	switch (cap->cap) {
+	case KVM_CAP_X86_DISABLE_PMU_SW_ACCOUNTING:
+		r = -EINVAL;
+		if (cap->args[0] & ~KVM_PMU_SW_ACCOUNTING_VALID_MASK)
+			break;
+
+		mutex_lock(&kvm->lock);
+		if (!kvm->created_vcpus) {
+			kvm->arch.pmu_disable_sw_accounting = cap->args[0];
+			r = 0;
+		}
+		mutex_unlock(&kvm->lock);
+		break;
 	case KVM_CAP_DISABLE_QUIRKS2:
 		r = -EINVAL;
 		if (cap->args[0] & ~kvm_caps.supported_quirks)
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 419011097fa8..186054245b80 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -997,6 +997,7 @@ struct kvm_enable_cap {
 #define KVM_CAP_S390_KEYOP 247
 #define KVM_CAP_S390_VSIE_ESAMODE 248
 #define KVM_CAP_S390_HPAGE_2G 249
+#define KVM_CAP_X86_DISABLE_PMU_SW_ACCOUNTING 250
 
 struct kvm_irq_routing_irqchip {
 	__u32 irqchip;
-- 
2.47.3


^ permalink raw reply related

* [RFC PATCH 0/2] KVM: x86/pmu: Let userspace disable SW accounting of emulated instructions
From: Luka Absandze @ 2026-07-20 19:22 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini, kvm
  Cc: linux-kernel, linux-doc, Alexander Graf, David Woodhouse,
	Luka Absandze

On hosts that run guests with an *emulated* vPMU -- guest PMU MSR
accesses trap into KVM and each guest counter is backed by a host
perf_event -- the per-emulated-instruction PMU accounting added by commit
9cd803d496e7 ("KVM: x86: Update vPMCs when retiring instructions") is
pathologically expensive.

Whenever a guest programs a counter for retired instructions (0xc0) or
retired branches (0xc2) -- routine for any profiler-style workload --
every instruction KVM emulates triggers a software increment plus a
batched KVM_REQ_PMU reprogram. The reprogram is drained on the next
VM-entry regardless of which exit preceded it, so ordinary exits (most
importantly the guest's 1kHz timer tick) absorb a full
reprogram_counter() cycle: ctx->mutex, ctx_resched(), and a burst of
serialized PMU-MSR writes. That inflates timer-interrupt handling into
the hundreds of microseconds and, under load, can escalate to a guest
CSD lockup.

This series adds KVM_CAP_X86_DISABLE_PMU_SW_ACCOUNTING, a VM-scoped
capability that takes a bitmask of PERF_COUNT_HW_* event IDs and makes
KVM skip the software accounting of emulated instructions for those
events. It is opt-in and defaults off; the only cost to an opted-in
guest is that instructions-retired / branches-retired counters
undercount by the instructions KVM emulates in host context, i.e. the
behavior that predates that accounting. Hardware-executed guest
instructions are still counted by the backing perf_event and its
overflow/PMI path is untouched.

  Patch 1 adds the capability and the accounting short-circuit.
  Patch 2 documents it in Documentation/virt/kvm/api.rst.

Measured benefit
================

Host: AMD EPYC 7R13 "Milan", KVM, v7.2-rc4 + this series. Guest: QEMU,
-cpu host,pmu=on, 8 vCPUs. The guest workload keeps an instructions-
retired counter resident on every vCPU and periodically reprograms the
PMU, driving the accounting storm above, while the vCPUs are kept busy
so the 1kHz tick fires. The guest's local-APIC timer interrupt handler
(__sysvec_apic_timer_interrupt) is traced with ftrace function_graph
for 30s (~160k samples per run). The capability is toggled per-VM via
KVM_ENABLE_CAP; nothing else changes between runs.

  Timer-interrupt handler duration:

    metric   accounting ON (baseline)   accounting OFF (CAP)   speedup
    ------   ------------------------   --------------------   -------
    mean            209.8 us                   3.16 us           66x
    p50             256.4 us                   3.14 us           82x
    p99             463.3 us                   4.45 us          104x
    p99.9           915.2 us                   5.03 us          182x
    max             929.7 us                  22.55 us           41x

Some workloads genuinely need the extra precision that the emulated-
instruction accounting provides, and for them the default behavior is
the right trade-off. But for many others the accuracy gained is not
worth the cost it imposes on every exit, and this capability lets those
users opt out of paying it.

Luka Absandze (2):
  KVM: x86/pmu: Add CAP to disable SW accounting of emulated
    instructions
  KVM: Documentation: Document KVM_CAP_X86_DISABLE_PMU_SW_ACCOUNTING

 Documentation/virt/kvm/api.rst  | 24 ++++++++++++++++++++++++
 arch/x86/include/asm/kvm_host.h |  7 +++++++
 arch/x86/kvm/pmu.c              |  6 ++++++
 arch/x86/kvm/pmu.h              |  9 +++++++++
 arch/x86/kvm/x86.c              | 10 ++++++++++
 include/uapi/linux/kvm.h        |  1 +
 6 files changed, 57 insertions(+)

-- 
2.47.3


^ permalink raw reply

* Re: [patch 00/18] entry: Consolidate and rework syscall entry handling
From: Thomas Gleixner @ 2026-07-20 19:21 UTC (permalink / raw)
  To: Magnus Lindholm
  Cc: LKML, Peter Zijlstra, Michael Ellerman, Shrikanth Hegde,
	linuxppc-dev, Kees Cook, Huacai Chen, loongarch, Paul Walmsley,
	Palmer Dabbelt, linux-riscv, Sven Schnelle, linux-s390, x86,
	Mark Rutland, Jinjie Ruan, Andy Lutomirski, Oleg Nesterov,
	Richard Henderson, Russell King, Catalin Marinas, Guo Ren,
	Geert Uytterhoeven, Thomas Bogendoerfer, Helge Deller,
	Yoshinori Sato, Richard Weinberger, Chris Zankel,
	linux-arm-kernel, linux-alpha, linux-csky, linux-m68k, linux-mips,
	linux-parisc, linux-sh, linux-um, Arnd Bergmann, Vineet Gupta,
	Will Deacon, Brian Cain, Michal Simek, Dinh Nguyen,
	David S. Miller, Andreas Larsson, linux-snps-arc, linux-hexagon,
	linux-openrisc, sparclinux, linux-arch, Michal Suchánek,
	Jonathan Corbet, linux-doc
In-Reply-To: <CA+=Fv5TDNH8+JAojV9_W36kUkEE68uiA00FQ7i0vJunebqs1sA@mail.gmail.com>

Magnus!

On Sun, Jul 19 2026 at 13:25, Magnus Lindholm wrote:
> One Alpha-specific wrinkle I hit while rebasing the GENERIC_ENTRY patch is
> that Alpha cannot unconditionally pre-seed the normal -ENOSYS return state
> before calling into the generic entry code: syscall_set_return_value() also
> updates r19/a3, which is still syscall argument 4 on the normal entry path.
> Doing that too early broke early userspace mmap() during boot.

Similar to what Peter and me suggested in the related discussion
vs. s390 which has a similar issue, you can just have a dedicated
syscall_return member in your pt_regs struct, which is preset to -ENOSYS
and operate on that. You have an unused padding entry there which means
it won't even change the size.

> Are you planning to send a v2 with the seccomp recheck fix and any other
> review fixups folded in? Is the intention to route this upstream
> during the next merge window?

You were on CC for the V2, no?

The whole pile including Jinjie's seccomp bypass fix is in

    git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git core/entry

and is targeted for the 7.3 merge window.

If you want to base your stuff on that for 7.3, I can add a tag which
makes it immutable so it can be pulled into the alpha tree.

Thanks,

        tglx


^ permalink raw reply

* Re: [PATCH v3] docs: dt: maintainer: Add Devicetree and OF maintainer profile document
From: Jonathan Corbet @ 2026-07-20 19:19 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Shuah Khan, devicetree, workflows, linux-doc,
	linux-kernel
  Cc: Krzysztof Kozlowski, Saravana Kannan
In-Reply-To: <20260720184040.9870-2-krzysztof.kozlowski@oss.qualcomm.com>

Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com> writes:

> Document how Devicetree and Open Firmware maintainers handle their
> subsystem, especially focusing on two caveats:
>
> Devicetree subsystem handles patches with a minor difference comparing
> to other subsystems: while DT maintainers pick up OF code, they only
> provide review of DT bindings without applying these.
>
> All three DT bindings maintainers rely currently on Patchwork and due to
> enormous amount of emails per day, regardless how much DT maintainers
> try, they cannot read all the emails.
>
> Cc: Rob Herring <robh@kernel.org>
> Cc: Conor Dooley <conor+dt@kernel.org>
> Cc: Saravana Kannan <saravanak@kernel.org>
> Cc: devicetree@vger.kernel.org
> Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
>
> ---
>
> I expect patch to be picked up by Rob, after review.
>
> Changes in v3:
> 1. Add also F: entry
>
> Changes in v2:
> 1. Correct typos and trailing white spaces.
> 2. Fix order of P: after C: in maintainers.
> ---
>  .../process/maintainer-devicetree.rst         | 70 +++++++++++++++++++
>  MAINTAINERS                                   |  3 +
>  2 files changed, 73 insertions(+)
>  create mode 100644 Documentation/process/maintainer-devicetree.rst

You'll need to add this new file to index.rst as well.

Thanks,

jon

^ permalink raw reply

* Re: [PATCH 1/2] x86/resctrl, Documentation: Keep mbm_assign_mode "default" on boot
From: Babu Moger @ 2026-07-20 19:15 UTC (permalink / raw)
  To: Reinette Chatre, tony.luck, bp
  Cc: x86, Dave.Martin, james.morse, corbet, skhan, tglx, mingo,
	dave.hansen, hpa, linux-kernel, linux-doc, eranian, peternewman
In-Reply-To: <f3fd774d-0662-4094-90b6-f0c5a4bc5d26@intel.com>

Hi Reinette,


On 7/20/26 13:27, Reinette Chatre wrote:
> Hi Babu,
> 
> On 7/20/26 10:00 AM, Babu Moger wrote:
>> Hi Reinette,
>>
>> Thanks for quick response.
>>
>> On 7/17/26 17:56, Reinette Chatre wrote:
>>> Hi Babu,
>>>
>>> On 7/17/26 2:13 PM, Babu Moger wrote:
>>>> The kernel currently enables the ABMC-based "mbm_event" mode by default on
>>>> hardware that supports it. However, this can cause bandwidth monitoring
>>>> failures with existing userspace tools such as pqos.
>>>>
>>>> The pqos tool mounts the resctrl filesystem and creates 16 or more resctrl
>>>> groups by default. On systems with 32 or fewer ABMC counters, this default
>>>> configuration can consume all available counters, since each group requires
>>>> one counter for local MBM and another for total MBM. If additional
>>>> monitoring groups are created, counter resources are exhausted and pqos
>>>> tool reports memory bandwidth counters as zero for those groups.
>>>
>>> It is not obvious to me that this is a problem. If I understand correctly
>>> there are two scenarios possible with this pqos behavior:
>>>
>>> - ABMC is not in use ("mbm_assign_mode" is set to "default")
>>>     - pqos can create 16 or more monitor groups
>>>     - hardware still supports a limited number of counters with consequence that
>>>       underlying counters reset at any time as the different monitoring groups
>>>       need to be tracked.
>>>     - pqos can read monitoring data of all 16 monitor groups, sometimes reading the
>>>       events would return "Unavailable", sometimes reading the events return data.
>>>     - *None* of the monitoring numbers returned are guaranteed to be accurate.
>>>
>>> - ABMC is in use ("mbm_assign_mode" is set to "mbm_event"):
>>>     - pqos can create 16 or more monitor groups
>>>     - only a subset of monitoring groups have counters assigned and these counters
>>>       are guaranteed to only track the monitor groups/events they are assigned to
>>>     - pqos can read monitoring data of all 16 monitor groups with two possibilities:
>>>       - monitor group/event has counter assigned: monitoring numbers are guaranteed to be accurate
>>>       - monitor group/event does not have counter assigned: monitoring numbers return 0
>>>
>>> If my understanding is correct then the preference is to rather have wrong data than
>>> see 0? This does not sound right. What am I missing?
>>
>>
>> This hardware can monitor up to 64 RMIDs without any counter resets.
> 
> How many RMIDs does the hardware claim to support via CPUID that ends up being shown to user
> space via "num_rmids"?

#cat /sys/fs/resctrl/info/L3_MON/num_rmids
4096

> 
> I understood from original ABMC enabling that the underlying hardware counters of "default"
> and "mbm_event" mode on AMD are the same. That is, in "default" mode the hardware does a
> "best effort" assignment of hardware counters to events while "mbm_event" mode lets the user
> control the assignment. It instead sounds like this is not the case and there are actually
> two distinct underlying hardware counter mechanisms?

That is correct. They are two different counters.

> 
> 
>> As you know, the pqos tool creates COS1 through COS15 regardless of
>> the command-line options used, resulting in a total of 16 groups
>> including the default group. With ABMC enabled, this consumes all
>> available ABMC counters(32 counters, 2 counters for each group).
> 
>>
>> When pqos is invoked with the -m option, it creates additional monitoring groups. For example:
>>
>> pqos -m all:0     -> creates 1 monitoring group
>> pqos -m all:0,1   -> creates 2 monitoring groups
>>
>>
>> Since all ABMC counters have already been allocated to the default
>> set of groups, no counters remain for these additional monitoring
>> groups. As a result, the monitoring commands report zero values,
>> effectively making monitoring unusable.
>>
>> In contrast, the default monitoring mode can still support up to 48
>> additional monitoring groups (64 total RMIDs minus the 16 default
>> groups created by pqos).
>>
>> For this reason, I still believe keeping the default monitoring mode as the default is the better option.
> 
> It is not clear to me where the "64" number comes from. Even if resctrl sets the "default"

The count of 64 is known from internal information. It can also be 
determined by allocating monitoring counters in a loop until the 
hardware starts returning an "unavailable" response, which occurs after 
all 64 counters have been assigned. This information is not documented.

> mode as default, what will happen to the scenario you describe and 49, instead of 48,
> additional groups are created? From what I understand the moment the 65th group is created user will
> transition from "accurate per monitor group monitoring data for all 64 monitoring groups" to "inaccurate
> per monitor group monitoring data for all 65 monitoring groups" with no indication that this is happening?

Yes. That is correct.

> 
> I believe AMD supports more than 64 RMIDs and in this case there seems to be three ranges:
> [supported by ABMC, depends on events but lets say RMID 0 to 16] < [RMID 17 to 64] < [RMID 65 to total number of RMIDs supported]
> 
> Current default "mbm_event" mode uses ABMC so as you state this always results in:
> - accurate counts for 16 monitor groups
> - zero for all other monitor groups up to total number of RMIDs supported

That is correct. With ABMC, there are 32 available counters, allowing up 
to 32 monitoring events to be tracked simultaneously. Since each 
monitoring group requires two counters—one for local MBM and one for 
total MBM—the system can support only 16 monitoring groups at a time.

> 
> As I see it switching to the "default" mode would result in:
> Scenario 1, 64 or fewer monitor groups are created:
> - accurate counts for all monitor groups
> Scenario 2, 65 or more monitor groups are created:
> - inaccurate counts for all monitor groups

That is true.

> 
> I do not believe there is any way for user space to know when or if system switches from "scenario 1" to
> "scenario 2" on these systems and this unpredictable behavior that results in wrong data does not
> sound ideal to me.
  I agree, it's not an ideal situation, but that's how it has always 
worked. At the moment, I'm not sure about a better alternative.

Thanks,
Babu

^ permalink raw reply

* Re: [RFC PATCH v3 0/3] mm/damon: introduce DAMOS_SPLIT action
From: Zi Yan @ 2026-07-20 19:12 UTC (permalink / raw)
  To: Lian Wang, david
  Cc: damon, linux-mm, sj, akpm, linux-kernel, ljs, liam, vbabka, rppt,
	surenb, mhocko, npache, baolin.wang, ryan.roberts, daichaobing,
	wangkefeng.wang, gutierrez.asier, zengheng4, kasong, corbet,
	skhan, linux-doc, linux-kselftest, lianux.wang, kunwu.chan
In-Reply-To: <20260720095633.32281-1-lianux.mm@gmail.com>

On Mon Jul 20, 2026 at 5:56 AM EDT, Lian Wang wrote:
> Hi David,
>
> On 7/20/2026 10:44 AM, David Hildenbrand (Arm) wrote:
>> you give no real motivation and evaluation why this is required or
>> why this gives the user any benefit.
>> A SPLIT with an explicit order is not really want we want and it
>> does not fit the existing primitives.
>
> Thank you for the direct feedback.  Let me explain where this came
> from -- the cover letter should have included this context.
>
> This started from a real problem at Sangfor.  The scenario is:
>
>   KVM-QEMU virtualization on Kunpeng 920, with KVM guest memory
>   backed by tmpfs shared mappings (THP=always on the host).  An
>   Oracle database runs inside the VM.  DAMON monitors the KVM
>   process on the host to measure the hot-memory ratio.
>
>   The KVM process allocates and uses a large amount of memory.
>   Under the same workload, DAMON reports a significantly higher
>   hot-memory ratio with THP enabled versus THP disabled.  Direct
>   tmpfs write tests inside the VM -- touching at 4K and 2M
>   strides -- show a clear gap between the two cases.
>
>   DAMON parameters used:
>
>     operations=vaddr
>     monitoring_attrs/nr_regions/min=500
>     monitoring_attrs/nr_regions/max=2000
>     monitoring_attrs/intervals/sample_us=500000
>     monitoring_attrs/intervals/aggr_us=20000000
>     monitoring_attrs/intervals/update_us=60000000
>     schemes/0/action=stat
>     schemes/0/access_pattern/nr_accesses/min=1
>     schemes/0/access_pattern/nr_accesses/max=max
>
> The underlying issue is that under PMD-mapped THP, DAMON's monitoring
> granularity is coarser than the actual working set -- a single
> Accessed bit covers 512 base pages.  Before SJ's probe infrastructure
> arrives, there is a gap: DAMON cannot distinguish hot sub-pages from
> cold ones within a single THP.
>
> Split is one possible mechanism to bridge that gap -- by dismantling
> the PMD mapping, each base page gets its own PTE Accessed bit and
> DAMON recovers fine-grain monitoring.  It is not intended to be a
> permanent API, and certainly not "the opposite of collapse".

If you just want PTE level access bit information, why not split PMD
mapping instead of the THP itself?

In addition, the issue is about access monitoring granularity in DAMON,
why should user care and know about THP split operations? I would expect
DAMON detects the inability of getting fine grain access information and
split the PMD mapping itself instead of a user initiated DAMON_SPLIT. If
that is not possible with DAMON, an alternative is to provide something
more generic like DAMON_SAMPLE, which does the split under the hood,
instead of exposing MM internal operations.

>
> I did not write this scenario into the cover letter because our test
> results do not yet show a clear quantitative benefit worth claiming,
> and I did not want to oversell.  Without the context, I understand it
> looks like I randomly proposed a new primitive -- that was not the
> intention.
>
> SJ acknowledged [1] that the monitoring problem under THP is real.
> My RFC is a concrete proposal to start the discussion.  If split with
> an explicit order is not the right primitive, I would appreciate your
> thoughts on what the correct DAMOS abstraction for this should be.
>
> [1] https://lore.kernel.org/20260620203915.82947-1-sj@kernel.org/
>
> Thanks,
> Lian Wang




-- 
Best Regards,
Yan, Zi


^ permalink raw reply

* [PATCH v3] docs: dt: maintainer: Add Devicetree and OF maintainer profile document
From: Krzysztof Kozlowski @ 2026-07-20 18:40 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
	Shuah Khan, devicetree, workflows, linux-doc, linux-kernel
  Cc: Krzysztof Kozlowski, Saravana Kannan

Document how Devicetree and Open Firmware maintainers handle their
subsystem, especially focusing on two caveats:

Devicetree subsystem handles patches with a minor difference comparing
to other subsystems: while DT maintainers pick up OF code, they only
provide review of DT bindings without applying these.

All three DT bindings maintainers rely currently on Patchwork and due to
enormous amount of emails per day, regardless how much DT maintainers
try, they cannot read all the emails.

Cc: Rob Herring <robh@kernel.org>
Cc: Conor Dooley <conor+dt@kernel.org>
Cc: Saravana Kannan <saravanak@kernel.org>
Cc: devicetree@vger.kernel.org
Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>

---

I expect patch to be picked up by Rob, after review.

Changes in v3:
1. Add also F: entry

Changes in v2:
1. Correct typos and trailing white spaces.
2. Fix order of P: after C: in maintainers.
---
 .../process/maintainer-devicetree.rst         | 70 +++++++++++++++++++
 MAINTAINERS                                   |  3 +
 2 files changed, 73 insertions(+)
 create mode 100644 Documentation/process/maintainer-devicetree.rst

diff --git a/Documentation/process/maintainer-devicetree.rst b/Documentation/process/maintainer-devicetree.rst
new file mode 100644
index 000000000000..d8ffe752bf5d
--- /dev/null
+++ b/Documentation/process/maintainer-devicetree.rst
@@ -0,0 +1,70 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================================
+Devicetree and Open Firmware Subsystem
+======================================
+
+Other Process Documents
+-----------------------
+
+Please see the documents in Documentation/devicetree/bindings/ for information
+on how to write proper Devicetree bindings and how to submit patches.
+
+Patch Review and Handling
+-------------------------
+
+Patches handled by Devicetree maintainers are processed differently depending
+on the patch type:
+
+1. Core OF driver code, e.g. drivers/of/:
+   patches are reviewed and applied by DT maintainers.
+
+2. Devicetree bindings:
+   patches are reviewed by DT maintainers but, except in certain cases, should
+   be applied by subsystem maintainers.  See also *For kernel maintainers* in
+   Documentation/devicetree/bindings/submitting-patches.rst.
+
+3. DTS and drivers:
+   DT maintainers might provide comments, but review is generally not expected.
+
+Patchwork
+~~~~~~~~~
+
+Devicetree maintainers review patches using Patchwork, so the current status of
+a patch can be checked there. For typical driver submissions, Patchwork
+receives the entire patch set, but only a few patches are usually Devicetree
+bindings that are reviewed by DT maintainers.
+
+Explanation of Patchwork statuses:
+
+ - **New**: Not yet processed by the automation toolset.
+ - **Needs ACK**: Waiting for review by DT maintainers.
+ - **Handled Elsewhere**: Non-DT patch; not being reviewed here.
+ - **RFC**: Patch was likely ignored because it was an incomplete RFC.
+ - **Changes Requested**: Patch was reviewed and DT maintainers expect changes.
+ - **Accepted**: Patch was reviewed and applied by DT maintainers to their tree.
+ - **Not Applicable**: Patch was reviewed and is likely in good shape, with a
+   *Reviewed-by* or *Acked-by* tag provided, but DT maintainers expect someone
+   else to apply it.
+
+Patch Re-review and Pinging
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Due to the high volume of email traffic, Devicetree maintainers do not read
+every email they receive and instead rely on Patchwork during the review
+process. They also often skip patches that have already been reviewed.
+
+As a result, maintainers might miss:
+
+1. Questions about already reviewed patches.
+2. Pings, for example when a patch has been reviewed by DT maintainers but has
+   not been picked up by subsystem maintainers.
+
+Such cases can be addressed by:
+
+1. Pinging DT maintainers on the IRC channel.
+2. Dropping the DT maintainer’s *Acked-by* or *Reviewed-by* tag when sending a new
+   version of the patch set, together with an explanation in the patch
+   changelog describing why the tag was removed and what is expected from DT
+   maintainers.
+
diff --git a/MAINTAINERS b/MAINTAINERS
index 0b6b9e6b3900..be6aa5d174c5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20285,6 +20285,7 @@ S:	Maintained
 Q:	http://patchwork.kernel.org/project/devicetree/list/
 W:	http://www.devicetree.org/
 C:	irc://irc.libera.chat/devicetree
+P:	Documentation/process/maintainer-devicetree.rst
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git
 F:	Documentation/ABI/testing/sysfs-firmware-ofw
 F:	drivers/of/
@@ -20306,8 +20307,10 @@ L:	devicetree@vger.kernel.org
 S:	Maintained
 Q:	http://patchwork.kernel.org/project/devicetree/list/
 C:	irc://irc.libera.chat/devicetree
+P:	Documentation/process/maintainer-devicetree.rst
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git
 F:	Documentation/devicetree/
+F:	Documentation/process/maintainer-devicetree.rst
 F:	arch/*/boot/dts/
 F:	include/dt-bindings/
 
-- 
2.53.0


^ permalink raw reply related

* Re: [RFC PATCH 0/7] mm: defer address-space teardown of large exiting processes to a kthread
From: Aditya Sharma @ 2026-07-20 18:38 UTC (permalink / raw)
  To: Liam R . Howlett
  Cc: Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
	Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
	David Rientjes, Shakeel Butt, Jonathan Corbet, Shuah Khan,
	Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers, Kees Cook,
	Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
	Dietmar Eggemann, Ben Segall, Mel Gorman, Valentin Schneider,
	K Prateek Nayak, linux-mm, linux-doc, linux-trace-kernel,
	linux-kernel, imbrenda
In-Reply-To: <ljr2jkxomq4ccgzq2ip3c66mjdryomhl2gxkswv4wzry5ye2ax@xaehxzjkkk3m>



Sent using Zoho Mail


From: Liam R . Howlett <liam@infradead.org>
To: "Aditya Sharma"<adi.sharma@zohomail.in>
Cc: "Andrew Morton"<akpm@linux-foundation.org>, "David Hildenbrand"<david@kernel.org>, "Lorenzo Stoakes"<ljs@kernel.org>, "Vlastimil Babka"<vbabka@kernel.org>, "Mike Rapoport"<rppt@kernel.org>, "Suren Baghdasaryan"<surenb@google.com>, "Michal Hocko"<mhocko@suse.com>, "David Rientjes"<rientjes@google.com>, "Shakeel Butt"<shakeel.butt@linux.dev>, "Jonathan Corbet"<corbet@lwn.net>, "Shuah Khan"<skhan@linuxfoundation.org>, "Steven Rostedt"<rostedt@goodmis.org>, "Masami Hiramatsu"<mhiramat@kernel.org>, "Mathieu Desnoyers"<mathieu.desnoyers@efficios.com>, "Kees Cook"<kees@kernel.org>, "Ingo Molnar"<mingo@redhat.com>, "Peter Zijlstra"<peterz@infradead.org>, "Juri Lelli"<juri.lelli@redhat.com>, "Vincent Guittot"<vincent.guittot@linaro.org>, "Dietmar Eggemann"<dietmar.eggemann@arm.com>, "Ben Segall"<bsegall@google.com>, "Mel Gorman"<mgorman@suse.de>, "Valentin Schneider"<vschneid@redhat.com>, "K Prateek Nayak"<kprateek.nayak@amd.com>, <linux-mm@kvack.org>, <linux-doc@vger.kernel.org>, <linux-trace-kernel@vger.kernel.org>, <linux-kernel@vger.kernel.org>, <imbrenda@linux.ibm.com>
Date: Mon, 20 Jul 2026 03:58:47 +0530
Subject: Re: [RFC PATCH 0/7] mm: defer address-space teardown of large exiting processes to a kthread


 > This reads like it was written by an AI, but there is no assisted-by
 > tags.  Which AI did you use to generate these patches and/or cover
 > letter?

I used claude opus 4.8 to refine commit messages (the design is mine, 
the model was used to convey the intentions concretely). Further, I used
claude fable 5 for drafting the cover letter and reviewing the series, where
it flagged issues (I fixed some of those by hand and some were fixed by fable itself,
which I then reviewed). It also assisted in writing the benchmark harness
behind the numbers and did the litmus test the cover letter mentions. Every numbers
was measured by me, on my hardware. I take full responsibily of the entire 
series.

My bad that I did not add an assisted-by tag. Should have added it to v1
v2 will add it to the patches

 > I do not see a benefit to running the task cleanup 'off the side'.  In
 > modern computers, that may mean switching core types that run at
 > different speeds.

That's why the reaper's affinity is a preference, not a bind (patch 2).
It uses kthread_affine_preferred() with housekeeping mask, so it can be
re-affined. kthread_bind_mask() would have taken that away. On a hybrid cpu,
it can be affined(by the (super)user) to whichever cores are appropriate. 

I do not have numbers for P core and E core teardown costs. Will measure and
send them.

 > Regardless of speed, the CPU that will be doing the work is remote to
 > the workload by design, (or may be, depending on the scheduling?).  That
 > is, you are tasking another CPU to do cleanup of local CPU-aware
 > information.. The ideal CPU to do a task exit is the one that's doing it
 > already.

Yes, it does cost something. Five consecutive 4GB inline teardowns in my testing
costed 372ms in exit path, while 440 ms in the case of kthread drain for the same
(five teardowns on another cpu). This cost became 1.18x. For multi gb mms,
the memory to be reaped would be much larger than the cpu cache can hold.
For small mms (< 64 MB, configurable by sysctl), the teardowns happen inline. 

The oom_reaper already reaps the victim's address space from a remote context,
as returning memory matters more than locality of teardown.

 > There are hidden costs to your approach and I don't really understand
 > how this could possibly speed things up - even parallelized tasks will
 > take more CPU time in total, even if locking issues are avoided.

This is not meant to speed up teardown. Rather the latency for someone waiting
on death is reduced. Killing a 16 GB redis cost 292.97ms before returning to
waitpid(). It costs 0.24 with async teardown. The memory itself was returned
in about 300ms.

"parallelized" - This series does not parallelize the teardown. The design is serial.
N procesess exiting together today would tear down concurrently today. The reaper here,
uses one thread (nice 19, affined to housekeeping mask).

The numbers in cover letter support that this work is memory bound rather than CPU bound.
In sync case: teardown is around 56 GB/s on my machine (1GB in 18.01 ms and 16GB in 282.16ms).
Thirty two concurrent 0.5GB exits reap in 157.42ms (around 101 GB/s). So, a concurrency
of 32 gets about 1.8x. The single reaper thread takes 650ms to clear the same (about 25 GB/s).
Thus, this serialization took 4x the drain time, but takes peak concurrency down to
one cpu. Memory actully comes back later. Whether that's a good tradeoff, depends on 
what else we wish to run on the box (open questions #1 in cover letter)

 > Sure, the task waiting for cleanup to complete may think it's done and
 > continue to do the Next Thing - but if it really needs the cleanup to be
 > completed

It is off by default and opt in (sysctl), so anything that depends on the
current semantics, continues to work exactly the same by doing nothing.

Also, mmput_async() already defers the teardown to a workqueue. So, in the current
kernel, there already exists a way where continuing to the next thing
does not necessarily mean that the memory is reclaimed.

 > completed then you've just made it impossible to know when it has
 > happened.

There are three vmstat counters and two tracepoint (patch 6).
Someone who opts in to the mechanism, and does need to make sure that the
cleanup is completed, can use the counters and tracepoints.

 > Also, you've just made this extremely hard to debug if something is
 > missed.

There are reap tracepoints that carry the pid, so we can get per pid completion status.

However, there is no number indicating the current depth of the queue. The series addds queued,
sync and rejected counters. Will add a reaped counter in v2 so that current queue depth
can be estimated. Please mention what else tracability can be added to this,
and what senairo do you have in mind that these counters and tracepoints might not
cover. I can add those to v2 as well.

 > The work that is done in teardown is necessary.  Your time (and tokens?)
 > would be better spent trying to improve what we have to do instead of
 > shifting the work around.

This does not compete with with current teardown path. it goes through the
same exit_mm(). Any future improvements there, also improve the reaper's drain
times.

Thanks

Aditya

^ permalink raw reply

* Re: [PATCH v7 09/11] x86/virt/tdx: Enable Dynamic PAMT
From: Edgecombe, Rick P @ 2026-07-20 18:34 UTC (permalink / raw)
  To: sashiko-reviews@lists.linux.dev
  Cc: kvm@vger.kernel.org, linux-coco@lists.linux.dev, Huang, Kai,
	Hansen, Dave, Zhao, Yan Y, tony.lindgren@linux.intel.com,
	Wu, Binbin, kas@kernel.org, seanjc@google.com, mingo@redhat.com,
	linux-kernel@vger.kernel.org, pbonzini@redhat.com,
	nik.borisov@suse.com, linux-doc@vger.kernel.org, hpa@zytor.com,
	tglx@kernel.org, Annapurve, Vishal, bp@alien8.de, Gao, Chao,
	x86@kernel.org
In-Reply-To: <20260718015627.21D9F1F000E9@smtp.kernel.org>

On Sat, 2026-07-18 at 01:56 +0000, sashiko-bot@kernel.org wrote:
> > Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> > - [Low] The global variable `tdx_enable_dpamt` is defined without the `static` keyword, polluting the global namespace.
> > --
> > 
> > --- Patch [9]: [PATCH v7 09/11] x86/virt/tdx: Enable Dynamic PAMT ---
> > commit a5e057035573a191a887572dacd25f3faa79112d
> > Author: Kiryl Shutsemau <kas@kernel.org>
> > 
> > x86/virt/tdx: Enable Dynamic PAMT
> > 
> > This commit enables Dynamic PAMT for TDX modules to reduce memory usage
> > during module initialization. It introduces a kernel command line parameter
> > "tdx_dpamt" to allow users to opt-in to this feature when supported by the
> > hardware.
> > 
> > > > diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
> > > > index e31b62f36300d..69834b0d3e602 100644
> > > > --- a/arch/x86/virt/vmx/tdx/tdx.c
> > > > +++ b/arch/x86/virt/vmx/tdx/tdx.c
> > > > @@ -47,6 +47,8 @@
> > > >   #include "seamcall_internal.h"
> > > >   #include "tdx.h"
> > > >   
> > > > +bool tdx_enable_dpamt __ro_after_init;
> > 
> > [Severity: Low]
> > Since tdx_enable_dpamt is only used within this file, should it be marked
> > as static? Leaving it globally exposed can trigger compiler warnings like
> > -Wmissing-declarations when building with W=1 and unnecessarily pollutes
> > the global namespace.

Sigh. Yes this is valid. I forgot to make it static after deciding to move
tdx_supports_dynamic_pamt() out of the header instead. Then it didn't need to
reference out of this file. Doh.

Seems maybe something that could be fixed up when applying if the rest of the
series look ok?

^ permalink raw reply

* Re: [PATCH v7 11/11] x86/virt/tdx: Optimize tdx_pamt_get/put()
From: Edgecombe, Rick P @ 2026-07-20 18:33 UTC (permalink / raw)
  To: tony.lindgren@linux.intel.com, linux-coco@lists.linux.dev,
	Huang, Kai, Hansen, Dave, Zhao, Yan Y, Wu, Binbin, kas@kernel.org,
	seanjc@google.com, mingo@redhat.com, linux-kernel@vger.kernel.org,
	pbonzini@redhat.com, nik.borisov@suse.com,
	linux-doc@vger.kernel.org, sashiko-reviews@lists.linux.dev,
	hpa@zytor.com, tglx@kernel.org, Annapurve, Vishal, bp@alien8.de,
	Gao, Chao, x86@kernel.org
  Cc: kvm@vger.kernel.org
In-Reply-To: <20260718020053.C2FC81F000E9@smtp.kernel.org>

On Sat, 2026-07-18 at 02:00 +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Medium] Using `atomic_dec_and_lock` in `tdx_pamt_put` lacks underflow protection, causing an unbalanced put (refcount 0) to silently underflow the refcount to -1, which breaks future `tdx_pamt_get` calls for that PFN.

The report is essentially that this patch doesn't handle unbalanced
tdx_pamt_put() calls (a tdx_pamt_put() without a previous paired
tdx_pamt_get()). So this isn't a bug per-se, but it is true that the
optimization patch has a bit worse warnings in the case of buggy calling code.

More analysis... The function is:

int atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock)
{
	/* Subtract 1 from counter unless that drops it to 0 (ie. it was 1) */
	if (atomic_add_unless(atomic, -1, 1))
		return 0;

	/* Otherwise do it the slow way */
	spin_lock(lock);
	if (atomic_dec_and_test(atomic))
		return 1;
	spin_unlock(lock);
	return 0;
}

In the fast path atomic_add_unless() subtracts 1 unless the counter was exactly
1. So if it was 0 (no paired get has been called), the counter would underflow.

In the non-optimized patch, the put code was more robust to this scenario. In
that code if a put was done with the refcount at 0, the put would see it was
less than 1 and try to remove it. The result would be a warning from the
SEAMCALL failure.

So this patch trades some warnings for performance. But the warning loss is
pretty small. Future buggy code is unlikely to get away without a warning.
Missing PAMT is not like double free or bug like that. With the count
unbalanced, then next tdx_pamt_get() will fail to add the Dynamic PAMT page pair
and the TDX module will return an error when it tries to use the page.

Getting a warning exactly at the point of the unpaired put would be better. But
this version has other clear benefits. On balance, I'd prefer this one. It might
be possible to utilize refcount_t to get some better underflow warnings, but
probably for the cost of more memory ordering things to consider.

Hmm, another cheap, but not complete option would be to just warn on get/put out
paths if any strange refcounts are seen. Since the point is just to catch the
error closer to where it happens. It's just developer help. Simpler code with
less well timed warnings seems like a good tradeoff for developers?

> --
> 
> --- Patch [11]: [PATCH v7 11/11] x86/virt/tdx: Optimize tdx_pamt_get/put() ---
> commit f3bde8ccf9659f8b1e1cedb9b81df0b3b209a9fa
> Author: Kiryl Shutsemau <kas@kernel.org>
> 
> x86/virt/tdx: Optimize tdx_pamt_get/put()
> 
> The Dynamic PAMT get/put helpers use a global spinlock to serialize all
> refcount updates and SEAMCALL invocations. This gives correct behavior for
> concurrent callers, but leads to contention.
> 
> > diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
> > index 69834b0d3e602..4394bba7c9a7c 100644
> > --- a/arch/x86/virt/vmx/tdx/tdx.c
> > +++ b/arch/x86/virt/vmx/tdx/tdx.c
> 
> [ ... ]
> 
> > @@ -2214,17 +2227,13 @@ void tdx_pamt_put(kvm_pfn_t pfn)
> >  
> >  	pamt_refcount = tdx_find_pamt_refcount(pfn);
> >  
> > -	spin_lock(&pamt_lock);
> >  	/*
> >  	 * If there is more than 1 reference on the pamt page, don't
> >  	 * remove it yet. Just decrement the refcount.
> >  	 */
> > -	if (atomic_read(pamt_refcount) > 1) {
> > -		atomic_dec(pamt_refcount);
> > -		goto out_unlock;
> > -	}
> > +	if (!atomic_dec_and_lock(pamt_refcount, &pamt_lock))
> > +		return;
> 
> [Severity: Medium]
> If an unbalanced put occurs on an un-added PAMT page (where the refcount
> is 0), does atomic_dec_and_lock() lack underflow protection and silently
> drop the refcount to -1 instead of proceeding to the SEAMCALL failure
> warning as the previous logic did?
> 
> lib/dec_and_lock.c:atomic_dec_and_lock() {
>     /* Subtract 1 from counter unless that drops it to 0 (ie. it was 1) */
>     if (atomic_add_unless(atomic, -1, 1))
>         return 0;
>     ...
> }
> 
> If the refcount silently becomes -1, would a subsequent call to
> tdx_pamt_get() for this PFN incorrectly succeed without adding the PAMT
> memory?
> 
> arch/x86/virt/vmx/tdx/tdx.c:tdx_pamt_get() {
>     ...
>     if (atomic_inc_not_zero(pamt_refcount))
>         return 0;
>     ...
> }
> 
> Since -1 is not zero, atomic_inc_not_zero() would increment the refcount
> to 0 and return true, bypassing the tdh_phymem_pamt_add() SEAMCALL.
> 


^ permalink raw reply

* Re: [RFC PATCH 0/7] mm: defer address-space teardown of large exiting processes to a kthread
From: Aditya Sharma @ 2026-07-20 18:30 UTC (permalink / raw)
  To: David Hildenbrand (Arm)
  Cc: Andrew Morton, Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
	Mike Rapoport, Suren Baghdasaryan, Michal Hocko, David Rientjes,
	Shakeel Butt, Jonathan Corbet, Shuah Khan, Steven Rostedt,
	Masami Hiramatsu, Mathieu Desnoyers, Kees Cook, Ingo Molnar,
	Peter Zijlstra, Juri Lelli, Vincent Guittot, Dietmar Eggemann,
	Ben Segall, Mel Gorman, Valentin Schneider, K Prateek Nayak,
	linux-mm, linux-doc, linux-trace-kernel, linux-kernel, imbrenda
In-Reply-To: <d1180a70-f4b4-4c22-9ce5-881cb0e2370d@kernel.org>



Sent using Zoho Mail


From: David Hildenbrand (Arm) <david@kernel.org>
To: "Aditya Sharma"<adi.sharma@zohomail.in>, "Andrew Morton"<akpm@linux-foundation.org>, "Lorenzo Stoakes"<ljs@kernel.org>, "Liam R . Howlett"<liam@infradead.org>, "Vlastimil Babka"<vbabka@kernel.org>, "Mike Rapoport"<rppt@kernel.org>, "Suren Baghdasaryan"<surenb@google.com>, "Michal Hocko"<mhocko@suse.com>
Cc: "David Rientjes"<rientjes@google.com>, "Shakeel Butt"<shakeel.butt@linux.dev>, "Jonathan Corbet"<corbet@lwn.net>, "Shuah Khan"<skhan@linuxfoundation.org>, "Steven Rostedt"<rostedt@goodmis.org>, "Masami Hiramatsu"<mhiramat@kernel.org>, "Mathieu Desnoyers"<mathieu.desnoyers@efficios.com>, "Kees Cook"<kees@kernel.org>, "Ingo Molnar"<mingo@redhat.com>, "Peter Zijlstra"<peterz@infradead.org>, "Juri Lelli"<juri.lelli@redhat.com>, "Vincent Guittot"<vincent.guittot@linaro.org>, "Dietmar Eggemann"<dietmar.eggemann@arm.com>, "Ben Segall"<bsegall@google.com>, "Mel Gorman"<mgorman@suse.de>, "Valentin Schneider"<vschneid@redhat.com>, "K Prateek Nayak"<kprateek.nayak@amd.com>, <linux-mm@kvack.org>, <linux-doc@vger.kernel.org>, <linux-trace-kernel@vger.kernel.org>, <linux-kernel@vger.kernel.org>, <imbrenda@linux.ibm.com>
Date: Mon, 20 Jul 2026 13:43:34 +0530
Subject: Re: [RFC PATCH 0/7] mm: defer address-space teardown of large exiting processes to a kthread


 > QEMU has an rather short implementation for async teardown using
 > clone(CLONE_VM), which is IIRC essentially the result of Claudios previous
 > kernel work you note below.
 > 
 > So nothing got merged because the problem was solvable in userspace.

Thanks for this. My related works paragraph is wrong on this then. It
says that the s390 case was addressed with KVM specific async destruction,
but what you point to is QEMU's shadow process. I will correct that in v2.

Yeah, that CLONE_VM trick works, so I would measure and compare them
and get back.

I plan to measure:

- Memory return latency
- N concurrent shadow teardowns on N cpus vs the reaper.

One difference between CLONE_VM approach and CONFIG_ASYNC_MM_TEARDOWN
is that the shadow process has to be created by the process that would
be torn down itself. If that same behaviour is wanted on a workload
where we dont own the source, CONFIG_ASYNC_MM_TEARDOWN enables it here
as well. Also, in the QEMU case, it also means that the deferred teardown
decision was made without knowing how large the process might become.
CONFIG_ASYNC_MM_TEARDOWN adds a gate where for the exiting process,
RSS is evaluated againsts the current backlog before queuing it for
async teardown.

I will drop a follow up with numbers

Aditya


^ permalink raw reply

* Re: [PATCH 1/2] x86/resctrl, Documentation: Keep mbm_assign_mode "default" on boot
From: Reinette Chatre @ 2026-07-20 18:27 UTC (permalink / raw)
  To: Babu Moger, tony.luck, bp
  Cc: x86, Dave.Martin, james.morse, corbet, skhan, tglx, mingo,
	dave.hansen, hpa, linux-kernel, linux-doc, eranian, peternewman
In-Reply-To: <b0dc49dc-ac35-457b-90c6-842a3cfcf8e1@amd.com>

Hi Babu,

On 7/20/26 10:00 AM, Babu Moger wrote:
> Hi Reinette,
> 
> Thanks for quick response.
> 
> On 7/17/26 17:56, Reinette Chatre wrote:
>> Hi Babu,
>>
>> On 7/17/26 2:13 PM, Babu Moger wrote:
>>> The kernel currently enables the ABMC-based "mbm_event" mode by default on
>>> hardware that supports it. However, this can cause bandwidth monitoring
>>> failures with existing userspace tools such as pqos.
>>>
>>> The pqos tool mounts the resctrl filesystem and creates 16 or more resctrl
>>> groups by default. On systems with 32 or fewer ABMC counters, this default
>>> configuration can consume all available counters, since each group requires
>>> one counter for local MBM and another for total MBM. If additional
>>> monitoring groups are created, counter resources are exhausted and pqos
>>> tool reports memory bandwidth counters as zero for those groups.
>>
>> It is not obvious to me that this is a problem. If I understand correctly
>> there are two scenarios possible with this pqos behavior:
>>
>> - ABMC is not in use ("mbm_assign_mode" is set to "default")
>>    - pqos can create 16 or more monitor groups
>>    - hardware still supports a limited number of counters with consequence that
>>      underlying counters reset at any time as the different monitoring groups
>>      need to be tracked.
>>    - pqos can read monitoring data of all 16 monitor groups, sometimes reading the
>>      events would return "Unavailable", sometimes reading the events return data.
>>    - *None* of the monitoring numbers returned are guaranteed to be accurate.
>>
>> - ABMC is in use ("mbm_assign_mode" is set to "mbm_event"):
>>    - pqos can create 16 or more monitor groups
>>    - only a subset of monitoring groups have counters assigned and these counters
>>      are guaranteed to only track the monitor groups/events they are assigned to
>>    - pqos can read monitoring data of all 16 monitor groups with two possibilities:
>>      - monitor group/event has counter assigned: monitoring numbers are guaranteed to be accurate
>>      - monitor group/event does not have counter assigned: monitoring numbers return 0
>>
>> If my understanding is correct then the preference is to rather have wrong data than
>> see 0? This does not sound right. What am I missing?
> 
> 
> This hardware can monitor up to 64 RMIDs without any counter resets.

How many RMIDs does the hardware claim to support via CPUID that ends up being shown to user
space via "num_rmids"?

I understood from original ABMC enabling that the underlying hardware counters of "default"
and "mbm_event" mode on AMD are the same. That is, in "default" mode the hardware does a
"best effort" assignment of hardware counters to events while "mbm_event" mode lets the user
control the assignment. It instead sounds like this is not the case and there are actually
two distinct underlying hardware counter mechanisms?


> As you know, the pqos tool creates COS1 through COS15 regardless of
> the command-line options used, resulting in a total of 16 groups
> including the default group. With ABMC enabled, this consumes all
> available ABMC counters(32 counters, 2 counters for each group).

> 
> When pqos is invoked with the -m option, it creates additional monitoring groups. For example:
> 
> pqos -m all:0     -> creates 1 monitoring group
> pqos -m all:0,1   -> creates 2 monitoring groups
> 
> 
> Since all ABMC counters have already been allocated to the default
> set of groups, no counters remain for these additional monitoring
> groups. As a result, the monitoring commands report zero values,
> effectively making monitoring unusable.
> 
> In contrast, the default monitoring mode can still support up to 48
> additional monitoring groups (64 total RMIDs minus the 16 default
> groups created by pqos).
> 
> For this reason, I still believe keeping the default monitoring mode as the default is the better option.

It is not clear to me where the "64" number comes from. Even if resctrl sets the "default"
mode as default, what will happen to the scenario you describe and 49, instead of 48,
additional groups are created? From what I understand the moment the 65th group is created user will
transition from "accurate per monitor group monitoring data for all 64 monitoring groups" to "inaccurate
per monitor group monitoring data for all 65 monitoring groups" with no indication that this is happening?

I believe AMD supports more than 64 RMIDs and in this case there seems to be three ranges:
[supported by ABMC, depends on events but lets say RMID 0 to 16] < [RMID 17 to 64] < [RMID 65 to total number of RMIDs supported]

Current default "mbm_event" mode uses ABMC so as you state this always results in:
- accurate counts for 16 monitor groups
- zero for all other monitor groups up to total number of RMIDs supported

As I see it switching to the "default" mode would result in:
Scenario 1, 64 or fewer monitor groups are created:
- accurate counts for all monitor groups
Scenario 2, 65 or more monitor groups are created:
- inaccurate counts for all monitor groups

I do not believe there is any way for user space to know when or if system switches from "scenario 1" to
"scenario 2" on these systems and this unpredictable behavior that results in wrong data does not
sound ideal to me.

Reinette


^ permalink raw reply

* Re: [RFC] cxl: Device protocol AER injection
From: Bowman, Terry @ 2026-07-20 18:12 UTC (permalink / raw)
  To: Dave Jiang, Bjorn Helgaas, Dan Williams, Ira Weiny,
	Jonathan Cameron, Robert Richter
  Cc: linux-acpi, linux-cxl, linux-doc, linux-kernel, linux-pci,
	linuxppc-dev, Alejandro Lucero, Alison Schofield, Ben Cheatham,
	Davidlohr Bueso, Gregory Price
In-Reply-To: <6ff8d646-61b1-496e-bdc0-f4a4011d1d58@intel.com>

On 7/20/2026 12:38 PM, Dave Jiang wrote:
> 
> 
> On 7/17/26 3:57 PM, Terry Bowman wrote:
>> This patch is intended to provide a method of testing the recently submitted
>> cxl series "cxl: Enable CXL PCIe Port Protocol Error handling and logging" found
>> here:
>>
>> https://lore.kernel.org/linux-cxl/20260717222706.3540281-1-terry.bowman@amd.com/T/#md90ec1fdd1b374bf1e32e7736e2b3e34b328c701
>>
>> The changes in this patch will allow CXL RAS protocol testing by injecting
>> AER errors using AER EINJ. The RAS register block status is updated
>> using a central function to augment RAS register block returned by
>> to_ras_base(). This supports all CXL devices including Root Ports,
>> Upstream Switch Ports, Downstream Switch Ports, Endpoints, and RCH
>> Downstream Ports.
>>
>> Add debugfs-based CXL protocol error injection for testing CXL RAS
>> error handling paths. Injects CXL RAS protocol errors using AER internal
>> error inject interface via /sys/kernel/debug/cxl/aer_einj_inject.
>>
>> RAS CXL status is set using to_ras_base() function override when kernel config
>> CONFIG_CXL_PROTO_AER_EINJ is enabled.
>>
>> Usage:
>>   echo "DDDD:BB:DD.F [UCE|CE] AER_STATUS RAS_STATUS [RCH]" > \
>>       /sys/kernel/debug/cxl/aer_einj_inject
>>
>> Move struct aer_error_inj and aer_inject() to linux/aer.h so CXL
>> can invoke AER injection directly. Export aer_inject() with
>> EXPORT_SYMBOL_GPL.
>>
>> Make cxl_debugfs non-static in port.c and declare it extern in
>> core.h so the debugfs file can be created under the existing CXL
>> debugfs root.
>>
>> Co-developed-by: Ben Cheatham <Benjamin.Cheatham@amd.com>
>> Signed-off-by: Ben Cheatham <Benjamin.Cheatham@amd.com>
>> Signed-off-by: Terry Bowman <terry.bowman@amd.com>
>> ---
>>  drivers/cxl/Kconfig           |  13 +++
>>  drivers/cxl/core/core.h       |  21 ++++
>>  drivers/cxl/core/port.c       |   2 +-
>>  drivers/cxl/core/ras.c        | 208 ++++++++++++++++++++++++++++++++++
>>  drivers/cxl/core/ras_rch.c    |  12 ++
>>  drivers/pci/pcie/aer_inject.c |  29 ++---
>>  include/linux/aer.h           |  15 +++
>>  7 files changed, 281 insertions(+), 19 deletions(-)
>>
>> diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig
>> index 80aeb0d556bd7..ef449228b2549 100644
>> --- a/drivers/cxl/Kconfig
>> +++ b/drivers/cxl/Kconfig
>> @@ -238,6 +238,19 @@ config CXL_RAS
>>  	def_bool y
>>  	depends on ACPI_APEI_GHES && PCIEAER && CXL_BUS
>>  
>> +config CXL_PROTO_AER_EINJ
>> +	bool "CXL: RAS Protocol Error Injection using AER EINJ"
>> +	depends on CXL_RAS
>> +	depends on PCIEAER_INJECT
>> +	help
>> +	  Enable debugfs-based CXL protocol error injection. Writes to
>> +	  /sys/kernel/debug/cxl/aer_einj_inject inject CXL RAS protocol
>> +	  errors using the AER internal error inject interface.
>> +
>> +	  This is a debug/test facility. Say N for production kernels.
>> +
>> +	  If unsure say N.
>> +
>>  config CXL_ATL
>>  	def_bool y
>>  	depends on CXL_REGION
>> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
>> index a55a4e409feda..91910d2bb5d39 100644
>> --- a/drivers/cxl/core/core.h
>> +++ b/drivers/cxl/core/core.h
>> @@ -182,6 +182,9 @@ static inline struct device *dport_to_host(struct cxl_dport *dport)
>>  		return port->uport_dev;
>>  	return &port->dev;
>>  }
>> +
>> +extern struct dentry *cxl_debugfs;
>> +
>>  #ifdef CONFIG_CXL_RAS
>>  void cxl_ras_init(void);
>>  void cxl_ras_exit(void);
>> @@ -244,4 +247,22 @@ int cxl_set_feature(struct cxl_mailbox *cxl_mbox, const uuid_t *feat_uuid,
>>  
>>  resource_size_t cxl_rcd_component_reg_phys(struct device *dev,
>>  					   struct cxl_dport *dport);
>> +
>> +#ifdef CONFIG_CXL_PROTO_AER_EINJ
>> +
>> +#define AER_REGISTER_SIZE 5
>> +#define RAS_REGISTER_SIZE (CXL_RAS_CAPABILITY_LENGTH / sizeof(u32))
>> +
>> +struct cxl_aer_einj {
>> +	int correctable;
>> +	bool is_rch;
>> +	struct mutex *lock;
>> +	struct device *dev;
>> +	u32 aer_registers[AER_REGISTER_SIZE];
>> +	u32 ras_registers[RAS_REGISTER_SIZE];
>> +};
>> +
>> +extern struct cxl_aer_einj cxl_aer_einj;
>> +#endif
>> +
>>  #endif /* __CXL_CORE_H__ */
>> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
>> index a76f3ee05cba8..79657e5fddaac 100644
>> --- a/drivers/cxl/core/port.c
>> +++ b/drivers/cxl/core/port.c
>> @@ -2501,7 +2501,7 @@ const struct bus_type cxl_bus_type = {
>>  };
>>  EXPORT_SYMBOL_NS_GPL(cxl_bus_type, "CXL");
>>  
>> -static struct dentry *cxl_debugfs;
>> +struct dentry *cxl_debugfs;
>>  
>>  struct dentry *cxl_debugfs_create_dir(const char *dir)
>>  {
>> diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c
>> index d77208af41e03..d41deea899d30 100644
>> --- a/drivers/cxl/core/ras.c
>> +++ b/drivers/cxl/core/ras.c
>> @@ -3,6 +3,7 @@
>>  
>>  #include <linux/pci.h>
>>  #include <linux/aer.h>
>> +#include <linux/debugfs.h>
>>  #include <cxl/event.h>
>>  #include <cxlmem.h>
>>  #include <cxlpci.h>
>> @@ -117,6 +118,195 @@ static void cxl_cper_prot_err_work_fn(struct work_struct *work)
>>  }
>>  static DECLARE_WORK(cxl_cper_prot_err_work, cxl_cper_prot_err_work_fn);
>>  
>> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
> 
> Can this be moved to a header file? Prefer to not introduce #ifdef in the C source.
> 

Yes, I will move to core.h. 

>> +
>> +static DEFINE_MUTEX(cxl_aer_einj_mutex);
>> +
>> +struct cxl_aer_einj cxl_aer_einj = {
>> +	.lock = &cxl_aer_einj_mutex,
>> +};
>> +
>> +static const char cxl_aer_einj_usage[] =
>> +	"ssss:bb:dd.f [UCE|CE] AER_STATUS RAS_STATUS [RCH]\n";
>> +
>> +static int cxl_aer_inject_error(struct pci_dev *pdev, bool correctable,
>> +				u32 aer_status, u32 ras_status)
>> +{
>> +	/* RCD errors are signaled as internal errors on the associated RCEC */
>> +	if (pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_END) {
>> +		if (!pdev->rcec)
>> +			return -ENODEV;
>> +		pdev = pdev->rcec;
>> +	}
>> +
>> +	struct aer_error_inj einj = {
>> +		.bus = pdev->bus->number,
>> +		.dev = PCI_SLOT(pdev->devfn),
>> +		.fn = PCI_FUNC(pdev->devfn),
>> +		.domain = pci_domain_nr(pdev->bus),
>> +	};
>> +	int ret;
>> +	int aer_offset;
>> +	int ras_offset;
>> +
>> +	if (correctable) {
>> +		einj.cor_status = aer_status | PCI_ERR_COR_INTERNAL;
>> +		aer_offset = PCI_ERR_COR_STATUS / sizeof(u32);
>> +		ras_offset = CXL_RAS_CORRECTABLE_STATUS_OFFSET / sizeof(u32);
>> +	} else {
>> +		einj.uncor_status = aer_status | PCI_ERR_UNC_INTN;
>> +		aer_offset = PCI_ERR_UNCOR_STATUS / sizeof(u32);
>> +		ras_offset = CXL_RAS_UNCORRECTABLE_STATUS_OFFSET / sizeof(u32);
>> +	}
>> +
>> +	cxl_aer_einj.correctable = correctable;
>> +	cxl_aer_einj.aer_registers[aer_offset] = aer_status;
>> +	cxl_aer_einj.ras_registers[ras_offset] = ras_status;
>> +
>> +	ret = aer_inject(&einj);
>> +	if (ret) {
>> +		pr_err("cxl-einj: aer_inject failed: %d\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static ssize_t cxl_aer_einj_write(struct file *file,
>> +				    const char __user *ubuf,
>> +				    size_t count, loff_t *ppos)
>> +{
>> +	char sbdf[16], severity[4], topology[4] = "";
>> +	unsigned int domain, bus, dev, fn;
>> +	u32 aer_status, ras_status;
>> +	struct cxl_dport *dport;
>> +	char buf[128];
>> +	int nargs;
>> +	int ret;
>> +
>> +	if (!capable(CAP_SYS_ADMIN))
>> +		return -EPERM;
>> +
>> +	if (count >= sizeof(buf)) {
>> +		pr_err("cxl-einj: input too long (%zu bytes, max %zu)\n", count, sizeof(buf) - 1);
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (copy_from_user(buf, ubuf, count)) {
>> +		pr_err("cxl-einj: copy_from_user failed\n");
>> +		return -EFAULT;
>> +	}
>> +	buf[count] = '\0';
>> +
>> +	nargs = sscanf(buf, "%15s %3s %x %x %3s", sbdf, severity,
>> +		       &aer_status, &ras_status, topology);
>> +	if (nargs < 4) {
>> +		pr_err("cxl-einj: expected format: <SBDF> <UCE|CE> <aer_status> <ras_status>\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (nargs == 5 && strcmp(topology, "RCH") != 0)
>> +		return -EINVAL;
>> +
>> +	if (strcmp(severity, "UCE") != 0 && strcmp(severity, "CE") != 0) {
>> +		pr_err("cxl-einj: expected 'UCE' or 'CE', got '%s'\n", severity);
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (sscanf(sbdf, "%x:%x:%x.%x", &domain, &bus, &dev, &fn) != 4) {
>> +		pr_err("cxl-einj: invalid SBDF format '%s', expected DDDD:BB:DD.F\n", sbdf);
>> +		return -EINVAL;
>> +	}
>> +
>> +	struct pci_dev *pdev __free(pci_dev_put) =
>> +		pci_get_domain_bus_and_slot(domain, bus, PCI_DEVFN(dev, fn));
>> +	if (!pdev) {
>> +		pr_err("cxl-einj: device %s not found\n", sbdf);
>> +		return -ENODEV;
>> +	}
>> +
>> +	guard(mutex)(cxl_aer_einj.lock);
>> +	cxl_aer_einj.dev = NULL;
>> +
>> +	struct cxl_port *port __free(put_cxl_port) = find_cxl_port_by_dev(&pdev->dev, &dport);
>> +	if (!port) {
>> +		dev_err(&pdev->dev, "cxl-einj: Failed to find CXL Port.\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	if (!to_ras_base(port, dport)) {
>> +		dev_err(&pdev->dev, "cxl-einj: RAS not initialized.\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	cxl_aer_einj.is_rch = (nargs == 5 && strcmp(topology, "RCH") == 0);
>> +	if (!cxl_aer_einj.is_rch)
>> +		pci_dev_get(pdev);
>> +	cxl_aer_einj.dev = cxl_aer_einj.is_rch ? pdev->dev.parent : &pdev->dev;
>> +	ret = cxl_aer_inject_error(pdev, strcmp(severity, "CE") == 0,
>> +				   aer_status, ras_status);
>> +	if (ret) {
>> +		if (!cxl_aer_einj.is_rch)
>> +			pci_dev_put(pdev);
>> +		cxl_aer_einj.dev = NULL;
>> +		pr_err("cxl-einj: injection failed for %s: %d\n", sbdf, ret);
>> +		return ret;
>> +	}
>> +
>> +	return count;
>> +}
>> +
>> +static ssize_t cxl_aer_einj_read(struct file *file, char __user *ubuf,
>> +				 size_t count, loff_t *ppos)
>> +{
>> +	return simple_read_from_buffer(ubuf, count, ppos,
>> +				       cxl_aer_einj_usage,
>> +				       sizeof(cxl_aer_einj_usage) - 1);
>> +}
>> +
>> +static const struct file_operations cxl_ras_error_fops = {
>> +	.owner		= THIS_MODULE,
>> +	.read		= cxl_aer_einj_read,
>> +	.write		= cxl_aer_einj_write,
>> +	.llseek		= default_llseek,
>> +};
>> +
>> +static void cxl_ras_create_debugfs(struct dentry *dir)
>> +{
>> +	debugfs_create_file("aer_einj_inject", 0600, dir, NULL,
>> +			    &cxl_ras_error_fops);
>> +}
>> +
>> +static void __iomem *to_einj_ras_base(struct cxl_port *port, struct cxl_dport *dport)
>> +{
>> +	if (dport) {
>> +		if (cxl_aer_einj.is_rch) {
>> +			if (cxl_aer_einj.dev == dport->dport_dev) {
>> +				cxl_aer_einj.dev = NULL;
>> +				return (__force void __iomem *)cxl_aer_einj.ras_registers;
>> +			}
>> +		} else {
>> +			if (cxl_aer_einj.dev == dport->dport_dev) {
>> +				pci_dev_put(to_pci_dev(cxl_aer_einj.dev));
>> +				cxl_aer_einj.dev = NULL;
>> +				return (__force void __iomem *)cxl_aer_einj.ras_registers;
>> +			}
>> +		}
>> +	} else if (!cxl_aer_einj.is_rch) {
>> +		struct device *dev = is_cxl_endpoint(port) ?
>> +			port->uport_dev->parent : port->uport_dev;
>> +
>> +		if (dev_is_pci(dev) && cxl_aer_einj.dev == dev) {
>> +			pci_dev_put(to_pci_dev(cxl_aer_einj.dev));
>> +			cxl_aer_einj.dev = NULL;
>> +			return (__force void __iomem *)cxl_aer_einj.ras_registers;
>> +		}
>> +	}
>> +
>> +	return NULL;
>> +}
>> +#endif
>> +
>>  static void cxl_unmask_proto_interrupts(struct device *dev)
>>  {
>>  	struct pci_dev *pdev;
>> @@ -238,6 +428,14 @@ void __iomem *to_ras_base(struct cxl_port *port, struct cxl_dport *dport)
>>  	if (!port)
>>  		return NULL;
>>  
>> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
> 
> I think you can do something like below instead:
> 
> 	if (IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ))
> 
> DJ
> 

Good idea. Thanks for pointing this out.

Terry

[snip]


^ permalink raw reply

* [PATCH 2/2] selftests: ntsync: test absolute MONOTONIC waits under time namespaces
From: Iván Ezequiel Rodriguez @ 2026-07-20 17:54 UTC (permalink / raw)
  To: Elizabeth Figura
  Cc: Jonathan Corbet, linux-doc, Shuah Khan, linux-kselftest,
	linux-kernel, Iván Ezequiel Rodriguez, Maoyi Xie
In-Reply-To: <20260720175402.44338-1-ivanrwcm25@gmail.com>

Cover the timens conversion in ntsync_schedule(): with a negative
CLOCK_MONOTONIC offset, a 100 ms absolute wait must not return
immediately against the host clock.

Suggested-by: Maoyi Xie <maoyixie.tju@gmail.com>
Signed-off-by: Iván Ezequiel Rodriguez <ivanrwcm25@gmail.com>
---
 .../testing/selftests/drivers/ntsync/ntsync.c | 87 +++++++++++++++++++
 1 file changed, 87 insertions(+)

diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
index e6a37214aa46..ce000ff41535 100644
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
@@ -8,12 +8,18 @@
 #define _GNU_SOURCE
 #include <sys/ioctl.h>
 #include <sys/stat.h>
+#include <sys/wait.h>
 #include <fcntl.h>
+#include <sched.h>
 #include <time.h>
 #include <pthread.h>
 #include <linux/ntsync.h>
 #include "kselftest_harness.h"
 
+#ifndef CLONE_NEWTIME
+#define CLONE_NEWTIME 0x00000080
+#endif
+
 static int read_sem_state(int sem, __u32 *count, __u32 *max)
 {
 	struct ntsync_sem_args args;
@@ -1340,4 +1346,85 @@ TEST(stress_wait)
 	close(stress_device);
 }
 
+/*
+ * Absolute MONOTONIC timeouts must honour the caller's time namespace.
+ * With a negative monotonic offset, a 100 ms wait must still take ~100 ms
+ * of namespace time (not return immediately against the host clock).
+ */
+TEST(wait_any_monotonic_timens)
+{
+	struct ntsync_sem_args sem_args = {0};
+	struct ntsync_wait_args wait_args = {0};
+	struct timespec start, end;
+	char buf[64];
+	__u64 elapsed_ns;
+	int fd, offset_fd, sem, ret, status, len;
+	pid_t pid;
+
+	if (access("/proc/self/ns/time", F_OK))
+		SKIP(return, "Time namespaces are not supported");
+
+	fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
+	if (fd < 0)
+		SKIP(return, "/dev/ntsync is not available");
+
+	ret = unshare(CLONE_NEWTIME);
+	if (ret) {
+		close(fd);
+		if (errno == EPERM)
+			SKIP(return, "need CAP_SYS_ADMIN for CLONE_NEWTIME");
+		ASSERT_EQ(0, ret);
+	}
+
+	len = snprintf(buf, sizeof(buf), "%d %d 0", CLOCK_MONOTONIC, -10);
+	offset_fd = open("/proc/self/timens_offsets", O_WRONLY);
+	ASSERT_LE(0, offset_fd);
+	ASSERT_EQ(len, write(offset_fd, buf, len));
+	close(offset_fd);
+
+	pid = fork();
+	ASSERT_LE(0, pid);
+	if (!pid) {
+		int obj;
+
+		sem_args.count = 0;
+		sem_args.max = 1;
+		sem = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
+		if (sem < 0)
+			_exit(1);
+
+		obj = sem;
+		wait_args.timeout = get_abs_timeout(100);
+		wait_args.objs = (uintptr_t)&obj;
+		wait_args.count = 1;
+		wait_args.owner = 123;
+		wait_args.index = 0xdeadbeef;
+
+		if (clock_gettime(CLOCK_MONOTONIC, &start))
+			_exit(2);
+		ret = ioctl(fd, NTSYNC_IOC_WAIT_ANY, &wait_args);
+		if (clock_gettime(CLOCK_MONOTONIC, &end))
+			_exit(2);
+
+		if (ret != -1 || errno != ETIMEDOUT)
+			_exit(3);
+
+		elapsed_ns = (end.tv_sec - start.tv_sec) * 1000000000ULL +
+			     (end.tv_nsec - start.tv_nsec);
+		/* Without timens conversion this returns in ~0 ms. */
+		if (elapsed_ns < 50 * 1000000ULL)
+			_exit(4);
+		if (elapsed_ns > 1000 * 1000000ULL)
+			_exit(5);
+
+		_exit(0);
+	}
+
+	ASSERT_EQ(pid, waitpid(pid, &status, 0));
+	EXPECT_TRUE(WIFEXITED(status));
+	EXPECT_EQ(0, WEXITSTATUS(status));
+
+	close(fd);
+}
+
 TEST_HARNESS_MAIN
-- 
2.43.0


^ permalink raw reply related

* [PATCH 1/2] docs: ntsync: align ioctl names and struct layouts with uapi
From: Iván Ezequiel Rodriguez @ 2026-07-20 17:54 UTC (permalink / raw)
  To: Elizabeth Figura
  Cc: Jonathan Corbet, linux-doc, Shuah Khan, linux-kselftest,
	linux-kernel, Iván Ezequiel Rodriguez
In-Reply-To: <20260720175402.44338-1-ivanrwcm25@gmail.com>

The uAPI documentation used outdated or conceptual ioctl macro names
and listed ntsync_event_args / ntsync_wait_args fields in the wrong
order. Match include/uapi/linux/ntsync.h and note that absolute
MONOTONIC timeouts are interpreted in the caller's time namespace.

Signed-off-by: Iván Ezequiel Rodriguez <ivanrwcm25@gmail.com>
---
 Documentation/userspace-api/ntsync.rst | 33 +++++++++++++-------------
 1 file changed, 17 insertions(+), 16 deletions(-)

diff --git a/Documentation/userspace-api/ntsync.rst b/Documentation/userspace-api/ntsync.rst
index 25e7c4aef968..a778016feb87 100644
--- a/Documentation/userspace-api/ntsync.rst
+++ b/Documentation/userspace-api/ntsync.rst
@@ -83,18 +83,18 @@ structures used in ioctl calls::
    };
 
    struct ntsync_event_args {
-   	__u32 signaled;
    	__u32 manual;
+   	__u32 signaled;
    };
 
    struct ntsync_wait_args {
    	__u64 timeout;
    	__u64 objs;
    	__u32 count;
-   	__u32 owner;
    	__u32 index;
-   	__u32 alert;
    	__u32 flags;
+   	__u32 owner;
+   	__u32 alert;
    	__u32 pad;
    };
 
@@ -152,7 +152,7 @@ The ioctls on the device file are as follows:
 
 The ioctls on the individual objects are as follows:
 
-.. c:macro:: NTSYNC_IOC_SEM_POST
+.. c:macro:: NTSYNC_IOC_SEM_RELEASE
 
   Post to a semaphore object. Takes a pointer to a 32-bit integer,
   which on input holds the count to be added to the semaphore, and on
@@ -186,7 +186,7 @@ The ioctls on the individual objects are as follows:
   unowned and signaled, and eligible threads waiting on it will be
   woken as appropriate.
 
-.. c:macro:: NTSYNC_IOC_SET_EVENT
+.. c:macro:: NTSYNC_IOC_EVENT_SET
 
   Signal an event object. Takes a pointer to a 32-bit integer, which on
   output contains the previous state of the event.
@@ -194,12 +194,12 @@ The ioctls on the individual objects are as follows:
   Eligible threads will be woken, and auto-reset events will be
   designaled appropriately.
 
-.. c:macro:: NTSYNC_IOC_RESET_EVENT
+.. c:macro:: NTSYNC_IOC_EVENT_RESET
 
   Designal an event object. Takes a pointer to a 32-bit integer, which
   on output contains the previous state of the event.
 
-.. c:macro:: NTSYNC_IOC_PULSE_EVENT
+.. c:macro:: NTSYNC_IOC_EVENT_PULSE
 
   Wake threads waiting on an event object while leaving it in an
   unsignaled state. Takes a pointer to a 32-bit integer, which on
@@ -213,7 +213,7 @@ The ioctls on the individual objects are as follows:
   afterwards, and a simultaneous read operation will always report the
   event as unsignaled.
 
-.. c:macro:: NTSYNC_IOC_READ_SEM
+.. c:macro:: NTSYNC_IOC_SEM_READ
 
   Read the current state of a semaphore object. Takes a pointer to
   struct :c:type:`ntsync_sem_args`, which is used as follows:
@@ -225,7 +225,7 @@ The ioctls on the individual objects are as follows:
      * - ``max``
        - On output, contains the maximum count of the semaphore.
 
-.. c:macro:: NTSYNC_IOC_READ_MUTEX
+.. c:macro:: NTSYNC_IOC_MUTEX_READ
 
   Read the current state of a mutex object. Takes a pointer to struct
   :c:type:`ntsync_mutex_args`, which is used as follows:
@@ -242,7 +242,7 @@ The ioctls on the individual objects are as follows:
   ``EOWNERDEAD``. In this case, ``count`` and ``owner`` are set to
   zero.
 
-.. c:macro:: NTSYNC_IOC_READ_EVENT
+.. c:macro:: NTSYNC_IOC_EVENT_READ
 
   Read the current state of an event object. Takes a pointer to struct
   :c:type:`ntsync_event_args`, which is used as follows:
@@ -255,7 +255,7 @@ The ioctls on the individual objects are as follows:
        - On output, contains 1 if the event is a manual-reset event,
          and 0 otherwise.
 
-.. c:macro:: NTSYNC_IOC_KILL_OWNER
+.. c:macro:: NTSYNC_IOC_MUTEX_KILL
 
   Mark a mutex as unowned and abandoned if it is owned by the given
   owner. Takes an input-only pointer to a 32-bit integer denoting the
@@ -276,11 +276,12 @@ The ioctls on the individual objects are as follows:
      * - ``timeout``
        - Absolute timeout in nanoseconds. If ``NTSYNC_WAIT_REALTIME``
          is set, the timeout is measured against the REALTIME clock;
-         otherwise it is measured against the MONOTONIC clock. If the
-         timeout is equal to or earlier than the current time, the
-         function returns immediately without sleeping. If ``timeout``
-         is U64_MAX, the function will sleep until an object is
-         signaled, and will not fail with ``ETIMEDOUT``.
+         otherwise it is measured against the MONOTONIC clock in the
+         caller's time namespace. If the timeout is equal to or earlier
+         than the current time, the function returns immediately
+         without sleeping. If ``timeout`` is U64_MAX, the function will
+         sleep until an object is signaled, and will not fail with
+         ``ETIMEDOUT``.
      * - ``objs``
        - Pointer to an array of ``count`` file descriptors
          (specified as an integer so that the structure has the same
-- 
2.43.0


^ permalink raw reply related

* [PATCH 0/2] ntsync: docs uAPI fixup and timens selftest
From: Iván Ezequiel Rodriguez @ 2026-07-20 17:54 UTC (permalink / raw)
  To: Elizabeth Figura
  Cc: Jonathan Corbet, linux-doc, Shuah Khan, linux-kselftest,
	linux-kernel, Iván Ezequiel Rodriguez

Short series for ntsync:

1/2 docs: align ioctl macro names and struct field order in
   Documentation/userspace-api/ntsync.rst with include/uapi/linux/ntsync.h,
   and note that absolute MONOTONIC timeouts use the caller's time namespace.

2/2 selftests: add wait_any_monotonic_timens to lock in the timens
   conversion in ntsync_schedule() (negative CLOCK_MONOTONIC offset must
   not make a 100 ms absolute wait return immediately).

No driver code changes.


Iván Ezequiel Rodriguez (2):
  docs: ntsync: align ioctl names and struct layouts with uapi
  selftests: ntsync: test absolute MONOTONIC waits under time namespaces

 Documentation/userspace-api/ntsync.rst        | 33 +++----
 .../testing/selftests/drivers/ntsync/ntsync.c | 87 +++++++++++++++++++
 2 files changed, 104 insertions(+), 16 deletions(-)

-- 
2.43.0


^ permalink raw reply

* Re: [PATCH v2 02/15] drm/xlnx/zynqmp_dpsub: remove dependency on DRM simple helpers
From: Pandey, Radhey Shyam @ 2026-07-20 17:49 UTC (permalink / raw)
  To: Diogo Silva, Jingoo Han, Inki Dae, Seung-Woo Kim, Kyungmin Park,
	David Airlie, Simona Vetter, Krzysztof Kozlowski, Peter Griffin,
	Alim Akhtar, Laurent Pinchart, Tomi Valkeinen, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, Michal Simek, Thierry Reding,
	Mikko Perttunen, Jonathan Hunter, Stefan Agner, Alison Wang,
	Anitha Chrisanthus, David Airlie, Gerd Hoffmann, Dmitry Osipenko,
	Gurchetan Singh, Chia-I Wu, Jyri Sarha, Liu Ying, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	AngeloGioacchino Del Regno, Geert Uytterhoeven, Xinliang Liu,
	Sumit Semwal, Yongqin Liu, John Stultz, Liviu Dudau,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Jonathan Corbet, Shuah Khan
  Cc: dri-devel, linux-arm-kernel, linux-samsung-soc, linux-kernel,
	linux-tegra, virtualization, imx, linux-mediatek,
	linux-renesas-soc, linux-amlogic, linux-doc
In-Reply-To: <20260720-drm_simple_encoder_init-v2-2-5020b630668a@gmail.com>

On 7/20/2026 9:10 PM, Diogo Silva wrote:
> The simple KMS helpers are deprecated because they only add an
> intermediate layer between drivers and atomic modesetting.
> 
> Open-code drm_simple_encoder_init() by calling drm_encoder_init()
> directly and providing driver-local drm_encoder_funcs.
> Also check the return value from drm_encoder_init() to avoid silent
> failures.
> 
> Signed-off-by: Diogo Silva <diogompaissilva@gmail.com>

Reviewed-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
Thanks!
> ---
>   drivers/gpu/drm/xlnx/zynqmp_kms.c | 14 ++++++++++++--
>   1 file changed, 12 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.c b/drivers/gpu/drm/xlnx/zynqmp_kms.c
> index d5f922450565..ac9197e026af 100644
> --- a/drivers/gpu/drm/xlnx/zynqmp_kms.c
> +++ b/drivers/gpu/drm/xlnx/zynqmp_kms.c
> @@ -29,8 +29,8 @@
>   #include <drm/drm_managed.h>
>   #include <drm/drm_mode_config.h>
>   #include <drm/drm_plane.h>
> +#include <drm/drm_print.h>
>   #include <drm/drm_probe_helper.h>
> -#include <drm/drm_simple_kms_helper.h>
>   #include <drm/drm_vblank.h>
>   
>   #include <linux/clk.h>
> @@ -417,6 +417,10 @@ static const struct drm_driver zynqmp_dpsub_drm_driver = {
>   	.minor				= 0,
>   };
>   
> +static const struct drm_encoder_funcs zynqmp_dpsub_encoder_funcs = {
> +	.destroy = drm_encoder_cleanup,
> +};
> +
>   static int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub)
>   {
>   	struct drm_encoder *encoder = &dpsub->drm->encoder;
> @@ -436,7 +440,13 @@ static int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub)
>   
>   	/* Create the encoder and attach the bridge. */
>   	encoder->possible_crtcs |= drm_crtc_mask(&dpsub->drm->crtc);
> -	drm_simple_encoder_init(&dpsub->drm->dev, encoder, DRM_MODE_ENCODER_NONE);
> +	ret = drm_encoder_init(&dpsub->drm->dev, encoder,
> +			       &zynqmp_dpsub_encoder_funcs,
> +			       DRM_MODE_ENCODER_NONE, NULL);
> +	if (ret) {
> +		drm_err(&dpsub->drm->dev, "failed to initialize encoder\n");
> +		return ret;
> +	}
>   
>   	ret = drm_bridge_attach(encoder, dpsub->bridge, NULL,
>   				DRM_BRIDGE_ATTACH_NO_CONNECTOR);
> 


^ permalink raw reply

* Re: [PATCH] fscrypt: Update encryption policy version docs
From: Eric Biggers @ 2026-07-20 17:47 UTC (permalink / raw)
  To: linux-fscrypt; +Cc: linux-kernel, linux-doc
In-Reply-To: <20260718212526.53943-1-ebiggers@kernel.org>

On Sat, Jul 18, 2026 at 02:25:26PM -0700, Eric Biggers wrote:
> Update the wording of the documentation to put v1 encryption policies a
> bit more firmly in the past, explicitly calling them "deprecated" (which
> is consistent with the warning message the kernel has printed ever since
> v5.4).  Do the same for FS_IOC_GET_ENCRYPTION_POLICY which supports only
> v1 policies, and remove the explicit recommendation to fall back to it.
> 
> Also clarify that reusing master keys across policies isn't the best
> practice or normal usage, even though it's technically allowed for v2.
> 
> Signed-off-by: Eric Biggers <ebiggers@kernel.org>
> ---
> 
> This patch is targeting the fscrypt tree

Applied to https://git.kernel.org/pub/scm/fs/fscrypt/linux.git/log/?h=for-next

- Eric

^ permalink raw reply

* Re: [PATCH 0/6] More fscrypt and blk-crypto doc updates and code removals
From: Eric Biggers @ 2026-07-20 17:43 UTC (permalink / raw)
  To: linux-fscrypt
  Cc: linux-block, linux-fsdevel, linux-ext4, linux-f2fs-devel,
	linux-doc
In-Reply-To: <20260718214655.63186-1-ebiggers@kernel.org>

On Sat, Jul 18, 2026 at 02:46:49PM -0700, Eric Biggers wrote:
> This series applies on top of
> "[PATCH v3 00/17] fscrypt: Standardize on blk-crypto"
> (https://lore.kernel.org/linux-fscrypt/20260713023708.9245-1-ebiggers@kernel.org/).
> It gets more documentation up to date, and it
> removes a couple more functions that are no longer used.
> I'm planning to take both series via the fscrypt tree.
> 
> Eric Biggers (6):
>   fs: Update outdated comment for SB_INLINECRYPT
>   f2fs: Update outdated comment in f2fs_write_begin()
>   fscrypt: Remove unused function fscrypt_finalize_bounce_page()
>   fscrypt: Update docs for data path
>   blk-crypto: Remove unused function blk_crypto_config_supported()
>   blk-crypto: Update docs for blk-crypto-fallback motivation

Applied to https://git.kernel.org/pub/scm/fs/fscrypt/linux.git/log/?h=for-next

- Eric

^ permalink raw reply

* Re: [RFC] cxl: Device protocol AER injection
From: Dave Jiang @ 2026-07-20 17:38 UTC (permalink / raw)
  To: Terry Bowman, Bjorn Helgaas, Dan Williams, Ira Weiny,
	Jonathan Cameron, Len Brown, Rafael J . Wysocki, Robert Richter
  Cc: linux-acpi, linux-cxl, linux-doc, linux-kernel, linux-pci,
	linuxppc-dev, Alejandro Lucero, Alison Schofield, Ankit Agrawal,
	Ard Biesheuvel, Ben Cheatham, Borislav Petkov, Breno Leitao,
	Davidlohr Bueso, Fabio M . De Francesco, Gregory Price,
	Hanjun Guo, Jonathan Corbet, Kees Cook,
	Kuppuswamy Sathyanarayanan, Li Ming, Mahesh J Salgaonkar,
	Mauro Carvalho Chehab, Oliver O'Halloran, Shiju Jose,
	Shuah Khan, Shuai Xue, Smita Koralahalli, Tony Luck, Vishal Verma
In-Reply-To: <20260717225700.3543801-1-terry.bowman@amd.com>



On 7/17/26 3:57 PM, Terry Bowman wrote:
> This patch is intended to provide a method of testing the recently submitted
> cxl series "cxl: Enable CXL PCIe Port Protocol Error handling and logging" found
> here:
> 
> https://lore.kernel.org/linux-cxl/20260717222706.3540281-1-terry.bowman@amd.com/T/#md90ec1fdd1b374bf1e32e7736e2b3e34b328c701
> 
> The changes in this patch will allow CXL RAS protocol testing by injecting
> AER errors using AER EINJ. The RAS register block status is updated
> using a central function to augment RAS register block returned by
> to_ras_base(). This supports all CXL devices including Root Ports,
> Upstream Switch Ports, Downstream Switch Ports, Endpoints, and RCH
> Downstream Ports.
> 
> Add debugfs-based CXL protocol error injection for testing CXL RAS
> error handling paths. Injects CXL RAS protocol errors using AER internal
> error inject interface via /sys/kernel/debug/cxl/aer_einj_inject.
> 
> RAS CXL status is set using to_ras_base() function override when kernel config
> CONFIG_CXL_PROTO_AER_EINJ is enabled.
> 
> Usage:
>   echo "DDDD:BB:DD.F [UCE|CE] AER_STATUS RAS_STATUS [RCH]" > \
>       /sys/kernel/debug/cxl/aer_einj_inject
> 
> Move struct aer_error_inj and aer_inject() to linux/aer.h so CXL
> can invoke AER injection directly. Export aer_inject() with
> EXPORT_SYMBOL_GPL.
> 
> Make cxl_debugfs non-static in port.c and declare it extern in
> core.h so the debugfs file can be created under the existing CXL
> debugfs root.
> 
> Co-developed-by: Ben Cheatham <Benjamin.Cheatham@amd.com>
> Signed-off-by: Ben Cheatham <Benjamin.Cheatham@amd.com>
> Signed-off-by: Terry Bowman <terry.bowman@amd.com>
> ---
>  drivers/cxl/Kconfig           |  13 +++
>  drivers/cxl/core/core.h       |  21 ++++
>  drivers/cxl/core/port.c       |   2 +-
>  drivers/cxl/core/ras.c        | 208 ++++++++++++++++++++++++++++++++++
>  drivers/cxl/core/ras_rch.c    |  12 ++
>  drivers/pci/pcie/aer_inject.c |  29 ++---
>  include/linux/aer.h           |  15 +++
>  7 files changed, 281 insertions(+), 19 deletions(-)
> 
> diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig
> index 80aeb0d556bd7..ef449228b2549 100644
> --- a/drivers/cxl/Kconfig
> +++ b/drivers/cxl/Kconfig
> @@ -238,6 +238,19 @@ config CXL_RAS
>  	def_bool y
>  	depends on ACPI_APEI_GHES && PCIEAER && CXL_BUS
>  
> +config CXL_PROTO_AER_EINJ
> +	bool "CXL: RAS Protocol Error Injection using AER EINJ"
> +	depends on CXL_RAS
> +	depends on PCIEAER_INJECT
> +	help
> +	  Enable debugfs-based CXL protocol error injection. Writes to
> +	  /sys/kernel/debug/cxl/aer_einj_inject inject CXL RAS protocol
> +	  errors using the AER internal error inject interface.
> +
> +	  This is a debug/test facility. Say N for production kernels.
> +
> +	  If unsure say N.
> +
>  config CXL_ATL
>  	def_bool y
>  	depends on CXL_REGION
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index a55a4e409feda..91910d2bb5d39 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -182,6 +182,9 @@ static inline struct device *dport_to_host(struct cxl_dport *dport)
>  		return port->uport_dev;
>  	return &port->dev;
>  }
> +
> +extern struct dentry *cxl_debugfs;
> +
>  #ifdef CONFIG_CXL_RAS
>  void cxl_ras_init(void);
>  void cxl_ras_exit(void);
> @@ -244,4 +247,22 @@ int cxl_set_feature(struct cxl_mailbox *cxl_mbox, const uuid_t *feat_uuid,
>  
>  resource_size_t cxl_rcd_component_reg_phys(struct device *dev,
>  					   struct cxl_dport *dport);
> +
> +#ifdef CONFIG_CXL_PROTO_AER_EINJ
> +
> +#define AER_REGISTER_SIZE 5
> +#define RAS_REGISTER_SIZE (CXL_RAS_CAPABILITY_LENGTH / sizeof(u32))
> +
> +struct cxl_aer_einj {
> +	int correctable;
> +	bool is_rch;
> +	struct mutex *lock;
> +	struct device *dev;
> +	u32 aer_registers[AER_REGISTER_SIZE];
> +	u32 ras_registers[RAS_REGISTER_SIZE];
> +};
> +
> +extern struct cxl_aer_einj cxl_aer_einj;
> +#endif
> +
>  #endif /* __CXL_CORE_H__ */
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index a76f3ee05cba8..79657e5fddaac 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -2501,7 +2501,7 @@ const struct bus_type cxl_bus_type = {
>  };
>  EXPORT_SYMBOL_NS_GPL(cxl_bus_type, "CXL");
>  
> -static struct dentry *cxl_debugfs;
> +struct dentry *cxl_debugfs;
>  
>  struct dentry *cxl_debugfs_create_dir(const char *dir)
>  {
> diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c
> index d77208af41e03..d41deea899d30 100644
> --- a/drivers/cxl/core/ras.c
> +++ b/drivers/cxl/core/ras.c
> @@ -3,6 +3,7 @@
>  
>  #include <linux/pci.h>
>  #include <linux/aer.h>
> +#include <linux/debugfs.h>
>  #include <cxl/event.h>
>  #include <cxlmem.h>
>  #include <cxlpci.h>
> @@ -117,6 +118,195 @@ static void cxl_cper_prot_err_work_fn(struct work_struct *work)
>  }
>  static DECLARE_WORK(cxl_cper_prot_err_work, cxl_cper_prot_err_work_fn);
>  
> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)

Can this be moved to a header file? Prefer to not introduce #ifdef in the C source.

> +
> +static DEFINE_MUTEX(cxl_aer_einj_mutex);
> +
> +struct cxl_aer_einj cxl_aer_einj = {
> +	.lock = &cxl_aer_einj_mutex,
> +};
> +
> +static const char cxl_aer_einj_usage[] =
> +	"ssss:bb:dd.f [UCE|CE] AER_STATUS RAS_STATUS [RCH]\n";
> +
> +static int cxl_aer_inject_error(struct pci_dev *pdev, bool correctable,
> +				u32 aer_status, u32 ras_status)
> +{
> +	/* RCD errors are signaled as internal errors on the associated RCEC */
> +	if (pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_END) {
> +		if (!pdev->rcec)
> +			return -ENODEV;
> +		pdev = pdev->rcec;
> +	}
> +
> +	struct aer_error_inj einj = {
> +		.bus = pdev->bus->number,
> +		.dev = PCI_SLOT(pdev->devfn),
> +		.fn = PCI_FUNC(pdev->devfn),
> +		.domain = pci_domain_nr(pdev->bus),
> +	};
> +	int ret;
> +	int aer_offset;
> +	int ras_offset;
> +
> +	if (correctable) {
> +		einj.cor_status = aer_status | PCI_ERR_COR_INTERNAL;
> +		aer_offset = PCI_ERR_COR_STATUS / sizeof(u32);
> +		ras_offset = CXL_RAS_CORRECTABLE_STATUS_OFFSET / sizeof(u32);
> +	} else {
> +		einj.uncor_status = aer_status | PCI_ERR_UNC_INTN;
> +		aer_offset = PCI_ERR_UNCOR_STATUS / sizeof(u32);
> +		ras_offset = CXL_RAS_UNCORRECTABLE_STATUS_OFFSET / sizeof(u32);
> +	}
> +
> +	cxl_aer_einj.correctable = correctable;
> +	cxl_aer_einj.aer_registers[aer_offset] = aer_status;
> +	cxl_aer_einj.ras_registers[ras_offset] = ras_status;
> +
> +	ret = aer_inject(&einj);
> +	if (ret) {
> +		pr_err("cxl-einj: aer_inject failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static ssize_t cxl_aer_einj_write(struct file *file,
> +				    const char __user *ubuf,
> +				    size_t count, loff_t *ppos)
> +{
> +	char sbdf[16], severity[4], topology[4] = "";
> +	unsigned int domain, bus, dev, fn;
> +	u32 aer_status, ras_status;
> +	struct cxl_dport *dport;
> +	char buf[128];
> +	int nargs;
> +	int ret;
> +
> +	if (!capable(CAP_SYS_ADMIN))
> +		return -EPERM;
> +
> +	if (count >= sizeof(buf)) {
> +		pr_err("cxl-einj: input too long (%zu bytes, max %zu)\n", count, sizeof(buf) - 1);
> +		return -EINVAL;
> +	}
> +
> +	if (copy_from_user(buf, ubuf, count)) {
> +		pr_err("cxl-einj: copy_from_user failed\n");
> +		return -EFAULT;
> +	}
> +	buf[count] = '\0';
> +
> +	nargs = sscanf(buf, "%15s %3s %x %x %3s", sbdf, severity,
> +		       &aer_status, &ras_status, topology);
> +	if (nargs < 4) {
> +		pr_err("cxl-einj: expected format: <SBDF> <UCE|CE> <aer_status> <ras_status>\n");
> +		return -EINVAL;
> +	}
> +
> +	if (nargs == 5 && strcmp(topology, "RCH") != 0)
> +		return -EINVAL;
> +
> +	if (strcmp(severity, "UCE") != 0 && strcmp(severity, "CE") != 0) {
> +		pr_err("cxl-einj: expected 'UCE' or 'CE', got '%s'\n", severity);
> +		return -EINVAL;
> +	}
> +
> +	if (sscanf(sbdf, "%x:%x:%x.%x", &domain, &bus, &dev, &fn) != 4) {
> +		pr_err("cxl-einj: invalid SBDF format '%s', expected DDDD:BB:DD.F\n", sbdf);
> +		return -EINVAL;
> +	}
> +
> +	struct pci_dev *pdev __free(pci_dev_put) =
> +		pci_get_domain_bus_and_slot(domain, bus, PCI_DEVFN(dev, fn));
> +	if (!pdev) {
> +		pr_err("cxl-einj: device %s not found\n", sbdf);
> +		return -ENODEV;
> +	}
> +
> +	guard(mutex)(cxl_aer_einj.lock);
> +	cxl_aer_einj.dev = NULL;
> +
> +	struct cxl_port *port __free(put_cxl_port) = find_cxl_port_by_dev(&pdev->dev, &dport);
> +	if (!port) {
> +		dev_err(&pdev->dev, "cxl-einj: Failed to find CXL Port.\n");
> +		return -ENODEV;
> +	}
> +
> +	if (!to_ras_base(port, dport)) {
> +		dev_err(&pdev->dev, "cxl-einj: RAS not initialized.\n");
> +		return -ENODEV;
> +	}
> +
> +	cxl_aer_einj.is_rch = (nargs == 5 && strcmp(topology, "RCH") == 0);
> +	if (!cxl_aer_einj.is_rch)
> +		pci_dev_get(pdev);
> +	cxl_aer_einj.dev = cxl_aer_einj.is_rch ? pdev->dev.parent : &pdev->dev;
> +	ret = cxl_aer_inject_error(pdev, strcmp(severity, "CE") == 0,
> +				   aer_status, ras_status);
> +	if (ret) {
> +		if (!cxl_aer_einj.is_rch)
> +			pci_dev_put(pdev);
> +		cxl_aer_einj.dev = NULL;
> +		pr_err("cxl-einj: injection failed for %s: %d\n", sbdf, ret);
> +		return ret;
> +	}
> +
> +	return count;
> +}
> +
> +static ssize_t cxl_aer_einj_read(struct file *file, char __user *ubuf,
> +				 size_t count, loff_t *ppos)
> +{
> +	return simple_read_from_buffer(ubuf, count, ppos,
> +				       cxl_aer_einj_usage,
> +				       sizeof(cxl_aer_einj_usage) - 1);
> +}
> +
> +static const struct file_operations cxl_ras_error_fops = {
> +	.owner		= THIS_MODULE,
> +	.read		= cxl_aer_einj_read,
> +	.write		= cxl_aer_einj_write,
> +	.llseek		= default_llseek,
> +};
> +
> +static void cxl_ras_create_debugfs(struct dentry *dir)
> +{
> +	debugfs_create_file("aer_einj_inject", 0600, dir, NULL,
> +			    &cxl_ras_error_fops);
> +}
> +
> +static void __iomem *to_einj_ras_base(struct cxl_port *port, struct cxl_dport *dport)
> +{
> +	if (dport) {
> +		if (cxl_aer_einj.is_rch) {
> +			if (cxl_aer_einj.dev == dport->dport_dev) {
> +				cxl_aer_einj.dev = NULL;
> +				return (__force void __iomem *)cxl_aer_einj.ras_registers;
> +			}
> +		} else {
> +			if (cxl_aer_einj.dev == dport->dport_dev) {
> +				pci_dev_put(to_pci_dev(cxl_aer_einj.dev));
> +				cxl_aer_einj.dev = NULL;
> +				return (__force void __iomem *)cxl_aer_einj.ras_registers;
> +			}
> +		}
> +	} else if (!cxl_aer_einj.is_rch) {
> +		struct device *dev = is_cxl_endpoint(port) ?
> +			port->uport_dev->parent : port->uport_dev;
> +
> +		if (dev_is_pci(dev) && cxl_aer_einj.dev == dev) {
> +			pci_dev_put(to_pci_dev(cxl_aer_einj.dev));
> +			cxl_aer_einj.dev = NULL;
> +			return (__force void __iomem *)cxl_aer_einj.ras_registers;
> +		}
> +	}
> +
> +	return NULL;
> +}
> +#endif
> +
>  static void cxl_unmask_proto_interrupts(struct device *dev)
>  {
>  	struct pci_dev *pdev;
> @@ -238,6 +428,14 @@ void __iomem *to_ras_base(struct cxl_port *port, struct cxl_dport *dport)
>  	if (!port)
>  		return NULL;
>  
> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)

I think you can do something like below instead:

	if (IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ))

DJ

 > +	if (cxl_aer_einj.dev) {
> +		void __iomem *einj = to_einj_ras_base(port, dport);
> +		if (einj)
> +			return einj;
> +	}
> +#endif
> +
>  	if (dport)
>  		return dport->regs.ras;
>  
> @@ -458,10 +656,20 @@ void cxl_ras_init(void)
>  	cxl_cper_register_prot_err_work(&cxl_cper_prot_err_work);
>  	cxl_register_proto_err_work(&cxl_proto_err_work,
>  				   cxl_proto_err_do_flush);
> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
> +	cxl_ras_create_debugfs(cxl_debugfs);
> +#endif
>  }
>  
>  void cxl_ras_exit(void)
>  {
>  	cxl_unregister_proto_err_work();
>  	cxl_cper_unregister_prot_err_work();
> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
> +	if (cxl_aer_einj.dev) {
> +		if (!cxl_aer_einj.is_rch)
> +			pci_dev_put(to_pci_dev(cxl_aer_einj.dev));
> +		cxl_aer_einj.dev = NULL;
> +	}
> +#endif
>  }
> diff --git a/drivers/cxl/core/ras_rch.c b/drivers/cxl/core/ras_rch.c
> index 14bb3bdb2d092..5071cf86e4a68 100644
> --- a/drivers/cxl/core/ras_rch.c
> +++ b/drivers/cxl/core/ras_rch.c
> @@ -110,6 +110,14 @@ void cxl_handle_rdport_errors(struct pci_dev *pdev)
>  	if (!dport)
>  		return;
>  
> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
> +	if (cxl_aer_einj.is_rch && cxl_aer_einj.dev) {
> +		severity = cxl_aer_einj.correctable ?
> +			AER_CORRECTABLE : AER_FATAL;
> +		goto handle_ras;
> +	}
> +#endif
> +
>  	if (!cxl_rch_get_aer_info(dport->regs.dport_aer, &aer_regs))
>  		return;
>  
> @@ -117,6 +125,10 @@ void cxl_handle_rdport_errors(struct pci_dev *pdev)
>  		return;
>  
>  	pci_print_aer(pdev, severity, &aer_regs);
> +
> +#if IS_ENABLED(CONFIG_CXL_PROTO_AER_EINJ)
> +handle_ras:
> +#endif
>  	if (severity == AER_CORRECTABLE)
>  		cxl_handle_cor_ras(dport->port, dport,
>  				   to_ras_base(port, dport), pdev->dsn);
> diff --git a/drivers/pci/pcie/aer_inject.c b/drivers/pci/pcie/aer_inject.c
> index 09bfc7194ef31..b313adef680ae 100644
> --- a/drivers/pci/pcie/aer_inject.c
> +++ b/drivers/pci/pcie/aer_inject.c
> @@ -14,6 +14,7 @@
>  
>  #define dev_fmt(fmt) "aer_inject: " fmt
>  
> +#include <linux/aer.h>
>  #include <linux/module.h>
>  #include <linux/init.h>
>  #include <linux/interrupt.h>
> @@ -31,19 +32,6 @@
>  static bool aer_mask_override;
>  module_param(aer_mask_override, bool, 0);
>  
> -struct aer_error_inj {
> -	u8 bus;
> -	u8 dev;
> -	u8 fn;
> -	u32 uncor_status;
> -	u32 cor_status;
> -	u32 header_log0;
> -	u32 header_log1;
> -	u32 header_log2;
> -	u32 header_log3;
> -	u32 domain;
> -};
> -
>  struct aer_error {
>  	struct list_head list;
>  	u32 domain;
> @@ -316,7 +304,7 @@ static int pci_bus_set_aer_ops(struct pci_bus *bus)
>  	return 0;
>  }
>  
> -static int aer_inject(struct aer_error_inj *einj)
> +int aer_inject(struct aer_error_inj *einj)
>  {
>  	struct aer_error *err, *rperr;
>  	struct aer_error *err_alloc = NULL, *rperr_alloc = NULL;
> @@ -332,10 +320,14 @@ static int aer_inject(struct aer_error_inj *einj)
>  	dev = pci_get_domain_bus_and_slot(einj->domain, einj->bus, devfn);
>  	if (!dev)
>  		return -ENODEV;
> -	rpdev = pcie_find_root_port(dev);
> -	/* If Root Port not found, try to find an RCEC */
> -	if (!rpdev)
> -		rpdev = dev->rcec;
> +	if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC)
> +		rpdev = dev;
> +	else {
> +		rpdev = pcie_find_root_port(dev);
> +		/* If Root Port not found, try to find an RCEC */
> +		if (!rpdev)
> +			rpdev = dev->rcec;
> +	}
>  	if (!rpdev) {
>  		pci_err(dev, "Neither Root Port nor RCEC found\n");
>  		ret = -ENODEV;
> @@ -482,6 +474,7 @@ static int aer_inject(struct aer_error_inj *einj)
>  	pci_dev_put(dev);
>  	return ret;
>  }
> +EXPORT_SYMBOL_GPL(aer_inject);
>  
>  static ssize_t aer_inject_write(struct file *filp, const char __user *ubuf,
>  				size_t usize, loff_t *off)
> diff --git a/include/linux/aer.h b/include/linux/aer.h
> index b3657b80564b9..65c22ba597657 100644
> --- a/include/linux/aer.h
> +++ b/include/linux/aer.h
> @@ -27,6 +27,21 @@
>  struct pci_dev;
>  struct work_struct;
>  
> +struct aer_error_inj {
> +	u8 bus;
> +	u8 dev;
> +	u8 fn;
> +	u32 uncor_status;
> +	u32 cor_status;
> +	u32 header_log0;
> +	u32 header_log1;
> +	u32 header_log2;
> +	u32 header_log3;
> +	u32 domain;
> +};
> +
> +int aer_inject(struct aer_error_inj *einj);
> +
>  struct pcie_tlp_log {
>  	union {
>  		u32 dw[PCIE_STD_MAX_TLP_HEADERLOG];


^ permalink raw reply

* Re: [PATCH 0/4] vmcore-tasks: export per-task metadata to vmcoreinfo
From: Omar Sandoval @ 2026-07-20 17:35 UTC (permalink / raw)
  To: Baoquan He
  Cc: Mike Rapoport, Pnina Feder, Simon Horman, Andrew Morton,
	Pasha Tatashin, Pratyush Yadav, Thomas Bogendoerfer,
	Paul Walmsley, Palmer Dabbelt, Albert Ou, Dave Young,
	Jonathan Corbet, Alexandre Ghiti, kexec, linux-kernel, linux-mips,
	linux-riscv, linux-doc, linux-debuggers
In-Reply-To: <al4yeHrkDHFoo6A0@MiWiFi-R3L-srv>

On Mon, Jul 20, 2026 at 10:40:43PM +0800, Baoquan He wrote:
> On 07/20/26 at 03:43pm, Mike Rapoport wrote:
> > Hi Simon,
> > 
> > On Mon, Jul 20, 2026 at 10:19:27AM +0100, Simon Horman wrote:
> > > On Tue, Jul 07, 2026 at 09:21:58AM +0300, Mike Rapoport wrote:
> > > > On Tue, Jun 23, 2026 at 12:14:26AM +0300, Pnina Feder wrote:
> > > > > This series extends vmcoreinfo with struct offsets and sizes needed by
> > > > > the vmcore-tasks userspace tool to extract per-task state from a vmcore
> > > > > dump without requiring kernel debug symbols (DWARF/BTF).
> > > > > 
> > > > > The vmcore-tasks tool reads /proc/vmcore (or a saved vmcore file) and
> > > > > reconstructs, for each task:
> > > > >   - task name, pid, state, flags
> > > > >   - VMA list (start, end, flags, backing file)
> > > > >   - user register state (saved on the kernel stack at kernel entry)
> > > > >   - user-space backtrace with VMA/filename mapping
> > > > >   - kernel dmesg buffer
> > > > > 
> > > > > This provides a lightweight post-mortem crash analysis capability for
> > > > > production environments where full debug info (DWARF/BTF) is not
> > > > > available.
> > > > > 
> > > > > The companion userspace tool is submitted to kexec-tools:
> > > > >   https://lore.kernel.org/all/20260622205550.1087163-1-pnina.feder@mobileye.com/
> > > > 
> > > > Sorry for the delay, this fell between the cracks somehow.
> > > > 
> > > > The kernel side looks fine overall, but to merge it there should be an
> > > > agreement from the userspace side maintainers that vmcore-tasks is
> > > > something they are wishing to accept.
> > > 
> > > Hi Mike, all,
> > > 
> > > Sorry for the extended delay.
> > > 
> > > I will send some minor feedback to the user-space tool patchset
> > > but overall, yes, this is something I would be happy to accept.
> > > 
> > > I don't want to create a chicken-and-egg type problem here.
> > > But it's probably worth mentioning that usually features
> > > hit the kernel before the corresponding code is accepted
> > > into kexec-tools.
> > 
> > Sorry if I wasn't clear. 
> > 
> > I didn't mean that the feature should be merged into kexec-tools before
> > merging the kernel bits. I just wanted to make sure there's no fundamental
> > issue from kexec-tools perspective.
> > 
> > > Let me know how you would like to proceed.
> > 
> > I'm going to wait for Baoquan's review and once that's done I'll pick up
> > the kernel bits.
> 
> Oh, sorry, I just noticed this series, but a quick look give me a hint
> of 'No, I don't like it.' We have had Crash utility, Drgn, now a new one
> comes up. I have some concerns to Pnina:
> 
> 1. Exporting maple tree internals (maple_node, maple_range_64,
>    maple_arange_64, maple_metadata) as vmcoreinfo ABI is problematic.
>    These are private implementation details, not stable structures like
>    task_struct. Any refactoring of the maple tree will silently break
>    the userspace tool. 
> 
> 2. Without DWARF/BTF, the debugging scope is inherently limited — no
>    kernel stack backtrace, no symbol resolution, no variable access.
>    That's essentially "ps + /proc/PID/maps" from a vmcore. Have you
>    considered enabling CONFIG_DEBUG_INFO_BTF on your platforms instead?
>    BTF is compact (typically < 1 MB) and would give drgn full access
>    without needing vmlinux debug packages. That seems like a better
>    return-on-investment than maintaining ~40 new vmcoreinfo exports.
> 
> 3. Beyond the technical concerns, I wonder about adoption. vmcore-tasks
>    targets a fairly narrow use case — platforms without DWARF/BTF, where
>    you still have vmcore but can't ship vmlinux. Is Mobileye currently
>    the only consumer? Are you guys already using it widely and fully?
>    If this lands but doesn't see broader uptake, the ~40 exports risk
>    becoming dead weight in vmcoreinfo — nobody actively uses them, but
>    kernel changes still need to keep them consistent, or they silently
>    rot and give users wrong results. 
> 
> Regards,
> Baoquan

(Adding linux-debuggers mailing list).

I agree with all of Baoquan's points here, with a couple of notes:

1. drgn's BTF support is currently a work in progress, but it's shaping
   up well: https://github.com/osandov/drgn/pull/625.
2. makedumpfile is also gaining BTF extension support:
   https://lore.kernel.org/all/20260617051834.95404-1-ltao@redhat.com/.
   It's lighter-weight than drgn while still being much more general
   than this series.

Thanks,
Omar

^ permalink raw reply

* [PATCH v8 11/11] virt/steal_governor: Enable the driver
From: Shrikanth Hegde @ 2026-07-20 17:22 UTC (permalink / raw)
  To: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
	yury.norov, kprateek.nayak, iii, corbet
  Cc: sshegde, tglx, gregkh, pbonzini, seanjc, vschneid, huschle,
	rostedt, dietmar.eggemann, maddy, srikar, hdanton, chleroy,
	vineeth, frederic, arighi, pauld, christian.loehle, tj,
	tommaso.cucinotta, maz, rafael, rdunlap, kernellwp, linux-doc,
	jgross, virtualization
In-Reply-To: <20260720172250.2257582-1-sshegde@linux.ibm.com>

Provide a config option for users to enable the driver.
Since the feature works for paravirtualized case and make sense only
with SMP, enforce those dependencies. Driver is going to select
CONFIG_PREFERRED_CPU=y for scheduler mechanisms to work.

It is recommended to build the driver as module instead of yes
due to below reasons.
- Module parameters are read only after init. If one wants to change
  them having as module allows that.
- Driver can be disabled if it is built as module.
- If it is module, admin user has to enable it. Since this feature works
  best when all VMs work in co-operative manner, admin will likely
  enable it in all VMs.

Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
 drivers/virt/Kconfig                 |  2 ++
 drivers/virt/Makefile                |  1 +
 drivers/virt/steal_governor/Kconfig  | 18 ++++++++++++++++++
 drivers/virt/steal_governor/Makefile |  6 ++++++
 4 files changed, 27 insertions(+)
 create mode 100644 drivers/virt/steal_governor/Kconfig
 create mode 100644 drivers/virt/steal_governor/Makefile

diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig
index 52eb7e4ba71f..4fbc928022a4 100644
--- a/drivers/virt/Kconfig
+++ b/drivers/virt/Kconfig
@@ -47,6 +47,8 @@ source "drivers/virt/nitro_enclaves/Kconfig"
 
 source "drivers/virt/acrn/Kconfig"
 
+source "drivers/virt/steal_governor/Kconfig"
+
 endif
 
 source "drivers/virt/coco/Kconfig"
diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile
index f29901bd7820..512dcfd6c73e 100644
--- a/drivers/virt/Makefile
+++ b/drivers/virt/Makefile
@@ -10,3 +10,4 @@ obj-y				+= vboxguest/
 obj-$(CONFIG_NITRO_ENCLAVES)	+= nitro_enclaves/
 obj-$(CONFIG_ACRN_HSM)		+= acrn/
 obj-y				+= coco/
+obj-$(CONFIG_STEAL_GOVERNOR)	+= steal_governor/
diff --git a/drivers/virt/steal_governor/Kconfig b/drivers/virt/steal_governor/Kconfig
new file mode 100644
index 000000000000..926bb351db4f
--- /dev/null
+++ b/drivers/virt/steal_governor/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config STEAL_GOVERNOR
+	tristate "Dynamic vCPU management based on steal time"
+	depends on PARAVIRT && SMP
+	select PREFERRED_CPU
+	default m
+	help
+	  This driver helps to reduce the steal time in paravirtualised
+	  environment, thereby reducing vCPU preemption. Reducing vCPU
+	  preemption provides improved lock holder preemption and reduces
+	  cost of vCPU preemption in the host.
+
+	  By default preferred CPUs will be same as active CPUs. Depending
+	  on the steal time when steal_governor driver is enabled,
+	  preferred CPUs could become subset of active CPUs.
+
+	  It is recommended to build it as module and load the module
+	  to enable it.
diff --git a/drivers/virt/steal_governor/Makefile b/drivers/virt/steal_governor/Makefile
new file mode 100644
index 000000000000..eb90ed6b94d7
--- /dev/null
+++ b/drivers/virt/steal_governor/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Steal time governor to alter preferred CPU state.
+obj-$(CONFIG_STEAL_GOVERNOR) += steal_governor.o
+
+steal_governor-y := core.o
-- 
2.47.3


^ permalink raw reply related

* [PATCH v8 10/11] virt/steal_governor: Implement steal_governor policy loop
From: Shrikanth Hegde @ 2026-07-20 17:22 UTC (permalink / raw)
  To: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
	yury.norov, kprateek.nayak, iii, corbet
  Cc: sshegde, tglx, gregkh, pbonzini, seanjc, vschneid, huschle,
	rostedt, dietmar.eggemann, maddy, srikar, hdanton, chleroy,
	vineeth, frederic, arighi, pauld, christian.loehle, tj,
	tommaso.cucinotta, maz, rafael, rdunlap, kernellwp, linux-doc,
	jgross, virtualization
In-Reply-To: <20260720172250.2257582-1-sshegde@linux.ibm.com>

schedule work at regular intervals to implement the steal_governor
policy of monitoring the steal time and take action on the state of
preferred CPUs. The interval is determined by interval_ms parameter.
schedule_delayed_work is used since interval_ms
is in the order of milliseconds. Work need not happen instantly.

Periodic policy loop essentially does:
- Gets the total/delta steal values and cpus to use steal_ratio.
  (get_system_steal_time, get_num_cpus_steal_ratio)
- Calculate the steal_ratio as below.

       steal_ratio = (delta_steal * 100*100)/(delta_ns * num_cpus())

  It is calculated to consider the fractional values of steal time.
  I.e 10 means 0.1% steal time. A few tricks such as divide by 10,000
  are used to avoid possible overflow.
- If steal value is higher than high threshold, call the method to reduce
  the preferred CPUs. (decrease_preferred_cpus)
- If steal value is lower or equal to low threshold, call the method to
  increase the preferred CPUs. (increase_preferred_cpus)
- If the steal value is in between, no action is taken.
- Save the values for next delta calculations.
- Ensure design checks are met.
  1. At least one core/CPU must be there in preferred mask.
  2. preferred CPUs is subset of active CPUs.
If not met, then restore preferred CPUs to active and stop
requeue of the work. Driver is effectively non-functional after that.

In Order to help the above loop, a few helper functions have been added.

1. get_system_steal_time()
- steal governor takes global view of steal time instead of individual
  vCPU. Collect the steal values across the vCPUs of interest.
- Sum up steal time values across possible CPUs. This helps to keep it
  a monotonically increasing number and avoids spikes due to CPU
  hotplug.

2. decrease_preferred_cpus()
- Called when there is high steal time. It needs to decide which CPUs to
  mark as non-preferred and set that state.
- Get first housekeeping CPU and its core mask. Mark it as
  protected core. This helps to keep at least one core as preferred.
  kernel ensures at least one housekeeping CPU stays active.
- Find the last CPU outside of this protected core mask. (target CPU)
- Based on that target CPU, get its sibling and mark them as
  non-preferred.

3. increase_preferred_cpus()
- Called when there is low steal time. It needs to decide which CPUs to
  mark as preferred and set that state.
- Get the first active non-preferred CPUs. This likely is the last
  set of CPUs being marked as non-preferred.
- get the siblings of that CPU and mark them as preferred.

4. get_num_cpus_steal_ratio()
- informs how many CPUs needs to be considered for steal_ratio
  calculations.
- Return number of possible CPUs as get_system_steal_time computes
  steal values across possible CPUs.

Notes:
1. Using core instead of individual CPUs performs better as SMT is
   quite common and some hypervisor such as powerVM does core scheduling.

2. This doesn't do any NUMA splicing to keep the code simpler and
   minimal overhead. Current code expects CPUs spread uniformly
   across NUMA nodes.

Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
 drivers/virt/steal_governor/core.c | 160 +++++++++++++++++++++++++++++
 drivers/virt/steal_governor/core.h |   5 +
 2 files changed, 165 insertions(+)

diff --git a/drivers/virt/steal_governor/core.c b/drivers/virt/steal_governor/core.c
index 1cb766a8ce28..97f82d6df60f 100644
--- a/drivers/virt/steal_governor/core.c
+++ b/drivers/virt/steal_governor/core.c
@@ -89,6 +89,158 @@ module_param_named(low_threshold, sg_core_ctx.low_threshold, uint, 0444);
 MODULE_PARM_DESC(low_threshold,
 		 "Low steal threshold. default: 200 i.e 2%. Must be < high_threshold");
 
+/*
+ * Returns steal time of the full system.
+ * Compute collective steal time across all possible CPUs.
+ */
+static u64 get_system_steal_time(void)
+{
+	int cpu;
+	u64 total_steal = 0;
+
+	for_each_possible_cpu(cpu)
+		total_steal += kcpustat_cpu(cpu).cpustat[CPUTIME_STEAL];
+
+	return total_steal;
+}
+
+/*
+ * Returns number of CPUs to consider for steal ratio.
+ * Return possible CPUs.
+ */
+static unsigned int get_num_cpus_steal_ratio(void)
+{
+	return num_possible_cpus();
+}
+
+/*
+ * Take action to decrease preferred CPUs.
+ *
+ * Decrease the preferred CPUs by 1 core.
+ * Take out the last core in the active & preferred.
+ *
+ * Must ensure
+ * - least one housekeeping core is always kept as preferred
+ * - preferred is always subset of active.
+ */
+static void decrease_preferred_cpus(void)
+{
+	int tmp_cpu, first_hk_cpu, last_cpu;
+	const struct cpumask *first_hk_core;
+	int target_cpu = nr_cpu_ids;
+
+	guard(cpus_read_lock)();
+	first_hk_cpu = cpumask_first_and(housekeeping_cpumask(HK_TYPE_KERNEL_NOISE),
+					 cpu_preferred_mask);
+	if (first_hk_cpu >= nr_cpu_ids)
+		return;
+
+	last_cpu = cpumask_last(cpu_preferred_mask);
+
+	if (last_cpu >= nr_cpu_ids)
+		return;
+
+	/* Always leave first housekeeping core as preferred. */
+	first_hk_core = topology_sibling_cpumask(first_hk_cpu);
+
+	/* Find the last CPU which doesn't belong to that first hk_core. */
+	if (!cpumask_test_cpu(last_cpu, first_hk_core)) {
+		target_cpu = last_cpu;
+	} else {
+		for_each_cpu_andnot(tmp_cpu, cpu_preferred_mask, first_hk_core)
+			target_cpu = tmp_cpu;
+	}
+
+	/* Only the first housekeeping core remains */
+	if (target_cpu >= nr_cpu_ids)
+		return;
+
+	for_each_cpu_and(tmp_cpu, topology_sibling_cpumask(target_cpu),
+			 cpu_preferred_mask)
+		set_cpu_preferred(tmp_cpu, false);
+}
+
+/*
+ * Take action to increase preferred CPUs.
+ *
+ * Increase the preferred CPUs by 1 core.
+ * Add the first core in active & !preferred
+ *
+ * Must ensure preferred is subset of active.
+ */
+static void increase_preferred_cpus(void)
+{
+	int first_cpu, tmp_cpu;
+
+	guard(cpus_read_lock)();
+	first_cpu = cpumask_first_andnot(cpu_active_mask, cpu_preferred_mask);
+
+	/* All CPUs are preferred. Nothing to increase further */
+	if (first_cpu >= nr_cpu_ids)
+		return;
+
+	for_each_cpu_and(tmp_cpu, topology_sibling_cpumask(first_cpu),
+			 cpu_active_mask)
+		set_cpu_preferred(tmp_cpu, true);
+}
+
+static void compute_preferred_cpus_work(struct work_struct *work)
+{
+	u64 curr_steal, delta_steal, delta_ns, steal_ratio;
+	ktime_t now;
+
+	now = ktime_get();
+	delta_ns = ktime_to_ns(ktime_sub(now, sg_core_ctx.time));
+
+	if (unlikely(delta_ns < NSEC_PER_MSEC)) {
+		pr_err_ratelimited("steal_governor: work scheduled too soon delta_ns: %llu\n",
+				   delta_ns);
+		goto requeue_work;
+	}
+
+	curr_steal = get_system_steal_time();
+	delta_steal = curr_steal > sg_core_ctx.steal ?
+		      curr_steal - sg_core_ctx.steal : 0;
+
+	/* Update for next calculation */
+	sg_core_ctx.steal = curr_steal;
+	sg_core_ctx.time = now;
+
+	/*
+	 * steal_ratio = (delta_steal * 100*100)/(delta_ns * num_cpus())
+	 * To avoid possible overflow, divide the denominator early.
+	 * Note minimum interval is 100ms.
+	 */
+	delta_ns = max_t(u64, div_u64(delta_ns * get_num_cpus_steal_ratio(),
+				      100 * 100), 1);
+	steal_ratio = div64_u64(delta_steal, delta_ns);
+
+	if (steal_ratio > sg_core_ctx.high_threshold)
+		decrease_preferred_cpus();
+	if (steal_ratio <= sg_core_ctx.low_threshold)
+		increase_preferred_cpus();
+
+	/* maintain design constructs always */
+	if (cpumask_empty(cpu_preferred_mask)) {
+		pr_err("empty preferred mask. stop steal governor\n");
+		restore_preferred_to_active();
+		return;
+	}
+
+	if (!cpumask_subset(cpu_preferred_mask, cpu_active_mask)) {
+		pr_err("preferred: %*pbl is not subset of active: %*pbl, stop steal governor\n",
+		       cpumask_pr_args(cpu_preferred_mask),
+		       cpumask_pr_args(cpu_active_mask));
+		restore_preferred_to_active();
+		return;
+	}
+
+requeue_work:
+	/* Trigger for next sampling */
+	schedule_delayed_work(&sg_core_ctx.work,
+			      msecs_to_jiffies(sg_core_ctx.interval_ms));
+}
+
 static int __init steal_governor_init(void)
 {
 	if (sg_core_ctx.low_threshold >= sg_core_ctx.high_threshold) {
@@ -100,11 +252,19 @@ static int __init steal_governor_init(void)
 	pr_info("steal_governor is enabled. interval: %ums, high_threshold: %u, low_threshold: %u\n",
 		sg_core_ctx.interval_ms, sg_core_ctx.high_threshold, sg_core_ctx.low_threshold);
 
+	INIT_DELAYED_WORK(&sg_core_ctx.work, compute_preferred_cpus_work);
+	sg_core_ctx.steal = get_system_steal_time();
+	sg_core_ctx.time = ktime_get();
+
+	schedule_delayed_work(&sg_core_ctx.work,
+			      msecs_to_jiffies(sg_core_ctx.interval_ms));
+
 	return 0;
 }
 
 static void __exit steal_governor_exit(void)
 {
+	disable_delayed_work_sync(&sg_core_ctx.work);
 	restore_preferred_to_active();
 	pr_info("steal_governor is disabled\n");
 }
diff --git a/drivers/virt/steal_governor/core.h b/drivers/virt/steal_governor/core.h
index e27305284ac0..59329c1d7109 100644
--- a/drivers/virt/steal_governor/core.h
+++ b/drivers/virt/steal_governor/core.h
@@ -12,6 +12,11 @@
 #include <linux/workqueue.h>
 #include <linux/ktime.h>
 #include <linux/kconfig.h>
+#include <linux/kernel_stat.h>
+#include <linux/topology.h>
+#include <linux/sched/isolation.h>
+#include <linux/cleanup.h>
+#include <linux/math64.h>
 
 struct steal_governor {
 	struct delayed_work	work;
-- 
2.47.3


^ permalink raw reply related

* [PATCH v8 09/11] virt/steal_governor: Add control knobs for handling steal values
From: Shrikanth Hegde @ 2026-07-20 17:22 UTC (permalink / raw)
  To: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
	yury.norov, kprateek.nayak, iii, corbet
  Cc: sshegde, tglx, gregkh, pbonzini, seanjc, vschneid, huschle,
	rostedt, dietmar.eggemann, maddy, srikar, hdanton, chleroy,
	vineeth, frederic, arighi, pauld, christian.loehle, tj,
	tommaso.cucinotta, maz, rafael, rdunlap, kernellwp, linux-doc,
	jgross, virtualization
In-Reply-To: <20260720172250.2257582-1-sshegde@linux.ibm.com>

These are the knobs to control the steal_governor.

interval_ms:
How often steal governor checks for steal time.
(Default: 1000 i.e 1 second)
This controls how fast steal governor driver reacts to changes to
the contention of physical CPUs.
Can be set between 100 to 100000. i.e. 100ms to 100seconds.
100ms is kept as minimum to ensure few meaningful steal values
accumulate even with HZ=100.

low_threshold:
lower threshold value in percentage * 100.
(Default: 200, i.e 2% steal is considered as low threshold)
This determines what values should be considered as nil/no steal values.
When steal governor see steal time is below or equal to this value, it
will increase the preferred CPUs by 1 core. Having value as zero
might cause oscillations

high_threshold:
higher threshold value in percentage * 100
(Default: 500, i.e 5% steal is considered as high threshold)
This determines what values should be considered as high steal values.
When steal governor sees steal time is higher than this value, it will
reduce the preferred CPUs by 1 core.

module_param_cb methods are used to do the validation checks.
This helps to ensure one configures sane values.
Since low and high are dependent, that check is done at module init.

Parameters values can't be changed at runtime. One has to unload
the module and change it. Hence recommended to build it as module.

Also available at: Documentation/driver-api/steal-governor.rst

Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
 drivers/virt/steal_governor/core.c | 73 +++++++++++++++++++++++++++++-
 1 file changed, 71 insertions(+), 2 deletions(-)

diff --git a/drivers/virt/steal_governor/core.c b/drivers/virt/steal_governor/core.c
index 055f155d2045..1cb766a8ce28 100644
--- a/drivers/virt/steal_governor/core.c
+++ b/drivers/virt/steal_governor/core.c
@@ -17,7 +17,11 @@
 #error "Steal Governor requires CONFIG_PREFERRED_CPU"
 #endif
 
-static struct steal_governor sg_core_ctx;
+static struct steal_governor sg_core_ctx = {
+	.interval_ms	=	1000,	/* 1 second */
+	.high_threshold =	500,	/* 5% */
+	.low_threshold	=	200,	/* 2% */
+};
 
 static void restore_preferred_to_active(void)
 {
@@ -28,9 +32,74 @@ static void restore_preferred_to_active(void)
 		set_cpu_preferred(cpu, true);
 }
 
+static int param_set_interval_ms(const char *val, const struct kernel_param *kp)
+{
+	unsigned int interval;
+	int ret;
+
+	ret = kstrtouint(val, 0, &interval);
+	if (ret)
+		return ret;
+
+	if (interval < 100 || interval > 100000) {
+		pr_err("steal_governor: interval_ms must be between 100 and 100000\n");
+		return -EINVAL;
+	}
+
+	return param_set_uint(val, kp);
+}
+
+static const struct kernel_param_ops interval_ms_ops = {
+	.set = param_set_interval_ms,
+	.get = param_get_uint,
+};
+
+module_param_cb(interval_ms, &interval_ms_ops, &sg_core_ctx.interval_ms, 0444);
+MODULE_PARM_DESC(interval_ms,
+		 "Sampling frequency in milliseconds. default: 1000");
+
+static int param_set_high_threshold(const char *val, const struct kernel_param *kp)
+{
+	unsigned int threshold;
+	int ret;
+
+	ret = kstrtouint(val, 0, &threshold);
+	if (ret)
+		return ret;
+
+	if (threshold >= 100 * 100) {
+		pr_err("steal_governor: high_threshold (%u) can't be more than 99.99%%\n",
+		       threshold);
+		return -EINVAL;
+	}
+
+	return param_set_uint(val, kp);
+}
+
+static const struct kernel_param_ops high_threshold_ops = {
+	.set = param_set_high_threshold,
+	.get = param_get_uint,
+};
+
+module_param_cb(high_threshold, &high_threshold_ops, &sg_core_ctx.high_threshold, 0444);
+MODULE_PARM_DESC(high_threshold,
+		 "High steal threshold. default: 500 i.e 5%. Must be > low_threshold");
+
+module_param_named(low_threshold, sg_core_ctx.low_threshold, uint, 0444);
+MODULE_PARM_DESC(low_threshold,
+		 "Low steal threshold. default: 200 i.e 2%. Must be < high_threshold");
+
 static int __init steal_governor_init(void)
 {
-	pr_info("steal_governor is enabled\n");
+	if (sg_core_ctx.low_threshold >= sg_core_ctx.high_threshold) {
+		pr_err("steal_governor: low_threshold (%u) must be less than high_threshold (%u)\n",
+		       sg_core_ctx.low_threshold, sg_core_ctx.high_threshold);
+		return -EINVAL;
+	}
+
+	pr_info("steal_governor is enabled. interval: %ums, high_threshold: %u, low_threshold: %u\n",
+		sg_core_ctx.interval_ms, sg_core_ctx.high_threshold, sg_core_ctx.low_threshold);
+
 	return 0;
 }
 
-- 
2.47.3


^ permalink raw reply related


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