mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2024-12-29 17:23:36 +00:00
3253aba340
Add initial abstractions for working with blk-mq.
This patch is a maintained, refactored subset of code originally published
by Wedson Almeida Filho <wedsonaf@gmail.com> [1].
[1] f2cfd2fe0e/rust/kernel/blk/mq.rs
Cc: Wedson Almeida Filho <wedsonaf@gmail.com>
Signed-off-by: Andreas Hindborg <a.hindborg@samsung.com>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Link: https://lore.kernel.org/r/20240611114551.228679-2-nmi@metaspace.dk
Signed-off-by: Jens Axboe <axboe@kernel.dk>
99 lines
3.7 KiB
Rust
99 lines
3.7 KiB
Rust
// 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::types::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
|
|
//!
|
|
//! # 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<Request<Self>>, _is_last: bool) -> Result {
|
|
//! Request::end_ok(rq);
|
|
//! Ok(())
|
|
//! }
|
|
//!
|
|
//! fn commit_rqs() {}
|
|
//! }
|
|
//!
|
|
//! 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(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;
|