Add configfs attributes for managing zone states in the rnull zoned block device emulation. The `zone_offline` and `zone_readonly` attributes allow setting specific zones to offline or read-only states, which is useful for testing how applications handle degraded zones. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 80 +++++++++++++++++++++++++++++++++++ drivers/block/rnull/disk_storage.rs | 59 +++++++++++++------------- drivers/block/rnull/rnull.rs | 2 +- drivers/block/rnull/zoned.rs | 83 ++++++++++++++++++++++++++----------- 4 files changed, 168 insertions(+), 56 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs.rs index 395da68d96dc6..1c0d95ded7e9f 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -125,6 +125,8 @@ fn make_group( max_sectors: 29, virt_boundary: 30, shared_tag_bitmap: 31, + zone_offline: 32, + zone_readonly: 33, ], }; @@ -639,3 +641,81 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { configfs_simple_field!(DeviceConfig, 29, max_sectors, u32); configfs_simple_bool_field!(DeviceConfig, 30, virt_boundary); configfs_simple_bool_field!(DeviceConfig, 31, shared_tag_bitmap); + +#[cfg(CONFIG_BLK_DEV_ZONED)] +fn set_zone_condition( + this: &DeviceConfig, + sector: u64, + cb: impl FnOnce( + &crate::zoned::ZoneOptions, + &DiskStorage, + &mut crate::zoned::ZoneDescriptor, + ) -> Result, +) -> Result { + use crate::zoned::ZoneType; + let data_guard = this.data.lock(); + let null_disk = data_guard.disk.as_ref().ok_or(EBUSY)?.queue_data(); + let storage = &null_disk.storage; + let zone_options = &null_disk.zoned; + zone_options.enabled.then_some(()).ok_or(EINVAL)?; + let mut zone = zone_options.zone(sector)?.lock(); + + if zone.kind == ZoneType::Conventional { + return Err(EINVAL); + } + + cb(zone_options, storage, &mut zone) +} + +#[cfg(CONFIG_BLK_DEV_ZONED)] +configfs_attribute!( + DeviceConfig, + 32, + show: |_this, _page| Ok(0), + store: |this,page| { + let text = core::str::from_utf8(page)?.trim(); + let sector = text.parse().map_err(|_| EINVAL)?; + + set_zone_condition(this, sector, |zone_options, storage, zone| { + zone_options.offline_zone(storage, zone) + })?; + Ok(()) + }, +); + +#[cfg(CONFIG_BLK_DEV_ZONED)] +configfs_attribute!( + DeviceConfig, + 33, + show: |_this, _page| Ok(0), + store: |this,page| { + let text = core::str::from_utf8(page)?.trim(); + let sector = text.parse().map_err(|_| EINVAL)?; + + set_zone_condition(this, sector, |zone_options, storage, zone| { + zone_options.read_only_zone(storage, zone) + })?; + + Ok(()) + }, +); + +#[cfg(not(CONFIG_BLK_DEV_ZONED))] +configfs_attribute!( + DeviceConfig, + 32, + show: |this, page| {Ok(0)}, + store: |this,page| { + Err(ENOTSUPP) + }, +); + +#[cfg(not(CONFIG_BLK_DEV_ZONED))] +configfs_attribute!( + DeviceConfig, + 33, + show: |this, page| {Ok(0)}, + store: |this,page| { + Err(ENOTSUPP) + }, +); diff --git a/drivers/block/rnull/disk_storage.rs b/drivers/block/rnull/disk_storage.rs index d9f2703957fc0..802e9534ca2a5 100644 --- a/drivers/block/rnull/disk_storage.rs +++ b/drivers/block/rnull/disk_storage.rs @@ -65,27 +65,45 @@ pub(crate) fn lock(&self) -> SpinLockGuard<'_, Pin>> { self.trees.lock() } - pub(crate) fn discard( - &self, - hw_data: &Pin<&SpinLock>, - mut sector: u64, - sectors: u32, - ) { - let mut tree_guard = self.lock(); - let mut hw_data_guard = hw_data.lock(); - - let mut access = self.access(&mut tree_guard, &mut hw_data_guard, None); + pub(crate) fn discard(&self, mut sector: u64, sectors: u32) { + let tree_guard = self.lock(); + let mut cache_guard = tree_guard.cache_tree.lock(); + let mut disk_guard = tree_guard.cache_tree.lock(); let mut remaining_bytes = sectors_to_bytes(sectors); while remaining_bytes > 0 { - access.free_sector(sector); + self.free_sector(&mut cache_guard, &mut disk_guard, sector); let processed = remaining_bytes.min(self.block_size); sector += Into::::into(bytes_to_sectors(processed)); remaining_bytes -= processed; } } + fn free_sector_tree(tree_access: &mut xarray::Guard<'_, TreeNode>, sector: u64) { + let index = DiskStorageAccess::to_index(sector); + if let Some(page) = tree_access.get_mut(index) { + page.set_free(sector); + + if page.is_empty() { + tree_access.remove(index); + } + } + } + + pub(crate) fn free_sector<'a>( + &self, + cache_guard: &mut xarray::Guard<'a, TreeNode>, + disk_guard: &mut xarray::Guard<'a, TreeNode>, + sector: u64, + ) { + if self.cache_size > 0 { + Self::free_sector_tree(cache_guard, sector); + } + + Self::free_sector_tree(disk_guard, sector); + } + pub(crate) fn flush(&self, hw_data: &Pin<&SpinLock>) { let mut tree_guard = self.lock(); let mut hw_data_guard = hw_data.lock(); @@ -262,25 +280,6 @@ pub(crate) fn get_read_page(&self, sector: u64) -> Option<&NullBlockPage> { self.disk_guard.get(index) } } - - fn free_sector_tree(tree_access: &mut xarray::Guard<'_, TreeNode>, sector: u64) { - let index = Self::to_index(sector); - if let Some(page) = tree_access.get_mut(index) { - page.set_free(sector); - - if page.is_empty() { - tree_access.remove(index); - } - } - } - - pub(crate) fn free_sector(&mut self, sector: u64) { - if self.disk_storage.cache_size > 0 { - Self::free_sector_tree(&mut self.cache_guard, sector); - } - - Self::free_sector_tree(&mut self.disk_guard, sector); - } } type Tree = XArray; diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 5bf965908ef63..91f8636d74cca 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -739,7 +739,7 @@ fn handle_regular_command( if self.memory_backed { if rq.command() == mq::Command::Discard { - self.storage.discard(hw_data, rq.sector(), sectors); + self.storage.discard(rq.sector(), sectors); } else { self.transfer(hw_data, rq, rq.command(), sectors)?; } diff --git a/drivers/block/rnull/zoned.rs b/drivers/block/rnull/zoned.rs index 0f15f4cc4e5c3..3b8ebf5449a8d 100644 --- a/drivers/block/rnull/zoned.rs +++ b/drivers/block/rnull/zoned.rs @@ -178,7 +178,7 @@ pub(crate) fn handle_zoned_command( match rq.command() { ZoneAppend | Write => self.zoned_write(hw_data, rq)?, ZoneReset | ZoneResetAll | ZoneOpen | ZoneClose | ZoneFinish => { - self.zone_management(hw_data, rq)? + self.zone_management(rq)? } _ => self.zoned_read(hw_data, rq)?, } @@ -186,18 +186,14 @@ pub(crate) fn handle_zoned_command( Ok(()) } - fn zone_management( - &self, - hw_data: &Pin<&SpinLock>, - rq: &mut Owned>, - ) -> Result { + fn zone_management(&self, rq: &mut Owned>) -> Result { if rq.command() == mq::Command::ZoneResetAll { for zone in self.zoned.zones_iter() { let mut zone = zone.lock(); use ZoneCondition::*; match zone.condition { Empty | ReadOnly | Offline => continue, - _ => self.zoned.reset_zone(&self.storage, hw_data, &mut zone)?, + _ => self.zoned.reset_zone(&self.storage, &mut zone)?, } } @@ -213,10 +209,10 @@ fn zone_management( use mq::Command::*; match rq.command() { - ZoneOpen => self.zoned.open_zone(&mut zone, rq.sector()), + ZoneOpen => self.zoned.open_zone(&mut zone), ZoneClose => self.zoned.close_zone(&mut zone), - ZoneReset => self.zoned.reset_zone(&self.storage, hw_data, &mut zone), - ZoneFinish => self.zoned.finish_zone(&mut zone, rq.sector()), + ZoneReset => self.zoned.reset_zone(&self.storage, &mut zone), + ZoneFinish => self.zoned.finish_zone(&mut zone), _ => Err(EIO), } } @@ -282,7 +278,7 @@ fn zoned_write( if self.zoned.use_accounting() { let mut accounting = self.zoned.accounting.lock(); self.zoned - .check_zone_resources(&mut accounting, &mut zone, rq.sector())?; + .check_zone_resources(&mut accounting, &mut zone)?; if zone.condition == ZoneCondition::Closed { accounting.closed -= 1; @@ -365,7 +361,7 @@ fn zone_no(&self, sector: u64) -> usize { (sector >> self.size_sectors.ilog2()) as usize } - fn zone(&self, sector: u64) -> Result<&Mutex> { + pub(crate) fn zone(&self, sector: u64) -> Result<&Mutex> { self.zones.get(self.zone_no(sector)).ok_or(EINVAL) } @@ -418,7 +414,7 @@ fn try_close_implicit_open_zone(&self, accounting: &mut ZoneAccounting, sector: Err(EINVAL) } - fn open_zone(&self, zone: &mut ZoneDescriptor, sector: u64) -> Result { + fn open_zone(&self, zone: &mut ZoneDescriptor) -> Result { if zone.kind == ZoneType::Conventional { return Err(EINVAL); } @@ -434,13 +430,13 @@ fn open_zone(&self, zone: &mut ZoneDescriptor, sector: u64) -> Result { let mut accounting = self.accounting.lock(); match zone.condition { Empty => { - self.check_zone_resources(&mut accounting, zone, sector)?; + self.check_zone_resources(&mut accounting, zone)?; } ImplicitOpen => { accounting.implicit_open -= 1; } Closed => { - self.check_zone_resources(&mut accounting, zone, sector)?; + self.check_zone_resources(&mut accounting, zone)?; accounting.closed -= 1; } _ => (), @@ -457,14 +453,13 @@ fn check_zone_resources( &self, accounting: &mut ZoneAccounting, zone: &mut ZoneDescriptor, - sector: u64, ) -> Result { match zone.condition { ZoneCondition::Empty => { self.check_active_zones(accounting)?; - self.check_open_zones(accounting, sector) + self.check_open_zones(accounting, zone.start_sector) } - ZoneCondition::Closed => self.check_open_zones(accounting, sector), + ZoneCondition::Closed => self.check_open_zones(accounting, zone.start_sector), _ => Err(EIO), } } @@ -533,7 +528,7 @@ fn close_zone(&self, zone: &mut ZoneDescriptor) -> Result { Ok(()) } - fn finish_zone(&self, zone: &mut ZoneDescriptor, sector: u64) -> Result { + fn finish_zone(&self, zone: &mut ZoneDescriptor) -> Result { if zone.kind == ZoneType::Conventional { return Err(EINVAL); } @@ -545,12 +540,12 @@ fn finish_zone(&self, zone: &mut ZoneDescriptor, sector: u64) -> Result { match zone.condition { Full => return Ok(()), Empty => { - self.check_zone_resources(&mut accounting, zone, sector)?; + self.check_zone_resources(&mut accounting, zone)?; } ImplicitOpen => accounting.implicit_open -= 1, ExplicitOpen => accounting.explicit_open -= 1, Closed => { - self.check_zone_resources(&mut accounting, zone, sector)?; + self.check_zone_resources(&mut accounting, zone)?; accounting.closed -= 1; } _ => return Err(EIO), @@ -566,7 +561,6 @@ fn finish_zone(&self, zone: &mut ZoneDescriptor, sector: u64) -> Result { fn reset_zone( &self, storage: &crate::disk_storage::DiskStorage, - hw_data: &Pin<&SpinLock>, zone: &mut ZoneDescriptor, ) -> Result { if zone.kind == ZoneType::Conventional { @@ -589,16 +583,55 @@ fn reset_zone( zone.condition = ZoneCondition::Empty; zone.write_pointer = zone.start_sector; - storage.discard(hw_data, zone.start_sector, zone.size_sectors); + storage.discard(zone.start_sector, zone.size_sectors); + + Ok(()) + } + + fn set_zone_condition( + &self, + storage: &crate::disk_storage::DiskStorage, + zone: &mut ZoneDescriptor, + condition: ZoneCondition, + ) -> Result { + if zone.condition == condition { + zone.condition = ZoneCondition::Empty; + zone.write_pointer = zone.start_sector; + storage.discard(zone.start_sector, zone.size_sectors); + } else { + if matches!( + zone.condition, + ZoneCondition::ReadOnly | ZoneCondition::Offline + ) { + self.finish_zone(zone)?; + } + zone.condition = ZoneCondition::Offline; + zone.write_pointer = u64::MAX; + } Ok(()) } + pub(crate) fn offline_zone( + &self, + storage: &crate::disk_storage::DiskStorage, + zone: &mut ZoneDescriptor, + ) -> Result { + self.set_zone_condition(storage, zone, ZoneCondition::Offline) + } + + pub(crate) fn read_only_zone( + &self, + storage: &crate::disk_storage::DiskStorage, + zone: &mut ZoneDescriptor, + ) -> Result { + self.set_zone_condition(storage, zone, ZoneCondition::ReadOnly) + } } pub(crate) struct ZoneDescriptor { start_sector: u64, size_sectors: u32, - kind: ZoneType, + pub(crate) kind: ZoneType, capacity_sectors: u32, write_pointer: u64, condition: ZoneCondition, @@ -626,7 +659,7 @@ fn check_bounds_read(&self, sector: u64, sectors: u32) -> Result { #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[repr(u32)] -enum ZoneType { +pub(crate) enum ZoneType { Conventional = bindings::blk_zone_type_BLK_ZONE_TYPE_CONVENTIONAL, SequentialWriteRequired = bindings::blk_zone_type_BLK_ZONE_TYPE_SEQWRITE_REQ, #[expect(dead_code)] -- 2.51.2