diff --git a/src/engine/strat_engine/backstore/blockdev/mod.rs b/src/engine/strat_engine/backstore/blockdev/mod.rs index c1c2e363b31..54542797048 100644 --- a/src/engine/strat_engine/backstore/blockdev/mod.rs +++ b/src/engine/strat_engine/backstore/blockdev/mod.rs @@ -12,7 +12,7 @@ use crate::{ engine::{ strat_engine::{ backstore::{devices::BlockSizes, range_alloc::PerDevSegments}, - metadata::{BDAExtendedSize, BlockdevSize, MDADataSize, BDA}, + metadata::{BlockdevSize, MDADataSize, BDA}, }, types::{DevUuid, StratSigblockVersion}, }, @@ -71,12 +71,13 @@ pub trait InternalBlockDev { fn available(&self) -> Sectors; // ALL SIZE METHODS (except size(), which is in BlockDev impl.) - /// The number of Sectors on this device used by Stratis for metadata - fn metadata_size(&self) -> BDAExtendedSize; + /// The number of Sectors on this device used by Stratis and potentially other devicemapper + /// layers for metadata + fn metadata_size(&self) -> Sectors; - /// The maximum size of variable length metadata that can be accommodated. + /// The maximum size of variable length Stratis metadata that can be accommodated. /// self.max_metadata_size() < self.metadata_size() - fn max_metadata_size(&self) -> MDADataSize; + fn max_stratis_metadata_size(&self) -> MDADataSize; /// Whether or not the blockdev is in use by upper layers. It is if the /// sum of the blocks used exceeds the Stratis metadata size. diff --git a/src/engine/strat_engine/backstore/blockdev/v1.rs b/src/engine/strat_engine/backstore/blockdev/v1.rs index d2f6245ee71..17372aea98e 100644 --- a/src/engine/strat_engine/backstore/blockdev/v1.rs +++ b/src/engine/strat_engine/backstore/blockdev/v1.rs @@ -28,8 +28,8 @@ use crate::{ crypt::handle::v1::CryptHandle, device::blkdev_size, metadata::{ - disown_device, static_header, BDAExtendedSize, BlockdevSize, MDADataSize, - MetadataLocation, StaticHeader, BDA, + disown_device, static_header, BlockdevSize, MDADataSize, MetadataLocation, + StaticHeader, BDA, }, serde_structs::{BaseBlockDevSave, Recordable}, types::BDAResult, @@ -197,8 +197,8 @@ impl StratBlockDev { } /// The maximum size of variable length metadata that can be accommodated. - /// self.max_metadata_size() < self.metadata_size() - pub fn max_metadata_size(&self) -> MDADataSize { + /// self.max_stratis_metadata_size() > self.metadata_size() + pub fn max_stratis_metadata_size(&self) -> MDADataSize { self.bda.max_data_size() } @@ -360,16 +360,16 @@ impl InternalBlockDev for StratBlockDev { self.used.available() } - fn metadata_size(&self) -> BDAExtendedSize { - self.bda.extended_size() + fn metadata_size(&self) -> Sectors { + self.bda.extended_size().sectors() } - fn max_metadata_size(&self) -> MDADataSize { + fn max_stratis_metadata_size(&self) -> MDADataSize { self.bda.max_data_size() } fn in_use(&self) -> bool { - self.used.used() > self.metadata_size().sectors() + self.used.used() > self.metadata_size() } fn alloc(&mut self, size: Sectors) -> PerDevSegments { diff --git a/src/engine/strat_engine/backstore/blockdev/v2.rs b/src/engine/strat_engine/backstore/blockdev/v2.rs index 8fea0d0e210..1da604d4999 100644 --- a/src/engine/strat_engine/backstore/blockdev/v2.rs +++ b/src/engine/strat_engine/backstore/blockdev/v2.rs @@ -13,7 +13,7 @@ use std::{ use chrono::{DateTime, Utc}; use serde_json::Value; -use devicemapper::{Device, Sectors}; +use devicemapper::{Bytes, Device, Sectors, IEC}; use crate::{ engine::{ @@ -26,8 +26,8 @@ use crate::{ }, device::blkdev_size, metadata::{ - disown_device, static_header, BDAExtendedSize, BlockdevSize, MDADataSize, - MetadataLocation, StaticHeader, BDA, + disown_device, static_header, BlockdevSize, MDADataSize, MetadataLocation, + StaticHeader, BDA, }, serde_structs::{BaseBlockDevSave, Recordable}, types::BDAResult, @@ -40,6 +40,17 @@ use crate::{ stratis::{StratisError, StratisResult}, }; +/// Return the amount of space required for integrity for a device of the given size. +/// +/// This is a slight overestimation for the sake of simplicity. Because it uses the whole disk +/// size, once the integrity metadata size is calculated, the remaining data size is now smaller +/// than the metadata region could support for integrity. +pub fn integrity_meta_space(total_space: Sectors) -> Sectors { + Bytes(4096).sectors() + + Bytes::from(64 * IEC::Mi).sectors() + + Bytes::from((*total_space * 32u64 + 4095) & !4096).sectors() +} + #[derive(Debug)] pub struct StratBlockDev { dev: Device, @@ -163,16 +174,12 @@ impl StratBlockDev { Ok(blkdev_size(&File::open(physical_path)?)?.sectors()) } - /// Allocate room for metadata from the front of the device. - #[allow(dead_code)] - fn alloc_meta_front(&mut self, size: Sectors) -> PerDevSegments { - self.used.alloc_front(size) - } - - /// Allocate room for metadata from the back of the device. - #[allow(dead_code)] - fn alloc_meta_back(&mut self, size: Sectors) -> PerDevSegments { - self.used.alloc_back(size) + /// Allocate room for integrity metadata from the back of the device. + pub fn alloc_int_meta_back(&mut self, size: Sectors) { + let segs = self.used.alloc_back(size); + for (start, len) in segs.iter() { + self.integrity_meta_allocs.push((*start, *len)); + } } /// Set the newly detected size of a block device. @@ -230,16 +237,17 @@ impl InternalBlockDev for StratBlockDev { self.used.available() } - fn metadata_size(&self) -> BDAExtendedSize { - self.bda.extended_size() + fn metadata_size(&self) -> Sectors { + self.bda.extended_size().sectors() + + self.integrity_meta_allocs.iter().map(|(_, len)| *len).sum() } - fn max_metadata_size(&self) -> MDADataSize { + fn max_stratis_metadata_size(&self) -> MDADataSize { self.bda.max_data_size() } fn in_use(&self) -> bool { - self.used.used() > self.metadata_size().sectors() + self.used.used() > self.metadata_size() } fn alloc(&mut self, size: Sectors) -> PerDevSegments { diff --git a/src/engine/strat_engine/backstore/blockdevmgr.rs b/src/engine/strat_engine/backstore/blockdevmgr.rs index 7ece1b3fa32..4a67b662999 100644 --- a/src/engine/strat_engine/backstore/blockdevmgr.rs +++ b/src/engine/strat_engine/backstore/blockdevmgr.rs @@ -371,7 +371,7 @@ where let candidates = self .block_devs .iter_mut() - .filter(|b| b.max_metadata_size().bytes() >= data_size); + .filter(|b| b.max_stratis_metadata_size().bytes() >= data_size); debug!( "Writing {} of pool level metadata to devices in pool", @@ -416,10 +416,7 @@ where /// The number of sectors given over to Stratis metadata /// self.allocated_size() - self.metadata_size() >= self.avail_space() pub fn metadata_size(&self) -> Sectors { - self.block_devs - .iter() - .map(|bd| bd.metadata_size().sectors()) - .sum() + self.block_devs.iter().map(|bd| bd.metadata_size()).sum() } pub fn grow(&mut self, dev: DevUuid) -> StratisResult { diff --git a/src/engine/strat_engine/backstore/data_tier.rs b/src/engine/strat_engine/backstore/data_tier.rs index e0891066281..6c9da247c76 100644 --- a/src/engine/strat_engine/backstore/data_tier.rs +++ b/src/engine/strat_engine/backstore/data_tier.rs @@ -28,6 +28,8 @@ use crate::{ stratis::StratisResult, }; +use super::blockdev::v2::integrity_meta_space; + /// Handles the lowest level, base layer of this tier. #[derive(Debug)] pub struct DataTier { @@ -101,7 +103,15 @@ impl DataTier { /// Initially 0 segments are allocated. /// /// WARNING: metadata changing event - pub fn new(block_mgr: BlockDevMgr) -> DataTier { + pub fn new(mut block_mgr: BlockDevMgr) -> DataTier { + for (_, bd) in block_mgr.blockdevs_mut() { + bd.alloc_int_meta_back(integrity_meta_space( + // NOTE: Subtracting metadata size works here because the only metadata currently + // recorded in a newly created block device is the BDA. If this becomes untrue in + // the future, this code will no longer work. + bd.total_size().sectors() - bd.metadata_size(), + )) + } DataTier { block_mgr, segments: AllocatedAbove { inner: vec![] }, @@ -116,7 +126,29 @@ impl DataTier { pool_uuid: PoolUuid, devices: UnownedDevices, ) -> StratisResult> { - self.block_mgr.add(pool_uuid, devices) + let uuids = self.block_mgr.add(pool_uuid, devices)?; + let bds = self + .block_mgr + .blockdevs_mut() + .into_iter() + .filter_map(|(uuid, bd)| { + if uuids.contains(&uuid) { + Some(bd) + } else { + None + } + }) + .collect::>(); + assert_eq!(bds.len(), uuids.len()); + for bd in bds { + bd.alloc_int_meta_back(integrity_meta_space( + // NOTE: Subtracting metadata size works here because the only metadata currently + // recorded in a newly created block device is the BDA. If this becomes untrue in + // the future, this code will no longer work. + bd.total_size().sectors() - bd.metadata_size(), + )); + } + Ok(uuids) } /// Lookup an immutable blockdev by its Stratis UUID. diff --git a/src/engine/strat_engine/metadata/mod.rs b/src/engine/strat_engine/metadata/mod.rs index 5088c2832fa..cfc1dc1411b 100644 --- a/src/engine/strat_engine/metadata/mod.rs +++ b/src/engine/strat_engine/metadata/mod.rs @@ -17,7 +17,7 @@ mod static_header; pub use self::{ bda::BDA, - sizes::{BDAExtendedSize, BlockdevSize, MDADataSize}, + sizes::{BlockdevSize, MDADataSize}, static_header::{ device_identifiers, disown_device, static_header, MetadataLocation, StaticHeader, StaticHeaderResult, StratisIdentifiers,