use crate::{
bindings,
block::mq::{request::RequestDataWrapper, Request},
error::{from_result, Result},
prelude::*,
sync::{aref::ARef, Refcount},
types::ForeignOwnable,
};
use core::marker::PhantomData;
type ForeignBorrowed<'a, T> = <T as ForeignOwnable>::Borrowed<'a>;
#[macros::vtable]
pub trait Operations: Sized {
type QueueData: ForeignOwnable;
fn queue_rq(
queue_data: ForeignBorrowed<'_, Self::QueueData>,
rq: ARef<Request<Self>>,
is_last: bool,
) -> Result;
fn commit_rqs(queue_data: ForeignBorrowed<'_, Self::QueueData>);
fn complete(rq: ARef<Request<Self>>);
fn poll() -> bool {
build_error!(crate::error::VTABLE_DEFAULT_ERROR)
}
}
pub(crate) struct OperationsVTable<T: Operations>(PhantomData<T>);
impl<T: Operations> OperationsVTable<T> {
unsafe extern "C" fn queue_rq_callback(
hctx: *mut bindings::blk_mq_hw_ctx,
bd: *const bindings::blk_mq_queue_data,
) -> bindings::blk_status_t {
let request = unsafe { &*(*bd).rq.cast::<Request<T>>() };
request.wrapper_ref().refcount().set(2);
let rq = unsafe { Request::aref_from_raw((*bd).rq) };
let queue_data = unsafe { (*(*hctx).queue).queuedata };
let queue_data = unsafe { T::QueueData::borrow(queue_data) };
unsafe { Request::start_unchecked(&rq) };
let ret = T::queue_rq(
queue_data,
rq,
unsafe { (*bd).last },
);
if let Err(e) = ret {
e.to_blk_status()
} else {
bindings::BLK_STS_OK as bindings::blk_status_t
}
}
unsafe extern "C" fn commit_rqs_callback(hctx: *mut bindings::blk_mq_hw_ctx) {
let queue_data = unsafe { (*(*hctx).queue).queuedata };
let queue_data = unsafe { T::QueueData::borrow(queue_data) };
T::commit_rqs(queue_data)
}
unsafe extern "C" fn complete_callback(rq: *mut bindings::request) {
let aref = unsafe { Request::aref_from_raw(rq) };
T::complete(aref);
}
unsafe extern "C" fn poll_callback(
_hctx: *mut bindings::blk_mq_hw_ctx,
_iob: *mut bindings::io_comp_batch,
) -> crate::ffi::c_int {
T::poll().into()
}
unsafe extern "C" fn init_hctx_callback(
_hctx: *mut bindings::blk_mq_hw_ctx,
_tagset_data: *mut crate::ffi::c_void,
_hctx_idx: crate::ffi::c_uint,
) -> crate::ffi::c_int {
from_result(|| Ok(0))
}
unsafe extern "C" fn exit_hctx_callback(
_hctx: *mut bindings::blk_mq_hw_ctx,
_hctx_idx: crate::ffi::c_uint,
) {
}
unsafe extern "C" fn init_request_callback(
_set: *mut bindings::blk_mq_tag_set,
rq: *mut bindings::request,
_hctx_idx: crate::ffi::c_uint,
_numa_node: crate::ffi::c_uint,
) -> crate::ffi::c_int {
from_result(|| {
let pdu = unsafe { Request::wrapper_ptr(rq.cast::<Request<T>>()) };
unsafe { RequestDataWrapper::refcount_ptr(pdu.as_ptr()).write(Refcount::new(0)) };
Ok(0)
})
}
unsafe extern "C" fn exit_request_callback(
_set: *mut bindings::blk_mq_tag_set,
rq: *mut bindings::request,
_hctx_idx: crate::ffi::c_uint,
) {
let pdu = unsafe { bindings::blk_mq_rq_to_pdu(rq) }.cast::<RequestDataWrapper>();
unsafe { core::ptr::drop_in_place(pdu) };
}
const VTABLE: bindings::blk_mq_ops = bindings::blk_mq_ops {
queue_rq: Some(Self::queue_rq_callback),
queue_rqs: None,
commit_rqs: Some(Self::commit_rqs_callback),
get_budget: None,
put_budget: None,
set_rq_budget_token: None,
get_rq_budget_token: None,
timeout: None,
poll: if T::HAS_POLL {
Some(Self::poll_callback)
} else {
None
},
complete: Some(Self::complete_callback),
init_hctx: Some(Self::init_hctx_callback),
exit_hctx: Some(Self::exit_hctx_callback),
init_request: Some(Self::init_request_callback),
exit_request: Some(Self::exit_request_callback),
cleanup_rq: None,
busy: None,
map_queues: None,
#[cfg(CONFIG_BLK_DEBUG_FS)]
show_rq: None,
};
pub(crate) const fn build() -> &'static bindings::blk_mq_ops {
&Self::VTABLE
}
}