diff --git a/Cargo.toml b/Cargo.toml index 288f1dbc48e..b42e06f8a49 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,7 +84,7 @@ version = "2.3.0" optional = true [dependencies.devicemapper] -version = "0.33.1" +version = "0.33.2" optional = true [dependencies.dbus] diff --git a/src/engine/strat_engine/backstore/backstore.rs b/src/engine/strat_engine/backstore/backstore.rs index ad88046d592..c5fe7d703eb 100644 --- a/src/engine/strat_engine/backstore/backstore.rs +++ b/src/engine/strat_engine/backstore/backstore.rs @@ -10,7 +10,10 @@ use chrono::{DateTime, Utc}; use serde_json::Value; use tempfile::TempDir; -use devicemapper::{CacheDev, Device, DmDevice, LinearDev, Sectors}; +use devicemapper::{ + CacheDev, CacheDevTargetTable, CacheTargetParams, DevId, Device, DmDevice, DmFlags, DmOptions, + LinearDev, LinearDevTargetParams, LinearTargetParams, Sectors, TargetLine, TargetTable, +}; use crate::{ engine::{ @@ -52,6 +55,7 @@ fn make_cache( pool_uuid: PoolUuid, cache_tier: &CacheTier, origin: LinearDev, + cap: Option, new: bool, ) -> StratisResult { let (dm_name, dm_uuid) = format_backstore_ids(pool_uuid, CacheRole::MetaSub); @@ -80,6 +84,32 @@ fn make_cache( )?; let (dm_name, dm_uuid) = format_backstore_ids(pool_uuid, CacheRole::Cache); + if cap.is_some() { + let dm = get_dm(); + dm.device_suspend( + &DevId::Name(&dm_name), + DmOptions::default().set_flags(DmFlags::DM_SUSPEND), + )?; + let table = CacheDevTargetTable::new( + Sectors(0), + origin.size(), + CacheTargetParams::new( + meta.device(), + cache.device(), + origin.device(), + CACHE_BLOCK_SIZE, + vec!["writethrough".into()], + "default".to_owned(), + Vec::new(), + ), + ); + dm.table_load( + &DevId::Name(&dm_name), + &table.to_raw_table(), + DmOptions::default(), + )?; + dm.device_suspend(&DevId::Name(&dm_name), DmOptions::default())?; + }; Ok(CacheDev::setup( get_dm(), &dm_name, @@ -91,6 +121,18 @@ fn make_cache( )?) } +/// Set up the linear device on top of the data tier that can later be converted to a +/// cache device and serves as a placeholder for the device beneath encryption. +fn make_cap_linear_dev(pool_uuid: PoolUuid, origin: &LinearDev) -> Result { + let (dm_name, dm_uuid) = format_backstore_ids(pool_uuid, CacheRole::Cache); + let target = vec![TargetLine::new( + Sectors(0), + origin.size(), + LinearDevTargetParams::Linear(LinearTargetParams::new(origin.device(), Sectors(0))), + )]; + LinearDev::setup(get_dm(), &dm_name, Some(&dm_uuid), target).map_err(StratisError::from) +} + /// This structure can allocate additional space to the upper layer, but it /// cannot accept returned space. When it is extended to be able to accept /// returned space the allocation algorithm will have to be revised. @@ -105,6 +147,8 @@ pub struct Backstore { data_tier: DataTier, /// A linear DM device. linear: Option, + /// A placeholder device to be converted to cache. + cap_linear: Option, /// Index for managing allocation of cap device next: Sectors, } @@ -162,7 +206,7 @@ impl Backstore { } }; - let (cache_tier, cache, origin) = if !cachedevs.is_empty() { + let (cap_linear, cache_tier, cache, origin) = if !cachedevs.is_empty() { let block_mgr = BlockDevMgr::new(cachedevs, Some(last_update_time)); match backstore_save.cache_tier { Some(ref cache_tier_save) => { @@ -174,7 +218,8 @@ impl Backstore { } }; - let cache_device = match make_cache(pool_uuid, &cache_tier, origin, false) { + let cache_device = match make_cache(pool_uuid, &cache_tier, origin, None, false) + { Ok(cd) => cd, Err(e) => { return Err(( @@ -188,7 +233,7 @@ impl Backstore { )); } }; - (Some(cache_tier), Some(cache_device), None) + (None, Some(cache_tier), Some(cache_device), None) } None => { let err_msg = "Cachedevs exist, but cache metadata does not exist"; @@ -204,7 +249,11 @@ impl Backstore { } } } else { - (None, None, Some(origin)) + let cap_linear = match make_cap_linear_dev(pool_uuid, &origin) { + Ok(cap) => cap, + Err(e) => return Err((e, data_tier.block_mgr.into_bdas())), + }; + (Some(cap_linear), None, None, Some(origin)) }; Ok(Backstore { @@ -212,6 +261,7 @@ impl Backstore { cache_tier, linear: origin, cache, + cap_linear, next: backstore_save.cap.allocs[0].1, }) } @@ -245,6 +295,7 @@ impl Backstore { cache_tier: None, linear: None, cache: None, + cap_linear: None, next: Sectors(0), }) } @@ -287,8 +338,11 @@ impl Backstore { let linear = self.linear .take() .expect("some space has already been allocated from the backstore => (cache_tier.is_none() <=> self.linear.is_some())"); + let cap = self.cap_linear + .take() + .expect("some space has already been allocated from the backstore => (cache_tier.is_none() <=> self.cap_linear.is_some())"); - let cache = make_cache(pool_uuid, &cache_tier, linear, true)?; + let cache = make_cache(pool_uuid, &cache_tier, linear, Some(cap), true)?; self.cache = Some(cache); @@ -369,7 +423,12 @@ impl Backstore { /// device if it does not already exist. Return an error if DM /// operations fail. Use all segments currently allocated in the data tier. fn extend_cap_device(&mut self, pool_uuid: PoolUuid) -> StratisResult<()> { - let create = match (self.cache.as_mut(), self.linear.as_mut()) { + let create = match ( + self.cache.as_mut(), + self.cap_linear + .as_mut() + .and_then(|c| self.linear.as_mut().map(|l| (c, l))), + ) { (None, None) => true, (Some(cache), None) => { let table = self.data_tier.segments.map_to_dm(); @@ -377,10 +436,20 @@ impl Backstore { cache.resume(get_dm())?; false } - (None, Some(linear)) => { + (None, Some((cap, linear))) => { let table = self.data_tier.segments.map_to_dm(); linear.set_table(get_dm(), table)?; linear.resume(get_dm())?; + let table = vec![TargetLine::new( + Sectors(0), + linear.size(), + LinearDevTargetParams::Linear(LinearTargetParams::new( + linear.device(), + Sectors(0), + )), + )]; + cap.set_table(get_dm(), table)?; + cap.resume(get_dm())?; false } _ => panic!("NOT (self.cache().is_some() AND self.linear.is_some())"), @@ -390,7 +459,9 @@ impl Backstore { let table = self.data_tier.segments.map_to_dm(); let (dm_name, dm_uuid) = format_backstore_ids(pool_uuid, CacheRole::OriginSub); let origin = LinearDev::setup(get_dm(), &dm_name, Some(&dm_uuid), table)?; + let cap = make_cap_linear_dev(pool_uuid, &origin)?; self.linear = Some(origin); + self.cap_linear = Some(cap); } Ok(()) @@ -604,7 +675,7 @@ impl Backstore { self.cache .as_ref() .map(|d| d.device()) - .or_else(|| self.linear.as_ref().map(|d| d.device())) + .or_else(|| self.cap_linear.as_ref().map(|d| d.device())) } /// Lookup an immutable blockdev by its Stratis UUID. @@ -1311,7 +1382,7 @@ mod tests { invariant(&backstore); - assert_ne!(backstore.device(), old_device); + assert_eq!(backstore.device(), old_device); backstore.destroy(pool_uuid).unwrap(); }