[RFC PATCH 2/8] rust: kernel: add `request` related TargetOperations

Qingsong Chen posted 8 patches 2 years, 7 months ago
[RFC PATCH 2/8] rust: kernel: add `request` related TargetOperations
Posted by Qingsong Chen 2 years, 7 months ago
- Add `request` related and end_io TargetOperations.
- Add a dummy `request` wrapper and `blk_status_t`.

Signed-off-by: Qingsong Chen <changxian.cqs@antgroup.com>
---
 rust/kernel/device_mapper.rs | 199 +++++++++++++++++++++++++++++++++++
 1 file changed, 199 insertions(+)

diff --git a/rust/kernel/device_mapper.rs b/rust/kernel/device_mapper.rs
index 28286b373b59..153bae3aad79 100644
--- a/rust/kernel/device_mapper.rs
+++ b/rust/kernel/device_mapper.rs
@@ -31,6 +31,39 @@ pub trait TargetOperations: Sized {
     /// Map block IOs. Return [`MapState`] to indicate how to handle the `bio`
     /// later (end or resubmit).
     fn map(t: &mut Target<Self>, bio: Pin<&mut Bio>) -> MapState;
+
+    /// Map `request`. Return [`MapState`] and the optional cloned `request`.
+    #[allow(unused)]
+    fn clone_and_map_rq<'a>(
+        t: &mut Target<Self>,
+        rq: Pin<&mut Request>,
+        map_ctx: &mut MapInfo,
+    ) -> (MapState, Option<Pin<&'a mut Request>>) {
+        unimplemented!()
+    }
+
+    /// Release the cloned `request`.
+    #[allow(unused)]
+    fn release_clone_rq(rq: Pin<&mut Request>, map_ctx: &mut MapInfo) {
+        unimplemented!()
+    }
+
+    /// End the `bio`. Return [`EndState`] and [`BlkStatus`].
+    #[allow(unused)]
+    fn end_io(t: &mut Target<Self>, bio: Pin<&mut Bio>) -> (EndState, BlkStatus) {
+        unimplemented!()
+    }
+
+    /// End the `request`. Return [`EndState`].
+    #[allow(unused)]
+    fn rq_end_io(
+        t: &mut Target<Self>,
+        rq: Pin<&mut Request>,
+        map_ctx: &mut MapInfo,
+        err: BlkStatus,
+    ) -> EndState {
+        unimplemented!()
+    }
 }
 
 /// Wrap the kernel struct `target_type`.
@@ -85,6 +118,18 @@ pub fn register<T: TargetOperations>(
                     (HAS_CTR, ctr, dm_ctr_fn),
                     (HAS_DTR, dtr, dm_dtr_fn),
                     (HAS_MAP, map, dm_map_fn),
+                    (
+                        HAS_CLONE_AND_MAP_RQ,
+                        clone_and_map_rq,
+                        dm_clone_and_map_request_fn
+                    ),
+                    (
+                        HAS_RELEASE_CLONE_RQ,
+                        release_clone_rq,
+                        dm_release_clone_request_fn
+                    ),
+                    (HAS_END_IO, end_io, dm_endio_fn),
+                    (HAS_RQ_END_IO, rq_end_io, dm_request_endio_fn),
                 );
 
                 to_result(bindings::dm_register_target(tt))
@@ -148,6 +193,64 @@ impl TargetType {
             T::map(t, bio) as _
         }
     }
