summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Chan <jason.chan@mongodb.com>2023-04-20 19:57:59 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-04-20 20:28:15 +0000
commit85878215714ad3851e6067fce5c080dc8fe00bda (patch)
tree581569066325b51d52c0809f3295b2297c012865
parentd3baa192c0048b643e9f664ced15a0fe1cd49e08 (diff)
downloadmongo-85878215714ad3851e6067fce5c080dc8fe00bda.tar.gz
SERVER-73943 Pin program code segments in memory on startup
-rw-r--r--buildscripts/resmokelib/mongod_fuzzer_configs.py2
-rw-r--r--etc/evergreen_yml_components/variants/ibm.yml2
-rw-r--r--etc/evergreen_yml_components/variants/misc_release.yml3
-rw-r--r--jstests/noPassthrough/pin_code_segments_on_startup.js15
-rw-r--r--src/mongo/db/SConscript1
-rw-r--r--src/mongo/util/SConscript15
-rw-r--r--src/mongo/util/pin_code_segments.cpp107
-rw-r--r--src/mongo/util/pin_code_segments_params.idl42
8 files changed, 186 insertions, 1 deletions
diff --git a/buildscripts/resmokelib/mongod_fuzzer_configs.py b/buildscripts/resmokelib/mongod_fuzzer_configs.py
index a9750997fef..874bc7764cf 100644
--- a/buildscripts/resmokelib/mongod_fuzzer_configs.py
+++ b/buildscripts/resmokelib/mongod_fuzzer_configs.py
@@ -92,6 +92,8 @@ def generate_independent_parameters(rng):
# The old retryable writes format is used by other variants. Weight towards turning on the
# new retryable writes format on in this one.
ret["storeFindAndModifyImagesInSideCollection"] = True
+ # TODO (SERVER-75632): Uncomment this to enable passthrough testing.
+ # ret["lockCodeSegmentsInMemory"] = rng.choice([True, False])
return ret
diff --git a/etc/evergreen_yml_components/variants/ibm.yml b/etc/evergreen_yml_components/variants/ibm.yml
index cdefcb2e8b2..5c1ef417944 100644
--- a/etc/evergreen_yml_components/variants/ibm.yml
+++ b/etc/evergreen_yml_components/variants/ibm.yml
@@ -80,7 +80,7 @@ buildvariants:
multiversion_architecture: s390x
multiversion_edition: enterprise
test_flags: >-
- --excludeWithAnyTags=incompatible_with_s390x
+ --excludeWithAnyTags=incompatible_with_s390x,requires_increased_memlock_limits
tasks:
- name: compile_test_and_package_serial_TG
distros:
diff --git a/etc/evergreen_yml_components/variants/misc_release.yml b/etc/evergreen_yml_components/variants/misc_release.yml
index 2f9b8639957..73e9d2a02c5 100644
--- a/etc/evergreen_yml_components/variants/misc_release.yml
+++ b/etc/evergreen_yml_components/variants/misc_release.yml
@@ -1159,6 +1159,7 @@ buildvariants:
packager_distro: suse12
repo_edition: org
scons_cache_scope: shared
+ test_flags: --excludeWithAnyTags=requires_increased_memlock_limits
large_distro_name: suse12-sp5-large
tasks:
- name: compile_test_and_package_serial_TG
@@ -1300,6 +1301,7 @@ buildvariants:
packager_distro: suse15
repo_edition: org
scons_cache_scope: shared
+ test_flags: --excludeWithAnyTags=requires_increased_memlock_limits
large_distro_name: suse15-build
tasks:
- name: compile_test_and_package_serial_TG
@@ -1550,6 +1552,7 @@ buildvariants:
repo_edition: org
scons_cache_scope: shared
large_distro_name: ubuntu2204-large
+ test_flags: --excludeWithAnyTags=requires_increased_memlock_limits
tasks:
- name: compile_test_and_package_serial_TG
distros:
diff --git a/jstests/noPassthrough/pin_code_segments_on_startup.js b/jstests/noPassthrough/pin_code_segments_on_startup.js
new file mode 100644
index 00000000000..9135fae85ba
--- /dev/null
+++ b/jstests/noPassthrough/pin_code_segments_on_startup.js
@@ -0,0 +1,15 @@
+/**
+ * Tests that a standalone mongod is able to pin code segments on startup when
+ * 'lockCodeSegmentsInMemory=true'.
+ * TODO (SERVER-75632): Re-enable this test on amazon linux once ulimits are configured.
+ * @tags: [requires_increased_memlock_limits, incompatible_with_macos,
+ * incompatible_with_windows_tls, incompatible_with_amazon_linux]
+ */
+
+(function() {
+"use strict";
+
+const conn = MongoRunner.runMongod({setParameter: {lockCodeSegmentsInMemory: true}});
+assert.neq(null, conn, "mongod was unable to start up");
+assert.eq(0, MongoRunner.stopMongod(conn));
+}());
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index aeed37ed277..c46bee24dcd 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -2385,6 +2385,7 @@ env.Library(
'$BUILD_DIR/mongo/util/ntservice',
'$BUILD_DIR/mongo/util/options_parser/options_parser_init',
'$BUILD_DIR/mongo/util/periodic_runner_factory',
+ '$BUILD_DIR/mongo/util/pin_code_segments' if env.TargetOSIs('linux') else [],
'$BUILD_DIR/mongo/util/testing_options',
'$BUILD_DIR/mongo/util/version_impl',
'$BUILD_DIR/mongo/watchdog/watchdog_mongod',
diff --git a/src/mongo/util/SConscript b/src/mongo/util/SConscript
index b8e918e2e5d..663e2688f22 100644
--- a/src/mongo/util/SConscript
+++ b/src/mongo/util/SConscript
@@ -573,6 +573,21 @@ env.Library(
],
)
+if env.TargetOSIs('linux'):
+ env.Library(
+ target='pin_code_segments',
+ source=[
+ 'pin_code_segments.cpp',
+ 'pin_code_segments_params.idl',
+ ],
+ LIBDEPS=[
+ '$BUILD_DIR/mongo/base',
+ ],
+ LIBDEPS_PRIVATE=[
+ '$BUILD_DIR/mongo/idl/server_parameter',
+ ],
+ )
+
env.Benchmark(
target='hash_table_bm',
source='hash_table_bm.cpp',
diff --git a/src/mongo/util/pin_code_segments.cpp b/src/mongo/util/pin_code_segments.cpp
new file mode 100644
index 00000000000..13273f72b31
--- /dev/null
+++ b/src/mongo/util/pin_code_segments.cpp
@@ -0,0 +1,107 @@
+/**
+ * Copyright (C) 2023-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * 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
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kControl
+
+#include <elf.h>
+#include <link.h>
+#include <sys/mman.h>
+#include <vector>
+
+#include "mongo/db/initialize_server_global_state.h"
+#include "mongo/db/initialize_server_global_state_gen.h"
+#include "mongo/logv2/log.h"
+#include "mongo/util/hex.h"
+#include "mongo/util/pin_code_segments_params_gen.h"
+
+namespace mongo {
+namespace {
+
+using ElfWEhdr = ElfW(Ehdr);
+using ElfWHalf = ElfW(Half);
+using ElfWPhdr = ElfW(Phdr);
+
+struct CodeSegment {
+ void* addr;
+ size_t memSize;
+};
+
+int appendCodeSegments(dl_phdr_info* info, size_t size, void* data) {
+ auto segments = reinterpret_cast<std::vector<CodeSegment>*>(data);
+ for (ElfWHalf i = 0; i < info->dlpi_phnum; ++i) {
+ const ElfWPhdr& phdr(info->dlpi_phdr[i]);
+ if (phdr.p_type != PT_LOAD) {
+ // Code segments must be of LOAD type.
+ continue;
+ }
+ if (auto f = phdr.p_flags; f & PF_R && f & PF_X && !(f & PF_W)) {
+ // Code segments typically have read and execute permissions, but not writable
+ // permissions. Meanwhile, data segments, which we want to ignore, typically have
+ // read, write and execute permissions.
+ continue;
+ }
+ segments->push_back(
+ {reinterpret_cast<void*>(info->dlpi_addr + phdr.p_vaddr), phdr.p_memsz});
+ }
+ return 0;
+}
+
+std::vector<CodeSegment> getCodeSegments() {
+ std::vector<CodeSegment> segments;
+ dl_iterate_phdr(appendCodeSegments, &segments);
+ return segments;
+}
+
+MONGO_INITIALIZER(PinCodeSegments)(InitializerContext*) {
+ if (!gLockCodeSegmentsInMemory) {
+ return;
+ }
+ LOGV2(7394303, "Pinning code segments");
+ size_t lockedMemSize = 0;
+ auto codeSegments = getCodeSegments();
+ for (auto&& segment : codeSegments) {
+ if (mlock(segment.addr, segment.memSize) != 0) {
+ auto ec = lastSystemError();
+ LOGV2_FATAL(
+ 7394301,
+ "Failed to lock code segment, ensure system ulimits are properly configured",
+ "error"_attr = errorMessage(ec),
+ "address"_attr = unsignedHex(reinterpret_cast<uintptr_t>(segment.addr)),
+ "memSize"_attr = segment.memSize);
+ }
+ lockedMemSize += segment.memSize;
+ }
+ LOGV2(7394302,
+ "Successfully locked code segments into memory",
+ "numSegments"_attr = codeSegments.size(),
+ "totalLockedMemSize"_attr = lockedMemSize);
+}
+
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/util/pin_code_segments_params.idl b/src/mongo/util/pin_code_segments_params.idl
new file mode 100644
index 00000000000..2686a22a60a
--- /dev/null
+++ b/src/mongo/util/pin_code_segments_params.idl
@@ -0,0 +1,42 @@
+# Copyright (C) 2023-present MongoDB, Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the Server Side Public License, version 1,
+# as published by MongoDB, Inc.
+#
+# 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
+# Server Side Public License for more details.
+#
+# You should have received a copy of the Server Side Public License
+# along with this program. If not, see
+# <http://www.mongodb.com/licensing/server-side-public-license>.
+#
+# As a special exception, the copyright holders give permission to link the
+# code of portions of this program with the OpenSSL library under certain
+# conditions as described in each individual source file and distribute
+# linked combinations including the program with the OpenSSL library. You
+# must comply with the Server Side Public License in all respects for
+# all of the code used other than as permitted herein. If you modify file(s)
+# with this exception, you may extend this exception to your version of the
+# file(s), but you are not obligated to do so. If you do not wish to do so,
+# delete this exception statement from your version. If you delete this
+# exception statement from all source files in the program, then also delete
+# it in the license file.
+#
+
+global:
+ cpp_namespace: "mongo"
+
+server_parameters:
+ lockCodeSegmentsInMemory:
+ description: >-
+ When enabled, the server will attempt to call mlock() on all memory ranges corresponding
+ to the process code segments (including shared libraries) on startup. If any call to mlock()
+ fails, the server will terminate. When disabled, the server will not attempt to pin any code
+ segments in memory. This feature is only available on Linux.
+ set_at: startup
+ cpp_vartype: bool
+ cpp_varname: gLockCodeSegmentsInMemory
+ default: false