From 4b6fa0bfc2e1f9c44b45e270bf26be5c28758889 Mon Sep 17 00:00:00 2001
From: Luca Bigliardi <shammash@google.com>
Date: Tue, 4 May 2010 14:55:21 +0100
Subject: [PATCH] Introduce Mlockall()

Add Mlockall() utility to lock current process' virtual adress space into RAM.

Signed-off-by: Luca Bigliardi <shammash@google.com>
Reviewed-by: Michael Hanselmann <hansmi@google.com>
---
 Makefile.am                            |  1 +
 lib/utils.py                           | 40 +++++++++++++++++++++++++
 test/ganeti.utils_mlockall_unittest.py | 41 ++++++++++++++++++++++++++
 3 files changed, 82 insertions(+)
 create mode 100755 test/ganeti.utils_mlockall_unittest.py

diff --git a/Makefile.am b/Makefile.am
index ddc0bdfbc..0aaab2a70 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -340,6 +340,7 @@ python_tests = \
 	test/ganeti.ssh_unittest.py \
 	test/ganeti.uidpool_unittest.py \
 	test/ganeti.utils_unittest.py \
+	test/ganeti.utils_mlockall_unittest.py \
 	test/ganeti.workerpool_unittest.py \
 	test/docs_unittest.py \
 	test/tempfile_fork_unittest.py
diff --git a/lib/utils.py b/lib/utils.py
index bcf811a7b..a9075e6e5 100644
--- a/lib/utils.py
+++ b/lib/utils.py
@@ -57,6 +57,11 @@ except ImportError:
   import sha
   sha1 = sha.new
 
+try:
+  import ctypes
+except ImportError:
+  ctypes = None
+
 from ganeti import errors
 from ganeti import constants
 
@@ -83,6 +88,10 @@ _RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
 _STRUCT_UCRED = "iII"
 _STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED)
 
+# Flags for mlockall() (from bits/mman.h)
+_MCL_CURRENT = 1
+_MCL_FUTURE = 2
+
 
 class RunResult(object):
   """Holds the result of running external programs.
@@ -1739,6 +1748,37 @@ def CloseFDs(noclose_fds=None):
     _CloseFDNoErr(fd)
 
 
+def Mlockall():
+  """Lock current process' virtual address space into RAM.
+
+  This is equivalent to the C call mlockall(MCL_CURRENT|MCL_FUTURE),
+  see mlock(2) for more details. This function requires ctypes module.
+
+  """
+  if ctypes is None:
+    logging.warning("Cannot set memory lock, ctypes module not found")
+    return
+
+  libc = ctypes.cdll.LoadLibrary("libc.so.6")
+  if libc is None:
+    logging.error("Cannot set memory lock, ctypes cannot load libc")
+    return
+
+  # Some older version of the ctypes module don't have built-in functionality
+  # to access the errno global variable, where function error codes are stored.
+  # By declaring this variable as a pointer to an integer we can then access
+  # its value correctly, should the mlockall call fail, in order to see what
+  # the actual error code was.
+  libc.__errno_location.restype = ctypes.POINTER(ctypes.c_int)
+
+  if libc.mlockall(_MCL_CURRENT | _MCL_FUTURE):
+    logging.error("Cannot set memory lock: %s" %
+                  os.strerror(libc.__errno_location().contents.value))
+    return
+
+  logging.debug("Memory lock set")
+
+
 def Daemonize(logfile):
   """Daemonize the current process.
 
diff --git a/test/ganeti.utils_mlockall_unittest.py b/test/ganeti.utils_mlockall_unittest.py
new file mode 100755
index 000000000..654d518a5
--- /dev/null
+++ b/test/ganeti.utils_mlockall_unittest.py
@@ -0,0 +1,41 @@
+#!/usr/bin/python
+#
+
+# Copyright (C) 2010 Google Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+
+"""Script for testing utils.Mlockall
+
+This test is run in a separate process because it changes memory behaviour.
+
+"""
+
+import unittest
+
+from ganeti import utils
+
+import testutils
+
+
+class TestResetTempfileModule(unittest.TestCase):
+  def test(self):
+    utils.Mlockall()
+
+
+if __name__ == "__main__":
+  testutils.GanetiTestProgram()
-- 
GitLab