Add support for the badblocks_once feature, which automatically clears bad blocks after they are encountered during I/O operations. This matches the functionality in the C null_blk driver. When badblocks_once is enabled: - Bad blocks are checked during I/O requests as usual - If a bad block is encountered, the I/O is marked as failed - The bad block range is immediately cleared from the bad blocks table - Subsequent I/O to the same sectors will succeed This feature is useful for testing scenarios where bad blocks are transient or where devices can recover from bad sectors after a single access attempt. The feature is configurable via the configfs badblocks_once attribute and disabled by default, maintaining compatibility with existing behavior. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 6 ++++++ drivers/block/rnull/rnull.rs | 21 +++++++++++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs.rs index 4db3ba26c2d1..05229ba9173a 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -102,6 +102,7 @@ fn make_group( discard: 10, no_sched:11, badblocks: 12, + badblocks_once: 13, ], }; @@ -125,6 +126,7 @@ fn make_group( discard: false, no_sched: false, bad_blocks: Arc::pin_init(BadBlocks::new(false), GFP_KERNEL)?, + bad_blocks_once: false, }), }), core::iter::empty(), @@ -195,6 +197,7 @@ struct DeviceConfigInner { discard: bool, no_sched: bool, bad_blocks: Arc, + bad_blocks_once: bool, } #[vtable] @@ -231,6 +234,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { discard: guard.discard, no_sched: guard.no_sched, bad_blocks: guard.bad_blocks.clone(), + bad_blocks_once: guard.bad_blocks_once, })?); guard.powered = true; } else if guard.powered && !power_op { @@ -383,3 +387,5 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { Ok(()) } } + +configfs_simple_bool_field!(DeviceConfig, 13, bad_blocks_once); diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 90dbf318c2f8..5486eb6dd921 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -159,6 +159,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { discard: module_parameters::discard.value(), no_sched: module_parameters::no_sched.value(), bad_blocks: Arc::pin_init(BadBlocks::new(false), GFP_KERNEL)?, + bad_blocks_once: false, })?; disks.push(disk, GFP_KERNEL)?; } @@ -186,6 +187,7 @@ struct NullBlkOptions<'a> { discard: bool, no_sched: bool, bad_blocks: Arc, + bad_blocks_once: bool, } struct NullBlkDevice; @@ -204,6 +206,7 @@ fn new(options: NullBlkOptions<'_>) -> Result> { discard, no_sched, bad_blocks, + bad_blocks_once, } = options; let mut flags = mq::tag_set::Flags::default(); @@ -246,6 +249,7 @@ fn new(options: NullBlkOptions<'_>) -> Result> { memory_backed, block_size: block_size.into(), bad_blocks, + bad_blocks_once, }), GFP_KERNEL, )?; @@ -416,6 +420,7 @@ struct QueueData { memory_backed: bool, block_size: u64, bad_blocks: Arc, + bad_blocks_once: bool, } #[pin_data] @@ -465,12 +470,16 @@ fn queue_rq( if queue_data.bad_blocks.enabled() { let start = rq.sector(); let end = start + u64::from(rq.sectors()); - if !matches!( - queue_data.bad_blocks.check(start..end), - badblocks::BlockStatus::None - ) { - rq.data_ref().error.store(1, ordering::Relaxed); - } + match queue_data.bad_blocks.check(start..end) { + badblocks::BlockStatus::None => {} + badblocks::BlockStatus::Acknowledged(range) + | badblocks::BlockStatus::Unacknowledged(range) => { + rq.data_ref().error.store(1, ordering::Relaxed); + if queue_data.bad_blocks_once { + queue_data.bad_blocks.set_good(range)?; + } + } + }; } // TODO: Skip IO if bad block. -- 2.51.2