diff --git a/src/pdata/btree.rs b/src/pdata/btree.rs index 2706bb6..d22956d 100644 --- a/src/pdata/btree.rs +++ b/src/pdata/btree.rs @@ -1,11 +1,11 @@ use anyhow::{anyhow, Result}; -use fixedbitset::FixedBitSet; use nom::{number::complete::*, IResult}; use std::collections::BTreeMap; use std::sync::{Arc, Mutex}; use crate::checksum; use crate::io_engine::*; +use crate::pdata::space_map::*; use crate::pdata::unpack::*; // FIXME: check that keys are in ascending order between nodes. @@ -153,7 +153,7 @@ pub trait NodeVisitor { #[derive(Clone)] pub struct BTreeWalker { pub engine: Arc, - pub seen: Arc>, + pub sm: Arc>, ignore_non_fatal: bool, } @@ -162,37 +162,35 @@ impl BTreeWalker { let nr_blocks = engine.get_nr_blocks() as usize; let r: BTreeWalker = BTreeWalker { engine, - seen: Arc::new(Mutex::new(FixedBitSet::with_capacity(nr_blocks))), + sm: Arc::new(Mutex::new(RestrictedSpaceMap::new(nr_blocks as u64))), ignore_non_fatal, }; r } - pub fn new_with_seen( + pub fn new_with_sm( engine: Arc, - seen: Arc>, + sm: Arc>, ignore_non_fatal: bool, - ) -> BTreeWalker { + ) -> Result { { - let seen = seen.lock().unwrap(); - assert_eq!(seen.len(), engine.get_nr_blocks() as usize); + let sm = sm.lock().unwrap(); + assert_eq!(sm.get_nr_blocks()?, engine.get_nr_blocks()); } - BTreeWalker { + Ok(BTreeWalker { engine, - seen, + sm, ignore_non_fatal, - } + }) } - fn is_seen(&self, b: u64) -> bool { - let mut seen = self.seen.lock().unwrap(); - if !seen[b as usize] { - seen.insert(b as usize); - return false; - } - - true + // Atomically increments the ref count, and returns the _old_ count. + fn sm_inc(&self, b: u64) -> Result { + let mut sm = self.sm.lock().unwrap(); + let count = sm.get(b)?; + sm.inc(b, 1)?; + Ok(count) } fn walk_nodes(&mut self, visitor: &mut NV, bs: &[u64]) -> Result<()> @@ -201,14 +199,11 @@ impl BTreeWalker { V: Unpack, { let mut blocks = Vec::new(); - let mut seen = self.seen.lock().unwrap(); for b in bs { - if !seen[*b as usize] { + if self.sm_inc(*b)? == 0 { blocks.push(Block::new(*b)); - seen.insert(*b as usize); } } - drop(seen); self.engine.read_many(&mut blocks)?; @@ -249,7 +244,7 @@ impl BTreeWalker { NV: NodeVisitor, V: Unpack, { - if self.is_seen(root.loc) { + if self.sm_inc(root.loc)? > 0 { Ok(()) } else { self.walk_node(visitor, &root, true) @@ -261,7 +256,7 @@ impl BTreeWalker { NV: NodeVisitor, V: Unpack, { - if self.is_seen(root) { + if self.sm_inc(root)? > 0 { Ok(()) } else { let mut root = Block::new(root); @@ -316,4 +311,17 @@ pub fn btree_to_map( Ok(visitor.values) } +pub fn btree_to_map_with_sm( + engine: Arc, + sm: Arc>, + ignore_non_fatal: bool, + root: u64, +) -> Result> { + let mut walker = BTreeWalker::new_with_sm(engine, sm, ignore_non_fatal)?; + let mut visitor = ValueCollector::::new(); + + walker.walk(&mut visitor, root)?; + Ok(visitor.values) +} + //------------------------------------------ diff --git a/src/pdata/space_map.rs b/src/pdata/space_map.rs index 91ac606..a111c86 100644 --- a/src/pdata/space_map.rs +++ b/src/pdata/space_map.rs @@ -1,4 +1,5 @@ use anyhow::{anyhow, Result}; +use fixedbitset::FixedBitSet; use nom::{number::complete::*, IResult}; use std::sync::{Arc, Mutex}; @@ -154,10 +155,13 @@ impl Unpack for Bitmap { //------------------------------------------ pub trait SpaceMap { + fn get_nr_blocks(&self) -> Result; fn get(&self, b: u64) -> Result; fn inc(&mut self, begin: u64, len: u64) -> Result<()>; } +//------------------------------------------ + pub struct CoreSpaceMap { counts: Vec, } @@ -177,6 +181,10 @@ impl SpaceMap for CoreSpaceMap where V: Copy + Default + std::ops::AddAssign + From + Into, { + fn get_nr_blocks(&self) -> Result { + Ok(self.counts.len() as u64) + } + fn get(&self, b: u64) -> Result { Ok(self.counts[b as usize].into()) } @@ -189,7 +197,7 @@ where } } -pub fn core_sm(nr_entries: u64, max_count: u32) -> Arc> { +pub fn core_sm(nr_entries: u64, max_count: u32) -> Arc> { if max_count <= u8::MAX as u32 { Arc::new(Mutex::new(CoreSpaceMap::::new(nr_entries))) } else if max_count <= u16::MAX as u32 { @@ -200,3 +208,41 @@ pub fn core_sm(nr_entries: u64, max_count: u32) -> Arc RestrictedSpaceMap { + RestrictedSpaceMap { + counts: FixedBitSet::with_capacity(nr_entries as usize), + } + } +} + +impl SpaceMap for RestrictedSpaceMap { + fn get_nr_blocks(&self) -> Result { + Ok(self.counts.len() as u64) + } + + fn get(&self, b: u64) -> Result { + if self.counts.contains(b as usize) { + Ok(1) + } else { + Ok(0) + } + } + + fn inc(&mut self, begin: u64, len: u64) -> Result<()> { + for b in begin..(begin + len) { + self.counts.insert(b as usize); + } + Ok(()) + } +} + +//------------------------------------------ diff --git a/src/thin/check.rs b/src/thin/check.rs index e7110c2..5d94ed9 100644 --- a/src/thin/check.rs +++ b/src/thin/check.rs @@ -1,5 +1,4 @@ use anyhow::{anyhow, Result}; -use fixedbitset::FixedBitSet; use nom::{number::complete::*, IResult}; use std::collections::BTreeMap; use std::path::Path; @@ -8,9 +7,9 @@ use threadpool::ThreadPool; use crate::checksum; use crate::io_engine::{AsyncIoEngine, Block, IoEngine, SyncIoEngine}; -use crate::pdata::unpack::*; -use crate::pdata::btree::{btree_to_map, BTreeWalker, Node, NodeVisitor}; +use crate::pdata::btree::{btree_to_map, btree_to_map_with_sm, BTreeWalker, Node, NodeVisitor}; use crate::pdata::space_map::*; +use crate::pdata::unpack::*; use crate::thin::superblock::*; //------------------------------------------ @@ -200,11 +199,26 @@ pub fn check(opts: &ThinCheckOptions) -> Result<()> { // superblock let sb = read_superblock(engine.as_ref(), SUPERBLOCK_LOCATION)?; - // device details + // Device details. We read this once to get the number of thin devices, and hence the + // maximum metadata ref count. Then create metadata space map, and reread to increment + // the ref counts for that metadata. let devs = btree_to_map::(engine.clone(), false, sb.details_root)?; let nr_devs = devs.len(); + let metadata_sm = core_sm(engine.get_nr_blocks(), nr_devs as u32); + let _devs = btree_to_map_with_sm::( + engine.clone(), + metadata_sm.clone(), + false, + sb.details_root, + )?; println!("found {} devices", nr_devs); + // increment superblock + { + let mut sm = metadata_sm.lock().unwrap(); + sm.inc(SUPERBLOCK_LOCATION, 1)?; + } + // mapping top level let roots = btree_to_map::(engine.clone(), false, sb.mapping_root)?; @@ -214,15 +228,12 @@ pub fn check(opts: &ThinCheckOptions) -> Result<()> { // FIXME: with a thread pool we need to return errors another way. let nr_workers = nr_threads; let pool = ThreadPool::new(nr_workers); - let seen = Arc::new(Mutex::new(FixedBitSet::with_capacity( - engine.get_nr_blocks() as usize, - ))); let root = unpack::(&sb.data_sm_root[0..])?; data_sm = core_sm(root.nr_blocks, nr_devs as u32); for (thin_id, root) in roots { - let mut w = BTreeWalker::new_with_seen(engine.clone(), seen.clone(), false); + let mut w = BTreeWalker::new_with_sm(engine.clone(), metadata_sm.clone(), false)?; let data_sm = data_sm.clone(); pool.execute(move || { let mut v = BottomLevelVisitor { data_sm }; @@ -325,7 +336,6 @@ pub fn check(opts: &ThinCheckOptions) -> Result<()> { } // Check the metadata space map. - Ok(()) }