From nobody Sun Feb 8 16:53:20 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4BF48C0015E for ; Sun, 25 Jun 2023 12:17:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232071AbjFYMR2 (ORCPT ); Sun, 25 Jun 2023 08:17:28 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49044 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232041AbjFYMRZ (ORCPT ); Sun, 25 Jun 2023 08:17:25 -0400 Received: from out0-210.mail.aliyun.com (out0-210.mail.aliyun.com [140.205.0.210]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 10660E76; Sun, 25 Jun 2023 05:17:14 -0700 (PDT) X-Alimail-AntiSpam: AC=PASS;BC=-1|-1;BR=01201311R101e4;CH=green;DM=||false|;DS=||;FP=0|-1|-1|-1|0|-1|-1|-1;HT=ay29a033018047192;MF=changxian.cqs@antgroup.com;NM=1;PH=DS;RN=15;SR=0;TI=SMTPD_---.TdJuHXi_1687695430; Received: from localhost(mailfrom:changxian.cqs@antgroup.com fp:SMTPD_---.TdJuHXi_1687695430) by smtp.aliyun-inc.com; Sun, 25 Jun 2023 20:17:11 +0800 From: "Qingsong Chen" To: linux-kernel@vger.kernel.org Cc: "=?UTF-8?B?55Sw5rSq5Lqu?=" , "Qingsong Chen" , "Miguel Ojeda" , "Alex Gaynor" , "Wedson Almeida Filho" , "Boqun Feng" , "Gary Guo" , "=?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?=" , "Benno Lossin" , "Martin Rodriguez Reboredo" , "Alice Ryhl" , "Vincenzo Palazzo" , "Asahi Lina" , Subject: [RFC PATCH 1/8] rust: kernel: add basic abstractions for device-mapper Date: Sun, 25 Jun 2023 20:16:50 +0800 Message-Id: <20230625121657.3631109-2-changxian.cqs@antgroup.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230625121657.3631109-1-changxian.cqs@antgroup.com> References: <20230625121657.3631109-1-changxian.cqs@antgroup.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" - Declare `vtable` for basic target operations. - Wrap `target_type` to register/unregister target. - Wrap `dm_target`/`dm_dev` to handle io request. - Add a dummy `bio` wrapper. Signed-off-by: Qingsong Chen --- rust/bindings/bindings_helper.h | 1 + rust/kernel/device_mapper.rs | 382 ++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 3 files changed, 384 insertions(+) create mode 100644 rust/kernel/device_mapper.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 3e601ce2548d..807fc9cf41b8 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -11,6 +11,7 @@ #include #include #include +#include =20 /* `bindgen` gets confused at certain things. */ const gfp_t BINDINGS_GFP_KERNEL =3D GFP_KERNEL; diff --git a/rust/kernel/device_mapper.rs b/rust/kernel/device_mapper.rs new file mode 100644 index 000000000000..28286b373b59 --- /dev/null +++ b/rust/kernel/device_mapper.rs @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Device Mapper. +//! +//! C header: [`include/linux/device-mapper.h`](../../../../include/linux/= device-mapper.h) + +use core::marker::PhantomData; +use core::ops::Index; +use core::ptr::{addr_of, NonNull}; + +use crate::error::to_result; +use crate::prelude::*; +use crate::str::CStr; +use crate::types::Opaque; + +/// Declare operations that a device mapper target can do. +#[vtable] +pub trait TargetOperations: Sized { + /// Persist user data. + type Private: Sync; + + /// Constructor. The target will already have the table, type, begin a= nd + /// len fields filled in. A `Private` struct can be returned to persist + /// its own context. + fn ctr(t: &mut Target, args: Args) -> Result>; + + /// Destructor. The target could clean up anything hidden in `Private`, + /// and `Private` itself can be dropped automatically. + fn dtr(t: &mut Target); + + /// Map block IOs. Return [`MapState`] to indicate how to handle the `= bio` + /// later (end or resubmit). + fn map(t: &mut Target, bio: Pin<&mut Bio>) -> MapState; +} + +/// Wrap the kernel struct `target_type`. +/// +/// It contains a struct `list_head` for internal device-mapper use, so it +/// should be pinned. Users can use this struct to register/unregister the= ir +/// own device mapper target. +#[pin_data(PinnedDrop)] +pub struct TargetType { + #[pin] + opaque: Opaque, +} + +/// Define target feature type. +pub type Feature =3D u64; + +// SAFETY: It's OK to access `TargetType` from multiple threads. The +// `dm_register_target` and `dm_unregister_target` provides its own +// synchronization. +unsafe impl Sync for TargetType {} + +macro_rules! check_target_operations { + ($tt:expr, $(($op:ident, $filed:ident, $func:ident),)+) =3D> {$( + if ::$op { + (*$tt).$filed =3D Some(TargetType::$func::); + } + )+}; +} + +impl TargetType { + /// Provide an in-place constructor to register a new device mapper ta= rget. + pub fn register( + module: &'static ThisModule, + name: &'static CStr, + version: [u32; 3], + feature: Feature, + ) -> impl PinInit { + // SAFETY: `slot` is valid while the closure is called. + unsafe { + init::pin_init_from_closure(move |slot: *mut Self| { + // `slot` contains uninit memory, avoid creating a referen= ce. + let opaque =3D addr_of!((*slot).opaque); + let tt =3D Opaque::raw_get(opaque); + + (*tt).module =3D module.0; + (*tt).name =3D name.as_char_ptr(); + (*tt).version =3D version; + (*tt).features =3D feature; + + check_target_operations!( + tt, + (HAS_CTR, ctr, dm_ctr_fn), + (HAS_DTR, dtr, dm_dtr_fn), + (HAS_MAP, map, dm_map_fn), + ); + + to_result(bindings::dm_register_target(tt)) + }) + } + } +} + +#[pinned_drop] +impl PinnedDrop for TargetType { + fn drop(self: Pin<&mut Self>) { + // SAFETY: `self.opaque` are initialized by the `register` constru= ctor, + // so it's valid. + unsafe { + bindings::dm_unregister_target(self.opaque.get()); + } + } +} + +impl TargetType { + unsafe extern "C" fn dm_ctr_fn( + target: *mut bindings::dm_target, + argc: core::ffi::c_uint, + argv: *mut *mut core::ffi::c_char, + ) -> core::ffi::c_int { + // SAFETY: the kernel splits arguments by `dm_split_args`, then pa= ss + // suitable `argc` and `argv` to `dm_ctr_fn`. If `argc` is not zer= o, + // `argv` is non-null and valid. + let args =3D unsafe { Args::new(argc, argv) }; + + // SAFETY: the kernel should pass a valid `dm_target`. + let t =3D unsafe { Target::borrow_mut(target) }; + T::ctr(t, args).map_or_else( + |e| e.to_errno(), + // SAFETY: the kernel should pass a valid `dm_target`. + |p| unsafe { + (*target).private =3D Box::into_raw(p) as _; + 0 + }, + ) + } + unsafe extern "C" fn dm_dtr_fn(ti: *mut bindings:= :dm_target) { + // SAFETY: the kernel should pass a valid `dm_target`. + let t =3D unsafe { Target::borrow_mut(ti) }; + T::dtr(t); + // SAFETY: `private` is constructed in `dm_ctr_fn`, and we drop it= here. + unsafe { + let ptr =3D (*ti).private as *mut T::Private; + drop(Box::from_raw(ptr)); + (*ti).private =3D core::ptr::null_mut(); + } + } + unsafe extern "C" fn dm_map_fn( + ti: *mut bindings::dm_target, + bio: *mut bindings::bio, + ) -> core::ffi::c_int { + // SAFETY: the kernel should pass a valid `dm_target` and `bio`. + unsafe { + let t =3D Target::borrow_mut(ti); + let bio =3D Bio::from_raw(bio); + T::map(t, bio) as _ + } + } +} + +/// Wrap the kernel struct `dm_target`. +/// +/// This struct represents a device mapper target. And the device mapper +/// core will alloc/free `dm_target` instances, so we just `borrow` it. +/// It also holds a `Private` struct, which is used to persist user's data, +/// and can be accessed by the `private` method. +pub struct Target { + opaque: Opaque, + _p: PhantomData<*mut T::Private>, +} + +impl Target { + unsafe fn borrow<'a>(ptr: *const bindings::dm_target) -> &'a Self { + // SAFETY: the caller should pass a valid `ptr`. + unsafe { &*(ptr as *const Self) } + } + + unsafe fn borrow_mut<'a>(ptr: *mut bindings::dm_target) -> &'a mut Sel= f { + // SAFETY: the caller should pass a valid `ptr`. + unsafe { &mut *(ptr as *mut Self) } + } + + /// Access user's private data. + pub fn private(&mut self) -> Option> { + let t =3D self.opaque.get(); + // SAFETY: `self.opaque` is borrowed from foreign pointer, should = be valid. + // And 'p' is non-null and assigned in `dm_ctr_fn`, so it's valid. + unsafe { + ((*t).private as *mut T::Private) + .as_mut() + .map(|p| Pin::new_unchecked(p)) + } + } + + /// Return the target name. + pub fn name(&self) -> &CStr { + // SAFETY: `self.opaque` is borrowed from foreign pointer, should = be valid. + unsafe { + let name =3D (*(*self.opaque.get()).type_).name; + CStr::from_char_ptr(name) + } + } + + /// Return the target version. + pub fn version(&self) -> [u32; 3] { + // SAFETY: `self.opaque` is borrowed from foreign pointer, should = be valid. + unsafe { (*(*self.opaque.get()).type_).version } + } + + /// Return the target begin sector. + pub fn begin_sector(&self) -> usize { + // SAFETY: `self.opaque` is borrowed from foreign pointer, should = be valid. + unsafe { (*self.opaque.get()).begin as _ } + } + + /// Return the target total sectors. + pub fn total_sectors(&self) -> usize { + // SAFETY: `self.opaque` is borrowed from foreign pointer, should = be valid. + unsafe { (*self.opaque.get()).len as _ } + } + + /// Get the underlying device by `path`. The `dm_put_device` will be c= alled when + /// [`TargetDevice`] is dropped. + pub fn get_device(&mut self, path: &CStr) -> Result> { + let mut dd =3D core::ptr::null_mut(); + // SAFETY: `self.opaque` is borrowed from foreign pointer, should = be valid. + unsafe { + let mode =3D bindings::dm_table_get_mode((*self.opaque.get()).= table); + match bindings::dm_get_device(self.opaque.get(), path.as_char_= ptr(), mode, &mut dd) { + 0 =3D> { + let ti =3D self.opaque.get(); + Ok(TargetDevice::from_raw(ti, dd)) + } + err =3D> Err(Error::from_errno(err)), + } + } + } +} + +/// Represent an underlying device of a device mapper target. +/// +/// We also holds a pointer to `dm_target`, so that we can call +/// `dm_put_device` in `drop`, to close the device correctly. +pub struct TargetDevice { + ti: NonNull, + dev: NonNull, + _p: PhantomData<*mut Target>, +} + +impl TargetDevice { + unsafe fn from_raw(ti: *mut bindings::dm_target, dd: *mut bindings::dm= _dev) -> Self { + // SAFETY: the caller should pass valid `dm_target` and `dm_dev`. + unsafe { + Self { + ti: NonNull::new_unchecked(ti), + dev: NonNull::new_unchecked(dd), + _p: PhantomData, + } + } + } + + /// Borrow the device mapper target. + pub fn target(&self) -> &Target { + // SAFETY: the `from_raw` caller should pass valid `ti` pointer. + unsafe { Target::borrow(self.ti.as_ptr()) } + } + + /// Borrow the device mapper target mutably. + pub fn mut_target(&mut self) -> &mut Target { + // SAFETY: the `from_raw` caller should pass valid `ti` pointer. + unsafe { Target::borrow_mut(self.ti.as_ptr()) } + } + + /// Return the device name. + pub fn device_name(&self) -> &CStr { + // SAFETY: the `from_raw` caller should pass valid `dev` pointer. + unsafe { + let name =3D (*self.dev.as_ptr()).name.as_ptr(); + CStr::from_char_ptr(name) + } + } + + /// Return the total device sectors. + pub fn device_sectors(&self) -> usize { + // SAFETY: the `from_raw` caller should pass valid `dev` pointer. + unsafe { (*(*self.dev.as_ptr()).bdev).bd_nr_sectors as _ } + } +} + +impl Drop for TargetDevice { + fn drop(&mut self) { + // SAFETY: the `from_raw` caller should pass valid `ti` and `dev`. + unsafe { bindings::dm_put_device(self.ti.as_ptr(), self.dev.as_ptr= ()) } + } +} + +/// The return values of target map function, i.e., [`TargetOperations::ma= p`]. +#[repr(u32)] +pub enum MapState { + /// The target will handle the io by resubmitting it later. + Submitted =3D bindings::DM_MAPIO_SUBMITTED, + + /// Simple remap complete. + Remapped =3D bindings::DM_MAPIO_REMAPPED, + + /// The target wants to requeue the io. + Requeue =3D bindings::DM_MAPIO_REQUEUE, + + /// The target wants to requeue the io after a delay. + DelayRequeue =3D bindings::DM_MAPIO_DELAY_REQUEUE, + + /// The target wants to complete the io. + Kill =3D bindings::DM_MAPIO_KILL, +} + +/// Wrap the `c_char` arguments, which yields `CStr`. +pub struct Args { + argc: core::ffi::c_uint, + argv: *mut *mut core::ffi::c_char, +} + +impl Args { + /// The caller should ensure that the number of valid `argv` pointers + /// should be exactly `argc`. + unsafe fn new(argc: core::ffi::c_uint, argv: *mut *mut core::ffi::c_ch= ar) -> Self { + Self { argc, argv } + } + + /// Return the number of arguments. + pub fn len(&self) -> usize { + self.argc as _ + } + + /// Return the `nth` (from zero) argument. + /// + /// If the index is out of bounds, return `None`. + pub fn get(&self, index: usize) -> Option<&CStr> { + if self.argc =3D=3D 0 || index >=3D self.argc as _ { + None + } else { + // SAFETY: the `new` caller should ensure the number of valid = `argv`. + unsafe { Some(CStr::from_char_ptr(*self.argv.add(index))) } + } + } +} + +impl Index for Args { + type Output =3D CStr; + + /// When using the indexing operator(`[]`), the caller should check the + /// length of [`Args`]. If the index is out of bounds, this will [`pan= ic`]. + fn index(&self, index: usize) -> &Self::Output { + if self.argc =3D=3D 0 || index >=3D self.argc as _ { + panic!( + "Index out of bounds: the length is {} but the index is {}= .", + self.argc, index + ) + } else { + // SAFETY: the `new` caller should ensure the number of valid = `argv`. + unsafe { CStr::from_char_ptr(*self.argv.add(index)) } + } + } +} + +/// Wrap the kernel struct `bio`. +/// +/// Dummy. +#[pin_data] +pub struct Bio { + #[pin] + opaque: Opaque, +} + +impl Bio { + unsafe fn from_raw<'a>(ptr: *mut bindings::bio) -> Pin<&'a mut Self> { + // SAFETY: the caller should pass a valid `bio` pointer. + unsafe { Pin::new(&mut *(ptr as *mut Self)) } + } + + fn op_and_flags(&self) -> u32 { + // SAFETY: the `from_raw` caller should pass valid `bio`, so + // `self.opaque` is valid too. + unsafe { (*self.opaque.get()).bi_opf } + } + + /// Return `true` if the bio request is write. + pub fn is_write(&self) -> bool { + self.op_and_flags() & bindings::req_op_REQ_OP_WRITE !=3D 0 + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 85b261209977..3dc19f4642f0 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -31,6 +31,7 @@ #[cfg(not(testlib))] mod allocator; mod build_assert; +pub mod device_mapper; pub mod error; pub mod init; pub mod ioctl; --=20 2.40.1 From nobody Sun Feb 8 16:53:20 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E45A1C0015E for ; Sun, 25 Jun 2023 12:17:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232105AbjFYMRo (ORCPT ); Sun, 25 Jun 2023 08:17:44 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49102 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232080AbjFYMRi (ORCPT ); Sun, 25 Jun 2023 08:17:38 -0400 Received: from out0-212.mail.aliyun.com (out0-212.mail.aliyun.com [140.205.0.212]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CD5CAE6E; Sun, 25 Jun 2023 05:17:20 -0700 (PDT) X-Alimail-AntiSpam: AC=PASS;BC=-1|-1;BR=01201311R651e4;CH=green;DM=||false|;DS=||;FP=0|-1|-1|-1|0|-1|-1|-1;HT=ay29a033018047211;MF=changxian.cqs@antgroup.com;NM=1;PH=DS;RN=11;SR=0;TI=SMTPD_---.TdJk.ax_1687695435; Received: from localhost(mailfrom:changxian.cqs@antgroup.com fp:SMTPD_---.TdJk.ax_1687695435) by smtp.aliyun-inc.com; Sun, 25 Jun 2023 20:17:15 +0800 From: "Qingsong Chen" To: linux-kernel@vger.kernel.org Cc: "=?UTF-8?B?55Sw5rSq5Lqu?=" , "Qingsong Chen" , "Miguel Ojeda" , "Alex Gaynor" , "Wedson Almeida Filho" , "Boqun Feng" , "Gary Guo" , "=?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?=" , "Benno Lossin" , Subject: [RFC PATCH 2/8] rust: kernel: add `request` related TargetOperations Date: Sun, 25 Jun 2023 20:16:51 +0800 Message-Id: <20230625121657.3631109-3-changxian.cqs@antgroup.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230625121657.3631109-1-changxian.cqs@antgroup.com> References: <20230625121657.3631109-1-changxian.cqs@antgroup.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" - Add `request` related and end_io TargetOperations. - Add a dummy `request` wrapper and `blk_status_t`. Signed-off-by: Qingsong Chen --- 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, bio: Pin<&mut Bio>) -> MapState; + + /// Map `request`. Return [`MapState`] and the optional cloned `reques= t`. + #[allow(unused)] + fn clone_and_map_rq<'a>( + t: &mut Target, + rq: Pin<&mut Request>, + map_ctx: &mut MapInfo, + ) -> (MapState, Option>) { + 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, bio: Pin<&mut Bio>) -> (EndState, BlkS= tatus) { + unimplemented!() + } + + /// End the `request`. Return [`EndState`]. + #[allow(unused)] + fn rq_end_io( + t: &mut Target, + rq: Pin<&mut Request>, + map_ctx: &mut MapInfo, + err: BlkStatus, + ) -> EndState { + unimplemented!() + } } =20 /// Wrap the kernel struct `target_type`. @@ -85,6 +118,18 @@ pub fn register( (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), ); =20 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( + 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 =3D Target::borrow_mut(ti); + let rq =3D Request::from_raw(rq); + let map_ctx =3D MapInfo::borrow_mut(map_context); + let (map_state, rq_cloned) =3D T::clone_and_map_rq(t, rq, map_= ctx); + *clone =3D rq_cloned.map_or(core::ptr::null_mut(), |rq| rq.opa= que.get()); + map_state as _ + } + } + unsafe extern "C" fn dm_release_clone_request_fn( + clone: *mut bindings::request, + map_context: *mut bindings::map_info, + ) { + // SAFETY: the kernel should pass valid `request` and `map_info` p= ointers. + unsafe { + let rq =3D Request::from_raw(clone); + let map_ctx =3D MapInfo::borrow_mut(map_context); + T::release_clone_rq(rq, map_ctx); + } + } + unsafe extern "C" fn dm_endio_fn( + 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 =3D Target::borrow_mut(ti); + let bio =3D Bio::from_raw(bio); + let (end_state, blk_status) =3D T::end_io(t, bio); + *error =3D blk_status as _; + end_state as _ + } + } + unsafe extern "C" fn dm_request_endio_fn( + 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 =3D Target::borrow_mut(ti); + let rq =3D Request::from_raw(clone); + let map_ctx =3D MapInfo::borrow_mut(map_context); + T::rq_end_io(t, rq, map_ctx, (error as u32).into()) as _ + } + } } =20 /// Wrap the kernel struct `dm_target`. @@ -305,6 +408,33 @@ pub enum MapState { Kill =3D bindings::DM_MAPIO_KILL, } =20 +/// The return values of target end_io function. +#[repr(u32)] +pub enum EndState { + /// Ended successfully. + Done =3D bindings::DM_ENDIO_DONE, + + /// The io has still not completed (eg, multipath target might + /// want to requeue a failed io). + Incomplete =3D bindings::DM_ENDIO_INCOMPLETE, + + /// The target wants to requeue the io. + Requeue =3D bindings::DM_ENDIO_REQUEUE, + + /// The target wants to requeue the io after a delay. + DelayRequeue =3D bindings::DM_ENDIO_DELAY_REQUEUE, +} + +/// Wrap the c struct `map_info`. +pub struct MapInfo(Opaque); + +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 !=3D 0 } } + +/// Wrap the kernel struct `request`. +/// +/// Dummy. +#[pin_data] +pub struct Request { + #[pin] + opaque: Opaque, +} + +impl Request { + unsafe fn from_raw<'a>(ptr: *mut bindings::request) -> Pin<&'a mut Sel= f> { + // 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 for BlkStatus { + fn from(value: u32) -> Self { + match value { + 0 =3D> Self::Ok, + 1 =3D> Self::NotSupp, + 2 =3D> Self::TimeOut, + 3 =3D> Self::NoSpc, + 4 =3D> Self::Transport, + 5 =3D> Self::Target, + 6 =3D> Self::Nexus, + 7 =3D> Self::Medium, + 8 =3D> Self::Protection, + 9 =3D> Self::Resource, + 10 =3D> Self::IoErr, + 11 =3D> Self::DmRequeue, + 12 =3D> Self::Again, + 13 =3D> Self::DevResource, + 14 =3D> Self::ZoneResource, + 15 =3D> Self::ZoneOpenResource, + 16 =3D> Self::ZoneActiveResource, + 17 =3D> Self::Offline, + _ =3D> Self::Undefined, + } + } +} --=20 2.40.1 From nobody Sun Feb 8 16:53:20 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B5B03EB64DC for ; Sun, 25 Jun 2023 12:17:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232143AbjFYMRt (ORCPT ); Sun, 25 Jun 2023 08:17:49 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49174 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232112AbjFYMRj (ORCPT ); Sun, 25 Jun 2023 08:17:39 -0400 Received: from out0-195.mail.aliyun.com (out0-195.mail.aliyun.com [140.205.0.195]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C869AE48; Sun, 25 Jun 2023 05:17:23 -0700 (PDT) X-Alimail-AntiSpam: AC=PASS;BC=-1|-1;BR=01201311R201e4;CH=green;DM=||false|;DS=||;FP=0|-1|-1|-1|0|-1|-1|-1;HT=ay29a033018047206;MF=changxian.cqs@antgroup.com;NM=1;PH=DS;RN=11;SR=0;TI=SMTPD_---.TdJuHZE_1687695439; Received: from localhost(mailfrom:changxian.cqs@antgroup.com fp:SMTPD_---.TdJuHZE_1687695439) by smtp.aliyun-inc.com; Sun, 25 Jun 2023 20:17:20 +0800 From: "Qingsong Chen" To: linux-kernel@vger.kernel.org Cc: "=?UTF-8?B?55Sw5rSq5Lqu?=" , "Qingsong Chen" , "Miguel Ojeda" , "Alex Gaynor" , "Wedson Almeida Filho" , "Boqun Feng" , "Gary Guo" , "=?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?=" , "Benno Lossin" , Subject: [RFC PATCH 3/8] rust: kernel: add some hook TargetOperations Date: Sun, 25 Jun 2023 20:16:52 +0800 Message-Id: <20230625121657.3631109-4-changxian.cqs@antgroup.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230625121657.3631109-1-changxian.cqs@antgroup.com> References: <20230625121657.3631109-1-changxian.cqs@antgroup.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" - Add hooks when suspend or resume a target. - Add operation to check if a target is busy. Signed-off-by: Qingsong Chen --- rust/kernel/device_mapper.rs | 76 ++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/rust/kernel/device_mapper.rs b/rust/kernel/device_mapper.rs index 153bae3aad79..aeda12245171 100644 --- a/rust/kernel/device_mapper.rs +++ b/rust/kernel/device_mapper.rs @@ -64,6 +64,42 @@ fn rq_end_io( ) -> EndState { unimplemented!() } + + /// Hook presuspend. + #[allow(unused)] + fn presuspend(t: &mut Target) { + unimplemented!() + } + + /// Hook presuspend_undo. + #[allow(unused)] + fn presuspend_undo(t: &mut Target) { + unimplemented!() + } + + /// Hook postsuspend. + #[allow(unused)] + fn postsuspend(t: &mut Target) { + unimplemented!() + } + + /// Hook preresume. + #[allow(unused)] + fn preresume(t: &mut Target) -> Result { + unimplemented!() + } + + /// Hook resume. + #[allow(unused)] + fn resume(t: &mut Target) { + unimplemented!() + } + + /// Check if target is busy. + #[allow(unused)] + fn busy(t: &mut Target) -> bool { + unimplemented!() + } } =20 /// Wrap the kernel struct `target_type`. @@ -130,6 +166,12 @@ pub fn register( ), (HAS_END_IO, end_io, dm_endio_fn), (HAS_RQ_END_IO, rq_end_io, dm_request_endio_fn), + (HAS_PRESUSPEND, presuspend, dm_presuspend_fn), + (HAS_PRESUSPEND_UNDO, presuspend_undo, dm_presuspend_u= ndo_fn), + (HAS_POSTSUSPEND, postsuspend, dm_postsuspend_fn), + (HAS_PRERESUME, preresume, dm_preresume_fn), + (HAS_RESUME, resume, dm_resume_fn), + (HAS_BUSY, busy, dm_busy_fn), ); =20 to_result(bindings::dm_register_target(tt)) @@ -251,6 +293,40 @@ impl TargetType { T::rq_end_io(t, rq, map_ctx, (error as u32).into()) as _ } } + unsafe extern "C" fn dm_presuspend_fn(ti: *mut bi= ndings::dm_target) { + // SAFETY: the kernel should pass valid `dm_target` pointer. + let t =3D unsafe { Target::borrow_mut(ti) }; + T::presuspend(t); + } + unsafe extern "C" fn dm_presuspend_undo_fn(ti: *m= ut bindings::dm_target) { + // SAFETY: the kernel should pass valid `dm_target` pointer. + let t =3D unsafe { Target::borrow_mut(ti) }; + T::presuspend_undo(t); + } + unsafe extern "C" fn dm_postsuspend_fn(ti: *mut b= indings::dm_target) { + // SAFETY: the kernel should pass valid `dm_target` pointer. + let t =3D unsafe { Target::borrow_mut(ti) }; + T::postsuspend(t); + } + unsafe extern "C" fn dm_preresume_fn( + ti: *mut bindings::dm_target, + ) -> core::ffi::c_int { + // SAFETY: the kernel should pass valid `dm_target` pointer. + let t =3D unsafe { Target::borrow_mut(ti) }; + T::preresume(t).map_or_else(|e| e.to_errno(), |_| 0) + } + unsafe extern "C" fn dm_resume_fn(ti: *mut bindin= gs::dm_target) { + // SAFETY: the kernel should pass valid `dm_target` pointer. + let t =3D unsafe { Target::borrow_mut(ti) }; + T::resume(t); + } + unsafe extern "C" fn dm_busy_fn( + ti: *mut bindings::dm_target, + ) -> core::ffi::c_int { + // SAFETY: the kernel should pass valid `dm_target` pointer. + let t =3D unsafe { Target::borrow_mut(ti) }; + T::busy(t) as _ + } } =20 /// Wrap the kernel struct `dm_target`. --=20 2.40.1 From nobody Sun Feb 8 16:53:20 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 59C71EB64DC for ; Sun, 25 Jun 2023 12:17:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232075AbjFYMR5 (ORCPT ); Sun, 25 Jun 2023 08:17:57 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49114 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232111AbjFYMRo (ORCPT ); Sun, 25 Jun 2023 08:17:44 -0400 Received: from out0-196.mail.aliyun.com (out0-196.mail.aliyun.com [140.205.0.196]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4F388E51; Sun, 25 Jun 2023 05:17:29 -0700 (PDT) X-Alimail-AntiSpam: AC=PASS;BC=-1|-1;BR=01201311R131e4;CH=green;DM=||false|;DS=||;FP=0|-1|-1|-1|0|-1|-1|-1;HT=ay29a033018047213;MF=changxian.cqs@antgroup.com;NM=1;PH=DS;RN=11;SR=0;TI=SMTPD_---.TdJk.cz_1687695444; Received: from localhost(mailfrom:changxian.cqs@antgroup.com fp:SMTPD_---.TdJk.cz_1687695444) by smtp.aliyun-inc.com; Sun, 25 Jun 2023 20:17:25 +0800 From: "Qingsong Chen" To: linux-kernel@vger.kernel.org Cc: "=?UTF-8?B?55Sw5rSq5Lqu?=" , "Qingsong Chen" , "Miguel Ojeda" , "Alex Gaynor" , "Wedson Almeida Filho" , "Boqun Feng" , "Gary Guo" , "=?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?=" , "Benno Lossin" , Subject: [RFC PATCH 4/8] rust: kernel: add some info handler TargetOperations Date: Sun, 25 Jun 2023 20:16:53 +0800 Message-Id: <20230625121657.3631109-5-changxian.cqs@antgroup.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230625121657.3631109-1-changxian.cqs@antgroup.com> References: <20230625121657.3631109-1-changxian.cqs@antgroup.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" - Add `status`/`message` to retrieve info of targets. - Add `report_zones` to propagate report zones command to underlying devices. Signed-off-by: Qingsong Chen --- rust/kernel/device_mapper.rs | 149 ++++++++++++++++++++++++++++++++++- 1 file changed, 148 insertions(+), 1 deletion(-) diff --git a/rust/kernel/device_mapper.rs b/rust/kernel/device_mapper.rs index aeda12245171..9a62208fda56 100644 --- a/rust/kernel/device_mapper.rs +++ b/rust/kernel/device_mapper.rs @@ -10,7 +10,7 @@ =20 use crate::error::to_result; use crate::prelude::*; -use crate::str::CStr; +use crate::str::{CStr, CString}; use crate::types::Opaque; =20 /// Declare operations that a device mapper target can do. @@ -100,6 +100,24 @@ fn resume(t: &mut Target) { fn busy(t: &mut Target) -> bool { unimplemented!() } + + /// Return the target status. + #[allow(unused)] + fn status(t: &mut Target, type_: StatusType, flags: StatusFlags)= -> Option { + unimplemented!() + } + + /// Handle messages to the target. + #[allow(unused)] + fn message(t: &mut Target, args: Args) -> Result { + unimplemented!() + } + + /// Propagate report zones command to underlying devices. + #[allow(unused)] + fn report_zones(t: &mut Target, args: &mut [ReportZonesArgs]) ->= Result { + unimplemented!() + } } =20 /// Wrap the kernel struct `target_type`. @@ -172,6 +190,9 @@ pub fn register( (HAS_PRERESUME, preresume, dm_preresume_fn), (HAS_RESUME, resume, dm_resume_fn), (HAS_BUSY, busy, dm_busy_fn), + (HAS_STATUS, status, dm_status_fn), + (HAS_MESSAGE, message, dm_message_fn), + (HAS_REPORT_ZONES, report_zones, dm_report_zones_fn), ); =20 to_result(bindings::dm_register_target(tt)) @@ -327,6 +348,69 @@ impl TargetType { let t =3D unsafe { Target::borrow_mut(ti) }; T::busy(t) as _ } + unsafe extern "C" fn dm_status_fn( + ti: *mut bindings::dm_target, + status_type: bindings::status_type_t, + status_flags: core::ffi::c_uint, + result: *mut core::ffi::c_char, + maxlen: core::ffi::c_uint, + ) { + // SAFETY: the kernel should pass valid `dm_target` pointer. + let t =3D unsafe { Target::borrow_mut(ti) }; + if let Some(s) =3D T::status(t, status_type.into(), status_flags.i= nto()) { + let count =3D s.len_with_nul().min(maxlen as _); + // SAFETY: the kernel should pass valid `result` pointer, and + // `count` is not greater than `maxlen`. + unsafe { + core::ptr::copy(s.as_char_ptr(), result, count); + } + } + } + unsafe extern "C" fn dm_message_fn( + ti: *mut bindings::dm_target, + argc: core::ffi::c_uint, + argv: *mut *mut core::ffi::c_char, + result: *mut core::ffi::c_char, + maxlen: core::ffi::c_uint, + ) -> core::ffi::c_int { + // SAFETY: the kernel splits arguments by `dm_split_args`, then pa= ss + // suitable `argc` and `argv` to `dm_ctr_fn`. If `argc` is not zer= o, + // `argv` is non-null and valid. + let args =3D unsafe { Args::new(argc, argv) }; + + // SAFETY: the kernel should pass valid `dm_target` pointer. + let t =3D unsafe { Target::borrow_mut(ti) }; + T::message(t, args).map_or_else( + |e| e.to_errno(), + // SAFETY: the kernel should pass valid `result` pointer, and + // `count` is not greater than `maxlen`. + |ret| unsafe { + let count =3D ret.len_with_nul().min(maxlen as _); + core::ptr::copy(ret.as_char_ptr(), result, count); + 0 + }, + ) + } + unsafe extern "C" fn dm_report_zones_fn( + ti: *mut bindings::dm_target, + args: *mut bindings::dm_report_zones_args, + nr_zones: core::ffi::c_uint, + ) -> core::ffi::c_int { + // SAFETY: the kernel should pass valid `dm_target` pointer. + let t =3D unsafe { Target::borrow_mut(ti) }; + + let args =3D if nr_zones > 0 { + // SAFETY: the kernel should pass valid `args`, if `nr_zones` + // is not zero. + unsafe { + let first =3D ReportZonesArgs::borrow_mut(args); + core::slice::from_raw_parts_mut(first as _, nr_zones as _) + } + } else { + &mut [] + }; + T::report_zones(t, args).map_or_else(|e| e.to_errno(), |_| 0) + } } =20 /// Wrap the kernel struct `dm_target`. @@ -511,6 +595,69 @@ unsafe fn borrow_mut<'a>(ptr: *mut bindings::map_info)= -> &'a mut Self { } } =20 +/// Define target status types. +#[repr(u32)] +pub enum StatusType { + /// Request for some general info. + Info, + + /// Request for more detailed info. + Table, + + /// Request for some integrity info. + Ima, + + /// Undefined. + Undefined, +} + +impl From for StatusType { + fn from(value: u32) -> Self { + match value { + bindings::status_type_t_STATUSTYPE_INFO =3D> Self::Info, + bindings::status_type_t_STATUSTYPE_TABLE =3D> Self::Table, + bindings::status_type_t_STATUSTYPE_IMA =3D> Self::Ima, + _ =3D> Self::Undefined, + } + } +} + +/// Define target status flags. +#[repr(u32)] +pub enum StatusFlags { + /// See `DM_STATUS_NOFLUSH_FLAG`. + NoFlush =3D 1 << 0, + + /// Undefined. + Undefined, +} + +impl From for StatusFlags { + fn from(value: u32) -> Self { + match value { + 0 =3D> Self::NoFlush, + _ =3D> Self::Undefined, + } + } +} + +/// Wrap the kernel struct `dm_report_zones_args`. +pub struct ReportZonesArgs(Opaque); + +impl ReportZonesArgs { + unsafe fn borrow_mut<'a>(ptr: *mut bindings::dm_report_zones_args) -> = &'a mut Self { + // SAFETY: the caller should pass a valid `ptr`. + unsafe { &mut *(ptr as *mut Self) } + } + + /// Return the next sector. + pub fn next_sector(&self) -> usize { + // SAFETY: `self.0` is borrowed form foreign pointer, + // should be valid. + unsafe { (*self.0.get()).next_sector as _ } + } +} + /// Wrap the `c_char` arguments, which yields `CStr`. pub struct Args { argc: core::ffi::c_uint, --=20 2.40.1 From nobody Sun Feb 8 16:53:20 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id BE8F1C001B1 for ; Sun, 25 Jun 2023 12:18:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232084AbjFYMR7 (ORCPT ); Sun, 25 Jun 2023 08:17:59 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49420 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232133AbjFYMRr (ORCPT ); Sun, 25 Jun 2023 08:17:47 -0400 Received: from out0-219.mail.aliyun.com (out0-219.mail.aliyun.com [140.205.0.219]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 79E3210F6; Sun, 25 Jun 2023 05:17:33 -0700 (PDT) X-Alimail-AntiSpam: AC=PASS;BC=-1|-1;BR=01201311R201e4;CH=green;DM=||false|;DS=||;FP=0|-1|-1|-1|0|-1|-1|-1;HT=ay29a033018047205;MF=changxian.cqs@antgroup.com;NM=1;PH=DS;RN=11;SR=0;TI=SMTPD_---.TdJuHaw_1687695449; Received: from localhost(mailfrom:changxian.cqs@antgroup.com fp:SMTPD_---.TdJuHaw_1687695449) by smtp.aliyun-inc.com; Sun, 25 Jun 2023 20:17:29 +0800 From: "Qingsong Chen" To: linux-kernel@vger.kernel.org Cc: "=?UTF-8?B?55Sw5rSq5Lqu?=" , "Qingsong Chen" , "Miguel Ojeda" , "Alex Gaynor" , "Wedson Almeida Filho" , "Boqun Feng" , "Gary Guo" , "=?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?=" , "Benno Lossin" , Subject: [RFC PATCH 5/8] rust: kernel: add underlying device related TargetOperations Date: Sun, 25 Jun 2023 20:16:54 +0800 Message-Id: <20230625121657.3631109-6-changxian.cqs@antgroup.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230625121657.3631109-1-changxian.cqs@antgroup.com> References: <20230625121657.3631109-1-changxian.cqs@antgroup.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" - Add `prepare_ioctl` to pass ioctls to underlying device. - Add `iterate_devices` to iterate underlying devices. - Add `io_hints` to setup target request queue limits. Signed-off-by: Qingsong Chen --- rust/kernel/device_mapper.rs | 101 +++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/rust/kernel/device_mapper.rs b/rust/kernel/device_mapper.rs index 9a62208fda56..ba13294f2d0b 100644 --- a/rust/kernel/device_mapper.rs +++ b/rust/kernel/device_mapper.rs @@ -118,6 +118,24 @@ fn message(t: &mut Target, args: Args) -> Result= { fn report_zones(t: &mut Target, args: &mut [ReportZonesArgs]) ->= Result { unimplemented!() } + + /// Pass on ioctl to the underlying device. + #[allow(unused)] + fn prepare_ioctl(t: &mut Target) -> (i32, Option>>) { + unimplemented!() + } + + /// Iterate the underlying devices. + #[allow(unused)] + fn iterate_devices(t: &mut Target) -> Result>>> { + unimplemented!() + } + + /// Setup target request queue limits. + #[allow(unused)] + fn io_hints(t: &mut Target, limits: &mut QueueLimits) { + unimplemented!() + } } =20 /// Wrap the kernel struct `target_type`. @@ -193,6 +211,9 @@ pub fn register( (HAS_STATUS, status, dm_status_fn), (HAS_MESSAGE, message, dm_message_fn), (HAS_REPORT_ZONES, report_zones, dm_report_zones_fn), + (HAS_PREPARE_IOCTL, prepare_ioctl, dm_prepare_ioctl_fn= ), + (HAS_ITERATE_DEVICES, iterate_devices, dm_iterate_devi= ces_fn), + (HAS_IO_HINTS, io_hints, dm_io_hints_fn), ); =20 to_result(bindings::dm_register_target(tt)) @@ -411,6 +432,68 @@ impl TargetType { }; T::report_zones(t, args).map_or_else(|e| e.to_errno(), |_| 0) } + unsafe extern "C" fn dm_prepare_ioctl_fn( + ti: *mut bindings::dm_target, + bdev: *mut *mut bindings::block_device, + ) -> core::ffi::c_int { + // SAFETY: the kernel should pass valid `dm_target` pointer. + let t =3D unsafe { Target::borrow_mut(ti) }; + + match T::prepare_ioctl(t) { + (err, None) =3D> err, + // SAFETY: `td` and `dev` is `NonNull`, both of them are valid. + (ret, Some(td)) =3D> unsafe { + let dm_dev =3D td.as_ref().dev.as_ptr(); + let block_dev =3D (*dm_dev).bdev; + *bdev =3D block_dev; + ret + }, + } + } + unsafe extern "C" fn dm_iterate_devices_fn( + ti: *mut bindings::dm_target, + fn_: bindings::iterate_devices_callout_fn, + data: *mut core::ffi::c_void, + ) -> core::ffi::c_int { + let Some(fn_) =3D fn_ else { + pr_warn!("Invalid iterate_devices_callout_fn, skipped!"); + return 0; + }; + + // SAFETY: the kernel should pass valid `dm_target` pointer. + let t =3D unsafe { Target::borrow_mut(ti) }; + let devices =3D match T::iterate_devices(t) { + Err(e) =3D> return e.to_errno(), + Ok(devices) =3D> devices, + }; + + let mut ret =3D 0; + for (target_device, start, len) in devices { + // SAFETY: `target_device` is `NonNull`, it is also valid. + // `fn_` is checked above, it is non-null, and the kernel + // should pass valid `iterate_devices_callout_fn`. + unsafe { + let dev =3D target_device.as_ref().dev.as_ptr(); + ret =3D fn_(ti, dev, start as _, len as _, data); + if ret !=3D 0 { + break; + } + } + } + ret + } + unsafe extern "C" fn dm_io_hints_fn( + ti: *mut bindings::dm_target, + limits: *mut bindings::queue_limits, + ) { + // SAFETY: the kernel should pass valid `dm_target` and `queue_lim= its` + // pointers. + unsafe { + let t =3D Target::borrow_mut(ti); + let limits =3D QueueLimits::borrow_mut(limits); + T::io_hints(t, limits); + } + } } =20 /// Wrap the kernel struct `dm_target`. @@ -549,6 +632,12 @@ fn drop(&mut self) { } } =20 +/// Gather info about underlying device of target. +/// +/// The first is the [`TargetDevice`], the second is the start sector of +/// the device, and the third is the length (in sectors) of the device. +pub type IterDevice =3D (NonNull>, usize, usize); + /// The return values of target map function, i.e., [`TargetOperations::ma= p`]. #[repr(u32)] pub enum MapState { @@ -802,3 +891,15 @@ fn from(value: u32) -> Self { } } } + +/// Wrap the kernel struct `queue_limits`. +/// +/// Dummy. +pub struct QueueLimits(Opaque); + +impl QueueLimits { + unsafe fn borrow_mut<'a>(ptr: *mut bindings::queue_limits) -> &'a mut = Self { + // SAFETY: the caller should pass a valid `ptr`. + unsafe { &mut *(ptr as *mut Self) } + } +} --=20 2.40.1 From nobody Sun Feb 8 16:53:20 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B8086EB64DD for ; Sun, 25 Jun 2023 12:18:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232210AbjFYMSN (ORCPT ); Sun, 25 Jun 2023 08:18:13 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49644 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232140AbjFYMSA (ORCPT ); Sun, 25 Jun 2023 08:18:00 -0400 Received: from out0-216.mail.aliyun.com (out0-216.mail.aliyun.com [140.205.0.216]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8E82CE67; Sun, 25 Jun 2023 05:17:40 -0700 (PDT) X-Alimail-AntiSpam: AC=PASS;BC=-1|-1;BR=01201311R101e4;CH=green;DM=||false|;DS=||;FP=0|-1|-1|-1|0|-1|-1|-1;HT=ay29a033018047187;MF=changxian.cqs@antgroup.com;NM=1;PH=DS;RN=13;SR=0;TI=SMTPD_---.TdIoyYF_1687695455; Received: from localhost(mailfrom:changxian.cqs@antgroup.com fp:SMTPD_---.TdIoyYF_1687695455) by smtp.aliyun-inc.com; Sun, 25 Jun 2023 20:17:35 +0800 From: "Qingsong Chen" To: linux-kernel@vger.kernel.org Cc: "=?UTF-8?B?55Sw5rSq5Lqu?=" , "Qingsong Chen" , "Miguel Ojeda" , "Alex Gaynor" , "Wedson Almeida Filho" , "Boqun Feng" , "Gary Guo" , "=?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?=" , "Benno Lossin" , "Martin Rodriguez Reboredo" , "Alice Ryhl" , Subject: [RFC PATCH 6/8] rust: kernel: add DAX related TargetOperations Date: Sun, 25 Jun 2023 20:16:55 +0800 Message-Id: <20230625121657.3631109-7-changxian.cqs@antgroup.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230625121657.3631109-1-changxian.cqs@antgroup.com> References: <20230625121657.3631109-1-changxian.cqs@antgroup.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" - Add `direct_access` to translate `pgoff` to `pfn`. - Add `dax_zero_page_range` to zero page range. - Add `dax_recovery_write` to recover a poisoned range. Signed-off-by: Qingsong Chen --- rust/bindings/bindings_helper.h | 1 + rust/kernel/device_mapper.rs | 139 +++++++++++++++++++++++++++++++- 2 files changed, 139 insertions(+), 1 deletion(-) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 807fc9cf41b8..edda3a9173dd 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -12,6 +12,7 @@ #include #include #include +#include =20 /* `bindgen` gets confused at certain things. */ const gfp_t BINDINGS_GFP_KERNEL =3D GFP_KERNEL; diff --git a/rust/kernel/device_mapper.rs b/rust/kernel/device_mapper.rs index ba13294f2d0b..d2c9d0a5bcd5 100644 --- a/rust/kernel/device_mapper.rs +++ b/rust/kernel/device_mapper.rs @@ -5,7 +5,7 @@ //! C header: [`include/linux/device-mapper.h`](../../../../include/linux/= device-mapper.h) =20 use core::marker::PhantomData; -use core::ops::Index; +use core::ops::{Index, Range}; use core::ptr::{addr_of, NonNull}; =20 use crate::error::to_result; @@ -136,6 +136,39 @@ fn iterate_devices(t: &mut Target) -> Result, limits: &mut QueueLimits) { unimplemented!() } + + /// Translate a device-relative logical-page-offset into an + /// absolute physical pfn. + /// + /// Return the `addr` and the `pages` available for `DAX` at + /// that pfn, if success. + #[allow(unused)] + fn direct_access( + t: &mut Target, + pgoff: usize, + nr_pages: usize, + mode: DaxMode, + ) -> Result<(usize, Range)> { + unimplemented!() + } + + /// Zero page range. + #[allow(unused)] + fn dax_zero_page_range(t: &mut Target, pgoff: usize, nr_pages: u= size) -> Result { + unimplemented!() + } + + /// Recover a poisoned range by DAX device driver capable of + /// clearing poison. + #[allow(unused)] + fn dax_recovery_write( + t: &mut Target, + iov_iter: Pin<&mut IovIter>, + pgoff: usize, + region: Range, + ) -> usize { + unimplemented!() + } } =20 /// Wrap the kernel struct `target_type`. @@ -214,6 +247,17 @@ pub fn register( (HAS_PREPARE_IOCTL, prepare_ioctl, dm_prepare_ioctl_fn= ), (HAS_ITERATE_DEVICES, iterate_devices, dm_iterate_devi= ces_fn), (HAS_IO_HINTS, io_hints, dm_io_hints_fn), + (HAS_DIRECT_ACCESS, direct_access, dm_dax_direct_acces= s_fn), + ( + HAS_DAX_ZERO_PAGE_RANGE, + dax_zero_page_range, + dm_dax_zero_page_range_fn + ), + ( + HAS_DAX_RECOVERY_WRITE, + dax_recovery_write, + dm_dax_recovery_write_fn + ), ); =20 to_result(bindings::dm_register_target(tt)) @@ -494,6 +538,60 @@ impl TargetType { T::io_hints(t, limits); } } + unsafe extern "C" fn dm_dax_direct_access_fn( + ti: *mut bindings::dm_target, + pgoff: core::ffi::c_ulong, + nr_pages: core::ffi::c_long, + mode: bindings::dax_access_mode, + kaddr: *mut *mut core::ffi::c_void, + pfn: *mut bindings::pfn_t, + ) -> core::ffi::c_long { + // SAFETY: the kernel should pass valid `dm_target`, `kaddr` and + // `pfn` pointers. + unsafe { + let t =3D Target::borrow_mut(ti); + match T::direct_access(t, pgoff as _, nr_pages as _, mode.into= ()) { + Ok((addr, pages)) =3D> { + *kaddr =3D addr as _; + (*pfn).val =3D pages.start as _; + pages.len() as _ + } + Err(e) =3D> e.to_errno() as _, + } + } + } + unsafe extern "C" fn dm_dax_zero_page_range_fn( + ti: *mut bindings::dm_target, + pgoff: core::ffi::c_ulong, + nr_pages: usize, + ) -> core::ffi::c_int { + // SAFETY: the kernel should pass valid `dm_target` pointer. + unsafe { + let t =3D Target::borrow_mut(ti); + T::dax_zero_page_range(t, pgoff as _, nr_pages as _) + .map_or_else(|e| e.to_errno(), |_| 0) + } + } + unsafe extern "C" fn dm_dax_recovery_write_fn( + ti: *mut bindings::dm_target, + pgoff: core::ffi::c_ulong, + addr: *mut core::ffi::c_void, + bytes: usize, + i: *mut bindings::iov_iter, + ) -> usize { + let region =3D Range { + start: addr as usize, + end: (addr as usize) + bytes, + }; + + // SAFETY: the kernel should pass valid `dm_target` and `iov_iter` + // pointers. + unsafe { + let t =3D Target::borrow_mut(ti); + let iov_iter =3D IovIter::from_raw(i); + T::dax_recovery_write(t, iov_iter, pgoff as _, region) + } + } } =20 /// Wrap the kernel struct `dm_target`. @@ -903,3 +1001,42 @@ unsafe fn borrow_mut<'a>(ptr: *mut bindings::queue_li= mits) -> &'a mut Self { unsafe { &mut *(ptr as *mut Self) } } } + +/// Define dax direct_access mode. +pub enum DaxMode { + /// Normal dax access. + Access, + + /// Recovery write. + RecoveryWrite, + + /// Undefined. + Undefined, +} + +impl From for DaxMode { + fn from(value: i32) -> Self { + match value { + bindings::dax_access_mode_DAX_ACCESS =3D> Self::Access, + bindings::dax_access_mode_DAX_RECOVERY_WRITE =3D> Self::Recove= ryWrite, + _ =3D> Self::Undefined, + } + } +} + +/// Wrap the kernel struct `iov_iter`. +/// +/// Dummy. +#[allow(dead_code)] +#[pin_data] +pub struct IovIter { + #[pin] + opaque: Opaque, +} + +impl IovIter { + unsafe fn from_raw<'a>(ptr: *mut bindings::iov_iter) -> Pin<&'a mut Se= lf> { + // SAFETY: the caller should pass a valid `ptr`. + unsafe { Pin::new_unchecked(&mut *(ptr as *mut Self)) } + } +} --=20 2.40.1 From nobody Sun Feb 8 16:53:20 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E6875EB64DD for ; Sun, 25 Jun 2023 12:18:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232096AbjFYMSX (ORCPT ); Sun, 25 Jun 2023 08:18:23 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49714 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232167AbjFYMSE (ORCPT ); Sun, 25 Jun 2023 08:18:04 -0400 Received: from out0-211.mail.aliyun.com (out0-211.mail.aliyun.com [140.205.0.211]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D294FE4C; Sun, 25 Jun 2023 05:17:43 -0700 (PDT) X-Alimail-AntiSpam: AC=PASS;BC=-1|-1;BR=01201311R191e4;CH=green;DM=||false|;DS=||;FP=0|-1|-1|-1|0|-1|-1|-1;HT=ay29a033018047187;MF=changxian.cqs@antgroup.com;NM=1;PH=DS;RN=11;SR=0;TI=SMTPD_---.TdJWI.A_1687695459; Received: from localhost(mailfrom:changxian.cqs@antgroup.com fp:SMTPD_---.TdJWI.A_1687695459) by smtp.aliyun-inc.com; Sun, 25 Jun 2023 20:17:40 +0800 From: "Qingsong Chen" To: linux-kernel@vger.kernel.org Cc: "=?UTF-8?B?55Sw5rSq5Lqu?=" , "Qingsong Chen" , "Miguel Ojeda" , "Alex Gaynor" , "Wedson Almeida Filho" , "Boqun Feng" , "Gary Guo" , "=?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?=" , "Benno Lossin" , Subject: [RFC PATCH 7/8] rust: kernel: prepare to implement dm target in Rust Date: Sun, 25 Jun 2023 20:16:56 +0800 Message-Id: <20230625121657.3631109-8-changxian.cqs@antgroup.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230625121657.3631109-1-changxian.cqs@antgroup.com> References: <20230625121657.3631109-1-changxian.cqs@antgroup.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" - Add some methods to set fields in `dm_target`. - Add some methods to `TargetDevice` to operate on the underlying devices. - Add some methods to dummy `bio`, for remapping. Signed-off-by: Qingsong Chen --- rust/helpers.c | 7 ++ rust/kernel/device_mapper.rs | 237 +++++++++++++++++++++++++++++++++++ 2 files changed, 244 insertions(+) diff --git a/rust/helpers.c b/rust/helpers.c index bb594da56137..706541b58432 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -27,6 +27,7 @@ #include #include #include +#include =20 __noreturn void rust_helper_BUG(void) { @@ -135,6 +136,12 @@ void rust_helper_put_task_struct(struct task_struct *t) } EXPORT_SYMBOL_GPL(rust_helper_put_task_struct); =20 +void rust_helper_bio_set_dev(struct bio *bio, struct block_device *bdev) +{ + bio_set_dev(bio, bdev); +} +EXPORT_SYMBOL_GPL(rust_helper_bio_set_dev); + /* * We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` ty= pe * as the Rust `usize` type, so we can use it in contexts where Rust diff --git a/rust/kernel/device_mapper.rs b/rust/kernel/device_mapper.rs index d2c9d0a5bcd5..fa9b33d529b7 100644 --- a/rust/kernel/device_mapper.rs +++ b/rust/kernel/device_mapper.rs @@ -671,6 +671,124 @@ pub fn get_device(&mut self, path: &CStr) -> Result> { } } } + + /// Return maximum size (in sectors) of I/O submitted to the target. + pub fn max_io_len(&self) -> usize { + // SAFETY: `self.opaque` is borrowed from foreign pointer, should = be valid. + unsafe { (*self.opaque.get()).max_io_len as _ } + } + + /// Set maximum size (in sectors) of I/O submitted to the target. + pub fn set_max_io_len(&mut self, len: usize) -> Result { + // SAFETY: `self.opaque` is borrowed from foreign pointer, should = be valid. + unsafe { + to_result(bindings::dm_set_target_max_io_len( + self.opaque.get(), + len as _, + )) + } + } + + /// Return the number of zero-length barrier bios that will be submitt= ed + /// to the target for the purpose of flushing cache. + pub fn num_flush_bios(&self) -> usize { + // SAFETY: `self.opaque` is borrowed from foreign pointer, should = be valid. + unsafe { (*self.opaque.get()).num_flush_bios as _ } + } + + /// Set the number of zero-length barrier bios that will be submitted + /// to the target for the purpose of flushing cache. + pub fn set_num_flush_bios(&mut self, num: usize) { + // SAFETY: `self.opaque` is borrowed from foreign pointer, should = be valid. + unsafe { + (*self.opaque.get()).num_flush_bios =3D num as _; + } + } + + /// Return the number of discard bios. + pub fn num_discard_bios(&self) -> usize { + // SAFETY: `self.opaque` is borrowed from foreign pointer, should = be valid. + unsafe { (*self.opaque.get()).num_discard_bios as _ } + } + + /// Set the number of discard bios. + pub fn set_num_discard_bios(&mut self, num: usize) { + // SAFETY: `self.opaque` is borrowed from foreign pointer, should = be valid. + unsafe { + (*self.opaque.get()).num_discard_bios =3D num as _; + } + } + + /// Return the number of secure erase bios. + pub fn num_secure_erase_bios(&self) -> usize { + // SAFETY: `self.opaque` is borrowed from foreign pointer, should = be valid. + unsafe { (*self.opaque.get()).num_secure_erase_bios as _ } + } + + /// Set the number of secure erase bios. + pub fn set_num_secure_erase_bios(&mut self, num: usize) { + // SAFETY: `self.opaque` is borrowed from foreign pointer, should = be valid. + unsafe { + (*self.opaque.get()).num_secure_erase_bios =3D num as _; + } + } + + /// Return the number of WRITE ZEROES bios. + pub fn num_write_zeroes_bios(&self) -> usize { + // SAFETY: `self.opaque` is borrowed from foreign pointer, should = be valid. + unsafe { (*self.opaque.get()).num_write_zeroes_bios as _ } + } + + /// Set the number of WRITE ZEROES bios. + pub fn set_num_write_zeroes_bios(&mut self, num: usize) { + // SAFETY: `self.opaque` is borrowed from foreign pointer, should = be valid. + unsafe { + (*self.opaque.get()).num_write_zeroes_bios =3D num as _; + } + } + + /// Return the minimum number of extra bytes allocated in each io + /// for the target to use. + pub fn per_io_data_size(&self) -> usize { + // SAFETY: `self.opaque` is borrowed from foreign pointer, should = be valid. + unsafe { (*self.opaque.get()).per_io_data_size as _ } + } + + /// Set the minimum number of extra bytes allocated in each io + /// for the target to use. + pub fn set_per_io_data_size(&mut self, size: usize) { + // SAFETY: `self.opaque` is borrowed from foreign pointer, should = be valid. + unsafe { + (*self.opaque.get()).per_io_data_size =3D size as _; + } + } + + /// Set an error string for the target, could be used + /// by [`TargetOperations::ctr`]. + pub fn set_error(&mut self, err: &CStr) { + // SAFETY: `self.opaque` is borrowed from foreign pointer, should = be valid. + unsafe { + (*self.opaque.get()).error =3D err.as_char_ptr() as _; + } + } + + /// Check if the target is suspended. + pub fn suspended(&self) -> bool { + // SAFETY: `self.opaque` is borrowed from foreign pointer, should = be valid. + unsafe { bindings::dm_suspended(self.opaque.get()) !=3D 0 } + } + + /// Check if the target is post_suspending. + pub fn post_suspending(&self) -> bool { + // SAFETY: `self.opaque` is borrowed from foreign pointer, should = be valid. + unsafe { bindings::dm_post_suspending(self.opaque.get()) !=3D 0 } + } + + /// Check if the target is noflush_suspending. + pub fn noflush_suspending(&self) -> bool { + // SAFETY: `self.opaque` is borrowed from foreign pointer, should = be valid. + unsafe { bindings::dm_noflush_suspending(self.opaque.get()) !=3D 0= } + } } =20 /// Represent an underlying device of a device mapper target. @@ -721,6 +839,100 @@ pub fn device_sectors(&self) -> usize { // SAFETY: the `from_raw` caller should pass valid `dev` pointer. unsafe { (*(*self.dev.as_ptr()).bdev).bd_nr_sectors as _ } } + + /// Return [NonNull]. + pub fn as_ptr(&mut self) -> NonNull { + // SAFETY: `self` is non-null and valid. + unsafe { NonNull::new_unchecked(self as *mut Self) } + } + + /// Propagate report zones command to underlying devices. + #[cfg(CONFIG_BLK_DEV_ZONED)] + pub fn report_zones( + &self, + start: usize, + sector: usize, + args: &mut [ReportZonesArgs], + ) -> Result { + // SAFETY: the `from_raw` caller should pass valid `dev` pointer. + unsafe { + let bdev =3D (*self.dev.as_ptr()).bdev; + let ptr =3D args.as_mut_ptr() as *mut bindings::dm_report_zone= s_args; + to_result(bindings::dm_report_zones( + bdev, + start as _, + sector as _, + ptr, + args.len() as _, + )) + } + } + + /// Translate a device-relative logical-page-offset into an + /// absolute physical pfn. + /// + /// Return the `addr` and the `pages` available for `DAX` at + /// that pfn, if success. + #[cfg(CONFIG_FS_DAX)] + pub fn dax_direct_access( + &mut self, + pgoff: usize, + nr_pages: usize, + mode: DaxMode, + ) -> Result<(usize, Range)> { + let mut kaddr =3D core::ptr::null_mut(); + let mut pfn =3D bindings::pfn_t::default(); + + // SAFETY: the `from_raw` caller should pass valid `dev` pointer. + let count =3D unsafe { + let dax_dev =3D (*self.dev.as_ptr()).dax_dev; + bindings::dax_direct_access( + dax_dev, + pgoff as _, + nr_pages as _, + mode as _, + &mut kaddr, + &mut pfn, + ) + }; + + if count < 0 { + Err(Error::from_errno(count as _)) + } else { + let pages =3D Range { + start: pfn.val as usize, + end: (pfn.val as usize) + (count as usize), + }; + Ok((kaddr as _, pages)) + } + } + + /// Zero page range. + #[cfg(CONFIG_FS_DAX)] + pub fn dax_zero_page_range(&mut self, pgoff: usize, nr_pages: usize) -= > Result { + // SAFETY: the `from_raw` caller should pass valid `dev` pointer. + unsafe { + let dax_dev =3D (*self.dev.as_ptr()).dax_dev; + to_result(bindings::dax_zero_page_range(dax_dev, pgoff as _, n= r_pages)) + } + } + + /// Recover a poisoned range by DAX device driver capable of + /// clearing poison. + #[cfg(CONFIG_FS_DAX)] + pub fn dax_recovery_write( + &mut self, + iov_iter: Pin<&mut IovIter>, + pgoff: usize, + region: Range, + ) -> usize { + // SAFETY: the `from_raw` caller should pass `dev` pointer. + unsafe { + let dax_dev =3D (*self.dev.as_ptr()).dax_dev; + let i =3D iov_iter.opaque.get(); + bindings::dax_recovery_write(dax_dev, pgoff as _, region.start= as _, region.len(), i) + } + } } =20 impl Drop for TargetDevice { @@ -919,6 +1131,31 @@ fn op_and_flags(&self) -> u32 { pub fn is_write(&self) -> bool { self.op_and_flags() & bindings::req_op_REQ_OP_WRITE !=3D 0 } + + /// Return the start sector of bio. + pub fn sector(&self) -> usize { + // SAFETY: the `from_raw` caller should pass valid `bio`, so + // `self.opaque` is valid too. + unsafe { (*self.opaque.get()).bi_iter.bi_sector as _ } + } + + /// Set the start sector of bio. + pub fn set_sector(&mut self, sector: usize) { + // SAFETY: the `from_raw` caller should pass valid `bio`, so + // `self.opaque` is valid too. + unsafe { + (*self.opaque.get()).bi_iter.bi_sector =3D sector as _; + } + } + + /// Set the block device of bio. + pub fn set_dev(&mut self, td: &TargetDevice) { + // SAFETY: the `from_raw` caller should pass valid `bio`, so + // `self.opaque` is valid too. + unsafe { + bindings::bio_set_dev(self.opaque.get(), (*td.dev.as_ptr()).bd= ev); + } + } } =20 /// Wrap the kernel struct `request`. --=20 2.40.1 From nobody Sun Feb 8 16:53:20 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E8B98EB64DC for ; Sun, 25 Jun 2023 12:18:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232133AbjFYMS0 (ORCPT ); Sun, 25 Jun 2023 08:18:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49734 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232171AbjFYMSF (ORCPT ); Sun, 25 Jun 2023 08:18:05 -0400 Received: from out0-220.mail.aliyun.com (out0-220.mail.aliyun.com [140.205.0.220]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 83493E47; Sun, 25 Jun 2023 05:17:45 -0700 (PDT) X-Alimail-AntiSpam: AC=PASS;BC=-1|-1;BR=01201311R231e4;CH=green;DM=||false|;DS=||;FP=0|-1|-1|-1|0|-1|-1|-1;HT=ay29a033018047190;MF=changxian.cqs@antgroup.com;NM=1;PH=DS;RN=11;SR=0;TI=SMTPD_---.TdIoyYs_1687695461; Received: from localhost(mailfrom:changxian.cqs@antgroup.com fp:SMTPD_---.TdIoyYs_1687695461) by smtp.aliyun-inc.com; Sun, 25 Jun 2023 20:17:42 +0800 From: "Qingsong Chen" To: linux-kernel@vger.kernel.org Cc: "=?UTF-8?B?55Sw5rSq5Lqu?=" , "Qingsong Chen" , "Miguel Ojeda" , "Alex Gaynor" , "Wedson Almeida Filho" , "Boqun Feng" , "Gary Guo" , "=?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?=" , "Benno Lossin" , Subject: [RFC PATCH 8/8] samples: rust: add a device mapper linear target Date: Sun, 25 Jun 2023 20:16:57 +0800 Message-Id: <20230625121657.3631109-9-changxian.cqs@antgroup.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230625121657.3631109-1-changxian.cqs@antgroup.com> References: <20230625121657.3631109-1-changxian.cqs@antgroup.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Add a device mapper linear target sample, based on the rust device_mapper abstractions. Signed-off-by: Qingsong Chen --- samples/rust/Kconfig | 10 ++ samples/rust/Makefile | 1 + samples/rust/rust_dm_linear.rs | 257 +++++++++++++++++++++++++++++++++ 3 files changed, 268 insertions(+) create mode 100644 samples/rust/rust_dm_linear.rs diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index b0f74a81c8f9..0d532c995f9a 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -30,6 +30,16 @@ config SAMPLE_RUST_PRINT =20 If unsure, say N. =20 +config SAMPLE_RUST_DM_LINEAR + tristate "Device mapper linear target" + help + This option builds the Rust device mapper linear sample. + + To compile this as a module, choose M here: + the module will be called rust_dm_linear. + + If unsure, say N. + config SAMPLE_RUST_HOSTPROGS bool "Host programs" help diff --git a/samples/rust/Makefile b/samples/rust/Makefile index 03086dabbea4..cf4bf82312d8 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -2,5 +2,6 @@ =20 obj-$(CONFIG_SAMPLE_RUST_MINIMAL) +=3D rust_minimal.o obj-$(CONFIG_SAMPLE_RUST_PRINT) +=3D rust_print.o +obj-$(CONFIG_SAMPLE_RUST_DM_LINEAR) +=3D rust_dm_linear.o =20 subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) +=3D hostprogs diff --git a/samples/rust/rust_dm_linear.rs b/samples/rust/rust_dm_linear.rs new file mode 100644 index 000000000000..1f338e6b227f --- /dev/null +++ b/samples/rust/rust_dm_linear.rs @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust device mapper linear target sample. +//! +//! C version: drivers/md/dm-linear.c + +use core::ops::Range; +use core::ptr::NonNull; +use kernel::bindings::PAGE_SECTORS_SHIFT; +use kernel::device_mapper::*; +use kernel::prelude::*; +use kernel::{c_str, fmt, str::CString}; + +module! { + type: RustDmLinear, + name: "rust_dm_linear", + author: "Rust for Linux Contributors", + description: "Rust device mapper linear target sample", + license: "GPL", +} + +struct Linear { + dev: TargetDevice, + start: usize, +} + +impl Linear { + fn new(dev: TargetDevice, start: usize) -> impl Init { + init!(Self { dev, start }) + } + + fn map_sector(&self, sector: usize, base: usize) -> usize { + sector - base + self.start + } +} + +// SAFETY: `Linear` could be used from all threads. +unsafe impl Sync for Linear {} + +#[vtable] +impl TargetOperations for Linear { + type Private =3D Linear; + + fn ctr(t: &mut Target, args: Args) -> Result> { + if args.len() !=3D 2 { + t.set_error(c_str!("Invalid argument count")); + return Err(EINVAL); + } + + let dev =3D match t.get_device(&args[0]) { + Ok(dev) =3D> dev, + Err(e) =3D> { + t.set_error(c_str!("Device lookup failed")); + return Err(e); + } + }; + + let start =3D &args[1].to_str().map_err(|_| EINVAL)?; + let start =3D usize::from_str_radix(start, 10).map_err(|_| { + t.set_error(c_str!("Invalid device sector")); + EINVAL + })?; + + t.set_num_flush_bios(1); + t.set_num_discard_bios(1); + t.set_num_secure_erase_bios(1); + t.set_num_write_zeroes_bios(1); + + Box::init(Linear::new(dev, start)) + } + + fn dtr(_: &mut Target) {} + + fn map(t: &mut Target, mut bio: Pin<&mut Bio>) -> MapState { + let Some(linear) =3D t.private() else { + pr_warn!("Error, found no rust_linear\n"); + return MapState::Kill; + }; + + let offset =3D bio.sector() - linear.dev.target().begin_sector(); + bio.set_dev(&linear.dev); + bio.set_sector(linear.start + offset); + MapState::Remapped + } + + fn status(t: &mut Target, type_: StatusType, _: StatusFlags) -> = Option { + let Some(linear) =3D t.private() else { + pr_warn!("Error, found no rust_linear\n"); + return None; + }; + + match type_ { + StatusType::Info =3D> None, + StatusType::Table =3D> { + CString::try_from_fmt(fmt!("{} {}", linear.dev.device_name= (), linear.start)).ok() + } + StatusType::Ima =3D> { + let version =3D linear.dev.target().version(); + CString::try_from_fmt(fmt!( + "target_name=3D{},target_version=3D{}.{}.{},device_nam= e=3D{},start=3D{};", + linear.dev.target().name(), + version[0], + version[1], + version[2], + linear.dev.device_name(), + linear.start + )) + .ok() + } + _ =3D> { + pr_warn!("Unsupported dm_status_type\n"); + None + } + } + } + + fn prepare_ioctl(t: &mut Target) -> (i32, Option>>) { + let Some(mut linear) =3D t.private() else { + pr_warn!("Error, found no rust_linear\n"); + return (EINVAL.to_errno(), None); + }; + + let mut ret =3D 0; + if linear.start > 0 || linear.dev.target().total_sectors() !=3D li= near.dev.device_sectors() { + ret =3D 1; + } + (ret, Some(linear.dev.as_ptr())) + } + + fn iterate_devices(t: &mut Target) -> Result>>> { + let Some(mut linear) =3D t.private() else { + pr_warn!("Error, found no rust_linear\n"); + return Err(EINVAL); + }; + + Ok(Box::init(IterLinear::new( + linear.dev.as_ptr(), + linear.start, + t.total_sectors(), + ))?) + } + + #[cfg(CONFIG_BLK_DEV_ZONED)] + fn report_zones(t: &mut Target, args: &mut [ReportZonesArgs]) ->= Result { + if args.len() =3D=3D 0 { + pr_warn!("Invalid report_zones_args\n"); + return Err(EINVAL); + } + + let Some(linear) =3D t.private() else { + pr_warn!("Error, found no rust_linear\n"); + return Err(EINVAL); + }; + + let sector =3D linear.map_sector(args[0].next_sector(), linear.dev= .target().begin_sector()); + linear.dev.report_zones(linear.start, sector, args) + } + + #[cfg(CONFIG_FS_DAX)] + fn direct_access( + t: &mut Target, + pgoff: usize, + nr_pages: usize, + mode: DaxMode, + ) -> Result<(usize, Range)> { + let base =3D t.begin_sector(); + let Some(mut linear) =3D t.private() else { + pr_warn!("Error, found no rust_linear\n"); + return Err(EINVAL); + }; + + let sector =3D linear.map_sector(pgoff << PAGE_SECTORS_SHIFT, base= ); + let offset =3D (linear.dev.device_sectors() + sector) >> PAGE_SECT= ORS_SHIFT; + linear.dev.dax_direct_access(offset, nr_pages, mode) + } + + #[cfg(CONFIF_FS_DAX)] + fn dax_zero_page_range(t: &mut Target, pgoff: usize, nr_pages: u= size) -> Result { + let base =3D t.begin_sector(); + let Some(mut linear) =3D t.private() else { + pr_warn!("Error, found no rust_linear\n"); + return Err(EINVAL); + }; + + let sector =3D linear.map_sector(pgoff << PAGE_SECTORS_SHIFT, base= ); + let offset =3D (linear.dev.device_sectors() + sector) >> PAGE_SECT= ORS_SHIFT; + linear.dev.dax_zero_page_range(offset, nr_pages) + } + + #[cfg(CONFIF_FS_DAX)] + fn dax_recovery_write( + t: &mut Target, + iov_iter: Pin<&mut IovIter>, + pgoff: usize, + region: Range, + ) -> usize { + let base =3D t.begin_sector(); + let Some(mut linear) =3D t.private() else { + pr_warn!("Error, found no rust_linear\n"); + return 0; + }; + + let sector =3D linear.map_sector(pgoff << PAGE_SECTORS_SHIFT, base= ); + let offset =3D (linear.dev.device_sectors() + sector) >> PAGE_SECT= ORS_SHIFT; + linear.dev.dax_recovery_write(iov_iter, offset, region) + } +} + +struct IterLinear { + item: Option>, +} + +impl IterLinear { + fn new(dev: NonNull>, start: usize, len: usize) -= > impl Init { + init!(Self { + item: Some((dev, start, len)) + }) + } +} + +impl Iterator for IterLinear { + type Item =3D IterDevice; + + fn next(&mut self) -> Option { + self.item.take() + } +} + +struct RustDmLinear(Pin>); + +impl kernel::Module for RustDmLinear { + fn init(_module: &'static ThisModule) -> Result { + pr_info!("Rust device mapper linear target sample (init)\n"); + + let target =3D Box::pin_init(TargetType::register::( + _module, + c_str!("rust_linear"), + [0, 0, 1], + 0, + )); + + let target =3D match target { + Ok(target) =3D> target, + Err(e) =3D> { + pr_warn!("Target register failed, errno: {}\n", e.to_errno= ()); + return Err(e); + } + }; + Ok(RustDmLinear(target)) + } +} + +impl Drop for RustDmLinear { + fn drop(&mut self) { + pr_info!("Rust device mapper linear target sample (exit)\n"); + } +} --=20 2.40.1