]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
Add mlockall and SCHED_FIFO support
authorJohn Hasler <john@dhh.gt.org>
Tue, 10 Feb 2009 16:59:57 +0000 (17:59 +0100)
committerMiroslav Lichvar <mlichvar@redhat.com>
Tue, 10 Feb 2009 17:02:28 +0000 (18:02 +0100)
The attached patch adds support for mlockall() as well as the SCHED_FIFO
real-time scheduler. It should result in reduced (and more consistent)
latency. Usage is documented in all the documents.

chrony.texi
chronyd.8
conf.c
conf.h
configure
examples/chrony.conf.example
main.c
sys.c
sys.h
sys_linux.c
sys_linux.h

index ce29f7aab7cd5dd947dd109e677e0abdd89ece93..13a914d8d26baf90d2fcec97946b60f7e3ffcc33 100644 (file)
@@ -1088,13 +1088,19 @@ useful for dial-up systems that are shut down when not in use.  For this
 to work well, it relies on @code{chronyd} having been able to determine
 accurate statistics for the difference between the real time clock and
 system clock last time the computer was on.
-
 @item -u <user>
 When this option is used, chronyd will drop root privileges to the specified
 user.  So far, it works only on Linux when compiled with capabilities support.
 @item -v
 This option displays @code{chronyd's} version number to the terminal and
 exits.
+@item -P <priority>
+This option will select the SCHED_FIFO real-time scheduler at the
+specified priority (which must be between 0 and 100).  This mode is
+supported only on Linux.
+@item -m     
+This option will lock chronyd into RAM so that it will never be paged
+out.  This mode is only supported on Linux.
 @end table
 
 On systems that support an @file{/etc/rc.local} file for starting
@@ -1187,6 +1193,9 @@ directives can occur in any order in the file.
 * rtcfile directive::           Specify the file where real-time clock data is stored
 * rtconutc directive::          Specify that the real time clock keeps UTC not local time
 * server directive::            Specify an NTP server
+* sched_priority directive::    Require real-time scheduling and specify a priority for it.
+* lock_all directive::          Require that chronyd be locked into RAM. 
+
 @end menu
 @c }}}
 @c {{{ comments in config file
@@ -2177,6 +2186,37 @@ If the @code{rtconutc} directive appears, it means the RTC is required
 to keep UTC.  The directive takes no arguments.  It is equivalent to
 specifying the @code{-u} switch to the Linux @file{/sbin/clock} program.
 @c }}}
+@c {{{ sched_priority
+@node sched_priority directive
+@subsection sched_priority
+
+The @code{sched_priority} directive will select the SCHED_FIFO real-time
+scheduler at the specified priority (which must be between 0 and 100).
+This mode is supported only on Linux.
+
+This directive uses the Linux sched_setscheduler() system call to
+instruct the kernel to use the SCHED_FIFO first-in, first-out
+real-time scheduling policy for Chronyd with the specified priority.
+This means that whenever Chronyd is ready to run it will run,
+interrupting whatever else is running unless it is a higher priority
+real-time process.  This should not impact performance as Chronyd's
+resource requirements are modest, but it should result in lower and
+more consistent latency since Chronyd will not need to wait for the
+scheduler to get around to running it.  You should not use this unless
+you really need it.  The sched_setscheduler man page has more details.
+@c }}}
+@c {{{ lock_all
+@node lock_all directive
+@subsection lock_all
+
+The @code{lock_all} directive will lock chronyd into RAM so that it
+will never be paged out.  This mode is only supported on Linux.  This
+directive uses the Linux mlockall() system call to prevent Chronyd
+from ever being swapped out.  This should result in lower and more
+consistent latency.  It should not have significant impact on
+performance as Chronyd's memory usage is modest.  The mlockall man
+page has more details.
+@c }}}
 @c {{{ server
 @node server directive
 @subsection server
@@ -2287,6 +2327,20 @@ chrony when disconnecting the dial-up link.  (It will still be necessary to use
 chronyc's @code{online} (@pxref{online command}) command when the link has been
 established, to enable measurements to start.)
 
+@item sched_priority
+This directive tells chronyd to use the real-time FIFO scheduler with the
+specified priority (which must be between 0 and 100).  This should result
+in reduced latency.  You don't need it unless you really have a requirement
+for extreme clock stability.  Works only on Linux.  Note that the "-P"
+command-line switch will override this.
+
+@item lock_all
+This directive tells chronyd to use the mlockall() syscall to lock itself
+into RAM so that it will never be paged out.  This should result in reduced
+latency.  You don't need it unless you really have a requirement
+for extreme clock stability.  Works only on Linux.  Note that the "-m"
+command-line switch will also enable this feature.
+
 @end table
 @c }}}
 @c }}}
