* Re: [PATCH] iommu/arm-smmu-v3: Allow disabling Stage 1 translation
From: Jason Gunthorpe @ 2026-04-22 16:23 UTC (permalink / raw)
To: Evangelos Petrongonas
Cc: Will Deacon, Robin Murphy, Joerg Roedel, Nicolin Chen,
Pranjal Shrivastava, Lu Baolu, linux-arm-kernel, iommu,
linux-kernel, nh-open-source, Zeev Zilberman
In-Reply-To: <20260422064431.GA49867@dev-dsk-epetron-1c-1d4d9719.eu-west-1.amazon.com>
On Wed, Apr 22, 2026 at 06:44:31AM +0000, Evangelos Petrongonas wrote:
> The motivation is live update of the hypervisor: we want to kexec into a
> new kernel while keeping DMA from passthrough devices flowing, which
> means the SMMU's translation state has to survive the handover. The Live
> Update Orchestrator work [1] and the in-progress "iommu: Add live
> update state preservation" series [2] are building exactly this plumbing
> on top of KHO; [2]'s cover letter calls out Arm SMMUv3 support as future
> work, and an earlier RFC from Amazon [3] sketched the same idea for
> iommufd.
It would be appropriate to keep this patch with the rest of that out
of tree pile, for example in the series that enables s2 only support
in smmuv3.
> For this use case, Stage 2 is materially easier to persist than Stage 1,
> for structural rather than performance reasons:
I don't think so. The driver needs to know each and every STE that
will survive KHO. The ones that don't survive need to be reset to
abort STEs. From that point it is trivial enough to include the CD
memory in the preservation.
It would help to send a preparation series to switch the ARM STE and
CD logic away from dma_alloc_coherent and use iommu-pages instead,
since we only expect iommu-pages to support preservation..
I could maybe see only supporting non-PASID as a first-series, but a
CD table with SSID 0 only populated is still pretty trivial.
Jason
^ permalink raw reply
* [PATCH v17 5/5] ring-buffer: Show commit numbers in buffer_meta file
From: Masami Hiramatsu (Google) @ 2026-04-22 16:17 UTC (permalink / raw)
To: Steven Rostedt, Catalin Marinas, Will Deacon
Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, Ian Rogers, linux-arm-kernel
In-Reply-To: <177687458572.932171.10907864814735342737.stgit@mhiramat.tok.corp.google.com>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
In addition to the index number, show the commit numbers of
each data page in the per_cpu buffer_meta file.
This is useful for understanding the current status of the
persistent ring buffer. (Note that this file is shown
only for persistent ring buffer and its backup instance)
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Changes in v17:
- Added NULL check for dpage in rbm_show in ring_buffer.c.
Changes in v16:
- update description.
---
kernel/trace/ring_buffer.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 31448c5ea791..15dcbf554d49 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -2215,6 +2215,7 @@ static int rbm_show(struct seq_file *m, void *v)
struct ring_buffer_per_cpu *cpu_buffer = m->private;
struct ring_buffer_cpu_meta *meta = cpu_buffer->ring_meta;
unsigned long val = (unsigned long)v;
+ struct buffer_data_page *dpage;
if (val == 1) {
seq_printf(m, "head_buffer: %d\n",
@@ -2227,7 +2228,9 @@ static int rbm_show(struct seq_file *m, void *v)
}
val -= 2;
- seq_printf(m, "buffer[%ld]: %d\n", val, meta->buffers[val]);
+ dpage = rb_range_buffer(cpu_buffer, val);
+ seq_printf(m, "buffer[%ld]: %d (commit: %ld)\n",
+ val, meta->buffers[val], dpage ? local_read(&dpage->commit) : -1);
return 0;
}
^ permalink raw reply related
* [PATCH v17 4/5] ring-buffer: Add persistent ring buffer invalid-page inject test
From: Masami Hiramatsu (Google) @ 2026-04-22 16:16 UTC (permalink / raw)
To: Steven Rostedt, Catalin Marinas, Will Deacon
Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, Ian Rogers, linux-arm-kernel
In-Reply-To: <177687458572.932171.10907864814735342737.stgit@mhiramat.tok.corp.google.com>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Add a self-corrupting test for the persistent ring buffer.
This will inject an erroneous value to some sub-buffer pages (where
the index is even or multiples of 5) in the persistent ring buffer
when the kernel panics, and checks whether the number of detected
invalid pages and the total entry_bytes are the same as the recorded
values after reboot.
This ensures that the kernel can correctly recover a partially
corrupted persistent ring buffer after a reboot or panic.
The test only runs on the persistent ring buffer whose name is
"ptracingtest". The user has to fill it with events before a
kernel panic.
To run the test, enable CONFIG_RING_BUFFER_PERSISTENT_INJECT
and add the following kernel cmdline:
reserve_mem=20M:2M:trace trace_instance=ptracingtest^traceoff@trace
panic=1
Run the following commands after the 1st boot:
cd /sys/kernel/tracing/instances/ptracingtest
echo 1 > tracing_on
echo 1 > events/enable
sleep 3
echo c > /proc/sysrq-trigger
After panic message, the kernel will reboot and run the verification
on the persistent ring buffer, e.g.
Ring buffer meta [2] invalid buffer page detected
Ring buffer meta [2] is from previous boot! (318 pages discarded)
Ring buffer testing [2] invalid pages: PASSED (318/318)
Ring buffer testing [2] entry_bytes: PASSED (1300476/1300476)
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Changes in v17:
- In rb_test_inject_invalid_pages(), changed entry_bytes and
idx to unsigned long
- Added NULL checks for cpu_buffer and meta.
- In allocate_trace_buffer(), added a NULL check for tr->name
before comparing it with strcmp.
Changes in v16:
- Update description and comments according to review comments.
Changes in v15:
- Use pr_warn() for test result.
- Inject errors on the page index is multiples of 5 so that
this can reproduce contiguous empty pages.
Changes in v14:
- Rename config to CONFIG_RING_BUFFER_PERSISTENT_INJECT.
- Clear meta->nr_invalid/entry_bytes after testing.
- Add test commands in config comment.
Changes in v10:
- Add entry_bytes test.
- Do not compile test code if CONFIG_RING_BUFFER_PERSISTENT_SELFTEST=n.
Changes in v9:
- Test also reader pages.
---
include/linux/ring_buffer.h | 1 +
kernel/trace/Kconfig | 34 +++++++++++++++++++
kernel/trace/ring_buffer.c | 79 +++++++++++++++++++++++++++++++++++++++++++
kernel/trace/trace.c | 4 ++
4 files changed, 118 insertions(+)
diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h
index 994f52b34344..0670742b2d60 100644
--- a/include/linux/ring_buffer.h
+++ b/include/linux/ring_buffer.h
@@ -238,6 +238,7 @@ int ring_buffer_subbuf_size_get(struct trace_buffer *buffer);
enum ring_buffer_flags {
RB_FL_OVERWRITE = 1 << 0,
+ RB_FL_TESTING = 1 << 1,
};
#ifdef CONFIG_RING_BUFFER
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index e130da35808f..084f34dc6c9f 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -1202,6 +1202,40 @@ config RING_BUFFER_VALIDATE_TIME_DELTAS
Only say Y if you understand what this does, and you
still want it enabled. Otherwise say N
+config RING_BUFFER_PERSISTENT_INJECT
+ bool "Enable persistent ring buffer error injection test"
+ depends on RING_BUFFER
+ help
+ This option will have the kernel check if the persistent ring
+ buffer is named "ptracingtest". and if so, it will corrupt some
+ of its pages on a kernel panic. This is used to test if the
+ persistent ring buffer can recover from some of its sub-buffers
+ being corrupted.
+ To use this, boot a kernel with a "ptracingtest" persistent
+ ring buffer, e.g.
+
+ reserve_mem=20M:2M:trace trace_instance=ptracingtest@trace panic=1
+
+ And after the 1st boot, run the following commands:
+
+ cd /sys/kernel/tracing/instances/ptracingtest
+ echo 1 > events/enable
+ echo 1 > tracing_on
+ sleep 3
+ echo c > /proc/sysrq-trigger
+
+ After the panic message, the kernel will reboot and will show
+ the test results in the console output.
+
+ Note that events for the test ring buffer needs to be enabled
+ prior to crashing the kernel so that the ring buffer has content
+ that the test will corrupt.
+ As the test will corrupt events in the "ptracingtest" persistent
+ ring buffer, it should not be used for any other purpose other
+ than this test.
+
+ If unsure, say N
+
config MMIOTRACE_TEST
tristate "Test module for mmiotrace"
depends on MMIOTRACE && m
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 0e3d2d037d4d..31448c5ea791 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -64,6 +64,10 @@ struct ring_buffer_cpu_meta {
unsigned long commit_buffer;
__u32 subbuf_size;
__u32 nr_subbufs;
+#ifdef CONFIG_RING_BUFFER_PERSISTENT_INJECT
+ __u32 nr_invalid;
+ __u32 entry_bytes;
+#endif
int buffers[];
};
@@ -2085,6 +2089,21 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
if (discarded)
pr_cont(" (%d pages discarded)", discarded);
pr_cont("\n");
+
+#ifdef CONFIG_RING_BUFFER_PERSISTENT_INJECT
+ if (meta->nr_invalid)
+ pr_warn("Ring buffer testing [%d] invalid pages: %s (%d/%d)\n",
+ cpu_buffer->cpu,
+ (discarded == meta->nr_invalid) ? "PASSED" : "FAILED",
+ discarded, meta->nr_invalid);
+ if (meta->entry_bytes)
+ pr_warn("Ring buffer testing [%d] entry_bytes: %s (%ld/%ld)\n",
+ cpu_buffer->cpu,
+ (entry_bytes == meta->entry_bytes) ? "PASSED" : "FAILED",
+ (long)entry_bytes, (long)meta->entry_bytes);
+ meta->nr_invalid = 0;
+ meta->entry_bytes = 0;
+#endif
return;
invalid:
@@ -2565,12 +2584,72 @@ static void rb_free_cpu_buffer(struct ring_buffer_per_cpu *cpu_buffer)
kfree(cpu_buffer);
}
+#ifdef CONFIG_RING_BUFFER_PERSISTENT_INJECT
+static void rb_test_inject_invalid_pages(struct trace_buffer *buffer)
+{
+ struct ring_buffer_per_cpu *cpu_buffer;
+ struct ring_buffer_cpu_meta *meta;
+ struct buffer_data_page *dpage;
+ unsigned long entry_bytes = 0;
+ unsigned long ptr;
+ int subbuf_size;
+ int invalid = 0;
+ int cpu;
+ int i;
+
+ if (!(buffer->flags & RB_FL_TESTING))
+ return;
+
+ guard(preempt)();
+ cpu = smp_processor_id();
+
+ cpu_buffer = buffer->buffers[cpu];
+ if (!cpu_buffer)
+ return;
+ meta = cpu_buffer->ring_meta;
+ if (!meta)
+ return;
+
+ ptr = (unsigned long)rb_subbufs_from_meta(meta);
+ subbuf_size = meta->subbuf_size;
+
+ for (i = 0; i < meta->nr_subbufs; i++) {
+ unsigned long idx = meta->buffers[i];
+
+ dpage = (void *)(ptr + idx * subbuf_size);
+ /* Skip unused pages */
+ if (!local_read(&dpage->commit))
+ continue;
+
+ /*
+ * Invalidate even pages or multiples of 5. This will cause 3
+ * contiguous invalidated(empty) pages.
+ */
+ if (!(i & 0x1) || !(i % 5)) {
+ local_add(subbuf_size + 1, &dpage->commit);
+ invalid++;
+ } else {
+ /* Count total commit bytes. */
+ entry_bytes += local_read(&dpage->commit);
+ }
+ }
+
+ pr_info("Inject invalidated %d pages on CPU%d, total size: %ld\n",
+ invalid, cpu, (long)entry_bytes);
+ meta->nr_invalid = invalid;
+ meta->entry_bytes = entry_bytes;
+}
+#else /* !CONFIG_RING_BUFFER_PERSISTENT_INJECT */
+#define rb_test_inject_invalid_pages(buffer) do { } while (0)
+#endif
+
/* Stop recording on a persistent buffer and flush cache if needed. */
static int rb_flush_buffer_cb(struct notifier_block *nb, unsigned long event, void *data)
{
struct trace_buffer *buffer = container_of(nb, struct trace_buffer, flush_nb);
ring_buffer_record_off(buffer);
+ rb_test_inject_invalid_pages(buffer);
arch_ring_buffer_flush_range(buffer->range_addr_start, buffer->range_addr_end);
return NOTIFY_DONE;
}
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index e9455d46ec16..d972b24cd73b 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -9436,6 +9436,8 @@ static void setup_trace_scratch(struct trace_array *tr,
memset(tscratch, 0, size);
}
+#define TRACE_TEST_PTRACING_NAME "ptracingtest"
+
static int
allocate_trace_buffer(struct trace_array *tr, struct array_buffer *buf, unsigned long size)
{
@@ -9448,6 +9450,8 @@ allocate_trace_buffer(struct trace_array *tr, struct array_buffer *buf, unsigned
buf->tr = tr;
if (tr->range_addr_start && tr->range_addr_size) {
+ if (tr->name && !strcmp(tr->name, TRACE_TEST_PTRACING_NAME))
+ rb_flags |= RB_FL_TESTING;
/* Add scratch buffer to handle 128 modules */
buf->buffer = ring_buffer_alloc_range(size, rb_flags, 0,
tr->range_addr_start,
^ permalink raw reply related
* [PATCH v17 3/5] ring-buffer: Skip invalid sub-buffers when rewinding persistent ring buffer
From: Masami Hiramatsu (Google) @ 2026-04-22 16:16 UTC (permalink / raw)
To: Steven Rostedt, Catalin Marinas, Will Deacon
Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, Ian Rogers, linux-arm-kernel
In-Reply-To: <177687458572.932171.10907864814735342737.stgit@mhiramat.tok.corp.google.com>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Skip invalid sub-buffers when rewinding the persistent ring buffer
instead of stopping the rewinding the ring buffer. The skipped
buffers are cleared.
To ensure the rewinding stops at the unused page, this also clears
buffer_data_page::time_stamp when tracing resets the buffer. This
allows us to identify unused pages and empty pages.
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Changes in v17:
- Fix to verify head_page at first before using its timestamp.
- Reset timestamp if the page is invalid.
Changes in v12:
- Fix build error.
Changes in v11:
- Reset timestamp when the buffer is invalid.
- When rewinding, skip subbuf page if timestamp is wrong and
check timestamp after validating buffer data page.
Changes in v10:
- Newly added.
---
kernel/trace/ring_buffer.c | 92 ++++++++++++++++++++++++++------------------
1 file changed, 54 insertions(+), 38 deletions(-)
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 26507b93cf40..0e3d2d037d4d 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -363,6 +363,7 @@ struct buffer_page {
static void rb_init_page(struct buffer_data_page *bpage)
{
local_set(&bpage->commit, 0);
+ bpage->time_stamp = 0;
}
static __always_inline unsigned int rb_page_commit(struct buffer_page *bpage)
@@ -1878,12 +1879,14 @@ static int rb_read_data_buffer(struct buffer_data_page *dpage, int tail, int cpu
return events;
}
-static int rb_validate_buffer(struct buffer_data_page *dpage, int cpu,
- struct ring_buffer_cpu_meta *meta)
+static int rb_validate_buffer(struct buffer_page *bpage, int cpu,
+ struct ring_buffer_cpu_meta *meta, u64 prev_ts, u64 next_ts)
{
+ struct buffer_data_page *dpage = bpage->page;
unsigned long long ts;
unsigned long tail;
u64 delta;
+ int ret = -1;
/*
* When a sub-buffer is recovered from a read, the commit value may
@@ -1892,9 +1895,19 @@ static int rb_validate_buffer(struct buffer_data_page *dpage, int cpu,
* subbuf_size is considered invalid.
*/
tail = local_read(&dpage->commit) & ~RB_MISSED_MASK;
- if (tail > meta->subbuf_size)
- return -1;
- return rb_read_data_buffer(dpage, tail, cpu, &ts, &delta);
+ if (tail <= meta->subbuf_size)
+ ret = rb_read_data_buffer(dpage, tail, cpu, &ts, &delta);
+
+ if (ret < 0 || (prev_ts && prev_ts > ts) || (next_ts && ts > next_ts)) {
+ local_set(&bpage->entries, 0);
+ local_set(&bpage->page->commit, 0);
+ bpage->page->time_stamp = prev_ts ? prev_ts : next_ts;
+ ret = -1;
+ } else {
+ local_set(&bpage->entries, ret);
+ }
+
+ return ret;
}
/* If the meta data has been validated, now validate the events */
@@ -1914,25 +1927,29 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
orig_head = head_page = cpu_buffer->head_page;
- /* Do the reader page first */
- ret = rb_validate_buffer(cpu_buffer->reader_page->page, cpu_buffer->cpu, meta);
+ /* Do the head page first */
+ ret = rb_validate_buffer(head_page, cpu_buffer->cpu, meta, 0, 0);
+ if (ret < 0) {
+ pr_info("Ring buffer meta [%d] invalid head page detected\n",
+ cpu_buffer->cpu);
+ goto skip_rewind;
+ }
+ ts = head_page->page->time_stamp;
+
+ /* Do the reader page - reader must be previous to head. */
+ ret = rb_validate_buffer(cpu_buffer->reader_page, cpu_buffer->cpu, meta, 0, ts);
if (ret < 0) {
pr_info("Ring buffer meta [%d] invalid reader page detected\n",
cpu_buffer->cpu);
discarded++;
- /* Instead of discard whole ring buffer, discard only this sub-buffer. */
- local_set(&cpu_buffer->reader_page->entries, 0);
- local_set(&cpu_buffer->reader_page->page->commit, 0);
} else {
entries += ret;
entry_bytes += rb_page_size(cpu_buffer->reader_page);
- local_set(&cpu_buffer->reader_page->entries, ret);
+ ts = cpu_buffer->reader_page->page->time_stamp;
}
- ts = head_page->page->time_stamp;
-
/*
- * Try to rewind the head so that we can read the pages which already
+ * Try to rewind the head so that we can read the pages which are already
* read in the previous boot.
*/
if (head_page == cpu_buffer->tail_page)
@@ -1945,26 +1962,27 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
if (head_page == cpu_buffer->tail_page)
break;
- /* Ensure the page has older data than head. */
- if (ts < head_page->page->time_stamp)
- break;
-
- ts = head_page->page->time_stamp;
- /* Ensure the page has correct timestamp and some data. */
- if (!ts || rb_page_commit(head_page) == 0)
+ /* Rewind until unused page (no timestamp, no commit). */
+ if (!head_page->page->time_stamp && rb_page_commit(head_page) == 0)
break;
- /* Stop rewind if the page is invalid. */
- ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu, meta);
- if (ret < 0)
- break;
-
- /* Recover the number of entries and update stats. */
- local_set(&head_page->entries, ret);
- if (ret)
- local_inc(&cpu_buffer->pages_touched);
- entries += ret;
- entry_bytes += rb_page_size(head_page);
+ /*
+ * Skip if the page is invalid, or its timestamp is newer than the
+ * previous valid page.
+ */
+ ret = rb_validate_buffer(head_page, cpu_buffer->cpu, meta, 0, ts);
+ if (ret < 0) {
+ if (!discarded)
+ pr_info("Ring buffer meta [%d] invalid buffer page detected\n",
+ cpu_buffer->cpu);
+ discarded++;
+ } else {
+ entries += ret;
+ entry_bytes += rb_page_size(head_page);
+ if (ret > 0)
+ local_inc(&cpu_buffer->pages_touched);
+ ts = head_page->page->time_stamp;
+ }
}
if (i)
pr_info("Ring buffer [%d] rewound %d pages\n", cpu_buffer->cpu, i);
@@ -2026,6 +2044,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
/* Nothing more to do, the only page is the reader page */
goto done;
}
+ ts = head_page->page->time_stamp;
/* Iterate until finding the commit page */
for (i = 0; i < meta->nr_subbufs + 1; i++, rb_inc_page(&head_page)) {
@@ -2034,15 +2053,12 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
if (head_page == cpu_buffer->reader_page)
continue;
- ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu, meta);
+ ret = rb_validate_buffer(head_page, cpu_buffer->cpu, meta, ts, 0);
if (ret < 0) {
if (!discarded)
pr_info("Ring buffer meta [%d] invalid buffer page detected\n",
cpu_buffer->cpu);
discarded++;
- /* Instead of discard whole ring buffer, discard only this sub-buffer. */
- local_set(&head_page->entries, 0);
- local_set(&head_page->page->commit, 0);
} else {
/* If the buffer has content, update pages_touched */
if (ret)
@@ -2050,7 +2066,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
entries += ret;
entry_bytes += rb_page_size(head_page);
- local_set(&head_page->entries, ret);
+ ts = head_page->page->time_stamp;
}
if (head_page == cpu_buffer->commit_page)
break;
@@ -2083,7 +2099,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
/* Reset all the subbuffers */
for (i = 0; i < meta->nr_subbufs - 1; i++, rb_inc_page(&head_page)) {
local_set(&head_page->entries, 0);
- local_set(&head_page->page->commit, 0);
+ rb_init_page(head_page->page);
}
}
^ permalink raw reply related
* [PATCH v17 2/5] ring-buffer: Skip invalid sub-buffers when validating persistent ring buffer
From: Masami Hiramatsu (Google) @ 2026-04-22 16:16 UTC (permalink / raw)
To: Steven Rostedt, Catalin Marinas, Will Deacon
Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, Ian Rogers, linux-arm-kernel
In-Reply-To: <177687458572.932171.10907864814735342737.stgit@mhiramat.tok.corp.google.com>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Skip invalid sub-buffers when validating the persistent ring buffer
instead of discarding the entire ring buffer. Only skipped buffers
are invalidated (cleared).
If the cache data in memory fails to be synchronized during a reboot,
the persistent ring buffer may become partially corrupted, but other
sub-buffers may still contain readable event data. Only discard the
subbuffers that are found to be corrupted.
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Changes in v17:
- Fix to use rb_page_size() of rewound pages for entry_bytes.
Changes in v15:
- Skip reader_page loop check on persistent ring buffer because
there can be contiguous empty(invalidated) pages.
- Do not show discarded page number information if it is 0.
Changes in v11:
- Fix a typo.
Changes in v9:
- Add meta->subbuf_size check.
- Fix a typo.
- Handle invalid reader_page case.
Changes in v8:
- Add comment in rb_valudate_buffer()
- Clear the RB_MISSED_* flags in rb_valudate_buffer() instead of
skipping subbuf.
- Remove unused subbuf local variable from rb_cpu_meta_valid().
Changes in v7:
- Combined with Handling RB_MISSED_* flags patch, focus on validation at boot.
- Remove checking subbuffer data when validating metadata, because it should be done
later.
- Do not mark the discarded sub buffer page but just reset it.
Changes in v6:
- Show invalid page detection message once per CPU.
Changes in v5:
- Instead of showing errors for each page, just show the number
of discarded pages at last.
Changes in v3:
- Record missed data event on commit.
---
kernel/trace/ring_buffer.c | 111 ++++++++++++++++++++++++++------------------
1 file changed, 66 insertions(+), 45 deletions(-)
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index b5ed4c72643e..26507b93cf40 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -370,6 +370,12 @@ static __always_inline unsigned int rb_page_commit(struct buffer_page *bpage)
return local_read(&bpage->page->commit);
}
+/* Size is determined by what has been committed */
+static __always_inline unsigned int rb_page_size(struct buffer_page *bpage)
+{
+ return rb_page_commit(bpage) & ~RB_MISSED_MASK;
+}
+
static void free_buffer_page(struct buffer_page *bpage)
{
/* Range pages are not to be freed */
@@ -1762,7 +1768,6 @@ static bool rb_cpu_meta_valid(struct ring_buffer_cpu_meta *meta, int cpu,
unsigned long *subbuf_mask)
{
int subbuf_size = PAGE_SIZE;
- struct buffer_data_page *subbuf;
unsigned long buffers_start;
unsigned long buffers_end;
int i;
@@ -1770,6 +1775,11 @@ static bool rb_cpu_meta_valid(struct ring_buffer_cpu_meta *meta, int cpu,
if (!subbuf_mask)
return false;
+ if (meta->subbuf_size != PAGE_SIZE) {
+ pr_info("Ring buffer boot meta [%d] invalid subbuf_size\n", cpu);
+ return false;
+ }
+
buffers_start = meta->first_buffer;
buffers_end = meta->first_buffer + (subbuf_size * meta->nr_subbufs);
@@ -1786,11 +1796,12 @@ static bool rb_cpu_meta_valid(struct ring_buffer_cpu_meta *meta, int cpu,
return false;
}
- subbuf = rb_subbufs_from_meta(meta);
-
bitmap_clear(subbuf_mask, 0, meta->nr_subbufs);
- /* Is the meta buffers and the subbufs themselves have correct data? */
+ /*
+ * Ensure the meta::buffers array has correct data. The data in each subbufs
+ * are checked later in rb_meta_validate_events().
+ */
for (i = 0; i < meta->nr_subbufs; i++) {
if (meta->buffers[i] < 0 ||
meta->buffers[i] >= meta->nr_subbufs) {
@@ -1798,18 +1809,12 @@ static bool rb_cpu_meta_valid(struct ring_buffer_cpu_meta *meta, int cpu,
return false;
}
- if ((unsigned)local_read(&subbuf->commit) > subbuf_size) {
- pr_info("Ring buffer boot meta [%d] buffer invalid commit\n", cpu);
- return false;
- }
-
if (test_bit(meta->buffers[i], subbuf_mask)) {
pr_info("Ring buffer boot meta [%d] array has duplicates\n", cpu);
return false;
}
set_bit(meta->buffers[i], subbuf_mask);
- subbuf = (void *)subbuf + subbuf_size;
}
return true;
@@ -1873,13 +1878,22 @@ static int rb_read_data_buffer(struct buffer_data_page *dpage, int tail, int cpu
return events;
}
-static int rb_validate_buffer(struct buffer_data_page *dpage, int cpu)
+static int rb_validate_buffer(struct buffer_data_page *dpage, int cpu,
+ struct ring_buffer_cpu_meta *meta)
{
unsigned long long ts;
+ unsigned long tail;
u64 delta;
- int tail;
- tail = local_read(&dpage->commit);
+ /*
+ * When a sub-buffer is recovered from a read, the commit value may
+ * have RB_MISSED_* bits set, as these bits are reset on reuse.
+ * Even after clearing these bits, a commit value greater than the
+ * subbuf_size is considered invalid.
+ */
+ tail = local_read(&dpage->commit) & ~RB_MISSED_MASK;
+ if (tail > meta->subbuf_size)
+ return -1;
return rb_read_data_buffer(dpage, tail, cpu, &ts, &delta);
}
@@ -1890,6 +1904,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
struct buffer_page *head_page, *orig_head;
unsigned long entry_bytes = 0;
unsigned long entries = 0;
+ int discarded = 0;
int ret;
u64 ts;
int i;
@@ -1900,14 +1915,19 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
orig_head = head_page = cpu_buffer->head_page;
/* Do the reader page first */
- ret = rb_validate_buffer(cpu_buffer->reader_page->page, cpu_buffer->cpu);
+ ret = rb_validate_buffer(cpu_buffer->reader_page->page, cpu_buffer->cpu, meta);
if (ret < 0) {
- pr_info("Ring buffer reader page is invalid\n");
- goto invalid;
+ pr_info("Ring buffer meta [%d] invalid reader page detected\n",
+ cpu_buffer->cpu);
+ discarded++;
+ /* Instead of discard whole ring buffer, discard only this sub-buffer. */
+ local_set(&cpu_buffer->reader_page->entries, 0);
+ local_set(&cpu_buffer->reader_page->page->commit, 0);
+ } else {
+ entries += ret;
+ entry_bytes += rb_page_size(cpu_buffer->reader_page);
+ local_set(&cpu_buffer->reader_page->entries, ret);
}
- entries += ret;
- entry_bytes += local_read(&cpu_buffer->reader_page->page->commit);
- local_set(&cpu_buffer->reader_page->entries, ret);
ts = head_page->page->time_stamp;
@@ -1935,7 +1955,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
break;
/* Stop rewind if the page is invalid. */
- ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu);
+ ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu, meta);
if (ret < 0)
break;
@@ -1944,7 +1964,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
if (ret)
local_inc(&cpu_buffer->pages_touched);
entries += ret;
- entry_bytes += rb_page_commit(head_page);
+ entry_bytes += rb_page_size(head_page);
}
if (i)
pr_info("Ring buffer [%d] rewound %d pages\n", cpu_buffer->cpu, i);
@@ -2014,21 +2034,24 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
if (head_page == cpu_buffer->reader_page)
continue;
- ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu);
+ ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu, meta);
if (ret < 0) {
- pr_info("Ring buffer meta [%d] invalid buffer page\n",
- cpu_buffer->cpu);
- goto invalid;
- }
-
- /* If the buffer has content, update pages_touched */
- if (ret)
- local_inc(&cpu_buffer->pages_touched);
-
- entries += ret;
- entry_bytes += local_read(&head_page->page->commit);
- local_set(&head_page->entries, ret);
+ if (!discarded)
+ pr_info("Ring buffer meta [%d] invalid buffer page detected\n",
+ cpu_buffer->cpu);
+ discarded++;
+ /* Instead of discard whole ring buffer, discard only this sub-buffer. */
+ local_set(&head_page->entries, 0);
+ local_set(&head_page->page->commit, 0);
+ } else {
+ /* If the buffer has content, update pages_touched */
+ if (ret)
+ local_inc(&cpu_buffer->pages_touched);
+ entries += ret;
+ entry_bytes += rb_page_size(head_page);
+ local_set(&head_page->entries, ret);
+ }
if (head_page == cpu_buffer->commit_page)
break;
}
@@ -2042,7 +2065,10 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
local_set(&cpu_buffer->entries, entries);
local_set(&cpu_buffer->entries_bytes, entry_bytes);
- pr_info("Ring buffer meta [%d] is from previous boot!\n", cpu_buffer->cpu);
+ pr_info("Ring buffer meta [%d] is from previous boot!", cpu_buffer->cpu);
+ if (discarded)
+ pr_cont(" (%d pages discarded)", discarded);
+ pr_cont("\n");
return;
invalid:
@@ -3329,12 +3355,6 @@ rb_iter_head_event(struct ring_buffer_iter *iter)
return NULL;
}
-/* Size is determined by what has been committed */
-static __always_inline unsigned rb_page_size(struct buffer_page *bpage)
-{
- return rb_page_commit(bpage) & ~RB_MISSED_MASK;
-}
-
static __always_inline unsigned
rb_commit_index(struct ring_buffer_per_cpu *cpu_buffer)
{
@@ -5647,11 +5667,12 @@ __rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer)
again:
/*
* This should normally only loop twice. But because the
- * start of the reader inserts an empty page, it causes
- * a case where we will loop three times. There should be no
- * reason to loop four times (that I know of).
+ * start of the reader inserts an empty page, it causes a
+ * case where we will loop three times. There should be no
+ * reason to loop four times unless the ring buffer is a
+ * recovered persistent ring buffer.
*/
- if (RB_WARN_ON(cpu_buffer, ++nr_loops > 3)) {
+ if (RB_WARN_ON(cpu_buffer, ++nr_loops > 3 && !cpu_buffer->ring_meta)) {
reader = NULL;
goto out;
}
^ permalink raw reply related
* [PATCH v17 1/5] ring-buffer: Flush and stop persistent ring buffer on panic
From: Masami Hiramatsu (Google) @ 2026-04-22 16:16 UTC (permalink / raw)
To: Steven Rostedt, Catalin Marinas, Will Deacon
Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, Ian Rogers, linux-arm-kernel
In-Reply-To: <177687458572.932171.10907864814735342737.stgit@mhiramat.tok.corp.google.com>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
On real hardware, panic and machine reboot may not flush hardware cache
to memory. This means the persistent ring buffer, which relies on a
coherent state of memory, may not have its events written to the buffer
and they may be lost. Moreover, there may be inconsistency with the
counters which are used for validation of the integrity of the
persistent ring buffer which may cause all data to be discarded.
To avoid this issue, stop recording of the ring buffer on panic and
flush the cache of the ring buffer's memory.
Fixes: e645535a954a ("tracing: Add option to use memmapped memory for trace boot instance")
Cc: stable@vger.kernel.org
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
---
Changes in v13:
- Fix a rebase conflict.
Changes in v11:
- Do nothing by default since flush_cache_vmap() does nothing on x86
but it can cause deadlock on some architectures via on_each_cpu()
because other CPUs will be stoppped when panic notifier is called.
Changes in v9:
- Fix typo of & to &&.
- Fix typo of "Generic"
Changes in v6:
- Introduce asm/ring_buffer.h for arch_ring_buffer_flush_range().
- Use flush_cache_vmap() instead of flush_cache_all().
Changes in v5:
- Use ring_buffer_record_off() instead of ring_buffer_record_disable().
- Use flush_cache_all() to ensure flush all cache.
Changes in v3:
- update patch description.
---
arch/alpha/include/asm/Kbuild | 1 +
arch/arc/include/asm/Kbuild | 1 +
arch/arm/include/asm/Kbuild | 1 +
arch/arm64/include/asm/ring_buffer.h | 10 ++++++++++
arch/csky/include/asm/Kbuild | 1 +
arch/hexagon/include/asm/Kbuild | 1 +
arch/loongarch/include/asm/Kbuild | 1 +
arch/m68k/include/asm/Kbuild | 1 +
arch/microblaze/include/asm/Kbuild | 1 +
arch/mips/include/asm/Kbuild | 1 +
arch/nios2/include/asm/Kbuild | 1 +
arch/openrisc/include/asm/Kbuild | 1 +
arch/parisc/include/asm/Kbuild | 1 +
arch/powerpc/include/asm/Kbuild | 1 +
arch/riscv/include/asm/Kbuild | 1 +
arch/s390/include/asm/Kbuild | 1 +
arch/sh/include/asm/Kbuild | 1 +
arch/sparc/include/asm/Kbuild | 1 +
arch/um/include/asm/Kbuild | 1 +
arch/x86/include/asm/Kbuild | 1 +
arch/xtensa/include/asm/Kbuild | 1 +
include/asm-generic/ring_buffer.h | 13 +++++++++++++
kernel/trace/ring_buffer.c | 22 ++++++++++++++++++++++
23 files changed, 65 insertions(+)
create mode 100644 arch/arm64/include/asm/ring_buffer.h
create mode 100644 include/asm-generic/ring_buffer.h
diff --git a/arch/alpha/include/asm/Kbuild b/arch/alpha/include/asm/Kbuild
index 483965c5a4de..b154b4e3dfa8 100644
--- a/arch/alpha/include/asm/Kbuild
+++ b/arch/alpha/include/asm/Kbuild
@@ -5,4 +5,5 @@ generic-y += agp.h
generic-y += asm-offsets.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
generic-y += text-patching.h
diff --git a/arch/arc/include/asm/Kbuild b/arch/arc/include/asm/Kbuild
index 4c69522e0328..483caacc6988 100644
--- a/arch/arc/include/asm/Kbuild
+++ b/arch/arc/include/asm/Kbuild
@@ -5,5 +5,6 @@ generic-y += extable.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
generic-y += parport.h
+generic-y += ring_buffer.h
generic-y += user.h
generic-y += text-patching.h
diff --git a/arch/arm/include/asm/Kbuild b/arch/arm/include/asm/Kbuild
index 03657ff8fbe3..decad5f2c826 100644
--- a/arch/arm/include/asm/Kbuild
+++ b/arch/arm/include/asm/Kbuild
@@ -3,6 +3,7 @@ generic-y += early_ioremap.h
generic-y += extable.h
generic-y += flat.h
generic-y += parport.h
+generic-y += ring_buffer.h
generated-y += mach-types.h
generated-y += unistd-nr.h
diff --git a/arch/arm64/include/asm/ring_buffer.h b/arch/arm64/include/asm/ring_buffer.h
new file mode 100644
index 000000000000..62316c406888
--- /dev/null
+++ b/arch/arm64/include/asm/ring_buffer.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _ASM_ARM64_RING_BUFFER_H
+#define _ASM_ARM64_RING_BUFFER_H
+
+#include <asm/cacheflush.h>
+
+/* Flush D-cache on persistent ring buffer */
+#define arch_ring_buffer_flush_range(start, end) dcache_clean_pop(start, end)
+
+#endif /* _ASM_ARM64_RING_BUFFER_H */
diff --git a/arch/csky/include/asm/Kbuild b/arch/csky/include/asm/Kbuild
index 3a5c7f6e5aac..7dca0c6cdc84 100644
--- a/arch/csky/include/asm/Kbuild
+++ b/arch/csky/include/asm/Kbuild
@@ -9,6 +9,7 @@ generic-y += qrwlock.h
generic-y += qrwlock_types.h
generic-y += qspinlock.h
generic-y += parport.h
+generic-y += ring_buffer.h
generic-y += user.h
generic-y += vmlinux.lds.h
generic-y += text-patching.h
diff --git a/arch/hexagon/include/asm/Kbuild b/arch/hexagon/include/asm/Kbuild
index 1efa1e993d4b..0f887d4238ed 100644
--- a/arch/hexagon/include/asm/Kbuild
+++ b/arch/hexagon/include/asm/Kbuild
@@ -5,4 +5,5 @@ generic-y += extable.h
generic-y += iomap.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
generic-y += text-patching.h
diff --git a/arch/loongarch/include/asm/Kbuild b/arch/loongarch/include/asm/Kbuild
index 9034b583a88a..7e92957baf6a 100644
--- a/arch/loongarch/include/asm/Kbuild
+++ b/arch/loongarch/include/asm/Kbuild
@@ -10,5 +10,6 @@ generic-y += qrwlock.h
generic-y += user.h
generic-y += ioctl.h
generic-y += mmzone.h
+generic-y += ring_buffer.h
generic-y += statfs.h
generic-y += text-patching.h
diff --git a/arch/m68k/include/asm/Kbuild b/arch/m68k/include/asm/Kbuild
index b282e0dd8dc1..62543bf305ff 100644
--- a/arch/m68k/include/asm/Kbuild
+++ b/arch/m68k/include/asm/Kbuild
@@ -3,5 +3,6 @@ generated-y += syscall_table.h
generic-y += extable.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
generic-y += spinlock.h
generic-y += text-patching.h
diff --git a/arch/microblaze/include/asm/Kbuild b/arch/microblaze/include/asm/Kbuild
index 7178f990e8b3..0030309b47ad 100644
--- a/arch/microblaze/include/asm/Kbuild
+++ b/arch/microblaze/include/asm/Kbuild
@@ -5,6 +5,7 @@ generic-y += extable.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
generic-y += parport.h
+generic-y += ring_buffer.h
generic-y += syscalls.h
generic-y += tlb.h
generic-y += user.h
diff --git a/arch/mips/include/asm/Kbuild b/arch/mips/include/asm/Kbuild
index 684569b2ecd6..9771c3d85074 100644
--- a/arch/mips/include/asm/Kbuild
+++ b/arch/mips/include/asm/Kbuild
@@ -12,5 +12,6 @@ generic-y += mcs_spinlock.h
generic-y += parport.h
generic-y += qrwlock.h
generic-y += qspinlock.h
+generic-y += ring_buffer.h
generic-y += user.h
generic-y += text-patching.h
diff --git a/arch/nios2/include/asm/Kbuild b/arch/nios2/include/asm/Kbuild
index 28004301c236..0a2530964413 100644
--- a/arch/nios2/include/asm/Kbuild
+++ b/arch/nios2/include/asm/Kbuild
@@ -5,6 +5,7 @@ generic-y += cmpxchg.h
generic-y += extable.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
generic-y += spinlock.h
generic-y += user.h
generic-y += text-patching.h
diff --git a/arch/openrisc/include/asm/Kbuild b/arch/openrisc/include/asm/Kbuild
index cef49d60d74c..8aa34621702d 100644
--- a/arch/openrisc/include/asm/Kbuild
+++ b/arch/openrisc/include/asm/Kbuild
@@ -8,4 +8,5 @@ generic-y += spinlock_types.h
generic-y += spinlock.h
generic-y += qrwlock_types.h
generic-y += qrwlock.h
+generic-y += ring_buffer.h
generic-y += user.h
diff --git a/arch/parisc/include/asm/Kbuild b/arch/parisc/include/asm/Kbuild
index 4fb596d94c89..d48d158f7241 100644
--- a/arch/parisc/include/asm/Kbuild
+++ b/arch/parisc/include/asm/Kbuild
@@ -4,4 +4,5 @@ generated-y += syscall_table_64.h
generic-y += agp.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
generic-y += user.h
diff --git a/arch/powerpc/include/asm/Kbuild b/arch/powerpc/include/asm/Kbuild
index 2e23533b67e3..805b5aeebb6f 100644
--- a/arch/powerpc/include/asm/Kbuild
+++ b/arch/powerpc/include/asm/Kbuild
@@ -5,4 +5,5 @@ generated-y += syscall_table_spu.h
generic-y += agp.h
generic-y += mcs_spinlock.h
generic-y += qrwlock.h
+generic-y += ring_buffer.h
generic-y += early_ioremap.h
diff --git a/arch/riscv/include/asm/Kbuild b/arch/riscv/include/asm/Kbuild
index bd5fc9403295..7721b63642f4 100644
--- a/arch/riscv/include/asm/Kbuild
+++ b/arch/riscv/include/asm/Kbuild
@@ -14,5 +14,6 @@ generic-y += ticket_spinlock.h
generic-y += qrwlock.h
generic-y += qrwlock_types.h
generic-y += qspinlock.h
+generic-y += ring_buffer.h
generic-y += user.h
generic-y += vmlinux.lds.h
diff --git a/arch/s390/include/asm/Kbuild b/arch/s390/include/asm/Kbuild
index 80bad7de7a04..0c1fc47c3ba0 100644
--- a/arch/s390/include/asm/Kbuild
+++ b/arch/s390/include/asm/Kbuild
@@ -7,3 +7,4 @@ generated-y += unistd_nr.h
generic-y += asm-offsets.h
generic-y += mcs_spinlock.h
generic-y += mmzone.h
+generic-y += ring_buffer.h
diff --git a/arch/sh/include/asm/Kbuild b/arch/sh/include/asm/Kbuild
index 4d3f10ed8275..f0403d3ee8ab 100644
--- a/arch/sh/include/asm/Kbuild
+++ b/arch/sh/include/asm/Kbuild
@@ -3,4 +3,5 @@ generated-y += syscall_table.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
generic-y += parport.h
+generic-y += ring_buffer.h
generic-y += text-patching.h
diff --git a/arch/sparc/include/asm/Kbuild b/arch/sparc/include/asm/Kbuild
index 17ee8a273aa6..49c6bb326b75 100644
--- a/arch/sparc/include/asm/Kbuild
+++ b/arch/sparc/include/asm/Kbuild
@@ -4,4 +4,5 @@ generated-y += syscall_table_64.h
generic-y += agp.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
generic-y += text-patching.h
diff --git a/arch/um/include/asm/Kbuild b/arch/um/include/asm/Kbuild
index 1b9b82bbe322..2a1629ba8140 100644
--- a/arch/um/include/asm/Kbuild
+++ b/arch/um/include/asm/Kbuild
@@ -17,6 +17,7 @@ generic-y += module.lds.h
generic-y += parport.h
generic-y += percpu.h
generic-y += preempt.h
+generic-y += ring_buffer.h
generic-y += runtime-const.h
generic-y += softirq_stack.h
generic-y += switch_to.h
diff --git a/arch/x86/include/asm/Kbuild b/arch/x86/include/asm/Kbuild
index 4566000e15c4..078fd2c0d69d 100644
--- a/arch/x86/include/asm/Kbuild
+++ b/arch/x86/include/asm/Kbuild
@@ -14,3 +14,4 @@ generic-y += early_ioremap.h
generic-y += fprobe.h
generic-y += mcs_spinlock.h
generic-y += mmzone.h
+generic-y += ring_buffer.h
diff --git a/arch/xtensa/include/asm/Kbuild b/arch/xtensa/include/asm/Kbuild
index 13fe45dea296..e57af619263a 100644
--- a/arch/xtensa/include/asm/Kbuild
+++ b/arch/xtensa/include/asm/Kbuild
@@ -6,5 +6,6 @@ generic-y += mcs_spinlock.h
generic-y += parport.h
generic-y += qrwlock.h
generic-y += qspinlock.h
+generic-y += ring_buffer.h
generic-y += user.h
generic-y += text-patching.h
diff --git a/include/asm-generic/ring_buffer.h b/include/asm-generic/ring_buffer.h
new file mode 100644
index 000000000000..201d2aee1005
--- /dev/null
+++ b/include/asm-generic/ring_buffer.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Generic arch dependent ring_buffer macros.
+ */
+#ifndef __ASM_GENERIC_RING_BUFFER_H__
+#define __ASM_GENERIC_RING_BUFFER_H__
+
+#include <linux/cacheflush.h>
+
+/* Flush cache on ring buffer range if needed. Do nothing by default. */
+#define arch_ring_buffer_flush_range(start, end) do { } while (0)
+
+#endif /* __ASM_GENERIC_RING_BUFFER_H__ */
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index cef49f8871d2..b5ed4c72643e 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -7,6 +7,7 @@
#include <linux/ring_buffer_types.h>
#include <linux/sched/isolation.h>
#include <linux/trace_recursion.h>
+#include <linux/panic_notifier.h>
#include <linux/trace_events.h>
#include <linux/ring_buffer.h>
#include <linux/trace_clock.h>
@@ -31,6 +32,7 @@
#include <linux/oom.h>
#include <linux/mm.h>
+#include <asm/ring_buffer.h>
#include <asm/local64.h>
#include <asm/local.h>
#include <asm/setup.h>
@@ -559,6 +561,7 @@ struct trace_buffer {
unsigned long range_addr_start;
unsigned long range_addr_end;
+ struct notifier_block flush_nb;
struct ring_buffer_meta *meta;
@@ -2520,6 +2523,16 @@ static void rb_free_cpu_buffer(struct ring_buffer_per_cpu *cpu_buffer)
kfree(cpu_buffer);
}
+/* Stop recording on a persistent buffer and flush cache if needed. */
+static int rb_flush_buffer_cb(struct notifier_block *nb, unsigned long event, void *data)
+{
+ struct trace_buffer *buffer = container_of(nb, struct trace_buffer, flush_nb);
+
+ ring_buffer_record_off(buffer);
+ arch_ring_buffer_flush_range(buffer->range_addr_start, buffer->range_addr_end);
+ return NOTIFY_DONE;
+}
+
static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,
int order, unsigned long start,
unsigned long end,
@@ -2650,6 +2663,12 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,
mutex_init(&buffer->mutex);
+ /* Persistent ring buffer needs to flush cache before reboot. */
+ if (start && end) {
+ buffer->flush_nb.notifier_call = rb_flush_buffer_cb;
+ atomic_notifier_chain_register(&panic_notifier_list, &buffer->flush_nb);
+ }
+
return_ptr(buffer);
fail_free_buffers:
@@ -2748,6 +2767,9 @@ ring_buffer_free(struct trace_buffer *buffer)
{
int cpu;
+ if (buffer->range_addr_start && buffer->range_addr_end)
+ atomic_notifier_chain_unregister(&panic_notifier_list, &buffer->flush_nb);
+
cpuhp_state_remove_instance(CPUHP_TRACE_RB_PREPARE, &buffer->node);
irq_work_sync(&buffer->irq_work.work);
^ permalink raw reply related
* [PATCH v17 0/5] ring-buffer: Making persistent ring buffers robust
From: Masami Hiramatsu (Google) @ 2026-04-22 16:16 UTC (permalink / raw)
To: Steven Rostedt, Catalin Marinas, Will Deacon
Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, Ian Rogers, linux-arm-kernel
Hi,
Here is the 17th version of improvement patches for making persistent
ring buffers robust to failures.
The previous version is here:
https://lore.kernel.org/all/177547105523.259641.14385891517704197263.stgit@mhiramat.tok.corp.google.com/
This version fixes some review comments from Sashiko[1], which
includes:
[2/5] Fix to use rb_page_size() of rewound pages for entry_bytes.
[3/5] - Fix to verify head_page at first before using its timestamp.
- Reset timestamp if the page is invalid.
[4/5] - In rb_test_inject_invalid_pages(), changed entry_bytes and
idx to unsigned long
- Added NULL checks for cpu_buffer and meta.
- In allocate_trace_buffer(), added a NULL check for tr->name
before comparing it with strcmp.
[5/5] Added NULL check for dpage in rbm_show in ring_buffer.c.
[1] https://sashiko.dev/#/patchset/177552432201.853249.5125045538812833325.stgit%40mhiramat.tok.corp.google.com
Thank you,
Masami Hiramatsu (Google) (5):
ring-buffer: Flush and stop persistent ring buffer on panic
ring-buffer: Skip invalid sub-buffers when validating persistent ring buffer
ring-buffer: Skip invalid sub-buffers when rewinding persistent ring buffer
ring-buffer: Add persistent ring buffer invalid-page inject test
ring-buffer: Show commit numbers in buffer_meta file
arch/alpha/include/asm/Kbuild | 1
arch/arc/include/asm/Kbuild | 1
arch/arm/include/asm/Kbuild | 1
arch/arm64/include/asm/ring_buffer.h | 10 +
arch/csky/include/asm/Kbuild | 1
arch/hexagon/include/asm/Kbuild | 1
arch/loongarch/include/asm/Kbuild | 1
arch/m68k/include/asm/Kbuild | 1
arch/microblaze/include/asm/Kbuild | 1
arch/mips/include/asm/Kbuild | 1
arch/nios2/include/asm/Kbuild | 1
arch/openrisc/include/asm/Kbuild | 1
arch/parisc/include/asm/Kbuild | 1
arch/powerpc/include/asm/Kbuild | 1
arch/riscv/include/asm/Kbuild | 1
arch/s390/include/asm/Kbuild | 1
arch/sh/include/asm/Kbuild | 1
arch/sparc/include/asm/Kbuild | 1
arch/um/include/asm/Kbuild | 1
arch/x86/include/asm/Kbuild | 1
arch/xtensa/include/asm/Kbuild | 1
include/asm-generic/ring_buffer.h | 13 ++
include/linux/ring_buffer.h | 1
kernel/trace/Kconfig | 34 ++++
kernel/trace/ring_buffer.c | 275 ++++++++++++++++++++++++++--------
kernel/trace/trace.c | 4
26 files changed, 290 insertions(+), 67 deletions(-)
create mode 100644 arch/arm64/include/asm/ring_buffer.h
create mode 100644 include/asm-generic/ring_buffer.h
base-commit: 6170922f137231b98fc568571befef63e1edff3f
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply
* Re: [PATCH] net/stmmac: Fix typos: 'tx_undeflow_irq' -> 'tx_underflow_irq'
From: Andrew Lunn @ 2026-04-22 16:15 UTC (permalink / raw)
To: Jakub Raczynski
Cc: netdev, linux-kernel, kuba, davem, andrew+netdev, kernel-janitors,
linux-arm-kernel, linux-stm32
In-Reply-To: <aejYCYObZyFPpLat@AMDC4622.eu.corp.samsungelectronics.net>
On Wed, Apr 22, 2026 at 04:15:37PM +0200, Jakub Raczynski wrote:
> On Wed, Apr 22, 2026 at 02:47:38PM +0200, Andrew Lunn wrote:
> > > I don't see anything wrong with it?
> > > - naming is correct, same as stmmac_extra_stats from common.h, as it
> > > wouldn't compile otherwise
> > > - string length is ok, as max name length is ETH_GSTRING_LEN=32 and it is
> > > not close
> > > - ethtool just polls data from driver and in my tests it is ok
> > > - all instances of 'undeflow' are changed
> > > - 'underflow' semantic is ok, 'undeflow' is just not correct
> > >
> > > Please correct me if I am wrong, but imo no issues with this patch.
> >
> > ABI
> >
> > This name is published as part of the kAPI. You are changing its
> > name. User space could be looking for this name, even thought it has a
> > typo in it.
> >
> > Andrew
> >
> I don't think it is? This part of extra stats (struct stmmac_extra_stats) and
> is not part of standard ABI from
> Documentation/ABI/testing/sysfs-class-net-statistics
> nor is mentioned in
> Documentation/networking/device_drivers/ethernet/stmicro/stmmac.rst
>
> These extra stats are specific to stmmac driver and most of these are more
> than standard
> https://www.kernel.org/doc/html/v7.0/networking/statistics.html#c.rtnl_link_stats64
> This name does not exist outside stmmac driver, so while some application may
> expect this (stmmac specific app), question is should this typo stick?
47dd7a540b8a0 drivers/net/stmmac/stmmac_ethtool.c (Giuseppe Cavallaro 2009-10-14 15:13:45 -0700 81) STMMAC_STAT(tx_undeflow_irq),
It has been exposed to user space for 17 years. In that time, there
could well be stmmac specific apps using it.
Just because it is not documented as ABI does not make it not ABI.
Andrew
^ permalink raw reply
* Re: [PATCH net v2 2/2] net: airoha: Add size check for TX NAPIs in airoha_qdma_cleanup()
From: Lorenzo Bianconi @ 2026-04-22 16:12 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, linux-arm-kernel, linux-mediatek, netdev
In-Reply-To: <20260420-airoha_qdma_init_rx_queue-fix-v2-2-d99347e5c18d@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 2645 bytes --]
> If airoha_qdma_init routine fails before airoha_qdma_tx_irq_init() runs
> successfully for all TX NAPIs, airoha_qdma_cleanup() will
> unconditionally runs netif_napi_del() on TX NAPIs, triggering a NULL
> pointer dereference. Fix the issue relying on q_tx_irq size value to
> check if the TX NAPIs is properly initialized in airoha_qdma_cleanup().
> Moreover, run netif_napi_add_tx() just if irq_q queue is properly
> allocated.
>
> Fixes: 23020f049327 ("net: airoha: Introduce ethernet support for EN7581 SoC")
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
> drivers/net/ethernet/airoha/airoha_eth.c | 11 ++++++++---
> 1 file changed, 8 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index fc79c456743c..fd8c4f817d85 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> @@ -996,8 +996,6 @@ static int airoha_qdma_tx_irq_init(struct airoha_tx_irq_queue *irq_q,
> struct airoha_eth *eth = qdma->eth;
> dma_addr_t dma_addr;
>
> - netif_napi_add_tx(eth->napi_dev, &irq_q->napi,
> - airoha_qdma_tx_napi_poll);
> irq_q->q = dmam_alloc_coherent(eth->dev, size * sizeof(u32),
> &dma_addr, GFP_KERNEL);
> if (!irq_q->q)
> @@ -1007,6 +1005,9 @@ static int airoha_qdma_tx_irq_init(struct airoha_tx_irq_queue *irq_q,
> irq_q->size = size;
> irq_q->qdma = qdma;
>
> + netif_napi_add_tx(eth->napi_dev, &irq_q->napi,
> + airoha_qdma_tx_napi_poll);
> +
> airoha_qdma_wr(qdma, REG_TX_IRQ_BASE(id), dma_addr);
> airoha_qdma_rmw(qdma, REG_TX_IRQ_CFG(id), TX_IRQ_DEPTH_MASK,
> FIELD_PREP(TX_IRQ_DEPTH_MASK, size));
> @@ -1398,8 +1399,12 @@ static void airoha_qdma_cleanup(struct airoha_qdma *qdma)
> }
> }
>
> - for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++)
> + for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) {
> + if (!qdma->q_tx_irq[i].size)
> + continue;
> +
> netif_napi_del(&qdma->q_tx_irq[i].napi);
> + }
>
> for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) {
> if (!qdma->q_tx[i].ndesc)
>
> --
> 2.53.0
>
Commenting the issue reported by Sashiko here:
https://sashiko.dev/#/patchset/20260420-airoha_qdma_init_rx_queue-fix-v2-0-d99347e5c18d%40kernel.org
- Could a similar vulnerability still exist in the TX queue initialization and cleanup path?
This issue is not related to this patch and already fixed here:
https://patchwork.kernel.org/project/netdevbpf/patch/20260417-airoha_qdma_cleanup_tx_queue-fix-net-v4-1-e04bcc2c9642@kernel.org/
Regards,
Lorenzo
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH net v2 1/2] net: airoha: Move ndesc initialization at end of airoha_qdma_init_rx_queue()
From: Lorenzo Bianconi @ 2026-04-22 16:09 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, linux-arm-kernel, linux-mediatek, netdev
In-Reply-To: <20260420-airoha_qdma_init_rx_queue-fix-v2-1-d99347e5c18d@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 2817 bytes --]
> If queue entry or DMA descriptor list allocation fails in
> airoha_qdma_init_rx_queue routine, airoha_qdma_cleanup() will trigger a
> NULL pointer dereference running netif_napi_del() for RX queue NAPIs
> since netif_napi_add() has never been executed to this particular RX NAPI.
> The issue is due to the early ndesc initialization in
> airoha_qdma_init_rx_queue() since airoha_qdma_cleanup() relies on ndesc
> value to check if the queue is properly initialized. Fix the issue moving
> ndesc initialization at end of airoha_qdma_init_tx routine.
> Move page_pool allocation after descriptor list allocation in order to
> avoid memory leaks if desc allocation fails.
>
> Fixes: 23020f049327 ("net: airoha: Introduce ethernet support for EN7581 SoC")
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
> drivers/net/ethernet/airoha/airoha_eth.c | 14 +++++++-------
> 1 file changed, 7 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index e1ab15f1ee7d..fc79c456743c 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> @@ -745,14 +745,18 @@ static int airoha_qdma_init_rx_queue(struct airoha_queue *q,
> dma_addr_t dma_addr;
>
> q->buf_size = PAGE_SIZE / 2;
> - q->ndesc = ndesc;
> q->qdma = qdma;
>
> - q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry),
> + q->entry = devm_kzalloc(eth->dev, ndesc * sizeof(*q->entry),
> GFP_KERNEL);
> if (!q->entry)
> return -ENOMEM;
>
> + q->desc = dmam_alloc_coherent(eth->dev, ndesc * sizeof(*q->desc),
> + &dma_addr, GFP_KERNEL);
> + if (!q->desc)
> + return -ENOMEM;
> +
> q->page_pool = page_pool_create(&pp_params);
> if (IS_ERR(q->page_pool)) {
> int err = PTR_ERR(q->page_pool);
> @@ -761,11 +765,7 @@ static int airoha_qdma_init_rx_queue(struct airoha_queue *q,
> return err;
> }
>
> - q->desc = dmam_alloc_coherent(eth->dev, q->ndesc * sizeof(*q->desc),
> - &dma_addr, GFP_KERNEL);
> - if (!q->desc)
> - return -ENOMEM;
> -
> + q->ndesc = ndesc;
> netif_napi_add(eth->napi_dev, &q->napi, airoha_qdma_rx_napi_poll);
>
> airoha_qdma_wr(qdma, REG_RX_RING_BASE(qid), dma_addr);
>
> --
> 2.53.0
>
As requested, I am commenting the issue reported by Sashiko on this patch:
https://sashiko.dev/#/patchset/20260420-airoha_qdma_init_rx_queue-fix-v2-0-d99347e5c18d%40kernel.org
- Does this code leave a regression in the TX path by omitting the equivalent fix?
This issue is not related to this patch and already fixed here:
https://patchwork.kernel.org/project/netdevbpf/patch/20260417-airoha_qdma_cleanup_tx_queue-fix-net-v4-1-e04bcc2c9642@kernel.org/
Regards,
Lorenzo
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v2 6/6] selftests/resctrl: Remove the definition of the IMC counter config array and imcs.
From: Reinette Chatre @ 2026-04-22 16:05 UTC (permalink / raw)
To: Yifan Wu, tony.luck, Dave.Martin, james.morse, babu.moger, shuah,
tan.shaopeng, fenghuay, ben.horgan, jonathan.cameron, zengheng4,
linux-kernel, linux-arm-kernel, linux-kselftest, linuxarm
Cc: xiaqinxin, prime.zeng, wangyushan12, xuwei5, fanghao11, wangzhou1
In-Reply-To: <20260410093352.3988125-7-wuyifan50@huawei.com>
Hi Yifan,
On 4/10/26 2:33 AM, Yifan Wu wrote:
> The definitions of the imc counter configuration array, imcs, and MAX_IMCS
> are removed.
Please squash these removals with the patch that removes the last
usage.
Reinette
^ permalink raw reply
* Re: [PATCH v2 4/6] selftests/resctrl: Refactor perf event open/close using linked list
From: Reinette Chatre @ 2026-04-22 16:05 UTC (permalink / raw)
To: Yifan Wu, tony.luck, Dave.Martin, james.morse, babu.moger, shuah,
tan.shaopeng, fenghuay, ben.horgan, jonathan.cameron, zengheng4,
linux-kernel, linux-arm-kernel, linux-kselftest, linuxarm
Cc: xiaqinxin, prime.zeng, wangyushan12, xuwei5, fanghao11, wangzhou1
In-Reply-To: <20260410093352.3988125-5-wuyifan50@huawei.com>
Hi Yifan,
On 4/10/26 2:33 AM, Yifan Wu wrote:
> Using linked list when open/close perf event.
>
To make this easier to review and avoid breaking the tests, please split this
patch with changes to open_perf_read_event() located in preparatory patch and
the rest located with all the other changes to switch tests to the list instead of the
array.
Please similarly split the similar patches that follow.
Reinette
^ permalink raw reply
* Re: [PATCH v2 3/6] selftests/resctrl: Refactor the initialization of IMC's perf_event_attr using linked list
From: Reinette Chatre @ 2026-04-22 16:05 UTC (permalink / raw)
To: Yifan Wu, tony.luck, Dave.Martin, james.morse, babu.moger, shuah,
tan.shaopeng, fenghuay, ben.horgan, jonathan.cameron, zengheng4,
linux-kernel, linux-arm-kernel, linux-kselftest, linuxarm
Cc: xiaqinxin, prime.zeng, wangyushan12, xuwei5, fanghao11, wangzhou1
In-Reply-To: <20260410093352.3988125-4-wuyifan50@huawei.com>
Hi Yifan,
On 4/10/26 2:33 AM, Yifan Wu wrote:
> @@ -292,15 +290,17 @@ static int num_of_imcs(void)
>
> int initialize_read_mem_bw_imc(void)
> {
> - int imc;
> + struct imc_counter_config *imc_counter;
> + int ret;
>
> - imcs = num_of_imcs();
> - if (imcs <= 0)
> - return imcs;
> + ret = num_of_imcs();
> + if (ret < 0)
> + return ret;
I see this change from "imcs" to "ret" as a consequence of the semantic change
to num_of_imcs() done in previous patch. Please move this change to be
located with the semantic change that will make that switch easier to understand.
Reinette
^ permalink raw reply
* Re: [PATCH v2 2/6] selftests/resctrl: Refactor the discovery of IMC counters using linked list
From: Reinette Chatre @ 2026-04-22 16:04 UTC (permalink / raw)
To: Yifan Wu, tony.luck, Dave.Martin, james.morse, babu.moger, shuah,
tan.shaopeng, fenghuay, ben.horgan, jonathan.cameron, zengheng4,
linux-kernel, linux-arm-kernel, linux-kselftest, linuxarm
Cc: xiaqinxin, prime.zeng, wangyushan12, xuwei5, fanghao11, wangzhou1
In-Reply-To: <20260410093352.3988125-3-wuyifan50@huawei.com>
Hi Yifan,
On 4/10/26 2:33 AM, Yifan Wu wrote:
> Use linked list to refactor the discovery of IMC counters. The counting
> during the discovery and the check on the upper limit of the number
> of IMC counters are removed.
Please apply the changelog comment of patch #1 to all patches in this
series.
>
> Signed-off-by: Yifan Wu <wuyifan50@huawei.com>
> ---
> tools/testing/selftests/resctrl/resctrl_val.c | 35 ++++++++-----------
> 1 file changed, 14 insertions(+), 21 deletions(-)
>
> diff --git a/tools/testing/selftests/resctrl/resctrl_val.c b/tools/testing/selftests/resctrl/resctrl_val.c
> index d9ae24e9d971..60cda2214c13 100644
> --- a/tools/testing/selftests/resctrl/resctrl_val.c
> +++ b/tools/testing/selftests/resctrl/resctrl_val.c
> @@ -75,7 +75,7 @@ static void read_mem_bw_ioctl_perf_event_ioc_disable(int i)
> * @cas_count_cfg: Config
> * @count: iMC number
Note function description above containing description of @count parameter
> */
> -static void get_read_event_and_umask(char *cas_count_cfg, unsigned int count)
> +static void get_read_event_and_umask(char *cas_count_cfg, struct imc_counter_config *imc_counter)
Since above replaces @count with @imc_counter, please update function description to match.
> {
> char *token[MAX_TOKENS];
> int i = 0;
> @@ -89,9 +89,9 @@ static void get_read_event_and_umask(char *cas_count_cfg, unsigned int count)
> if (!token[i])
> break;
> if (strcmp(token[i], "event") == 0)
> - imc_counters_config[count].event = strtol(token[i + 1], NULL, 16);
> + imc_counter->event = strtol(token[i + 1], NULL, 16);
> if (strcmp(token[i], "umask") == 0)
> - imc_counters_config[count].umask = strtol(token[i + 1], NULL, 16);
> + imc_counter->umask = strtol(token[i + 1], NULL, 16);
> }
> }
>
> @@ -111,12 +111,11 @@ static int open_perf_read_event(int i, int cpu_no)
> return 0;
> }
>
> -static int parse_imc_read_bw_events(char *imc_dir, unsigned int type,
> - unsigned int *count)
> +static int parse_imc_read_bw_events(char *imc_dir, unsigned int type)
> {
> char imc_events_dir[PATH_MAX], imc_counter_cfg[PATH_MAX];
> struct imc_counter_config *imc_counter;
> - unsigned int orig_count = *count;
> + bool found_event = false;
> char cas_count_cfg[1024];
> struct dirent *ep;
> int path_len;
> @@ -166,23 +165,18 @@ static int parse_imc_read_bw_events(char *imc_dir, unsigned int type,
> ksft_perror("Could not get iMC cas count read");
> goto out_close;
> }
> - if (*count >= MAX_IMCS) {
> - ksft_print_msg("Maximum iMC count exceeded\n");
> - goto out_close;
> - }
> imc_counter = calloc(1, sizeof(*imc_counter));
> if (!imc_counter) {
> ksft_perror("Unable to allocate memory for iMC counters\n");
> goto out_close;
> }
>
> - imc_counters_config[*count].type = type;
> - get_read_event_and_umask(cas_count_cfg, *count);
> - /* Do not fail after incrementing *count. */
> - *count += 1;
> + imc_counter->type = type;
> + get_read_event_and_umask(cas_count_cfg, imc_counter);
> list_add(&imc_counter->entry, &imc_counters_list);
> + found_event = true;
> }
> - if (*count == orig_count) {
> + if (!found_event) {
> ksft_print_msg("Unable to find events in %s\n", imc_events_dir);
> goto out_close;
> }
> @@ -193,7 +187,7 @@ static int parse_imc_read_bw_events(char *imc_dir, unsigned int type,
> }
>
> /* Get type and config of an iMC counter's read event. */
> -static int read_from_imc_dir(char *imc_dir, unsigned int *count)
> +static int read_from_imc_dir(char *imc_dir)
> {
> char imc_counter_type[PATH_MAX];
> unsigned int type;
> @@ -221,7 +215,7 @@ static int read_from_imc_dir(char *imc_dir, unsigned int *count)
> ksft_perror("Could not get iMC type");
> return -1;
> }
> - ret = parse_imc_read_bw_events(imc_dir, type, count);
> + ret = parse_imc_read_bw_events(imc_dir, type);
> if (ret) {
> ksft_print_msg("Unable to parse bandwidth event and umask\n");
> return ret;
> @@ -245,7 +239,6 @@ static int read_from_imc_dir(char *imc_dir, unsigned int *count)
> static int num_of_imcs(void)
> {
> char imc_dir[512], *temp;
> - unsigned int count = 0;
> struct dirent *ep;
> int ret;
> DIR *dp;
> @@ -274,7 +267,7 @@ static int num_of_imcs(void)
> if (temp[0] >= '0' && temp[0] <= '9') {
> sprintf(imc_dir, "%s/%s/", DYN_PMU_PATH,
> ep->d_name);
> - ret = read_from_imc_dir(imc_dir, &count);
> + ret = read_from_imc_dir(imc_dir);
> if (ret) {
> closedir(dp);
>
> @@ -283,7 +276,7 @@ static int num_of_imcs(void)
> }
> }
> closedir(dp);
> - if (count == 0) {
> + if (list_empty(&imc_counters_list)) {
> ksft_print_msg("Unable to find iMC counters\n");
>
> return -1;
> @@ -294,7 +287,7 @@ static int num_of_imcs(void)
> return -1;
> }
>
> - return count;
> + return 0;
Since num_of_imcs() now returns a code instead of the number of iMCs found it seems
appropriate to change its name to match, something like "enumerate_imcs()"? Also
please note that num_of_imcs() still has function comments that needs to be updated
to match the new return code. Please check all patches to ensure when a function
signature is changed its description is considered also.
> }
>
> int initialize_read_mem_bw_imc(void)
hmm ... this patch changes how iMCs are enumerated, now placing the data in the new
linked list, but the rest of the code still refers to the (now uninitialized) array.
After this patch the tests are thus broken and if somebody ever needs to do a bisect
and happens to land on this patch is will cause inconvenience.
Please split patches to be incremental changes where tests continue working after
every patch. You can do so with one patch where utilities receive pointer to
array element instead of index as parameter and another patch that switches the
code to use a list. (If this sounds familiar I just copied&pasted the previous
sentence from the v1 review.)
To provide more detail on what this would look like, consider the changes to
get_read_event_and_umask() in this patch:
@@ -75,7 +75,7 @@ static void read_mem_bw_ioctl_perf_event_ioc_disable(int i)
* @cas_count_cfg: Config
* @count: iMC number
*/
-static void get_read_event_and_umask(char *cas_count_cfg, unsigned int count)
+static void get_read_event_and_umask(char *cas_count_cfg, struct imc_counter_config *imc_counter)
{
char *token[MAX_TOKENS];
int i = 0;
@@ -89,9 +89,9 @@ static void get_read_event_and_umask(char *cas_count_cfg, unsigned int count)
if (!token[i])
break;
if (strcmp(token[i], "event") == 0)
- imc_counters_config[count].event = strtol(token[i + 1], NULL, 16);
+ imc_counter->event = strtol(token[i + 1], NULL, 16);
if (strcmp(token[i], "umask") == 0)
- imc_counters_config[count].umask = strtol(token[i + 1], NULL, 16);
+ imc_counter->umask = strtol(token[i + 1], NULL, 16);
}
}
A change like above can be made as first patch in the series in the code that still supports the
array with the callers providing a pointer to array element instead, for example:
get_read_event_and_umask(cas_count_cfg, &imc_counters_config[*count]);
All the changes to utilities similar to above that simply replaces the index with a pointer to
struct imc_counter_config can be grouped into that first patch of the series. For example, the changes to
read_mem_bw_initialize_perf_event_attr(), open_perf_read_event(), read_mem_bw_ioctl_perf_event_ioc_reset_enable(), etc.
Grouping such changes into a preparatory patch is simple to review and reduces the churn when
switching to the list.
Reinette
^ permalink raw reply
* Re: [PATCH v2 1/6] selftests/resctrl: Introduced linked list management for IMC counters
From: Reinette Chatre @ 2026-04-22 16:02 UTC (permalink / raw)
To: Yifan Wu, tony.luck, Dave.Martin, james.morse, babu.moger, shuah,
tan.shaopeng, fenghuay, ben.horgan, jonathan.cameron, zengheng4,
linux-kernel, linux-arm-kernel, linux-kselftest, linuxarm
Cc: xiaqinxin, prime.zeng, wangyushan12, xuwei5, fanghao11, wangzhou1
In-Reply-To: <20260410093352.3988125-2-wuyifan50@huawei.com>
Hi Yifan,
On 4/10/26 2:33 AM, Yifan Wu wrote:
> Added linked list based management for IMC counter configurations,
> allowing the system to dynamically allocate and clean up resources based on
> actual hardware capabilities.
Above provides a motivation for this work but it does not help reviewer understand
what the patch does. Could you please expand with more detail about what the patch
does and since it is incomplete, provide insight into the context of this work to
help review it? For example, above just has "Added linked list based management
for IMC counter configurations" to describe what the patch does (rest is motivation)
but the patch does not actually do this ... it just adds a new and unused data structure
with empty elements in parallel to existing data structures.
Nit: Could you please write all changelogs with an imperative tone? For example,
"Introduced" -> "Introduce" in the subject and "Added" -> "Add" above?
>
> Signed-off-by: Yifan Wu <wuyifan50@huawei.com>
> ---
> tools/testing/selftests/resctrl/mba_test.c | 1 +
> tools/testing/selftests/resctrl/mbm_test.c | 1 +
> tools/testing/selftests/resctrl/resctrl.h | 2 ++
> tools/testing/selftests/resctrl/resctrl_val.c | 20 +++++++++++++++++++
> 4 files changed, 24 insertions(+)
>
> diff --git a/tools/testing/selftests/resctrl/mba_test.c b/tools/testing/selftests/resctrl/mba_test.c
> index 39cee9898359..4bb1a82eb195 100644
> --- a/tools/testing/selftests/resctrl/mba_test.c
> +++ b/tools/testing/selftests/resctrl/mba_test.c
> @@ -166,6 +166,7 @@ static int check_results(void)
>
> static void mba_test_cleanup(void)
> {
> + cleanup_read_mem_bw_imc();
> remove(RESULT_FILE_NAME);
> }
>
> diff --git a/tools/testing/selftests/resctrl/mbm_test.c b/tools/testing/selftests/resctrl/mbm_test.c
> index 6dbbc3b76003..68c89f50a34a 100644
> --- a/tools/testing/selftests/resctrl/mbm_test.c
> +++ b/tools/testing/selftests/resctrl/mbm_test.c
> @@ -125,6 +125,7 @@ static int mbm_measure(const struct user_params *uparams,
>
> static void mbm_test_cleanup(void)
> {
> + cleanup_read_mem_bw_imc();
> remove(RESULT_FILE_NAME);
> }
>
> diff --git a/tools/testing/selftests/resctrl/resctrl.h b/tools/testing/selftests/resctrl/resctrl.h
> index 175101022bf3..a7556cdae0de 100644
> --- a/tools/testing/selftests/resctrl/resctrl.h
> +++ b/tools/testing/selftests/resctrl/resctrl.h
> @@ -24,6 +24,7 @@
> #include <linux/perf_event.h>
> #include <linux/compiler.h>
> #include <linux/bits.h>
> +#include <linux/list.h>
> #include "kselftest.h"
>
> #define MB (1024 * 1024)
> @@ -183,6 +184,7 @@ void mem_flush(unsigned char *buf, size_t buf_size);
> void fill_cache_read(unsigned char *buf, size_t buf_size, bool once);
> ssize_t get_fill_buf_size(int cpu_no, const char *cache_type);
> int initialize_read_mem_bw_imc(void);
> +void cleanup_read_mem_bw_imc(void);
> int measure_read_mem_bw(const struct user_params *uparams,
> struct resctrl_val_param *param, pid_t bm_pid);
> void initialize_mem_bw_resctrl(const struct resctrl_val_param *param,
> diff --git a/tools/testing/selftests/resctrl/resctrl_val.c b/tools/testing/selftests/resctrl/resctrl_val.c
> index f20d2194c35f..d9ae24e9d971 100644
> --- a/tools/testing/selftests/resctrl/resctrl_val.c
> +++ b/tools/testing/selftests/resctrl/resctrl_val.c
> @@ -28,6 +28,7 @@ struct membw_read_format {
> };
>
> struct imc_counter_config {
> + struct list_head entry;
> __u32 type;
> __u64 event;
> __u64 umask;
> @@ -38,6 +39,7 @@ struct imc_counter_config {
> static char mbm_total_path[1024];
> static int imcs;
> static struct imc_counter_config imc_counters_config[MAX_IMCS];
> +LIST_HEAD(imc_counters_list);
> static const struct resctrl_test *current_test;
>
> static void read_mem_bw_initialize_perf_event_attr(int i)
> @@ -113,6 +115,7 @@ static int parse_imc_read_bw_events(char *imc_dir, unsigned int type,
> unsigned int *count)
> {
> char imc_events_dir[PATH_MAX], imc_counter_cfg[PATH_MAX];
> + struct imc_counter_config *imc_counter;
> unsigned int orig_count = *count;
> char cas_count_cfg[1024];
> struct dirent *ep;
> @@ -167,11 +170,17 @@ static int parse_imc_read_bw_events(char *imc_dir, unsigned int type,
> ksft_print_msg("Maximum iMC count exceeded\n");
> goto out_close;
> }
> + imc_counter = calloc(1, sizeof(*imc_counter));
> + if (!imc_counter) {
> + ksft_perror("Unable to allocate memory for iMC counters\n");
> + goto out_close;
> + }
>
> imc_counters_config[*count].type = type;
> get_read_event_and_umask(cas_count_cfg, *count);
> /* Do not fail after incrementing *count. */
> *count += 1;
> + list_add(&imc_counter->entry, &imc_counters_list);
> }
> if (*count == orig_count) {
> ksft_print_msg("Unable to find events in %s\n", imc_events_dir);
Should cleanup_read_mem_bw_imc() be called on error exit path?
> @@ -303,6 +312,17 @@ int initialize_read_mem_bw_imc(void)
> return 0;
> }
>
> +void cleanup_read_mem_bw_imc(void)
> +{
> + struct imc_counter_config *imc_counter, *tmp;
> +
> + list_for_each_entry_safe(imc_counter, tmp,
> + &imc_counters_list, entry) {
Looks like above can fit on one line.
> + list_del(&imc_counter->entry);
> + free(imc_counter);
> + }
> +}
> +
> static void perf_close_imc_read_mem_bw(void)
> {
> int mc;
Reinette
^ permalink raw reply
* Re: [PATCH] iommu/arm-smmu-v3: Allow disabling Stage 1 translation
From: Pranjal Shrivastava @ 2026-04-22 15:44 UTC (permalink / raw)
To: Evangelos Petrongonas
Cc: Jason Gunthorpe, Will Deacon, Robin Murphy, Joerg Roedel,
Nicolin Chen, Lu Baolu, linux-arm-kernel, iommu, linux-kernel,
nh-open-source, Zeev Zilberman
In-Reply-To: <20260422064431.GA49867@dev-dsk-epetron-1c-1d4d9719.eu-west-1.amazon.com>
On Wed, Apr 22, 2026 at 06:44:31AM +0000, Evangelos Petrongonas wrote:
> On Mon, Apr 20, 2026 at 09:40:32AM -0300 Jason Gunthorpe wrote:
> > On Mon, Apr 20, 2026 at 12:32:01PM +0000, Evangelos Petrongonas wrote:
> > > When the hardware advertises both Stage 1 and Stage 2 translation, the
> > > driver prefers Stage 1 for DMA domain allocation and only falls back to
> > > Stage 2 if Stage 1 is not supported.
> > >
> > > Some configurations may want to force Stage 2 translation even when the
> > > hardware supports Stage 1.
> >
> > Why? You really need to explain why for a patch like this.
> >
> > If there really is some HW issue I think it is more appropriate to get
> > an IORT flag or IDR detection that the HW has a problem.
>
> It's not a hardware bug there's no IORT or IDR bit that would make sense
> here.
>
> The motivation is live update of the hypervisor: we want to kexec into a
> new kernel while keeping DMA from passthrough devices flowing, which
> means the SMMU's translation state has to survive the handover. The Live
> Update Orchestrator work [1] and the in-progress "iommu: Add live
> update state preservation" series [2] are building exactly this plumbing
> on top of KHO; [2]'s cover letter calls out Arm SMMUv3 support as future
> work, and an earlier RFC from Amazon [3] sketched the same idea for
> iommufd.
>
> For this use case, Stage 2 is materially easier to persist than Stage 1,
> for structural rather than performance reasons: An S2 STE carries the
> whole translation configuration inline. To hand over an S2 domain, the
> pre-kexec kernel only needs to preserve the stream table pages and the
> S2 pgtable pages. An S1 STE points at a Context Descriptor table and as
> a result Persisting S1 therefore requires preserving the CD table pages
> too, and because the CD is keyed by ASID coordinating ASID identity
> across the handover.
>
> In the long term the plan should be to persist both stages.
> However, until a patch series that properly introduces SMMU support for
> is developed/posted we would like to experiment with S1+S2-capable
> hardware with an easier to implement handover machinery, that relies on
> S2 translations.
>
Hi Evangelos,
We (Google) currently have a series in the works specifically for
arm-smmu-v3 state preservation. Our plan is to post it in phases (S2
preservation first then the S1 + CD series) once the iommu: liveupdate
persistence series has stabilized.
Since the iommu core liveupdate framework itself is still in flux,
it’s a bit premature to accept/merge this patch before both the series.
Furthermore, it must be noted that even if the iommu liveupdate series
is merged, until the framework is fully integrated with the SMMU driver,
liveupdate shall remain essentially non-functional or 'broken' for
drivers that haven't yet implemented the necessary support hooks.
We’d prefer to wait until the core infrastructure is solid so we can
ensure the SMMUv3 implementation aligns perfectly with the final
requirements of the iommu liveupdate persistence series.
That said, we don't mind posting our arm-smmu-v3 series with S2-only
preservation early as an early RFC if that helps align on the design
and implementation details.
Thanks,
Praan
> [1] https://lwn.net/Articles/1021442/ — Live Update Orchestrator
> [2] https://lore.kernel.org/all/20260203220948.2176157-1-skhawaja@google.com/ —
> [PATCH 00/14] iommu: Add live update state preservation
> [3] https://lore.kernel.org/all/20240916113102.710522-1-jgowans@amazon.com/ — [RFC
> PATCH 00/13] Support iommu(fd) persistence for live update
>
> > Jason
>
> Kind Regards,
> Evangelos
>
>
>
> Amazon Web Services Development Center Germany GmbH
> Tamara-Danz-Str. 13
> 10243 Berlin
> Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger
> Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
> Sitz: Berlin
> Ust-ID: DE 365 538 597
^ permalink raw reply
* Re: [PATCH v13 00/48] arm64: Support for Arm CCA in KVM
From: Steven Price @ 2026-04-22 15:38 UTC (permalink / raw)
To: Jiahao zheng
Cc: alexandru.elisei, alpergun, aneesh.kumar, catalin.marinas,
christoffer.dall, fj0570is, gankulkarni, gshan, james.morse,
joey.gouly, kvm, kvmarm, linux-arm-kernel, linux-coco,
linux-kernel, maz, oliver.upton, sdonthineni, suzuki.poulose,
tabba, vannapurve, will, yuzenghui
In-Reply-To: <20260421135145.14789-1-jahao.zheng@gmail.com_quarantine>
On 21/04/2026 14:51, Jiahao zheng wrote:
> Hi Steven,
>
> I've been testing CCA patch series and noticed Realm VM cannot boot successfully when the host is forced to run in nVHE mode (e.g., via `kvm-arm.mode=nvhe`). The kvmtool debug information will be truncated in set_guest_bank_private_gpa.
>
> Currently, in `kvm_ioctl_vcpu_run()`, running a Realm VM (REC) bypasses the standard nVHE EL2 stub. `kvm_rec_enter()` directly executes the SMC instruction to transition to the RMM. Upon returning to the EL1 host, the code falls back to `kvm_vgic_sync_hwstate()`, where the VGIC save operation is explicitly skipped for nVHE. Since the EL2 stub was bypassed, `__vgic_v3_save_state()` is never executed, and `ICH_*_EL2` states are lost.
>
> To resolve this, I have a couple of thoughts:
> 1. If Host nVHE mode is not intended to be supported for Realms:
> Since RME implies ARMv9 which mandates VHE, running a Realm with an nVHE host might just be an unsupported edge case. If so, we should explicitly reject RME initialization or REC creation when `!is_kernel_in_hyp_mode()`. This would cleanly prevent the undefined behavior.
> 2. If Host nVHE mode is intended to be supported:
> Since RMM should remain agnostic to the Non-Secure VGIC states, the burden of saving these states falls strictly on KVM. However, the EL1 host cannot access `ICH_*_EL2`. Therefore, KVM needs to add specific logic for this scenario. We would likely need to route the REC exit through a dedicated nVHE EL2 stub to invoke `__vgic_v3_save_state()` before dropping back to EL1, rather than jumping straight back to `kvm_ioctl_vcpu_run()`.
>
> I might have missed some documentation or comments regarding nVHE restrictions for CCA. If this is an oversight, it would be great to see a check added in the next iteration of the series.
Thanks for the testing. Yes indeed this is an oversight. For now option
1 is what I'm going to go for. There's nothing stopping nVHE mode being
supported but as you note any platform with RME will have VHE so it's
not an immediate priority to support.
One interesting case of nVHE is of course pKVM and for that there needs
to be some significant work to ensure that the EL2 hypervisor
understands the RMM communication and prevent any confused-deputy style
attacks. E.g. the host must not be able to map a pVM's private memory
into a realm guest.
I don't have any immediate plans to work on nVHE - my focus is getting
the basic support merged. But I know there was some interest to ensure
that pKVM and CCA would be able to co-exist on a platform so I expect it
will come in some form or another.
Thanks,
Steve
^ permalink raw reply
* Re: [PATCH] arm64: traps: Add a macro to simplify the condition codes check
From: Mark Rutland @ 2026-04-22 15:16 UTC (permalink / raw)
To: Vladimir Murzin
Cc: smostafa, kees, catalin.marinas, Jinjie Ruan, linux-kernel,
mrigendra.chaubey, maz, leitao, will, linux-arm-kernel
In-Reply-To: <fbd9268f-ace7-42d8-b920-8b6f9b97cf6d@arm.com>
On Wed, Apr 22, 2026 at 03:43:39PM +0100, Vladimir Murzin wrote:
> Hi Jinjie,
>
> On 4/22/26 04:06, Jinjie Ruan wrote:
> >
> > On 3/20/2026 4:28 PM, Jinjie Ruan wrote:
> >> Add DEFINE_COND_CHECK macro to define the simple __check_* functions
> >> to simplify the condition codes check.
> >>
> >> No functional changes.
> > Gentle ping.
> >
> >> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
> >> ---
> >> arch/arm64/kernel/traps.c | 59 ++++++++++-----------------------------
> >> 1 file changed, 15 insertions(+), 44 deletions(-)
> >>
> >> diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
> >> index 914282016069..6216fe9e8e42 100644
> >> --- a/arch/arm64/kernel/traps.c
> >> +++ b/arch/arm64/kernel/traps.c
> >> @@ -49,45 +49,21 @@
> >> #include <asm/system_misc.h>
> >> #include <asm/sysreg.h>
> >>
> >> -static bool __kprobes __check_eq(unsigned long pstate)
> >> -{
> >> - return (pstate & PSR_Z_BIT) != 0;
> >> -}
> >> -
> >> -static bool __kprobes __check_ne(unsigned long pstate)
> >> -{
> >> - return (pstate & PSR_Z_BIT) == 0;
> >> -}
> >> -
> >> -static bool __kprobes __check_cs(unsigned long pstate)
> >> -{
> >> - return (pstate & PSR_C_BIT) != 0;
> >> -}
> >> -
> >> -static bool __kprobes __check_cc(unsigned long pstate)
> >> -{
> >> - return (pstate & PSR_C_BIT) == 0;
> >> -}
> >> -
> >> -static bool __kprobes __check_mi(unsigned long pstate)
> >> -{
> >> - return (pstate & PSR_N_BIT) != 0;
> >> -}
> >> -
> >> -static bool __kprobes __check_pl(unsigned long pstate)
> >> -{
> >> - return (pstate & PSR_N_BIT) == 0;
> >> -}
> >> -
> >> -static bool __kprobes __check_vs(unsigned long pstate)
> >> -{
> >> - return (pstate & PSR_V_BIT) != 0;
> >> -}
> >> -
> >> -static bool __kprobes __check_vc(unsigned long pstate)
> >> -{
> >> - return (pstate & PSR_V_BIT) == 0;
> >> -}
> >> +#define DEFINE_COND_CHECK(name, flag, expected) \
> >> +static bool __kprobes __check_##name(unsigned long pstate) \
> >> +{ \
> >> + return ((pstate & (flag)) != 0) == (expected); \
> >> +}
> >> +
> >> +DEFINE_COND_CHECK(eq, PSR_Z_BIT, true)
> >> +DEFINE_COND_CHECK(ne, PSR_Z_BIT, false)
> >> +DEFINE_COND_CHECK(cs, PSR_C_BIT, true)
> >> +DEFINE_COND_CHECK(cc, PSR_C_BIT, false)
> >> +DEFINE_COND_CHECK(mi, PSR_N_BIT, true)
> >> +DEFINE_COND_CHECK(pl, PSR_N_BIT, false)
> >> +DEFINE_COND_CHECK(vs, PSR_V_BIT, true)
> >> +DEFINE_COND_CHECK(vc, PSR_V_BIT, false)
> >> +DEFINE_COND_CHECK(al, 0, false) /* Always true */
> >>
> >> static bool __kprobes __check_hi(unsigned long pstate)
> >> {
> >> @@ -131,11 +107,6 @@ static bool __kprobes __check_le(unsigned long pstate)
> >> return (temp & PSR_N_BIT) != 0;
> >> }
> >>
> >> -static bool __kprobes __check_al(unsigned long pstate)
> >> -{
> >> - return true;
> >> -}
> >> -
> >> /*
> >> * Note that the ARMv8 ARM calls condition code 0b1111 "nv", but states that
> >> * it behaves identically to 0b1110 ("al").
> >
>
> It looks like we now have a mix of checks implemented via macros
> and others written out explicitly. The existing approach has the
> advantage of being consistent and easy to follow, whereas
> introducing macros here, even if it reduces some duplication,
> adds a bit of cognitive overhead when reading the code.
>
> This may come down to preference, but I think sticking to a
> single, consistent style would make the code easier to scan and
> maintain.
FWIW, I agree. I think it'd be better to leave this as-is for now.
Mark.
^ permalink raw reply
* Re: [PATCH v4 1/8] sframe: Allow kernelspace sframe sections
From: Dylan Hatch @ 2026-04-22 15:15 UTC (permalink / raw)
To: Jens Remus
Cc: Roman Gushchin, Weinan Liu, Will Deacon, Josh Poimboeuf,
Indu Bhagat, Peter Zijlstra, Steven Rostedt, Catalin Marinas,
Jiri Kosina, Mark Rutland, Prasanna Kumar T S M, Puranjay Mohan,
Song Liu, joe.lawrence, linux-toolchains, linux-kernel,
live-patching, linux-arm-kernel, Randy Dunlap, Heiko Carstens
In-Reply-To: <38c9d976-6205-409c-8874-4c9757b25fd6@linux.ibm.com>
On Wed, Apr 22, 2026 at 7:08 AM Jens Remus <jremus@linux.ibm.com> wrote:
>
> On 4/22/2026 12:51 AM, Dylan Hatch wrote:
> > Generalize the sframe lookup code to support kernelspace sections. This
> > is done by defining a SFRAME_LOOKUP option that can be activated
> > separate from HAVE_UNWIND_USER_SFRAME, as there will be other client to
> > this library than just userspace unwind.
> >
> > Sframe section location is now tracked in a separate sec_type field to
> > determine whether user-access functions are necessary to read the sframe
> > data. Relevant type delarations are moved and renamed to reflect the
> > non-user sframe support.
> >
> > Signed-off-by: Dylan Hatch <dylanbhatch@google.com>
>
> With return -EFAULT changed to goto label in DATA_COPY() and DATA_GET():
>
> Reviewed-by: Jens Remus <jremus@linux.ibm.com>
>
> > ---
> > MAINTAINERS | 2 +-
> > arch/Kconfig | 4 +
> > .../{unwind_user_sframe.h => unwind_sframe.h} | 6 +-
> > arch/x86/include/asm/unwind_user.h | 12 +-
> > include/linux/sframe.h | 48 ++--
> > include/linux/unwind_types.h | 46 +++
> > include/linux/unwind_user_types.h | 41 ---
> > kernel/unwind/Makefile | 2 +-
> > kernel/unwind/sframe.c | 270 ++++++++++++------
> > kernel/unwind/user.c | 41 +--
> > 10 files changed, 293 insertions(+), 179 deletions(-)
> > rename arch/x86/include/asm/{unwind_user_sframe.h => unwind_sframe.h} (50%)
> > create mode 100644 include/linux/unwind_types.h
>
> > diff --git a/include/linux/sframe.h b/include/linux/sframe.h
>
> > +enum sframe_sec_type {
> > + SFRAME_KERNEL,
> > + SFRAME_USER,
> > +};
>
> > struct sframe_section {
> > - struct rcu_head rcu;
> > + struct rcu_head rcu;
> > #ifdef CONFIG_DYNAMIC_DEBUG
> > - const char *filename;
> > + const char *filename;
> > #endif
> > - unsigned long sframe_start;
> > - unsigned long sframe_end;
> > - unsigned long text_start;
> > - unsigned long text_end;
> > -
> > - unsigned long fdes_start;
> > - unsigned long fres_start;
> > - unsigned long fres_end;
> > - unsigned int num_fdes;
> > -
> > - signed char ra_off;
> > - signed char fp_off;
> > + enum sframe_sec_type sec_type;
> > + unsigned long sframe_start;
> > + unsigned long sframe_end;
> > + unsigned long text_start;
> > + unsigned long text_end;
> > +
> > + unsigned long fdes_start;
> > + unsigned long fres_start;
> > + unsigned long fres_end;
> > + unsigned int num_fdes;
> > +
> > + signed char ra_off;
> > + signed char fp_off;
> > };
>
> > diff --git a/kernel/unwind/sframe.c b/kernel/unwind/sframe.c
>
> > +#define DATA_COPY(sec, to, from, size, label) \
> > +({ \
> > + switch (sec->sec_type) { \
> > + case SFRAME_KERNEL: \
> > + KERNEL_COPY(to, from, size, label); \
> > + break; \
> > + case SFRAME_USER: \
> > + UNSAFE_USER_COPY(to, from, size, label); \
> > + break; \
>
> I wonder whether it would be worthwhile to come up with an approach
> where this would get evaluated at compile time instead at run time?
> Or is this overengineering? Of course such improvement could be
> made later on, so no need to solve that now.
I had a similar thought when I was writing this patch, but I ended up
deciding to avoid premature optimization before getting feedback. I'd
definitely be interested in improving upon this later on.
>
> Options that came into my mind:
> A) Introduce and pass through a "bool user" parameter, whose value is
> specified in sframe_find_user() and sframe_find_kernel(). Due to
> inlining I would expect that to get any conditions based on that
> to get evaluated at compile time. See below. Downside is the
> ugly additional parameter.
>
> B) Introduce lightweight .c wrappers, e.g. sframe_kernel.c and
> sframe_user.c, that define DATA_GET() and DATA_COPY() and include
> sframe.c. All HAVE_UNWIND_KERNEL_SFRAME code would be moved into
> sframe_kernel.c and likewise all HAVE_UNWIND_USER_SFRAME code into
> sframe_user.c.
(A) definitely sounds simpler to implement. For (B) it seems uncommon
for .c files to include one another. Style-wise, is this something
that is typically allowed (e.g. by checkpatch.pl)?
>
> > + default: \
> > + return -EFAULT; \
>
> goto label; \
>
> Users of DATA_COPY() do expect the macro to branch to the label in case
> of an error and therefore do not evaluate any return value. The
> wrapping then needs also be changed from "({ .. })" to
> "do { ... } while (0)".
>
> > + } \
> > +})
> > +
> > +#define DATA_GET(sec, to, from, type, label) \
> > +({ \
> > + switch (sec->sec_type) { \
> > + case SFRAME_KERNEL: \
> > + KERNEL_GET(to, from, type, label); \
> > + break; \
> > + case SFRAME_USER: \
> > + UNSAFE_USER_GET(to, from, type, label); \
> > + break; \
> > + default: \
> > + return -EFAULT; \
>
> Likewise.
>
> > + } \
> > +})
>
> > +#ifdef CONFIG_HAVE_UNWIND_USER_SFRAME
> > +
> > +int sframe_find_user(unsigned long ip, struct unwind_frame *frame)
> > +{
> > + struct mm_struct *mm = current->mm;
> > + struct sframe_section *sec;
> > + int ret;
> > +
> > + if (!mm)
> > + return -EINVAL;
> > +
> > + guard(srcu)(&sframe_srcu);
> > +
> > + sec = mtree_load(&mm->sframe_mt, ip);
> > + if (!sec)
> > + return -EINVAL;
> > +
> > + if (!user_read_access_begin((void __user *)sec->sframe_start,
> > + sec->sframe_end - sec->sframe_start))
> > + return -EFAULT;
> > +
> > + ret = __sframe_find(sec, ip, frame);
>
> In sframe_find_user() sec->sec_type must be SFRAME_USER. Likewise in
> sframe_find_kernel() it must be SFRAME_KERNEL. So instead of
> introducing sec_type, we could add a parameter
> __sframe_find(..., bool user) and do:
>
> ret = __sframe_find(sec, ip, frame, true);
>
> The downside is that this then requires to pass that flag through
> everywhere... (see below).
>
> > +
> > + user_read_access_end();
> > +
> > + if (ret == -EFAULT) {
> > + dbg_sec("removing bad .sframe section\n");
> > + WARN_ON_ONCE(sframe_remove_section(sec->sframe_start));
> > + }
> > +
> > + return ret;
> > +}
> Regards,
> Jens
> --
> Jens Remus
> Linux on Z Development (D3303)
> jremus@de.ibm.com / jremus@linux.ibm.com
>
> IBM Deutschland Research & Development GmbH; Vorsitzender des Aufsichtsrats: Wolfgang Wendt; Geschäftsführung: David Faller; Sitz der Gesellschaft: Ehningen; Registergericht: Amtsgericht Stuttgart, HRB 243294
> IBM Data Privacy Statement: https://www.ibm.com/privacy/
>
^ permalink raw reply
* Re: [PATCH v7 1/5] clk: bcm: rpi: Manage clock rate in prepare/unprepare callbacks
From: Mark Brown @ 2026-04-22 15:12 UTC (permalink / raw)
To: Linus Torvalds, Stephen Boyd
Cc: Maíra Canal, Michael Turquette, Stephen Boyd,
Nicolas Saenz Julienne, Florian Fainelli, Stefan Wahren,
Maxime Ripard, Melissa Wen, Iago Toral Quiroga, Chema Casanova,
Dave Stevenson, Philipp Zabel, linux-clk, dri-devel,
linux-rpi-kernel, linux-arm-kernel,
Broadcom internal kernel review list, kernel-dev
In-Reply-To: <5f0bec08-f458-4fba-8bf3-06817a100c4c@sirena.org.uk>
[-- Attachment #1: Type: text/plain, Size: 982 bytes --]
On Tue, Mar 31, 2026 at 01:49:24PM +0100, Mark Brown wrote:
> On Thu, Mar 12, 2026 at 06:34:23PM -0300, Maíra Canal wrote:
> > On current firmware versions, RPI_FIRMWARE_SET_CLOCK_STATE doesn't
> > actually power off the clock. To achieve meaningful power savings, the
> > clock rate must be set to the minimum before disabling. This might be
> > fixed in future firmware releases.
>
> I'm seeing boot regressions in -next with NFS root on Raspberry Pi 3B+
> which bisect to this commit. We get a likely unrelated oops from the
> firmware interface and the boot grinds to a halt some time later since
> the ethernet never comes up:
This boot regression, which was originally reported against -next
several weeks ago, is now present in mainline. Maíra quickly provided a
fix:
https://lore.kernel.org/r/20260401111416.562279-2-mcanal@igalia.com
which I have confirmed works but I've not seen any other response to
either that fix or the original thread.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH v4 35/49] KVM: arm64: GICv3: nv: Plug L1 LR sync into deactivation primitive
From: Vishnu Pajjuri @ 2026-04-22 14:57 UTC (permalink / raw)
To: Marc Zyngier
Cc: Fuad Tabba, Joey Gouly, Suzuki K Poulose, Oliver Upton,
Zenghui Yu, Christoffer Dall, Mark Brown, kvm, linux-arm-kernel,
kvmarm, Darren Hart
In-Reply-To: <86eck71o1v.wl-maz@kernel.org>
Hi Marc,
On 22-04-2026 12:25, Marc Zyngier wrote:
> [+ Darren]
>
> On Tue, 31 Mar 2026 10:42:57 +0100,
> Marc Zyngier <maz@kernel.org> wrote:
>>
>> On Tue, 31 Mar 2026 07:31:54 +0100,
>> Vishnu Pajjuri <vishnu@os.amperecomputing.com> wrote:
>>>
>>>>> LOG:
>>>>> [ 164.647367] Call trace:
>>>>> [ 164.647368] smp_call_function_many_cond+0x334/0x7a0 (P)
>>>>> [ 164.647372] smp_call_function_many+0x20/0x40
>>>>> [ 164.647374] kvm_make_all_cpus_request+0xec/0x1b8
>>>>> [ 164.647377] vgic_queue_irq_unlock+0x1c8/0x2c8
>>>>> [ 164.647380] kvm_vgic_inject_irq+0x194/0x1e0
>>>>> [ 164.647381] kvm_vm_ioctl_irq_line+0x170/0x400
>>>>> [ 164.647386] kvm_vm_ioctl+0x7b8/0xc88
>>>>> [ 164.647389] __arm64_sys_ioctl+0xb4/0x118
>>>>> [ 164.647393] invoke_syscall+0x6c/0x100
>>>>> [ 164.647397] el0_svc_common.constprop.0+0x48/0xf0
>>>>> [ 164.647398] do_el0_svc+0x24/0x38
>>>>> [ 164.647400] el0_svc+0x3c/0x170
>>>>> [ 164.647403] el0t_64_sync_handler+0xa0/0xe8
>>>>> [ 164.647405] el0t_64_sync+0x1b0/0x1b8
>>>>
>>>> This trace is about interrupt injection from userspace, not
>>>> deactivation of a HW interrupt.
>>>> None of that makes much sense.
>>>
>>> Although this behavior is puzzling, it matches the trace I typically
>>> observe on L0. After reverting the patch, I was able to boot L2 guests
>>> successfully.
>>
>> Well, this patch fixes real bugs, so it isn't going anywhere.
>>
>> The patch you are reverting addresses the deactivation of a HW
>> interrupt, which is likely to be a timer (that's the only one we
>> support). The stacktrace points to the userspace injection of an SPI.
>>
>> If we need to broadcast IPI, that's because there is no other SPI
>> currently in flight. But if a CPU is not responding to the IPI, what
>> is it doing? How does this interact with the patch you are reverting?
>>
>> Given that I don't know what you're running, how you are running it,
>> that I don't have access to whatever HW you are using, and that you
>> are providing no useful information that'd help me debug this, I will
>> leave it up to you to debug it and come back with a detailed analysis
>> of the problem.
>
> Have you made progress on this? I can't reproduce it at all despite my
> best effort. I'm perfectly happy to help, but you need to give me
> *something* to go on.
Thanks for your support!!
The issue is triggered as soon as the timer interrupt (IRQ 27) is
deactivated. Preventing the deactivation of IRQ 27 during nested VGIC
state transitions prevents the failure from reproducing.
I am currently tracing execution paths and inspecting VGIC state to
determine how disabling this interrupt leads to the observed behavior.
Regards,
-Vishnu.
> Thanks,
>
> M.
>
^ permalink raw reply
* Re: [PATCH] spi: sun6i: Set SPI mode in prepare_message
From: Mark Brown @ 2026-04-22 14:57 UTC (permalink / raw)
To: Kevin Mehall
Cc: Chen-Yu Tsai, Jernej Skrabec, Samuel Holland, Mirko Vogt,
Ralf Schlatterbeck, linux-spi, linux-arm-kernel, linux-sunxi,
linux-kernel
In-Reply-To: <20260420164755.1131645-1-km@kevinmehall.net>
[-- Attachment #1: Type: text/plain, Size: 821 bytes --]
On Mon, Apr 20, 2026 at 10:46:04AM -0600, Kevin Mehall wrote:
> With a GPIO chip select, CS is asserted before entering transfer_one.
> The spi-sun6i driver previously configured the SPI mode (including clock
> polarity) and enabled the bus in transfer_one, which can cause an
> extraneous SCK transition with CS asserted, corrupting the transferred
> data.
> This patch moves the SPI mode configuration and bus enable to the
> spi_prepare_message callback, ensuring that SCK is driven to the correct
> level prior to asserting CS.
> + /* We want to control the chip select manually */
> + reg |= SUN6I_TFR_CTL_CS_MANUAL;
> +
> + sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg);
Might this cause the native chip select to get asserted, we didn't set
up values so it'll have defaults if it wasn't previously configured?
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH 1/4] arm64: signal: Preserve POR_EL0 if poe_context is missing
From: Kevin Brodsky @ 2026-04-22 14:55 UTC (permalink / raw)
To: Will Deacon
Cc: linux-arm-kernel, linux-kernel, Catalin Marinas, Joey Gouly,
Mark Brown, Shuah Khan, linux-kselftest
In-Reply-To: <aei87MmXaEdg8YOf@willie-the-truck>
On 22/04/2026 14:19, Will Deacon wrote:
>> @@ -74,8 +76,12 @@ struct rt_sigframe_user_layout {
>> * This state needs to be carefully managed to ensure that it doesn't cause
>> * uaccess to fail when setting up the signal frame, and the signal handler
>> * itself also expects a well-defined state when entered.
>> + *
>> + * The valid_fields member is a bitfield (see UA_STATE_HAS_*), specifying which
>> + * of the remaining fields is valid (has been set to a value).
>> */
>> struct user_access_state {
>> + unsigned int valid_fields;
>> u64 por_el0;
>> };
> Do you think it would be worth adding some accessors to make it easier
> to keep the flags in sync? For example:
>
> /* Stores por_el0 into uas->por_el0 and sets UA_STATE_HAS_POR_EL0 */
> void set_ua_state_por_el0(struct user_access_state *uas, u64 por_el0);
>
> /*
> * If UA_STATE_HAS_POR_EL0, *por_el0 = uas->por_el0 and return 0.
> * Otherwise, return -ENOENT.
> */
> int get_ua_state_por_el0(struct user_access_state *uas, u64 *por_el0);
>
> WDYT?
I did get a feeling having helpers would be a good idea. I wonder if
getters/setters aren't a bit overkill though, as they make accesses to
the struct more cumbersome and we'd need a pair for every member (unless
we use some macro magic). Maybe it would be sufficient to have say
ua_state_has_field(POR_EL0) to check if the bit is set, and
ua_state_set_field_valid(POR_EL0) to set the bit?
>> @@ -1095,7 +1104,7 @@ SYSCALL_DEFINE0(rt_sigreturn)
>> {
>> struct pt_regs *regs = current_pt_regs();
>> struct rt_sigframe __user *frame;
>> - struct user_access_state ua_state;
>> + struct user_access_state ua_state = {0};
> nit: {} should do (no need for the '0'). Same in setup_rt_frame().
Will change it, I have some vague recollection that GCC and Clang
disagreed about the meaning of {}... but that's probably fixed nowadays.
Thanks for the review!
- Kevin
^ permalink raw reply
* Re: [PATCH] arm64: traps: Add a macro to simplify the condition codes check
From: Vladimir Murzin @ 2026-04-22 14:43 UTC (permalink / raw)
To: Jinjie Ruan, catalin.marinas, will, mark.rutland, kees, maz,
ada.coupriediaz, smostafa, leitao, mrigendra.chaubey,
linux-arm-kernel, linux-kernel
In-Reply-To: <dd2f076b-976c-480d-8414-e6685cad40dd@huawei.com>
Hi Jinjie,
On 4/22/26 04:06, Jinjie Ruan wrote:
>
> On 3/20/2026 4:28 PM, Jinjie Ruan wrote:
>> Add DEFINE_COND_CHECK macro to define the simple __check_* functions
>> to simplify the condition codes check.
>>
>> No functional changes.
> Gentle ping.
>
>> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
>> ---
>> arch/arm64/kernel/traps.c | 59 ++++++++++-----------------------------
>> 1 file changed, 15 insertions(+), 44 deletions(-)
>>
>> diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
>> index 914282016069..6216fe9e8e42 100644
>> --- a/arch/arm64/kernel/traps.c
>> +++ b/arch/arm64/kernel/traps.c
>> @@ -49,45 +49,21 @@
>> #include <asm/system_misc.h>
>> #include <asm/sysreg.h>
>>
>> -static bool __kprobes __check_eq(unsigned long pstate)
>> -{
>> - return (pstate & PSR_Z_BIT) != 0;
>> -}
>> -
>> -static bool __kprobes __check_ne(unsigned long pstate)
>> -{
>> - return (pstate & PSR_Z_BIT) == 0;
>> -}
>> -
>> -static bool __kprobes __check_cs(unsigned long pstate)
>> -{
>> - return (pstate & PSR_C_BIT) != 0;
>> -}
>> -
>> -static bool __kprobes __check_cc(unsigned long pstate)
>> -{
>> - return (pstate & PSR_C_BIT) == 0;
>> -}
>> -
>> -static bool __kprobes __check_mi(unsigned long pstate)
>> -{
>> - return (pstate & PSR_N_BIT) != 0;
>> -}
>> -
>> -static bool __kprobes __check_pl(unsigned long pstate)
>> -{
>> - return (pstate & PSR_N_BIT) == 0;
>> -}
>> -
>> -static bool __kprobes __check_vs(unsigned long pstate)
>> -{
>> - return (pstate & PSR_V_BIT) != 0;
>> -}
>> -
>> -static bool __kprobes __check_vc(unsigned long pstate)
>> -{
>> - return (pstate & PSR_V_BIT) == 0;
>> -}
>> +#define DEFINE_COND_CHECK(name, flag, expected) \
>> +static bool __kprobes __check_##name(unsigned long pstate) \
>> +{ \
>> + return ((pstate & (flag)) != 0) == (expected); \
>> +}
>> +
>> +DEFINE_COND_CHECK(eq, PSR_Z_BIT, true)
>> +DEFINE_COND_CHECK(ne, PSR_Z_BIT, false)
>> +DEFINE_COND_CHECK(cs, PSR_C_BIT, true)
>> +DEFINE_COND_CHECK(cc, PSR_C_BIT, false)
>> +DEFINE_COND_CHECK(mi, PSR_N_BIT, true)
>> +DEFINE_COND_CHECK(pl, PSR_N_BIT, false)
>> +DEFINE_COND_CHECK(vs, PSR_V_BIT, true)
>> +DEFINE_COND_CHECK(vc, PSR_V_BIT, false)
>> +DEFINE_COND_CHECK(al, 0, false) /* Always true */
>>
>> static bool __kprobes __check_hi(unsigned long pstate)
>> {
>> @@ -131,11 +107,6 @@ static bool __kprobes __check_le(unsigned long pstate)
>> return (temp & PSR_N_BIT) != 0;
>> }
>>
>> -static bool __kprobes __check_al(unsigned long pstate)
>> -{
>> - return true;
>> -}
>> -
>> /*
>> * Note that the ARMv8 ARM calls condition code 0b1111 "nv", but states that
>> * it behaves identically to 0b1110 ("al").
>
It looks like we now have a mix of checks implemented via macros
and others written out explicitly. The existing approach has the
advantage of being consistent and easy to follow, whereas
introducing macros here, even if it reduces some duplication,
adds a bit of cognitive overhead when reading the code.
This may come down to preference, but I think sticking to a
single, consistent style would make the code easier to scan and
maintain.
Just my 2p.
Cheers
Vladimir
^ permalink raw reply
* Re: [PATCH] dt-bindings: arm-smmu: qcom:: Fix Hawi compatible placement
From: Krzysztof Kozlowski @ 2026-04-22 14:37 UTC (permalink / raw)
To: Mukesh Ojha, Krzysztof Kozlowski
Cc: Will Deacon, Joerg Roedel, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Robin Murphy, linux-arm-kernel, iommu, devicetree,
linux-kernel
In-Reply-To: <20260422142710.2f6f2oijbsjojbmi@hu-mojha-hyd.qualcomm.com>
On 22/04/2026 16:27, Mukesh Ojha wrote:
>>> - enum:
>>> - qcom,eliza-smmu-500
>>> - qcom,glymur-smmu-500
>>> + - qcom,hawi-smmu-500
>>> - qcom,kaanapali-smmu-500
>>> - qcom,milos-smmu-500
>>> - qcom,qcm2290-smmu-500
>>> @@ -93,7 +94,6 @@ properties:
>>> items:
>>> - enum:
>>> - qcom,glymur-smmu-500
>>> - - qcom,hawi-smmu-500
>>
>> No, why? That's for GPU. Why are you moving GPU compatible to non-GPU place?
>
> Earlier commit was meant for CPUs and not for GPUs, hence moving it Or,
OK, but:
> Are you saying do not do the movement and add one for CPUs ?
yeah, because you might need it in this place for the GPU. Therefore I
would propose to check what compatible you expect for the GPU part and
just add it.
Best regards,
Krzysztof
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox