Add a timer completion mode to `rnull`. This will complete requests after a specified time has elapsed. To use this mode of operation, set `irqmode` to `2` and write a timeout in nanoseconds to `completion_nsec`. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 13 +++++++-- drivers/block/rnull/rnull.rs | 63 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 70 insertions(+), 6 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs.rs index eafa71dfc40d3..7952f41f42bfd 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -26,7 +26,8 @@ kstrtobool_bytes, CString, // }, - sync::Mutex, // + sync::Mutex, + time, // }; use macros::{ configfs_simple_bool_field, @@ -58,7 +59,7 @@ impl AttributeOperations<0> for Config { fn show(_this: &Config, page: &mut [u8; PAGE_SIZE]) -> Result { let mut writer = kernel::str::Formatter::new(page); - writer.write_str("blocksize,size,rotational,irqmode\n")?; + writer.write_str("blocksize,size,rotational,irqmode,completion_nsec\n")?; Ok(writer.bytes_written()) } } @@ -81,6 +82,7 @@ fn make_group( rotational: 2, size: 3, irqmode: 4, + completion_nsec: 5, ], }; @@ -96,6 +98,7 @@ fn make_group( disk: None, capacity_mib: 4096, irq_mode: IRQMode::None, + completion_time: time::Delta::ZERO, name: name.try_into()?, }), }), @@ -108,6 +111,7 @@ fn make_group( pub(crate) enum IRQMode { None, Soft, + Timer, } impl TryFrom for IRQMode { @@ -117,6 +121,7 @@ fn try_from(value: u8) -> Result { match value { 0 => Ok(Self::None), 1 => Ok(Self::Soft), + 2 => Ok(Self::Timer), _ => Err(EINVAL), } } @@ -127,6 +132,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::None => f.write_str("0")?, Self::Soft => f.write_str("1")?, + Self::Timer => f.write_str("2")?, } Ok(()) } @@ -146,6 +152,7 @@ struct DeviceConfigInner { rotational: bool, capacity_mib: u64, irq_mode: IRQMode, + completion_time: time::Delta, disk: Option>, } @@ -176,6 +183,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { guard.rotational, guard.capacity_mib, guard.irq_mode, + guard.completion_time, )?); guard.powered = true; } else if guard.powered && !power_op { @@ -191,6 +199,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { configfs_simple_bool_field!(DeviceConfig, 2, rotational); configfs_simple_field!(DeviceConfig, 3, capacity_mib, u64); configfs_simple_field!(DeviceConfig, 4, irq_mode, IRQMode); +configfs_simple_field!(DeviceConfig, 5, completion_time, i64, into time::Delta::from_nanos); impl core::str::FromStr for IRQMode { type Err = Error; diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 065639fc4f941..55e56c39f1c2c 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -28,6 +28,15 @@ Arc, Mutex, // }, + time::{ + hrtimer::{ + HrTimerCallback, + HrTimerCallbackContext, + HrTimerPointer, + HrTimerRestart, // + }, + Delta, + }, types::{ OwnableRefCounted, Owned, // @@ -61,7 +70,11 @@ }, irqmode: u8 { default: 0, - description: "IRQ completion handler. 0-none, 1-softirq", + description: "IRQ completion handler. 0-none, 1-softirq, 2-timer", + }, + completion_nsec: u64 { + default: 10_000, + description: "Time in ns to complete a request in hardware. Default: 10,000ns", }, }, } @@ -81,6 +94,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { let mut disks = KVec::new(); let defer_init = move || -> Result<_, Error> { + let completion_time: i64 = (*module_parameters::completion_nsec.value()).try_into()?; for i in 0..(*module_parameters::nr_devices.value()) { let name = CString::try_from_fmt(fmt!("rnullb{}", i))?; @@ -90,6 +104,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { *module_parameters::rotational.value() != 0, *module_parameters::gb.value() * 1024, (*module_parameters::irqmode.value()).try_into()?, + Delta::from_nanos(completion_time), )?; disks.push(disk, GFP_KERNEL)?; } @@ -113,10 +128,17 @@ fn new( rotational: bool, capacity_mib: u64, irq_mode: IRQMode, + completion_time: Delta, ) -> Result> { let tagset = Arc::pin_init(TagSet::new(1, 256, 1), GFP_KERNEL)?; - let queue_data = Box::new(QueueData { irq_mode }, GFP_KERNEL)?; + let queue_data = Box::new( + QueueData { + irq_mode, + completion_time, + }, + GFP_KERNEL, + )?; gen_disk::GenDiskBuilder::new() .capacity_sectors(capacity_mib << (20 - block::SECTOR_SHIFT)) @@ -129,15 +151,43 @@ fn new( struct QueueData { irq_mode: IRQMode, + completion_time: Delta, +} + +#[pin_data] +struct Pdu { + #[pin] + timer: kernel::time::hrtimer::HrTimer, +} + +impl HrTimerCallback for Pdu { + type Pointer<'a> = ARef>; + + fn run(this: Self::Pointer<'_>, _context: HrTimerCallbackContext<'_, Self>) -> HrTimerRestart { + OwnableRefCounted::try_from_shared(this) + .map_err(|_e| kernel::error::code::EIO) + .expect("Failed to complete request") + .end_ok(); + HrTimerRestart::NoRestart + } +} + +kernel::impl_has_hr_timer! { + impl HasHrTimer for Pdu { + mode: kernel::time::hrtimer::RelativeMode, + field: self.timer, + } } #[vtable] impl Operations for NullBlkDevice { type QueueData = KBox; - type RequestData = (); + type RequestData = Pdu; fn new_request_data() -> impl PinInit { - pin_init::zeroed::() + pin_init!(Pdu { + timer <- kernel::time::hrtimer::HrTimer::new(), + }) } #[inline(always)] @@ -145,6 +195,11 @@ fn queue_rq(queue_data: &QueueData, rq: Owned>, _is_last: bool match queue_data.irq_mode { IRQMode::None => rq.end_ok(), IRQMode::Soft => mq::Request::complete(rq.into()), + IRQMode::Timer => { + OwnableRefCounted::into_shared(rq) + .start(queue_data.completion_time) + .dismiss(); + } } Ok(()) } -- 2.51.2