index dfc40047c6e11731e8022f93f2331dfb36a50a66..52e06e239a5aad7a41f76f8d0b44a595fb3f9993 100644 (file)
--- a/chronyd.8
+++ b/chronyd.8
@@ -35,6 +35,15 @@ Information messages and warnings will be logged to syslog.
 .SH OPTIONS
 A summary of the options supported by \fBchronyd\fR is included below.
 
+.TP
+\fB\-P\fR \fIpriority\fR
+This option will select the SCHED_FIFO real-time scheduler at the specified
+priority (which must be between 0 and 100).  This mode is supported only on
+Linux.
+.TP
+.B \-m
+This option will lock chronyd into RAM so that it will never be paged out.
+This mode is only supported on Linux.
 .TP
 .B \-d
 When run in this mode, the program will not detach itself from the
diff --git a/conf.c b/conf.c
index 8e6c1d9a63053eb0adac71c71d5d052ee0813d23..06ff10406a7abc96696349c10b5ad0e5f49e3645 100644 (file)
--- a/conf.c
+++ b/conf.c
@@ -93,6 +93,14 @@ static void parse_broadcast(const char *);
 static void parse_linux_hz(const char *);
 static void parse_linux_freq_scale(const char *);
 
+#if defined(HAVE_SCHED_SETSCHEDULER)
+static void parse_sched_priority(const char *);
+#endif
+
+#if defined(HAVE_MLOCKALL)
+static void parse_lockall(const char *);
+#endif
+
 /* ================================================== */
 /* Configuration variables */
 
@@ -209,6 +217,14 @@ static const Command commands[] = {
   {"broadcast", 9, parse_broadcast},
   {"linux_hz", 8, parse_linux_hz},
   {"linux_freq_scale", 16, parse_linux_freq_scale}
+#if defined(HAVE_SCHED_SETSCHEDULER)
+  ,{"sched_priority", 14, parse_sched_priority}
+#endif
+
+#if defined(HAVE_MLOCKALL)
+  ,{"lock_all", 8, parse_lockall}
+#endif
+
 };
 
 static int n_commands = (sizeof(commands) / sizeof(commands[0]));
@@ -365,6 +381,26 @@ parse_source(const char *line, NTP_Source_Type type)
 
 /* ================================================== */
 