+    unsafe extern "C" fn dm_clone_and_map_request_fn<T: TargetOperations>(
+        ti: *mut bindings::dm_target,
+        rq: *mut bindings::request,
+        map_context: *mut bindings::map_info,
+        clone: *mut *mut bindings::request,
+    ) -> core::ffi::c_int {
+        // SAFETY: the kernel should pass valid `dm_target`, `request`
+        // and `map_info` pointers.
+        unsafe {
+            let t = Target::borrow_mut(ti);
+            let rq = Request::from_raw(rq);
+            let map_ctx = MapInfo::borrow_mut(map_context);
+            let (map_state, rq_cloned) = T::clone_and_map_rq(t, rq, map_ctx);
+            *clone = rq_cloned.map_or(core::ptr::null_mut(), |rq| rq.opaque.get());
+            map_state as _
+        }
+    }
+    unsafe extern "C" fn dm_release_clone_request_fn<T: TargetOperations>(
+        clone: *mut bindings::request,
+        map_context: *mut bindings::map_info,
+    ) {
+        // SAFETY: the kernel should pass valid `request` and `map_info` pointers.
+        unsafe {
+            let rq = Request::from_raw(clone);
+            let map_ctx = MapInfo::borrow_mut(map_context);
+            T::release_clone_rq(rq, map_ctx);
+        }
+    }
+    unsafe extern "C" fn dm_endio_fn<T: TargetOperations>(
+        ti: *mut bindings::dm_target,
+        bio: *mut bindings::bio,
+        error: *mut bindings::blk_status_t,
+    ) -> core::ffi::c_int {
+        // SAFETY: the kernel should pass valid `dm_target`, `bio` and
+        // `error` pointers.
+        unsafe {
+            let t = Target::borrow_mut(ti);
+            let bio = Bio::from_raw(bio);
+            let (end_state, blk_status) = T::end_io(t, bio);
+            *error = blk_status as _;
+            end_state as _
+        }
+    }
+    unsafe extern "C" fn dm_request_endio_fn<T: TargetOperations>(
+        ti: *mut bindings::dm_target,
+        clone: *mut bindings::request,
+        error: bindings::blk_status_t,
+        map_context: *mut bindings::map_info,
+    ) -> core::ffi::c_int {
+        // SAFETY: the kernel should pass valid `dm_target`, `request`
+        // and `map_info` pointers.
+        unsafe {
+            let t = Target::borrow_mut(ti);
+            let rq = Request::from_raw(clone);
+            let map_ctx = MapInfo::borrow_mut(map_context);
+            T::rq_end_io(t, rq, map_ctx, (error as u32).into()) as _
+        }
+    }
 }
 
 /// Wrap the kernel struct `dm_target`.
@@ -305,6 +408,33 @@ pub enum MapState {
     Kill = bindings::DM_MAPIO_KILL,
 }
 
+/// The return values of target end_io function.
+#[repr(u32)]
+pub enum EndState {
+    /// Ended successfully.
+    Done = bindings::DM_ENDIO_DONE,
+
+    /// The io has still not completed (eg, multipath target might
+    /// want to requeue a failed io).
+    Incomplete = bindings::DM_ENDIO_INCOMPLETE,
+
+    /// The target wants to requeue the io.
+    Requeue = bindings::DM_ENDIO_REQUEUE,
+
+    /// The target wants to requeue the io after a delay.
+    DelayRequeue = bindings::DM_ENDIO_DELAY_REQUEUE,
+}
+
+/// Wrap the c struct `map_info`.
+pub struct MapInfo(Opaque<bindings::map_info>);
+
+impl MapInfo {
+    unsafe fn borrow_mut<'a>(ptr: *mut bindings::map_info) -> &'a mut Self {
+        // SAFETY: the caller should pass a valid `ptr`.
+        unsafe { &mut *(ptr as *mut Self) }
+    }
+}
+
 /// Wrap the `c_char` arguments, which yields `CStr`.
 pub struct Args {
     argc: core::ffi::c_uint,
@@ -380,3 +510,72 @@ pub fn is_write(&self) -> bool {
         self.op_and_flags() & bindings::req_op_REQ_OP_WRITE != 0
     }
 }
