]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
common: use close_range on Linux 61625/head
authoredef <edef@edef.eu>
Thu, 16 Mar 2023 09:43:58 +0000 (09:43 +0000)
committerPeter Razumovsky <prazumovsky@mirantis.com>
Mon, 3 Feb 2025 13:17:28 +0000 (17:17 +0400)
Fix rook/rook#10110, which occurs when _SC_OPEN_MAX/RLIMIT_NOFILE is
set to very large values (2^30), leaving fork_function pegging a core
busylooping.

The glibc wrappers closefrom(3)/close_range(3) are not available before
glibc 2.34, so we invoke the syscall directly. When glibc 2.34 is old
enough to be a reasonable hard minimum dependency, we should switch to
using closefrom.

If we're not running on (recent enough) Linux, we fall back to the
existing approach.

Fixes: https://tracker.ceph.com/issues/59125
Signed-off-by: edef <edef@edef.eu>
(cherry picked from commit 1a33cefa1980a18b11479275c1f708c1de24ee37)

src/common/SubProcess.cc
src/common/fork_function.h

index 1faf33e36eeeb45af57068185edeacc2407bd10a..8f28ff25ca2153bc0b8013dedb118f2824e5de74 100644 (file)
@@ -4,6 +4,9 @@
 #include <sys/types.h>
 #include <signal.h>
 #endif
+#ifdef __linux__
+#include <sys/syscall.h>
+#endif
 #include <stdarg.h>
 #include <fcntl.h>
 #include <unistd.h>
@@ -200,6 +203,12 @@ int SubProcess::spawn() {
     int maxfd = sysconf(_SC_OPEN_MAX);
     if (maxfd == -1)
       maxfd = 16384;
+
+#if defined(__linux__) && defined(SYS_close_range)
+    if (::syscall(SYS_close_range, STDERR_FILENO + 1, ~0U, 0) == 0)
+      maxfd = STDERR_FILENO;
+#endif
+
     for (int fd = 0; fd <= maxfd; fd++) {
       if (fd == STDIN_FILENO && stdin_op != CLOSE)
        continue;
index 3a4f2f29c08a274d13c9e92c54a6f5f58a2326f7..5c94be4dcaf72e599c43101d12b522ba9c11e940 100644 (file)
@@ -13,6 +13,9 @@
 #ifndef _WIN32
 #include <sys/wait.h>
 #endif
+#ifdef __linux__
+#include <sys/syscall.h>
+#endif
 #include <sys/types.h>
 
 #include "include/ceph_assert.h"
@@ -53,17 +56,23 @@ static inline int fork_function(
   // we are forker (first child)
 
   // close all fds
-  int maxfd = sysconf(_SC_OPEN_MAX);
-  if (maxfd == -1)
-    maxfd = 16384;
-  for (int fd = 0; fd <= maxfd; fd++) {
-    if (fd == STDIN_FILENO)
-      continue;
-    if (fd == STDOUT_FILENO)
-      continue;
-    if (fd == STDERR_FILENO)
-      continue;
-    ::close(fd);
+#if defined(__linux__) && defined(SYS_close_range)
+  if (::syscall(SYS_close_range, STDERR_FILENO + 1, ~0U, 0))
+#endif
+  {
+    // fall back to manually closing
+    int maxfd = sysconf(_SC_OPEN_MAX);
+    if (maxfd == -1)
+      maxfd = 16384;
+    for (int fd = 0; fd <= maxfd; fd++) {
+      if (fd == STDIN_FILENO)
+        continue;
+      if (fd == STDOUT_FILENO)
+        continue;
+      if (fd == STDERR_FILENO)
+        continue;
+      ::close(fd);
+    }
   }
 
   sigset_t mask, oldmask;