diff --git a/meta-opencentauri/recipes-apps/klipper/files/0005-mlock-keep-klippy-resident.patch b/meta-opencentauri/recipes-apps/klipper/files/0005-mlock-keep-klippy-resident.patch new file mode 100644 index 00000000..1d60c9b0 --- /dev/null +++ b/meta-opencentauri/recipes-apps/klipper/files/0005-mlock-keep-klippy-resident.patch @@ -0,0 +1,72 @@ +diff --git a/klippy/extras/mlock.py b/klippy/extras/mlock.py +new file mode 100644 +index 0000000..ee7bac9 +--- /dev/null ++++ b/klippy/extras/mlock.py +@@ -0,0 +1,66 @@ ++# Keep klippy resident in RAM. ++# ++# Klipper has hard real-time deadlines towards the MCUs (trsync during ++# homing, the move queue during a print). On a host with little RAM and an ++# aggressive swap policy the kernel is free to page klippy out, and a major ++# page fault at the wrong moment shows up as "Timer too close" or "Unable to ++# obtain 'trsync_state' response". Locking the process in memory takes the ++# kernel out of that decision. ++# ++# [mlock] ++# ++# Copyright (C) 2026 COSMOS contributors ++# ++# This file may be distributed under the terms of the GNU GPLv3 license. ++import ctypes ++import logging ++import resource ++ ++MCL_CURRENT = 1 ++MCL_FUTURE = 2 ++# Lock pages as they are first touched instead of faulting the whole address ++# space in at once (Linux 4.4+). Falls back to a plain lock if unsupported. ++MCL_ONFAULT = 4 ++ ++ ++class MemLock: ++ def __init__(self, config): ++ printer = config.get_printer() ++ try: ++ resource.setrlimit( ++ resource.RLIMIT_MEMLOCK, ++ (resource.RLIM_INFINITY, resource.RLIM_INFINITY), ++ ) ++ except (ValueError, OSError) as e: ++ logging.warning("mlock: could not raise RLIMIT_MEMLOCK: %s", e) ++ libc = ctypes.CDLL(None, use_errno=True) ++ flags = MCL_CURRENT | MCL_FUTURE ++ rc = libc.mlockall(flags | MCL_ONFAULT) ++ if rc != 0 and ctypes.get_errno() == 22: # EINVAL: no MCL_ONFAULT ++ rc = libc.mlockall(flags) ++ if rc != 0: ++ err = ctypes.get_errno() ++ logging.warning("mlock: mlockall failed: errno %d", err) ++ return ++ printer.register_event_handler("klippy:ready", self._report) ++ ++ def _report(self): ++ try: ++ with open("/proc/self/status") as f: ++ st = dict( ++ l.split(":", 1) ++ for l in f ++ if l.startswith(("VmLck", "VmRSS", "VmSwap")) ++ ) ++ logging.info( ++ "mlock: locked %s resident %s swapped %s", ++ st.get("VmLck", "?").strip(), ++ st.get("VmRSS", "?").strip(), ++ st.get("VmSwap", "?").strip(), ++ ) ++ except OSError: ++ pass ++ ++ ++def load_config(config): ++ return MemLock(config) diff --git a/meta-opencentauri/recipes-apps/klipper/files/machine.cfg b/meta-opencentauri/recipes-apps/klipper/files/machine.cfg index 18354024..b34ad307 100644 --- a/meta-opencentauri/recipes-apps/klipper/files/machine.cfg +++ b/meta-opencentauri/recipes-apps/klipper/files/machine.cfg @@ -48,6 +48,11 @@ path: /etc/klipper/gcodes [danger_options] multi_mcu_trsync_timeout: 0.03 +# Keep klippy resident in RAM: with 117 MB and swappiness 150 the kernel +# otherwise pages it out, and a page fault during homing or a print ends in +# "Timer too close". +[mlock] + [stepper_x] step_pin: PE8 dir_pin: !PE7 diff --git a/meta-opencentauri/recipes-apps/klipper/kalico_2026.02.00.inc b/meta-opencentauri/recipes-apps/klipper/kalico_2026.02.00.inc index 0d54c95f..53d17ad6 100644 --- a/meta-opencentauri/recipes-apps/klipper/kalico_2026.02.00.inc +++ b/meta-opencentauri/recipes-apps/klipper/kalico_2026.02.00.inc @@ -14,6 +14,7 @@ SRC_URI = "git://github.com/OpenCentauri/kalico.git;protocol=https;branch=hx711s file://0001-force-specific-input-shaper.patch \ file://0003-drv8833-motor-control.patch \ file://0004-allow-config-exclude.patch \ + file://0005-mlock-keep-klippy-resident.patch \ " SRCREV = "3f166182f82f95584112571b4f7b2258922be60d"