[PATCH 09/10] rv: Add rtapp_pagefault monitor
From: Nam Cao <hidden>
Date: 2025-03-11 17:05:32
Also in:
linux-arm-kernel, linux-riscv, lkml
Subsystem:
documentation, runtime verification (rv), the rest, tracing · Maintainers:
Jonathan Corbet, Steven Rostedt, Gabriele Monaco, Linus Torvalds, Masami Hiramatsu
Real time applications can have unexpected latency due to page faults. Add an RV LTL monitor to detect such cases. For complete description, see Documentation/trace/rv/monitor_rtapp_pagefault.rst. The document is mostly from: https://www.linutronix.de/blog/A-Checklist-for-Real-Time-Applications-in-Linux Signed-off-by: Nam Cao <redacted> --- Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: Will Deacon <will@kernel.org> Cc: Paul Walmsley <redacted> Cc: Palmer Dabbelt <palmer@dabbelt.com> Cc: Albert Ou <aou@eecs.berkeley.edu> Cc: Alexandre Ghiti <alex@ghiti.fr> Cc: Thomas Gleixner <redacted> Cc: Ingo Molnar <mingo@redhat.com> Cc: Borislav Petkov <bp@alien8.de> Cc: Dave Hansen <dave.hansen@linux.intel.com> Cc: x86@kernel.org Cc: H. Peter Anvin <hpa@zytor.com> Cc: Andy Lutomirski <luto@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: linux-arm-kernel@lists.infradead.org Cc: linux-riscv@lists.infradead.org --- .../trace/rv/monitor_rtapp_pagefault.rst | 36 ++++ kernel/trace/rv/Kconfig | 8 + kernel/trace/rv/Makefile | 2 + kernel/trace/rv/monitors/rtapp_pagefault/ba.c | 139 +++++++++++++++ kernel/trace/rv/monitors/rtapp_pagefault/ba.h | 158 ++++++++++++++++++ kernel/trace/rv/monitors/rtapp_pagefault/ltl | 1 + .../rtapp_pagefault/rtapp_pagefault.c | 84 ++++++++++ kernel/trace/rv/rv_trace.h | 20 +++ 8 files changed, 448 insertions(+) create mode 100644 Documentation/trace/rv/monitor_rtapp_pagefault.rst create mode 100644 kernel/trace/rv/monitors/rtapp_pagefault/ba.c create mode 100644 kernel/trace/rv/monitors/rtapp_pagefault/ba.h create mode 100644 kernel/trace/rv/monitors/rtapp_pagefault/ltl create mode 100644 kernel/trace/rv/monitors/rtapp_pagefault/rtapp_pagefault.c
diff --git a/Documentation/trace/rv/monitor_rtapp_pagefault.rst b/Documentation/trace/rv/monitor_rtapp_pagefault.rst
new file mode 100644
index 000000000000..0ee223d83d09
--- /dev/null
+++ b/Documentation/trace/rv/monitor_rtapp_pagefault.rst@@ -0,0 +1,36 @@ +Monitor rtapp_pagefault +======================= + +- Name: rtapp_pagefault - realtime applications raising page faults +- Type: per-task linear temporal logic monitor +- Author: Nam Cao <namcao@linutronix.de> + +Introduction +------------ + +One of the most devastating situations for a real-time application is the need to assign or "page +in" memory. This can be due to the over-commitment behavior of Linux when accessing allocated or +reserved memory for the first time. Or it can be paging in disk data (such as text segments) when +calling functions for the first time. Whatever the case, it must be avoided in order to meet +response requirements. + +The monitor reports these situation where real-time applications raise page faults. + +How to fix the monitor's warnings? +---------------------------------- + +The first thing a real-time application needs to do is configure glibc to use a single +non-shrinkable heap for the application. This guarantees that a pool of readily accessible physical +RAM can be made available to the real-time application. This is accomplished using the mallopt(3) +function (M_MMAP_MAX=0, M_ARENA_MAX=1, M_TRIM_THRESHOLD=-1). + +Next, all allocated and mapped virtual memory must be assigned to physical RAM and locked so that it +cannot be reclaimed for other purposes. This is accomplished using the mlockall(2) function +(MCL_CURRENT | MCL_FUTURE). + +Finally, the amounts of stack and heap needed during the lifetime of the real-time application must +be allocated and written to in order to trigger heap and stack assignments to physical RAM. This is +known as pre-faulting and is usually accomplished by memsetting a large buffer within a stack frame +and allocating, memsetting, and freeing a large heap buffer. + +Pitfall: Keep in mind that each thread will have its own stack.
diff --git a/kernel/trace/rv/Kconfig b/kernel/trace/rv/Kconfig
index d65bf9bda2f2..2822af1b2b4d 100644
--- a/kernel/trace/rv/Kconfig
+++ b/kernel/trace/rv/Kconfig@@ -39,6 +39,14 @@ config RV_MON_RTAPP_BLOCK Enable rtapp_wakeup which monitors that realtime tasks are not blocked. For details, see Documentation/trace/rv/monitor_rtapp_block.rst. +config RV_MON_RTAPP_PAGEFAULT + depends on RV + select DA_MON_EVENTS + bool "rtapp_pagefault monitor" + help + Enable rtapp_pagefault which monitors that real-time tasks do not raise page faults. + See Documentation/trace/rv/monitor_rtapp_pagefault.rst for full details. + config RV_REACTORS bool "Runtime verification reactors" default y
diff --git a/kernel/trace/rv/Makefile b/kernel/trace/rv/Makefile
index 6570a3116127..7f21a679ad70 100644
--- a/kernel/trace/rv/Makefile
+++ b/kernel/trace/rv/Makefile@@ -7,6 +7,8 @@ obj-$(CONFIG_RV_MON_WIP) += monitors/wip/wip.o obj-$(CONFIG_RV_MON_WWNR) += monitors/wwnr/wwnr.o obj-$(CONFIG_RV_MON_RTAPP_BLOCK) += monitors/rtapp_block/ba.o \ monitors/rtapp_block/rtapp_block.o +obj-$(CONFIG_RV_MON_RTAPP_PAGEFAULT) += monitors/rtapp_pagefault/ba.o \ + monitors/rtapp_pagefault/rtapp_pagefault.o # Add new monitors here obj-$(CONFIG_RV_REACTORS) += rv_reactors.o obj-$(CONFIG_RV_REACT_PRINTK) += reactor_printk.o
diff --git a/kernel/trace/rv/monitors/rtapp_pagefault/ba.c b/kernel/trace/rv/monitors/rtapp_pagefault/ba.c
new file mode 100644
index 000000000000..6f6322717854
--- /dev/null
+++ b/kernel/trace/rv/monitors/rtapp_pagefault/ba.c@@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is generated, do not edit. + */ +#include <linux/rv.h> +#include <rv/instrumentation.h> +#include <trace/events/task.h> +#include <trace/events/sched.h> + +#include "ba.h" + +static_assert(NUM_ATOM <= RV_MAX_LTL_ATOM); + +enum buchi_state { + INIT, + S1, + DEAD, +}; + +int rv_rtapp_pagefault_task_slot = RV_PER_TASK_MONITOR_INIT; + +static void init_monitor(struct task_struct *task) +{ + struct ltl_monitor *mon = rv_rtapp_pagefault_get_monitor(task); + + for (int i = 0; i < NUM_ATOM; ++i) + mon->atoms[i] = LTL_UNDETERMINED; + mon->state = INIT; +} + +static void handle_task_newtask(void *data, struct task_struct *task, unsigned long flags) +{ + struct ltl_monitor *mon = rv_rtapp_pagefault_get_monitor(task); + + init_monitor(task); + + rv_rtapp_pagefault_atoms_init(task, mon); + rv_rtapp_pagefault_atoms_fetch(task, mon); +} + +int rv_rtapp_pagefault_init(size_t data_size) +{ + struct task_struct *g, *p; + int ret, cpu; + + if (WARN_ON(data_size > RV_MAX_DATA_SIZE)) + return -EINVAL; + + ret = rv_get_task_monitor_slot(); + if (ret < 0) + return ret; + + rv_rtapp_pagefault_task_slot = ret; + + rv_attach_trace_probe("rtapp_pagefault", task_newtask, handle_task_newtask); + + read_lock(&tasklist_lock); + + for_each_process_thread(g, p) + init_monitor(p); + + for_each_present_cpu(cpu) + init_monitor(idle_task(cpu)); + + read_unlock(&tasklist_lock); + + return 0; +} + +void rv_rtapp_pagefault_destroy(void) +{ + rv_put_task_monitor_slot(rv_rtapp_pagefault_task_slot); + rv_rtapp_pagefault_task_slot = RV_PER_TASK_MONITOR_INIT; + + rv_detach_trace_probe("rtapp_pagefault", task_newtask, handle_task_newtask); +} + +static void illegal_state(struct task_struct *task, struct ltl_monitor *mon) +{ + mon->state = INIT; + rv_rtapp_pagefault_error(task, mon); +} + +static void rv_rtapp_pagefault_attempt_start(struct task_struct *task, struct ltl_monitor *mon) +{ + int i; + + mon = rv_rtapp_pagefault_get_monitor(task); + + rv_rtapp_pagefault_atoms_fetch(task, mon); + + for (i = 0; i < NUM_ATOM; ++i) { + if (mon->atoms[i] == LTL_UNDETERMINED) + return; + } + + if (((!mon->atoms[RT] || !mon->atoms[PAGEFAULT]))) + mon->state = S1; + else + illegal_state(task, mon); +} + +static void rv_rtapp_pagefault_step(struct task_struct *task, struct ltl_monitor *mon) +{ + switch (mon->state) { + case S1: + if (((!mon->atoms[RT] || !mon->atoms[PAGEFAULT]))) + mon->state = S1; + else + illegal_state(task, mon); + break; + case DEAD: + case INIT: + break; + default: + WARN_ON_ONCE(1); + } +} + +void rv_rtapp_pagefault_atom_update(struct task_struct *task, unsigned int atom, bool value) +{ + struct ltl_monitor *mon = rv_rtapp_pagefault_get_monitor(task); + + rv_rtapp_pagefault_atom_set(mon, atom, value); + + if (mon->state == DEAD) + return; + + if (mon->state == INIT) + rv_rtapp_pagefault_attempt_start(task, mon); + if (mon->state == INIT) + return; + + mon->atoms[atom] = value; + + rv_rtapp_pagefault_atoms_fetch(task, mon); + + rv_rtapp_pagefault_step(task, mon); +}
diff --git a/kernel/trace/rv/monitors/rtapp_pagefault/ba.h b/kernel/trace/rv/monitors/rtapp_pagefault/ba.h
new file mode 100644
index 000000000000..eec2068c1419
--- /dev/null
+++ b/kernel/trace/rv/monitors/rtapp_pagefault/ba.h@@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file is generated, do not edit. + * + * This file includes necessary functions to glue the Buchi automaton and the kernel together. + * Some of these functions must be manually implemented (look for "Must be implemented", or just + * let the compiler tells you). + * + * Essentially, you need to manually define the meaning of the atomic propositions in the LTL + * property. The primary function for that is rv_rtapp_pagefault_atom_update(), which can be called + * in tracepoints' handlers for example. In some specific cases where + * rv_rtapp_pagefault_atom_update() is not convenient, rv_rtapp_pagefault_atoms_fetch() can be used. + * + * rv_rtapp_pagefault_init()/rv_rtapp_pagefault_destroy() must be called while enabling/disabling + * the monitor. + * + * If the fields in struct ltl_monitor is not enough, extra custom data can be used. See + * rv_rtapp_pagefault_get_data(). + */ + +#include <linux/sched.h> + +enum rtapp_pagefault_atom { + PAGEFAULT, + RT, + NUM_ATOM +}; + +/** + * rv_rtapp_pagefault_init + * @data_size: required custom data size, can be zero + * + * Must be called while enabling the monitor + */ +int rv_rtapp_pagefault_init(size_t data_size); + +/** + * rv_rtapp_pagefault_destroy + * + * must be called while disabling the monitor + */ +void rv_rtapp_pagefault_destroy(void); + +/** + * rv_rtapp_pagefault_error - report violation of the LTL property + * @task: the task violating the LTL property + * @mon: the LTL monitor + * + * Must be implemented. This function should invoke the RV reactor and the monitor's tracepoints. + */ +void rv_rtapp_pagefault_error(struct task_struct *task, struct ltl_monitor *mon); + +extern int rv_rtapp_pagefault_task_slot; + +/** + * rv_rtapp_pagefault_get_monitor - get the struct ltl_monitor of a task + */ +static inline struct ltl_monitor *rv_rtapp_pagefault_get_monitor(struct task_struct *task) +{ + return &task->rv[rv_rtapp_pagefault_task_slot].ltl_mon; +} + +/** + * rv_rtapp_pagefault_atoms_init - initialize the atomic propositions + * @task: the task + * @mon: the LTL monitor + * + * Must be implemented. This function is called during task creation, and should initialize all + * atomic propositions. rv_rtapp_pagefault_atom_set() should be used to implement this function. + * + * This function does not have to initialize atomic propositions that are updated by + * rv_rtapp_pagefault_atoms_fetch(), because the two functions are called together. + */ +void rv_rtapp_pagefault_atoms_init(struct task_struct *task, struct ltl_monitor *mon); + +/** + * rv_rtapp_pagefault_atoms_fetch - fetch the atomic propositions + * @task: the task + * @mon: the LTL monitor + * + * Must be implemented. This function is called anytime the Buchi automaton is triggered. Its + * intended purpose is to update the atomic propositions which are expensive to trace and can be + * easily read from @task. rv_rtapp_pagefault_atom_set() should be used to implement this function. + * + * Using this function may cause incorrect verification result if it is important for the LTL that + * the atomic propositions must be updated at the correct time. Therefore, if it is possible, + * updating atomic propositions should be done with rv_rtapp_pagefault_atom_update() instead. + * + * An example where this function is useful is with the LTL property: + * always (RT imply not PAGEFAULT) + * (a realtime task does not raise page faults) + * + * In this example, adding tracepoints to track RT is complicated, because it is changed in + * differrent places (mutex's priority boosting, sched_setscheduler). Furthermore, for this LTL + * property, we don't care exactly when RT changes, as long as we have its correct value when + * PAGEFAULT==true. Therefore, it is better to update RT in rv_rtapp_pagefault_atoms_fetch(), as it + * can easily be retrieved from task_struct. + * + * This function can be empty. + */ +void rv_rtapp_pagefault_atoms_fetch(struct task_struct *task, struct ltl_monitor *mon); + +/** + * rv_rtapp_pagefault_atom_update - update an atomic proposition + * @task: the task + * @atom: the atomic proposition, one of enum rtapp_pagefault_atom + * @value: the new value for @atom + * + * Update an atomic proposition and trigger the Buchi atomaton to check for violation of the LTL + * property. This function can be called in tracepoints' handler, for example. + */ +void rv_rtapp_pagefault_atom_update(struct task_struct *task, unsigned int atom, bool value); + +/** + * rv_rtapp_pagefault_atom_get - get an atomic proposition + * @mon: the monitor + * @atom: the atomic proposition, one of enum rtapp_pagefault_atom + * + * Returns the value of an atomic proposition. + */ +static inline +enum ltl_truth_value rv_rtapp_pagefault_atom_get(struct ltl_monitor *mon, unsigned int atom) +{ + return mon->atoms[atom]; +} + +/** + * rv_rtapp_pagefault_atom_set - set an atomic proposition + * @mon: the monitor + * @atom: the atomic proposition, one of enum rtapp_pagefault_atom + * @value: the new value for @atom + * + * Update an atomic proposition without triggering the Buchi automaton. This can be useful to + * implement rv_rtapp_pagefault_atoms_fetch() and rv_rtapp_pagefault_atoms_init(). + * + * Another use case for this function is when multiple atomic propositions change at the same time, + * because calling rv_rtapp_pagefault_atom_update() (and thus triggering the Buchi automaton) + * multiple times may be incorrect. In that case, rv_rtapp_pagefault_atom_set() can be used to avoid + * triggering the Buchi automaton, and rv_rtapp_pagefault_atom_update() is only used for the last + * atomic proposition. + */ +static inline +void rv_rtapp_pagefault_atom_set(struct ltl_monitor *mon, unsigned int atom, bool value) +{ + mon->atoms[atom] = value; +} + +/** + * rv_rtapp_pagefault_get_data - get the custom data of this monitor. + * @mon: the monitor + * + * If this function is used, rv_rtapp_pagefault_init() must have been called with a positive + * data_size. + */ +static inline void *rv_rtapp_pagefault_get_data(struct ltl_monitor *mon) +{ + return &mon->data; +}
diff --git a/kernel/trace/rv/monitors/rtapp_pagefault/ltl b/kernel/trace/rv/monitors/rtapp_pagefault/ltl
new file mode 100644
index 000000000000..d7ce62102733
--- /dev/null
+++ b/kernel/trace/rv/monitors/rtapp_pagefault/ltl@@ -0,0 +1 @@ +RULE = always (RT imply not PAGEFAULT)
diff --git a/kernel/trace/rv/monitors/rtapp_pagefault/rtapp_pagefault.c b/kernel/trace/rv/monitors/rtapp_pagefault/rtapp_pagefault.c
new file mode 100644
index 000000000000..32aaae9fea46
--- /dev/null
+++ b/kernel/trace/rv/monitors/rtapp_pagefault/rtapp_pagefault.c@@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/ftrace.h> +#include <linux/tracepoint.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/rv.h> +#include <rv/instrumentation.h> +#include <linux/sched/rt.h> +#include <trace/events/sched.h> +#include <trace/events/exceptions.h> +#include <rv_trace.h> + +#include "ba.h" + +static void handle_page_fault(void *data, unsigned long address, struct pt_regs *regs, + unsigned long error_code) +{ + rv_rtapp_pagefault_atom_update(current, PAGEFAULT, true); + rv_rtapp_pagefault_atom_update(current, PAGEFAULT, false); +} + +void rv_rtapp_pagefault_atoms_fetch(struct task_struct *task, struct ltl_monitor *mon) +{ + rv_rtapp_pagefault_atom_set(mon, RT, rt_task(task)); +} + +void rv_rtapp_pagefault_atoms_init(struct task_struct *task, struct ltl_monitor *mon) +{ + rv_rtapp_pagefault_atom_set(mon, PAGEFAULT, false); +} + +static int enable_rtapp_pagefault(void) +{ + int ret; + + ret = rv_rtapp_pagefault_init(0); + if (ret) + return ret; + + rv_attach_trace_probe("rtapp_pagefault", page_fault_kernel, handle_page_fault); + rv_attach_trace_probe("rtapp_pagefault", page_fault_user, handle_page_fault); + + return 0; +} + +static void disable_rtapp_pagefault(void) +{ + rv_detach_trace_probe("rtapp_pagefault", page_fault_kernel, handle_page_fault); + rv_detach_trace_probe("rtapp_pagefault", page_fault_user, handle_page_fault); + + rv_rtapp_pagefault_destroy(); +} + +static struct rv_monitor rv_rtapp_pagefault = { + .name = "rtapp_pagefault", + .description = "monitor RT tasks do not page fault", + .enable = enable_rtapp_pagefault, + .disable = disable_rtapp_pagefault, +}; + +void rv_rtapp_pagefault_error(struct task_struct *task, struct ltl_monitor *mon) +{ + trace_rtapp_pagefault_error(task); +#ifdef CONFIG_RV_REACTORS + if (rv_rtapp_pagefault.react) + rv_rtapp_pagefault.react("rv: %s[%d](RT) raises a page fault\n", + task->comm, task->pid); +#endif +} + +static int __init register_rtapp_pagefault(void) +{ + rv_register_monitor(&rv_rtapp_pagefault); + return 0; +} + +static void __exit unregister_rtapp_pagefault(void) +{ + rv_unregister_monitor(&rv_rtapp_pagefault); +} + +module_init(register_rtapp_pagefault); +module_exit(unregister_rtapp_pagefault);
diff --git a/kernel/trace/rv/rv_trace.h b/kernel/trace/rv/rv_trace.h
index 79a7388b5c55..560581d7edcb 100644
--- a/kernel/trace/rv/rv_trace.h
+++ b/kernel/trace/rv/rv_trace.h@@ -165,6 +165,26 @@ TRACE_EVENT(rtapp_block_sleep_error, TP_printk("rv: %s[%d](RT) is blocked\n", __get_str(comm), __entry->pid) ); #endif +#ifdef CONFIG_RV_MON_RTAPP_PAGEFAULT +TRACE_EVENT(rtapp_pagefault_error, + + TP_PROTO(struct task_struct *task), + + TP_ARGS(task), + + TP_STRUCT__entry( + __string(comm, task->comm) + __field(pid_t, pid) + ), + + TP_fast_assign( + __assign_str(comm); + __entry->pid = task->pid; + ), + + TP_printk("rv: %s[%d](RT) raises a page fault", __get_str(comm), __entry->pid) +); +#endif #endif /* _TRACE_RV_H */ /* This part must be outside protection */
--
2.39.5