From nobody Fri Oct 3 14:29:02 2025 Received: from mailrelay-egress16.pub.mailoutpod3-cph3.one.com (mailrelay-egress16.pub.mailoutpod3-cph3.one.com [46.30.212.3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C26D654279 for ; Fri, 29 Aug 2025 07:18:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=46.30.212.3 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756451903; cv=none; b=sI52h46QrfkIZLcEfZjtVF3DxiISuxc0wuDf7fxhnmmyBdhY/F3+LzegF/qc1JA/e+3bUxeTANvbe8RXQY5mIEXZC8nAThAQoSUN0dZ2U/YxB2V6xADd+gQkQSF2F1cvFjMQnL1y3PJ5vZqn0kB/RS62g33wuyEP77Siw/4XXbw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756451903; c=relaxed/simple; bh=qzl/6sy8OVffzxtl8FMD/NNTrKt229GDFr99TgraCLc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ewq9gJBOQ/aFThBgnzkr08jqg75/5zjO0fDrAapIGMYODB3lCGz2orI/iWJnTSVXldzRjYhlr4p5r9ThWqpNCXOVcw2oEFGa1GF7WB+S8zxJVNebNHHl4sMka0m2GtrMpd8IxUof/7ds9MBILmjOKDzSb+kMCp+xobwHwyp1rXA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=konsulko.se; spf=none smtp.mailfrom=konsulko.se; dkim=pass (2048-bit key) header.d=konsulko.se header.i=@konsulko.se header.b=apDgEv3l; dkim=permerror (0-bit key) header.d=konsulko.se header.i=@konsulko.se header.b=cXOKDVYl; arc=none smtp.client-ip=46.30.212.3 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=konsulko.se Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=konsulko.se Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=konsulko.se header.i=@konsulko.se header.b="apDgEv3l"; dkim=permerror (0-bit key) header.d=konsulko.se header.i=@konsulko.se header.b="cXOKDVYl" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; t=1756451900; x=1757056700; d=konsulko.se; s=rsa2; h=content-transfer-encoding:mime-version:references:in-reply-to:message-id:date: subject:cc:to:from:from; bh=JIj8SIbtAgtpy0p9VklqThtQxV6/s8k0YSWKmDTTMMA=; b=apDgEv3lhMuPlcRQ1NhVoYp8wX175iTZBNHvVtxmNQOLwePO8EppFq1p+eu/Ig3v8bKtBWAaI8djc v4zB2A6Uzfq2mzyqhosRL+SR/NSqRqiT8CNVD0xnucex8oILQNpB25mI3tuxpf2DDESNCZ5xGdYQ6i kgqzV9pObhLI759vb1SDzLUBGA9VIwjrVR74o4dDg4AFXeZW43UO53KKb2DBuYnn0lX/fia2vXGV/I d42L3mfagyX4OHHpdqdq6dP+OaAQ/gpYZWjBQP/3BcGY2A1EYwydiWTzqHcZKNf8DuMb9+5AJ62Ntu EdZM7OS3FAlxM52jnzMR6pwZ7+JGMJw== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; t=1756451900; x=1757056700; d=konsulko.se; s=ed2; h=content-transfer-encoding:mime-version:references:in-reply-to:message-id:date: subject:cc:to:from:from; bh=JIj8SIbtAgtpy0p9VklqThtQxV6/s8k0YSWKmDTTMMA=; b=cXOKDVYlm/PRDmxYuD+0ykRgCvcBbCb/JB7u4uqebxbesDEr/Cuid7qmtMDVZ5CS5yIZagW19rypJ EoDdWbCCg== X-HalOne-ID: 5560e6fc-84a8-11f0-8d86-fb5fec76084d Received: from localhost.localdomain (host-95-203-16-218.mobileonline.telia.com [95.203.16.218]) by mailrelay3.pub.mailoutpod3-cph3.one.com (Halon) with ESMTPSA id 5560e6fc-84a8-11f0-8d86-fb5fec76084d; Fri, 29 Aug 2025 07:18:19 +0000 (UTC) From: Vitaly Wool To: rust-for-linux@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Uladzislau Rezki , Danilo Krummrich , Alice Ryhl , Vlastimil Babka , Lorenzo Stoakes , "Liam R . Howlett" , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , Bjorn Roy Baron , Benno Lossin , Andreas Hindborg , Trevor Gross , Johannes Weiner , Yosry Ahmed , Nhat Pham , linux-mm@kvack.org, Vitaly Wool Subject: [PATCH v5 1/2] rust: alloc: add from_raw method to Flags Date: Fri, 29 Aug 2025 09:18:14 +0200 Message-Id: <20250829071814.1973188-1-vitaly.wool@konsulko.se> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20250829071709.1973086-1-vitaly.wool@konsulko.se> References: <20250829071709.1973086-1-vitaly.wool@konsulko.se> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" We need to be able to create Flags from its raw representation as u32 to properly map zpool C API into Rust. This patch adds from_raw method to Flags and makes it crate private. Signed-off-by: Vitaly Wool --- rust/kernel/alloc.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs index b39c279236f5..0c839813a15d 100644 --- a/rust/kernel/alloc.rs +++ b/rust/kernel/alloc.rs @@ -41,6 +41,13 @@ pub struct Flags(u32); =20 impl Flags { + /// Create `Flags` from the raw representation. + /// + /// `f` must be a valid combination of GFP flags. + pub(crate) fn from_raw(f: u32) -> Self { + Self(f) + } + /// Get the raw representation of this flag. pub(crate) fn as_raw(self) -> u32 { self.0 --=20 2.39.2 From nobody Fri Oct 3 14:29:02 2025 Received: from mailrelay-egress16.pub.mailoutpod3-cph3.one.com (mailrelay-egress16.pub.mailoutpod3-cph3.one.com [46.30.212.3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 34C593FC7 for ; Fri, 29 Aug 2025 07:18:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=46.30.212.3 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756451914; cv=none; b=Y44YEU2qzGsZCFDvKMtbUR0YZiu+n8GMN+W5r6BjHt7dhP7BrcGiXmA8bq3QaAyadhfN8caLwGKv/ls9pc2x9bA9zb4HOw+plU9zh65gSYE6TsFsAfzAIKIvhgSOh8ZSq1AZzzWpxpqb9ntBxjmhW2rNXMZVwyowMlfPYPtNU9E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756451914; c=relaxed/simple; bh=lYqKeWj8pxpi7Q+bLJjXC6H+KWNvHcw7mX2gMVghkMo=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=oQPMMRlUXZezHSOi9BE/QNsB4sC9Jx+JQepR5u27yT4Lpqg/eMzu6iL6jigPS8tSyTELrR5k6jsgbeTdWMnx83qgpQG5PNNc3ahMq2Ff1Hy4hLQCV7H+5sWb/L75x81bpE8FO1x1WZcWkeveIE7zFypvJbBKRWNu4yMC26o2hg8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=konsulko.se; spf=none smtp.mailfrom=konsulko.se; dkim=pass (2048-bit key) header.d=konsulko.se header.i=@konsulko.se header.b=ScJyEpEX; dkim=permerror (0-bit key) header.d=konsulko.se header.i=@konsulko.se header.b=odPTH+GP; arc=none smtp.client-ip=46.30.212.3 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=konsulko.se Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=konsulko.se Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=konsulko.se header.i=@konsulko.se header.b="ScJyEpEX"; dkim=permerror (0-bit key) header.d=konsulko.se header.i=@konsulko.se header.b="odPTH+GP" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; t=1756451910; x=1757056710; d=konsulko.se; s=rsa2; h=content-transfer-encoding:mime-version:references:in-reply-to:message-id:date: subject:cc:to:from:from; bh=T9Y+Fj2hUAr+t/jILO9pLud/GiihvaaYAyjQAhHGMt8=; b=ScJyEpEXWezoa4612nRrHoaPm29GLVRGKU1pF8W0eBAPnUfqZgDUwsUnAr+5CLjfiHX7hzOuCTiTc 9rFpOMCvKkSETzXMq2v54YS6q1d6xZOsOCBsXamAqp9T3kr9HtpENWsJ8BDoi3cw3sJvPzZUYvyMvc TI/l53PYxsEY34GU6E/GW+7QhsYp8IavPTd5kCG1+ucMV81jzbZHEQXkzYdt9xC60VZO16b9jK9weW 7aKDwGm8SvWtZBhTNc1IMUmSG1YW2Ff+u9LPiHFbFLzVJ/OOmcikoEcU/G+Ni5JjEIP7Nzfu45wpwu gCwub8XzzNFJ6bxvTs7dKV56W9zDKvA== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; t=1756451910; x=1757056710; d=konsulko.se; s=ed2; h=content-transfer-encoding:mime-version:references:in-reply-to:message-id:date: subject:cc:to:from:from; bh=T9Y+Fj2hUAr+t/jILO9pLud/GiihvaaYAyjQAhHGMt8=; b=odPTH+GPiXF8CDbBXZtXOShz9t2kAkrLWa9JC2VAVzCEPFD0zKPNhEiACcLwbf+T9Dqq23kHQA9Y2 iDrs8AZAw== X-HalOne-ID: 5b54343f-84a8-11f0-8d94-fb5fec76084d Received: from localhost.localdomain (host-95-203-16-218.mobileonline.telia.com [95.203.16.218]) by mailrelay3.pub.mailoutpod3-cph3.one.com (Halon) with ESMTPSA id 5b54343f-84a8-11f0-8d94-fb5fec76084d; Fri, 29 Aug 2025 07:18:29 +0000 (UTC) From: Vitaly Wool To: rust-for-linux@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Uladzislau Rezki , Danilo Krummrich , Alice Ryhl , Vlastimil Babka , Lorenzo Stoakes , "Liam R . Howlett" , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , Bjorn Roy Baron , Benno Lossin , Andreas Hindborg , Trevor Gross , Johannes Weiner , Yosry Ahmed , Nhat Pham , linux-mm@kvack.org, Vitaly Wool Subject: [PATCH v5 2/2] rust: zpool: add abstraction for zpool drivers Date: Fri, 29 Aug 2025 09:18:27 +0200 Message-Id: <20250829071827.1973212-1-vitaly.wool@konsulko.se> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20250829071709.1973086-1-vitaly.wool@konsulko.se> References: <20250829071709.1973086-1-vitaly.wool@konsulko.se> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Zpool is a common frontend for memory storage pool implementations. These pools are typically used to store compressed memory objects, e. g. for Zswap, the lightweight compressed cache for swap pages. This patch provides the interface to use Zpool in Rust kernel code, thus enabling Rust implementations of Zpool allocators for Zswap. Co-developed-by: Alice Ryhl Signed-off-by: Alice Ryhl Signed-off-by: Vitaly Wool --- MAINTAINERS | 2 + rust/bindings/bindings_helper.h | 1 + rust/helpers/helpers.c | 1 + rust/helpers/zpool.c | 8 + rust/kernel/lib.rs | 2 + rust/kernel/zpool.rs | 377 ++++++++++++++++++++++++++++++++ 6 files changed, 391 insertions(+) create mode 100644 rust/helpers/zpool.c create mode 100644 rust/kernel/zpool.rs diff --git a/MAINTAINERS b/MAINTAINERS index b6f7c6939ff8..42b68dc800fb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16267,9 +16267,11 @@ W: http://www.linux-mm.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm F: rust/helpers/mm.c F: rust/helpers/page.c +F: rust/helpers/zpool.c F: rust/kernel/mm.rs F: rust/kernel/mm/ F: rust/kernel/page.rs +F: rust/kernel/zpool.rs =20 MEMORY MAPPING M: Andrew Morton diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 84d60635e8a9..f0c4c454882b 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -75,6 +75,7 @@ #include #include #include +#include #include =20 #if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE) diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 7cf7fe95e41d..e1a7556cc700 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -51,3 +51,4 @@ #include "wait.c" #include "workqueue.c" #include "xarray.c" +#include "zpool.c" diff --git a/rust/helpers/zpool.c b/rust/helpers/zpool.c new file mode 100644 index 000000000000..6dd3edfc7fc8 --- /dev/null +++ b/rust/helpers/zpool.c @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +void rust_helper_zpool_register_driver(struct zpool_driver *driver) +{ + zpool_register_driver(driver); +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index ed53169e795c..165d52feeea4 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -129,6 +129,8 @@ pub mod uaccess; pub mod workqueue; pub mod xarray; +#[cfg(CONFIG_ZPOOL)] +pub mod zpool; =20 #[doc(hidden)] pub use bindings; diff --git a/rust/kernel/zpool.rs b/rust/kernel/zpool.rs new file mode 100644 index 000000000000..ae564ea22b77 --- /dev/null +++ b/rust/kernel/zpool.rs @@ -0,0 +1,377 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Implementation of Rust interface towards zpool API. + +use crate::{ + bindings, + error::{from_result, Result}, + kernel::alloc::Flags, + str::{CString, CStr}, + types::{ForeignOwnable, Opaque}, +}; +use core::ffi::{c_int, c_uchar, c_void}; +use core::ptr::{null_mut, NonNull}; +use kernel::alloc::NumaNode; +use kernel::driver; +use kernel::ThisModule; + +/// The Rust representation of zpool handle. +pub struct ZpoolHandle(usize); + +impl ZpoolHandle { + /// Create `ZpoolHandle` from the raw representation. + pub fn from_raw(h: usize) -> Self { + Self(h) + } + + /// Get the raw representation of the handle. + pub fn as_raw(self) -> usize { + self.0 + } +} + +/// Zpool API. +/// +/// The [`ZpoolDriver`] trait serves as an interface for Zpool drivers imp= lemented in Rust. +/// Such drivers implement memory storage pools in accordance with the zpo= ol API. +/// +/// # Example +/// +/// A zpool driver implementation which uses KVec of 2**n sizes, n =3D 6, = 7, ..., PAGE_SHIFT. +/// Every zpool object is packed into a KVec that is sufficiently large, a= nd n (the +/// denominator) is saved in the least significant bits of the handle, whi= ch is guaranteed +/// to be at least 2**6 aligned by kmalloc. +/// +/// ``` +/// use core::ptr::{NonNull, copy_nonoverlapping}; +/// use core::sync::atomic::{AtomicU64, Ordering}; +/// use kernel::alloc::{Flags, KBox, KVec, NumaNode}; +/// use kernel::page::PAGE_SHIFT; +/// use kernel::prelude::EINVAL; +/// use kernel::str::CString; +/// use kernel::zpool::*; +/// +/// struct MyZpool { +/// name: CString, +/// bytes_used: AtomicU64, +/// } +/// +/// struct MyZpoolDriver; +/// +/// impl ZpoolDriver for MyZpoolDriver { +/// type Pool =3D KBox; +/// +/// fn create(name: CString, gfp: Flags) -> Result> { +/// let my_pool =3D MyZpool { name, bytes_used: AtomicU64::new(0) = }; +/// let pool =3D KBox::new(my_pool, gfp)?; +/// +/// pr_debug!("Pool {:?} created\n", pool.name); +/// Ok(pool) +/// } +/// +/// fn malloc(pool: &MyZpool, size: usize, _gfp: Flags, _nid: NumaNode= ) -> Result { +/// let pow =3D size.next_power_of_two().trailing_zeros().max(6); +/// match pow { +/// 0 =3D> Err(EINVAL), +/// m if m > PAGE_SHIFT as u32 =3D> Err(ENOSPC), +/// _ =3D> { +/// let vec =3D KVec::::with_capacity(1 << pow, GFP_KE= RNEL)?; +/// let (ptr, _len, _cap) =3D vec.into_raw_parts(); +/// +/// // We assume that kmalloc-64, kmalloc-128 etc. kmem ca= ches will be used for +/// // our allocations, so it's actually `1 << pow` bytes = that we have consumed. +/// pool.bytes_used.fetch_add(1 << pow, Ordering::Relaxed); +/// +/// // `kmalloc` guarantees that an allocation of size x*2= ^n is 2^n aligned. +/// // Therefore the 6 lower bits are zeros and we can use= these to store `pow`. +/// Ok(ZpoolHandle::from_raw(ptr as usize | (pow as usize = - 6))) +/// } +/// } +/// } +/// +/// unsafe fn free(pool: &MyZpool, handle: ZpoolHandle) { +/// let h =3D handle.as_raw(); +/// let n =3D (h & 0x3F) + 6; +/// let uptr =3D h & !0x3F; +/// +/// // SAFETY: +/// // - we derive `uptr` from handle by zeroing 6 lower bits wher= e we store the power +/// // denominator for the vector capacity. As noted above, the = result will be exactly the +/// // pointer to the area allocated by `KVec`. Thus, uptr is a = valid pointer pointing to +/// // the vector allocated by `alloc` function above. +/// // - 1 << n =3D=3D capacity and is coming from the first 6 bit= s of handle. +/// let vec =3D unsafe { KVec::::from_raw_parts(uptr as *mut u= 8, 0, 1 << n) }; +/// drop(vec); +/// pool.bytes_used.fetch_sub(1 << n, Ordering::Relaxed); +/// } +/// +/// unsafe fn read_begin(_pool: &MyZpool, handle: ZpoolHandle) -> NonN= ull { +/// let uptr =3D handle.as_raw() & !0x3F; +/// // SAFETY: +/// // - we derive `uptr` from handle by zeroing 6 lower bits wher= e we store the power +/// // denominator for the vector capacity. As noted above, the = result will be exactly the +/// // pointer to the area allocated by `KVec`. Thus, uptr is a = valid pointer pointing to +/// // the vector allocated by `alloc` function above. +/// unsafe { NonNull::new_unchecked(uptr as *mut u8) } +/// } +/// +/// unsafe fn read_end(_pool: &MyZpool, _handle: ZpoolHandle, _handle_= mem: NonNull) {} +/// +/// unsafe fn write(_p: &MyZpool, handle: ZpoolHandle, h_mem: NonNull<= u8>, mem_len: usize) { +/// let uptr =3D handle.as_raw() & !0x3F; +/// // SAFETY: +/// // - `h_mem` is a valid non-null pointer provided by zpool. +/// // - `uptr` is derived from handle by zeroing 6 lower bits whe= re we store the power +/// // denominator for the vector capacity. As noted above, the = result will be exactly the +/// // pointer to the area allocated by `KVec`. Thus, uptr is a = valid pointer pointing to +/// // the vector allocated by `alloc` function above. +/// unsafe { +/// copy_nonoverlapping(h_mem.as_ptr().cast(), uptr as *mut c_= void, mem_len) +/// }; +/// } +/// +/// fn total_pages(pool: &MyZpool) -> u64 { +/// pool.bytes_used.load(Ordering::Relaxed) >> PAGE_SHIFT +/// } +/// } +/// ``` +/// +pub trait ZpoolDriver { + /// Opaque Rust representation of `struct zpool`. + type Pool: ForeignOwnable; + + /// Create a pool. + fn create(name: CString, gfp: Flags) -> Result; + + /// Allocate an object of `size` bytes from `pool`, with the allocatio= n flags `gfp` and + /// preferred NUMA node `nid`. If the allocation is successful, an opa= que handle is returned. + fn malloc( + pool: ::Borrowed<'_>, + size: usize, + gfp: Flags, + nid: NumaNode, + ) -> Result; + + /// Free an object previously allocated from the `pool`, represented b= y `handle`. + /// + /// # Safety + /// + /// - `handle` must be a valid handle previously returned by `malloc`. + /// - `handle` must not be used any more after the call to `free`. + unsafe fn free(pool: ::Borrowed<'_>, han= dle: ZpoolHandle); + + /// Make all the necessary preparations for the caller to be able to r= ead from the object + /// represented by `handle` and return a valid pointer to that object'= s memory to be read. + /// + /// # Safety + /// + /// - `handle` must be a valid handle previously returned by `malloc`. + /// - `read_end` with the same `handle` must be called for each `read_= begin`. + unsafe fn read_begin( + pool: ::Borrowed<'_>, + handle: ZpoolHandle, + ) -> NonNull; + + /// Finish reading from a previously allocated `handle`. `handle_mem` = must be the pointer + /// previously returned by `read_begin`. + /// + /// # Safety + /// + /// - `handle` must be a valid handle previously returned by `malloc`. + /// - `handle_mem` must be the pointer previously returned by `read_be= gin`. + unsafe fn read_end( + pool: ::Borrowed<'_>, + handle: ZpoolHandle, + handle_mem: NonNull, + ); + + /// Write to the object represented by a previously allocated `handle`= . `handle_mem` points + /// to the memory to copy data from, and `mem_len` defines the length = of the data block to + /// be copied. + /// + /// # Safety + /// + /// - `handle` must be a valid handle previously returned by `malloc`. + /// - `handle_mem` must be a valid pointer into the allocated memory a= read represented by + /// `handle`. + /// - `handle_mem + mem_len - 1` must not point outside the allocated = memory area. + unsafe fn write( + pool: ::Borrowed<'_>, + handle: ZpoolHandle, + handle_mem: NonNull, + mem_len: usize, + ); + + /// Get the number of pages used by the `pool`. + fn total_pages(pool: ::Borrowed<'_>) -> = u64; +} + +/// An "adapter" for the registration of zpool drivers. +pub struct Adapter(T); + +impl Adapter { + extern "C" fn create_(name: *const c_uchar, gfp: u32) -> *mut c_void { + match (|| -> Result { + // SAFETY: the memory pointed to by name is guaranteed by `zpo= ol` to be a valid string. + let name_r =3D unsafe { CStr::from_char_ptr(name).to_cstring()= }?; + T::create(name_r, Flags::from_raw(gfp)) + }) () { + Err(_) =3D> null_mut(), + Ok(pool) =3D> T::Pool::into_foreign(pool), + } + } + + extern "C" fn destroy_(pool: *mut c_void) { + // SAFETY: The pointer originates from an `into_foreign` call. + drop(unsafe { T::Pool::from_foreign(pool) }) + } + + extern "C" fn malloc_( + pool: *mut c_void, + size: usize, + gfp: u32, + handle: *mut usize, + nid: c_int, + ) -> c_int { + // SAFETY: The pointer originates from an `into_foreign` call. If = `pool` is passed to + // `from_foreign`, then that happens in `destroy_` which will not = be called during this + // method. + let pool =3D unsafe { T::Pool::borrow(pool) }; + + from_result(|| { + let real_nid =3D match nid { + bindings::NUMA_NO_NODE =3D> NumaNode::NO_NODE, + _ =3D> NumaNode::new(nid)?, + }; + let h =3D T::malloc(pool, size, Flags::from_raw(gfp), real_nid= )?; + // SAFETY: `handle` is guaranteed to be a valid pointer by `zp= ool`. + unsafe { *handle =3D h.as_raw() }; + Ok(0) + }) + } + + extern "C" fn free_(pool: *mut c_void, handle: usize) { + // SAFETY: The pointer originates from an `into_foreign` call. If = `pool` is passed to + // `from_foreign`, then that happens in `destroy_` which will not = be called during this + // method. + let pool =3D unsafe { T::Pool::borrow(pool) }; + + // SAFETY: + // - the caller (`zpool`) guarantees that `handle` is a valid hand= le previously allocated + // by `malloc`. + // - the caller (`zpool`) guarantees that it will not call any oth= er function with this + // `handle` as a parameter after this call. + unsafe { T::free(pool, ZpoolHandle::from_raw(handle)) } + } + + extern "C" fn obj_read_begin_( + pool: *mut c_void, + handle: usize, + _local_copy: *mut c_void, + ) -> *mut c_void { + // SAFETY: The pointer originates from an `into_foreign` call. If = `pool` is passed to + // `from_foreign`, then that happens in `destroy_` which will not = be called during this + // method. + let pool =3D unsafe { T::Pool::borrow(pool) }; + + // SAFETY: the caller (`zpool`) guarantees that `handle` is a vali= d handle previously + // allocated by `malloc`. + let non_null_ptr =3D unsafe { T::read_begin(pool, ZpoolHandle::fro= m_raw(handle)) }; + non_null_ptr.as_ptr().cast() + } + + extern "C" fn obj_read_end_(pool: *mut c_void, handle: usize, handle_m= em: *mut c_void) { + // SAFETY: The pointer originates from an `into_foreign` call. If = `pool` is passed to + // `from_foreign`, then that happens in `destroy_` which will not = be called during this + // method. + let pool =3D unsafe { T::Pool::borrow(pool) }; + + // SAFETY: `handle_mem` is guaranteed to be non-null by the caller= (`zpool`). + let handle_mem_ptr =3D unsafe { NonNull::new_unchecked(handle_mem.= cast()) }; + + // SAFETY: the caller (`zpool`) guarantees that `handle` is a vali= d handle previously + // allocated by `malloc`. + unsafe { T::read_end(pool, ZpoolHandle::from_raw(handle), handle_m= em_ptr) } + } + + extern "C" fn obj_write_( + pool: *mut c_void, + handle: usize, + handle_mem: *mut c_void, + mem_len: usize, + ) { + // SAFETY: The pointer originates from an `into_foreign` call. If = `pool` is passed to + // `from_foreign`, then that happens in `destroy_` which will not = be called during this + // method. + let pool =3D unsafe { T::Pool::borrow(pool) }; + + // SAFETY: `handle_mem` is guaranteed to be non-null by the caller= (zpool). + let handle_mem_ptr =3D unsafe { NonNull::new_unchecked(handle_mem.= cast()) }; + + // SAFETY: the caller (`zpool`) guarantees that `handle` is a vali= d handle previously + // allocated by `malloc`. + unsafe { + T::write(pool, ZpoolHandle::from_raw(handle), handle_mem_ptr, = mem_len); + } + } + extern "C" fn total_pages_(pool: *mut c_void) -> u64 { + // SAFETY: The pointer originates from an `into_foreign` call. If = `pool` is passed to + // `from_foreign`, then that happens in `destroy_` which will not = be called during this + // method. + let pool =3D unsafe { T::Pool::borrow(pool) }; + T::total_pages(pool) + } +} + +// SAFETY: A call to `unregister` for a given instance of `RegType` is gua= ranteed to be valid +// because preceding call to `register` never fails for zpool. +unsafe impl driver::RegistrationOps for Adapter<= T> { + type RegType =3D bindings::zpool_driver; + + unsafe fn register( + pdrv: &Opaque, + name: &'static CStr, + _module: &'static ThisModule, + ) -> Result { + // SAFETY: It's safe to set the fields of `struct zpool_driver` on= initialization. + unsafe { + (*(pdrv.get())).type_ =3D name.as_char_ptr().cast_mut(); + (*(pdrv.get())).create =3D Some(Self::create_); + (*(pdrv.get())).destroy =3D Some(Self::destroy_); + (*(pdrv.get())).malloc =3D Some(Self::malloc_); + (*(pdrv.get())).free =3D Some(Self::free_); + (*(pdrv.get())).obj_read_begin =3D Some(Self::obj_read_begin_); + (*(pdrv.get())).obj_read_end =3D Some(Self::obj_read_end_); + (*(pdrv.get())).obj_write =3D Some(Self::obj_write_); + (*(pdrv.get())).total_pages =3D Some(Self::total_pages_); + + bindings::zpool_register_driver(pdrv.get()); + } + Ok(()) + } + unsafe fn unregister(pdrv: &Opaque) { + // SAFETY: `pdrv` is guaranteed to be a valid `RegType`. + unsafe { bindings::zpool_unregister_driver(pdrv.get()) }; + } +} + +/// Declares a kernel module that exposes a zpool driver (i. e. an impleme= ntation of the zpool +/// API). +/// +/// # Examples +/// +///```ignore +/// kernel::module_zpool_driver! { +/// type: MyDriver, +/// name: "Module name", +/// authors: ["Author name"], +/// description: "Description", +/// license: "GPL", +/// } +///``` +#[macro_export] +macro_rules! module_zpool_driver { + ($($f:tt)*) =3D> { + $crate::module_driver!(, $crate::zpool::Adapter, { $($f)* }); + }; +} --=20 2.39.2