From 32019ac388e0443bc61470cbb98659b3bc2e9bc2 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 22 Jul 2020 16:07:34 +0100 Subject: [PATCH] [thin_shrink] add a trivial snapshot test. Still need much more work on snap tests. --- tests/thin_shrink.rs | 290 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 287 insertions(+), 3 deletions(-) diff --git a/tests/thin_shrink.rs b/tests/thin_shrink.rs index 279a26a..0291340 100644 --- a/tests/thin_shrink.rs +++ b/tests/thin_shrink.rs @@ -1,8 +1,10 @@ use anyhow::{anyhow, Result}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use rand::prelude::*; +use std::collections::VecDeque; use std::fs::OpenOptions; use std::io::{Cursor, Read, Seek, SeekFrom, Write}; +use std::ops::Range; use std::path::{Path, PathBuf}; use tempfile::tempdir; @@ -603,7 +605,282 @@ fn shrink_fragmented_thin_64() -> Result<()> { //------------------------------------ -/* +struct Allocator { + runs: VecDeque>, +} + +impl Allocator { + fn new_shuffled(total_len: u64, run_len: Range) -> Allocator { + let mut runs = Vec::new(); + + let mut b = 0u64; + while b < total_len { + let len = u64::min( + total_len - b, + thread_rng().gen_range(run_len.start, run_len.end), + ); + runs.push(b..(b + len)); + b += len; + } + + runs.shuffle(&mut thread_rng()); + let runs: VecDeque> = runs.iter().map(|r| r.clone()).collect(); + Allocator { runs } + } + + fn is_empty(&self) -> bool { + self.runs.is_empty() + } + + fn alloc(&mut self, len: u64) -> Result>> { + let mut len = len; + let mut runs = Vec::new(); + + while len > 0 { + let r = self.runs.pop_front(); + + if r.is_none() { + return Err(anyhow!("could not allocate; out of space")); + } + + let mut r = r.unwrap(); + let rlen = r.end - r.start; + if len < rlen { + runs.push(r.start..(r.start + len)); + + // We need to push something back. + self.runs.push_front((r.start + len)..r.end); + len = 0; + } else { + runs.push(r.start..r.end); + len -= rlen; + } + } + + Ok(runs) + } +} + +// Having explicitly unmapped regions makes it easier to +// apply snapshots. +#[derive(Clone)] +enum Run { + Mapped { data_begin: u64, len: u64 }, + UnMapped { len: u64 }, +} + +impl Run { + fn len(&self) -> u64 { + match self { + Run::Mapped { + data_begin: _data_begin, + len, + } => *len, + Run::UnMapped { len } => *len, + } + } + + fn split(&self, n: u64) -> (Option, Option) { + if n == 0 { + return (None, Some(self.clone())); + } else { + if self.len() <= n { + return (Some(self.clone()), None); + } else { + match self { + Run::Mapped { data_begin, len } => ( + Some(Run::Mapped { + data_begin: *data_begin, + len: n, + }), + Some(Run::Mapped { + data_begin: data_begin + n, + len: len - n, + }), + ), + Run::UnMapped { len } => ( + Some(Run::UnMapped { len: n }), + Some(Run::UnMapped { len: len - n }), + ), + } + } + } + } +} + +#[derive(Clone)] +struct ThinDev { + thin_id: u32, + dev_size: u64, + runs: Vec, +} + +impl ThinDev { + fn emit(&self, v: &mut dyn xml::MetadataVisitor) -> Result<()> { + v.device_b(&xml::Device { + dev_id: self.thin_id, + mapped_blocks: self.dev_size, + transaction: 0, + creation_time: 0, + snap_time: 0, + })?; + + let mut b = 0; + for r in &self.runs { + match r { + Run::Mapped { data_begin, len } => { + v.map(&xml::Map { + thin_begin: b, + data_begin: *data_begin, + time: 0, + len: *len, + })?; + b += len; + } + Run::UnMapped { len } => { + b += len; + } + } + } + + v.device_e()?; + Ok(()) + } +} + +#[derive(Clone)] +enum SnapRunType { + Same, + Diff, + Hole, +} + +#[derive(Clone)] +struct SnapRun(SnapRunType, u64); + +fn mk_origin(thin_id: u32, total_len: u64, allocator: &mut Allocator) -> Result { + let mut runs = Vec::new(); + let mut b = 0; + while b < total_len { + let len = u64::min(thread_rng().gen_range(16, 64), total_len - b); + match thread_rng().gen_range(0, 2) { + 0 => { + for data in allocator.alloc(len)? { + assert!(data.end >= data.start); + runs.push(Run::Mapped { + data_begin: data.start, + len: data.end - data.start, + }); + } + } + 1 => { + runs.push(Run::UnMapped { len }); + } + _ => { + return Err(anyhow!("bad value returned from rng")); + } + }; + + b += len; + } + + Ok(ThinDev { + thin_id, + dev_size: total_len, + runs, + }) +} + +fn mk_snap_mapping( + total_len: u64, + run_len: Range, + same_percent: usize, + diff_percent: usize, +) -> Vec { + let mut runs = Vec::new(); + + let mut b = 0u64; + while b < total_len { + let len = u64::min( + total_len - b, + thread_rng().gen_range(run_len.start, run_len.end), + ); + + let n = thread_rng().gen_range(0, 100); + + if n < same_percent { + runs.push(SnapRun(SnapRunType::Same, len)); + } else if n < diff_percent { + runs.push(SnapRun(SnapRunType::Diff, len)); + } else { + runs.push(SnapRun(SnapRunType::Hole, len)); + } + + b += len; + } + + runs +} + +fn split_runs(mut n: u64, runs: &Vec) -> (Vec, Vec) { + let mut before = Vec::new(); + let mut after = Vec::new(); + + for r in runs { + match r.split(n) { + (Some(lhs), None) => { + before.push(lhs); + } + (Some(lhs), Some(rhs)) => { + before.push(lhs); + after.push(rhs); + } + (None, Some(rhs)) => { + after.push(rhs); + } + (None, None) => {} + } + n -= r.len(); + } + + (before, after) +} + +fn apply_snap_runs( + origin: &Vec, + snap: &Vec, + allocator: &mut Allocator, +) -> Result> { + let mut origin = origin.clone(); + let mut runs = Vec::new(); + + for SnapRun(st, slen) in snap { + let (os, rest) = split_runs(*slen, &origin); + match st { + SnapRunType::Same => { + for o in os { + runs.push(o); + } + } + SnapRunType::Diff => { + for data in allocator.alloc(*slen)? { + runs.push(Run::Mapped { + data_begin: data.start, + len: data.end - data.start, + }); + } + } + SnapRunType::Hole => { + runs.push(Run::UnMapped { len: *slen }); + } + } + + origin = rest; + } + + Ok(runs) +} + // Snapshots share mappings, not neccessarily the entire ranges. struct SnapS { len: u64, @@ -633,7 +910,14 @@ impl SnapS { impl Scenario for SnapS { fn generate_xml(&mut self, v: &mut dyn xml::MetadataVisitor) -> Result<()> { - let origin = mk_runs(0, self.len, 8..64); + let mut allocator = Allocator::new_shuffled(self.old_nr_data_blocks, 64..512); + let origin = mk_origin(0, self.len, &mut allocator)?; + + v.superblock_b(&common_sb(self.old_nr_data_blocks))?; + origin.emit(v)?; + v.superblock_e()?; + + Ok(()) } fn get_new_nr_blocks(&self) -> u64 { @@ -646,5 +930,5 @@ fn shrink_identical_snap() -> Result<()> { let mut s = SnapS::new(1024, 1, 0); test_shrink(&mut s) } -*/ + //------------------------------------