diff --git a/configure.ac b/configure.ac index 4221a58a21..fad0d659d3 100644 --- a/configure.ac +++ b/configure.ac @@ -943,6 +943,25 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ [ AC_MSG_RESULT([no])] ) +AC_MSG_CHECKING(for ioprio syscalls) +AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([[ + #define _GNU_SOURCE + #include + #include + ]],[[ + int x = syscall(SYS_ioprio_get, 1, 0); + syscall(SYS_ioprio_set, 1, 0, x); + ]]) +],[ + have_ioprio_syscall=yes + AC_DEFINE(HAVE_IOPRIO_SYSCALL,1,[Define this symbol if you have ioprio syscalls]) +],[ + have_ioprio_syscall=no +]) +AC_MSG_RESULT($have_ioprio_syscall) +AM_CONDITIONAL([HAVE_IOPRIO_SYSCALL], [test "$have_ioprio_syscall" = "yes"]) + dnl Check for different ways of gathering OS randomness AC_MSG_CHECKING([for Linux getrandom function]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ diff --git a/src/Makefile.am b/src/Makefile.am index 1ccb5332c4..763eb11d80 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -312,6 +312,7 @@ BITCOIN_CORE_H = \ util/hash_type.h \ util/hasher.h \ util/insert.h \ + util/ioprio.h \ util/macros.h \ util/moneystr.h \ util/overflow.h \ @@ -762,6 +763,10 @@ libbitcoin_util_a_SOURCES = \ $(BITCOIN_CORE_H) # +if HAVE_IOPRIO_SYSCALL +libbitcoin_util_a_SOURCES += util/ioprio.cpp +endif + # cli # libbitcoin_cli_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_cli_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 13ea3a29be..70e6dab870 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -2516,7 +2516,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& // Fast-path: in this case it is possible to serve the block directly from disk, // as the network format matches the format on disk std::vector block_data; - if (!m_chainman.m_blockman.ReadRawBlockFromDisk(block_data, block_pos)) { + if (!m_chainman.m_blockman.ReadRawBlockFromDisk(block_data, block_pos, /*lowprio=*/true)) { if (WITH_LOCK(m_chainman.GetMutex(), return m_chainman.m_blockman.IsBlockPruned(*pindex))) { LogPrint(BCLog::NET, "Block was pruned before it could be read, disconnect peer=%s\n", pfrom.GetId()); } else { @@ -2530,7 +2530,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& } else { // Send block from disk std::shared_ptr pblockRead = std::make_shared(); - if (!m_chainman.m_blockman.ReadBlockFromDisk(*pblockRead, block_pos)) { + if (!m_chainman.m_blockman.ReadBlockFromDisk(*pblockRead, block_pos, /*lowprio=*/true)) { if (WITH_LOCK(m_chainman.GetMutex(), return m_chainman.m_blockman.IsBlockPruned(*pindex))) { LogPrint(BCLog::NET, "Block was pruned before it could be read, disconnect peer=%s\n", pfrom.GetId()); } else { @@ -4434,7 +4434,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, if (!block_pos.IsNull()) { CBlock block; - const bool ret{m_chainman.m_blockman.ReadBlockFromDisk(block, block_pos)}; + const bool ret{m_chainman.m_blockman.ReadBlockFromDisk(block, block_pos, /*lowprio=*/true)}; // If height is above MAX_BLOCKTXN_DEPTH then this block cannot get // pruned after we release cs_main above, so this read should never fail. assert(ret); @@ -6052,7 +6052,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto) PushMessage(*pto, std::move(cached_cmpctblock_msg.value())); } else { CBlock block; - const bool ret{m_chainman.m_blockman.ReadBlockFromDisk(block, *pBestIndex)}; + const bool ret{m_chainman.m_blockman.ReadBlockFromDisk(block, *pBestIndex, /*lowprio=*/true)}; assert(ret); CBlockHeaderAndShortTxIDs cmpctblock{block, m_rng.rand64()}; MakeAndPushMessage(*pto, NetMsgType::CMPCTBLOCK, cmpctblock); diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp index 96cf69927c..6556770175 100644 --- a/src/node/blockstorage.cpp +++ b/src/node/blockstorage.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -1034,10 +1035,13 @@ bool BlockManager::WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValid return true; } -bool BlockManager::ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos) const +bool BlockManager::ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const bool lowprio) const { block.SetNull(); + { + IOPRIO_IDLER(lowprio); + // Open history file to read AutoFile filein{OpenBlockFile(pos, true)}; if (filein.IsNull()) { @@ -1053,6 +1057,8 @@ bool BlockManager::ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos) cons return false; } + } // end IOPRIO_IDLER scope + // Check the header if (!CheckProofOfWork(block.GetHash(), block.nBits, GetConsensus())) { LogError("%s: Errors in block header at %s\n", __func__, pos.ToString()); @@ -1068,11 +1074,11 @@ bool BlockManager::ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos) cons return true; } -bool BlockManager::ReadBlockFromDisk(CBlock& block, const CBlockIndex& index) const +bool BlockManager::ReadBlockFromDisk(CBlock& block, const CBlockIndex& index, const bool lowprio) const { const FlatFilePos block_pos{WITH_LOCK(cs_main, return index.GetBlockPos())}; - if (!ReadBlockFromDisk(block, block_pos)) { + if (!ReadBlockFromDisk(block, block_pos, lowprio)) { return false; } if (block.GetHash() != index.GetBlockHash()) { @@ -1082,7 +1088,7 @@ bool BlockManager::ReadBlockFromDisk(CBlock& block, const CBlockIndex& index) co return true; } -bool BlockManager::ReadRawBlockFromDisk(std::vector& block, const FlatFilePos& pos) const +bool BlockManager::ReadRawBlockFromDisk(std::vector& block, const FlatFilePos& pos, const bool lowprio) const { FlatFilePos hpos = pos; // If nPos is less than 8 the pos is null and we don't have the block data @@ -1092,6 +1098,9 @@ bool BlockManager::ReadRawBlockFromDisk(std::vector& block, const FlatF return false; } hpos.nPos -= 8; // Seek back 8 bytes for meta header + + IOPRIO_IDLER(lowprio); + AutoFile filein{OpenBlockFile(hpos, true)}; if (filein.IsNull()) { LogError("%s: OpenBlockFile failed for %s\n", __func__, pos.ToString()); diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h index 821bbf5109..abbbb0c8d9 100644 --- a/src/node/blockstorage.h +++ b/src/node/blockstorage.h @@ -420,9 +420,9 @@ public: void UnlinkPrunedFiles(const std::set& setFilesToPrune) const; /** Functions for disk access for blocks */ - bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos) const; - bool ReadBlockFromDisk(CBlock& block, const CBlockIndex& index) const; - bool ReadRawBlockFromDisk(std::vector& block, const FlatFilePos& pos) const; + bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, bool lowprio = false) const; + bool ReadBlockFromDisk(CBlock& block, const CBlockIndex& index, bool lowprio = false) const; + bool ReadRawBlockFromDisk(std::vector& block, const FlatFilePos& pos, bool lowprio = false) const; bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex& index) const; diff --git a/src/util/ioprio.cpp b/src/util/ioprio.cpp new file mode 100644 index 0000000000..4e86f24d02 --- /dev/null +++ b/src/util/ioprio.cpp @@ -0,0 +1,37 @@ +// Copyright (c) 2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#if defined(HAVE_CONFIG_H) +#include +#endif + +#include + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include + +#ifndef IOPRIO_WHO_PROCESS +#define IOPRIO_WHO_PROCESS 1 +#endif +#ifndef IOPRIO_CLASS_IDLE +#define IOPRIO_CLASS_IDLE 3 +#endif +#ifndef IOPRIO_CLASS_SHIFT +#define IOPRIO_CLASS_SHIFT 13 +#endif + +int ioprio_get() { + return syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, 0); +} + +int ioprio_set(const int ioprio) { + return syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, 0, ioprio); +} + +int ioprio_set_idle() { + return ioprio_set(7 | (IOPRIO_CLASS_IDLE << IOPRIO_CLASS_SHIFT)); +} diff --git a/src/util/ioprio.h b/src/util/ioprio.h new file mode 100644 index 0000000000..cc6ff6a95a --- /dev/null +++ b/src/util/ioprio.h @@ -0,0 +1,57 @@ +// Copyright (c) 2016 Satoshi Nakamoto +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_UTIL_IOPRIO_H +#define BITCOIN_UTIL_IOPRIO_H + +#if defined(HAVE_CONFIG_H) +#include +#endif + +#include + +#ifdef HAVE_IOPRIO_SYSCALL +int ioprio_get(); +int ioprio_set(int ioprio); +int ioprio_set_idle(); + +class ioprio_idler { +private: + int orig; + +public: + ioprio_idler(const bool lowprio) { + if (!lowprio) { + orig = -1; + return; + } + + orig = ioprio_get(); + if (orig == -1) { + return; + } + if (ioprio_set_idle() == -1) { + orig = -1; + } + } + + ~ioprio_idler() { + if (orig == -1) { + return; + } + if (ioprio_set(orig) == -1) { + LogPrintf("failed to restore ioprio\n"); + } + } +}; +#define IOPRIO_IDLER(lowprio) ioprio_idler ioprio_idler_(lowprio) + +#else +#define ioprio_get() ((void)-1) +#define ioprio_set(ioprio) ((void)-1) +#define ioprio_set_idle() ((void)-1) +#define IOPRIO_IDLER(lowprio) (void)lowprio; +#endif + +#endif // BITCOIN_UTIL_IOPRIO_H diff --git a/src/validation.cpp b/src/validation.cpp index 8f75b2e30a..ee03c51775 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -4742,7 +4742,7 @@ VerifyDBResult CVerifyDB::VerifyDB( } CBlock block; // check level 0: read from disk - if (!chainstate.m_blockman.ReadBlockFromDisk(block, *pindex)) { + if (!chainstate.m_blockman.ReadBlockFromDisk(block, *pindex, /*lowprio=*/true)) { LogPrintf("Verification error: ReadBlockFromDisk failed at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); return VerifyDBResult::CORRUPTED_BLOCK_DB; } @@ -4808,7 +4808,7 @@ VerifyDBResult CVerifyDB::VerifyDB( m_notifications.progress(_("Verifying blocks…"), percentageDone, false); pindex = chainstate.m_chain.Next(pindex); CBlock block; - if (!chainstate.m_blockman.ReadBlockFromDisk(block, *pindex)) { + if (!chainstate.m_blockman.ReadBlockFromDisk(block, *pindex, /*lowprio=*/true)) { LogPrintf("Verification error: ReadBlockFromDisk failed at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); return VerifyDBResult::CORRUPTED_BLOCK_DB; } @@ -4837,7 +4837,7 @@ bool Chainstate::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& in AssertLockHeld(cs_main); // TODO: merge with ConnectBlock CBlock block; - if (!m_blockman.ReadBlockFromDisk(block, *pindex)) { + if (!m_blockman.ReadBlockFromDisk(block, *pindex, /*lowprio=*/true)) { LogError("ReplayBlock(): ReadBlockFromDisk failed at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); return false; } @@ -4895,7 +4895,7 @@ bool Chainstate::ReplayBlocks() while (pindexOld != pindexFork) { if (pindexOld->nHeight > 0) { // Never disconnect the genesis block. CBlock block; - if (!m_blockman.ReadBlockFromDisk(block, *pindexOld)) { + if (!m_blockman.ReadBlockFromDisk(block, *pindexOld, /*lowprio=*/true)) { LogError("RollbackBlock(): ReadBlockFromDisk() failed at %d, hash=%s\n", pindexOld->nHeight, pindexOld->GetBlockHash().ToString()); return false; }