[cache_check (rust)] Implement basic functions
This commit is contained in:
parent
faa371c208
commit
2bb3bf65b7
62
src/bin/cache_check.rs
Normal file
62
src/bin/cache_check.rs
Normal file
@ -0,0 +1,62 @@
|
||||
extern crate clap;
|
||||
extern crate thinp;
|
||||
|
||||
use clap::{App, Arg};
|
||||
use std::path::Path;
|
||||
use thinp::cache::check::{check, CacheCheckOptions};
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
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("SB_ONLY")
|
||||
.help("Only check the superblock.")
|
||||
.long("super-block-only")
|
||||
.value_name("SB_ONLY"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("SKIP_MAPPINGS")
|
||||
.help("Don't check the mapping array")
|
||||
.long("skip-mappings")
|
||||
.value_name("SKIP_MAPPINGS"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("SKIP_HINTS")
|
||||
.help("Don't check the hint array")
|
||||
.long("skip-hints")
|
||||
.value_name("SKIP_HINTS"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("SKIP_DISCARDS")
|
||||
.help("Don't check the discard bitset")
|
||||
.long("skip-discards")
|
||||
.value_name("SKIP_DISCARDS"),
|
||||
);
|
||||
|
||||
let matches = parser.get_matches();
|
||||
let input_file = Path::new(matches.value_of("INPUT").unwrap());
|
||||
|
||||
let opts = CacheCheckOptions {
|
||||
dev: &input_file,
|
||||
async_io: false,
|
||||
sb_only: matches.is_present("SB_ONLY"),
|
||||
skip_mappings: matches.is_present("SKIP_MAPPINGS"),
|
||||
skip_hints: matches.is_present("SKIP_HINTS"),
|
||||
skip_discards: matches.is_present("SKIP_DISCARDS"),
|
||||
};
|
||||
|
||||
if let Err(reason) = check(opts) {
|
||||
eprintln!("{}", reason);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------
|
166
src/cache/check.rs
vendored
Normal file
166
src/cache/check.rs
vendored
Normal file
@ -0,0 +1,166 @@
|
||||
use anyhow::anyhow;
|
||||
use std::marker::PhantomData;
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::collections::*;
|
||||
|
||||
use crate::io_engine::{AsyncIoEngine, IoEngine, SyncIoEngine};
|
||||
use crate::cache::hint::*;
|
||||
use crate::cache::mapping::*;
|
||||
use crate::cache::superblock::*;
|
||||
use crate::pdata::array_walker::*;
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
const MAX_CONCURRENT_IO: u32 = 1024;
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
struct CheckMappingVisitor {
|
||||
allowed_flags: u32,
|
||||
seen_oblocks: Arc<Mutex<BTreeSet<u64>>>,
|
||||
}
|
||||
|
||||
impl CheckMappingVisitor {
|
||||
fn new(metadata_version: u32) -> CheckMappingVisitor {
|
||||
let mut flags: u32 = MappingFlags::Valid as u32;
|
||||
if metadata_version == 1 {
|
||||
flags |= MappingFlags::Dirty as u32;
|
||||
}
|
||||
CheckMappingVisitor {
|
||||
allowed_flags: flags,
|
||||
seen_oblocks: Arc::new(Mutex::new(BTreeSet::new())),
|
||||
}
|
||||
}
|
||||
|
||||
fn seen_oblock(&self, b: u64) -> bool {
|
||||
let seen_oblocks = self.seen_oblocks.lock().unwrap();
|
||||
return seen_oblocks.contains(&b);
|
||||
}
|
||||
|
||||
fn record_oblock(&self, b: u64) {
|
||||
let mut seen_oblocks = self.seen_oblocks.lock().unwrap();
|
||||
seen_oblocks.insert(b);
|
||||
}
|
||||
|
||||
// FIXME: is it possible to validate flags at an early phase?
|
||||
// e.g., move to ctor of Mapping?
|
||||
fn has_unknown_flags(&self, m: &Mapping) -> bool {
|
||||
return (m.flags & self.allowed_flags) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
impl ArrayBlockVisitor<Mapping> for CheckMappingVisitor {
|
||||
fn visit(&self, _index: u64, m: Mapping) -> anyhow::Result<()> {
|
||||
if !m.is_valid() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if self.seen_oblock(m.oblock) {
|
||||
return Err(anyhow!("origin block already mapped"));
|
||||
}
|
||||
|
||||
self.record_oblock(m.oblock);
|
||||
|
||||
if !self.has_unknown_flags(&m) {
|
||||
return Err(anyhow!("unknown flags in mapping"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
struct CheckHintVisitor<Width> {
|
||||
_not_used: PhantomData<Width>,
|
||||
}
|
||||
|
||||
impl<Width> CheckHintVisitor<Width> {
|
||||
fn new() -> CheckHintVisitor<Width> {
|
||||
CheckHintVisitor {
|
||||
_not_used: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Width: typenum::Unsigned> ArrayBlockVisitor<Hint<Width>> for CheckHintVisitor<Width> {
|
||||
fn visit(&self, _index: u64, _hint: Hint<Width>) -> anyhow::Result<()> {
|
||||
// TODO: check hints
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
// TODO: ignore_non_fatal, clear_needs_check, auto_repair
|
||||
pub struct CacheCheckOptions<'a> {
|
||||
pub dev: &'a Path,
|
||||
pub async_io: bool,
|
||||
pub sb_only: bool,
|
||||
pub skip_mappings: bool,
|
||||
pub skip_hints: bool,
|
||||
pub skip_discards: bool,
|
||||
}
|
||||
|
||||
// TODO: thread pool, report
|
||||
struct Context {
|
||||
engine: Arc<dyn IoEngine + Send + Sync>,
|
||||
}
|
||||
|
||||
fn mk_context(opts: &CacheCheckOptions) -> anyhow::Result<Context> {
|
||||
let engine: Arc<dyn IoEngine + Send + Sync>;
|
||||
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn check(opts: CacheCheckOptions) -> anyhow::Result<()> {
|
||||
let ctx = mk_context(&opts)?;
|
||||
|
||||
let engine = &ctx.engine;
|
||||
|
||||
let sb = read_superblock(engine.as_ref(), SUPERBLOCK_LOCATION)?;
|
||||
|
||||
if opts.sb_only {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// TODO: factor out into check_mappings()
|
||||
if !opts.skip_mappings {
|
||||
let w = ArrayWalker::new(engine.clone(), false);
|
||||
let c = Box::new(CheckMappingVisitor::new(sb.version));
|
||||
w.walk(c, sb.mapping_root)?;
|
||||
|
||||
if sb.version >= 2 {
|
||||
// TODO: check dirty bitset
|
||||
}
|
||||
}
|
||||
|
||||
if !opts.skip_hints && sb.hint_root != 0 {
|
||||
let w = ArrayWalker::new(engine.clone(), false);
|
||||
type Width = typenum::U4; // FIXME: check sb.policy_hint_size
|
||||
let c = Box::new(CheckHintVisitor::<Width>::new());
|
||||
w.walk(c, sb.hint_root)?;
|
||||
}
|
||||
|
||||
if !opts.skip_discards {
|
||||
// TODO: check discard bitset
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
//------------------------------------------
|
1
src/cache/mod.rs
vendored
1
src/cache/mod.rs
vendored
@ -1,3 +1,4 @@
|
||||
pub mod check;
|
||||
pub mod hint;
|
||||
pub mod mapping;
|
||||
pub mod superblock;
|
||||
|
Loading…
Reference in New Issue
Block a user