| 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | |
| 3 | //! This module provides types for implementing block drivers that interface the |
| 4 | //! blk-mq subsystem. |
| 5 | //! |
| 6 | //! To implement a block device driver, a Rust module must do the following: |
| 7 | //! |
| 8 | //! - Implement [`Operations`] for a type `T`. |
| 9 | //! - Create a [`TagSet<T>`]. |
| 10 | //! - Create a [`GenDisk<T>`], via the [`GenDiskBuilder`]. |
| 11 | //! - Add the disk to the system by calling [`GenDiskBuilder::build`] passing in |
| 12 | //! the `TagSet` reference. |
| 13 | //! |
| 14 | //! The types available in this module that have direct C counterparts are: |
| 15 | //! |
| 16 | //! - The [`TagSet`] type that abstracts the C type `struct tag_set`. |
| 17 | //! - The [`GenDisk`] type that abstracts the C type `struct gendisk`. |
| 18 | //! - The [`Request`] type that abstracts the C type `struct request`. |
| 19 | //! |
| 20 | //! The kernel will interface with the block device driver by calling the method |
| 21 | //! implementations of the `Operations` trait. |
| 22 | //! |
| 23 | //! IO requests are passed to the driver as [`kernel::sync::aref::ARef<Request>`] |
| 24 | //! instances. The `Request` type is a wrapper around the C `struct request`. |
| 25 | //! The driver must mark end of processing by calling one of the |
| 26 | //! `Request::end`, methods. Failure to do so can lead to deadlock or timeout |
| 27 | //! errors. Please note that the C function `blk_mq_start_request` is implicitly |
| 28 | //! called when the request is queued with the driver. |
| 29 | //! |
| 30 | //! The `TagSet` is responsible for creating and maintaining a mapping between |
| 31 | //! `Request`s and integer ids as well as carrying a pointer to the vtable |
| 32 | //! generated by `Operations`. This mapping is useful for associating |
| 33 | //! completions from hardware with the correct `Request` instance. The `TagSet` |
| 34 | //! determines the maximum queue depth by setting the number of `Request` |
| 35 | //! instances available to the driver, and it determines the number of queues to |
| 36 | //! instantiate for the driver. If possible, a driver should allocate one queue |
| 37 | //! per core, to keep queue data local to a core. |
| 38 | //! |
| 39 | //! One `TagSet` instance can be shared between multiple `GenDisk` instances. |
| 40 | //! This can be useful when implementing drivers where one piece of hardware |
| 41 | //! with one set of IO resources are represented to the user as multiple disks. |
| 42 | //! |
| 43 | //! One significant difference between block device drivers implemented with |
| 44 | //! these Rust abstractions and drivers implemented in C, is that the Rust |
| 45 | //! drivers have to own a reference count on the `Request` type when the IO is |
| 46 | //! in flight. This is to ensure that the C `struct request` instances backing |
| 47 | //! the Rust `Request` instances are live while the Rust driver holds a |
| 48 | //! reference to the `Request`. In addition, the conversion of an integer tag to |
| 49 | //! a `Request` via the `TagSet` would not be sound without this bookkeeping. |
| 50 | //! |
| 51 | //! [`GenDisk`]: gen_disk::GenDisk |
| 52 | //! [`GenDisk<T>`]: gen_disk::GenDisk |
| 53 | //! [`GenDiskBuilder`]: gen_disk::GenDiskBuilder |
| 54 | //! [`GenDiskBuilder::build`]: gen_disk::GenDiskBuilder::build |
| 55 | //! |
| 56 | //! # Examples |
| 57 | //! |
| 58 | //! ```rust |
| 59 | //! use kernel::{ |
| 60 | //! alloc::flags, |
| 61 | //! block::mq::*, |
| 62 | //! new_mutex, |
| 63 | //! prelude::*, |
| 64 | //! sync::{aref::ARef, Arc, Mutex}, |
| 65 | //! }; |
| 66 | //! |
| 67 | //! struct MyBlkDevice; |
| 68 | //! |
| 69 | //! #[vtable] |
| 70 | //! impl Operations for MyBlkDevice { |
| 71 | //! type QueueData = (); |
| 72 | //! |
| 73 | //! fn queue_rq(_queue_data: (), rq: ARef<Request<Self>>, _is_last: bool) -> Result { |
| 74 | //! Request::end_ok(rq); |
| 75 | //! Ok(()) |
| 76 | //! } |
| 77 | //! |
| 78 | //! fn commit_rqs(_queue_data: ()) {} |
| 79 | //! |
| 80 | //! fn complete(rq: ARef<Request<Self>>) { |
| 81 | //! Request::end_ok(rq) |
| 82 | //! .map_err(|_e| kernel::error::code::EIO) |
| 83 | //! .expect("Fatal error - expected to be able to end request"); |
| 84 | //! } |
| 85 | //! } |
| 86 | //! |
| 87 | //! let tagset: Arc<TagSet<MyBlkDevice>> = |
| 88 | //! Arc::pin_init(TagSet::new(1, 256, 1), flags::GFP_KERNEL)?; |
| 89 | //! let mut disk = gen_disk::GenDiskBuilder::new() |
| 90 | //! .capacity_sectors(4096) |
| 91 | //! .build(fmt!("myblk"), tagset, ())?; |
| 92 | //! |
| 93 | //! # Ok::<(), kernel::error::Error>(()) |
| 94 | //! ``` |
| 95 | |
| 96 | |
| 97 | |
| 98 | |
| 99 | |
| 100 | |
| 101 | pub use Operations; |
| 102 | pub use Request; |
| 103 | pub use TagSet; |