use crate::block_manager::v2::memory::StorageKind;
use super::PhysicalLayout;
use aligned_vec::{AVec, avec};
use anyhow::{Result, anyhow};
use blake3::Hasher;
use std::{
collections::HashMap,
fs::File,
io::{Read, Seek},
mem::ManuallyDrop,
ops::Range,
os::fd::FromRawFd,
};
use cudarc::runtime::sys::{cudaMemcpy, cudaMemcpyKind};
pub type BlockChecksum = String;
pub fn compute_block_checksums(
layout: &PhysicalLayout,
block_ids: &[usize],
) -> Result<HashMap<usize, BlockChecksum>> {
let mut checksums = HashMap::new();
for &block_id in block_ids {
let checksum = compute_single_block_checksum(layout, block_id, None)?;
checksums.insert(block_id, checksum);
}
Ok(checksums)
}
pub fn compute_layer_checksums(
layout: &PhysicalLayout,
block_ids: &[usize],
layer_range: Range<usize>,
) -> Result<HashMap<usize, BlockChecksum>> {
let config = layout.layout().config();
if layer_range.end > config.num_layers {
return Err(anyhow!(
"Layer range {:?} exceeds num_layers {}",
layer_range,
config.num_layers
));
}
let mut checksums = HashMap::new();
for &block_id in block_ids {
let checksum = compute_single_block_checksum(layout, block_id, Some(layer_range.clone()))?;
checksums.insert(block_id, checksum);
}
Ok(checksums)
}
fn compute_single_block_checksum(
layout: &PhysicalLayout,
block_id: usize,
layer_range: Option<Range<usize>>,
) -> Result<String> {
let config = layout.layout().config();
if block_id >= config.num_blocks {
return Err(anyhow!("Block ID {} out of range", block_id));
}
let num_layers = config.num_layers;
let outer_dim = config.outer_dim;
let layers = layer_range.unwrap_or(0..num_layers);
if layers.end > config.num_layers {
return Err(anyhow!(
"Layer range {:?} exceeds num_layers {}",
layers,
config.num_layers
));
}
let mut hasher = Hasher::new();
for layer_id in layers {
for outer_id in 0..outer_dim {
let region = layout.memory_region(block_id, layer_id, outer_id)?;
match layout.location() {
StorageKind::System | StorageKind::Pinned => {
let slice = unsafe {
std::slice::from_raw_parts(region.addr() as *const u8, region.size())
};
hasher.update(slice);
}
StorageKind::Device(_) => {
let system_region: Vec<u8> = vec![0; region.size()];
unsafe {
cudaMemcpy(
system_region.as_ptr() as *mut std::ffi::c_void,
region.addr() as *const std::ffi::c_void,
region.size(),
cudaMemcpyKind::cudaMemcpyDeviceToHost,
);
}
hasher.update(system_region.as_slice());
}
StorageKind::Disk(fd) => {
let mut system_region: AVec<u8, _> = avec![[4096]| 0; region.size()];
let mut file = ManuallyDrop::new(unsafe { File::from_raw_fd(fd as i32) });
file.seek(std::io::SeekFrom::Start(region.addr() as u64))?;
file.read_exact(&mut system_region)?;
hasher.update(system_region.as_slice());
}
}
}
}
Ok(hasher.finalize().to_string())
}
#[cfg(all(test, feature = "testing-nixl"))]
mod tests {
use super::super::tests::*;
use super::*;
use crate::block_manager::v2::physical::transfer::{FillPattern, fill_blocks};
#[test]
fn test_checksum_constant_pattern() {
let physical = builder(2)
.fully_contiguous()
.allocate_system()
.build()
.unwrap();
fill_blocks(&physical, &[0, 1], FillPattern::Constant(42)).unwrap();
let checksums = compute_block_checksums(&physical, &[0, 1]).unwrap();
assert_eq!(checksums[&0], checksums[&1]);
let memory_region = physical.memory_region(0, 0, 0).unwrap();
let slice = unsafe {
std::slice::from_raw_parts(memory_region.addr() as *const u8, memory_region.size())
};
assert!(slice.iter().all(|&b| b == 42));
let mut hasher = Hasher::new();
hasher.update(slice);
let checksum_mr_slice = hasher.finalize().to_string();
let vec = vec![42; memory_region.size()];
let mut hasher = Hasher::new();
hasher.update(&vec);
let checksum_vec = hasher.finalize().to_string();
assert_eq!(checksum_mr_slice, checksum_vec);
}
}