diff --git a/src/bin/cache_dump.rs b/src/bin/cache_dump.rs new file mode 100644 index 0000000..4d4bce7 --- /dev/null +++ b/src/bin/cache_dump.rs @@ -0,0 +1,41 @@ +extern crate clap; +extern crate thinp; + +use clap::{App, Arg}; +use std::path::Path; +use thinp::cache::dump::{dump, CacheDumpOptions}; + +//------------------------------------------ + +fn main() { + let parser = App::new("cache_check") + .version(thinp::version::TOOLS_VERSION) + .arg( + Arg::with_name("INPUT") + .help("Specify the input device to check") + .required(true) + .index(1), + ) + .arg( + Arg::with_name("REPAIR") + .help("") + .long("repair") + .value_name("REPAIR"), + ); + + let matches = parser.get_matches(); + let input_file = Path::new(matches.value_of("INPUT").unwrap()); + + let opts = CacheDumpOptions { + dev: &input_file, + async_io: false, + repair: matches.is_present("REPAIR"), + }; + + if let Err(reason) = dump(opts) { + eprintln!("{}", reason); + std::process::exit(1); + } +} + +//------------------------------------------ diff --git a/src/cache/dump.rs b/src/cache/dump.rs new file mode 100644 index 0000000..566673f --- /dev/null +++ b/src/cache/dump.rs @@ -0,0 +1,157 @@ +use std::marker::PhantomData; +use std::path::Path; +use std::sync::{Arc, Mutex}; + +use crate::io_engine::{AsyncIoEngine, IoEngine, SyncIoEngine}; +use crate::cache::hint::Hint; +use crate::cache::mapping::Mapping; +use crate::cache::superblock::*; +use crate::cache::xml::{self, MetadataVisitor}; +use crate::pdata::array_walker::*; + +//------------------------------------------ + +const MAX_CONCURRENT_IO: u32 = 1024; + +//------------------------------------------ + +// TODO: pull out MetadataVisitor from the xml crate? +struct MappingEmitter { + emitter: Arc>, +} + +impl MappingEmitter { + pub fn new(emitter: Arc>) -> MappingEmitter { + MappingEmitter { + emitter: emitter, + } + } +} + +impl ArrayBlockVisitor for MappingEmitter { + fn visit(&self, index: u64, m: Mapping) -> anyhow::Result<()> { + if m.oblock == 0 { + return Ok(()); + } + + // TODO: eliminate xml::Map? + let m = xml::Map { + cblock: index as u32, + oblock: m.oblock, + dirty: m.is_dirty(), + }; + + let mut emitter = self.emitter.lock().unwrap(); + emitter.mapping(&m)?; + + Ok(()) + } +} + +//----------------------------------------- + +struct HintEmitter { + emitter: Arc>, + _not_used: PhantomData, +} + +impl HintEmitter { + pub fn new(emitter: Arc>) -> HintEmitter { + HintEmitter { + emitter: emitter, + _not_used: PhantomData, + } + } +} + +impl ArrayBlockVisitor> for HintEmitter { + fn visit(&self, index: u64, hint: Hint) -> anyhow::Result<()> { + // TODO: skip invalid blocks + + let h = xml::Hint { + cblock: index as u32, + data: hint.hint.to_vec(), + }; + + let mut emitter = self.emitter.lock().unwrap(); + emitter.hint(&h)?; + + Ok(()) + } +} + +//------------------------------------------ + +pub struct CacheDumpOptions<'a> { + pub dev: &'a Path, + pub async_io: bool, + pub repair: bool, +} + +// TODO: add report +struct Context { + engine: Arc, +} + +fn mk_context(opts: &CacheDumpOptions) -> anyhow::Result { + let engine: Arc; + + if opts.async_io { + engine = Arc::new(AsyncIoEngine::new( + opts.dev, + MAX_CONCURRENT_IO, + false, + )?); + } else { + let nr_threads = std::cmp::max(8, num_cpus::get() * 2); + engine = Arc::new(SyncIoEngine::new(opts.dev, nr_threads, false)?); + } + + Ok(Context { + engine, + }) +} + +fn dump_metadata(ctx: &Context, sb: &Superblock, _repair: bool) -> anyhow::Result<()>{ + let engine = &ctx.engine; + + let out = Arc::new(Mutex::new(xml::XmlWriter::new(std::io::stdout()))); + let xml_sb = xml::Superblock { + uuid: "".to_string(), + block_size: sb.data_block_size, + nr_cache_blocks: sb.cache_blocks, + policy: std::str::from_utf8(&sb.policy_name[..])?.to_string(), + hint_width: sb.policy_hint_size, + }; + out.lock().unwrap().superblock_b(&xml_sb)?; + + out.lock().unwrap().mappings_b()?; + let w = ArrayWalker::new(engine.clone(), false); + let emitter = Box::new(MappingEmitter::new(out.clone())); + w.walk(emitter, sb.mapping_root)?; + out.lock().unwrap().mappings_e()?; + + out.lock().unwrap().hints_b()?; + type Width = typenum::U4; // FIXME: align with sb.policy_hint_size + let emitter = Box::new(HintEmitter::::new(out.clone())); + w.walk(emitter, sb.hint_root)?; + out.lock().unwrap().hints_e()?; + + // FIXME: walk discards + //out.lock().unwrap().discards_b()?; + //out.lock().unwrap().discards_e()?; + + out.lock().unwrap().superblock_e()?; + + Ok(()) +} + +pub fn dump(opts: CacheDumpOptions) -> anyhow::Result<()> { + let ctx = mk_context(&opts)?; + let engine = &ctx.engine; + let sb = read_superblock(engine.as_ref(), SUPERBLOCK_LOCATION)?; + + dump_metadata(&ctx, &sb, opts.repair) +} + +//------------------------------------------ diff --git a/src/cache/mod.rs b/src/cache/mod.rs index a120ce6..5aa61fa 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -1,4 +1,5 @@ pub mod check; +pub mod dump; pub mod hint; pub mod mapping; pub mod superblock;