[cache_check (rust)] Check space map counts
- Support space map checking and auto-repair
This commit is contained in:
parent
636d50a38d
commit
cf4b937ade
@ -49,6 +49,11 @@ fn main() {
|
||||
.help("Only return a non-zero exit code if a fatal error is found.")
|
||||
.long("ignore-non-fatal-errors"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("AUTO_REPAIR")
|
||||
.help("Auto repair trivial issues.")
|
||||
.long("auto-repair"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("QUIET")
|
||||
.help("Suppress output messages, return only exit code.")
|
||||
@ -76,6 +81,7 @@ fn main() {
|
||||
skip_hints: matches.is_present("SKIP_HINTS"),
|
||||
skip_discards: matches.is_present("SKIP_DISCARDS"),
|
||||
ignore_non_fatal: matches.is_present("IGNORE_NON_FATAL"),
|
||||
auto_repair: matches.is_present("AUTO_REPAIR"),
|
||||
report,
|
||||
};
|
||||
|
||||
|
45
src/cache/check.rs
vendored
45
src/cache/check.rs
vendored
@ -10,6 +10,9 @@ use crate::io_engine::{AsyncIoEngine, IoEngine, SyncIoEngine};
|
||||
use crate::pdata::array::{self, ArrayBlock, ArrayError};
|
||||
use crate::pdata::array_walker::*;
|
||||
use crate::pdata::bitset::*;
|
||||
use crate::pdata::space_map::*;
|
||||
use crate::pdata::space_map_checker::*;
|
||||
use crate::pdata::unpack::unpack;
|
||||
use crate::report::*;
|
||||
|
||||
//------------------------------------------
|
||||
@ -18,6 +21,14 @@ const MAX_CONCURRENT_IO: u32 = 1024;
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
fn inc_superblock(sm: &ASpaceMap) -> anyhow::Result<()> {
|
||||
let mut sm = sm.lock().unwrap();
|
||||
sm.inc(SUPERBLOCK_LOCATION, 1)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
mod format1 {
|
||||
use super::*;
|
||||
|
||||
@ -204,6 +215,7 @@ pub struct CacheCheckOptions<'a> {
|
||||
pub skip_hints: bool,
|
||||
pub skip_discards: bool,
|
||||
pub ignore_non_fatal: bool,
|
||||
pub auto_repair: bool,
|
||||
pub report: Arc<Report>,
|
||||
}
|
||||
|
||||
@ -240,6 +252,8 @@ pub fn check(opts: CacheCheckOptions) -> anyhow::Result<()> {
|
||||
let ctx = mk_context(&opts)?;
|
||||
|
||||
let engine = &ctx.engine;
|
||||
let metadata_sm = core_sm(engine.get_nr_blocks(), u8::MAX as u32);
|
||||
inc_superblock(&metadata_sm)?;
|
||||
|
||||
let sb = read_superblock(engine.as_ref(), SUPERBLOCK_LOCATION)?;
|
||||
check_superblock(&sb)?;
|
||||
@ -258,7 +272,7 @@ pub fn check(opts: CacheCheckOptions) -> anyhow::Result<()> {
|
||||
|
||||
// TODO: factor out into check_mappings()
|
||||
if !opts.skip_mappings {
|
||||
let w = ArrayWalker::new(engine.clone(), opts.ignore_non_fatal);
|
||||
let w = ArrayWalker::new_with_sm(engine.clone(), metadata_sm.clone(), opts.ignore_non_fatal)?;
|
||||
match sb.version {
|
||||
1 => {
|
||||
let mut c = format1::MappingChecker::new(nr_origin_blocks);
|
||||
@ -267,13 +281,13 @@ pub fn check(opts: CacheCheckOptions) -> anyhow::Result<()> {
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
// TODO: allow ignore_none_fatal
|
||||
let (dirty_bits, err) = read_bitset(
|
||||
let (dirty_bits, err) = read_bitset_with_sm(
|
||||
engine.clone(),
|
||||
sb.dirty_root.unwrap(),
|
||||
sb.cache_blocks as usize,
|
||||
metadata_sm.clone(),
|
||||
opts.ignore_non_fatal,
|
||||
);
|
||||
)?;
|
||||
if err.is_some() {
|
||||
ctx.report.fatal(&format!("{}", err.unwrap()));
|
||||
}
|
||||
@ -292,7 +306,7 @@ pub fn check(opts: CacheCheckOptions) -> anyhow::Result<()> {
|
||||
if sb.policy_hint_size != 4 {
|
||||
return Err(anyhow!("cache_check only supports policy hint size of 4"));
|
||||
}
|
||||
let w = ArrayWalker::new(engine.clone(), opts.ignore_non_fatal);
|
||||
let w = ArrayWalker::new_with_sm(engine.clone(), metadata_sm.clone(), opts.ignore_non_fatal)?;
|
||||
let mut c = HintChecker::new();
|
||||
if let Err(e) = w.walk(&mut c, sb.hint_root) {
|
||||
ctx.report.fatal(&format!("{}", e));
|
||||
@ -302,17 +316,34 @@ pub fn check(opts: CacheCheckOptions) -> anyhow::Result<()> {
|
||||
// The discard bitset might not be available if the cache has never been suspended,
|
||||
// e.g., a crash of freshly created cache.
|
||||
if !opts.skip_discards && sb.discard_root != 0 {
|
||||
let (discard_bits, err) = read_bitset(
|
||||
let (_discard_bits, err) = read_bitset_with_sm(
|
||||
engine.clone(),
|
||||
sb.discard_root,
|
||||
sb.cache_blocks as usize,
|
||||
metadata_sm.clone(),
|
||||
opts.ignore_non_fatal,
|
||||
);
|
||||
)?;
|
||||
if err.is_some() {
|
||||
ctx.report.fatal(&format!("{}", err.unwrap()));
|
||||
}
|
||||
}
|
||||
|
||||
let root = unpack::<SMRoot>(&sb.metadata_sm_root[0..])?;
|
||||
let metadata_leaks = check_metadata_space_map(
|
||||
engine.clone(),
|
||||
ctx.report.clone(),
|
||||
root,
|
||||
metadata_sm.clone(),
|
||||
opts.ignore_non_fatal,
|
||||
)?;
|
||||
|
||||
if opts.auto_repair {
|
||||
if !metadata_leaks.is_empty() {
|
||||
ctx.report.info("Repairing metadata leaks.");
|
||||
repair_space_map(ctx.engine.clone(), metadata_leaks, metadata_sm.clone())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -4,12 +4,14 @@ use crate::io_engine::*;
|
||||
use crate::pdata::array::{self, *};
|
||||
use crate::pdata::btree::{self, *};
|
||||
use crate::pdata::btree_walker::*;
|
||||
use crate::pdata::space_map::*;
|
||||
use crate::pdata::unpack::*;
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
pub struct ArrayWalker {
|
||||
engine: Arc<dyn IoEngine + Send + Sync>,
|
||||
sm: Arc<Mutex<dyn SpaceMap + Send + Sync>>,
|
||||
ignore_non_fatal: bool,
|
||||
}
|
||||
|
||||
@ -23,17 +25,20 @@ pub trait ArrayVisitor<V: Unpack> {
|
||||
struct BlockValueVisitor<'a, V> {
|
||||
engine: Arc<dyn IoEngine + Send + Sync>,
|
||||
array_visitor: &'a mut dyn ArrayVisitor<V>,
|
||||
sm: Arc<Mutex<dyn SpaceMap + Send + Sync>>,
|
||||
array_errs: Mutex<Vec<ArrayError>>,
|
||||
}
|
||||
|
||||
impl<'a, V: Unpack + Copy> BlockValueVisitor<'a, V> {
|
||||
pub fn new(
|
||||
e: Arc<dyn IoEngine + Send + Sync>,
|
||||
sm: Arc<Mutex<dyn SpaceMap + Send + Sync>>,
|
||||
v: &'a mut dyn ArrayVisitor<V>,
|
||||
) -> BlockValueVisitor<'a, V> {
|
||||
BlockValueVisitor {
|
||||
engine: e,
|
||||
array_visitor: v,
|
||||
sm: sm,
|
||||
array_errs: Mutex::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
@ -85,6 +90,8 @@ impl<'a, V: Unpack + Copy> NodeVisitor<u64> for BlockValueVisitor<'a, V> {
|
||||
if let Err(e) = self.array_visitor.visit(keys[i], array_block) {
|
||||
self.array_errs.lock().unwrap().push(e);
|
||||
}
|
||||
let mut sm = self.sm.lock().unwrap();
|
||||
sm.inc(b.loc, 1).unwrap();
|
||||
},
|
||||
Err(e) => {
|
||||
self.array_errs.lock().unwrap().push(e);
|
||||
@ -113,21 +120,44 @@ impl<'a, V: Unpack + Copy> NodeVisitor<u64> for BlockValueVisitor<'a, V> {
|
||||
|
||||
impl ArrayWalker {
|
||||
pub fn new(engine: Arc<dyn IoEngine + Send + Sync>, ignore_non_fatal: bool) -> ArrayWalker {
|
||||
let nr_blocks = engine.get_nr_blocks() as u64;
|
||||
let r: ArrayWalker = ArrayWalker {
|
||||
engine,
|
||||
sm: Arc::new(Mutex::new(RestrictedSpaceMap::new(nr_blocks))),
|
||||
ignore_non_fatal,
|
||||
};
|
||||
r
|
||||
}
|
||||
|
||||
pub fn new_with_sm(
|
||||
engine: Arc<dyn IoEngine + Send + Sync>,
|
||||
sm: Arc<Mutex<dyn SpaceMap + Send + Sync>>,
|
||||
ignore_non_fatal: bool,
|
||||
) -> array::Result<ArrayWalker> {
|
||||
{
|
||||
let sm = sm.lock().unwrap();
|
||||
assert_eq!(sm.get_nr_blocks().unwrap(), engine.get_nr_blocks());
|
||||
}
|
||||
|
||||
Ok(ArrayWalker {
|
||||
engine,
|
||||
sm,
|
||||
ignore_non_fatal,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn walk<V>(&self, visitor: &mut dyn ArrayVisitor<V>, root: u64) -> array::Result<()>
|
||||
where
|
||||
V: Unpack + Copy,
|
||||
{
|
||||
let w = BTreeWalker::new(self.engine.clone(), self.ignore_non_fatal);
|
||||
let w = BTreeWalker::new_with_sm(
|
||||
self.engine.clone(),
|
||||
self.sm.clone(),
|
||||
self.ignore_non_fatal
|
||||
)?;
|
||||
let mut path = Vec::new();
|
||||
path.push(0);
|
||||
let v = BlockValueVisitor::<V>::new(self.engine.clone(), visitor);
|
||||
let v = BlockValueVisitor::<V>::new(self.engine.clone(), self.sm.clone(), visitor);
|
||||
let btree_err = w.walk(&mut path, &v, root).map_err(|e| ArrayError::BTreeError(e));
|
||||
|
||||
let mut array_errs = v.array_errs.into_inner().unwrap();
|
||||
|
@ -4,6 +4,7 @@ use std::sync::{Arc, Mutex};
|
||||
use crate::io_engine::IoEngine;
|
||||
use crate::pdata::array::{self, ArrayBlock};
|
||||
use crate::pdata::array_walker::{ArrayVisitor, ArrayWalker};
|
||||
use crate::pdata::space_map::*;
|
||||
|
||||
pub struct CheckedBitSet {
|
||||
bits: FixedBitSet,
|
||||
@ -77,7 +78,7 @@ pub fn read_bitset(
|
||||
nr_entries: usize,
|
||||
ignore_none_fatal: bool,
|
||||
)-> (CheckedBitSet, Option<array::ArrayError>) {
|
||||
let w = ArrayWalker::new(engine.clone(), ignore_none_fatal);
|
||||
let w = ArrayWalker::new(engine, ignore_none_fatal);
|
||||
let mut v = BitsetVisitor::new(nr_entries);
|
||||
let err = w.walk(&mut v, root);
|
||||
let e = match err {
|
||||
@ -86,3 +87,21 @@ pub fn read_bitset(
|
||||
};
|
||||
return (v.get_bitset(), e);
|
||||
}
|
||||
|
||||
// TODO: multi-threaded is possible
|
||||
pub fn read_bitset_with_sm(
|
||||
engine: Arc<dyn IoEngine + Send + Sync>,
|
||||
root: u64,
|
||||
nr_entries: usize,
|
||||
sm: Arc<Mutex<dyn SpaceMap + Send + Sync>>,
|
||||
ignore_none_fatal: bool,
|
||||
)-> array::Result<(CheckedBitSet, Option<array::ArrayError>)> {
|
||||
let w = ArrayWalker::new_with_sm(engine, sm, ignore_none_fatal)?;
|
||||
let mut v = BitsetVisitor::new(nr_entries);
|
||||
let err = w.walk(&mut v, root);
|
||||
let e = match err {
|
||||
Ok(()) => None,
|
||||
Err(e) => Some(e),
|
||||
};
|
||||
return Ok((v.get_bitset(), e));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user