2021-04-13 11:09:14 +05:30
|
|
|
use anyhow::anyhow;
|
2021-03-04 16:43:08 +05:30
|
|
|
use fixedbitset::FixedBitSet;
|
2021-02-25 15:44:44 +05:30
|
|
|
use std::path::Path;
|
|
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
|
|
|
|
use crate::cache::hint::Hint;
|
|
|
|
use crate::cache::mapping::Mapping;
|
|
|
|
use crate::cache::superblock::*;
|
|
|
|
use crate::cache::xml::{self, MetadataVisitor};
|
2021-03-04 16:43:08 +05:30
|
|
|
use crate::io_engine::{AsyncIoEngine, IoEngine, SyncIoEngine};
|
2021-04-16 15:22:54 +05:30
|
|
|
use crate::pdata::array::{self, ArrayBlock};
|
2021-02-25 15:44:44 +05:30
|
|
|
use crate::pdata::array_walker::*;
|
|
|
|
|
|
|
|
//------------------------------------------
|
|
|
|
|
|
|
|
const MAX_CONCURRENT_IO: u32 = 1024;
|
|
|
|
|
|
|
|
//------------------------------------------
|
|
|
|
|
2021-03-04 16:43:08 +05:30
|
|
|
mod format1 {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
struct Inner<'a> {
|
|
|
|
visitor: &'a mut dyn MetadataVisitor,
|
|
|
|
valid_mappings: FixedBitSet,
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
2021-02-25 15:44:44 +05:30
|
|
|
|
2021-03-04 16:43:08 +05:30
|
|
|
impl<'a> ArrayVisitor<Mapping> for MappingEmitter<'a> {
|
2021-04-16 15:22:54 +05:30
|
|
|
fn visit(&self, index: u64, b: ArrayBlock<Mapping>) -> array::Result<()> {
|
2021-05-12 16:09:59 +05:30
|
|
|
let cbegin = index as u32 * b.header.max_entries;
|
|
|
|
let cend = cbegin + b.header.nr_entries;
|
|
|
|
for (map, cblock) in b.values.iter().zip(cbegin..cend) {
|
2021-04-16 15:22:54 +05:30
|
|
|
if !map.is_valid() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-03-04 16:43:08 +05:30
|
|
|
let m = xml::Map {
|
2021-05-12 16:09:59 +05:30
|
|
|
cblock,
|
2021-04-16 15:22:54 +05:30
|
|
|
oblock: map.oblock,
|
|
|
|
dirty: map.is_dirty(),
|
2021-03-04 16:43:08 +05:30
|
|
|
};
|
|
|
|
|
|
|
|
let mut inner = self.inner.lock().unwrap();
|
2021-05-12 16:09:59 +05:30
|
|
|
inner.valid_mappings.set(cblock as usize, true);
|
2021-05-11 18:11:30 +05:30
|
|
|
inner
|
|
|
|
.visitor
|
|
|
|
.mapping(&m)
|
|
|
|
.map_err(|e| array::value_err(format!("{}", e)))?;
|
2021-03-04 16:43:08 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
2021-02-25 15:44:44 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-04 16:43:08 +05:30
|
|
|
//------------------------------------------
|
|
|
|
|
|
|
|
mod format2 {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
//-------------------
|
|
|
|
// Dirty bitset visitor
|
|
|
|
pub struct DirtyVisitor {
|
|
|
|
nr_entries: usize,
|
|
|
|
bits: Mutex<FixedBitSet>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DirtyVisitor {
|
|
|
|
pub fn new(nr_entries: usize) -> Self {
|
|
|
|
DirtyVisitor {
|
2021-04-16 15:22:54 +05:30
|
|
|
nr_entries, // number of bits
|
2021-03-04 16:43:08 +05:30
|
|
|
bits: Mutex::new(FixedBitSet::with_capacity(nr_entries)),
|
|
|
|
}
|
2021-02-25 15:44:44 +05:30
|
|
|
}
|
|
|
|
|
2021-03-04 16:43:08 +05:30
|
|
|
pub fn get_bits(self) -> FixedBitSet {
|
|
|
|
self.bits.into_inner().unwrap()
|
|
|
|
}
|
|
|
|
}
|
2021-02-25 15:44:44 +05:30
|
|
|
|
2021-03-04 16:43:08 +05:30
|
|
|
impl ArrayVisitor<u64> for DirtyVisitor {
|
2021-04-16 15:22:54 +05:30
|
|
|
fn visit(&self, index: u64, b: ArrayBlock<u64>) -> array::Result<()> {
|
2021-05-11 18:11:30 +05:30
|
|
|
let mut pos = (index as usize * (b.header.max_entries as usize)) << 6;
|
2021-05-12 16:09:59 +05:30
|
|
|
for bits in b.values.iter() {
|
2021-04-16 15:22:54 +05:30
|
|
|
for bi in 0..64u64 {
|
|
|
|
if pos >= self.nr_entries {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.bits.lock().unwrap().set(pos, bits & (1 << bi) != 0);
|
|
|
|
pos += 1;
|
2021-03-04 16:43:08 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
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> {
|
2021-05-11 18:11:30 +05:30
|
|
|
pub fn new(
|
|
|
|
nr_entries: usize,
|
|
|
|
dirty_bits: FixedBitSet,
|
|
|
|
visitor: &'a mut dyn MetadataVisitor,
|
|
|
|
) -> MappingEmitter<'a> {
|
2021-03-04 16:43:08 +05:30
|
|
|
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> {
|
2021-04-16 15:22:54 +05:30
|
|
|
fn visit(&self, index: u64, b: ArrayBlock<Mapping>) -> array::Result<()> {
|
2021-05-12 16:09:59 +05:30
|
|
|
let cbegin = index as u32 * b.header.max_entries;
|
|
|
|
let cend = cbegin + b.header.nr_entries;
|
|
|
|
for (map, cblock) in b.values.iter().zip(cbegin..cend) {
|
2021-04-16 15:22:54 +05:30
|
|
|
if !map.is_valid() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-03-04 16:43:08 +05:30
|
|
|
let mut inner = self.inner.lock().unwrap();
|
2021-05-12 16:09:59 +05:30
|
|
|
let dirty = inner.dirty_bits.contains(cblock as usize);
|
2021-03-04 16:43:08 +05:30
|
|
|
let m = xml::Map {
|
2021-05-12 16:09:59 +05:30
|
|
|
cblock,
|
2021-04-16 15:22:54 +05:30
|
|
|
oblock: map.oblock,
|
2021-03-04 16:43:08 +05:30
|
|
|
dirty,
|
|
|
|
};
|
|
|
|
|
2021-05-12 16:09:59 +05:30
|
|
|
inner.valid_mappings.set(cblock as usize, true);
|
2021-05-11 18:11:30 +05:30
|
|
|
inner
|
|
|
|
.visitor
|
|
|
|
.mapping(&m)
|
|
|
|
.map_err(|e| array::value_err(format!("{}", e)))?;
|
2021-03-04 16:43:08 +05:30
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2021-02-25 15:44:44 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------
|
|
|
|
|
2021-03-04 16:43:08 +05:30
|
|
|
struct HintEmitter<'a> {
|
|
|
|
emitter: Mutex<&'a mut dyn MetadataVisitor>,
|
|
|
|
valid_mappings: FixedBitSet,
|
2021-02-25 15:44:44 +05:30
|
|
|
}
|
|
|
|
|
2021-03-04 16:43:08 +05:30
|
|
|
impl<'a> HintEmitter<'a> {
|
|
|
|
pub fn new(emitter: &'a mut dyn MetadataVisitor, valid_mappings: FixedBitSet) -> HintEmitter {
|
2021-02-25 15:44:44 +05:30
|
|
|
HintEmitter {
|
2021-03-04 16:43:08 +05:30
|
|
|
emitter: Mutex::new(emitter),
|
|
|
|
valid_mappings,
|
2021-02-25 15:44:44 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-04 16:43:08 +05:30
|
|
|
impl<'a> ArrayVisitor<Hint> for HintEmitter<'a> {
|
2021-04-16 15:22:54 +05:30
|
|
|
fn visit(&self, index: u64, b: ArrayBlock<Hint>) -> array::Result<()> {
|
2021-05-12 16:09:59 +05:30
|
|
|
let cbegin = index as u32 * b.header.max_entries;
|
|
|
|
let cend = cbegin + b.header.nr_entries;
|
|
|
|
for (hint, cblock) in b.values.iter().zip(cbegin..cend) {
|
2021-04-16 15:22:54 +05:30
|
|
|
if !self.valid_mappings.contains(cblock as usize) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-03-04 16:43:08 +05:30
|
|
|
let h = xml::Hint {
|
2021-04-16 15:22:54 +05:30
|
|
|
cblock,
|
2021-03-04 16:43:08 +05:30
|
|
|
data: hint.hint.to_vec(),
|
|
|
|
};
|
2021-02-25 15:44:44 +05:30
|
|
|
|
2021-04-13 11:09:14 +05:30
|
|
|
self.emitter
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.hint(&h)
|
|
|
|
.map_err(|e| array::value_err(format!("{}", e)))?;
|
2021-03-04 16:43:08 +05:30
|
|
|
}
|
2021-02-25 15:44:44 +05:30
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------
|
|
|
|
|
|
|
|
pub struct CacheDumpOptions<'a> {
|
|
|
|
pub dev: &'a Path,
|
|
|
|
pub async_io: bool,
|
|
|
|
pub repair: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Context {
|
|
|
|
engine: Arc<dyn IoEngine + Send + Sync>,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn mk_context(opts: &CacheDumpOptions) -> anyhow::Result<Context> {
|
|
|
|
let engine: Arc<dyn IoEngine + Send + Sync>;
|
|
|
|
|
|
|
|
if opts.async_io {
|
2021-03-04 16:43:08 +05:30
|
|
|
engine = Arc::new(AsyncIoEngine::new(opts.dev, MAX_CONCURRENT_IO, false)?);
|
2021-02-25 15:44:44 +05:30
|
|
|
} else {
|
|
|
|
let nr_threads = std::cmp::max(8, num_cpus::get() * 2);
|
|
|
|
engine = Arc::new(SyncIoEngine::new(opts.dev, nr_threads, false)?);
|
|
|
|
}
|
|
|
|
|
2021-03-04 16:43:08 +05:30
|
|
|
Ok(Context { engine })
|
2021-02-25 15:44:44 +05:30
|
|
|
}
|
|
|
|
|
2021-03-04 16:43:08 +05:30
|
|
|
fn dump_metadata(ctx: &Context, sb: &Superblock, _repair: bool) -> anyhow::Result<()> {
|
2021-02-25 15:44:44 +05:30
|
|
|
let engine = &ctx.engine;
|
|
|
|
|
2021-03-04 16:43:08 +05:30
|
|
|
let mut out = xml::XmlWriter::new(std::io::stdout());
|
2021-02-25 15:44:44 +05:30
|
|
|
let xml_sb = xml::Superblock {
|
|
|
|
uuid: "".to_string(),
|
|
|
|
block_size: sb.data_block_size,
|
|
|
|
nr_cache_blocks: sb.cache_blocks,
|
2021-05-12 16:09:59 +05:30
|
|
|
policy: std::str::from_utf8(&sb.policy_name)?.to_string(),
|
2021-02-25 15:44:44 +05:30
|
|
|
hint_width: sb.policy_hint_size,
|
|
|
|
};
|
2021-03-04 16:43:08 +05:30
|
|
|
out.superblock_b(&xml_sb)?;
|
|
|
|
|
|
|
|
out.mappings_b()?;
|
|
|
|
let valid_mappings = match sb.version {
|
|
|
|
1 => {
|
|
|
|
let w = ArrayWalker::new(engine.clone(), false);
|
|
|
|
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);
|
|
|
|
|
|
|
|
if let Some(root) = sb.dirty_root {
|
|
|
|
w.walk(&mut v, root)?;
|
|
|
|
} else {
|
|
|
|
// FIXME: is there a way this can legally happen? eg,
|
|
|
|
// a crash of a freshly created cache?
|
|
|
|
return Err(anyhow!("format 2 selected, but no dirty bitset present"));
|
|
|
|
}
|
|
|
|
let dirty_bits = v.get_bits();
|
|
|
|
|
|
|
|
let w = ArrayWalker::new(engine.clone(), false);
|
2021-05-11 18:11:30 +05:30
|
|
|
let mut emitter =
|
|
|
|
format2::MappingEmitter::new(sb.cache_blocks as usize, dirty_bits, &mut out);
|
2021-03-04 16:43:08 +05:30
|
|
|
w.walk(&mut emitter, sb.mapping_root)?;
|
|
|
|
emitter.get_valid()
|
|
|
|
}
|
|
|
|
v => {
|
|
|
|
return Err(anyhow!("unsupported metadata version: {}", v));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
out.mappings_e()?;
|
2021-02-25 15:44:44 +05:30
|
|
|
|
2021-03-04 16:43:08 +05:30
|
|
|
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()?;
|
2021-02-25 15:44:44 +05:30
|
|
|
|
2021-03-04 16:43:08 +05:30
|
|
|
out.superblock_e()?;
|
2021-02-25 15:44:44 +05:30
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn dump(opts: CacheDumpOptions) -> anyhow::Result<()> {
|
|
|
|
let ctx = mk_context(&opts)?;
|
|
|
|
let engine = &ctx.engine;
|
|
|
|
let sb = read_superblock(engine.as_ref(), SUPERBLOCK_LOCATION)?;
|
|
|
|
|
|
|
|
dump_metadata(&ctx, &sb, opts.repair)
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------
|