Expand description
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
Operationsfor a typeT. - Create a
TagSet<T>. - Create a
GenDisk<T>, via theGenDiskBuilder. - Add the disk to the system by calling
GenDiskBuilder::buildpassing in theTagSetreference.
The types available in this module that have direct C counterparts are:
- The
TagSettype that abstracts the C typestruct tag_set. - The
GenDisktype that abstracts the C typestruct gendisk. - The
Requesttype that abstracts the C typestruct 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
Requests 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.
§Example
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)?;
Modules§
- gen_
disk - Generic disk abstraction.
Structs§
- Request
- A wrapper around a blk-mq
struct request. This represents an IO request. - TagSet
- A wrapper for the C
struct blk_mq_tag_set.
Traits§
- Operations
- Implement this trait to interface blk-mq as block devices.