minerd: MSR support

This commit is contained in:
x
2025-12-23 13:58:16 +00:00
parent 6ccae14e68
commit 8aef372b11
4 changed files with 237 additions and 6 deletions

View File

@@ -21,6 +21,9 @@ secure = false
# PoW miner number of threads to use
#threads = 4
# Assign full L3 cache to CPU
cache_qos = false
# Polling rate to ask darkfid for mining jobs
#polling_rate = 2

View File

@@ -16,8 +16,190 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/// CPU detection
use std::collections::HashSet;
use tracing::{debug, error, info, warn};
/// CPU detection pub mod cpuid;
pub mod cpuid;
use cpuid::{CpuInfo, CpuThreads};
/// Model-Specific Registers
pub mod msr;
use msr::{msr_presets, Msr, MsrItem, MsrPreset, NO_MASK};
// MSR registers for Cache QoS
const IA32_PQR_ASSOC: u32 = 0xC8F; // PQR (Platform QoS Resource) Association
const IA32_L3_QOS_MASK_1: u32 = 0xC91; // L3 Cache QoS Mask for COS 1
// Class of Service assignments
const COS_FULL_CACHE: u64 = 0; // COS 0 = full L3 cache access
const COS_LIMITED_CACHE: u64 = 1 << 32; // COS 1 = limited L3 cache (bit 32 sets COS in PQR_ASSOC)
#[derive(Default)]
pub struct RxMsr {
is_initialized: bool,
is_enabled: bool,
cache_qos: bool,
saved_items: Vec<MsrItem>,
}
impl RxMsr {
pub fn new() -> Self {
Self { is_initialized: false, is_enabled: false, cache_qos: false, saved_items: vec![] }
}
pub fn init(&mut self, cache_qos: bool, threads: usize, save: bool) -> bool {
if self.is_initialized {
return self.is_enabled
}
self.is_initialized = true;
self.is_enabled = false;
// Detect CPU and find MSR preset
let cpu = CpuInfo::detect();
let msr_preset = if cpu.is_amd() && cpu.zen_generation.is_some() {
msr_presets::get_preset(MsrPreset::from_zen(cpu.zen_generation.unwrap()))
} else if cpu.is_intel() {
msr_presets::get_preset(MsrPreset::Intel)
} else {
msr_presets::get_preset(MsrPreset::None)
};
if msr_preset.is_empty() {
return false
}
self.cache_qos = cache_qos;
if self.cache_qos && !cpu.has_cat_l3 {
warn!("This CPU doesn't support cat_l3");
self.cache_qos = false;
}
self.is_enabled = self.wrmsr(msr_preset, threads, self.cache_qos, save);
if self.is_enabled {
info!("[msr] MSR register values set successfully");
} else {
error!("[msr] Failed to apply MSR mod, hashrate will be low");
}
self.is_enabled
}
pub fn destroy(&mut self) {
if !self.is_initialized {
return
}
self.is_initialized = false;
self.is_enabled = false;
if self.saved_items.is_empty() {
return
}
let saved_items = std::mem::take(&mut self.saved_items);
if !self.wrmsr(&saved_items, 0, self.cache_qos, false) {
error!("[msr] Failed to restore to initial state");
}
}
fn wrmsr(
&mut self,
preset: &[MsrItem],
threads: usize, // TODO: This should be a slice of threads
cache_qos: bool,
save: bool,
) -> bool {
let msr = Msr::get();
if msr.is_none() {
return false
}
let msr = msr.unwrap();
if save {
self.saved_items.reserve(preset.len());
for i in preset {
if let Some(item) = msr.read(i.reg(), -1, true) {
if !item.is_valid() {
self.saved_items.clear();
return false
}
self.saved_items.push(item);
}
}
}
// Which CPU cores will have access top the full L3 cache
// TODO: Check xmrig/crypto/rx/RxMsr.cpp::wsmsr()
//let cpu_threads = CpuThreads::detect();
//let units: HashSet<i32> = cpu_threads.thread_ids().into_iter().collect();
let cache_enabled: HashSet<i32> = HashSet::new();
//let mut cache_qos_disabled = threads.is_empty();
let cache_qos_disabled = true;
/*
if cache_qos && !cache_qos_disabled {
for thread in threads {
let affinity = thread.affinity();
// If some thread has no affinity or wrong affinity,
// disable cache QoS
if affinity < 0 || !units.contains(&affinity) {
cache_qos_disabled = true;
warn!(
"Cache QoS can only be enabled when all mining threads have affinity set"
);
}
break
}
cache_enabled.insert(affinity);
}
*/
// Apply MSR values to all CPUs
msr.write_all(|cpu| {
debug!("msr.write_all cpu={} get_cpu={}", cpu, get_cpu(cpu));
for item in preset {
if !msr.write(item.reg(), item.value(), get_cpu(cpu), item.mask(), true) {
return false
}
}
if !cache_qos {
return true
}
// Cache QoS configuration
if cache_qos_disabled || cache_enabled.contains(&cpu) {
// Assign Class of Service 0 (full L3 cache) to this CPU
return msr.write(IA32_PQR_ASSOC, COS_FULL_CACHE, get_cpu(cpu), NO_MASK, true)
}
// For CPUs not running mining threads:
// Disable L3 cache for Class of Service 1
if !msr.write(IA32_L3_QOS_MASK_1, 0, get_cpu(cpu), NO_MASK, true) {
// Some CPUs don't allow setting it to all zeros
if !msr.write(IA32_L3_QOS_MASK_1, 1, get_cpu(cpu), NO_MASK, true) {
return false
}
}
// Assign Class of Service 1 (limited cache) to this CPU
msr.write(IA32_PQR_ASSOC, COS_LIMITED_CACHE, get_cpu(cpu), NO_MASK, true)
})
}
}
#[cfg(target_os = "windows")]
const fn get_cpu(cpu: i32) -> i32 {
-1
}
#[cfg(not(target_os = "windows"))]
const fn get_cpu(cpu: i32) -> i32 {
cpu
}

