| // SPDX-License-Identifier: GPL-2.0 |
| |
| //! This is a Rust implementation of the C null block driver. |
| |
| mod configfs; |
| |
| use configfs::IRQMode; |
| use kernel::{ |
| block::{ |
| self, |
| mq::{ |
| self, |
| gen_disk::{self, GenDisk}, |
| Operations, TagSet, |
| }, |
| }, |
| error::Result, |
| pr_info, |
| prelude::*, |
| sync::Arc, |
| types::ARef, |
| }; |
| use pin_init::PinInit; |
| |
| module! { |
| type: NullBlkModule, |
| name: "rnull_mod", |
| authors: ["Andreas Hindborg"], |
| description: "Rust implementation of the C null block driver", |
| license: "GPL v2", |
| } |
| |
| #[pin_data] |
| struct NullBlkModule { |
| #[pin] |
| configfs_subsystem: kernel::configfs::Subsystem<configfs::Config>, |
| } |
| |
| impl kernel::InPlaceModule for NullBlkModule { |
| fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> { |
| pr_info!("Rust null_blk loaded\n"); |
| |
| try_pin_init!(Self { |
| configfs_subsystem <- configfs::subsystem(), |
| }) |
| } |
| } |
| |
| struct NullBlkDevice; |
| |
| impl NullBlkDevice { |
| fn new( |
| name: &CStr, |
| block_size: u32, |
| rotational: bool, |
| capacity_mib: u64, |
| irq_mode: IRQMode, |
| ) -> Result<GenDisk<Self>> { |
| let tagset = Arc::pin_init(TagSet::new(1, 256, 1), GFP_KERNEL)?; |
| |
| let queue_data = Box::new(QueueData { irq_mode }, GFP_KERNEL)?; |
| |
| gen_disk::GenDiskBuilder::new() |
| .capacity_sectors(capacity_mib << (20 - block::SECTOR_SHIFT)) |
| .logical_block_size(block_size)? |
| .physical_block_size(block_size)? |
| .rotational(rotational) |
| .build(fmt!("{}", name.to_str()?), tagset, queue_data) |
| } |
| } |
| |
| struct QueueData { |
| irq_mode: IRQMode, |
| } |
| |
| #[vtable] |
| impl Operations for NullBlkDevice { |
| type QueueData = KBox<QueueData>; |
| |
| #[inline(always)] |
| fn queue_rq(queue_data: &QueueData, rq: ARef<mq::Request<Self>>, _is_last: bool) -> Result { |
| match queue_data.irq_mode { |
| IRQMode::None => mq::Request::end_ok(rq) |
| .map_err(|_e| kernel::error::code::EIO) |
| // We take no refcounts on the request, so we expect to be able to |
| // end the request. The request reference must be unique at this |
| // point, and so `end_ok` cannot fail. |
| .expect("Fatal error - expected to be able to end request"), |
| IRQMode::Soft => mq::Request::complete(rq), |
| } |
| Ok(()) |
| } |
| |
| fn commit_rqs(_queue_data: &QueueData) {} |
| |
| fn complete(rq: ARef<mq::Request<Self>>) { |
| mq::Request::end_ok(rq) |
| .map_err(|_e| kernel::error::code::EIO) |
| // We take no refcounts on the request, so we expect to be able to |
| // end the request. The request reference must be unique at this |
| // point, and so `end_ok` cannot fail. |
| .expect("Fatal error - expected to be able to end request"); |
| } |
| } |