From nobody Mon Jun 8 07:21:50 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 007103CD8CA; Thu, 4 Jun 2026 20:00:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780603216; cv=none; b=tnmmcpuux1mJteHUYkmzPL2Jtpvzf3R6lFe2m/B3jGQQZnyLzf/Yw9W+giF00d9CdZegRCN1m57TQH7GFuIsXV4UmRiiViXvLtRQPtJiCRE4A8IHD4cEwO61RmvBVS0EDAgxaqOgIrNFJLEiXnpbsBi4CVQ9RhwmFvEFvXKz0jE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780603216; c=relaxed/simple; bh=ZnsSmRIcKWyPnQt9XJSezr7E2APtrOKIkUlZBJgDeoc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=WGD/jmOz1d8HBrVewCxbu2boM7cJIKJFFHFkXpv2alAkNYxGlQGG7UXq0mK4YZQ6O75uPC4UgSW5+A05yIJWc1qjaMiVR0dOxW/W71qQRYG4mCwxqgIB87YfAdEw6BO+6ZLT/iStz1RAhl3/YHcXn/GEOBgULRQ/DWpIyvLvytk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=EGPRnclD; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="EGPRnclD" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4380D1F00893; Thu, 4 Jun 2026 20:00:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1780603214; bh=ErazmYbt0dY2iWxeKQ4eTQoLenALnLYrFj4/sdzoSv8=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=EGPRnclDRb7Z9n3MYzg/+aOk861bUXrEPXeCD0IpfKGvkZvGZJpWXXxv21qHrHTE6 Zfhc/lITsreP0PZ0Vw2F3dyoz0+3WzZCgzVRnRB/GQED2Mkf0XXhbrcA9di4hr/3P2 Ip6TkTrxUliNGkyuAJ1TxsrweGWcLHhQQeBnL8oolyYCxFJqi0yWxEaP8MTAulUebs UPlWpxafvWN1CqGARcLZakFlWG5cODPk3HEa4NtDRbWBYnls5TEmURZX9W755QrO69 l8DoU2YSjP5mboEwABopMrXamnSmpTSOMcfoCJ0TeZYzQfctRnEC6YRukLyH4UtRzH zG6K6Q36ZM81w== From: Andreas Hindborg Date: Thu, 04 Jun 2026 21:58:07 +0200 Subject: [PATCH v4 01/11] rust: xarray: minor formatting fixes Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260604-xarray-entry-send-v4-1-965f6028790e@kernel.org> References: <20260604-xarray-entry-send-v4-0-965f6028790e@kernel.org> In-Reply-To: <20260604-xarray-entry-send-v4-0-965f6028790e@kernel.org> To: Miguel Ojeda , Alex Gaynor , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Andrew Morton , Christoph Lameter , David Rientjes , Roman Gushchin , Tamir Duberstein , Boqun Feng , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Tamir Duberstein , Boqun Feng , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo Cc: Daniel Gomez , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg , Daniel Gomez , "Mukesh Kumar Chaurasiya (IBM)" X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=2328; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=ZnsSmRIcKWyPnQt9XJSezr7E2APtrOKIkUlZBJgDeoc=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqIdjqik1u79zxxTS5iPXhIesPlF5+ES1kJPa6B cllYqDV22OJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaiHY6gAKCRD6UCkIqsW9 0Mm/D/44llvOJsB8w05qq4AFJIZ2dQ5QiIf7ylxkQxemH6eQEz6OmLBUQFpDQKj293/zgmGJrwH DHg2r5nYszcCQmqUN7AGK8n3XNql6jIXgVEf+z6Yn07CRb0mHnTuC3jDHlgP2ty1EiBkSyVJiYV HumYv9quQv3Y6JJ/rgVNrcWKFbjWYCoqvcc4TM23V0SdtWw8nUZ1g+FWKm8U86OAfuE5uWdUE3a QRc9Ez86w642m7/r+ZTQ2befa7Sb/Q5VpEYTLHCvF5B4Ts/AAAwrlneWFAWncbTK5yqm9mgXsi0 ETbNTGD06G+Wh8U1zSoYLHwQGY4J7uw0YmYSKCwEzsktAHtk3JN+91suy7X1LArJ9Foy60W6Sdx b1SPv54GOLGFSnayzByQnpjj+j6DQxu29YHG8HTRxBM+d8RLSYRhj9/mnzWAS2G56krVuLnM59T LbOcC5PtvMAu8otev9wOVVlBqAxBkPSFgYaG59TaCs1EMTbNf9VM+X8Q1y5H/UiFkY5m0diHNWJ FeiQd+24c25yQu7WRrlF84lSJrDjsrjW+C4RTRXGI2sWkSJDCiP/sEqsmsSUOVm55gq6vONkeuk 6bsx0I3/xAme5YJewuipS0ThYJUqdnStwsym6IMeijycAgbCr//UscgAsya0XZAe7Zokyf7h0gq Y3zuGVJR+u6FDEw== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Fix formatting in xarray module to comply with kernel coding guidelines: - Update use clauses to use vertical layout with each import on its own line. - Add trailing empty comments to preserve formatting and prevent rustfmt from collapsing imports. - Break long assert_eq! statement in documentation across multiple lines for better readability. Reviewed-by: Gary Guo Reviewed-by: Tamir Duberstein Acked-by: Tamir Duberstein Reviewed-by: Daniel Gomez Acked-by: Liam R. Howlett Reviewed-by: Mukesh Kumar Chaurasiya (IBM) Signed-off-by: Andreas Hindborg --- rust/kernel/xarray.rs | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/rust/kernel/xarray.rs b/rust/kernel/xarray.rs index 46e5f43223fe..b80fb7a262d0 100644 --- a/rust/kernel/xarray.rs +++ b/rust/kernel/xarray.rs @@ -4,14 +4,33 @@ //! //! C header: [`include/linux/xarray.h`](srctree/include/linux/xarray.h) =20 -use crate::{ - alloc, bindings, build_assert, - error::{Error, Result}, +use core::{ + iter, + marker::PhantomData, + pin::Pin, + ptr::NonNull, // +}; +use kernel::{ + alloc, + bindings, + build_assert, // + error::{ + Error, + Result, // + }, ffi::c_void, - types::{ForeignOwnable, NotThreadSafe, Opaque}, + types::{ + ForeignOwnable, + NotThreadSafe, + Opaque, // + }, +}; +use pin_init::{ + pin_data, + pin_init, + pinned_drop, + PinInit, // }; -use core::{iter, marker::PhantomData, pin::Pin, ptr::NonNull}; -use pin_init::{pin_data, pin_init, pinned_drop, PinInit}; =20 /// An array which efficiently maps sparse integer indices to owned object= s. /// @@ -44,7 +63,10 @@ /// *guard.get_mut(0).unwrap() =3D 0xffff; /// assert_eq!(guard.get(0).copied(), Some(0xffff)); /// -/// assert_eq!(guard.store(0, beef, GFP_KERNEL)?.as_deref().copied(), Some= (0xffff)); +/// assert_eq!( +/// guard.store(0, beef, GFP_KERNEL)?.as_deref().copied(), +/// Some(0xffff) +/// ); /// assert_eq!(guard.get(0).copied(), Some(0xbeef)); /// /// guard.remove(0); --=20 2.51.2 From nobody Mon Jun 8 07:21:50 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 950443CBE75; Thu, 4 Jun 2026 20:00:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780603203; cv=none; b=jGGV6ErFVAFpK4uWxpV26ltjV5nbnPl0xtji65qQVEfXO2emyFIhXdvCks8Ypx4Dm4fOH9/xBzU1qGxXR1J1O9JQ3QSBuENeOf1hfLqugqZc1Zcs4AYJu/h3r60AZtQx8OQvpS9GokxLmcGdfcQUhr9TfTO3VD6Yi3TzQ1B1r+E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780603203; c=relaxed/simple; bh=l0CwU5Ulb0y9mTSS9KkZDum+7qeUfx4uMpAmUgVECbw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=LqlTQw1ImRU5oVEXfxVlWeUNmc7zTf7Wtxuk3Om9ITFsbWk8VDd5YiU7wVRVTACTOTA8+grHog6YXLKwce9GOS5LkUnOVrOpxCqYPStOyiqJZ0ZHtYrok0FKEAug4DXfY/tpy0zIT4ISedarH9Je7d2hlZflAWqPwpC1LKE0vm4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=YO/iaCXs; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="YO/iaCXs" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5D10D1F00893; Thu, 4 Jun 2026 19:59:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1780603201; bh=0nBF57LRsWrMEo8iN2euSSytrxyB+R/0oRMmaelTwQA=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=YO/iaCXs1ZU63nZVH1a43zbW8BPeEPyW7LFZCDDd5aVO0JXVJTt/1OW0VvZuvisiW TJlHfnUzpTJxFgNyVRQjDc9WNCg5gvwTxlj96TNDApW5aOAFjFIrrRbHSkFWMifdUu i0pT8C0EpBz5CqaV296YpJaGTWF4MHkz3khQmEMFsyGmOMfQj7jnRszGL/OPKeFbs7 KECsJiYIyKv8ieP81f7zerO+8Dyn7RESVDAsSHQOKk4DHYZKbqdzWpL5FSwnPqBKQw qm/zy69GWjTXKAbjAZPq8pyWhCzr8yZqaUTyQHcR1JssnixZdcZw1T7J8E5hXhrAjM 8J4uN7fBuaSmQ== From: Andreas Hindborg Date: Thu, 04 Jun 2026 21:58:08 +0200 Subject: [PATCH v4 02/11] rust: xarray: add debug format for `StoreError` Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260604-xarray-entry-send-v4-2-965f6028790e@kernel.org> References: <20260604-xarray-entry-send-v4-0-965f6028790e@kernel.org> In-Reply-To: <20260604-xarray-entry-send-v4-0-965f6028790e@kernel.org> To: Miguel Ojeda , Alex Gaynor , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Andrew Morton , Christoph Lameter , David Rientjes , Roman Gushchin , Tamir Duberstein , Boqun Feng , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Tamir Duberstein , Boqun Feng , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo Cc: Daniel Gomez , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg , Daniel Gomez X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=1148; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=l0CwU5Ulb0y9mTSS9KkZDum+7qeUfx4uMpAmUgVECbw=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqIdjrzorzq+4T6PurdpmkIUWDjVZ/tEBb8myon 5d/0/v7XIqJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaiHY6wAKCRD6UCkIqsW9 0EfUD/9OWWqFNFIktQfpK1Hyle/LSAjmNlESK91BK2sb65VFLfMXyoTm67fb3PbbhdLRKbEkrN8 Cmf/q4f9mPWpw5IZXPdvpE6COADtszfD/KLvrYr+fgFA16tWnvXtjpxp2Sy+SWI02uLkeOO3YYU 6lln3/eE5N9bc4aMSJHSFGXFOWQUbPnqy3NYMONVbnZuBUd+nQdSE9iX67mmCZINedr9HMVKdpe SmDNlTDHEEyfCRUn6bwv9VEARI3o2Vm8HOhMaLbmPqRm2h3WCa6DpRqQbyVDa5lnsRR/UNLJ6Ky gF9D/p/gAu66ru+R26U0G13ri+7ntEggShXuRw6SKs44stEdanQk/P+BnTs0ap6R6eI6a8wnnbx +jFNpn5TgiXEWqTPXZwn7LAQYbuBsr8K6bmC3qhDypfzYg2kJJq0k+Fnk1kPOrNT0FiwDg+pw0N JBdHH3RkypHidqpglHSixmYT/p4ggHiD/osQLnLyHsjjOuXS7yXxmfkwBCqfZmRf7W/RDniTy2i cuz2hcNkOaEPrH5oEwGuWi4o6V/oYIKLq8RISJViVqVr4z9TfRI0hrQVRkot4v8PuOI2Abl8QoB +HOJ0ZrYUPLbqm880J95kzG6gBV3JFxPGpPPxQSJoBcYFyK0BH1nEPSdJGZkiUx21JcbCpmM1p4 frYqT+ng6G3oPrw== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a `Debug` implementation for `StoreError` to enable better error reporting and debugging. The implementation only displays the `error` field and omits the `value` field, as `T` may not implement `Debug`. Reviewed-by: Gary Guo Reviewed-by: Daniel Gomez Acked-by: Tamir Duberstein Acked-by: Liam R. Howlett Signed-off-by: Andreas Hindborg --- rust/kernel/xarray.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rust/kernel/xarray.rs b/rust/kernel/xarray.rs index b80fb7a262d0..d54942aeb201 100644 --- a/rust/kernel/xarray.rs +++ b/rust/kernel/xarray.rs @@ -193,6 +193,14 @@ pub struct StoreError { pub value: T, } =20 +impl kernel::fmt::Debug for StoreError { + fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Resu= lt { + f.debug_struct("StoreError") + .field("error", &self.error) + .finish() + } +} + impl From> for Error { #[inline] fn from(value: StoreError) -> Self { --=20 2.51.2 From nobody Mon Jun 8 07:21:50 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 1CB543C5529; Thu, 4 Jun 2026 19:59:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780603198; cv=none; b=IVY+WR9avMZgMG7V/anGmkboQZ+GrSDvlHc21HVKTLd1EjwdsP7cayMkFchqibI5mpNGOCQ0Sf8LkkZk8PFzY3xCZlUVfSn5WIW7pC1+2FesWRNpxrVnli5mLXRbQkPYuOnAsGbLd2dDsLnUB9HsCGlffCjJ2CNEgmrCln3H0d8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780603198; c=relaxed/simple; bh=wW3dDYAPEV4zxCchJaOgHpoU9SxK5JCQAEcaobGW2ME=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=qGd5tKQLC6deCq6VDpHfX95xQEG8VH9V8+DJYfVMNIlj5nYekk4aBycw0+2ITQWQcjdEAvPHSggKfF7smPvyQy/vJ8+8RnjwpRQLoQOMyyWlDKu5F0cywu2z6f1o1zt3i51nqz5HJeeBqLUUpADBJztOaTec+Y0RIgjDrAVC4Vs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=erNGeM2b; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="erNGeM2b" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3BE141F00898; Thu, 4 Jun 2026 19:59:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1780603194; bh=Ixi+QJOhPBvaKKM++NPOJdur0t/SwSTk/yHnYjkZNng=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=erNGeM2bqlTFuHgqScFLsIgo9udKdgieAvty5VzUEKsfA2o3xirRR5x0ULJkbEsH7 T/hpr8VKTJ2ckcJIH6SfKpMRTQtgd+CoENYNfiDZs6CVoFQUdcNdsD8t4K+yWK14Tn RhuktW5+RgVpp5DQ95jqYMGl+0KxOOp0xh+KtnvdWjjy9kwwqMNq30iPX6N6O+Oyhr AmxmA2Va+XSxhOYAloOKe1QVLD6Zu5gkwWcQwCNvS1sMuF2W1wAnBmTCLK6JwOFp/C BjTNimW2xa3V/8X7kQrHYS9g69PB96U8s9nmdoAzkOP3jxZ7Qk8dSdVa9nvsBVrDX4 JChl3U7TC+Iaw== From: Andreas Hindborg Date: Thu, 04 Jun 2026 21:58:09 +0200 Subject: [PATCH v4 03/11] rust: xarray: add `XArrayState` Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260604-xarray-entry-send-v4-3-965f6028790e@kernel.org> References: <20260604-xarray-entry-send-v4-0-965f6028790e@kernel.org> In-Reply-To: <20260604-xarray-entry-send-v4-0-965f6028790e@kernel.org> To: Miguel Ojeda , Alex Gaynor , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Andrew Morton , Christoph Lameter , David Rientjes , Roman Gushchin , Tamir Duberstein , Boqun Feng , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Tamir Duberstein , Boqun Feng , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo Cc: Daniel Gomez , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=4215; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=wW3dDYAPEV4zxCchJaOgHpoU9SxK5JCQAEcaobGW2ME=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqIdjsiRRLOBz8U2tFyUbjDABp9LNAcfFnMAacB q/TryPb/ISJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaiHY7AAKCRD6UCkIqsW9 0OYsEAChP21SQ0tJK1PcYeptIDAx+bvYcOm3teBt0ihZgYnh6nYrnTdBGHf2kMuIvfs12QTpp+T zfNl/w5+U5C0LlK4b5tXCvMMtUaocbXe+5Zh/rSN00zoJs4lDVVneujrjMjLCludaNgUxiSCee2 zQYlDt53i82yVNaMF61aUtErkLrQ8l4jlOanqN5wbz2oYGH9PYNgxgOwKHWzCayrWq9k5kBtSf5 9aEe26/crBBXnrPNSZFNe7wSqZ/pzzhblbGRLGs9Qu8DGpy1kiXB5Hs1q55np19W1HOjP8C4E/X qr1bWj5mMZVqW262zqgRr87XmdAiOgKsT3zEqbbnCcJPNM8Z8cnaNCWXAqGouU8I0m9XKVMp+xe BLveGRujcqD4FAn22ZeIJhbrc3PcL5rwpDxpPycTJcQt5K84ullP7MSDR4U4sZxqZNOtJJj3D4Q qXhGXu9eKmJ6NkLWNfg+t7DKg3FhxPZVT0TrnYOOXXIkl6fxkP3C7ajQqy4CihoJjHm3lgeVbdC PzNd7/RQ5Kv+5mwaFlptrzXy/7L+CmnKQuk2lnYX3N3L9ZEpfvCcGYcYGzb4yJJbAbyYQrPZzbb xxOWYE7RQfr8JjTCqUyV4K6WWYACKR+hYc6utgnsyOw4gn1H57G5rWBG4VinID/419rAnxs/hQP rXW2fFRBvTDAMPQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add `XArrayState` as internal state for XArray iteration and entry operations. This struct wraps the C `xa_state` structure and holds a reference to a `Guard` to ensure exclusive access to the XArray for the lifetime of the state object. The `XAS_RESTART` constant is also exposed through the bindings helper to properly initialize the `xa_node` field. The struct and its constructor are marked with `#[expect(dead_code)]` as there are no users yet. We will remove this annotation in a later patch. Signed-off-by: Andreas Hindborg --- rust/bindings/bindings_helper.h | 7 +++++ rust/kernel/xarray.rs | 66 +++++++++++++++++++++++++++++++++++++= +++- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 446dbeaf0866..d4093367a4a8 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -123,6 +123,13 @@ const xa_mark_t RUST_CONST_HELPER_XA_PRESENT =3D XA_PR= ESENT; =20 const gfp_t RUST_CONST_HELPER_XA_FLAGS_ALLOC =3D XA_FLAGS_ALLOC; const gfp_t RUST_CONST_HELPER_XA_FLAGS_ALLOC1 =3D XA_FLAGS_ALLOC1; +/* + * `XAS_RESTART` is `((struct xa_node *)3UL)` -- a sentinel pointer value,= not + * an address. Cast to `size_t` so bindgen emits a plain `usize` constant;= for + * pointer-typed macro values bindgen otherwise generates a `pub static mu= t`, + * see https://github.com/rust-lang/rust-bindgen/issues/3347. + */ +const size_t RUST_CONST_HELPER_XAS_RESTART =3D (size_t)XAS_RESTART; =20 const vm_flags_t RUST_CONST_HELPER_VM_MERGEABLE =3D VM_MERGEABLE; const vm_flags_t RUST_CONST_HELPER_VM_READ =3D VM_READ; diff --git a/rust/kernel/xarray.rs b/rust/kernel/xarray.rs index d54942aeb201..6d0d4905004a 100644 --- a/rust/kernel/xarray.rs +++ b/rust/kernel/xarray.rs @@ -8,7 +8,10 @@ iter, marker::PhantomData, pin::Pin, - ptr::NonNull, // + ptr::{ + null_mut, + NonNull, // + }, }; use kernel::{ alloc, @@ -299,6 +302,67 @@ pub fn store( } } =20 +/// A reference to a [`Guard`], either shared or mutable, that exposes the +/// underlying xarray pointer and the value type stored in the array. +pub(crate) trait GuardRef { + type Value: ForeignOwnable; + fn xa_ptr(&self) -> *mut bindings::xarray; +} + +impl<'a, T: ForeignOwnable> GuardRef for &Guard<'a, T> { + type Value =3D T; + fn xa_ptr(&self) -> *mut bindings::xarray { + self.xa.xa.get() + } +} + +impl<'a, T: ForeignOwnable> GuardRef for &mut Guard<'a, T> { + type Value =3D T; + fn xa_ptr(&self) -> *mut bindings::xarray { + self.xa.xa.get() + } +} + +/// Internal state for XArray iteration and entry operations. +/// +/// `R` is the borrow held on the guard: either `&Guard` for read-only cal= lers +/// or `&mut Guard` for entry-style APIs that need to surrender the borrow= back +/// via [`XArrayState::into_guard`]. +/// +/// # Invariants +/// +/// - `state` is always a valid `bindings::xa_state`. +/// - `state.xa` aliases the xarray reachable through `guard`. +#[expect(dead_code)] +pub(crate) struct XArrayState { + guard: R, + state: bindings::xa_state, +} + +impl XArrayState { + #[expect(dead_code)] + fn new(guard: R, index: usize) -> Self { + let xa_ptr =3D guard.xa_ptr(); + // INVARIANT: `state` is initialized to a valid `xa_state` whose `= xa` field aliases the + // xarray reachable through `guard`. + Self { + guard, + state: bindings::xa_state { + xa: xa_ptr, + xa_index: index, + xa_shift: 0, + xa_sibs: 0, + xa_offset: 0, + xa_pad: 0, + xa_node: bindings::XAS_RESTART as *mut bindings::xa_node, + xa_alloc: null_mut(), + xa_update: None, + xa_lru: null_mut(), + }, + } + } +} + // SAFETY: `XArray` has no shared mutable state so it is `Send` iff `T`= is `Send`. unsafe impl Send for XArray {} =20 --=20 2.51.2 From nobody Mon Jun 8 07:21:50 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 B0BD33CF02A; Thu, 4 Jun 2026 20:00:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780603237; cv=none; b=EbX5WbwQkS+qhdGTh9Jy2qdwgTe8zRqV7TLxBW4k9IDI3pvY4jQayVF3GXZxY9XzT0nUi97g5yZii+HenzYhVu5vQGhgf3XefqaDjsXp91u4rMWx0ZeL0gsKxpr4oH7EOyvMAA1DGmc10s1QkEGOMuEh6S9AQVzIOfbJFnluccA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780603237; c=relaxed/simple; bh=oYaezwlxJQpR8UtIF1kAo4ndp1y9jT02xjr/aB92Sp8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=iYLfcyVmM+GpEJQDbYk7gTS6pVYq7dKuNm7vUIlPzTkIUjS+5nJxPiV827mu/Ok9TkDYUNUPYjtGeKjyB6N0NycFgpO/y1aHFZjZ2tKcQBHa63ogdIZhfzFgvRnNNTn7YIkbm9zGGxBKQeRD+Jy9HrGyj4R96R01jzo95URYuws= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Td6OZ0oB; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Td6OZ0oB" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2E8091F00898; Thu, 4 Jun 2026 20:00:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1780603234; bh=p6YuYMOYnDWDBMZ9bbEPIsqKNsVYpVFYY4aODNh2Npw=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=Td6OZ0oB6NVr21aNjgjcbw9w8ngfB+Z+W7pM31bHdmaKrm8mUW+6P8SMyO01LTBB7 aZ28jpYveay9tJ5hxsepy0tMwjd+/K0b0b62mriLyAfadJMX7yOH1xbVLTH+GjxXIW xXYy9mdQsnEBLW5xw1hY9Kx7iB/BqUo8bgnwsoQkDL9Z4TKuBOLPi1A22j8IJPb4Ti Ka+XELivsbPTaK1xUt4+ZtPAvJCiGqPgtpQLJloAD9LeeO44w71KP1xUap4+A46yWV 5FvJdE5XAPsDDdly6nKoknfZMmHKtutNTU2Qbpxq6B6s7x+Xz24d0yStjFZQO6gQYr 7yMi00M3QyZsw== From: Andreas Hindborg Date: Thu, 04 Jun 2026 21:58:10 +0200 Subject: [PATCH v4 04/11] rust: xarray: use `xas_load` instead of `xa_load` in `Guard::load` Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260604-xarray-entry-send-v4-4-965f6028790e@kernel.org> References: <20260604-xarray-entry-send-v4-0-965f6028790e@kernel.org> In-Reply-To: <20260604-xarray-entry-send-v4-0-965f6028790e@kernel.org> To: Miguel Ojeda , Alex Gaynor , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Andrew Morton , Christoph Lameter , David Rientjes , Roman Gushchin , Tamir Duberstein , Boqun Feng , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Tamir Duberstein , Boqun Feng , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo Cc: Daniel Gomez , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=2450; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=oYaezwlxJQpR8UtIF1kAo4ndp1y9jT02xjr/aB92Sp8=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqIdjtXXR6p/YMNbWL1d7vr9jQHRbkshID8Gu3F 6Mk1qpxbzqJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaiHY7QAKCRD6UCkIqsW9 0Hw9D/sExDzOpSwLMZPByJRIDOftL7P3W6n7egMhr5eH9jrbr9Z0/bBmlY2GkTSChDdjCeti3iT PVzlvvzpBFAgB4xhGAZc1fFPSzbzCqh4RJPCS0uQJQPIVONDRjtimSwNNnBq96darpyBzlwFvEn KjsE5NUuh3cK4rWz1wFY4lmNJYvY2V/WhPSgF5uATxvWeWGOLBpehSE3VVwxLmPHj6pV7bLUUUz khgcIVMTUB/gNsL83eoQZ/W5bC2Kn+KxIdS3rHNP914QHRl6SFYIe8JmcLhKOLo1mKY7cx/Qws+ 4MxvLHw9WzELoT2fl28hr+JJvFP98b717NvSCD8IlX1nQtNIGO176AFpvBxQva/iR9H1B/tIKUr iVyK/L7Sf6AC85xIEkE/HgiHEYgWDLSgIurtf/qli6Z/acWX218XdPN/t4eBjvX+viDKN2FgU6F 8tHLcVGzVaS0RSEYlgZdGH2HOWEsoVrPnscKZxlDw0uNJL2bI0DuJJf/pq+QUdwC7shxxbP5aJm f0vrRRd58MuAyjwHmuDGjD/mR4jVQ2IocIaoDuGFn/21bOKIoo2viRFX61R0ygKGgslQdPNHbrN gTq6DgLkQnZ3tAS9a7ey3JIY2SuAfHi//Uwe6zTdpbn10CTe4EAimfAbEFxVji5kns8Fk9TGqWE PbSDm83CaEkfRDA== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Replace the call to `xa_load` with `xas_load` in `Guard::load`. The `xa_load` function takes the RCU lock internally, which we do not need, since the `Guard` already holds an exclusive lock on the `XArray`. The `xas_load` function operates on `xa_state` and assumes the required locks are already held. This change also removes the `#[expect(dead_code)]` annotation from `XArrayState` and its constructor, as they are now in use. Signed-off-by: Andreas Hindborg --- rust/kernel/xarray.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/rust/kernel/xarray.rs b/rust/kernel/xarray.rs index 6d0d4905004a..05e6dc1ffe69 100644 --- a/rust/kernel/xarray.rs +++ b/rust/kernel/xarray.rs @@ -216,10 +216,8 @@ fn load(&self, index: usize, f: F) -> Option where F: FnOnce(NonNull) -> U, { - // SAFETY: `self.xa.xa` is always valid by the type invariant. - let ptr =3D unsafe { bindings::xa_load(self.xa.xa.get(), index) }; - let ptr =3D NonNull::new(ptr.cast())?; - Some(f(ptr)) + let mut state =3D XArrayState::new(self, index); + Some(f(state.load()?)) } =20 /// Provides a reference to the element at the given index. @@ -333,14 +331,12 @@ fn xa_ptr(&self) -> *mut bindings::xarray { /// /// - `state` is always a valid `bindings::xa_state`. /// - `state.xa` aliases the xarray reachable through `guard`. -#[expect(dead_code)] pub(crate) struct XArrayState { guard: R, state: bindings::xa_state, } =20 impl XArrayState { - #[expect(dead_code)] fn new(guard: R, index: usize) -> Self { let xa_ptr =3D guard.xa_ptr(); // INVARIANT: `state` is initialized to a valid `xa_state` whose `= xa` field aliases the @@ -361,6 +357,14 @@ fn new(guard: R, index: usize) -> Self { }, } } + + fn load(&mut self) -> Option> { + // SAFETY: `self.state` is a valid `xa_state` by the type invarian= t. By the same + // invariant, `self.state.xa` aliases the xarray reachable through= `self.guard`, whose + // lock we hold. + let ptr =3D unsafe { bindings::xas_load(&raw mut self.state) }; + NonNull::new(ptr.cast()) + } } =20 // SAFETY: `XArray` has no shared mutable state so it is `Send` iff `T`= is `Send`. --=20 2.51.2 From nobody Mon Jun 8 07:21:50 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 67B0B3C9EC6; Thu, 4 Jun 2026 20:00:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780603244; cv=none; b=JQ4z+tOl9y1JaY1swgHtyrnnDwrY3wh6ygvVrZ4ea/rYc/qs1bVMmIpdnjndaWTmYQ5k8wtOyFVwia8ebDeW0djNgrbm0nM5tBQhrTQueg7fbQM5EKcS6DGlQfhXCazu510xK6prV5X0MxifsHOAFd4MFi/yumu28esSrerFbuE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780603244; c=relaxed/simple; bh=ezQhkpviZx/0cjtMqszOUvlxFZxlxC9tkEpF0/WvfOY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=NuuQ7tonNIU5C3EhZuz/+dpT8bZyxh/BJRuo5L0HvTt8tMi620ULW++dCztqyg4VkfQXM9+IaH9eLaFULv3Wvz/cCh7pzKvsluu9LhRyCR5gXjZP1bbXAAkny6lV/PNrcbXZuwhlbiWksbgobkPFgCiHONuavSffLJWCGZVv9d0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=cySSMS0w; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="cySSMS0w" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C29BB1F00893; Thu, 4 Jun 2026 20:00:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1780603240; bh=uzpE/Seo/CVQh6i9kKpA38HdPooRgCjlsYOlxSZWRAo=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=cySSMS0wQoYMLFZzNwdzKmfmDrtcSP/NCTMcBFjGrWbz/6X+Fz/vnrepqH5ta2nh1 lhCk9aTbZPsqiZNsvbjOpL4PQxz8kP+DABIXN56KvsyAjv59XHUSu7zIHiAoEbqhPm 1esZpcUGWk6ajslGpO2jjpcIxVt6RdEiE6v7A1RYXbUZ8oEM1XcBwgXVVBKTFf1wg7 A9AfnP56HKtTmklCWvGhwU/UpbvkpJ/N3Xx67srCiTyhHV1gejmg4wmO0SpwR+eOcl Ac5Hdplhy87NTF5pJ+2+4er41Stu9yUF0Qjed+2CogvW7P+aqeNxfZXdaox+bkAki9 laeW5QIFtTTTA== From: Andreas Hindborg Date: Thu, 04 Jun 2026 21:58:11 +0200 Subject: [PATCH v4 05/11] rust: xarray: simplify `Guard::load` Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260604-xarray-entry-send-v4-5-965f6028790e@kernel.org> References: <20260604-xarray-entry-send-v4-0-965f6028790e@kernel.org> In-Reply-To: <20260604-xarray-entry-send-v4-0-965f6028790e@kernel.org> To: Miguel Ojeda , Alex Gaynor , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Andrew Morton , Christoph Lameter , David Rientjes , Roman Gushchin , Tamir Duberstein , Boqun Feng , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Tamir Duberstein , Boqun Feng , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo Cc: Daniel Gomez , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=1921; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=ezQhkpviZx/0cjtMqszOUvlxFZxlxC9tkEpF0/WvfOY=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqIdju4CQhaBJkwad79lAfp/ue4pLZH7qUsiCm/ +qQ25t3ElKJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaiHY7gAKCRD6UCkIqsW9 0PkLEACqUwe49HzCnXbMEXytmhd0EooYaXxjvSqDIcIDhP4BqsAGSFzbXsRfdR93yV15pTd5Btl JHv74i4b5Z0UVvF6rEXcae2n9TT/TdoVv/YNvAQPOxodV9+9pCG64KN4WoVJm882c8kllHD70qk tikJcRAEbjzyuVBJLcxZXzqkRiSG5Q9sGUHb/P9OacmoI9I/czaNbF+qmD7KqDV4zJwY1z2zni/ lgoGOJFES7OBBQPeLRWZpfHgViKACn71Fetc6wyljzbj2iSwwm2pEAtOLFFNxtbPJub04IgQfeH S7TZ6+OE6b/N0Wsz4vVgOQB+gsSlDqG9cuvkgayZ8xasT1X6h3snzsh+VL6DXuzLnxAb083ktup Wx3n9CP4v6XAmNyDEf1KG9jQiAaQ+OqLjFyfshRwHn+2l0dL1YUlFd3O3ncsb35BJOj8QoTegAo soE0Crn8McmYMPM64I0llHlTFjJsVxwKrMVViZb1/fY5FQXsSLNOwWllgKwdCkSpGwQYgX/7WHm fxjoYCVj4oVoQ4kMayxYBlTYUrcfz355KKByl+Y4455Mel4QLQlngxC9hYIcQdNjlQohUFBwzQp n1fy6EZejVLk87xWLxOfnqFhP9BelJBmHncIW3inXaCP1jgOVFdU4IJrvuIvaVpR/Gq8hk8tBNZ eVEHfHszdt31wHw== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Simplify the implementation by removing the closure-based API from `Guard::load` in favor of returning `Option>` directly. Signed-off-by: Andreas Hindborg --- rust/kernel/xarray.rs | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/rust/kernel/xarray.rs b/rust/kernel/xarray.rs index 05e6dc1ffe69..7da57c778669 100644 --- a/rust/kernel/xarray.rs +++ b/rust/kernel/xarray.rs @@ -212,28 +212,23 @@ fn from(value: StoreError) -> Self { } =20 impl<'a, T: ForeignOwnable> Guard<'a, T> { - fn load(&self, index: usize, f: F) -> Option - where - F: FnOnce(NonNull) -> U, - { - let mut state =3D XArrayState::new(self, index); - Some(f(state.load()?)) + fn load(&self, index: usize) -> Option> { + XArrayState::new(self, index).load() } =20 /// Provides a reference to the element at the given index. pub fn get(&self, index: usize) -> Option> { - self.load(index, |ptr| { - // SAFETY: `ptr` came from `T::into_foreign`. - unsafe { T::borrow(ptr.as_ptr()) } - }) + let ptr =3D self.load(index)?; + // SAFETY: `ptr` came from `T::into_foreign`. + Some(unsafe { T::borrow(ptr.as_ptr()) }) } =20 /// Provides a mutable reference to the element at the given index. pub fn get_mut(&mut self, index: usize) -> Option> { - self.load(index, |ptr| { - // SAFETY: `ptr` came from `T::into_foreign`. - unsafe { T::borrow_mut(ptr.as_ptr()) } - }) + let ptr =3D self.load(index)?; + + // SAFETY: `ptr` came from `T::into_foreign`. + Some(unsafe { T::borrow_mut(ptr.as_ptr()) }) } =20 /// Removes and returns the element at the given index. --=20 2.51.2 From nobody Mon Jun 8 07:21:50 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 A85573CB90A; Thu, 4 Jun 2026 19:59:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780603185; cv=none; b=VRg+dqIiwUMZN0hfVcG/UATNMbiXKuatJYPMJQElFD6B0uYrUTV172k9FAOO/3El4OswQIB5soIQt323g5AfXZUyTJmu9rGGiXeMBiT1VojQS7EdEn0vr1CgAv8Fn0x9ImjT5a/hAho463coJ6YlgjQV/PD+LWfvaPWsUT9W5t8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780603185; c=relaxed/simple; bh=pYvfABtxRJd+65+UwL/yf+feoNbyxJANq7/Ip719Xh8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Q+YqKVrd7+lMSPMb8oCdJQzL1pYvS5R6dvEffClCD1A3SoJOZYB2foVzu2y6A+s2/h0Ze/ga3zfzzDrnZx+7/eT7eu+hEdSwoIkRJVf0sg9925PhZTtf93uP4ueF/fUjIgcNqoTbzZnBe1OB+ADZnUo7MoChOyW+kPZWhAIMzgE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=VlfIx0wC; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="VlfIx0wC" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9BA0B1F00899; Thu, 4 Jun 2026 19:59:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1780603182; bh=lMIuC5KfuikN+IJdOVRU49vpMgSH5gGglwhQwCzLUFg=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=VlfIx0wCRHD2Ke7Wsiv7XSdTrg7Sa6HU7/t+c4g9REs5Qyr8LXkV1krikhS7ObdhM MT7MIlG0zpL4Cmc+xNe6rDPuWF4UlNx5eaNi5sOdPHBfR/PIRAzYyL0vJpTBd6uy1X yBYGIj27ZhHe6qlNfb7Jm3seZDil7qIYkKUDWKuXrQInSCm8peHVNDaaJ6kuhEDhS5 NBXCRaCL7WiqPRSsMqQFd9aKMB8y5hWggLRNM5axIXqzL1IDqEtEogZXIeTKSFf7eF ZnGDMcnbQSfND7TL5brTrBWJRHtP5Z0rRkwzCyRdyNPeaPbEpK/9lN6ocFbOqq9oUJ IbTSb3kDkapsA== From: Andreas Hindborg Date: Thu, 04 Jun 2026 21:58:12 +0200 Subject: [PATCH v4 06/11] rust: xarray: add `find_next` and `find_next_mut` Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260604-xarray-entry-send-v4-6-965f6028790e@kernel.org> References: <20260604-xarray-entry-send-v4-0-965f6028790e@kernel.org> In-Reply-To: <20260604-xarray-entry-send-v4-0-965f6028790e@kernel.org> To: Miguel Ojeda , Alex Gaynor , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Andrew Morton , Christoph Lameter , David Rientjes , Roman Gushchin , Tamir Duberstein , Boqun Feng , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Tamir Duberstein , Boqun Feng , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo Cc: Daniel Gomez , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=4101; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=pYvfABtxRJd+65+UwL/yf+feoNbyxJANq7/Ip719Xh8=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqIdjvzVaY77frrnGM8IY/dUfo7TEJOl6r6lp4/ 8ZNkt95LtKJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaiHY7wAKCRD6UCkIqsW9 0DctEACTBcYdYrPHXcRAgJacN+h2yee/0JerT2FT+RLQY6DC9bEhyT3fD46b3iRQu/jCAWa675Z d6HKR93TpZvIJv8QQHAIb92hONgHK2s5uJsOYAc9P4Qvzs9K/IVUiM0KXvgJNhX5GFyb4TDv1jO 2smQMdyVMEttC72eLIBDHHpRWSwtXuPTxdU7cv0SBIZN+AE34de28/CINYQt5++uW2UOHytaNi1 H7RPytogVxfYyRFbJdMd7AMIMBNc616llsluuWsFi3B07RVLNEBZAQEqWOvzC4jV2IWO5TuoO48 N2cydn6UwqftBqgsNMRSGWXHAlbnbQvFLIOJnk4alxO2BsUZ21HIk5moH06xAo9XUpsUxrzcFI6 Ed2Vld+ARN11CNYw7quDe8PGtzA66RjkHis+U3sDssztBD4Da32BwiKy6Yigs/hshoPfqAWQPFb Q1T07XK7mKWe0a9kKFEBtXQRGWk3BnZNZy51FIgR8eSxtM86GNA9NyBAkTZyGOdMFG/JL+2OKov a0s8LpZa+rfWKhROBR8RDm/Adm7j/fW+jUuDbR2YCYpcwQiduU0e6KSPdZ9yBbzmNVfXTXjFKEp W9scryc9F1sDDOw0MxkqiuhmCNYvL1Yz0jX13YZJl1r6Xl+Qyn7pgHdTjJEhS8xvsWp3zec5asx PQ6xLhqf8mPLl7w== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add methods to find the next element in an XArray starting from a given index. The methods return a tuple containing the index where the element was found and a reference to the element. The implementation uses the XArray state API via `xas_find` to avoid taking the rcu lock as an exclusive lock is already held by `Guard`. Signed-off-by: Andreas Hindborg --- rust/kernel/xarray.rs | 69 +++++++++++++++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 69 insertions(+) diff --git a/rust/kernel/xarray.rs b/rust/kernel/xarray.rs index 7da57c778669..7efa8a36d559 100644 --- a/rust/kernel/xarray.rs +++ b/rust/kernel/xarray.rs @@ -231,6 +231,67 @@ pub fn get_mut(&mut self, index: usize) -> Option> { Some(unsafe { T::borrow_mut(ptr.as_ptr()) }) } =20 + fn load_next(&self, index: usize) -> Option<(usize, NonNull)> { + XArrayState::new(self, index).load_next() + } + + /// Finds the next element starting from the given index. + /// + /// # Examples + /// + /// ``` + /// # use kernel::{prelude::*, xarray::{AllocKind, XArray}}; + /// let mut xa =3D KBox::pin_init(XArray::>::new(AllocKind::= Alloc), GFP_KERNEL)?; + /// let mut guard =3D xa.lock(); + /// + /// guard.store(10, KBox::new(10u32, GFP_ATOMIC)?, GFP_ATOMIC)?; + /// guard.store(20, KBox::new(20u32, GFP_ATOMIC)?, GFP_ATOMIC)?; + /// + /// if let Some((found_index, value)) =3D guard.find_next(11) { + /// assert_eq!(found_index, 20); + /// assert_eq!(*value, 20); + /// } + /// + /// if let Some((found_index, value)) =3D guard.find_next(5) { + /// assert_eq!(found_index, 10); + /// assert_eq!(*value, 10); + /// } + /// + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + pub fn find_next(&self, index: usize) -> Option<(usize, T::Borrowed<'_= >)> { + self.load_next(index) + // SAFETY: `ptr` came from `T::into_foreign`. + .map(|(index, ptr)| (index, unsafe { T::borrow(ptr.as_ptr()) }= )) + } + + /// Finds the next element starting from the given index, returning a = mutable reference. + /// + /// # Examples + /// + /// ``` + /// # use kernel::{prelude::*, xarray::{AllocKind, XArray}}; + /// let mut xa =3D KBox::pin_init(XArray::>::new(AllocKind::= Alloc), GFP_KERNEL)?; + /// let mut guard =3D xa.lock(); + /// + /// guard.store(10, KBox::new(10u32, GFP_ATOMIC)?, GFP_ATOMIC)?; + /// guard.store(20, KBox::new(20u32, GFP_ATOMIC)?, GFP_ATOMIC)?; + /// + /// if let Some((found_index, mut_value)) =3D guard.find_next_mut(5) { + /// assert_eq!(found_index, 10); + /// *mut_value =3D 0x99; + /// } + /// + /// assert_eq!(guard.get(10).copied(), Some(0x99)); + /// + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + pub fn find_next_mut(&mut self, index: usize) -> Option<(usize, T::Bor= rowedMut<'_>)> { + self.load_next(index) + // SAFETY: `ptr` came from `T::into_foreign`. + .map(move |(index, ptr)| (index, unsafe { T::borrow_mut(ptr.as= _ptr()) })) + } + /// Removes and returns the element at the given index. pub fn remove(&mut self, index: usize) -> Option { // SAFETY: @@ -360,6 +421,14 @@ fn load(&mut self) -> Option> { let ptr =3D unsafe { bindings::xas_load(&raw mut self.state) }; NonNull::new(ptr.cast()) } + + fn load_next(&mut self) -> Option<(usize, NonNull)> { + // SAFETY: `self.state` is a valid `xa_state` by the type invarian= t. By the same + // invariant, `self.state.xa` aliases the xarray reachable through= `self.guard`, whose + // lock we hold. + let ptr =3D unsafe { bindings::xas_find(&raw mut self.state, usize= ::MAX) }; + NonNull::new(ptr).map(|ptr| (self.state.xa_index, ptr)) + } } =20 // SAFETY: `XArray` has no shared mutable state so it is `Send` iff `T`= is `Send`. --=20 2.51.2 From nobody Mon Jun 8 07:21:50 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 C096C3C9EEC; Thu, 4 Jun 2026 20:00:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780603209; cv=none; b=aSX7u+fpEgXtid/J8SVRe/r1zdJdlpILn3SRcoFEgadPeO3JdYTKz1GUMzczS8cq0JFA4XJpraTW6MBEaCWGLkOEl6+XojnylwmqJ1yTrhwhxqqMmFB48MlKepcKT3WEuGs3VGAl7uMqSt/zNFG8u9v3eq49nhEBTWnbde/WHvI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780603209; c=relaxed/simple; bh=nlvjywsgkgwKWYwmhZEaELFYOvxdO57Snukucz2qinw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=b79Ag3+yh+hqVzpEMS720oRZ0RVFn3dbEQiKXD2KvnwIi+ObqSJLctgOPmjOROuuZHkOMIqREMzphJY70J56WDGHXrlA6m0snZ87AaaUg0LsfuTWvbIGYvMGfVrhYNoNsJDcYOhou/jHZAtkA6a2QUWPNXoyDW9DDAm4ThZcu90= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=F6a6LUuZ; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="F6a6LUuZ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id EBD4A1F00898; Thu, 4 Jun 2026 20:00:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1780603207; bh=ENe2+RbkrnGLbFAMfmpauwPqW9/ZPihu1GWKc3En83U=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=F6a6LUuZ+0uA4IscK29EpyKW8gRosVRCxCRHMd7Mt1uW9slQSDL6S2H39Ldm9qLwP 7IAmxzA63lVD3WIkhYhCQuPjZanRSGKyo0aZezcZfSKWnhQxT8oVXaJIXjQs55LoPf B+9LxHjqdSFTbMecw8zrIpdBQbb2X2LWbJGrK/J9RxVDyaUxZ13sqMVIaND9pJ5itb VMVCRx0il+Sn9gsKsp10EdKjjBiXGn726PLL5jN+kNMYFsGBdQa913euX1X5EJXoan 1v+eYMzUPVK6sPwLag3hdN+Ifls9mGJYCJMhPwJl27YHumi28OjIEm+xhopWuyQXHa mnWutRkq/oYZw== From: Andreas Hindborg Date: Thu, 04 Jun 2026 21:58:13 +0200 Subject: [PATCH v4 07/11] rust: xarray: add entry API Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260604-xarray-entry-send-v4-7-965f6028790e@kernel.org> References: <20260604-xarray-entry-send-v4-0-965f6028790e@kernel.org> In-Reply-To: <20260604-xarray-entry-send-v4-0-965f6028790e@kernel.org> To: Miguel Ojeda , Alex Gaynor , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Andrew Morton , Christoph Lameter , David Rientjes , Roman Gushchin , Tamir Duberstein , Boqun Feng , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Tamir Duberstein , Boqun Feng , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo Cc: Daniel Gomez , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=22322; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=nlvjywsgkgwKWYwmhZEaELFYOvxdO57Snukucz2qinw=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqIdjwnrGRdAGGtUGeZ9sdbYVzKA6crQiwDMufW SjZm0k1NeeJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaiHY8AAKCRD6UCkIqsW9 0AkTEACMpaYXBU4br85iiZ2DCHt3qwwPV4t9RoZdnqiKrdQNH8DH2W7F5AUdTt0DaNGLLQXZSrN 6eBYB7xHG3UsWSIEe9jcr9IZjRvPIEa82EbRfy7jB4DYXwKy2Sjm2TXqHR5BSVOUM2Y4Ybb5O/d Telr9uHs3oz9BR1XXDc0lnOeF4Np4DkFP843AUY0HZ2dLWDvTGoMAcrN4lc3WYT5iASf521VbAZ 03qXpt6OoHOIJ5pYpp3jBurvjfSRx5AJFti78bingHYBx09KRxqqLygCYROw384r2DHHEH/C2wu cra3mzNx4QVqCFXIRWjHAjSHIuwH+nYDgpTHmU8O6AzazYDZCNaOj9YJ4j3m2HAwVHtD8x91PFw DK7iwquwS6uNjq1I0otFGt2vkxdZxQHICZ4kSnYGJRJrrxIuHv9GhPVxfHNab3yp3PFtbnu5Wiz 4h9UoMRMDwJuuY9A1LOiyNuO6zOQndYQP+9dsCSuoIUwsi/cqnJofOs6ZwRPXKFjo0Yw9LL20nn cCm9HA+tQ+MmXJyE/3PVg7mphoyE2uFtmpeEINysjxc7L6vdkRTwDcxtNRLnpye/n1QE+N+qy8R ISvMxcJnsSOlFNJ9gLH9Guwi90js4FM9DBzmGgX7ekKkYtFbhSY50x/XP5Sv+FqLYO/KvzqPnGa cnPmvw53r9xcw6g== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add an Entry API for XArray that provides ergonomic access to array slots that may be vacant or occupied. The API follows the pattern of Rust's standard library HashMap entry API, allowing efficient conditional insertion and modification of entries. The implementation uses the XArray state API (`xas_*` functions) for efficient operations without requiring multiple lookups. Helper functions are added to rust/helpers/xarray.c to wrap C macros that are not directly accessible from Rust. Also update MAINTAINERS to cover the new rust files. Signed-off-by: Andreas Hindborg --- MAINTAINERS | 1 + rust/helpers/xarray.c | 17 ++ rust/kernel/xarray.rs | 131 +++++++++++++++ rust/kernel/xarray/entry.rs | 385 ++++++++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 534 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 882214b0e7db..8bc0f7d95dce 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -28984,6 +28984,7 @@ B: https://github.com/Rust-for-Linux/linux/issues C: https://rust-for-linux.zulipchat.com T: git https://github.com/Rust-for-Linux/linux.git xarray-next F: rust/kernel/xarray.rs +F: rust/kernel/xarray/ =20 XBOX DVD IR REMOTE M: Benjamin Valentin diff --git a/rust/helpers/xarray.c b/rust/helpers/xarray.c index 08979b304341..19fce6862c78 100644 --- a/rust/helpers/xarray.c +++ b/rust/helpers/xarray.c @@ -26,3 +26,20 @@ __rust_helper void rust_helper_xa_unlock(struct xarray *= xa) { return xa_unlock(xa); } + +void *rust_helper_xas_result(struct xa_state *xas, void *curr) +{ + if (xa_err(xas->xa_node)) + curr =3D xas->xa_node; + return curr; +} + +void *rust_helper_xa_zero_to_null(void *entry) +{ + return xa_is_zero(entry) ? NULL : entry; +} + +int rust_helper_xas_error(const struct xa_state *xas) +{ + return xas_error(xas); +} diff --git a/rust/kernel/xarray.rs b/rust/kernel/xarray.rs index 7efa8a36d559..f6d5e5908c8b 100644 --- a/rust/kernel/xarray.rs +++ b/rust/kernel/xarray.rs @@ -13,11 +13,17 @@ NonNull, // }, }; +pub use entry::{ + Entry, + OccupiedEntry, + VacantEntry, // +}; use kernel::{ alloc, bindings, build_assert, // error::{ + to_result, Error, Result, // }, @@ -231,6 +237,35 @@ pub fn get_mut(&mut self, index: usize) -> Option> { Some(unsafe { T::borrow_mut(ptr.as_ptr()) }) } =20 + /// Gets an entry for the specified index, which can be vacant or occu= pied. + /// + /// # Examples + /// + /// ``` + /// # use kernel::{prelude::*, xarray::{AllocKind, XArray, Entry}}; + /// let mut xa =3D KBox::pin_init(XArray::>::new(AllocKind::= Alloc), GFP_KERNEL)?; + /// let mut guard =3D xa.lock(); + /// + /// assert!(guard.get(42).is_none()); + /// + /// match guard.entry(42) { + /// Entry::Vacant(entry) =3D> { + /// entry.insert(KBox::new(0x1337u32, GFP_KERNEL)?)?; + /// } + /// Entry::Occupied(_) =3D> unreachable!("We did not insert an ent= ry yet"), + /// } + /// + /// assert_eq!(guard.get(42), Some(&0x1337)); + /// + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + pub fn entry<'b>(&'b mut self, index: usize) -> Entry<'a, 'b, T> { + match self.load(index) { + None =3D> Entry::Vacant(VacantEntry::new(self, index)), + Some(ptr) =3D> Entry::Occupied(OccupiedEntry::new(self, index,= ptr)), + } + } + fn load_next(&self, index: usize) -> Option<(usize, NonNull)> { XArrayState::new(self, index).load_next() } @@ -292,6 +327,72 @@ pub fn find_next_mut(&mut self, index: usize) -> Optio= n<(usize, T::BorrowedMut<' .map(move |(index, ptr)| (index, unsafe { T::borrow_mut(ptr.as= _ptr()) })) } =20 + /// Finds the next occupied entry starting from the given index. + /// + /// # Examples + /// + /// ``` + /// # use kernel::{prelude::*, xarray::{AllocKind, XArray}}; + /// let mut xa =3D KBox::pin_init(XArray::>::new(AllocKind::= Alloc), GFP_KERNEL)?; + /// let mut guard =3D xa.lock(); + /// + /// guard.store(10, KBox::new(10u32, GFP_ATOMIC)?, GFP_ATOMIC)?; + /// guard.store(20, KBox::new(20u32, GFP_ATOMIC)?, GFP_ATOMIC)?; + /// + /// if let Some(entry) =3D guard.find_next_entry(5) { + /// assert_eq!(entry.index(), 10); + /// let value =3D entry.remove(); + /// assert_eq!(*value, 10); + /// } + /// + /// assert_eq!(guard.get(10), None); + /// + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + pub fn find_next_entry<'b>(&'b mut self, index: usize) -> Option> { + let mut state =3D XArrayState::new(self, index); + let (_, ptr) =3D state.load_next()?; + Some(OccupiedEntry { state, ptr }) + } + + /// Finds the next occupied entry starting at the given index, wrappin= g around. + /// + /// Searches for an entry starting at `index` up to the maximum index.= If no entry + /// is found, wraps around and searches from index 0 up to `index`. + /// + /// # Examples + /// + /// ``` + /// # use kernel::{prelude::*, xarray::{AllocKind, XArray}}; + /// let mut xa =3D KBox::pin_init(XArray::>::new(AllocKind::= Alloc), GFP_KERNEL)?; + /// let mut guard =3D xa.lock(); + /// + /// guard.store(100, KBox::new(42u32, GFP_ATOMIC)?, GFP_ATOMIC)?; + /// let entry =3D guard.find_next_entry_circular(101); + /// assert_eq!(entry.map(|e| e.index()), Some(100)); + /// + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + pub fn find_next_entry_circular<'b>( + &'b mut self, + index: usize, + ) -> Option> { + let mut state =3D XArrayState::new(self, index); + + // SAFETY: `state.state` is a valid `xa_state` by the type invaria= nt of `XArrayState`. By + // the same invariant, `state.state.xa` aliases the xarray reachab= le through `state.guard`, + // whose lock we hold. + let ptr =3D NonNull::new(unsafe { bindings::xas_find(&mut state.st= ate, usize::MAX) }) + .or_else(|| { + state.state.xa_node =3D bindings::XAS_RESTART as *mut bind= ings::xa_node; + state.state.xa_index =3D 0; + // SAFETY: As above. + NonNull::new(unsafe { bindings::xas_find(&mut state.state,= index) }) + })?; + + Some(OccupiedEntry { state, ptr }) + } + /// Removes and returns the element at the given index. pub fn remove(&mut self, index: usize) -> Option { // SAFETY: @@ -429,8 +530,38 @@ fn load_next(&mut self) -> Option<(usize, NonNull)> { let ptr =3D unsafe { bindings::xas_find(&raw mut self.state, usize= ::MAX) }; NonNull::new(ptr).map(|ptr| (self.state.xa_index, ptr)) } + + fn status(&self) -> Result { + // SAFETY: `self.state` is a valid `xa_state` by the type invarian= t. + to_result(unsafe { bindings::xas_error(&self.state) }) + } + + fn insert(&mut self, value: R::Value) -> Result<*mut c_void, StoreErro= r> { + let new =3D R::Value::into_foreign(value).cast(); + + // SAFETY: `self.state` is a valid `xa_state` by the type invarian= t. By the same + // invariant, `self.state.xa` aliases the xarray reachable through= `self.guard`, + // whose lock we hold. `new` came from `R::Value::into_foreign`. + unsafe { bindings::xas_store(&mut self.state, new) }; + + self.status().map(|()| new).map_err(|error| { + // SAFETY: `new` came from `R::Value::into_foreign` and `xas_s= tore` does not take + // ownership of the value on error. + let value =3D unsafe { R::Value::from_foreign(new) }; + StoreError { value, error } + }) + } } =20 +impl<'a, 'b, T: ForeignOwnable> XArrayState<&'b mut Guard<'a, T>> { + /// Consumes `self` and returns the inner `&mut Guard`. + pub(crate) fn into_guard(self) -> &'b mut Guard<'a, T> { + self.guard + } +} + +mod entry; + // SAFETY: `XArray` has no shared mutable state so it is `Send` iff `T`= is `Send`. unsafe impl Send for XArray {} =20 diff --git a/rust/kernel/xarray/entry.rs b/rust/kernel/xarray/entry.rs new file mode 100644 index 000000000000..5ad79a499156 --- /dev/null +++ b/rust/kernel/xarray/entry.rs @@ -0,0 +1,385 @@ +// SPDX-License-Identifier: GPL-2.0 + +use super::{ + Guard, + StoreError, + XArrayState, // +}; +use core::ptr::NonNull; +use kernel::{ + prelude::*, + types::ForeignOwnable, // +}; + +/// Represents either a vacant or occupied entry in an XArray. +pub enum Entry<'a, 'b, T: ForeignOwnable> { + /// A vacant entry that can have a value inserted. + Vacant(VacantEntry<'a, 'b, T>), + /// An occupied entry containing a value. + Occupied(OccupiedEntry<'a, 'b, T>), +} + +impl Entry<'_, '_, T> { + /// Returns true if this entry is occupied. + /// + /// # Examples + /// + /// ``` + /// # use kernel::{prelude::*, xarray::{AllocKind, XArray, Entry}}; + /// let mut xa =3D KBox::pin_init(XArray::>::new(AllocKind::= Alloc), GFP_KERNEL)?; + /// let mut guard =3D xa.lock(); + /// + /// + /// let entry =3D guard.entry(42); + /// assert_eq!(entry.is_occupied(), false); + /// + /// guard.store(42, KBox::new(0x1337u32, GFP_ATOMIC)?, GFP_ATOMIC)?; + /// let entry =3D guard.entry(42); + /// assert_eq!(entry.is_occupied(), true); + /// + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + pub fn is_occupied(&self) -> bool { + matches!(self, Entry::Occupied(_)) + } +} + +/// A view into a vacant entry in an XArray. +pub struct VacantEntry<'a, 'b, T: ForeignOwnable> { + state: XArrayState<&'b mut Guard<'a, T>>, +} + +impl<'a, 'b, T> VacantEntry<'a, 'b, T> +where + T: ForeignOwnable, +{ + pub(crate) fn new(guard: &'b mut Guard<'a, T>, index: usize) -> Self { + Self { + state: XArrayState::new(guard, index), + } + } + + /// Consumes the entry and returns a mutable reference to the underlyi= ng + /// guard. + /// + /// This releases the slot reservation but retains the lock guard so t= he + /// caller can perform further operations on the array. + pub fn into_guard(self) -> &'b mut Guard<'a, T> { + self.state.into_guard() + } + + /// Inserts a value into this vacant entry. + /// + /// Returns a reference to the newly inserted value. + /// + /// - This method will fail if the nodes on the path to the index + /// represented by this entry are not present in the XArray. + /// - This method will not drop the XArray lock. + /// + /// + /// # Examples + /// + /// ``` + /// # use kernel::{prelude::*, xarray::{AllocKind, XArray, Entry}}; + /// let mut xa =3D KBox::pin_init(XArray::>::new(AllocKind::= Alloc), GFP_KERNEL)?; + /// let mut guard =3D xa.lock(); + /// + /// assert_eq!(guard.get(42), None); + /// + /// if let Entry::Vacant(entry) =3D guard.entry(42) { + /// let value =3D KBox::new(0x1337u32, GFP_ATOMIC)?; + /// let borrowed =3D entry.insert(value)?; + /// assert_eq!(*borrowed, 0x1337); + /// } + /// + /// assert_eq!(guard.get(42).copied(), Some(0x1337)); + /// + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + pub fn insert(mut self, value: T) -> Result, StoreE= rror> { + let new =3D self.state.insert(value)?; + + // SAFETY: `new` came from `T::into_foreign`. The entry has exclus= ive + // ownership of `new` as it holds a mutable reference to `Guard`. + Ok(unsafe { T::borrow_mut(new) }) + } + + /// Inserts a value and returns an occupied entry representing the new= ly inserted value. + /// + /// - This method will fail if the nodes on the path to the index + /// represented by this entry are not present in the XArray. + /// - This method will not drop the XArray lock. + /// + /// # Examples + /// + /// ``` + /// # use kernel::{prelude::*, xarray::{AllocKind, XArray, Entry}}; + /// let mut xa =3D KBox::pin_init(XArray::>::new(AllocKind::= Alloc), GFP_KERNEL)?; + /// let mut guard =3D xa.lock(); + /// + /// assert_eq!(guard.get(42), None); + /// + /// if let Entry::Vacant(entry) =3D guard.entry(42) { + /// let value =3D KBox::new(0x1337u32, GFP_ATOMIC)?; + /// let occupied =3D entry.insert_entry(value)?; + /// assert_eq!(occupied.index(), 42); + /// } + /// + /// assert_eq!(guard.get(42).copied(), Some(0x1337)); + /// + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + pub fn insert_entry(mut self, value: T) -> Result, StoreError> { + let new =3D self.state.insert(value)?; + + Ok(OccupiedEntry::<'a, 'b, T> { + state: self.state, + // SAFETY: `new` came from `T::into_foreign` and is guaranteed= non-null. + ptr: unsafe { core::ptr::NonNull::new_unchecked(new) }, + }) + } + + /// Returns the index of this vacant entry. + /// + /// # Examples + /// + /// ``` + /// # use kernel::{prelude::*, xarray::{AllocKind, XArray, Entry}}; + /// let mut xa =3D KBox::pin_init(XArray::>::new(AllocKind::= Alloc), GFP_KERNEL)?; + /// let mut guard =3D xa.lock(); + /// + /// assert_eq!(guard.get(42), None); + /// + /// if let Entry::Vacant(entry) =3D guard.entry(42) { + /// assert_eq!(entry.index(), 42); + /// } + /// + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + pub fn index(&self) -> usize { + self.state.state.xa_index + } +} + +/// A view into an occupied entry in an XArray. +pub struct OccupiedEntry<'a, 'b, T: ForeignOwnable> { + pub(crate) state: XArrayState<&'b mut Guard<'a, T>>, + pub(crate) ptr: NonNull, +} + +impl<'a, 'b, T> OccupiedEntry<'a, 'b, T> +where + T: ForeignOwnable, +{ + pub(crate) fn new(guard: &'b mut Guard<'a, T>, index: usize, ptr: NonN= ull) -> Self { + Self { + state: XArrayState::new(guard, index), + ptr, + } + } + + /// Consumes the entry and returns a mutable reference to the underlyi= ng + /// guard. + /// + /// This releases the borrow on the entry's slot but retains the lock + /// guard so the caller can perform further operations on the array. + pub fn into_guard(self) -> &'b mut Guard<'a, T> { + self.state.into_guard() + } + + /// Removes the value from this occupied entry and returns it, consumi= ng the entry. + /// + /// # Examples + /// + /// ``` + /// # use kernel::{prelude::*, xarray::{AllocKind, XArray, Entry}}; + /// let mut xa =3D KBox::pin_init(XArray::>::new(AllocKind::= Alloc), GFP_KERNEL)?; + /// let mut guard =3D xa.lock(); + /// + /// guard.store(42, KBox::new(0x1337u32, GFP_ATOMIC)?, GFP_ATOMIC)?; + /// assert_eq!(guard.get(42).copied(), Some(0x1337)); + /// + /// if let Entry::Occupied(entry) =3D guard.entry(42) { + /// let value =3D entry.remove(); + /// assert_eq!(*value, 0x1337); + /// } + /// + /// assert_eq!(guard.get(42), None); + /// + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + pub fn remove(mut self) -> T { + // SAFETY: `self.state.state` is properly initialized and valid fo= r XAS operations. + let ptr =3D unsafe { + bindings::xas_result( + &mut self.state.state, + bindings::xa_zero_to_null(bindings::xas_store( + &mut self.state.state, + core::ptr::null_mut(), + )), + ) + }; + + // SAFETY: `ptr` is a valid return value from xas_result. + let errno =3D unsafe { bindings::xa_err(ptr) }; + + // NOTE: Storing NULL to an occupied slot never fails. This is by = design + // of the xarray data structure. If a slot is occupied, a store is= a + // simple pointer swap. + debug_assert!(errno =3D=3D 0); + + // SAFETY: + // - `ptr` came from `T::into_foreign`. + // - As this method takes self by value, the lifetimes of any [`T:= :Borrowed`] and + // [`T::BorrowedMut`] we have created must have ended. + unsafe { T::from_foreign(ptr.cast()) } + } + + /// Returns the index of this occupied entry. + /// + /// # Examples + /// + /// ``` + /// # use kernel::{prelude::*, xarray::{AllocKind, XArray, Entry}}; + /// let mut xa =3D KBox::pin_init(XArray::>::new(AllocKind::= Alloc), GFP_KERNEL)?; + /// let mut guard =3D xa.lock(); + /// + /// guard.store(42, KBox::new(0x1337u32, GFP_ATOMIC)?, GFP_ATOMIC)?; + /// + /// if let Entry::Occupied(entry) =3D guard.entry(42) { + /// assert_eq!(entry.index(), 42); + /// } + /// + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + pub fn index(&self) -> usize { + self.state.state.xa_index + } + + /// Replaces the value in this occupied entry and returns the old valu= e. + /// + /// # Examples + /// + /// ``` + /// # use kernel::{prelude::*, xarray::{AllocKind, XArray, Entry}}; + /// let mut xa =3D KBox::pin_init(XArray::>::new(AllocKind::= Alloc), GFP_KERNEL)?; + /// let mut guard =3D xa.lock(); + /// + /// guard.store(42, KBox::new(0x1337u32, GFP_ATOMIC)?, GFP_ATOMIC)?; + /// + /// if let Entry::Occupied(mut entry) =3D guard.entry(42) { + /// let new_value =3D KBox::new(0x9999u32, GFP_ATOMIC)?; + /// let old_value =3D entry.insert(new_value); + /// assert_eq!(*old_value, 0x1337); + /// } + /// + /// assert_eq!(guard.get(42).copied(), Some(0x9999)); + /// + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + pub fn insert(&mut self, value: T) -> T { + let new =3D T::into_foreign(value).cast(); + // SAFETY: `new` came from `T::into_foreign` and is guaranteed non= -null. + self.ptr =3D unsafe { NonNull::new_unchecked(new) }; + + // SAFETY: `self.state.state` is properly initialized and valid fo= r XAS operations. + let old =3D unsafe { + bindings::xas_result( + &mut self.state.state, + bindings::xa_zero_to_null(bindings::xas_store(&mut self.st= ate.state, new)), + ) + }; + + // SAFETY: `old` is a valid return value from xas_result. + let errno =3D unsafe { bindings::xa_err(old) }; + + // NOTE: Storing NULL to an occupied slot never fails. This is by = design + // of the xarray data structure. If a slot is occupied, a store is= a + // simple pointer swap. + debug_assert!(errno =3D=3D 0); + + // SAFETY: + // - `ptr` came from `T::into_foreign`. + // - As this method takes self by value, the lifetimes of any [`T:= :Borrowed`] and + // [`T::BorrowedMut`] we have created must have ended. + unsafe { T::from_foreign(old) } + } + + /// Converts this occupied entry into a mutable reference to the value= in the slot represented + /// by the entry. + /// + /// # Examples + /// + /// ``` + /// # use kernel::{prelude::*, xarray::{AllocKind, XArray, Entry}}; + /// let mut xa =3D KBox::pin_init(XArray::>::new(AllocKind::= Alloc), GFP_KERNEL)?; + /// let mut guard =3D xa.lock(); + /// + /// guard.store(42, KBox::new(0x1337u32, GFP_ATOMIC)?, GFP_ATOMIC)?; + /// + /// if let Entry::Occupied(entry) =3D guard.entry(42) { + /// let value_ref =3D entry.into_mut(); + /// *value_ref =3D 0x9999; + /// } + /// + /// assert_eq!(guard.get(42).copied(), Some(0x9999)); + /// + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + pub fn into_mut(self) -> T::BorrowedMut<'b> { + // SAFETY: `ptr` came from `T::into_foreign`. + unsafe { T::borrow_mut(self.ptr.as_ptr()) } + } + + /// Swaps the value in this entry with the provided value. + /// + /// Returns the old value that was in the entry. + /// + /// # Examples + /// + /// ``` + /// # use kernel::{prelude::*, xarray::{AllocKind, XArray, Entry}}; + /// let mut xa =3D KBox::pin_init(XArray::>::new(AllocKind::= Alloc), GFP_KERNEL)?; + /// let mut guard =3D xa.lock(); + /// + /// guard.store(42, KBox::new(100u32, GFP_ATOMIC)?, GFP_ATOMIC)?; + /// + /// if let Entry::Occupied(mut entry) =3D guard.entry(42) { + /// let mut other =3D 200u32; + /// entry.swap(&mut other); + /// assert_eq!(other, 100); + /// assert_eq!(*entry, 200); + /// } + /// + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + pub fn swap(&mut self, other: &mut U) + where + T: for<'c> ForeignOwnable =3D &'c U, BorrowedMut<'c> = =3D &'c mut U>, + { + use core::ops::DerefMut; + core::mem::swap(self.deref_mut(), other); + } +} + +impl core::ops::Deref for OccupiedEntry<'_, '_, T> +where + T: for<'a> ForeignOwnable =3D &'a U, BorrowedMut<'a> =3D = &'a mut U>, +{ + type Target =3D U; + + fn deref(&self) -> &Self::Target { + // SAFETY: `ptr` came from `T::into_foreign`. + unsafe { T::borrow(self.ptr.as_ptr()) } + } +} + +impl core::ops::DerefMut for OccupiedEntry<'_, '_, T> +where + T: for<'a> ForeignOwnable =3D &'a U, BorrowedMut<'a> =3D = &'a mut U>, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + // SAFETY: `ptr` came from `T::into_foreign`. + unsafe { T::borrow_mut(self.ptr.as_ptr()) } + } +} --=20 2.51.2 From nobody Mon Jun 8 07:21:50 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 1CD333C4B83; Thu, 4 Jun 2026 19:59:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780603172; cv=none; b=TXYranRV/lVeTAST8/qqV3PkYz5McqX1YDKfXCHK+gkMU+H2dz1l1fR9YIEyJ33awqHe+4p6fE2gkG9KgnVnylSbCfY7tc9dDuoxSgTBwvXjoxfD7kw0caioVKPwLLfvhnduwshvhMtnRhiyuRZEreTPcY4sm+3Sa2wNXYBmZA4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780603172; c=relaxed/simple; bh=viLP6+3hpO8mvWihp4DUk3gqlSHe9yjJWhzRprF92ok=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=LWtdW+IRrzCujvZbmSmpAZncYWI9X0pRgKvHCVknAJw/KRw5GfukuW1CQ8OmKXf+qY0d0sIGKFhMfljqN9+vriwy4QkUsO+7BsBSsE4SDOro1J52sNdlqpCV8tG6LVM+Sy0IGlx5f6F5mM/7JASO0Eq9uGYhP95e6AdyH/p5+Jo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Xvg0uI4y; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Xvg0uI4y" Received: by smtp.kernel.org (Postfix) with ESMTPSA id CC1241F00893; Thu, 4 Jun 2026 19:59:23 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1780603169; bh=HkrPB3YSV5eoOFoyhfAfX4Z/mUMvl7lJVOyi27h88P0=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=Xvg0uI4yPw6dAeAd1KVMxqccdfEKlCn1/n5arQlO8nEPUDH8u7UqR9ghH/2PeGv+r qEfEFG9BYEKz6p3dipOvSO/W0pGjwtRUCS2++6rb7NZjbIR8QViJYpM1ZjvhBYHLKR eqW2UnZt+Xm0qEzAJX+4K8UbqY0itnJNK6UQBCCn67E93k28mbZJ4OSi1OA7wI1col oBvtYzm5eMyZjH7bo8CYPS6r52xU5OsozrDZGRLME6JOe4Mbmo3y8Uw+AVRK98fifU yyR0iJ1d3/+2vl5N90alJpzxFwA7NWtr3kSYtGFOUqrMdyUe75DhAmQwgiVc56m+Gm O+oZ5eAygN60A== From: Andreas Hindborg Date: Thu, 04 Jun 2026 21:58:14 +0200 Subject: [PATCH v4 08/11] rust: mm: add abstractions for allocating from a `sheaf` Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260604-xarray-entry-send-v4-8-965f6028790e@kernel.org> References: <20260604-xarray-entry-send-v4-0-965f6028790e@kernel.org> In-Reply-To: <20260604-xarray-entry-send-v4-0-965f6028790e@kernel.org> To: Miguel Ojeda , Alex Gaynor , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Andrew Morton , Christoph Lameter , David Rientjes , Roman Gushchin , Tamir Duberstein , Boqun Feng , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Tamir Duberstein , Boqun Feng , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo Cc: Daniel Gomez , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg , "Matthew Wilcox (Oracle)" X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=16725; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=viLP6+3hpO8mvWihp4DUk3gqlSHe9yjJWhzRprF92ok=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqIdjwZfTi7enAUEzhtZXOHEZYr5m9Nt438lwNU VMFyGhts7yJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaiHY8AAKCRD6UCkIqsW9 0GU7EACirZDEMZs6fIt/24kf/2CBcsMDTt+Qdsk1uNoEQxOdA7ABsHbpu83iwz3IyTQOtBrRcDk +QoNEx+h5dGozUYlbEnTTBzQMYVRLdJfJ5k38uoolzf/2ATvetpoPCDN5upxezVuxev6RsBbn9g hovzXCL/Y/ff1NfsPTM61C7B0tGI4d1AFJh1EZppxvGGS3JlOtcvguLchPkZwiairkgGvrQrnL7 HtjpoMmgkdpgNbPjUjn9HE8BN+dYpVAuw7CVox90PVtp4kf2D/AMgYzNrQqmurCbfWrwlotDUb8 hYN8awj8YzcAdfPjW+nQpQ4sxXCVAMr4A+3vv1xHpOa0Q4VI7UW0t5FOf4njnFtNFH04vjDttes tVp+BHeyc2Z4tpNL3tPRNYvDhftihFb6Y/J3pa4ezo6pamGYMLCiXBTx9LxITFxBUkZawS23wKf hE6N4irm3VpTOj9BDc0e0PoNVfRUn+ReJhGs+jwIb4AuSLCjAJnxH+Fd1j5D9CkjHo9bZwIzQXi P6TaHogbHIGaRH5/gc5ay8Qb04IDMe7IEKEhyV2qNcUnU5LNXYO4VzqRcLgMfLO/RkOkUvfB+OS 347z8fQ8FU8pIaC5XwyxpdHjgsF5Po5DNNO9EQ1VcWuh43Umoi0LxbDvQajcTOBmgO6rbcCYpCY cu+X3R/SO5m/5Tw== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add Rust APIs for allocating objects from a `sheaf`. Introduce a reduced abstraction `KMemCacheInit` for `struct kmem_cache` to support management of the `Sheaf`s. Initialize objects using in-place initialization when objects are allocated from a `Sheaf`. This is different from C which tends to do some initialization when the cache is filled. This approach is chosen because there is no destructor/drop capability in `struct kmem_cache` that can be invoked when the cache is dropped. Cc: Vlastimil Babka Cc: "Liam R. Howlett" Cc: "Matthew Wilcox (Oracle)" Cc: Lorenzo Stoakes Cc: linux-mm@kvack.org Signed-off-by: Andreas Hindborg --- rust/kernel/mm.rs | 1 + rust/kernel/mm/sheaf.rs | 407 ++++++++++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 408 insertions(+) diff --git a/rust/kernel/mm.rs b/rust/kernel/mm.rs index 4764d7b68f2a..1aa44424b0d5 100644 --- a/rust/kernel/mm.rs +++ b/rust/kernel/mm.rs @@ -18,6 +18,7 @@ }; use core::{ops::Deref, ptr::NonNull}; =20 +pub mod sheaf; pub mod virt; use virt::VmaRef; =20 diff --git a/rust/kernel/mm/sheaf.rs b/rust/kernel/mm/sheaf.rs new file mode 100644 index 000000000000..f23fdaa2dbc3 --- /dev/null +++ b/rust/kernel/mm/sheaf.rs @@ -0,0 +1,407 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Slub allocator sheaf abstraction. +//! +//! Sheaves are percpu array-based caching layers for the slub allocator. +//! They provide a mechanism for pre-allocating objects that can later +//! be retrieved without risking allocation failure, making them useful in +//! contexts where memory allocation must be guaranteed to succeed. +//! +//! The term "sheaf" is the english word for a bundle of straw. In this co= ntext +//! it means a bundle of pre-allocated objects. A per-NUMA-node cache of s= heaves +//! is called a "barn". Because you store your sheafs in barns. +//! +//! # Use cases +//! +//! Sheaves are particularly useful when: +//! +//! - Allocations must be guaranteed to succeed in a restricted context (e= .g., +//! while holding locks or in atomic context). +//! - Multiple allocations need to be performed as a batch operation. +//! - Fast-path allocation performance is critical, as sheaf allocations a= void +//! atomic operations by using local locks with preemption disabled. +//! +//! # Architecture +//! +//! The sheaf system consists of three main components: +//! +//! - [`KMemCache`]: A slab cache configured with sheaf support. +//! - [`Sheaf`]: A pre-filled container of objects from a specific cache. +//! - [`SBox`]: An owned allocation from a sheaf, similar to a `Box`. +//! +//! # Example +//! +//! ``` +//! use kernel::c_str; +//! use kernel::mm::sheaf::{KMemCache, KMemCacheInit, Sheaf, SBox}; +//! use kernel::prelude::*; +//! +//! struct MyObject { +//! value: u32, +//! } +//! +//! impl KMemCacheInit for MyObject { +//! fn init() -> impl Init { +//! init!(MyObject { value: 0 }) +//! } +//! } +//! +//! // Create a cache with sheaf capacity of 16 objects. +//! let cache =3D KMemCache::::new(c_str!("my_cache"), 16)?; +//! +//! // Pre-fill a sheaf with 8 objects. +//! let mut sheaf =3D cache.as_arc_borrow().sheaf(8, GFP_KERNEL)?; +//! +//! // Allocations from the sheaf are guaranteed to succeed until empty. +//! let obj =3D sheaf.alloc().unwrap(); +//! +//! // Return the sheaf when done, attempting to refill it. +//! sheaf.return_refill(GFP_KERNEL); +//! # Ok::<(), Error>(()) +//! ``` +//! +//! # Constraints +//! +//! - Sheaves are slower when `CONFIG_SLUB_TINY` or `CONFIG_SLUB_DEBUG` is +//! enabled due to cpu sheaves being disabled. All prefilled sheaves bec= ome +//! "oversize" and go through a slower allocation path. +//! - The sheaf capacity is fixed at cache creation time. + +use core::{ + convert::Infallible, + marker::PhantomData, + ops::{Deref, DerefMut}, + ptr::NonNull, +}; + +use kernel::prelude::*; + +use crate::sync::{Arc, ArcBorrow}; + +/// A slab cache with sheaf support. +/// +/// This type wraps a kernel `kmem_cache` configured with a sheaf capacity, +/// enabling pre-allocation of objects via [`Sheaf`]. +/// +/// For now, this type only exists for sheaf management. +/// +/// # Type parameter +/// +/// - `T`: The type of objects managed by this cache. Must implement +/// [`KMemCacheInit`] to provide initialization logic for new allocation= s. +/// +/// # Invariants +/// +/// - `cache` is a valid pointer to a `kmem_cache` created with +/// `__kmem_cache_create_args`. +/// - The cache is valid for the lifetime of this struct. +pub struct KMemCache> { + cache: NonNull, + _p: PhantomData, +} + +impl> KMemCache { + /// Creates a new slab cache with sheaf support. + /// + /// Creates a kernel slab cache for objects of type `T` with the speci= fied + /// sheaf capacity. The cache uses the provided `name` for identificat= ion + /// in `/sys/kernel/slab/` and debugging output. + /// + /// # Arguments + /// + /// - `name`: A string identifying the cache. This name appears in sys= fs and + /// debugging output. + /// - `sheaf_capacity`: The maximum number of objects a sheaf from this + /// cache can hold. A capacity of zero disables sheaf support. + /// + /// # Errors + /// + /// Returns an error if: + /// + /// - The cache could not be created due to memory pressure. + /// - The size of `T` cannot be represented as a `c_uint`. + pub fn new(name: &CStr, sheaf_capacity: u32) -> Result> + where + T: KMemCacheInit, + { + let flags =3D 0; + let mut args: bindings::kmem_cache_args =3D pin_init::zeroed(); + args.sheaf_capacity =3D sheaf_capacity; + + // NOTE: We are not initializing at object allocation time, because + // there is no matching teardown function on the C side machinery. + args.ctor =3D None; + + // SAFETY: `name` is a valid C string, `args` is properly initiali= zed, + // and the size of `T` has been validated to fit in a `c_uint`. + let ptr =3D unsafe { + bindings::__kmem_cache_create_args( + name.as_char_ptr(), + core::mem::size_of::().try_into()?, + &mut args, + flags, + ) + }; + + // INVARIANT: `ptr` was returned by `__kmem_cache_create_args` and= is + // non-null (checked below). The cache is valid until + // `kmem_cache_destroy` is called in `Drop`. + Ok(Arc::new( + Self { + cache: NonNull::new(ptr).ok_or(ENOMEM)?, + _p: PhantomData, + }, + GFP_KERNEL, + )?) + } + + /// Creates a pre-filled sheaf from this cache. + /// + /// Allocates a sheaf and pre-fills it with `size` objects. Once creat= ed, + /// allocations from the sheaf via [`Sheaf::alloc`] are guaranteed to + /// succeed until the sheaf is depleted. + /// + /// # Arguments + /// + /// - `size`: The number of objects to pre-allocate. Must not exceed t= he + /// cache's `sheaf_capacity`. + /// - `gfp`: Allocation flags controlling how memory is obtained. Use + /// [`GFP_KERNEL`] for normal allocations that may sleep, or + /// [`GFP_NOWAIT`] for non-blocking allocations. + /// + /// # Errors + /// + /// Returns [`ENOMEM`] if the sheaf or its objects could not be alloca= ted. + /// + /// # Warnings + /// + /// The kernel will warn if `size` exceeds `sheaf_capacity`. + pub fn sheaf( + self: ArcBorrow<'_, Self>, + size: usize, + gfp: kernel::alloc::Flags, + ) -> Result> { + // SAFETY: `self.as_raw()` returns a valid cache pointer, and `siz= e` + // has been validated to fit in a `c_uint`. + let ptr =3D unsafe { + bindings::kmem_cache_prefill_sheaf(self.as_raw(), gfp.as_raw()= , size.try_into()?) + }; + + // INVARIANT: `ptr` was returned by `kmem_cache_prefill_sheaf` and= is + // non-null (checked below). `cache` is the cache from which this = sheaf + // was created. `dropped` is false since the sheaf has not been re= turned. + Ok(Sheaf { + sheaf: NonNull::new(ptr).ok_or(ENOMEM)?, + cache: self.into(), + dropped: false, + }) + } + + fn as_raw(&self) -> *mut bindings::kmem_cache { + self.cache.as_ptr() + } +} + +impl> Drop for KMemCache { + fn drop(&mut self) { + // SAFETY: `self.as_raw()` returns a valid cache pointer that was + // created by `__kmem_cache_create_args`. As all objects allocated= from + // this hold a reference on `self`, they must have been dropped fo= r this + // `drop` method to execute. + unsafe { bindings::kmem_cache_destroy(self.as_raw()) }; + } +} + +/// Trait for types that can be initialized in a slab cache. +/// +/// This trait provides the initialization logic for objects allocated fro= m a +/// [`KMemCache`]. When the slab allocator creates new objects, it invokes= the +/// constructor to ensure objects are in a valid initial state. +/// +/// # Implementation +/// +/// Implementors must provide [`init`](KMemCacheInit::init), which returns +/// a in-place initializer for the type. +/// +/// # Example +/// +/// ``` +/// use kernel::mm::sheaf::KMemCacheInit; +/// use kernel::prelude::*; +/// +/// struct MyData { +/// counter: u32, +/// name: [u8; 16], +/// } +/// +/// impl KMemCacheInit for MyData { +/// fn init() -> impl Init { +/// init!(MyData { +/// counter: 0, +/// name: [0; 16], +/// }) +/// } +/// } +/// ``` +pub trait KMemCacheInit { + /// Returns an initializer for creating new objects of type `T`. + /// + /// This method is called by the allocator's constructor to initialize= newly + /// allocated objects. The initializer should set all fields to their + /// default or initial values. + fn init() -> impl Init; +} + +/// A pre-filled container of slab objects. +/// +/// A sheaf holds a set of pre-allocated objects from a [`KMemCache`]. +/// Allocations from a sheaf are guaranteed to succeed until the sheaf is +/// depleted, making sheaves useful in contexts where allocation failure is +/// not acceptable. +/// +/// Sheaves provide faster allocation than direct allocation because they = use +/// local locks with preemption disabled rather than atomic operations. +/// +/// # Lifecycle +/// +/// Sheaves are created via [`KMemCache::sheaf`] and should be returned to= the +/// allocator when no longer needed via [`Sheaf::return_refill`]. If a she= af is +/// simply dropped, it is returned with `GFP_NOWAIT` flags, which may resu= lt in +/// the sheaf being flushed and freed rather than being cached for reuse. +/// +/// # Invariants +/// +/// - `sheaf` is a valid pointer to a `slab_sheaf` obtained from +/// `kmem_cache_prefill_sheaf`. +/// - `cache` is the cache from which this sheaf was created. +/// - `dropped` tracks whether the sheaf has been explicitly returned. +pub struct Sheaf> { + sheaf: NonNull, + cache: Arc>, + dropped: bool, +} + +impl> Sheaf { + fn as_raw(&self) -> *mut bindings::slab_sheaf { + self.sheaf.as_ptr() + } + + /// Return the sheaf and try to refill using `flags`. + /// + /// If the sheaf cannot simply become the percpu spare sheaf, but ther= e's + /// space for a full sheaf in the barn, we try to refill the sheaf bac= k to + /// the cache's sheaf_capacity to avoid handling partially full sheave= s. + /// + /// If the refill fails because gfp is e.g. GFP_NOWAIT, or the barn is= full, + /// the sheaf is instead flushed and freed. + pub fn return_refill(mut self, flags: kernel::alloc::Flags) { + self.dropped =3D true; + // SAFETY: `self.cache.as_raw()` and `self.as_raw()` return valid + // pointers to the cache and sheaf respectively. + unsafe { + bindings::kmem_cache_return_sheaf(self.cache.as_raw(), flags.a= s_raw(), self.as_raw()) + }; + drop(self); + } + + /// Allocates an object from the sheaf. + /// + /// Returns a new [`SBox`] containing an initialized object, or [`None= `] + /// if the sheaf is depleted. Allocations are guaranteed to succeed as + /// long as the sheaf contains pre-allocated objects. + /// + /// The `gfp` flags passed to `kmem_cache_alloc_from_sheaf` are set to= zero, + /// meaning no additional flags like `__GFP_ZERO` or `__GFP_ACCOUNT` a= re + /// applied. + /// + /// The returned `T` is initialized as part of this function. + pub fn alloc(&mut self) -> Option> { + // SAFETY: `self.cache.as_raw()` and `self.as_raw()` return valid + // pointers. The function returns NULL when the sheaf is empty. + let ptr =3D unsafe { + bindings::kmem_cache_alloc_from_sheaf_noprof(self.cache.as_raw= (), 0, self.as_raw()) + }; + + // SAFETY: + // - `ptr` is a valid pointer as it was just returned by the cache. + // - The initializer is infallible, so an error is never returned. + unsafe { T::init().__init(ptr.cast()) }.expect("Initializer is inf= allible"); + + let ptr =3D NonNull::new(ptr.cast::())?; + + // INVARIANT: `ptr` was returned by `kmem_cache_alloc_from_sheaf_n= oprof` + // and initialized above. `cache` is the cache from which this obj= ect + // was allocated. The object remains valid until freed in `Drop`. + Some(SBox { + ptr, + cache: self.cache.clone(), + }) + } +} + +impl> Drop for Sheaf { + fn drop(&mut self) { + if !self.dropped { + // SAFETY: `self.cache.as_raw()` and `self.as_raw()` return va= lid + // pointers. Using `GFP_NOWAIT` because the drop may occur in a + // context where sleeping is not permitted. + unsafe { + bindings::kmem_cache_return_sheaf( + self.cache.as_raw(), + GFP_NOWAIT.as_raw(), + self.as_raw(), + ) + }; + } + } +} + +/// An owned allocation from a cache sheaf. +/// +/// `SBox` is similar to `Box` but is backed by a slab cache allocation ob= tained +/// through a [`Sheaf`]. It provides owned access to an initialized object= and +/// ensures the object is properly freed back to the cache when dropped. +/// +/// The contained `T` is initialized when the `SBox` is returned from allo= c and +/// dropped when the `SBox` is dropped. +/// +/// # Invariants +/// +/// - `ptr` points to a valid, initialized object of type `T`. +/// - `cache` is the cache from which this object was allocated. +/// - The object remains valid for the lifetime of the `SBox`. +pub struct SBox> { + ptr: NonNull, + cache: Arc>, +} + +impl> Deref for SBox { + type Target =3D T; + + fn deref(&self) -> &Self::Target { + // SAFETY: `ptr` is valid and properly aligned per the type invari= ants. + unsafe { self.ptr.as_ref() } + } +} + +impl> DerefMut for SBox { + fn deref_mut(&mut self) -> &mut Self::Target { + // SAFETY: `ptr` is valid and properly aligned per the type invari= ants, + // and we have exclusive access via `&mut self`. + unsafe { self.ptr.as_mut() } + } +} + +impl> Drop for SBox { + fn drop(&mut self) { + // SAFETY: By type invariant, `ptr` points to a valid and initiali= zed + // object. We do not touch `ptr` after returning it to the cache. + unsafe { core::ptr::drop_in_place(self.ptr.as_ptr()) }; + + // SAFETY: `self.ptr` was allocated from `self.cache` via + // `kmem_cache_alloc_from_sheaf_noprof` and is valid. + unsafe { + bindings::kmem_cache_free(self.cache.as_raw(), self.ptr.as_ptr= ().cast()); + } + } +} --=20 2.51.2 From nobody Mon Jun 8 07:21:50 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 192EB2DA756; Thu, 4 Jun 2026 20:00:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780603222; cv=none; b=MuQTTcmbWV4xpMLri34uGdpKSTDA9k6VeZAfkjciW48fCQHa1zn9wXO3GtIO0dp8cA24WETcLMBlEV/kKK2Hx/m/PN427GYSV827eoqW2o+OUFtN9uE0QOAGHEkj5DEImTR+EkmyaYrxoIy+nEmKGgFkOB5KAo+5YKIzMISExwQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780603222; c=relaxed/simple; bh=OXlzrpYOmH6xGYf35dAObz77Rlr5Pswn0A4JAdJKpCw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=kttXceOpQN0q0c3CZ3UNayNUSqanNZk2qsYOHao9hEj7AFStZVitP7SH6uHvaMHFY2/ZFDKwqfvA0zazKLOih59l6ZAsQE5nVyldGpblm5BKZwqMZO812lZAAnvKqTF6+UxMO4Ns6eZHWA/Zj2css+TIeiOI6UC1Uu6hwTy5HUg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=JImfcU0y; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="JImfcU0y" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 02E341F00898; Thu, 4 Jun 2026 20:00:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1780603220; bh=9JCKYElz/+/RJ/zhCN0Vl5fzPuI/cBiMyLCI6JtnVSQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=JImfcU0yR/fTF+GGsQGxddNSyWkJxrQ+w+G0O/PyAXVSWK0AUwubE49wjNhckOWGI 05T/RmHE2dmyMERpEYQDFzs2s2Xwz6CMbLn3NCbDTosv6UoyaCENNGo9JOojmyo4w5 rPZulbVb161v+ktZwe6tAjo7czPRNVAnQor6tuhhb1Qrirpa9Syp7KDxsC5sJeKGaO k17k9Ed7ahoQlbV82hMdjsQYo5w2ctEl/I/xDigQtZNdl2s1O9x0HFxAfYP/e2eEwh vrJiDRKAcQGbjwTP2OYp5qN02eosxmeqH1zAwsQauiOsssWtCZIJMRycxTpyD5MD04 SR7B8Dan0Jb0A== From: Andreas Hindborg Date: Thu, 04 Jun 2026 21:58:15 +0200 Subject: [PATCH v4 09/11] rust: mm: sheaf: allow use of C initialized static caches Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260604-xarray-entry-send-v4-9-965f6028790e@kernel.org> References: <20260604-xarray-entry-send-v4-0-965f6028790e@kernel.org> In-Reply-To: <20260604-xarray-entry-send-v4-0-965f6028790e@kernel.org> To: Miguel Ojeda , Alex Gaynor , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Andrew Morton , Christoph Lameter , David Rientjes , Roman Gushchin , Tamir Duberstein , Boqun Feng , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Tamir Duberstein , Boqun Feng , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo Cc: Daniel Gomez , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg , "Matthew Wilcox (Oracle)" X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=21499; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=OXlzrpYOmH6xGYf35dAObz77Rlr5Pswn0A4JAdJKpCw=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqIdjxOzytE41FB5Im0cfyJj1khIhKTWJJ5z9xi KhhuDXTP5KJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaiHY8QAKCRD6UCkIqsW9 0NfiD/9o0zd3/3T/+ujR6+5qwoazeyra/SZPobXlJk1hFkvJw68gmJ+TxIulH/yJ4xx1sPrnQRX HbF6ZS+m7POgBwLFC5TRGAQTOKkvcmFxqLzOeuXqjWz74ldz3KxthFjViNlgEpXqcGjL6CsJMXE W4A5bhe0Mm58kr8vSFWMH7QvPVUV4pV3TjEMBqKGkrn90e18n5ayovszEZcn38v9XMsVlzubuhU qiZtre8ljRtyOJRsSbdEumJbdnJU5Hg0qKB3CmouH86I4JbKSyWtGZ3qnCzebk1kwbC8dQrzNR3 0VHkchpTS/5NxG7Pq5UokaIVw1ha1myQqshWkAXK2B9PkhgeIYtsN0jcl+LKErI76kgSC+DDtyp xFyOv4XibkXEPmO1+fWusPq6OCSXk5kOQm54NWVhZzwKo6ieZTOk1JZJ2qFAXMTTNQNqvkfCCj2 ajoHKMa8JF/Gla0das5Nl9hiK0gwkUb5DUGDFA7ER3BdyF++tE60DW1y3I66qtBqOcMV34UORBj YR/qhgqUw5Oonx4m9BJT7ReWailmaYoSwEDZwc6zhyoEVPmKaFGigtLWGiJid3rIa1fgRG0V/Ar JDJtmqMpH4X0EB2bGxkUmXZstG60M8JTEPmzlMpPUWNsLbQ3p9U3vZ8qOx2NIluNisCYulKXQRu 5ifZTmoCADBUKdg== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Extend the sheaf abstraction to support caches initialized by C at kernel boot time, in addition to dynamically created Rust caches. Introduce `KMemCache` as a transparent wrapper around `kmem_cache` for static caches with `'static` lifetime. Rename the previous `KMemCache` to `KMemCacheHandle` to represent dynamically created, reference-counted caches. Add `Static` and `Dynamic` marker types along with `StaticSheaf` and `DynamicSheaf` type aliases to distinguish sheaves from each cache type. The `Sheaf` type now carries lifetime and allocation mode type parameters. Add `SBox::into_ptr()` and `SBox::static_from_ptr()` methods for passing allocations through C code via raw pointers. Add `KMemCache::from_raw()` for wrapping C-initialized static caches and `Sheaf::refill()` for replenishing a sheaf to a minimum size. Export `kmem_cache_prefill_sheaf`, `kmem_cache_return_sheaf`, `kmem_cache_refill_sheaf`, and `kmem_cache_alloc_from_sheaf_noprof` to allow Rust module code to use the sheaf API. Cc: Vlastimil Babka Cc: "Liam R. Howlett" Cc: "Matthew Wilcox (Oracle)" Cc: Lorenzo Stoakes Cc: linux-mm@kvack.org Signed-off-by: Andreas Hindborg --- mm/slub.c | 4 + rust/kernel/mm/sheaf.rs | 343 +++++++++++++++++++++++++++++++++++++++++++-= ---- 2 files changed, 317 insertions(+), 30 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 0baa906f39ab..87f138588100 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -5063,6 +5063,7 @@ kmem_cache_prefill_sheaf(struct kmem_cache *s, gfp_t = gfp, unsigned int size) =20 return sheaf; } +EXPORT_SYMBOL(kmem_cache_prefill_sheaf); =20 /* * Use this to return a sheaf obtained by kmem_cache_prefill_sheaf() @@ -5118,6 +5119,7 @@ void kmem_cache_return_sheaf(struct kmem_cache *s, gf= p_t gfp, barn_put_full_sheaf(barn, sheaf); stat(s, BARN_PUT); } +EXPORT_SYMBOL(kmem_cache_return_sheaf); =20 /* * Refill a sheaf previously returned by kmem_cache_prefill_sheaf to at le= ast @@ -5174,6 +5176,7 @@ int kmem_cache_refill_sheaf(struct kmem_cache *s, gfp= _t gfp, *sheafp =3D sheaf; return 0; } +EXPORT_SYMBOL(kmem_cache_refill_sheaf); =20 /* * Allocate from a sheaf obtained by kmem_cache_prefill_sheaf() @@ -5211,6 +5214,7 @@ kmem_cache_alloc_from_sheaf_noprof(struct kmem_cache = *s, gfp_t gfp, =20 return ret; } +EXPORT_SYMBOL(kmem_cache_alloc_from_sheaf_noprof); =20 unsigned int kmem_cache_sheaf_size(struct slab_sheaf *sheaf) { diff --git a/rust/kernel/mm/sheaf.rs b/rust/kernel/mm/sheaf.rs index f23fdaa2dbc3..1881a78670ac 100644 --- a/rust/kernel/mm/sheaf.rs +++ b/rust/kernel/mm/sheaf.rs @@ -23,17 +23,26 @@ //! //! # Architecture //! -//! The sheaf system consists of three main components: +//! The sheaf system supports two modes of operation: +//! +//! - **Static caches**: [`KMemCache`] represents a cache initialized by C= code at +//! kernel boot time. These have `'static` lifetime and produce [`Static= Sheaf`] +//! instances. +//! - **Dynamic caches**: [`KMemCacheHandle`] wraps a cache created at run= time by +//! Rust code. These are reference-counted and produce [`DynamicSheaf`] = instances. +//! +//! Both modes use the same core types: //! -//! - [`KMemCache`]: A slab cache configured with sheaf support. //! - [`Sheaf`]: A pre-filled container of objects from a specific cache. //! - [`SBox`]: An owned allocation from a sheaf, similar to a `Box`. //! //! # Example //! +//! Using a dynamically created cache: +//! //! ``` //! use kernel::c_str; -//! use kernel::mm::sheaf::{KMemCache, KMemCacheInit, Sheaf, SBox}; +//! use kernel::mm::sheaf::{KMemCacheHandle, KMemCacheInit, Sheaf, SBox}; //! use kernel::prelude::*; //! //! struct MyObject { @@ -47,7 +56,7 @@ //! } //! //! // Create a cache with sheaf capacity of 16 objects. -//! let cache =3D KMemCache::::new(c_str!("my_cache"), 16)?; +//! let cache =3D KMemCacheHandle::::new(c_str!("my_cache"), 16)= ?; //! //! // Pre-fill a sheaf with 8 objects. //! let mut sheaf =3D cache.as_arc_borrow().sheaf(8, GFP_KERNEL)?; @@ -76,7 +85,102 @@ =20 use kernel::prelude::*; =20 -use crate::sync::{Arc, ArcBorrow}; +use crate::{ + sync::{Arc, ArcBorrow}, + types::Opaque, +}; + +/// A slab cache with sheaf support. +/// +/// This type is a transparent wrapper around a kernel `kmem_cache`. It ca= n be +/// used with caches created either by C code or via [`KMemCacheHandle`]. +/// +/// When a reference to this type has `'static` lifetime (i.e., `&'static +/// KMemCache`), it typically represents a cache initialized by C at bo= ot +/// time. Such references produce [`StaticSheaf`] instances via [`sheaf`]. +/// +/// [`sheaf`]: KMemCache::sheaf +/// +/// # Type parameter +/// +/// - `T`: The type of objects managed by this cache. Must implement +/// [`KMemCacheInit`] to provide initialization logic for allocations. +#[repr(transparent)] +pub struct KMemCache> { + inner: Opaque, + _p: PhantomData, +} + +impl> KMemCache { + /// Creates a pre-filled sheaf from this cache. + /// + /// Allocates a sheaf and pre-fills it with `size` objects. Once creat= ed, + /// allocations from the sheaf via [`Sheaf::alloc`] are guaranteed to + /// succeed until the sheaf is depleted. + /// + /// # Arguments + /// + /// - `size`: The number of objects to pre-allocate. Must not exceed t= he + /// cache's `sheaf_capacity`. + /// - `gfp`: Allocation flags controlling how memory is obtained. Use + /// [`GFP_KERNEL`] for normal allocations that may sleep, or + /// [`GFP_NOWAIT`] for non-blocking allocations. + /// + /// # Errors + /// + /// Returns [`ENOMEM`] if the sheaf or its objects could not be alloca= ted. + /// + /// # Warnings + /// + /// The kernel will warn if `size` exceeds `sheaf_capacity`. + pub fn sheaf( + &'static self, + size: usize, + gfp: kernel::alloc::Flags, + ) -> Result> { + // SAFETY: `self.as_raw()` returns a valid cache pointer, and `siz= e` + // has been validated to fit in a `c_uint`. + let ptr =3D unsafe { + bindings::kmem_cache_prefill_sheaf(self.inner.get(), gfp.as_ra= w(), size.try_into()?) + }; + + // INVARIANT: `ptr` was returned by `kmem_cache_prefill_sheaf` and= is + // non-null (checked below). `cache` is the cache from which this = sheaf + // was created. `dropped` is false since the sheaf has not been re= turned. + Ok(Sheaf { + sheaf: NonNull::new(ptr).ok_or(ENOMEM)?, + // SAFETY: `self` is a valid reference, so the pointer is non-= null. + cache: CacheRef::Static(unsafe { + NonNull::new_unchecked((&raw const *self).cast_mut()) + }), + dropped: false, + _p: PhantomData, + }) + } + + fn as_raw(&self) -> *mut bindings::kmem_cache { + self.inner.get() + } + + /// Creates a reference to a [`KMemCache`] from a raw pointer. + /// + /// This is useful for wrapping a C-initialized static `kmem_cache`, s= uch as + /// the global `radix_tree_node_cachep` used by XArrays. + /// + /// # Safety + /// + /// - `ptr` must be a valid pointer to a `kmem_cache` that was created= for + /// objects of type `T`. + /// - The cache must remain valid for the lifetime `'a`. + /// - The caller must ensure that the cache was configured appropriate= ly for + /// the type `T`, including proper size and alignment. + pub unsafe fn from_raw<'a>(ptr: *mut bindings::kmem_cache) -> &'a Self= { + // SAFETY: The caller guarantees that `ptr` is a valid pointer to a + // `kmem_cache` created for objects of type `T`, that it remains v= alid + // for lifetime `'a`, and that the cache is properly configured fo= r `T`. + unsafe { &*ptr.cast::() } + } +} =20 /// A slab cache with sheaf support. /// @@ -95,12 +199,12 @@ /// - `cache` is a valid pointer to a `kmem_cache` created with /// `__kmem_cache_create_args`. /// - The cache is valid for the lifetime of this struct. -pub struct KMemCache> { - cache: NonNull, - _p: PhantomData, +#[repr(transparent)] +pub struct KMemCacheHandle> { + cache: NonNull>, } =20 -impl> KMemCache { +impl> KMemCacheHandle { /// Creates a new slab cache with sheaf support. /// /// Creates a kernel slab cache for objects of type `T` with the speci= fied @@ -148,8 +252,7 @@ pub fn new(name: &CStr, sheaf_capacity: u32) -> Result<= Arc> // `kmem_cache_destroy` is called in `Drop`. Ok(Arc::new( Self { - cache: NonNull::new(ptr).ok_or(ENOMEM)?, - _p: PhantomData, + cache: NonNull::new(ptr.cast()).ok_or(ENOMEM)?, }, GFP_KERNEL, )?) @@ -176,11 +279,11 @@ pub fn new(name: &CStr, sheaf_capacity: u32) -> Resul= t> /// # Warnings /// /// The kernel will warn if `size` exceeds `sheaf_capacity`. - pub fn sheaf( - self: ArcBorrow<'_, Self>, + pub fn sheaf<'a>( + self: ArcBorrow<'a, Self>, size: usize, gfp: kernel::alloc::Flags, - ) -> Result> { + ) -> Result> { // SAFETY: `self.as_raw()` returns a valid cache pointer, and `siz= e` // has been validated to fit in a `c_uint`. let ptr =3D unsafe { @@ -192,17 +295,18 @@ pub fn sheaf( // was created. `dropped` is false since the sheaf has not been re= turned. Ok(Sheaf { sheaf: NonNull::new(ptr).ok_or(ENOMEM)?, - cache: self.into(), + cache: CacheRef::Arc(self.into()), dropped: false, + _p: PhantomData, }) } =20 fn as_raw(&self) -> *mut bindings::kmem_cache { - self.cache.as_ptr() + self.cache.as_ptr().cast() } } =20 -impl> Drop for KMemCache { +impl> Drop for KMemCacheHandle { fn drop(&mut self) { // SAFETY: `self.as_raw()` returns a valid cache pointer that was // created by `__kmem_cache_create_args`. As all objects allocated= from @@ -215,13 +319,13 @@ fn drop(&mut self) { /// Trait for types that can be initialized in a slab cache. /// /// This trait provides the initialization logic for objects allocated fro= m a -/// [`KMemCache`]. When the slab allocator creates new objects, it invokes= the -/// constructor to ensure objects are in a valid initial state. +/// [`KMemCache`]. The initializer is called when objects are allocated fr= om a +/// sheaf via [`Sheaf::alloc`]. /// /// # Implementation /// -/// Implementors must provide [`init`](KMemCacheInit::init), which returns -/// a in-place initializer for the type. +/// Implementors must provide [`init`](KMemCacheInit::init), which returns= an +/// infallible initializer for the type. /// /// # Example /// @@ -252,6 +356,28 @@ pub trait KMemCacheInit { fn init() -> impl Init; } =20 +/// Marker type for sheaves from static caches. +/// +/// Used as a type parameter for [`Sheaf`] to indicate the sheaf was creat= ed +/// from a `&'static KMemCache`. +pub enum Static {} + +/// Marker type for sheaves from dynamic caches. +/// +/// Used as a type parameter for [`Sheaf`] to indicate the sheaf was creat= ed +/// from a [`KMemCacheHandle`] via [`ArcBorrow`]. +pub enum Dynamic {} + +/// A sheaf from a static cache. +/// +/// This is a [`Sheaf`] backed by a `&'static KMemCache`. +pub type StaticSheaf<'a, T> =3D Sheaf<'a, T, Static>; + +/// A sheaf from a dynamic cache. +/// +/// This is a [`Sheaf`] backed by a reference-counted [`KMemCacheHandle`]. +pub type DynamicSheaf<'a, T> =3D Sheaf<'a, T, Dynamic>; + /// A pre-filled container of slab objects. /// /// A sheaf holds a set of pre-allocated objects from a [`KMemCache`]. @@ -262,12 +388,23 @@ pub trait KMemCacheInit { /// Sheaves provide faster allocation than direct allocation because they = use /// local locks with preemption disabled rather than atomic operations. /// +/// # Type parameters +/// +/// - `'a`: The lifetime of the cache reference. +/// - `T`: The type of objects in this sheaf. +/// - `A`: Either [`Static`] or [`Dynamic`], indicating whether the backing +/// cache is a static reference or a reference-counted handle. +/// +/// For convenience, [`StaticSheaf`] and [`DynamicSheaf`] type aliases are +/// provided. +/// /// # Lifecycle /// -/// Sheaves are created via [`KMemCache::sheaf`] and should be returned to= the -/// allocator when no longer needed via [`Sheaf::return_refill`]. If a she= af is -/// simply dropped, it is returned with `GFP_NOWAIT` flags, which may resu= lt in -/// the sheaf being flushed and freed rather than being cached for reuse. +/// Sheaves are created via [`KMemCache::sheaf`] or [`KMemCacheHandle::she= af`] +/// and should be returned to the allocator when no longer needed via +/// [`Sheaf::return_refill`]. If a sheaf is simply dropped, it is returned= with +/// `GFP_NOWAIT` flags, which may result in the sheaf being flushed and fr= eed +/// rather than being cached for reuse. /// /// # Invariants /// @@ -275,13 +412,14 @@ pub trait KMemCacheInit { /// `kmem_cache_prefill_sheaf`. /// - `cache` is the cache from which this sheaf was created. /// - `dropped` tracks whether the sheaf has been explicitly returned. -pub struct Sheaf> { +pub struct Sheaf<'a, T: KMemCacheInit, A> { sheaf: NonNull, - cache: Arc>, + cache: CacheRef, dropped: bool, + _p: PhantomData<(&'a KMemCache, A)>, } =20 -impl> Sheaf { +impl<'a, T: KMemCacheInit, A> Sheaf<'a, T, A> { fn as_raw(&self) -> *mut bindings::slab_sheaf { self.sheaf.as_ptr() } @@ -304,6 +442,75 @@ pub fn return_refill(mut self, flags: kernel::alloc::F= lags) { drop(self); } =20 + /// Refills the sheaf to at least the specified size. + /// + /// Replenishes the sheaf by preallocating objects until it contains at + /// least `size` objects. If the sheaf already contains `size` or more + /// objects, this is a no-op. In practice, the sheaf is refilled to its + /// full capacity. + /// + /// # Arguments + /// + /// - `flags`: Allocation flags controlling how memory is obtained. + /// - `size`: The minimum number of objects the sheaf should contain a= fter + /// refilling. If `size` exceeds the cache's `sheaf_capacity`, the s= heaf + /// may be replaced with a larger one. + /// + /// # Errors + /// + /// Returns an error if the objects could not be allocated. If refilli= ng + /// fails, the existing sheaf is left intact. + pub fn refill(&mut self, flags: kernel::alloc::Flags, size: usize) -> = Result { + // SAFETY: `self.cache.as_raw()` returns a valid cache pointer and + // `&raw mut self.sheaf` points to a valid sheaf per the type inva= riants. + kernel::error::to_result(unsafe { + bindings::kmem_cache_refill_sheaf( + self.cache.as_raw(), + flags.as_raw(), + (&raw mut (self.sheaf)).cast(), + size.try_into()?, + ) + }) + } +} + +impl<'a, T: KMemCacheInit> Sheaf<'a, T, Static> { + /// Allocates an object from the sheaf. + /// + /// Returns a new [`SBox`] containing an initialized object, or [`None= `] + /// if the sheaf is depleted. Allocations are guaranteed to succeed as + /// long as the sheaf contains pre-allocated objects. + /// + /// The `gfp` flags passed to `kmem_cache_alloc_from_sheaf` are set to= zero, + /// meaning no additional flags like `__GFP_ZERO` or `__GFP_ACCOUNT` a= re + /// applied. + /// + /// The returned `T` is initialized as part of this function. + pub fn alloc(&mut self) -> Option> { + // SAFETY: `self.cache.as_raw()` and `self.as_raw()` return valid + // pointers. The function returns NULL when the sheaf is empty. + let ptr =3D unsafe { + bindings::kmem_cache_alloc_from_sheaf_noprof(self.cache.as_raw= (), 0, self.as_raw()) + }; + + // SAFETY: + // - `ptr` is a valid pointer as it was just returned by the cache. + // - The initializer is infallible, so an error is never returned. + unsafe { T::init().__init(ptr.cast()) }.expect("Initializer is inf= allible"); + + let ptr =3D NonNull::new(ptr.cast::())?; + + // INVARIANT: `ptr` was returned by `kmem_cache_alloc_from_sheaf_n= oprof` + // and initialized above. `cache` is the cache from which this obj= ect + // was allocated. The object remains valid until freed in `Drop`. + Some(SBox { + ptr, + cache: self.cache.clone(), + }) + } +} + +impl<'a, T: KMemCacheInit> Sheaf<'a, T, Dynamic> { /// Allocates an object from the sheaf. /// /// Returns a new [`SBox`] containing an initialized object, or [`None= `] @@ -339,7 +546,7 @@ pub fn alloc(&mut self) -> Option> { } } =20 -impl> Drop for Sheaf { +impl<'a, T: KMemCacheInit, A> Drop for Sheaf<'a, T, A> { fn drop(&mut self) { if !self.dropped { // SAFETY: `self.cache.as_raw()` and `self.as_raw()` return va= lid @@ -356,6 +563,39 @@ fn drop(&mut self) { } } =20 +/// Internal reference to a cache, either static or reference-counted. +/// +/// # Invariants +/// +/// - For `CacheRef::Static`: the `NonNull` points to a valid `KMemCache` +/// with `'static` lifetime, derived from a `&'static KMemCache` refe= rence. +enum CacheRef> { + /// A reference-counted handle to a dynamically created cache. + Arc(Arc>), + /// A pointer to a static lifetime cache. + Static(NonNull>), +} + +impl> Clone for CacheRef { + fn clone(&self) -> Self { + match self { + Self::Arc(arg0) =3D> Self::Arc(arg0.clone()), + Self::Static(arg0) =3D> Self::Static(*arg0), + } + } +} + +impl> CacheRef { + fn as_raw(&self) -> *mut bindings::kmem_cache { + match self { + CacheRef::Arc(handle) =3D> handle.as_raw(), + // SAFETY: By type invariant, `ptr` points to a valid `KMemCac= he` + // with `'static` lifetime. + CacheRef::Static(ptr) =3D> unsafe { ptr.as_ref() }.as_raw(), + } + } +} + /// An owned allocation from a cache sheaf. /// /// `SBox` is similar to `Box` but is backed by a slab cache allocation ob= tained @@ -372,7 +612,50 @@ fn drop(&mut self) { /// - The object remains valid for the lifetime of the `SBox`. pub struct SBox> { ptr: NonNull, - cache: Arc>, + cache: CacheRef, +} + +impl> SBox { + /// Consumes the `SBox` and returns the raw pointer to the contained v= alue. + /// + /// The caller becomes responsible for freeing the memory. The object = is not + /// dropped and remains initialized. Use [`static_from_ptr`] to recons= truct + /// an `SBox` from the pointer. + /// + /// [`static_from_ptr`]: SBox::static_from_ptr + pub fn into_ptr(self) -> *mut T { + let ptr =3D self.ptr.as_ptr(); + core::mem::forget(self); + ptr + } + + /// Reconstructs an `SBox` from a raw pointer and cache. + /// + /// This is intended for use with objects that were previously convert= ed to + /// raw pointers via [`into_ptr`], typically for passing through C cod= e. + /// + /// [`into_ptr`]: SBox::into_ptr + /// + /// # Safety + /// + /// - `cache` must be a valid pointer to the `kmem_cache` from which `= value` + /// was allocated. + /// - `value` must be a valid pointer to an initialized `T` that was + /// allocated from `cache`. + /// - The caller must ensure that no other `SBox` or reference exists = for + /// `value`. + pub unsafe fn static_from_ptr(cache: *mut bindings::kmem_cache, value:= *mut T) -> Self { + // INVARIANT: The caller guarantees `value` points to a valid, + // initialized `T` allocated from `cache`. + Self { + // SAFETY: By function safety requirements, `value` is not nul= l. + ptr: unsafe { NonNull::new_unchecked(value) }, + cache: CacheRef::Static( + // SAFETY: By function safety requirements, `cache` is not= null. + unsafe { NonNull::new_unchecked(cache.cast()) }, + ), + } + } } =20 impl> Deref for SBox { --=20 2.51.2 From nobody Mon Jun 8 07:21:50 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 1DF793CC7D3; Thu, 4 Jun 2026 19:59:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780603192; cv=none; b=aOKGAOJLO9oKBBjhNmwETeAR7QY4H1x6VVS3YjJtRvLivcjzTAA+8a8zKoNLvO8DrRlhk3urv+dUIacoEsRNHU3KvBHVKnMWzVNupktDzjGeRdIkQOaU6yvIAz33QzCZ+pe4pnuPdT65hJxrh3kOCUksQBqjvhky6YE9+8I96p0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780603192; c=relaxed/simple; bh=BhCLKxRMqF4V+mE7gy9xjhXHgxHe6HNf41/h4Z3bGDc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=NqJAqHmEqBGzrW3JQ7+XRn8Z3XDDdC2UD3wUr1UH/gYFfO1It/AvyPuz8MWfnS8WgjQIS2ugOgEdvG07XeO0ByFdt1XalV+CcwBfEJhtHVtBw+59SMDhp8lLDCUuvFDQnGYER1YKw9EpS9N8YNDVf+xbp/SICbEo8rAdaItKovk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=JPB7jQM4; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="JPB7jQM4" Received: by smtp.kernel.org (Postfix) with ESMTPSA id BB3E91F00893; Thu, 4 Jun 2026 19:59:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1780603188; bh=SmiYFIKuE1zvPsAEwECS8Z/13j8fqEOETWiRIjYfqnM=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=JPB7jQM4qJtWMPT2RpIm3aDZ+OqvI++pmnDzF8FMZBTb31pYMzeuqqRAwPEt2U/M8 oJKnydYcgbMlOhGRVfZEoV0I58oPOh+XPf6uMTBjDM9Eep/0dTqrdtnj68lmGASZox PBX5XLmf0gL1N51R9I3dAOan6GiEkpZ++4QT/rp6O1XJsmBIswbVhxlZIoWaRneVA4 /VQkNhLFs1jQhfm8QOigz8afOxpQ3XRLy5aaXRNNqiIuKX9UKc1/ytGC3dlFArNUql LdwpoFUyRNrClq/wrHMIXnf1s7kSW43K7mdm0+mdt1SdV+xeMClipC88ye1INxd9de IvKZV6qi03OKA== From: Andreas Hindborg Date: Thu, 04 Jun 2026 21:58:16 +0200 Subject: [PATCH v4 10/11] xarray, radix-tree: enable sheaf support for kmem_cache Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260604-xarray-entry-send-v4-10-965f6028790e@kernel.org> References: <20260604-xarray-entry-send-v4-0-965f6028790e@kernel.org> In-Reply-To: <20260604-xarray-entry-send-v4-0-965f6028790e@kernel.org> To: Miguel Ojeda , Alex Gaynor , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Andrew Morton , Christoph Lameter , David Rientjes , Roman Gushchin , Tamir Duberstein , Boqun Feng , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Tamir Duberstein , Boqun Feng , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo Cc: Daniel Gomez , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg , "Matthew Wilcox (Oracle)" X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=1241; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=BhCLKxRMqF4V+mE7gy9xjhXHgxHe6HNf41/h4Z3bGDc=; b=kA0DAAoB+lApCKrFvdAByyZiAGoh2PKiOBQrTuWb4+c//hzTix9gM8yNN9SYEMxv0B6vxjLml 4kCMwQAAQoAHRYhBFeK2cjZZnYmKsBqhvpQKQiqxb3QBQJqIdjyAAoJEPpQKQiqxb3Qbq0P/jVv ytNdtugJpYOvBMGd1/YyL95/92eWNJhenjaWD1s1c0k4gvnjG7MsQU66xf/lWXcu++IXGQOkSqx NU+8laAbYXgJ9wN62QfkdW8g08qC/0aduBghYfnV6wkNkH2TAeb+plHFV3GehmBmt5ah3diPeSd q8/UWaLDuC6dFp/gK0VpFU4Jx5nq/I18kDluysSCN2SVcm0YYMEee4taiZaClYxtYt9nmS/9fjz pm0KexPuArJ4IHr0LUsEX7m5k+JCUG9v5jFyWHbg97msdx4K9q7/8l/06Ru1TrMFJB31+FGgYFX PiQny9SX83eY87vZ2QFPBZoflNlDll8SIEVX0fD3AKwXL8IkIK7GeyvG2pGgs2Tb37siozoMGJs BL1TLFNii7SWJoNjTxQcrVb+hIi1ri4hAibMvDE3DCV05yVhiEGX2sQIQnFPEEAoUlH6K2ueN6b idJJycJ6yaueCdo5sT7p0hyuIk5m2MJprCBbkbviOogzBeBdhlc3FyovogAtHhd1eS4XMtEsg6+ NdE5CmDY8wrOrdGdIheNX9d88Utv3GLICqXBLW5kKukdbGuLnXFDjds6oeWEJnBsPr2BfZ+Pj2c 7f/YdnKxZaIpg5SIR+wVwdSoXlrLhifSSTWTd/QAehI+Ash+Nc0acpg3gWI/knsDdI/46xM0rPI 0qePx X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 The rust null block driver plans to rely on preloading xarray nodes from the radix_tree_node_cachep kmem_cache. Cc: "Matthew Wilcox (Oracle)" Signed-off-by: Andreas Hindborg --- lib/radix-tree.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/radix-tree.c b/lib/radix-tree.c index 976b9bd02a1b..1cf0012b15ad 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c @@ -1598,10 +1598,16 @@ void __init radix_tree_init(void) BUILD_BUG_ON(RADIX_TREE_MAX_TAGS + __GFP_BITS_SHIFT > 32); BUILD_BUG_ON(ROOT_IS_IDR & ~GFP_ZONEMASK); BUILD_BUG_ON(XA_CHUNK_SIZE > 255); - radix_tree_node_cachep =3D kmem_cache_create("radix_tree_node", - sizeof(struct radix_tree_node), 0, - SLAB_PANIC | SLAB_RECLAIM_ACCOUNT, - radix_tree_node_ctor); + + struct kmem_cache_args args =3D { + .ctor =3D radix_tree_node_ctor, + .sheaf_capacity =3D 64, + }; + + radix_tree_node_cachep =3D kmem_cache_create( + "radix_tree_node", sizeof(struct radix_tree_node), &args, + SLAB_PANIC | SLAB_RECLAIM_ACCOUNT); + ret =3D cpuhp_setup_state_nocalls(CPUHP_RADIX_DEAD, "lib/radix:dead", NULL, radix_tree_cpu_dead); WARN_ON(ret < 0); --=20 2.51.2 From nobody Mon Jun 8 07:21:50 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 671263AE703; Thu, 4 Jun 2026 19:59:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780603178; cv=none; b=GLc2vSc19QDLPdcvzS3glvXZ/siRiM0gKRL0nrMJVPt9xOhxo0SE7FyXQI8ZQUt1OeqLR3xICS5EqBFk1D7EK6hnQLSosGCrcgsPt/UycDjdoIwM0b+fTWpxX90HfLSBcJThfD+5ggc76P3v6rpICaB6OolqaOYGRYLZq7EfkMo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780603178; c=relaxed/simple; bh=5COargo+4lqjh4Cb4xJSNaFVbCtXU6TNlsW5EcI7kwI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=j/mELwrGqwUKUjNqqLo6t1Le0qmjLOPDIQY5pGEM1AH+/50JV2PEj7gtahfpGCbgHS5rv1UCa6FatRNlGuL4Cur+An1q2P/0tuvZu2+WPYljxImFluEq/omLJzhfFKcsOYzT2PE2hUEHUOKZAOsU9QmUAnSON1e6pNBYY2DOIYA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=SwmGOfJA; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="SwmGOfJA" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5229D1F00898; Thu, 4 Jun 2026 19:59:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1780603176; bh=jWkyJgBS4U3MzwhYPxW5jTWMJGrf1Kb2DOah+vODPh8=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=SwmGOfJAmda6pxHHfbwxN4QpX7lHcecQl9WPU7ZRNOQcD9QEPagTkPpG81/6KWpxm fEiqU1PA2h/OC3JpyN+cE20V/hCbhb79VngotsIUSbG3+pPjBRES1oJMQWqRHzI8Fu R4ipfNjA3kv6Bq1SST9Au/THTYq/qeoTLyMc40Aj/mbbDYIoe4pTAc608QdNrqn8YI 6kr2fnhJuvw9EoWVO2qpl2MhSAgCdy4IG6NDQdfkMaTTo7xE9x/UFUGN8i18MANKCv SXt7le9RztLatnjNXN2B45UPwgwgwV+45bVY2spuwZ7/giMcZy1n/JVpCF8DqlW4YA n5SECd7VL7oiQ== From: Andreas Hindborg Date: Thu, 04 Jun 2026 21:58:17 +0200 Subject: [PATCH v4 11/11] rust: xarray: add preload API Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260604-xarray-entry-send-v4-11-965f6028790e@kernel.org> References: <20260604-xarray-entry-send-v4-0-965f6028790e@kernel.org> In-Reply-To: <20260604-xarray-entry-send-v4-0-965f6028790e@kernel.org> To: Miguel Ojeda , Alex Gaynor , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Andrew Morton , Christoph Lameter , David Rientjes , Roman Gushchin , Tamir Duberstein , Boqun Feng , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Tamir Duberstein , Boqun Feng , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo Cc: Daniel Gomez , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg , "Matthew Wilcox (Oracle)" X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=17687; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=5COargo+4lqjh4Cb4xJSNaFVbCtXU6TNlsW5EcI7kwI=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqIdjzic/WbeAMQSxtT5hfJijfLPo7w6j7pTmzh Fy4wKo9qzGJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaiHY8wAKCRD6UCkIqsW9 0Dz5D/9Hmp7Kqfhv3++IqPtm6wInDWzXBbkl7sElLgvTOgLHcVB4+2BOmABfRb7FwjWNwODY2nb rIS8GSWSUO1OVGsj2Srq31QdQikeaRtpUd18P0WJD+GTZGrhrxsQs7buCt89l+9lixtmKsK3Eet TGVKBX1SzYuOBPDAn+df1oft90wFo+6i4F6CbpZMxWYDGWZyMJfn10pUAaTY5vE2VR9i2zi5l03 /cUNzEAgOfWXoiSQqLhorMNBSReyj+a7rUNYWLX68elpTTbigi7d9syAFaETkhtDUThVRUDjuB8 vjA6NCECGG8Yc3yp9PE3gZ0xkv0cKysPvqUujP6aGpAJiiPXu0n9nWklEFPInOZe5fVHIklYL1w dymfMkC6DtlRm4Gt+iaBxFPgRhiW+MSVysHfd1moI3C3kQHnX1MF8Z6Y1vY8PdN2tKkUeIMGWEi iU8598v7zuHDM03qP5DaxGhfHjPqK8p2u4NAB43z2AORANubRqpMvf+5PgM6iNBv8TB2X6fNnGQ qAGtd8nU1jAGsIkdzocLXFWHFCxQtQzD0XWX0IM64/D+jLkyFNknJObhtkw9f5eUdAbfNuRQKQN IVv/bJTRUxcMqjuYiX+2bDR3ueF0RClVV0NeDulGeGvwXkI9rzhSAVAs96q/YYtU9g6YR8req0F SnIvLz//mNZsE4g== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a preload API that allows preallocating memory for XArray insertions. This enables insertions to proceed without allocation failures in contexts where memory allocation is not desirable, such as in atomic contexts. The implementation introduces `XArrayNode` representing a single XArray node and `XArraySheaf` as a type alias for a sheaf of preallocated nodes. Add the function `xarray_kmem_cache` to provide access to the global XArray node cache for creating sheaves. Update `VacantEntry::insert` and `VacantEntry::insert_entry` to accept an optional sheaf argument for preloaded memory. Add a new `Guard::insert_entry` method for inserting with preload support. When an insertion would fail due to ENOMEM, the XArray state API automatically consumes a preallocated node from the sheaf if available. Export `radix_tree_node_ctor` and `radix_tree_node_cachep` from C to enable Rust code to work with the radix tree node cache. Cc: "Liam R. Howlett" Cc: "Matthew Wilcox (Oracle)" Signed-off-by: Andreas Hindborg --- include/linux/radix-tree.h | 3 + lib/radix-tree.c | 5 +- rust/bindings/bindings_helper.h | 3 + rust/kernel/xarray.rs | 185 +++++++++++++++++++++++++++++++++++-= ---- rust/kernel/xarray/entry.rs | 29 +++++-- 5 files changed, 194 insertions(+), 31 deletions(-) diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h index eae67015ce51..c3699f12b070 100644 --- a/include/linux/radix-tree.h +++ b/include/linux/radix-tree.h @@ -469,4 +469,7 @@ static __always_inline void __rcu **radix_tree_next_slo= t(void __rcu **slot, slot =3D radix_tree_next_slot(slot, iter, \ RADIX_TREE_ITER_TAGGED | tag)) =20 + +void radix_tree_node_ctor(void *arg); + #endif /* _LINUX_RADIX_TREE_H */ diff --git a/lib/radix-tree.c b/lib/radix-tree.c index 1cf0012b15ad..ddd67ce672f5 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c @@ -33,6 +33,7 @@ * Radix tree node cache. */ struct kmem_cache *radix_tree_node_cachep; +EXPORT_SYMBOL(radix_tree_node_cachep); =20 /* * The radix tree is variable-height, so an insert operation not only has @@ -1566,14 +1567,14 @@ void idr_destroy(struct idr *idr) } EXPORT_SYMBOL(idr_destroy); =20 -static void -radix_tree_node_ctor(void *arg) +void radix_tree_node_ctor(void *arg) { struct radix_tree_node *node =3D arg; =20 memset(node, 0, sizeof(*node)); INIT_LIST_HEAD(&node->private_list); } +EXPORT_SYMBOL(radix_tree_node_ctor); =20 static int radix_tree_cpu_dead(unsigned int cpu) { diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index d4093367a4a8..03fae45d5076 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -130,6 +130,9 @@ const gfp_t RUST_CONST_HELPER_XA_FLAGS_ALLOC1 =3D XA_FL= AGS_ALLOC1; * see https://github.com/rust-lang/rust-bindgen/issues/3347. */ const size_t RUST_CONST_HELPER_XAS_RESTART =3D (size_t)XAS_RESTART; +const size_t RUST_CONST_HELPER_XA_CHUNK_SHIFT =3D XA_CHUNK_SHIFT; +const size_t RUST_CONST_HELPER_XA_CHUNK_SIZE =3D XA_CHUNK_SIZE; +extern struct kmem_cache *radix_tree_node_cachep; =20 const vm_flags_t RUST_CONST_HELPER_VM_MERGEABLE =3D VM_MERGEABLE; const vm_flags_t RUST_CONST_HELPER_VM_READ =3D VM_READ; diff --git a/rust/kernel/xarray.rs b/rust/kernel/xarray.rs index f6d5e5908c8b..cbb16368c2ca 100644 --- a/rust/kernel/xarray.rs +++ b/rust/kernel/xarray.rs @@ -5,6 +5,7 @@ //! C header: [`include/linux/xarray.h`](srctree/include/linux/xarray.h) =20 use core::{ + convert::Infallible, iter, marker::PhantomData, pin::Pin, @@ -23,11 +24,17 @@ bindings, build_assert, // error::{ + code::*, to_result, Error, Result, // }, ffi::c_void, + mm::sheaf::{ + KMemCache, + SBox, + StaticSheaf, // + }, types::{ ForeignOwnable, NotThreadSafe, @@ -35,12 +42,54 @@ }, }; use pin_init::{ + init, pin_data, pin_init, pinned_drop, + Init, PinInit, // }; =20 +/// Sheaf of preallocated [`XArray`] nodes. +pub type XArraySheaf<'a> =3D StaticSheaf<'a, XArrayNode>; + +/// Returns a reference to the global XArray node cache. +/// +/// This provides access to the kernel's `radix_tree_node_cachep`, which i= s the +/// slab cache used for allocating internal XArray nodes. This cache can b= e used +/// to create sheaves for preallocating XArray nodes. +pub fn xarray_kmem_cache() -> &'static KMemCache { + // SAFETY: `radix_tree_node_cachep` is a valid, statically initialized + // kmem_cache that remains valid for the lifetime of the kernel. The c= ache + // is configured for `xa_node` objects which match our `XArrayNode` ty= pe. + unsafe { KMemCache::from_raw(bindings::radix_tree_node_cachep) } +} + +/// An preallocated XArray node. +/// +/// This represents a single preallocated internal node for an XArray. +pub struct XArrayNode { + node: Opaque, +} + +impl kernel::mm::sheaf::KMemCacheInit for XArrayNode { + fn init() -> impl Init { + init!(Self { + // SAFETY: + // - This initialization cannot fail and will never return `Er= r`. + // - The xa_node does not move during initalization. + node <- unsafe { + pin_init::init_from_closure( + |place: *mut Opaque| -> Result<(), = Infallible> { + bindings::radix_tree_node_ctor(place.cast::()); + Ok(()) + }, + ) + } + }) + } +} + /// An array which efficiently maps sparse integer indices to owned object= s. /// /// This is similar to a [`crate::alloc::kvec::Vec>`], but more = efficient when there are @@ -137,15 +186,22 @@ fn iter(&self) -> impl Iterator> + '_ { let mut index =3D 0; =20 // SAFETY: `self.xa` is always valid by the type invariant. - iter::once(unsafe { - bindings::xa_find(self.xa.get(), &mut index, usize::MAX, bindi= ngs::XA_PRESENT) - }) - .chain(iter::from_fn(move || { - // SAFETY: `self.xa` is always valid by the type invariant. - Some(unsafe { - bindings::xa_find_after(self.xa.get(), &mut index, usize::= MAX, bindings::XA_PRESENT) - }) - })) + Iterator::chain( + iter::once(unsafe { + bindings::xa_find(self.xa.get(), &mut index, usize::MAX, b= indings::XA_PRESENT) + }), + iter::from_fn(move || { + // SAFETY: `self.xa` is always valid by the type invariant. + Some(unsafe { + bindings::xa_find_after( + self.xa.get(), + &mut index, + usize::MAX, + bindings::XA_PRESENT, + ) + }) + }), + ) .map_while(|ptr| NonNull::new(ptr.cast())) } =20 @@ -166,7 +222,6 @@ pub fn try_lock(&self) -> Option> { pub fn lock(&self) -> Guard<'_, T> { // SAFETY: `self.xa` is always valid by the type invariant. unsafe { bindings::xa_lock(self.xa.get()) }; - Guard { xa: self, _not_send: NotThreadSafe, @@ -250,7 +305,7 @@ pub fn get_mut(&mut self, index: usize) -> Option> { /// /// match guard.entry(42) { /// Entry::Vacant(entry) =3D> { - /// entry.insert(KBox::new(0x1337u32, GFP_KERNEL)?)?; + /// entry.insert(KBox::new(0x1337u32, GFP_ATOMIC)?, None)?; /// } /// Entry::Occupied(_) =3D> unreachable!("We did not insert an ent= ry yet"), /// } @@ -455,6 +510,45 @@ pub fn store( Ok(unsafe { T::try_from_foreign(old) }) } } + + /// Inserts a value and returns an occupied entry for further operatio= ns. + /// + /// If a value is already present, the operation fails. + /// + /// This method will not drop the XArray lock. If memory allocation is + /// required for the operation to succeed, the user should supply memo= ry + /// through the `preload` argument. + /// + /// # Examples + /// + /// ``` + /// # use kernel::{prelude::*, xarray::{AllocKind, XArray}}; + /// let mut xa =3D KBox::pin_init(XArray::>::new(AllocKind::= Alloc), GFP_KERNEL)?; + /// let mut guard =3D xa.lock(); + /// + /// assert_eq!(guard.get(42), None); + /// + /// let value =3D KBox::new(0x1337u32, GFP_ATOMIC)?; + /// let entry =3D guard.insert_entry(42, value, None)?; + /// let borrowed =3D entry.into_mut(); + /// assert_eq!(borrowed, &0x1337); + /// + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + pub fn insert_entry<'b>( + &'b mut self, + index: usize, + value: T, + preload: Option<&mut XArraySheaf<'_>>, + ) -> Result, StoreError> { + match self.entry(index) { + Entry::Vacant(entry) =3D> entry.insert_entry(value, preload), + Entry::Occupied(_) =3D> Err(StoreError { + error: EBUSY, + value, + }), + } + } } =20 /// A reference to a [`Guard`], either shared or mutable, that exposes the @@ -493,6 +587,30 @@ pub(crate) struct XArrayState { state: bindings::xa_state, } =20 +impl Drop for XArrayState { + fn drop(&mut self) { + free_xa_alloc(&mut self.state); + } +} + +fn free_xa_alloc(state: &mut bindings::xa_state) { + if !state.xa_alloc.is_null() { + // SAFETY: + // - `xa_alloc` is only set via `SBox::into_ptr()` in `insert()` w= here + // the node comes from an `XArraySheaf` backed by `radix_tree_no= de_cachep`. + // - `xa_alloc` points to a valid, initialized `XArrayNode`. + // - The caller has exclusive ownership of `xa_alloc`, and no other + // `SBox` or reference exists for this value. + drop(unsafe { + SBox::::static_from_ptr( + bindings::radix_tree_node_cachep, + state.xa_alloc.cast(), + ) + }); + state.xa_alloc =3D null_mut(); + } +} + impl XArrayState { fn new(guard: R, index: usize) -> Self { let xa_ptr =3D guard.xa_ptr(); @@ -536,15 +654,36 @@ fn status(&self) -> Result { to_result(unsafe { bindings::xas_error(&self.state) }) } =20 - fn insert(&mut self, value: R::Value) -> Result<*mut c_void, StoreErro= r> { + fn insert( + &mut self, + value: R::Value, + mut preload: Option<&mut XArraySheaf<'_>>, + ) -> Result<*mut c_void, StoreError> { let new =3D R::Value::into_foreign(value).cast(); =20 - // SAFETY: `self.state` is a valid `xa_state` by the type invarian= t. By the same - // invariant, `self.state.xa` aliases the xarray reachable through= `self.guard`, - // whose lock we hold. `new` came from `R::Value::into_foreign`. - unsafe { bindings::xas_store(&mut self.state, new) }; - - self.status().map(|()| new).map_err(|error| { + loop { + // SAFETY: `self.state` is a valid `xa_state` by the type inva= riant. By the same + // invariant, `self.state.xa` aliases the xarray reachable thr= ough `self.guard`, + // whose lock we hold. `new` came from `R::Value::into_foreign= `. + unsafe { bindings::xas_store(&mut self.state, new) }; + + match self.status() { + Ok(()) =3D> break Ok(new), + Err(ENOMEM) =3D> { + debug_assert!(self.state.xa_alloc.is_null()); + let node =3D match preload.as_mut().map(|sheaf| sheaf.= alloc().ok_or(ENOMEM)) { + None =3D> break Err(ENOMEM), + Some(Err(e)) =3D> break Err(e), + Some(Ok(node)) =3D> node, + }; + + self.state.xa_alloc =3D node.into_ptr().cast(); + continue; + } + Err(e) =3D> break Err(e), + } + } + .map_err(|error| { // SAFETY: `new` came from `R::Value::into_foreign` and `xas_s= tore` does not take // ownership of the value on error. let value =3D unsafe { R::Value::from_foreign(new) }; @@ -554,9 +693,15 @@ fn insert(&mut self, value: R::Value) -> Result<*mut c= _void, StoreError XArrayState<&'b mut Guard<'a, T>> { - /// Consumes `self` and returns the inner `&mut Guard`. + /// Consumes `self`, releases any preallocated node held in `xa_alloc`= , and + /// returns the inner `&mut Guard`. pub(crate) fn into_guard(self) -> &'b mut Guard<'a, T> { - self.guard + // Suppress the `Drop` impl so we can move `guard` out by hand. + let mut this =3D core::mem::ManuallyDrop::new(self); + free_xa_alloc(&mut this.state); + // SAFETY: `ManuallyDrop` prevents `Drop::drop` from running, so t= his is the only place + // that consumes `guard`. `state` has no other resources after `fr= ee_xa_alloc`. + unsafe { core::ptr::read(&this.guard) } } } =20 diff --git a/rust/kernel/xarray/entry.rs b/rust/kernel/xarray/entry.rs index 5ad79a499156..e979481dd57e 100644 --- a/rust/kernel/xarray/entry.rs +++ b/rust/kernel/xarray/entry.rs @@ -3,6 +3,7 @@ use super::{ Guard, StoreError, + XArraySheaf, XArrayState, // }; use core::ptr::NonNull; @@ -29,9 +30,9 @@ impl Entry<'_, '_, T> { /// let mut xa =3D KBox::pin_init(XArray::>::new(AllocKind::= Alloc), GFP_KERNEL)?; /// let mut guard =3D xa.lock(); /// - /// /// let entry =3D guard.entry(42); /// assert_eq!(entry.is_occupied(), false); + /// drop(entry); /// /// guard.store(42, KBox::new(0x1337u32, GFP_ATOMIC)?, GFP_ATOMIC)?; /// let entry =3D guard.entry(42); @@ -73,7 +74,8 @@ pub fn into_guard(self) -> &'b mut Guard<'a, T> { /// Returns a reference to the newly inserted value. /// /// - This method will fail if the nodes on the path to the index - /// represented by this entry are not present in the XArray. + /// represented by this entry are not present in the XArray and no m= emory + /// is available via the `preload` argument. /// - This method will not drop the XArray lock. /// /// @@ -88,7 +90,7 @@ pub fn into_guard(self) -> &'b mut Guard<'a, T> { /// /// if let Entry::Vacant(entry) =3D guard.entry(42) { /// let value =3D KBox::new(0x1337u32, GFP_ATOMIC)?; - /// let borrowed =3D entry.insert(value)?; + /// let borrowed =3D entry.insert(value, None)?; /// assert_eq!(*borrowed, 0x1337); /// } /// @@ -96,8 +98,12 @@ pub fn into_guard(self) -> &'b mut Guard<'a, T> { /// /// # Ok::<(), kernel::error::Error>(()) /// ``` - pub fn insert(mut self, value: T) -> Result, StoreE= rror> { - let new =3D self.state.insert(value)?; + pub fn insert( + mut self, + value: T, + preload: Option<&mut XArraySheaf<'_>>, + ) -> Result, StoreError> { + let new =3D self.state.insert(value, preload)?; =20 // SAFETY: `new` came from `T::into_foreign`. The entry has exclus= ive // ownership of `new` as it holds a mutable reference to `Guard`. @@ -107,7 +113,8 @@ pub fn insert(mut self, value: T) -> Result, StoreError> { /// Inserts a value and returns an occupied entry representing the new= ly inserted value. /// /// - This method will fail if the nodes on the path to the index - /// represented by this entry are not present in the XArray. + /// represented by this entry are not present in the XArray and no m= emory + /// is available via the `preload` argument. /// - This method will not drop the XArray lock. /// /// # Examples @@ -121,7 +128,7 @@ pub fn insert(mut self, value: T) -> Result, StoreError> { /// /// if let Entry::Vacant(entry) =3D guard.entry(42) { /// let value =3D KBox::new(0x1337u32, GFP_ATOMIC)?; - /// let occupied =3D entry.insert_entry(value)?; + /// let occupied =3D entry.insert_entry(value, None)?; /// assert_eq!(occupied.index(), 42); /// } /// @@ -129,8 +136,12 @@ pub fn insert(mut self, value: T) -> Result, StoreError> { /// /// # Ok::<(), kernel::error::Error>(()) /// ``` - pub fn insert_entry(mut self, value: T) -> Result, StoreError> { - let new =3D self.state.insert(value)?; + pub fn insert_entry( + mut self, + value: T, + preload: Option<&mut XArraySheaf<'_>>, + ) -> Result, StoreError> { + let new =3D self.state.insert(value, preload)?; =20 Ok(OccupiedEntry::<'a, 'b, T> { state: self.state, --=20 2.51.2