+#if defined(HAVE_SCHED_SETSCHEDULER)
+static void
+parse_sched_priority(const char *line)
+{
+  if (SchedPriority == 0) { /* Command-line switch must have priority */
+    sscanf(line, "%d", &SchedPriority);
+  }
+}
+#endif
+
+#if defined(HAVE_MLOCKALL)
+static void
+parse_lockall(const char *line)
+{
+  LockAll = 1;
+}
+#endif
+
+/* ================================================== */
+
 static void
 parse_server(const char *line)
 {
diff --git a/conf.h b/conf.h
index 166cd1bfa6ba3a205ad4131ba98b7685bcdac59e..fa967aeb25b5f9023e540d256a855fde96366a21 100644 (file)
--- a/conf.h
+++ b/conf.h
@@ -71,4 +71,12 @@ extern int CNF_AllowLocalReference(int *stratum);
 
 extern void CNF_SetupAccessRestrictions(void);
 
+#if defined(HAVE_SCHED_SETSCHEDULER)
+extern int SchedPriority;
+#endif
+
+#if defined(HAVE_MLOCKALL)
+extern int LockAll;
+#endif
+
 #endif /* GOT_CONF_H */
index 9027b85a8f9b1c373261e7fc8490db8ef7db1ffc..1ff2bbfb239eb5eec21b3fe4a1bff5ee777a07b7 100755 (executable)
--- a/configure
+++ b/configure
@@ -257,7 +257,7 @@ case $SYSTEM in
             EXTRA_DEFS+=" -DFEAT_LINUXCAPS=1"
             EXTRA_LIBS="-lcap"
         fi
-        SYSDEFS="-DLINUX"
+        SYSDEFS="-DLINUX -DHAVE_SCHED_SETSCHEDULER -DHAVE_MLOCKALL"
         echo "Configuring for " $SYSTEM
         if [ "${MACHINE}" = "alpha" ]; then
            echo "Enabling -mieee"
index 5488e72d0b2bbca9d00adef94b742d29c4cd6cc6..fbf8e47f182f61184ac0b9a997d9bc97ce81453a 100644 (file)
@@ -287,3 +287,21 @@ cmdallow 127.0.0.1
 ! rtcdevice /dev/misc/rtc
 
 #######################################################################
+### REAL TIME SCHEDULER
+# This directive tells chronyd to use the real-time FIFO scheduler with the
+# specified priority (which must be between 0 and 100).  This should result
+# in reduced latency.  You don't need it unless you really have a requirement
+# for extreme clock stability.  Works only on Linux.  Note that the "-P"
+# command-line switch will override this.
+
+! sched_priority 1
+
+#######################################################################
+### LOCKING CHRONYD INTO RAM
+# This directive tells chronyd to use the mlockall() syscall to lock itself
+# into RAM so that it will never be paged out.  This should result in reduced
+# latency.  You don't need it unless you really have a requirement
+# for extreme clock stability.  Works only on Linux.  Note that the "-m"
+# command-line switch will also enable this feature.
+
+! lock_all
diff --git a/main.c b/main.c
index ba6e4a95528999424f9aabef61c0d612e9263762..65942c1354a41d24c58ec67b7878a30f0ad284ef 100644 (file)
--- a/main.c
+++ b/main.c
 
   =======================================================================
 
+2009-1-28 John G. Hasler <jhasler@debian.org>
+
+       Added real-time support (Linux only) using sched_setscheduler() and
+       mlockall(). Files affected: main.c, conf.c, sys.c, sys_linux.c,
+       conf.h, configure, chronyd.8, chrony.texi, and
+       examples/chrony.conf.example.  The changes are licensed under
+       version 2 of the GPL as described above.
+
+  =======================================================================
+
   The main program
   */
 
@@ -211,6 +221,10 @@ int main
   int do_init_rtc = 0;
   int other_pid;
 
+#if defined(HAVE_SCHED_SETSCHEDULER)
+  int return_value = 0;
+#endif
+
   LOG_Initialise();
 
   /* Parse command line options */
@@ -219,6 +233,24 @@ int main
     if (!strcmp("-f", *argv)) {
       ++argv, --argc;
       conf_file = *argv;
+
+#if defined(HAVE_SCHED_SETSCHEDULER)
+      /* Get real-time scheduler priority */
+    } else if (!strcmp("-P", *argv)) {
+      ++argv, --argc;
+      return_value = sscanf(*argv, "%d", &SchedPriority);
+      if (return_value != 1 || SchedPriority < 1 || SchedPriority > 99) { 
+       SchedPriority = 0;
+       LOG(LOGS_WARN, LOGF_Main, "Bad scheduler priority: [%s]", *argv);
+      }
+#endif /* HAVE_SCHED_SETCHEDULER */
+
+#if defined(HAVE_MLOCKALL)
+      /* Detect lockall switch */
+    } else if (!strcmp("-m", *argv)) {
+      LockAll = 1;
+#endif /* HAVE_MLOCKALL */
+
     } else if (!strcmp("-r", *argv)) {
       reload = 1;
     } else if (!strcmp("-u", *argv)) {
@@ -307,6 +339,18 @@ int main
   signal(SIGHUP, signal_cleanup);
 #endif /* WINNT */
 
+#if defined(HAVE_SCHED_SETSCHEDULER)
+  if (SchedPriority > 0) {
+    SYS_SetScheduler(SchedPriority);
+  }
+#endif
+
+#if defined(HAVE_MLOCKALL)
+  if (LockAll == 1 ) {
+    SYS_MemLockAll(LockAll);
+  }
+#endif
+
   /* The program normally runs under control of the main loop in
      the scheduler. */
   SCH_MainLoop();
diff --git a/sys.c b/sys.c
index 048ba4d885ce7a4fdafc3da9c9e42abdfc31cf2e..714fd4f6435dfa59132aaa350dbd99a588a35cbd 100644 (file)
--- a/sys.c
+++ b/sys.c
@@ -106,6 +106,23 @@ void SYS_DropRoot(char *user)
 }
 
 /* ================================================== */
+
+void SYS_SetScheduler(int SchedPriority)
+{
+#if defined(LINUX) && defined(HAVE_SCHED_SETSCHEDULER)
+  SYS_Linux_SetScheduler(SchedPriority);
+#endif
+  ;;
+}
+
+void SYS_MemLockAll(int LockAll)
+{
+#if defined(LINUX) && defined(HAVE_MLOCKALL)
+  SYS_Linux_MemLockAll(LockAll);
+#endif
+  ;;
+}
+
 /* ================================================== */
 
 
diff --git a/sys.h b/sys.h
index 50b8e46291f516e70e862dbb77003c832cf2d612..2efd19cac87c486de29def0a67f439cb2db2d431 100644 (file)
--- a/sys.h
+++ b/sys.h
@@ -42,4 +42,7 @@ extern void SYS_Finalise(void);
 /* Drop root privileges to the specified user */
 extern void SYS_DropRoot(char *user);
 
+extern void SYS_SetScheduler(int SchedPriority);
+extern void SYS_MemLockAll(int LockAll);
+
 #endif /* GOT_SYS_H */
index d734b5e1ca155be793e32996c9fca673502b45fe..969229f7704fd2b73621979e6eb571a9b030c420 100644 (file)
 #include <assert.h>
 #include <sys/utsname.h>
 
+#if defined(HAVE_SCHED_SETSCHEDULER)
+#  include <sched.h>
+int SchedPriority = 0;
+#endif
+
+#if defined(HAVE_MLOCKALL)
+#  include <sys/mman.h>
+#include <sys/resource.h>
+int LockAll = 0;
+#endif
+
 #ifdef FEAT_LINUXCAPS
 #include <sys/types.h>
 #include <pwd.h>
@@ -898,6 +909,58 @@ SYS_Linux_DropRoot(char *user)
 
 /* ================================================== */
 
+#if defined(HAVE_SCHED_SETSCHEDULER)
+  /* Install SCHED_FIFO real-time scheduler with specified priority */
+void SYS_Linux_SetScheduler(int SchedPriority)
+{
+  int pmax, pmin;
+  struct sched_param sched;
+
+  if (SchedPriority > 0) {
+    sched.sched_priority = SchedPriority;
+    pmax = sched_get_priority_max(SCHED_FIFO);
+    pmin = sched_get_priority_min(SCHED_FIFO);
+    if ( SchedPriority > pmax ) {
+      sched.sched_priority = pmax;
+    }
+    else if ( SchedPriority < pmin ) {
+      sched.sched_priority = pmin;
+    }
+    if ( sched_setscheduler(0, SCHED_FIFO, &sched) == -1 ) {
+      LOG(LOGS_ERR, LOGF_SysLinux, "sched_setscheduler() failed");
+    }
+    else {
+      LOG(LOGS_INFO, LOGF_SysLinux, "Enabled SCHED_FIFO with priority %d", sched.sched_priority);
+    }
+  }
+}
+#endif /* HAVE_SCHED_SETSCHEDULER */
+
+#if defined(HAVE_MLOCKALL)
+/* Lock the process into RAM so that it will never be swapped out */ 
+void SYS_Linux_MemLockAll(int LockAll)
+{
+  struct rlimit rlim;
+  if (LockAll == 1 ) {
+    /* Make sure that we will be able to lock all the memory we need */
+    /* even after dropping privileges.  This does not actually reaerve any memory */
+    rlim.rlim_max = RLIM_INFINITY;
+    rlim.rlim_cur = RLIM_INFINITY;
+    if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0) {
+      LOG(LOGS_ERR, LOGF_SysLinux, "setrlimit() failed: not locking into RAM");
+    }
+    else {
+      if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) {
+       LOG(LOGS_ERR, LOGF_SysLinux, "mlockall() failed");
+      }
+      else {
+       LOG(LOGS_INFO, LOGF_SysLinux, "Successfully locked into RAM");
+      }
+    }
+  }
+}
+#endif /* HAVE_MLOCKALL */
+
 #endif /* LINUX */
 
 /* vim:ts=8
index 53639a5d1e00e37890e862e51e96d28e76d42d74..d4bc2f6faccd7cce216a74257cb9f0a9f204f255 100644 (file)
@@ -39,4 +39,8 @@ extern void SYS_Linux_GetKernelVersion(int *major, int *minor, int *patchlevel);
 
 extern void SYS_Linux_DropRoot(char *user);
 
+extern void SYS_Linux_MemLockAll(int LockAll);
+
+extern void SYS_linux_SetScheduler(int SchedPriority);
+
 #endif  /* GOT_SYS_LINUX_H */