* [PATCH v2 2/5] mm/swapin: Retry swapin by VMA lock if the lock was released for I/O
From: Barry Song (Xiaomi) @ 2026-04-30 4:04 UTC (permalink / raw)
To: akpm, linux-mm, willy
Cc: david, ljs, liam, vbabka, rppt, surenb, mhocko, jack, pfalcato,
wanglian, chentao, lianux.mm, kunwu.chan, liyangouwen1, chrisl,
kasong, shikemeng, nphamcs, bhe, youngjun.park, linux-arm-kernel,
linux-kernel, loongarch, linuxppc-dev, linux-riscv, linux-s390,
Barry Song (Xiaomi)
In-Reply-To: <20260430040427.4672-1-baohua@kernel.org>
If the current do_swap_page() took the per-VMA lock and we dropped it only
to wait for I/O completion (e.g., use folio_wait_locked()), then when
do_swap_page() is retried after the I/O completes, it should still qualify
for the per-VMA-lock path.
Tested-by: Wang Lian <wanglian@kylinos.cn>
Tested-by: Kunwu Chan <chentao@kylinos.cn>
Reviewed-by: Wang Lian <lianux.mm@gmail.com>
Reviewed-by: Kunwu Chan <kunwu.chan@gmail.com>
Signed-off-by: Barry Song (Xiaomi) <baohua@kernel.org>
---
mm/memory.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/mm/memory.c b/mm/memory.c
index 199214f8de08..00ee1599d637 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -4791,6 +4791,7 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
unsigned long page_idx;
unsigned long address;
pte_t *ptep;
+ bool retry_by_vma_lock = false;
if (!pte_unmap_same(vmf))
goto out;
@@ -4896,8 +4897,13 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
swapcache = folio;
ret |= folio_lock_or_retry(folio, vmf);
- if (ret & VM_FAULT_RETRY)
+ if (ret & VM_FAULT_RETRY) {
+ if (fault_flag_allow_retry_first(vmf->flags) &&
+ !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT) &&
+ (vmf->flags & FAULT_FLAG_VMA_LOCK))
+ retry_by_vma_lock = true;
goto out_release;
+ }
page = folio_file_page(folio, swp_offset(entry));
/*
@@ -5182,7 +5188,7 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
}
if (si)
put_swap_device(si);
- return ret;
+ return ret | (retry_by_vma_lock ? VM_FAULT_RETRY_VMA : 0);
}
static bool pte_range_none(pte_t *pte, int nr_pages)
--
2.39.3 (Apple Git-146)
^ permalink raw reply related
* [PATCH v2 1/5] mm/filemap: Retry fault by VMA lock if the lock was released for I/O
From: Barry Song (Xiaomi) @ 2026-04-30 4:04 UTC (permalink / raw)
To: akpm, linux-mm, willy
Cc: david, ljs, liam, vbabka, rppt, surenb, mhocko, jack, pfalcato,
wanglian, chentao, lianux.mm, kunwu.chan, liyangouwen1, chrisl,
kasong, shikemeng, nphamcs, bhe, youngjun.park, linux-arm-kernel,
linux-kernel, loongarch, linuxppc-dev, linux-riscv, linux-s390,
Barry Song
In-Reply-To: <20260430040427.4672-1-baohua@kernel.org>
From: Oven Liyang <liyangouwen1@oppo.com>
If the current page fault is using the per-VMA lock, and we only released
the lock to wait for I/O completion (e.g., using folio_lock()), then when
the fault is retried after the I/O completes, it should still qualify for
the per-VMA-lock path.
Acked-by: Pedro Falcato <pfalcato@suse.de>
Tested-by: Wang Lian <wanglian@kylinos.cn>
Tested-by: Kunwu Chan <chentao@kylinos.cn>
Reviewed-by: Wang Lian <lianux.mm@gmail.com>
Reviewed-by: Kunwu Chan <kunwu.chan@gmail.com>
Signed-off-by: Oven Liyang <liyangouwen1@oppo.com>
Co-developed-by: Barry Song <baohua@kernel.org>
Signed-off-by: Barry Song <baohua@kernel.org>
---
arch/arm/mm/fault.c | 5 +++++
arch/arm64/mm/fault.c | 5 +++++
arch/loongarch/mm/fault.c | 4 ++++
arch/powerpc/mm/fault.c | 5 ++++-
arch/riscv/mm/fault.c | 4 ++++
arch/s390/mm/fault.c | 4 ++++
arch/x86/mm/fault.c | 4 ++++
include/linux/mm_types.h | 9 +++++----
mm/filemap.c | 5 ++++-
9 files changed, 39 insertions(+), 6 deletions(-)
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
index e62cc4be5adf..5971e02845f7 100644
--- a/arch/arm/mm/fault.c
+++ b/arch/arm/mm/fault.c
@@ -391,6 +391,7 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
if (!(flags & FAULT_FLAG_USER))
goto lock_mmap;
+retry_vma:
vma = lock_vma_under_rcu(mm, addr);
if (!vma)
goto lock_mmap;
@@ -420,6 +421,10 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
goto no_context;
return 0;
}
+
+ /* If the first try is only about waiting for the I/O to complete */
+ if (fault & VM_FAULT_RETRY_VMA)
+ goto retry_vma;
lock_mmap:
retry:
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 739800835920..d0362a3e11b7 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -673,6 +673,7 @@ static int __kprobes do_page_fault(unsigned long far, unsigned long esr,
if (!(mm_flags & FAULT_FLAG_USER))
goto lock_mmap;
+retry_vma:
vma = lock_vma_under_rcu(mm, addr);
if (!vma)
goto lock_mmap;
@@ -719,6 +720,10 @@ static int __kprobes do_page_fault(unsigned long far, unsigned long esr,
goto no_context;
return 0;
}
+
+ /* If the first try is only about waiting for the I/O to complete */
+ if (fault & VM_FAULT_RETRY_VMA)
+ goto retry_vma;
lock_mmap:
retry:
diff --git a/arch/loongarch/mm/fault.c b/arch/loongarch/mm/fault.c
index 2c93d33356e5..738f495560c0 100644
--- a/arch/loongarch/mm/fault.c
+++ b/arch/loongarch/mm/fault.c
@@ -219,6 +219,7 @@ static void __kprobes __do_page_fault(struct pt_regs *regs,
if (!(flags & FAULT_FLAG_USER))
goto lock_mmap;
+retry_vma:
vma = lock_vma_under_rcu(mm, address);
if (!vma)
goto lock_mmap;
@@ -265,6 +266,9 @@ static void __kprobes __do_page_fault(struct pt_regs *regs,
no_context(regs, write, address);
return;
}
+ /* If the first try is only about waiting for the I/O to complete */
+ if (fault & VM_FAULT_RETRY_VMA)
+ goto retry_vma;
lock_mmap:
retry:
diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
index 806c74e0d5ab..cb7ffc20c760 100644
--- a/arch/powerpc/mm/fault.c
+++ b/arch/powerpc/mm/fault.c
@@ -487,6 +487,7 @@ static int ___do_page_fault(struct pt_regs *regs, unsigned long address,
if (!(flags & FAULT_FLAG_USER))
goto lock_mmap;
+retry_vma:
vma = lock_vma_under_rcu(mm, address);
if (!vma)
goto lock_mmap;
@@ -516,7 +517,9 @@ static int ___do_page_fault(struct pt_regs *regs, unsigned long address,
if (fault_signal_pending(fault, regs))
return user_mode(regs) ? 0 : SIGBUS;
-
+ /* If the first try is only about waiting for the I/O to complete */
+ if (fault & VM_FAULT_RETRY_VMA)
+ goto retry_vma;
lock_mmap:
/* When running in the kernel we expect faults to occur only to
diff --git a/arch/riscv/mm/fault.c b/arch/riscv/mm/fault.c
index 04ed6f8acae4..b94cf57c2b9a 100644
--- a/arch/riscv/mm/fault.c
+++ b/arch/riscv/mm/fault.c
@@ -347,6 +347,7 @@ void handle_page_fault(struct pt_regs *regs)
if (!(flags & FAULT_FLAG_USER))
goto lock_mmap;
+retry_vma:
vma = lock_vma_under_rcu(mm, addr);
if (!vma)
goto lock_mmap;
@@ -376,6 +377,9 @@ void handle_page_fault(struct pt_regs *regs)
no_context(regs, addr);
return;
}
+ /* If the first try is only about waiting for the I/O to complete */
+ if (fault & VM_FAULT_RETRY_VMA)
+ goto retry_vma;
lock_mmap:
retry:
diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c
index 191cc53caead..e0576e629f65 100644
--- a/arch/s390/mm/fault.c
+++ b/arch/s390/mm/fault.c
@@ -294,6 +294,7 @@ static void do_exception(struct pt_regs *regs, int access)
flags |= FAULT_FLAG_WRITE;
if (!(flags & FAULT_FLAG_USER))
goto lock_mmap;
+retry_vma:
vma = lock_vma_under_rcu(mm, address);
if (!vma)
goto lock_mmap;
@@ -318,6 +319,9 @@ static void do_exception(struct pt_regs *regs, int access)
handle_fault_error_nolock(regs, 0);
return;
}
+ /* If the first try is only about waiting for the I/O to complete */
+ if (fault & VM_FAULT_RETRY_VMA)
+ goto retry_vma;
lock_mmap:
retry:
vma = lock_mm_and_find_vma(mm, address, regs);
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index f0e77e084482..0589fc693eea 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -1322,6 +1322,7 @@ void do_user_addr_fault(struct pt_regs *regs,
if (!(flags & FAULT_FLAG_USER))
goto lock_mmap;
+retry_vma:
vma = lock_vma_under_rcu(mm, address);
if (!vma)
goto lock_mmap;
@@ -1351,6 +1352,9 @@ void do_user_addr_fault(struct pt_regs *regs,
ARCH_DEFAULT_PKEY);
return;
}
+ /* If the first try is only about waiting for the I/O to complete */
+ if (fault & VM_FAULT_RETRY_VMA)
+ goto retry_vma;
lock_mmap:
retry:
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index a308e2c23b82..5907200ea587 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -1678,10 +1678,11 @@ enum vm_fault_reason {
VM_FAULT_NOPAGE = (__force vm_fault_t)0x000100,
VM_FAULT_LOCKED = (__force vm_fault_t)0x000200,
VM_FAULT_RETRY = (__force vm_fault_t)0x000400,
- VM_FAULT_FALLBACK = (__force vm_fault_t)0x000800,
- VM_FAULT_DONE_COW = (__force vm_fault_t)0x001000,
- VM_FAULT_NEEDDSYNC = (__force vm_fault_t)0x002000,
- VM_FAULT_COMPLETED = (__force vm_fault_t)0x004000,
+ VM_FAULT_RETRY_VMA = (__force vm_fault_t)0x000800,
+ VM_FAULT_FALLBACK = (__force vm_fault_t)0x001000,
+ VM_FAULT_DONE_COW = (__force vm_fault_t)0x002000,
+ VM_FAULT_NEEDDSYNC = (__force vm_fault_t)0x004000,
+ VM_FAULT_COMPLETED = (__force vm_fault_t)0x008000,
VM_FAULT_HINDEX_MASK = (__force vm_fault_t)0x0f0000,
};
diff --git a/mm/filemap.c b/mm/filemap.c
index ab34cab2416a..a045b771e8de 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -3525,6 +3525,7 @@ vm_fault_t filemap_fault(struct vm_fault *vmf)
struct folio *folio;
vm_fault_t ret = 0;
bool mapping_locked = false;
+ bool retry_by_vma_lock = false;
max_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
if (unlikely(index >= max_idx))
@@ -3621,6 +3622,8 @@ vm_fault_t filemap_fault(struct vm_fault *vmf)
*/
if (fpin) {
folio_unlock(folio);
+ if (vmf->flags & FAULT_FLAG_VMA_LOCK)
+ retry_by_vma_lock = true;
goto out_retry;
}
if (mapping_locked)
@@ -3671,7 +3674,7 @@ vm_fault_t filemap_fault(struct vm_fault *vmf)
filemap_invalidate_unlock_shared(mapping);
if (fpin)
fput(fpin);
- return ret | VM_FAULT_RETRY;
+ return ret | VM_FAULT_RETRY | (retry_by_vma_lock ? VM_FAULT_RETRY_VMA : 0);
}
EXPORT_SYMBOL(filemap_fault);
--
2.39.3 (Apple Git-146)
^ permalink raw reply related
* [PATCH v2 0/5] mm: reduce mmap_lock contention and improve page fault performance
From: Barry Song (Xiaomi) @ 2026-04-30 4:04 UTC (permalink / raw)
To: akpm, linux-mm, willy
Cc: david, ljs, liam, vbabka, rppt, surenb, mhocko, jack, pfalcato,
wanglian, chentao, lianux.mm, kunwu.chan, liyangouwen1, chrisl,
kasong, shikemeng, nphamcs, bhe, youngjun.park, linux-arm-kernel,
linux-kernel, loongarch, linuxppc-dev, linux-riscv, linux-s390,
Barry Song (Xiaomi)
Oven observed most mmap_lock contention and priority inversion
come from page fault retries after waiting for I/O completion.
Oven subsequently raised the following idea:
There is no need to always fall back to mmap_lock when the per-VMA lock
is released only to wait for the page cache to become ready. On a page
fault retry, the per-VMA lock can still be reused.
We believe the same should also apply to anonymous folios. However, there
is a case where I/O has completed but we fail to acquire the folio lock
because a concurrent thread may be installing PTEs for the folio. This
is expected to be short-lived, so retrying the page fault is unnecessary.
This patchset handles two cases:
(1) If we need to wait for I/O completion, we still drop the per-VMA lock, as
current page fault handling already does. Holding it for too long may introduce
various priority inversion issues on mobile devices. After I/O completes, we
retry the page fault with the per-VMA lock, rather than falling back to
mmap_lock.
(2) If I/O has already completed and the folio is up to date, the wait is
likely due to a concurrent PTE installation. In this case, we keep the
per-VMA lock and avoid retrying the page fault.
With (1), the dramatically reduced mmap_lock contention leads to a
significant improvement in Douyin performance. Oven’s data is shown
below.
Douyin (the Chinese version of TikTok) warm start on a smartphone with
8GB RAM.
== mmap_lock Acquisitions And Wait Time ==
Metric Before (Avg) After (Avg) Change
------------------------------------------------------------------------
Read Lock Count 20,010 5,719 -71.42%
Read Total Wait (us) 10,695,877 408,436 -96.18%
Read Avg Wait (us) 534.00 71.00 -86.70%
Write Lock Count 838 909 +8.47%
Write Total Wait (us) 501,293 97,633 -80.52%
Write Avg Wait (us) 598.00 107.00 -82.11%
== Read Lock Waiting Time Distribution of mmap_lock ==
Range (us) Before (Avg) After (Avg) Change
------------------------------------------------------------------------
[0, 1) 9,927 4,286 -56.82%
[1, 10) 9,179 1,327 -85.54%
[10, 100) 191 88 -53.93%
[100, 1000) 57 6 -89.47%
[1000, 10000) 328 9 -97.26%
[10000, 100000) 328 6 -98.17%
[100000, 1000000) 0 0 N/A
[1000000, +) 0 0 N/A
== Write Lock Waiting Time Distribution of mmap_lock ==
Range (us) Before (Avg) After (Avg) Change
------------------------------------------------------------------------
[0, 1) 250 300 +20.00%
[1, 10) 483 556 +15.11%
[10, 100) 52 41 -21.15%
[100, 1000) 12 5 -58.33%
[1000, 10000) 22 4 -81.82%
[10000, 100000) 16 1 -93.75%
[100000, 1000000) 0 0 N/A
[1000000, +) 0 0 N/A
After the optimization, the number of read lock acquisitions is
significantly reduced, and both lock waiting time and tail latency are
dramatically improved.
Kunwu and Lian also developed a model to capture the situation described
by Matthew [1], where a memcg with limited memory may fail to make
progress. This happens because after I/O is initiated on the first page
fault, the folios may be reclaimed by the time of the retry, leaving the
workload with little or no forward progress.
A stress setup made by Kunwu and Lian as follows:
* 256-core x86 system
* 500 threads continuously faulting on 16MB files
The model was running within a memcg with limited memory,
as shown below:
systemd-run --scope -p MemoryHigh=1G -p MemoryMax=1.2G -p MemorySwapMax=0 \
--unit=mmap-thrash-$$ ./mmap_lock & \
TEST_PID=$!
The reproducer code is shown below:
#define THREADS 500
#define FILE_SIZE (16 * 1024 * 1024) /* 16MB */
static _Atomic int g_stop = 0;
#define RUN_SECONDS 600
struct worker_arg {
long id;
uint64_t *counts;
};
void *worker(void *arg)
{
struct worker_arg *wa = (struct worker_arg *)arg;
long id = wa->id;
char path[64];
uint64_t local_rounds = 0;
snprintf(path, sizeof(path), "./test_file_%d_%ld.dat",
getpid(), id);
int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd < 0) return NULL;
if (ftruncate(fd, FILE_SIZE) < 0) {
close(fd); return NULL;
}
while (!atomic_load_explicit(&g_stop, memory_order_relaxed)) {
char *f_map = mmap(NULL, FILE_SIZE, PROT_READ,
MAP_SHARED, fd, 0);
if (f_map != MAP_FAILED) {
/* Pure page cache thrashing */
for (int i = 0; i < FILE_SIZE; i += 4096) {
volatile unsigned char c =
(unsigned char)f_map[i];
(void)c;
}
munmap(f_map, FILE_SIZE);
local_rounds++;
}
}
wa->counts[id] = local_rounds;
close(fd);
unlink(path);
return NULL;
}
int main(void)
{
printf("Pure File Thrashing Started. PID: %d\n", getpid());
pthread_t t[THREADS];
uint64_t local_counts[THREADS];
memset(local_counts, 0, sizeof(local_counts));
struct worker_arg args[THREADS];
for (long i = 0; i < THREADS; i++) {
args[i].id = i;
args[i].counts = local_counts;
pthread_create(&t[i], NULL, worker, &args[i]);
}
sleep(RUN_SECONDS);
atomic_store_explicit(&g_stop, 1, memory_order_relaxed);
for (int i = 0; i < THREADS; i++) pthread_join(t[i], NULL);
uint64_t total = 0;
for (int i = 0; i < THREADS; i++) total += local_counts[i];
printf("Total rounds : %llu\n", (unsigned long long)total);
printf("Throughput : %.2f rounds/sec\n",
(double)total / RUN_SECONDS);
return 0;
}
They also added temporary counters in page fault retries [2]:
- RETRY_IO_MISS : folio not present after I/O completion
- RETRY_MMAP_DROP : retry fallback due to waiting for I/O
Their results are as follows:
| Case | Total Rounds | Throughput | Miss/Drop(%) | RETRY_MMAP_DROP | RETRY_IO_MISS |
| ------------------- | ------------ | ---------- | ------------ | --------------- | ------------- |
| Baseline (Run 1) | 22,711 | 37.85 /s | 45.04 | 970,078 | 436,956 |
| Baseline (Run 2) | 23,530 | 39.22 /s | 44.96 | 972,043 | 437,077 |
| With Series (Run A) | 54,428 | 90.71 /s | 1.69 | 1,204,124 | 20,398 |
| With Series (Run B) | 35,949 | 59.91 /s | 0.03 | 327,023 | 99 |
Without this series, nearly half of the retries fail to observe completed
I/O results, leading to significant CPU and I/O waste. With the finer-
grained VMA lock, faulting threads avoid the heavily contended mmap_lock
during retries and are therefore able to complete the page fault.
With (2), there is a clear improvement in swap-in bandwidth in a model
with five threads issuing MADV_PAGEOUT-based swap-outs and five threads
performing swap-ins on a 100MB anonymous mmap VMA.
#define SIZE (100 * 1024 * 1024)
#define PAGE_SIZE 4096
#define WRITER_THREADS 5
#define READER_THREADS 5
#define RUN_SECONDS 30
static uint8_t *buf;
static atomic_ulong pageout_rounds = 0;
static atomic_ulong swapin_rounds = 0;
static atomic_int stop_flag = 0;
static void *pageout_thread(void *arg)
{
(void)arg;
while (!atomic_load(&stop_flag)) {
if (madvise(buf, SIZE, MADV_PAGEOUT) == 0) {
atomic_fetch_add(&pageout_rounds, 1);
}
}
return NULL;
}
static void *reader_thread(void *arg)
{
(void)arg;
volatile uint64_t sum = 0;
while (!atomic_load(&stop_flag)) {
for (size_t i = 0; i < SIZE; i += PAGE_SIZE) {
sum += buf[i];
}
/* One full pass over 100MB, counted as one swap-in round (approximate) */
atomic_fetch_add(&swapin_rounds, 1);
}
return NULL;
}
int main(void)
{
pthread_t writers[WRITER_THREADS];
pthread_t readers[READER_THREADS];
buf = mmap(NULL, SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (buf == MAP_FAILED) {
exit(EXIT_FAILURE);
}
memset(buf, 0, SIZE);
for (int i = 0; i < WRITER_THREADS; i++) {
if (pthread_create(&writers[i], NULL, pageout_thread, NULL) != 0) {
perror("pthread_create");
exit(EXIT_FAILURE);
}
}
for (int i = 0; i < READER_THREADS; i++) {
if (pthread_create(&readers[i], NULL, reader_thread, NULL) != 0) {
perror("pthread_create");
exit(EXIT_FAILURE);
}
}
sleep(RUN_SECONDS);
atomic_store(&stop_flag, 1);
for (int i = 0; i < WRITER_THREADS; i++)
pthread_join(writers[i], NULL);
for (int i = 0; i < READER_THREADS; i++)
pthread_join(readers[i], NULL);
printf("=== Result (30s) ===\n");
printf("Pageout rounds: %lu\n", pageout_rounds);
printf("Swap-in rounds (approx): %lu\n", swapin_rounds);
munmap(buf, SIZE);
return 0;
}
W/o patches:
=== Result (30s) ===
Pageout rounds: 1324847
Swap-in rounds (approx): 874
W/patches:
=== Result (30s) ===
Pageout rounds: 1330550
Swap-in rounds (approx): 1017
[1] https://lore.kernel.org/linux-mm/aSip2mWX13sqPW_l@casper.infradead.org/
[2] https://github.com/lianux-mm/ioretry_test/
-v2:
* collect tags from Pedro, Kunwu and Lian, thanks!
* handle case (2), for uptodate folios, don't retry PF
-RFC:
https://lore.kernel.org/linux-mm/20251127011438.6918-1-21cnbao@gmail.com/
Barry Song (Xiaomi) (4):
mm/swapin: Retry swapin by VMA lock if the lock was released for I/O
mm: Move folio_lock_or_retry() and drop __folio_lock_or_retry()
mm: Don't retry page fault if folio is uptodate during swap-in
mm/filemap: Avoid retrying page faults on uptodate folios in filemap
faults
Oven Liyang (1):
mm/filemap: Retry fault by VMA lock if the lock was released for I/O
arch/arm/mm/fault.c | 5 +++
arch/arm64/mm/fault.c | 5 +++
arch/loongarch/mm/fault.c | 4 +++
arch/powerpc/mm/fault.c | 5 ++-
arch/riscv/mm/fault.c | 4 +++
arch/s390/mm/fault.c | 4 +++
arch/x86/mm/fault.c | 4 +++
include/linux/mm_types.h | 9 ++---
include/linux/pagemap.h | 17 ----------
mm/filemap.c | 57 ++++++-------------------------
mm/memory.c | 70 +++++++++++++++++++++++++++++++++++++--
11 files changed, 114 insertions(+), 70 deletions(-)
--
* The work began during my collaboration with OPPO and has continued through
my current collaboration with Xiaomi. Although the OPPO collaboration has
ended, OPPO still deserves more than half of the credit for this series,
if any credit is to be assigned.
2.39.3 (Apple Git-146)
^ permalink raw reply
* [PATCH] arm64/mm: Rename ptdesc_t
From: Anshuman Khandual @ 2026-04-30 3:49 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Anshuman Khandual, Catalin Marinas, Will Deacon,
David Hildenbrand, Mike Rapoport, linux-efi, linux-kernel
ptdesc_t sounds very similar to the core MM struct ptdesc which is actually
the memory descriptor for page table allocations. Hence rename this typedef
element as pxxval_t instead for better clarity and separation.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: David Hildenbrand <david@kernel.org>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: linux-efi@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Suggested-by: David Hildenbrand (Arm) <david@kernel.org>
Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
---
This patch applies on v7.1-rc1
Took the liberty in including David's tag from an internal discussion.
arch/arm64/include/asm/io.h | 2 +-
arch/arm64/include/asm/pgtable-types.h | 14 +++++++-------
arch/arm64/include/asm/ptdump.h | 8 ++++----
arch/arm64/include/asm/tlbflush.h | 4 ++--
arch/arm64/kernel/efi.c | 4 ++--
arch/arm64/kernel/pi/map_kernel.c | 2 +-
arch/arm64/kernel/pi/map_range.c | 4 ++--
arch/arm64/kernel/pi/pi.h | 2 +-
arch/arm64/mm/mmap.c | 4 ++--
arch/arm64/mm/pageattr.c | 2 +-
arch/arm64/mm/ptdump.c | 2 +-
11 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h
index 8cbd1e96fd50..dd548a93342d 100644
--- a/arch/arm64/include/asm/io.h
+++ b/arch/arm64/include/asm/io.h
@@ -270,7 +270,7 @@ static inline void __iomem *ioremap_prot(phys_addr_t phys, size_t size,
pgprot_t user_prot)
{
pgprot_t prot;
- ptdesc_t user_prot_val = pgprot_val(user_prot);
+ pxxval_t user_prot_val = pgprot_val(user_prot);
if (WARN_ON_ONCE(!(user_prot_val & PTE_USER)))
return NULL;
diff --git a/arch/arm64/include/asm/pgtable-types.h b/arch/arm64/include/asm/pgtable-types.h
index 265e8301d7ba..53b5ada823ee 100644
--- a/arch/arm64/include/asm/pgtable-types.h
+++ b/arch/arm64/include/asm/pgtable-types.h
@@ -17,13 +17,13 @@
* Generic page table descriptor format from which
* all level specific descriptors can be derived.
*/
-typedef u64 ptdesc_t;
+typedef u64 pxxval_t;
-typedef ptdesc_t pteval_t;
-typedef ptdesc_t pmdval_t;
-typedef ptdesc_t pudval_t;
-typedef ptdesc_t p4dval_t;
-typedef ptdesc_t pgdval_t;
+typedef pxxval_t pteval_t;
+typedef pxxval_t pmdval_t;
+typedef pxxval_t pudval_t;
+typedef pxxval_t p4dval_t;
+typedef pxxval_t pgdval_t;
/*
* These are used to make use of C type-checking..
@@ -54,7 +54,7 @@ typedef struct { pgdval_t pgd; } pgd_t;
#define pgd_val(x) ((x).pgd)
#define __pgd(x) ((pgd_t) { (x) } )
-typedef struct { ptdesc_t pgprot; } pgprot_t;
+typedef struct { pxxval_t pgprot; } pgprot_t;
#define pgprot_val(x) ((x).pgprot)
#define __pgprot(x) ((pgprot_t) { (x) } )
diff --git a/arch/arm64/include/asm/ptdump.h b/arch/arm64/include/asm/ptdump.h
index baff24004459..f4113b7c881c 100644
--- a/arch/arm64/include/asm/ptdump.h
+++ b/arch/arm64/include/asm/ptdump.h
@@ -26,8 +26,8 @@ struct ptdump_info {
};
struct ptdump_prot_bits {
- ptdesc_t mask;
- ptdesc_t val;
+ pxxval_t mask;
+ pxxval_t val;
const char *set;
const char *clear;
};
@@ -36,7 +36,7 @@ struct ptdump_pg_level {
const struct ptdump_prot_bits *bits;
char name[4];
int num;
- ptdesc_t mask;
+ pxxval_t mask;
};
/*
@@ -53,7 +53,7 @@ struct ptdump_pg_state {
const struct mm_struct *mm;
unsigned long start_address;
int level;
- ptdesc_t current_prot;
+ pxxval_t current_prot;
bool check_wx;
unsigned long wx_pages;
unsigned long uxn_pages;
diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h
index c0bf5b398041..968a78d0c69e 100644
--- a/arch/arm64/include/asm/tlbflush.h
+++ b/arch/arm64/include/asm/tlbflush.h
@@ -725,9 +725,9 @@ static inline void arch_tlbbatch_add_pending(struct arch_tlbflush_unmap_batch *b
sme_dvmsync_add_pending(batch, mm);
}
-static inline bool __pte_flags_need_flush(ptdesc_t oldval, ptdesc_t newval)
+static inline bool __pte_flags_need_flush(pxxval_t oldval, pxxval_t newval)
{
- ptdesc_t diff = oldval ^ newval;
+ pxxval_t diff = oldval ^ newval;
/* invalid to valid transition requires no flush */
if (!(oldval & PTE_VALID))
diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
index a81cb4aa4738..3d322252b4bf 100644
--- a/arch/arm64/kernel/efi.c
+++ b/arch/arm64/kernel/efi.c
@@ -31,7 +31,7 @@ static bool region_is_misaligned(const efi_memory_desc_t *md)
* executable, everything else can be mapped with the XN bits
* set. Also take the new (optional) RO/XP bits into account.
*/
-static __init ptdesc_t create_mapping_protection(efi_memory_desc_t *md)
+static __init pxxval_t create_mapping_protection(efi_memory_desc_t *md)
{
u64 attr = md->attribute;
u32 type = md->type;
@@ -85,7 +85,7 @@ static __init ptdesc_t create_mapping_protection(efi_memory_desc_t *md)
int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
{
- ptdesc_t prot_val = create_mapping_protection(md);
+ pxxval_t prot_val = create_mapping_protection(md);
bool page_mappings_only = (md->type == EFI_RUNTIME_SERVICES_CODE ||
md->type == EFI_RUNTIME_SERVICES_DATA);
diff --git a/arch/arm64/kernel/pi/map_kernel.c b/arch/arm64/kernel/pi/map_kernel.c
index a852264958c3..168858e75f75 100644
--- a/arch/arm64/kernel/pi/map_kernel.c
+++ b/arch/arm64/kernel/pi/map_kernel.c
@@ -165,7 +165,7 @@ static void noinline __section(".idmap.text") set_ttbr0_for_lpa2(phys_addr_t ttb
static void __init remap_idmap_for_lpa2(void)
{
/* clear the bits that change meaning once LPA2 is turned on */
- ptdesc_t mask = PTE_SHARED;
+ pxxval_t mask = PTE_SHARED;
/*
* We have to clear bits [9:8] in all block or page descriptors in the
diff --git a/arch/arm64/kernel/pi/map_range.c b/arch/arm64/kernel/pi/map_range.c
index de52cd85c691..f7cbad6e3bbc 100644
--- a/arch/arm64/kernel/pi/map_range.c
+++ b/arch/arm64/kernel/pi/map_range.c
@@ -31,7 +31,7 @@ void __init map_range(phys_addr_t *pte, u64 start, u64 end, phys_addr_t pa,
u64 va_offset)
{
u64 cmask = (level == 3) ? CONT_PTE_SIZE - 1 : U64_MAX;
- ptdesc_t protval = pgprot_val(prot) & ~PTE_TYPE_MASK;
+ pxxval_t protval = pgprot_val(prot) & ~PTE_TYPE_MASK;
int lshift = (3 - level) * PTDESC_TABLE_SHIFT;
u64 lmask = (PAGE_SIZE << lshift) - 1;
@@ -88,7 +88,7 @@ void __init map_range(phys_addr_t *pte, u64 start, u64 end, phys_addr_t pa,
}
}
-asmlinkage phys_addr_t __init create_init_idmap(pgd_t *pg_dir, ptdesc_t clrmask)
+asmlinkage phys_addr_t __init create_init_idmap(pgd_t *pg_dir, pxxval_t clrmask)
{
phys_addr_t ptep = (phys_addr_t)pg_dir + PAGE_SIZE; /* MMU is off */
pgprot_t text_prot = PAGE_KERNEL_ROX;
diff --git a/arch/arm64/kernel/pi/pi.h b/arch/arm64/kernel/pi/pi.h
index aec3172d4003..2eb24a4205f6 100644
--- a/arch/arm64/kernel/pi/pi.h
+++ b/arch/arm64/kernel/pi/pi.h
@@ -35,4 +35,4 @@ void map_range(phys_addr_t *pte, u64 start, u64 end, phys_addr_t pa,
asmlinkage void early_map_kernel(u64 boot_status, phys_addr_t fdt);
-asmlinkage phys_addr_t create_init_idmap(pgd_t *pgd, ptdesc_t clrmask);
+asmlinkage phys_addr_t create_init_idmap(pgd_t *pgd, pxxval_t clrmask);
diff --git a/arch/arm64/mm/mmap.c b/arch/arm64/mm/mmap.c
index 92b2f5097a96..bc3fef91a8ce 100644
--- a/arch/arm64/mm/mmap.c
+++ b/arch/arm64/mm/mmap.c
@@ -34,7 +34,7 @@ static pgprot_t protection_map[16] __ro_after_init = {
[VM_SHARED | VM_EXEC | VM_WRITE | VM_READ] = PAGE_SHARED_EXEC
};
-static ptdesc_t gcs_page_prot __ro_after_init = _PAGE_GCS_RO;
+static pxxval_t gcs_page_prot __ro_after_init = _PAGE_GCS_RO;
/*
* You really shouldn't be using read() or write() on /dev/mem. This might go
@@ -87,7 +87,7 @@ arch_initcall(adjust_protection_map);
pgprot_t vm_get_page_prot(vm_flags_t vm_flags)
{
- ptdesc_t prot;
+ pxxval_t prot;
/* Short circuit GCS to avoid bloating the table. */
if (system_supports_gcs() && (vm_flags & VM_SHADOW_STACK)) {
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index ce035e1b4eaf..e691e258b0e4 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -21,7 +21,7 @@ struct page_change_data {
pgprot_t clear_mask;
};
-static ptdesc_t set_pageattr_masks(ptdesc_t val, struct mm_walk *walk)
+static pxxval_t set_pageattr_masks(pxxval_t val, struct mm_walk *walk)
{
struct page_change_data *masks = walk->private;
diff --git a/arch/arm64/mm/ptdump.c b/arch/arm64/mm/ptdump.c
index ab9899ca1e5f..235e5d6a3d76 100644
--- a/arch/arm64/mm/ptdump.c
+++ b/arch/arm64/mm/ptdump.c
@@ -194,7 +194,7 @@ void note_page(struct ptdump_state *pt_st, unsigned long addr, int level,
struct ptdump_pg_state *st = container_of(pt_st, struct ptdump_pg_state, ptdump);
struct ptdump_pg_level *pg_level = st->pg_level;
static const char units[] = "KMGTPE";
- ptdesc_t prot = 0;
+ pxxval_t prot = 0;
/* check if the current level has been folded dynamically */
if (st->mm && ((level == 1 && mm_p4d_folded(st->mm)) ||
--
2.30.2
^ permalink raw reply related
* Re: [PATCH] media: cedrus: clean up media device on probe failure
From: kernel test robot @ 2026-04-30 3:42 UTC (permalink / raw)
To: 박명훈, Maxime Ripard, Paul Kocialkowski,
Mauro Carvalho Chehab, Greg Kroah-Hartman, Chen-Yu Tsai,
Jernej Skrabec, Samuel Holland
Cc: llvm, oe-kbuild-all, linux-media, Myeonghun Pak, linux-staging,
linux-arm-kernel, linux-sunxi, linux-kernel, stable, Ijae Kim
In-Reply-To: <20260427100049.29034-1-pakmyeonghun@bagmyeonghun-ui-MacBookPro.local>
Hi 박명훈,
kernel test robot noticed the following build warnings:
[auto build test WARNING on sunxi/sunxi/for-next]
[also build test WARNING on linus/master v7.1-rc1 next-20260429]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/media-cedrus-clean-up-media-device-on-probe-failure/20260427-235223
base: https://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux.git sunxi/for-next
patch link: https://lore.kernel.org/r/20260427100049.29034-1-pakmyeonghun%40bagmyeonghun-ui-MacBookPro.local
patch subject: [PATCH] media: cedrus: clean up media device on probe failure
config: s390-allmodconfig (https://download.01.org/0day-ci/archive/20260430/202604301101.Nszhy0yB-lkp@intel.com/config)
compiler: clang version 18.1.8 (https://github.com/llvm/llvm-project 3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260430/202604301101.Nszhy0yB-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202604301101.Nszhy0yB-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/staging/media/sunxi/cedrus/cedrus.c:538:1: warning: unused label 'err_m2m' [-Wunused-label]
538 | err_m2m:
| ^~~~~~~~
1 warning generated.
vim +/err_m2m +538 drivers/staging/media/sunxi/cedrus/cedrus.c
50e761516f2b8c Paul Kocialkowski 2018-09-13 449
50e761516f2b8c Paul Kocialkowski 2018-09-13 450 static int cedrus_probe(struct platform_device *pdev)
50e761516f2b8c Paul Kocialkowski 2018-09-13 451 {
50e761516f2b8c Paul Kocialkowski 2018-09-13 452 struct cedrus_dev *dev;
50e761516f2b8c Paul Kocialkowski 2018-09-13 453 struct video_device *vfd;
50e761516f2b8c Paul Kocialkowski 2018-09-13 454 int ret;
50e761516f2b8c Paul Kocialkowski 2018-09-13 455
50e761516f2b8c Paul Kocialkowski 2018-09-13 456 dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
50e761516f2b8c Paul Kocialkowski 2018-09-13 457 if (!dev)
50e761516f2b8c Paul Kocialkowski 2018-09-13 458 return -ENOMEM;
50e761516f2b8c Paul Kocialkowski 2018-09-13 459
708938f8495147 Dmitry Osipenko 2022-08-18 460 platform_set_drvdata(pdev, dev);
708938f8495147 Dmitry Osipenko 2022-08-18 461
50e761516f2b8c Paul Kocialkowski 2018-09-13 462 dev->vfd = cedrus_video_device;
50e761516f2b8c Paul Kocialkowski 2018-09-13 463 dev->dev = &pdev->dev;
50e761516f2b8c Paul Kocialkowski 2018-09-13 464 dev->pdev = pdev;
50e761516f2b8c Paul Kocialkowski 2018-09-13 465
50e761516f2b8c Paul Kocialkowski 2018-09-13 466 ret = cedrus_hw_probe(dev);
50e761516f2b8c Paul Kocialkowski 2018-09-13 467 if (ret) {
50e761516f2b8c Paul Kocialkowski 2018-09-13 468 dev_err(&pdev->dev, "Failed to probe hardware\n");
50e761516f2b8c Paul Kocialkowski 2018-09-13 469 return ret;
50e761516f2b8c Paul Kocialkowski 2018-09-13 470 }
50e761516f2b8c Paul Kocialkowski 2018-09-13 471
50e761516f2b8c Paul Kocialkowski 2018-09-13 472 mutex_init(&dev->dev_mutex);
50e761516f2b8c Paul Kocialkowski 2018-09-13 473
7c38a551bda1b7 Jernej Skrabec 2022-02-01 474 INIT_DELAYED_WORK(&dev->watchdog_work, cedrus_watchdog);
7c38a551bda1b7 Jernej Skrabec 2022-02-01 475
50e761516f2b8c Paul Kocialkowski 2018-09-13 476 ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
50e761516f2b8c Paul Kocialkowski 2018-09-13 477 if (ret) {
50e761516f2b8c Paul Kocialkowski 2018-09-13 478 dev_err(&pdev->dev, "Failed to register V4L2 device\n");
50e761516f2b8c Paul Kocialkowski 2018-09-13 479 return ret;
50e761516f2b8c Paul Kocialkowski 2018-09-13 480 }
50e761516f2b8c Paul Kocialkowski 2018-09-13 481
50e761516f2b8c Paul Kocialkowski 2018-09-13 482 vfd = &dev->vfd;
50e761516f2b8c Paul Kocialkowski 2018-09-13 483 vfd->lock = &dev->dev_mutex;
50e761516f2b8c Paul Kocialkowski 2018-09-13 484 vfd->v4l2_dev = &dev->v4l2_dev;
50e761516f2b8c Paul Kocialkowski 2018-09-13 485
50e761516f2b8c Paul Kocialkowski 2018-09-13 486 snprintf(vfd->name, sizeof(vfd->name), "%s", cedrus_video_device.name);
50e761516f2b8c Paul Kocialkowski 2018-09-13 487 video_set_drvdata(vfd, dev);
50e761516f2b8c Paul Kocialkowski 2018-09-13 488
50e761516f2b8c Paul Kocialkowski 2018-09-13 489 dev->m2m_dev = v4l2_m2m_init(&cedrus_m2m_ops);
50e761516f2b8c Paul Kocialkowski 2018-09-13 490 if (IS_ERR(dev->m2m_dev)) {
50e761516f2b8c Paul Kocialkowski 2018-09-13 491 v4l2_err(&dev->v4l2_dev,
50e761516f2b8c Paul Kocialkowski 2018-09-13 492 "Failed to initialize V4L2 M2M device\n");
50e761516f2b8c Paul Kocialkowski 2018-09-13 493 ret = PTR_ERR(dev->m2m_dev);
50e761516f2b8c Paul Kocialkowski 2018-09-13 494
bac875349f8525 Jernej Skrabec 2019-04-07 495 goto err_v4l2;
50e761516f2b8c Paul Kocialkowski 2018-09-13 496 }
50e761516f2b8c Paul Kocialkowski 2018-09-13 497
50e761516f2b8c Paul Kocialkowski 2018-09-13 498 dev->mdev.dev = &pdev->dev;
50e761516f2b8c Paul Kocialkowski 2018-09-13 499 strscpy(dev->mdev.model, CEDRUS_NAME, sizeof(dev->mdev.model));
ae0688f659adb1 Hans Verkuil 2019-08-30 500 strscpy(dev->mdev.bus_info, "platform:" CEDRUS_NAME,
ae0688f659adb1 Hans Verkuil 2019-08-30 501 sizeof(dev->mdev.bus_info));
50e761516f2b8c Paul Kocialkowski 2018-09-13 502
50e761516f2b8c Paul Kocialkowski 2018-09-13 503 media_device_init(&dev->mdev);
50e761516f2b8c Paul Kocialkowski 2018-09-13 504 dev->mdev.ops = &cedrus_m2m_media_ops;
50e761516f2b8c Paul Kocialkowski 2018-09-13 505 dev->v4l2_dev.mdev = &dev->mdev;
50e761516f2b8c Paul Kocialkowski 2018-09-13 506
0e17c50fcf6a9e Hans Verkuil 2020-02-03 507 ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
50e761516f2b8c Paul Kocialkowski 2018-09-13 508 if (ret) {
50e761516f2b8c Paul Kocialkowski 2018-09-13 509 v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
e8660f15f165e6 Myeonghun Pak 2026-04-27 510 goto err_media_cleanup;
50e761516f2b8c Paul Kocialkowski 2018-09-13 511 }
50e761516f2b8c Paul Kocialkowski 2018-09-13 512
50e761516f2b8c Paul Kocialkowski 2018-09-13 513 v4l2_info(&dev->v4l2_dev,
50e761516f2b8c Paul Kocialkowski 2018-09-13 514 "Device registered as /dev/video%d\n", vfd->num);
50e761516f2b8c Paul Kocialkowski 2018-09-13 515
bac875349f8525 Jernej Skrabec 2019-04-07 516 ret = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd,
bac875349f8525 Jernej Skrabec 2019-04-07 517 MEDIA_ENT_F_PROC_VIDEO_DECODER);
bac875349f8525 Jernej Skrabec 2019-04-07 518 if (ret) {
bac875349f8525 Jernej Skrabec 2019-04-07 519 v4l2_err(&dev->v4l2_dev,
bac875349f8525 Jernej Skrabec 2019-04-07 520 "Failed to initialize V4L2 M2M media controller\n");
bac875349f8525 Jernej Skrabec 2019-04-07 521 goto err_video;
bac875349f8525 Jernej Skrabec 2019-04-07 522 }
bac875349f8525 Jernej Skrabec 2019-04-07 523
50e761516f2b8c Paul Kocialkowski 2018-09-13 524 ret = media_device_register(&dev->mdev);
50e761516f2b8c Paul Kocialkowski 2018-09-13 525 if (ret) {
50e761516f2b8c Paul Kocialkowski 2018-09-13 526 v4l2_err(&dev->v4l2_dev, "Failed to register media device\n");
50e761516f2b8c Paul Kocialkowski 2018-09-13 527 goto err_m2m_mc;
50e761516f2b8c Paul Kocialkowski 2018-09-13 528 }
50e761516f2b8c Paul Kocialkowski 2018-09-13 529
50e761516f2b8c Paul Kocialkowski 2018-09-13 530 return 0;
50e761516f2b8c Paul Kocialkowski 2018-09-13 531
50e761516f2b8c Paul Kocialkowski 2018-09-13 532 err_m2m_mc:
50e761516f2b8c Paul Kocialkowski 2018-09-13 533 v4l2_m2m_unregister_media_controller(dev->m2m_dev);
50e761516f2b8c Paul Kocialkowski 2018-09-13 534 err_video:
50e761516f2b8c Paul Kocialkowski 2018-09-13 535 video_unregister_device(&dev->vfd);
e8660f15f165e6 Myeonghun Pak 2026-04-27 536 err_media_cleanup:
e8660f15f165e6 Myeonghun Pak 2026-04-27 537 media_device_cleanup(&dev->mdev);
bac875349f8525 Jernej Skrabec 2019-04-07 @538 err_m2m:
bac875349f8525 Jernej Skrabec 2019-04-07 539 v4l2_m2m_release(dev->m2m_dev);
50e761516f2b8c Paul Kocialkowski 2018-09-13 540 err_v4l2:
50e761516f2b8c Paul Kocialkowski 2018-09-13 541 v4l2_device_unregister(&dev->v4l2_dev);
50e761516f2b8c Paul Kocialkowski 2018-09-13 542
50e761516f2b8c Paul Kocialkowski 2018-09-13 543 return ret;
50e761516f2b8c Paul Kocialkowski 2018-09-13 544 }
50e761516f2b8c Paul Kocialkowski 2018-09-13 545
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* [PATCH v19 6/7] ring-buffer: Cleanup persistent ring buffer validation
From: Masami Hiramatsu (Google) @ 2026-04-30 3:28 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: <177751968499.2136606.17388366710182662849.stgit@mhiramat.tok.corp.google.com>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Cleanup rb_meta_validate_events() function to make it easier to read.
This includes the following cleanups:
- Introduce rb_validatation_state to hold working variables in
validation.
- Move repleated validation state updates into rb_validate_buffer().
- Move reader_page injection code outside of rb_meta_validate_events().
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Changes in v19:
- Add a kerneldoc about rb_validate_buffer().
---
kernel/trace/ring_buffer.c | 198 ++++++++++++++++++++++++--------------------
1 file changed, 107 insertions(+), 91 deletions(-)
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 2654315c1b7c..236fee6bc8f6 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -1883,8 +1883,16 @@ static int rb_read_data_buffer(struct buffer_data_page *dpage, int tail, int cpu
return events;
}
-static int rb_validate_buffer(struct buffer_page *bpage, int cpu,
- struct ring_buffer_cpu_meta *meta, u64 prev_ts, u64 next_ts)
+struct rb_validation_state {
+ unsigned long entries;
+ unsigned long entry_bytes;
+ int discarded;
+ u64 ts;
+};
+
+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;
@@ -1922,16 +1930,94 @@ static int rb_validate_buffer(struct buffer_page *bpage, int cpu,
return ret;
}
+/**
+ * rb_validate_buffer - validates a single buffer page and updates the state.
+ * @bpage: buffer page to validate
+ * @cpu_buffer: cpu_buffer this page belongs to
+ * @meta: meta of the cpu_buffer
+ * @state: validation state
+ * @prev_ts: previous buffer's timestamp (optional)
+ * @next_ts: next buffer's timestamp (optional)
+ *
+ * If the page is invalid (wrong event length or timestamp), it increments the
+ * discarded counter and warns it. Otherwise, it updates the validation state.
+ */
+static void rb_validate_buffer(struct buffer_page *bpage,
+ struct ring_buffer_per_cpu *cpu_buffer,
+ struct ring_buffer_cpu_meta *meta,
+ struct rb_validation_state *state,
+ u64 prev_ts, u64 next_ts)
+{
+ int ret;
+
+ ret = __rb_validate_buffer(bpage, cpu_buffer->cpu, meta, prev_ts, next_ts);
+ if (ret < 0) {
+ if (!state->discarded)
+ pr_info("Ring buffer meta [%d] invalid buffer page detected\n",
+ cpu_buffer->cpu);
+ state->discarded++;
+ } else {
+ /* If the buffer has content, update pages_touched */
+ if (ret)
+ local_inc(&cpu_buffer->pages_touched);
+
+ state->entries += ret;
+ state->entry_bytes += rb_page_size(bpage);
+ state->ts = bpage->page->time_stamp;
+ }
+}
+
+static void rb_meta_inject_reader_page(struct ring_buffer_per_cpu *cpu_buffer,
+ struct ring_buffer_cpu_meta *meta,
+ struct buffer_page *orig_head,
+ struct buffer_page *head_page)
+{
+ struct buffer_page *bpage = orig_head;
+ int i;
+
+ rb_dec_page(&bpage);
+ /*
+ * Insert the reader_page before the original head page.
+ * Since the list encode RB_PAGE flags, general list
+ * operations should be avoided.
+ */
+ cpu_buffer->reader_page->list.next = &orig_head->list;
+ cpu_buffer->reader_page->list.prev = orig_head->list.prev;
+ orig_head->list.prev = &cpu_buffer->reader_page->list;
+ bpage->list.next = &cpu_buffer->reader_page->list;
+
+ /* Make the head_page the reader page */
+ cpu_buffer->reader_page = head_page;
+ bpage = head_page;
+ rb_inc_page(&head_page);
+ head_page->list.prev = bpage->list.prev;
+ rb_dec_page(&bpage);
+ bpage->list.next = &head_page->list;
+ rb_set_list_to_head(&bpage->list);
+ cpu_buffer->pages = &head_page->list;
+
+ cpu_buffer->head_page = head_page;
+ meta->head_buffer = (unsigned long)head_page->page;
+
+ /* Reset all the indexes */
+ bpage = cpu_buffer->reader_page;
+ meta->buffers[0] = rb_meta_subbuf_idx(meta, bpage->page);
+ bpage->id = 0;
+
+ for (i = 1, bpage = head_page; i < meta->nr_subbufs;
+ i++, rb_inc_page(&bpage)) {
+ meta->buffers[i] = rb_meta_subbuf_idx(meta, bpage->page);
+ bpage->id = i;
+ }
+}
+
/* If the meta data has been validated, now validate the events */
static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
{
struct ring_buffer_cpu_meta *meta = cpu_buffer->ring_meta;
struct buffer_page *head_page, *orig_head, *orig_reader;
- unsigned long entry_bytes = 0;
- unsigned long entries = 0;
- int discarded = 0;
+ struct rb_validation_state state = { 0 };
int ret;
- u64 ts;
int i;
if (!meta || !meta->head_buffer)
@@ -1941,25 +2027,16 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
orig_reader = cpu_buffer->reader_page;
/* Do the head page first */
- ret = rb_validate_buffer(head_page, cpu_buffer->cpu, meta, 0, 0);
+ 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;
+ state.ts = head_page->page->time_stamp;
/* Do the reader page - reader must be previous to head. */
- ret = rb_validate_buffer(orig_reader, cpu_buffer->cpu, meta, 0, ts);
- if (ret < 0) {
- pr_info("Ring buffer meta [%d] invalid reader page detected\n",
- cpu_buffer->cpu);
- discarded++;
- } else {
- entries += ret;
- entry_bytes += rb_page_size(orig_reader);
- ts = orig_reader->page->time_stamp;
- }
+ rb_validate_buffer(orig_reader, cpu_buffer, meta, &state, 0, state.ts);
/*
* Try to rewind the head so that we can read the pages which are already
@@ -1983,19 +2060,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
* 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;
- }
+ rb_validate_buffer(head_page, cpu_buffer, meta, &state, 0, state.ts);
}
if (i)
pr_info("Ring buffer [%d] rewound %d pages\n", cpu_buffer->cpu, i);
@@ -2009,43 +2074,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
* into the location just before the original head page.
*/
if (head_page != orig_head) {
- struct buffer_page *bpage = orig_head;
-
- rb_dec_page(&bpage);
- /*
- * Insert the reader_page before the original head page.
- * Since the list encode RB_PAGE flags, general list
- * operations should be avoided.
- */
- cpu_buffer->reader_page->list.next = &orig_head->list;
- cpu_buffer->reader_page->list.prev = orig_head->list.prev;
- orig_head->list.prev = &cpu_buffer->reader_page->list;
- bpage->list.next = &cpu_buffer->reader_page->list;
-
- /* Make the head_page the reader page */
- cpu_buffer->reader_page = head_page;
- bpage = head_page;
- rb_inc_page(&head_page);
- head_page->list.prev = bpage->list.prev;
- rb_dec_page(&bpage);
- bpage->list.next = &head_page->list;
- rb_set_list_to_head(&bpage->list);
- cpu_buffer->pages = &head_page->list;
-
- cpu_buffer->head_page = head_page;
- meta->head_buffer = (unsigned long)head_page->page;
-
- /* Reset all the indexes */
- bpage = cpu_buffer->reader_page;
- meta->buffers[0] = rb_meta_subbuf_idx(meta, bpage->page);
- bpage->id = 0;
-
- for (i = 1, bpage = head_page; i < meta->nr_subbufs;
- i++, rb_inc_page(&bpage)) {
- meta->buffers[i] = rb_meta_subbuf_idx(meta, bpage->page);
- bpage->id = i;
- }
-
+ rb_meta_inject_reader_page(cpu_buffer, meta, orig_head, head_page);
/* We'll restart verifying from orig_head */
head_page = orig_head;
}
@@ -2057,7 +2086,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;
+ state.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)) {
@@ -2066,21 +2095,8 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
if (head_page == orig_reader)
continue;
- 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++;
- } else {
- /* If the buffer has content, update pages_touched */
- if (ret)
- local_inc(&cpu_buffer->pages_touched);
+ rb_validate_buffer(head_page, cpu_buffer, meta, &state, state.ts, 0);
- entries += ret;
- entry_bytes += rb_page_size(head_page);
- ts = head_page->page->time_stamp;
- }
if (head_page == cpu_buffer->commit_page)
break;
}
@@ -2091,25 +2107,25 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
goto invalid;
}
done:
- local_set(&cpu_buffer->entries, entries);
- local_set(&cpu_buffer->entries_bytes, entry_bytes);
+ local_set(&cpu_buffer->entries, state.entries);
+ local_set(&cpu_buffer->entries_bytes, state.entry_bytes);
pr_info("Ring buffer meta [%d] is from previous boot!", cpu_buffer->cpu);
- if (discarded)
- pr_cont(" (%d pages discarded)", discarded);
+ if (state.discarded)
+ pr_cont(" (%d pages discarded)", state.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);
+ (state.discarded == meta->nr_invalid) ? "PASSED" : "FAILED",
+ state.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);
+ (state.entry_bytes == meta->entry_bytes) ? "PASSED" : "FAILED",
+ (long)state.entry_bytes, (long)meta->entry_bytes);
meta->nr_invalid = 0;
meta->entry_bytes = 0;
#endif
^ permalink raw reply related
* [PATCH v19 7/7] ring-buffer: Cleanup buffer_data_page related code
From: Masami Hiramatsu (Google) @ 2026-04-30 3:29 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: <177751968499.2136606.17388366710182662849.stgit@mhiramat.tok.corp.google.com>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Code cleanup related to buffer_data_page for readability,
which includes:
- Introduce rb_data_page_commit() and rb_data_page_size()
- Use 'dpage' for buffer_data_page, instead of 'bpage' because
'bpage' is used for buffer_page.
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
kernel/trace/ring_buffer.c | 112 ++++++++++++++++++++++++--------------------
1 file changed, 60 insertions(+), 52 deletions(-)
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 236fee6bc8f6..afbea7d918e4 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -364,21 +364,30 @@ struct buffer_page {
#define RB_WRITE_MASK 0xfffff
#define RB_WRITE_INTCNT (1 << 20)
-static void rb_init_page(struct buffer_data_page *bpage)
+static void rb_init_data_page(struct buffer_data_page *bpage)
{
local_set(&bpage->commit, 0);
bpage->time_stamp = 0;
}
+static __always_inline long rb_data_page_commit(struct buffer_data_page *dpage)
+{
+ return local_read(&dpage->commit);
+}
+
+static __always_inline long rb_data_page_size(struct buffer_data_page *dpage)
+{
+ return rb_data_page_commit(dpage) & ~RB_MISSED_MASK;
+}
+
static __always_inline unsigned int rb_page_commit(struct buffer_page *bpage)
{
- return local_read(&bpage->page->commit);
+ return rb_data_page_commit(bpage->page);
}
-/* 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;
+ return rb_data_page_size(bpage->page);
}
static void free_buffer_page(struct buffer_page *bpage)
@@ -419,7 +428,7 @@ static struct buffer_data_page *alloc_cpu_data(int cpu, int order)
return NULL;
dpage = page_address(page);
- rb_init_page(dpage);
+ rb_init_data_page(dpage);
return dpage;
}
@@ -659,7 +668,7 @@ static void verify_event(struct ring_buffer_per_cpu *cpu_buffer,
do {
if (page == tail_page || WARN_ON_ONCE(stop++ > 100))
done = true;
- commit = local_read(&page->page->commit);
+ commit = rb_page_commit(page);
write = local_read(&page->write);
if (addr >= (unsigned long)&page->page->data[commit] &&
addr < (unsigned long)&page->page->data[write])
@@ -1906,7 +1915,7 @@ static int __rb_validate_buffer(struct buffer_page *bpage, int cpu,
* Even after clearing these bits, a commit value greater than the
* subbuf_size is considered invalid.
*/
- tail = local_read(&dpage->commit) & ~RB_MISSED_MASK;
+ tail = rb_data_page_size(dpage);
if (tail <= meta->subbuf_size - BUF_PAGE_HDR_SIZE)
ret = rb_read_data_buffer(dpage, tail, cpu, &ts, &delta);
else
@@ -2138,12 +2147,12 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
/* Reset the reader page */
local_set(&cpu_buffer->reader_page->entries, 0);
- rb_init_page(cpu_buffer->reader_page->page);
+ rb_init_data_page(cpu_buffer->reader_page->page);
/* Reset all the subbuffers */
for (i = 0; i < meta->nr_subbufs - 1; i++, rb_inc_page(&head_page)) {
local_set(&head_page->entries, 0);
- rb_init_page(head_page->page);
+ rb_init_data_page(head_page->page);
}
}
@@ -2203,7 +2212,7 @@ static void rb_range_meta_init(struct trace_buffer *buffer, int nr_pages, int sc
*/
for (i = 0; i < meta->nr_subbufs; i++) {
meta->buffers[i] = i;
- rb_init_page(subbuf);
+ rb_init_data_page(subbuf);
subbuf += meta->subbuf_size;
}
}
@@ -2255,7 +2264,7 @@ static int rbm_show(struct seq_file *m, void *v)
val -= 2;
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);
+ val, meta->buffers[val], dpage ? rb_data_page_commit(dpage) : -1);
return 0;
}
@@ -2646,7 +2655,7 @@ static void rb_test_inject_invalid_pages(struct trace_buffer *buffer)
dpage = (void *)(ptr + idx * subbuf_size);
/* Skip unused pages */
- if (!local_read(&dpage->commit))
+ if (!rb_data_page_commit(dpage))
continue;
/*
@@ -2658,7 +2667,7 @@ static void rb_test_inject_invalid_pages(struct trace_buffer *buffer)
invalid++;
} else {
/* Count total commit bytes. */
- entry_bytes += local_read(&dpage->commit) & ~RB_MISSED_MASK;
+ entry_bytes += rb_data_page_size(dpage);
}
}
@@ -4187,8 +4196,7 @@ rb_set_commit_to_write(struct ring_buffer_per_cpu *cpu_buffer)
local_set(&cpu_buffer->commit_page->page->commit,
rb_page_write(cpu_buffer->commit_page));
RB_WARN_ON(cpu_buffer,
- local_read(&cpu_buffer->commit_page->page->commit) &
- ~RB_WRITE_MASK);
+ rb_page_commit(cpu_buffer->commit_page) & ~RB_WRITE_MASK);
barrier();
}
@@ -4560,7 +4568,7 @@ static const char *show_interrupt_level(void)
return show_irq_str(level);
}
-static void dump_buffer_page(struct buffer_data_page *bpage,
+static void dump_buffer_page(struct buffer_data_page *dpage,
struct rb_event_info *info,
unsigned long tail)
{
@@ -4568,12 +4576,12 @@ static void dump_buffer_page(struct buffer_data_page *bpage,
u64 ts, delta;
int e;
- ts = bpage->time_stamp;
+ ts = dpage->time_stamp;
pr_warn(" [%lld] PAGE TIME STAMP\n", ts);
for (e = 0; e < tail; e += rb_event_length(event)) {
- event = (struct ring_buffer_event *)(bpage->data + e);
+ event = (struct ring_buffer_event *)(dpage->data + e);
switch (event->type_len) {
@@ -4623,7 +4631,7 @@ static atomic_t ts_dump;
} \
atomic_inc(&cpu_buffer->record_disabled); \
pr_warn(fmt, ##__VA_ARGS__); \
- dump_buffer_page(bpage, info, tail); \
+ dump_buffer_page(dpage, info, tail); \
atomic_dec(&ts_dump); \
/* There's some cases in boot up that this can happen */ \
if (WARN_ON_ONCE(system_state != SYSTEM_BOOTING)) \
@@ -4639,16 +4647,16 @@ static void check_buffer(struct ring_buffer_per_cpu *cpu_buffer,
struct rb_event_info *info,
unsigned long tail)
{
- struct buffer_data_page *bpage;
+ struct buffer_data_page *dpage;
u64 ts, delta;
bool full = false;
int ret;
- bpage = info->tail_page->page;
+ dpage = info->tail_page->page;
if (tail == CHECK_FULL_PAGE) {
full = true;
- tail = local_read(&bpage->commit);
+ tail = rb_data_page_commit(dpage);
} else if (info->add_timestamp &
(RB_ADD_STAMP_FORCE | RB_ADD_STAMP_ABSOLUTE)) {
/* Ignore events with absolute time stamps */
@@ -4659,7 +4667,7 @@ static void check_buffer(struct ring_buffer_per_cpu *cpu_buffer,
* Do not check the first event (skip possible extends too).
* Also do not check if previous events have not been committed.
*/
- if (tail <= 8 || tail > local_read(&bpage->commit))
+ if (tail <= 8 || tail > rb_data_page_commit(dpage))
return;
/*
@@ -4668,7 +4676,7 @@ static void check_buffer(struct ring_buffer_per_cpu *cpu_buffer,
if (atomic_inc_return(this_cpu_ptr(&checking)) != 1)
goto out;
- ret = rb_read_data_buffer(bpage, tail, cpu_buffer->cpu, &ts, &delta);
+ ret = rb_read_data_buffer(dpage, tail, cpu_buffer->cpu, &ts, &delta);
if (ret < 0) {
if (delta < ts) {
buffer_warn_return("[CPU: %d]ABSOLUTE TIME WENT BACKWARDS: last ts: %lld absolute ts: %lld clock:%pS\n",
@@ -6459,7 +6467,7 @@ static void rb_clear_buffer_page(struct buffer_page *page)
{
local_set(&page->write, 0);
local_set(&page->entries, 0);
- rb_init_page(page->page);
+ rb_init_data_page(page->page);
page->read = 0;
}
@@ -6944,7 +6952,7 @@ ring_buffer_alloc_read_page(struct trace_buffer *buffer, int cpu)
local_irq_restore(flags);
if (bpage->data) {
- rb_init_page(bpage->data);
+ rb_init_data_page(bpage->data);
} else {
bpage->data = alloc_cpu_data(cpu, cpu_buffer->buffer->subbuf_order);
if (!bpage->data) {
@@ -6969,8 +6977,8 @@ void ring_buffer_free_read_page(struct trace_buffer *buffer, int cpu,
struct buffer_data_read_page *data_page)
{
struct ring_buffer_per_cpu *cpu_buffer;
- struct buffer_data_page *bpage = data_page->data;
- struct page *page = virt_to_page(bpage);
+ struct buffer_data_page *dpage = data_page->data;
+ struct page *page = virt_to_page(dpage);
unsigned long flags;
if (!buffer || !buffer->buffers || !buffer->buffers[cpu])
@@ -6990,15 +6998,15 @@ void ring_buffer_free_read_page(struct trace_buffer *buffer, int cpu,
arch_spin_lock(&cpu_buffer->lock);
if (!cpu_buffer->free_page) {
- cpu_buffer->free_page = bpage;
- bpage = NULL;
+ cpu_buffer->free_page = dpage;
+ dpage = NULL;
}
arch_spin_unlock(&cpu_buffer->lock);
local_irq_restore(flags);
out:
- free_pages((unsigned long)bpage, data_page->order);
+ free_pages((unsigned long)dpage, data_page->order);
kfree(data_page);
}
EXPORT_SYMBOL_GPL(ring_buffer_free_read_page);
@@ -7043,7 +7051,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
{
struct ring_buffer_per_cpu *cpu_buffer = buffer->buffers[cpu];
struct ring_buffer_event *event;
- struct buffer_data_page *bpage;
+ struct buffer_data_page *dpage;
struct buffer_page *reader;
unsigned long missed_events;
unsigned int commit;
@@ -7069,8 +7077,8 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
if (data_page->order != buffer->subbuf_order)
return -1;
- bpage = data_page->data;
- if (!bpage)
+ dpage = data_page->data;
+ if (!dpage)
return -1;
guard(raw_spinlock_irqsave)(&cpu_buffer->reader_lock);
@@ -7136,7 +7144,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
* We have already ensured there's enough space if this
* is a time extend. */
size = rb_event_length(event);
- memcpy(bpage->data + pos, rpage->data + rpos, size);
+ memcpy(dpage->data + pos, rpage->data + rpos, size);
len -= size;
@@ -7152,9 +7160,9 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
size = rb_event_ts_length(event);
} while (len >= size);
- /* update bpage */
- local_set(&bpage->commit, pos);
- bpage->time_stamp = save_timestamp;
+ /* update dpage */
+ local_set(&dpage->commit, pos);
+ dpage->time_stamp = save_timestamp;
/* we copied everything to the beginning */
read = 0;
@@ -7164,13 +7172,13 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
cpu_buffer->read_bytes += rb_page_size(reader);
/* swap the pages */
- rb_init_page(bpage);
- bpage = reader->page;
+ rb_init_data_page(dpage);
+ dpage = reader->page;
reader->page = data_page->data;
local_set(&reader->write, 0);
local_set(&reader->entries, 0);
reader->read = 0;
- data_page->data = bpage;
+ data_page->data = dpage;
/*
* Use the real_end for the data size,
@@ -7178,12 +7186,12 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
* on the page.
*/
if (reader->real_end)
- local_set(&bpage->commit, reader->real_end);
+ local_set(&dpage->commit, reader->real_end);
}
cpu_buffer->lost_events = 0;
- commit = local_read(&bpage->commit);
+ commit = rb_data_page_commit(dpage);
/*
* Set a flag in the commit field if we lost events
*/
@@ -7192,19 +7200,19 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
* missed events, then record it there.
*/
if (buffer->subbuf_size - commit >= sizeof(missed_events)) {
- memcpy(&bpage->data[commit], &missed_events,
+ memcpy(&dpage->data[commit], &missed_events,
sizeof(missed_events));
- local_add(RB_MISSED_STORED, &bpage->commit);
+ local_add(RB_MISSED_STORED, &dpage->commit);
commit += sizeof(missed_events);
}
- local_add(RB_MISSED_EVENTS, &bpage->commit);
+ local_add(RB_MISSED_EVENTS, &dpage->commit);
}
/*
* This page may be off to user land. Zero it out here.
*/
if (commit < buffer->subbuf_size)
- memset(&bpage->data[commit], 0, buffer->subbuf_size - commit);
+ memset(&dpage->data[commit], 0, buffer->subbuf_size - commit);
return read;
}
@@ -7835,7 +7843,7 @@ int ring_buffer_map_get_reader(struct trace_buffer *buffer, int cpu)
if (missed_events) {
if (cpu_buffer->reader_page != cpu_buffer->commit_page) {
- struct buffer_data_page *bpage = reader->page;
+ struct buffer_data_page *dpage = reader->page;
unsigned int commit;
/*
* Use the real_end for the data size,
@@ -7843,18 +7851,18 @@ int ring_buffer_map_get_reader(struct trace_buffer *buffer, int cpu)
* on the page.
*/
if (reader->real_end)
- local_set(&bpage->commit, reader->real_end);
+ local_set(&dpage->commit, reader->real_end);
/*
* If there is room at the end of the page to save the
* missed events, then record it there.
*/
commit = rb_page_size(reader);
if (buffer->subbuf_size - commit >= sizeof(missed_events)) {
- memcpy(&bpage->data[commit], &missed_events,
+ memcpy(&dpage->data[commit], &missed_events,
sizeof(missed_events));
- local_add(RB_MISSED_STORED, &bpage->commit);
+ local_add(RB_MISSED_STORED, &dpage->commit);
}
- local_add(RB_MISSED_EVENTS, &bpage->commit);
+ local_add(RB_MISSED_EVENTS, &dpage->commit);
} else if (!WARN_ONCE(cpu_buffer->reader_page == cpu_buffer->tail_page,
"Reader on commit with %ld missed events",
missed_events)) {
^ permalink raw reply related
* [PATCH v19 5/7] ring-buffer: Show commit numbers in buffer_meta file
From: Masami Hiramatsu (Google) @ 2026-04-30 3:28 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: <177751968499.2136606.17388366710182662849.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 c7cd93b66e0a..2654315c1b7c 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -2224,6 +2224,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",
@@ -2236,7 +2237,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 v19 3/7] ring-buffer: Skip invalid sub-buffers when rewinding persistent ring buffer
From: Masami Hiramatsu (Google) @ 2026-04-30 3:28 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: <177751968499.2136606.17388366710182662849.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 v19:
- Cleanup rb_validate_buffer() so that it is more readable,
and add a comment about the timestamp validation.
Changes in v18:
- Reset timestamp of reader_page when the entire cpu_buffer is
invalid.
- Minor update by new fix.
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 | 102 +++++++++++++++++++++++++++-----------------
1 file changed, 63 insertions(+), 39 deletions(-)
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 2ac09b1abe9f..9a82eaa684c1 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;
/*
* When a sub-buffer is recovered from a read, the commit value may
@@ -1892,9 +1895,27 @@ static int rb_validate_buffer(struct buffer_data_page *dpage, int cpu,
* subbuf_size is considered invalid.
*/
tail = local_read(&dpage->commit) & ~RB_MISSED_MASK;
- if (tail > meta->subbuf_size - BUF_PAGE_HDR_SIZE)
- return -1;
- return rb_read_data_buffer(dpage, tail, cpu, &ts, &delta);
+ if (tail <= meta->subbuf_size - BUF_PAGE_HDR_SIZE)
+ ret = rb_read_data_buffer(dpage, tail, cpu, &ts, &delta);
+ else
+ ret = -1;
+
+ /*
+ * The timestamp must be greater than @prev_ts and smaller than @next_ts.
+ * Since this function works in both forward (verify) and reverse (unwind)
+ * loop, we don't know both @prev_ts and @next_ts at the same time.
+ * So use the known boundary as the boundary.
+ */
+ if (ret < 0 || (prev_ts && prev_ts > ts) || (next_ts && ts > next_ts)) {
+ local_set(&bpage->entries, 0);
+ local_set(&dpage->commit, 0);
+ dpage->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 */
@@ -1915,25 +1936,29 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
orig_head = head_page = cpu_buffer->head_page;
orig_reader = cpu_buffer->reader_page;
- /* Do the reader page first */
- ret = rb_validate_buffer(orig_reader->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(orig_reader, 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(&orig_reader->entries, 0);
- local_set(&orig_reader->page->commit, 0);
} else {
entries += ret;
entry_bytes += rb_page_size(orig_reader);
- local_set(&orig_reader->entries, ret);
+ ts = orig_reader->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)
@@ -1946,26 +1971,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)
+ /* Rewind until unused page (no timestamp, no commit). */
+ if (!head_page->page->time_stamp && rb_page_commit(head_page) == 0)
break;
- ts = head_page->page->time_stamp;
- /* Ensure the page has correct timestamp and some data. */
- if (!ts || 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);
@@ -2027,6 +2053,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)) {
@@ -2035,15 +2062,12 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
if (head_page == orig_reader)
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)
@@ -2051,7 +2075,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;
@@ -2079,12 +2103,12 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
/* Reset the reader page */
local_set(&cpu_buffer->reader_page->entries, 0);
- local_set(&cpu_buffer->reader_page->page->commit, 0);
+ rb_init_page(cpu_buffer->reader_page->page);
/* 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 v19 4/7] ring-buffer: Add persistent ring buffer invalid-page inject test
From: Masami Hiramatsu (Google) @ 2026-04-30 3:28 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: <177751968499.2136606.17388366710182662849.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 v18:
- Fix to mask RB_MISSED_* flags when counting entry_bytes.
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 9a82eaa684c1..c7cd93b66e0a 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[];
};
@@ -2094,6 +2098,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:
@@ -2574,12 +2593,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) & ~RB_MISSED_MASK;
+ }
+ }
+
+ 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 6eb4d3097a4d..4573f65d68ce 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -8383,6 +8383,8 @@ static void setup_trace_scratch(struct trace_array *tr,
memset(tscratch, 0, size);
}
+#define TRACE_TEST_PTRACING_NAME "ptracingtest"
+
int allocate_trace_buffer(struct trace_array *tr, struct array_buffer *buf, int size)
{
enum ring_buffer_flags rb_flags;
@@ -8394,6 +8396,8 @@ int allocate_trace_buffer(struct trace_array *tr, struct array_buffer *buf, int
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 v19 2/7] ring-buffer: Skip invalid sub-buffers when validating persistent ring buffer
From: Masami Hiramatsu (Google) @ 2026-04-30 3:28 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: <177751968499.2136606.17388366710182662849.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 v19:
- Limit max number of loops to nr_pages in __rb_get_reader_page()
and update comment.
Changes in v18:
- Minor update by the new fix.
- Fix to substract BUF_PAGE_HDR_SIZE from meta->subbuf_size
to make the limit of commit size.
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 | 116 +++++++++++++++++++++++++++-----------------
1 file changed, 70 insertions(+), 46 deletions(-)
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 7288383b1f27..2ac09b1abe9f 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 - BUF_PAGE_HDR_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, *orig_reader;
unsigned long entry_bytes = 0;
unsigned long entries = 0;
+ int discarded = 0;
int ret;
u64 ts;
int i;
@@ -1901,14 +1916,19 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
orig_reader = cpu_buffer->reader_page;
/* Do the reader page first */
- ret = rb_validate_buffer(orig_reader->page, cpu_buffer->cpu);
+ ret = rb_validate_buffer(orig_reader->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(&orig_reader->entries, 0);
+ local_set(&orig_reader->page->commit, 0);
+ } else {
+ entries += ret;
+ entry_bytes += rb_page_size(orig_reader);
+ local_set(&orig_reader->entries, ret);
}
- entries += ret;
- entry_bytes += local_read(&orig_reader->page->commit);
- local_set(&orig_reader->entries, ret);
ts = head_page->page->time_stamp;
@@ -1936,7 +1956,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;
@@ -1945,7 +1965,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);
@@ -2015,21 +2035,24 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
if (head_page == orig_reader)
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;
}
@@ -2043,7 +2066,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:
@@ -3330,12 +3356,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)
{
@@ -5635,8 +5655,9 @@ __rb_get_reader_page_from_remote(struct ring_buffer_per_cpu *cpu_buffer)
static struct buffer_page *
__rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer)
{
- struct buffer_page *reader = NULL;
+ int max_loops = cpu_buffer->ring_meta ? cpu_buffer->nr_pages : 3;
unsigned long bsize = READ_ONCE(cpu_buffer->buffer->subbuf_size);
+ struct buffer_page *reader = NULL;
unsigned long overwrite;
unsigned long flags;
int nr_loops = 0;
@@ -5648,11 +5669,14 @@ __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. For persistent ring buffers,
+ * invalid pages are reset during recovery, so there may be more
+ * than 3 contiguous pages can be empty, but less than nr_pages.
*/
- if (RB_WARN_ON(cpu_buffer, ++nr_loops > 3)) {
+ if (RB_WARN_ON(cpu_buffer, ++nr_loops > max_loops)) {
reader = NULL;
goto out;
}
^ permalink raw reply related
* [PATCH v19 1/7] ring-buffer: Flush and stop persistent ring buffer on panic
From: Masami Hiramatsu (Google) @ 2026-04-30 3:28 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: <177751968499.2136606.17388366710182662849.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>
Acked-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
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 5326924615a4..7288383b1f27 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;
@@ -2521,6 +2524,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,
@@ -2651,6 +2664,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:
@@ -2749,6 +2768,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
* Re: [PATCH bpf-next v2 0/2] bpf, arm64/riscv: Remove redundant icache flush after pack allocator finalize
From: patchwork-bot+linux-riscv @ 2026-04-30 3:25 UTC (permalink / raw)
To: Puranjay Mohan
Cc: linux-riscv, bpf, ast, daniel, andrii, martin.lau, eddyz87,
memxor, song, yonghong.song, jolsa, xukuohai, catalin.marinas,
will, luke.r.nels, xi.wang, bjorn, pulehui, pjw, palmer, aou,
alex, linux-arm-kernel, linux-kernel
In-Reply-To: <20260413191111.3426023-1-puranjay@kernel.org>
Hello:
This series was applied to riscv/linux.git (fixes)
by Alexei Starovoitov <ast@kernel.org>:
On Mon, 13 Apr 2026 12:11:07 -0700 you wrote:
> Changelog:
> v1: https://lore.kernel.org/all/20260413123256.3296452-1-puranjay@kernel.org/
> Changes in v2:
> - Remove "#include <asm/cacheflush.h>" as it is not needed now.
> - Add Acked-by: Song Liu <song@kernel.org>
>
> When the BPF prog pack allocator was added for arm64 and riscv, the
> existing bpf_flush_icache() calls were retained after
> bpf_jit_binary_pack_finalize(). However, the finalize path copies the
> JITed code via architecture text patching routines (__text_poke on arm64,
> patch_text_nosync on riscv) that already perform a full
> flush_icache_range() internally. The subsequent bpf_flush_icache()
> repeats the same cache maintenance on the same range.
>
> [...]
Here is the summary with links:
- [bpf-next,v2,1/2] bpf, arm64: Remove redundant bpf_flush_icache() after pack allocator finalize
https://git.kernel.org/riscv/c/42f18ae53011
- [bpf-next,v2,2/2] bpf, riscv: Remove redundant bpf_flush_icache() after pack allocator finalize
https://git.kernel.org/riscv/c/46ee1342b887
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* [PATCH v19 0/7] ring-buffer: Making persistent ring buffers robust
From: Masami Hiramatsu (Google) @ 2026-04-30 3:28 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 19th version of improvement patches for making persistent
ring buffers robust to failures.
The previous version is here:
https://lore.kernel.org/all/177701351903.2223789.17087009302463188638.stgit@mhiramat.tok.corp.google.com/
This version rebased on v7.1-rc1 and cleanup code.
[2/7] Limit max number of loops to nr_pages in __rb_get_reader_page()
and update comment.
[3/7] Cleanup rb_validate_buffer() so that it is more readable,
and add a comment about the timestamp validation.
[6/7] Add a kerneldoc about rb_validate_buffer().
Thank you,
Masami Hiramatsu (Google) (7):
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
ring-buffer: Cleanup persistent ring buffer validation
ring-buffer: Cleanup buffer_data_page related code
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 | 490 +++++++++++++++++++++++-----------
kernel/trace/trace.c | 4
26 files changed, 415 insertions(+), 157 deletions(-)
create mode 100644 arch/arm64/include/asm/ring_buffer.h
create mode 100644 include/asm-generic/ring_buffer.h
base-commit: 254f49634ee16a731174d2ae34bc50bd5f45e731
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply
* Re: [PATCH 0/8] firmware: sysfb: Consolidate config/code wrt. sysfb_primary_screen
From: patchwork-bot+linux-riscv @ 2026-04-30 3:25 UTC (permalink / raw)
To: Thomas Zimmermann
Cc: linux-riscv, javierm, arnd, ardb, ilias.apalodimas, chenhuacai,
kernel, maarten.lankhorst, mripard, airlied, simona, kys,
haiyangz, wei.liu, decui, longli, deller, linux-arm-kernel,
loongarch, linux-efi, dri-devel, linux-hyperv, linux-fbdev
In-Reply-To: <20260402092305.208728-1-tzimmermann@suse.de>
Hello:
This series was applied to riscv/linux.git (fixes)
by Ard Biesheuvel <ardb@kernel.org>:
On Thu, 2 Apr 2026 11:09:14 +0200 you wrote:
> The global state sysfb_primary_screen holds information about the
> framebuffer provided by EFI/BIOS systems. It is part of the sysfb
> module, but used in several places without direct connection to
> sysfb. Fix this by making users of sysfb_primary_screen depend on
> CONFIG_SYSFB. Fix a few issues in the process.
>
> Patches 1 and 2 fix general errors in the Kconfig rules. In any case,
> these patches should be considered even without the rest of the series.
>
> [...]
Here is the summary with links:
- [1/8] hv: Select CONFIG_SYSFB only for CONFIG_HYPERV_VMBUS
https://git.kernel.org/riscv/c/d33db956c961
- [2/8] firmware: efi: Never declare sysfb_primary_display on x86
https://git.kernel.org/riscv/c/5241c2ca33bb
- [3/8] firmware: sysfb: Make CONFIG_SYSFB a user-selectable option
(no matching commit)
- [4/8] firmware: sysfb: Split sysfb.c into sysfb_primary.c and sysfb_pci.c
(no matching commit)
- [5/8] firmware: sysfb: Implement screen_info relocation for primary display
(no matching commit)
- [6/8] firmware: sysfb: Avoid forward-declaring sysfb_parent_dev()
(no matching commit)
- [7/8] firmware: efi: Make CONFIG_EFI_EARLYCON depend on CONFIG_SYSFB; clean up
(no matching commit)
- [8/8] firmware: sysfb: Move CONFIG_FIRMWARE_EDID to firmware options
(no matching commit)
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH v5 0/2] Fix bugs and performance of kstack offset randomisation
From: patchwork-bot+linux-riscv @ 2026-04-30 3:25 UTC (permalink / raw)
To: Ryan Roberts
Cc: linux-riscv, catalin.marinas, will, chenhuacai, maddy, mpe, pjw,
palmer, aou, hca, gor, agordeev, tglx, mingo, bp, dave.hansen,
kees, gustavoars, arnd, mark.rutland, Jason, ardb, jeremy.linton,
david.laight.linux, linux-kernel, linux-arm-kernel, loongarch,
linuxppc-dev, linux-s390, linux-hardening
In-Reply-To: <20260303150840.3789438-1-ryan.roberts@arm.com>
Hello:
This series was applied to riscv/linux.git (fixes)
by Kees Cook <kees@kernel.org>:
On Tue, 3 Mar 2026 15:08:37 +0000 you wrote:
> [Kees; I'm hoping this is now good-to-go via your hardening tree? It would be
> good to get some linux-next testing.]
>
> Hi All,
>
> As I reported at [1], kstack offset randomisation suffers from a couple of bugs
> and, on arm64 at least, the performance is poor. This series attempts to fix
> both; patch 1 provides back-portable fixes for the functional bugs. Patch 2
> proposes a performance improvement approach.
>
> [...]
Here is the summary with links:
- [v5,1/2] randomize_kstack: Maintain kstack_offset per task
https://git.kernel.org/riscv/c/37beb4256016
- [v5,2/2] randomize_kstack: Unify random source across arches
https://git.kernel.org/riscv/c/a96ef5848cb0
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH v4 1/8] perf unwind: Refactor get_entries to allow dynamic libdw/libunwind selection
From: patchwork-bot+linux-riscv @ 2026-04-30 3:25 UTC (permalink / raw)
To: Ian Rogers
Cc: linux-riscv, acme, 9erthalion6, adrian.hunter, alex,
alexander.shishkin, andrew.jones, aou, atrajeev, blakejones,
ctshao, dapeng1.mi, howardchu95, james.clark, john.g.garry, jolsa,
leo.yan, libunwind-devel, linux-arm-kernel, linux-kernel,
linux-perf-users, mingo, namhyung, palmer, peterz, pjw,
shimin.guo, tglozar, tmricht, will, yuzhuo
In-Reply-To: <20260413024805.1316480-2-irogers@google.com>
Hello:
This series was applied to riscv/linux.git (fixes)
by Namhyung Kim <namhyung@kernel.org>:
On Sun, 12 Apr 2026 19:47:58 -0700 you wrote:
> Currently, both libdw and libunwind define 'unwind__get_entries'. This
> causes a duplicate symbol build failure when both are compiled into
> perf.
>
> This commit refactors the DWARF unwind post-processing to be
> configurable at runtime via the .perfconfig file option
> 'unwind.style', or using the argument '--unwind-style' in the commands
> 'perf report', 'perf script' and 'perf inject', in a similar manner to
> the addr2line or the disassembler style.
>
> [...]
Here is the summary with links:
- [v4,1/8] perf unwind: Refactor get_entries to allow dynamic libdw/libunwind selection
(no matching commit)
- [v4,2/8] perf build loongarch: Remove reference to missing file
https://git.kernel.org/riscv/c/841dbf4871c5
- [v4,3/8] tools build: Deduplicate test-libunwind for different architectures
(no matching commit)
- [v4,4/8] perf build: Be more programmatic when setting up libunwind variables
(no matching commit)
- [v4,5/8] perf unwind-libunwind: Make libunwind register reading cross platform
(no matching commit)
- [v4,6/8] perf unwind-libunwind: Move flush/finish access out of local
(no matching commit)
- [v4,7/8] perf unwind-libunwind: Remove libunwind-local
(no matching commit)
- [v4,8/8] perf unwind-libunwind: Add RISC-V libunwind support
(no matching commit)
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH v2 0/6] change young flag check functions to return bool
From: patchwork-bot+linux-riscv @ 2026-04-30 3:25 UTC (permalink / raw)
To: Baolin Wang
Cc: linux-riscv, akpm, david, ljs, Liam.Howlett, vbabka, rppt, surenb,
mhocko, linux-arm-kernel, x86, linux-parisc, linuxppc-dev,
linux-s390, kvm, linux-mm, linux-kernel
In-Reply-To: <cover.1774075004.git.baolin.wang@linux.alibaba.com>
Hello:
This series was applied to riscv/linux.git (fixes)
by Andrew Morton <akpm@linux-foundation.org>:
On Sat, 21 Mar 2026 14:42:48 +0800 you wrote:
> This is a cleanup patchset to change all young flag check functions to
> return bool, as discussed with David in the previous thread[1]. Since
> callers only care about whether the young flag was set, returning bool
> makes the intention clearer. No functional changes intended.
>
> Ran mm selftests on Arm64 and x86 machines, and no issues were found.
>
> [...]
Here is the summary with links:
- [v2,1/6] mm: change to return bool for ptep_test_and_clear_young()
https://git.kernel.org/riscv/c/a62ca3f40fea
- [v2,2/6] mm: change to return bool for ptep_clear_flush_young()/clear_flush_young_ptes()
https://git.kernel.org/riscv/c/06c4dfa3ced6
- [v2,3/6] mm: change to return bool for pmdp_test_and_clear_young()
https://git.kernel.org/riscv/c/42e26354c4ef
- [v2,4/6] mm: change to return bool for pmdp_clear_flush_young()
https://git.kernel.org/riscv/c/2d46a3974721
- [v2,5/6] mm: change to return bool for pudp_test_and_clear_young()
https://git.kernel.org/riscv/c/fb87c8827297
- [v2,6/6] mm: change to return bool for the MMU notifier's young flag check
https://git.kernel.org/riscv/c/1fc7dc675e26
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH v2 00/33] rust: bump minimum Rust and `bindgen` versions
From: patchwork-bot+linux-riscv @ 2026-04-30 3:25 UTC (permalink / raw)
To: Miguel Ojeda
Cc: linux-riscv, nathan, nsc, dakr, a.hindborg, catalin.marinas, will,
pjw, palmer, aou, acourbot, airlied, simona, brendan.higgins,
david, gregkh, arve, tkjos, christian, cmllamas, aliceryhl,
corbet, boqun, gary, bjorn3_gh, lossin, tmgross, rust-for-linux,
linux-kbuild, lorenzo.stoakes, vbabka, Liam.Howlett, urezki,
linux-block, linux-arm-kernel, alex, nouveau, dri-devel,
raemoar63, linux-kselftest, kunit-dev, nick.desaulniers+lkml,
morbo, justinstitt, llvm, linux-kernel, skhan, linux-doc
In-Reply-To: <20260405235309.418950-1-ojeda@kernel.org>
Hello:
This series was applied to riscv/linux.git (fixes)
by Miguel Ojeda <ojeda@kernel.org>:
On Mon, 6 Apr 2026 01:52:36 +0200 you wrote:
> As proposed in the past in e.g. LPC 2025 and the Maintainers Summit [1],
> we are going to follow Debian Stable's Rust versions as our minimum
> supported version.
>
> Debian Trixie was released with a Rust 1.85.0 toolchain [2], which it
> still uses to this day [3] (i.e. no update to Rust 1.85.1).
>
> [...]
Here is the summary with links:
- [v2,01/33] rust: kbuild: remove `--remap-path-prefix` workarounds
https://git.kernel.org/riscv/c/c8cbe2fc22e4
- [v2,02/33] rust: kbuild: remove "`try` keyword" workaround for `bindgen` < 0.59.2
https://git.kernel.org/riscv/c/518b9ad2fab3
- [v2,03/33] rust: kbuild: remove unneeded old `allow`s for generated layout tests
https://git.kernel.org/riscv/c/92cc022f044f
- [v2,04/33] gpu: nova-core: bindings: remove unneeded `cfg_attr`
https://git.kernel.org/riscv/c/341c51ee3293
- [v2,05/33] rust: bump Rust minimum supported version to 1.85.0 (Debian Trixie)
https://git.kernel.org/riscv/c/f32fb9c58a5b
- [v2,06/33] rust: bump Clippy's MSRV and clean `incompatible_msrv` allows
https://git.kernel.org/riscv/c/b6cfba436623
- [v2,07/33] rust: allow globally `clippy::incompatible_msrv`
https://git.kernel.org/riscv/c/7ed188605e1d
- [v2,08/33] rust: simplify `RUSTC_VERSION` Kconfig conditions
https://git.kernel.org/riscv/c/b28711ac98e8
- [v2,09/33] rust: remove `RUSTC_HAS_SLICE_AS_FLATTENED` and simplify code
https://git.kernel.org/riscv/c/9b398d056543
- [v2,10/33] rust: remove `RUSTC_HAS_COERCE_POINTEE` and simplify code
https://git.kernel.org/riscv/c/4ab22c543f18
- [v2,11/33] rust: kbuild: remove skipping of `-Wrustdoc::unescaped_backticks`
https://git.kernel.org/riscv/c/0f6e1e0705f0
- [v2,12/33] rust: kbuild: remove `feature(...)`s that are now stable
https://git.kernel.org/riscv/c/d1aa40daa777
- [v2,13/33] rust: transmute: simplify code with Rust 1.80.0 `split_at_*checked()`
https://git.kernel.org/riscv/c/f309a6edda53
- [v2,14/33] rust: alloc: simplify with `NonNull::add()` now that it is stable
https://git.kernel.org/riscv/c/161dd7b51e96
- [v2,15/33] rust: macros: simplify code using `feature(extract_if)`
https://git.kernel.org/riscv/c/42ec980024f0
- [v2,16/33] rust: block: update `const_refs_to_static` MSRV TODO comment
https://git.kernel.org/riscv/c/961b72d45ae4
- [v2,17/33] rust: bump `bindgen` minimum supported version to 0.71.1 (Debian Trixie)
https://git.kernel.org/riscv/c/c3a00a3f31ff
- [v2,18/33] rust: rust_is_available: remove warning for `bindgen` 0.66.[01]
https://git.kernel.org/riscv/c/41cfbb4295cf
- [v2,19/33] rust: rust_is_available: remove warning for `bindgen` < 0.69.5 && libclang >= 19.1
https://git.kernel.org/riscv/c/ae64324ad5c1
- [v2,20/33] rust: kbuild: update `bindgen --rust-target` version and replace comment
https://git.kernel.org/riscv/c/276ed30c558e
- [v2,21/33] rust: kbuild: remove "dummy parameter" workaround for `bindgen` < 0.71.1
https://git.kernel.org/riscv/c/93553d9922b0
- [v2,22/33] docs: rust: quick-start: openSUSE provides `rust-src` package nowadays
https://git.kernel.org/riscv/c/53c9647c0a48
- [v2,23/33] docs: rust: quick-start: update Ubuntu versioned packages
https://git.kernel.org/riscv/c/982e1aa6de73
- [v2,24/33] docs: rust: quick-start: update minimum Ubuntu version
https://git.kernel.org/riscv/c/6767147cb941
- [v2,25/33] docs: rust: quick-start: add Ubuntu 26.04 LTS and remove subsection title
https://git.kernel.org/riscv/c/780f847e1419
- [v2,26/33] docs: rust: quick-start: remove Gentoo "testing" note
https://git.kernel.org/riscv/c/99c672426aed
- [v2,27/33] docs: rust: quick-start: remove Nix "unstable channel" note
https://git.kernel.org/riscv/c/b69a14650009
- [v2,28/33] docs: rust: quick-start: remove GDB/Binutils mention
https://git.kernel.org/riscv/c/a4392ed1c8b9
- [v2,29/33] docs: rust: general-information: simplify Kconfig example
https://git.kernel.org/riscv/c/9375ea727d7e
- [v2,30/33] docs: rust: general-information: use real example
https://git.kernel.org/riscv/c/86c5d1c6740c
- [v2,31/33] rust: declare cfi_encoding for lru_status
https://git.kernel.org/riscv/c/9e5946de3a38
- [v2,32/33] rust: kbuild: support global per-version flags
https://git.kernel.org/riscv/c/b2aa1535ecdd
- [v2,33/33] rust: kbuild: allow `clippy::precedence` for Rust < 1.86.0
https://git.kernel.org/riscv/c/2e2f8b5a0656
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* [PATCH v6 4/4] arm64: dts: imx93: update the tmu compatible string
From: Jacky Bai @ 2026-04-30 2:53 UTC (permalink / raw)
To: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Shawn Guo,
Sascha Hauer, Fabio Estevam, Pengutronix Kernel Team, Frank Li
Cc: linux-pm, devicetree, imx, linux-arm-kernel, Jacky Bai
In-Reply-To: <20260430-imx93_tmu-v6-0-485459d7b54f@nxp.com>
The i.MX93 TMU node compatible need to be updated to apply the SoC
specific configuration and TMU errata workaround.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Jacky Bai <ping.bai@nxp.com>
---
- v6 changes:
- no
- v5 changes:
- no
- v4 changes:
- no
---
arch/arm64/boot/dts/freescale/imx93.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/freescale/imx93.dtsi b/arch/arm64/boot/dts/freescale/imx93.dtsi
index b9abe143cb567e722277960ff677d460154dfc8a..06443d52a4290f9525c320f8bcf5fba1a8435d11 100644
--- a/arch/arm64/boot/dts/freescale/imx93.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx93.dtsi
@@ -80,7 +80,7 @@ mu1: mailbox@44230000 {
};
tmu: tmu@44482000 {
- compatible = "fsl,qoriq-tmu";
+ compatible = "fsl,imx93-tmu";
reg = <0x44482000 0x1000>;
interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk IMX93_CLK_TMC_GATE>;
--
2.34.1
^ permalink raw reply related
* [PATCH v6 3/4] thermal: qoriq: workaround unexpected temperature readings from tmu
From: Jacky Bai @ 2026-04-30 2:53 UTC (permalink / raw)
To: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Shawn Guo,
Sascha Hauer, Fabio Estevam, Pengutronix Kernel Team, Frank Li
Cc: linux-pm, devicetree, imx, linux-arm-kernel, Jacky Bai
In-Reply-To: <20260430-imx93_tmu-v6-0-485459d7b54f@nxp.com>
Invalid temperature measurements may be observed across the temperature
range specified in the device data sheet. The invalid temperature can
be read from any remote site and from any capture or report registers.
The invalid change in temperature can be positive or negative and the
resulting temperature can be outside the calibrated range, in which
case the TSR[ORL] or TSR[ORH] bit will be set.
Workaround:
Use the raising/falling edge threshold to filter out the invalid temp.
Check the TIDR register to make sure no jump happens When reading the temp.
i.MX93 ERR052243:
(https://www.nxp.com/webapp/Download?colCode=IMX93_2P87F&appType=license)
Signed-off-by: Jacky Bai <ping.bai@nxp.com>
---
- v6 changes:
- remove the first errata check condition in get_temp function
- v5 changes:
- replace the check errata macro with inline function
- v4 changes:
- include bitfield.h to fix compilation errors for RISC-V
- use macro define for temp rate related setting
- v3 changes:
- refine the code with FIELD_PREP macro
- add errata doc url link and refine the commit log
---
drivers/thermal/qoriq_thermal.c | 40 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 39 insertions(+), 1 deletion(-)
diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c
index e4b61d531e44f6927b74e79a81f1e63dda7f9aca..35439ec5f8bcbd97e20ab8e770f5ee00e812da1f 100644
--- a/drivers/thermal/qoriq_thermal.c
+++ b/drivers/thermal/qoriq_thermal.c
@@ -3,6 +3,7 @@
// Copyright 2016 Freescale Semiconductor, Inc.
// Copyright 2025 NXP
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
@@ -30,6 +31,9 @@
#define TMU_VER1 0x1
#define TMU_VER2 0x2
+/* errata ID info define */
+#define TMU_ERR052243 BIT(0)
+
#define REGS_TMR 0x000 /* Mode Register */
#define TMR_DISABLE 0x0
#define TMR_ME 0x80000000
@@ -45,6 +49,15 @@
#define REGS_TIER 0x020 /* Interrupt Enable Register */
#define TIER_DISABLE 0x0
+#define REGS_TIDR 0x24
+#define TEMP_RATE_IRQ_MASK GENMASK(25, 24)
+#define TMRTRCTR 0x70
+#define TMRTRCTR_EN BIT(31)
+#define TMRTRCTR_TEMP_MASK GENMASK(7, 0)
+#define TMFTRCTR 0x74
+#define TMFTRCTR_EN BIT(31)
+#define TMFTRCTR_TEMP_MASK GENMASK(7, 0)
+#define TEMP_RATE_THR_LVL 0x7
#define REGS_TTCFGR 0x080 /* Temperature Configuration Register */
#define REGS_TSCFGR 0x084 /* Sensor Configuration Register */
@@ -77,6 +90,7 @@ struct qoriq_sensor {
struct tmu_drvdata {
u32 teumr0;
+ u32 tmu_errata;
};
struct qoriq_tmu_data {
@@ -88,6 +102,12 @@ struct qoriq_tmu_data {
const struct tmu_drvdata *drvdata;
};
+static inline bool qoriq_tmu_has_errata(const struct tmu_drvdata *drvdata,
+ u32 flag)
+{
+ return drvdata->tmu_errata & flag;
+}
+
static struct qoriq_tmu_data *qoriq_sensor_to_data(struct qoriq_sensor *s)
{
return container_of(s, struct qoriq_tmu_data, sensor[s->id]);
@@ -97,7 +117,7 @@ static int tmu_get_temp(struct thermal_zone_device *tz, int *temp)
{
struct qoriq_sensor *qsensor = thermal_zone_device_priv(tz);
struct qoriq_tmu_data *qdata = qoriq_sensor_to_data(qsensor);
- u32 val;
+ u32 val, tidr;
/*
* REGS_TRITSR(id) has the following layout:
*
@@ -130,6 +150,15 @@ static int tmu_get_temp(struct thermal_zone_device *tz, int *temp)
10 * USEC_PER_MSEC))
return -ENODATA;
+ /*ERR052243: If a raising or falling edge happens, try later */
+ if (qoriq_tmu_has_errata(qdata->drvdata, TMU_ERR052243)) {
+ regmap_read(qdata->regmap, REGS_TIDR, &tidr);
+ if (tidr & TEMP_RATE_IRQ_MASK) {
+ regmap_write(qdata->regmap, REGS_TIDR, TEMP_RATE_IRQ_MASK);
+ return -EAGAIN;
+ }
+ }
+
if (qdata->ver == TMU_VER1) {
*temp = (val & GENMASK(7, 0)) * MILLIDEGREE_PER_DEGREE;
} else {
@@ -245,6 +274,14 @@ static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
data->drvdata->teumr0);
}
+ /* ERR052243: Set the raising & falling edge monitor */
+ if (qoriq_tmu_has_errata(data->drvdata, TMU_ERR052243)) {
+ regmap_write(data->regmap, TMRTRCTR, TMRTRCTR_EN |
+ FIELD_PREP(TMRTRCTR_TEMP_MASK, TEMP_RATE_THR_LVL));
+ regmap_write(data->regmap, TMFTRCTR, TMFTRCTR_EN |
+ FIELD_PREP(TMFTRCTR_TEMP_MASK, TEMP_RATE_THR_LVL));
+
+ }
/* Disable monitoring */
regmap_write(data->regmap, REGS_TMR, TMR_DISABLE);
}
@@ -398,6 +435,7 @@ static const struct tmu_drvdata imx8mq_tmu_data = {
static const struct tmu_drvdata imx93_data = {
.teumr0 = TEUMR0_V21,
+ .tmu_errata = TMU_ERR052243,
};
static const struct of_device_id qoriq_tmu_match[] = {
--
2.34.1
^ permalink raw reply related
* [PATCH v6 2/4] thermal: qoriq: add i.MX93 tmu support
From: Jacky Bai @ 2026-04-30 2:53 UTC (permalink / raw)
To: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Shawn Guo,
Sascha Hauer, Fabio Estevam, Pengutronix Kernel Team, Frank Li
Cc: linux-pm, devicetree, imx, linux-arm-kernel, Jacky Bai, Alice Guo
In-Reply-To: <20260430-imx93_tmu-v6-0-485459d7b54f@nxp.com>
For Thermal monitor unit(TMU) used on i.MX93, the HW revision info read
from the ID register is the same the one used on some of the QorIQ
platform, but the config has some slight differance. Add i.MX93 compatible
string and corresponding code for it.
Signed-off-by: Alice Guo <alice.guo@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Jacky Bai <ping.bai@nxp.com>
---
- v6 changes:
- drop the unnecessary local teumr0_val variable
- v5 changes:
- drop the unnecessary GET_TEUMR0 helper macro
- add drvdata for each of the platforms
- v4 changes:
- no
---
drivers/thermal/qoriq_thermal.c | 31 ++++++++++++++++++++++++++++---
1 file changed, 28 insertions(+), 3 deletions(-)
diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c
index 01b58be0dcc64d14ca5e4bba654eed8f15e827fc..e4b61d531e44f6927b74e79a81f1e63dda7f9aca 100644
--- a/drivers/thermal/qoriq_thermal.c
+++ b/drivers/thermal/qoriq_thermal.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
//
// Copyright 2016 Freescale Semiconductor, Inc.
+// Copyright 2025 NXP
#include <linux/clk.h>
#include <linux/err.h>
@@ -24,6 +25,7 @@
#define TMTMIR_DEFAULT 0x0000000f
#define TIER_DISABLE 0x0
#define TEUMR0_V2 0x51009c00
+#define TEUMR0_V21 0x55000c00
#define TMSARA_V2 0xe
#define TMU_VER1 0x1
#define TMU_VER2 0x2
@@ -73,12 +75,17 @@ struct qoriq_sensor {
int id;
};
+struct tmu_drvdata {
+ u32 teumr0;
+};
+
struct qoriq_tmu_data {
int ver;
u32 ttrcr[NUM_TTRCR_MAX];
struct regmap *regmap;
struct clk *clk;
struct qoriq_sensor sensor[SITES_MAX];
+ const struct tmu_drvdata *drvdata;
};
static struct qoriq_tmu_data *qoriq_sensor_to_data(struct qoriq_sensor *s)
@@ -234,7 +241,8 @@ static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
regmap_write(data->regmap, REGS_TMTMIR, TMTMIR_DEFAULT);
} else {
regmap_write(data->regmap, REGS_V2_TMTMIR, TMTMIR_DEFAULT);
- regmap_write(data->regmap, REGS_V2_TEUMR(0), TEUMR0_V2);
+ regmap_write(data->regmap, REGS_V2_TEUMR(0),
+ data->drvdata->teumr0);
}
/* Disable monitoring */
@@ -319,6 +327,10 @@ static int qoriq_tmu_probe(struct platform_device *pdev)
data->ver = (ver >> 8) & 0xff;
+ data->drvdata = of_device_get_match_data(&pdev->dev);
+ if (!data->drvdata)
+ return dev_err_probe(dev, -EINVAL, "Failed to get match data\n");
+
qoriq_tmu_init_device(data); /* TMU initialization */
ret = qoriq_tmu_calibration(dev, data); /* TMU calibration */
@@ -376,9 +388,22 @@ static int qoriq_tmu_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
qoriq_tmu_suspend, qoriq_tmu_resume);
+static const struct tmu_drvdata qoriq_tmu_data = {
+ .teumr0 = TEUMR0_V2,
+};
+
+static const struct tmu_drvdata imx8mq_tmu_data = {
+ .teumr0 = TEUMR0_V2,
+};
+
+static const struct tmu_drvdata imx93_data = {
+ .teumr0 = TEUMR0_V21,
+};
+
static const struct of_device_id qoriq_tmu_match[] = {
- { .compatible = "fsl,qoriq-tmu", },
- { .compatible = "fsl,imx8mq-tmu", },
+ { .compatible = "fsl,qoriq-tmu", .data = &qoriq_tmu_data },
+ { .compatible = "fsl,imx8mq-tmu", .data = &imx8mq_tmu_data },
+ { .compatible = "fsl,imx93-tmu", .data = &imx93_data },
{},
};
MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
--
2.34.1
^ permalink raw reply related
* [PATCH v6 1/4] dt-bindings: thermal: qoriq: Add compatible string for imx93
From: Jacky Bai @ 2026-04-30 2:53 UTC (permalink / raw)
To: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Shawn Guo,
Sascha Hauer, Fabio Estevam, Pengutronix Kernel Team, Frank Li
Cc: linux-pm, devicetree, imx, linux-arm-kernel, Jacky Bai,
Conor Dooley
In-Reply-To: <20260430-imx93_tmu-v6-0-485459d7b54f@nxp.com>
Add i.MX93 compatible string 'fsl,imx93-tmu' because Thermal monitor
unit(TMU) on i.MX93 has differences with QorIQ platform and not fully
compatible with existing Platform, such as fsl,qoriq-tmu.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Jacky Bai <ping.bai@nxp.com>
---
- v6 changes:
- no
- v5 changes:
- no
---
Documentation/devicetree/bindings/thermal/qoriq-thermal.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/thermal/qoriq-thermal.yaml b/Documentation/devicetree/bindings/thermal/qoriq-thermal.yaml
index aa756dae512a2c6e3f0b6bb1ab4a65c01e373ea7..f3b136f5e1cba1fff7e90678d7e3d7ec3ddd25c1 100644
--- a/Documentation/devicetree/bindings/thermal/qoriq-thermal.yaml
+++ b/Documentation/devicetree/bindings/thermal/qoriq-thermal.yaml
@@ -25,6 +25,7 @@ properties:
enum:
- fsl,qoriq-tmu
- fsl,imx8mq-tmu
+ - fsl,imx93-tmu
reg:
maxItems: 1
--
2.34.1
^ permalink raw reply related
* [PATCH v6 0/4] Update the thermal support for imx93
From: Jacky Bai @ 2026-04-30 2:53 UTC (permalink / raw)
To: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Shawn Guo,
Sascha Hauer, Fabio Estevam, Pengutronix Kernel Team, Frank Li
Cc: linux-pm, devicetree, imx, linux-arm-kernel, Jacky Bai,
Conor Dooley, Alice Guo
The TMU (Thermal Monitoring Unit) on the i.MX93 requires specific
configurations and workarounds that differ from previous implementations.
So, using the 'fsl,qoriq-tmu' compatible string is not appropriate.
To address this, a dedicated compatible string and corresponding driver
changes need to be introduced to properly support the i.MX93 TMU.
Signed-off-by: Jacky Bai <ping.bai@nxp.com>
---
Changes in v6:
- Drop the unnecessary local variable
- Drop the first errata check in get_temp function
- Link to v5: https://lore.kernel.org/r/20260421-imx93_tmu-v5-0-05ea1969bb9f@nxp.com
Changes in v5:
- Drop the unnecessary macro defines in patch 2/3
- Add the drvdata info for each of the platform as suggested by Daniel
- Link to v4: https://lore.kernel.org/r/20250821-imx93_tmu-v4-0-6cf5688bf016@nxp.com
Changes in v4:
- Include bitfield.h to fix the build error for RISC-V
- Use macro to define temp rate threshold related settings
- Link to v3: https://lore.kernel.org/r/20250818-imx93_tmu-v3-0-35f79a86c072@nxp.com
---
Jacky Bai (4):
dt-bindings: thermal: qoriq: Add compatible string for imx93
thermal: qoriq: add i.MX93 tmu support
thermal: qoriq: workaround unexpected temperature readings from tmu
arm64: dts: imx93: update the tmu compatible string
.../devicetree/bindings/thermal/qoriq-thermal.yaml | 1 +
arch/arm64/boot/dts/freescale/imx93.dtsi | 2 +-
drivers/thermal/qoriq_thermal.c | 71 ++++++++++++++++++++--
3 files changed, 69 insertions(+), 5 deletions(-)
---
base-commit: c7275b05bc428c7373d97aa2da02d3a7fa6b9f66
change-id: 20250804-imx93_tmu-7888c85d176e
Best regards,
--
Jacky Bai <ping.bai@nxp.com>
^ permalink raw reply
* [PATCH v5 net-next 15/15] net: dsa: netc: add support for ethtool private statistics
From: Wei Fang @ 2026-04-30 2:49 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, robh, krzk+dt, conor+dt,
f.fainelli, frank.li, chleroy, horms, linux
Cc: netdev, linux-kernel, devicetree, linuxppc-dev, linux-arm-kernel,
imx
In-Reply-To: <20260430024945.3413973-1-wei.fang@nxp.com>
Implement the ethtool private statistics interface to expose additional
port-level and MAC-level counters that are not covered by the standard
IEEE 802.3 statistics. The pMAC counters are only reported when the port
supports Frame Preemption (802.1Qbu/802.3br).
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
drivers/net/dsa/netc/netc_ethtool.c | 107 ++++++++++++++++++++++++++
drivers/net/dsa/netc/netc_main.c | 3 +
drivers/net/dsa/netc/netc_switch.h | 9 +++
drivers/net/dsa/netc/netc_switch_hw.h | 58 ++++++++++++++
4 files changed, 177 insertions(+)
diff --git a/drivers/net/dsa/netc/netc_ethtool.c b/drivers/net/dsa/netc/netc_ethtool.c
index ac8940b5a85c..8d04db534347 100644
--- a/drivers/net/dsa/netc/netc_ethtool.c
+++ b/drivers/net/dsa/netc/netc_ethtool.c
@@ -19,6 +19,56 @@ static const struct ethtool_rmon_hist_range netc_rmon_ranges[] = {
{ }
};
+static const struct netc_port_stat netc_port_counters[] = {
+ { NETC_PTGSLACR, "port gate late arrival frames" },
+ { NETC_PSDFTCR, "port SDF transmit frames" },
+ { NETC_PSDFDDCR, "port SDF drop duplicate frames" },
+ { NETC_PRXDCR, "port rx discard frames" },
+ { NETC_PRXDCRRR, "port rx discard read-reset" },
+ { NETC_PRXDCRR0, "port rx discard reason 0" },
+ { NETC_PRXDCRR1, "port rx discard reason 1" },
+ { NETC_PTXDCR, "port tx discard frames" },
+ { NETC_PTXDCRRR, "port tx discard read-reset" },
+ { NETC_PTXDCRR0, "port tx discard reason 0" },
+ { NETC_PTXDCRR1, "port tx discard reason 1" },
+ { NETC_BPDCR, "bridge port discard frames" },
+ { NETC_BPDCRRR, "bridge port discard read-reset" },
+ { NETC_BPDCRR0, "bridge port discard reason 0" },
+ { NETC_BPDCRR1, "bridge port discard reason 1" },
+};
+
+static const struct netc_port_stat netc_emac_counters[] = {
+ { NETC_PM_ROCT(0), "eMAC rx octets" },
+ { NETC_PM_RVLAN(0), "eMAC rx VLAN frames" },
+ { NETC_PM_RERR(0), "eMAC rx frame errors" },
+ { NETC_PM_RUCA(0), "eMAC rx unicast frames" },
+ { NETC_PM_RDRP(0), "eMAC rx dropped packets" },
+ { NETC_PM_RPKT(0), "eMAC rx packets" },
+ { NETC_PM_TOCT(0), "eMAC tx octets" },
+ { NETC_PM_TVLAN(0), "eMAC tx VLAN frames" },
+ { NETC_PM_TFCS(0), "eMAC tx FCS errors" },
+ { NETC_PM_TUCA(0), "eMAC tx unicast frames" },
+ { NETC_PM_TPKT(0), "eMAC tx packets" },
+ { NETC_PM_TUND(0), "eMAC tx undersized packets" },
+ { NETC_PM_TIOCT(0), "eMAC tx invalid octets" },
+};
+
+static const struct netc_port_stat netc_pmac_counters[] = {
+ { NETC_PM_ROCT(1), "pMAC rx octets" },
+ { NETC_PM_RVLAN(1), "pMAC rx VLAN frames" },
+ { NETC_PM_RERR(1), "pMAC rx frame errors" },
+ { NETC_PM_RUCA(1), "pMAC rx unicast frames" },
+ { NETC_PM_RDRP(1), "pMAC rx dropped packets" },
+ { NETC_PM_RPKT(1), "pMAC rx packets" },
+ { NETC_PM_TOCT(1), "pMAC tx octets" },
+ { NETC_PM_TVLAN(1), "pMAC tx VLAN frames" },
+ { NETC_PM_TFCS(1), "pMAC tx FCS errors" },
+ { NETC_PM_TUCA(1), "pMAC tx unicast frames" },
+ { NETC_PM_TPKT(1), "pMAC tx packets" },
+ { NETC_PM_TUND(1), "pMAC tx undersized packets" },
+ { NETC_PM_TIOCT(1), "pMAC tx invalid octets" },
+};
+
static void netc_port_pause_stats(struct netc_port *np, int mac,
struct ethtool_pause_stats *stats)
{
@@ -188,3 +238,60 @@ void netc_port_get_eth_mac_stats(struct dsa_switch *ds, int port,
break;
}
}
+
+int netc_port_get_sset_count(struct dsa_switch *ds, int port, int sset)
+{
+ struct netc_port *np = NETC_PORT(ds, port);
+ int size;
+
+ if (sset != ETH_SS_STATS)
+ return -EOPNOTSUPP;
+
+ size = ARRAY_SIZE(netc_port_counters) +
+ ARRAY_SIZE(netc_emac_counters);
+
+ if (np->caps.pmac)
+ size += ARRAY_SIZE(netc_pmac_counters);
+
+ return size;
+}
+
+void netc_port_get_strings(struct dsa_switch *ds, int port,
+ u32 sset, u8 *data)
+{
+ struct netc_port *np = NETC_PORT(ds, port);
+ int i;
+
+ if (sset != ETH_SS_STATS)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(netc_port_counters); i++)
+ ethtool_cpy(&data, netc_port_counters[i].name);
+
+ for (i = 0; i < ARRAY_SIZE(netc_emac_counters); i++)
+ ethtool_cpy(&data, netc_emac_counters[i].name);
+
+ if (!np->caps.pmac)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(netc_pmac_counters); i++)
+ ethtool_cpy(&data, netc_pmac_counters[i].name);
+}
+
+void netc_port_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data)
+{
+ struct netc_port *np = NETC_PORT(ds, port);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(netc_port_counters); i++)
+ *data++ = netc_port_rd(np, netc_port_counters[i].reg);
+
+ for (i = 0; i < ARRAY_SIZE(netc_emac_counters); i++)
+ *data++ = netc_port_rd64(np, netc_emac_counters[i].reg);
+
+ if (!np->caps.pmac)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(netc_pmac_counters); i++)
+ *data++ = netc_port_rd64(np, netc_pmac_counters[i].reg);
+}
diff --git a/drivers/net/dsa/netc/netc_main.c b/drivers/net/dsa/netc/netc_main.c
index ae8f28b82b72..803582d939b0 100644
--- a/drivers/net/dsa/netc/netc_main.c
+++ b/drivers/net/dsa/netc/netc_main.c
@@ -1472,6 +1472,9 @@ static const struct dsa_switch_ops netc_switch_ops = {
.get_rmon_stats = netc_port_get_rmon_stats,
.get_eth_ctrl_stats = netc_port_get_eth_ctrl_stats,
.get_eth_mac_stats = netc_port_get_eth_mac_stats,
+ .get_sset_count = netc_port_get_sset_count,
+ .get_strings = netc_port_get_strings,
+ .get_ethtool_stats = netc_port_get_ethtool_stats,
};
static int netc_switch_probe(struct pci_dev *pdev,
diff --git a/drivers/net/dsa/netc/netc_switch.h b/drivers/net/dsa/netc/netc_switch.h
index d477b0cd2157..90d750331fb9 100644
--- a/drivers/net/dsa/netc/netc_switch.h
+++ b/drivers/net/dsa/netc/netc_switch.h
@@ -93,6 +93,11 @@ struct netc_fdb_entry {
struct hlist_node node;
};
+struct netc_port_stat {
+ int reg;
+ char name[ETH_GSTRING_LEN] __nonstring;
+};
+
struct netc_switch {
struct pci_dev *pdev;
struct device *dev;
@@ -159,5 +164,9 @@ void netc_port_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
struct ethtool_eth_ctrl_stats *ctrl_stats);
void netc_port_get_eth_mac_stats(struct dsa_switch *ds, int port,
struct ethtool_eth_mac_stats *mac_stats);
+int netc_port_get_sset_count(struct dsa_switch *ds, int port, int sset);
+void netc_port_get_strings(struct dsa_switch *ds, int port,
+ u32 sset, u8 *data);
+void netc_port_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data);
#endif
diff --git a/drivers/net/dsa/netc/netc_switch_hw.h b/drivers/net/dsa/netc/netc_switch_hw.h
index f8d436ad9623..1b016e7dd03e 100644
--- a/drivers/net/dsa/netc/netc_switch_hw.h
+++ b/drivers/net/dsa/netc/netc_switch_hw.h
@@ -87,6 +87,17 @@
#define PSR_TX_BUSY BIT(0)
#define PSR_RX_BUSY BIT(1)
+#define NETC_PTGSLACR 0x130
+
+#define NETC_PRXDCR 0x1c0
+#define NETC_PRXDCRRR 0x1c4
+#define NETC_PRXDCRR0 0x1c8
+#define NETC_PRXDCRR1 0x1cc
+#define NETC_PTXDCR 0x1e0
+#define NETC_PTXDCRRR 0x1e4
+#define NETC_PTXDCRR0 0x1e8
+#define NETC_PTXDCRR1 0x1ec
+
#define NETC_PTCTMSDUR(a) (0x208 + (a) * 0x20)
#define PTCTMSDUR_MAXSDU GENMASK(15, 0)
#define PTCTMSDUR_SDU_TYPE GENMASK(17, 16)
@@ -94,6 +105,9 @@
#define SDU_TYPE_MPDU 1
#define SDU_TYPE_MSDU 2
+#define NETC_PSDFTCR 0x4c4
+#define NETC_PSDFDDCR 0x4c8
+
#define NETC_BPCR 0x500
#define BPCR_DYN_LIMIT GENMASK(15, 0)
#define BPCR_MLO GENMASK(22, 20)
@@ -142,6 +156,11 @@ enum netc_stg_stage {
NETC_STG_STATE_FORWARDING,
};
+#define NETC_BPDCR 0x580
+#define NETC_BPDCRRR 0x584
+#define NETC_BPDCRR0 0x588
+#define NETC_BPDCRR1 0x58c
+
/* Definition of Switch ethernet MAC port registers */
#define NETC_PMAC_OFFSET 0x400
#define NETC_PM_CMD_CFG(a) (0x1008 + (a) * 0x400)
@@ -176,6 +195,9 @@ enum netc_stg_stage {
/* Port MAC 0/1 Receive Ethernet Octets Counter */
#define NETC_PM_REOCT(a) (0x1100 + (a) * 0x400)
+/* Port MAC 0/1 Receive Octets Counter */
+#define NETC_PM_ROCT(a) (0x1108 + (a) * 0x400)
+
/* Port MAC 0/1 Receive Alignment Error Counter Register */
#define NETC_PM_RALN(a) (0x1110 + (a) * 0x400)
@@ -188,12 +210,27 @@ enum netc_stg_stage {
/* Port MAC 0/1 Receive Frame Check Sequence Error Counter */
#define NETC_PM_RFCS(a) (0x1128 + (a) * 0x400)
+/* Port MAC 0/1 Receive VLAN Frame Counter */
+#define NETC_PM_RVLAN(a) (0x1130 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive Frame Error Counter */
+#define NETC_PM_RERR(a) (0x1138 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive Unicast Frame Counter */
+#define NETC_PM_RUCA(a) (0x1140 + (a) * 0x400)
+
/* Port MAC 0/1 Receive Multicast Frame Counter */
#define NETC_PM_RMCA(a) (0x1148 + (a) * 0x400)
/* Port MAC 0/1 Receive Broadcast Frame Counter */
#define NETC_PM_RBCA(a) (0x1150 + (a) * 0x400)
+/* Port MAC 0/1 Receive Dropped Packets Counter */
+#define NETC_PM_RDRP(a) (0x1158 + (a) * 0x400)
+
+/* Port MAC 0/1 Receive Packets Counter */
+#define NETC_PM_RPKT(a) (0x1160 + (a) * 0x400)
+
/* Port MAC 0/1 Receive Undersized Packet Counter */
#define NETC_PM_RUND(a) (0x1168 + (a) * 0x400)
@@ -236,6 +273,9 @@ enum netc_stg_stage {
/* Port MAC 0/1 Transmit Ethernet Octets Counter */
#define NETC_PM_TEOCT(a) (0x1200 + (a) * 0x400)
+/* Port MAC 0/1 Transmit Octets Counter */
+#define NETC_PM_TOCT(a) (0x1208 + (a) * 0x400)
+
/* Port MAC 0/1 Transmit Excessive Deferral Packet Counter */
#define NETC_PM_TEDFR(a) (0x1210 + (a) * 0x400)
@@ -245,15 +285,30 @@ enum netc_stg_stage {
/* Port MAC 0/1 Transmit Frame Counter */
#define NETC_PM_TFRM(a) (0x1220 + (a) * 0x400)
+/* Port MAC 0/1 Transmit Frame Check Sequence Error Counter */
+#define NETC_PM_TFCS(a) (0x1228 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit VLAN Frame Counter */
+#define NETC_PM_TVLAN(a) (0x1230 + (a) * 0x400)
+
/* Port MAC 0/1 Transmit Frame Error Counter */
#define NETC_PM_TERR(a) (0x1238 + (a) * 0x400)
+/* Port MAC 0/1 Transmit Unicast Frame Counter */
+#define NETC_PM_TUCA(a) (0x1240 + (a) * 0x400)
+
/* Port MAC 0/1 Transmit Multicast Frame Counter */
#define NETC_PM_TMCA(a) (0x1248 + (a) * 0x400)
/* Port MAC 0/1 Transmit Broadcast Frame Counter */
#define NETC_PM_TBCA(a) (0x1250 + (a) * 0x400)
+/* Port MAC 0/1 Transmit Packets Counter */
+#define NETC_PM_TPKT(a) (0x1260 + (a) * 0x400)
+
+/* Port MAC 0/1 Transmit Undersized Packet Counter */
+#define NETC_PM_TUND(a) (0x1268 + (a) * 0x400)
+
/* Port MAC 0/1 Transmit 64-Octet Packet Counter */
#define NETC_PM_T64(a) (0x1270 + (a) * 0x400)
@@ -293,6 +348,9 @@ enum netc_stg_stage {
/* Port MAC 0/1 Transmit Excessive Collisions Counter */
#define NETC_PM_TECOL(a) (0x12f0 + (a) * 0x400)
+/* Port MAC 0/1 Transmit Invalid Octets Counter */
+#define NETC_PM_TIOCT(a) (0x12f8 + (a) * 0x400)
+
#define NETC_PEMDIOCR 0x1c00
#define NETC_EMDIO_BASE NETC_PEMDIOCR
--
2.34.1
^ permalink raw reply related
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