Code review of cache_dump/check

Added support for metadata format2 to cache_dump.  Nothing tested.
This commit is contained in:
Joe Thornber 2021-03-04 11:13:08 +00:00
parent e6c6275aea
commit 7e869bb8e0
7 changed files with 222 additions and 117 deletions

View File

@ -33,7 +33,6 @@ threadpool = "1.8"
thiserror = "1.0" thiserror = "1.0"
tui = "0.10" tui = "0.10"
termion = "1.5" termion = "1.5"
typenum = "1.12.0"
[dev-dependencies] [dev-dependencies]
json = "0.12" json = "0.12"

49
src/cache/check.rs vendored
View File

@ -1,13 +1,12 @@
use anyhow::anyhow; use anyhow::anyhow;
use std::marker::PhantomData; use std::collections::*;
use std::path::Path; use std::path::Path;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::collections::*;
use crate::io_engine::{AsyncIoEngine, IoEngine, SyncIoEngine};
use crate::cache::hint::*; use crate::cache::hint::*;
use crate::cache::mapping::*; use crate::cache::mapping::*;
use crate::cache::superblock::*; use crate::cache::superblock::*;
use crate::io_engine::{AsyncIoEngine, IoEngine, SyncIoEngine};
use crate::pdata::array_walker::*; use crate::pdata::array_walker::*;
//------------------------------------------ //------------------------------------------
@ -50,7 +49,7 @@ impl CheckMappingVisitor {
} }
} }
impl ArrayBlockVisitor<Mapping> for CheckMappingVisitor { impl ArrayVisitor<Mapping> for CheckMappingVisitor {
fn visit(&self, _index: u64, m: Mapping) -> anyhow::Result<()> { fn visit(&self, _index: u64, m: Mapping) -> anyhow::Result<()> {
if !m.is_valid() { if !m.is_valid() {
return Ok(()); return Ok(());
@ -72,20 +71,16 @@ impl ArrayBlockVisitor<Mapping> for CheckMappingVisitor {
//------------------------------------------ //------------------------------------------
struct CheckHintVisitor<Width> { struct CheckHintVisitor {}
_not_used: PhantomData<Width>,
}
impl<Width> CheckHintVisitor<Width> { impl CheckHintVisitor {
fn new() -> CheckHintVisitor<Width> { fn new() -> CheckHintVisitor {
CheckHintVisitor { CheckHintVisitor {}
_not_used: PhantomData,
}
} }
} }
impl<Width: typenum::Unsigned> ArrayBlockVisitor<Hint<Width>> for CheckHintVisitor<Width> { impl ArrayVisitor<Hint> for CheckHintVisitor {
fn visit(&self, _index: u64, _hint: Hint<Width>) -> anyhow::Result<()> { fn visit(&self, _index: u64, _hint: Hint) -> anyhow::Result<()> {
// TODO: check hints // TODO: check hints
Ok(()) Ok(())
} }
@ -112,19 +107,13 @@ fn mk_context(opts: &CacheCheckOptions) -> anyhow::Result<Context> {
let engine: Arc<dyn IoEngine + Send + Sync>; let engine: Arc<dyn IoEngine + Send + Sync>;
if opts.async_io { if opts.async_io {
engine = Arc::new(AsyncIoEngine::new( engine = Arc::new(AsyncIoEngine::new(opts.dev, MAX_CONCURRENT_IO, false)?);
opts.dev,
MAX_CONCURRENT_IO,
false,
)?);
} else { } else {
let nr_threads = std::cmp::max(8, num_cpus::get() * 2); let nr_threads = std::cmp::max(8, num_cpus::get() * 2);
engine = Arc::new(SyncIoEngine::new(opts.dev, nr_threads, false)?); engine = Arc::new(SyncIoEngine::new(opts.dev, nr_threads, false)?);
} }
Ok(Context { Ok(Context { engine })
engine,
})
} }
pub fn check(opts: CacheCheckOptions) -> anyhow::Result<()> { pub fn check(opts: CacheCheckOptions) -> anyhow::Result<()> {
@ -141,19 +130,21 @@ pub fn check(opts: CacheCheckOptions) -> anyhow::Result<()> {
// TODO: factor out into check_mappings() // TODO: factor out into check_mappings()
if !opts.skip_mappings { if !opts.skip_mappings {
let w = ArrayWalker::new(engine.clone(), false); let w = ArrayWalker::new(engine.clone(), false);
let c = Box::new(CheckMappingVisitor::new(sb.version)); let mut c = CheckMappingVisitor::new(sb.version);
w.walk(c, sb.mapping_root)?; w.walk(&mut c, sb.mapping_root)?;
if sb.version >= 2 { if sb.version >= 2 {
// TODO: check dirty bitset // TODO: check dirty bitset
} }
} }
if !opts.skip_hints && sb.hint_root != 0 { if !opts.skip_hints && sb.hint_root != 0 && sb.policy_hint_size != 0 {
if sb.policy_hint_size != 4 {
return Err(anyhow!("cache_check only supports policy hint size of 4"));
}
let w = ArrayWalker::new(engine.clone(), false); let w = ArrayWalker::new(engine.clone(), false);
type Width = typenum::U4; // FIXME: check sb.policy_hint_size let mut c = CheckHintVisitor::new();
let c = Box::new(CheckHintVisitor::<Width>::new()); w.walk(&mut c, sb.hint_root)?;
w.walk(c, sb.hint_root)?;
} }
if !opts.skip_discards { if !opts.skip_discards {

252
src/cache/dump.rs vendored
View File

@ -1,12 +1,13 @@
use std::marker::PhantomData; use anyhow::{anyhow, Result};
use fixedbitset::FixedBitSet;
use std::path::Path; use std::path::Path;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use crate::io_engine::{AsyncIoEngine, IoEngine, SyncIoEngine};
use crate::cache::hint::Hint; use crate::cache::hint::Hint;
use crate::cache::mapping::Mapping; use crate::cache::mapping::Mapping;
use crate::cache::superblock::*; use crate::cache::superblock::*;
use crate::cache::xml::{self, MetadataVisitor}; use crate::cache::xml::{self, MetadataVisitor};
use crate::io_engine::{AsyncIoEngine, IoEngine, SyncIoEngine};
use crate::pdata::array_walker::*; use crate::pdata::array_walker::*;
//------------------------------------------ //------------------------------------------
@ -15,66 +16,167 @@ const MAX_CONCURRENT_IO: u32 = 1024;
//------------------------------------------ //------------------------------------------
// TODO: pull out MetadataVisitor from the xml crate? mod format1 {
struct MappingEmitter { use super::*;
emitter: Arc<Mutex<dyn MetadataVisitor>>,
}
impl MappingEmitter { struct Inner<'a> {
pub fn new(emitter: Arc<Mutex<dyn MetadataVisitor>>) -> MappingEmitter { visitor: &'a mut dyn MetadataVisitor,
MappingEmitter { valid_mappings: FixedBitSet,
emitter, }
pub struct MappingEmitter<'a> {
inner: Mutex<Inner<'a>>,
}
impl<'a> MappingEmitter<'a> {
pub fn new(nr_entries: usize, visitor: &'a mut dyn MetadataVisitor) -> MappingEmitter<'a> {
MappingEmitter {
inner: Mutex::new(Inner {
visitor,
valid_mappings: FixedBitSet::with_capacity(nr_entries),
}),
}
}
pub fn get_valid(self) -> FixedBitSet {
let inner = self.inner.into_inner().unwrap();
inner.valid_mappings
}
}
impl<'a> ArrayVisitor<Mapping> for MappingEmitter<'a> {
fn visit(&self, index: u64, m: Mapping) -> Result<()> {
if m.is_valid() {
let m = xml::Map {
cblock: index as u32,
oblock: m.oblock,
dirty: m.is_dirty(),
};
let mut inner = self.inner.lock().unwrap();
inner.valid_mappings.set(index as usize, true);
inner.visitor.mapping(&m)?;
}
Ok(())
} }
} }
} }
impl ArrayBlockVisitor<Mapping> for MappingEmitter { //------------------------------------------
fn visit(&self, index: u64, m: Mapping) -> anyhow::Result<()> {
if m.oblock == 0 { mod format2 {
return Ok(()); use super::*;
//-------------------
// Dirty bitset visitor
pub struct DirtyVisitor {
nr_entries: usize,
bits: Mutex<FixedBitSet>,
}
impl DirtyVisitor {
pub fn new(nr_entries: usize) -> Self {
DirtyVisitor {
nr_entries,
bits: Mutex::new(FixedBitSet::with_capacity(nr_entries)),
}
} }
// TODO: eliminate xml::Map? pub fn get_bits(self) -> FixedBitSet {
let m = xml::Map { self.bits.into_inner().unwrap()
cblock: index as u32, }
oblock: m.oblock, }
dirty: m.is_dirty(),
};
let mut emitter = self.emitter.lock().unwrap(); impl ArrayVisitor<u64> for DirtyVisitor {
emitter.mapping(&m)?; fn visit(&self, index: u64, bits: u64) -> Result<()> {
for i in 0..64u64 {
if (index + i) >= self.nr_entries as u64 {
break;
}
Ok(()) self.bits.lock().unwrap().set((index + i) as usize, bits & (1 << i) != 0);
}
Ok(())
}
}
//-------------------
// Mapping visitor
struct Inner<'a> {
visitor: &'a mut dyn MetadataVisitor,
dirty_bits: FixedBitSet,
valid_mappings: FixedBitSet,
}
pub struct MappingEmitter<'a> {
inner: Mutex<Inner<'a>>,
}
impl<'a> MappingEmitter<'a> {
pub fn new(nr_entries: usize, dirty_bits: FixedBitSet, visitor: &'a mut dyn MetadataVisitor) -> MappingEmitter<'a> {
MappingEmitter {
inner: Mutex::new(Inner {
visitor,
dirty_bits,
valid_mappings: FixedBitSet::with_capacity(nr_entries),
}),
}
}
pub fn get_valid(self) -> FixedBitSet {
let inner = self.inner.into_inner().unwrap();
inner.valid_mappings
}
}
impl<'a> ArrayVisitor<Mapping> for MappingEmitter<'a> {
fn visit(&self, index: u64, m: Mapping) -> Result<()> {
if m.is_valid() {
let mut inner = self.inner.lock().unwrap();
let dirty = inner.dirty_bits.contains(index as usize);
let m = xml::Map {
cblock: index as u32,
oblock: m.oblock,
dirty,
};
inner.valid_mappings.set(index as usize, true);
inner.visitor.mapping(&m)?;
}
Ok(())
}
} }
} }
//----------------------------------------- //-----------------------------------------
struct HintEmitter<Width> { struct HintEmitter<'a> {
emitter: Arc<Mutex<dyn MetadataVisitor>>, emitter: Mutex<&'a mut dyn MetadataVisitor>,
_not_used: PhantomData<Width>, valid_mappings: FixedBitSet,
} }
impl<Width> HintEmitter<Width> { impl<'a> HintEmitter<'a> {
pub fn new(emitter: Arc<Mutex<dyn MetadataVisitor>>) -> HintEmitter<Width> { pub fn new(emitter: &'a mut dyn MetadataVisitor, valid_mappings: FixedBitSet) -> HintEmitter {
HintEmitter { HintEmitter {
emitter, emitter: Mutex::new(emitter),
_not_used: PhantomData, valid_mappings,
} }
} }
} }
impl<Width: typenum::Unsigned> ArrayBlockVisitor<Hint<Width>> for HintEmitter<Width> { impl<'a> ArrayVisitor<Hint> for HintEmitter<'a> {
fn visit(&self, index: u64, hint: Hint<Width>) -> anyhow::Result<()> { fn visit(&self, index: u64, hint: Hint) -> anyhow::Result<()> {
// TODO: skip invalid blocks if self.valid_mappings.contains(index as usize) {
let h = xml::Hint {
cblock: index as u32,
data: hint.hint.to_vec(),
};
let h = xml::Hint { self.emitter.lock().unwrap().hint(&h)?;
cblock: index as u32, }
data: hint.hint.to_vec(),
};
let mut emitter = self.emitter.lock().unwrap();
emitter.hint(&h)?;
Ok(()) Ok(())
} }
@ -88,7 +190,6 @@ pub struct CacheDumpOptions<'a> {
pub repair: bool, pub repair: bool,
} }
// TODO: add report
struct Context { struct Context {
engine: Arc<dyn IoEngine + Send + Sync>, engine: Arc<dyn IoEngine + Send + Sync>,
} }
@ -97,25 +198,19 @@ fn mk_context(opts: &CacheDumpOptions) -> anyhow::Result<Context> {
let engine: Arc<dyn IoEngine + Send + Sync>; let engine: Arc<dyn IoEngine + Send + Sync>;
if opts.async_io { if opts.async_io {
engine = Arc::new(AsyncIoEngine::new( engine = Arc::new(AsyncIoEngine::new(opts.dev, MAX_CONCURRENT_IO, false)?);
opts.dev,
MAX_CONCURRENT_IO,
false,
)?);
} else { } else {
let nr_threads = std::cmp::max(8, num_cpus::get() * 2); let nr_threads = std::cmp::max(8, num_cpus::get() * 2);
engine = Arc::new(SyncIoEngine::new(opts.dev, nr_threads, false)?); engine = Arc::new(SyncIoEngine::new(opts.dev, nr_threads, false)?);
} }
Ok(Context { Ok(Context { engine })
engine,
})
} }
fn dump_metadata(ctx: &Context, sb: &Superblock, _repair: bool) -> anyhow::Result<()>{ fn dump_metadata(ctx: &Context, sb: &Superblock, _repair: bool) -> anyhow::Result<()> {
let engine = &ctx.engine; let engine = &ctx.engine;
let out = Arc::new(Mutex::new(xml::XmlWriter::new(std::io::stdout()))); let mut out = xml::XmlWriter::new(std::io::stdout());
let xml_sb = xml::Superblock { let xml_sb = xml::Superblock {
uuid: "".to_string(), uuid: "".to_string(),
block_size: sb.data_block_size, block_size: sb.data_block_size,
@ -123,25 +218,50 @@ fn dump_metadata(ctx: &Context, sb: &Superblock, _repair: bool) -> anyhow::Resul
policy: std::str::from_utf8(&sb.policy_name[..])?.to_string(), policy: std::str::from_utf8(&sb.policy_name[..])?.to_string(),
hint_width: sb.policy_hint_size, hint_width: sb.policy_hint_size,
}; };
out.lock().unwrap().superblock_b(&xml_sb)?; out.superblock_b(&xml_sb)?;
out.lock().unwrap().mappings_b()?; out.mappings_b()?;
let w = ArrayWalker::new(engine.clone(), false); let valid_mappings = match sb.version {
let emitter = Box::new(MappingEmitter::new(out.clone())); 1 => {
w.walk(emitter, sb.mapping_root)?; let w = ArrayWalker::new(engine.clone(), false);
out.lock().unwrap().mappings_e()?; let mut emitter = format1::MappingEmitter::new(sb.cache_blocks as usize, &mut out);
w.walk(&mut emitter, sb.mapping_root)?;
emitter.get_valid()
}
2 => {
// We need to walk the dirty bitset first.
let w = ArrayWalker::new(engine.clone(), false);
let mut v = format2::DirtyVisitor::new(sb.cache_blocks as usize);
out.lock().unwrap().hints_b()?; if let Some(root) = sb.dirty_root {
type Width = typenum::U4; // FIXME: align with sb.policy_hint_size w.walk(&mut v, root)?;
let emitter = Box::new(HintEmitter::<Width>::new(out.clone())); } else {
w.walk(emitter, sb.hint_root)?; // FIXME: is there a way this can legally happen? eg,
out.lock().unwrap().hints_e()?; // a crash of a freshly created cache?
return Err(anyhow!("format 2 selected, but no dirty bitset present"));
}
let dirty_bits = v.get_bits();
// FIXME: walk discards let w = ArrayWalker::new(engine.clone(), false);
//out.lock().unwrap().discards_b()?; let mut emitter = format2::MappingEmitter::new(sb.cache_blocks as usize, dirty_bits, &mut out);
//out.lock().unwrap().discards_e()?; w.walk(&mut emitter, sb.mapping_root)?;
emitter.get_valid()
}
v => {
return Err(anyhow!("unsupported metadata version: {}", v));
}
};
out.mappings_e()?;
out.lock().unwrap().superblock_e()?; out.hints_b()?;
{
let w = ArrayWalker::new(engine.clone(), false);
let mut emitter = HintEmitter::new(&mut out, valid_mappings);
w.walk(&mut emitter, sb.hint_root)?;
}
out.hints_e()?;
out.superblock_e()?;
Ok(()) Ok(())
} }

16
src/cache/hint.rs vendored
View File

@ -1,5 +1,4 @@
use nom::IResult; use nom::IResult;
use std::marker::PhantomData;
use std::convert::TryInto; use std::convert::TryInto;
use crate::pdata::unpack::*; use crate::pdata::unpack::*;
@ -7,24 +6,21 @@ use crate::pdata::unpack::*;
//------------------------------------------ //------------------------------------------
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct Hint<Width> { pub struct Hint {
pub hint: [u8; 4], // FIXME: support various hint sizes pub hint: [u8; 4],
_not_used: PhantomData<Width>,
} }
impl<Width: typenum::Unsigned> Unpack for Hint<Width> { impl Unpack for Hint {
fn disk_size() -> u32 { fn disk_size() -> u32 {
Width::to_u32() 4
} }
// FIXME: support different width fn unpack(i: &[u8]) -> IResult<&[u8], Hint> {
fn unpack(i: &[u8]) -> IResult<&[u8], Hint<Width>> { let size = 4;
let size = Width::to_usize();
Ok(( Ok((
&i[size..], &i[size..],
Hint { Hint {
hint: i[0..size].try_into().unwrap(), hint: i[0..size].try_into().unwrap(),
_not_used: PhantomData,
}, },
)) ))
} }

View File

@ -1,5 +1,5 @@
use nom::IResult;
use nom::number::complete::*; use nom::number::complete::*;
use nom::IResult;
use crate::pdata::unpack::*; use crate::pdata::unpack::*;
@ -30,7 +30,6 @@ impl Mapping {
} }
} }
impl Unpack for Mapping { impl Unpack for Mapping {
fn disk_size() -> u32 { fn disk_size() -> u32 {
8 8

View File

@ -1,7 +1,7 @@
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use nom::{bytes::complete::*, number::complete::*, IResult};
use crate::io_engine::*; use crate::io_engine::*;
use nom::{bytes::complete::*, number::complete::*, IResult};
//------------------------------------------ //------------------------------------------

View File

@ -14,20 +14,20 @@ pub struct ArrayWalker {
} }
// FIXME: define another Result type for array visiting? // FIXME: define another Result type for array visiting?
pub trait ArrayBlockVisitor<V: Unpack> { pub trait ArrayVisitor<V: Unpack> {
fn visit(&self, index: u64, v: V) -> anyhow::Result<()>; fn visit(&self, index: u64, v: V) -> anyhow::Result<()>;
} }
struct BlockValueVisitor<V> { struct BlockValueVisitor<'a, V> {
engine: Arc<dyn IoEngine + Send + Sync>, engine: Arc<dyn IoEngine + Send + Sync>,
array_block_visitor: Box<dyn ArrayBlockVisitor<V>>, array_block_visitor: &'a mut dyn ArrayVisitor<V>,
} }
impl<V: Unpack + Copy> BlockValueVisitor<V> { impl<'a, V: Unpack + Copy> BlockValueVisitor<'a, V> {
pub fn new( pub fn new(
e: Arc<dyn IoEngine + Send + Sync>, e: Arc<dyn IoEngine + Send + Sync>,
v: Box<dyn ArrayBlockVisitor<V>>, v: &'a mut dyn ArrayVisitor<V>,
) -> BlockValueVisitor<V> { ) -> BlockValueVisitor<'a, V> {
BlockValueVisitor { BlockValueVisitor {
engine: e, engine: e,
array_block_visitor: v, array_block_visitor: v,
@ -44,7 +44,7 @@ impl<V: Unpack + Copy> BlockValueVisitor<V> {
} }
} }
impl<V: Unpack + Copy> NodeVisitor<u64> for BlockValueVisitor<V> { impl<'a, V: Unpack + Copy> NodeVisitor<u64> for BlockValueVisitor<'a, V> {
// FIXME: return errors // FIXME: return errors
fn visit( fn visit(
&self, &self,
@ -81,7 +81,7 @@ impl ArrayWalker {
} }
// FIXME: redefine the Result type for array visiting? // FIXME: redefine the Result type for array visiting?
pub fn walk<V>(&self, visitor: Box<dyn ArrayBlockVisitor<V>>, root: u64) -> Result<()> pub fn walk<V>(&self, visitor: &mut dyn ArrayVisitor<V>, root: u64) -> Result<()>
where where
V: Unpack + Copy, V: Unpack + Copy,
{ {