]> git.ipfire.org Git - thirdparty/openssh-portable.git/commitdiff
Add support for locking memory on Linux
authorDaniil Tatianin <d-tatianin@yandex-team.ru>
Thu, 27 Feb 2025 08:37:13 +0000 (11:37 +0300)
committerDarren Tucker <dtucker@dtucker.net>
Sun, 23 Mar 2025 00:24:03 +0000 (11:24 +1100)
Linux wakes up kcompactd threads in order to make more contiguous memory
available on the system, it does this by migrating live movable pages
(actively modifying live processes' page tables and constantly flooding
them with page invalidation IPIs, which can be up to millions per
second), which causes the process to become unresponsive for up to
seconds or even minutes in some severe cases. In case of sshd, we want
to always be able to connect to the system, even if it's under heavy
kcompactd load.

Introduce an option to protect sshd and its children sessions from being
compacted by kcompactd (this works in cojunction with
compact_unevictable_allowed = 0). Note that we depend on MCL_ONFAULT
being available, which was introduced in linux 4.4. MCL_ONFAULT allows
the system to lock pages lazily, thus drastically reducing memory usage
of a locked process (without MCL_ONFAULT, every existing mapping in the
process is instantly write-faulted).

configure.ac
openbsd-compat/port-linux.c
openbsd-compat/port-linux.h
platform-listen.c

index 40fc1193fd6281be414c0568c76e0db7ff2f473c..f0d6d5b0d9de8184564ca2a02f7d3a49c7c83e4a 100644 (file)
@@ -928,6 +928,27 @@ int main(void) { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
        AC_DEFINE([_PATH_BTMP], ["/var/log/btmp"], [log for bad login attempts])
        AC_DEFINE([USE_BTMP])
        AC_DEFINE([LINUX_OOM_ADJUST], [1], [Adjust Linux out-of-memory killer])
+       AC_ARG_WITH([linux-memlock-onfault],
+               [  --with-linux-memlock-onfault Enables memory locking on Linux],
+               [
+                       if test "x$withval" != "xno" ; then
+                               AC_MSG_CHECKING([for MCL_ONFAULT])
+                               AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+                                       [[ #include <sys/mman.h> ]],
+                                       [[ mlockall(MCL_FUTURE | MCL_ONFAULT); ]],
+                               )],
+                               [
+                                       AC_MSG_RESULT([supported])
+                                       AC_DEFINE([LINUX_MEMLOCK_ONFAULT], [1],
+                                       [Lock all memory to protect sshd against Linux kcompactd] )],
+                               [
+                                       AC_MSG_RESULT([not supported])
+                                       AC_MSG_ERROR([MCL_ONFAULT is not available on your system])
+                               ])
+                       fi
+               ],
+       )
+
        AC_DEFINE([SYSTEMD_NOTIFY], [1], [Have sshd notify systemd on start/reload])
        inet6_default_4in6=yes
        case `uname -r` in
index 8adfec5a7a8393123de52d56ddf0508a34ad87cd..c1d54f38d656a9587b4c438bcf8fedc7580d5473 100644 (file)
@@ -319,6 +319,19 @@ oom_adjust_restore(void)
 }
 #endif /* LINUX_OOM_ADJUST */
 
+#ifdef LINUX_MEMLOCK_ONFAULT
+#include <sys/mman.h>
+
+void
+memlock_onfault_setup(void)
+{
+       if (mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT) < 0)
+               verbose("unable to lock memory: %s", strerror(errno));
+       else
+               debug("memory locked");
+}
+#endif /* LINUX_MEMLOCK_ONFAULT */
+
 #ifdef SYSTEMD_NOTIFY
 
 static void ssh_systemd_notify(const char *, ...)
index 14064f87db8ef80edc5f054671c509b9e19131f6..959430de13683a9a65ed5ead28a21eb544e17a19 100644 (file)
@@ -30,6 +30,10 @@ void oom_adjust_restore(void);
 void oom_adjust_setup(void);
 #endif
 
+#ifdef LINUX_MEMLOCK_ONFAULT
+void memlock_onfault_setup(void);
+#endif
+
 #ifdef SYSTEMD_NOTIFY
 void ssh_systemd_notify_ready(void);
 void ssh_systemd_notify_reload(void);
index 4da2855eea735285911462a2d4806c0ce6375941..45df1f6c36c945dd73ed7dff8a9e5c6898ce9a24 100644 (file)
@@ -34,6 +34,13 @@ platform_pre_listen(void)
        /* Adjust out-of-memory killer so listening process is not killed */
        oom_adjust_setup();
 #endif
+#ifdef LINUX_MEMLOCK_ONFAULT
+       /*
+        * Protect ourselves against kcompactd so that we are able to process
+        * new connections while it is active and migrating pages.
+        */
+       memlock_onfault_setup();
+#endif
 }
 
 void
@@ -84,4 +91,11 @@ platform_post_fork_child(void)
 
 void platform_pre_session_start(void)
 {
+#ifdef LINUX_MEMLOCK_ONFAULT
+       /*
+        * Memlock flags are dropped on fork, lock the memory again so that the
+        * child connection is also protected against kcompactd.
+        */
+       memlock_onfault_setup();
+#endif
 }