Userspace real-time applications may have design flaws that they raise
page faults in real-time threads, and thus have unexpected latencies.

Add an linear temporal logic monitor to detect this scenario.

Reviewed-by: Gabriele Monaco <gmon...@redhat.com>
Signed-off-by: Nam Cao <nam...@linutronix.de>
---
 kernel/trace/rv/Kconfig                       |  1 +
 kernel/trace/rv/Makefile                      |  1 +
 kernel/trace/rv/monitors/pagefault/Kconfig    | 20 +++++
 .../trace/rv/monitors/pagefault/pagefault.c   | 87 +++++++++++++++++++
 .../trace/rv/monitors/pagefault/pagefault.h   | 57 ++++++++++++
 .../rv/monitors/pagefault/pagefault_trace.h   | 14 +++
 kernel/trace/rv/rv_trace.h                    |  1 +
 tools/verification/models/rtapp/pagefault.ltl |  1 +
 8 files changed, 182 insertions(+)
 create mode 100644 kernel/trace/rv/monitors/pagefault/Kconfig
 create mode 100644 kernel/trace/rv/monitors/pagefault/pagefault.c
 create mode 100644 kernel/trace/rv/monitors/pagefault/pagefault.h
 create mode 100644 kernel/trace/rv/monitors/pagefault/pagefault_trace.h
 create mode 100644 tools/verification/models/rtapp/pagefault.ltl

diff --git a/kernel/trace/rv/Kconfig b/kernel/trace/rv/Kconfig
index 5c407d2916614..6f86d8501e87e 100644
--- a/kernel/trace/rv/Kconfig
+++ b/kernel/trace/rv/Kconfig
@@ -42,6 +42,7 @@ source "kernel/trace/rv/monitors/scpd/Kconfig"
 source "kernel/trace/rv/monitors/snep/Kconfig"
 source "kernel/trace/rv/monitors/sncid/Kconfig"
 source "kernel/trace/rv/monitors/rtapp/Kconfig"
+source "kernel/trace/rv/monitors/pagefault/Kconfig"
 # Add new monitors here
 
 config RV_REACTORS
diff --git a/kernel/trace/rv/Makefile b/kernel/trace/rv/Makefile
index 9b28c24199955..353ecf939d0e9 100644
--- a/kernel/trace/rv/Makefile
+++ b/kernel/trace/rv/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_RV_MON_SCPD) += monitors/scpd/scpd.o
 obj-$(CONFIG_RV_MON_SNEP) += monitors/snep/snep.o
 obj-$(CONFIG_RV_MON_SNCID) += monitors/sncid/sncid.o
 obj-$(CONFIG_RV_MON_RTAPP) += monitors/rtapp/rtapp.o
