| // 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<T>`]. | |
| //! - Create a [`GenDisk<T>`], 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::sync::aref::ARef<Request>`] | |
| //! 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<T>`]: gen_disk::GenDisk | |
| //! [`GenDiskBuilder`]: gen_disk::GenDiskBuilder | |
| //! [`GenDiskBuilder::build`]: gen_disk::GenDiskBuilder::build | |
| //! | |
| //! # Examples | |
| //! | |
| //! ```rust | |
| //! use kernel::{ | |
| //! alloc::flags, | |
| //! block::mq::*, | |
| //! new_mutex, | |
| //! prelude::*, | |
| //! sync::{aref::ARef, Arc, Mutex}, | |
| //! }; | |
| //! | |
| //! struct MyBlkDevice; | |
| //! | |
| //! #[vtable] | |
| //! impl Operations for MyBlkDevice { | |
| //! type QueueData = (); | |
| //! | |
| //! fn queue_rq(_queue_data: (), rq: ARef<Request<Self>>, _is_last: bool) -> Result { | |
| //! Request::end_ok(rq); | |
| //! Ok(()) | |
| //! } | |
| //! | |
| //! fn commit_rqs(_queue_data: ()) {} | |
| //! | |
| //! fn complete(rq: ARef<Request<Self>>) { | |
| //! Request::end_ok(rq) | |
| //! .map_err(|_e| kernel::error::code::EIO) | |
| //! .expect("Fatal error - expected to be able to end request"); | |
| //! } | |
| //! } | |
| //! | |
| //! let tagset: Arc<TagSet<MyBlkDevice>> = | |
| //! Arc::pin_init(TagSet::new(1, 256, 1), flags::GFP_KERNEL)?; | |
| //! let mut disk = gen_disk::GenDiskBuilder::new() | |
| //! .capacity_sectors(4096) | |
| //! .build(fmt!("myblk"), tagset, ())?; | |
| //! | |
| //! # Ok::<(), kernel::error::Error>(()) | |
| //! ``` | |
| pub mod gen_disk; | |
| mod operations; | |
| mod request; | |
| mod tag_set; | |
| pub use operations::Operations; | |
| pub use request::Request; | |
| pub use tag_set::TagSet; |