diff --git a/docs/src/install/latency-test.adoc b/docs/src/install/latency-test.adoc index e0f23e39cee..91f2b467285 100644 --- a/docs/src/install/latency-test.adoc +++ b/docs/src/install/latency-test.adoc @@ -193,6 +193,39 @@ Use `--show` to show count for the off-chart [pos|neg] bin. image::../config/images/latency-histogram.png["latency-histogram Window"] +[[sec:latency-status-bar]] +=== Realtime status bar + +The `latency-test` and `latency-histogram` windows show a status bar along the +bottom with three fields. A field turns red when it indicates a condition that +makes the measured latency unrepresentative of a properly configured realtime +system. + +Realtime:: + The running realtime type, for example `RT-PREEMPT_RT`, `RT-RTAI` or + `RT-Xenomai`. It reads `no realtime` (red) when `rtapi_app` cannot obtain + realtime scheduling, either because the running kernel is not a realtime + kernel (see <>) or because `rtapi_app` lacks the required privileges on a + run-in-place build (see <>, run `sudo make setuid` + or `sudo make setcap`). In this state the figures reflect ordinary, + non-realtime scheduling and will be far worse than a realtime kernel + delivers. For testing only, realtime can be forced with the + `LINUXCNC_FORCE_REALTIME=1` environment variable. + +CPU governor:: + The active CPU frequency governor. It is shown in red when it is not + `performance`, because frequency scaling lets the CPU drop to a lower clock + speed and adds latency spikes. See <>. + +isolcpus:: + Whether any CPUs are isolated for the exclusive use of LinuxCNC + (`isolcpus=...`) or not (`no_isolcpus`). Isolating a CPU keeps other tasks + off it and improves latency; see <>. + + == Latency tuning LinuxCNC can run on many different hardware platforms and with many @@ -234,6 +267,23 @@ tuning in the BIOS are: * Disable any hardware you do not intend to use. +[[sec:latency-governor]] +=== CPU frequency governor + +Frequency scaling lets the CPU lower its clock speed to save power, which adds +latency while the CPU ramps back up to service a realtime thread. For the best +latency, set the governor to `performance` on every CPU: + +---- +echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor +---- + +This setting does not survive a reboot. To make it permanent, apply it at boot +with a small systemd service, or with a CPU frequency management tool such as +`cpupower` (Debian/Ubuntu package `linux-cpupower`) or `tuned`. + + +[[sec:tuning-preempt-rt]] === Tuning Preempt-RT for latency The Preempt-RT kernel may benefit from tuning in order to provide the diff --git a/scripts/latency-histogram b/scripts/latency-histogram index 245a2c386ee..1b2696b92cd 100755 --- a/scripts/latency-histogram +++ b/scripts/latency-histogram @@ -111,6 +111,21 @@ proc which_exe {name} { return -code error "$name: executable not found" } ;# which_exe +proc check_rt_privileges {} { + # Ask the realtime layer whether realtime is actually available, via + # 'realtime verify' (exits 0 when realtime is up, non-zero otherwise, + # usually a missing 'sudo make setuid' or 'sudo make setcap'). + if {[catch {exec linuxcnc_var REALTIME} rt]} return ;# cannot locate, stay quiet + if {![catch {exec $rt verify 2>@1}]} return ;# realtime available + set msg "Warning: realtime is not available." + append msg "\nThreads get no SCHED_FIFO priority or locked memory," + append msg "\nso the latency shown will be far worse than reality." + append msg "\nOn a run-in-place build, run 'sudo make setuid' (or 'sudo make setcap') in src/." + # The GUI surfaces this persistently in the status bar (see rt_status_items); + # keep the console message for non-X / terminal runs. + puts stderr $msg +} ;# check_rt_privileges + proc program_check {plist} { foreach prog $plist { if [catch { @@ -335,6 +350,56 @@ proc load_packages {} { } } ;# load_packages +proc rt_status_items {} { + # Returns a list of {text color} pairs for the status bar. + # color is "" for normal (inherit theme); set only when it needs attention. + set red "#cc4444" + set items {} + + # realtime: concise type when available, red "no realtime" when not. + # 'realtime verify' is authoritative for the yes/no; the type label is + # derived from the same kernel signals rtapi uses, for display only. + set rt_ok 0 + if {![catch {exec linuxcnc_var REALTIME} rt]} { + if {![catch {exec $rt verify 2>@1}]} {set rt_ok 1} + } + if {$rt_ok} { + set t SCHED_FIFO + if {[string match -nocase *-rtai* [exec uname -r]]} { + set t RTAI + } elseif {[file exists /dev/evl]} { + set t Xenomai-EVL + } elseif {[file exists /proc/xenomai]} { + set t Xenomai + } elseif {[regexp -nocase {PREEMPT[ _]RT} [exec uname -v]]} { + set t PREEMPT_RT + } + lappend items [list "RT-$t" ""] + } else { + lappend items [list "no realtime" $red] + } + + # cpu frequency governor: bare value, red when not 'performance' + set gov "" + catch {set gov [string trim [exec cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor]]} + if {$gov eq ""} {set gov "gov?"} + if {$gov eq "performance"} { + lappend items [list $gov ""] + } else { + lappend items [list $gov $red] + } + + # isolcpus: single concise value + set cmdline "" + catch {set cmdline [exec cat /proc/cmdline]} + if {[regexp {isolcpus=(\S+)} $cmdline -> iso]} { + lappend items [list "isolcpus=$iso" ""] + } else { + lappend items [list "no_isolcpus" ""] + } + return $items +} ;# rt_status_items + proc make_gui { {w .} } { set f [frame ${w}fa] pack $f -side top -fill x -expand 1 @@ -438,6 +503,28 @@ proc make_gui { {w .} } { } + # --- realtime status bar (true footer): one line of axis-style sunken + # cells split into equal thirds, left/centre/right aligned --- + set sb [frame ${w}statusbar] + pack $sb -side bottom -fill x + set sbitems [rt_status_items] + # Fixed-width sides (chars), elastic middle gets the rest. + # Left fits the longest RT label (RT-Xenomai-EVL); right fits isolcpus up to ~4 cpus. + set sbwidth {14 0 18} + set sbweight {0 1 0} + set sbanch {w center e} + set i 0 + foreach item $sbitems { + lassign $item text color + label $sb.s$i -text $text -relief sunken -bd 1 -anchor [lindex $sbanch $i] -padx 6 + set wdt [lindex $sbwidth $i] + if {$wdt > 0} {$sb.s$i configure -width $wdt} + if {$color ne ""} {$sb.s$i configure -fg $color} + grid $sb.s$i -row 0 -column $i -sticky we + grid columnconfigure $sb $i -weight [lindex $sbweight $i] + incr i + } + set f [frame ${w}bot] pack $f -side bottom -anchor w -fill x -expand 1 pack [button $f.b -padx 0 -pady 0 -text Reset -command reset_data ] \ @@ -624,6 +711,7 @@ proc start_collection {} { } } hal start + check_rt_privileges if {!$::LH(legacy)} { # Attach the Tcl hal_stream binding (added in halsh.c) to each # thread's latencybinstream FIFO. The handle stays open for the diff --git a/scripts/latency-test b/scripts/latency-test index 63b34c0034d..f69d87baf46 100755 --- a/scripts/latency-test +++ b/scripts/latency-test @@ -81,6 +81,29 @@ if [ "$BASE" -eq "$SERVO" ]; then BASE=0; fi BASE_HUMAN=$(human_time "$BASE") SERVO_HUMAN=$(human_time "$SERVO") +# --- status-bar info (axis-style footer; colour only when attention needed) --- +# 'realtime verify' is authoritative for yes/no; the type label is derived from +# the same kernel signals rtapi uses, for display only. +RED="#cc4444" +if realtime verify >/dev/null 2>&1; then + if uname -r | grep -qi -- '-rtai'; then RT_T="RTAI" + elif [ -e /dev/evl ]; then RT_T="Xenomai-EVL" + elif [ -e /proc/xenomai ]; then RT_T="Xenomai" + elif uname -v | grep -qiE 'PREEMPT[_ ]RT'; then RT_T="PREEMPT_RT" + else RT_T="SCHED_FIFO"; fi + RT_TEXT="RT-$RT_T"; RT_FG="" +else + RT_TEXT="no realtime"; RT_FG="foreground=\"$RED\"" +fi +GOV=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor 2>/dev/null) +[ -z "$GOV" ] && GOV="gov?" +if [ "$GOV" = performance ]; then GOV_FG=""; else GOV_FG="foreground=\"$RED\""; fi +if grep -q 'isolcpus=' /proc/cmdline 2>/dev/null; then + ISOL_TEXT=$(tr ' ' '\n' < /proc/cmdline | grep '^isolcpus=' | head -1) +else + ISOL_TEXT="no_isolcpus" +fi + if [ "$BASE" -eq 0 ]; then cat > lat.hal <> lat.xml <