View File

@@ -33,12 +33,15 @@ use std::sync::{Arc, Weak};
use once_cell::sync::Lazy;
use parking_lot::Mutex;
use super::CpuThreads;
mod error;
mod msr_item;
mod msr_presets;
pub(super) mod msr_presets;
use error::{MsrError, MsrResult};
use msr_item::MsrItem;
pub use msr_item::{MsrItem, NO_MASK};
pub use msr_presets::MsrPreset;
#[cfg(target_os = "linux")]
mod msr_linux;
@@ -66,7 +69,7 @@ impl Msr {
/// Get or create the MSR singleton
///
/// Returns `None` if MSR is not available on this system.
pub fn get(units: Vec<i32>) -> Option<Arc<Self>> {
pub fn get() -> Option<Arc<Self>> {
let mut instance = INSTANCE.lock();
// Try to upgrade the weak reference
@@ -76,7 +79,33 @@ impl Msr {
}
}
// Create new instance
// Autodetect CPU units
let units = CpuThreads::detect().thread_ids();
let msr = Arc::new(Self { inner: MsrImpl::new(units) });
if msr.is_available() {
*instance = Arc::downgrade(&msr);
Some(msr)
} else {
None
}
}
/// Get or create the MSR singleton with specific CPU units
///
/// Returns `None` if MSR is not available on this system.
pub fn get_with_units(units: Vec<i32>) -> Option<Arc<Self>> {
let mut instance = INSTANCE.lock();
// Try to upgrade the weak reference
if let Some(msr) = instance.upgrade() {
if msr.is_available() {
return Some(msr);
}
}
// Create new instance with provided units
let msr = Arc::new(Self { inner: MsrImpl::new(units) });
if msr.is_available() {

View File

@@ -32,7 +32,11 @@ use darkfi_sdk::{
pasta::pallas,
};
use minerd::{benchmark::benchmark, hw::cpuid::CpuInfo, MinerNodeConfig, Minerd};
use minerd::{
benchmark::benchmark,
hw::{cpuid::CpuInfo, RxMsr},
MinerNodeConfig, Minerd,
};
const CONFIG_FILE: &str = "minerd.toml";
const CONFIG_FILE_CONTENTS: &str = include_str!("../minerd.toml");
@@ -65,6 +69,10 @@ struct Args {
/// PoW miner number of threads to use
threads: usize,
#[structopt(long)]
/// Assign full L3 cache to CPU
cache_qos: bool,
#[structopt(short, long, default_value = "2")]
/// Polling rate to ask darkfid for mining jobs
polling_rate: u64,
@@ -88,6 +96,10 @@ struct Args {
#[structopt(long)]
/// Print CPU information
cpuid: bool,
#[structopt(long)]
/// Perform oneshot hashrate boost ops
boost: bool,
}
#[derive(Clone, Debug, Deserialize, StructOpt, StructOptToml)]
@@ -166,6 +178,11 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
return Ok(())
}
if args.boost {
let mut rxmsr = RxMsr::new();
rxmsr.init(args.cache_qos, args.threads, true);
}
// Run system hashrate benchmark if requested
if let Some(nonces) = args.bench {
return benchmark(!args.light_mode, args.large_pages, args.secure, args.threads, nonces)