diff --git a/cmake/randomx.cmake b/cmake/randomx.cmake index 83c8e5b3a..976f11862 100644 --- a/cmake/randomx.cmake +++ b/cmake/randomx.cmake @@ -101,25 +101,25 @@ if (WITH_RANDOMX) if (XMRIG_OS_WIN) list(APPEND SOURCES_CRYPTO - src/crypto/rx/Rx_win.cpp - src/hw/msr/Msr_win.cpp src/crypto/rx/RxFix_win.cpp + src/hw/msr/Msr_win.cpp ) elseif (XMRIG_OS_LINUX) list(APPEND SOURCES_CRYPTO - src/crypto/rx/Rx_linux.cpp - src/hw/msr/Msr_linux.cpp src/crypto/rx/RxFix_linux.cpp + src/hw/msr/Msr_linux.cpp ) endif() list(APPEND HEADERS_CRYPTO src/crypto/rx/RxFix.h + src/crypto/rx/RxMsr.h src/hw/msr/Msr.h src/hw/msr/MsrItem.h ) list(APPEND SOURCES_CRYPTO + src/crypto/rx/RxMsr.cpp src/hw/msr/Msr.cpp src/hw/msr/MsrItem.cpp ) diff --git a/src/backend/cpu/interfaces/ICpuInfo.h b/src/backend/cpu/interfaces/ICpuInfo.h index b772a92c4..7a8887239 100644 --- a/src/backend/cpu/interfaces/ICpuInfo.h +++ b/src/backend/cpu/interfaces/ICpuInfo.h @@ -1,6 +1,6 @@ /* XMRig - * Copyright (c) 2018-2020 SChernykh - * Copyright (c) 2016-2020 XMRig + * Copyright (c) 2018-2021 SChernykh + * Copyright (c) 2016-2021 XMRig * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -86,18 +86,21 @@ public: inline constexpr static bool is64bit() { return false; } # endif + virtual Arch arch() const = 0; virtual Assembly::Id assembly() const = 0; virtual bool has(Flag feature) const = 0; virtual bool hasAES() const = 0; virtual bool hasAVX() const = 0; virtual bool hasAVX2() const = 0; virtual bool hasBMI2() const = 0; + virtual bool hasCatL3() const = 0; virtual bool hasOneGbPages() const = 0; virtual bool hasXOP() const = 0; - virtual bool hasCatL3() const = 0; virtual bool isVM() const = 0; + virtual bool jccErratum() const = 0; virtual const char *backend() const = 0; virtual const char *brand() const = 0; + virtual const std::vector &units() const = 0; virtual CpuThreads threads(const Algorithm &algorithm, uint32_t limit) const = 0; virtual MsrMod msrMod() const = 0; virtual rapidjson::Value toJSON(rapidjson::Document &doc) const = 0; @@ -108,8 +111,6 @@ public: virtual size_t packages() const = 0; virtual size_t threads() const = 0; virtual Vendor vendor() const = 0; - virtual Arch arch() const = 0; - virtual bool jccErratum() const = 0; }; diff --git a/src/backend/cpu/platform/BasicCpuInfo.cpp b/src/backend/cpu/platform/BasicCpuInfo.cpp index 2a4dc829d..bcf77e927 100644 --- a/src/backend/cpu/platform/BasicCpuInfo.cpp +++ b/src/backend/cpu/platform/BasicCpuInfo.cpp @@ -1,7 +1,7 @@ /* XMRig * Copyright (c) 2017-2019 XMR-Stak , - * Copyright (c) 2018-2020 SChernykh - * Copyright (c) 2016-2020 XMRig + * Copyright (c) 2018-2021 SChernykh + * Copyright (c) 2016-2021 XMRig * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -190,6 +190,11 @@ xmrig::BasicCpuInfo::BasicCpuInfo() : m_flags.set(FLAG_CAT_L3, has_cat_l3()); m_flags.set(FLAG_VM, is_vm()); + m_units.resize(m_threads); + for (int32_t i = 0; i < static_cast(m_threads); ++i) { + m_units[i] = i; + } + # ifdef XMRIG_FEATURE_ASM if (hasAES()) { char vendor[13] = { 0 }; diff --git a/src/backend/cpu/platform/BasicCpuInfo.h b/src/backend/cpu/platform/BasicCpuInfo.h index edf119a2e..88760bac8 100644 --- a/src/backend/cpu/platform/BasicCpuInfo.h +++ b/src/backend/cpu/platform/BasicCpuInfo.h @@ -1,12 +1,7 @@ /* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2017-2019 XMR-Stak , - * Copyright 2018-2020 SChernykh - * Copyright 2016-2020 XMRig + * Copyright (c) 2017-2019 XMR-Stak , + * Copyright (c) 2018-2021 SChernykh + * Copyright (c) 2016-2021 XMRig * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -45,34 +40,36 @@ protected: CpuThreads threads(const Algorithm &algorithm, uint32_t limit) const override; rapidjson::Value toJSON(rapidjson::Document &doc) const override; - inline Assembly::Id assembly() const override { return m_assembly; } - inline bool has(Flag flag) const override { return m_flags.test(flag); } - inline bool hasAES() const override { return has(FLAG_AES); } - inline bool hasAVX() const override { return has(FLAG_AVX); } - inline bool hasAVX2() const override { return has(FLAG_AVX2); } - inline bool hasBMI2() const override { return has(FLAG_BMI2); } - inline bool hasOneGbPages() const override { return has(FLAG_PDPE1GB); } - inline bool hasXOP() const override { return has(FLAG_XOP); } - inline bool hasCatL3() const override { return has(FLAG_CAT_L3); } - inline bool isVM() const override { return has(FLAG_VM); } - inline const char *brand() const override { return m_brand; } - inline MsrMod msrMod() const override { return m_msrMod; } - inline size_t cores() const override { return 0; } - inline size_t L2() const override { return 0; } - inline size_t L3() const override { return 0; } - inline size_t nodes() const override { return 0; } - inline size_t packages() const override { return 1; } - inline size_t threads() const override { return m_threads; } - inline Vendor vendor() const override { return m_vendor; } - inline Arch arch() const override { return m_arch; } - inline bool jccErratum() const override { return m_jccErratum; } + inline Arch arch() const override { return m_arch; } + inline Assembly::Id assembly() const override { return m_assembly; } + inline bool has(Flag flag) const override { return m_flags.test(flag); } + inline bool hasAES() const override { return has(FLAG_AES); } + inline bool hasAVX() const override { return has(FLAG_AVX); } + inline bool hasAVX2() const override { return has(FLAG_AVX2); } + inline bool hasBMI2() const override { return has(FLAG_BMI2); } + inline bool hasCatL3() const override { return has(FLAG_CAT_L3); } + inline bool hasOneGbPages() const override { return has(FLAG_PDPE1GB); } + inline bool hasXOP() const override { return has(FLAG_XOP); } + inline bool isVM() const override { return has(FLAG_VM); } + inline bool jccErratum() const override { return m_jccErratum; } + inline const char *brand() const override { return m_brand; } + inline const std::vector &units() const override { return m_units; } + inline MsrMod msrMod() const override { return m_msrMod; } + inline size_t cores() const override { return 0; } + inline size_t L2() const override { return 0; } + inline size_t L3() const override { return 0; } + inline size_t nodes() const override { return 0; } + inline size_t packages() const override { return 1; } + inline size_t threads() const override { return m_threads; } + inline Vendor vendor() const override { return m_vendor; } protected: - char m_brand[64 + 6]{}; - size_t m_threads; - Vendor m_vendor = VENDOR_UNKNOWN; Arch m_arch = ARCH_UNKNOWN; bool m_jccErratum = false; + char m_brand[64 + 6]{}; + size_t m_threads; + std::vector m_units; + Vendor m_vendor = VENDOR_UNKNOWN; private: # ifndef XMRIG_ARM diff --git a/src/backend/cpu/platform/HwlocCpuInfo.cpp b/src/backend/cpu/platform/HwlocCpuInfo.cpp index 6a6841246..8b1c4536b 100644 --- a/src/backend/cpu/platform/HwlocCpuInfo.cpp +++ b/src/backend/cpu/platform/HwlocCpuInfo.cpp @@ -1,6 +1,6 @@ /* XMRig - * Copyright (c) 2018-2020 SChernykh - * Copyright (c) 2016-2020 XMRig + * Copyright (c) 2018-2021 SChernykh + * Copyright (c) 2016-2021 XMRig * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -170,7 +170,8 @@ xmrig::HwlocCpuInfo::HwlocCpuInfo() findCache(root, 2, 3, [this](hwloc_obj_t found) { this->m_cache[found->attr->cache.depth] += found->attr->cache.size; }); - m_threads = countByType(m_topology, HWLOC_OBJ_PU); + setThreads(countByType(m_topology, HWLOC_OBJ_PU)); + m_cores = countByType(m_topology, HWLOC_OBJ_CORE); m_nodes = std::max(hwloc_bitmap_weight(hwloc_topology_get_complete_nodeset(m_topology)), 1); m_packages = countByType(m_topology, HWLOC_OBJ_PACKAGE); @@ -395,3 +396,20 @@ void xmrig::HwlocCpuInfo::processTopLevelCache(hwloc_obj_t cache, const Algorith } # endif } + + +void xmrig::HwlocCpuInfo::setThreads(size_t threads) +{ + m_threads = threads; + + if (m_units.size() != m_threads) { + m_units.resize(m_threads); + } + + hwloc_obj_t pu = nullptr; + size_t i = 0; + + while ((pu = hwloc_get_next_obj_by_type(m_topology, HWLOC_OBJ_PU, pu)) != nullptr) { + m_units[i++] = static_cast(pu->os_index); + } +} diff --git a/src/backend/cpu/platform/HwlocCpuInfo.h b/src/backend/cpu/platform/HwlocCpuInfo.h index 6a07a0374..69b67a305 100644 --- a/src/backend/cpu/platform/HwlocCpuInfo.h +++ b/src/backend/cpu/platform/HwlocCpuInfo.h @@ -1,6 +1,6 @@ /* XMRig - * Copyright (c) 2018-2020 SChernykh - * Copyright (c) 2016-2020 XMRig + * Copyright (c) 2018-2021 SChernykh + * Copyright (c) 2016-2021 XMRig * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -66,7 +66,7 @@ protected: private: CpuThreads allThreads(const Algorithm &algorithm, uint32_t limit) const; void processTopLevelCache(hwloc_obj_t obj, const Algorithm &algorithm, CpuThreads &threads, size_t limit) const; - + void setThreads(size_t threads); static uint32_t m_features; diff --git a/src/crypto/rx/Rx.cpp b/src/crypto/rx/Rx.cpp index 99ca6b0ad..a485b382f 100644 --- a/src/crypto/rx/Rx.cpp +++ b/src/crypto/rx/Rx.cpp @@ -27,8 +27,9 @@ #include "crypto/randomx/aes_hash.hpp" -#ifdef XMRIG_FIX_RYZEN +#ifdef XMRIG_FEATURE_MSR # include "crypto/rx/RxFix.h" +# include "crypto/rx/RxMsr.h" #endif @@ -39,8 +40,6 @@ class RxPrivate; static bool osInitialized = false; -static bool msrInitialized = false; -static bool msrEnabled = false; static RxPrivate *d_ptr = nullptr; @@ -70,9 +69,9 @@ xmrig::RxDataset *xmrig::Rx::dataset(const Job &job, uint32_t nodeId) void xmrig::Rx::destroy() { - if (osInitialized) { - msrDestroy(); - } +# ifdef XMRIG_FEATURE_MSR + RxMsr::destroy(); +# endif delete d_ptr; @@ -90,11 +89,9 @@ template bool xmrig::Rx::init(const T &seed, const RxConfig &config, const CpuConfig &cpu) { if (seed.algorithm().family() != Algorithm::RANDOM_X) { - if (msrInitialized) { - msrDestroy(); - msrInitialized = false; - msrEnabled = false; - } +# ifdef XMRIG_FEATURE_MSR + RxMsr::destroy(); +# endif return true; } @@ -103,10 +100,11 @@ bool xmrig::Rx::init(const T &seed, const RxConfig &config, const CpuConfig &cpu randomx_set_huge_pages_jit(cpu.isHugePagesJit()); randomx_set_optimized_dataset_init(config.initDatasetAVX2()); - if (!msrInitialized) { - msrEnabled = msrInit(config, cpu.threads().get(seed.algorithm()).data()); - msrInitialized = true; +# ifdef XMRIG_FEATURE_MSR + if (!RxMsr::isInitialized()) { + RxMsr::init(config, cpu.threads().get(seed.algorithm()).data()); } +# endif if (!osInitialized) { # ifdef XMRIG_FIX_RYZEN @@ -139,24 +137,7 @@ bool xmrig::Rx::isReady(const T &seed) #ifdef XMRIG_FEATURE_MSR bool xmrig::Rx::isMSR() { - return msrEnabled; -} -#else -bool xmrig::Rx::msrInit(const RxConfig &, const std::vector &) -{ - return false; -} - - -void xmrig::Rx::msrDestroy() -{ -} -#endif - - -#ifndef XMRIG_FIX_RYZEN -void xmrig::Rx::setupMainLoopExceptionFrame() -{ + return RxMsr::isEnabled(); } #endif diff --git a/src/crypto/rx/Rx.h b/src/crypto/rx/Rx.h index a93f7449f..af8047062 100644 --- a/src/crypto/rx/Rx.h +++ b/src/crypto/rx/Rx.h @@ -57,10 +57,6 @@ public: # else static constexpr bool isMSR() { return false; } # endif - -private: - static bool msrInit(const RxConfig &config, const std::vector& threads); - static void msrDestroy(); }; diff --git a/src/crypto/rx/RxMsr.cpp b/src/crypto/rx/RxMsr.cpp new file mode 100644 index 000000000..6083e9b33 --- /dev/null +++ b/src/crypto/rx/RxMsr.cpp @@ -0,0 +1,184 @@ +/* XMRig + * Copyright (c) 2018-2019 tevador + * Copyright (c) 2018-2021 SChernykh + * Copyright (c) 2016-2021 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "crypto/rx/RxMsr.h" +#include "backend/cpu/Cpu.h" +#include "backend/cpu/CpuThread.h" +#include "base/io/log/Log.h" +#include "base/tools/Chrono.h" +#include "crypto/rx/RxConfig.h" +#include "hw/msr/Msr.h" + + +#include +#include + + +namespace xmrig { + + +bool RxMsr::m_enabled = false; +bool RxMsr::m_initialized = false; + + +static MsrItems items; + + +#ifdef XMRIG_OS_WIN +static constexpr inline int32_t get_cpu(int32_t) { return -1; } +#else +static constexpr inline int32_t get_cpu(int32_t cpu) { return cpu; } +#endif + + +static bool wrmsr(const MsrItems &preset, const std::vector &threads, bool cache_qos, bool save) +{ + auto msr = Msr::get(); + if (!msr) { + return false; + } + + if (save) { + items.reserve(preset.size()); + + for (const auto &i : preset) { + auto item = msr->read(i.reg()); + if (!item.isValid()) { + items.clear(); + + return false; + } + + LOG_VERBOSE("%s " CYAN_BOLD("0x%08" PRIx32) CYAN(":0x%016" PRIx64) CYAN_BOLD(" -> 0x%016" PRIx64), Msr::tag(), i.reg(), item.value(), MsrItem::maskedValue(item.value(), i.value(), i.mask())); + + items.emplace_back(item); + } + } + + // Which CPU cores will have access to the full L3 cache + std::set cacheEnabled; + bool cacheQoSDisabled = threads.empty(); + + if (cache_qos) { + const auto &units = Cpu::info()->units(); + + for (const auto &t : threads) { + const auto affinity = static_cast(t.affinity()); + + // If some thread has no affinity or wrong affinity, disable cache QoS + if (affinity < 0 || std::find(units.begin(), units.end(), affinity) == units.end()) { + cacheQoSDisabled = true; + + LOG_WARN("%s " YELLOW_BOLD("cache QoS can only be enabled when all mining threads have affinity set"), Msr::tag()); + break; + } + + cacheEnabled.insert(affinity); + } + } + + return msr->write([&msr, &preset, cache_qos, &cacheEnabled, cacheQoSDisabled](int32_t cpu) { + for (const auto &item : preset) { + if (!msr->write(item, get_cpu(cpu))) { + return false; + } + } + + if (!cache_qos) { + return true; + } + + // Assign Class Of Service 0 to current CPU core (default, full L3 cache available) + if (cacheQoSDisabled || cacheEnabled.count(cpu)) { + return msr->write(0xC8F, 0, get_cpu(cpu)); + } + + // Disable L3 cache for Class Of Service 1 + if (!msr->write(0xC91, 0, get_cpu(cpu))) { + // Some CPUs don't let set it to all zeros + if (!msr->write(0xC91, 1, get_cpu(cpu))) { + return false; + } + } + + // Assign Class Of Service 1 to current CPU core + return msr->write(0xC8F, 1ULL << 32, get_cpu(cpu)); + }); +} + + +} // namespace xmrig + + +bool xmrig::RxMsr::init(const RxConfig &config, const std::vector &threads) +{ + if (isInitialized()) { + return isEnabled(); + } + + m_initialized = true; + m_enabled = false; + + const auto &preset = config.msrPreset(); + if (preset.empty()) { + return false; + } + + const uint64_t ts = Chrono::steadyMSecs(); + bool cache_qos = config.cacheQoS(); + + if (cache_qos && !Cpu::info()->hasCatL3()) { + if (!threads.empty()) { + LOG_WARN("%s " YELLOW_BOLD("this CPU doesn't support cat_l3, cache QoS is unavailable"), Msr::tag()); + } + + cache_qos = false; + } + + if ((m_enabled = wrmsr(preset, threads, cache_qos, config.rdmsr()))) { + LOG_NOTICE("%s " GREEN_BOLD("register values for \"%s\" preset have been set successfully") BLACK_BOLD(" (%" PRIu64 " ms)"), Msr::tag(), config.msrPresetName(), Chrono::steadyMSecs() - ts); + } + else { + LOG_ERR("%s " RED_BOLD("FAILED TO APPLY MSR MOD, HASHRATE WILL BE LOW"), Msr::tag()); + } + + return isEnabled(); +} + + +void xmrig::RxMsr::destroy() +{ + if (!isInitialized()) { + return; + } + + m_initialized = false; + m_enabled = false; + + if (items.empty()) { + return; + } + + const uint64_t ts = Chrono::steadyMSecs(); + + if (!wrmsr(items, std::vector(), true, false)) { + LOG_ERR("%s " RED_BOLD("failed to restore initial state" BLACK_BOLD(" (%" PRIu64 " ms)")), Msr::tag(), Chrono::steadyMSecs() - ts); + } +} diff --git a/src/crypto/rx/RxMsr.h b/src/crypto/rx/RxMsr.h new file mode 100644 index 000000000..870e298cb --- /dev/null +++ b/src/crypto/rx/RxMsr.h @@ -0,0 +1,53 @@ +/* XMRig + * Copyright (c) 2018-2019 tevador + * Copyright (c) 2018-2021 SChernykh + * Copyright (c) 2016-2021 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef XMRIG_RXMSR_H +#define XMRIG_RXMSR_H + + +#include + + +namespace xmrig +{ + + +class CpuThread; +class RxConfig; + + +class RxMsr +{ +public: + static inline bool isEnabled() { return m_enabled; } + static inline bool isInitialized() { return m_initialized; } + + static bool init(const RxConfig &config, const std::vector &threads); + static void destroy(); + +private: + static bool m_enabled; + static bool m_initialized; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_RXMSR_H */ diff --git a/src/crypto/rx/Rx_linux.cpp b/src/crypto/rx/Rx_linux.cpp deleted file mode 100644 index e73980026..000000000 --- a/src/crypto/rx/Rx_linux.cpp +++ /dev/null @@ -1,272 +0,0 @@ -/* XMRig - * Copyright (c) 2018-2019 tevador - * Copyright (c) 2000 Transmeta Corporation - * Copyright (c) 2004-2008 H. Peter Anvin - * Copyright (c) 2018-2020 SChernykh - * Copyright (c) 2016-2020 XMRig , - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - -#include "crypto/rx/Rx.h" -#include "backend/cpu/Cpu.h" -#include "backend/cpu/CpuThread.h" -#include "base/io/log/Log.h" -#include "base/tools/Chrono.h" -#include "crypto/rx/RxConfig.h" - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace xmrig { - - -static const char *tag = YELLOW_BG_BOLD(WHITE_BOLD_S " msr ") " "; -static MsrItems savedState; - - -static inline int dir_filter(const struct dirent *dirp) -{ - return isdigit(dirp->d_name[0]) ? 1 : 0; -} - - -bool rdmsr_on_cpu(uint32_t reg, uint32_t cpu, uint64_t &value) -{ - char msr_file_name[64]{}; - - sprintf(msr_file_name, "/dev/cpu/%u/msr", cpu); - int fd = open(msr_file_name, O_RDONLY); - if (fd < 0) { - return false; - } - - const bool success = pread(fd, &value, sizeof value, reg) == sizeof value; - - close(fd); - - return success; -} - - -static MsrItem rdmsr(uint32_t reg) -{ - uint64_t value = 0; - if (!rdmsr_on_cpu(reg, 0, value)) { - LOG_WARN(CLEAR "%s" YELLOW_BOLD_S "cannot read MSR 0x%08" PRIx32, tag, reg); - - return {}; - } - - return { reg, value }; -} - - -static uint64_t get_masked_value(uint64_t old_value, uint64_t new_value, uint64_t mask) -{ - return (new_value & mask) | (old_value & ~mask); -} - - -static bool wrmsr_on_cpu(uint32_t reg, uint32_t cpu, uint64_t value, uint64_t mask) -{ - // If a bit in mask is set to 1, use new value, otherwise use old value - if (mask != MsrItem::kNoMask) { - uint64_t old_value; - if (rdmsr_on_cpu(reg, cpu, old_value)) { - value = get_masked_value(old_value, value, mask); - } - } - - char msr_file_name[64]{}; - - sprintf(msr_file_name, "/dev/cpu/%u/msr", cpu); - int fd = open(msr_file_name, O_WRONLY); - if (fd < 0) { - return false; - } - - const bool success = pwrite(fd, &value, sizeof value, reg) == sizeof value; - - close(fd); - - return success; -} - - -template -static bool wrmsr_on_all_cpus(uint32_t reg, uint64_t value, uint64_t mask, T&& callback) -{ - struct dirent **namelist; - int dir_entries = scandir("/dev/cpu", &namelist, dir_filter, 0); - int errors = 0; - - while (dir_entries--) { - if (!callback(reg, strtoul(namelist[dir_entries]->d_name, nullptr, 10), value, mask)) { - ++errors; - } - - free(namelist[dir_entries]); - } - - free(namelist); - - if (errors) { - LOG_WARN(CLEAR "%s" YELLOW_BOLD_S "cannot set MSR 0x%08" PRIx32 " to 0x%08" PRIx64, tag, reg, value); - } - - return errors == 0; -} - - -static bool wrmsr_modprobe() -{ - if (system("/sbin/modprobe msr allow_writes=on > /dev/null 2>&1") != 0) { - LOG_WARN(CLEAR "%s" YELLOW_BOLD_S "msr kernel module is not available", tag); - - return false; - } - - return true; -} - - -static bool wrmsr(const MsrItems& preset, const std::vector& threads, bool cache_qos, bool save) -{ - if (!wrmsr_modprobe()) { - return false; - } - - if (save) { - for (const auto &i : preset) { - auto item = rdmsr(i.reg()); - LOG_VERBOSE(CLEAR "%s" CYAN_BOLD("0x%08" PRIx32) CYAN(":0x%016" PRIx64) CYAN_BOLD(" -> 0x%016" PRIx64), tag, i.reg(), item.value(), get_masked_value(item.value(), i.value(), i.mask())); - - if (item.isValid()) { - savedState.emplace_back(item); - } - } - } - - for (const auto &i : preset) { - if (!wrmsr_on_all_cpus(i.reg(), i.value(), i.mask(), [](uint32_t reg, uint32_t cpu, uint64_t value, uint64_t mask) { return wrmsr_on_cpu(reg, cpu, value, mask); })) { - return false; - } - } - - const uint32_t n = Cpu::info()->threads(); - - // Which CPU cores will have access to the full L3 cache - std::vector cacheEnabled(n, false); - bool cacheQoSDisabled = threads.empty(); - - for (const CpuThread& t : threads) { - // If some thread has no affinity or wrong affinity, disable cache QoS - if ((t.affinity() < 0) || (t.affinity() >= n)) { - cacheQoSDisabled = true; - if (cache_qos) { - LOG_WARN(CLEAR "%s" YELLOW_BOLD_S "Cache QoS can only be enabled when all mining threads have affinity set", tag); - } - break; - } - - cacheEnabled[t.affinity()] = true; - } - - if (cache_qos && !Cpu::info()->hasCatL3()) { - if (!threads.empty()) { - LOG_WARN(CLEAR "%s" YELLOW_BOLD_S "This CPU doesn't support cat_l3, cache QoS is unavailable", tag); - } - cache_qos = false; - } - - bool result = true; - - if (cache_qos) { - result = wrmsr_on_all_cpus(0xC8F, 0, MsrItem::kNoMask, [&cacheEnabled, cacheQoSDisabled](uint32_t, uint32_t cpu, uint64_t, uint64_t) { - if (cacheQoSDisabled || (cpu >= cacheEnabled.size()) || cacheEnabled[cpu]) { - // Assign Class Of Service 0 to current CPU core (default, full L3 cache available) - if (!wrmsr_on_cpu(0xC8F, cpu, 0, MsrItem::kNoMask)) { - return false; - } - } - else { - // Disable L3 cache for Class Of Service 1 - if (!wrmsr_on_cpu(0xC91, cpu, 0, MsrItem::kNoMask)) { - // Some CPUs don't let set it to all zeros - if (!wrmsr_on_cpu(0xC91, cpu, 1, MsrItem::kNoMask)) { - return false; - } - } - - // Assign Class Of Service 1 to current CPU core - if (!wrmsr_on_cpu(0xC8F, cpu, 1ULL << 32, MsrItem::kNoMask)) { - return false; - } - } - return true; - }); - } - - return result; -} - - -} // namespace xmrig - - -bool xmrig::Rx::msrInit(const RxConfig &config, const std::vector &threads) -{ - const auto &preset = config.msrPreset(); - if (preset.empty()) { - return false; - } - - const uint64_t ts = Chrono::steadyMSecs(); - - if (wrmsr(preset, threads, config.cacheQoS(), config.rdmsr())) { - LOG_NOTICE(CLEAR "%s" GREEN_BOLD_S "register values for \"%s\" preset have been set successfully" BLACK_BOLD(" (%" PRIu64 " ms)"), tag, config.msrPresetName(), Chrono::steadyMSecs() - ts); - - return true; - } - - - LOG_ERR(CLEAR "%s" RED_BOLD_S "FAILED TO APPLY MSR MOD, HASHRATE WILL BE LOW", tag); - - return false; -} - - -void xmrig::Rx::msrDestroy() -{ - if (savedState.empty()) { - return; - } - - const uint64_t ts = Chrono::steadyMSecs(); - - if (!wrmsr(savedState, std::vector(), true, false)) { - LOG_ERR(CLEAR "%s" RED_BOLD_S "failed to restore initial state" BLACK_BOLD(" (%" PRIu64 " ms)"), tag, Chrono::steadyMSecs() - ts); - } -} diff --git a/src/crypto/rx/Rx_win.cpp b/src/crypto/rx/Rx_win.cpp deleted file mode 100644 index 36e13819b..000000000 --- a/src/crypto/rx/Rx_win.cpp +++ /dev/null @@ -1,386 +0,0 @@ -/* XMRig - * Copyright (c) 2018-2019 tevador - * Copyright (c) 2000 Transmeta Corporation - * Copyright (c) 2004-2008 H. Peter Anvin - * Copyright (c) 2007-2009 hiyohiyo , - * Copyright (c) 2018-2020 SChernykh - * Copyright (c) 2016-2020 XMRig , - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - -#include "crypto/rx/Rx.h" -#include "backend/cpu/Cpu.h" -#include "backend/cpu/CpuThread.h" -#include "base/io/log/Log.h" -#include "base/kernel/Platform.h" -#include "base/tools/Chrono.h" -#include "crypto/rx/RxConfig.h" - - -#include -#include -#include -#include - - -#define SERVICE_NAME L"WinRing0_1_2_0" - - -namespace xmrig { - - -static bool reuseDriver = false; -static const char *tag = YELLOW_BG_BOLD(WHITE_BOLD_S " msr ") " "; -static MsrItems savedState; - - -static SC_HANDLE hManager; -static SC_HANDLE hService; - - -static bool wrmsr_uninstall_driver() -{ - if (!hService) { - return true; - } - - bool result = true; - - if (!reuseDriver) { - SERVICE_STATUS serviceStatus; - - if (!ControlService(hService, SERVICE_CONTROL_STOP, &serviceStatus)) { - result = false; - } - - if (!DeleteService(hService)) { - LOG_ERR(CLEAR "%s" RED_S "failed to remove WinRing0 driver, error %u", tag, GetLastError()); - result = false; - } - } - - CloseServiceHandle(hService); - hService = nullptr; - - return result; -} - - -static HANDLE wrmsr_install_driver() -{ - DWORD err = 0; - - hManager = OpenSCManager(nullptr, nullptr, SC_MANAGER_ALL_ACCESS); - if (!hManager) { - err = GetLastError(); - - if (err == ERROR_ACCESS_DENIED) { - LOG_WARN(CLEAR "%s" YELLOW_BOLD_S "to write MSR registers Administrator privileges required.", tag); - } - else { - LOG_ERR(CLEAR "%s" RED_S "failed to open service control manager, error %u", tag, err); - } - - return nullptr; - } - - std::vector dir; - dir.resize(MAX_PATH); - do { - dir.resize(dir.size() * 2); - GetModuleFileNameW(nullptr, dir.data(), dir.size()); - err = GetLastError(); - } while (err == ERROR_INSUFFICIENT_BUFFER); - - if (err != ERROR_SUCCESS) { - LOG_ERR(CLEAR "%s" RED_S "failed to get path to driver, error %u", tag, err); - return nullptr; - } - - for (auto it = dir.end() - 1; it != dir.begin(); --it) { - if ((*it == L'\\') || (*it == L'/')) { - ++it; - *it = L'\0'; - break; - } - } - - std::wstring driverPath = dir.data(); - driverPath += L"WinRing0x64.sys"; - - hService = OpenServiceW(hManager, SERVICE_NAME, SERVICE_ALL_ACCESS); - if (hService) { - LOG_WARN(CLEAR "%s" YELLOW("service ") YELLOW_BOLD("WinRing0_1_2_0") YELLOW(" already exists"), tag); - - SERVICE_STATUS status; - const auto rc = QueryServiceStatus(hService, &status); - - if (rc) { - DWORD dwBytesNeeded; - - QueryServiceConfigA(hService, nullptr, 0, &dwBytesNeeded); - if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - std::vector buffer(dwBytesNeeded); - auto config = reinterpret_cast(buffer.data()); - - if (QueryServiceConfigA(hService, config, buffer.size(), &dwBytesNeeded)) { - LOG_INFO(CLEAR "%s" YELLOW("service path: ") YELLOW_BOLD("\"%s\""), tag, config->lpBinaryPathName); - } - } - } - - if (rc && status.dwCurrentState == SERVICE_RUNNING) { - reuseDriver = true; - } - else if (!wrmsr_uninstall_driver()) { - return nullptr; - } - } - - if (!reuseDriver) { - hService = CreateServiceW(hManager, SERVICE_NAME, SERVICE_NAME, SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, driverPath.c_str(), nullptr, nullptr, nullptr, nullptr, nullptr); - if (!hService) { - LOG_ERR(CLEAR "%s" RED_S "failed to install WinRing0 driver, error %u", tag, GetLastError()); - - return nullptr; - } - - if (!StartService(hService, 0, nullptr)) { - err = GetLastError(); - if (err != ERROR_SERVICE_ALREADY_RUNNING) { - if (err == ERROR_FILE_NOT_FOUND) { - LOG_ERR(CLEAR "%s" RED("failed to start WinRing0 driver: ") RED_BOLD("\"WinRing0x64.sys not found\""), tag); - } - else { - LOG_ERR(CLEAR "%s" RED_S "failed to start WinRing0 driver, error %u", tag, err); - } - - wrmsr_uninstall_driver(); - - return nullptr; - } - } - } - - HANDLE hDriver = CreateFileW(L"\\\\.\\" SERVICE_NAME, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); - if (!hDriver) { - LOG_ERR(CLEAR "%s" RED_S "failed to connect to WinRing0 driver, error %u", tag, GetLastError()); - - return nullptr; - } - - return hDriver; -} - - -#define IOCTL_READ_MSR CTL_CODE(40000, 0x821, METHOD_BUFFERED, FILE_ANY_ACCESS) -#define IOCTL_WRITE_MSR CTL_CODE(40000, 0x822, METHOD_BUFFERED, FILE_ANY_ACCESS) - - -static bool rdmsr(HANDLE driver, uint32_t reg, uint64_t &value) -{ - DWORD size = 0; - - return DeviceIoControl(driver, IOCTL_READ_MSR, ®, sizeof(reg), &value, sizeof(value), &size, nullptr) != 0; -} - - -static MsrItem rdmsr(HANDLE driver, uint32_t reg) -{ - uint64_t value = 0; - if (!rdmsr(driver, reg, value)) { - LOG_WARN(CLEAR "%s" YELLOW_BOLD_S "cannot read MSR 0x%08" PRIx32, tag, reg); - - return {}; - } - - return { reg, value }; -} - - -static uint64_t get_masked_value(uint64_t old_value, uint64_t new_value, uint64_t mask) -{ - return (new_value & mask) | (old_value & ~mask); -} - - -static bool wrmsr(HANDLE driver, uint32_t reg, uint64_t value, uint64_t mask) -{ - struct { - uint32_t reg = 0; - uint32_t value[2]{}; - } input; - - static_assert(sizeof(input) == 12, "Invalid struct size for WinRing0 driver"); - - // If a bit in mask is set to 1, use new value, otherwise use old value - if (mask != MsrItem::kNoMask) { - uint64_t old_value; - if (rdmsr(driver, reg, old_value)) { - value = get_masked_value(old_value, value, mask); - } - } - - input.reg = reg; - *(reinterpret_cast(input.value)) = value; - - DWORD output; - DWORD k; - - if (!DeviceIoControl(driver, IOCTL_WRITE_MSR, &input, sizeof(input), &output, sizeof(output), &k, nullptr)) { - LOG_WARN(CLEAR "%s" YELLOW_BOLD_S "cannot set MSR 0x%08" PRIx32 " to 0x%08" PRIx64, tag, reg, value); - - return false; - } - - return true; -} - - -static bool wrmsr(const MsrItems &preset, const std::vector& threads, bool cache_qos, bool save) -{ - bool success = true; - - HANDLE driver = wrmsr_install_driver(); - if (!driver) { - wrmsr_uninstall_driver(); - - if (hManager) { - CloseServiceHandle(hManager); - } - - return false; - } - - if (save) { - for (const auto &i : preset) { - auto item = rdmsr(driver, i.reg()); - LOG_VERBOSE(CLEAR "%s" CYAN_BOLD("0x%08" PRIx32) CYAN(":0x%016" PRIx64) CYAN_BOLD(" -> 0x%016" PRIx64), tag, i.reg(), item.value(), get_masked_value(item.value(), i.value(), i.mask())); - - if (item.isValid()) { - savedState.emplace_back(item); - } - } - } - - const uint32_t n = Cpu::info()->threads(); - - // Which CPU cores will have access to the full L3 cache - std::vector cacheEnabled(n, false); - bool cacheQoSDisabled = threads.empty(); - - for (const CpuThread& t : threads) { - // If some thread has no affinity or wrong affinity, disable cache QoS - if ((t.affinity() < 0) || (t.affinity() >= n)) { - cacheQoSDisabled = true; - if (cache_qos) { - LOG_WARN(CLEAR "%s" YELLOW_BOLD_S "Cache QoS can only be enabled when all mining threads have affinity set", tag); - } - break; - } - - cacheEnabled[t.affinity()] = true; - } - - if (cache_qos && !Cpu::info()->hasCatL3()) { - if (!threads.empty()) { - LOG_WARN(CLEAR "%s" YELLOW_BOLD_S "This CPU doesn't support cat_l3, cache QoS is unavailable", tag); - } - cache_qos = false; - } - - std::thread wrmsr_thread([n, driver, &preset, &cacheEnabled, cache_qos, cacheQoSDisabled, &success]() { - for (uint32_t i = 0; i < n; ++i) { - if (!Platform::setThreadAffinity(i)) { - continue; - } - - for (const auto &i : preset) { - success &= wrmsr(driver, i.reg(), i.value(), i.mask()); - } - - if (cache_qos) { - if (cacheQoSDisabled || cacheEnabled[i]) { - // Assign Class Of Service 0 to current CPU core (default, full L3 cache available) - success &= wrmsr(driver, 0xC8F, 0, MsrItem::kNoMask); - } - else { - // Disable L3 cache for Class Of Service 1 - if (!wrmsr(driver, 0xC91, 0, MsrItem::kNoMask)) { - // Some CPUs don't let set it to all zeros - if (!wrmsr(driver, 0xC91, 1, MsrItem::kNoMask)) { - success = false; - } - } - - // Assign Class Of Service 1 to current CPU core - success &= wrmsr(driver, 0xC8F, 1ULL << 32, MsrItem::kNoMask); - } - } - - if (!success) { - break; - } - } - }); - - wrmsr_thread.join(); - - CloseHandle(driver); - - wrmsr_uninstall_driver(); - CloseServiceHandle(hManager); - - return success; -} - - -} // namespace xmrig - - -bool xmrig::Rx::msrInit(const RxConfig &config, const std::vector& threads) -{ - const auto &preset = config.msrPreset(); - if (preset.empty()) { - return false; - } - - const uint64_t ts = Chrono::steadyMSecs(); - - if (wrmsr(preset, threads, config.cacheQoS(), config.rdmsr())) { - LOG_NOTICE(CLEAR "%s" GREEN_BOLD_S "register values for \"%s\" preset has been set successfully" BLACK_BOLD(" (%" PRIu64 " ms)"), tag, config.msrPresetName(), Chrono::steadyMSecs() - ts); - - return true; - } - - LOG_ERR(CLEAR "%s" RED_BOLD_S "FAILED TO APPLY MSR MOD, HASHRATE WILL BE LOW", tag); - - return false; -} - - -void xmrig::Rx::msrDestroy() -{ - if (savedState.empty()) { - return; - } - - const uint64_t ts = Chrono::steadyMSecs(); - - if (!wrmsr(savedState, std::vector(), true, false)) { - LOG_ERR(CLEAR "%s" RED_BOLD_S "failed to restore initial state" BLACK_BOLD(" (%" PRIu64 " ms)"), tag, Chrono::steadyMSecs() - ts); - } -} diff --git a/src/hw/msr/Msr.cpp b/src/hw/msr/Msr.cpp index 68c60e6ed..1fed87760 100644 --- a/src/hw/msr/Msr.cpp +++ b/src/hw/msr/Msr.cpp @@ -55,6 +55,24 @@ std::shared_ptr xmrig::Msr::get() } +bool xmrig::Msr::write(uint32_t reg, uint64_t value, int32_t cpu, uint64_t mask, bool verbose) +{ + if (mask != MsrItem::kNoMask) { + uint64_t old_value; + if (rdmsr(reg, cpu, old_value)) { + value = MsrItem::maskedValue(old_value, value, mask); + } + } + + const bool result = wrmsr(reg, value, cpu); + if (!result && verbose) { + LOG_WARN("%s " YELLOW_BOLD("cannot set MSR 0x%08" PRIx32 " to 0x%016" PRIx64), tag(), reg, value); + } + + return result; +} + + xmrig::MsrItem xmrig::Msr::read(uint32_t reg, int32_t cpu, bool verbose) const { uint64_t value = 0; diff --git a/src/hw/msr/Msr.h b/src/hw/msr/Msr.h index 5f325a251..3bd88dd56 100644 --- a/src/hw/msr/Msr.h +++ b/src/hw/msr/Msr.h @@ -24,6 +24,7 @@ #include "hw/msr/MsrItem.h" +#include #include @@ -39,17 +40,24 @@ class Msr public: XMRIG_DISABLE_COPY_MOVE(Msr) + using Callback = std::function; + Msr(); ~Msr(); static const char *tag(); static std::shared_ptr get(); + inline bool write(const MsrItem &item, int32_t cpu = -1, bool verbose = true) { return write(item.reg(), item.value(), cpu, item.mask(), verbose); } + bool isAvailable() const; + bool write(uint32_t reg, uint64_t value, int32_t cpu = -1, uint64_t mask = MsrItem::kNoMask, bool verbose = true); + bool write(Callback &&callback); MsrItem read(uint32_t reg, int32_t cpu = -1, bool verbose = true) const; private: bool rdmsr(uint32_t reg, int32_t cpu, uint64_t &value) const; + bool wrmsr(uint32_t reg, uint64_t value, int32_t cpu); MsrPrivate *d_ptr = nullptr; }; diff --git a/src/hw/msr/MsrItem.h b/src/hw/msr/MsrItem.h index ad3c9d393..00c0416ae 100644 --- a/src/hw/msr/MsrItem.h +++ b/src/hw/msr/MsrItem.h @@ -46,6 +46,11 @@ public: inline uint64_t value() const { return m_value; } inline uint64_t mask() const { return m_mask; } + static inline uint64_t maskedValue(uint64_t old_value, uint64_t new_value, uint64_t mask) + { + return (new_value & mask) | (old_value & ~mask); + } + rapidjson::Value toJSON(rapidjson::Document &doc) const; String toString() const; diff --git a/src/hw/msr/Msr_linux.cpp b/src/hw/msr/Msr_linux.cpp index 02a29c72f..9053c32f1 100644 --- a/src/hw/msr/Msr_linux.cpp +++ b/src/hw/msr/Msr_linux.cpp @@ -19,6 +19,7 @@ #include "hw/msr/Msr.h" #include "3rdparty/fmt/core.h" +#include "backend/cpu/Cpu.h" #include "base/io/log/Log.h" @@ -31,8 +32,6 @@ #include #include #include -#include -#include namespace xmrig { @@ -70,6 +69,20 @@ bool xmrig::Msr::isAvailable() const } +bool xmrig::Msr::write(Callback &&callback) +{ + const auto &units = Cpu::info()->units(); + + for (int32_t pu : units) { + if (!callback(pu)) { + return false; + } + } + + return true; +} + + bool xmrig::Msr::rdmsr(uint32_t reg, int32_t cpu, uint64_t &value) const { const auto name = fmt::format("/dev/cpu/{}/msr", cpu < 0 ? 0 : cpu); @@ -84,3 +97,20 @@ bool xmrig::Msr::rdmsr(uint32_t reg, int32_t cpu, uint64_t &value) const return success; } + + +bool xmrig::Msr::wrmsr(uint32_t reg, uint64_t value, int32_t cpu) +{ + const auto name = fmt::format("/dev/cpu/{}/msr", cpu < 0 ? 0 : cpu); + int fd = open(name.c_str(), O_WRONLY); + + if (fd < 0) { + return false; + } + + const bool success = pwrite(fd, &value, sizeof value, reg) == sizeof value; + + close(fd); + + return success; +} diff --git a/src/hw/msr/Msr_win.cpp b/src/hw/msr/Msr_win.cpp index d055785cd..654f67783 100644 --- a/src/hw/msr/Msr_win.cpp +++ b/src/hw/msr/Msr_win.cpp @@ -18,10 +18,13 @@ #include "hw/msr/Msr.h" +#include "backend/cpu/Cpu.h" #include "base/io/log/Log.h" +#include "base/kernel/Platform.h" #include +#include #include #include @@ -196,9 +199,57 @@ bool xmrig::Msr::isAvailable() const } -bool xmrig::Msr::rdmsr(uint32_t reg, int32_t, uint64_t &value) const +bool xmrig::Msr::write(Callback &&callback) { + const auto &units = Cpu::info()->units(); + bool success = false; + + std::thread thread([&callback, &units, &success]() { + for (int32_t pu : units) { + if (!Platform::setThreadAffinity(pu)) { + continue; + } + + if (!callback(pu)) { + return; + } + } + + success = true; + }); + + thread.join(); + + return success; +} + + +bool xmrig::Msr::rdmsr(uint32_t reg, int32_t cpu, uint64_t &value) const +{ + assert(cpu < 0); + DWORD size = 0; return DeviceIoControl(d_ptr->driver, IOCTL_READ_MSR, ®, sizeof(reg), &value, sizeof(value), &size, nullptr) != 0; } + + +bool xmrig::Msr::wrmsr(uint32_t reg, uint64_t value, int32_t cpu) +{ + assert(cpu < 0); + + struct { + uint32_t reg = 0; + uint32_t value[2]{}; + } input; + + static_assert(sizeof(input) == 12, "Invalid struct size for WinRing0 driver"); + + input.reg = reg; + *(reinterpret_cast(input.value)) = value; + + DWORD output; + DWORD k; + + return DeviceIoControl(d_ptr->driver, IOCTL_WRITE_MSR, &input, sizeof(input), &output, sizeof(output), &k, nullptr) != 0; +}