Merge bitcoin/bitcoin#30065: init: fixes file descriptor accounting

d4c7c4009d init: error out if -maxconnections is negative (Sergi Delgado Segura)
c773649481 init: improves file descriptors accounting and docs (Sergi Delgado Segura)
29008a7ff4 init: fixes fd accounting regarding poll/select (Sergi Delgado Segura)

Pull request description:

  The current logic for file descriptor accounting is pretty convoluted and hard to follow. This is partially caused by the lack of documentation plus non-intuitive variable naming (which was more intuitive when fewer things were accounted for, but
  hasn't aged well). This has led to this accounting being error-prone and hard to maintain (as shown in the first commit of this PR).

  Redefine some of the constants, plus document what are we accounting for so this can be extended more easily

  Fixes #18911

ACKs for top commit:
  sr-gi:
    > ACK [d4c7c40](d4c7c4009d)
  naumenkogs:
    ACK d4c7c4009d
  vasild:
    ACK d4c7c4009d
  TheCharlatan:
    ACK d4c7c4009d

Tree-SHA512: 1524d10c8ad8f354f6ab9c244699adbcdae2dd7aba37de5b8f9e177c629e8a2cce0f6e8117e076dde3a592f5283bd30a4201db96a3c011e335c02d1fde7414bc
This commit is contained in:
Ryan Ofsky 2024-09-10 12:40:37 -04:00
commit 2756797eca
No known key found for this signature in database
GPG key ID: 46800E30FC748A66

View file

@ -148,11 +148,12 @@ static constexpr bool DEFAULT_STOPAFTERBLOCKIMPORT{false};
// Win32 LevelDB doesn't use filedescriptors, and the ones used for // Win32 LevelDB doesn't use filedescriptors, and the ones used for
// accessing block files don't count towards the fd_set size limit // accessing block files don't count towards the fd_set size limit
// anyway. // anyway.
#define MIN_CORE_FILEDESCRIPTORS 0 #define MIN_LEVELDB_FDS 0
#else #else
#define MIN_CORE_FILEDESCRIPTORS 150 #define MIN_LEVELDB_FDS 150
#endif #endif
static constexpr int MIN_CORE_FDS = MIN_LEVELDB_FDS + NUM_FDS_MESSAGE_CAPTURE;
static const char* DEFAULT_ASMAP_FILENAME="ip_asn.map"; static const char* DEFAULT_ASMAP_FILENAME="ip_asn.map";
/** /**
@ -838,8 +839,7 @@ void InitLogging(const ArgsManager& args)
namespace { // Variables internal to initialization process only namespace { // Variables internal to initialization process only
int nMaxConnections; int nMaxConnections;
int nUserMaxConnections; int available_fds;
int nFD;
ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK_LIMITED | NODE_WITNESS); ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK_LIMITED | NODE_WITNESS);
int64_t peer_connect_timeout; int64_t peer_connect_timeout;
std::set<BlockFilterType> g_enabled_filter_types; std::set<BlockFilterType> g_enabled_filter_types;
@ -991,27 +991,33 @@ bool AppInitParameterInteraction(const ArgsManager& args)
return InitError(Untranslated("Cannot set -listen=0 together with -listenonion=1")); return InitError(Untranslated("Cannot set -listen=0 together with -listenonion=1"));
} }
// Make sure enough file descriptors are available // Make sure enough file descriptors are available. We need to reserve enough FDs to account for the bare minimum,
// plus all manual connections and all bound interfaces. Any remainder will be available for connection sockets
// Number of bound interfaces (we have at least one)
int nBind = std::max(nUserBind, size_t(1)); int nBind = std::max(nUserBind, size_t(1));
nUserMaxConnections = args.GetIntArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS); // Maximum number of connections with other nodes, this accounts for all types of outbounds and inbounds except for manual
nMaxConnections = std::max(nUserMaxConnections, 0); int user_max_connection = args.GetIntArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
if (user_max_connection < 0) {
return InitError(Untranslated("-maxconnections must be greater or equal than zero"));
}
// Reserve enough FDs to account for the bare minimum, plus any manual connections, plus the bound interfaces
int min_required_fds = MIN_CORE_FDS + MAX_ADDNODE_CONNECTIONS + nBind;
nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS + MAX_ADDNODE_CONNECTIONS + nBind + NUM_FDS_MESSAGE_CAPTURE); // Try raising the FD limit to what we need (available_fds may be smaller than the requested amount if this fails)
available_fds = RaiseFileDescriptorLimit(user_max_connection + min_required_fds);
#ifdef USE_POLL // If we are using select instead of poll, our actual limit may be even smaller
int fd_max = nFD; #ifndef USE_POLL
#else available_fds = std::min(FD_SETSIZE, available_fds);
int fd_max = FD_SETSIZE;
#endif #endif
// Trim requested connection counts, to fit into system limitations if (available_fds < min_required_fds)
// <int> in std::min<int>(...) to work around FreeBSD compilation issue described in #2695 return InitError(strprintf(_("Not enough file descriptors available. %d available, %d required."), available_fds, min_required_fds));
nMaxConnections = std::max(std::min<int>(nMaxConnections, fd_max - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS - NUM_FDS_MESSAGE_CAPTURE), 0);
if (nFD < MIN_CORE_FILEDESCRIPTORS)
return InitError(_("Not enough file descriptors available."));
nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS - NUM_FDS_MESSAGE_CAPTURE, nMaxConnections);
if (nMaxConnections < nUserMaxConnections) // Trim requested connection counts, to fit into system limitations
InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), nUserMaxConnections, nMaxConnections)); nMaxConnections = std::min(available_fds - min_required_fds, user_max_connection);
if (nMaxConnections < user_max_connection)
InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), user_max_connection, nMaxConnections));
// ********************************************************* Step 3: parameter-to-internal-flags // ********************************************************* Step 3: parameter-to-internal-flags
if (auto result{init::SetLoggingCategories(args)}; !result) return InitError(util::ErrorString(result)); if (auto result{init::SetLoggingCategories(args)}; !result) return InitError(util::ErrorString(result));
@ -1159,7 +1165,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
return false; return false;
} }
LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, nFD); LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, available_fds);
// Warn about relative -datadir path. // Warn about relative -datadir path.
if (args.IsArgSet("-datadir") && !args.GetPathArg("-datadir").is_absolute()) { if (args.IsArgSet("-datadir") && !args.GetPathArg("-datadir").is_absolute()) {