From nobody Sat Sep 26 10:01:18 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 0EF214A0EF8; Wed, 2 Sep 2026 13:28:35 +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=1788355717; cv=none; b=kvlICf7go2nHDFMB3Yl9mdRVAdtgbIAHtjOyDHLU+9Z611dqw44kkN5YkcPO+SPxjOzg19P6DnLA3T9dnRSKxl62XABgE58tTSwwo/AJIj7i9d7u/B7M4K6uKOqyZqZrBvJC2vAgFpKiHq/Wxp8vl4MQp6+oUEvw67Iiu0Rbvkw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788355717; c=relaxed/simple; bh=+omDfc9TL/XAqCElB1ECov07Wz53Bjd9L1vrszIACpU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=sB8jlQ9ptcJIB+js2Z/Kuh/mKNTgUHKlL0NnXvILHAryGdDiP8MPd1uzChFLwuYfSWt8CqpoMaHzI3QIfG/fWtG6ESUna82WSWQETWUI38BJPsCWxlu0yMmxzPMFxCipo+SFFLVcJoE94NZL7t5TviNFsfya96yhoGNQv5NtN7s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Lm0rohuP; 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="Lm0rohuP" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 07F4C1F000E9; Wed, 2 Sep 2026 13:28:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788355715; bh=dd8siENsqtHhQzXPZF0LKxm97fALr2t5Pd26+e10O2o=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=Lm0rohuP6ISavxKdLUcodpGsLZ35LIfEfFgviN75v83FXuw7EgygD65gM6GLUyGZY gDUftNOffZpkrhrXf4H0OLW7BoSl4QFXeuLj+M3LhJ+ZwIeQvNKdj6pgNPN7GBgZBX FxiHHsWHlUfTuVSUyMly7Z/1mSWzTOFXHqIYSQJr2rnEa3W3VHxf7OB90QKnZU06y1 lXKYJtPkdUJ9yS6Vkaz+NCBaLnQ6ov27PGmV6JrJU792vLc8lTQEnwEJIHiySCHAhB lrMc05SplsUftNtFpG7F8+Xk4UOgquQKVu+NPvf3jQho1uRyYd0cmtVRnNRksN4Ci0 5zW+xzYq4enxQ== From: Andreas Hindborg Date: Wed, 02 Sep 2026 15:25:57 +0200 Subject: [PATCH v5 01/12] 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: <20260902-xarray-entry-send-v5-1-d18adae40708@kernel.org> References: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> In-Reply-To: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> To: Tamir Duberstein , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= , Matthew Wilcox , Andrew Morton , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Christoph Lameter , David Rientjes , Roman Gushchin Cc: Andreas Hindborg , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-mm@kvack.org, Daniel Gomez , "Mukesh Kumar Chaurasiya (IBM)" , Tamir Duberstein , "Liam R. Howlett" X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2371; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=+omDfc9TL/XAqCElB1ECov07Wz53Bjd9L1vrszIACpU=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqmCQawZH0SGA2p5ke/OiKg/3VhhXiRaJhAB+XD XruU96yNTiJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCapgkGgAKCRD6UCkIqsW9 0P50D/4iu74xgFGoPRqYXhC28GSw5li89gEWBMELQwbtd37y7DiKm4hsVD/JbvDhAySxNOPcKhY 3eKCjD3RcaZfmpBH71/DViwRdJ8tlHt8tTqzlA3ovdM9ck6v7Yfel55OaXcRTHm54+H7BXQSBnD 5yMll5okuX3iDijeDKXWdrxUWoJl7hBUEjKK8vybks+uuNFVczRVeXkKwqyT3CyUrHVLn1V4KMP RdDBJFxj6OwrIZsiNfcMCudWMQUJG/onFXCx3U68Z+ehIkOob7fgN/VOcJNn2t965Y7sYPUMOoN O9//Evx9jynx2ZuRBiF/1UmgahJee8SFUjc9m14llAhhIO+M1PeX6z6hJQJqaldgm9Qdy6jqndD OxCyUPALpVpUdvb9XXFJNcxofJxB0BY687RxgRgEu0WztaPZ76QcnNawY2dW0wjdQQhHaHBmrnW dWi0dhVHd1pW2zd5mJInLzyJm598EsjCYyAHyRhx0aSmDEhiob9gW7Pwa9K5cGqIOkNu/+2ivLD nb3iy7mqO82EfMZUcRabMDSpPI4zLGis+bCLVosrvetRa73KwyXeS9ZIjr4ohalgkD6jn8nnpXR O6xbyhDUhDXsje6gCw634mB56nB51QY0Kij2FWVCTRI7wjuNo4UDQtNZJRRWye+JH6AroE8oK/n lp4H2sXM7jXm+Mg== 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) Reviewed-by: Daniel Almeida Assisted-by: LLM Signed-off-by: Andreas Hindborg --- rust/kernel/xarray.rs | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/rust/kernel/xarray.rs b/rust/kernel/xarray.rs index 987c9c0c21989..02f93ae1f92de 100644 --- a/rust/kernel/xarray.rs +++ b/rust/kernel/xarray.rs @@ -4,20 +4,33 @@ //! //! C header: [`include/linux/xarray.h`](srctree/include/linux/xarray.h) =20 -use crate::{ +use core::{ + iter, + marker::PhantomData, + pin::Pin, + ptr::NonNull, // +}; +use kernel::{ alloc, bindings, - build_assert::build_assert, - error::{Error, Result}, + build_assert::build_assert, // + error::{ + Error, + Result, // + }, ffi::c_void, 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. /// @@ -50,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 Sat Sep 26 10:01:18 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 CCAFE4A2A79; Wed, 2 Sep 2026 13:28:23 +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=1788355705; cv=none; b=BePugPjh6kTQxwyOqy8UE4BmXq9z8A2d5HWME1KwWfgVA0FyTcCAFdSjPOAC/nvfISkcgdei2Jn54qlMo2s6zmPrpzNNQhvmsGi+mELQVopZJs1n2SxQOrZML0OImlSkUSZ4UXUdluFNjd2jzWn4a4dlcbBMawQY34Vt9vwlA3k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788355705; c=relaxed/simple; bh=9B4p2CL9qctopDIxo+KXlNZsHJI+dVZmzPAUpOedsMA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Elr9MvNTV7ovV+ScQ/dNGhMujCJaNra1W1hSFMnOcgEYwmPmODJWTbOpm+lLQRiLlQwoCyLnIDFNNVFsnnkQvsFe1zXfCQfzfTBpa/U+Qfhru4U1aBJekaxLG/1wuFBtkbuQMZj+SSrwgBS4ahWBLu0k6Pt6oOeqfIFW3wMk9lQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=OdHOrsfn; 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="OdHOrsfn" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 168B31F000E9; Wed, 2 Sep 2026 13:28:17 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788355703; bh=geBhGzf9cUnk3yzaHjFsAOOWlmrxzTwK75Z/iOmcQgA=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=OdHOrsfnpLKRwrNRRHiRk90GY8/Q3wL1/wflXxZVpvaAM6Hm9CvoKx8HPLNiRyzPP xFx7ULxE8b7uJVlcBi/1EZ81OOfCTA+UqUKocZhmJ/ELEGQlDIbyJAIpPPn1uHW1S6 fKa9rB1h8oYtP1fW5xSMrjF/A7lmVp8PHG2ks/v4DZZ8HTCnRf51lOC/rN75tevE23 t4Ji4k96NdJ4oeZQQiPu8NKYUYmf8vUu3oPmOqhaENd5daDHQrbiZ6Jjym89mZdiWo PBumOsJthuIobny1/oJZdWoK4UWyfjewkehCd2wPuM9vevmmx/IOldCZINdUySKK66 f1UxjVQp6z1bg== From: Andreas Hindborg Date: Wed, 02 Sep 2026 15:25:58 +0200 Subject: [PATCH v5 02/12] 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: <20260902-xarray-entry-send-v5-2-d18adae40708@kernel.org> References: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> In-Reply-To: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> To: Tamir Duberstein , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= , Matthew Wilcox , Andrew Morton , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Christoph Lameter , David Rientjes , Roman Gushchin Cc: Andreas Hindborg , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-mm@kvack.org, Daniel Gomez , Tamir Duberstein , "Liam R. Howlett" X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1348; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=9B4p2CL9qctopDIxo+KXlNZsHJI+dVZmzPAUpOedsMA=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqmCQaCGpAFW6/BSWw+xrp731q7NFnlJ0hRz0Gh uySzsBhGKeJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCapgkGgAKCRD6UCkIqsW9 0Jq9EACwI4S5rCSSyPKySNKDNJdjwO/Du5ue1uvV7K+mHum1BdqTjcpM9OJd9L1enaG52oAnr4b me3nVmab/3UMApxgHkQ7X34jM2Fd7PBu7EFCD+hgfplYwC4v9Vqc/FnU6b19ecj/tgh/Nzv7k75 ON/4yE1cVBLgt3rCu8yD+2A0MkbAGEqb8/3/MYz1KVOwXwjcWV3nAiXxEDYbmUUqZh/Uaivl0BG h+kgD8VSKUAtJNV9tjVHpvgQ8nU0u1MprjrpvtQeOhPUXd7eYnFEp5YG2gAabRCW9KJbJ0feLiu 6TLohZTDaUYjwdZP+qsxX9P+xxilpfiQeSuSch+Az9ZnTWJH5Bybuk67Kp70EK0SeAfuA93sDXn OqPqirC8XkzdLX3J2ebc+di2QN9JkeGr4aWQAXdV8Om4wJ1+jTHa3Hk1qjDsw9/FzIdiqjMJMtv iDq8kyIrN/ACCUTu1ZPcMuCIiGSGFQ5BcOdpQkcSWg3uMsViX7CA2yF5hfJ3/qNMeunGMaDvLVb MfjAW2PKwLz2lsY/yFdhzN2bteOhXq8F9uWr0OR1EYDN9oudTJ8TtnPFeF0TSXb3oZioYZvHCZn 4YONbdXaN4DizDc6Qw6GZwVZHH1WPXsCVOqXyyGacyEZzp0Bc63Jez4DFSi5Qo+Hf4R0UxVm/VF EH2M9JViuxyJAMA== 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 Reviewed-by: Daniel Almeida Assisted-by: LLM Signed-off-by: Andreas Hindborg --- rust/kernel/xarray.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rust/kernel/xarray.rs b/rust/kernel/xarray.rs index 02f93ae1f92d..4335caab8cca 100644 --- a/rust/kernel/xarray.rs +++ b/rust/kernel/xarray.rs @@ -19,6 +19,7 @@ Result, // }, ffi::c_void, + fmt, types::{ ForeignOwnable, NotThreadSafe, @@ -193,6 +194,14 @@ pub struct StoreError { pub value: T, } =20 +impl fmt::Debug for StoreError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + 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 Sat Sep 26 10:01:18 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 C5F8B4A0EE1; Wed, 2 Sep 2026 13:28: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=1788355711; cv=none; b=eK5uDa3sVDLc9HJhD+HWga3C0s3P3A7xlZUs53XkHUq5+l8zq7QH6v+K2m5jbpS2zMvA6CfGxfMr4828S70uROU3uPTo8iiP147JzDNZeAP55YiKl5hbqCPw/ZRcRqVTAlrJM+8cjwFV9CjGxdDOXtpl802DuRZDJcLDCCYsHcY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788355711; c=relaxed/simple; bh=qh/kWsxpWtJTnMsjGDetI3+BPXuYwIehfqEkMzoAKOk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=OjkTzoO0yz8D/AzKM7N6BrCKXEN0Xikdl4/R+MDiMLNHsL/8tqfbJnKbZgW7QT1xcA1e5oJr30rUCmjalpZWqQZmyqL/iG0H+O8SLOGkxP6xj4hcJ4Zfglb834iBD8nuPpZ+eYKauz5zHD1GQmk4FvT44nFzr4CnSWarF3T81YA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=YUfwhx54; 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="YUfwhx54" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 561D11F00A3A; Wed, 2 Sep 2026 13:28:24 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788355709; bh=X7gwJtRN282x+tCWLvqdh4zA4VQZ73eNGEmZwXXg9AA=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=YUfwhx54IjYX5Ey/zFGYqewmbqaoJ6TrjvfumzLMK7iM/U8sK5VpTQjpMxyy43ryX /Eo9EnkVyXkXT3Cfs0aRWlwdwb6NH7i2iggVOj43ZbBP0tTOEFtK/jekD0iUqPuewc 5LyYZyPA6Fd5rPaE4BsbsB3/iQ8stZUSZ59iYXPVnmX/plkb/oh8k2rfcOPMbaC0Rw YvKD9GFuWlQF8pfXXAmHutTk4HPoY33h17zj9T7cxrrkK49Xi0WqmoLn4o3Sg6Krti 2I0bshwofTUlzt1BUa94WoMO7+DG9xnVQ0VcLC4MaafqE/c8aDg46x3fzjZUPO4QJk lLAsSO8Kj4F7A== From: Andreas Hindborg Date: Wed, 02 Sep 2026 15:25:59 +0200 Subject: [PATCH v5 03/12] xarray: move xas_result() and xa_zero_to_null() to the header 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: <20260902-xarray-entry-send-v5-3-d18adae40708@kernel.org> References: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> In-Reply-To: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> To: Tamir Duberstein , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= , Matthew Wilcox , Andrew Morton , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Christoph Lameter , David Rientjes , Roman Gushchin Cc: Andreas Hindborg , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-mm@kvack.org X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2745; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=qh/kWsxpWtJTnMsjGDetI3+BPXuYwIehfqEkMzoAKOk=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqmCQbKtU2TBroNoHBj4gHXVX9d4qVvUTe3v5l7 WHLs1k9iNCJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCapgkGwAKCRD6UCkIqsW9 0AApEACtvY4bZttqTtKgG4ptRmCZeXOYE0MPktLb/8/ea9/SZRCy8yzPZlN3pzNq/EzI5dX0fYo 31bJ9XOJas2xPibfFTAvrxYUOV1abZxiu2M1fagVBF//mZ3oEydoi696PMOgOjJNd+J0RGTNAHl pfCRn15HO0aJUaow6XnYWdJX6I0nG2oMbiF0fXHuc8jeBvPa7uS+RWBiAqQsALpl7vQQz09IlZa iiG1HMl7gJ2VxvlStC4oMiMU4xg1IBFOV76/BUYvTgVJwVEVERujj2++HCjQyE/iRzaonRMNaCD Ro5Lkc086QvEvuvj5J6qw8GIVH/9JL//owEXcMEY7FHxCbwhYBsUHIrSgjj0xhx2e7PFNQnpCW+ zf3nxBzzqtT/loJ5o5F5QbU2GibP47awBVdfmzWGwD0qI2RlGypGddQLDCJzAuyoEAJO3v2adMd 2WCzT3dkAK+eyjiL4xF+xTIBXhFg+/8mUU5tUFuN+17hOT6zQw0R2Az/lgpBt8ah94Mngh07WqI fVrLdESuNxxGVext+AjR5Rbgje9KdvdUeO5h7NjhOVjpsIEMbkVWvCdeOHC8SrEK37YfzLxHEyq yfhgBfNFwYC06b9faLttEBD1l7A9Y9mpYF5OjgE7v1W+FbcyPFek/4RAJ3hyK73pCWMhZ90sqo6 QzzutF1hJTxulGQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 The functions xas_result() and xa_zero_to_null() are static in lib/xarray.c. Upcoming Rust XArray changes need to call them from Rust helper functions. Move them to include/linux/xarray.h as static inline functions, following the approach of commit 79ada2ae6615 ("xarray: extract helper from __xa_{insert,cmpxchg}"). No functional change. Suggested-by: Tamir Duberstein Assisted-by: LLM Signed-off-by: Andreas Hindborg --- include/linux/xarray.h | 26 ++++++++++++++++++++++++++ lib/xarray.c | 12 ------------ 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/include/linux/xarray.h b/include/linux/xarray.h index be850174e802e..db2520864aaa7 100644 --- a/include/linux/xarray.h +++ b/include/linux/xarray.h @@ -191,6 +191,17 @@ static inline bool xa_is_zero(const void *entry) return unlikely(entry =3D=3D XA_ZERO_ENTRY); } =20 +/** + * xa_zero_to_null() - Convert an internal zero entry into a NULL pointer. + * @entry: XArray entry. + * + * Return: %NULL if @entry is a zero entry, @entry otherwise. + */ +static inline void *xa_zero_to_null(void *entry) +{ + return xa_is_zero(entry) ? NULL : entry; +} + /** * xa_is_err() - Report whether an XArray operation returned an error * @entry: Result from calling an XArray function @@ -1437,6 +1448,21 @@ static inline int xas_error(const struct xa_state *x= as) return xa_err(xas->xa_node); } =20 +/** + * xas_result() - Extract the result of an XArray operation. + * @xas: XArray operation state. + * @curr: Entry returned by the operation. + * + * Return: @curr if the operation succeeded, the error encoded in @xas + * otherwise. + */ +static inline void *xas_result(struct xa_state *xas, void *curr) +{ + if (xas_error(xas)) + curr =3D xas->xa_node; + return curr; +} + /** * xas_set_err() - Note an error in the xa_state. * @xas: XArray operation state. diff --git a/lib/xarray.c b/lib/xarray.c index 9a8b4916540cf..cddb44f6b4ab2 100644 --- a/lib/xarray.c +++ b/lib/xarray.c @@ -437,11 +437,6 @@ static unsigned long max_index(void *entry) return (XA_CHUNK_SIZE << xa_to_node(entry)->shift) - 1; } =20 -static inline void *xa_zero_to_null(void *entry) -{ - return xa_is_zero(entry) ? NULL : entry; -} - static void xas_shrink(struct xa_state *xas) { struct xarray *xa =3D xas->xa; @@ -1624,13 +1619,6 @@ void *xa_load(struct xarray *xa, unsigned long index) } EXPORT_SYMBOL(xa_load); =20 -static void *xas_result(struct xa_state *xas, void *curr) -{ - if (xas_error(xas)) - curr =3D xas->xa_node; - return curr; -} - /** * __xa_erase() - Erase this entry from the XArray while locked. * @xa: XArray. --=20 2.51.2 From nobody Sat Sep 26 10:01:18 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 2297A49364B; Wed, 2 Sep 2026 13:27:27 +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=1788355651; cv=none; b=KOTd2N2HHx9AfXmWkVVLBztoKayEqKgNftTN1i+Zzi0K1b5oAJA3SDKCJrecxT3D7jufNGmFFnzT7bgqgC5iW/xtJdsdYTHa0c9jgiNKBCmSWXxYAq0KWPuCksSYpSLMBTrwzq5iKW5ISPAFgC9QA+gRBIYZSeUTzWZsT3Sxrac= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788355651; c=relaxed/simple; bh=uIAZTAMI6EKyFLH4zWLpCBUlLxJgzsKNrPDLyyXwmL8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=CIYfWV93kTyGgYPIKnTo32Yh5LL8xb8e4VQsyatokMLoKwWfIfJaqKfYl+6W20yN64pCs3pSnX1aRj8cr6NF5v4iO8DvLAbY4dXmfhs0pfEiOoB/MahN5fAX1b3nWLxMjzgYfs5LW2JrAwV3Y2M2FIYbm5Ns+dMeC1SkFFA7wmk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=oveZxi0S; 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="oveZxi0S" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4E8161F000E9; Wed, 2 Sep 2026 13:27:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788355645; bh=EKD5ZTJn8sxqNRm37VbuPZ2kXqgx5R9J/beNmFemwCE=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=oveZxi0SOjjDyrwpWs84Nhn2Td1BAXYmQoz6FklO2UR0iJhQkks2zxuIIIoTx4dIW mB/ynoQf1I7b/Q2Wm2ePI54e6IyVbPSgWQUC6aF6kARqWi4pEE0UvRFm0uR8tXp9Wm Sslv5qHcO8HuYb/kkv6mFFgXms05h9NkymV+JyQVbTYTun/X2fLK30qY6fR5WvW2IQ knOH+a2Sb9goYrT+Oqhf3Z9bHTdW/dGZqj3XCcpQCoC0dLAuA6W4ki2wCWcM3FGCkj i9wodQ/KX2cdPKFSrLHxHa2sXxuXiPCOOWNy8VvsAIllEvK2NQ943gSQt16XBlfX9Y DeBW6EVp71PkA== From: Andreas Hindborg Date: Wed, 02 Sep 2026 15:26:00 +0200 Subject: [PATCH v5 04/12] 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: <20260902-xarray-entry-send-v5-4-d18adae40708@kernel.org> References: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> In-Reply-To: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> To: Tamir Duberstein , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= , Matthew Wilcox , Andrew Morton , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Christoph Lameter , David Rientjes , Roman Gushchin Cc: Andreas Hindborg , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-mm@kvack.org X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=6538; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=uIAZTAMI6EKyFLH4zWLpCBUlLxJgzsKNrPDLyyXwmL8=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqmCQccl8sgPMa9QSqMPBIn7AuzBniR3mDYRUEL AaTUAdQd5aJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCapgkHAAKCRD6UCkIqsW9 0NkhD/41qhOo1fHzwGL3+jOmJ7ywFgDg5h7zJNwQTLsTStKSuLg31/UpCMzC3l1pdRmfZnux7dZ N2uO5YzXyP4c2TzDESlyUAV25BuVM704cLmS0zdnBrTF/PEuom4ROealCRoakjn85163ngBqyDF yytVrxSmjG3ScF13r7UVPaVgI7xxMb5uT/7mKgEyYNTY5h1oaZHcPCwP5YeKK6C9txcxkDM6fXr wp6SxyQwlZ7w5ijdgJS2PJPHoH/xl85DA1+rEBPVTVwPuArk7uEWPBlV+BOX1nEmloMVA1YV8Of wHCcRfD0xMZJVGUtqKRZF7ZCKMJ20fFnXiuAugYmANVfIsXthDyAxHBYBkNH0btI/lmGBr8vXfL xNeYKA2od8U22fxjGS740BeoadmsDL2XcIryu4fW18xHvuY8KCH9q+BN7niyoDA9mphkMajKgZt BFn82CkJl1WtmTC+km86lK42BeOReHf+Q+m/CAglszQZ3uKbJzLknZ1tMr5JvXPPtduhjRYwR8V s/0S8gSir18/YPTHSOnXEBb269y0mVyZH2JzqXirkQe0LZ31p6VWvGz36MDqas4pOcQ8NYQcGK7 nLlHdBNaZg+7RdfcLLRjQ84Z0QUrwTZ33jm/qHVVQIjJdlo+QOMlB1iIcn5wuO+yWi/Tn8rWilF d21t2nfy5D0eZ9w== 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. `XArrayState` is generic over the guard borrow through a `Deref` bound, so it can hold either a shared or a mutable reference to the guard. Use the new state in `Guard::load` by replacing the call to `xa_load` with `xas_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. Unlike `xa_load`, `xas_load` does not filter out internal entries. Arrays created with `AllocKind::Alloc1` store `XA_ZERO_ENTRY` at index 0 when they are expanded from empty, so convert zero entries to `NULL` like the C normal API does, exposing the `xa_zero_to_null` helper for this purpose. The `XAS_RESTART` constant is also exposed through the bindings helper to properly initialize the `xa_node` field. The `guard` field of `XArrayState` is not read until a later patch adds `into_guard`, so it is annotated with `#[expect(dead_code)]` until then. Assisted-by: LLM Signed-off-by: Andreas Hindborg --- rust/bindings/bindings_helper.h | 7 ++++ rust/helpers/xarray.c | 5 +++ rust/kernel/xarray.rs | 74 +++++++++++++++++++++++++++++++++++++= +--- 3 files changed, 81 insertions(+), 5 deletions(-) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 1124785e210b3..419e6b74fedc3 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -124,6 +124,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/helpers/xarray.c b/rust/helpers/xarray.c index 08979b3043410..79799c55c3d73 100644 --- a/rust/helpers/xarray.c +++ b/rust/helpers/xarray.c @@ -26,3 +26,8 @@ __rust_helper void rust_helper_xa_unlock(struct xarray *x= a) { return xa_unlock(xa); } + +__rust_helper void *rust_helper_xa_zero_to_null(void *entry) +{ + return xa_zero_to_null(entry); +} diff --git a/rust/kernel/xarray.rs b/rust/kernel/xarray.rs index 4335caab8ccae..e8082df2b4797 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, @@ -214,10 +217,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. @@ -300,6 +301,69 @@ pub fn store( } } =20 +/// 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`. +pub(crate) struct XArrayState { + // The borrow is held to guarantee exclusive access to the array. It is + // not read until a later patch adds `into_guard`, so silence the dead + // code warning until then. + #[expect(dead_code)] + guard: R, + state: bindings::xa_state, +} + +impl<'a, R, T> XArrayState +where + T: ForeignOwnable + 'a, + R: core::ops::Deref>, +{ + #[inline] + fn new(guard: R, index: usize) -> Self { + let xa_ptr =3D guard.xa.xa.get(); + // 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(), + }, + } + } + + 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) }; + + // Unlike the normal API, `xas_load` does not filter out internal = entries. Arrays + // created with [`AllocKind::Alloc1`] store `XA_ZERO_ENTRY` at ind= ex 0 when they are + // expanded from empty, so convert zero entries to `NULL` like `xa= _load` does. Retry + // entries cannot be observed here because they require concurrent= modification of the + // array, and we hold the lock. + // + // SAFETY: `xa_zero_to_null` only inspects the value of `ptr`. + NonNull::new(unsafe { bindings::xa_zero_to_null(ptr) }.cast()) + } +} + // 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 Sat Sep 26 10:01:18 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 591B84A0134; Wed, 2 Sep 2026 13:28:05 +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=1788355686; cv=none; b=QeUaNHWu6Z7DuRiJy8uLx+kdRrEg95v7qPN573ZaQ9KeISve2Lqj00BKhNQVIQvrkIblNXs4YQ5UrBPP289ZkDeHoeQe1UeBqPsr6jAKeYpxosAmzmlTkYslO52yK0uwCR11oEqoRI8n1fI5T0743v9kTqoqpQIkNpiXzQUAoEs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788355686; c=relaxed/simple; bh=cfwTSDtHE61D+hAv0+T26KhcBz89hIRC5Ad95CVeQR0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=h3fi6cKyKMJY9hvTqWfOLn7FfZlKbmJsD+QPGY9NGehw10tiklhMICNJMwy+IlooBtG8kajrRCIuhUNLmfIIzy9RHvmnur6Tw7AHgZcyC5CU17Yb6G+ssNjUxf6q7cUhOKoPwXeL62SAt7RofS/p0BgdsubAQlMKiJakf92ReQI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=a+tYMlPt; 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="a+tYMlPt" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4C42D1F00A3D; Wed, 2 Sep 2026 13:27:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788355684; bh=xHSGAuvtInu1rhm1zYqbkY0gUKKIuZH8RVv8W86t1CI=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=a+tYMlPtBLTjjh8u4I0FUUTv+/ATr23STxu8JvEOsEC6BB2JLrQVwYo+ibZ8djmMm VHref0lIdaBJb2xNwMdS8HjCFo/mDPXX11icCPyHj57I3Zu8+fu2IzzXLlVfp6IR1F OMlIRrh4bV/u8vZuFYYwmo3DGulz2YelZt4eTqfJREz1Ev2yA5dmUsNFNQIrFZZ8pM 8ThXilZ7eMwbyHcSYRthiu/BW9VKB6fLjKcTjTBvPTjbav1e/UtbSHBgp/uSuQxqR7 w95LewgIlqCvuL5AyH05omlOOHJQXRI8WYE+wSU/UBvn0m21HFlnL668t0RgUZWfhi GU9HNUd6fjGUA== From: Andreas Hindborg Date: Wed, 02 Sep 2026 15:26:01 +0200 Subject: [PATCH v5 05/12] 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: <20260902-xarray-entry-send-v5-5-d18adae40708@kernel.org> References: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> In-Reply-To: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> To: Tamir Duberstein , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= , Matthew Wilcox , Andrew Morton , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Christoph Lameter , David Rientjes , Roman Gushchin Cc: Andreas Hindborg , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-mm@kvack.org X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2495; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=cfwTSDtHE61D+hAv0+T26KhcBz89hIRC5Ad95CVeQR0=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqmCQdaA3WUZjTsSciZ+SO9NZseZTE17fvIgVUS 7/2O2oFwYiJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCapgkHQAKCRD6UCkIqsW9 0N2lEACRu02pSYdB4X0b63wrrITFo4OhOg2TPdse92x+5KfKfGKDppJb7bXIJJDBoju8vstecyJ 9whvl5jPTyAvNMFuL+x0fDNgHq5O75raA0PfKMRLbyIj1PXP1YhgxQp1v+BH3qyhT536HEwKqOy wM8t5iym9EjYlBQmXKlDL+RZF6vjGBKkFSvze+ATeHfWmJxvFfgFg2MgAzR7dxRqaiz26kZCQy9 yIo/wiuFqKq06cNyJLV2tTc/XHUzyjJH9g7hLJrAYueFElS/YirzAcpLj8FrRd61AR6RHVCAIvS LWfXLLrJlIdbeoh5CHV9fwDqt+KAAc2jnMMr+5CYMhfRs/L6rAltcBoBTrqCcJav56AIKwC/Zeb DIS6YN6o0NKgdQmVhY7pJqjWkNhzwVktGNByfaoFV2hCwJT84GfYjyKUBEvL2fIpAFMsXR+So1X jU7ysdOue364CTVO/lqLwRvP1+V9aU/YZzaCXNw6IOsHxeOVwA2VJNDwVlLwK7DDHihUxHJYp8v W1LwD4Im1Iij/LgRuAFg/XuwY8aUESgH8ul/nYnylvlgKATq0PBZvw9iI2g2CbA4Biwteni+qsr yj8HT77VWNvsqcDt8Kb32SLXfhLW1hunq6xoc4j82/rdS5Uad8lCLEjsixrc9i2hefZSA7NdaJ+ AQgODgpC2vdQJJw== 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. The closure-based API existed to avoid passing around untyped pointers. The following patches add find and entry operations that need to store the returned pointer in entry objects and pass it between internal functions, where the closure style does not scale. Change `load` to return the pointer directly, establishing the style used for the rest of the series. Pointers are still only converted to references at the public API boundary. Reviewed-by: Daniel Almeida Assisted-by: LLM Signed-off-by: Andreas Hindborg --- rust/kernel/xarray.rs | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/rust/kernel/xarray.rs b/rust/kernel/xarray.rs index e8082df2b479..a14f874ad630 100644 --- a/rust/kernel/xarray.rs +++ b/rust/kernel/xarray.rs @@ -213,28 +213,25 @@ 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()?)) + #[inline] + fn load(&self, index: usize) -> Option> { + XArrayState::new(self, index).load() } =20 /// Provides a reference to the element at the given index. + #[inline] 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. + #[inline] 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 Sat Sep 26 10:01:18 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 1577E44AB8A; Wed, 2 Sep 2026 13:28:11 +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=1788355692; cv=none; b=bSg66Yaw4usI2vGYYVwalbfTMWeQxbrnj2K3RrBB73+QN79qiiOLv6GXWkvYOmF96ZrxwUnsZH4mPEG/xPXOFGcyxauCmcetHvCH9k5xLbRACOlGrLI/29F8wpb0LJO9vdJlBXRJpkOwQiGX1IjncOgxZmi59l5NlDj7r9trSg0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788355692; c=relaxed/simple; bh=2P+u2evSNuuMf4FBE5dnzxmrTE45UpvZNO/XGsFxtzk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=hgO3khOshxpzexf6WyzUHpgABXoEPe2AhPzRyc0iijEAqao7zFgFXSdOaEXzoacaHKhPGpCCsepPD23MIvQSey8MmbgVqFcXiit1zeNu8wvgvGjWRlHTMfEp6w9Yv5+kJHOcsIVGqacfGDWwvQ0Rs6dN2s49zJsSmkp4rbSfE5U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=YXqN/kIe; 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="YXqN/kIe" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8F2BF1F000E9; Wed, 2 Sep 2026 13:28:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788355691; bh=VS9LQdp7NkiQgnxIwqzDIraeCmzg3HvYohE2ygTY9TU=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=YXqN/kIeNaq+lJzMUoBaz8Kp/YKh+8m+uwOyVd1GpVg9fcelm2bjLuRMOiZgpTXRa X7PDoN8gF/M0QkLGlNi3utM0B9qHJd5hru4Zn6q1SLXNxLoudl/9mNMwo0zpR8rUTC 8oQv6zu8D4IYIDmz4xrseArNbIskD96HZr2rHUSZYfDFdEhZklp6I4eu19G6dFfSC+ mahtDTJywL8A4eLKkKCm2Hl3nkFOUWB2VcONEk9aEacaWZaDiHgZ8w1+CK0kUecT7I obKAZR7KyWJDi5slZvofnpFHKPOUv04vGzyyhKXB7A+SaLM13JyJYscUGhX2WppEr/ OAkX3fE7rryjg== From: Andreas Hindborg Date: Wed, 02 Sep 2026 15:26:02 +0200 Subject: [PATCH v5 06/12] 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: <20260902-xarray-entry-send-v5-6-d18adae40708@kernel.org> References: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> In-Reply-To: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> To: Tamir Duberstein , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= , Matthew Wilcox , Andrew Morton , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Christoph Lameter , David Rientjes , Roman Gushchin Cc: Andreas Hindborg , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-mm@kvack.org X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=5938; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=2P+u2evSNuuMf4FBE5dnzxmrTE45UpvZNO/XGsFxtzk=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqmCQeZh+yM/Lo9ujPEXm7kNCxhZn06Kgb64T24 Hz94PDsAXGJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCapgkHgAKCRD6UCkIqsW9 0D28EACu5MO7rnm5jy8eTjtmuVUmSYK+/hF/m9SvPkSmDggfcQXwjphQFoCr7jQejjUOgk7ggmq yGYXHIBX+K8e26yBI9+7plMGEvv0yWJDNkLr4IVi4GLoHKwltxM1xgSD4rkI9WoFOSMmKHTzqVP 7zojUxmZy7xDeUScIqIjL+2s8fTP+Fs+5fbojtQ+fC4ZOw7cXvbGACxDAGUUdCnaJJ8/ucw3RIA H0PmMspSofYJNXHDA5bJq/ox2EmHUHVNczPp5y0YxL8/HTBrn0ZAkS6jrQAWfud0PY244gHY+dc QCbi6+jMMsJUli+SZjAo0LXL6axlD51AoGLW6WeTm8Jjlzw47WknY0X01QrLdoO/noXs47TlN+C CDtjAyCQ7hS08Mhmhbu0K99y32hLVegyR54hxE/O3RhE1r0uGHygQEBLI9DhG65Mf/j5ZrrfFOa MYz2eaciQTsIKzghYIBdRbq8tysHzC0nLqpZgA1rceS/zjsp+HGncK77Uw3VF4YRPfsEdBoTFc7 INUZ9u2NQxevOsVLwZU2YqG4g3DseAhxtJBFUx+UKIq4uO5uZKnK+bbhVpwGxDOKiCGoR82+95X 7UdmA4bFSoQ7NjUEN/K9C7nNm3GDEXx0D3EpxjB87XReQrNzhNe+wICVgGiuwt8RCML79ZV17sA cw7CTQA0lGs+y/A== 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`. Assisted-by: LLM Signed-off-by: Andreas Hindborg --- rust/kernel/xarray.rs | 101 ++++++++++++++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 101 insertions(+) diff --git a/rust/kernel/xarray.rs b/rust/kernel/xarray.rs index a14f874ad630..9993783fc854 100644 --- a/rust/kernel/xarray.rs +++ b/rust/kernel/xarray.rs @@ -219,6 +219,22 @@ fn load(&self, index: usize) -> Option= > { } =20 /// Provides a reference to the element at the given index. + /// + /// # Examples + /// + /// ``` + /// # use kernel::{prelude::*, xarray::{AllocKind, XArray}}; + /// let xa =3D KBox::pin_init(XArray::>::new(AllocKind::Allo= c1), GFP_KERNEL)?; + /// let mut guard =3D xa.lock(); + /// + /// // Expanding an empty `Alloc1` array stores an internal zero entry= at + /// // index 0. It must not be visible through the API. + /// guard.store(5, KBox::new(0xcafeu32, GFP_ATOMIC)?, GFP_ATOMIC)?; + /// assert_eq!(guard.get(0), None); + /// assert_eq!(guard.find_next(0).map(|(i, v)| (i, *v)), Some((5, 0xca= fe))); + /// + /// # Ok::<(), kernel::error::Error>(()) + /// ``` #[inline] pub fn get(&self, index: usize) -> Option> { let ptr =3D self.load(index)?; @@ -234,6 +250,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(usize::MAX) + } + + /// 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: @@ -359,6 +436,30 @@ fn load(&mut self) -> Option> { // SAFETY: `xa_zero_to_null` only inspects the value of `ptr`. NonNull::new(unsafe { bindings::xa_zero_to_null(ptr) }.cast()) } + + fn load_next(&mut self, max: usize) -> Option<(usize, NonNull)= > { + 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. + let ptr =3D unsafe { bindings::xas_find(&raw mut self.state, m= ax) }; + if ptr.is_null() { + break None; + } + + // Unlike the normal API, `xas_find` does not filter out inter= nal entries. Arrays + // created with [`AllocKind::Alloc1`] store `XA_ZERO_ENTRY` at= index 0 when they + // are expanded from empty. Skip zero entries and continue the= search, like the + // `xas_retry` loop in `xa_find` does. Retry entries cannot be= observed here + // because they require concurrent modification of the array, = and we hold the + // lock. + // + // SAFETY: `xa_zero_to_null` only inspects the value of `ptr`. + if let Some(ptr) =3D NonNull::new(unsafe { bindings::xa_zero_t= o_null(ptr) }) { + break Some((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 Sat Sep 26 10:01:18 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 EAFE947CC94; Wed, 2 Sep 2026 13:27:59 +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=1788355683; cv=none; b=PLlJl0PnoOsMoogud66PS8SVFU8kkUo8GeOFBfhdI1Kieztku+QXdV6FsFQuEsvzH8Fb/YGcZKSqrQRuHkzp0WCJhaAolLdY2R8OwyKfONLgyXmOoQcNXiMd19b0CRVeRgM4KZgggdjANmfDP4CCJDAxzNntaP5hml5N9aB1igE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788355683; c=relaxed/simple; bh=N0seg49LGL+ncCSnIaEcZdb0Lz2Cj3067QB2ZoDt4f8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=lFdpIWEeVyWW24SZRkjO9ez/PQRFIaAAi0s3/cuztspPRkj8nXiRPG9LjkLju60/y3ZCAZWI9lSJUjjmWHl2Sn+exrFVhb30MvPMeFDDxcvbyJXLzF0RKSTJ3aCGkrA1h/L4GRTRvNO7iRGtXexVNYiR74XFBQ52G0FSa/nZNGg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Sn6LU0vz; 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="Sn6LU0vz" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 390961F000E9; Wed, 2 Sep 2026 13:27:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788355678; bh=TtGXfe3UgrJr2ur/bX83JZ7bo7tBV7HzFH0EsANUt8I=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=Sn6LU0vzyBw7YCgbOTekoR56JeLfGlyr9O/NrQbQcRubm7gFSB3LYs/mIR0TRsK+A EtAb/CnnubejXMtKLlQdL74bc8Pfp4JLsGSBITYM/2ajlew/IpVT+/2QAl2TwDXmsc ifKjTJpGdFcxOF2Z0pFmRl2V7joTMWuZV4ZGL9n5PLRVLUpvEQdGPLyLPIVUvNAFqr 3G1E6/xaQCkPFHRSF4Q8N0evXGOP8wgLrDiAcqSqJWmxJbCF4Hh/1dddYd1vtDsO1q X/9bFUtOU9/hJyo8g4O4ZzioXpOFEUTuto15ImI2EMrqX6Yh5TuWMTmu7pTD+0iWAv /5Ch2dEsGIUTw== From: Andreas Hindborg Date: Wed, 02 Sep 2026 15:26:03 +0200 Subject: [PATCH v5 07/12] 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: <20260902-xarray-entry-send-v5-7-d18adae40708@kernel.org> References: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> In-Reply-To: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> To: Tamir Duberstein , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= , Matthew Wilcox , Andrew Morton , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Christoph Lameter , David Rientjes , Roman Gushchin Cc: Andreas Hindborg , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-mm@kvack.org X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=25013; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=N0seg49LGL+ncCSnIaEcZdb0Lz2Cj3067QB2ZoDt4f8=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqmCQftwjyRwwB7slItWIwhGpZLWXd6654Bd8iE 29IpNLZp/KJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCapgkHwAKCRD6UCkIqsW9 0EtnD/4x5CwgZg4JMtMZFlVFu8tlgPVC7TMNG9+wpGGez3Q4UvyKtuQGK6Ub2Om3DsS9DwHfKhV PuZTgn+Iwy2yp5GNaMz04CIUGDhZrBWtCjAhmKtsh+diz7ZiWeBsatPeW1WaH0DzkJmTeY175ph 1WgSn1lPiU8KNwOEvju2G7yBtd7K/uq925aP4hwoVR0PcQGASj6OYGPA4O3EweBek+d2RavtotG +dKXHbHA3FbGw5Ipg6yAfNOTGMCH2ZJSzMzMaSmQSjNP68H1WCtlkm/qpXjpjcr21K9tXw5MGOJ A3vAurUriLWqxwNs8VvHf4O1GrUx7ufRstDqQIFYLLwB1WfipIuddyNy3OfLsyDRp9CISlmAZdj UUM9y4L4NB5KPSkZXBk4elN4sfytxeyoypkX/mtgH7CsSN8SE+xDVrKAR/SBONF+7deUhvOYZi9 tTotY39SPiO0cS1yd+JTsEfuUgag5RvDTjOux9fFtFWxippWoGc6/vXPI4rbsFN72kWiMcHotQh GK4Xiba9F5KwaElBLgHbIhJMn5ZR6L8mdDBQGrvpaRBs3nwBBzCL+PBhDt2GTi9IDEsMyC8frA3 fnji8J//iopnqXd6hQKVolrmbPhWKBuLpgH2d7dTiZgg18+yZxdongcLsFOJOBOtCzRuClOPxK+ FRCBKdq+HkX0LHw== 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. `Guard::entry` returns an `Entry` that is either `Vacant` or `Occupied`. The functionality provided by the two entry types is motivated by the needs of the Rust null block driver memory backing implementation: - `VacantEntry::insert` inserts a value and returns a borrow of it. `VacantEntry::insert_entry` inserts a value and returns an `OccupiedEntry` instead, for callers that need to perform further operations on the slot after insertion. - `OccupiedEntry` provides access to the value through `Deref` and `DerefMut`, replaces the value through `insert` or `swap`, and removes it from the array through `remove`. - `Entry::is_occupied` and the `index` methods support conditional logic and bookkeeping in callers. - `into_guard` releases the borrow of the slot and returns the underlying guard, so a caller can continue operating on the array without dropping the lock. - `Guard::find_next_entry` and `Guard::find_next_entry_circular` return an `OccupiedEntry` for the next occupied slot, the latter wrapping around at the end of the array. `Guard::insert_entry` combines lookup and insertion into one operation. 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 static inline C functions that are not directly accessible from Rust. Also update MAINTAINERS to cover the new rust files. Assisted-by: LLM Signed-off-by: Andreas Hindborg --- MAINTAINERS | 1 + rust/bindings/bindings_helper.h | 1 + rust/helpers/xarray.c | 10 ++ rust/kernel/xarray.rs | 174 ++++++++++++++++++- rust/kernel/xarray/entry.rs | 362 ++++++++++++++++++++++++++++++++++++= ++++ 5 files changed, 544 insertions(+), 4 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 8014b9f8253ed..b2c9911b88d1d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -29347,6 +29347,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/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 419e6b74fedc3..5dda2bb36e3c2 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -121,6 +121,7 @@ const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ROTATIO= NAL =3D BLK_FEAT_ROTATIONAL const fop_flags_t RUST_CONST_HELPER_FOP_UNSIGNED_OFFSET =3D FOP_UNSIGNED_O= FFSET; =20 const xa_mark_t RUST_CONST_HELPER_XA_PRESENT =3D XA_PRESENT; +const xa_mark_t RUST_CONST_HELPER_XA_FREE_MARK =3D XA_FREE_MARK; =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; diff --git a/rust/helpers/xarray.c b/rust/helpers/xarray.c index 79799c55c3d73..d7548a17ae0d0 100644 --- a/rust/helpers/xarray.c +++ b/rust/helpers/xarray.c @@ -27,7 +27,17 @@ __rust_helper void rust_helper_xa_unlock(struct xarray *= xa) return xa_unlock(xa); } =20 +__rust_helper void *rust_helper_xas_result(struct xa_state *xas, void *cur= r) +{ + return xas_result(xas, curr); +} + __rust_helper void *rust_helper_xa_zero_to_null(void *entry) { return xa_zero_to_null(entry); } + +__rust_helper 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 9993783fc854a..cf7248bddb8b9 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::build_assert, // error::{ + to_result, Error, Result, // }, @@ -250,6 +256,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(usize::MAX) } @@ -311,6 +346,66 @@ 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(usize::MAX)?; + 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); + + let (_, ptr) =3D state.load_next(usize::MAX).or_else(|| { + state.restart_at(0); + state.load_next(index) + })?; + + Some(OccupiedEntry { state, ptr }) + } + /// Removes and returns the element at the given index. pub fn remove(&mut self, index: usize) -> Option { // SAFETY: @@ -386,10 +481,6 @@ pub fn store( /// - `state` is always a valid `bindings::xa_state`. /// - `state.xa` aliases the xarray reachable through `guard`. pub(crate) struct XArrayState { - // The borrow is held to guarantee exclusive access to the array. It is - // not read until a later patch adds `into_guard`, so silence the dead - // code warning until then. - #[expect(dead_code)] guard: R, state: bindings::xa_state, } @@ -460,8 +551,83 @@ fn load_next(&mut self, max: usize) -> Option<(usize, = NonNull)> { } } } + + 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) }) + } + + /// Resets the state so the next operation walks the tree from the roo= t, + /// starting at `index`. + fn restart_at(&mut self, index: usize) { + self.state.xa_index =3D index; + self.state.xa_node =3D bindings::XAS_RESTART as *mut bindings::xa_= node; + } } =20 +// Operations that modify the array require exclusive access to the guard,= so +// they are only implemented for `XArrayState<&mut Guard>`. +impl<'a, 'b, T: ForeignOwnable> XArrayState<&'b mut Guard<'a, T>> { + /// Stores `new` at the index of this state, returning the previous en= try. + /// + /// The slot at the index of this state must be occupied. Storing to an + /// occupied slot is a simple pointer swap that cannot fail, by design= of + /// the xarray data structure. + fn replace(&mut self, new: *mut c_void) -> *mut c_void { + // 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 old =3D unsafe { + bindings::xas_result( + &raw mut self.state, + bindings::xa_zero_to_null(bindings::xas_store(&raw mut sel= f.state, new)), + ) + }; + + // SAFETY: `old` is a valid return value from `xas_result`. + let errno =3D unsafe { bindings::xa_err(old) }; + + // NOTE: Storing to an occupied slot never fails. This is by desig= n of + // the xarray data structure. If a slot is occupied, a store is a + // simple pointer swap. + debug_assert!(errno =3D=3D 0); + + old + } + + fn insert(&mut self, value: T) -> Result<*mut c_void, StoreError> { + let new =3D T::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 `T::into_foreign`. + unsafe { bindings::xas_store(&mut self.state, new) }; + + // All arrays created by this abstraction have `XA_FLAGS_TRACK_FRE= E` set, so the + // free mark must be cleared for a newly occupied index, as `__xa_= store` does. + // This is a no-op if the store above failed. + // + // SAFETY: `self.state` is a valid `xa_state` by the type invarian= t, and we hold + // the lock on the xarray it refers to. + unsafe { bindings::xas_clear_mark(&self.state, bindings::XA_FREE_M= ARK) }; + + self.status().map(|()| new).map_err(|error| { + // SAFETY: `new` came from `T::into_foreign` and `xas_store` d= oes not take + // ownership of the value on error. + let value =3D unsafe { T::from_foreign(new) }; + StoreError { value, error } + }) + } + + /// Consumes `self` and returns the inner `&mut Guard`. + #[inline] + 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 0000000000000..c6c5385e6b4f9 --- /dev/null +++ b/rust/kernel/xarray/entry.rs @@ -0,0 +1,362 @@ +// 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); + /// drop(entry); + /// + /// 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>(()) + /// ``` + #[inline] + 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. + #[inline] + 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>(()) + /// ``` + #[inline] + 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. + #[inline] + 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 { + let ptr =3D self.state.replace(core::ptr::null_mut()); + + // 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>(()) + /// ``` + #[inline] + 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) }; + + let old =3D self.state.replace(new); + + // SAFETY: + // - `old` came from `T::into_foreign`. + // - As this method takes `self` by mutable reference, the lifetim= es of any + // [`T::Borrowed`] and [`T::BorrowedMut`] we have created must h= ave 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: ForeignOwnable =3D &'b U, BorrowedMut<'b> =3D &'b = mut U> + 'b, + U: 'b, + { + use core::ops::DerefMut; + core::mem::swap(self.deref_mut(), other); + } +} + +impl<'a, 'b, T, U> core::ops::Deref for OccupiedEntry<'a, 'b, T> +where + T: ForeignOwnable =3D &'b U, BorrowedMut<'b> =3D &'b mut = U> + 'b, + U: 'b, +{ + type Target =3D U; + + fn deref(&self) -> &Self::Target { + // SAFETY: `ptr` came from `T::into_foreign`. + unsafe { T::borrow(self.ptr.as_ptr()) } + } +} + +impl<'a, 'b, T, U> core::ops::DerefMut for OccupiedEntry<'a, 'b, T> +where + T: ForeignOwnable =3D &'b U, BorrowedMut<'b> =3D &'b mut = U> + 'b, + U: 'b, +{ + 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 Sat Sep 26 10:01:18 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 119F449F11C; Wed, 2 Sep 2026 13:27:33 +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=1788355659; cv=none; b=H82MeUNNq6QACYMyEgGTfpFpNvpepDlWH8smd5NplvhH9XOMZmx3yzaks4WJEq8Vu8TFq1FbgJj9GUlolAfW1DNT58HmUd+wjqacnNVkMB7ktR6U1Ks2lgzJMe+A6Vd1giKo5vRCFmL9+PuOhEYnkx8Y4ZB3eZMv2A2MB7ZVGnY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788355659; c=relaxed/simple; bh=RjS4bVXdE61HMAu4upNw+EYFUe3wUNyGbYdxylQjok8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=duyliy2ZZTWPa7vZUxFknTWoJ3qnzlnla02Vmog175svdSI7B9Qbe312QB6dfnIVZdM2zf4OaGMSASNeLT5v6eFtduxn6jHGpR/hCmFw9MX+F4xs8Oo7ERw/uSoRkq7IOhvsPX6/hKhbC02X5m1PWcb7+dH8CbaqTCfcKZ7k8Bo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=dGbKuYye; 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="dGbKuYye" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 618841F00A3E; Wed, 2 Sep 2026 13:27:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788355652; bh=lA7FWSljb2LQaxATt6lejnbnwqONrNFpZpcIwgRu0wY=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=dGbKuYyeTksF1KkirkhK/VWSN3TLarmu2BveEY9w1msKkaZH5gfoUBQgQ1C1Ms2sJ G+l+2dSfcgRE1zgpYaLtkXj2zDjEucv0ANfMUL1N453hQhbNVT0hr2wx39o4Atgg0B fJOdJtDmuat208RIbRwL7XvKnVuOjYXCOoyrMnWuHszrHX2mJSo5fWTSnK46vQBh9d 7CHhZY2DaADC85mE9XjivccZmYLt/s2CKRJwPKeibQ/V9XJhs8EjTj0lbdB3xFMsiZ 0pHnAoPzidSxQ6YpKz4N/yIULerQF+C7/KEGuG3yL7ttFNLvSWxKUpNbXWKVYV+/mM Sa/YdduBbyPFw== From: Andreas Hindborg Date: Wed, 02 Sep 2026 15:26:04 +0200 Subject: [PATCH v5 08/12] 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: <20260902-xarray-entry-send-v5-8-d18adae40708@kernel.org> References: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> In-Reply-To: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> To: Tamir Duberstein , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= , Matthew Wilcox , Andrew Morton , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Christoph Lameter , David Rientjes , Roman Gushchin Cc: Andreas Hindborg , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-mm@kvack.org, Vlastimil Babka , "Liam R. Howlett" , Lorenzo Stoakes X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=19705; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=RjS4bVXdE61HMAu4upNw+EYFUe3wUNyGbYdxylQjok8=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqmCQg11lMGYGlRZeIE/G3smnqwEx46oHVePm6J HYpVyUg7xWJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCapgkIAAKCRD6UCkIqsW9 0IpxEACe00crXipmdKSsy/VkKKKxGhySGh7cNmoCQUxbK3okXnfzOR41t3JdSYQEzS4ZkVAgrNZ 0FQSzuJB5y7p0dJqe0Dki2IXge4fcjKYIxnTbHwD5ik8uv/ygiIes07NhGeYP9i2bJtCKFz5rg1 TcKtiaT71OHHCWIe48/kJrg0s8iv/XNSR6WvpsfdYYMUR7y17unWC28TVP5cXTvPtcHZYRiYos3 5d/vVg0VU7UFGedaioSU6xv+xhNgg2zGgG6rR8Z+q2YYDMkr8Ddv/E/2lQ6/xMmSDLpK7fMQMf8 YHBysrfzQbd9IzQdtoomdJFwRXwtR4EjSVXP+oLf3L+Cekbk+0gsELq8M5EbJCbjxJvgG2SvgHz nkSNIYFzkI7y7c6Ct260B0ALSutL6QhNtVQlGhVlw8Ow/Sij7IUhfWUGkLmhELrBEJIWVqEv+zs /EgyOsA72GnvM31mnFwk6UbAykdibbvY56tGatYwSUJaXEAO0GZ2nVk1aZChIaAp57VO6mepE5Z m1Fo8I854GnNNlgzWO2FZctE0o5Hfn01mWKUrP+qnHifaOEhBgFzE0+IsCU555FalncqHtHBbJe q5iV3ZVkapMe8UUVRoXJO+fqIiRkUuYZuB9volWr/AsbF6wrmFyfrLsGAffNmDnEcBYm+t5eoX5 FLG31NRui7k6EtQ== 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 Assisted-by: LLM Signed-off-by: Andreas Hindborg Acked-by: Vlastimil Babka (SUSE) --- rust/kernel/mm.rs | 1 + rust/kernel/mm/sheaf.rs | 466 ++++++++++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 467 insertions(+) diff --git a/rust/kernel/mm.rs b/rust/kernel/mm.rs index 4764d7b68f2a7..1aa44424b0d53 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 0000000000000..8f310a06d8404 --- /dev/null +++ b/rust/kernel/mm/sheaf.rs @@ -0,0 +1,466 @@ +// 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. +/// +/// # Context +/// +/// Dropping the last reference to a `KMemCache` destroys the cache via +/// `kmem_cache_destroy`, which may sleep. [`Sheaf`] and [`SBox`] instances +/// created from the cache each hold a reference, so the last reference may +/// be dropped when one of those is dropped. The last reference must not be +/// dropped from a context where sleeping is not allowed. +/// +/// # 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, +} + +// SAFETY: `KMemCache` owns a `kmem_cache`, which is internally +// synchronized and has no thread affinity. The cache may be destroyed fro= m a +// thread other than the one that created it. +unsafe impl + Send> Send for KMemCache {} + +// SAFETY: All operations available through `&KMemCache` are +// internally synchronized by the C side, so the cache may be used from +// multiple threads concurrently if the objects it manages can be sent bet= ween +// threads. +unsafe impl + Send> Sync for KMemCache {} + +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; + + // With an alignment of zero, the slab allocator only guarantees + // `ARCH_SLAB_MINALIGN`, which may be smaller than the alignment o= f `T`. + args.align =3D core::mem::align_of::().try_into()?; + + // 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, + }) + } + + #[inline] + 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`. + /// + /// The initializer is applied to newly allocated objects when they are + /// allocated from a sheaf via [`Sheaf::alloc`]. The cache itself has = no + /// constructor. The initializer should set all fields to their defaul= t 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. +/// +/// A sheaf holds a reference to the [`KMemCache`] it was created from. +/// Dropping the sheaf may thus drop the last reference to the cache and +/// destroy the cache, which may sleep. See the `# Context` section of +/// [`KMemCache`]. +/// +/// # 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, +} + +// SAFETY: A prefilled sheaf is exclusively owned by the caller and has no +// affinity to the CPU or thread that created it: `kmem_cache_alloc_from_s= heaf` +// does not touch percpu state, and `kmem_cache_return_sheaf` reattaches t= he +// sheaf to the CPU that is current at return time. Thus the sheaf may be = sent +// to another thread if the objects it manages can. +unsafe impl + Send> Send for Sheaf {} + +// NOTE: `Sheaf` is deliberately not `Sync`. The C side mutates sheaf state +// without synchronization, relying on the caller's exclusive ownership. T= he +// mutable receivers of the methods on `Sheaf` enforce this exclusivity. + +impl> Sheaf { + #[inline] + 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()) + }; + + let ptr =3D NonNull::new(ptr.cast::())?; + + // SAFETY: + // - `ptr` is a valid, non-null pointer as it was just returned by= the + // cache. + // - The initializer is infallible, so an error is never returned. + unsafe { T::init().__init(ptr.as_ptr()) }.expect("Initializer is i= nfallible"); + + // 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. +/// +/// An `SBox` holds a reference to the [`KMemCache`] it was allocated from. +/// Dropping the `SBox` may thus drop the last reference to the cache and +/// destroy the cache, which may sleep. See the `# Context` section of +/// [`KMemCache`]. +/// +/// # 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>, +} + +// SAFETY: `SBox` owns a `T`. Sheaf allocated objects are ordinary slab +// objects that may be freed from any thread, so an `SBox` may be sent = to +// another thread if `T` can. +unsafe impl + Send> Send for SBox {} + +// SAFETY: `SBox` has no interior mutability, so sharing `&SBox` bet= ween +// threads only shares `&T`. +unsafe impl + Sync> Sync for SBox {} + +impl> Deref for SBox { + type Target =3D T; + + #[inline] + 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 { + #[inline] + 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 Sat Sep 26 10:01:18 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 4B09E49F123; Wed, 2 Sep 2026 13:27: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=1788355646; cv=none; b=PNQvKmczxUZPe85uQnk0bqG8YYLmadd8/TRYoXQKc+hKpwF2LUKsVmuccIb6/aa5LQeX2Pi/2ftZHSKZSFfqg9PlZ2VQ1nZwR9Duh0cI9oW8y4Id/Sfyu38gAqKB0imjxurv5H1RbQBb/dtv143aKdxKpfVlG0I+nKIUJ6gYlLk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788355646; c=relaxed/simple; bh=hSXv4LMWQCbypjAASJ+FoyrW2+2FNqHfuDlOIPK3ZZg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=UmFKlF1RSazaELRX90eE+vGJNSCF7DQePKlxHGqESx2/d6pYsWilEWaWIJeDIvqJwy85lEdjkCh9Cv4fZZ1fY96l8nRT8EWO7C/drbnhEqhHeXYrih0jcrRUIYAqMvUSlykXMfAFbPw0mhzEJ5G9B9AwNZ2t5wdoftLFuDfX+PM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=SSqE9wxP; 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="SSqE9wxP" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6FA3C1F00A3A; Wed, 2 Sep 2026 13:27:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788355639; bh=Un9ds6+NKCC6oaj+ZnxMv2FssWRr9ogFaViHgOUc1jY=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=SSqE9wxP38nP9gBIp4pO/g3IPIw1XL1Sxhh1NhA3N/XrzabRY0P+vX9fzkCGlsZtj 0ZBvEhDk3k5MW9wTyqWV7c4T+Ef3KTjy+8SLl2m39gkT0tXUawBh3fRTGvKyZFLd8n 0rt6ee1T4lOld3znaGrM1osxkelBSGuvgcJckmlbkzLJf6+eGNUcUdRpwcicpRFAUs JMRm7h3YpmxUUZxzvM2x8x72ieBvXyMAgWHETNQtWzg1dqfFJCZvpGVnFUsQeK+A0J g8NlvFqBHHCIxrkC/sldU1oKa+rk5DwVexvRt+Ok8pGKKaqRfJsDvuCLkJUpFyNnI1 7CUzDckSYkMeQ== From: Andreas Hindborg Date: Wed, 02 Sep 2026 15:26:05 +0200 Subject: [PATCH v5 09/12] 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: <20260902-xarray-entry-send-v5-9-d18adae40708@kernel.org> References: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> In-Reply-To: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> To: Tamir Duberstein , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= , Matthew Wilcox , Andrew Morton , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Christoph Lameter , David Rientjes , Roman Gushchin Cc: Andreas Hindborg , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-mm@kvack.org, Vlastimil Babka , "Liam R. Howlett" , Lorenzo Stoakes X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=26935; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=hSXv4LMWQCbypjAASJ+FoyrW2+2FNqHfuDlOIPK3ZZg=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqmCQhKOoD7Qw+Ml02pCZuMIZj7noY1O3xY70OM w1v6apvx5+JAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCapgkIQAKCRD6UCkIqsW9 0P/cEACp9buFkbtrQ2WUArAz/OwpG3jFQJPlt9CmqVhv2q/mMGeD+OMXW5IzS807+a5dIKcs0xD HL+77/wxWy9/GPUo2DA8l8yxfu0EdFEV530ul4WzXr/yg+W/9WYccghL7g3w5SbeYq3QQWmDciR iW1vZ4B1mpxYfKMwCCpTZAiQk3Qd68/7dkdqpIdqGIJVPxXzWsVr6f7hzVTHYccDXmztw5FEHi5 8a3MkYaZoiBKIL/QwbbvF1XIxHIuZDnuMGS9pNbUAygcooroJehYx755axryK39qgxIdAbJKQ31 zA2pYr5bimBf/ijQPi/vdQCydDkQLQjWV84EC4nItDKDz6Tsv5fTGjmh52O1yuZ2t2XM/p0EG+F bf0bI37HR1jBd8gJR+kcvtodncEX9htXSqO7vfG6HMR85UWXYRcEyOVvioO8EVPxehZy8l8sv9L ++D379qatS+hzoW2w4gbk5/LYulj4AMwsoxlMQVsNwe7WZukspkdakzOTWAahSiC+Gnf7dhuaW/ yjOMKZUY+8gw5gyxwgVlxs1Tk0vAT8dkIqouu0UZEoZcQziy2M/vvDQ5Hk886t/BbHx5CkM37Sg HJ3RJRUi7ZMdrcXFHUliqsaQKaz2FAdhFwcnAUGplCVFsdKdctlpWqT14WRixzstOGVjYs/noX7 aDeVMMaZGhNNf9A== 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 Assisted-by: LLM Signed-off-by: Andreas Hindborg Acked-by: Vlastimil Babka (SUSE) --- mm/slub.c | 4 + rust/kernel/mm/sheaf.rs | 401 ++++++++++++++++++++++++++++++++++++++++++--= ---- 2 files changed, 356 insertions(+), 49 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 0337e60db5ac..d81dbf2abb86 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -5101,6 +5101,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() @@ -5156,6 +5157,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 @@ -5211,6 +5213,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() @@ -5249,6 +5252,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 8f310a06d840..f6df856c7f4b 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,114 @@ =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, +} + +// SAFETY: The C `kmem_cache` is internally synchronized and has no thread +// affinity, so a `KMemCache` may be sent to another thread if the obje= cts +// it manages can. +unsafe impl + Send> Send for KMemCache {} + +// SAFETY: All operations available through `&KMemCache` (creating shea= ves +// and allocating objects) are internally synchronized by the C side, so t= he +// cache may be used from multiple threads concurrently if the objects it +// manages can be sent between threads. +unsafe impl + Send> Sync for KMemCache {} + +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, + }) + } + + #[inline] + 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. /// @@ -92,9 +208,9 @@ /// /// # Context /// -/// Dropping the last reference to a `KMemCache` destroys the cache via +/// Dropping the last reference to a `KMemCacheHandle` destroys the cache = via /// `kmem_cache_destroy`, which may sleep. [`Sheaf`] and [`SBox`] instances -/// created from the cache each hold a reference, so the last reference may +/// created from the handle each hold a reference, so the last reference m= ay /// be dropped when one of those is dropped. The last reference must not be /// dropped from a context where sleeping is not allowed. /// @@ -103,23 +219,23 @@ /// - `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 -// SAFETY: `KMemCache` owns a `kmem_cache`, which is internally +// SAFETY: `KMemCacheHandle` owns a `kmem_cache`, which is internally // synchronized and has no thread affinity. The cache may be destroyed fro= m a // thread other than the one that created it. -unsafe impl + Send> Send for KMemCache {} +unsafe impl + Send> Send for KMemCacheHandle {} =20 -// SAFETY: All operations available through `&KMemCache` are +// SAFETY: All operations available through `&KMemCacheHandle` are // internally synchronized by the C side, so the cache may be used from // multiple threads concurrently if the objects it manages can be sent bet= ween // threads. -unsafe impl + Send> Sync for KMemCache {} +unsafe impl + Send> Sync for KMemCacheHandle {} =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 @@ -171,8 +287,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, )?) @@ -199,11 +314,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 { @@ -215,18 +330,19 @@ 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 #[inline] 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 @@ -239,13 +355,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 /// @@ -270,13 +386,34 @@ fn drop(&mut self) { pub trait KMemCacheInit { /// Returns an initializer for creating new objects of type `T`. /// - /// The initializer is applied to newly allocated objects when they are - /// allocated from a sheaf via [`Sheaf::alloc`]. The cache itself has = no - /// constructor. The initializer should set all fields to their defaul= t or - /// initial values. + /// The initializer is applied to newly allocated objects when they ar= e allocated from a sheaf + /// via [`Sheaf::alloc`]. The initializer should set all fields to the= ir default or initial + /// values. 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`]. @@ -287,17 +424,28 @@ 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. /// -/// A sheaf holds a reference to the [`KMemCache`] it was created from. -/// Dropping the sheaf may thus drop the last reference to the cache and -/// destroy the cache, which may sleep. See the `# Context` section of -/// [`KMemCache`]. +/// A sheaf created from a [`KMemCacheHandle`] holds a reference to the +/// handle. Dropping the sheaf may thus drop the last reference to the han= dle +/// and destroy the cache, which may sleep. See the `# Context` section of +/// [`KMemCacheHandle`]. /// /// # Invariants /// @@ -305,10 +453,11 @@ 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 // SAFETY: A prefilled sheaf is exclusively owned by the caller and has no @@ -316,13 +465,13 @@ pub struct Sheaf> { // does not touch percpu state, and `kmem_cache_return_sheaf` reattaches t= he // sheaf to the CPU that is current at return time. Thus the sheaf may be = sent // to another thread if the objects it manages can. -unsafe impl + Send> Send for Sheaf {} +unsafe impl + Send, A> Send for Sheaf<'_, T, A> {} =20 // NOTE: `Sheaf` is deliberately not `Sync`. The C side mutates sheaf state // without synchronization, relying on the caller's exclusive ownership. T= he // mutable receivers of the methods on `Sheaf` enforce this exclusivity. =20 -impl> Sheaf { +impl<'a, T: KMemCacheInit, A> Sheaf<'a, T, A> { #[inline] fn as_raw(&self) -> *mut bindings::slab_sheaf { self.sheaf.as_ptr() @@ -346,6 +495,39 @@ 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= `] @@ -382,7 +564,44 @@ pub fn alloc(&mut self) -> Option> { } } =20 -impl> Drop for Sheaf { +impl<'a, T: KMemCacheInit> Sheaf<'a, T, Dynamic> { + /// 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()) + }; + + let ptr =3D NonNull::new(ptr.cast::())?; + + // SAFETY: + // - `ptr` is a valid, non-null pointer as it was just returned by= the + // cache. + // - The initializer is infallible, so an error is never returned. + unsafe { T::init().__init(ptr.as_ptr()) }.expect("Initializer is i= nfallible"); + + // 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, 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 @@ -399,6 +618,40 @@ 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 { + #[inline] + 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 @@ -408,10 +661,10 @@ fn drop(&mut self) { /// The contained `T` is initialized when the `SBox` is returned from allo= c and /// dropped when the `SBox` is dropped. /// -/// An `SBox` holds a reference to the [`KMemCache`] it was allocated from. -/// Dropping the `SBox` may thus drop the last reference to the cache and -/// destroy the cache, which may sleep. See the `# Context` section of -/// [`KMemCache`]. +/// An `SBox` allocated from a [`KMemCacheHandle`] backed sheaf holds a +/// reference to the handle. Dropping the `SBox` may thus drop the last +/// reference to the handle and destroy the cache, which may sleep. See the +/// `# Context` section of [`KMemCacheHandle`]. /// /// # Invariants /// @@ -420,7 +673,7 @@ fn drop(&mut self) { /// - The object remains valid for the lifetime of the `SBox`. pub struct SBox> { ptr: NonNull, - cache: Arc>, + cache: CacheRef, } =20 // SAFETY: `SBox` owns a `T`. Sheaf allocated objects are ordinary slab @@ -432,6 +685,56 @@ unsafe impl + Send> Send for SBox<= T> {} // threads only shares `&T`. unsafe impl + Sync> Sync for SBox {} =20 +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. + /// + /// This method is only intended for objects allocated from a static c= ache. + /// Calling it on an `SBox` backed by a [`KMemCacheHandle`] leaks a + /// reference on the handle, preventing the cache from ever being + /// destroyed, as [`static_from_ptr`] cannot restore the reference. + /// + /// [`static_from_ptr`]: SBox::static_from_ptr + pub fn into_ptr(self) -> *mut T { + debug_assert!(matches!(self.cache, CacheRef::Static(_))); + 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. + /// - `cache` must be a statically allocated cache that is never destr= oyed. + /// - `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()) }, + ), + } + } +} + impl> Deref for SBox { type Target =3D T; =20 --=20 2.51.2 From nobody Sat Sep 26 10:01:18 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 EBC874A092A; Wed, 2 Sep 2026 13:27:54 +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=1788355677; cv=none; b=pN6nmY4TQRh5lTD2pE8VRNaG7OZIIxjGKCL39e7YMJYQ/c2wbUCyfwTX+blgYWVtTdUo+e67do14a9391J95vws4IikRRPdHoIiXN43tect90PXRqd0i0rDRBa11fOb7eQxUenu9sSyo2/U91qrx/Z4OqwkDlnpx9krQTiLYQqQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788355677; c=relaxed/simple; bh=IJL8sHLJzETBX5EUCq0+XoQYAigxpIcbmnUp4kDolk0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ow5kMCdGWFtVwaQ4QlRyiYoRKrjcAkoLW4slNK6/UtfOps3umQObKCZ/Q4DOxFQaI94w2eSF+mn9cfXG9MNcbBnF0wh7RoxOR6UodAVU02N37hLQAJYlJ57u4htcVrBLNrEy/c78J1bUJPVSUcjuHd63khkmNvAhzcVRTj4j6ec= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=k+7PMWtr; 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="k+7PMWtr" Received: by smtp.kernel.org (Postfix) with ESMTPSA id ED25D1F00A3F; Wed, 2 Sep 2026 13:27:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788355672; bh=rmjpEvPISZ+t3PGfRKTnDAOa6lOnl7cuFJjli1+tyLs=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=k+7PMWtrOSFTyQ9F5g7M7pAPrvsEadjDIxkAlifrK3tS7HWsm2efcg1GN0UE8dXi/ J8nmGfqbi4ZBMR/I5JaAo/uWxbElKXGEmWPWvW2MDhPgO8jexUfhVlFda5mKtUwWhs btH5gqf8Z+qJ3mDS225R6aLd0Cs5RugRuIiASI+AdyQR3ySMWW42UQe1M48jeAzS/o fB6q2i/ah5Rfh9e6YzYh7jtevdOucodmF+3AKJ6Vc20FQWpSWmPwQyvYQ3PlpcrWTw 1iJD6F9WyYK1tvLap+od60/zIkt7WXHWYjJX1XJLoi4Ne9f6YvDoO0SsQ8tz6DQXtc CCydLNfXcKkLA== From: Andreas Hindborg Date: Wed, 02 Sep 2026 15:26:06 +0200 Subject: [PATCH v5 10/12] 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: <20260902-xarray-entry-send-v5-10-d18adae40708@kernel.org> References: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> In-Reply-To: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> To: Tamir Duberstein , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= , Matthew Wilcox , Andrew Morton , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Christoph Lameter , David Rientjes , Roman Gushchin Cc: Andreas Hindborg , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-mm@kvack.org X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1261; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=IJL8sHLJzETBX5EUCq0+XoQYAigxpIcbmnUp4kDolk0=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqmCQiFeVnhwTDdQFJRjCYOO1PxQcejmTfYtGJX zaKc7loM6SJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCapgkIgAKCRD6UCkIqsW9 0AS4D/4mR4N32k1U3pAh2rJfK5jfyROypSsF3fJL4XksWWspR996nY603YS/pWVLIEy1IvXUWiG zqBNIfTISK+5Gx4Zb1jq+4DqRo0sq1xW2/UYQyFZYdUXfAZ5LZQTcZa9CTnZmDTqKXZLb6nZ0MI cXa5QRrjyQluHCkg5ZWkVZ52B5rdcJZdknWPvvFNd73JOGc7bUNfxHvUC1W8x2nx86MTv0rePvL bbMOofBww0Ps/BY5MW+snKMunRkCvVyn3nr3+yYzLBkC9mCj76NoXooxUafImT2SMhTS+j/KkV8 gr5BizGqw4DTv9N/O8hzxe0raN/NkIdnAmUtXONK/DmjRBDL1dkl1xlHeFix6R6MWJjFcSK9SdC i6AV4GeoM7//WiIye3g5JDS22RbYfF3go+/RiKCgEgSTLZA6NkI6b/159rmlcPGZMmk6VEzcADO KmeclUmPOi6HKrWMP+NOymfrt04o0ji7XvH1qJ4ma3lITbaBkU8m7EafGDO0omxGmFCCr5eIzzi RrkCMszJ71vAm8fOhGeCYJnKFMtnWkjJJNyLwj/qmTOC6ZD5lDmcTd9b4yNcVuUjQLRMSJUZ6P3 QsXVwKBoA8ADBipBkSodYaBfCwnJRH9UGwYkK0x4jHKlUIYl6ScJzFxNrSttLGjX4q9XzoCB1jH m32JydtEphMt1uA== 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)" Assisted-by: LLM 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 976b9bd02a1b5..1cf0012b15ade 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 Sat Sep 26 10:01:18 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 96FC2434407; Wed, 2 Sep 2026 13:28:17 +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=1788355699; cv=none; b=DB79fb1/+2MsQcbTYn1C689wG9I6hTTneb2Q4RHAjWfFyEfHL6oCfQ+DOD/j8zYsmbbkcOAXM3+YDG0EOo7YNVozs5t1ck03BrxpTSBJ2s7tc40PAYG0laoSLIHozV0bOP7udL3Fs0ewBFpu6o3Gr4e0a3iVbKE8VnquiV4XzNY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788355699; c=relaxed/simple; bh=KQvqFkd73+KLhJE+UaIWBrkWmcMkBP46A3dBwLH0V1I=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=XfBFt3l7869bz4U8KrpUmsdSDE3rqRK+JcLRPAV1dSGXZCfbfRe4BT55gl++TPdUJADi2ku8ovXgEINYX0TKpSUbHAF8uECTy9+alJC/CEEBlibfqrslHcT8HHN6Qdpfzq6f8QElKlZmcsczRuAcvgJXAVs93DxcPOXGA3yIYfU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=DSA2LjIn; 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="DSA2LjIn" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A23881F00A3A; Wed, 2 Sep 2026 13:28:11 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788355697; bh=gWLIEeqPSv/ucpXrHg5udp/+YKo5YFuXiXIqJHSm6p8=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=DSA2LjInRx7tF+skCryAG5rdkUJluf9h4hq6bSgNqB5si0IxSMv25i0Cm8sHZdlml +rVmnLEBXPvSgSmTqKcWi0NiCutIynrb4iaMtHSya+Dnr8F+mWrzsssTfkBrOQbuS2 MCwtMZknfQ55t+E4DhtHeuPriH4tY3LHa/RUKnhniJdvaAaHCBiOA2XFgmA6j1JUdw s2I8s/Jkw+xOXvAXta80b2gshuxuHSOJdxh0N/RxpySeQFoYvdB+HV2lUrsLbFRIA9 XkugaOnbFRjOTI0xaJmkyQfHK6uxa0A/umy19SKUsSBmRfEBmRhejtBHcbJoKH2zsP p5H+DlqTrskEw== From: Andreas Hindborg Date: Wed, 02 Sep 2026 15:26:07 +0200 Subject: [PATCH v5 11/12] 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: <20260902-xarray-entry-send-v5-11-d18adae40708@kernel.org> References: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> In-Reply-To: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> To: Tamir Duberstein , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= , Matthew Wilcox , Andrew Morton , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Christoph Lameter , David Rientjes , Roman Gushchin Cc: Andreas Hindborg , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-mm@kvack.org, "Liam R. Howlett" X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=18837; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=KQvqFkd73+KLhJE+UaIWBrkWmcMkBP46A3dBwLH0V1I=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqmCQisXwWcJP4hFSKVWaVPMLw7FprpMsPG0D0r w7W518G/+OJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCapgkIgAKCRD6UCkIqsW9 0IcDEACbok7lfFfJRtX9YoIFrUa7T6ZB2oN/OL5NzGHvbMhMzESzb/FnHv0U/q4uewQaI57Ky4a UZOaGrMDy3YZYQkYmjk38DcD9Cn8iBH5PiDgBGqKEwVXxlnc04fHaDWhmql2lUytXKpaksDMNcO m2n324OQlQ/mA3pstUygOsyA0Q+F307TIHof1F8i+BdXJZz5IfdCFSFleef70w/diSsytJkRI+U +8JJaPHeAeIp+94zvbNdnSzDHC6z1zIGn0we9Fjb0A0oPZCdRom75oEnt3rOL3lojRFM767mkFW 0vvShSLY4V3sE5yTU2Id30gzW/8cisGn5zOEx3vj2VhGGu9tfy2IdJ40GifbXfTzLW/EDEfJwNK I6A4kY6QwvtMDFT9eKbSm0ZCYq8To7/AyqW48JLh8v0/aZfAvXFfUkYrzZHy2782k2QPTUrWA+a ypfo9K05uuLFhV6r3Dqk0/rkeJPPmAJMGTGgVe/iBSjyVIFC3FR6MzSnzIgDS2HCTlVXBXHLhlm F/cWI0HH6h0HN7K7wpOUp8mjce5E3FxynYVtrS1/Mo6Tpy4LTVMICuP/bxMbMXiGf8p0oYiVYJ3 jbmscZvUC92Yf66FA246oCxI8Ozby98MG8R7Tbli5eMB6WY7ip6glaurNiTkXM3KPFPvYj9tU8n HRj1q/FyfuD8XUw== 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)" Assisted-by: LLM 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 | 214 +++++++++++++++++++++++++++++++++++-= ---- rust/kernel/xarray/entry.rs | 27 +++-- 5 files changed, 215 insertions(+), 37 deletions(-) diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h index eae67015ce51a..c3699f12b070c 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 1cf0012b15ade..ddd67ce672f5c 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 5dda2bb36e3c2..ccbd92880dc88 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -132,6 +132,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 cf7248bddb8b9..87123ab96a92f 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,12 +24,18 @@ bindings, build_assert::build_assert, // error::{ + code::*, to_result, Error, Result, // }, ffi::c_void, fmt, + mm::sheaf::{ + KMemCache, + SBox, + StaticSheaf, // + }, types::{ ForeignOwnable, NotThreadSafe, @@ -36,12 +43,63 @@ }, }; 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. +/// +/// This type is `#[repr(transparent)]` as it is cast to and from pointers= to +/// the inner [`bindings::xa_node`]. +#[repr(transparent)] +pub struct XArrayNode { + node: Opaque, +} + +// SAFETY: A preallocated `xa_node` is opaque storage for the C XArray +// implementation, which moves nodes between CPUs freely. It is not tied to +// the thread that allocated it. +unsafe impl Send for XArrayNode {} + +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 initialization. + 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 @@ -138,15 +196,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 @@ -167,7 +232,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, @@ -269,7 +333,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"), /// } @@ -468,6 +532,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 /// Internal state for XArray iteration and entry operations. @@ -485,6 +588,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<'a, R, T> XArrayState where T: ForeignOwnable + 'a, @@ -595,23 +722,50 @@ fn replace(&mut self, new: *mut c_void) -> *mut c_voi= d { old } =20 - fn insert(&mut self, value: T) -> Result<*mut c_void, StoreError> { + fn insert( + &mut self, + value: T, + mut preload: Option<&mut XArraySheaf<'_>>, + ) -> Result<*mut c_void, StoreError> { let new =3D T::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 `T::into_foreign`. - unsafe { bindings::xas_store(&mut self.state, new) }; - - // All arrays created by this abstraction have `XA_FLAGS_TRACK_FRE= E` set, so the - // free mark must be cleared for a newly occupied index, as `__xa_= store` does. - // This is a no-op if the store above failed. - // - // SAFETY: `self.state` is a valid `xa_state` by the type invarian= t, and we hold - // the lock on the xarray it refers to. - unsafe { bindings::xas_clear_mark(&self.state, bindings::XA_FREE_M= ARK) }; + 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 `T::into_foreign`. + unsafe { bindings::xas_store(&mut self.state, new) }; =20 - self.status().map(|()| new).map_err(|error| { + // All arrays created by this abstraction have `XA_FLAGS_TRACK= _FREE` set, so the + // free mark must be cleared for a newly occupied index, as `_= _xa_store` does. + // This is a no-op if the store above failed. + // + // SAFETY: `self.state` is a valid `xa_state` by the type inva= riant, and we hold + // the lock on the xarray it refers to. + unsafe { bindings::xas_clear_mark(&self.state, bindings::XA_FR= EE_MARK) }; + + 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(); + + // On allocation failure, `xas_store` leaves `XA_ERROR= (-ENOMEM)` in + // `self.state.xa_node`, which makes further operation= s on the state fail + // immediately without consuming `xa_alloc`. Reset the= state so the retry + // walks the tree again, as `xas_nomem` does. + self.restart_at(self.state.xa_index); + continue; + } + Err(e) =3D> break Err(e), + } + } + .map_err(|error| { // SAFETY: `new` came from `T::into_foreign` and `xas_store` d= oes not take // ownership of the value on error. let value =3D unsafe { T::from_foreign(new) }; @@ -619,10 +773,16 @@ fn insert(&mut self, value: T) -> Result<*mut c_void,= StoreError> { }) } =20 - /// Consumes `self` and returns the inner `&mut Guard`. + /// Consumes `self`, releases any preallocated node held in `xa_alloc`= , and + /// returns the inner `&mut Guard`. #[inline] 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 c6c5385e6b4f9..bd295358d9751 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; @@ -75,7 +76,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. /// /// @@ -90,7 +92,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); /// } /// @@ -98,8 +100,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`. @@ -109,7 +115,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 @@ -123,7 +130,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); /// } /// @@ -131,8 +138,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 From nobody Sat Sep 26 10:01:18 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 A5FC149F10C; Wed, 2 Sep 2026 13:27:47 +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=1788355670; cv=none; b=Y6A9sIlFRWUkHdJFS/RoVB8hlshx8CqRBBJR4ywNEnpzCZfkHVz//LCu4uQ4n7l5hehzvxnt+6eH7u1ENO47081CwibkC9iXBsLp3FAoqSVnuigOxe/huLHCSvSXnqOIqzAu+WT4hH5R7RRXzZnVnk/ytXXm3yy18SaDB2Hgj1o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788355670; c=relaxed/simple; bh=YsZRkENbIkLSyIXntyIruQahHQMq15ROVwQjNeshW1I=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=LiScHufKRhw37LdnA6zYk8eP44+0aUUnaSOs5xQto3qPlI3zcYYH/zBjuDP6lot+ITo7+PYw0EUYpflKERnMZfL3ZD0Vie2PcQ6SRIsJ5fd/AB1xeUXn88Ii2OpaGknu/l+JJbNY7Gj2hTQgvBsbnhNV4/D7uEp+nafSv6L2NIc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Z9/mA62T; 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="Z9/mA62T" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7A8EB1F00A3A; Wed, 2 Sep 2026 13:27:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788355666; bh=rvrdQ1X2D5s1R4J+rxE40TWU2LjD4CJYJF+HOAohSg0=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=Z9/mA62TsFxmdp6qOn6JL9z5lNw3p8Igcv8uQLBoLG25/22AsmTKxdHEyZc3J4n4E vXFNARAodEVuC8SisiGBSUmqW9whWKZzDla7OSY9p/6iltuB2fwnu9Ym3kcEjDCSmJ XeqnTM48BKunAlYgBaa1TGatN5AM/wNxTKVBGrA0wMjcWtScAkYUqs7uw6PpqygcxJ f/juEYygRnoQdOCqAj7qBu2vWJrwZ2RdOtT4HtJTTb1ikMjDmHJMVi41vExedVK5NY U6DoMZ8lrsO445LisxgZAr2ZVJBFuUNPGstRJ5am8ccxUdesAh0m+f5HIEr5BfqWJ5 qnmpxagCLXUnA== From: Andreas Hindborg Date: Wed, 02 Sep 2026 15:26:08 +0200 Subject: [PATCH v5 12/12] rust: xarray: document `Guard` lock drop semantics 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: <20260902-xarray-entry-send-v5-12-d18adae40708@kernel.org> References: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> In-Reply-To: <20260902-xarray-entry-send-v5-0-d18adae40708@kernel.org> To: Tamir Duberstein , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , Daniel Almeida , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= , Matthew Wilcox , Andrew Morton , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Harry Yoo , Hao Li , Christoph Lameter , David Rientjes , Roman Gushchin Cc: Andreas Hindborg , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-mm@kvack.org, Sashiko X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2705; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=YsZRkENbIkLSyIXntyIruQahHQMq15ROVwQjNeshW1I=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqmCQjltpaMuy9pxr0plzFO8VrPqBiBwDIEQ3/Q /LPI/ftfsqJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCapgkIwAKCRD6UCkIqsW9 0OYoEACjwP3ewbl4zVUWZWh7Q57z4bSf5HmjEqnTenAVKH162Qu8PsggMNdwBjI+5GrSS4QC1f7 agvP8MhuRkcIWon43Um4pPe1+hwNTAIZzsPPQRSA9LBn9aqQYYOiY3P9PaNR8zaepid8BTNXYNd uviGW1OvMbTMc+nXySoe+d4vyYskroiYCFSmND1vBGRpD16KAITSVoNqZ3BX9elp+JAqDYUhhQ5 mYgAm6eGW2Mhw6MfFJH+IW8/WEbKDUpksipvvKBroclQDjnTA6C71klgIhWWji6965B8LEKiEvO PG24IEDR85SgOrO6MmIMNdlXDFExtKpX3/0M5/px8V5IXPqkzcg/y5KaoUB6M37JiFkg0PAxxQv 8Hxfuknl+cM6kd4ZhljhOdvxNYrsQ5Ifyqq/bxUryevVYY0ZdfyOkRnwyQbDN6TC3G9rNUOiQjr jVUVP5WPSFjxl1dz6YO/LEkv/WnxhuwxbnWcUlZVE6yLyHwrDxxv+2B3cp+MgKgh9AVUXx8jh5y vFUcEPal/Exk5Qrg+Z8sxV4s/uqwiTVCcc4sQrnVjOdIffQhp2Ytz2lG0VPPqIqF2759WLuH+t6 bT35o0iU4oifBYIvjcGYDkkHtnvT2sw/zn8M8TjRqHXlOtSqbSZQO3djX9El7lWJhzDhbCHkIEx t+kiqY0kTwiRcJA== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 `Guard::store` calls `__xa_store`, which drops the xarray lock to allocate memory when called with blocking allocation flags and reacquires it afterwards. A Rust lock guard is normally expected to provide continuous mutual exclusion for its entire lifetime, so this behavior can surprise users: a check-then-act sequence spanning a blocking `store` call is not atomic. Document the behavior on `Guard` and expand the `store` docs, pointing to the entry API with preallocated memory as the way to modify the array without dropping the lock. Suggested-by: Sashiko Assisted-by: LLM Signed-off-by: Andreas Hindborg --- rust/kernel/xarray.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/rust/kernel/xarray.rs b/rust/kernel/xarray.rs index 87123ab96a92..a11472acc661 100644 --- a/rust/kernel/xarray.rs +++ b/rust/kernel/xarray.rs @@ -242,6 +242,21 @@ pub fn lock(&self) -> Guard<'_, T> { /// A lock guard. /// /// The lock is unlocked when the guard goes out of scope. +/// +/// # Temporary lock drops +/// +/// Unlike a typical Rust lock guard, holding a `Guard` does not guarantee +/// continuous mutual exclusion for its entire lifetime: [`store`] may dro= p and +/// reacquire the lock to allocate memory when called with blocking alloca= tion +/// flags. Other threads may lock and modify the array in that window, so a +/// sequence of operations on the guard that spans such a call is not atom= ic. +/// +/// To modify the array without dropping the lock, use the entry API with +/// preallocated memory, see [`entry`] and [`insert_entry`]. +/// +/// [`store`]: Guard::store +/// [`entry`]: Guard::entry +/// [`insert_entry`]: Guard::insert_entry #[must_use =3D "the lock unlocks immediately when the guard is unused"] pub struct Guard<'a, T: ForeignOwnable> { xa: &'a XArray, @@ -485,7 +500,12 @@ pub fn remove(&mut self, index: usize) -> Option { =20 /// Stores an element at the given index. /// - /// May drop the lock if needed to allocate memory, and then reacquire= it afterwards. + /// If `gfp` contains blocking allocation flags, this method may drop = the + /// lock to allocate memory and reacquire it afterwards. Other threads= may + /// lock and modify the array in that window, so callers must not rely= on + /// this method being atomic with respect to other operations on the + /// guard. To store without dropping the lock, use [`Guard::insert_ent= ry`] + /// with preallocated memory. /// /// On success, returns the element which was previously at the given = index. /// --=20 2.51.2