+obj-$(CONFIG_RV_MON_PAGEFAULT) += monitors/pagefault/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/pagefault/Kconfig 
b/kernel/trace/rv/monitors/pagefault/Kconfig
new file mode 100644
index 0000000000000..5e16625f16537
--- /dev/null
+++ b/kernel/trace/rv/monitors/pagefault/Kconfig
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+config RV_MON_PAGEFAULT
+       depends on RV
+       select RV_LTL_MONITOR
+       depends on RV_MON_RTAPP
+       depends on X86 || RISCV
+       default y
+       select LTL_MON_EVENTS_ID
+       bool "pagefault monitor"
+       help
+         Monitor that real-time tasks do not raise page faults, causing
+         undesirable latency.
+
+         If you are developing a real-time system and not entirely sure whether
+         the applications are designed correctly for real-time, you want to say
+         Y here.
+
+         This monitor does not affect execution speed while it is not running,
+         therefore it is safe to enable this in production kernel.
diff --git a/kernel/trace/rv/monitors/pagefault/pagefault.c 
b/kernel/trace/rv/monitors/pagefault/pagefault.c
new file mode 100644
index 0000000000000..80f6d7ecf5cfb
--- /dev/null
+++ b/kernel/trace/rv/monitors/pagefault/pagefault.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/ftrace.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/rv.h>
+#include <linux/sched/deadline.h>
+#include <linux/sched/rt.h>
+#include <linux/tracepoint.h>
+#include <rv/instrumentation.h>
+
+#define MODULE_NAME "pagefault"
+
+#include <rv_trace.h>
+#include <trace/events/exceptions.h>
+#include <monitors/rtapp/rtapp.h>
+
+#include "pagefault.h"
+#include <rv/ltl_monitor.h>
+
+static void ltl_atoms_fetch(struct task_struct *task, struct ltl_monitor *mon)
+{
+       /*
+        * This includes "actual" real-time tasks and also PI-boosted tasks. A 
task being PI-boosted
+        * means it is blocking an "actual" real-task, therefore it should also 
obey the monitor's
+        * rule, otherwise the "actual" real-task may be delayed.
+        */
+       ltl_atom_set(mon, LTL_RT, rt_or_dl_task(task));
+}
+
+static void ltl_atoms_init(struct task_struct *task, struct ltl_monitor *mon, 
bool task_creation)
+{
+       if (task_creation)
+               ltl_atom_set(mon, LTL_PAGEFAULT, false);
+}
+
+static void handle_page_fault(void *data, unsigned long address, struct 
pt_regs *regs,
+                             unsigned long error_code)
+{
+       ltl_atom_pulse(current, LTL_PAGEFAULT, true);
+}
+
+static int enable_pagefault(void)
+{
+       int retval;
+
+       retval = ltl_monitor_init();
+       if (retval)
+               return retval;
+
+       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_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);
+
+       ltl_monitor_destroy();
+}
+
+static struct rv_monitor rv_pagefault = {
+       .name = "pagefault",
+       .description = "Monitor that RT tasks do not raise page faults",
+       .enable = enable_pagefault,
+       .disable = disable_pagefault,
+};
+
+static int __init register_pagefault(void)
+{
+       return rv_register_monitor(&rv_pagefault, &rv_rtapp);
+}
+
+static void __exit unregister_pagefault(void)
+{
+       rv_unregister_monitor(&rv_pagefault);
+}
+
+module_init(register_pagefault);
+module_exit(unregister_pagefault);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nam Cao <nam...@linutronix.de>");
+MODULE_DESCRIPTION("pagefault: Monitor that RT tasks do not raise page 
faults");
diff --git a/kernel/trace/rv/monitors/pagefault/pagefault.h 
b/kernel/trace/rv/monitors/pagefault/pagefault.h
new file mode 100644
index 0000000000000..94c0fe4fdaa54
--- /dev/null
+++ b/kernel/trace/rv/monitors/pagefault/pagefault.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include <linux/rv.h>
+
+#define MONITOR_NAME pagefault
+
+enum ltl_atom {
+       LTL_PAGEFAULT,
+       LTL_RT,
+       LTL_NUM_ATOM
+};
+static_assert(LTL_NUM_ATOM <= RV_MAX_LTL_ATOM);
+
+static const char *ltl_atom_str(enum ltl_atom atom)
+{
+       static const char *const names[] = {
+               "pa",
+               "rt",
+       };
+
+       return names[atom];
+}
+
+enum ltl_buchi_state {
+       S0,
+       RV_NUM_BA_STATES
+};
+static_assert(RV_NUM_BA_STATES <= RV_MAX_BA_STATES);
+
+static void ltl_start(struct task_struct *task, struct ltl_monitor *mon)
+{
+       bool pagefault = test_bit(LTL_PAGEFAULT, mon->atoms);
+       bool val3 = !pagefault;
+       bool rt = test_bit(LTL_RT, mon->atoms);
+       bool val1 = !rt;
+       bool val4 = val1 || val3;
+
+       if (val4)
+               __set_bit(S0, mon->states);
+}
+
+static void
+ltl_possible_next_states(struct ltl_monitor *mon, unsigned int state, unsigned 
long *next)
+{
+       bool pagefault = test_bit(LTL_PAGEFAULT, mon->atoms);
+       bool val3 = !pagefault;
+       bool rt = test_bit(LTL_RT, mon->atoms);
+       bool val1 = !rt;
+       bool val4 = val1 || val3;
+
+       switch (state) {
+       case S0:
+               if (val4)
+                       __set_bit(S0, next);
+               break;
+       }
+}
diff --git a/kernel/trace/rv/monitors/pagefault/pagefault_trace.h 
b/kernel/trace/rv/monitors/pagefault/pagefault_trace.h
new file mode 100644
index 0000000000000..fe1f82597b1ac
--- /dev/null
+++ b/kernel/trace/rv/monitors/pagefault/pagefault_trace.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Snippet to be included in rv_trace.h
+ */
+
+#ifdef CONFIG_RV_MON_PAGEFAULT
+DEFINE_EVENT(event_ltl_monitor_id, event_pagefault,
+            TP_PROTO(struct task_struct *task, char *states, char *atoms, char 
*next),
+            TP_ARGS(task, states, atoms, next));
+DEFINE_EVENT(error_ltl_monitor_id, error_pagefault,
+            TP_PROTO(struct task_struct *task),
+            TP_ARGS(task));
+#endif /* CONFIG_RV_MON_PAGEFAULT */
diff --git a/kernel/trace/rv/rv_trace.h b/kernel/trace/rv/rv_trace.h
index 27698c5791a04..5c101c82da235 100644
--- a/kernel/trace/rv/rv_trace.h
+++ b/kernel/trace/rv/rv_trace.h
@@ -172,6 +172,7 @@ DECLARE_EVENT_CLASS(error_ltl_monitor_id,
 
        TP_printk("%s[%d]: violation detected", __get_str(comm), __entry->pid)
 );
+#include <monitors/pagefault/pagefault_trace.h>
 // Add new monitors based on CONFIG_LTL_MON_EVENTS_ID here
 #endif /* CONFIG_LTL_MON_EVENTS_ID */
 #endif /* _TRACE_RV_H */
diff --git a/tools/verification/models/rtapp/pagefault.ltl 
b/tools/verification/models/rtapp/pagefault.ltl
new file mode 100644
index 0000000000000..d7ce621027336
--- /dev/null
+++ b/tools/verification/models/rtapp/pagefault.ltl
@@ -0,0 +1 @@
+RULE = always (RT imply not PAGEFAULT)
-- 
2.39.5


Reply via email to