From 1398cf31d1c29e2e72b6c342bc6bb8a88593a520 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 27 Jul 2020 15:53:42 +0100 Subject: [PATCH] [thin_check (Rust)] work in progress --- src/bin/thin_check.rs | 84 +++++++++++++++---------- src/block_manager.rs | 139 ++++++++++++++++++++++++++++++++++------- src/lib.rs | 2 - src/thin/check.rs | 20 ++++-- src/thin/superblock.rs | 6 +- 5 files changed, 187 insertions(+), 64 deletions(-) diff --git a/src/bin/thin_check.rs b/src/bin/thin_check.rs index 3ad7c01..46ae7ef 100644 --- a/src/bin/thin_check.rs +++ b/src/bin/thin_check.rs @@ -2,6 +2,7 @@ extern crate clap; extern crate thinp; use clap::{App, Arg}; +use std::path::Path; use std::process; use thinp::file_utils; @@ -9,46 +10,63 @@ use std::process::exit; fn main() { let parser = App::new("thin_check") - .version(thinp::version::TOOLS_VERSION) + .version(thinp::version::TOOLS_VERSION) .about("Validates thin provisioning metadata on a device or file.") - .arg(Arg::with_name("QUIET") - .help("Suppress output messages, return only exit code.") - .short("q") - .long("quiet") - .value_name("QUIET")) - .arg(Arg::with_name("SB_ONLY") - .help("Only check the superblock.") - .long("super-block-only") - .value_name("SB_ONLY")) - .arg(Arg::with_name("ignore-non-fatal-errors") - .help("Only return a non-zero exit code if a fatal error is found.") - .long("ignore-non-fatal-errors") - .value_name("IGNORE_NON_FATAL")) - .arg(Arg::with_name("clear-needs-check-flag") - .help("Clears the 'needs_check' flag in the superblock") - .long("clear-needs-check") - .value_name("CLEAR_NEEDS_CHECK")) - .arg(Arg::with_name("OVERRIDE_MAPPING_ROOT") - .help("Specify a mapping root to use") - .long("override-mapping-root") - .value_name("OVERRIDE_MAPPING_ROOT") - .takes_value(true)) - .arg(Arg::with_name("METADATA_SNAPSHOT") - .help("Check the metadata snapshot on a live pool") - .short("m") - .long("metadata-snapshot") - .value_name("METADATA_SNAPSHOT")) + .arg( + Arg::with_name("QUIET") + .help("Suppress output messages, return only exit code.") + .short("q") + .long("quiet") + .value_name("QUIET"), + ) + .arg( + Arg::with_name("SB_ONLY") + .help("Only check the superblock.") + .long("super-block-only") + .value_name("SB_ONLY"), + ) + .arg( + Arg::with_name("ignore-non-fatal-errors") + .help("Only return a non-zero exit code if a fatal error is found.") + .long("ignore-non-fatal-errors") + .value_name("IGNORE_NON_FATAL"), + ) + .arg( + Arg::with_name("clear-needs-check-flag") + .help("Clears the 'needs_check' flag in the superblock") + .long("clear-needs-check") + .value_name("CLEAR_NEEDS_CHECK"), + ) + .arg( + Arg::with_name("OVERRIDE_MAPPING_ROOT") + .help("Specify a mapping root to use") + .long("override-mapping-root") + .value_name("OVERRIDE_MAPPING_ROOT") + .takes_value(true), + ) + .arg( + Arg::with_name("METADATA_SNAPSHOT") + .help("Check the metadata snapshot on a live pool") + .short("m") + .long("metadata-snapshot") + .value_name("METADATA_SNAPSHOT"), + ) + .arg( + Arg::with_name("INPUT") + .help("Specify the input device to check") + .required(true) + .index(1), + ); let matches = parser.get_matches(); - let input_file = matches.value_of("INPUT").unwrap(); - let output_file = matches.value_of("OUTPUT").unwrap(); + let input_file = Path::new(matches.value_of("INPUT").unwrap()); if !file_utils::file_exists(input_file) { - eprintln!("Couldn't find input file '{}'.", &input_file); + eprintln!("Couldn't find input file '{:?}'.", &input_file); exit(1); } - - if let Err(reason) = thinp::pack::toplevel::unpack(&input_file, &output_file) { + + if let Err(reason) = thinp::thin::check::check(&input_file) { println!("Application error: {}", reason); process::exit(1); } diff --git a/src/block_manager.rs b/src/block_manager.rs index 606eb30..df1274e 100644 --- a/src/block_manager.rs +++ b/src/block_manager.rs @@ -1,51 +1,148 @@ +use anyhow::{anyhow, Result}; +use rio::{self, Completion, Rio}; +use std::alloc::{alloc, dealloc, Layout}; +use std::collections::HashMap; +use std::fs::File; +use std::fs::OpenOptions; use std::io; use std::io::{Read, Seek}; -use std::fs::OpenOptions; use std::os::unix::fs::OpenOptionsExt; -use std::fs::File; +use std::path::Path; +use std::sync::{Arc, Mutex}; pub const BLOCK_SIZE: usize = 4096; +const ALIGN: usize = 4096; +// FIXME: introduce a cache +// FIXME: use O_DIRECT +#[derive(Debug)] pub struct Block { pub loc: u64, - pub data: [u8; BLOCK_SIZE as usize], -} - -pub struct BlockManager { - pub nr_blocks: u64, - input: File, + data: *mut u8, } -fn get_nr_blocks(path: &str) -> io::Result { +impl Block { + pub fn new(loc: u64) -> Block { + let layout = Layout::from_size_align(BLOCK_SIZE, ALIGN).unwrap(); + let ptr = unsafe { alloc(layout) }; + assert!(!ptr.is_null(), "out of memory"); + Block { loc, data: ptr } + } + + fn get_data(&self) -> &mut [u8] { + unsafe { std::slice::from_raw_parts_mut::<'static>(self.data, BLOCK_SIZE) } + } +} + +impl Drop for Block { + fn drop(&mut self) { + let layout = Layout::from_size_align(BLOCK_SIZE, ALIGN).unwrap(); + unsafe { + dealloc(self.data, layout); + } + } +} + +//------------------------------------------ + +pub trait IoEngine { + fn get_nr_blocks(&self) -> u64; + fn read(&mut self, blocks: &mut Vec) -> Result<()>; +} + +fn get_nr_blocks(path: &Path) -> io::Result { let metadata = std::fs::metadata(path)?; Ok(metadata.len() / (BLOCK_SIZE as u64)) } -impl BlockManager { - pub fn new(path: &str, _cache_size: usize) -> io::Result { +//------------------------------------------ + +pub struct SyncIoEngine { + nr_blocks: u64, + input: File, +} + +impl SyncIoEngine { + pub fn new(path: &Path) -> Result { let input = OpenOptions::new() .read(true) .write(false) .custom_flags(libc::O_DIRECT) .open(path)?; - Ok(BlockManager { + let ring = rio::new()?; + + Ok(SyncIoEngine { + nr_blocks: get_nr_blocks(path)?, + input, + }) + } +} + +impl IoEngine for SyncIoEngine { + fn get_nr_blocks(&self) -> u64 { + self.nr_blocks + } + + fn read(&mut self, blocks: &mut Vec) -> Result<()> { + for b in blocks.into_iter() { + self.input.seek(io::SeekFrom::Start(0))?; + self.input.read_exact(&mut b.get_data())?; + } + + Ok(()) + } +} + +//------------------------------------------ + +/* +pub struct AsyncIoEngine { + ring: Rio, + nr_blocks: u64, + input: File, +} + +impl AsyncIoEngine { + pub fn new(path: &Path) -> Result { + let input = OpenOptions::new() + .read(true) + .write(false) + .custom_flags(libc::O_DIRECT) + .open(path)?; + + let ring = rio::new()?; + + Ok(IoEngine { + ring, nr_blocks: get_nr_blocks(path)?, input, }) } - pub fn get(&mut self, b: u64) -> io::Result { - self.read_block(b) - } + pub fn read(&self, blocks: &mut Vec) -> Result<()> { + // FIXME: using a bounce buffer as a hack, since b.get_data() will not have + // a big enough lifetime. + let mut bounce_buffer = vec![0; blocks.len() * BLOCK_SIZE]; + let mut completions = Vec::new(); - fn read_block(&mut self, b: u64) -> io::Result - { - let mut buf = Block {loc: b, data: [0; BLOCK_SIZE]}; + for n in 0..blocks.len() { + let b = &blocks[n]; + let at = b.loc * BLOCK_SIZE as u64; + let completion = self.ring.read_at(&self.input, &slice, at); + completions.push(completion); + } - self.input.seek(io::SeekFrom::Start(b * (BLOCK_SIZE as u64)))?; - self.input.read_exact(&mut buf.data)?; + for c in completions { + let n = c.wait()?; + if n != BLOCK_SIZE { + return Err(anyhow!("short read")); + } + } - Ok(buf) + // copy out of the bounce buffer + + Ok(()) } } +*/ diff --git a/src/lib.rs b/src/lib.rs index d63b864..b7b2082 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,11 +16,9 @@ extern crate quickcheck; extern crate quickcheck_macros; pub mod block_manager; -pub mod check; pub mod file_utils; pub mod pack; pub mod shrink; pub mod thin; pub mod version; -pub mod thin; pub mod checksum; diff --git a/src/thin/check.rs b/src/thin/check.rs index a97ca01..580e468 100644 --- a/src/thin/check.rs +++ b/src/thin/check.rs @@ -1,13 +1,23 @@ use std::error::Error; +use std::path::Path; +use std::time::{Duration, Instant}; +use std::thread; +use std::sync::{Arc, Mutex}; -use crate::block_manager::BlockManager; +use crate::block_manager::{Block, IoEngine, SyncIoEngine, BLOCK_SIZE}; -pub fn check(dev: &str) -> Result<(), Box> { - let mut bm = BlockManager::new(dev, 1024)?; +pub fn check(dev: &Path) -> Result<(), Box> { + let mut engine = SyncIoEngine::new(dev)?; + let count = 4096; - for b in 0..100 { - let _block = bm.get(b)?; + let mut blocks = Vec::new(); + for n in 0..count { + blocks.push(Block::new(n)); } + let now = Instant::now(); + engine.read(&mut blocks)?; + println!("read {} blocks in {} ms", count, now.elapsed().as_millis()); + Ok(()) } diff --git a/src/thin/superblock.rs b/src/thin/superblock.rs index 6b6d701..3b43319 100644 --- a/src/thin/superblock.rs +++ b/src/thin/superblock.rs @@ -1,4 +1,3 @@ -use anyhow::Result; use crate::block_manager::*; use crate::checksum::*; @@ -12,7 +11,7 @@ pub struct Superblock { transaction_id: u64, metadata_snap: u64, data_sm_root: [u8; SPACE_MAP_ROOT_SIZE], - metadata_sn_root: [u8; SPACE_MAP_ROOT_SIZE], + metadata_sm_root: [u8; SPACE_MAP_ROOT_SIZE], mapping_root: u64, details_root: u64, data_block_size: u32, @@ -44,6 +43,7 @@ struct SuperblockError { kind: ErrorType, } +/* use SuperblockDamage::*; //------------------------------ @@ -57,5 +57,5 @@ pub fn check_type(b: &Block) -> Result<()> { UNKNOWN => Err(Box::new(BadChecksum)), } } - +*/ //------------------------------