kernel::block

Module mq

Source
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:

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 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§

Structs§

Traits§

  • Implement this trait to interface blk-mq as block devices.