mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-08 22:28:12 -05:00
minerd: MSR support
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user