[era_invalidate (rust)] First code drop
This commit is contained in:
parent
0791208ca4
commit
4559039066
85
src/commands/era_invalidate.rs
Normal file
85
src/commands/era_invalidate.rs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
extern crate clap;
|
||||||
|
|
||||||
|
use clap::{App, Arg};
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process;
|
||||||
|
|
||||||
|
use crate::commands::utils::*;
|
||||||
|
use crate::era::invalidate::{invalidate, EraInvalidateOptions};
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
pub fn run(args: &[std::ffi::OsString]) {
|
||||||
|
let parser = App::new("era_invalidate")
|
||||||
|
.version(crate::version::tools_version())
|
||||||
|
.about("List blocks that may have changed since a given era")
|
||||||
|
// flags
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("ASYNC_IO")
|
||||||
|
.help("Force use of io_uring for synchronous io")
|
||||||
|
.long("async-io")
|
||||||
|
.hidden(true),
|
||||||
|
)
|
||||||
|
// options
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("OUTPUT")
|
||||||
|
.help("Specify the output file rather than stdout")
|
||||||
|
.short("o")
|
||||||
|
.long("output")
|
||||||
|
.value_name("FILE"),
|
||||||
|
)
|
||||||
|
// arguments
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("INPUT")
|
||||||
|
.help("Specify the input device to dump")
|
||||||
|
.required(true)
|
||||||
|
.index(1),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("WRITTEN_SINCE")
|
||||||
|
.help("Blocks written since the given era will be listed")
|
||||||
|
.long("written-since")
|
||||||
|
.required(true)
|
||||||
|
.value_name("ERA"),
|
||||||
|
);
|
||||||
|
|
||||||
|
let matches = parser.get_matches_from(args);
|
||||||
|
let input_file = Path::new(matches.value_of("INPUT").unwrap());
|
||||||
|
let output_file = if matches.is_present("OUTPUT") {
|
||||||
|
Some(Path::new(matches.value_of("OUTPUT").unwrap()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a temporary report just in case these checks
|
||||||
|
// need to report anything.
|
||||||
|
let report = std::sync::Arc::new(crate::report::mk_simple_report());
|
||||||
|
check_input_file(input_file, &report);
|
||||||
|
check_file_not_tiny(input_file, &report);
|
||||||
|
drop(report);
|
||||||
|
|
||||||
|
let threshold = matches
|
||||||
|
.value_of("WRITTEN_SINCE")
|
||||||
|
.map(|s| {
|
||||||
|
s.parse::<u32>().unwrap_or_else(|_| {
|
||||||
|
eprintln!("Couldn't parse written_since");
|
||||||
|
process::exit(1);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
|
let opts = EraInvalidateOptions {
|
||||||
|
input: input_file,
|
||||||
|
output: output_file,
|
||||||
|
async_io: matches.is_present("ASYNC_IO"),
|
||||||
|
threshold,
|
||||||
|
metadata_snap: matches.is_present("METADATA_SNAP"),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(reason) = invalidate(&opts) {
|
||||||
|
eprintln!("{}", reason);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
@ -4,6 +4,7 @@ pub mod cache_repair;
|
|||||||
pub mod cache_restore;
|
pub mod cache_restore;
|
||||||
pub mod era_check;
|
pub mod era_check;
|
||||||
pub mod era_dump;
|
pub mod era_dump;
|
||||||
|
pub mod era_invalidate;
|
||||||
pub mod era_repair;
|
pub mod era_repair;
|
||||||
pub mod era_restore;
|
pub mod era_restore;
|
||||||
pub mod thin_check;
|
pub mod thin_check;
|
||||||
|
285
src/era/invalidate.rs
Normal file
285
src/era/invalidate.rs
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use quick_xml::events::{BytesEnd, BytesStart, Event};
|
||||||
|
use quick_xml::Writer;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{BufWriter, Write};
|
||||||
|
use std::path::Path;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use crate::era::superblock::*;
|
||||||
|
use crate::era::writeset::*;
|
||||||
|
use crate::io_engine::{AsyncIoEngine, IoEngine, SyncIoEngine};
|
||||||
|
use crate::math::div_up;
|
||||||
|
use crate::pdata::array::{self, value_err, ArrayBlock};
|
||||||
|
use crate::pdata::array_walker::*;
|
||||||
|
use crate::pdata::btree_walker::*;
|
||||||
|
use crate::xml::mk_attr;
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
const MAX_CONCURRENT_IO: u32 = 1024;
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
struct BitsetCollator<'a> {
|
||||||
|
composed_bits: Box<Mutex<&'a mut [u64]>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> BitsetCollator<'a> {
|
||||||
|
fn new(bitset: &mut [u64]) -> BitsetCollator {
|
||||||
|
BitsetCollator {
|
||||||
|
composed_bits: Box::new(Mutex::new(bitset)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ArrayVisitor<u64> for BitsetCollator<'a> {
|
||||||
|
fn visit(&self, index: u64, b: ArrayBlock<u64>) -> array::Result<()> {
|
||||||
|
let mut bitset = self.composed_bits.lock().unwrap();
|
||||||
|
let idx = index as usize * b.header.max_entries as usize; // index of u64 in bitset array
|
||||||
|
for (entry, dest) in b.values.iter().zip(bitset.iter_mut().skip(idx)) {
|
||||||
|
*dest |= entry;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
struct EraArrayCollator<'a> {
|
||||||
|
composed_bits: Box<Mutex<&'a mut [u64]>>,
|
||||||
|
threshold: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> EraArrayCollator<'a> {
|
||||||
|
fn new(bitset: &mut [u64], threshold: u32) -> EraArrayCollator {
|
||||||
|
EraArrayCollator {
|
||||||
|
composed_bits: Box::new(Mutex::new(bitset)),
|
||||||
|
threshold,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ArrayVisitor<u32> for EraArrayCollator<'a> {
|
||||||
|
fn visit(&self, index: u64, b: ArrayBlock<u32>) -> array::Result<()> {
|
||||||
|
let blk_begin = index as usize * b.header.max_entries as usize; // range of data blocks
|
||||||
|
let blk_end = blk_begin + b.header.max_entries as usize;
|
||||||
|
|
||||||
|
let mut bitset = self.composed_bits.lock().unwrap();
|
||||||
|
let mut bitset_iter = bitset.iter_mut();
|
||||||
|
let mut idx = blk_begin >> 6; // index of u64 in bitset array
|
||||||
|
let mut dest = bitset_iter
|
||||||
|
.nth(idx)
|
||||||
|
.ok_or_else(|| value_err("array index out of bounds".to_string()))?;
|
||||||
|
let mut buf = *dest;
|
||||||
|
for (era, blk) in b.values.iter().zip(blk_begin..blk_end) {
|
||||||
|
if *era < self.threshold {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let steps = (blk >> 6) - idx;
|
||||||
|
if steps > 0 {
|
||||||
|
*dest = buf;
|
||||||
|
idx += steps;
|
||||||
|
dest = bitset_iter
|
||||||
|
.nth(steps - 1)
|
||||||
|
.ok_or_else(|| value_err("array index out of bounds".to_string()))?;
|
||||||
|
buf = *dest;
|
||||||
|
}
|
||||||
|
buf |= 1 << (blk & 0x3F);
|
||||||
|
}
|
||||||
|
*dest = buf;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
fn collate_writeset(
|
||||||
|
engine: Arc<dyn IoEngine + Send + Sync>,
|
||||||
|
writeset_root: u64,
|
||||||
|
marked_bits: &mut [u64],
|
||||||
|
) -> Result<()> {
|
||||||
|
let w = ArrayWalker::new(engine, false);
|
||||||
|
let mut c = BitsetCollator::new(marked_bits);
|
||||||
|
w.walk(&mut c, writeset_root)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collate_era_array(
|
||||||
|
engine: Arc<dyn IoEngine + Send + Sync>,
|
||||||
|
era_array_root: u64,
|
||||||
|
marked_bits: &mut [u64],
|
||||||
|
threshold: u32,
|
||||||
|
) -> Result<()> {
|
||||||
|
let w = ArrayWalker::new(engine, false);
|
||||||
|
let mut c = EraArrayCollator::new(marked_bits, threshold);
|
||||||
|
w.walk(&mut c, era_array_root)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mark_blocks_since(
|
||||||
|
engine: Arc<dyn IoEngine + Send + Sync>,
|
||||||
|
sb: &Superblock,
|
||||||
|
threshold: u32,
|
||||||
|
) -> Result<Vec<u64>> {
|
||||||
|
let mut marked_bits = Vec::<u64>::new();
|
||||||
|
marked_bits.resize(div_up(sb.nr_blocks as usize, 64), 0);
|
||||||
|
|
||||||
|
let mut path = vec![0];
|
||||||
|
let wsets = btree_to_map::<Writeset>(&mut path, engine.clone(), false, sb.writeset_tree_root)?;
|
||||||
|
for (era, ws) in wsets.iter() {
|
||||||
|
if (*era as u32) < threshold {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
collate_writeset(engine.clone(), ws.root, &mut marked_bits)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(archived_begin) = wsets.keys().next() {
|
||||||
|
if *archived_begin as u32 > threshold {
|
||||||
|
collate_era_array(
|
||||||
|
engine.clone(),
|
||||||
|
sb.era_array_root,
|
||||||
|
&mut marked_bits,
|
||||||
|
threshold,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(marked_bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_start<W: Write>(w: &mut Writer<W>) -> Result<()> {
|
||||||
|
let elem = BytesStart::owned_name(b"blocks".to_vec());
|
||||||
|
w.write_event(Event::Start(elem))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_end<W: Write>(w: &mut Writer<W>) -> Result<()> {
|
||||||
|
let elem = BytesEnd::borrowed(b"blocks");
|
||||||
|
w.write_event(Event::End(elem))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_range<W: Write>(w: &mut Writer<W>, begin: u32, end: u32) -> Result<()> {
|
||||||
|
if end > begin + 1 {
|
||||||
|
let mut elem = BytesStart::owned_name(b"range".to_vec());
|
||||||
|
elem.push_attribute(mk_attr(b"begin", begin));
|
||||||
|
elem.push_attribute(mk_attr(b"end", end));
|
||||||
|
w.write_event(Event::Empty(elem))?;
|
||||||
|
} else if end > begin {
|
||||||
|
let mut elem = BytesStart::owned_name(b"block".to_vec());
|
||||||
|
elem.push_attribute(mk_attr(b"block", begin));
|
||||||
|
w.write_event(Event::Empty(elem))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_blocks<W: Write>(marked_bits: &[u64], nr_blocks: u32, w: &mut Writer<W>) -> Result<()> {
|
||||||
|
let mut begin: u32 = 0;
|
||||||
|
let mut end: u32 = 0;
|
||||||
|
|
||||||
|
emit_start(w)?;
|
||||||
|
|
||||||
|
for (index, entry) in marked_bits.iter().enumerate() {
|
||||||
|
let mut n = *entry;
|
||||||
|
|
||||||
|
if n == u64::max_value() {
|
||||||
|
end = std::cmp::min(end + 64, nr_blocks);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
while n > 0 {
|
||||||
|
let zeros = n.trailing_zeros();
|
||||||
|
if zeros > 0 {
|
||||||
|
if end > begin {
|
||||||
|
emit_range(w, begin, end)?;
|
||||||
|
}
|
||||||
|
n >>= zeros;
|
||||||
|
end += zeros;
|
||||||
|
begin = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ones = n.trailing_ones();
|
||||||
|
n >>= ones;
|
||||||
|
end = std::cmp::min(end + ones, nr_blocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
let endpos = (index << 6) as u32 + 64;
|
||||||
|
if end < endpos {
|
||||||
|
if end > begin {
|
||||||
|
emit_range(w, begin, end)?;
|
||||||
|
}
|
||||||
|
begin = endpos;
|
||||||
|
end = begin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if end > begin {
|
||||||
|
emit_range(w, begin, end)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit_end(w)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
pub struct EraInvalidateOptions<'a> {
|
||||||
|
pub input: &'a Path,
|
||||||
|
pub output: Option<&'a Path>,
|
||||||
|
pub async_io: bool,
|
||||||
|
pub threshold: u32,
|
||||||
|
pub metadata_snap: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Context {
|
||||||
|
engine: Arc<dyn IoEngine + Send + Sync>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mk_context(opts: &EraInvalidateOptions) -> anyhow::Result<Context> {
|
||||||
|
let engine: Arc<dyn IoEngine + Send + Sync>;
|
||||||
|
|
||||||
|
if opts.async_io {
|
||||||
|
engine = Arc::new(AsyncIoEngine::new_with(
|
||||||
|
opts.input,
|
||||||
|
MAX_CONCURRENT_IO,
|
||||||
|
false,
|
||||||
|
!opts.metadata_snap,
|
||||||
|
)?);
|
||||||
|
} else {
|
||||||
|
let nr_threads = std::cmp::max(8, num_cpus::get() * 2);
|
||||||
|
engine = Arc::new(SyncIoEngine::new_with(
|
||||||
|
opts.input,
|
||||||
|
nr_threads,
|
||||||
|
false,
|
||||||
|
!opts.metadata_snap,
|
||||||
|
)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Context { engine })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invalidate(opts: &EraInvalidateOptions) -> Result<()> {
|
||||||
|
let ctx = mk_context(opts)?;
|
||||||
|
|
||||||
|
let mut sb = read_superblock(ctx.engine.as_ref(), SUPERBLOCK_LOCATION)?;
|
||||||
|
if opts.metadata_snap {
|
||||||
|
sb = read_superblock(ctx.engine.as_ref(), sb.metadata_snap)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let w: Box<dyn Write>;
|
||||||
|
if opts.output.is_some() {
|
||||||
|
w = Box::new(BufWriter::new(File::create(opts.output.unwrap())?));
|
||||||
|
} else {
|
||||||
|
w = Box::new(BufWriter::new(std::io::stdout()));
|
||||||
|
}
|
||||||
|
let mut writer = Writer::new_with_indent(w, 0x20, 2);
|
||||||
|
|
||||||
|
let marked_bits = mark_blocks_since(ctx.engine, &sb, opts.threshold)?;
|
||||||
|
emit_blocks(&marked_bits, sb.nr_blocks, &mut writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
@ -1,5 +1,6 @@
|
|||||||
pub mod check;
|
pub mod check;
|
||||||
pub mod dump;
|
pub mod dump;
|
||||||
|
pub mod invalidate;
|
||||||
pub mod ir;
|
pub mod ir;
|
||||||
pub mod repair;
|
pub mod repair;
|
||||||
pub mod restore;
|
pub mod restore;
|
||||||
|
Loading…
Reference in New Issue
Block a user