+
+/// Wrap the kernel struct `request`.
+///
+/// Dummy.
+#[pin_data]
+pub struct Request {
+    #[pin]
+    opaque: Opaque<bindings::request>,
+}
+
+impl Request {
+    unsafe fn from_raw<'a>(ptr: *mut bindings::request) -> Pin<&'a mut Self> {
+        // SAFETY: the caller should pass a valid `request` pointer.
+        unsafe { Pin::new_unchecked(&mut *(ptr as *mut Self)) }
+    }
+}
+
+/// Wrap the block error status values (see [blk_status_t]).
+///
+/// [`blk_status_t`]: ../../../../include/linux/blk_types.h
+#[repr(u32)]
+#[allow(missing_docs)]
+pub enum BlkStatus {
+    Ok,
+    NotSupp,
+    TimeOut,
+    NoSpc,
+    Transport,
+    Target,
+    Nexus,
+    Medium,
+    Protection,
+    Resource,
+    IoErr,
+    DmRequeue,
+    Again,
+    DevResource,
+    ZoneResource,
+    ZoneOpenResource,
+    ZoneActiveResource,
+    Offline,
+    Undefined,
+}
+
+impl From<u32> for BlkStatus {
+    fn from(value: u32) -> Self {
+        match value {
+            0 => Self::Ok,
+            1 => Self::NotSupp,
+            2 => Self::TimeOut,
+            3 => Self::NoSpc,
+            4 => Self::Transport,
+            5 => Self::Target,
+            6 => Self::Nexus,
+            7 => Self::Medium,
+            8 => Self::Protection,
+            9 => Self::Resource,
+            10 => Self::IoErr,
+            11 => Self::DmRequeue,
+            12 => Self::Again,
+            13 => Self::DevResource,
+            14 => Self::ZoneResource,
+            15 => Self::ZoneOpenResource,
+            16 => Self::ZoneActiveResource,
+            17 => Self::Offline,
+            _ => Self::Undefined,
+        }
+    }
+}
-- 
2.40.1
Re: [RFC PATCH 2/8] rust: kernel: add `request` related TargetOperations
Posted by Benno Lossin 2 years, 7 months ago
> - Add `request` related and end_io TargetOperations.
> - Add a dummy `request` wrapper and `blk_status_t`.
> 
> Signed-off-by: Qingsong Chen <changxian.cqs@antgroup.com>
> ---
>  rust/kernel/device_mapper.rs | 199 +++++++++++++++++++++++++++++++++++
>  1 file changed, 199 insertions(+)
> 
> diff --git a/rust/kernel/device_mapper.rs b/rust/kernel/device_mapper.rs
> index 28286b373b59..153bae3aad79 100644
> --- a/rust/kernel/device_mapper.rs
> +++ b/rust/kernel/device_mapper.rs
> @@ -31,6 +31,39 @@ pub trait TargetOperations: Sized {
>      /// Map block IOs. Return [`MapState`] to indicate how to handle the `bio`
>      /// later (end or resubmit).
>      fn map(t: &mut Target<Self>, bio: Pin<&mut Bio>) -> MapState;
> +
> +    /// Map `request`. Return [`MapState`] and the optional cloned `request`.
> +    #[allow(unused)]
> +    fn clone_and_map_rq<'a>(
> +        t: &mut Target<Self>,
> +        rq: Pin<&mut Request>,
> +        map_ctx: &mut MapInfo,
> +    ) -> (MapState, Option<Pin<&'a mut Request>>) {
> +        unimplemented!()

Note that these should be implemented when you submit this as an actual
patch. (Not sure what you would do in this case, but throwing a panic is
not a good idea.) This also applies to all subsequent patches.
I think you might have done this, because you want the C side to choose
the default implementation. But it should also be possible to call these
from the Rust side.

--
Cheers,
Benno

> +    }
> +
> +    /// Release the cloned `request`.
> +    #[allow(unused)]
> +    fn release_clone_rq(rq: Pin<&mut Request>, map_ctx: &mut MapInfo) {
> +        unimplemented!()
> +    }
> +
> +    /// End the `bio`. Return [`EndState`] and [`BlkStatus`].
> +    #[allow(unused)]
> +    fn end_io(t: &mut Target<Self>, bio: Pin<&mut Bio>) -> (EndState, BlkStatus) {
> +        unimplemented!()
> +    }
> +
> +    /// End the `request`. Return [`EndState`].
> +    #[allow(unused)]
> +    fn rq_end_io(
> +        t: &mut Target<Self>,
> +        rq: Pin<&mut Request>,
> +        map_ctx: &mut MapInfo,
> +        err: BlkStatus,
> +    ) -> EndState {
> +        unimplemented!()
> +    }
>  }
> 
>  /// Wrap the kernel struct `target_type`.
> @@ -85,6 +118,18 @@ pub fn register<T: TargetOperations>(
>                      (HAS_CTR, ctr, dm_ctr_fn),
>                      (HAS_DTR, dtr, dm_dtr_fn),
>                      (HAS_MAP, map, dm_map_fn),
> +                    (
> +                        HAS_CLONE_AND_MAP_RQ,
> +                        clone_and_map_rq,
> +                        dm_clone_and_map_request_fn
> +                    ),
> +                    (
> +                        HAS_RELEASE_CLONE_RQ,
> +                        release_clone_rq,
> +                        dm_release_clone_request_fn
> +                    ),
> +                    (HAS_END_IO, end_io, dm_endio_fn),
> +                    (HAS_RQ_END_IO, rq_end_io, dm_request_endio_fn),
>                  );
> 
>                  to_result(bindings::dm_register_target(tt))
> @@ -148,6 +193,64 @@ impl TargetType {
>              T::map(t, bio) as _
>          }
>      }
> +    unsafe extern "C" fn dm_clone_and_map_request_fn<T: TargetOperations>(
> +        ti: *mut bindings::dm_target,
> +        rq: *mut bindings::request,
> +        map_context: *mut bindings::map_info,
> +        clone: *mut *mut bindings::request,
> +    ) -> core::ffi::c_int {
> +        // SAFETY: the kernel should pass valid `dm_target`, `request`
> +        // and `map_info` pointers.
> +        unsafe {
> +            let t = Target::borrow_mut(ti);
> +            let rq = Request::from_raw(rq);
> +            let map_ctx = MapInfo::borrow_mut(map_context);
> +            let (map_state, rq_cloned) = T::clone_and_map_rq(t, rq, map_ctx);
> +            *clone = rq_cloned.map_or(core::ptr::null_mut(), |rq| rq.opaque.get());
> +            map_state as _
> +        }
> +    }
> +    unsafe extern "C" fn dm_release_clone_request_fn<T: TargetOperations>(
> +        clone: *mut bindings::request,
> +        map_context: *mut bindings::map_info,
> +    ) {
> +        // SAFETY: the kernel should pass valid `request` and `map_info` pointers.
> +        unsafe {
> +            let rq = Request::from_raw(clone);
> +            let map_ctx = MapInfo::borrow_mut(map_context);
> +            T::release_clone_rq(rq, map_ctx);
> +        }
> +    }
> +    unsafe extern "C" fn dm_endio_fn<T: TargetOperations>(
> +        ti: *mut bindings::dm_target,
> +        bio: *mut bindings::bio,
> +        error: *mut bindings::blk_status_t,
> +    ) -> core::ffi::c_int {
> +        // SAFETY: the kernel should pass valid `dm_target`, `bio` and
> +        // `error` pointers.
> +        unsafe {
> +            let t = Target::borrow_mut(ti);
> +            let bio = Bio::from_raw(bio);
> +            let (end_state, blk_status) = T::end_io(t, bio);
> +            *error = blk_status as _;
> +            end_state as _
> +        }
> +    }
> +    unsafe extern "C" fn dm_request_endio_fn<T: TargetOperations>(
> +        ti: *mut bindings::dm_target,
> +        clone: *mut bindings::request,
> +        error: bindings::blk_status_t,
> +        map_context: *mut bindings::map_info,
> +    ) -> core::ffi::c_int {
> +        // SAFETY: the kernel should pass valid `dm_target`, `request`
> +        // and `map_info` pointers.
> +        unsafe {
> +            let t = Target::borrow_mut(ti);
> +            let rq = Request::from_raw(clone);
> +            let map_ctx = MapInfo::borrow_mut(map_context);
> +            T::rq_end_io(t, rq, map_ctx, (error as u32).into()) as _
> +        }
> +    }
>  }
> 
>  /// Wrap the kernel struct `dm_target`.
> @@ -305,6 +408,33 @@ pub enum MapState {
>      Kill = bindings::DM_MAPIO_KILL,
>  }
> 
> +/// The return values of target end_io function.
> +#[repr(u32)]
> +pub enum EndState {
> +    /// Ended successfully.
> +    Done = bindings::DM_ENDIO_DONE,
> +
> +    /// The io has still not completed (eg, multipath target might
> +    /// want to requeue a failed io).
> +    Incomplete = bindings::DM_ENDIO_INCOMPLETE,
> +
> +    /// The target wants to requeue the io.
> +    Requeue = bindings::DM_ENDIO_REQUEUE,
> +
> +    /// The target wants to requeue the io after a delay.
> +    DelayRequeue = bindings::DM_ENDIO_DELAY_REQUEUE,
> +}
> +
> +/// Wrap the c struct `map_info`.
> +pub struct MapInfo(Opaque<bindings::map_info>);
> +
> +impl MapInfo {
> +    unsafe fn borrow_mut<'a>(ptr: *mut bindings::map_info) -> &'a mut Self {
> +        // SAFETY: the caller should pass a valid `ptr`.
> +        unsafe { &mut *(ptr as *mut Self) }
> +    }
> +}
> +
>  /// Wrap the `c_char` arguments, which yields `CStr`.
>  pub struct Args {
>      argc: core::ffi::c_uint,
> @@ -380,3 +510,72 @@ pub fn is_write(&self) -> bool {
>          self.op_and_flags() & bindings::req_op_REQ_OP_WRITE != 0
>      }
>  }
> +
> +/// Wrap the kernel struct `request`.
> +///
> +/// Dummy.
> +#[pin_data]
> +pub struct Request {
> +    #[pin]
> +    opaque: Opaque<bindings::request>,
> +}
> +
> +impl Request {
> +    unsafe fn from_raw<'a>(ptr: *mut bindings::request) -> Pin<&'a mut Self> {
> +        // SAFETY: the caller should pass a valid `request` pointer.
> +        unsafe { Pin::new_unchecked(&mut *(ptr as *mut Self)) }
> +    }
> +}
> +
> +/// Wrap the block error status values (see [blk_status_t]).
> +///
> +/// [`blk_status_t`]: ../../../../include/linux/blk_types.h
> +#[repr(u32)]
> +#[allow(missing_docs)]
> +pub enum BlkStatus {
> +    Ok,
> +    NotSupp,
> +    TimeOut,
> +    NoSpc,
> +    Transport,
> +    Target,
> +    Nexus,
> +    Medium,
> +    Protection,
> +    Resource,
> +    IoErr,
> +    DmRequeue,
> +    Again,
> +    DevResource,
> +    ZoneResource,
> +    ZoneOpenResource,
> +    ZoneActiveResource,
> +    Offline,
> +    Undefined,
> +}
> +
> +impl From<u32> for BlkStatus {
> +    fn from(value: u32) -> Self {
> +        match value {
> +            0 => Self::Ok,
> +            1 => Self::NotSupp,
> +            2 => Self::TimeOut,
> +            3 => Self::NoSpc,
> +            4 => Self::Transport,
> +            5 => Self::Target,
> +            6 => Self::Nexus,
> +            7 => Self::Medium,
> +            8 => Self::Protection,
> +            9 => Self::Resource,
> +            10 => Self::IoErr,
> +            11 => Self::DmRequeue,
> +            12 => Self::Again,
> +            13 => Self::DevResource,
> +            14 => Self::ZoneResource,
> +            15 => Self::ZoneOpenResource,
> +            16 => Self::ZoneActiveResource,
> +            17 => Self::Offline,
> +            _ => Self::Undefined,
> +        }
> +    }
> +}
> --
> 2.40.1
> 
Re: [RFC PATCH 2/8] rust: kernel: add `request` related TargetOperations
Posted by Qingsong Chen 2 years, 6 months ago
On 7/14/23 11:49 PM, Benno Lossin wrote:
>> @@ -31,6 +31,39 @@ pub trait TargetOperations: Sized {
>>      /// Map block IOs. Return [`MapState`] to indicate how to handle the `bio`
>>      /// later (end or resubmit).
>>      fn map(t: &mut Target<Self>, bio: Pin<&mut Bio>) -> MapState;
>> +
>> +    /// Map `request`. Return [`MapState`] and the optional cloned `request`.
>> +    #[allow(unused)]
>> +    fn clone_and_map_rq<'a>(
>> +        t: &mut Target<Self>,
>> +        rq: Pin<&mut Request>,
>> +        map_ctx: &mut MapInfo,
>> +    ) -> (MapState, Option<Pin<&'a mut Request>>) {
>> +        unimplemented!()
> 
> Note that these should be implemented when you submit this as an actual
> patch. (Not sure what you would do in this case, but throwing a panic is
> not a good idea.) This also applies to all subsequent patches.
> I think you might have done this, because you want the C side to choose
> the default implementation. But it should also be possible to call these
> from the Rust side.

In fact, we don't want users to call these callbacks manually. The 
normal call-chain would be like this: `map_request`(c) ->
`dm_clone_and_map_rq_fn`(unsafe rust) -> `clone_and_map_rq`(rust).
If users don't implement `clone_and_map_rq`, we would check the
corresponding field of `TargetType`, using the `check_target_operations`
macro. So it won't call the default implementation generally.

Regards,
Qingsong