summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDirk Mueller <dirk@dmllr.de>2018-10-04 14:37:25 +0200
committerDirk Mueller <dirk@dmllr.de>2019-09-16 11:54:18 +0200
commitfdb32096ab1fafbacf116b722140b24f0e2cd834 (patch)
tree6ca72c87f3864f5aa09c398011c03624cc3cfe1a
parentc5a39636310e4cf96a377404ca6544d6a0037c44 (diff)
downloadoslo-rootwrap-5.14.2.tar.gz
Run rootwrap with lower fd ulimit by defaultrocky-em5.14.2
On Python 2.x, a subprocess.Popen() with close_fds=True will fork and then close filedescriptors range(3..os.sysconf("SC_OPEN_MAX")), which thanks to Kernel PTI (Kaiser patches) is significantly slower in 2018 when the range is very large. With a soft limit of 1048576, benchmark.py reports an overhead of ~ 400ms without this patch and 2ms with the patch applied. This patch adds a configuration option and leaves it disabled for the stable/* backports to not change default behavior. Also includes Ben Nemec's release note entry, adjusted for the stable backport. This is Ib29e96307caa39c21936f216d9aed7907e7a7331 for master. Also includes I2391315f77718a3c9eb9fc8c03a6882237f33548 from master. Also includes I3b481ddd14ae2b948270d715aad157cf3996def7 from master. (cherry picked from commit c0a86998203315858721a7b2c8ab75fbf5cd51d9) (cherry picked from commit 8f148f5e9e786d8a3922b0d5ae29bc54b8e8e1e9) (cherry picked from commit 5259c08c09eb3aeab64af928cddf84bc213594d3) Closes-Bug: 1796267 Closes-Bug: #1804639 Change-Id: Idd98c183eca3e2df8648fc0f37d27fe9cc6d0563
-rw-r--r--doc/source/user/usage.rst11
-rw-r--r--etc/rootwrap.conf.sample5
-rw-r--r--oslo_rootwrap/cmd.py40
-rw-r--r--oslo_rootwrap/wrapper.py6
-rw-r--r--releasenotes/notes/file-descriptor-limit-e2b2a3033b9ef21e.yaml13
5 files changed, 74 insertions, 1 deletions
diff --git a/doc/source/user/usage.rst b/doc/source/user/usage.rst
index 56f4bcb..bc25bbb 100644
--- a/doc/source/user/usage.rst
+++ b/doc/source/user/usage.rst
@@ -113,6 +113,17 @@ syslog_log_level
unsuccessful attempts. Example:
``syslog_log_level=ERROR``
+rlimit_nofile
+ Specify rlimit for number of open file descriptors used by oslo rootwrap
+ and its child processes by default. This is useful in case there is a
+ excessively large ulimit configured for the calling process that shouldn't
+ inherit to oslo.rootwrap and its called processes. Will not attempt to raise
+ the limit. Defaults to -1, which means this functionality is disabled. Set it to
+ 1024 (or a higher number, depending on your usecase) to enable it.
+
+ Ignored on platforms that do not provide "/proc/self/fd" (e.g. non-Linux).
+
+
.filters files
==============
diff --git a/etc/rootwrap.conf.sample b/etc/rootwrap.conf.sample
index b8f528f..22be5a2 100644
--- a/etc/rootwrap.conf.sample
+++ b/etc/rootwrap.conf.sample
@@ -28,3 +28,8 @@ syslog_log_level=ERROR
# Rootwrap daemon exits after this seconds of inactivity
daemon_timeout=600
+
+# Rootwrap daemon limits itself to that many file descriptors (Linux only)
+# Set to -1 for disabling (which is the default).
+# rlimit_nofile=1024
+rlimit_nofile=-1
diff --git a/oslo_rootwrap/cmd.py b/oslo_rootwrap/cmd.py
index 0036fa5..49e7583 100644
--- a/oslo_rootwrap/cmd.py
+++ b/oslo_rootwrap/cmd.py
@@ -33,11 +33,19 @@
from __future__ import print_function
import logging
+import os
import sys
+from oslo_rootwrap import subprocess
+from oslo_rootwrap import wrapper
+
from six import moves
-from oslo_rootwrap import wrapper
+try:
+ # This isn't available on all platforms (e.g. Windows).
+ import resource
+except ImportError:
+ resource = None
RC_UNAUTHORIZED = 99
RC_NOCOMMAND = 98
@@ -83,6 +91,36 @@ def main(run_daemon=False):
_exit_error(execname, "Incorrect configuration file: %s" % configfile,
RC_BADCONFIG, log=False)
+ if resource:
+ # When use close_fds=True on Python 2.x, calling subprocess with
+ # close_fds=True (which we do by default) can be inefficient when
+ # the current fd ulimits are large, because it blindly closes
+ # all fds in the range(1, $verylargenumber)
+
+ # Lower our ulimit to a reasonable value to regain performance.
+ fd_limits = resource.getrlimit(resource.RLIMIT_NOFILE)
+ sensible_fd_limit = min(config.rlimit_nofile, fd_limits[0])
+ if (sensible_fd_limit > 0 and fd_limits[0] > sensible_fd_limit):
+ # Close any fd beyond sensible_fd_limit prior adjusting our
+ # rlimit to ensure all fds are closed
+ for fd_entry in os.listdir('/proc/self/fd'):
+ # NOTE(dmllr): In a previous patch revision non-numeric
+ # dir entries were silently ignored which reviewers
+ # didn't like. Readd exception handling when it occurs.
+ fd = int(fd_entry)
+ if fd >= sensible_fd_limit:
+ os.close(fd)
+ # Unfortunately this inherits to our children, so allow them to
+ # re-raise by passing through the hard limit unmodified
+ resource.setrlimit(
+ resource.RLIMIT_NOFILE, (sensible_fd_limit, fd_limits[1]))
+ # This is set on import to the hard ulimit. if its defined we
+ # already have imported it, so we need to update it to the new
+ # limit
+ if (hasattr(subprocess, 'MAXFD') and
+ subprocess.MAXFD > sensible_fd_limit):
+ subprocess.MAXFD = sensible_fd_limit
+
if config.use_syslog:
wrapper.setup_syslog(execname,
config.syslog_log_facility,
diff --git a/oslo_rootwrap/wrapper.py b/oslo_rootwrap/wrapper.py
index 3b63866..3a67f60 100644
--- a/oslo_rootwrap/wrapper.py
+++ b/oslo_rootwrap/wrapper.py
@@ -97,6 +97,12 @@ class RootwrapConfig(object):
else:
self.daemon_timeout = 600
+ # fd ulimit
+ if config.has_option("DEFAULT", "rlimit_nofile"):
+ self.rlimit_nofile = int(config.get("DEFAULT", "rlimit_nofile"))
+ else:
+ self.rlimit_nofile = -1
+
def setup_syslog(execname, facility, level):
try:
diff --git a/releasenotes/notes/file-descriptor-limit-e2b2a3033b9ef21e.yaml b/releasenotes/notes/file-descriptor-limit-e2b2a3033b9ef21e.yaml
new file mode 100644
index 0000000..a509243
--- /dev/null
+++ b/releasenotes/notes/file-descriptor-limit-e2b2a3033b9ef21e.yaml
@@ -0,0 +1,13 @@
+---
+features:
+ - |
+ A configurable limit on the number of file descriptors that can be opened
+ by a rootwrap-started process has been added. It defaults to disabled, but
+ can be adjusted by setting the ``rlimit_nofile`` option in rootwrap.conf
+ to a larger or smaller value.
+upgrade:
+ - |
+ For OpenStack Rocky and older, the functionality is disabled by default.
+ Users affected by the original issue and would like to make use of it
+ can optionally enable it by setting the ``rlimit_nofile`` option in
+ rootwrap.conf to a value of 1024 or higher.