use crate::{
bindings,
block::mq::Operations,
error::Result,
sync::{
aref::{ARef, AlwaysRefCounted},
atomic::Relaxed,
Refcount,
},
types::Opaque,
};
use core::{marker::PhantomData, ptr::NonNull};
#[repr(transparent)]
pub struct Request<T>(Opaque<bindings::request>, PhantomData<T>);
impl<T: Operations> Request<T> {
pub(crate) unsafe fn aref_from_raw(ptr: *mut bindings::request) -> ARef<Self> {
unsafe { ARef::from_raw(NonNull::new_unchecked(ptr.cast())) }
}
pub(crate) unsafe fn start_unchecked(this: &ARef<Self>) {
unsafe { bindings::blk_mq_start_request(this.0.get()) };
}
fn try_set_end(this: ARef<Self>) -> Result<*mut bindings::request, ARef<Self>> {
if let Err(_old) = this
.wrapper_ref()
.refcount()
.as_atomic()
.cmpxchg(2, 0, Relaxed)
{
return Err(this);
}
let request_ptr = this.0.get();
core::mem::forget(this);
Ok(request_ptr)
}
pub fn end_ok(this: ARef<Self>) -> Result<(), ARef<Self>> {
let request_ptr = Self::try_set_end(this)?;
unsafe {
bindings::blk_mq_end_request(
request_ptr,
bindings::BLK_STS_OK as bindings::blk_status_t,
)
};
Ok(())
}
pub fn complete(this: ARef<Self>) {
let ptr = ARef::into_raw(this).cast::<bindings::request>().as_ptr();
if !unsafe { bindings::blk_mq_complete_request_remote(ptr) } {
let this = unsafe { Request::aref_from_raw(ptr) };
T::complete(this);
}
}
pub(crate) unsafe fn wrapper_ptr(this: *mut Self) -> NonNull<RequestDataWrapper> {
let request_ptr = this.cast::<bindings::request>();
let wrapper_ptr =
unsafe { bindings::blk_mq_rq_to_pdu(request_ptr).cast::<RequestDataWrapper>() };
unsafe { NonNull::new_unchecked(wrapper_ptr) }
}
pub(crate) fn wrapper_ref(&self) -> &RequestDataWrapper {
unsafe { Self::wrapper_ptr(core::ptr::from_ref(self).cast_mut()).as_ref() }
}
}
pub(crate) struct RequestDataWrapper {
refcount: Refcount,
}
impl RequestDataWrapper {
pub(crate) fn refcount(&self) -> &Refcount {
&self.refcount
}
pub(crate) unsafe fn refcount_ptr(this: *mut Self) -> *mut Refcount {
unsafe { &raw mut (*this).refcount }
}
}
unsafe impl<T: Operations> Send for Request<T> {}
unsafe impl<T: Operations> Sync for Request<T> {}
unsafe impl<T: Operations> AlwaysRefCounted for Request<T> {
fn inc_ref(&self) {
self.wrapper_ref().refcount().inc();
}
unsafe fn dec_ref(obj: core::ptr::NonNull<Self>) {
let wrapper_ptr = unsafe { Self::wrapper_ptr(obj.as_ptr()).as_ptr() };
let refcount = unsafe { &*RequestDataWrapper::refcount_ptr(wrapper_ptr) };
#[cfg_attr(not(CONFIG_DEBUG_MISC), allow(unused_variables))]
let is_zero = refcount.dec_and_test();
#[cfg(CONFIG_DEBUG_MISC)]
if is_zero {
panic!("Request reached refcount zero in Rust abstractions");
}
}
}