diff --git a/Makefile.am b/Makefile.am
index 9f5b7bed..c8ecf681 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -139,6 +139,7 @@ console_bs_SOURCES = \
console/executor_events.cpp \
console/executor_logging.cpp \
console/executor_options.cpp \
+ console/executor_paging.cpp \
console/executor_runner.cpp \
console/executor_scans.cpp \
console/executor_signals.cpp \
diff --git a/builds/msvc/vs2022/bs/bs.vcxproj b/builds/msvc/vs2022/bs/bs.vcxproj
index e8fb0765..3c389ad6 100644
--- a/builds/msvc/vs2022/bs/bs.vcxproj
+++ b/builds/msvc/vs2022/bs/bs.vcxproj
@@ -137,6 +137,7 @@
+
diff --git a/builds/msvc/vs2022/bs/bs.vcxproj.filters b/builds/msvc/vs2022/bs/bs.vcxproj.filters
index 6a0be741..8699124f 100644
--- a/builds/msvc/vs2022/bs/bs.vcxproj.filters
+++ b/builds/msvc/vs2022/bs/bs.vcxproj.filters
@@ -69,6 +69,9 @@
src
+
+ src
+
src
diff --git a/console/executor.hpp b/console/executor.hpp
index 647ac21b..420ba2b6 100644
--- a/console/executor.hpp
+++ b/console/executor.hpp
@@ -63,6 +63,7 @@ class executor
// Store dumps.
void dump_version() const;
void dump_hardware() const;
+ void dump_paging() const;
void dump_options() const;
void dump_configuration() const;
void dump_body_sizes() const;
diff --git a/console/executor_paging.cpp b/console/executor_paging.cpp
new file mode 100644
index 00000000..6ff5dc28
--- /dev/null
+++ b/console/executor_paging.cpp
@@ -0,0 +1,99 @@
+/**
+ * Copyright (c) 2011-2026 libbitcoin developers (see AUTHORS)
+ *
+ * This file is part of libbitcoin.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+#include "executor.hpp"
+#include "localize.hpp"
+
+#if defined(HAVE_LINUX)
+#include
+#include
+#include
+
+namespace {
+
+// Read a single decimal integer from a /proc/sys/vm sysctl entry.
+std::optional read_vm_param(const char* path) noexcept
+{
+ char buffer[24]{};
+ const int fd = ::open(path, O_RDONLY);
+ if (fd < 0)
+ return std::nullopt;
+
+ const auto count = ::read(fd, buffer, sizeof(buffer) - 1u);
+ ::close(fd);
+
+ if (count <= 0)
+ return std::nullopt;
+
+ uint64_t value{};
+ const auto [ptr, ec] = std::from_chars(buffer, buffer + count, value);
+ if (ec != std::errc{})
+ return std::nullopt;
+
+ return value;
+}
+
+} // namespace
+#endif
+
+namespace libbitcoin {
+namespace server {
+
+using boost::format;
+
+// Paging dump.
+// ----------------------------------------------------------------------------
+
+void executor::dump_paging() const
+{
+#if defined(HAVE_LINUX)
+ const auto dirty_background_bytes = read_vm_param("/proc/sys/vm/dirty_background_bytes");
+ const auto dirty_bytes = read_vm_param("/proc/sys/vm/dirty_bytes");
+ const auto dirty_background_ratio = read_vm_param("/proc/sys/vm/dirty_background_ratio");
+ const auto dirty_ratio = read_vm_param("/proc/sys/vm/dirty_ratio");
+ const auto dirty_expire_centisecs = read_vm_param("/proc/sys/vm/dirty_expire_centisecs");
+ const auto dirty_writeback_centisecs = read_vm_param("/proc/sys/vm/dirty_writeback_centisecs");
+
+ if (!dirty_background_bytes || !dirty_bytes || !dirty_background_ratio ||
+ !dirty_ratio || !dirty_expire_centisecs || !dirty_writeback_centisecs)
+ return;
+
+ const auto& log = metadata_.configured.log;
+ const auto bytes_mode = (*dirty_bytes != 0u) || (*dirty_background_bytes != 0u);
+ if (!bytes_mode)
+ {
+ if (log.dirty_ratio_minimum != 0u &&
+ *dirty_ratio >= log.dirty_ratio_minimum &&
+ log.dirty_background_ratio_minimum != 0u &&
+ *dirty_background_ratio >= log.dirty_background_ratio_minimum)
+ return;
+ }
+
+ logger(format(BS_PAGING_TABLE)
+ % (bytes_mode ? "bytes" : "ratio")
+ % *dirty_bytes
+ % *dirty_ratio
+ % *dirty_background_bytes
+ % *dirty_background_ratio
+ % *dirty_expire_centisecs
+ % *dirty_writeback_centisecs);
+#endif
+}
+
+} // namespace server
+} // namespace libbitcoin
diff --git a/console/executor_runner.cpp b/console/executor_runner.cpp
index f9b25c64..bdd8d060 100644
--- a/console/executor_runner.cpp
+++ b/console/executor_runner.cpp
@@ -150,6 +150,7 @@ bool executor::do_run()
capture_.start();
dump_version();
dump_hardware();
+ dump_paging();
dump_options();
logger(BS_NODE_INTERRUPT);
diff --git a/console/localize.hpp b/console/localize.hpp
index cac237cc..383bf6dc 100644
--- a/console/localize.hpp
+++ b/console/localize.hpp
@@ -257,6 +257,16 @@
#define BS_HARDWARE_TABLE2 \
"platform:%1% compiled:%2%."
+#define BS_PAGING_TABLE \
+ "Linux VM dirty page kernel settings...\n" \
+ "control_mode :%1%.\n" \
+ "dirty_bytes :%2%.\n" \
+ "dirty_ratio :%3%.\n" \
+ "dirty_background_bytes :%4%.\n" \
+ "dirty_background_ratio :%5%.\n" \
+ "dirty_expire_centisecs :%6%.\n" \
+ "dirty_writeback_centisecs:%7%."
+
#define BS_LOG_TABLE_HEADER \
"Log system configuration..."
#define BS_LOG_TABLE \
diff --git a/data/bs.cfg b/data/bs.cfg
index 451bf2b0..7a765de3 100644
--- a/data/bs.cfg
+++ b/data/bs.cfg
@@ -19,6 +19,10 @@ maximum_archive_files = 0
statistics_server = 0.0.0.0:0
# Enable verbose logging, defaults to false.
verbose = false
+# Warn at startup if vm.dirty_ratio is below this threshold, defaults to 90 (0 disables).
+dirty_ratio_minimum = 90
+# Warn at startup if vm.dirty_background_ratio is below this threshold, defaults to 90 (0 disables).
+dirty_background_ratio_minimum = 90
[bitcoin]
# The difficulty retargeting factor, defaults to 4.
diff --git a/include/bitcoin/server/settings.hpp b/include/bitcoin/server/settings.hpp
index f1c4fa20..aedbe66c 100644
--- a/include/bitcoin/server/settings.hpp
+++ b/include/bitcoin/server/settings.hpp
@@ -46,6 +46,8 @@ class BCS_API settings
bool verbose;
uint32_t maximum_size;
+ uint32_t dirty_ratio_minimum;
+ uint32_t dirty_background_ratio_minimum;
std::filesystem::path path;
#if defined (HAVE_MSC)
diff --git a/src/parser.cpp b/src/parser.cpp
index db9e85f8..106c1eb7 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -1759,6 +1759,16 @@ options_metadata parser::load_settings() THROWS
"Enable verbose logging, defaults to 'false'."
)
#endif
+ (
+ "log.dirty_ratio_minimum",
+ value(&configured.log.dirty_ratio_minimum),
+ "Warn at startup if vm.dirty_ratio is below this threshold, defaults to 90 (0 disables)."
+ )
+ (
+ "log.dirty_background_ratio_minimum",
+ value(&configured.log.dirty_background_ratio_minimum),
+ "Warn at startup if vm.dirty_background_ratio is below this threshold, defaults to 90 (0 disables)."
+ )
(
"log.maximum_size",
value(&configured.log.maximum_size),
diff --git a/src/settings.cpp b/src/settings.cpp
index bfcc5e4a..197f541a 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -44,7 +44,9 @@ settings::settings() NOEXCEPT
quitting{ false /*levels::quitting_defined*/ },
objects{ false /*levels::objects_defined*/ },
verbose{ false /*levels::verbose_defined*/ },
- maximum_size{ 1'000'000_u32 }
+ maximum_size{ 1'000'000_u32 },
+ dirty_ratio_minimum{ 90_u32 },
+ dirty_background_ratio_minimum{ 90_u32 }
{
}