diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs.rs index 7c2eb5c0b7228..a9d46a5113403 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -35,7 +35,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,blocking\n")?; Ok(writer.bytes_written()) } } @@ -58,6 +58,7 @@ impl configfs::GroupOperations for Config { rotational: 2, size: 3, irqmode: 4, + blocking: 5, ], }; @@ -73,6 +74,7 @@ impl configfs::GroupOperations for Config { disk: None, capacity_mib: 4096, irq_mode: IRQMode::None, + blocking: false, name: name.try_into()?, }), }), @@ -122,6 +124,7 @@ struct DeviceConfigInner { rotational: bool, capacity_mib: u64, irq_mode: IRQMode, + blocking: bool, disk: Option>, } @@ -152,6 +155,7 @@ impl configfs::AttributeOperations<0> for DeviceConfig { guard.rotational, guard.capacity_mib, guard.irq_mode, + guard.blocking, )?); guard.powered = true; } else if guard.powered && !power_op { @@ -259,3 +263,29 @@ impl configfs::AttributeOperations<4> for DeviceConfig { Ok(()) } } + +#[vtable] +impl configfs::AttributeOperations<5> for DeviceConfig { + type Data = DeviceConfig; + + fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result { + let mut writer = kernel::str::Formatter::new(page); + + if this.data.lock().blocking { + writer.write_str("1\n")?; + } else { + writer.write_str("0\n")?; + } + + Ok(writer.bytes_written()) + } + + fn store(this: &DeviceConfig, page: &[u8]) -> Result { + if this.data.lock().powered { + return Err(EBUSY); + } + + this.data.lock().blocking = kstrtobool_bytes(page)?; + Ok(()) + } +} diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 0ca8715febe83..d7ebd504d8df6 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -11,7 +11,7 @@ use kernel::{ mq::{ self, gen_disk::{self, GenDisk}, - Operations, TagSet, + Operations, TagSet, TagSetFlags, }, }, prelude::*, @@ -51,8 +51,15 @@ impl NullBlkDevice { rotational: bool, capacity_mib: u64, irq_mode: IRQMode, + blocking: bool, ) -> Result> { - let tagset = Arc::pin_init(TagSet::new(1, 256, 1), GFP_KERNEL)?; + let flags = if blocking { + TagSetFlags::BLOCKING + } else { + TagSetFlags::empty() + }; + + let tagset = Arc::pin_init(TagSet::new_with_flags(1, 256, 1, flags), GFP_KERNEL)?; let queue_data = Box::new(QueueData { irq_mode }, GFP_KERNEL)?; diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index 1fd0d54dd5493..799afdf36539d 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -100,4 +100,4 @@ mod tag_set; pub use operations::Operations; pub use request::Request; -pub use tag_set::TagSet; +pub use tag_set::{TagSet, TagSetFlags}; diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set.rs index dae9df408a862..72d9bce5b11fa 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -16,6 +16,80 @@ use crate::{ use core::{convert::TryInto, marker::PhantomData}; use pin_init::{pin_data, pinned_drop, PinInit}; +/// Flags that control blk-mq tag set behavior. +/// +/// They can be combined with the operators `|`, `&`, and `!`. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct TagSetFlags(u32); + +impl TagSetFlags { + /// Returns an empty instance where no flags are set. + pub const fn empty() -> Self { + Self(0) + } + + /// Register as a blocking blk-mq driver device. + pub const BLOCKING: Self = Self::new(bindings::BLK_MQ_F_BLOCKING as u32); + + /// Use an underlying blk-mq device for completing I/O. + pub const STACKING: Self = Self::new(bindings::BLK_MQ_F_STACKING as u32); + + /// Share hardware contexts between tags. + pub const TAG_HCTX_SHARED: Self = Self::new(bindings::BLK_MQ_F_TAG_HCTX_SHARED as u32); + + /// Allocate tags on a round-robin basis. + pub const TAG_RR: Self = Self::new(bindings::BLK_MQ_F_TAG_RR as u32); + + /// Disable the I/O scheduler by default. + pub const NO_SCHED_BY_DEFAULT: Self = + Self::new(bindings::BLK_MQ_F_NO_SCHED_BY_DEFAULT as u32); + + /// Check whether `flags` is contained in `self`. + pub fn contains(self, flags: Self) -> bool { + (self & flags) == flags + } + + pub(crate) const fn as_raw(self) -> u32 { + self.0 + } + + const fn all_bits() -> u32 { + Self::BLOCKING.0 + | Self::STACKING.0 + | Self::TAG_HCTX_SHARED.0 + | Self::TAG_RR.0 + | Self::NO_SCHED_BY_DEFAULT.0 + } + + const fn new(value: u32) -> Self { + Self(value) + } +} + +impl core::ops::BitOr for TagSetFlags { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } +} + +impl core::ops::BitAnd for TagSetFlags { + type Output = Self; + + fn bitand(self, rhs: Self) -> Self::Output { + Self(self.0 & rhs.0) + } +} + +impl core::ops::Not for TagSetFlags { + type Output = Self; + + fn not(self) -> Self::Output { + Self(!self.0 & Self::all_bits()) + } +} + /// A wrapper for the C `struct blk_mq_tag_set`. /// /// `struct blk_mq_tag_set` contains a `struct list_head` and so must be pinned. @@ -37,6 +111,16 @@ impl TagSet { nr_hw_queues: u32, num_tags: u32, num_maps: u32, + ) -> impl PinInit { + Self::new_with_flags(nr_hw_queues, num_tags, num_maps, TagSetFlags::empty()) + } + + /// Try to create a new tag set with the given blk-mq flags. + pub fn new_with_flags( + nr_hw_queues: u32, + num_tags: u32, + num_maps: u32, + flags: TagSetFlags, ) -> impl PinInit { let tag_set: bindings::blk_mq_tag_set = pin_init::zeroed(); let tag_set: Result<_> = core::mem::size_of::() @@ -49,7 +133,7 @@ impl TagSet { numa_node: bindings::NUMA_NO_NODE, queue_depth: num_tags, cmd_size, - flags: 0, + flags: flags.as_raw(), driver_data: core::ptr::null_mut::(), nr_maps: num_maps, ..tag_set