// SPDX-License-Identifier: GPL-2.0 //! This module provides types for implementing block drivers that interface the //! blk-mq subsystem. //! //! To implement a block device driver, a Rust module must do the following: //! //! - Implement [`Operations`] for a type `T`. //! - Create a [`TagSet`]. //! - Create a [`GenDisk`], via the [`GenDiskBuilder`]. //! - Add the disk to the system by calling [`GenDiskBuilder::build`] passing in //! the `TagSet` reference. //! //! The types available in this module that have direct C counterparts are: //! //! - The [`TagSet`] type that abstracts the C type `struct tag_set`. //! - The [`GenDisk`] type that abstracts the C type `struct gendisk`. //! - The [`Request`] type that abstracts the C type `struct request`. //! //! The kernel will interface with the block device driver by calling the method //! implementations of the `Operations` trait. //! //! IO requests are passed to the driver as [`kernel::types::ARef`] //! instances. The `Request` type is a wrapper around the C `struct request`. //! The driver must mark end of processing by calling one of the //! `Request::end`, methods. Failure to do so can lead to deadlock or timeout //! errors. Please note that the C function `blk_mq_start_request` is implicitly //! called when the request is queued with the driver. //! //! The `TagSet` is responsible for creating and maintaining a mapping between //! `Request`s and integer ids as well as carrying a pointer to the vtable //! generated by `Operations`. This mapping is useful for associating //! completions from hardware with the correct `Request` instance. The `TagSet` //! determines the maximum queue depth by setting the number of `Request` //! instances available to the driver, and it determines the number of queues to //! instantiate for the driver. If possible, a driver should allocate one queue //! per core, to keep queue data local to a core. //! //! One `TagSet` instance can be shared between multiple `GenDisk` instances. //! This can be useful when implementing drivers where one piece of hardware //! with one set of IO resources are represented to the user as multiple disks. //! //! One significant difference between block device drivers implemented with //! these Rust abstractions and drivers implemented in C, is that the Rust //! drivers have to own a reference count on the `Request` type when the IO is //! in flight. This is to ensure that the C `struct request` instances backing //! the Rust `Request` instances are live while the Rust driver holds a //! reference to the `Request`. In addition, the conversion of an integer tag to //! a `Request` via the `TagSet` would not be sound without this bookkeeping. //! //! [`GenDisk`]: gen_disk::GenDisk //! [`GenDisk`]: gen_disk::GenDisk //! [`GenDiskBuilder`]: gen_disk::GenDiskBuilder //! [`GenDiskBuilder::build`]: gen_disk::GenDiskBuilder::build //! //! # Example //! //! ```rust //! use kernel::{ //! alloc::flags, //! block::mq::*, //! new_mutex, //! prelude::*, //! sync::{Arc, Mutex}, //! types::{ARef, ForeignOwnable}, //! }; //! //! struct MyBlkDevice; //! //! #[vtable] //! impl Operations for MyBlkDevice { //! //! fn queue_rq(rq: ARef>, _is_last: bool) -> Result { //! Request::end_ok(rq); //! Ok(()) //! } //! //! fn commit_rqs() {} //! } //! //! let tagset: Arc> = //! Arc::pin_init(TagSet::new(1, 256, 1), flags::GFP_KERNEL)?; //! let mut disk = gen_disk::GenDiskBuilder::new() //! .capacity_sectors(4096) //! .build(format_args!("myblk"), tagset)?; //! //! # Ok::<(), kernel::error::Error>(()) //! ``` pub mod gen_disk; mod operations; mod raw_writer; mod request; mod tag_set; pub use operations::Operations; pub use request::Request; pub use tag_set::TagSet;