From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 3F38E2D8384; Sun, 15 Feb 2026 23:45:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199116; cv=none; b=jm3Ug9ekB3fVR79MNqxKye9jbZL+WPaXDQSwNu4YdAXVLt0z9dbSOQnHvrcK4dKl0KRTAzuMOiToj04vyxlQo74oDQs5OJC8ltA+1RZV0QjHRNzgScN0X7p0bzLJh9Vli7d69to69ST0M5EKts0a0UT6GNEXzW9d6Ko9tVzhvc0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199116; c=relaxed/simple; bh=xknlJKjDgQx2PklWhfpIJtdR7sb0fmRIcoqZtD2O+JQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=d+uA1MjU7mKCND9SUcVlw3GTfOTgSU2v8Oul7hl9x9C8RYq3DbV2FGmAKAQPpKeF0eV+dxvHAcavF25hczfCTjMxF0KbQ/NqCiD592J3uycAU6dvTS9iSEmmyxe1/7h1UAqMWkYdyjjkQe152StqV74IRRIFrK3IeD2yxIw/faY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=DTq0574l; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="DTq0574l" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A8BA5C4CEF7; Sun, 15 Feb 2026 23:45:11 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199116; bh=xknlJKjDgQx2PklWhfpIJtdR7sb0fmRIcoqZtD2O+JQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=DTq0574lt4lFIjV5BGtsOf53qYZq/UUXgIQFVYEJnLtavbUVAreck4t93U/I85b4w 8rtpG/bqaQF0Cl4uI4mW2ivUpfjqgcqtlad46pfHK2iq2nF9FgWbopKXZmp2MxtF0M qDiMt3QAsZGIJoPl9/85vRHZdH5bY8sRMYw9PJ+YWjXXmuEoB8vPr2h9OxoJWSrK5X LblQ1dOTw+BPdGutBKjuTMjRhVLuvBNJIKxM/Q+PnSQfLKxV2m9GWo8np2086DBB9Q NauJCPEqCnOcEW/PTtC3Jdk0N+EvnM5Uib1/M0iw+/Xc4qvrUCnSTasP9UKa0aCPch dEXJRtvDrsbqQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:34:48 +0100 Subject: [PATCH 01/79] block: rnull: adopt new formatting guidelines 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: <20260216-rnull-v6-19-rc5-send-v1-1-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1060; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=xknlJKjDgQx2PklWhfpIJtdR7sb0fmRIcoqZtD2O+JQ=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklgcx+Hel7olsXH4zRoVtUmD4dmhza68hS3aH Ic1/VCoziCJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYHAAKCRDhuBo+eShj d/mQEAC2VIEFmtp53Y73KZqpnd3iisJkJprBqibxgwe4PmVofc2jb+vYmI0M27QBCSy7TZI70qa fGabetSqKwfit5BTtDd9i8VWhdIt28z2tBa7lQAQU6gUXN7+h/0OrgZUjrQMYNSiMHEFfjDKRnm vabPA9SvNgKI9Nnq8mkj/4DwHLO9QedW9ZuM5LWiGLNx2tYrP6i4d6f17F0u0F3Q/YEXzg7Ad79 rCfup6mPXjh+Xf0R9Yh17SM491NorZ3/e62rV/U9PgM2mDlpJz52MpGgdcWoecew5g0h1GVb26L zFkE5WCDPNOrzhFCo7ezaTdQ1lwwVrBjyYuJZb4tEmPP7qHwjN6Awf73ZPJCjhBN31N9Hfe1l9+ AvHq3/DZURYUCKi8+421oXhmInZaNb9i7n8dd5vLyZar08vDXomuh6rNdNoUDo5MHhEkI2NvbXs zwiWTGN2OytTL79j5+i6BQQQQkYcUR3mpk+wN0LAw3G/kd1FGEicOnVdIs7u25hOO5FvJUSuWKE /5IDy8vVRxcU0Bxei4QO7I70wnsJPp8f2XIZhRzeWn75szLBUXWyEoO1QJssJXqH8ztnRr3rdL4 HSQUvjWibaQyn2e79bqnYQP7AjY51xhJ+EF84ezBywBBDfAj+JSzVpIgtytQanunAnN6dcj1GU/ jqhUKqDc7YaEwKQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Reformat `use` statements to have one item per line as required by the updated Rust formatting guidelines. Apply a formatting workaround to ensure `rustfmt` produces the expected output. Signed-off-by: Andreas Hindborg Reviewed-by: Alice Ryhl --- drivers/block/rnull/rnull.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index a9d5e575a2c43..2c83b014b05e9 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -10,14 +10,21 @@ self, mq::{ self, - gen_disk::{self, GenDisk}, - Operations, TagSet, + gen_disk::{ + self, + GenDisk, // + }, + Operations, + TagSet, // }, }, error::Result, pr_info, prelude::*, - sync::{aref::ARef, Arc}, + sync::{ + aref::ARef, + Arc, // + }, }; use pin_init::PinInit; =20 --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 30047312826; Sun, 15 Feb 2026 23:46:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199211; cv=none; b=KzEUySZKs78TGzK5i+iDuEw9Wj8IvKFIgnqg6gafOGb9+c3v+iUUn3o/xrW7iLh3kqTY9DizQIezRl3lYZqo/YSR0V9egvj0/jCNYwZPsdRWUKTnvd+/fjrJVbMsz2sLpQCNr/E6PlRELDPTRYgu7YU9IiWyasTSylIhyY4cgh8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199211; c=relaxed/simple; bh=rgMlD4sSCyRxG8oxO2Pb6vDkLa6w1B6y8QPvsSGTaZc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=nS2e1AiehrjzVGtagNHJcg2N+hhIMWwQbapiJ+vpGTTOTePo4oKq3B7MrvUYzv86gjCm63L05cC8cBAyvZ6KGvW70OITCTEV9oq+S3NzU1hh93TbpgxeDI1ZtyIg8A7/cZYOsh9ODC8moFHsxW2wvz1gyv9X/5++ZTg7ALw6tHw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=WmPoQJMO; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="WmPoQJMO" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A05CFC4CEF7; Sun, 15 Feb 2026 23:46:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199211; bh=rgMlD4sSCyRxG8oxO2Pb6vDkLa6w1B6y8QPvsSGTaZc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=WmPoQJMOP0UiKshCcGC2ekbgXi4gGMlJFjAOZ5MxskaSSSd+rXiJqBFSm59d9+B3m 0s5y1/PJFCE3IQLKtRAlU/wup1FgMbqxO4DKfmsSGzk6bQlt56ouh40xpjiJIu+xQ+ wUp4adpOBJ4FzHFcEec+9kxPGsWy9C69T3jmsLiZoXBV0KfD5lTEx8XwdR7Wv3vcfk gopZcVsalEwSnEnreQVd+cFZIwC2gG5zdA9SFBdB2aLCV5v7uKeHzK9IeUHgcOLghj ZfPFSozibHnKLdm772y85ZlrajGlHVSPadvHQKgUENZtO9GBKw4L2VHOOciApLT+5c KSkkG/y80PoXw== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:34:49 +0100 Subject: [PATCH 02/79] block: rnull: add module parameters 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: <20260216-rnull-v6-19-rc5-send-v1-2-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=3195; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=rgMlD4sSCyRxG8oxO2Pb6vDkLa6w1B6y8QPvsSGTaZc=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklgdRABPWARqS1bNRC+vWhj9vKzZMZZcE3x1J YLYupX3Y7yJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYHQAKCRDhuBo+eShj d9LAD/9LvTRLSgtyV5rn6DK63XIy00fXEsKbYH8ZTfptYGBha30Wxnq4BRbzlAGzSI7eseVirtu U2fUA67OrAjwLTSxPkmJhR53AM7ylkIh+qc60AWS9hwAu0T5MyOK8jphpvwA0rWLKHaKw8JOFjA v4I49NQ7hDBxCmBG6svW1KzaxBnEN3sLb5zn7bAE6+wDppgTZc+pBYJXRK2ITRlWSl3BsDAY6fX eMajzH3BrBzqbS49rnM0/yjEGCyezkbXE+KCLx54uudBjCle9yLGzBqcTa6iFsxsgbj3xQl3W5d UUFXc3j/AjYSOLJaQPRE/AUjaFuXjdoEi72vpE5Be5eiikPFPU1N5KESsjdZ0rziWu7N5n2zBmI xUFRcHYOGWJlCoasEcDU1LzAy7NTFzrxsFs2c1oLrFLgVfbC/C4sQW3VDJ8S2OGeUsRhu7vdHtH SHF5YH9h5dksV5cM8rhy0uh+BC63HhvCM6ZqByKuFvxhd6Fh4al+YYmVYoYQgmFLMYcHprOo32D Oh+mJ02nU0YBCHqy0HGNrwQQBloNC864jBfkOPR62speL9TbdseULqpzx96q5EY1BBzzolCMUss lUP0KXF3Sp4JTC+3IqQ5sB3l/ZjUTARO52gSRtkLFn4XTHn5rP77RTo8Si+9U17qsEQvvKHrZt8 vbnD6kdgtG+8tPw== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Module parameter support for Rust modules was merged a few releases back. Add module parameter support to the rnull driver. This allows the user to control the driver either via configfs or module parameters, just like the C counterpart. Please note that the rust module parameters do not support boolean values. Flags that should have been booleans are parsed as integers and compared to zero. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/rnull.rs | 51 ++++++++++++++++++++++++++++++++++++++++= ++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 2c83b014b05e9..bd883a5061cab 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -19,11 +19,13 @@ }, }, error::Result, - pr_info, + new_mutex, pr_info, prelude::*, + str::CString, sync::{ aref::ARef, - Arc, // + Arc, + Mutex, // }, }; use pin_init::PinInit; @@ -34,20 +36,65 @@ authors: ["Andreas Hindborg"], description: "Rust implementation of the C null block driver", license: "GPL v2", + params: { + gb: u64 { + default: 4, + description: "Device capacity in GiB", + }, + rotational: u8 { + default: 0, + description: + "Set the rotational feature for the device (0 for false, 1 for= true). Default: 0", + }, + bs: u32 { + default: 4096, + description: "Block size (in bytes)", + }, + nr_devices: u64 { + default: 1, + description: "Number of devices to register", + }, + irqmode: u8 { + default: 0, + description: "IRQ completion handler. 0-none, 1-softirq", + }, + }, } =20 #[pin_data] struct NullBlkModule { #[pin] configfs_subsystem: kernel::configfs::Subsystem, + #[pin] + param_disks: Mutex>>, } =20 impl kernel::InPlaceModule for NullBlkModule { fn init(_module: &'static ThisModule) -> impl PinInit { pr_info!("Rust null_blk loaded\n"); =20 + let mut disks =3D KVec::new(); + + let defer_init =3D move || -> Result<_, Error> { + for i in 0..(*module_parameters::nr_devices.value()) { + let name =3D CString::try_from_fmt(fmt!("rnullb{}", i))?; + + let disk =3D NullBlkDevice::new( + &name, + *module_parameters::bs.value(), + *module_parameters::rotational.value() !=3D 0, + *module_parameters::gb.value() * 1024, + (*module_parameters::irqmode.value()).try_into()?, + )?; + disks.push(disk, GFP_KERNEL)?; + } + + Ok(disks) + }; + try_pin_init!(Self { configfs_subsystem <- configfs::subsystem(), + param_disks <- new_mutex!(defer_init()?), }) } } --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 D67AD2D94BE; Sun, 15 Feb 2026 23:42:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198953; cv=none; b=QUGReWhcnV3Cxr7rFscaWMo3P3VMcIoeaihGrxRY5lyV8xK7dPoLkveOtBnRBfnRv3mlr2Yo6JLpnNKhv9U9TM804z38bbtIW1DqQsiwNAPDs9EldFWLDXozBRCenQXeIkfRnjb6wsnCTdBJjs/6fFFv2tLYi3PQDTCz0T1gW2U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198953; c=relaxed/simple; bh=ZBT3g2yxwr2ga5MlCDT4cJc5rd6AoVQ9CyP5UJLr7JY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=SSkmRB9niKNZutB+V+ITW322IlefuETSSKVmu+DsXlx/OKtGrSq0k0+xVjGrrB8SRbi1NY2sljZNvajtrBYNPqzeyGGJzPiwrfIRmIjI4Zm2Kl2WvqyiGd9h4uoyTvF3sJQK2qmQ1u9tdPd6gx0Ar/7u0AunKcZh33+HUe3si/o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=oQ0LJSHO; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="oQ0LJSHO" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4DBDCC19422; Sun, 15 Feb 2026 23:42:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198953; bh=ZBT3g2yxwr2ga5MlCDT4cJc5rd6AoVQ9CyP5UJLr7JY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=oQ0LJSHOI5yscAmkUVG++3cAQSWRAceqv3D0uq2h4/A0ND+d+Vay35uo/6D8Nhd6t Kzr1zb85rkn8P/78LpvGZ59R4GfHdtt3COdBcJEYKIr7lRH4bRoIKWw+fymLavNP3q SBtVcCVRmM1yfS7oBEqzJe77DVBax4bkBD6j2pITc/rOXCSZ1qH1STZdMsBJNZV+OG yXnGajUfOZZ7QyYxYwN6FUzK9Vjj63nHRv3vcVqya/h5YfvC3g/PWL7l2ZdE2XLJwd h9KR3R+ehhy5DQU9iIce96EAr3JH88izhhVM63PLazxU4ndAAPzn3uOjCSAVGobvf2 oevjZEmirz1wA== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:34:50 +0100 Subject: [PATCH 03/79] block: rnull: add macros to define configfs attributes 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: <20260216-rnull-v6-19-rc5-send-v1-3-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=10210; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=ZBT3g2yxwr2ga5MlCDT4cJc5rd6AoVQ9CyP5UJLr7JY=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklgeSt5Gpp+/cjlALcsKqnCvx2CtOLaSgTmuZ 5S1M+PAvxKJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYHgAKCRDhuBo+eShj dxG0EACwY4lNl7HxgkSgg52iQKuudaW+KKtUEtLbOPnjN4vwDRaW5/rzgKREi8+8L4Hn5i/pRaE aLRUmsG035uMlSrRwgqucvAmHDuETFlxewsvaN7bHlDpPIc69KG7I6dqME4ApI99615IFir39Il US4j/8i7YXr6fLC7NRBJwm0YG31P22xqc/oHntGgoG8zFsyiUbOp3AnMBP/2ffB1zdwLTVDNDGp zcFP3+yr7euceudcVIoeVJkw74154p/lEI8rerssardEivgs7WsVg5nDpvSJO3elWw2ZSyCP/u0 v/XB3MuW+tuiYWeJl4ZQdSwQecBB4T5lirjtCUR87ijjFr4U7gV+0uzyEf4htnzdnOY3LzVkxxX +UDQYhjIA3VI5xFOLVe9lA9vmGmpgVXANMzhsaqH10mf4qx/EeqxFqFPDUskqY2caxftq7KxGQK mvVrfajabD6Bsbpoe19hmDSh1GG5dQ0AZwVDTGVLQt5Yhgrx6jhCDqV8tfHRV88+vj6++g1QAOy 5AGIXTLcyWlLBc57zWrNCWZKLv2ZFE1hewbPQQhGbMmvmBmzAtSA6EVXeBEtZrgKGzGNu73fCGb /GXzLK6AF3F/BfkVnOcRSln/2rPEyETi0aqJoa8EPiCrGY7UEoJuwxs0SkRb93ZhKRRxCoqTEcJ fIvQw9h7rLuyy/Q== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Defining configfs attributes in rust is a bit verbose at the moment. Add some macros to make the attribute definition less verbose. The configfs Rust abstractions should eventually provide procedural macros for this task. When we get more users of the configfs Rust abstractions, we shall consider this task. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 134 +++++++++--------------------= ---- drivers/block/rnull/configfs/macros.rs | 128 +++++++++++++++++++++++++++++= ++ 2 files changed, 164 insertions(+), 98 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index ea38b27a9011c..eafa71dfc40d3 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -1,20 +1,41 @@ // SPDX-License-Identifier: GPL-2.0 =20 -use super::{NullBlkDevice, THIS_MODULE}; +use super::{ + NullBlkDevice, + THIS_MODULE, // +}; use kernel::{ - block::mq::gen_disk::{GenDisk, GenDiskBuilder}, + block::mq::gen_disk::{ + GenDisk, + GenDiskBuilder, // + }, c_str, - configfs::{self, AttributeOperations}, + configfs::{ + self, + AttributeOperations, // + }, configfs_attrs, - fmt::{self, Write as _}, + fmt::{ + self, + Write as _, // + }, new_mutex, page::PAGE_SIZE, prelude::*, - str::{kstrtobool_bytes, CString}, - sync::Mutex, + str::{ + kstrtobool_bytes, + CString, // + }, + sync::Mutex, // +}; +use macros::{ + configfs_simple_bool_field, + configfs_simple_field, // }; use pin_init::PinInit; =20 +mod macros; + pub(crate) fn subsystem() -> impl PinInit, Error> { let item_type =3D configfs_attrs! { container: configfs::Subsystem, @@ -166,99 +187,16 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { } } =20 -#[vtable] -impl configfs::AttributeOperations<1> for DeviceConfig { - type Data =3D DeviceConfig; - - fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result { - let mut writer =3D kernel::str::Formatter::new(page); - writer.write_fmt(fmt!("{}\n", this.data.lock().block_size))?; - Ok(writer.bytes_written()) - } - - fn store(this: &DeviceConfig, page: &[u8]) -> Result { - if this.data.lock().powered { - return Err(EBUSY); - } - - let text =3D core::str::from_utf8(page)?.trim(); - let value =3D text.parse::().map_err(|_| EINVAL)?; - - GenDiskBuilder::validate_block_size(value)?; - this.data.lock().block_size =3D value; - Ok(()) - } -} - -#[vtable] -impl configfs::AttributeOperations<2> for DeviceConfig { - type Data =3D DeviceConfig; - - fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result { - let mut writer =3D kernel::str::Formatter::new(page); - - if this.data.lock().rotational { - writer.write_str("1\n")?; - } else { - writer.write_str("0\n")?; - } - - Ok(writer.bytes_written()) - } - - fn store(this: &DeviceConfig, page: &[u8]) -> Result { - if this.data.lock().powered { - return Err(EBUSY); - } - - this.data.lock().rotational =3D kstrtobool_bytes(page)?; - - Ok(()) - } -} - -#[vtable] -impl configfs::AttributeOperations<3> for DeviceConfig { - type Data =3D DeviceConfig; - - fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result { - let mut writer =3D kernel::str::Formatter::new(page); - writer.write_fmt(fmt!("{}\n", this.data.lock().capacity_mib))?; - Ok(writer.bytes_written()) - } - - fn store(this: &DeviceConfig, page: &[u8]) -> Result { - if this.data.lock().powered { - return Err(EBUSY); - } - - let text =3D core::str::from_utf8(page)?.trim(); - let value =3D text.parse::().map_err(|_| EINVAL)?; - - this.data.lock().capacity_mib =3D value; - Ok(()) - } -} - -#[vtable] -impl configfs::AttributeOperations<4> for DeviceConfig { - type Data =3D DeviceConfig; - - fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result { - let mut writer =3D kernel::str::Formatter::new(page); - writer.write_fmt(fmt!("{}\n", this.data.lock().irq_mode))?; - Ok(writer.bytes_written()) - } +configfs_simple_field!(DeviceConfig, 1, block_size, u32, check GenDiskBuil= der::validate_block_size); +configfs_simple_bool_field!(DeviceConfig, 2, rotational); +configfs_simple_field!(DeviceConfig, 3, capacity_mib, u64); +configfs_simple_field!(DeviceConfig, 4, irq_mode, IRQMode); =20 - fn store(this: &DeviceConfig, page: &[u8]) -> Result { - if this.data.lock().powered { - return Err(EBUSY); - } +impl core::str::FromStr for IRQMode { + type Err =3D Error; =20 - let text =3D core::str::from_utf8(page)?.trim(); - let value =3D text.parse::().map_err(|_| EINVAL)?; - - this.data.lock().irq_mode =3D IRQMode::try_from(value)?; - Ok(()) + fn from_str(s: &str) -> Result { + let value: u8 =3D s.parse().map_err(|_| EINVAL)?; + value.try_into() } } diff --git a/drivers/block/rnull/configfs/macros.rs b/drivers/block/rnull/c= onfigfs/macros.rs new file mode 100644 index 0000000000000..53ce9d5dbdc8a --- /dev/null +++ b/drivers/block/rnull/configfs/macros.rs @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-2.0 + +use super::DeviceConfig; +use core::{fmt::Write, str::FromStr}; +use kernel::{page::PAGE_SIZE, prelude::*}; + +pub(crate) fn show_field(value: T, page: &mut [u8; PAGE_S= IZE]) -> Result { + let mut writer =3D kernel::str::Formatter::new(page); + writer.write_fmt(fmt!("{}\n", value))?; + Ok(writer.bytes_written()) +} + +pub(crate) fn store_with_power_check(this: &DeviceConfig, page: &[u8], = store_fn: F) -> Result +where + F: FnOnce(&DeviceConfig, &[u8]) -> Result, +{ + if this.data.lock().powered { + return Err(EBUSY); + } + store_fn(this, page) +} + +pub(crate) fn store_number_with_power_check( + this: &DeviceConfig, + page: &[u8], + store_fn: F, +) -> Result +where + F: FnOnce(&DeviceConfig, T) -> Result, + T: FromStr, +{ + if this.data.lock().powered { + return Err(EBUSY); + } + + let text =3D core::str::from_utf8(page)?.trim(); + let value =3D text.parse::().map_err(|_| EINVAL)?; + + store_fn(this, value) +} + +macro_rules! configfs_attribute { + ( + $type:ty, + $id:literal, + show: |$show_this:ident, $show_page:ident| $show_block:expr, + store: |$store_this:ident, $store_page:ident| $store_block:expr + $(,)? + ) =3D> { + #[vtable] + impl configfs::AttributeOperations<$id> for $type { + type Data =3D $type; + + fn show($show_this: &$type, $show_page: &mut [u8; PAGE_SIZE]) = -> Result { + $show_block + } + + fn store($store_this: &$type, $store_page: &[u8]) -> Result { + $store_block + } + } + }; +} +pub(crate) use configfs_attribute; + +// Specialized macro for simple boolean fields that just store kstrtobool_= bytes result. +macro_rules! configfs_simple_bool_field { + ($type:ty, $id:literal, $field:ident) =3D> { + crate::configfs::macros::configfs_attribute!($type, $id, + show: |this, page| crate::configfs::macros::show_field(this.da= ta.lock().$field, page), + store: |this, page| + crate::configfs::macros::store_with_power_check(this, page, = |this, page| { + this.data.lock().$field =3D kstrtobool_bytes(page)?; + Ok(()) + }) + ); + }; +} +pub(crate) use configfs_simple_bool_field; + +// Specialized macro for simple numeric fields that just parse and assign +macro_rules! configfs_simple_field { + // Simple direct assignment + ($type:ty, $id:literal, $field:ident, $field_type:ty) =3D> { + crate::configfs::macros::configfs_attribute!($type, $id, + show: |this, page| crate::configfs::macros::show_field(this.da= ta.lock().$field, page), + store: |this, page| crate::configfs::macros::store_number_with= _power_check( + this, + page, + |this, value: $field_type| { + this.data.lock().$field =3D value; + Ok(()) + } + ) + ); + }; + // With infallible conversion expression (direct value) + ($type:ty, $id:literal, $field:ident, $field_type:ty, into $convert:ex= pr) =3D> { + crate::configfs::macros::configfs_attribute!($type, $id, + show: |this, page| + crate::configfs::macros::show_field(this.data.lock().$fiel= d, page), + store: |this, page| crate::configfs::macros::store_number_with= _power_check( + this, + page, + |this, value: $field_type| { + this.data.lock().$field =3D $convert(value); + Ok(()) + } + ) + ); + }; + // With check, no conversion + ($type:ty, $id:literal, $field:ident, $field_type:ty, check $check:exp= r) =3D> { + crate::configfs::macros::configfs_attribute!($type, $id, + show: |this, page| crate::configfs::macros::show_field(this.da= ta.lock().$field, page), + store: |this, page| crate::configfs::macros::store_number_with= _power_check( + this, + page, + |this, value: $field_type| { + $check(value)?; + this.data.lock().$field =3D value; + Ok(()) + } + ) + ); + }; +} +pub(crate) use configfs_simple_field; --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 26AA52E2EF2; Sun, 15 Feb 2026 23:41:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198914; cv=none; b=NeEGpCi/6cSRb7iEBq9/WSLYsykNl0MMSatlhMP/p8ThBU2zuJcHoEvGZcDWV5mwE/kt8m9PO6BXQYfZGt7PrgrNAmn/ED6D1fFCaBz6tMEw8DH/N9UEEK14NPxxfSM8EWJ/lQGfytdrnPROrXaZJegN2DJAt0sKOFhfHG6yWq8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198914; c=relaxed/simple; bh=RW3MTz8xkW1tKyTv3RDY6oAQVdhOz9YY/OU7mpdXt4Q=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=nqcp09tjR/igh8HlrZq4F0hxCBjtKfmje32q6E5cKD7P0nXpK1hteuJbVm6lY5Kj+Cnt5OQrIkVNEyaSSHSC1LtS1Y2XCBwxBE+/mCSFLKThv5tXmFAED7sealVh0L9c0tOBqAl/9xnjqoEQUajABBNvGZQvdHl9XlNM/Ca3XEw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=vMsAXC55; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="vMsAXC55" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C8DA3C19425; Sun, 15 Feb 2026 23:41:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198914; bh=RW3MTz8xkW1tKyTv3RDY6oAQVdhOz9YY/OU7mpdXt4Q=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=vMsAXC55989y+KeDRqJR9UNrCpg6LC4ikm9xU96z5HhXpXX2/OVMOrxMJky9+pV2I OpQHTkkXKIMaSUkEox8w+LPL55BJg9WmjYVy5gcfFHwyI+BOsbKN4suMV02ot8g8E/ vcFo6GVQhbKSHgoVmdLIR29O9ErRwX4DlpBc0jTUBP5AFlWaOH3fd2gqeO8jj9fMdT hewruuea3G9jMNzgye73d02EIDvEUJXvyR5KyxlfW8yTj8SNxK9lqMiIovW1QfYjD3 2ZjW9fih3l1wcubS9H2S0O+TO8gfP/cL1FsGO8diL3nuoK6/+Y/wu6V/k/lE4uk1GP cV92ez/Pa1pVg== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:34:51 +0100 Subject: [PATCH 04/79] block: rust: fix generation of bindings to `BLK_STS_.*` 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: <20260216-rnull-v6-19-rc5-send-v1-4-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=3781; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=RW3MTz8xkW1tKyTv3RDY6oAQVdhOz9YY/OU7mpdXt4Q=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklgfSRI5aGYFC7AMdVoyVwiZX6IUJslmIUQF5 nE91okZ7AmJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYHwAKCRDhuBo+eShj d2QXD/98TQdnkcOiqdL0C2IVNHBsSku5PEaZGrrhYH6IoIEShsbzgzZ5XEkKnf4VsaEQmG5p8Tw KfcM3mfT0ZA2sy76Jf1PWiCH+ayYRqEXtS9oF9b4cQfrWlw56O/7iu6yP9YEnL63/bF4bB2tcVn cVh0TNZAkEMPVzxsoQ5CMX2xcOyu0gM7Q/pKqDohmVn7TJ911Ie4cLrnFOrM1NcKk/vAMPL+BuH gbKFIHCjL3kkQU5UZGfb4cpxAXe6QPfiJZ6Hq37JzBdLITGKE7JmOJm5Q8k6HUVgrAexdyEwHL5 sShRPJp3jfYw9ukmElW9NaDjNhrx3AbpW2fKqiOqCTnEp7ODnU3HdiMAZAI5k+2TG5ypMtw2mV8 DNJZiSdq/w4oWO6VZ6s2GvR43prj2eq1P40rGOraKOKq2DdW3xZvrDHoP0wWeqJXdtJCRX5aQ61 TSygTC1vqeX7rEnU+x6HWuoomxj7D0zflbJr+2/KBsqEVGZJ4Cars0gcWKNfUwfphENG6RCyVsP S0RMkbENgM5Ljfn63kw7+Lqmmo+AEd9rnH5stK133hs3D0zM6LTt2EAbqIRIc88g0mdhB8HBuUr yJl+eeWDzv3eZU77J4rrl+VtyrBzESbH8LXDt2bf43mIPstBhu9xgwxFiETlZwoNwLbmFSfRw83 /WUIjxXuemLeoyw== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Bindgen generates constants for CPP integer literals as u32. The `blk_status_t` type is defined as `u8` but the variants of the type are defined as integer literals via CPP macros. Thus the defined variants of the type are not of the same type as the type itself. Prevent bindgen from emitting generated bindings for the `BLK_STS_.*` defines and instead define constants manually in `bindings_helper.h` Also remove casts that are no longer necessary. Signed-off-by: Andreas Hindborg Reviewed-by: Alice Ryhl --- rust/bindgen_parameters | 3 +++ rust/bindings/bindings_helper.h | 19 +++++++++++++++++++ rust/kernel/block/mq/operations.rs | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/rust/bindgen_parameters b/rust/bindgen_parameters index fd2fd1c3cb9a5..0968541c9bc16 100644 --- a/rust/bindgen_parameters +++ b/rust/bindgen_parameters @@ -5,6 +5,9 @@ --blocklist-type __kernel_s?size_t --blocklist-type __kernel_ptrdiff_t =20 +# These are generated as u32 by bindgen, but should be u8. +--blocklist-item BLK_STS_.* + --opaque-type xregs_state --opaque-type desc_struct --opaque-type arch_lbr_state diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index a783106cdac6f..e0ba5c712c560 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -113,6 +113,25 @@ const gfp_t RUST_CONST_HELPER___GFP_ZERO =3D __GFP_ZER= O; const gfp_t RUST_CONST_HELPER___GFP_HIGHMEM =3D ___GFP_HIGHMEM; const gfp_t RUST_CONST_HELPER___GFP_NOWARN =3D ___GFP_NOWARN; const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ROTATIONAL =3D BLK_FEAT_RO= TATIONAL; +const blk_status_t RUST_CONST_HELPER_BLK_STS_OK =3D BLK_STS_OK; +const blk_status_t RUST_CONST_HELPER_BLK_STS_NOTSUPP =3D BLK_STS_NOTSUPP; +const blk_status_t RUST_CONST_HELPER_BLK_STS_TIMEOUT =3D BLK_STS_TIMEOUT; +const blk_status_t RUST_CONST_HELPER_BLK_STS_NOSPC =3D BLK_STS_NOSPC; +const blk_status_t RUST_CONST_HELPER_BLK_STS_TRANSPORT =3D BLK_STS_TRANSPO= RT; +const blk_status_t RUST_CONST_HELPER_BLK_STS_TARGET =3D BLK_STS_TARGET; +const blk_status_t RUST_CONST_HELPER_BLK_STS_RESV_CONFLICT =3D BLK_STS_RES= V_CONFLICT; +const blk_status_t RUST_CONST_HELPER_BLK_STS_MEDIUM =3D BLK_STS_MEDIUM; +const blk_status_t RUST_CONST_HELPER_BLK_STS_PROTECTION =3D BLK_STS_PROTEC= TION; +const blk_status_t RUST_CONST_HELPER_BLK_STS_RESOURCE =3D BLK_STS_RESOURCE; +const blk_status_t RUST_CONST_HELPER_BLK_STS_IOERR =3D BLK_STS_IOERR; +const blk_status_t RUST_CONST_HELPER_BLK_STS_DM_REQUEUE =3D BLK_STS_DM_REQ= UEUE; +const blk_status_t RUST_CONST_HELPER_BLK_STS_AGAIN =3D BLK_STS_AGAIN; +const blk_status_t RUST_CONST_HELPER_BLK_STS_DEV_RESOURCE =3D BLK_STS_DEV_= RESOURCE; +const blk_status_t RUST_CONST_HELPER_BLK_STS_ZONE_OPEN_RESOURCE =3D BLK_ST= S_ZONE_OPEN_RESOURCE; +const blk_status_t RUST_CONST_HELPER_BLK_STS_ZONE_ACTIVE_RESOURCE =3D BLK_= STS_ZONE_ACTIVE_RESOURCE; +const blk_status_t RUST_CONST_HELPER_BLK_STS_OFFLINE =3D BLK_STS_OFFLINE; +const blk_status_t RUST_CONST_HELPER_BLK_STS_DURATION_LIMIT =3D BLK_STS_DU= RATION_LIMIT; +const blk_status_t RUST_CONST_HELPER_BLK_STS_INVAL =3D BLK_STS_INVAL; 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; diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/oper= ations.rs index 8ad46129a52c4..b68c0208efc66 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -124,7 +124,7 @@ impl OperationsVTable { if let Err(e) =3D ret { e.to_blk_status() } else { - bindings::BLK_STS_OK as bindings::blk_status_t + bindings::BLK_STS_OK } } =20 --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 308652DF137; Sun, 15 Feb 2026 23:45:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199126; cv=none; b=rMYbde20c9FeDMe7JXeK6HLvHBA9EzO9yDVl2dq16LiLUy0nKLFSSv/Ivt0poSxHu1Bsh9ifcja5jh+5pAkdL2uTI6oY6u5vpVX4IiqC6mwxccvLxPP76vbGAGXsL5jeC82criZD5BxzaHlB9coKE+1MaxIquBePap6McGkTgbc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199126; c=relaxed/simple; bh=ItbNPma/0Fj+fEER3LzQBO101WvqRQL7byAlGzOg91g=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=A7Wm83bW190ehm+5h+qo2f1KFv/LFCYENZxzsTG6+cwZqxOaYNpDj4SP/+pbN814BmI8R0ZC8Yj6JriCGbnHKz88gK6rcGtraif7iVG4vEq7x/kLU3FMegD0EmVIZoUxR6KT6YuFsx1oFDjpPwTIYD0Kgz9FdXYhhk09Lv+HZXw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=aLfTZApU; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="aLfTZApU" Received: by smtp.kernel.org (Postfix) with ESMTPSA id CC7EDC4CEF7; Sun, 15 Feb 2026 23:45:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199126; bh=ItbNPma/0Fj+fEER3LzQBO101WvqRQL7byAlGzOg91g=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=aLfTZApU4RCDv38xz/C3FJeWGS/5MFxpm94QlC5LnsiF5wjh3bsfW8EnpImz9W1ym 3uQ1dUM5152vHSoo89+z8dadHhf7DkNQTi9MDF1KCKYgfJ+vYSSGEB5JNJixHUiZgM vZogYhr3wIOju46gr3UVu/97q4bE3+vwZ5LjysxN/Op0daUprhTg9OnmgT7Hh7vlca I3worT09vUEWqBc4PCoc/H+vl+vFhYy41dnne9dCnvrxgMksAzF1OJqRrgR93OO0rd 6noWN2IfMUml5u08GO9fBbqnQRGOG3oaRoCuxJQd8WyEjNuy1mgZwDeFgSxd89RzYW OZXUOEvSFjv3Q== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:34:52 +0100 Subject: [PATCH 05/79] block: rust: change `queue_rq` request type to `Owned` 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: <20260216-rnull-v6-19-rc5-send-v1-5-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=18850; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=ItbNPma/0Fj+fEER3LzQBO101WvqRQL7byAlGzOg91g=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklgg61yucg5HYfS/fmA4cKT/mjPfS44FFkQTV DD6Qkde0dGJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYIAAKCRDhuBo+eShj d2ZWEADAEyUV2yhMxSfOurSomvof5QE0iaB8WCk61pomynEOh/dIKA7iq4Z0CUGHF1VXdHtb/KM Meql91hcHpAsyIu6nwdJLOlt24l2ZgIbuS84OyYLu4vlV056pQ9Dm+wUCuTfrkBjTD9wMVUxaU0 e8tvy40UW7OxWPOFqng/7c/2ku1lX4e+RtQWJIVewkdniPIN739pj/TNcXtOi6hCIxDlAyOk9NQ auLiBOst2Xwa5RdGYa8W63Vh/xFqh+cDS0fYL2PGy5fCnIdy2FYeUw8H9egQ7c1Qxr8lN0lu6kO 1APjc4q2IhsfQNFXyiav/YjEuia4XvoLK7VaTZbVdAvCbi5CiQ8PDFD7SJWwVV+XKcOoqAzC63I AnFC9Fsfygrr4iB1pLiEHsLSJkKsuXDymra4UxRZJF6lTV6SCWJgWNj882F5ZuvaktJd4LpgEkC 4k5LSEkW8aqUc8Nbk+YjMR5k9U/9N9O+/aFUfq7DBWGH8WYU9bij5j27Cp9CKVlpSTGGMmtpK7l dubFrE4j7gbMq/AjjgWBdQtL++8dfkDiQhewIIq+b+ypWOCS9uTu7bybt41eqXUZp/o8JrSuTvn 1hmcZjF5K4Bkvre7mCY3VZ+SF3paHezpVJu7ijl38W4TL6It3A90DTuAOtVw1Ytv9VmRaW4z9VG Vo94xF0yiC9ja1g== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Simplify the reference counting scheme for `Request` from 4 states to 3 states. This is achieved by coalescing the zero state between block layer owned and uniquely owned by driver. Implement `Ownable` for `Request` and deliver `Request` to drivers as `Owned`. In this process: - Move uniqueness assertions out of `rnull` as these are now guaranteed by the `Owned` type. - Move `start_unchecked`, `try_set_end` and `end_ok` from `Request` to `Owned`, relying on type invariant for uniqueness. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/rnull.rs | 26 ++--- rust/kernel/block/mq.rs | 10 +- rust/kernel/block/mq/operations.rs | 27 +++-- rust/kernel/block/mq/request.rs | 218 +++++++++++++++++++++------------= ---- 4 files changed, 158 insertions(+), 123 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index bd883a5061cab..6a7f660d31998 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -19,7 +19,8 @@ }, }, error::Result, - new_mutex, pr_info, + new_mutex, + pr_info, prelude::*, str::CString, sync::{ @@ -27,6 +28,10 @@ Arc, Mutex, // }, + types::{ + OwnableRefCounted, + Owned, // + }, // }; use pin_init::PinInit; =20 @@ -131,15 +136,10 @@ impl Operations for NullBlkDevice { type QueueData =3D KBox; =20 #[inline(always)] - fn queue_rq(queue_data: &QueueData, rq: ARef>, _is_l= ast: bool) -> Result { + fn queue_rq(queue_data: &QueueData, rq: Owned>, _is_= last: bool) -> Result { match queue_data.irq_mode { - IRQMode::None =3D> mq::Request::end_ok(rq) - .map_err(|_e| kernel::error::code::EIO) - // We take no refcounts on the request, so we expect to be= able to - // end the request. The request reference must be unique a= t this - // point, and so `end_ok` cannot fail. - .expect("Fatal error - expected to be able to end request"= ), - IRQMode::Soft =3D> mq::Request::complete(rq), + IRQMode::None =3D> rq.end_ok(), + IRQMode::Soft =3D> mq::Request::complete(rq.into()), } Ok(()) } @@ -147,11 +147,9 @@ fn queue_rq(queue_data: &QueueData, rq: ARef>, _is_last: bool) fn commit_rqs(_queue_data: &QueueData) {} =20 fn complete(rq: ARef>) { - mq::Request::end_ok(rq) + OwnableRefCounted::try_from_shared(rq) .map_err(|_e| kernel::error::code::EIO) - // We take no refcounts on the request, so we expect to be abl= e to - // end the request. The request reference must be unique at th= is - // point, and so `end_ok` cannot fail. - .expect("Fatal error - expected to be able to end request"); + .expect("Failed to complete request") + .end_ok(); } } diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index 1fd0d54dd5493..b8ecd69abe980 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -62,6 +62,7 @@ //! new_mutex, //! prelude::*, //! sync::{aref::ARef, Arc, Mutex}, +//! types::{ForeignOwnable, OwnableRefCounted, Owned}, //! }; //! //! struct MyBlkDevice; @@ -70,17 +71,18 @@ //! impl Operations for MyBlkDevice { //! type QueueData =3D (); //! -//! fn queue_rq(_queue_data: (), rq: ARef>, _is_last: bo= ol) -> Result { -//! Request::end_ok(rq); +//! fn queue_rq(_queue_data: (), rq: Owned>, _is_last: b= ool) -> Result { +//! rq.end_ok(); //! Ok(()) //! } //! //! fn commit_rqs(_queue_data: ()) {} //! //! fn complete(rq: ARef>) { -//! Request::end_ok(rq) +//! OwnableRefCounted::try_from_shared(rq) //! .map_err(|_e| kernel::error::code::EIO) -//! .expect("Fatal error - expected to be able to end request"= ); +//! .expect("Fatal error - expected to be able to end request") +//! .end_ok(); //! } //! } //! diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/oper= ations.rs index b68c0208efc66..3dea79d647ff7 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -9,10 +9,10 @@ block::mq::{request::RequestDataWrapper, Request}, error::{from_result, Result}, prelude::*, - sync::{aref::ARef, Refcount}, - types::ForeignOwnable, + sync::{aref::ARef, atomic::ordering, Refcount}, + types::{ForeignOwnable, Owned}, }; -use core::marker::PhantomData; +use core::{marker::PhantomData, ptr::NonNull}; =20 type ForeignBorrowed<'a, T> =3D ::Borrowed<'a>; =20 @@ -36,7 +36,7 @@ pub trait Operations: Sized { /// `false`, the driver is allowed to defer committing the request. fn queue_rq( queue_data: ForeignBorrowed<'_, Self::QueueData>, - rq: ARef>, + rq: Owned>, is_last: bool, ) -> Result; =20 @@ -90,16 +90,23 @@ impl OperationsVTable { // this function. let request =3D unsafe { &*(*bd).rq.cast::>() }; =20 - // One refcount for the ARef, one for being in flight - request.wrapper_ref().refcount().set(2); + debug_assert!( + request + .wrapper_ref() + .refcount() + .as_atomic() + .load(ordering::Acquire) + =3D=3D 0 + ); =20 // SAFETY: - // - We own a refcount that we took above. We pass that to `ARef`. + // - By API contract, we own the request. // - By the safety requirements of this function, `request` is a = valid // `struct request` and the private data is properly initialize= d. // - `rq` will be alive until `blk_mq_end_request` is called and = is - // reference counted by `ARef` until then. - let rq =3D unsafe { Request::aref_from_raw((*bd).rq) }; + // reference counted by until then. + let mut rq =3D + unsafe { Owned::from_raw(NonNull::>::new_unchecked(= (*bd).rq.cast())) }; =20 // SAFETY: `hctx` is valid as required by this function. let queue_data =3D unsafe { (*(*hctx).queue).queuedata }; @@ -111,7 +118,7 @@ impl OperationsVTable { let queue_data =3D unsafe { T::QueueData::borrow(queue_data) }; =20 // SAFETY: We have exclusive access and we just set the refcount a= bove. - unsafe { Request::start_unchecked(&rq) }; + unsafe { rq.start_unchecked() }; =20 let ret =3D T::queue_rq( queue_data, diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index cf013b9e2cacf..148348b4ef245 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -7,13 +7,12 @@ use crate::{ bindings, block::mq::Operations, - error::Result, sync::{ - aref::{ARef, AlwaysRefCounted, RefCounted}, - atomic::Relaxed, + aref::{ARef, RefCounted}, + atomic::ordering, Refcount, }, - types::Opaque, + types::{Opaque, Ownable, OwnableRefCounted, Owned}, }; use core::{marker::PhantomData, ptr::NonNull}; =20 @@ -21,25 +20,21 @@ /// /// # Implementation details /// -/// There are four states for a request that the Rust bindings care about: +/// There are three states for a request that the Rust bindings care about: /// -/// 1. Request is owned by block layer (refcount 0). -/// 2. Request is owned by driver but with zero [`ARef`]s in existence -/// (refcount 1). -/// 3. Request is owned by driver with exactly one [`ARef`] in existence -/// (refcount 2). -/// 4. Request is owned by driver with more than one [`ARef`] in existence -/// (refcount > 2). +/// - 0: The request is owned by C block layer or is uniquely referenced (= by [`Owned<_>`]). +/// - 1: The request is owned by Rust abstractions but is not referenced. +/// - 2+: There is one or more [`ARef`] instances referencing the request. /// +/// We need to track 1 and 2 to make sure that `tag_to_rq` does not issue = any +/// [`ARef`] to requests not owned by the driver, or to requests that have= a +/// [`Owned`] referencing it. /// -/// We need to track 1 and 2 to ensure we fail tag to request conversions = for -/// requests that are not owned by the driver. -/// -/// We need to track 3 and 4 to ensure that it is safe to end the request = and hand -/// back ownership to the block layer. +/// We need to track 3 to know when it is safe to convert an [`ARef`] to a +/// [`Owned`]. /// /// Note that the driver can still obtain new `ARef` even if there is no `= ARef`s in existence by -/// using `tag_to_rq`, hence the need to distinguish B and C. +/// using `tag_to_rq`, hence the need to distinct 1 and 2. /// /// The states are tracked through the private `refcount` field of /// `RequestDataWrapper`. This structure lives in the private data area of= the C @@ -66,6 +61,7 @@ impl Request { /// /// * The caller must own a refcount on `ptr` that is transferred to t= he /// returned [`ARef`]. + /// * The refcount must be >=3D 2. /// * The type invariants for [`Request`] must hold for the pointee of= `ptr`. /// /// [`struct request`]: srctree/include/linux/blk-mq.h @@ -76,72 +72,6 @@ pub(crate) unsafe fn aref_from_raw(ptr: *mut bindings::r= equest) -> ARef { unsafe { ARef::from_raw(NonNull::new_unchecked(ptr.cast())) } } =20 - /// Notify the block layer that a request is going to be processed now. - /// - /// The block layer uses this hook to do proper initializations such as - /// starting the timeout timer. It is a requirement that block device - /// drivers call this function when starting to process a request. - /// - /// # Safety - /// - /// The caller must have exclusive ownership of `self`, that is - /// `self.wrapper_ref().refcount() =3D=3D 2`. - pub(crate) unsafe fn start_unchecked(this: &ARef) { - // SAFETY: By type invariant, `self.0` is a valid `struct request`= and - // we have exclusive access. - unsafe { bindings::blk_mq_start_request(this.0.get()) }; - } - - /// Try to take exclusive ownership of `this` by dropping the refcount= to 0. - /// This fails if `this` is not the only [`ARef`] pointing to the unde= rlying - /// [`Request`]. - /// - /// If the operation is successful, [`Ok`] is returned with a pointer = to the - /// C [`struct request`]. If the operation fails, `this` is returned i= n the - /// [`Err`] variant. - /// - /// [`struct request`]: srctree/include/linux/blk-mq.h - fn try_set_end(this: ARef) -> Result<*mut bindings::request, ARe= f> { - // To hand back the ownership, we need the current refcount to be = 2. - // Since we can race with `TagSet::tag_to_rq`, this needs to atomi= cally reduce - // refcount to 0. `Refcount` does not provide a way to do this, so= use the underlying - // atomics directly. - if let Err(_old) =3D this - .wrapper_ref() - .refcount() - .as_atomic() - .cmpxchg(2, 0, Relaxed) - { - return Err(this); - } - - let request_ptr =3D this.0.get(); - core::mem::forget(this); - - Ok(request_ptr) - } - - /// Notify the block layer that the request has been completed without= errors. - /// - /// This function will return [`Err`] if `this` is not the only [`ARef= `] - /// referencing the request. - pub fn end_ok(this: ARef) -> Result<(), ARef> { - let request_ptr =3D Self::try_set_end(this)?; - - // SAFETY: By type invariant, `this.0` was a valid `struct request= `. The - // success of the call to `try_set_end` guarantees that there are = no - // `ARef`s pointing to this request. Therefore it is safe to hand = it - // back to the block layer. - unsafe { - bindings::blk_mq_end_request( - request_ptr, - bindings::BLK_STS_OK as bindings::blk_status_t, - ) - }; - - Ok(()) - } - /// Complete the request by scheduling `Operations::complete` for /// execution. /// @@ -234,27 +164,125 @@ unsafe impl Sync for Request {} // matching reference count decrement is executed. unsafe impl RefCounted for Request { fn inc_ref(&self) { - self.wrapper_ref().refcount().inc(); + let refcount =3D &self.wrapper_ref().refcount().as_atomic(); + + // Load acquire, store relaxed. We sync with store release of + // `OwnableRefCounted::into_shared`. After that all unique referen= ces are dead and we have + // shared access. We can use relaxed ordering for the store. + #[cfg_attr(not(debug_assertions), allow(unused_variables))] + let old =3D refcount.fetch_add(1, ordering::Acquire); + + debug_assert!(old >=3D 1, "Request refcount zero clone"); } =20 unsafe fn dec_ref(obj: core::ptr::NonNull) { - // SAFETY: The type invariants of `ARef` guarantee that `obj` is v= alid + // SAFETY: The type invariants of `RefCounted` guarantee that `obj= ` is valid // for read. let wrapper_ptr =3D unsafe { Self::wrapper_ptr(obj.as_ptr()).as_pt= r() }; // SAFETY: The type invariant of `Request` guarantees that the pri= vate // data area is initialized and valid. let refcount =3D unsafe { &*RequestDataWrapper::refcount_ptr(wrapp= er_ptr) }; =20 - #[cfg_attr(not(CONFIG_DEBUG_MISC), allow(unused_variables))] - let is_zero =3D refcount.dec_and_test(); + // Store release to sync with load acquire in + // `OwnableRefCounted::try_from_shared`. + #[cfg_attr(not(debug_assertions), allow(unused_variables))] + let old =3D refcount.as_atomic().fetch_sub(1, ordering::Release); =20 - #[cfg(CONFIG_DEBUG_MISC)] - if is_zero { - panic!("Request reached refcount zero in Rust abstractions"); - } + debug_assert!( + old > 1, + "Request reached refcount zero in Rust abstractions" + ); + } +} + +impl Owned> { + /// Notify the block layer that a request is going to be processed now. + /// + /// The block layer uses this hook to do proper initializations such as + /// starting the timeout timer. It is a requirement that block device + /// drivers call this function when starting to process a request. + /// + /// # Safety + /// + /// The caller must have exclusive ownership of `self`, that is + /// `self.wrapper_ref().refcount() =3D=3D 0`. + /// + /// This can only be called once in the request life cycle. + pub(crate) unsafe fn start_unchecked(&mut self) { + // SAFETY: By type invariant, `self.0` is a valid `struct request`= and + // we have exclusive access. + unsafe { bindings::blk_mq_start_request(self.0.get()) }; + } + + /// Notify the block layer that the request has been completed without= errors. + pub fn end_ok(self) { + let request_ptr =3D self.0.get().cast(); + core::mem::forget(self); + // SAFETY: By type invariant, `this.0` was a valid `struct request= `. The + // existence of `self` guarantees that there are no `ARef`s pointi= ng to + // this request. Therefore it is safe to hand it back to the block + // layer. + unsafe { bindings::blk_mq_end_request(request_ptr, bindings::BLK_S= TS_OK) }; + } +} + +// SAFETY: The `release` implementation frees the underlying request accor= ding to the reference +// counting scheme for `Request`. +unsafe impl Ownable for Request { + unsafe fn release(this: NonNull) { + // SAFETY: The safety requirements of this function guarantee that= `this` + // is valid for read. + let wrapper_ptr =3D unsafe { Self::wrapper_ptr(this.as_ptr()).as_p= tr() }; + // SAFETY: The type invariant of `Request` guarantees that the pri= vate + // data area is initialized and valid. + let refcount =3D unsafe { &*RequestDataWrapper::refcount_ptr(wrapp= er_ptr) }; + + // Store release to sync with load acquire when converting back to= owned. + #[cfg_attr(not(debug_assertions), allow(unused_variables))] + let old =3D refcount.as_atomic().fetch_add(1, ordering::Release); + + debug_assert!( + old =3D=3D 0, + "Invalid refcount when releasing `Owned>`" + ); } } =20 -// SAFETY: We currently do not implement `Ownable`, thus it is okay to obt= ain an `ARef` -// from a `&Request` (but this will change in the future). -unsafe impl AlwaysRefCounted for Request {} +impl OwnableRefCounted for Request { + fn try_from_shared(this: ARef) -> core::result::Result, ARef> { + // Load acquire to sync with decrement store release to make sure = all + // shared access has ended. + let updated =3D this + .wrapper_ref() + .refcount() + .as_atomic() + .cmpxchg(2, 0, ordering::Acquire); + + match updated { + Ok(_) =3D> Ok( + // SAFETY: We achieved unique ownership above. + unsafe { Owned::from_raw(ARef::into_raw(this)) }, + ), + Err(_) =3D> Err(this), + } + } + + fn into_shared(this: Owned) -> ARef { + // Store release to sync with future increments using load acquire= to + // make sure exclusive access has ended before shared access start. + #[cfg_attr(not(debug_assertions), allow(unused_variables))] + let old =3D this + .wrapper_ref() + .refcount() + .as_atomic() + .fetch_add(2, ordering::Release); + + debug_assert!( + old =3D=3D 0, + "Invalid refcount when upgrading `Owned>`" + ); + + // SAFETY: We incremented the refcount above. + unsafe { ARef::from_raw(Owned::into_raw(this)) } + } +} --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 64A672DF6E9; Sun, 15 Feb 2026 23:45:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199106; cv=none; b=bIngt387DcWCBcHT0nV+uL9b/EymlpVz98zLTXjYpmO+81LgRYJFpWtu7cRXuNhNZjhDylIltErUZwz94IXZYqziA0N/onCZnMp7q9Mv/Qr2t4GIzYgut0EtfRauhuhyW7V3hIezUTtj1y+NecuQIJoeKRkx0AB7zh87kLb3BUo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199106; c=relaxed/simple; bh=nKDJESXMMdAbXJmbQH/TDKLxr1LGgof53q+XTMeqr04=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=g4S/XefkW8wlAy1CZl0DopAymOExDn8lVhtIExcsIlkW3gf2HRoyCGWmu5TjO/oG9Bdyl2ctycz1ax/ZmpKtJCzcU8DF4EsEApfLw4iPmEFPKI8HgBYsqEej3ZwUMQrgrPUvyQozdAX02dtNyJp4eSwfcAa4SeHwp2qQqdnfZME= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=fu0XHhmJ; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="fu0XHhmJ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id AF990C4CEF7; Sun, 15 Feb 2026 23:45:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199106; bh=nKDJESXMMdAbXJmbQH/TDKLxr1LGgof53q+XTMeqr04=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=fu0XHhmJy1gX/cIRnvFxxJ+Y6oO6mmRvqlrUbBDEAj7/MDVFRt8wHqVsDplzrIE5T 5EOS9gGCApRYbQcciwZ07npml4Uvrks09ds2DcHMJ1tcvhe4fNjl6tcbYAWqWtUl76 KnoZ76AcPMBYHrXAnJPaTJf9TmwRH0MdLxin18ihIcwxPV6Pcv5IYBq78fTPOTX3QR aloupiIdvfV5i1HQMGoQkNwR4OLzgwkaab1uWkDyTn6wtKtil5GQg+U3YmXLcAlmZc Jb/apVlrYiUjpbWdaOHC4gwTXpZtwM3V0S+fD1WZO/cQ2nNhTcNDvFbYb6OCfQ03LS x2WLZhUmvGAhQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:34:53 +0100 Subject: [PATCH 06/79] block: rust: add `Request` private data support 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: <20260216-rnull-v6-19-rc5-send-v1-6-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=8672; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=nKDJESXMMdAbXJmbQH/TDKLxr1LGgof53q+XTMeqr04=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklghxER7z4eSTdEgWu7X/lQaa701iP5OWF88X 2vR1dp8TKmJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYIQAKCRDhuBo+eShj d5E4D/0WP5q39O/TWBj0zA73T1cdUnagb39/g6S+WLyQMwhG/g/V+RteReTcHJduUiBc1vZIWc+ liak4eD/kewjxEn34O7jS0CPyMcS532TA4dhaGsMjw6ClQDzI/iqw6HMi/JZOEq3Dbkx7xk8htA sKWAUpXf3+xxGuF9t/Nc/OxHivpjIsArUeX5fz5anIMuwKMNQtWx3hCFMbXUDxr/VuJ8f5iqKBb ZdSonYSCcXir9fXGDpPV54nM18ZZja6RsoZTuNVKzbJO7YVzWIfFyMM38/Gr1XExlzehFsp33lU Lj97Q01XDL2fxwgY7ru1gJ3R+f+hmrBNdLQOQKECNVx/4UbBGt69bfO1BKXk82zHionAFBJXZnN 0wCOIVvqO2ecr3ZjQxKACU2XahihjhlnMyOa2u3NM7T72+D2fpV6PwB8awhuKMIC7oXyjsPMLBm xYfaURbuvtahZg9e2jLbtkf/bvg7hiYdYB4InVbQ2SrUyJYUu1rI2jc/Zzfohdoi/+QHFc+01Kp w1KhO+jQDsqeaf7qKbSybYPtkmK7BmGdnjK2xB5LtkyJHL+IXkZ7hTY8DCgvUp6Dnr6DsTZPBmp ETjeKMMkQ0b+BhUbxUZ7JBnWGuVOJl1pyKmN5HBFFl7FrmMbOP17swDr2zZ4tkL2aaalrIDXXMt J36cFJ6YWjJ1Ahw== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 C block device drivers can attach private data to a `struct request`. This data is stored next to the request structure and is part of the request allocation set up during driver initialization. Expose this private request data area to Rust block device drivers. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/rnull.rs | 5 +++++ rust/kernel/block/mq.rs | 6 ++++++ rust/kernel/block/mq/operations.rs | 24 +++++++++++++++++++++++- rust/kernel/block/mq/request.rs | 24 +++++++++++++++++++----- rust/kernel/block/mq/tag_set.rs | 2 +- 5 files changed, 54 insertions(+), 7 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 6a7f660d31998..065639fc4f941 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -134,6 +134,11 @@ struct QueueData { #[vtable] impl Operations for NullBlkDevice { type QueueData =3D KBox; + type RequestData =3D (); + + fn new_request_data() -> impl PinInit { + pin_init::zeroed::() + } =20 #[inline(always)] fn queue_rq(queue_data: &QueueData, rq: Owned>, _is_= last: bool) -> Result { diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index b8ecd69abe980..a285b753ada88 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -69,8 +69,14 @@ //! //! #[vtable] //! impl Operations for MyBlkDevice { +//! type RequestData =3D (); //! type QueueData =3D (); //! +//! fn new_request_data( +//! ) -> impl PinInit<()> { +//! pin_init::zeroed::<()>() +//! } +//! //! fn queue_rq(_queue_data: (), rq: Owned>, _is_last: b= ool) -> Result { //! rq.end_ok(); //! Ok(()) diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/oper= ations.rs index 3dea79d647ff7..cd37b939bbf30 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -13,6 +13,7 @@ types::{ForeignOwnable, Owned}, }; use core::{marker::PhantomData, ptr::NonNull}; +use pin_init::PinInit; =20 type ForeignBorrowed<'a, T> =3D ::Borrowed<'a>; =20 @@ -28,10 +29,24 @@ /// [module level documentation]: kernel::block::mq #[macros::vtable] pub trait Operations: Sized { + /// Data associated with a request. This data is located next to the r= equest + /// structure. + /// + /// To be able to handle accessing this data from interrupt context, t= his + /// data must be `Sync`. + /// + /// The `RequestData` object is initialized when the requests are allo= cated + /// during queue initialization, and it is are dropped when the reques= ts are + /// dropped during queue teardown. + type RequestData: Sized + Sync; + /// Data associated with the `struct request_queue` that is allocated = for /// the `GenDisk` associated with this `Operations` implementation. type QueueData: ForeignOwnable; =20 + /// Called by the kernel to get an initializer for a `Pin<&mut Request= Data>`. + fn new_request_data() -> impl PinInit; + /// Called by the kernel to queue a request with the driver. If `is_la= st` is /// `false`, the driver is allowed to defer committing the request. fn queue_rq( @@ -236,6 +251,13 @@ impl OperationsVTable { // it is valid for writes. unsafe { RequestDataWrapper::refcount_ptr(pdu.as_ptr()).write(= Refcount::new(0)) }; =20 + let initializer =3D T::new_request_data(); + + // SAFETY: `pdu` is a valid pointer as established above. We d= o not + // touch `pdu` if `__pinned_init` returns an error. We promise= ot to + // move the pointee of `pdu`. + unsafe { initializer.__pinned_init(RequestDataWrapper::data_pt= r(pdu.as_ptr()))? }; + Ok(0) }) } @@ -255,7 +277,7 @@ impl OperationsVTable { ) { // SAFETY: The tagset invariants guarantee that all requests are a= llocated with extra memory // for the request data. - let pdu =3D unsafe { bindings::blk_mq_rq_to_pdu(rq) }.cast::(); + let pdu =3D unsafe { bindings::blk_mq_rq_to_pdu(rq) }.cast::>(); =20 // SAFETY: `pdu` is valid for read and write and is properly initi= alised. unsafe { core::ptr::drop_in_place(pdu) }; diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index 148348b4ef245..8a6c29ac627ee 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -96,12 +96,12 @@ pub fn complete(this: ARef) { /// /// - `this` must point to a valid allocation of size at least size of /// [`Self`] plus size of [`RequestDataWrapper`]. - pub(crate) unsafe fn wrapper_ptr(this: *mut Self) -> NonNull { + pub(crate) unsafe fn wrapper_ptr(this: *mut Self) -> NonNull> { let request_ptr =3D this.cast::(); // SAFETY: By safety requirements for this function, `this` is a // valid allocation. let wrapper_ptr =3D - unsafe { bindings::blk_mq_rq_to_pdu(request_ptr).cast::() }; + unsafe { bindings::blk_mq_rq_to_pdu(request_ptr).cast::>() }; // SAFETY: By C API contract, `wrapper_ptr` points to a valid allo= cation // and is not null. unsafe { NonNull::new_unchecked(wrapper_ptr) } @@ -109,7 +109,7 @@ pub(crate) unsafe fn wrapper_ptr(this: *mut Self) -> No= nNull =20 /// Return a reference to the [`RequestDataWrapper`] stored in the pri= vate /// area of the request structure. - pub(crate) fn wrapper_ref(&self) -> &RequestDataWrapper { + pub(crate) fn wrapper_ref(&self) -> &RequestDataWrapper { // SAFETY: By type invariant, `self.0` is a valid allocation. Furt= her, // the private data associated with this request is initialized and // valid. The existence of `&self` guarantees that the private dat= a is @@ -121,16 +121,19 @@ pub(crate) fn wrapper_ref(&self) -> &RequestDataWrapp= er { /// A wrapper around data stored in the private area of the C [`struct req= uest`]. /// /// [`struct request`]: srctree/include/linux/blk-mq.h -pub(crate) struct RequestDataWrapper { +pub(crate) struct RequestDataWrapper { /// The Rust request refcount has the following states: /// /// - 0: The request is owned by C block layer. /// - 1: The request is owned by Rust abstractions but there are no [`= ARef`] references to it. /// - 2+: There are [`ARef`] references to the request. refcount: Refcount, + + /// Driver managed request data + data: T::RequestData, } =20 -impl RequestDataWrapper { +impl RequestDataWrapper { /// Return a reference to the refcount of the request that is embedding /// `self`. pub(crate) fn refcount(&self) -> &Refcount { @@ -148,6 +151,17 @@ pub(crate) unsafe fn refcount_ptr(this: *mut Self) -> = *mut Refcount { // field projection is safe. unsafe { &raw mut (*this).refcount } } + + /// Return a pointer to the `data` field of the `Self` pointed to by `= this`. + /// + /// # Safety + /// + /// - `this` must point to a live allocation of at least the size of `= Self`. + pub(crate) unsafe fn data_ptr(this: *mut Self) -> *mut T::RequestData { + // SAFETY: Because of the safety requirements of this function, the + // field projection is safe. + unsafe { &raw mut (*this).data } + } } =20 // SAFETY: Exclusive access is thread-safe for `Request`. `Request` has no= `&mut diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set= .rs index c3cf56d52beec..46481754b1335 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -41,7 +41,7 @@ pub fn new( // SAFETY: `blk_mq_tag_set` only contains integers and pointers, w= hich // all are allowed to be 0. let tag_set: bindings::blk_mq_tag_set =3D unsafe { core::mem::zero= ed() }; - let tag_set: Result<_> =3D core::mem::size_of::() + let tag_set: Result<_> =3D core::mem::size_of::>() .try_into() .map(|cmd_size| { bindings::blk_mq_tag_set { --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 1867F30E0EF; Sun, 15 Feb 2026 23:47:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199226; cv=none; b=V38v16BDcgrUn7SlwHjyj+NpvV9+r5Kkcd/4Xby1zUWgYL8l806+QCQDPi4GquuEkoRw9ZVl/4dklDOpau8wE939g3rSWeLZ5d8dcL5ZNP5poq1LbPfuOkNdvSR59octAHpJaYPxQBu0HUyW5l0zz2NySM0opGUhI3c2ol+T9fo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199226; c=relaxed/simple; bh=LfEJY7xsVq6LTYvFdGhIx9M30ktfxy1WzlpEoNzCFmo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=i3OSgHFav5ztsu1rH6tACUIhmCjOUZVOB4QL2DR+mIh1iejkGLN/NopsputiJ5C1rcmsmlOsgsCNnGUDBzK9G6yq9HPQDG+pBxOtMFGRg3if89KraBag2O1nPrejO+hxcFeQWopNdYU7YQ4+KVhzgYi7fB15b5eMmmGVHt4OReU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=NCQzTIxh; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="NCQzTIxh" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 67CB2C4CEF7; Sun, 15 Feb 2026 23:47:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199225; bh=LfEJY7xsVq6LTYvFdGhIx9M30ktfxy1WzlpEoNzCFmo=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=NCQzTIxhjQC5qtGYKhlogOzFs6mSy7j5BfyQbmkW6bZ23Nm01v0Gx4eL2MqdT9mEJ QdzoM0JbRegOH/Ds6ZhUpdUVZB6AMdNKgtKyYw2hMZOszucxe/+tijwhOYT6gRi8rm fBpRc/9Lwy+glV/7XYJmQ2FfdzUqBsVUr3z1drCq+1QHZpRsFCJ+WUQ4BW+CZn4Z2C vmf3SHyaCJXEWX/5+1ECHdQFc9VwRi0Fju6XaW0JjQXYjxAW1KmkaiL/DmMOchtXU8 zLufISjSFwQ54mlErZmQlKjOGyjxh+RISdZEs5tyglr1lkDFGqc3QM0ZfQ3pmPs1tu yFUSmC5OXS+LQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:34:54 +0100 Subject: [PATCH 07/79] block: rust: allow `hrtimer::Timer` in `RequestData` 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: <20260216-rnull-v6-19-rc5-send-v1-7-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=8261; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=LfEJY7xsVq6LTYvFdGhIx9M30ktfxy1WzlpEoNzCFmo=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklgisv81RN4Rm6WZCUvmCKYbNqWj7DW/uhMxn ihoGWWE2gyJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYIgAKCRDhuBo+eShj d984D/9kempnq428rshpY56BpINqqSCTNKH2DMV2IZPnlP7E8eGvuqDq20+uXKTtiBOUU1UDJS9 JsyksU6I4yySv9J8SCUa4V6Z3aEqSUwPDxhD//UgNKGzQ0nJ8Nhr7YjUaKg9nOMoe4wfYPhfoAH Y2/NRUUqy6aAlu1VgvQll0qkhpALuIbBF8agrW0CVP43NT1/X5hd840ZN9UbwEOnpjq6XntjVPG HXfqrq2dmYoSJtM8D4izvHgRbFw7AAcaf7ksJMkv7W3NcJ1i+5wVzXQbN8LSlLPPsK6p+scfu09 OOO6mbSb7svIwlTd0j6kzyvcQQD1W+2D5mzqkcMvrl5SmMm5Bbb20sKohnYN3HnEiWB9liR5y0C lgrpPV817hFLl/xMt03VZktA/OfPo1ogf5NKKE9UsE70O19LTrC+6HGWrsV69NoTbiF1z/WVVI3 A9jyXz1+RLxDfh7VuPW4PDb1c+/7qMOPklDfodVuMsi8UZCJjukk0Fsv7Kk21o89swP3EnJV3Gk twUkStoY4jMiJsePabJxYMkArymNF9Ua0rdt3ttxnpk681zxO/q1DvWpqvCRM+MKurGYa4vU3HK ILN/d57ssX56jNN6powR5bD20xHEC+C+GkWC+Cn86ABJhRTNq03AHi0RzFkChYFTQwqF95esHrQ Ab0LbB319eKwJlw== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 `Request` is essentially a smart pointer that derefs to `Operations::RequestData`. To use an `HrTimer` in `Operations::RequestData` via the `Request` pointer, we must implement `HrTimerPointer` for `Request`. Thus, implement `HrTimerPointer` and friends for `ARef`. Publicly export `HrTimer::raw_cancel` and `HrTimer::into_c`. Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq.rs | 1 + rust/kernel/block/mq/request.rs | 138 ++++++++++++++++++++++++++++++++++++= +++- rust/kernel/time/hrtimer.rs | 5 +- 3 files changed, 141 insertions(+), 3 deletions(-) diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index a285b753ada88..cedaf85ba0b1d 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -108,4 +108,5 @@ =20 pub use operations::Operations; pub use request::Request; +pub use request::RequestTimerHandle; pub use tag_set::TagSet; diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index 8a6c29ac627ee..f270060be27eb 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -12,9 +12,12 @@ atomic::ordering, Refcount, }, + time::hrtimer::{ + HasHrTimer, HrTimer, HrTimerCallback, HrTimerHandle, HrTimerMode, = HrTimerPointer, + }, types::{Opaque, Ownable, OwnableRefCounted, Owned}, }; -use core::{marker::PhantomData, ptr::NonNull}; +use core::{ffi::c_void, marker::PhantomData, ptr::NonNull}; =20 /// A wrapper around a blk-mq [`struct request`]. This represents an IO re= quest. /// @@ -116,6 +119,11 @@ pub(crate) fn wrapper_ref(&self) -> &RequestDataWrappe= r { // valid as a shared reference. unsafe { Self::wrapper_ptr(core::ptr::from_ref(self).cast_mut()).a= s_ref() } } + + /// Return a reference to the per-request data associated with this re= quest. + pub fn data_ref(&self) -> &T::RequestData { + &self.wrapper_ref().data + } } =20 /// A wrapper around data stored in the private area of the C [`struct req= uest`]. @@ -300,3 +308,131 @@ fn into_shared(this: Owned) -> ARef { unsafe { ARef::from_raw(Owned::into_raw(this)) } } } + +/// A handle for a timer that is embedded in a [`Request`] private data ar= ea. +pub struct RequestTimerHandle +where + T: Operations, + T::RequestData: HasHrTimer, +{ + inner: ARef>, +} + +// SAFETY: The drop implementation of `RequestTimerHandle` calls `cancel`,= which cancels the timer +// if it is running. `drop` will block if the timer handler is running. Th= is is ensured via a call +// to `HrTimer::raw_cancel` in the implementation of `cancel`. +unsafe impl HrTimerHandle for RequestTimerHandle +where + T: Operations, + T::RequestData: HasHrTimer, +{ + fn cancel(&mut self) -> bool { + let request_data_ptr =3D &self.inner.wrapper_ref().data as *const = T::RequestData; + + // SAFETY: As we obtained `self_ptr` from a valid reference above,= it + // must point to a valid `U`. + let timer_ptr =3D unsafe { + >::raw_get_timer(= request_data_ptr) + }; + + // SAFETY: As `timer_ptr` points into `U` and `U` is valid, `timer= _ptr` + // must point to a valid `HrTimer` instance. + unsafe { HrTimer::::raw_cancel(timer_ptr) } + } +} + +impl RequestTimerHandle +where + T: Operations, + T::RequestData: HasHrTimer, +{ + /// Drop the timer handle without cancelling the timer. + /// + /// This is safe because dropping the last [`ARef`] does not = drop the [`Request`]. + pub fn dismiss(mut self) { + let inner =3D core::ptr::from_mut(&mut self.inner); + + // SAFETY: `inner` is valid for reads and writes, is properly alig= ned and nonnull. We have + // exclusive access to `inner` and we do not access `inner` after = this call. + unsafe { core::ptr::drop_in_place(inner) }; + core::mem::forget(self); + } +} + +impl Drop for RequestTimerHandle +where + T: Operations, + T::RequestData: HasHrTimer, +{ + fn drop(&mut self) { + self.cancel(); + } +} + +impl HrTimerPointer for ARef> +where + T: Operations, + T::RequestData: HasHrTimer, + T::RequestData: Sync, +{ + type TimerMode =3D >::Tim= erMode; + type TimerHandle =3D RequestTimerHandle; + + fn start(self, expires: ::Expires) -> = RequestTimerHandle { + let pdu_ptr =3D self.data_ref() as *const T::RequestData; + + // SAFETY: `pdu_pointer` is coerced from a live reference to a `T`= and this points to a + // valid `T`. The reference is valid until `T` is dropped, and the= timer will be canceled + // before this. + unsafe { T::RequestData::start(pdu_ptr, expires) }; + + RequestTimerHandle { inner: self } + } +} + +impl kernel::time::hrtimer::RawHrTimerCallback for ARef> +where + T: Operations, + T::RequestData: HasHrTimer, + T::RequestData: for<'a> HrTimerCallback =3D ARef>>, + T::RequestData: Sync, +{ + type CallbackTarget<'a> =3D Self; + + unsafe extern "C" fn run(ptr: *mut bindings::hrtimer) -> bindings::hrt= imer_restart { + // `HrTimer` is `repr(transparent)` + let timer_ptr =3D ptr.cast::>(); + + // SAFETY: By C API contract `ptr` is the pointer we passed when + // enqueuing the timer, so it is a `HrTimer` embed= ded in a `T::RequestData` + let request_data_ptr =3D unsafe { T::RequestData::timer_container_= of(timer_ptr) }; + + let offset =3D core::mem::offset_of!(RequestDataWrapper, data); + + // SAFETY: This sub stays within the `bindings::request` allocatio= n and does not wrap. + let pdu_ptr =3D unsafe { + request_data_ptr + .cast::() + .sub(offset) + .cast::>() + }; + + // SAFETY: This request pointer was passed to us by the kernel in = `init_request_callback`. + let request_ptr =3D unsafe { bindings::blk_mq_rq_from_pdu(pdu_ptr.= cast::()) }; + + // SAFETY: By C API contract, we have ownership of the request. + let request_ref =3D unsafe { &*(request_ptr as *const Request) = }; + + request_ref.inc_ref(); + // SAFETY: We just incremented the refcount above. + let aref: ARef> =3D unsafe { ARef::from_raw(NonNull::fr= om(request_ref)) }; + + // SAFETY: + // - By C API contract `timer_ptr` is the pointer that we passed w= hen queuing the timer, so + // it is a valid pointer to a `HrTimer` embedded in a `T`. + // - We are within `RawHrTimerCallback::run` + let context =3D unsafe { kernel::time::hrtimer::HrTimerCallbackCon= text::from_raw(timer_ptr) }; + + T::RequestData::run(aref, context).into_c() + } +} diff --git a/rust/kernel/time/hrtimer.rs b/rust/kernel/time/hrtimer.rs index b2272b059e504..0512a8dfee82a 100644 --- a/rust/kernel/time/hrtimer.rs +++ b/rust/kernel/time/hrtimer.rs @@ -160,7 +160,7 @@ unsafe fn raw_get(this: *const Self) -> *mut bindings::= hrtimer { /// # Safety /// /// `this` must point to a valid `Self`. - pub(crate) unsafe fn raw_cancel(this: *const Self) -> bool { + pub unsafe fn raw_cancel(this: *const Self) -> bool { // SAFETY: `this` points to an allocation of at least `HrTimer` si= ze. let c_timer_ptr =3D unsafe { HrTimer::raw_get(this) }; =20 @@ -514,7 +514,8 @@ pub enum HrTimerRestart { } =20 impl HrTimerRestart { - fn into_c(self) -> bindings::hrtimer_restart { + /// Convert `self` into an integer for FFI use. + pub fn into_c(self) -> bindings::hrtimer_restart { self as bindings::hrtimer_restart } } --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 DEF572E7F3A; Sun, 15 Feb 2026 23:46:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199160; cv=none; b=f5jw0xjVR9J+MTIWGFovp8Lv8GAj93+CcF6v9Ah3c6o39KySJbopi1svCp9g69omVnTX3UlufnG9dKV+wD54fV6AiyMiEk1pkvz9UUreBxjDBmgqNDNSO9Gaoe6xzgm8/dk3FnpS/vMHw7z7KXcqfhrzXGDxz499HTPjxVCHRPM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199160; c=relaxed/simple; bh=jZ608odEKTUNSFrE/SOnMnlQT6DB0sc7l5Lx9qmISMk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=RS0ZRlVzo1c+O4GFJ0vWU9JpCRcPpA3lgjY6cEuZmPWIXwqDpUHwA4689n6mCCOf1PKTy/ZAKJkX3WzlijWJx27xggHLUlN8RrLAtw+Dew0IZ4V/mLFDn+wAqohyEnDmkxXuz3YTaX+noW7ndJDZS4K8p6WTX4lESABmkUcgssc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=XiiM5Ah7; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="XiiM5Ah7" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 74CB6C4CEF7; Sun, 15 Feb 2026 23:45:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199160; bh=jZ608odEKTUNSFrE/SOnMnlQT6DB0sc7l5Lx9qmISMk=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=XiiM5Ah7X1e+o6XtV6o5U46+0qoO5UBHAvPqA6M9opCsoMYbca+mSnjPGI5UUMZcB fcGlayUQjIl1YBDKK+G2+jJN2cSR3z0h0KlEqXphZui08jmMn/6CNHtl5J/5UUEonC AP0vm7CFamxgQ/m63F8pnzQ6GBB/f4vhMLuHgGqjb4mwNEYEpAw5SD77h7Ihsr2rsF 5jXy2Yu8W5PKJjqUNbiGuX9MvREIPFt3LLfP8TZqqOyY756I5j4TgsEs0PkFEjdP9g hwVDqNYVmmP7dpmAHf0hhFvirmBzf67K5gzMOPpd3ZYQumw3jncTu/l2qtgiJtPdYj BHow284+aMSHQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:34:55 +0100 Subject: [PATCH 08/79] block: rnull: add timer completion mode 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: <20260216-rnull-v6-19-rc5-send-v1-8-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=7586; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=jZ608odEKTUNSFrE/SOnMnlQT6DB0sc7l5Lx9qmISMk=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklgj0Yu8uMU7TEqY3x4l6rKBJxrFk0MqHBWwe o7L8Uuy2cWJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYIwAKCRDhuBo+eShj d7JtD/49ta7KxNhGfU2B490AUk8e8AXtKTzvkoZnq2sUdzew7/4YETUXrT4HgmA6SCqMCSaQ4H+ 55/4mU4jRsiwTzwPsga1dVDIMa7C+MCkEZTDlOLXP2m66kfpaRkdXSkp0dYLyh4EDfK6d7kINCl XPAPBtrruq6bFMqEI52dRmhZISzCDZw0ZSBM0SCneWoxYg155cjApTLUSJHVGFv/bVrLn/jC3Ar OOvgErOMpzy9k6f76F/7QiisnQABLat+plLHeIlQ3v8KfS8hErej5W2XtCLXowv9Z+r9IMWghvJ enfQQ5mfwS1UrCCz7WNbp7D6QFPqKdzZdhNnEoUuOipgC+QtYVtgEdsRQp4v6n3D5P1U1zkc3Lo f4IA0vDg+GddKYKIWY6y20i0/VvrmeIVRbd874mcvI3PfbiLdO1NblXOVNoTwChs1AOfMOFDEGH 7vZD0r3ErtgHVJ0hszcfD1wlIdbQb1ms8J292ZGsqsHdfF+hTtcrIInHuqw3R1TaWDB+NOqaygB llsnTOLj45WFwHhuGZNKZfvLsHYkdP3ikxcxojLPNjebDl3d38v+Acwe47Mj1tpJqwuYcgXV/cp On7C6vtYpqzO/BoztgKgse5Q9VQe1l7R7DYT/R7s5y3sZQIjOoJPZFZBKonL0F+37+MnaZrKnkA RO3m9tVKOHxQ50g== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a timer completion mode to `rnull`. This will complete requests after a specified time has elapsed. To use this mode of operation, set `irqmode` to `2` and write a timeout in nanoseconds to `completion_nsec`. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 13 +++++++-- drivers/block/rnull/rnull.rs | 63 +++++++++++++++++++++++++++++++++++++= +--- 2 files changed, 70 insertions(+), 6 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index eafa71dfc40d3..7952f41f42bfd 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -26,7 +26,8 @@ kstrtobool_bytes, CString, // }, - sync::Mutex, // + sync::Mutex, + time, // }; use macros::{ configfs_simple_bool_field, @@ -58,7 +59,7 @@ impl AttributeOperations<0> for Config { =20 fn show(_this: &Config, page: &mut [u8; PAGE_SIZE]) -> Result { let mut writer =3D kernel::str::Formatter::new(page); - writer.write_str("blocksize,size,rotational,irqmode\n")?; + writer.write_str("blocksize,size,rotational,irqmode,completion_nse= c\n")?; Ok(writer.bytes_written()) } } @@ -81,6 +82,7 @@ fn make_group( rotational: 2, size: 3, irqmode: 4, + completion_nsec: 5, ], }; =20 @@ -96,6 +98,7 @@ fn make_group( disk: None, capacity_mib: 4096, irq_mode: IRQMode::None, + completion_time: time::Delta::ZERO, name: name.try_into()?, }), }), @@ -108,6 +111,7 @@ fn make_group( pub(crate) enum IRQMode { None, Soft, + Timer, } =20 impl TryFrom for IRQMode { @@ -117,6 +121,7 @@ fn try_from(value: u8) -> Result { match value { 0 =3D> Ok(Self::None), 1 =3D> Ok(Self::Soft), + 2 =3D> Ok(Self::Timer), _ =3D> Err(EINVAL), } } @@ -127,6 +132,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Resul= t { match self { Self::None =3D> f.write_str("0")?, Self::Soft =3D> f.write_str("1")?, + Self::Timer =3D> f.write_str("2")?, } Ok(()) } @@ -146,6 +152,7 @@ struct DeviceConfigInner { rotational: bool, capacity_mib: u64, irq_mode: IRQMode, + completion_time: time::Delta, disk: Option>, } =20 @@ -176,6 +183,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { guard.rotational, guard.capacity_mib, guard.irq_mode, + guard.completion_time, )?); guard.powered =3D true; } else if guard.powered && !power_op { @@ -191,6 +199,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { configfs_simple_bool_field!(DeviceConfig, 2, rotational); configfs_simple_field!(DeviceConfig, 3, capacity_mib, u64); configfs_simple_field!(DeviceConfig, 4, irq_mode, IRQMode); +configfs_simple_field!(DeviceConfig, 5, completion_time, i64, into time::D= elta::from_nanos); =20 impl core::str::FromStr for IRQMode { type Err =3D Error; diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 065639fc4f941..55e56c39f1c2c 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -28,6 +28,15 @@ Arc, Mutex, // }, + time::{ + hrtimer::{ + HrTimerCallback, + HrTimerCallbackContext, + HrTimerPointer, + HrTimerRestart, // + }, + Delta, + }, types::{ OwnableRefCounted, Owned, // @@ -61,7 +70,11 @@ }, irqmode: u8 { default: 0, - description: "IRQ completion handler. 0-none, 1-softirq", + description: "IRQ completion handler. 0-none, 1-softirq, 2-ti= mer", + }, + completion_nsec: u64 { + default: 10_000, + description: "Time in ns to complete a request in hardware. D= efault: 10,000ns", }, }, } @@ -81,6 +94,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { let mut disks =3D KVec::new(); =20 let defer_init =3D move || -> Result<_, Error> { + let completion_time: i64 =3D (*module_parameters::completion_n= sec.value()).try_into()?; for i in 0..(*module_parameters::nr_devices.value()) { let name =3D CString::try_from_fmt(fmt!("rnullb{}", i))?; =20 @@ -90,6 +104,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { *module_parameters::rotational.value() !=3D 0, *module_parameters::gb.value() * 1024, (*module_parameters::irqmode.value()).try_into()?, + Delta::from_nanos(completion_time), )?; disks.push(disk, GFP_KERNEL)?; } @@ -113,10 +128,17 @@ fn new( rotational: bool, capacity_mib: u64, irq_mode: IRQMode, + completion_time: Delta, ) -> Result> { let tagset =3D Arc::pin_init(TagSet::new(1, 256, 1), GFP_KERNEL)?; =20 - let queue_data =3D Box::new(QueueData { irq_mode }, GFP_KERNEL)?; + let queue_data =3D Box::new( + QueueData { + irq_mode, + completion_time, + }, + GFP_KERNEL, + )?; =20 gen_disk::GenDiskBuilder::new() .capacity_sectors(capacity_mib << (20 - block::SECTOR_SHIFT)) @@ -129,15 +151,43 @@ fn new( =20 struct QueueData { irq_mode: IRQMode, + completion_time: Delta, +} + +#[pin_data] +struct Pdu { + #[pin] + timer: kernel::time::hrtimer::HrTimer, +} + +impl HrTimerCallback for Pdu { + type Pointer<'a> =3D ARef>; + + fn run(this: Self::Pointer<'_>, _context: HrTimerCallbackContext<'_, S= elf>) -> HrTimerRestart { + OwnableRefCounted::try_from_shared(this) + .map_err(|_e| kernel::error::code::EIO) + .expect("Failed to complete request") + .end_ok(); + HrTimerRestart::NoRestart + } +} + +kernel::impl_has_hr_timer! { + impl HasHrTimer for Pdu { + mode: kernel::time::hrtimer::RelativeMode, + field: self.timer, + } } =20 #[vtable] impl Operations for NullBlkDevice { type QueueData =3D KBox; - type RequestData =3D (); + type RequestData =3D Pdu; =20 fn new_request_data() -> impl PinInit { - pin_init::zeroed::() + pin_init!(Pdu { + timer <- kernel::time::hrtimer::HrTimer::new(), + }) } =20 #[inline(always)] @@ -145,6 +195,11 @@ fn queue_rq(queue_data: &QueueData, rq: Owned>, _is_last: bool match queue_data.irq_mode { IRQMode::None =3D> rq.end_ok(), IRQMode::Soft =3D> mq::Request::complete(rq.into()), + IRQMode::Timer =3D> { + OwnableRefCounted::into_shared(rq) + .start(queue_data.completion_time) + .dismiss(); + } } Ok(()) } --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 863732DC357; Sun, 15 Feb 2026 23:41:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198909; cv=none; b=Z2nQpy1DhDjDO7e37wDEkYg1eSioOKk1k3Gm0p1Wl/XQUkJ6Vt4Uv3jqz1K0aCCBSh0gTtjWfOqF8eSZolhYXuIQ8MM+pGbiZU365NWPcAJWWoX7HpZxdg84q+JFfYD3oLFCZzCVU16U/Ddc7us8XOvSTJuU2pj9JC08yXU0Rfg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198909; c=relaxed/simple; bh=3jSsPR6DqlVROWsPq3O4VPU+p5oOS9gtX1vDe2HGQEc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=p+kLySMlRZo368kZd3uMGoSI4T+XSr+e5atl/u0x/IFDfa8/wkwz9LhCSOi6LeRBc3g37F9gG39fcdndAfezwPFkCS3LGc32rgILTglZA6xVXMbn8Fy4nhlbkcYblOba+q3a9RQZQIEt6GXwX345hiUvZSTz2APx3DmSl8delII= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Hl7Tmqz+; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Hl7Tmqz+" Received: by smtp.kernel.org (Postfix) with ESMTPSA id F0629C4CEF7; Sun, 15 Feb 2026 23:41:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198909; bh=3jSsPR6DqlVROWsPq3O4VPU+p5oOS9gtX1vDe2HGQEc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Hl7Tmqz+0Fvhz8hwlErHaMsO7tJISR9XlVmpkYiQppuZSTFCBKNWgESvj/0Nf0Ply CRZdeNk75dFnprSpyOCR0CJxMVm+Z/sPRMGSBbgif5E8Dq9arpyq3Two/5LT+L0Swu whSfdnVLcvEF6ga7IFeckb+WJvFVupB+9e2Pcc0jkV6/D8hSNOrb20qS2yHLlvWJxF aSqd81szbrVV/k2nPBn4HxLYnYAThqpC+jH6QqCxXmDdxM7oQvwMdVzY/71/1jffx2 dKKBEleTSMLpYoSbLyMZWNSjmefTxzY8J/QUNiATTUEBZzdfURd9AXBjSTr2n0H5ea dcrYIhaSN4F5g== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:34:56 +0100 Subject: [PATCH 09/79] block: rust: introduce `kernel::block::bio` module 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: <20260216-rnull-v6-19-rc5-send-v1-9-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=26399; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=3jSsPR6DqlVROWsPq3O4VPU+p5oOS9gtX1vDe2HGQEc=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklgkucasGWHpajSPXxrED8rBqltt0EcLixAEg suwse4kUAKJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYJAAKCRDhuBo+eShj d8FlEACNP+9mEcTvQ/cYK77k1Ij4GH+4LLY18itblgp5Ir9Sw66ScumukAGrcConRWjd82NG19m tPJiHtwAVy76sda6JMivRqTFCYDHy6mpIrTI5I/BOwvm9XLEcChfiqEiO27nQwD8+BssfttTTSD EUlQTc7CV0TarApgisHZJUI+ooEMRIutUn68y/gDWvnMxTe/WMCDwOttgVTDeHK8CilmcPjt7G4 JFMxKDtl8fOGUzFRD5dwluUR3k5q2e4dai6/Rdwdn6Eg3ZrQtEbcxOhjgc9qNFuDusft9k+rYVM bY0E9AgYZganv3MWT6v5TNwNhZemrp/BCbiCIJNMogsNOBVHhIBOnta4V1HDiuQR+to4kWKtd9G ZE5f17cEiTADMnhVqVycwu5CpVocRG+sBU0WFHO5gJ6G3s2NotKyPF855LxwFdMWi0gD+P/fuT4 YaniVge7xWthuzKHwqqP8TtcOhbJDBDen+tbhYH3ubuc+a4DJPfRMb0IJZ9ie4c4KiHLpkAuW/q kzLxQQDUVnWavDxOJUtoNPArv4ePFSECZTD2W3XQUfnpDTzLW7V6kX5+5NyTG1lpiqvwX26xksH gC1zre4jGOGUpW97Hkf+zlK+MnLVt42VAO/1916lEkznRhqtZSyhI3u9DrZk1/LmGfSAspXvHCm Mlx8Gib9/DW7egg== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add Rust abstractions for working with `struct bio`, the core IO command descriptor for the block layer. The `Bio` type wraps `struct bio` and provides safe access to the IO vector describing the data buffers associated with the IO command. The data buffers are represented as a vector of `Segment`s, where each segment is a contiguous region of physical memory backed by `Page`. The `BioSegmentIterator` provides iteration over segments in a single bio, while `BioIterator` allows traversing a chain of bios. The `Segment` type offers methods for copying data to and from pages, as well as zeroing page contents, which are the fundamental operations needed by block device drivers to process IO requests. The `Request` type is extended with methods to access the bio chain associated with a request, allowing drivers to iterate over all data buffers that need to be processed. Signed-off-by: Andreas Hindborg --- rust/helpers/blk.c | 8 + rust/kernel/block.rs | 1 + rust/kernel/block/bio.rs | 143 +++++++++++++++ rust/kernel/block/bio/vec.rs | 389 ++++++++++++++++++++++++++++++++++++= ++++ rust/kernel/block/mq/request.rs | 46 +++++ rust/kernel/lib.rs | 2 + rust/kernel/page.rs | 2 +- 7 files changed, 590 insertions(+), 1 deletion(-) diff --git a/rust/helpers/blk.c b/rust/helpers/blk.c index cc9f4e6a2d234..53beba8c7782d 100644 --- a/rust/helpers/blk.c +++ b/rust/helpers/blk.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 =20 +#include #include #include =20 @@ -12,3 +13,10 @@ struct request *rust_helper_blk_mq_rq_from_pdu(void *pdu) { return blk_mq_rq_from_pdu(pdu); } + +void rust_helper_bio_advance_iter_single(const struct bio *bio, + struct bvec_iter *iter, + unsigned int bytes) +{ + bio_advance_iter_single(bio, iter, bytes); +} diff --git a/rust/kernel/block.rs b/rust/kernel/block.rs index 32c8d865afb62..17de727bc1047 100644 --- a/rust/kernel/block.rs +++ b/rust/kernel/block.rs @@ -2,6 +2,7 @@ =20 //! Types for working with the block layer. =20 +pub mod bio; pub mod mq; =20 /// Bit mask for masking out [`SECTOR_SIZE`]. diff --git a/rust/kernel/block/bio.rs b/rust/kernel/block/bio.rs new file mode 100644 index 0000000000000..94062ea5281e6 --- /dev/null +++ b/rust/kernel/block/bio.rs @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Types for working with the bio layer. +//! +//! C header: [`include/linux/blk_types.h`](../../include/linux/blk_types.= h) + +use core::fmt; +use core::marker::PhantomData; +use core::ptr::NonNull; + +mod vec; + +pub use vec::BioSegmentIterator; +pub use vec::Segment; + +use crate::types::Opaque; + +/// A block device IO descriptor (`struct bio`). +/// +/// A `Bio` is the main unit of IO for the block layer. It describes an IO= command and associated +/// data buffers. +/// +/// The data buffers associated with a `Bio` are represented by a vector o= f [`Segment`]s. These +/// segments represent physically contiguous regions of memory. The memory= is represented by +/// [`Page`] descriptors internally. +/// +/// The vector of [`Segment`]s can be iterated by obtaining a [`SegmentIte= rator`]. +/// +/// # Invariants +/// +/// Instances of this type is always reference counted. A call to +/// `bindings::bio_get()` ensures that the instance is valid for read at l= east +/// until a matching call to `bindings :bio_put()`. +#[repr(transparent)] +pub struct Bio(Opaque); + +impl Bio { + /// Returns an iterator over segments in this `Bio`. Does not consider + /// segments of other bios in this bio chain. + #[inline(always)] + pub fn segment_iter(&mut self) -> BioSegmentIterator<'_> { + BioSegmentIterator::new(self) + } + + /// Get the number of io vectors in this bio. + fn io_vec_count(&self) -> u16 { + // SAFETY: By the type invariant of `Bio` and existence of `&self`, + // `self.0` is valid for read. + unsafe { (*self.0.get()).bi_vcnt } + } + + /// Get slice referencing the `bio_vec` array of this bio + #[inline(always)] + fn io_vec(&self) -> NonNull { + let this =3D self.0.get(); + + // SAFETY: By the type invariant of `Bio` and existence of `&self`, + // `this` is valid for read. + let vec_ptr =3D unsafe { (*this).bi_io_vec }; + + // SAFETY: By C API contract, bi_io_vec is always set, even if bi_= vcnt + // is zero. + unsafe { NonNull::new_unchecked(vec_ptr) } + } + + /// Return a copy of the `bvec_iter` for this `Bio`. This iterator alw= ays + /// indexes to a valid `bio_vec` entry. + #[inline(always)] + fn raw_iter(&self) -> bindings::bvec_iter { + // SAFETY: By the type invariant of `Bio` and existence of `&self`, + // `self` is valid for read. + unsafe { (*self.0.get()).bi_iter } + } + + /// Create an instance of `Bio` from a raw pointer. + /// + /// # Safety + /// + /// Caller must ensure that the `ptr` is valid for use as a reference = to + /// `Bio` for the duration of `'a`. + #[inline(always)] + pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::bio) -> Option<&= 'a Self> { + Some( + // SAFETY: by the safety requirement of this funciton, `ptr` is + // valid for read for the duration of the returned lifetime + unsafe { &*NonNull::new(ptr)?.as_ptr().cast::() }, + ) + } + + /// Create an instance of `Bio` from a raw pointer. + /// + /// # Safety + /// + /// Caller must ensure that the `ptr` is valid for use as a unique ref= erence + /// to `Bio` for the duration of `'a`. + #[inline(always)] + pub(crate) unsafe fn from_raw_mut<'a>(ptr: *mut bindings::bio) -> Opti= on<&'a mut Self> { + Some( + // SAFETY: by the safety requirement of this funciton, `ptr` is + // valid for read for the duration of the returned lifetime + unsafe { &mut *NonNull::new(ptr)?.as_ptr().cast::() }, + ) + } +} + +impl core::fmt::Display for Bio { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "Bio({:?}, vcnt: {}, idx: {}, size: 0x{:x}, completed: 0x{:x})= ", + self.0.get(), + self.io_vec_count(), + self.raw_iter().bi_idx, + self.raw_iter().bi_size, + self.raw_iter().bi_bvec_done + ) + } +} + +/// An iterator over `Bio` in a bio chain, yielding `&mut Bio`. +/// +/// # Invariants +/// +/// `bio` must be either `None` or be valid for use as a `&mut Bio`. +pub struct BioIterator<'a> { + pub(crate) bio: Option>, + pub(crate) _p: PhantomData<&'a ()>, +} + +impl<'a> core::iter::Iterator for BioIterator<'a> { + type Item =3D &'a mut Bio; + + #[inline(always)] + fn next(&mut self) -> Option<&'a mut Bio> { + let mut current =3D self.bio.take()?; + // SAFETY: By the type invariant of `Bio` and type invariant on `S= elf`, + // `current` is valid for use as a unique reference. + let next =3D unsafe { (*current.as_ref().0.get()).bi_next }; + self.bio =3D NonNull::new(next.cast()); + // SAFETY: By type invariant, `bio` is valid for use as a referenc= e. + Some(unsafe { current.as_mut() }) + } +} diff --git a/rust/kernel/block/bio/vec.rs b/rust/kernel/block/bio/vec.rs new file mode 100644 index 0000000000000..20cea478050b9 --- /dev/null +++ b/rust/kernel/block/bio/vec.rs @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Types for working with `struct bio_vec` IO vectors +//! +//! C header: [`include/linux/bvec.h`](../../include/linux/bvec.h) + +use super::Bio; +use crate::error::{code, Result}; +use crate::page::{Page, SafePage, PAGE_SIZE}; +use crate::prelude::*; +use core::fmt; +use core::mem::ManuallyDrop; + +/// A segment of an IO request. +/// +/// [`Segment`] represents a contiguous range of physical memory addresses= of an IO request. A +/// segment has a offset and a length, representing the amount of data tha= t needs to be processed. +/// Processing the data increases the offset and reduces the length. +/// +/// The data buffer of a [`Segment`] is borrowed from a `Bio`. +/// +/// # Implementation details +/// +/// In the context of user driven block IO, the pages backing a [`Segment`= ] are often mapped to user +/// space concurrently with the IO operation. Further, the page backing a = `Segment` may be part of +/// multiple IO operations, if user space decides to issue multiple concur= rent IO operations +/// involving the same page. Thus, the data represented by a [`Segment`] m= ust always be assumed to +/// be subject to racy writes. +/// +/// A [`Segemnt`] is a wrapper around a `strutct bio_vec`. +/// +/// # Invariants +/// +/// `bio_vec` must always be initialized and valid for read and write +pub struct Segment<'a> { + bio_vec: bindings::bio_vec, + _marker: core::marker::PhantomData<&'a ()>, +} + +impl Segment<'_> { + /// Get he length of the segment in bytes. + #[inline(always)] + pub fn len(&self) -> u32 { + self.bio_vec.bv_len + } + + /// Returns true if the length of the segment is 0. + #[inline(always)] + pub fn is_empty(&self) -> bool { + self.len() =3D=3D 0 + } + + /// Get the offset field of the `bio_vec`. + #[inline(always)] + pub fn offset(&self) -> usize { + self.bio_vec.bv_offset as usize + } + + /// Advance the offset of the segment. + /// + /// If `count` is greater than the remaining size of the segment, an e= rror + /// is returned. + pub fn advance(&mut self, count: u32) -> Result { + if self.len() < count { + return Err(code::EINVAL); + } + + self.bio_vec.bv_offset +=3D count; + self.bio_vec.bv_len -=3D count; + Ok(()) + } + + /// Copy data of this segment into `dst_page`. + /// + /// Copies data from the current offset to the next page boundary. Tha= t is `PAGE_SIZE - + /// (self.offeset() % PAGE_SIZE)` bytes of data. Data is placed at off= set `self.offset()` in the + /// target page. This call will advance offset and reduce length of `s= elf`. + /// + /// Returns the number of bytes copied. + #[inline(always)] + pub fn copy_to_page(&mut self, dst_page: Pin<&mut SafePage>, dst_offse= t: usize) -> usize { + // SAFETY: We are not moving out of `dst_page`. + let dst_page =3D unsafe { Pin::into_inner_unchecked(dst_page) }; + let src_offset =3D self.offset() % PAGE_SIZE; + debug_assert!(dst_offset <=3D PAGE_SIZE); + let length =3D (PAGE_SIZE - src_offset) + .min(self.len() as usize) + .min(PAGE_SIZE - dst_offset); + let page_idx =3D self.offset() / PAGE_SIZE; + + // SAFETY: self.bio_vec is valid and thus bv_page must be a valid + // pointer to a `struct page` array. + let src_page =3D unsafe { Page::from_raw(self.bio_vec.bv_page.add(= page_idx)) }; + + src_page + .with_pointer_into_page(src_offset, length, |src| { + // SAFETY: + // - If `with_pointer_into_page` calls this closure, it ha= s performed bounds + // checking and guarantees that `src` is valid for `leng= th` bytes. + // - Any other operations to `src` are atomic or user spac= e operations. + // - We have exclusive ownership of `dst_page` and thus th= is write will not race. + unsafe { dst_page.write_bytewise_atomic(src, dst_offset, l= ength) } + }) + .expect("Assertion failure, bounds check failed."); + + self.advance(length as u32) + .expect("Assertion failure, bounds check failed."); + + length + } + + /// Copy data to the current page of this segment from `src_page`. + /// + /// Copies `PAGE_SIZE - (self.offset() % PAGE_SIZE` bytes of data fro= m `src_page` to this + /// segment starting at `self.offset()` from offset `self.offset() % P= AGE_SIZE`. This call + /// will advance offset and reduce length of `self`. + /// + /// Returns the number of bytes copied. + pub fn copy_from_page(&mut self, src_page: &SafePage, src_offset: usiz= e) -> usize { + let dst_offset =3D self.offset() % PAGE_SIZE; + debug_assert!(src_offset <=3D PAGE_SIZE); + let length =3D (PAGE_SIZE - dst_offset) + .min(self.len() as usize) + .min(PAGE_SIZE - src_offset); + let page_idx =3D self.offset() / PAGE_SIZE; + + // SAFETY: self.bio_vec is valid and thus bv_page must be a valid + // pointer to a `struct page`. + let dst_page =3D unsafe { Page::from_raw(self.bio_vec.bv_page.add(= page_idx)) }; + + dst_page + .with_pointer_into_page(dst_offset, length, |dst| { + // SAFETY: + // - If `with_pointer_into_page` calls this closure, then = it has performed bounds + // checks and guarantees that `dst` is valid for `length= ` bytes. + // - Any other operations to `dst` are atomic or user spac= e operations. + // - Since we have a shared reference to `src_page`, the r= ead cannot race with any + // writes to `src_page`. + unsafe { src_page.read_bytewise_atomic(dst, src_offset, le= ngth) } + }) + .expect("Assertion failure, bounds check failed."); + + self.advance(length as u32) + .expect("Assertion failure, bounds check failed."); + + length + } + + /// Copy zeroes to the current page of this segment. + /// + /// Copies `PAGE_SIZE - (self.offset() % PAGE_SIZE` bytes of data to = this + /// segment starting at `self.offset()`. This call will advance offset= and reduce length of + /// `self`. + /// + /// Returns the number of bytes written to this segment. + pub fn zero_page(&mut self) -> usize { + let offset =3D self.offset() % PAGE_SIZE; + let length =3D (PAGE_SIZE - offset).min(self.len() as usize); + let page_idx =3D self.offset() / PAGE_SIZE; + + // SAFETY: self.bio_vec is valid and thus bv_page must be a valid + // pointer to a `struct page`. We do not own the page, but we prev= ent + // drop by wrapping the `Page` in `ManuallyDrop`. + let dst_page =3D + ManuallyDrop::new(unsafe { Page::from_raw(self.bio_vec.bv_page= .add(page_idx)) }); + + // SAFETY: TODO: This migt race with user space writes. + unsafe { dst_page.fill_zero_raw(offset, length) } + .expect("Assertion failure, bounds check failed."); + + self.advance(length as u32) + .expect("Assertion failure, bounds check failed."); + + length + } +} + +impl core::fmt::Display for Segment<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "Segment {:?} len: {}, offset: {}", + self.bio_vec.bv_page, self.bio_vec.bv_len, self.bio_vec.bv_off= set + ) + } +} + +/// An iterator over `Segment` +/// +/// # Invariants +/// +/// If `iter.bi_size` > 0, `iter` must always index a valid `bio_vec` in `= bio.io_vec()`. +pub struct BioSegmentIterator<'a> { + bio: &'a mut Bio, + iter: bindings::bvec_iter, +} + +impl<'a> BioSegmentIterator<'a> { + /// Creeate a new segemnt iterator for iterating the segments of `bio`= . The + /// iterator starts at the beginning of `bio`. + #[inline(always)] + pub(crate) fn new(bio: &'a mut Bio) -> BioSegmentIterator<'a> { + let iter =3D bio.raw_iter(); + + // INVARIANT: `bio.raw_iter()` returns an index that indexes into = a valid + // `bio_vec` in `bio.io_vec()`. + Self { bio, iter } + } + + // The accessors in this implementation block are modelled after C side + // macros and static functions `bvec_iter_*` and `mp_bvec_iter_*` from + // bvec.h. + + /// Construct a `bio_vec` from the current iterator state. + /// + /// This will return a `bio_vec`of size <=3D PAGE_SIZE + /// + /// # Safety + /// + /// Caller must ensure that `self.iter.bi_size` > 0 before calling this + /// method. + unsafe fn io_vec(&self) -> bindings::bio_vec { + debug_assert!(self.iter.bi_size > 0); + // SAFETY: By safety requirement of this function `self.iter.bi_si= ze` is + // greater than 0. + unsafe { + bindings::bio_vec { + bv_page: self.page(), + bv_len: self.len(), + bv_offset: self.offset(), + } + } + } + + /// Get the currently indexed `bio_vec` entry. + /// + /// # Safety + /// + /// Caller must ensure that `self.iter.bi_size` > 0 before calling this + /// method. + #[inline(always)] + unsafe fn bvec(&self) -> &bindings::bio_vec { + debug_assert!(self.iter.bi_size > 0); + // SAFETY: By the safety requirement of this function and the type + // invariant of `Self`, `self.iter.bi_idx` indexes into a valid + // `bio_vec` + unsafe { self.bio.io_vec().offset(self.iter.bi_idx as isize).as_re= f() } + } + + /// Get the as u32currently indexed page, indexing into pages of orde= r >=3D 0. + /// + /// # Safety + /// + /// Caller must ensure that `self.iter.bi_size` > 0 before calling this + /// method. + #[inline(always)] + unsafe fn page(&self) -> *mut bindings::page { + debug_assert!(self.iter.bi_size > 0); + // SAFETY: By C API contract, the following offset cannot exceed p= ages + // allocated to this bio. + unsafe { self.mp_page().add(self.mp_page_idx()) } + } + + /// Get the remaining bytes in the current page. Never more than PAGE_= SIZE. + /// + /// # Safety + /// + /// Caller must ensure that `self.iter.bi_size` > 0 before calling this + /// method. + #[inline(always)] + unsafe fn len(&self) -> u32 { + debug_assert!(self.iter.bi_size > 0); + // SAFETY: By safety requirement of this function `self.iter.bi_si= ze` is + // greater than 0. + unsafe { + self.mp_len() + .min((bindings::PAGE_SIZE as u32) - self.offset()) + } + } + + /// Get the offset from the last page boundary in the currently indexed + /// `bio_vec` entry. Never more than PAGE_SIZE. + /// + /// # Safety + /// + /// Caller must ensure that `self.iter.bi_size` > 0 before calling this + /// method. + #[inline(always)] + unsafe fn offset(&self) -> u32 { + debug_assert!(self.iter.bi_size > 0); + // SAFETY: By safety requirement of this function `self.iter.bi_si= ze` is + // greater than 0. + unsafe { self.mp_offset() % (bindings::PAGE_SIZE as u32) } + } + + /// Return the first page of the currently indexed `bio_vec` entry. Th= is + /// might be a multi-page entry, meaning that page might have order > = 0. + /// + /// # Safety + /// + /// Caller must ensure that `self.iter.bi_size` > 0 before calling this + /// method. + #[inline(always)] + unsafe fn mp_page(&self) -> *mut bindings::page { + debug_assert!(self.iter.bi_size > 0); + // SAFETY: By safety requirement of this function `self.iter.bi_si= ze` is + // greater than 0. + unsafe { self.bvec().bv_page } + } + + /// Get the offset in whole pages into the currently indexed `bio_vec`= . This + /// can be more than 0 is the page has order > 0. + /// + /// # Safety + /// + /// Caller must ensure that `self.iter.bi_size` > 0 before calling this + /// method. + #[inline(always)] + unsafe fn mp_page_idx(&self) -> usize { + debug_assert!(self.iter.bi_size > 0); + // SAFETY: By safety requirement of this function `self.iter.bi_si= ze` is + // greater than 0. + (unsafe { self.mp_offset() } / (bindings::PAGE_SIZE as u32)) as us= ize + } + + /// Get the offset in the currently indexed `bio_vec` multi-page entry= . This + /// can be more than `PAGE_SIZE` if the page has order > 0. + /// + /// # Safety + /// + /// Caller must ensure that `self.iter.bi_size` > 0 before calling this + /// method. + #[inline(always)] + unsafe fn mp_offset(&self) -> u32 { + debug_assert!(self.iter.bi_size > 0); + // SAFETY: By safety requirement of this function `self.iter.bi_si= ze` is + // greater than 0. + unsafe { self.bvec().bv_offset + self.iter.bi_bvec_done } + } + + /// Get the number of remaining bytes for the currently indexed `bio_v= ec` + /// entry. Can be more than PAGE_SIZE for `bio_vec` entries with pages= of + /// order > 0. + /// + /// # Safety + /// + /// Caller must ensure that `self.iter.bi_size` > 0 before calling this + /// method. + #[inline(always)] + unsafe fn mp_len(&self) -> u32 { + debug_assert!(self.iter.bi_size > 0); + // SAFETY: By safety requirement of this function `self.iter.bi_si= ze` is + // greater than 0. + self.iter + .bi_size + .min(unsafe { self.bvec().bv_len } - self.iter.bi_bvec_done) + } +} + +impl<'a> core::iter::Iterator for BioSegmentIterator<'a> { + type Item =3D Segment<'a>; + + #[inline(always)] + fn next(&mut self) -> Option { + if self.iter.bi_size =3D=3D 0 { + return None; + } + + // SAFETY: We checked that `self.iter.bi_size` > 0 above. + let bio_vec_ret =3D unsafe { self.io_vec() }; + + // SAFETY: By existence of reference `&bio`, `bio.0` contains a va= lid + // `struct bio`. By type invariant of `BioSegmentItarator` `self.i= ter` + // indexes into a valid `bio_vec` entry. By C API contracit, `bv_l= en` + // does not exceed the size of the bio. + unsafe { + bindings::bio_advance_iter_single( + self.bio.0.get(), + core::ptr::from_mut(&mut self.iter), + bio_vec_ret.bv_len, + ) + }; + + Some(Segment { + bio_vec: bio_vec_ret, + _marker: core::marker::PhantomData, + }) + } +} diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index f270060be27eb..b49197a0c66d7 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -19,6 +19,9 @@ }; use core::{ffi::c_void, marker::PhantomData, ptr::NonNull}; =20 +use crate::block::bio::Bio; +use crate::block::bio::BioIterator; + /// A wrapper around a blk-mq [`struct request`]. This represents an IO re= quest. /// /// # Implementation details @@ -92,6 +95,49 @@ pub fn complete(this: ARef) { } } =20 + /// Get a reference to the first [`Bio`] in this request. + #[inline(always)] + pub fn bio(&self) -> Option<&Bio> { + // SAFETY: By type invariant of `Self`, `self.0` is valid and the = deref + // is safe. + let ptr =3D unsafe { (*self.0.get()).bio }; + // SAFETY: By C API contract, if `bio` is not null it will have a + // positive refcount at least for the duration of the lifetime of + // `&self`. + unsafe { Bio::from_raw(ptr) } + } + + /// Get a mutable reference to the first [`Bio`] in this request. + #[inline(always)] + pub fn bio_mut(&mut self) -> Option<&mut Bio> { + // SAFETY: By type invariant of `Self`, `self.0` is valid and the = deref + // is safe. + let ptr =3D unsafe { (*self.0.get()).bio }; + // SAFETY: By C API contract, if `bio` is not null it will have a + // positive refcount at least for the duration of the lifetime of + // `&self`. + unsafe { Bio::from_raw_mut(ptr) } + } + + /// Get an iterator over all bio structurs in this request. + #[inline(always)] + pub fn bio_iter_mut<'a>(self: &'a mut Owned) -> BioIterator<'a> { + // INVARIANT: By C API contract, if the bio pointer is not null, i= t is a valid `struct bio`. + // `NonNull::new` will return `None` if the pointer is null. + BioIterator { + // SAFETY: By type invariant `self.0` is a valid `struct reque= st`. + bio: NonNull::new(unsafe { (*self.0.get()).bio.cast() }), + _p: PhantomData, + } + } + + /// Get the target sector for the request. + #[inline(always)] + pub fn sector(&self) -> usize { + // SAFETY: By type invariant of `Self`, `self.0` is valid and live. + unsafe { (*self.0.get()).__sector as usize } + } + /// Return a pointer to the [`RequestDataWrapper`] stored in the priva= te area /// of the request structure. /// diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 72642e6f1c295..73226348c729f 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -23,6 +23,8 @@ // // Stable since Rust 1.80.0. #![feature(slice_flatten)] +// Stable in Rust 1.80 +#![feature(non_null_convenience)] // // Stable since Rust 1.81.0. #![feature(lint_reasons)] diff --git a/rust/kernel/page.rs b/rust/kernel/page.rs index 22a920c15f45f..760aa286dc9ce 100644 --- a/rust/kernel/page.rs +++ b/rust/kernel/page.rs @@ -266,7 +266,7 @@ fn with_page_mapped(&self, f: impl FnOnce(*mut u8) -= > T) -> T { /// different addresses. However, even if the addresses are different,= the underlying memory is /// still the same for these purposes (e.g., it's still a data race if= they both write to the /// same underlying byte at the same time). - fn with_pointer_into_page( + pub(crate) fn with_pointer_into_page( &self, off: usize, len: usize, --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 B96A61BD035; Sun, 15 Feb 2026 23:41:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198884; cv=none; b=WMzWVKhgw6SfVQsdfjirN+GTFvZ6egrjxOIqSUBvyOZ+eeTq/20G4wqzrYu2X1IqN5Nov1SEZbP7G8wEW5NF/fhhqiP3rqwiW8eSJs8LDR3eXqwELisaaZEBIPWjYrOCThzwGdBhYYIH8neuXEFW6ux8MdKek/7nB8GU27qRk9Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198884; c=relaxed/simple; bh=t1ASQJfBzdDC92qEwscNrrmiCuGCWMUOPUsdRJYBsD8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=BFFIx66uFERLj0gMwYyZ282HxH5903p+XwiXfxdAi5CdXNAIv2OKemU1D/OkU0U3o2ndbnyNJRGtErKuedsMCzCMWypGzE6fDB+xCcv0galmBHA4KhF2LNNaiEa3ff2oImtu6/X6x0sP8EGBv/9HkVwht2rjioEhJ4GxU7adg8M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=O5f/Fu5M; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="O5f/Fu5M" Received: by smtp.kernel.org (Postfix) with ESMTPSA id DEAC5C4AF09; Sun, 15 Feb 2026 23:41:19 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198884; bh=t1ASQJfBzdDC92qEwscNrrmiCuGCWMUOPUsdRJYBsD8=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=O5f/Fu5Mr1MpI9+DceCrppsOt8psR2zmL7tDTzmcftdQ82droIygIw3qEVSdA89T6 8QowxFsoEi6dOmlL43k5mzgEmmd8Td6sTcsrQSVYH2C3uq0SaFssB7lba16WPzLF9R pU07XUhSftlvfzcWiMqHD34xGm8E1vly+l9DpiO9bNtB0QaYbVYu/ErUJoFJWebREv yWsDQs5J5qHu/Q1WWAvW/xa6tHisRCxr4G0slgal/K5pN5nj/al/DmcUeWPAo2zizH dpiKXSVu7eN1l1MulH7Im2A30QmXaTcpa/lJZ0qJ70Kmj7IiVvy2QByMbp1eGyqkKt 6FyckWmhmb0Sg== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:34:57 +0100 Subject: [PATCH 10/79] block: rust: add `command` getter to `Request` 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: <20260216-rnull-v6-19-rc5-send-v1-10-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1169; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=t1ASQJfBzdDC92qEwscNrrmiCuGCWMUOPUsdRJYBsD8=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklgkNOXP5tPUBMAf9BmZYuGDAbCtojzNtAios +8laK9Y39mJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYJAAKCRDhuBo+eShj d194D/4mIQB1EGLbNrOl+I/uS4wCtD6CzbRXmXcl5fFe92Xg2YsO6rekD3VJzEIABP7VCe7qKRP wyC9KwDV8OaosaLR2awz+ORVTzJXWo82qjUiMIyAjXz8esf4P9iapP4hVXk9vY34LNXQGC87V0H mDn2MaQcbXjd5riAooVucs9b2nJ9AFLI9Du4Mj1N23oKn8ghSYPkVz4/dibfpINwnuYqZimSlB/ 018p/1lX+xO/t1GESKTGSWUvBN+nImW651QYrVCOMXiKIQ7/I9SaMI08YLF3c92ex0FPsvRqUt7 aHsYLVPCTduOZXdOG+MEmi45DjRUH+0NuHjRY8CtxLRWVc5AxGPVaJD1QZMfkegYDG4EgIqN1lH gd6HhCH64uOuuJnhY6tJpBZlVOiXP/wcUMu+Srf5DEqxl51YH5m33GeBoORPGQakjhCTge8iYlx WF1R5hKUTYs1gQ3NpNRFMrsBQBj0e+eu805gzj+ZUBPQUe+SZ/oxrA15hLHeOm6yngwleIe0y4F nl9AzTzV8Cw3cE6Ejx0m9WNZbxjxiCcKa5MaPzJchaymBQ4yvZS4fq8ylJ+YsRJPJcXoqorkiZv xpFAzJPcHId1f0zPP6AwbMCWXuX3APyh8UTjsuCWMsHNGWxIH9Zbqy4NvlWnTZXsFoAY4toT5JJ GfwScsfr1lGfJ8g== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a method to extract the command operation code from a request. The command is obtained by masking the lower bits of `cmd_flags` as defined by `REQ_OP_BITS`. This allows Rust block drivers to determine the type of operation being requested. Signed-off-by: Andreas Hindborg Reviewed-by: Alice Ryhl --- rust/kernel/block/mq/request.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index b49197a0c66d7..0dd329ae93dfc 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -78,6 +78,12 @@ pub(crate) unsafe fn aref_from_raw(ptr: *mut bindings::r= equest) -> ARef { unsafe { ARef::from_raw(NonNull::new_unchecked(ptr.cast())) } } =20 + /// Get the command identifier for the request + pub fn command(&self) -> u32 { + // SAFETY: By C API contract and type invariant, `cmd_flags` is va= lid for read + unsafe { (*self.0.get()).cmd_flags & ((1 << bindings::REQ_OP_BITS)= - 1) } + } + /// Complete the request by scheduling `Operations::complete` for /// execution. /// --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 D70B32D7397; Sun, 15 Feb 2026 23:41:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198894; cv=none; b=YD2BIHE6RjfpsRJ1v6IxdrYbnEDCJXkvHXpnaEkvJYUShTpevwZLWLRuN0NER3lzuwOj4oqv/kWGikYc8lSv0k0KqAWWDjxVnYcCYtOgbAJvN1pKDqmCGtE5U9vTftXPSyixY0F0+BIoIm62ldcI2boZFAE7q6BSCaPiVEoXwgw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198894; c=relaxed/simple; bh=acRR0LGwJ+y2FUW7GXjH8dY8YE5jwTLkAI8WFKvdvS0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Ef5bhLxH+iKxEZ0s+if+FFz4uQg8UlWYGaKBSVpyCNY5pJ2tSWu4TRwJS2tLynRLIwDdfjPBswC8QPnTGAGzaAK3Z4mnAQtc8E+U4ZqbGrOKlaNySS65NJswO/Ml1YDI6UFYSq5pDs0XxBIgEyaJ1h4EVx7uJIkl/JbEwbIlojw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=L77sVJGJ; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="L77sVJGJ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 036A0C4CEF7; Sun, 15 Feb 2026 23:41:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198894; bh=acRR0LGwJ+y2FUW7GXjH8dY8YE5jwTLkAI8WFKvdvS0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=L77sVJGJj5AWUp/VBo02FiIgfdQ5aGIMLXeURfrgkHpVzHAQnM6emwYJGi8nBy5YN t/GKtv+Gjp4xid2JjZNhlyatNgOb7mH2RS/eft1GgYfaCaNmM5Pu0G5H72jxN9OzDG w3F+cBlWWqSGMa2nmyzVu3HC3NcLLbgrNITUhjXaDMbfjbs0/zfLPxlVedLGGXOYux yuOqqPmQnqKFkIMfuLTpSHndjtU+rmQ+oJt6Vb9TQ8EyO0+6j3IyO1ajTSVacikhD9 jqrMgSgnKrf/+Wnt77SiDoOTx6nCqE0qyFMpSpuSRDVPfRMoJqNX2Z706Z7jnpWmVD l5SOvkxYC5D5w== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:34:58 +0100 Subject: [PATCH 11/79] block: rust: mq: use GFP_KERNEL from prelude 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: <20260216-rnull-v6-19-rc5-send-v1-11-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1071; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=acRR0LGwJ+y2FUW7GXjH8dY8YE5jwTLkAI8WFKvdvS0=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklglP/KMsdGWPNdgIxkYpZcNKm6lzoIdY5t40 tyXldVLppiJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYJQAKCRDhuBo+eShj d9xOD/9FOygg/84nz9+W8W2ER0YJsvYemP2aXeb/lR7sW6F05uvwWohT2XxSYaSLDGF9vFZeAD1 hFVl9PzB7B+uzt+uZNFcjaB+bdmOI0mHU3orIn3M8rnrF3u0v8Kfde2uUiLXVglcvGw7QpyJOEu QfHPsmTtsQhxtmMf3EdNBhYioH7d3kAUg521/kYeZlr+SF/rlyXgLWGyZ2kZrG6UI+iBE9U3bv2 7pV//iJSSizhA75X6E4l4eCTHgvfSKo/4gDmSM97YQq9wTHfDUIYXzH0psvShnnWyNSoUWthLn5 Ka4nBngHT9e0jBZo2bu/WUizXidDTYl3kze8Ur1GDXIT5wOIlAypakvPe4Uv9cEba1PftVtAoqY xvxWf37mkn5PQPazg0xs+Mlyu+9GmFGokhtxA304Br4V07pwcix7JGNpWNOQGCnzb3HbB4vDjjR 5GKbbztCE7IaQ1RI6xKpTXL/+nPBgDCOo7qBXhBPKIDFiy4T2CAFZjFKOGoh3XAXkhRcMm+iQXi Ng5c0mmdcJLRK4e2sBs8p61im4GeUsYwQXXIiOWsRr15rwNpVzyjZHawhzJ/zT54QgBtFQ0jTOh LH4gIX5WigNLyJb+sT6pkoWj5AukpenSB4cKMxYLmkv5Q/AvLYidf8bNUs/kPcFx651A38xcX0n 5SgmfJP+zdDT4WQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Remove the explicit import of kernel::alloc::flags and use GFP_KERNEL directly from the prelude in the module documentation example. This simplifies the import list and follows the pattern of using commonly used constants from the prelude. Signed-off-by: Andreas Hindborg Reviewed-by: Alice Ryhl --- rust/kernel/block/mq.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index cedaf85ba0b1d..884bf18abbba5 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -57,7 +57,6 @@ //! //! ```rust //! use kernel::{ -//! alloc::flags, //! block::mq::*, //! new_mutex, //! prelude::*, @@ -93,7 +92,7 @@ //! } //! //! let tagset: Arc> =3D -//! Arc::pin_init(TagSet::new(1, 256, 1), flags::GFP_KERNEL)?; +//! Arc::pin_init(TagSet::new(1, 256, 1), GFP_KERNEL)?; //! let mut disk =3D gen_disk::GenDiskBuilder::new() //! .capacity_sectors(4096) //! .build(fmt!("myblk"), tagset, ())?; --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 533B530F533; Sun, 15 Feb 2026 23:44:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199096; cv=none; b=tNUaFVzN9Ki7kwV7OQNcc5eTW5UItNwZXXEKQ1h21hqi9/jT47tYISCYn2WsS1fOdHwu6i9KV52q8luuwY2jeJir2dSsIWK1x37/Gw1/muBuM+M0VRzpKFYuxfJFy9NRtAZqhVHWtaP1nb1Gqx5cpD3kq7BEyiRGjXBWSpkmMW8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199096; c=relaxed/simple; bh=Px3eI2zDMAVJLYyUnOivw8WZDG9ekgsY8W3TNggTDzU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=q9YyHaBdZv1F8hDbPmrro7C0rwT+LGH4r6X4zqCoGp7Gsabx82jLXdb6wuQezlkuNXHKLe+QA1OOUfJXGCY+IAvnGI+uSuhzEL6NKktKWetqCV/Q6vZEz1/No85QOjUr+WEnzBZLFvIwogbCj2ZZtcT0dpP39aWPhgbuXKNrzZ4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Lh34cizW; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Lh34cizW" Received: by smtp.kernel.org (Postfix) with ESMTPSA id EF8BAC4CEF7; Sun, 15 Feb 2026 23:44:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199096; bh=Px3eI2zDMAVJLYyUnOivw8WZDG9ekgsY8W3TNggTDzU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Lh34cizW7d/gBLwSVeE4ys9vsfLxOpCx97lcyLN9wxX6puurpKIPB60ex0e5s2pek YjX/ebbARJzLTG04MyEdPzMVV8Tgdw/dwPJGNal7y41AdpZ3PKjDvhcvhYMpTW0jkd DNsXPxviMaBGEAsG/BzwPx7ZU5gUJR3UAZJxbJ9AW4wL4eL80+fsb0zBRIe6sprGiM xnOJvxKjn+0I1dbfo5ntcXOhQvoEJVqfjDFDMFMoa+mzPsu78A9Plf5iKkq/s6Wl5C sPHH3+IahusncPzCzzai6AmDK4+YLxoY0evmg5qON45mmAfw6IklPFobLgFDXdrtou JOvITASMzTdng== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:34:59 +0100 Subject: [PATCH 12/79] block: rust: add `TagSet` flags 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: <20260216-rnull-v6-19-rc5-send-v1-12-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4410; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=Px3eI2zDMAVJLYyUnOivw8WZDG9ekgsY8W3TNggTDzU=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklgmG/fV80AH9+jNFOPrjKBYXkqkQyBHVCZEq oBbsCPTrhOJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYJgAKCRDhuBo+eShj dy2UD/4oSy3vFK2Ftcro2uRPRmoKZLNWf32I0hhmtmP678KPwSICk/IrlxiYYhnymniK9BKQa5d 5KcVEYOR1sET2O5Yl1YQ3LBgLxVdRTQck6w98SZBT5b14hXnnSDh7re/DfJMArLV+BYyzFYSdpu bVk3syb0ZLc9gMOdL+rNBTIYRFI/7O/LRceo3pTAanFs33idABcN2sTUhQE80/hS8KPUX822thb 9rws+ncn3h0w6/xkR0jpPS30yjl9Cx3cjezlE3esWeVTVL6edNP4U9ohZSgS6VgTbrLxUdpse3i TOMvfF/K0ggKh1dZWgXkkGFTGUuTfCZUp4TY2HcLzJmZClMwf/zY6A1KwHRlkT/NaqhtZv2A1t0 fA6HP6ysJx5YDnRpAtBNk0x0W0FpJzAmO2Jcvz9vWqbbQhVITHwUt/ghW3H4h8izrNl3tK17zYu LrEBWs0SgTmbXo1RLsY3+ylr9iT31SuEx1DcNL5ty/FR5C9Nq5kUkCvKkmnFxasoAJDtv7yd6n5 lyL0SHlB0pJ87F6LAfBz15p7x0EJCK6FAs3aVO4/6dcrLKlaFFrySaEcuOP9+GASye/21wQEbFY EKKUhH2/Lxv/5WJI9obMOKll1+CvHy+r2jq+fweRjr+Vd1MEFg3gIEwGhenEY9K1ze9p2pLCVOZ BLLMwQb1LMThPfw== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add support for `TagSet` flags by introducing a `Flags` type and adding a flags parameter to `TagSet::new`. This allows configuring tagset behavior such as blocking vs non-blocking operation. The Flags type supports bitwise operations and provides values like `Blocking` for common use cases. The module documentation example is updated to demonstrate the new API. For now, only a single flag is added. Signed-off-by: Andreas Hindborg Reviewed-by: Alice Ryhl --- drivers/block/rnull/rnull.rs | 5 ++++- rust/kernel/block/mq.rs | 6 +++--- rust/kernel/block/mq/tag_set.rs | 7 ++++++- rust/kernel/block/mq/tag_set/flags.rs | 19 +++++++++++++++++++ 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 55e56c39f1c2c..33c1144550a0e 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -130,7 +130,10 @@ fn new( irq_mode: IRQMode, completion_time: Delta, ) -> Result> { - let tagset =3D Arc::pin_init(TagSet::new(1, 256, 1), GFP_KERNEL)?; + let tagset =3D Arc::pin_init( + TagSet::new(1, 256, 1, mq::tag_set::Flags::default()), + GFP_KERNEL, + )?; =20 let queue_data =3D Box::new( QueueData { diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index 884bf18abbba5..76e790cdb1f8f 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -57,7 +57,7 @@ //! //! ```rust //! use kernel::{ -//! block::mq::*, +//! block::mq::{self, *}, //! new_mutex, //! prelude::*, //! sync::{aref::ARef, Arc, Mutex}, @@ -92,7 +92,7 @@ //! } //! //! let tagset: Arc> =3D -//! Arc::pin_init(TagSet::new(1, 256, 1), GFP_KERNEL)?; +//! Arc::pin_init(TagSet::new(1, 256, 1, mq::tag_set::Flags::default()= ), GFP_KERNEL)?; //! let mut disk =3D gen_disk::GenDiskBuilder::new() //! .capacity_sectors(4096) //! .build(fmt!("myblk"), tagset, ())?; @@ -103,7 +103,7 @@ pub mod gen_disk; mod operations; mod request; -mod tag_set; +pub mod tag_set; =20 pub use operations::Operations; pub use request::Request; diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set= .rs index 46481754b1335..af214f7dd0c10 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -16,6 +16,10 @@ use core::{convert::TryInto, marker::PhantomData}; use pin_init::{pin_data, pinned_drop, PinInit}; =20 +mod flags; +pub use flags::Flag; +pub use flags::Flags; + /// A wrapper for the C `struct blk_mq_tag_set`. /// /// `struct blk_mq_tag_set` contains a `struct list_head` and so must be p= inned. @@ -37,6 +41,7 @@ pub fn new( nr_hw_queues: u32, num_tags: u32, num_maps: u32, + flags: Flags, ) -> impl PinInit { // SAFETY: `blk_mq_tag_set` only contains integers and pointers, w= hich // all are allowed to be 0. @@ -51,7 +56,7 @@ pub fn new( numa_node: bindings::NUMA_NO_NODE, queue_depth: num_tags, cmd_size, - flags: 0, + flags: flags.into_inner(), driver_data: core::ptr::null_mut::= (), nr_maps: num_maps, ..tag_set diff --git a/rust/kernel/block/mq/tag_set/flags.rs b/rust/kernel/block/mq/t= ag_set/flags.rs new file mode 100644 index 0000000000000..768db938b9a95 --- /dev/null +++ b/rust/kernel/block/mq/tag_set/flags.rs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0 + +use kernel::prelude::*; + +impl_flags! { + + /// Flags to be used when creating [`super::TagSet`] objects. + #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)] + pub struct Flags(u32); + + /// Allowed values for [`Flags`]. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum Flag { + /// Indicate that the queues associated with this tag set might sl= eep when + /// processing IO. When this flag is not set, IO is processed in a= tomic + /// context. When this flag is set, IO is processed in process con= text. + Blocking =3D bindings::BLK_MQ_F_BLOCKING, + } +} --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 DBAF72C08C4; Sun, 15 Feb 2026 23:44:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199086; cv=none; b=oGplryiYUjOYotHp1j/z9bSONjhki7dTHRB8zIr9LWGo3otG7+/pA7HlummxJeRR4+U4BknQhs9T1WIcmDOyJvahkZ7zPti7j3GeGKmv+KVxfPcB4NRuCs0iTFHVue0xXhbQOwZi5ZvXXs86WYYX9Fm2RKU5k9V+tgvsy983g3Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199086; c=relaxed/simple; bh=3rrDHSRDSri7M7s71IQjuy8vdWUBLRb6Dxl+38bP14w=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=rs/zFo1itKWnh7hNhkzD948c12243aMpXU8mGvGqjhIFl3HXjvOa3fZ7UwwGTLhvTswr64zRDY34Hf3Z+JFBrGg3StstMBxxmLvQ7dRnd0vTGzdvGV4rVy1VzKIXAnP6QJi/KxmR5lZq9YMA+6Z1gzp09SQkjugps6GFD2L9SyM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Az8alV7B; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Az8alV7B" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 412F1C4CEF7; Sun, 15 Feb 2026 23:44:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199086; bh=3rrDHSRDSri7M7s71IQjuy8vdWUBLRb6Dxl+38bP14w=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Az8alV7BraHRqfL7hqn1+NA0R7pNI58SQGJvj5divghBndBEdJ7+CWNP9YYTwuCvW xnR+vaaQmP1sIsn8x8tjLpNvnDCbh56EmdBr9xaqP1yY5IQoHoFDgDz3r5JQEyRcqZ XSeGPCJCBW3BEsaYvNBylzzVvEufatAxXeDpNIBCjdUounvU4KAEGOWq21FtrYyGqm p/N1/k0Us5fBVlmokfwPcvqXqLxdiA6eqDwKxVlT6SCwd6XmlW+4782dpmicVQ6lp/ lbHcEgBGjOKlEpwOLg4XrnDQzzD+HSCKGcWy6uyuBekezyCnwiy5+VCdAFP7/5IsMZ OxCBFb8nOKloQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:00 +0100 Subject: [PATCH 13/79] block: rnull: add memory backing 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: <20260216-rnull-v6-19-rc5-send-v1-13-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=9784; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=3rrDHSRDSri7M7s71IQjuy8vdWUBLRb6Dxl+38bP14w=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklgnM2ycmnCqrRa591Gzsguln6y/lmOkye/8U u03WEeU+5iJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYJwAKCRDhuBo+eShj dzQDD/4+t9unn5Mhb8F+eGinmC5GX+e4Vgm8WLHicT77yJovXDieWRqEZcKzenYhQQpF44ENEuG 5RUNKWiHM3Ld1h8Xfhrhn6KEMyfdJXRwRMLVtxqF29PL7pFggp3FrR1o4YC9j1Ohl3XUXCCBUMQ KjoN8T4omTMpp7P24ryCxPy9xhOPpZhZIGlQXkhbhCsjefas33oC9eUHu0B8E/XKuW06dOZ4ir+ nZW1q0FvtPphCJ02Bfham9zg2LyAz3SZCnSMUaKXForaWUtnF/JJBrVNnEPxvk7oylk7/5lXDgr wgjrZ9chQDAEujX8ngpxIyNdKJunI6tUkr6PBJIJxbAZsLoqBZ/Sbtxee1g9KGqw4idw/MKU6bs cbu/ZoAjf970wlAdt/zgJNu3VRKt0JWQ90UuiHNwZSUyNiVLieslklWD+cr6EPQJHFAqS06xvK8 S46rxQHj7DXbSZbYaRBaBsTCcGssCHJDwJIFgAOLt5NLs4UzK7RfNCTjTqSGP+ZBowoG0qsLCQo L//dMYspPRPLKIrl1S51xt42j1hdnbd/hGbDWnltSU3K2DCbVyoL0oBbD323LGMJDe3mZQpMID0 ND4pY6RuGedkc/qQkHVe58XTsPKi7+MNoTd2F+u3n83cy9hhPVuJxS6KpYJUHFiHl2Pe/Mdjw2d /aEyUyu1Iq58/AA== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add memory backing to the rust null block driver. This implementation will always allocate a page on write, even though a page backing the written sector is already allocated, in which case the page will be released again. A later patch will fix this inefficiency. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 37 +++++++++++- drivers/block/rnull/rnull.rs | 125 ++++++++++++++++++++++++++++++++++--= ---- 2 files changed, 144 insertions(+), 18 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 7952f41f42bfd..b5dc30c5d3e20 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -59,7 +59,7 @@ impl AttributeOperations<0> for Config { =20 fn show(_this: &Config, page: &mut [u8; PAGE_SIZE]) -> Result { let mut writer =3D kernel::str::Formatter::new(page); - writer.write_str("blocksize,size,rotational,irqmode,completion_nse= c\n")?; + writer.write_str("blocksize,size,rotational,irqmode,completion_nse= c,memory_backed\n")?; Ok(writer.bytes_written()) } } @@ -83,6 +83,7 @@ fn make_group( size: 3, irqmode: 4, completion_nsec: 5, + memory_backed: 6, ], }; =20 @@ -100,6 +101,7 @@ fn make_group( irq_mode: IRQMode::None, completion_time: time::Delta::ZERO, name: name.try_into()?, + memory_backed: false, }), }), core::iter::empty(), @@ -154,6 +156,7 @@ struct DeviceConfigInner { irq_mode: IRQMode, completion_time: time::Delta, disk: Option>, + memory_backed: bool, } =20 #[vtable] @@ -184,6 +187,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { guard.capacity_mib, guard.irq_mode, guard.completion_time, + guard.memory_backed, )?); guard.powered =3D true; } else if guard.powered && !power_op { @@ -209,3 +213,34 @@ fn from_str(s: &str) -> Result { value.try_into() } } + +#[vtable] +impl configfs::AttributeOperations<6> for DeviceConfig { + type Data =3D DeviceConfig; + + fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result { + let mut writer =3D kernel::str::Formatter::new(page); + + if this.data.lock().memory_backed { + writer.write_fmt(fmt!("1\n"))?; + } else { + writer.write_fmt(fmt!("0\n"))?; + } + + Ok(writer.bytes_written()) + } + + fn store(this: &DeviceConfig, page: &[u8]) -> Result { + if this.data.lock().powered { + return Err(EBUSY); + } + + this.data.lock().memory_backed =3D core::str::from_utf8(page)? + .trim() + .parse::() + .map_err(|_| kernel::error::code::EINVAL)? + !=3D 0; + + Ok(()) + } +} diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 33c1144550a0e..a1156a368c467 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -6,8 +6,10 @@ =20 use configfs::IRQMode; use kernel::{ + bindings, block::{ self, + bio::Segment, mq::{ self, gen_disk::{ @@ -19,15 +21,12 @@ }, }, error::Result, - new_mutex, + new_mutex, new_xarray, + page::SafePage, pr_info, prelude::*, str::CString, - sync::{ - aref::ARef, - Arc, - Mutex, // - }, + sync::{aref::ARef, Arc, Mutex}, time::{ hrtimer::{ HrTimerCallback, @@ -40,7 +39,8 @@ types::{ OwnableRefCounted, Owned, // - }, // + }, + xarray::XArray, }; use pin_init::PinInit; =20 @@ -76,6 +76,10 @@ default: 10_000, description: "Time in ns to complete a request in hardware. D= efault: 10,000ns", }, + memory_backed: u8 { + default: 0, + description: "Create a memory-backed block device. 0-false, 1-= true. Default: 0", + }, }, } =20 @@ -105,6 +109,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { *module_parameters::gb.value() * 1024, (*module_parameters::irqmode.value()).try_into()?, Delta::from_nanos(completion_time), + *module_parameters::memory_backed.value() !=3D 0, )?; disks.push(disk, GFP_KERNEL)?; } @@ -129,17 +134,23 @@ fn new( capacity_mib: u64, irq_mode: IRQMode, completion_time: Delta, + memory_backed: bool, ) -> Result> { - let tagset =3D Arc::pin_init( - TagSet::new(1, 256, 1, mq::tag_set::Flags::default()), - GFP_KERNEL, - )?; + let flags =3D if memory_backed { + mq::tag_set::Flag::Blocking.into() + } else { + mq::tag_set::Flags::default() + }; + + let tagset =3D Arc::pin_init(TagSet::new(1, 256, 1, flags), GFP_KE= RNEL)?; =20 - let queue_data =3D Box::new( - QueueData { + let queue_data =3D Box::pin_init( + pin_init!(QueueData { + tree <- new_xarray!(kernel::xarray::AllocKind::Alloc), irq_mode, completion_time, - }, + memory_backed, + }), GFP_KERNEL, )?; =20 @@ -150,11 +161,72 @@ fn new( .rotational(rotational) .build(fmt!("{}", name.to_str()?), tagset, queue_data) } + + #[inline(always)] + fn write(tree: &Tree, mut sector: usize, mut segment: Segment<'_>) -> = Result { + while !segment.is_empty() { + let page =3D SafePage::alloc_page(GFP_NOIO)?; + let mut tree =3D tree.lock(); + + let page_idx =3D sector >> block::PAGE_SECTORS_SHIFT; + + let page =3D if let Some(page) =3D tree.get_mut(page_idx) { + page + } else { + tree.store(page_idx, page, GFP_NOIO)?; + tree.get_mut(page_idx).unwrap() + }; + + let page_offset =3D (sector & block::SECTOR_MASK as usize) << = block::SECTOR_SHIFT; + sector +=3D segment.copy_to_page(page, page_offset) >> block::= SECTOR_SHIFT; + } + Ok(()) + } + + #[inline(always)] + fn read(tree: &Tree, mut sector: usize, mut segment: Segment<'_>) -> R= esult { + let tree =3D tree.lock(); + + while !segment.is_empty() { + let idx =3D sector >> block::PAGE_SECTORS_SHIFT; + + if let Some(page) =3D tree.get(idx) { + let page_offset =3D (sector & block::SECTOR_MASK as usize)= << block::SECTOR_SHIFT; + sector +=3D segment.copy_from_page(page, page_offset) >> b= lock::SECTOR_SHIFT; + } else { + sector +=3D segment.zero_page() >> block::SECTOR_SHIFT; + } + } + + Ok(()) + } + + #[inline(never)] + fn transfer( + command: bindings::req_op, + tree: &Tree, + sector: usize, + segment: Segment<'_>, + ) -> Result { + match command { + bindings::req_op_REQ_OP_WRITE =3D> Self::write(tree, sector, s= egment)?, + bindings::req_op_REQ_OP_READ =3D> Self::read(tree, sector, seg= ment)?, + _ =3D> (), + } + Ok(()) + } } =20 +type TreeNode =3D Owned; +type Tree =3D XArray; + +#[pin_data] struct QueueData { + #[pin] + tree: Tree, irq_mode: IRQMode, completion_time: Delta, + memory_backed: bool, } =20 #[pin_data] @@ -184,7 +256,7 @@ impl HasHrTimer for Pdu { =20 #[vtable] impl Operations for NullBlkDevice { - type QueueData =3D KBox; + type QueueData =3D Pin>; type RequestData =3D Pdu; =20 fn new_request_data() -> impl PinInit { @@ -194,7 +266,26 @@ fn new_request_data() -> impl PinInit { } =20 #[inline(always)] - fn queue_rq(queue_data: &QueueData, rq: Owned>, _is_= last: bool) -> Result { + fn queue_rq( + queue_data: Pin<&QueueData>, + mut rq: Owned>, + _is_last: bool, + ) -> Result { + if queue_data.memory_backed { + let tree =3D &queue_data.tree; + let command =3D rq.command(); + let mut sector =3D rq.sector(); + + for bio in rq.bio_iter_mut() { + let segment_iter =3D bio.segment_iter(); + for segment in segment_iter { + let length =3D segment.len(); + Self::transfer(command, tree, sector, segment)?; + sector +=3D length as usize >> block::SECTOR_SHIFT; + } + } + } + match queue_data.irq_mode { IRQMode::None =3D> rq.end_ok(), IRQMode::Soft =3D> mq::Request::complete(rq.into()), @@ -207,7 +298,7 @@ fn queue_rq(queue_data: &QueueData, rq: Owned>, _is_last: bool Ok(()) } =20 - fn commit_rqs(_queue_data: &QueueData) {} + fn commit_rqs(_queue_data: Pin<&QueueData>) {} =20 fn complete(rq: ARef>) { OwnableRefCounted::try_from_shared(rq) --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 92CB61BD035; Sun, 15 Feb 2026 23:41:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198874; cv=none; b=l0Wbjd9iU50bU7lFxLo4h3uDaIWW+UUtcdb2roQVvGQczemDdZWW0S5QStIum198zywaM4rxw0wm0bmGGwDKBDSCKxGH3RitoatPuH3QGHo2kVOgMrbria9yx47eymmyUXPWi+XsdEnUYPN0s0EjVMFmyEuto/eAgJIc1g/+3Sg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198874; c=relaxed/simple; bh=oPe8xTPtUm7MU6OaQHlhI5ZvU2RiaWhjCxNoERzRn94=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Vnr/R2eBX0obCEtmYvjoWFD+iykSao+a1LbFRek6/AG7SJ1CnyNOnTFMailErGQ4gYR4N3FFLSODj0++CrDbWgBSfUK1c7vkPp3ZUqmM5udi5Y2OWZO4ZKsdVQIDAqre6ZF2hkWCwowDufpAfhVssZJAbJHby94YA/oLaV0IZTA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=N4HkhPLL; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="N4HkhPLL" Received: by smtp.kernel.org (Postfix) with ESMTPSA id F060BC19422; Sun, 15 Feb 2026 23:41:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198874; bh=oPe8xTPtUm7MU6OaQHlhI5ZvU2RiaWhjCxNoERzRn94=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=N4HkhPLL2+UClx4hGhGXzseXnzQg4SGO27nQbo0G78ClH94QxOAUF1svWavDlEMws v+3GCnbJ7PelKTBeWj8hNce8IoNTYBolND+Ut5M27Ysam9gPKWyrWfc2EDSzQXqLSr Yo0Ex3gxHOOwWTCfC4oAKWJZ9PnN5Yx/F2ZMFpMb5EcB70d3yZgvtJdB7GhZekydp2 OaxZjK1/R9a0y0VO4L54lxhONQMfQ2lqR2O1QYqNe2tfmCdkhO+6hVGN0v6n6Ql6Ux 9kscZ03Jow8PgfjhYUa8uwY0MW10rD9N1taHvH4xGq47DHr47fh9efvydVBZXIM/lm KTlvuKxEH/Nwg== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:01 +0100 Subject: [PATCH 14/79] block: rnull: add submit queue count config option 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: <20260216-rnull-v6-19-rc5-send-v1-14-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=7281; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=oPe8xTPtUm7MU6OaQHlhI5ZvU2RiaWhjCxNoERzRn94=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklgocr9tcJuX0/u2H2v2dGPhOmiIz0V7rredg ljKxj2DbTKJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYKAAKCRDhuBo+eShj d0KfD/9HduJ9u1Dz2i53t5uptOOHZJWiq5J44S0C7hFuqOn7ciTpEW9jB6p012sRG0NdfGlU9wC 5wY8zNp0II+nrQ3Z7baNUQBjj8eCmgzIgoiyajufI1VTC5j9OlMlGv76VVGW8abuA21RtpTxvu1 LQ051KmcQCFrMWY6JNnI9ZsaCEXwBPPpwfZ2fROuKL7NEQutSKWYmXwnr5JF0EB746b3Gl7YFYc KxsNfXHcoCH8H2Q7pEeQf6FeQOgbVITNW8TuqZdpVeSUYJYU7tku/KuiKWVWv9QrjV14aRTjmXW 4nZ0oVFSB0UdmMjsU2ki3/jUuDXRnjbSYO4bIqC6cU7VK7ozdHWz+krobdr8HhkQNFZbiGjpVnZ ExhyYez/CA9Idt7BzXrTql/MKkF1IixJQK3ipYI5toRQNg2u/RrOgNDBv+iFHDFm1//zHQwIgIS 9n9Na57cPIsHWxjD+l7tgB/NV7dWAXKkvHqRNlxOuYKmsRVIRPWnXoguKayXs2rxSTn0REmbbpk f4vnf3sMSFHq3+VtUQh+443S2fzdMTttk1L5mDhYEExwB34ujSW7QV5UkMSZLrkLidPAT3j2AOg ZpAcNEJydOMa9BQOh8KqyhJrrJh62rbtBDCXdf34YCvx6kcf40950ai1DR0uHlg+smHF+QdvFJC vCqrzlQJiQAoKFA== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Allow user space to control the number of submission queues when creating null block devices. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 56 +++++++++++++++++++++++++++++++++----= ---- drivers/block/rnull/rnull.rs | 56 +++++++++++++++++++++++++++----------= ---- 2 files changed, 83 insertions(+), 29 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index b5dc30c5d3e20..fd3cbf7aa012e 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -59,7 +59,10 @@ impl AttributeOperations<0> for Config { =20 fn show(_this: &Config, page: &mut [u8; PAGE_SIZE]) -> Result { let mut writer =3D kernel::str::Formatter::new(page); - writer.write_str("blocksize,size,rotational,irqmode,completion_nse= c,memory_backed\n")?; + writer.write_str( + "blocksize,size,rotational,irqmode,completion_nsec,memory_back= ed\ + submit_queues\n", + )?; Ok(writer.bytes_written()) } } @@ -84,6 +87,7 @@ fn make_group( irqmode: 4, completion_nsec: 5, memory_backed: 6, + submit_queues: 7, ], }; =20 @@ -102,6 +106,7 @@ fn make_group( completion_time: time::Delta::ZERO, name: name.try_into()?, memory_backed: false, + submit_queues: 1, }), }), core::iter::empty(), @@ -157,6 +162,7 @@ struct DeviceConfigInner { completion_time: time::Delta, disk: Option>, memory_backed: bool, + submit_queues: u32, } =20 #[vtable] @@ -180,15 +186,16 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { let mut guard =3D this.data.lock(); =20 if !guard.powered && power_op { - guard.disk =3D Some(NullBlkDevice::new( - &guard.name, - guard.block_size, - guard.rotational, - guard.capacity_mib, - guard.irq_mode, - guard.completion_time, - guard.memory_backed, - )?); + guard.disk =3D Some(NullBlkDevice::new(crate::NullBlkOptions { + name: &guard.name, + block_size: guard.block_size, + rotational: guard.rotational, + capacity_mib: guard.capacity_mib, + irq_mode: guard.irq_mode, + completion_time: guard.completion_time, + memory_backed: guard.memory_backed, + submit_queues: guard.submit_queues, + })?); guard.powered =3D true; } else if guard.powered && !power_op { drop(guard.disk.take()); @@ -244,3 +251,32 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { Ok(()) } } + +#[vtable] +impl configfs::AttributeOperations<7> for DeviceConfig { + type Data =3D DeviceConfig; + + fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result { + let mut writer =3D kernel::str::Formatter::new(page); + writer.write_fmt(fmt!("{}\n", this.data.lock().submit_queues))?; + Ok(writer.bytes_written()) + } + + fn store(this: &DeviceConfig, page: &[u8]) -> Result { + if this.data.lock().powered { + return Err(EBUSY); + } + + let text =3D core::str::from_utf8(page)?.trim(); + let value =3D text + .parse::() + .map_err(|_| kernel::error::code::EINVAL)?; + + if value =3D=3D 0 || value > kernel::num_possible_cpus() { + return Err(kernel::error::code::EINVAL); + } + + this.data.lock().submit_queues =3D value; + Ok(()) + } +} diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index a1156a368c467..55ee5165b90b3 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -80,6 +80,10 @@ default: 0, description: "Create a memory-backed block device. 0-false, 1-= true. Default: 0", }, + submit_queues: u32 { + default: 1, + description: "Number of submission queues", + }, }, } =20 @@ -102,15 +106,16 @@ fn init(_module: &'static ThisModule) -> impl PinInit= { for i in 0..(*module_parameters::nr_devices.value()) { let name =3D CString::try_from_fmt(fmt!("rnullb{}", i))?; =20 - let disk =3D NullBlkDevice::new( - &name, - *module_parameters::bs.value(), - *module_parameters::rotational.value() !=3D 0, - *module_parameters::gb.value() * 1024, - (*module_parameters::irqmode.value()).try_into()?, - Delta::from_nanos(completion_time), - *module_parameters::memory_backed.value() !=3D 0, - )?; + let disk =3D NullBlkDevice::new(NullBlkOptions { + name: &name, + block_size: *module_parameters::bs.value(), + rotational: *module_parameters::rotational.value() != =3D 0, + capacity_mib: *module_parameters::gb.value() * 1024, + irq_mode: (*module_parameters::irqmode.value()).try_in= to()?, + completion_time: Delta::from_nanos(completion_time), + memory_backed: *module_parameters::memory_backed.value= () !=3D 0, + submit_queues: *module_parameters::submit_queues.value= (), + })?; disks.push(disk, GFP_KERNEL)?; } =20 @@ -124,25 +129,38 @@ fn init(_module: &'static ThisModule) -> impl PinInit= { } } =20 +struct NullBlkOptions<'a> { + name: &'a CStr, + block_size: u32, + rotational: bool, + capacity_mib: u64, + irq_mode: IRQMode, + completion_time: Delta, + memory_backed: bool, + submit_queues: u32, +} struct NullBlkDevice; =20 impl NullBlkDevice { - fn new( - name: &CStr, - block_size: u32, - rotational: bool, - capacity_mib: u64, - irq_mode: IRQMode, - completion_time: Delta, - memory_backed: bool, - ) -> Result> { + fn new(options: NullBlkOptions<'_>) -> Result> { + let NullBlkOptions { + name, + block_size, + rotational, + capacity_mib, + irq_mode, + completion_time, + memory_backed, + submit_queues, + } =3D options; + let flags =3D if memory_backed { mq::tag_set::Flag::Blocking.into() } else { mq::tag_set::Flags::default() }; =20 - let tagset =3D Arc::pin_init(TagSet::new(1, 256, 1, flags), GFP_KE= RNEL)?; + let tagset =3D Arc::pin_init(TagSet::new(submit_queues, 256, 1, fl= ags), GFP_KERNEL)?; =20 let queue_data =3D Box::pin_init( pin_init!(QueueData { --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 E19E23128B6; Sun, 15 Feb 2026 23:47:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199221; cv=none; b=hKIoV9rtO2b16ESf9CSA/PGaBeYVcZf7+SL3Dr7LsVzU72WO4WWONrVwvnDzjcjvNVbaZwgcZqEH9Dz7w9nIkuOAen7W5ZERQAK0m36kYKNJOFIfgJbvN7zjVrjdl+PPvqAH3rGKCMOGBknqBN9pQ5EXJ9aOViklaSoDfABCECo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199221; c=relaxed/simple; bh=+9VoTKjKEOvBExxZyNph/soC3n95VmzkDwZhpUL6gGk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=pQnos+EeQ3QsYl73HwcNNobYQBxSbKqTsyW0CtzQ2/7RpH5mf0NrZj13s2wGug8JJXY7C117gsGj7GidB4qP1AH7IArMmk44DILEJCu6ksZ7A8KbcQh4A8vWgL/akQOJRZfOH/ipYnPyTuvDM1Aty9vU/fqYgolkUFfNGpVzOVk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=p6yFBLnh; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="p6yFBLnh" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 89508C4CEF7; Sun, 15 Feb 2026 23:46:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199220; bh=+9VoTKjKEOvBExxZyNph/soC3n95VmzkDwZhpUL6gGk=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=p6yFBLnh1+UCZDBzNagelOCQiAZTy6t9bFvCt3DfPY2kQjp89cxxnx/WqzR7uPkja XtFGPzD709kaFS8rYUarJHVM5GG9Ji4D/BEmVk7PNzGqp6vPNfsy+s1KPD1tzNyA8/ kUp2P3pl+lqLRVQCboXDNycR9wZdV5J7kDe9/J7Pzji3+YlPfufknDWmzv02HHD9Rb Oy6tumJ0FYF4Fy+rwaPqkV8EERwiTTka5MsM6Z2EXPPqyza5mQ+q53Nl+f4snLPddz vNh/wt5/YsJA2FppjhCbiJIX11xf2/8kjwATVyvsSIAj+i1PGPC8ofwodRrTys3fVZ v/AlL1tiw4nZw== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:02 +0100 Subject: [PATCH 15/79] block: rnull: add `use_per_node_hctx` config option 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: <20260216-rnull-v6-19-rc5-send-v1-15-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4913; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=+9VoTKjKEOvBExxZyNph/soC3n95VmzkDwZhpUL6gGk=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklgp/QnG7POfeQUiu6svmmlJXvglJFlTee9SK tUtnVeNTJqJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYKQAKCRDhuBo+eShj d/YcEADDBnyhEHkcnKjgjSOfCTFCpflKQjLLvFu6y+3h6U/AAMU78Ooa0UWjtF/iAX3ljz+Jw38 +38ZE28EZIEB3oKXNFlW+Bu5c0Gl+sGHnDr9pWorObN7SOuzgXrqT20MVvUCfmBT7ZvhaSOJ1cU kdaZrlwcTmlB+TeD2Zf2MDIrdSgwoRdbRj7MjtFnWmmUEYeXBlAriFw9w6MTmNg5cuIu86YyV8G SMwkCgo8520RMC1HPaIMdnfafpsaSU+wwlOLWWmqRBGinA/yFpC64y0UjskOCk7PlsWYkKKrBXw N/sDKTZ8k9e1d/LTJctLsOKbdd3neRxwbJ+l+UNtu8qzgZhQR/CKepJxeHk5csYXyKosM7lZ8VL iBexxDt3LaW/TgzxQYlKqAR6cgVpDxCqLPGXN6N3lILjfobVN17IIIBMeS/dMaU9iIPUzRpE9c8 xFVesd0gonPwZU+0MngZ71gXTW7zoeG9UsufKNxQRc8KqmHik976k+KwoPxL34GdAd3iOsLBtgB cBqPbCD77dmJumgXnYPM4PXREAE748c76L66EeBrq1bJ/cEfek3j0jT+dOVA8Iro47s2HwJm7NN YGi/5HKi90LE3zlnMj+ayAvpkvKnvl3sYgkmwFijBqbX0hz0Wfiwa/tJJ3EzCpbKl94uhHlcPY/ HGlnL5hJHMYoRiw== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a configfs attribute to enable per-NUMA-node hardware contexts. When enabled, the driver creates one hardware queue per NUMA node instead of the default configuration. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 26 ++++++++++++++++++++++++-- drivers/block/rnull/rnull.rs | 28 ++++++++++++++++++++++------ 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index fd3cbf7aa012e..b05bfc23090c2 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -30,8 +30,11 @@ time, // }; use macros::{ + configfs_attribute, configfs_simple_bool_field, - configfs_simple_field, // + configfs_simple_field, + show_field, + store_with_power_check, // }; use pin_init::PinInit; =20 @@ -61,7 +64,7 @@ impl AttributeOperations<0> for Config { let mut writer =3D kernel::str::Formatter::new(page); writer.write_str( "blocksize,size,rotational,irqmode,completion_nsec,memory_back= ed\ - submit_queues\n", + submit_queues,use_per_node_hctx\n", )?; Ok(writer.bytes_written()) } @@ -88,6 +91,7 @@ fn make_group( completion_nsec: 5, memory_backed: 6, submit_queues: 7, + use_per_node_hctx: 8, ], }; =20 @@ -280,3 +284,21 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { Ok(()) } } + +configfs_attribute!(DeviceConfig, 8, + show: |this, page| show_field( + this.data.lock().submit_queues =3D=3D kernel::num_online_nodes(), = page + ), + store: |this, page| store_with_power_check(this, page, |this, page| { + let value =3D core::str::from_utf8(page)? + .trim() + .parse::() + .map_err(|_| kernel::error::code::EINVAL)? + !=3D 0; + + if value { + this.data.lock().submit_queues *=3D kernel::num_online_nodes(); + } + Ok(()) + }) +); diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 55ee5165b90b3..990dfcf95c9de 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -21,12 +21,17 @@ }, }, error::Result, - new_mutex, new_xarray, + new_mutex, + new_xarray, page::SafePage, pr_info, prelude::*, str::CString, - sync::{aref::ARef, Arc, Mutex}, + sync::{ + aref::ARef, + Arc, + Mutex, // + }, time::{ hrtimer::{ HrTimerCallback, @@ -40,7 +45,7 @@ OwnableRefCounted, Owned, // }, - xarray::XArray, + xarray::XArray, // }; use pin_init::PinInit; =20 @@ -73,8 +78,9 @@ description: "IRQ completion handler. 0-none, 1-softirq, 2-ti= mer", }, completion_nsec: u64 { - default: 10_000, - description: "Time in ns to complete a request in hardware. D= efault: 10,000ns", + default: 10_000, + description: + "Time in ns to complete a request in hardware. Default: 10,000= ns", }, memory_backed: u8 { default: 0, @@ -84,6 +90,11 @@ default: 1, description: "Number of submission queues", }, + use_per_node_hctx: u8 { + default: 0, + description: + "Use per-node allocation for hardware context queues, 0-false, 1-t= rue. Default: 0-false", + }, }, } =20 @@ -106,6 +117,11 @@ fn init(_module: &'static ThisModule) -> impl PinInit<= Self, Error> { for i in 0..(*module_parameters::nr_devices.value()) { let name =3D CString::try_from_fmt(fmt!("rnullb{}", i))?; =20 + let submit_queues =3D if *module_parameters::use_per_node_= hctx.value() !=3D 0 { + kernel::num_online_nodes() + } else { + *module_parameters::submit_queues.value() + }; let disk =3D NullBlkDevice::new(NullBlkOptions { name: &name, block_size: *module_parameters::bs.value(), @@ -114,7 +130,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { irq_mode: (*module_parameters::irqmode.value()).try_in= to()?, completion_time: Delta::from_nanos(completion_time), memory_backed: *module_parameters::memory_backed.value= () !=3D 0, - submit_queues: *module_parameters::submit_queues.value= (), + submit_queues, })?; disks.push(disk, GFP_KERNEL)?; } --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 C76AD2DB7A1; Sun, 15 Feb 2026 23:42:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198948; cv=none; b=fw4UuYJXUi/vTXe2ka78E8Kc8L/j/TBHIvUTEz/U8A8Iqft9lIflmwStWtWAynlbHpXEz1A0sWxpGriBUWHXaeOI1VWf1vHUCwvvudxzpWuGihForHMUJ8ic3YmnBk8T3stoqd4q+ghs5NIosIMEbpwYR25oCbD/Xw6zpRzGwS4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198948; c=relaxed/simple; bh=3w4qnZwh2+aU+MMv2rWpFqX4lbRY4HhOYDulfwtiM5M=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=LDgOHnlFkMRwm+lWePLO7NKB86bCUfvXrOo1PE8QWJeRnvdDwqU2ZARR2JA2+8L2CWF77YCcR+50zoCrNikkW1KJeuYr6CtppFdAWQSyeOSOmrK4LjSbcGfBQ6kOVu/mz1WuGLbd7rKXe4Wiq3/m7Gz0PJajADKNukB5LzX9olo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=hcNBsaOU; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="hcNBsaOU" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4A372C4CEF7; Sun, 15 Feb 2026 23:42:24 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198948; bh=3w4qnZwh2+aU+MMv2rWpFqX4lbRY4HhOYDulfwtiM5M=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=hcNBsaOUM934DH9Bn9Bt2eXMRFTjwTsogGjvGEoqIobTgU/yhXBgCW+djhbrod562 cotXZxz70B7WGOdbqDoyoRkCajfzwkKHrLswK0I/GXkWYW4EAy8zdjNCRojo+4EBSY nMI9JFDO46qVxu0S0dJJH0Wk0E3pZEummL/3uZeM9ieDl3tYUqxpYL2ZVOqr4nRqAE tvhlweYgaY5yXc+hpVGVN2U3k6cBwCQeYgYUGrpD6WkagJZO/ZPv69kh/TrThfiw93 dswZKTI/pyRE6JxRKOToSWKg4PXwe4oOhhIOuPFELLh0429JTk6gL6qhOmPrOcMX3l fxW8qyqfqpbBQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:03 +0100 Subject: [PATCH 16/79] block: rust: allow specifying home node when constructing `TagSet` 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: <20260216-rnull-v6-19-rc5-send-v1-16-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=2776; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=3w4qnZwh2+aU+MMv2rWpFqX4lbRY4HhOYDulfwtiM5M=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklgq/QYUe8bZ2utIQqjhHTdAHO+CoJMp7AuDq tKoe/uOJg6JAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYKgAKCRDhuBo+eShj d07ND/9tv906O8JV5IQLXSRDJsUM5zCW6KhC6KwqGSY4R86jRB+711HKXUg03JJQDCLhm5B1odN g5QNE1tI6Keo/Rlim+cgJ4C0MTNOFejGDuccLgjBur1v1qbhRtouw6LvcJhG8troiQXNaLo95Sh N4YJ1E0nvmwmxrrI/LnQZNCgwmyEzkipgRoWL3ii5hAa6JVBiuZFlheUXC9yUId42wJVfQrbyPG YcbY0u8K1j+ejkox7kq83opF2JANf984yHH5OC3PxPRrdXygZKUqKIKRG3Y7Z9MKfC11FHSzfXD bltiQQFd0bBP81hoCG2rNMg3gbY95zq8MxhhGVpndJA8yI6K0KkwuJv2Jp6lsrVOyLkBoJs24/4 8BFVbkxpRuoQv6ll/Bx+0XCfRq/COAOYxmIX60aqJ2M8q38PqYxCIB65sUizUlj2PN/+QCruQwR HInNKE7a+lLXj8GN7lo3X2z+jCyhoz1bebAowt6EWF6qWXD/n52dwMSpc2WBW2IMC53VYLHNopE xH2efg0tHDD8dX8CtvqT6ryGyT6oLFHGZB0OE2bnCSQuiS3iAJe0g/akikOI/b3ae1yLKsx99aL B5mslJIRkZyXSkcCXEy36iRF+6W26ki8gRLBy37mk0RcC39eLJZ4qPtzM8027gJ4xsyoEe5tHyT T5x6R19UphlS3XQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a `numa_node` parameter to `TagSet::new` to specify the home NUMA node for tag set allocations. This allows drivers to optimize memory placement for NUMA systems. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/rnull.rs | 5 ++++- rust/kernel/block/mq.rs | 5 ++++- rust/kernel/block/mq/tag_set.rs | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 990dfcf95c9de..278f75d886fd9 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -176,7 +176,10 @@ fn new(options: NullBlkOptions<'_>) -> Result> { mq::tag_set::Flags::default() }; =20 - let tagset =3D Arc::pin_init(TagSet::new(submit_queues, 256, 1, fl= ags), GFP_KERNEL)?; + let tagset =3D Arc::pin_init( + TagSet::new(submit_queues, 256, 1, bindings::NUMA_NO_NODE, fla= gs), + GFP_KERNEL, + )?; =20 let queue_data =3D Box::pin_init( pin_init!(QueueData { diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index 76e790cdb1f8f..d3957f2fb1a66 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -57,6 +57,7 @@ //! //! ```rust //! use kernel::{ +//! bindings, //! block::mq::{self, *}, //! new_mutex, //! prelude::*, @@ -92,7 +93,9 @@ //! } //! //! let tagset: Arc> =3D -//! Arc::pin_init(TagSet::new(1, 256, 1, mq::tag_set::Flags::default()= ), GFP_KERNEL)?; +//! Arc::pin_init( +//! TagSet::new(1, 256, 1, bindings::NUMA_NO_NODE, mq::tag_set::Fl= ags::default()), +//! GFP_KERNEL)?; //! let mut disk =3D gen_disk::GenDiskBuilder::new() //! .capacity_sectors(4096) //! .build(fmt!("myblk"), tagset, ())?; diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set= .rs index af214f7dd0c10..e9b36d6329b9b 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -41,6 +41,7 @@ pub fn new( nr_hw_queues: u32, num_tags: u32, num_maps: u32, + numa_node: i32, flags: Flags, ) -> impl PinInit { // SAFETY: `blk_mq_tag_set` only contains integers and pointers, w= hich @@ -53,7 +54,7 @@ pub fn new( ops: OperationsVTable::::build(), nr_hw_queues, timeout: 0, // 0 means default which is 30Hz in C - numa_node: bindings::NUMA_NO_NODE, + numa_node, queue_depth: num_tags, cmd_size, flags: flags.into_inner(), --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 D8C5E30C368; Sun, 15 Feb 2026 23:43:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199026; cv=none; b=FzE9ipdQMRzcIe8ZcFOFWKuL98o8bwGvwZDLCKEuKGGd+Oz6CiVSX6wt5QAhJao4O3C6pp7LjdJmNxFhtpe2i2LogJophQJXJfARDx8KLXykExIp/t3Qi0B2uROd4o7uq/6JQfaZ4sQYD/jMTPsTKuSCCvtGWV3n1dGrCGCUH7M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199026; c=relaxed/simple; bh=hyJWmGhHnYKgGumF3acRrYQ7yD9VZFPwyaRRW4uns7c=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=VhaVWf09pPPJbd0M+Yl9zWP1V9Sn+Frw3XVAk2ZNq2vEt1lQHd32Kiktcdy1zup9pJr2b2wCvXL3T6iQOXNlPsPEhYyD/HPg9a/zdeQRp7OJ0msnE30LVch8SA7c4Sn0J13A8/osM/QzZpNFoQwocrb/TAFC+lfLq8ef+s62II4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=r0Xvw+Xb; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="r0Xvw+Xb" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6DD5BC4CEF7; Sun, 15 Feb 2026 23:43:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199026; bh=hyJWmGhHnYKgGumF3acRrYQ7yD9VZFPwyaRRW4uns7c=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=r0Xvw+Xbl4WyoIV0Fgvc4CnezN4AquFYJprwOMMZ4tW2FWDP8IEMP5IspVkvapRKr eyQCXlRj9nuKYDdDqQr3EeH1EDajPMZ6uvg/8RHpfiYqBCE1AUpo51WkLT2mkQWzuf 8yAVDEjVXa5fUyolR1WoGZg95YCrxgWlLbnDCaI5uWYLA9l/ss7OYNKWrCggl1Bfxh /2MC4rdaD8XO4sPI0pN414gUY+NUp0nhQwJPAaunICom5uRzXiXKtQa0eL2W0q5vxT kJ8UlGWZDBAMe6iz/mIQmb//20aycZ9d1XVZvHVp5sNncJqa79a4o8+O28Mk8KfS6M O2UdjgNGNAg5Q== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:04 +0100 Subject: [PATCH 17/79] block: rnull: allow specifying the home numa node 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: <20260216-rnull-v6-19-rc5-send-v1-17-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4428; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=hyJWmGhHnYKgGumF3acRrYQ7yD9VZFPwyaRRW4uns7c=; b=kA0DAAoB4bgaPnkoY3cByyZiAGmSWCujcbaRtyQ+5c46GUtpYsZLNpV7VYMbAQXDEhY8owFWT okCMwQAAQoAHRYhBBLB+UdWv3wqFdbAEuG4Gj55KGN3BQJpklgrAAoJEOG4Gj55KGN3ydkQAInh m/4xDjaLWkap0qZVw4kdG+BQcpK/gQBOgCp5aQJ9g6jpMgw7xGymCSWaXegQ/45lRH69A69dmJ9 YevW/afwAjaK0Gy8+719L2V0WxkeUUbqtPvQP0r6h+O7HwKOEXeBp4svcMpD78XGzcaixFcTM73 +q3wrysM6xhIEIuFJxU4aPzz5XV6a0i4Am1u+LWbWUulfmD2VQzSH/qit7F7Al2Gh/xLWAaXsz7 mbeJOcNYaEtkyMwGIuoAAwOjcikxVvUHrAGHDVKgXx1rY7xVSAsw+GdBe+/2qKgSKKxCmiLIVzM Qdj9jl32dxV7g7L+SGVXd/XxSziAAft+K4g/ZY9TIw1BGIl1SzTZH+oayKtrCtHyjV45KFZ+Mor 3A1YvvKSQtwicHPmGYKsNR3neTKoLgAR9O8uHmNol4224yieRMP7lc8HkY6MNV4zI7b/UonXR6b WkRDZQmScebq/brfppkcJXOKGDu9tAWj9yp7JNC3fBMGFA6WmSxIXN+cF7/Qur2tofw7IKbsWuz EscfebPY9qQ/gPiYBQ21h0h/Wn0Tks5pLQ9TGjlsne3xZW9didm3pGauc/h3TFaEmIsjObi5vBT hlLepzDjKUpTaBBiRvm5KUtk7hxb4iqtjjqxihxO7H94YIe8K90FeU4QFctgIu785HWqX6gRMky 2c514 X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a configfs attribute to specify the NUMA node for rnull tag set and CPU map allocations. This allows testing NUMA-aware block device behavior and optimizing memory placement for specific hardware configurations. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 19 +++++++++++++++++++ drivers/block/rnull/rnull.rs | 18 ++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index b05bfc23090c2..fe00617d2b679 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -5,6 +5,7 @@ THIS_MODULE, // }; use kernel::{ + bindings, block::mq::gen_disk::{ GenDisk, GenDiskBuilder, // @@ -92,6 +93,7 @@ fn make_group( memory_backed: 6, submit_queues: 7, use_per_node_hctx: 8, + home_node: 9, ], }; =20 @@ -111,6 +113,7 @@ fn make_group( name: name.try_into()?, memory_backed: false, submit_queues: 1, + home_node: bindings::NUMA_NO_NODE, }), }), core::iter::empty(), @@ -167,6 +170,7 @@ struct DeviceConfigInner { disk: Option>, memory_backed: bool, submit_queues: u32, + home_node: i32, } =20 #[vtable] @@ -199,6 +203,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { completion_time: guard.completion_time, memory_backed: guard.memory_backed, submit_queues: guard.submit_queues, + home_node: guard.home_node, })?); guard.powered =3D true; } else if guard.powered && !power_op { @@ -302,3 +307,17 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { Ok(()) }) ); + +configfs_simple_field!( + DeviceConfig, + 9, + home_node, + i32, + check | value | { + if value =3D=3D 0 || value >=3D kernel::num_online_nodes().try_int= o()? { + Err(kernel::error::code::EINVAL) + } else { + Ok(()) + } + } +); diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 278f75d886fd9..fb3ce272d5bec 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -20,7 +20,10 @@ TagSet, // }, }, - error::Result, + error::{ + code, + Result, // + }, new_mutex, new_xarray, page::SafePage, @@ -95,6 +98,10 @@ description: "Use per-node allocation for hardware context queues, 0-false, 1-t= rue. Default: 0-false", }, + home_node: i32 { + default: -1, + description: "Home node for the device. Default: -1 (no node)", + }, }, } =20 @@ -131,6 +138,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { completion_time: Delta::from_nanos(completion_time), memory_backed: *module_parameters::memory_backed.value= () !=3D 0, submit_queues, + home_node: *module_parameters::home_node.value(), })?; disks.push(disk, GFP_KERNEL)?; } @@ -154,6 +162,7 @@ struct NullBlkOptions<'a> { completion_time: Delta, memory_backed: bool, submit_queues: u32, + home_node: i32, } struct NullBlkDevice; =20 @@ -168,6 +177,7 @@ fn new(options: NullBlkOptions<'_>) -> Result> { completion_time, memory_backed, submit_queues, + home_node, } =3D options; =20 let flags =3D if memory_backed { @@ -176,8 +186,12 @@ fn new(options: NullBlkOptions<'_>) -> Result> { mq::tag_set::Flags::default() }; =20 + if home_node > kernel::num_online_nodes().try_into()? { + return Err(code::EINVAL); + } + let tagset =3D Arc::pin_init( - TagSet::new(submit_queues, 256, 1, bindings::NUMA_NO_NODE, fla= gs), + TagSet::new(submit_queues, 256, 1, home_node, flags), GFP_KERNEL, )?; =20 --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 73D9E2DC78C; Sun, 15 Feb 2026 23:45:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199111; cv=none; b=rDknqMC7KnxCf/2xstcl8BQzhoZTuASJ9Jsn6HwYQxB6IwCkxMir/Zd29qt6kf7QiRpG9hPY/R4HD/+wChjiXfELVguzFMLId/qNtvM58n/ZH56ieftffLm1ChKtcRtIIIetLeLfwT8rhPuOL+vp5P/tojmawhVfNC539b+YkPk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199111; c=relaxed/simple; bh=FcDAFyIoSJaDhVyJTabIlJqI1bmrr6zZRJxwKKNDg40=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=qbU+qe14gJf25uuLPsytf+tUq1WorqJ00wp1zX/3IlwozbnAyPBCRxy+HxR5mCExM2qdZsL3nQ+au8TJD8Mn31N8IQmcyOfvHKO7Lk4ayP4b2ng2ba8z5IMWivT4EiDsA29Yme/Zg1aQ9S+Kr1YSpIWmiCl2RtGooFwHwavq/T4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=soe8zXn6; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="soe8zXn6" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 93DB3C4CEF7; Sun, 15 Feb 2026 23:45:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199111; bh=FcDAFyIoSJaDhVyJTabIlJqI1bmrr6zZRJxwKKNDg40=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=soe8zXn679EKVPAfQOboI9UKqjiwgKHFnpwMsPiql3Z0Cp4uolzIcGA63n0dWlUni v+qPLX6rDHuSPYzKLN3fyKIJ84tjBld/cc7zxKlQUZnBU1YaAWfU6xW21PSXjNEkWR PKyKHPwVvd3aL/KN+5GzarN2RqK8ZMDL9L/O7E/AJywo2HxXD4veGhulcUU7kSE7HQ Jj2dLopuXrIhd/VpyZSA3vPRx3z817oU/sYeLCUI4MNX1jZtVq5TU1FV1zTOmFPYUX EdF6XVVdzjSZ98ZZZvysqIfKodNx5FnnQ3YSREKCwLDg85oSBi9HbX8aViHSXnszbH IaTsNTXXn5TvA== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:05 +0100 Subject: [PATCH 18/79] block: rust: add Request::sectors() method 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: <20260216-rnull-v6-19-rc5-send-v1-18-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=982; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=FcDAFyIoSJaDhVyJTabIlJqI1bmrr6zZRJxwKKNDg40=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklgsVbgdhxlxk0yDLpeTpv4EyLGu60tCgyVLJ RCBjzOxsoaJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYLAAKCRDhuBo+eShj d1DgD/9LxN6RNRd1ZiyiLppcowV7i8sFKLIcLYPN0DyIf83awn5nqKner8IfCM070jzWJD+F3ET /AOVeXvIlsEuWxVewLuBSAOVLqSD79pFy2WwJHGIHxqDhnnI274ky8vEQ5PtuGDsdX+2luQPsB1 vlVkNT6W7n2PShGMSGwr79Zo/wOpki+BvZWcdSR8lCaZcx0NyRkfTXasZ+e92N+p4yAlKGBzU62 Od2r7Y5Cb9sEX6gL9LYCfryExzrBI0dthwX/SFDoginP1MTOv6l5kaVZprm8rAdZK8iILchC6kE QuJQLLEQr57MHeJ7pya234ViDpfixkQxKx7TQOp3mF7EjuyrD8x0P7aUMZVraD4F9uKI37DgRr8 pqxz6cwV4s2QFOUj2+Ta1GhAXIJvFG7WbDrLLftihFJmFyqP8EsVODQHnRkEuLtsnMvo1B2qTRO PXAlfOVUuLOyq2gWoUZIzIYmHy7x0hXnK3h7UFiWI+BqkOHk5LHzh9EHf/UVKGdOeA5k3pvkoNr cPHV/K2fn9jhs7Co1fIZ5S3N/Z8/4UDU2gHnSR+Z8aWs6UbdQEwbpgja+WzVrNfWNRqKhc5XRD/ wsHSZPMm4RR0SQdt/bjiCyqE7epA/R2/icuO7IxzjriDu/XqtzVVojq9x/qe+m3sv3nTHrnmQga BCRJn7OhpUjcHTQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a new method to get the size of a request in number of sectors. Signed-off-by: Andreas Hindborg Reviewed-by: Alice Ryhl --- rust/kernel/block/mq/request.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index 0dd329ae93dfc..9d11ae5651983 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -144,6 +144,13 @@ pub fn sector(&self) -> usize { unsafe { (*self.0.get()).__sector as usize } } =20 + /// Get the size of the request in number of sectors. + #[inline(always)] + pub fn sectors(&self) -> usize { + // SAFETY: By type invariant of `Self`, `self.0` is valid and live. + (unsafe { (*self.0.get()).__data_len as usize }) >> crate::block::= SECTOR_SHIFT + } + /// Return a pointer to the [`RequestDataWrapper`] stored in the priva= te area /// of the request structure. /// --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 B5B0C2D77E3; Sun, 15 Feb 2026 23:42:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198938; cv=none; b=uOoncBX13dyJNZNvzlN5lSlmT7pu8N71u74Ukw58XCPwjUc1RS6c5iyq8k/qr0488017DlJUs55h1hQ0keP6szoe0awCDB5LX/vCp2ZpoMU10/DSFkFNQVsKtSdD5Wepjumh6zBSSsD5wbxKotBFvfUD53KWtqBktNxr0m/jzZc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198938; c=relaxed/simple; bh=6LAOeUSwUtS+ZAfhWOk/dU6VGMrvdfMVJxYHu0ElVWg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=dv8jjiaRCkTa1bdupKS7x9jGS6bNf9Fb/U28l5uiaZ+K5i0kDUBf3cr9DMNfLe7VBTU8O9T7nhO8D6ZFb67dYFH5D4jLbHXj75q7Tm6ujq7bHY1npy7HDvkzDSOw8AWQtWJSeMNZdbbL9klYo6U6t8k9A9MqXMj87YofMKpK8cw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=kcb1swnd; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="kcb1swnd" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 350C8C4CEF7; Sun, 15 Feb 2026 23:42:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198938; bh=6LAOeUSwUtS+ZAfhWOk/dU6VGMrvdfMVJxYHu0ElVWg=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=kcb1swndbwEz60aKfQ99++Rx9ZSn4aB6GXhSvIWTAtBLl+Gei2BTtnc0kzm3a7S0/ ZEFK3Z/FEEJBNBU/1853eTcF1/WaLL5LIzC5aL/DY5xSwYWNBVPmfhSlCsa7L1g0EA s2M9buM51ruySlwcLy82Ty4vaqCXrRc2Xd165o0hXHjbsqBwLpRYRbo4NUcyAa+zTG IOrz5tLX6DqRxU90SXXemi3PAyj1TiSpW2Bc1cdQUy13U9B+Iooibo5xtPJGa/hlyx krgIJqFyXDL+10P2EqvACbuSXgjkgWocQEBfYfbfLy+N+Xny7G/B4I4dNE8+ax/k7t ZIJOiPjGtEneg== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:06 +0100 Subject: [PATCH 19/79] block: rust: mq: add max_hw_discard_sectors support to GenDiskBuilder 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: <20260216-rnull-v6-19-rc5-send-v1-19-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=2183; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=6LAOeUSwUtS+ZAfhWOk/dU6VGMrvdfMVJxYHu0ElVWg=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklgtNy3HVqeMKx/bZtfhBvhyhrqQKDLf+61b+ q5GOgtGnQCJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYLQAKCRDhuBo+eShj d9IBEADCxuBV98p4EeEhXjdQG5XWMxSpvLsVbj3M0E2kKiIwcrxpa9AJxkVUursKhIi9rUDxdXd NGWZ8HSlmyqls7Y3hpUwFYndcQuW4nxAIZWt42FirgHrX0NCX16lx5I5/+eo+IiCe4ccRoHMgEq 01JXhX/piAGvhuN76Mw1mnLR5cum7zQp7ycaIRJ4K4/UMpTsEF3OfLWykGYO5K0WPyk+KEbqEm9 sgiwnrbXmxW/bSIdx/1FPpb3hBkpCM2zn63p7d5iA80VcNj7gIlcujiu0fHe1Y1ReypylwCgQzs P1D7ypmft0pyj6sUvKE3DpC5IP5Mw4drtUI1vuNg3gA4679X3QodH4030qzOSKOZc365A865dmg GfRFkfTBC+7EpHTNS5k5/Yxjv9Gm3kF86NLFSsIKsceTL94/dhP3rKEwbkzqG6HmyS7Bv/kF0RB 5QkWfbIieVe+VA5YZz8BywbkcyjaDfWQG7QKSXjZywbVUM/iU6+1Mw/RCtFmSaB8sNcGTKLIQ+H 9ECe9/cLh7S9dHdjxSGMNER1PxwGSyywJRieit8ZRWY1dplVI1K+zBzDgYH9mNOTOSODhSau8rs gpAckvS/+VSaabzfijTBrBxcN45a6FbRcjIFeDm1mzmlEjyk1hONveYVeINnK3BBzTQXtb3VABW l0QslnlC7NVHN2g== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add support for configuring the maximum hardware discard sectors through GenDiskBuilder. This allows block devices to specify their discard/trim capabilities. Setting this value to 0 (the default) indicates that discard is not supported by the device. Non-zero values specify the maximum number of sectors that can be discarded in a single operation. Signed-off-by: Andreas Hindborg Reviewed-by: Alice Ryhl --- rust/kernel/block/mq/gen_disk.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_di= sk.rs index 1ce815c8cdab0..75968d6a57639 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -25,6 +25,7 @@ pub struct GenDiskBuilder { logical_block_size: u32, physical_block_size: u32, capacity_sectors: u64, + max_hw_discard_sectors: u32, } =20 impl Default for GenDiskBuilder { @@ -34,6 +35,7 @@ fn default() -> Self { logical_block_size: bindings::PAGE_SIZE as u32, physical_block_size: bindings::PAGE_SIZE as u32, capacity_sectors: 0, + max_hw_discard_sectors: 0, } } } @@ -94,6 +96,16 @@ pub fn capacity_sectors(mut self, capacity: u64) -> Self= { self } =20 + /// Set the maximum amount of sectors the underlying hardware device c= an + /// discard/trim in a single operation. + /// + /// Setting 0 (default) here will cause the disk to report discard not + /// supported. + pub fn max_hw_discard_sectors(mut self, max_hw_discard_sectors: u32) -= > Self { + self.max_hw_discard_sectors =3D max_hw_discard_sectors; + self + } + /// Build a new `GenDisk` and add it to the VFS. pub fn build( self, @@ -112,6 +124,7 @@ pub fn build( =20 lim.logical_block_size =3D self.logical_block_size; lim.physical_block_size =3D self.physical_block_size; + lim.max_hw_discard_sectors =3D self.max_hw_discard_sectors; if self.rotational { lim.features =3D bindings::BLK_FEAT_ROTATIONAL; } --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 A5DE02D8798; Sun, 15 Feb 2026 23:41:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198904; cv=none; b=pRAe6NEcMNKQ8+sbK0377jDMCyuPC6SJQXeFVjane2odvJpe2dt4n5F5TQLliIVB4qHOhF47fCiZ3kc/Y6KoVkt8vdafmyfBM38P8SlpIijYdnv2kmUlSORljiLORfTDqhzwh1oQbvhwlGE80r/0jVckNHYLcdlnnATWLi2MiLs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198904; c=relaxed/simple; bh=L3XSJojS07YDYbfXfGO4e+2UgYlsugCATTWsJ+jV4ls=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=CGt17bZgTpRgan+RSCa9VzR5W+WDVKFBCGsegR2Qxge0rnx3I3vECAbS32i3F4PdBuBS+9S/qF2DuzJ32VGta86wvOXtMS/UiqYJ5h93Xypb2XzGUiL15QuU0hAa+mJkKugKbyO8zZD3BqGKYxNJ3E02+589ptxyotND/uRnuUk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=igDz9M0v; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="igDz9M0v" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 22CC8C19422; Sun, 15 Feb 2026 23:41:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198904; bh=L3XSJojS07YDYbfXfGO4e+2UgYlsugCATTWsJ+jV4ls=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=igDz9M0vYhXKDIo+yjIsqqbNWXQbpqsH1646n5gEfRTWn8Uv/6oPPwENkcBFA55Js Yr0nrwjfAB8HxMohbg7LqLtlgQLA8+e6HtRquFS4kPiJlF7RFlHKiRTGt+ztkne6YO Jh8sDfztk1ShNdTtMjSBxXKPI8rWao8ch0oUtPz3PWZb5DURn7g/Yki0zNaHrFaCas ghpIGP8JrPGVlo8a533fw6N8Ls3yF3hwsZXYPqKC0xoeC/w0r69OQMffkohJkFViUH 4YUPNO8RfaarEmlgXRTUL+MYhQ9esViCqspjjFM8Vfa/A22YGikB0srm7nMl8G+0us E46u6vm1xhY2w== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:07 +0100 Subject: [PATCH 20/79] block: rnull: add discard support 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: <20260216-rnull-v6-19-rc5-send-v1-20-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=10018; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=L3XSJojS07YDYbfXfGO4e+2UgYlsugCATTWsJ+jV4ls=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklgtUZ2EEiKlJcCmtv5PNpC9ErgLsmTBI2uRU 9XxwKAejBGJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYLQAKCRDhuBo+eShj d8QpD/0aOYUEHA91S9PGUYMcbZUu6Nvel4/gIWiCvQFtcuJD+1ZAJOF+fOyT6RcBgie3kRaNwde WctVwIEmb5oeWENqVXTlE1YZ04r+TzBlvOfynxh4PxsHQUuQ5ra7xSIhW6zVg+qO3JwYxwcUO5+ mcVfO7w+gIN2k/feuuEY5OkquTWlDDK0qPGkN34ax2LICaFohGgugLDlnj6U3ugj3P8Pc2W1vfh JfMsqBCMDGwUBHABeOj/ttTyxmwbwbqEn0RE1CUPH+sBU37he60j+TxXOMe5lhTRj+wJ7n/soeq S4I7P4heDFRKG7JJSYvEOLGgzGnPy/Vz340UWzhBjYKcpCQnuN7hNbkq3c/6I5Dj5qMe/pzrod5 kkCsHYlLKQpqPU/REIZTagphKAtdAvF/16yoBmfj1RMNprpHqngs8sHwUSHKlsJzckOD/2plski Oeb3Z0XZiYjjs6O1SH+olc4NmLTjUaUSgIFoOzYK0WoRe44+DbOU5DIo+G/oOCxvKOSsn1Zm+pa 0URrr/009mux/KXiFLAAiSDDLLJiwztoqYEFMTHevmVKsYPct57PMBX3NHiRU4dUeNU5o/AMgei kNxccWqgKS6jRkjBti5yxV7YLsIkNn8KJRLiCz5mNu9YXgOjiRZYWUNFfl4uSJPfpKhtD6DeRsP SnyxLPVhFvuKO3A== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add support for discard operations to the rnull block driver: - Add discard module parameter and configfs attribute. - Set max_hw_discard_sectors when discard is enabled. - Add sector occupancy tracking. - Add discard handling that frees sectors and removes empty pages. - Discard operations require memory backing to function. The discard feature uses a bitmap to track which sectors in each page are occupied, allowing cleanup of pages when they are empty. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 15 ++++++ drivers/block/rnull/rnull.rs | 115 +++++++++++++++++++++++++++++++++++-= ---- 2 files changed, 116 insertions(+), 14 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index fe00617d2b679..4ec9be440105d 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -94,6 +94,7 @@ fn make_group( submit_queues: 7, use_per_node_hctx: 8, home_node: 9, + discard: 10, ], }; =20 @@ -114,6 +115,7 @@ fn make_group( memory_backed: false, submit_queues: 1, home_node: bindings::NUMA_NO_NODE, + discard: false, }), }), core::iter::empty(), @@ -171,6 +173,7 @@ struct DeviceConfigInner { memory_backed: bool, submit_queues: u32, home_node: i32, + discard: bool, } =20 #[vtable] @@ -204,6 +207,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { memory_backed: guard.memory_backed, submit_queues: guard.submit_queues, home_node: guard.home_node, + discard: guard.discard, })?); guard.powered =3D true; } else if guard.powered && !power_op { @@ -321,3 +325,14 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { } } ); + +configfs_attribute!(DeviceConfig, 10, + show: |this, page| show_field(this.data.lock().discard, page), + store: |this, page| store_with_power_check(this, page, |this, page| { + if !this.data.lock().memory_backed { + return Err(EINVAL); + } + this.data.lock().discard =3D kstrtobool_bytes(page)?; + Ok(()) + }) +); diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index fb3ce272d5bec..d1fb5fbc0c0be 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -19,14 +19,19 @@ Operations, TagSet, // }, + SECTOR_MASK, SECTOR_SHIFT, }, error::{ code, Result, // }, + ffi, new_mutex, new_xarray, - page::SafePage, + page::{ + SafePage, // + PAGE_SIZE, + }, pr_info, prelude::*, str::CString, @@ -102,6 +107,11 @@ default: -1, description: "Home node for the device. Default: -1 (no node)", }, + discard: u8 { + default: 0, + description: + "Support discard operations (requires memory-backed null_blk d= evice). Default: false", + }, }, } =20 @@ -139,6 +149,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { memory_backed: *module_parameters::memory_backed.value= () !=3D 0, submit_queues, home_node: *module_parameters::home_node.value(), + discard: *module_parameters::discard.value() !=3D 0, })?; disks.push(disk, GFP_KERNEL)?; } @@ -163,6 +174,7 @@ struct NullBlkOptions<'a> { memory_backed: bool, submit_queues: u32, home_node: i32, + discard: bool, } struct NullBlkDevice; =20 @@ -178,6 +190,7 @@ fn new(options: NullBlkOptions<'_>) -> Result> { memory_backed, submit_queues, home_node, + discard, } =3D options; =20 let flags =3D if memory_backed { @@ -201,22 +214,30 @@ fn new(options: NullBlkOptions<'_>) -> Result> { irq_mode, completion_time, memory_backed, + block_size: block_size as usize, }), GFP_KERNEL, )?; =20 - gen_disk::GenDiskBuilder::new() + let mut builder =3D gen_disk::GenDiskBuilder::new() .capacity_sectors(capacity_mib << (20 - block::SECTOR_SHIFT)) .logical_block_size(block_size)? .physical_block_size(block_size)? - .rotational(rotational) - .build(fmt!("{}", name.to_str()?), tagset, queue_data) + .rotational(rotational); + + if memory_backed && discard { + builder =3D builder + // Max IO size is u32::MAX bytes + .max_hw_discard_sectors(ffi::c_uint::MAX >> block::SECTOR_= SHIFT); + } + + builder.build(fmt!("{}", name.to_str()?), tagset, queue_data) } =20 #[inline(always)] fn write(tree: &Tree, mut sector: usize, mut segment: Segment<'_>) -> = Result { while !segment.is_empty() { - let page =3D SafePage::alloc_page(GFP_NOIO)?; + let page =3D NullBlockPage::new()?; let mut tree =3D tree.lock(); =20 let page_idx =3D sector >> block::PAGE_SECTORS_SHIFT; @@ -228,8 +249,10 @@ fn write(tree: &Tree, mut sector: usize, mut segment: = Segment<'_>) -> Result { tree.get_mut(page_idx).unwrap() }; =20 + page.set_occupied(sector); let page_offset =3D (sector & block::SECTOR_MASK as usize) << = block::SECTOR_SHIFT; - sector +=3D segment.copy_to_page(page, page_offset) >> block::= SECTOR_SHIFT; + sector +=3D + segment.copy_to_page(page.page.get_pin_mut(), page_offset)= >> block::SECTOR_SHIFT; } Ok(()) } @@ -243,7 +266,7 @@ fn read(tree: &Tree, mut sector: usize, mut segment: Se= gment<'_>) -> Result { =20 if let Some(page) =3D tree.get(idx) { let page_offset =3D (sector & block::SECTOR_MASK as usize)= << block::SECTOR_SHIFT; - sector +=3D segment.copy_from_page(page, page_offset) >> b= lock::SECTOR_SHIFT; + sector +=3D segment.copy_from_page(&page.page, page_offset= ) >> block::SECTOR_SHIFT; } else { sector +=3D segment.zero_page() >> block::SECTOR_SHIFT; } @@ -252,6 +275,32 @@ fn read(tree: &Tree, mut sector: usize, mut segment: S= egment<'_>) -> Result { Ok(()) } =20 + fn discard(tree: &Tree, mut sector: usize, sectors: usize, block_size:= usize) -> Result { + let mut remaining_bytes =3D sectors << SECTOR_SHIFT; + let mut tree =3D tree.lock(); + + while remaining_bytes > 0 { + let page_idx =3D sector >> block::PAGE_SECTORS_SHIFT; + let mut remove =3D false; + if let Some(page) =3D tree.get_mut(page_idx) { + page.set_free(sector); + if page.is_empty() { + remove =3D true; + } + } + + if remove { + drop(tree.remove(page_idx)) + } + + let processed =3D remaining_bytes.min(block_size); + sector +=3D processed >> SECTOR_SHIFT; + remaining_bytes -=3D processed; + } + + Ok(()) + } + #[inline(never)] fn transfer( command: bindings::req_op, @@ -268,7 +317,40 @@ fn transfer( } } =20 -type TreeNode =3D Owned; +const _CHEKC_STATUS_WIDTH: () =3D build_assert!((PAGE_SIZE >> SECTOR_SHIFT= ) <=3D 64); + +struct NullBlockPage { + page: Owned, + status: u64, +} + +impl NullBlockPage { + fn new() -> Result> { + Ok(KBox::new( + Self { + page: SafePage::alloc_page(GFP_NOIO | __GFP_ZERO)?, + status: 0, + }, + GFP_NOIO, + )?) + } + + fn set_occupied(&mut self, sector: usize) { + let idx =3D sector & SECTOR_MASK as usize; + self.status |=3D 1 << idx; + } + + fn set_free(&mut self, sector: usize) { + let idx =3D sector & SECTOR_MASK as usize; + self.status &=3D !(1 << idx); + } + + fn is_empty(&self) -> bool { + self.status =3D=3D 0 + } +} + +type TreeNode =3D KBox; type Tree =3D XArray; =20 #[pin_data] @@ -278,6 +360,7 @@ struct QueueData { irq_mode: IRQMode, completion_time: Delta, memory_backed: bool, + block_size: usize, } =20 #[pin_data] @@ -327,12 +410,16 @@ fn queue_rq( let command =3D rq.command(); let mut sector =3D rq.sector(); =20 - for bio in rq.bio_iter_mut() { - let segment_iter =3D bio.segment_iter(); - for segment in segment_iter { - let length =3D segment.len(); - Self::transfer(command, tree, sector, segment)?; - sector +=3D length as usize >> block::SECTOR_SHIFT; + if command =3D=3D bindings::req_op_REQ_OP_DISCARD { + Self::discard(tree, sector, rq.sectors(), queue_data.block= _size)?; + } else { + for bio in rq.bio_iter_mut() { + let segment_iter =3D bio.segment_iter(); + for segment in segment_iter { + let length =3D segment.len(); + Self::transfer(command, tree, sector, segment)?; + sector +=3D length as usize >> block::SECTOR_SHIFT; + } } } } --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 28C2F2E54AA; Sun, 15 Feb 2026 23:43:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198982; cv=none; b=o74gUij++bZSPQsgHsrwuTBoLZn0K0fvHegqtN1N8/ZPdlfr5W0wCAofLQvsM4KGMJ7zupuDt1adt/aX3SSQGDlzmDxiwEl0DgJ50Jw2OHSArmTS2f50FKigghRUeusWfQMMnstqoSx1g7YrQGBe30N5mOfg9TdJTvCS2RDsZSk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198982; c=relaxed/simple; bh=fZvIGl58JonxCsHjE1MOz0gJuaXvox3LIYllPG0cQyo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=U2vbekRcItvdpA0rmUWsrcjiRcPz7ZzOEdOFYVk9KN7WjftoDGmQEzS6ncRbW9Fjqn691kCb7Ro3pPkLv3BT8bKoTtVBu/Su4QwpQ9KustGdBpfSpcoKsM/Vh58w/YWL/cjE76lJwI7XKPAJb7v1mMKiek3fkGBDjLVlPq6/QSA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=tpakzWk4; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="tpakzWk4" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 74930C4CEF7; Sun, 15 Feb 2026 23:42:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198982; bh=fZvIGl58JonxCsHjE1MOz0gJuaXvox3LIYllPG0cQyo=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=tpakzWk4TWmcbuhpkBUEC9edsz+5JRgd1QpogL+InpyA0YDVT/qXAZTJK+BcS6IDm 4eLxyaf8IQfkbJw7hCl0onMo+VNhknT6ZzMihDkhrt6S9VkdC2fwHD7T26sVa/Pv1b HXwx08mbXDAKhZGPMHdRmOohz8nZB7elUkYYF+Jc2uvEaVxcemosU6VoPBJr3XW/0K dslY/CbFU9/WJYgyDZiCGY5ckt3eIqVvkEQPB28TPuwZGNhXcPEdTOdeA/vWvNPDji 4E59Cf0cRQ61S86hTjXM0monf/n3ZjwZVbeoqMoMjkBaV+RNbNZGHVy4G40q55Nhog 76Fh5AJmXKyIQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:08 +0100 Subject: [PATCH 21/79] block: rust: add `NoDefaultScheduler` flag for `TagSet` 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: <20260216-rnull-v6-19-rc5-send-v1-21-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1048; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=fZvIGl58JonxCsHjE1MOz0gJuaXvox3LIYllPG0cQyo=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklguOLYxvU7ZbLaMRp9Hmd+T5HDAKD73QjHU9 fYsKhE3Q8OJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYLgAKCRDhuBo+eShj d/JHD/0Qi2wOgU0MkzrqlvaS/lF0f0454+YaKacIDMS3nRLwpUa3xzj4xTQe2g3XcReJipitalT QVhPor15WdaNkr6AQcEeJs9Mml1rIsQFXWVK/KHwShX/ikW/jZiVME/UC2F1dWrL/P2IscSS8+r 6jfRd+XsFzduzj11vUU7XlPQDV7TXq+ZJ1if4wVpTLwvR9LyJHp+mHtx/4seUn3pj7/9BI0Obng C6PMUYI6JJNsbjcmjO2t7NbZwnlrXkyJxURPsW5B8uTNt/k56NhTKQS5cpjcMkkMozVX6zp8tt1 xe3GhZW5IsdQAe5XIJ0NiaPs4cL5in6gEx0jefPwu4F9lw+do6qvu1qKqZ+sPofYcLSG+y/9VyW QuIYVyAkdmvKwdZj2bId5oa/Ae4QWnioW/oKZ+v0POvs6raOLo9c+F/DMOmYNtb59WaGcU8gFaV jZTvQmYfMaS9ZrdUXE0rINFy7S8FcwPa1kmsCTFLzC/C4KCdJ7N32/HKOWACKfY0HIDECz4IRbR oxlMt4WWgbLKN6chtkBvXUbxhBR/tBabfHH5NV0US/xMsYc+ODU9JHYiasnbiO3sdP5E6dpLesk batWietAxuT0bgcv6zPNYoKn1f4TKNp2BFJKohYo/pW+aLS6Cb8uUVL88jiaW/HQBf7EY6PokTs llKN6jpceUvDq8Q== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a flag that maps to the BLK_MQ_F_NO_SCHED_BY_DEFAULT. This flag selects the 'none' scheduler during queue registration in case of a single hwq or shared hwqs instead of 'mq-deadline'. Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq/tag_set/flags.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rust/kernel/block/mq/tag_set/flags.rs b/rust/kernel/block/mq/t= ag_set/flags.rs index 768db938b9a95..1fca4fcb4dd20 100644 --- a/rust/kernel/block/mq/tag_set/flags.rs +++ b/rust/kernel/block/mq/tag_set/flags.rs @@ -15,5 +15,9 @@ pub enum Flag { /// processing IO. When this flag is not set, IO is processed in a= tomic /// context. When this flag is set, IO is processed in process con= text. Blocking =3D bindings::BLK_MQ_F_BLOCKING, + + /// Select 'none' during queue registration in case of a single hw= q or shared + /// hwqs instead of 'mq-deadline'. + NoDefaultScheduler =3D bindings::BLK_MQ_F_NO_SCHED_BY_DEFAULT, } } --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 E4906EEC0; Sun, 15 Feb 2026 23:43:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199022; cv=none; b=A61c3FdDlB32zgLLSnKLtK9QpBclgD3w6kliMUDiVrtyTONyirFRZ6PLRboVbOzs1iInS1mnDUZGRTH9rZLVdwNuT8SEyAMaMqjdioCQI3JzDEG0onW2fh6i6OpnRQDGvQNPh2BJUkpqc3L0kp+gO/TnvoXPYyQ4UXbdJ7CaanU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199022; c=relaxed/simple; bh=/Kuy6Fsv3n8+vl73J+RNkL3tqtg0kG+WMvIfcKimuAY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=hEYbWaOE4E3pUTPva+QQiVhuTZyoXBPGmASuNcP5heolLoEEYZa2T88UjxoFWp2aiS0nY1DeDZp3i8rOzs1H8wQLMQdKCxvsHMsCLC5DI8eYB17HmSLKA/5+bo2TwZd2GnnQMkA8yMaYFrU2wOpHfe7kxZAJ9DevQDpnjcQevHo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=FbOWUKbm; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="FbOWUKbm" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 77798C4CEF7; Sun, 15 Feb 2026 23:43:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199021; bh=/Kuy6Fsv3n8+vl73J+RNkL3tqtg0kG+WMvIfcKimuAY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=FbOWUKbm4n/CXe3V1um1I3TjOg20nmVQfW2OtVaFFxdNQP6kQ5k2icORCLV9qSFr+ yJiiKsSkDyEiyqCVq2kaGn/5bQTqiyXmkFnwID7eGTArXZjDUmy6eLzrqTgQ9v/lG1 jZMc3W1bhGJosTvvYjhCHmIuoVx9vC4NnwNMNQu5ZyA/gDXXdGDn/4gnnsYqViu5xV FMcXIeVhnCNDSNNOi41Vih3UrSVlAWYdB6SYUJBcn0XHodtOxIkic31DHfbzH7Blwo AzMlXTinavW6AQNmNAtnunlhJBNafB6RZECUpmoLDCJj3wIxyzObvWNkXPdGvuci1d I1LDxAxNta1Qw== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:09 +0100 Subject: [PATCH 22/79] block: rnull: add no_sched module parameter and configfs attribute 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: <20260216-rnull-v6-19-rc5-send-v1-22-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4831; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=/Kuy6Fsv3n8+vl73J+RNkL3tqtg0kG+WMvIfcKimuAY=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklgv7mC1vFpEakQN4AFwbqnINSdbK/gHyJEkH nv6vJSxCo+JAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYLwAKCRDhuBo+eShj d/ulD/0aSq4rGdIfmpcbHJkjMKGDZKFnDel5s8/iZYKX0kcfe5vL/96VBuvGiCoRVEkZ8B3AzCm JXU5pZeuGOCOYguGpmNw3/Y9W8riH+nzEPUw/tRHjqozu8EruCznv7k8kS/trwHLeYy5WvRVxxL 8G7TYYyWO3FqZaKQ0eqgvDnUGMIKHCQwnEvkH1/5Wd8wmFfACXM588wRW3G795keARNiosEC636 JepsEOmCU4dLOdjR/eOXrKvbtlNprOnX1J6YA1lWy9TMBHYA8cIvJ7m2LiwljAH+aU3epkpjIYD sXaRS6UlLf8z4ukbBBiEYsFmozNvXeCIQfN9aY1+KHROa6TJInR3Uink/5BUZxuASBgcWZBfoeT nRDUsg+8gX1JYv3dCe79oRV8gtmsRmOm+16DB0Wadp/1QNJhAOEtE82SP4vWiwgVXnBP4OZjG3Q hzV8bYoCNVVKzJuLqmsyuDpBO5oyY3ddBqfWlmNpBdUhNJAJTPOo9DCZL6HTPAc0kUnynM7C0vt 18V6eyeYOtNtBrMUqXNuN+hUZGAw10Ofal8hynsLJW+B877Z1Q8lIAHzPnvfFiw6nPSe3s9PDOO Wp8cjGQqFBBaOrxxTJyTCDtC1FWxmTTyfW8IaKgkman2/gkTU7pxbl0pdZUzSCH4luV/GtBamh+ LyYJCEG4DysFIgQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add support for disabling the default IO scheduler by adding: - no_sched module parameter to control scheduler selection at device creation. - no_sched configfs attribute (ID 11) for runtime configuration. - Use of NO_DEFAULT_SCHEDULER flag when no_sched is enabled. This allows bypassing the default 'mq-deadline' scheduler and using 'none' instead, which can improve performance for certain workloads. The flag selection logic is updated to use compound assignment operators for better readability. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 31 +++++++++++++++++++++++++++++++ drivers/block/rnull/rnull.rs | 25 ++++++++++++++++++------- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 4ec9be440105d..18e32a87673aa 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -95,6 +95,7 @@ fn make_group( use_per_node_hctx: 8, home_node: 9, discard: 10, + no_sched:11, ], }; =20 @@ -116,6 +117,7 @@ fn make_group( submit_queues: 1, home_node: bindings::NUMA_NO_NODE, discard: false, + no_sched: false, }), }), core::iter::empty(), @@ -174,6 +176,7 @@ struct DeviceConfigInner { submit_queues: u32, home_node: i32, discard: bool, + no_sched: bool, } =20 #[vtable] @@ -208,6 +211,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { submit_queues: guard.submit_queues, home_node: guard.home_node, discard: guard.discard, + no_sched: guard.no_sched, })?); guard.powered =3D true; } else if guard.powered && !power_op { @@ -336,3 +340,30 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { Ok(()) }) ); + +#[vtable] +impl configfs::AttributeOperations<11> for DeviceConfig { + type Data =3D DeviceConfig; + + fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result { + let mut writer =3D kernel::str::Formatter::new(page); + + if this.data.lock().no_sched { + writer.write_str("1\n")?; + } else { + writer.write_str("0\n")?; + } + + Ok(writer.bytes_written()) + } + + fn store(this: &DeviceConfig, page: &[u8]) -> Result { + if this.data.lock().powered { + return Err(EBUSY); + } + + this.data.lock().no_sched =3D kstrtobool_bytes(page)?; + + Ok(()) + } +} diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index d1fb5fbc0c0be..9a301b06fc5e0 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -29,8 +29,8 @@ new_mutex, new_xarray, page::{ - SafePage, // - PAGE_SIZE, + SafePage, + PAGE_SIZE, // }, pr_info, prelude::*, @@ -112,6 +112,10 @@ description: "Support discard operations (requires memory-backed null_blk d= evice). Default: false", }, + no_sched: u8 { + default: 0, + description: "No IO scheduler", + }, }, } =20 @@ -150,6 +154,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { submit_queues, home_node: *module_parameters::home_node.value(), discard: *module_parameters::discard.value() !=3D 0, + no_sched: *module_parameters::no_sched.value() !=3D 0, })?; disks.push(disk, GFP_KERNEL)?; } @@ -175,6 +180,7 @@ struct NullBlkOptions<'a> { submit_queues: u32, home_node: i32, discard: bool, + no_sched: bool, } struct NullBlkDevice; =20 @@ -191,13 +197,18 @@ fn new(options: NullBlkOptions<'_>) -> Result> { submit_queues, home_node, discard, + no_sched, } =3D options; =20 - let flags =3D if memory_backed { - mq::tag_set::Flag::Blocking.into() - } else { - mq::tag_set::Flags::default() - }; + let mut flags =3D mq::tag_set::Flags::default(); + + if memory_backed { + flags |=3D mq::tag_set::Flag::Blocking; + } + + if no_sched { + flags |=3D mq::tag_set::Flag::NoDefaultScheduler; + } =20 if home_node > kernel::num_online_nodes().try_into()? { return Err(code::EINVAL); --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 343B62E7F08; Sun, 15 Feb 2026 23:46:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199166; cv=none; b=rjUABgVl8xkTMl9XjVhUseHQeyjgxktrS7gq00gNQLQlfifZ7/54LahoiktFhjYdKRF7tdEosB6lB3XcwYcvxU9mPDP1GgL0kk7/EKLGz2U7ani9fs/St5VMDcBTooO15REpgpROJ0D05tFMPpnDcJ+5wX2RZ1zix6cMFbXFBsY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199166; c=relaxed/simple; bh=d7t58iATi7q3gu1CIeU43zy0p2OHAVGfrGhGT4Ou8wc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=k6zzJTRLvOOsVAiV4dMakQs019jEX2CmnNoh0f2dfL6Hr0wB/bjHIN0VAl18yJN3WPSJwb9zzUFEvZ8YrXeX2bkiXkeA6D+0tDTU7vEfo5ClzpUvyKF0SI7RkBIFKZEZWjj2mtqkA6vJKKenK1CztQO7oW4n1ZfjmA6OPuK3O3g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=QNs6Vzaa; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="QNs6Vzaa" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 66E2AC4CEF7; Sun, 15 Feb 2026 23:46:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199165; bh=d7t58iATi7q3gu1CIeU43zy0p2OHAVGfrGhGT4Ou8wc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=QNs6VzaaFLyKJhPXIQdsggC53pnifRgZLsxpkhFb6o286GjvGptAscqI/NtLH4UYr GHHIL0PB8AEJlm67qADR7pm/UBa7E0bZzBoUNgOqp4xbvzqwsuxhz71wZZNsTjkzEv qZjhXahjBCpB/FpSb0UQ6MzlZlxn4sp2JXCs0HfuCB/DzujSn3grK08XZuukB3Aa3M jQH5oO1urrpJ8uljWOOpTS4v1DGLqpvDOqOZJ9DGyFILQpbihqdtGRIJDzNYtuPYOz HDZqZdm8rooc8Rn+WGJA5t8O24GN3LBZTW+ZuCPrTEN8gjmzETH6w4bNwK05GEFX9C qqrAm6/aciFBQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:10 +0100 Subject: [PATCH 23/79] block: rust: change sector type from usize to u64 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: <20260216-rnull-v6-19-rc5-send-v1-23-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=7730; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=d7t58iATi7q3gu1CIeU43zy0p2OHAVGfrGhGT4Ou8wc=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklgwpp8ZG5eDYSzUchwIhxResrdcGVcsXmZyl g7CFHdwpJmJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYMAAKCRDhuBo+eShj dxV/D/kBcn108tJZUmshMYdi+NPSO3TZh9Irp94K2/f4Qe38WzevDLs2Aru5wDcx8XvSPqD3LeM uo18tQAhdWdBSigEK4JNxHJD8Bt5v/b8/9YnvxODXyMENZPx+mAY0YrDK8lM9/I7jHNgKqTAqGQ nsT2nJLfAYDtLhsZQ5SCxOZqtSF7KNCgYZkeuV5Bfk9kUIWIy8xeNVXyHlNvMjktBXNDpMHmpni mJZrAZonvjhSEaxiKJh6EYphfIptIDQUzKk/6scxinpgUaK+fvQPcoajU86eTPNtZU89htVKkIx 9pQ/YBWCI3tI2r4ESji3ikU8uMgMZzmtGgxBWg85GsMHEi4J5cB8vo1s9bfa5a54Hg/37Ck8jL0 V0lPA+79zKDGxaPs2ncIF+D5Bfk2uZxS+UqMTwf1QIEwfIzpUOnGGEVrTaq7n+p/XKvO7O6Jg9D UMnR2toQqstUM1xH79NAOukAp9569AqZuiFEtDONgJwVBzeA6/NoYp3YMHgIJ/cj+2x9Hg5lupa OBYvCYQeaRmbkHvFNJMMiMAWxcB/zL+Wcg4M3MsqW2bTApybWA5+mNHwxt6Z1ZGLAVsYyF/G1iv LZE0+bKDZTUws8BP8Ks2D7QhClalfiwAT9M0jDRaQm8+sZ6kBMyWpJ3YVCPS+CTjQi8xRvLaAh8 +X0suhCT07dImyg== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Change the `sector()` and `sectors()` methods in `Request` to return `u64` and `u32` respectively instead of `usize`. This matches the underlying kernel types. Update rnull driver to handle the new sector types with appropriate casting throughout the read, write, and discard operations. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/rnull.rs | 49 +++++++++++++++++++++----------------= ---- rust/kernel/block/mq/request.rs | 8 +++---- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 9a301b06fc5e0..b0008e2f9c398 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -225,7 +225,7 @@ fn new(options: NullBlkOptions<'_>) -> Result> { irq_mode, completion_time, memory_backed, - block_size: block_size as usize, + block_size: block_size.into(), }), GFP_KERNEL, )?; @@ -246,54 +246,55 @@ fn new(options: NullBlkOptions<'_>) -> Result> { } =20 #[inline(always)] - fn write(tree: &Tree, mut sector: usize, mut segment: Segment<'_>) -> = Result { + fn write(tree: &Tree, mut sector: u64, mut segment: Segment<'_>) -> Re= sult { while !segment.is_empty() { let page =3D NullBlockPage::new()?; let mut tree =3D tree.lock(); =20 let page_idx =3D sector >> block::PAGE_SECTORS_SHIFT; =20 - let page =3D if let Some(page) =3D tree.get_mut(page_idx) { + let page =3D if let Some(page) =3D tree.get_mut(page_idx as us= ize) { page } else { - tree.store(page_idx, page, GFP_NOIO)?; - tree.get_mut(page_idx).unwrap() + tree.store(page_idx as usize, page, GFP_NOIO)?; + tree.get_mut(page_idx as usize).unwrap() }; =20 page.set_occupied(sector); - let page_offset =3D (sector & block::SECTOR_MASK as usize) << = block::SECTOR_SHIFT; - sector +=3D - segment.copy_to_page(page.page.get_pin_mut(), page_offset)= >> block::SECTOR_SHIFT; + let page_offset =3D (sector & u64::from(block::SECTOR_MASK)) <= < block::SECTOR_SHIFT; + sector +=3D segment.copy_to_page(page.page.get_pin_mut(), page= _offset as usize) as u64 + >> block::SECTOR_SHIFT; } Ok(()) } =20 #[inline(always)] - fn read(tree: &Tree, mut sector: usize, mut segment: Segment<'_>) -> R= esult { + fn read(tree: &Tree, mut sector: u64, mut segment: Segment<'_>) -> Res= ult { let tree =3D tree.lock(); =20 while !segment.is_empty() { let idx =3D sector >> block::PAGE_SECTORS_SHIFT; =20 - if let Some(page) =3D tree.get(idx) { - let page_offset =3D (sector & block::SECTOR_MASK as usize)= << block::SECTOR_SHIFT; - sector +=3D segment.copy_from_page(&page.page, page_offset= ) >> block::SECTOR_SHIFT; + if let Some(page) =3D tree.get(idx as usize) { + let page_offset =3D (sector & u64::from(block::SECTOR_MASK= )) << block::SECTOR_SHIFT; + sector +=3D segment.copy_from_page(&page.page, page_offset= as usize) as u64 + >> block::SECTOR_SHIFT; } else { - sector +=3D segment.zero_page() >> block::SECTOR_SHIFT; + sector +=3D segment.zero_page() as u64 >> block::SECTOR_SH= IFT; } } =20 Ok(()) } =20 - fn discard(tree: &Tree, mut sector: usize, sectors: usize, block_size:= usize) -> Result { + fn discard(tree: &Tree, mut sector: u64, sectors: u64, block_size: u64= ) -> Result { let mut remaining_bytes =3D sectors << SECTOR_SHIFT; let mut tree =3D tree.lock(); =20 while remaining_bytes > 0 { let page_idx =3D sector >> block::PAGE_SECTORS_SHIFT; let mut remove =3D false; - if let Some(page) =3D tree.get_mut(page_idx) { + if let Some(page) =3D tree.get_mut(page_idx as usize) { page.set_free(sector); if page.is_empty() { remove =3D true; @@ -301,7 +302,7 @@ fn discard(tree: &Tree, mut sector: usize, sectors: usi= ze, block_size: usize) -> } =20 if remove { - drop(tree.remove(page_idx)) + drop(tree.remove(page_idx as usize)) } =20 let processed =3D remaining_bytes.min(block_size); @@ -316,7 +317,7 @@ fn discard(tree: &Tree, mut sector: usize, sectors: usi= ze, block_size: usize) -> fn transfer( command: bindings::req_op, tree: &Tree, - sector: usize, + sector: u64, segment: Segment<'_>, ) -> Result { match command { @@ -346,13 +347,13 @@ fn new() -> Result> { )?) } =20 - fn set_occupied(&mut self, sector: usize) { - let idx =3D sector & SECTOR_MASK as usize; + fn set_occupied(&mut self, sector: u64) { + let idx =3D sector & u64::from(SECTOR_MASK); self.status |=3D 1 << idx; } =20 - fn set_free(&mut self, sector: usize) { - let idx =3D sector & SECTOR_MASK as usize; + fn set_free(&mut self, sector: u64) { + let idx =3D sector & u64::from(SECTOR_MASK); self.status &=3D !(1 << idx); } =20 @@ -371,7 +372,7 @@ struct QueueData { irq_mode: IRQMode, completion_time: Delta, memory_backed: bool, - block_size: usize, + block_size: u64, } =20 #[pin_data] @@ -422,14 +423,14 @@ fn queue_rq( let mut sector =3D rq.sector(); =20 if command =3D=3D bindings::req_op_REQ_OP_DISCARD { - Self::discard(tree, sector, rq.sectors(), queue_data.block= _size)?; + Self::discard(tree, sector, rq.sectors().into(), queue_dat= a.block_size)?; } else { for bio in rq.bio_iter_mut() { let segment_iter =3D bio.segment_iter(); for segment in segment_iter { let length =3D segment.len(); Self::transfer(command, tree, sector, segment)?; - sector +=3D length as usize >> block::SECTOR_SHIFT; + sector +=3D u64::from(length) >> block::SECTOR_SHI= FT; } } } diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index 9d11ae5651983..8fe7e126c522a 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -139,16 +139,16 @@ pub fn bio_iter_mut<'a>(self: &'a mut Owned) ->= BioIterator<'a> { =20 /// Get the target sector for the request. #[inline(always)] - pub fn sector(&self) -> usize { + pub fn sector(&self) -> u64 { // SAFETY: By type invariant of `Self`, `self.0` is valid and live. - unsafe { (*self.0.get()).__sector as usize } + unsafe { (*self.0.get()).__sector } } =20 /// Get the size of the request in number of sectors. #[inline(always)] - pub fn sectors(&self) -> usize { + pub fn sectors(&self) -> u32 { // SAFETY: By type invariant of `Self`, `self.0` is valid and live. - (unsafe { (*self.0.get()).__data_len as usize }) >> crate::block::= SECTOR_SHIFT + (unsafe { (*self.0.get()).__data_len }) >> crate::block::SECTOR_SH= IFT } =20 /// Return a pointer to the [`RequestDataWrapper`] stored in the priva= te area --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 DDA401FE45A; Sun, 15 Feb 2026 23:41:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198864; cv=none; b=KIOQYuM/zdZ2QBMukgUFHvlr2E7rP5LD8Ca5nHf1H6KkPu8H+OHp0onMuIMc3vKYKXarSAAi28savtbo+73X404rLhMKpNvvHkKowcmDKgH+JBnsPOJMf38PFwheHMJyMcbgVsDyk202s9td/Is5DJNRdk5EmnsHWV7xDbiFAOg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198864; c=relaxed/simple; bh=fE067vjSsqKm/5ZnuiOwywsOnZPEYeb2N4KfqdpR9Ok=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=tLCugG0fuDof8dqk3T5haxpF6ZFO/tnh4fMjrGhLbY5051+nGB8yq6x2AoGHZpUganNsm/DbSaq3ztlEWQcLmuejkGc3/PDE7aF+m1gAWQ31VNhZuS0FSBvyYi6Swl7MUaz7QK5Umcb4N6kouUWFJx5bBlpzJXIcFedHF//8bSc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ASr/6BUI; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ASr/6BUI" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3FB4BC4CEF7; Sun, 15 Feb 2026 23:41:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198864; bh=fE067vjSsqKm/5ZnuiOwywsOnZPEYeb2N4KfqdpR9Ok=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=ASr/6BUIb1dhe/Hv43H9W3P2fLSyJ2WecFn2brWrZU9bV+4Y/c9SLb5RH90d23hXQ V9obpmrNw0+NsHX7KgioU5kOEkDfrhXDGXhbE1yrAgJH8kHidcsrxXcJksvVbiyB5R km7Y+gHPkn/RlWcbASf5sC5rmtlDld2AGB/bqfPja+2ogcMIjjfoYvJNRXo1UBC+zO O3KtHxa+/LmfjZRZp9iEcM/EgmNKbcma4wSzWo2gQclDFe48+Uf3O2pTEzZkxX8H4y Hu7Fse70rkUdKLl/u+vKf7+YZ0sjHVG8Ca+swkUBifzRJawkh68YgdoZphVASTJdln QYIutGe5cojVw== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:11 +0100 Subject: [PATCH 24/79] block: rust: add `BadBlocks` for bad block tracking 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: <20260216-rnull-v6-19-rc5-send-v1-24-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=30902; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=fE067vjSsqKm/5ZnuiOwywsOnZPEYeb2N4KfqdpR9Ok=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklgxYc8/ZqV61hBgMOPCcBPgcAzG7DSwwYj3l c9rmPFSNDWJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYMQAKCRDhuBo+eShj dynvD/9PFnZxZLF8sY5UbTOM26XTHt0KcAqsG8fk9vUnoX46IY00Fvi44wO82s8Bwp2dmnWWFv8 qAC29G4Sb5OLmWXt6N5gUHnd/thptS4NUyHjuPgTX94lfY8Zxx32hoPOCqJ6JFE3w9uVJ3E4KZ6 Ojs4RNxnQAg+xIFZRyULkMet0V1/+7fWMvRYWDYEY0ULwjdCDZenjkLs+sm7T3NrFu7eqjU1JXx sAgG5YzlwLoq7pnj36sMCZz9q6WWrVI6GiH+SIcW68IzR6i2PTAjH1SX1rRHpTy8u1lV1o06iMh Zj9+h1GDh5ZvO3mzD44KcNkVBtrw9lwfVAEvzyT1tuPcbezwdRZ4FyYIAAfj0q1+jIHQf8s0E8S lmgZxfXs+HiZNs3bEHvNUCjEbFVYdhwxkXcyJRwdMoC6DOgMqoXIOAf4OplZ2mnVFEcJCKcZWOF 1ATwcL83+8MvTtFPrnxt03lw4DgnKCo4hOrvT0jtDN5/8KfSQuCSuRev6ELYHYMQ8/nnZiQPVmZ aAcZMpfDXBwLoK09OFkn5clEoxRRQ/1ZADA+GiksUbwG3o5DmhBUZqmreAsezbA9ePURBqB5gu6 1qTLzavU3CRY0UqYI1iS4cO+kQSfBmfJ/Be7IgCIVFSx1t1PdUw01Aw53FeZAMp1e5111IYIYI2 r9yg8nAJ0W2SwrA== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a safe Rust wrapper around the Linux kernel's badblocks infrastructure to track and manage defective sectors on block devices. The BadBlocks type provides methods to: - Mark sectors as bad or good (set_bad/set_good) - Check if sector ranges contain bad blocks (check) - Automatically handle memory management with PinnedDrop The implementation includes comprehensive documentation with examples for block device drivers that need to avoid known bad sectors to maintain data integrity. Bad blocks information is used by device drivers, filesystem layers, and device management tools. Signed-off-by: Andreas Hindborg --- rust/bindings/bindings_helper.h | 1 + rust/kernel/block.rs | 1 + rust/kernel/block/badblocks.rs | 721 ++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 723 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index e0ba5c712c560..76b58c3fd1ff1 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/block.rs b/rust/kernel/block.rs index 17de727bc1047..19236ab95227b 100644 --- a/rust/kernel/block.rs +++ b/rust/kernel/block.rs @@ -2,6 +2,7 @@ =20 //! Types for working with the block layer. =20 +pub mod badblocks; pub mod bio; pub mod mq; =20 diff --git a/rust/kernel/block/badblocks.rs b/rust/kernel/block/badblocks.rs new file mode 100644 index 0000000000000..a5fe0fde2e755 --- /dev/null +++ b/rust/kernel/block/badblocks.rs @@ -0,0 +1,721 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Bad blocks tracking for block devices. +//! +//! This module provides a safe Rust wrapper around the badblocks +//! infrastructure, which is used to track and manage bad sectors on block +//! devices. Bad blocks are sectors that cannot reliably store data and sh= ould +//! be avoided during I/O operations. + +use core::ops::{Range, RangeBounds}; + +use crate::{ + error::to_result, + page::PAGE_SIZE, + prelude::*, + sync::atomic::{ordering, Atomic}, + types::Opaque, +}; +use pin_init::{pin_data, PinInit}; + +/// A bad blocks tracker for managing defective sectors on a block device. +/// +/// `BadBlocks` provides functionality to mark sectors as bad and check if +/// ranges contain bad blocks. This is useful for some classes of drivers = to +/// maintain data integrity by avoiding known bad sectors. +/// +/// # Storage Format +/// +/// Bad blocks are stored in a compact format where each 64-bit entry cont= ains: +/// - **Sector offset** (54 bits): Starting sector of the bad range +/// - **Length** (9 bits): Number of sectors (1-512) in the bad range +/// - **Acknowledged flag** (1 bit): Whether the bad blocks have been ackn= owledged +/// +/// The bad blocks tracker uses exactly one page ([`PAGE_SIZE`]) of memory= to store +/// bad block entries. This allows tracking up to `PAGE_SIZE/8` bad block = ranges +/// (typically 512 ranges on systems with 4KB pages). +/// +/// # Locking +/// +/// Operations on the structure is internally synchronized by a seqlock. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ```rust +/// # use kernel::block::badblocks::{BadBlocks, BlockStatus}; +/// # use kernel::prelude::*; +/// +/// // Create a new bad blocks tracker +/// let bad_blocks =3D KBox::pin_init(BadBlocks::new(true), GFP_KERNEL)?; +/// +/// // Mark sectors 100-109 as bad (unacknowledged) +/// bad_blocks.set_bad(100..110, false)?; +/// +/// // Check if sector range 95-104 contains bad blocks +/// match bad_blocks.check(95..105) { +/// BlockStatus::None =3D> pr_info!("No bad blocks found"), +/// BlockStatus::Acknowledged(range) =3D> pr_warn!("Acknowledged bad b= locks: {:?}", range), +/// BlockStatus::Unacknowledged(range) =3D> pr_err!("Unacknowledged ba= d blocks: {:?}", range), +/// } +/// # Ok::<(), kernel::error::Error>(()) +/// ``` +/// # Invariants +/// +/// - `self.blocks` is a valid `bindings::badblocks` struct. +#[pin_data(PinnedDrop)] +pub struct BadBlocks { + #[pin] + blocks: Opaque, +} + +impl BadBlocks { + /// Creates a new bad blocks tracker. + /// + /// Initializes an empty bad blocks tracker that can manage defective = sectors + /// on a block device. The tracker starts with no bad blocks recorded = and + /// allocates a single page for storing bad block entries. + /// + /// # Returns + /// + /// Returns a [`PinInit`] that can be used to initialize a [`BadBlocks= `] instance. + /// Initialization may fail with `ENOMEM` if memory allocation fails. + /// + /// # Examples + /// + /// ```rust + /// # use kernel::block::badblocks::{BadBlocks, BlockStatus}; + /// # use kernel::prelude::*; + /// + /// // Create and initialize a bad blocks tracker + /// let bad_blocks =3D KBox::pin_init(BadBlocks::new(true), GFP_KERNEL= )?; + /// + /// // The tracker is ready to use with no bad blocks initially + /// match bad_blocks.check(0..100) { + /// BlockStatus::None =3D> pr_info!("No bad blocks found initially= "), + /// _ =3D> unreachable!(), + /// } + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + pub fn new(enable: bool) -> impl PinInit { + // INVARIANT: We initialize `self.blocks` below. If initialization= fails, an error is + // returned. + try_pin_init!(Self { + blocks <- Opaque::try_ffi_init(|slot| { + // SAFETY: `slot` is a valid pointer to uninitialized memo= ry + // allocated by the Opaque type. `badblocks_init` is safe = to + // call with uninitialized memory. + to_result(unsafe {bindings::badblocks_init(slot, if enable= {1} else {0})}) + }), + }) + } + + fn shift_ref(&self) -> &Atomic { + // SAFETY: By type invariant self.blocks is valid. + let ptr =3D unsafe { &raw const (*self.blocks.get()).shift }; + // SAFETY: `shift` is only written by C code using atomic operatio= ns after initialization. + unsafe { Atomic::from_ptr(ptr.cast_mut().cast()) } + } + + /// Enables the bad blocks tracker if it was previously disabled. + /// + /// Attempts to enable bad block tracking by transitioning the tracker= from + /// a disabled state to an enabled state. + /// + /// # Behavior + /// + /// - If the tracker is disabled, it will be enabled. + /// - If the tracker is already enabled, this operation has no effect. + /// - The operation is atomic and thread-safe. + /// + /// # Usage + /// + /// Bad blocks trackers can be created in a disabled state and enabled= later + /// when needed. This is useful for conditional bad block tracking or = for + /// deferring activation until the device is fully initialized. + /// + /// # Examples + /// + /// ```rust + /// # use kernel::block::badblocks::BadBlocks; + /// # use kernel::prelude::*; + /// + /// // Create a disabled bad blocks tracker + /// let bad_blocks =3D KBox::pin_init(BadBlocks::new(false), GFP_KERNE= L)?; + /// assert!(!bad_blocks.enabled()); + /// + /// // Enable it when needed + /// bad_blocks.enable(); + /// assert!(bad_blocks.enabled()); + /// + /// // Subsequent enable calls have no effect + /// bad_blocks.enable(); + /// assert!(bad_blocks.enabled()); + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + pub fn enable(&self) { + let _ =3D self.shift_ref().cmpxchg(-1, 0, ordering::Relaxed); + } + + /// Checks whether the bad blocks tracker is currently enabled. + /// + /// Returns `true` if bad block tracking is active, `false` if it is d= isabled. + /// When disabled, the tracker will not perform bad block checks or op= erations. + /// + /// # Returns + /// + /// - `true` - Bad block tracking is enabled and operational + /// - `false` - Bad block tracking is disabled + /// + /// # Thread Safety + /// + /// This method is thread-safe and uses atomic operations to check the + /// tracker's state without requiring external synchronization. + /// + /// # Examples + /// + /// ```rust + /// # use kernel::block::badblocks::BadBlocks; + /// # use kernel::prelude::*; + /// + /// // Create an enabled tracker + /// let enabled_tracker =3D KBox::pin_init(BadBlocks::new(true), GFP_K= ERNEL)?; + /// assert!(enabled_tracker.enabled()); + /// + /// // Create a disabled tracker + /// let disabled_tracker =3D KBox::pin_init(BadBlocks::new(false), GFP= _KERNEL)?; + /// assert!(!disabled_tracker.enabled()); + /// + /// // Enable and verify + /// disabled_tracker.enable(); + /// assert!(disabled_tracker.enabled()); + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + pub fn enabled(&self) -> bool { + self.shift_ref().load(ordering::Relaxed) >=3D 0 + } + + /// Marks a range of sectors as bad. + /// + /// Records a contiguous range of sectors as defective in the bad bloc= ks tracker. + /// Bad sectors should be avoided during I/O operations to prevent dat= a corruption. + /// The implementation may merge, split, or extend existing ranges as = needed. + /// + /// # Parameters + /// + /// - `range` - The range of sectors to mark as bad. Each individual r= ange is limited to 512 + /// sectors maximum by the underlying implementation. + /// - `acknowledged` - Whether the bad blocks have been acknowledged t= o be bad. Acknowledged bad + /// blocks may be handled differently by some subsystems. + /// + /// # Acknowledgment Semantics + /// + /// - **Unacknowledged** (`acknowledged =3D false`): Newly discovered = bad blocks that + /// need attention. These are often treated as errors by upper layer= s. + /// - **Acknowledged** (`acknowledged =3D true`): Blocks that have bee= n confirmed bad. These may + /// be should be handled by remapping. + /// + /// # Range Management + /// + /// The implementation automatically: + /// - **Merges** adjacent or overlapping ranges with the same acknowle= dgment status + /// - **Splits** ranges when acknowledgment status differs + /// - **Extends** existing ranges when new bad blocks are adjacent + /// - **Limits** individual ranges to 512 sectors maximum (BB_MAX_LEN) + /// + /// Please see [C documentation] for details. + /// + /// # Performance + /// + /// Executes in O(n) time where n is number of entries in the bad bloc= k table. + /// + /// # Returns + /// + /// * `Ok(())` - Bad blocks were successfully recorded + /// * `Err(ENOMEM)` - Insufficient space in bad blocks table (table fu= ll) + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rust + /// # use kernel::block::badblocks::{BadBlocks, BlockStatus}; + /// # use kernel::prelude::*; + /// + /// let bad_blocks =3D KBox::pin_init(BadBlocks::new(true), GFP_KERNEL= )?; + /// + /// // Mark sectors 1000-1009 as bad (unacknowledged) + /// bad_blocks.set_bad(1000..1010, false)?; + /// + /// // Mark a single sector as bad and acknowledged + /// bad_blocks.set_bad(2000..2001, true)?; + /// + /// // Verify the bad blocks are recorded + /// assert!(matches!(bad_blocks.check(1000..1010), BlockStatus::Unackn= owledged(_))); + /// assert!(matches!(bad_blocks.check(2000..2001), BlockStatus::Acknow= ledged(_))); + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + /// + /// Range merging behavior: + /// + /// ```rust + /// # use kernel::block::badblocks::{BadBlocks, BlockStatus}; + /// # use kernel::prelude::*; + /// + /// let bad_blocks =3D KBox::pin_init(BadBlocks::new(true), GFP_KERNEL= )?; + /// + /// // Add adjacent ranges with same acknowledgment status + /// bad_blocks.set_bad(100..105, false)?; // Sectors 100-104 + /// bad_blocks.set_bad(105..108, false)?; // Sectors 105-107 + /// + /// // These will be merged into a single range 100-107 + /// match bad_blocks.check(100..108) { + /// BlockStatus::Unacknowledged(range) =3D> { + /// assert_eq!(range.start, 100); + /// assert_eq!(range.end, 108); + /// }, + /// _ =3D> panic!("Expected unacknowledged bad blocks"), + /// } + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + /// + /// Handling acknowledgment conflicts: + /// + /// ```rust + /// # use kernel::block::badblocks::{BadBlocks, BlockStatus}; + /// # use kernel::prelude::*; + /// + /// let bad_blocks =3D KBox::pin_init(BadBlocks::new(true), GFP_KERNEL= )?; + /// + /// // Mark range as unacknowledged + /// bad_blocks.set_bad(200..210, false)?; + /// + /// // Acknowledge part of the range (will split) + /// bad_blocks.set_bad(205..208, true)?; + /// + /// // Now we have: unack[200-204], ack[205-207], unack[208-209] + /// assert!(matches!(bad_blocks.check(200..205), BlockStatus::Unacknow= ledged(_))); + /// assert!(matches!(bad_blocks.check(205..208), BlockStatus::Acknowle= dged(_))); + /// assert!(matches!(bad_blocks.check(208..210), BlockStatus::Unacknow= ledged(_))); + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + /// + /// [C documentation]: srctree/block/badblocks.c + pub fn set_bad(&self, range: impl RangeBounds, acknowledged: bool= ) -> Result { + let range =3D Self::range(range); + + // SAFETY: By type invariant `self.blocks` is valid. The C function + // `badblocks_set` handles synchronization internally. + unsafe { + bindings::badblocks_set( + self.blocks.get(), + range.start, + range.end - range.start, + if acknowledged { 1 } else { 0 }, + ) + } + .then_some(()) + .ok_or(ENOMEM) + } + + /// Marks a range of sectors as good. + /// + /// Removes a contiguous range of sectors from the bad blocks tracker, + /// indicating that these sectors are now reliable for I/O operations. + /// This is typically used after bad sectors have been repaired, remap= ped, + /// or determined to be false positives. + /// + /// # Parameters + /// + /// - `range` - The range of sectors to mark as good. + /// + /// # Behavior + /// + /// The implementation handles various scenarios automatically: + /// - **Complete removal**: If the range exactly matches a bad block r= ange, it's removed + /// entirely. + /// - **Partial removal**: If the range partially overlaps, the bad bl= ock range is split or + /// trimmed. + /// - **No effect**: If the range doesn't overlap any bad blocks, the = operation succeeds without + /// changes. + /// - **Range splitting**: If the cleared range is in the middle of a = bad block range, it may + /// split the range in two. + /// + /// # Performance + /// + /// Executes in O(n) time where n is the number of entries in the bad = blocks table. + /// + /// # Returns + /// + /// * `Ok(())` - Sectors were successfully marked as good (or were alr= eady good) + /// * `Err(EINVAL)` - Operation failed (typically due to table constra= ints) + /// + /// # Examples + /// + /// Basic usage after repair: + /// + /// ```rust + /// # use kernel::block::badblocks::{BadBlocks, BlockStatus}; + /// # use kernel::prelude::*; + /// + /// let bad_blocks =3D KBox::pin_init(BadBlocks::new(true), GFP_KERNEL= )?; + /// + /// // Mark some sectors as bad initially + /// bad_blocks.set_bad(100..110, false)?; + /// assert!(matches!(bad_blocks.check(100..110), BlockStatus::Unacknow= ledged(_))); + /// + /// // After successful repair, mark them as good + /// bad_blocks.set_good(100..110)?; + /// assert!(matches!(bad_blocks.check(100..110), BlockStatus::None)); + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + /// + /// Partial clearing: + /// + /// ```rust + /// # use kernel::block::badblocks::{BadBlocks, BlockStatus}; + /// # use kernel::prelude::*; + /// + /// let bad_blocks =3D KBox::pin_init(BadBlocks::new(true), GFP_KERNEL= )?; + /// + /// // Mark a large range as bad + /// bad_blocks.set_bad(200..220, false)?; + /// + /// // Clear only the middle portion + /// bad_blocks.set_good(205..215)?; // Clear sectors 205-214 + /// + /// // Now we have bad blocks at the edges: 200-204 and 215-219 + /// assert!(matches!(bad_blocks.check(200..205), BlockStatus::Unacknow= ledged(_))); + /// assert!(matches!(bad_blocks.check(205..215), BlockStatus::None)); + /// assert!(matches!(bad_blocks.check(215..220), BlockStatus::Unacknow= ledged(_))); + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + /// + /// Safe clearing of potentially good sectors: + /// + /// ```rust + /// # use kernel::block::badblocks::{BadBlocks, BlockStatus}; + /// # use kernel::prelude::*; + /// + /// let bad_blocks =3D KBox::pin_init(BadBlocks::new(true), GFP_KERNEL= )?; + /// + /// // It's safe to clear sectors that were never marked as bad + /// bad_blocks.set_good(1000..1100)?; // No-op, but succeeds + /// assert!(matches!(bad_blocks.check(1000..1100), BlockStatus::None)); + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + pub fn set_good(&self, range: impl RangeBounds) -> Result { + let range =3D Self::range(range); + // SAFETY: By type invariant `self.blocks` is valid. The C function + // `badblocks_clear` handles synchronization internally. + unsafe { + bindings::badblocks_clear(self.blocks.get(), range.start, rang= e.end - range.start) + } + .then_some(()) + .ok_or(EINVAL) + } + + // Transform a `RangeBounds` to start included end excluded range. + fn range(range: impl RangeBounds) -> Range { + let start =3D match range.start_bound() { + core::ops::Bound::Included(start) =3D> *start, + core::ops::Bound::Excluded(start) =3D> start + 1, + core::ops::Bound::Unbounded =3D> u64::MIN, + }; + + let end =3D match range.end_bound() { + core::ops::Bound::Included(end) =3D> end + 1, + core::ops::Bound::Excluded(end) =3D> *end, + core::ops::Bound::Unbounded =3D> u64::MAX, + }; + + start..end + } + + /// Checks if a range of sectors contains any bad blocks. + /// + /// Examines the specified sector range to determine if it contains an= y sectors + /// that have been marked as bad. This is typically called before perf= orming I/O + /// operations to avoid accessing defective sectors. The check uses se= qlocks to + /// ensure consistent reads even under concurrent modifications. + /// + /// # Parameters + /// + /// - `range` - The range of sectors to check (supports any type imple= menting + /// `RangeBounds`). + /// + /// # Returns + /// + /// Returns a [`BlockStatus`] indicating the state of the checked rang= e: + /// + /// - `BlockStatus::None` - No bad blocks found in the specified range. + /// - `BlockStatus::Acknowledged(range)` - Contains acknowledged bad b= locks. + /// - `BlockStatus::Unacknowledged(range)` - Contains unacknowledged b= ad blocks. + /// + /// The returned range indicates the **first bad block range** encount= ered that + /// overlaps with the checked area. If multiple separate bad ranges ex= ist, only + /// the first is reported. + /// + /// # Performance + /// + /// The check operation uses binary search on the sorted bad blocks ta= ble, + /// providing O(log n) lookup time where n is the number of bad block = ranges. + /// + /// # Examples + /// + /// Basic checking: + /// + /// ```rust + /// # use kernel::block::badblocks::{BadBlocks, BlockStatus}; + /// # use kernel::prelude::*; + /// + /// let bad_blocks =3D KBox::pin_init(BadBlocks::new(true), GFP_KERNEL= )?; + /// + /// // Initially no bad blocks + /// assert!(matches!(bad_blocks.check(0..1000), BlockStatus::None)); + /// + /// // Mark some sectors as bad + /// bad_blocks.set_bad(100..110, false)?; + /// + /// // Check various ranges + /// match bad_blocks.check(90..120) { + /// BlockStatus::Unacknowledged(range) =3D> { + /// assert_eq!(range.start, 100); + /// assert_eq!(range.end, 110); + /// pr_warn!("Found unacknowledged bad blocks: {}-{}", range.s= tart, (range.end - 1)); + /// }, + /// _ =3D> panic!("Expected bad blocks"), + /// } + /// + /// // Check range that doesn't overlap + /// assert!(matches!(bad_blocks.check(0..50), BlockStatus::None)); + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + /// + /// Handling different acknowledgment states: + /// + /// ```rust + /// # use kernel::block::badblocks::{BadBlocks, BlockStatus}; + /// # use kernel::prelude::*; + /// + /// let bad_blocks =3D KBox::pin_init(BadBlocks::new(true), GFP_KERNEL= )?; + /// + /// // Add both acknowledged and unacknowledged bad blocks + /// bad_blocks.set_bad(100..105, true)?; // Acknowledged + /// bad_blocks.set_bad(200..205, false)?; // Unacknowledged + /// + /// match bad_blocks.check(95..105) { + /// BlockStatus::Acknowledged(range) =3D> { + /// pr_info!("Acknowledged bad blocks found, can potentially r= emap: {:?}", range); + /// // Continue with remapping logic + /// }, + /// BlockStatus::Unacknowledged(range) =3D> { + /// pr_err!("Unacknowledged bad blocks found, requires attenti= on: {:?}", range); + /// // Handle as error condition + /// }, + /// BlockStatus::None =3D> { + /// // Safe to proceed with I/O + /// }, + /// } + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + /// + /// Safe I/O operation pattern: + /// + /// ```rust + /// # use kernel::block::badblocks::{BadBlocks, BlockStatus}; + /// # use kernel::prelude::*; + /// # use core::ops::RangeBounds; + /// # fn perform_sector_read(range: impl RangeBounds) -> Result<(= )> { Ok(()) } + /// + /// fn safe_read_sectors( + /// bad_blocks: &BadBlocks, + /// range: impl RangeBounds + Clone + /// ) -> Result<()> { + /// // Check for bad blocks before attempting I/O + /// match bad_blocks.check(range.clone()) { + /// BlockStatus::None =3D> { + /// // Safe to proceed with I/O operation - convert range = to + /// // start/count for legacy function. + /// perform_sector_read(range) + /// }, + /// BlockStatus::Acknowledged(range) =3D> { + /// pr_warn!("I/O intersects acknowledged bad blocks: {:?}= ", range); + /// // Potentially remap or skip bad sectors + /// Err(EIO) + /// }, + /// BlockStatus::Unacknowledged(range) =3D> { + /// pr_err!("I/O intersects unacknowledged bad blocks: {:?= }", range); + /// // Treat as serious error + /// Err(EIO) + /// }, + /// } + /// } + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + pub fn check(&self, range: impl RangeBounds) -> BlockStatus { + let mut first_bad =3D 0; + let mut bad_count =3D 0; + let range =3D Self::range(range); + + // SAFETY: By type invariant `self.blocks` is valid. `first_bad` a= nd + // `bad_count` are valid mutable references The C function + // `badblocks_check` handles synchronization internally. + let ret =3D unsafe { + bindings::badblocks_check( + self.blocks.get(), + range.start, + range.end - range.start, + &mut first_bad, + &mut bad_count, + ) + }; + + match ret { + 0 =3D> BlockStatus::None, + 1 =3D> BlockStatus::Acknowledged(first_bad..first_bad + bad_co= unt), + -1 =3D> BlockStatus::Unacknowledged(first_bad..first_bad + bad= _count), + _ =3D> { + debug_assert!(false, "Illegal return value from `badblocks= _check`"); + BlockStatus::None + } + } + } + + /// Formats bad blocks information into a human-readable string. + /// + /// Exports the current bad blocks table to a text representation suit= able + /// for display via sysfs. The output format shows each bad block range + /// with sector numbers and acknowledgment status. + /// + /// # Parameters + /// + /// - `page` - A page-sized buffer to write the formatted output into. + /// - `show_unacknowledged` - Whether to include unacknowledged bad bl= ocks in output. + /// - `true`: Shows both acknowledged and unacknowledged bad blocks + /// - `false`: Shows only acknowledged bad blocks + /// + /// # Output Format + /// + /// The output consists of space-separated entries, each representing = a bad block range: + /// - Format: `start_sector length [acknowledgment_status]` + /// - Acknowledged blocks: Just sector and length (e.g., "100 10") + /// - Unacknowledged blocks: Sector, length, and "u" suffix (e.g., "20= 0 5 u") + /// + /// # Returns + /// + /// Returns the number of bytes written to the buffer, or a negative v= alue on error. + /// The returned length can be used to extract the valid portion of th= e buffer. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rust + /// # use kernel::block::badblocks::{BadBlocks, BlockStatus}; + /// # use kernel::prelude::*; + /// # use kernel::page::PAGE_SIZE; + /// + /// let bad_blocks =3D KBox::pin_init(BadBlocks::new(true), GFP_KERNEL= )?; + /// let mut page =3D [0u8; PAGE_SIZE]; + /// + /// // Add some bad blocks + /// bad_blocks.set_bad(100..110, true)?; // Acknowledged + /// bad_blocks.set_bad(200..205, false)?; // Unacknowledged + /// + /// // Show all bad blocks (including unacknowledged) + /// let len =3D bad_blocks.show(&mut page, true); + /// if len > 0 { + /// let output =3D core::str::from_utf8(&page[..len as usize]).unw= rap_or(""); + /// pr_info!("Bad blocks: {}", output); + /// // Output might be: "100 10 200 5 u" + /// } + /// # Ok::<(), kernel::error::Error>(()) + /// ``` + pub fn show(&self, page: &mut [u8; PAGE_SIZE], show_unacknowledged: bo= ol) -> isize { + // SAFETY: By type invariant `self.blocks` is valid. The C function + // `badblocks_show` handles synchronization internally. + // `page.as_mut_ptr()` returns a valid pointer to a PAGE_SIZE buff= er. + // The C function will not write beyond the provided buffer size. + unsafe { + bindings::badblocks_show( + self.blocks.get(), + page.as_mut_ptr(), + if show_unacknowledged { 1 } else { 0 }, + ) + } + } +} + +#[pinned_drop] +impl PinnedDrop for BadBlocks { + fn drop(self: Pin<&mut Self>) { + // SAFETY: We do not move out of `self` before it is dropped. + let this =3D unsafe { self.get_unchecked_mut() }; + // SAFETY: By type invariant `this.blocks` is valid. `badblocks_ex= it` is + // safe to call during destruction and will properly clean up allo= cated + // resources. + unsafe { bindings::badblocks_exit(this.blocks.get()) }; + } +} + +// SAFETY: `BadBlocks` can be safely dropped from other threads. +unsafe impl Send for BadBlocks {} + +// SAFETY: All `BadBlocks` methods use internal synchronization. +unsafe impl Sync for BadBlocks {} + +/// Status of a sector range after checking for bad blocks. +/// +/// This enum represents the result of checking a sector range against the= bad blocks +/// table. It distinguishes between ranges with no bad blocks, ranges with= acknowledged +/// bad blocks, and ranges with unacknowledged bad blocks. +/// +/// # Examples +/// +/// ```rust +/// # use kernel::block::badblocks::{BadBlocks, BlockStatus}; +/// # use kernel::prelude::*; +/// # use core::ops::{Range, RangeBounds}; +/// # fn perform_io(range: impl RangeBounds) -> Result<()> { Ok(()) } +/// # fn remap_and_retry(io_range: impl RangeBounds, bad_range: Range= ) +/// # -> Result<()> { Ok(()) } +/// +/// fn handle_io_request(bad_blocks: &BadBlocks, range: impl RangeBounds + Clone) +/// -> Result<()> +/// { +/// match bad_blocks.check(range.clone()) { +/// BlockStatus::None =3D> { +/// // Safe to proceed with I/O - convert range to start/count= for legacy function +/// perform_io(range) +/// }, +/// BlockStatus::Acknowledged(bad_range) =3D> { +/// pr_warn!("I/O overlaps acknowledged bad blocks: {:?}", bad= _range); +/// // Attempt remapping or alternative strategy +/// remap_and_retry(range, bad_range) +/// }, +/// BlockStatus::Unacknowledged(bad_range) =3D> { +/// pr_err!("I/O overlaps unacknowledged bad blocks: {:?}", ba= d_range); +/// // Treat as serious error +/// Err(EIO) +/// }, +/// } +/// } +/// # Ok::<(), kernel::error::Error>(()) +/// ``` +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum BlockStatus { + /// No bad blocks found in the checked range. + None, + /// The range contains acknowledged bad blocks. + /// + /// The contained range represents the first bad block + /// range encountered. + Acknowledged(Range), + /// The range contains unacknowledged bad blocks that need attention. + /// + /// The contained range represents the boundaries of the first bad blo= ck + /// range encountered. + Unacknowledged(Range), +} --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 4F76B2DE6E3; Sun, 15 Feb 2026 23:43:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198992; cv=none; b=fInowoPofPcfTxzTMC/mrJzgoF9EZAFt9b97cyv7S2UXv5ULNXX60SYYzYp5IgJTG+PvHgoP2m09CGLxLqzKg4sAFw7hfkCk5II8FPi61vUX+CM/5l7zhV7Z2sB0xAIQ0S/zUyMT2kR4zii7HPUrQaSi8YXLGslgspH6JDR2rLg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198992; c=relaxed/simple; bh=uZ4trczFY+LKih7mhcNOBkSAbmZSPt6rd19ccjIIzfA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=JGwwxa/yjYLp23SVl1RrA0tREN30ipn3JnI/bC2a/B2Z8Em2i3DJSK/tS9RPTkHneYef5MVrBb5/Px46RS4a1BP5bq2DEn1KX7ZsmD0D+j1a0oX3a49UqhBFSd1gHWiIi5voHAy1YR4Y4ouAh59O3QAGZdtvsVi3K9cAGDMgg0E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=D0O2olDr; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="D0O2olDr" Received: by smtp.kernel.org (Postfix) with ESMTPSA id B767EC4CEF7; Sun, 15 Feb 2026 23:43:07 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198992; bh=uZ4trczFY+LKih7mhcNOBkSAbmZSPt6rd19ccjIIzfA=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=D0O2olDrQqf3mr+n0B/sizmoyVYotgDI8QW2d+giikDSGklyaqt66EKQtPxwQIHK/ /+eyggsDhpVYkE72Jwu4mAsgs4zHHlNPa1861MK9HOJ1iwhcjMHCrLJMeMYNvSRw2W OOLrdq3ip4v/UEcJRCLuAyUQacedRcmMzdJtcdXWC87OxZOrpFkg9TVuD7sKdFVGii AcqxnDafvoCAt9hPcxtgYbwpS1W/0ahnc7KKXnl8HywgFP8pJ3/0R7IZnNjR/usFXg 8dLovfmM0CXcU6Y7KCfp9NiSNFzMGU6GofcyjKUnz1W8vPas/jJuv4/FTg0Mp9H9mR hzysrmKtP6VHQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:12 +0100 Subject: [PATCH 25/79] block: rust: mq: add Request::end() method for custom status codes 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: <20260216-rnull-v6-19-rc5-send-v1-25-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1367; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=uZ4trczFY+LKih7mhcNOBkSAbmZSPt6rd19ccjIIzfA=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklgyY61m40HV541SUFIPkQbPNupRrSnrD107/ ev7FWE4kAWJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYMgAKCRDhuBo+eShj d8J6EADFFGcykNftC/0A3bvASuRhPdNg8UE92icnkqqo0iumqG8bAA2yHrZF63Go/+zkqglK3R3 l/mR8piOTJFTaKLIbzeQ+Mlo1gKcErgI4mXi8hY+3UVPfaywVmNojac9uq3zeLgT74ZvJV2BcIt PoryU4zS55f7EaVjqCtpgMpQjiWMNu4RQ+tYSczZmLC3EQlt7GDw+q8bxW0uT7LTDBfkNpFqlB7 uQt3HWq0k4k9+cGZg7jWMla1dEiZ7tvnexQPWqfogiWAA4YDsiq/EPsbZXjCipB9jjM7qv8Zaev 1KVsjOtOFpH09C1G6bbEYV/yJNkxKbqbY2yEhjlTt1l+Gc6N934CuuHoTKFY/iAgcMvSfmFT3E5 K5tK+mPp/R7dcoh7/x7oOmJ12nETq2fQ+iPI9JwiaHuo+gKBrLzAGzLKtQdjOI5tBJK8LEHrMne EQ99RA6D8TAzgTufRXWNCZkfwNTs4lYrWHGUe3P3aoKAqNjqu196xvKG0vSJU1DjJtdS5Tq6FeC ZhyfWV50Q64PNQwYpqX3ivNHlfq4Bv3te2MrSUeAhjMNhySjA+XLzBiDOPWvd2OoncP9ynftR8X RUV+E457grGB/zBi/HAkuzdrb8RCb1OzjWCaaWBE7WkG/VTILheDTq4Ayf+O3aP1uspkMNT3Vc5 KHc8UW1mp+karKQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add end() method to Request that accepts a custom status code parameter, refactoring end_ok() to use it with BLK_STS_OK. Signed-off-by: Andreas Hindborg Reviewed-by: Alice Ryhl --- rust/kernel/block/mq/request.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index 8fe7e126c522a..38289b9f966fa 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -297,13 +297,18 @@ pub(crate) unsafe fn start_unchecked(&mut self) { =20 /// Notify the block layer that the request has been completed without= errors. pub fn end_ok(self) { + self.end(bindings::BLK_STS_OK) + } + + /// Notify the block layer that the request has been completed. + pub fn end(self, status: u8) { let request_ptr =3D self.0.get().cast(); core::mem::forget(self); // SAFETY: By type invariant, `this.0` was a valid `struct request= `. The // existence of `self` guarantees that there are no `ARef`s pointi= ng to // this request. Therefore it is safe to hand it back to the block // layer. - unsafe { bindings::blk_mq_end_request(request_ptr, bindings::BLK_S= TS_OK) }; + unsafe { bindings::blk_mq_end_request(request_ptr, status) }; } } =20 --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 CEA222DECA1; Sun, 15 Feb 2026 23:42:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198923; cv=none; b=aFlv2owfZKDoE+rjaoOIQPuDrn+NNDk/D6d7DEH0+2BEA3/fcsstLwOrKRWMclef132hq0ssmh3JTotQJe6h4s84RI9iruznNtoKXmomcEwKgFa60OUQ0wY7u5bA48zGjtN25uy2vXVEpopTF6Us5U4fak/6rfKJ6+O+yXv2jmc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198923; c=relaxed/simple; bh=EF8S7FmFl3vSFKxHiqAFcxC7opaobmJHHkh7NyfesPA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=bN2El3oXd3DI7Ft/TJctppf/cqC00ebY6bpJCA3Ak7x8SZoTCKOfDR4RGZCfqa3tnIdGqxEALVxQAR4HgZVh9rwcgZGoP0soSDRiX1nrBHBI8xzBez2WYRAAJljyQor0EvgzMxquGyfJYVMs4BP2T837qHMHR+nxxtcQ8C/nfng= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ohVjIgyq; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ohVjIgyq" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7A76FC19422; Sun, 15 Feb 2026 23:41:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198923; bh=EF8S7FmFl3vSFKxHiqAFcxC7opaobmJHHkh7NyfesPA=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=ohVjIgyq+9ZcKRmy85+VAIPos6ieoFUf7IrfB0vpWBkF4UmMEIJGcb7X3KUIu7szr uQp+/uHB89+wk4oLqScNs3x/4AKs9vyO6WBUXGIMKpZSWmh10K9TIK3nFv83V6mY+f 3FLGDsu8oRMyp6Ekxmwb+d3nnaraSnjz1U6/+9R3KKI5A3ZJKqrxQYYe8I0QyyaOsH PjD9BxtoBixN2sAf9tLYSkyhkWl0dsqPZo9gNpNXbDasvkkhxcxoXBZ+egOFkckO3G yQo4+hK0C0YQwHYvQait1QpFBjc1+x7ds+Uh4bCb5YlMzksGZPZ4gZQjLWNb85wOlQ M43NGEbAZPdTQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:13 +0100 Subject: [PATCH 26/79] block: rnull: add badblocks support 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: <20260216-rnull-v6-19-rc5-send-v1-26-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=8577; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=EF8S7FmFl3vSFKxHiqAFcxC7opaobmJHHkh7NyfesPA=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklgzCA5NKN0hlzRJ+B3wI8OXUsh2VHHxSR288 UF5GphVh4OJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYMwAKCRDhuBo+eShj d2wvEACO+eazDMVAJLN0GdcSEnzfv4GHoA4o8dfxM15esOo7Welb8VxAhDC2XXVg3Qb7tsdG3Wv yQ0qA8AvvWvnsdwXiElwFD14hB509xw5X7VZyEWLTmeUB7ZsKA0I/YSA3XSZr7+/C2e78nOPsf8 faDFaVBXfb/pXA+Pm9YHNn6j1mlKBfFj2u0j6M8Ei+rvZRxm0KzTQHa8inblXNOXFxGNYkyAwdv eEqQ6LLNz1AspU0lNhC4rtn/DBzN9v6MHJCEqUtesRgrxoNd41B2A6h2NZrmX63hSwJEFHzatyj UXAZF7luejDKv4T2xgWsN/HPbuxRiRam9mT0fVxZO4zOtjqd+vbr6VteXCRLC1fQNytkvrOxX/0 V46/1Qr9layTOdMPU6fF6hEDeV4wiJNImaicjCBNlEzmEY0g0sTjZ/xCq6XnQPeN5hVdI1MM3dl tKeIrYpzRDIPwrPQtoJ3JMwaIUMDeZhssAut5Dxu01PGoX2ayRd4Og8pJuVAJ61tVHPX8goJDkY PGAsDNIqyJybynjO2OFs0QdxeU8ali0gl3ivlIU3ycJ6APNzICDdHDoViaKV1fNPtx/FoOWxoPi 2ugP9oVP+5W9lWVpGYFFkxD8nz+hAsVx6LCIpFfvXGrVhUWftQUDYs0zQ9sArU2lynbpttaYx0S o2ykBH3e1UR4mjg== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add badblocks support to the rnull driver with a configfs interface for managing bad sectors. - Configfs attribute for adding/removing bad blocks via "+start-end" and "-start-end" syntax. - Request handling that checks for bad blocks and returns IO errors. - Updated request completion to handle error status properly. The badblocks functionality is disabled by default and is enabled when first bad block is added. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 63 +++++++++++++++++++++++++++++++++++++= +--- drivers/block/rnull/rnull.rs | 46 ++++++++++++++++++++++++++---- 2 files changed, 100 insertions(+), 9 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 18e32a87673aa..61a76addf468b 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -6,9 +6,12 @@ }; use kernel::{ bindings, - block::mq::gen_disk::{ - GenDisk, - GenDiskBuilder, // + block::{ + badblocks::BadBlocks, + mq::gen_disk::{ + GenDisk, + GenDiskBuilder, // + }, // }, c_str, configfs::{ @@ -27,7 +30,10 @@ kstrtobool_bytes, CString, // }, - sync::Mutex, + sync::{ + Arc, + Mutex, // + }, time, // }; use macros::{ @@ -96,6 +102,7 @@ fn make_group( home_node: 9, discard: 10, no_sched:11, + badblocks: 12, ], }; =20 @@ -118,6 +125,7 @@ fn make_group( home_node: bindings::NUMA_NO_NODE, discard: false, no_sched: false, + bad_blocks: Arc::pin_init(BadBlocks::new(false), GFP_K= ERNEL)?, }), }), core::iter::empty(), @@ -177,6 +185,7 @@ struct DeviceConfigInner { home_node: i32, discard: bool, no_sched: bool, + bad_blocks: Arc, } =20 #[vtable] @@ -212,6 +221,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { home_node: guard.home_node, discard: guard.discard, no_sched: guard.no_sched, + bad_blocks: guard.bad_blocks.clone(), })?); guard.powered =3D true; } else if guard.powered && !power_op { @@ -367,3 +377,48 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { Ok(()) } } + +#[vtable] +impl configfs::AttributeOperations<12> for DeviceConfig { + type Data =3D DeviceConfig; + + fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result { + let ret =3D this.data.lock().bad_blocks.show(page, false); + if ret < 0 { + Err(Error::from_errno(ret as c_int)) + } else { + Ok(ret as usize) + } + } + + fn store(this: &DeviceConfig, page: &[u8]) -> Result { + // This attribute can be set while device is powered. + + for line in core::str::from_utf8(page)?.lines() { + let mut chars =3D line.chars(); + match chars.next() { + Some(sign @ '+' | sign @ '-') =3D> { + if let Some((start, end)) =3D chars.as_str().split_onc= e('-') { + let start: u64 =3D start.parse().map_err(|_| EINVA= L)?; + let end: u64 =3D end.parse().map_err(|_| EINVAL)?; + + if start > end { + return Err(EINVAL); + } + + this.data.lock().bad_blocks.enable(); + + if sign =3D=3D '+' { + this.data.lock().bad_blocks.set_bad(start..=3D= end, true)?; + } else { + this.data.lock().bad_blocks.set_good(start..= =3Dend)?; + } + } + } + _ =3D> return Err(EINVAL), + } + } + + Ok(()) + } +} diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index b0008e2f9c398..861392c5b5841 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -9,6 +9,7 @@ bindings, block::{ self, + badblocks::{self, BadBlocks}, bio::Segment, mq::{ self, @@ -37,6 +38,10 @@ str::CString, sync::{ aref::ARef, + atomic::{ + ordering, + Atomic, // + }, Arc, Mutex, // }, @@ -155,6 +160,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { home_node: *module_parameters::home_node.value(), discard: *module_parameters::discard.value() !=3D 0, no_sched: *module_parameters::no_sched.value() !=3D 0, + bad_blocks: Arc::pin_init(BadBlocks::new(false), GFP_K= ERNEL)?, })?; disks.push(disk, GFP_KERNEL)?; } @@ -181,6 +187,7 @@ struct NullBlkOptions<'a> { home_node: i32, discard: bool, no_sched: bool, + bad_blocks: Arc, } struct NullBlkDevice; =20 @@ -198,6 +205,7 @@ fn new(options: NullBlkOptions<'_>) -> Result> { home_node, discard, no_sched, + bad_blocks, } =3D options; =20 let mut flags =3D mq::tag_set::Flags::default(); @@ -226,6 +234,7 @@ fn new(options: NullBlkOptions<'_>) -> Result> { completion_time, memory_backed, block_size: block_size.into(), + bad_blocks, }), GFP_KERNEL, )?; @@ -327,6 +336,16 @@ fn transfer( } Ok(()) } + + fn end_request(rq: Owned>) { + let status =3D rq.data_ref().error.load(ordering::Relaxed); + rq.data_ref().error.store(0, ordering::Relaxed); + + match status { + 0 =3D> rq.end_ok(), + _ =3D> rq.end(bindings::BLK_STS_IOERR), + } + } } =20 const _CHEKC_STATUS_WIDTH: () =3D build_assert!((PAGE_SIZE >> SECTOR_SHIFT= ) <=3D 64); @@ -373,12 +392,14 @@ struct QueueData { completion_time: Delta, memory_backed: bool, block_size: u64, + bad_blocks: Arc, } =20 #[pin_data] struct Pdu { #[pin] timer: kernel::time::hrtimer::HrTimer, + error: Atomic, } =20 impl HrTimerCallback for Pdu { @@ -408,6 +429,7 @@ impl Operations for NullBlkDevice { fn new_request_data() -> impl PinInit { pin_init!(Pdu { timer <- kernel::time::hrtimer::HrTimer::new(), + error: Atomic::new(0), }) } =20 @@ -417,6 +439,19 @@ fn queue_rq( mut rq: Owned>, _is_last: bool, ) -> Result { + if queue_data.bad_blocks.enabled() { + let start =3D rq.sector(); + let end =3D start + u64::from(rq.sectors()); + if !matches!( + queue_data.bad_blocks.check(start..end), + badblocks::BlockStatus::None + ) { + rq.data_ref().error.store(1, ordering::Relaxed); + } + } + + // TODO: Skip IO if bad block. + if queue_data.memory_backed { let tree =3D &queue_data.tree; let command =3D rq.command(); @@ -437,7 +472,7 @@ fn queue_rq( } =20 match queue_data.irq_mode { - IRQMode::None =3D> rq.end_ok(), + IRQMode::None =3D> Self::end_request(rq), IRQMode::Soft =3D> mq::Request::complete(rq.into()), IRQMode::Timer =3D> { OwnableRefCounted::into_shared(rq) @@ -451,9 +486,10 @@ fn queue_rq( fn commit_rqs(_queue_data: Pin<&QueueData>) {} =20 fn complete(rq: ARef>) { - OwnableRefCounted::try_from_shared(rq) - .map_err(|_e| kernel::error::code::EIO) - .expect("Failed to complete request") - .end_ok(); + Self::end_request( + OwnableRefCounted::try_from_shared(rq) + .map_err(|_e| kernel::error::code::EIO) + .expect("Failed to complete request"), + ) } } --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 B0FA52DC772; Sun, 15 Feb 2026 23:42:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198958; cv=none; b=Tz/ptuqrPYenRrv7RpPQfZcX8y3EC70FfJfTP2Ga/Kk8ef+HP3kmk5nyTfaIS0ZUXFh5/6qmMzIPdTOTy5hU7p5NgSUl6ql/mHe8EpNNIHTuNX5G1JdAETv68Sbo88h/mfFLYSYHUPpFYbYyZXyZq/h1s7WML8y+ttdgeIybYSU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198958; c=relaxed/simple; bh=mPaJdnA08bz6YzukufDATJZJlX5Z7yfcsTpWSx7drrU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=fpgTflLnDY+UkInjcqtvlGpevvPoEIeZ1nLhZT2oyjkNZjFFgN+3LWGNZJOnmPsxT0sJ2SC85s39jC9feaowsatmBkZ0fAhNov9hpWO8eWmTItGPr4fMy3BP9JeryQe82xknFZY/4d/uE430vZz+A1AvH3j06NgxbqHV27tYcyA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=uLg4izf1; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="uLg4izf1" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 528A3C4CEF7; Sun, 15 Feb 2026 23:42:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198958; bh=mPaJdnA08bz6YzukufDATJZJlX5Z7yfcsTpWSx7drrU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=uLg4izf1PM8gqhPDfHqZL929CRE2DTJ/KxnrvYokJWM7vmx++wtifuDiKP7IDH+/q 15hyyd/bcNJWdAMht6NQ8O5WHgwhmDA1mp2jtL4Vmyj4bL6IvYUnYAtcnPdlIWcByl jkz8KNty6uFOR0qPcf+m7WbDyPeonycK4debT3X+sKdLT3r9yW5jYep4sftXPAkLX2 ISpV0T0YhSXNjeTNxm2mBQVGXh3Y5U9CA7pIqz+Qvp72cZZUJH9CIEbzv0csjVaZin bDW6RU2bn/zpJ/0f0Bj2cQya7kaJyOWTWoOO0xB7x1e92pbEjzYGH2yDK9aCiAtPDy nFr65qRtcN8qA== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:14 +0100 Subject: [PATCH 27/79] block: rnull: add badblocks_once support 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: <20260216-rnull-v6-19-rc5-send-v1-27-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=5676; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=mPaJdnA08bz6YzukufDATJZJlX5Z7yfcsTpWSx7drrU=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklg0gpsXFXxzc1lHaiAxCsn05VWspInODyhcx +COma0+h1WJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYNAAKCRDhuBo+eShj d6NdD/9VzqziYOZORL+w+EyvJ+DjwGX57mT4SohYFt5k0+dyu6GNOpWEPwoiE96dQtqQncyWvEt TXtKlLSGDSZ3yfpf0ID75MqiBwnUtOmOtLS4R360hVOfPSs64mvN4lR3M9EcXoH+lgZFxcADFuv LSM3jC+mSrMj28AzbquDyrPLLDD+wABbXX67Lf2bVrNUajlK9T+8J0arhxvmY4sqF61cF0vDScq VtvnB61d8V02U0oYkIXlpr18oitcK4jHHzr+6R0Vh8WN74GW2fpuvX5P32RnHWVaIWR+qVXM+m9 OnwVifaDcOOR2QwKTFF/G5KoPn7H9Fp4hpDK9ieBVEioELGh+bep84Nw/VbqIIzLhtePNuNYH5+ zais2/7lYmLWqa/gtJZBIdq4IiA8sxlmDpurdga6YlezkCf/zSFIpTmuZE7sBCNg2AlQ6NCXtud 2CeNoEMzunwPewSKt7+W3bvaJvri93/eoFj16eH6AfhBLJoIiT048CTEjBNzYDjFE7QlRcxxMvM EtLAZvsvI3Pd+0i1zzB6WuQkVsAupbWzVf5k+gcbWy1pPAWaBeK/mSzAPKMYq+IS+g/C40POev6 j/y+QHdq2/G3Pfnqjis+7XJglP1xf4eBE6wUJesr/Jd3tB4VA5fklhxUZVq210VUyUTKSJXPxEN 1Vz7GfFZyKM7Kew== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add support for the badblocks_once feature, which automatically clears bad blocks after they are encountered during I/O operations. This matches the functionality in the C null_blk driver. When badblocks_once is enabled: - Bad blocks are checked during I/O requests as usual - If a bad block is encountered, the I/O is marked as failed - The bad block range is immediately cleared from the bad blocks table - Subsequent I/O to the same sectors will succeed This feature is useful for testing scenarios where bad blocks are transient or where devices can recover from bad sectors after a single access attempt. The feature is configurable via the configfs badblocks_once attribute and disabled by default, maintaining compatibility with existing behavior. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 31 +++++++++++++++++++++++++++++++ drivers/block/rnull/rnull.rs | 21 +++++++++++++++------ 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 61a76addf468b..a39691b39e374 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -103,6 +103,7 @@ fn make_group( discard: 10, no_sched:11, badblocks: 12, + badblocks_once: 13, ], }; =20 @@ -126,6 +127,7 @@ fn make_group( discard: false, no_sched: false, bad_blocks: Arc::pin_init(BadBlocks::new(false), GFP_K= ERNEL)?, + bad_blocks_once: false, }), }), core::iter::empty(), @@ -186,6 +188,7 @@ struct DeviceConfigInner { discard: bool, no_sched: bool, bad_blocks: Arc, + bad_blocks_once: bool, } =20 #[vtable] @@ -222,6 +225,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { discard: guard.discard, no_sched: guard.no_sched, bad_blocks: guard.bad_blocks.clone(), + bad_blocks_once: guard.bad_blocks_once, })?); guard.powered =3D true; } else if guard.powered && !power_op { @@ -422,3 +426,30 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { Ok(()) } } + +#[vtable] +impl configfs::AttributeOperations<13> for DeviceConfig { + type Data =3D DeviceConfig; + + fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result { + let mut writer =3D kernel::str::Formatter::new(page); + + if this.data.lock().bad_blocks_once { + writer.write_str("1\n")?; + } else { + writer.write_str("0\n")?; + } + + Ok(writer.bytes_written()) + } + + fn store(this: &DeviceConfig, page: &[u8]) -> Result { + if this.data.lock().powered { + return Err(EBUSY); + } + + this.data.lock().bad_blocks_once =3D kstrtobool_bytes(page)?; + + Ok(()) + } +} diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 861392c5b5841..0f569c5b65f77 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -161,6 +161,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { discard: *module_parameters::discard.value() !=3D 0, no_sched: *module_parameters::no_sched.value() !=3D 0, bad_blocks: Arc::pin_init(BadBlocks::new(false), GFP_K= ERNEL)?, + bad_blocks_once: false, })?; disks.push(disk, GFP_KERNEL)?; } @@ -188,6 +189,7 @@ struct NullBlkOptions<'a> { discard: bool, no_sched: bool, bad_blocks: Arc, + bad_blocks_once: bool, } struct NullBlkDevice; =20 @@ -206,6 +208,7 @@ fn new(options: NullBlkOptions<'_>) -> Result> { discard, no_sched, bad_blocks, + bad_blocks_once, } =3D options; =20 let mut flags =3D mq::tag_set::Flags::default(); @@ -235,6 +238,7 @@ fn new(options: NullBlkOptions<'_>) -> Result> { memory_backed, block_size: block_size.into(), bad_blocks, + bad_blocks_once, }), GFP_KERNEL, )?; @@ -393,6 +397,7 @@ struct QueueData { memory_backed: bool, block_size: u64, bad_blocks: Arc, + bad_blocks_once: bool, } =20 #[pin_data] @@ -442,12 +447,16 @@ fn queue_rq( if queue_data.bad_blocks.enabled() { let start =3D rq.sector(); let end =3D start + u64::from(rq.sectors()); - if !matches!( - queue_data.bad_blocks.check(start..end), - badblocks::BlockStatus::None - ) { - rq.data_ref().error.store(1, ordering::Relaxed); - } + match queue_data.bad_blocks.check(start..end) { + badblocks::BlockStatus::None =3D> {} + badblocks::BlockStatus::Acknowledged(range) + | badblocks::BlockStatus::Unacknowledged(range) =3D> { + rq.data_ref().error.store(1, ordering::Relaxed); + if queue_data.bad_blocks_once { + queue_data.bad_blocks.set_good(range)?; + } + } + }; } =20 // TODO: Skip IO if bad block. --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 C83082E7F3A; Sun, 15 Feb 2026 23:43:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199031; cv=none; b=mTtmOl3ZMwawjQlrFZQDpbxEi9VfzrhD/PPWlJhd3FC9WQNhtPZD5j3LxSpyqYhlvZOS+oIvElZtjh6Eu254baGzmVAA0m60ugdyw2BdnvHosI2x9ze3bMbn5JC4gTstyx6Hbks3hTIqgQ4iwQf+6gx4yffSDnFAFdng4J3l2Ls= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199031; c=relaxed/simple; bh=kRUjCKtoTGv3hT8Dyq+/AHzdexRsLl9w4VNxmn7tYGw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=P3xo2Ci2So+/Ck6krUTJMkzZgPspeGFtZJXWHDLMNbKhUPfxE6RVz6NixKpZwG4XOTWMCkjzFeKDPMBr2Fh0qOKr9Uc+JstpIfmJ89RAZKhp9vPAtj2tNSdO0QOyyDuh6QQ930hfc2GaNtNEZxuMBr2ITXrWwkrCpODl725s4jA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=nLiFV3Ln; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="nLiFV3Ln" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5F166C4CEF7; Sun, 15 Feb 2026 23:43:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199031; bh=kRUjCKtoTGv3hT8Dyq+/AHzdexRsLl9w4VNxmn7tYGw=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=nLiFV3Ln/kAwVQ7jgAzUZt+QYD+bmIuFMsT+Yi1JtwFIAC4Lujy/4vUBPaUqsLg44 Pz5kKMHGBj4HSoM6TCWWWgcdWSlyV5siE4mMwFGwsawfIDi81j2DYEZsrE4SOW8Rdl ppe7QzEeYHDnYcB99OkjNje7ruTXucVP2m+fbLKPcU+O1vOp7vsr6oUador9TD7ti0 bGP/7zKouCcCjV2LzilOwSYablNh8SiboQY5agiYoMXvp6KeR3QEY9vwL3IB3Z+zdb LhX4QTTScID+Yo4Mu7zeL6I5tphcET+DJqzX7jvoaMwa5mj37bI/TCLaThjyrKluyT WIWP+70m+b0Yg== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:15 +0100 Subject: [PATCH 28/79] block: rnull: add partial I/O support for bad blocks 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: <20260216-rnull-v6-19-rc5-send-v1-28-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=10153; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=kRUjCKtoTGv3hT8Dyq+/AHzdexRsLl9w4VNxmn7tYGw=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklg1reIhnxlHbTm+/U8X/ib+aOTFikBpNjyXx 0q1mGQ6z6OJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYNQAKCRDhuBo+eShj dyK7EADGq2Yc0ZJAzDtZeBoR2EMzSt3mLhli34HJVUPQzCDsXSWLN9by/tgnavK/GIhYiLm8NbT TKEb3gE133xHSC+rJ9eBZPLMuDl3FAzvZo3Kr+jQNmHa9aBIccULynHVxfQMJyfAdtmK/fOLZV1 MAcvY4iXxSDezJhmY8n3wYlux5RCjMpMgdrZonAnYinWmQ7OOaUMJQwtC5cI0JX1dOHpq5BIBLB 6pDyObhtaC2k/70xjE5lg1t3BCNvGRMZYqOXpe7nqXgE5ad6XaMZd8ForSAMIUauJAS1gSwLH9a QxI59iQ8+X+Q4lFVcPdtr5orNUyY73x4nI7enf9Gj4pFU61AOahn49yDt79J7RHIWoQJsVdznAc waWHA973pU0gBLE7xefFVSKEpoPU7SHPdbtHFsIr/Jsyl3HtL/5D5ki3IxdkRFWg9muENuhF9La jkeWWtmv8ea992mOpNlsQJUdMW2J5TPSPNowJs3nbfDcSUnUJL6r5cMPymG9nAMHp0yZAvW5Vu3 ByHMgLf3Jy1AxQZUloIQkxT10ERnBQiH3mvaDxhPrAynoJcLvRYtMHDdlWNerehpg8UR8gwGs3i pSz2BsirisJm7OvQrguGyGuM8GJTpPXGYTQAZh+jR/aaSwYaV1HpZ2M/20xcW+6rJSe+T6BBpnB hIlDtcIOxv/pL7Q== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add bad_blocks_partial_io configuration option that allows partial I/O completion when encountering bad blocks, rather than failing the entire request. When enabled, requests are truncated to stop before the first bad block range, allowing the valid portion to be processed successfully. This improves compatibility with applications that can handle partial reads/writes. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 32 ++-------- drivers/block/rnull/rnull.rs | 125 ++++++++++++++++++++++++++++--------= ---- 2 files changed, 95 insertions(+), 62 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index a39691b39e374..c08a3cbd66f18 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -104,6 +104,7 @@ fn make_group( no_sched:11, badblocks: 12, badblocks_once: 13, + badblocks_partial_io: 14, ], }; =20 @@ -128,6 +129,7 @@ fn make_group( no_sched: false, bad_blocks: Arc::pin_init(BadBlocks::new(false), GFP_K= ERNEL)?, bad_blocks_once: false, + bad_blocks_partial_io: false, }), }), core::iter::empty(), @@ -189,6 +191,7 @@ struct DeviceConfigInner { no_sched: bool, bad_blocks: Arc, bad_blocks_once: bool, + bad_blocks_partial_io: bool, } =20 #[vtable] @@ -226,6 +229,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { no_sched: guard.no_sched, bad_blocks: guard.bad_blocks.clone(), bad_blocks_once: guard.bad_blocks_once, + bad_blocks_partial_io: guard.bad_blocks_partial_io, })?); guard.powered =3D true; } else if guard.powered && !power_op { @@ -427,29 +431,5 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { } } =20 -#[vtable] -impl configfs::AttributeOperations<13> for DeviceConfig { - type Data =3D DeviceConfig; - - fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result { - let mut writer =3D kernel::str::Formatter::new(page); - - if this.data.lock().bad_blocks_once { - writer.write_str("1\n")?; - } else { - writer.write_str("0\n")?; - } - - Ok(writer.bytes_written()) - } - - fn store(this: &DeviceConfig, page: &[u8]) -> Result { - if this.data.lock().powered { - return Err(EBUSY); - } - - this.data.lock().bad_blocks_once =3D kstrtobool_bytes(page)?; - - Ok(()) - } -} +configfs_simple_bool_field!(DeviceConfig, 13, bad_blocks_once); +configfs_simple_bool_field!(DeviceConfig, 14, bad_blocks_partial_io); diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 0f569c5b65f77..6691e5912c5c9 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -162,6 +162,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { no_sched: *module_parameters::no_sched.value() !=3D 0, bad_blocks: Arc::pin_init(BadBlocks::new(false), GFP_K= ERNEL)?, bad_blocks_once: false, + bad_blocks_partial_io: false, })?; disks.push(disk, GFP_KERNEL)?; } @@ -190,6 +191,7 @@ struct NullBlkOptions<'a> { no_sched: bool, bad_blocks: Arc, bad_blocks_once: bool, + bad_blocks_partial_io: bool, } struct NullBlkDevice; =20 @@ -209,6 +211,7 @@ fn new(options: NullBlkOptions<'_>) -> Result> { no_sched, bad_blocks, bad_blocks_once, + bad_blocks_partial_io, } =3D options; =20 let mut flags =3D mq::tag_set::Flags::default(); @@ -239,6 +242,7 @@ fn new(options: NullBlkOptions<'_>) -> Result> { block_size: block_size.into(), bad_blocks, bad_blocks_once, + bad_blocks_partial_io, }), GFP_KERNEL, )?; @@ -327,16 +331,62 @@ fn discard(tree: &Tree, mut sector: u64, sectors: u64= , block_size: u64) -> Resul } =20 #[inline(never)] - fn transfer( - command: bindings::req_op, - tree: &Tree, - sector: u64, - segment: Segment<'_>, + fn transfer(rq: &mut Owned>, tree: &Tree, sectors: u= 32) -> Result { + let mut sector =3D rq.sector(); + let end_sector =3D sector + >::into(sectors); + let command =3D rq.command(); + + for bio in rq.bio_iter_mut() { + let segment_iter =3D bio.segment_iter(); + for segment in segment_iter { + // Length might be limited by bad blocks. + let length =3D segment + .len() + .min((sector - end_sector) as u32 >> SECTOR_SHIFT); + match command { + bindings::req_op_REQ_OP_WRITE =3D> Self::write(tree, s= ector, segment)?, + bindings::req_op_REQ_OP_READ =3D> Self::read(tree, sec= tor, segment)?, + _ =3D> (), + } + sector +=3D u64::from(length) >> SECTOR_SHIFT; + + if sector >=3D end_sector { + return Ok(()); + } + } + } + Ok(()) + } + + fn handle_bad_blocks( + rq: &mut Owned>, + queue_data: &QueueData, + sectors: &mut u32, ) -> Result { - match command { - bindings::req_op_REQ_OP_WRITE =3D> Self::write(tree, sector, s= egment)?, - bindings::req_op_REQ_OP_READ =3D> Self::read(tree, sector, seg= ment)?, - _ =3D> (), + if queue_data.bad_blocks.enabled() { + let start =3D rq.sector(); + let end =3D start + u64::from(*sectors); + match queue_data.bad_blocks.check(start..end) { + badblocks::BlockStatus::None =3D> {} + badblocks::BlockStatus::Acknowledged(mut range) + | badblocks::BlockStatus::Unacknowledged(mut range) =3D> { + rq.data_ref().error.store(1, ordering::Relaxed); + + if queue_data.bad_blocks_once { + queue_data.bad_blocks.set_good(range.clone())?; + } + + if queue_data.bad_blocks_partial_io { + let block_size_sectors =3D queue_data.block_size >= > SECTOR_SHIFT; + range.start =3D align_down(range.start, block_size= _sectors); + if start < range.start { + *sectors =3D (range.start - start) as u32; + } + } else { + *sectors =3D 0; + } + } + }; } Ok(()) } @@ -398,6 +448,7 @@ struct QueueData { block_size: u64, bad_blocks: Arc, bad_blocks_once: bool, + bad_blocks_partial_io: bool, } =20 #[pin_data] @@ -426,6 +477,30 @@ impl HasHrTimer for Pdu { } } =20 +fn is_power_of_two(value: T) -> bool +where + T: core::ops::Sub, + T: core::ops::BitAnd, + T: core::cmp::PartialOrd, + T: Copy, + T: From, +{ + (value > 0u8.into()) && (value & (value - 1u8.into())) =3D=3D 0u8.into= () +} + +fn align_down(value: T, to: T) -> T +where + T: core::ops::Sub, + T: core::ops::Not, + T: core::ops::BitAnd, + T: core::cmp::PartialOrd, + T: Copy, + T: From, +{ + debug_assert!(is_power_of_two(to)); + value & !(to - 1u8.into()) +} + #[vtable] impl Operations for NullBlkDevice { type QueueData =3D Pin>; @@ -444,39 +519,17 @@ fn queue_rq( mut rq: Owned>, _is_last: bool, ) -> Result { - if queue_data.bad_blocks.enabled() { - let start =3D rq.sector(); - let end =3D start + u64::from(rq.sectors()); - match queue_data.bad_blocks.check(start..end) { - badblocks::BlockStatus::None =3D> {} - badblocks::BlockStatus::Acknowledged(range) - | badblocks::BlockStatus::Unacknowledged(range) =3D> { - rq.data_ref().error.store(1, ordering::Relaxed); - if queue_data.bad_blocks_once { - queue_data.bad_blocks.set_good(range)?; - } - } - }; - } + let mut sectors =3D rq.sectors(); =20 - // TODO: Skip IO if bad block. + Self::handle_bad_blocks(&mut rq, queue_data.get_ref(), &mut sector= s)?; =20 if queue_data.memory_backed { let tree =3D &queue_data.tree; - let command =3D rq.command(); - let mut sector =3D rq.sector(); =20 - if command =3D=3D bindings::req_op_REQ_OP_DISCARD { - Self::discard(tree, sector, rq.sectors().into(), queue_dat= a.block_size)?; + if rq.command() =3D=3D bindings::req_op_REQ_OP_DISCARD { + Self::discard(tree, rq.sector(), sectors.into(), queue_dat= a.block_size)?; } else { - for bio in rq.bio_iter_mut() { - let segment_iter =3D bio.segment_iter(); - for segment in segment_iter { - let length =3D segment.len(); - Self::transfer(command, tree, sector, segment)?; - sector +=3D u64::from(length) >> block::SECTOR_SHI= FT; - } - } + Self::transfer(&mut rq, tree, sectors)?; } } =20 --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 D010DEEC0; Sun, 15 Feb 2026 23:41:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198899; cv=none; b=rQPtRp0Xj/ySzZRlaYgx0zSePwHemPnm9d/NHvaBXsLRp7Sp9G7ReS/E/5Ph+Ybr8pTwk5gaTZfd7Qb19fXMkw50qhX0hRrRxBJnz34CY0nSj1FOsw/n760USF5ePoDfIY2RcPAo3PoMs0WJR2NJyTsBLQCbqUz0Ox8jL8jw2FI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198899; c=relaxed/simple; bh=D7D66X+gJAFczQNNgh5CL7i3hR5Izk7o4xJkF6qeolE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=YizoMg+UtMMr2A2cp/XrAqbbkyLd8xGUNCyjgmQiPKZcAGaejHGrBDUIl0W0tZqEGNKhHCjDmCyWEOdMCXSeVf7QWjxQsWjFlQfVAAfX/R/oePJv9ZmmfhxZrdX8c086N/EpgJ0Dn8rjLdCw40dDOFmAToVkZ/g2X407zZoUyfw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=UiZ4uB0f; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="UiZ4uB0f" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3FAE9C19422; Sun, 15 Feb 2026 23:41:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198899; bh=D7D66X+gJAFczQNNgh5CL7i3hR5Izk7o4xJkF6qeolE=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=UiZ4uB0ftkvcfsuyCpp2BCvenjYi5DGaspOgGnronBlOVcBlhMu3VmURoNZfYtac6 NiVhgllacLrufMvslVuIeJtia9BaGhvMckaBy1dQio2CVJkduOxjuE3e1T8E4Ta/f4 TRfsHSjnMU/MEQSjnHOJcXq5084eZ3b0clE7i1+35RFxHF5uz9mIa048JqptbMPHw3 4DT6C+6bjXQyEW30Mg5Lk+P+JPc652re0g//ypjmuHRJxOWNmXRHY8G8bHyGYyIWKB 2fdGqxMMxiolOCw+saPhkpaQ6eNcXg6+S0l9fEc4uncVsDSxrdk8gX2gR/ndqswy5v 7ez5UBte+KnjQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:16 +0100 Subject: [PATCH 29/79] block: rust: add `TagSet` private data support 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: <20260216-rnull-v6-19-rc5-send-v1-29-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=5916; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=D7D66X+gJAFczQNNgh5CL7i3hR5Izk7o4xJkF6qeolE=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklg2rFmUUi6LLaqQTrSP7Cjji5l3ptpVNJ3Mb 1LLO0X1treJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYNgAKCRDhuBo+eShj d2x7EACrjvttPgu6cF9IkrYrF2Y2L7/35Z6wNExRAfCWDx4SrpD3/6Yne063iZ3oG/ih3R0obID qfp4ntGFuP3Z6/XfIV63okRkoTxcAw35XZg+kZuTy82328/rRgWZZsLUhCzPaTc3x2hU/06mgy3 0qG/0aC23ua7hHWpnLQtJxqoAxLce8qX9KRb/3HrJtN74hV5ffluD6bcFTtBAkVMISJoXC+qefe 29TQuTOpRbj0vQHXKZsbTXkj0XVdWfHd1cAfzpNRyi3rGkw+VDLDxYV5uDU0oJ/klMZAPCyF4FI V2MBB9AWE8449Mkh5qbEfw8/A68+Xd3rZ6a93K74icb2QnYf4g+aJm0GKvhgEwOz+Qe6LsD6ABe u5OgzcAomqzjOxMvd/MgqoW1bxnnupdUfdTyzSzujUjBKC1HVuBcRdupuN6PpW2VU6Vpa1ugb5y +65lOvPVgjVw2c6hEsZkSR1FULHvkbC714OwvUfo55pQolUV8vSt7Kjzl/TssRlXu8Wjkx7fx9Q WcxvPxxaSwcPexeGtoCF7BZ7F5Nh4FXQKCaReDSddZ3ioQTU8DNkmoeDzQ6CCGzJEeAfIRuUU2s mUD/Je4nqrrTVdvYB99b35nU+EQ/zBlzGjPcU26fzDxbCf3ozbpC/FKrL/PZsRtK9WQlOV4AZKi 17jWogjukxsD0ZA== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 C block device drivers can attach private data to a `struct blk_mq_tag_set`. Add support for this feature for Rust block device drivers via the `Operations::TagSetData` associated type. The private data is passed to `TagSet::new` and is stored in the `driver_data` field of the underlying `struct blk_mq_tag_set`. It is released when the `TagSet` is dropped. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/rnull.rs | 3 ++- rust/kernel/block/mq.rs | 6 ++++-- rust/kernel/block/mq/operations.rs | 4 ++++ rust/kernel/block/mq/tag_set.rs | 25 ++++++++++++++++++++----- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 6691e5912c5c9..9e8d085924040 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -229,7 +229,7 @@ fn new(options: NullBlkOptions<'_>) -> Result> { } =20 let tagset =3D Arc::pin_init( - TagSet::new(submit_queues, 256, 1, home_node, flags), + TagSet::new(submit_queues, (), 256, 1, home_node, flags), GFP_KERNEL, )?; =20 @@ -505,6 +505,7 @@ fn align_down(value: T, to: T) -> T impl Operations for NullBlkDevice { type QueueData =3D Pin>; type RequestData =3D Pdu; + type TagSetData =3D (); =20 fn new_request_data() -> impl PinInit { pin_init!(Pdu { diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index d3957f2fb1a66..415be31e9a777 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -71,6 +71,7 @@ //! impl Operations for MyBlkDevice { //! type RequestData =3D (); //! type QueueData =3D (); +//! type TagSetData =3D (); //! //! fn new_request_data( //! ) -> impl PinInit<()> { @@ -94,8 +95,9 @@ //! //! let tagset: Arc> =3D //! Arc::pin_init( -//! TagSet::new(1, 256, 1, bindings::NUMA_NO_NODE, mq::tag_set::Fl= ags::default()), -//! GFP_KERNEL)?; +//! TagSet::new(1, (), 256, 1, bindings::NUMA_NO_NODE, mq::tag_set= ::Flags::default()), +//! GFP_KERNEL +//! )?; //! let mut disk =3D gen_disk::GenDiskBuilder::new() //! .capacity_sectors(4096) //! .build(fmt!("myblk"), tagset, ())?; diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/oper= ations.rs index cd37b939bbf30..9aab6240428cc 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -44,6 +44,10 @@ pub trait Operations: Sized { /// the `GenDisk` associated with this `Operations` implementation. type QueueData: ForeignOwnable; =20 + /// Data associated with a `TagSet`. This is stored as a pointer in `s= truct + /// blk_mq_tag_set`. + type TagSetData: ForeignOwnable; + /// Called by the kernel to get an initializer for a `Pin<&mut Request= Data>`. fn new_request_data() -> impl PinInit; =20 diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set= .rs index e9b36d6329b9b..f18b51e5217fe 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -10,8 +10,8 @@ bindings, block::mq::{operations::OperationsVTable, request::RequestDataWrapper,= Operations}, error::{self, Result}, - prelude::try_pin_init, - types::Opaque, + try_pin_init, + types::{ForeignOwnable, Opaque}, }; use core::{convert::TryInto, marker::PhantomData}; use pin_init::{pin_data, pinned_drop, PinInit}; @@ -39,6 +39,7 @@ impl TagSet { /// Try to create a new tag set pub fn new( nr_hw_queues: u32, + tagset_data: T::TagSetData, num_tags: u32, num_maps: u32, numa_node: i32, @@ -58,7 +59,7 @@ pub fn new( queue_depth: num_tags, cmd_size, flags: flags.into_inner(), - driver_data: core::ptr::null_mut::= (), + driver_data: tagset_data.into_foreign(), nr_maps: num_maps, ..tag_set } @@ -71,7 +72,14 @@ pub fn new( // SAFETY: we do not move out of `tag_set`. let tag_set: &mut Opaque<_> =3D unsafe { Pin::get_unchecke= d_mut(tag_set) }; // SAFETY: `tag_set` is a reference to an initialized `blk= _mq_tag_set`. - error::to_result( unsafe { bindings::blk_mq_alloc_tag_set(= tag_set.get())}) + let status =3D error::to_result( + unsafe { bindings::blk_mq_alloc_tag_set(tag_set.get())} + ); + if status.is_err() { + // SAFETY: We created `driver_data` above with `into_f= oreign` + unsafe { T::TagSetData::from_foreign((*tag_set.get()).= driver_data) }; + } + status }), _p: PhantomData, }) @@ -87,7 +95,14 @@ pub(crate) fn raw_tag_set(&self) -> *mut bindings::blk_m= q_tag_set { impl PinnedDrop for TagSet { fn drop(self: Pin<&mut Self>) { // SAFETY: By type invariant `inner` is valid and has been properly - // initialized during construction. + // initialised during construction. + let tagset_data =3D unsafe { (*self.inner.get()).driver_data }; + + // SAFETY: `inner` is valid and has been properly initialised duri= ng construction. unsafe { bindings::blk_mq_free_tag_set(self.inner.get()) }; + + // SAFETY: `tagset_data` was created by a call to + // `ForeignOwnable::into_foreign` in `TagSet::try_new()` + unsafe { T::TagSetData::from_foreign(tagset_data) }; } } --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 D8E32312803; Sun, 15 Feb 2026 23:46:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199200; cv=none; b=C/2B8l3zQT+x9Bceo+G9M++zTwrOSE+liB5ji0NyTWFZQlT8HE+FX6P67jBx0pd11nE//S01m0Uv6dLLxXUZ3S28LnhEZPEHAbJ1SsGAMho8UlsRWAizVT4RRFypPtuplAW/GOfXCXEwbHzhJEwIAxKX8nLUHJuhVrtITzVvJGw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199200; c=relaxed/simple; bh=V7NPmcHt489UPhx819RkTB11oJ7rpInWWLTHwSOdN0k=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=PT3or5V3XEdjUipF2y3i00Kd3thd/4zTzhq6rBVGyfR8NjqudX6M1QGdTLT2dZRgYowBazP4l7oZ0NAzmvIbB3T3RtAfvReOTvfY4MTau5LuMEjxyh4iKwbhYCKEkeyMeYkOyGBP+mpnGI6TrIcQNdzLTfxfr9niMafk1k5LPbA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=QOAAWiOD; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="QOAAWiOD" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7043DC4CEF7; Sun, 15 Feb 2026 23:46:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199200; bh=V7NPmcHt489UPhx819RkTB11oJ7rpInWWLTHwSOdN0k=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=QOAAWiODki0/fVEd+vwj3qIdMPRlM/t1Wt0oN/BXImgWeFtx+e7pHGr/rrOldFWHW Qq5WsoutXlj/lz9xAQ8S/osGEmSW1h5JM4OWOtGphjFd6aBOsyBlDshTGy+PJ9a6NS sm9BwJt/4JxJD/aim4CEBeVdRT/IbTo5V4Oh3+iOen7SGETNd7v6e/icRmt+uNDqKt dQhKsEQ+QiMqvb6q07pVlYQFDwSx2B92Wcq1aV5DQiU3MMedDVkgfjtKTjn7b/7hKW j1fEcxeA1awXgYmE02RtIiYkeCzhdq1E/DBY3wtJ+WYVqFjNG1BzN724aDSYIjpz/B nXXddZuJ2B0/w== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:17 +0100 Subject: [PATCH 30/79] block: rust: add `hctx` private data support 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: <20260216-rnull-v6-19-rc5-send-v1-30-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=11810; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=V7NPmcHt489UPhx819RkTB11oJ7rpInWWLTHwSOdN0k=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklg2hepO/XMsV/RoMZ08ue9FZTE5Lj97prJGD j8WSlxnqgOJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYNgAKCRDhuBo+eShj d+EKD/9u8LB3MZ2sU5dzRt1fQ1mTyKDY3Y4tuS/8D47vQY5WL29VWojTttVXM62mDObm1xTLHde 6y2lYsO4hRrH+mB2SJnSx4ygR13sIWFKhLjVtwB/Q6uOj/cS049/8c2wDvaIdj/rtvlttrDdKqL HvAmiykeVZukA9xzP8OAY7bENI49gLT0u10mccqkfMahAqAqRsS6RMBGsTZ4Nk4boXYXIoe/JjA 2ANAoJmns8NzTtiHJAUsXL/zAge5zAn9kaNfEhSXVAriMC+5Xiaq832sUWMPeGnu3QW24aBc4dk BJ3fF10wT2+sC88QFzHc0aU5FaTDJONchPfXk2pVuglhVA3m5eF3cLHUSKXEdJraCvfRPxLFvS8 AhQ/4D14KsSIUITCXytGv2RQL9AM4D1DhO5/dgckrJPRA8gCTUhe2o7NtrJyt5DS2IEmyiJAq38 g4qycoOGZHQmxq2XCPzNDy++RkTIq3pJyOs7Odm65g2nrNPEO0OJJX6+11xOWSFDSEGkRNSqpcg naKWE/RVHDizTPOU1na0zdD8W2isxPUiE0lJdT1Gs4cSCci1bZLZd8XJURL4NPc7RGy3akPG1aF ulSl5POtOg9MQxfFF7qt/1QWFUJsE1P2gWtHiZ71LP8gpSw3vXsHTXdOtbv9wHNbymiikWE1i5U wgVtNsa9xbE/Fsw== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 C block device drivers can attach private data to a hardware context (`struct blk_mq_hw_ctx`). Add support for this feature for Rust block device drivers via the `Operations::HwData` associated type. The private data is created in the `init_hctx` callback and stored in the `driver_data` field of `blk_mq_hw_ctx`. It is passed to `queue_rq`, `commit_rqs`, and `poll` callbacks, and is released in `exit_hctx`. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/rnull.rs | 8 +++- rust/kernel/block/mq.rs | 23 ++++++++++- rust/kernel/block/mq/operations.rs | 85 +++++++++++++++++++++++++++++++---= ---- 3 files changed, 97 insertions(+), 19 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 9e8d085924040..4e226186d2f36 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -506,6 +506,7 @@ impl Operations for NullBlkDevice { type QueueData =3D Pin>; type RequestData =3D Pdu; type TagSetData =3D (); + type HwData =3D (); =20 fn new_request_data() -> impl PinInit { pin_init!(Pdu { @@ -516,6 +517,7 @@ fn new_request_data() -> impl PinInit { =20 #[inline(always)] fn queue_rq( + _hw_data: (), queue_data: Pin<&QueueData>, mut rq: Owned>, _is_last: bool, @@ -546,7 +548,11 @@ fn queue_rq( Ok(()) } =20 - fn commit_rqs(_queue_data: Pin<&QueueData>) {} + fn commit_rqs(_hw_data: (), _queue_data: Pin<&QueueData>) {} + + fn init_hctx(_tagset_data: (), _hctx_idx: u32) -> Result { + Ok(()) + } =20 fn complete(rq: ARef>) { Self::end_request( diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index 415be31e9a777..3eab3ca8f5480 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -17,6 +17,12 @@ //! - The [`GenDisk`] type that abstracts the C type `struct gendisk`. //! - The [`Request`] type that abstracts the C type `struct request`. //! +//! Many of the C types that this module abstracts allow a driver to carry +//! private data, either embedded in the struct directly, or as a C `void*= `. In +//! these abstractions, this data is typed. The types of the data is defin= ed by +//! associated types in `Operations`, see [`Operations::RequestData`] for = an +//! example. +//! //! The kernel will interface with the block device driver by calling the = method //! implementations of the `Operations` trait. //! @@ -71,6 +77,7 @@ //! impl Operations for MyBlkDevice { //! type RequestData =3D (); //! type QueueData =3D (); +//! type HwData =3D (); //! type TagSetData =3D (); //! //! fn new_request_data( @@ -78,12 +85,17 @@ //! pin_init::zeroed::<()>() //! } //! -//! fn queue_rq(_queue_data: (), rq: Owned>, _is_last: b= ool) -> Result { +//! fn queue_rq( +//! _hw_data: (), +//! _queue_data: (), +//! rq: Owned>, +//! _is_last: bool +//! ) -> Result { //! rq.end_ok(); //! Ok(()) //! } //! -//! fn commit_rqs(_queue_data: ()) {} +//! fn commit_rqs(_hw_data: (), _queue_data: ()) {} //! //! fn complete(rq: ARef>) { //! OwnableRefCounted::try_from_shared(rq) @@ -91,6 +103,13 @@ //! .expect("Fatal error - expected to be able to end request") //! .end_ok(); //! } +//! +//! fn init_hctx( +//! _tagset_data: (), +//! _hctx_idx: u32, +//! ) -> Result { +//! Ok(()) +//! } //! } //! //! let tagset: Arc> =3D diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/oper= ations.rs index 9aab6240428cc..6641c340d87f4 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -44,6 +44,10 @@ pub trait Operations: Sized { /// the `GenDisk` associated with this `Operations` implementation. type QueueData: ForeignOwnable; =20 + /// Data associated with a dispatch queue. This is stored as a pointer= in + /// the C `struct blk_mq_hw_ctx` that represents a hardware queue. + type HwData: ForeignOwnable; + /// Data associated with a `TagSet`. This is stored as a pointer in `s= truct /// blk_mq_tag_set`. type TagSetData: ForeignOwnable; @@ -54,20 +58,30 @@ pub trait Operations: Sized { /// Called by the kernel to queue a request with the driver. If `is_la= st` is /// `false`, the driver is allowed to defer committing the request. fn queue_rq( + hw_data: ForeignBorrowed<'_, Self::HwData>, queue_data: ForeignBorrowed<'_, Self::QueueData>, rq: Owned>, is_last: bool, ) -> Result; =20 /// Called by the kernel to indicate that queued requests should be su= bmitted. - fn commit_rqs(queue_data: ForeignBorrowed<'_, Self::QueueData>); + fn commit_rqs( + hw_data: ForeignBorrowed<'_, Self::HwData>, + queue_data: ForeignBorrowed<'_, Self::QueueData>, + ); + + /// Called by the kernel to allocate and initialize a driver specific = hardware context data. + fn init_hctx( + tagset_data: ForeignBorrowed<'_, Self::TagSetData>, + hctx_idx: u32, + ) -> Result; =20 /// Called by the kernel when the request is completed. fn complete(rq: ARef>); =20 /// Called by the kernel to poll the device for completed requests. On= ly /// used for poll queues. - fn poll() -> bool { + fn poll(_hw_data: ForeignBorrowed<'_, Self::HwData>) -> bool { build_error!(crate::error::VTABLE_DEFAULT_ERROR) } } @@ -127,6 +141,11 @@ impl OperationsVTable { let mut rq =3D unsafe { Owned::from_raw(NonNull::>::new_unchecked(= (*bd).rq.cast())) }; =20 + // SAFETY: The safety requirement for this function ensure that `h= ctx` + // is valid and that `driver_data` was produced by a call to + // `into_foreign` in `Self::init_hctx_callback`. + let hw_data =3D unsafe { T::HwData::borrow((*hctx).driver_data) }; + // SAFETY: `hctx` is valid as required by this function. let queue_data =3D unsafe { (*(*hctx).queue).queuedata }; =20 @@ -140,6 +159,7 @@ impl OperationsVTable { unsafe { rq.start_unchecked() }; =20 let ret =3D T::queue_rq( + hw_data, queue_data, rq, // SAFETY: `bd` is valid as required by the safety requirement= for @@ -162,6 +182,10 @@ impl OperationsVTable { /// This function may only be called by blk-mq C infrastructure. The c= aller /// must ensure that `hctx` is valid. unsafe extern "C" fn commit_rqs_callback(hctx: *mut bindings::blk_mq_h= w_ctx) { + // SAFETY: `driver_data` was installed by us in `init_hctx_callbac= k` as + // the result of a call to `into_foreign`. + let hw_data =3D unsafe { T::HwData::borrow((*hctx).driver_data) }; + // SAFETY: `hctx` is valid as required by this function. let queue_data =3D unsafe { (*(*hctx).queue).queuedata }; =20 @@ -170,7 +194,7 @@ impl OperationsVTable { // `ForeignOwnable::from_foreign()` is only called when the tagset= is // dropped, which happens after we are dropped. let queue_data =3D unsafe { T::QueueData::borrow(queue_data) }; - T::commit_rqs(queue_data) + T::commit_rqs(hw_data, queue_data) } =20 /// This function is called by the C kernel. A pointer to this functio= n is @@ -194,12 +218,18 @@ impl OperationsVTable { /// /// # Safety /// - /// This function may only be called by blk-mq C infrastructure. + /// This function may only be called by blk-mq C infrastructure. `hctx= ` must + /// be a pointer to a valid and aligned `struct blk_mq_hw_ctx` that was + /// previously initialized by a call to `init_hctx_callback`. unsafe extern "C" fn poll_callback( - _hctx: *mut bindings::blk_mq_hw_ctx, + hctx: *mut bindings::blk_mq_hw_ctx, _iob: *mut bindings::io_comp_batch, ) -> crate::ffi::c_int { - T::poll().into() + // SAFETY: By function safety requirement, `hctx` was initialized = by + // `init_hctx_callback` and thus `driver_data` came from a call to + // `into_foreign`. + let hw_data =3D unsafe { T::HwData::borrow((*hctx).driver_data) }; + T::poll(hw_data).into() } =20 /// This function is called by the C kernel. A pointer to this functio= n is @@ -207,15 +237,29 @@ impl OperationsVTable { /// /// # Safety /// - /// This function may only be called by blk-mq C infrastructure. This - /// function may only be called once before `exit_hctx_callback` is ca= lled - /// for the same context. + /// This function may only be called by blk-mq C infrastructure. + /// `tagset_data` must be initialized by the initializer returned by + /// `TagSet::try_new` as part of tag set initialization. `hctx` must b= e a + /// pointer to a valid `blk_mq_hw_ctx` where the `driver_data` field w= as not + /// yet initialized. This function may only be called onece before + /// `exit_hctx_callback` is called for the same context. unsafe extern "C" fn init_hctx_callback( - _hctx: *mut bindings::blk_mq_hw_ctx, - _tagset_data: *mut crate::ffi::c_void, - _hctx_idx: crate::ffi::c_uint, - ) -> crate::ffi::c_int { - from_result(|| Ok(0)) + hctx: *mut bindings::blk_mq_hw_ctx, + tagset_data: *mut core::ffi::c_void, + hctx_idx: crate::ffi::c_uint, + ) -> core::ffi::c_int { + from_result(|| { + // SAFETY: By the safety requirements of this function, + // `tagset_data` came from a call to `into_foreign` when the + // `TagSet` was initialized. + let tagset_data =3D unsafe { T::TagSetData::borrow(tagset_data= ) }; + let data =3D T::init_hctx(tagset_data, hctx_idx)?; + + // SAFETY: by the safety requirments of this function, `hctx` = is + // valid for write + unsafe { (*hctx).driver_data =3D data.into_foreign().cast() }; + Ok(0) + }) } =20 /// This function is called by the C kernel. A pointer to this functio= n is @@ -223,11 +267,20 @@ impl OperationsVTable { /// /// # Safety /// - /// This function may only be called by blk-mq C infrastructure. + /// This function may only be called by blk-mq C infrastructure. `hctx= ` must + /// be a valid pointer that was previously initialized by a call to + /// `init_hctx_callback`. This function may be called only once after + /// `init_hctx_callback` was called. unsafe extern "C" fn exit_hctx_callback( - _hctx: *mut bindings::blk_mq_hw_ctx, + hctx: *mut bindings::blk_mq_hw_ctx, _hctx_idx: crate::ffi::c_uint, ) { + // SAFETY: By the safety requirements of this function, `hctx` is = valid for read. + let ptr =3D unsafe { (*hctx).driver_data }; + + // SAFETY: By the safety requirements of this function, `ptr` came= from + // a call to `into_foreign` in `init_hctx_callback` + unsafe { T::HwData::from_foreign(ptr) }; } =20 /// This function is called by the C kernel. A pointer to this functio= n is --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 1696429D281; Sun, 15 Feb 2026 23:40:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198855; cv=none; b=piJAuq65eKg0Lxx4a6JTN+obeSk2agUaBHk8oYJ/VoWtAy9csWSnY0+BbpJekGDTpWf/laR6cBYBHKzsZatvAqcH6yAQZNUDHc1nSG5xVOfzmJwF6B4sXMLJcGBfmHgUPWrftT5c/IMhI6jTpgNnJ6o08YHsFNhAf3pXgHmjYnE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198855; c=relaxed/simple; bh=j04tEwwzTSFcwGlD1L1D21qzMW+eJ7IZYJ1Q3R3ScW4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=TgxYnDX1254ZC3C4P5ZXGlP2je3M2t1XSd9WJrZ4ocIleLky7gw2Ga49aqiBeNUSO4LKJCfo9VtSuFu5YoferkWwICzEcTVZzYgEi+NswtnrkB++PbCwfUc9kqj8h0WnJPhjZY8BjVe4wJFN6lzVSvbp4DnYsKwfzGWX1taU7Q0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=TVgbK22N; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="TVgbK22N" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 597C9C19422; Sun, 15 Feb 2026 23:40:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198854; bh=j04tEwwzTSFcwGlD1L1D21qzMW+eJ7IZYJ1Q3R3ScW4=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=TVgbK22N5OZBZ4H9uWrbvjMR0p2Jqu3MgtDnuzJWNdhzuEOa64B9d56Owzub6w8F5 gtooH/HHmaLk8T6yD1W6jgt8Yj+d2FLEQLVMvaex5FkEXc38wrvpIqq734PL0vQNd1 366PBMxPx3XmGnrJ1HoTz57WKaiSfgbojXzVUIuuSmknUT+KiGGwzyJQehUIzZJFRQ a0WE7AQ/EK8Pt2qzKBlsVRRPSiLpDW0j6/SEDBG0Ssy0sps09HZdFZLxhhRGCNRs6k bikh96Yk/8ri49OgQ0OwlGG/fIHKJnwIZSCsuXBtoIWJXdOy2ZYtkYSJj6rhVf8i7x OE56fJP9J/2wA== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:18 +0100 Subject: [PATCH 31/79] block: rnull: add volatile cache emulation 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: <20260216-rnull-v6-19-rc5-send-v1-31-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=33483; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=j04tEwwzTSFcwGlD1L1D21qzMW+eJ7IZYJ1Q3R3ScW4=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklg3//wSiEJI3m5TrzNRjHawy887jrK3aePZj z+b9mjSOvmJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYNwAKCRDhuBo+eShj d81UD/9yrlUyrqQgg6aerlzax0c5mH2sKhm+TJwa8bt1PrrqNaC5kHcxNqyEhJJ9DufHHBQq6pR 8BS12HhyFkMiq16M+X5CN+BcnlOeBTF4nGwcrxB2L1IKq44ADWzi4NERxbVpaAdORtWDpDxrO3i ZCquG43UejchUsx4KPcBY+KoSZAxdMXOu3L0zBrgmw1oEIbAQzyWR57Gma6VfsG3Iy/f6o9xOq3 JEvva/hQCy79EyTZzLQNs08/ccuWwnsTqO4+VXMqiSBN8A+9LrzCKXEOLrO1foLV3UZZOe/thL4 vepIDBp+w8/QYI53okog+68K0BjSYazQjw47lrkAqo7gFXpBYq25aDahUQI/aQDimStHXRtkhPh b1GPZjRLORVgnbuVkoXq0OXWyH9L0Uxj5Bnnxup+hfmIMgIevlxlsNCFUlqoRZrRs+SsXHpBTAR qHpuceqcY9kSJ91kA+dtwKa+hm6h9Y6E/EdLQT/5QxfkdyPkBNRq+TEKw2fhX7RanOxT2B5v7kD VQ0KFfU7vBs8B5NpND9YHLd60yCELpMUE3mojICRVpIDzBXy/ylBWJQZYH1Lv/NdzM5t3rhJtyk XEVNDcH/ZaMOlVqWJYBEEQtf53BtvUltCcDcwg/5BvZRGJ9pwhdnsKdHVcw2iPwhRYh+Wg0Qbwf KJEtCq8AfvRgkBg== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add volatile cache emulation to rnull. When enabled via the `cache_size_mib` configfs attribute, writes are first stored in a volatile cache before being written back to the simulated non-volatile storage. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 36 +++- drivers/block/rnull/disk_storage.rs | 248 ++++++++++++++++++++++++ drivers/block/rnull/disk_storage/page.rs | 75 ++++++++ drivers/block/rnull/rnull.rs | 316 ++++++++++++++++++---------= ---- 4 files changed, 538 insertions(+), 137 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index c08a3cbd66f18..d679f12ee6749 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -1,9 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 =20 use super::{ + DiskStorage, NullBlkDevice, THIS_MODULE, // }; +use core::fmt::Write; use kernel::{ bindings, block::{ @@ -19,10 +21,7 @@ AttributeOperations, // }, configfs_attrs, - fmt::{ - self, - Write as _, // - }, + fmt, new_mutex, page::PAGE_SIZE, prelude::*, @@ -105,17 +104,19 @@ fn make_group( badblocks: 12, badblocks_once: 13, badblocks_partial_io: 14, + cache_size_mib: 15, ], }; =20 + let block_size =3D 4096; Ok(configfs::Group::new( name.try_into()?, item_type, // TODO: cannot coerce new_mutex!() to impl PinInit<_, Error>,= so put mutex inside - try_pin_init!( DeviceConfig { + try_pin_init!(DeviceConfig { data <- new_mutex!(DeviceConfigInner { powered: false, - block_size: 4096, + block_size, rotational: false, disk: None, capacity_mib: 4096, @@ -130,6 +131,11 @@ fn make_group( bad_blocks: Arc::pin_init(BadBlocks::new(false), GFP_K= ERNEL)?, bad_blocks_once: false, bad_blocks_partial_io: false, + disk_storage: Arc::pin_init( + DiskStorage::new(0, block_size as usize), + GFP_KERNEL + )?, + cache_size_mib: 0, }), }), core::iter::empty(), @@ -192,6 +198,8 @@ struct DeviceConfigInner { bad_blocks: Arc, bad_blocks_once: bool, bad_blocks_partial_io: bool, + cache_size_mib: u64, + disk_storage: Arc, } =20 #[vtable] @@ -230,6 +238,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { bad_blocks: guard.bad_blocks.clone(), bad_blocks_once: guard.bad_blocks_once, bad_blocks_partial_io: guard.bad_blocks_partial_io, + storage: guard.disk_storage.clone(), })?); guard.powered =3D true; } else if guard.powered && !power_op { @@ -241,6 +250,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { } } =20 +// DiskStorage::new(cache_size_mib << 20, block_size as usize), configfs_simple_field!(DeviceConfig, 1, block_size, u32, check GenDiskBuil= der::validate_block_size); configfs_simple_bool_field!(DeviceConfig, 2, rotational); configfs_simple_field!(DeviceConfig, 3, capacity_mib, u64); @@ -433,3 +443,17 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { =20 configfs_simple_bool_field!(DeviceConfig, 13, bad_blocks_once); configfs_simple_bool_field!(DeviceConfig, 14, bad_blocks_partial_io); +configfs_attribute!(DeviceConfig, 15, + show: |this, page| show_field(this.data.lock().cache_size_mib, page), + store: |this, page| store_with_power_check(this, page, |this, page| { + let text =3D core::str::from_utf8(page)?.trim(); + let value =3D text.parse::().map_err(|_| EINVAL)?; + let mut guard =3D this.data.lock(); + guard.disk_storage =3D Arc::pin_init( + DiskStorage::new(value, guard.block_size as usize), + GFP_KERNEL + )?; + guard.cache_size_mib =3D value; + Ok(()) + }) +); diff --git a/drivers/block/rnull/disk_storage.rs b/drivers/block/rnull/disk= _storage.rs new file mode 100644 index 0000000000000..8a8a90e1cf0bd --- /dev/null +++ b/drivers/block/rnull/disk_storage.rs @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0 + +use super::HwQueueContext; +use core::pin::Pin; +use kernel::{ + block, + new_spinlock, + new_xarray, + page::PAGE_SIZE, + prelude::*, + sync::{ + atomic::{ordering, Atomic}, + SpinLock, SpinLockGuard, + }, + uapi::PAGE_SECTORS, + xarray::{ + self, + XArray, + XArraySheaf, // + }, // +}; +pub(crate) use page::NullBlockPage; + +mod page; + +#[pin_data] +pub(crate) struct DiskStorage { + // TODO: Get rid of this pointer indirection. + #[pin] + trees: SpinLock>>, + cache_size: u64, + cache_size_used: Atomic, + next_flush_sector: Atomic, + block_size: usize, +} + +impl DiskStorage { + pub(crate) fn new(cache_size: u64, block_size: usize) -> impl PinInit<= Self, Error> { + try_pin_init!( Self { + // TODO: Get rid of the box + // https://git.kernel.org/pub/scm/linux/kernel/git/boqun/linux= .git/commit/?h=3Dlocking&id=3Da5d84cafb3e253a11d2e078902c5b090be2f4227 + trees <- new_spinlock!(KBox::pin_init(TreeContainer::new(), GF= P_KERNEL)?), + cache_size, + cache_size_used: Atomic::new(0), + next_flush_sector: Atomic::new(0), + block_size + }) + } + + pub(crate) fn access<'a, 'b, 'c>( + &'a self, + tree_guard: &'a mut SpinLockGuard<'b, Pin>>, + hw_data_guard: &'a mut SpinLockGuard<'b, HwQueueContext>, + sheaf: Option>, + ) -> DiskStorageAccess<'a, 'b, 'c> { + DiskStorageAccess::new(self, tree_guard, hw_data_guard, sheaf) + } + + pub(crate) fn lock(&self) -> SpinLockGuard<'_, Pin= >> { + self.trees.lock() + } +} + +pub(crate) struct DiskStorageAccess<'a, 'b, 'c> { + cache_guard: xarray::Guard<'a, TreeNode>, + disk_guard: xarray::Guard<'a, TreeNode>, + hw_data_guard: &'a mut SpinLockGuard<'b, HwQueueContext>, + disk_storage: &'a DiskStorage, + pub(crate) sheaf: Option>, +} + +impl<'a, 'b, 'c> DiskStorageAccess<'a, 'b, 'c> { + fn new( + disk_storage: &'a DiskStorage, + tree_guard: &'a mut SpinLockGuard<'b, Pin>>, + hw_data_guard: &'a mut SpinLockGuard<'b, HwQueueContext>, + sheaf: Option>, + ) -> Self { + Self { + cache_guard: tree_guard.cache_tree.lock(), + disk_guard: tree_guard.disk_tree.lock(), + hw_data_guard, + disk_storage, + sheaf, + } + } + fn to_index(sector: u64) -> usize { + (sector >> block::PAGE_SECTORS_SHIFT) as usize + } + + fn to_sector(index: usize) -> u64 { + (index << block::PAGE_SECTORS_SHIFT) as u64 + } + + fn extract_cache_page(&mut self) -> Result> { + let cache_entry =3D self + .cache_guard + .find_next_entry_circular( + self.disk_storage.next_flush_sector.load(ordering::Relaxed= ) as usize + ) + .expect("Expected to find a page in the cache"); + + let index =3D cache_entry.index(); + + self.disk_storage + .next_flush_sector + .store(Self::to_sector(index).wrapping_add(1), ordering::Relax= ed); + + self.disk_storage.cache_size_used.store( + self.disk_storage.cache_size_used.load(ordering::Relaxed) - PA= GE_SIZE as u64, + ordering::Relaxed, + ); + + let page =3D match self.disk_guard.entry(index) { + xarray::Entry::Vacant(disk_entry) =3D> { + disk_entry + .insert(cache_entry.remove(), self.sheaf.as_mut()) + .expect("Preload is set up to allow insert without fai= lure"); + self.hw_data_guard + .page + .take() + .expect("Preload has allocated for us") + } + xarray::Entry::Occupied(mut disk_entry) =3D> { + let mut page =3D if cache_entry.is_full() { + disk_entry.insert(cache_entry.remove()) + } else { + let mut src =3D cache_entry; + let mut offset =3D 0; + for _ in 0..PAGE_SECTORS { + src.page_mut().get_pin_mut().copy_to_page( + disk_entry.page_mut().get_pin_mut(), + offset, + block::SECTOR_SIZE as usize, + )?; + offset +=3D block::SECTOR_SIZE as usize; + } + src.remove() + }; + page.reset(); + page + } + }; + + Ok(page) + } + + fn get_cache_page(&mut self, sector: u64) -> Result<&mut NullBlockPage= > { + let index =3D Self::to_index(sector); + + if self.cache_guard.contains_index(index) { + Ok(self.cache_guard.get_mut(index).expect("Index is present")) + } else { + let page =3D if self.disk_storage.cache_size_used.load(orderin= g::Relaxed) + < self.disk_storage.cache_size + { + self.hw_data_guard + .page + .take() + .expect("Expected to have a page available") + } else { + self.extract_cache_page()? + }; + Ok(self + .cache_guard + .insert_entry(index, page, self.sheaf.as_mut()) + .expect("Should be able to insert") + .into_mut()) + } + } + + fn get_disk_page(&mut self, sector: u64) -> Result<&mut NullBlockPage>= { + let index =3D Self::to_index(sector); + + let page =3D match self.disk_guard.entry(index) { + xarray::Entry::Vacant(e) =3D> e.insert( + self.hw_data_guard + .page + .take() + .expect("Expected page to be available"), + self.sheaf.as_mut(), + )?, + xarray::Entry::Occupied(e) =3D> e.into_mut(), + }; + + Ok(page) + } + + pub(crate) fn get_write_page(&mut self, sector: u64) -> Result<&mut Nu= llBlockPage> { + let page =3D if self.disk_storage.cache_size > 0 { + self.get_cache_page(sector)? + } else { + self.get_disk_page(sector)? + }; + + Ok(page) + } + + pub(crate) fn get_read_page(&self, sector: u64) -> Option<&NullBlockPa= ge> { + let index =3D Self::to_index(sector); + if self.disk_storage.cache_size > 0 { + self.cache_guard + .get(index) + .or_else(|| self.disk_guard.get(index)) + } else { + self.disk_guard.get(index) + } + } + + fn free_sector_tree(tree_access: &mut xarray::Guard<'_, TreeNode>, sec= tor: u64) { + let index =3D Self::to_index(sector); + if let Some(page) =3D tree_access.get_mut(index) { + page.set_free(sector); + + if page.is_empty() { + tree_access.remove(index); + } + } + } + + pub(crate) fn free_sector(&mut self, sector: u64) { + if self.disk_storage.cache_size > 0 { + Self::free_sector_tree(&mut self.cache_guard, sector); + } + + Self::free_sector_tree(&mut self.disk_guard, sector); + } +} + +type Tree =3D XArray; +type TreeNode =3D KBox; + +#[pin_data] +pub(crate) struct TreeContainer { + #[pin] + disk_tree: Tree, + #[pin] + cache_tree: Tree, +} + +impl TreeContainer { + fn new() -> impl PinInit { + pin_init!(TreeContainer { + disk_tree <- new_xarray!(xarray::AllocKind::Alloc), + cache_tree <- new_xarray!(xarray::AllocKind::Alloc), + }) + } +} diff --git a/drivers/block/rnull/disk_storage/page.rs b/drivers/block/rnull= /disk_storage/page.rs new file mode 100644 index 0000000000000..c2e18502cbdda --- /dev/null +++ b/drivers/block/rnull/disk_storage/page.rs @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0 + +use kernel::{ + block::{ + SECTOR_MASK, + SECTOR_SHIFT, // + }, + page::{ + SafePage, + PAGE_SIZE, // + }, + prelude::*, + types::Owned, + uapi::PAGE_SECTORS, // +}; + +const _CHEKC_STATUS_WIDTH: () =3D build_assert!((PAGE_SIZE >> SECTOR_SHIFT= ) <=3D 64); + +pub(crate) struct NullBlockPage { + page: Owned, + status: u64, + block_size: usize, +} + +impl NullBlockPage { + pub(crate) fn new(block_size: usize) -> Result> { + Ok(KBox::new( + Self { + page: SafePage::alloc_page(GFP_NOIO | __GFP_ZERO)?, + status: 0, + block_size, + }, + GFP_NOIO, + )?) + } + + pub(crate) fn set_occupied(&mut self, sector: u64) { + let idx =3D sector & u64::from(SECTOR_MASK); + self.status |=3D 1 << idx; + } + + pub(crate) fn set_free(&mut self, sector: u64) { + let idx =3D sector & u64::from(SECTOR_MASK); + self.status &=3D !(1 << idx); + } + + pub(crate) fn is_empty(&self) -> bool { + self.status =3D=3D 0 + } + + pub(crate) fn reset(&mut self) { + self.status =3D 0; + } + + pub(crate) fn is_full(&self) -> bool { + let blocks_per_page =3D PAGE_SIZE >> self.block_size.trailing_zero= s(); + let shift =3D PAGE_SECTORS as usize / blocks_per_page; + + for i in 0..blocks_per_page { + if self.status & (1 << (i * shift)) =3D=3D 0 { + return false; + } + } + + true + } + + pub(crate) fn page_mut(&mut self) -> &mut Owned { + &mut self.page + } + + pub(crate) fn page(&self) -> &Owned { + &self.page + } +} diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 4e226186d2f36..cca497aef40df 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -3,13 +3,22 @@ //! This is a Rust implementation of the C null block driver. =20 mod configfs; +mod disk_storage; =20 use configfs::IRQMode; +use disk_storage::{ + DiskStorage, + NullBlockPage, + TreeContainer, // +}; use kernel::{ bindings, block::{ self, - badblocks::{self, BadBlocks}, + badblocks::{ + self, + BadBlocks, // + }, bio::Segment, mq::{ self, @@ -20,7 +29,7 @@ Operations, TagSet, // }, - SECTOR_MASK, SECTOR_SHIFT, + SECTOR_SHIFT, }, error::{ code, @@ -28,11 +37,7 @@ }, ffi, new_mutex, - new_xarray, - page::{ - SafePage, - PAGE_SIZE, // - }, + new_spinlock, pr_info, prelude::*, str::CString, @@ -41,9 +46,11 @@ atomic::{ ordering, Atomic, // - }, + }, // Arc, - Mutex, // + Mutex, + SpinLock, + SpinLockGuard, }, time::{ hrtimer::{ @@ -58,7 +65,7 @@ OwnableRefCounted, Owned, // }, - xarray::XArray, // + xarray::XArraySheaf, // }; use pin_init::PinInit; =20 @@ -148,9 +155,11 @@ fn init(_module: &'static ThisModule) -> impl PinInit<= Self, Error> { } else { *module_parameters::submit_queues.value() }; + + let block_size =3D *module_parameters::bs.value(); let disk =3D NullBlkDevice::new(NullBlkOptions { name: &name, - block_size: *module_parameters::bs.value(), + block_size, rotational: *module_parameters::rotational.value() != =3D 0, capacity_mib: *module_parameters::gb.value() * 1024, irq_mode: (*module_parameters::irqmode.value()).try_in= to()?, @@ -163,6 +172,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { bad_blocks: Arc::pin_init(BadBlocks::new(false), GFP_K= ERNEL)?, bad_blocks_once: false, bad_blocks_partial_io: false, + storage: Arc::pin_init(DiskStorage::new(0, block_size = as usize), GFP_KERNEL)?, })?; disks.push(disk, GFP_KERNEL)?; } @@ -192,8 +202,20 @@ struct NullBlkOptions<'a> { bad_blocks: Arc, bad_blocks_once: bool, bad_blocks_partial_io: bool, + storage: Arc, +} + +#[pin_data] +struct NullBlkDevice { + storage: Arc, + irq_mode: IRQMode, + completion_time: Delta, + memory_backed: bool, + block_size: usize, + bad_blocks: Arc, + bad_blocks_once: bool, + bad_blocks_partial_io: bool, } -struct NullBlkDevice; =20 impl NullBlkDevice { fn new(options: NullBlkOptions<'_>) -> Result> { @@ -212,10 +234,14 @@ fn new(options: NullBlkOptions<'_>) -> Result> { bad_blocks, bad_blocks_once, bad_blocks_partial_io, + storage, } =3D options; =20 let mut flags =3D mq::tag_set::Flags::default(); =20 + // TODO: lim.features |=3D BLK_FEAT_WRITE_CACHE; + // if (dev->fua) + // lim.features |=3D BLK_FEAT_FUA; if memory_backed { flags |=3D mq::tag_set::Flag::Blocking; } @@ -233,13 +259,13 @@ fn new(options: NullBlkOptions<'_>) -> Result> { GFP_KERNEL, )?; =20 - let queue_data =3D Box::pin_init( - pin_init!(QueueData { - tree <- new_xarray!(kernel::xarray::AllocKind::Alloc), + let queue_data =3D Box::try_pin_init( + try_pin_init!(Self { + storage, irq_mode, completion_time, memory_backed, - block_size: block_size.into(), + block_size: block_size as usize, bad_blocks, bad_blocks_once, bad_blocks_partial_io, @@ -262,68 +288,133 @@ fn new(options: NullBlkOptions<'_>) -> Result> { builder.build(fmt!("{}", name.to_str()?), tagset, queue_data) } =20 + fn sheaf_size() -> usize { + 2 * ((usize::BITS as usize / bindings::XA_CHUNK_SHIFT) + + if (usize::BITS as usize % bindings::XA_CHUNK_SHIFT) =3D=3D = 0 { + 0 + } else { + 1 + }) + } + + fn preload<'b, 'c>( + tree_guard: &'b mut SpinLockGuard<'c, Pin>>, + hw_data_guard: &'b mut SpinLockGuard<'c, HwQueueContext>, + block_size: usize, + ) -> Result { + if hw_data_guard.page.is_none() { + hw_data_guard.page =3D + Some(tree_guard.do_unlocked(|| { + hw_data_guard.do_unlocked(|| NullBlockPage::new(block_= size)) + })?); + } + + Ok(()) + } + #[inline(always)] - fn write(tree: &Tree, mut sector: u64, mut segment: Segment<'_>) -> Re= sult { + fn write<'a, 'b, 'c>( + &'a self, + tree_guard: &'b mut SpinLockGuard<'c, Pin>>, + hw_data_guard: &'b mut SpinLockGuard<'c, HwQueueContext>, + mut sector: u64, + mut segment: Segment<'_>, + ) -> Result { + let mut sheaf: Option> =3D None; + while !segment.is_empty() { - let page =3D NullBlockPage::new()?; - let mut tree =3D tree.lock(); + Self::preload(tree_guard, hw_data_guard, self.block_size)?; =20 - let page_idx =3D sector >> block::PAGE_SECTORS_SHIFT; + match &mut sheaf { + Some(sheaf) =3D> { + tree_guard.do_unlocked(|| { + hw_data_guard.do_unlocked(|| sheaf.refill(GFP_KERN= EL, Self::sheaf_size())) + })?; + } + None =3D> { + let _ =3D sheaf.insert( + kernel::xarray::xarray_kmem_cache() + .sheaf(Self::sheaf_size(), GFP_NOWAIT) + .or(tree_guard.do_unlocked(|| { + hw_data_guard.do_unlocked(|| -> Result<_> { + kernel::xarray::xarray_kmem_cache() + .sheaf(Self::sheaf_size(), GFP_KER= NEL) + }) + }))?, + ); + } + } =20 - let page =3D if let Some(page) =3D tree.get_mut(page_idx as us= ize) { - page - } else { - tree.store(page_idx as usize, page, GFP_NOIO)?; - tree.get_mut(page_idx as usize).unwrap() - }; + let mut access =3D self.storage.access(tree_guard, hw_data_gua= rd, sheaf); =20 + let page =3D access.get_write_page(sector)?; page.set_occupied(sector); let page_offset =3D (sector & u64::from(block::SECTOR_MASK)) <= < block::SECTOR_SHIFT; - sector +=3D segment.copy_to_page(page.page.get_pin_mut(), page= _offset as usize) as u64 + + sector +=3D segment.copy_to_page(page.page_mut().get_pin_mut()= , page_offset as usize) + as u64 >> block::SECTOR_SHIFT; + + sheaf =3D access.sheaf; + } + + if let Some(sheaf) =3D sheaf { + tree_guard.do_unlocked(|| { + hw_data_guard.do_unlocked(|| { + sheaf.return_refill(GFP_KERNEL); + }) + }); } + Ok(()) } =20 #[inline(always)] - fn read(tree: &Tree, mut sector: u64, mut segment: Segment<'_>) -> Res= ult { - let tree =3D tree.lock(); + fn read<'a, 'b, 'c>( + &'a self, + tree_guard: &'b mut SpinLockGuard<'c, Pin>>, + hw_data_guard: &'b mut SpinLockGuard<'c, HwQueueContext>, + mut sector: u64, + mut segment: Segment<'_>, + ) -> Result { + let access =3D self.storage.access(tree_guard, hw_data_guard, None= ); =20 while !segment.is_empty() { - let idx =3D sector >> block::PAGE_SECTORS_SHIFT; - - if let Some(page) =3D tree.get(idx as usize) { - let page_offset =3D (sector & u64::from(block::SECTOR_MASK= )) << block::SECTOR_SHIFT; - sector +=3D segment.copy_from_page(&page.page, page_offset= as usize) as u64 - >> block::SECTOR_SHIFT; - } else { - sector +=3D segment.zero_page() as u64 >> block::SECTOR_SH= IFT; + let page =3D access.get_read_page(sector); + + match page { + Some(page) =3D> { + let page_offset =3D + (sector & u64::from(block::SECTOR_MASK)) << block:= :SECTOR_SHIFT; + sector +=3D segment.copy_from_page(page.page(), page_o= ffset as usize) as u64 + >> block::SECTOR_SHIFT; + } + None =3D> sector +=3D segment.zero_page() as u64 >> block:= :SECTOR_SHIFT, } } =20 Ok(()) } =20 - fn discard(tree: &Tree, mut sector: u64, sectors: u64, block_size: u64= ) -> Result { - let mut remaining_bytes =3D sectors << SECTOR_SHIFT; - let mut tree =3D tree.lock(); + fn discard( + &self, + hw_data: &Pin<&SpinLock>, + mut sector: u64, + sectors: u32, + ) -> Result { + let mut tree_guard =3D self.storage.lock(); + let mut hw_data_guard =3D hw_data.lock(); =20 - while remaining_bytes > 0 { - let page_idx =3D sector >> block::PAGE_SECTORS_SHIFT; - let mut remove =3D false; - if let Some(page) =3D tree.get_mut(page_idx as usize) { - page.set_free(sector); - if page.is_empty() { - remove =3D true; - } - } + let mut access =3D self + .storage + .access(&mut tree_guard, &mut hw_data_guard, None); =20 - if remove { - drop(tree.remove(page_idx as usize)) - } + let mut remaining_bytes =3D (sectors as usize) << SECTOR_SHIFT; =20 - let processed =3D remaining_bytes.min(block_size); - sector +=3D processed >> SECTOR_SHIFT; + while remaining_bytes > 0 { + access.free_sector(sector); + let processed =3D remaining_bytes.min(self.block_size); + sector +=3D (processed >> SECTOR_SHIFT) as u64; remaining_bytes -=3D processed; } =20 @@ -331,21 +422,34 @@ fn discard(tree: &Tree, mut sector: u64, sectors: u64= , block_size: u64) -> Resul } =20 #[inline(never)] - fn transfer(rq: &mut Owned>, tree: &Tree, sectors: u= 32) -> Result { + fn transfer( + &self, + hw_data: &Pin<&SpinLock>, + rq: &mut Owned>, + sectors: u32, + ) -> Result { let mut sector =3D rq.sector(); let end_sector =3D sector + >::into(sectors); let command =3D rq.command(); =20 + // TODO: Use `PerCpu` to get rid of this lock + let mut hw_data_guard =3D hw_data.lock(); + let mut tree_guard =3D self.storage.lock(); + for bio in rq.bio_iter_mut() { let segment_iter =3D bio.segment_iter(); for segment in segment_iter { // Length might be limited by bad blocks. let length =3D segment .len() - .min((sector - end_sector) as u32 >> SECTOR_SHIFT); + .min((end_sector - sector) as u32 >> SECTOR_SHIFT); match command { - bindings::req_op_REQ_OP_WRITE =3D> Self::write(tree, s= ector, segment)?, - bindings::req_op_REQ_OP_READ =3D> Self::read(tree, sec= tor, segment)?, + bindings::req_op_REQ_OP_WRITE =3D> { + self.write(&mut tree_guard, &mut hw_data_guard, se= ctor, segment)? + } + bindings::req_op_REQ_OP_READ =3D> { + self.read(&mut tree_guard, &mut hw_data_guard, sec= tor, segment)? + } _ =3D> (), } sector +=3D u64::from(length) >> SECTOR_SHIFT; @@ -355,29 +459,26 @@ fn transfer(rq: &mut Owned>, tree: = &Tree, sectors: u32) -> Res } } } + Ok(()) } =20 - fn handle_bad_blocks( - rq: &mut Owned>, - queue_data: &QueueData, - sectors: &mut u32, - ) -> Result { - if queue_data.bad_blocks.enabled() { + fn handle_bad_blocks(&self, rq: &mut Owned>, sectors= : &mut u32) -> Result { + if self.bad_blocks.enabled() { let start =3D rq.sector(); let end =3D start + u64::from(*sectors); - match queue_data.bad_blocks.check(start..end) { + match self.bad_blocks.check(start..end) { badblocks::BlockStatus::None =3D> {} badblocks::BlockStatus::Acknowledged(mut range) | badblocks::BlockStatus::Unacknowledged(mut range) =3D> { rq.data_ref().error.store(1, ordering::Relaxed); =20 - if queue_data.bad_blocks_once { - queue_data.bad_blocks.set_good(range.clone())?; + if self.bad_blocks_once { + self.bad_blocks.set_good(range.clone())?; } =20 - if queue_data.bad_blocks_partial_io { - let block_size_sectors =3D queue_data.block_size >= > SECTOR_SHIFT; + if self.bad_blocks_partial_io { + let block_size_sectors =3D (self.block_size >> SEC= TOR_SHIFT) as u64; range.start =3D align_down(range.start, block_size= _sectors); if start < range.start { *sectors =3D (range.start - start) as u32; @@ -402,53 +503,8 @@ fn end_request(rq: Owned>) { } } =20 -const _CHEKC_STATUS_WIDTH: () =3D build_assert!((PAGE_SIZE >> SECTOR_SHIFT= ) <=3D 64); - -struct NullBlockPage { - page: Owned, - status: u64, -} - -impl NullBlockPage { - fn new() -> Result> { - Ok(KBox::new( - Self { - page: SafePage::alloc_page(GFP_NOIO | __GFP_ZERO)?, - status: 0, - }, - GFP_NOIO, - )?) - } - - fn set_occupied(&mut self, sector: u64) { - let idx =3D sector & u64::from(SECTOR_MASK); - self.status |=3D 1 << idx; - } - - fn set_free(&mut self, sector: u64) { - let idx =3D sector & u64::from(SECTOR_MASK); - self.status &=3D !(1 << idx); - } - - fn is_empty(&self) -> bool { - self.status =3D=3D 0 - } -} - -type TreeNode =3D KBox; -type Tree =3D XArray; - -#[pin_data] -struct QueueData { - #[pin] - tree: Tree, - irq_mode: IRQMode, - completion_time: Delta, - memory_backed: bool, - block_size: u64, - bad_blocks: Arc, - bad_blocks_once: bool, - bad_blocks_partial_io: bool, +struct HwQueueContext { + page: Option>, } =20 #[pin_data] @@ -503,10 +559,10 @@ fn align_down(value: T, to: T) -> T =20 #[vtable] impl Operations for NullBlkDevice { - type QueueData =3D Pin>; + type QueueData =3D Pin>; type RequestData =3D Pdu; type TagSetData =3D (); - type HwData =3D (); + type HwData =3D Pin>>; =20 fn new_request_data() -> impl PinInit { pin_init!(Pdu { @@ -517,41 +573,39 @@ fn new_request_data() -> impl PinInit { =20 #[inline(always)] fn queue_rq( - _hw_data: (), - queue_data: Pin<&QueueData>, + hw_data: Pin<&SpinLock>, + this: Pin<&Self>, mut rq: Owned>, _is_last: bool, ) -> Result { let mut sectors =3D rq.sectors(); =20 - Self::handle_bad_blocks(&mut rq, queue_data.get_ref(), &mut sector= s)?; - - if queue_data.memory_backed { - let tree =3D &queue_data.tree; + Self::handle_bad_blocks(this.get_ref(), &mut rq, &mut sectors)?; =20 + if this.memory_backed { if rq.command() =3D=3D bindings::req_op_REQ_OP_DISCARD { - Self::discard(tree, rq.sector(), sectors.into(), queue_dat= a.block_size)?; + this.discard(&hw_data, rq.sector(), sectors)?; } else { - Self::transfer(&mut rq, tree, sectors)?; + this.transfer(&hw_data, &mut rq, sectors)?; } } =20 - match queue_data.irq_mode { + match this.irq_mode { IRQMode::None =3D> Self::end_request(rq), IRQMode::Soft =3D> mq::Request::complete(rq.into()), IRQMode::Timer =3D> { OwnableRefCounted::into_shared(rq) - .start(queue_data.completion_time) + .start(this.completion_time) .dismiss(); } } Ok(()) } =20 - fn commit_rqs(_hw_data: (), _queue_data: Pin<&QueueData>) {} + fn commit_rqs(_hw_data: Pin<&SpinLock>, _queue_data: P= in<&Self>) {} =20 - fn init_hctx(_tagset_data: (), _hctx_idx: u32) -> Result { - Ok(()) + fn init_hctx(_tagset_data: (), _hctx_idx: u32) -> Result= { + KBox::pin_init(new_spinlock!(HwQueueContext { page: None }), GFP_K= ERNEL) } =20 fn complete(rq: ARef>) { --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 01DD02DB7A1; Sun, 15 Feb 2026 23:42:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198944; cv=none; b=NZvmUvoNjxsDwezDLO0VFwEcoAjZqb01bFn/E8F61Sge6i9Ulzo07AuAKhvD8sdrmBHU+KLnPvcLxibVekNOzGlrEAJ0wQkQYT7LCrHtIptACf96Ek87PXb+gKQf5jEp1P9I1UwaprOUbF+B+OSQ7Aw2YjF0IcSh5DVkaOIUvWs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198944; c=relaxed/simple; bh=7BsUMdxU+4o8Atmxvi2C2Yk4sut1Zh21/NZvpuROV9U=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Ao3I0cGy+NmhMOtJwxU3oh3yq+pgkBIBb8QfaNatWkSF8QQ4ZHT/rrtjbBpq5i6xppOQHd/1Qv/Mmn71JPT8y5XBHR8dq+6J+BMRkuBa6khyU5IzgQ6Xh7FDs7t7khr5MjwbtuHW/nZzHj13ykJTS069br15x5K3lHUcZMAj5CE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=EegIw3n9; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="EegIw3n9" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3B953C19422; Sun, 15 Feb 2026 23:42:19 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198943; bh=7BsUMdxU+4o8Atmxvi2C2Yk4sut1Zh21/NZvpuROV9U=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=EegIw3n9AdLDkfngVG9lXqk9jft65qhpfo7Y99ZYTJpFjM0kcakmlgeOqfTqa39np CGDtEygqvaUzQVXVkn7uhm35v42HlHKpNJSF6ZWaMThfSXkDbT0d5oZuA29WBFXLSz yPcZJxj4Drwm1X8Od7cbhYIFkTCOpMGP/Mw8JO3JMPzvPFwe2h7VRME0TaLde5DGvU tqQcC3t4+ligYcd1ufKAT3OebAopO/oTInmhAMBA48c98UMtB9Q/eUtdUQzJ/kmKMC JAekf0ru/muJbn6mFt0y4zH+Qzy6ZtH4g3gt9gD2+t9NCIOeNypeXp6jnK2HBP6iEE jP1Jy+fPjLrkw== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:19 +0100 Subject: [PATCH 32/79] block: rust: implement `Sync` for `GenDisk`. 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: <20260216-rnull-v6-19-rc5-send-v1-32-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1028; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=7BsUMdxU+4o8Atmxvi2C2Yk4sut1Zh21/NZvpuROV9U=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklg4ccLaxdetyIDs2ivzwrKOGT9mU3qMRJbLi d6udKXYB6SJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYOAAKCRDhuBo+eShj d/SMD/4/4YwNGwDDtKVPiCoYpIESWdAQIduCAObFrzlsZL/kCIkB0OMU0EMKO2cMmBBT6bBYFd4 qwedoAdYdDByA3piKaKLOQezC5celEc4+F+IphMdiaMg4tBLjAMqFL89thiK2OnvtZbp6OL79rT NBUa7EFfsQiAnB4bZcRRYzklaRthtLFmRqcIdJZGHau7OsWUvw3wj+aPzdlf//RqEN63DtvzKEc mRMVMLqSeZr1ZHQ4CwBFbyEC1yrDP2WFEy6gj0BiiuDexcKL8K/0N/NvzxNK3bCPtUOV/1pHbfU 3uUAbAci131lesi6I+yYt/+QInj/lXfCzn3IKf823uTyiqVLSMPbbOVzKhxVmaTkMeRFucAhRSZ oiHx2upFd/aQAOuBcGrj/OkFuwkvJvIFJxLySCTC8t22KX8GEn6V6j7UuBH5S9W0B1McCBvVeFK XncX2V3/mHuBkbio45kWI+PXeWDVQSAeqXa/60JmUhvrhg9Ch/X1pWqsL712Tu/qQ24BswVQ8aO gI+GMJV7qKO28JtHvCtGjpLDZ1K8M12Y3tsyn1GNqq0EPY0CAlDJY0rrNnaNhFQGgYOlPfz9TCx g06pY4/jCcEw5VJ1WUVg/wtHYiBCYOySd7LvzbXLfFmR8VWI35FnRmPOOxWnuQE2C71SiAefNiI TzllyUbiu4t9g4w== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 `GenDisk` is a pointer to a `struct gendisk`. It is safe to reference this struct from multiple threads. Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq/gen_disk.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_di= sk.rs index 75968d6a57639..72bbf3cadfe82 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -218,6 +218,10 @@ pub struct GenDisk { // `TagSet` It is safe to send this to other threads as long as T is Send. unsafe impl Send for GenDisk {} =20 +// SAFETY: `GenDisk` is an owned pointer to a `struct gendisk` and an `Arc= ` to a `TagSet`. It is +// safe to reference these from multiple threads. +unsafe impl Sync for GenDisk {} + impl Drop for GenDisk { fn drop(&mut self) { // SAFETY: By type invariant of `Self`, `self.gendisk` points to a= valid --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 0DEFB3101BB; Sun, 15 Feb 2026 23:45:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199131; cv=none; b=SDcHfuehN2FRLOfdz9fsPpK7qjABYBvg9fGdgn9iHKxN982wYmD2BqnccOlWBjSLZHufsoQHlt11geP8ZNI4wZDb5RQ9FL9LnqZhi7k672Jcl9J0lJ0TXUnQJaix7GeOyvYC9SWUyalzeB8Xl5RtTAcgW2Bs+rUpYFREUu/GJt0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199131; c=relaxed/simple; bh=qLgrvGBsm8luC4Me0/E3xSd0EWUQhPqRJxd9FYG64Es=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=o+D/q+BTRkWf/CmkbbjchLwB+I/KAGZahFQR3zTZL2RlBCJ5PScyPEtxxqtMlVruC7szX4mZK6BPb+00bYa+Im662++0zzuj3PrvCOo9ED+44zjf2isPSdciPtU4CFyTMkavxe5O8hyJDfgK3Br6xRUv11qeYP2WOZ0wJ5kdzPA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=XgKHcPV4; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="XgKHcPV4" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A9F4DC4CEF7; Sun, 15 Feb 2026 23:45:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199130; bh=qLgrvGBsm8luC4Me0/E3xSd0EWUQhPqRJxd9FYG64Es=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=XgKHcPV4QAuD0r244AZm13XfsgYTI5i0W28mgVl156ZV3FmyeA34v8/B/HdF2AZRP fn12kMgKt8aygP8C+QGVOFi38wllCmJDHUBL1MoetTkeCn44uBLjoJ8nmSk5cyJew2 /NDCDb00AoszqOtPn0BKJZooSay2G06m2u5rSCxDszZQtbLRSWH3+cO3leL9LbMY+R hjgbnEmh1JDIcAMsXT7OEzPLoJy9/fW24lEIREoIbTeN5AH37e978wVqyzQeL4ww1s hRcnFKCi9WCY/gcLEWQGU+Vsspgn9mgoZNWC0dbXdSqHk0ouLw9W4/TqEjsuR1CZUA fE2sEwvljvdpg== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:20 +0100 Subject: [PATCH 33/79] block: rust: add a back reference feature to `GenDisk` 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: <20260216-rnull-v6-19-rc5-send-v1-33-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=5572; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=qLgrvGBsm8luC4Me0/E3xSd0EWUQhPqRJxd9FYG64Es=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklg5BJGIL8taDlyK7FXYOiW5WtQkD0IpNT66B xLE+gKs5UWJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYOQAKCRDhuBo+eShj d3LWD/wJEsfnfKofG0SwluYFnyTS45Ttty3Rtjxsq4+AtupouLO2AgmdqLPg3JbLfCwFLSujmDo IOchozOWKm8dC8h3VsaXsc0SABrxNZq/tnBzy5RexaTQSkIHM5bJ9ifkIFYHZWcHJIGojq9wQm4 LB47jsWD2AUovrxJQKow476RNvnDMBnkljJ2Rg1vB9+LApUxwQLEISWlLXXyy3bB+eqXridTC1q Hgps3tI+E0p/DGd4jFScyPxRuh8y5POxTm7oDuLgo3o8JwTSwuPcxFJ6eR6Kuxn882TEi+PHki8 fxs7A2SUBAIDTTRkLwpxROC7vwFQ+3ESHbbQLtvRSrtTuDmzaew4BaSAPapahnyGgdAV2T7Sfmj V5/HP7BcVlLRnGiqlJpl9pgPNUb0ggHHDDWKnHZk9pIDhuqWT4Hq0axj/LuPK97KerOVbLC4vDP hJGSAkUCIOSjr8vvU6CtJcqmldmX3SapQ6iQbD3DGA5DSKdRevhY6phf8SL/3v+oKL+O1pSaw9q +0QQzn/DD7/JFrnFPH/Vf7XwSP1ZGshRCh6Js7FriyGvqr8T7Rc/FcKTpNox2xPCuDyjP3jz57h jdT3zxo/L91N9XT/8u3v7cH+Kmz44M1dlBfxhVuhkyMFUuKB0bAnZGfCVrGqnLlZx2PwKZF4Als eC1laFvJ1AC8gmg== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 During certain block layer callbacks, drivers may need access to the Rust `GenDisk` representing a disk the driver is managing. In some situations it is only possible to obtain a pointer to the C `struct gendisk`. With the current setup, it is not possible to obtain the `GenDisk` for this C `gendisk`. To circumvent this, we add a back reference feature to the `GenDisk` so that we can store a reference counted reference to the `GenDisk` somewhere easily accessible. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 2 +- drivers/block/rnull/rnull.rs | 4 +-- rust/kernel/block/mq/gen_disk.rs | 62 ++++++++++++++++++++++++++++++++++++= ---- 3 files changed, 59 insertions(+), 9 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index d679f12ee6749..e365eb06be6de 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -189,7 +189,7 @@ struct DeviceConfigInner { capacity_mib: u64, irq_mode: IRQMode, completion_time: time::Delta, - disk: Option>, + disk: Option>>, memory_backed: bool, submit_queues: u32, home_node: i32, diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index cca497aef40df..aa59ede72e495 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -136,7 +136,7 @@ struct NullBlkModule { #[pin] configfs_subsystem: kernel::configfs::Subsystem, #[pin] - param_disks: Mutex>>, + param_disks: Mutex>>>, } =20 impl kernel::InPlaceModule for NullBlkModule { @@ -218,7 +218,7 @@ struct NullBlkDevice { } =20 impl NullBlkDevice { - fn new(options: NullBlkOptions<'_>) -> Result> { + fn new(options: NullBlkOptions<'_>) -> Result>> { let NullBlkOptions { name, block_size, diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_di= sk.rs index 72bbf3cadfe82..8d39bb70725b0 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -11,11 +11,13 @@ error::{self, from_err_ptr, Result}, fmt::{self, Write}, prelude::*, + revocable::Revocable, static_lock_class, str::NullTerminatedFormatter, - sync::Arc, + sync::{Arc, UniqueArc}, types::{ForeignOwnable, ScopeGuard}, }; +use core::ptr::NonNull; =20 /// A builder for [`GenDisk`]. /// @@ -112,7 +114,7 @@ pub fn build( name: fmt::Arguments<'_>, tagset: Arc>, queue_data: T::QueueData, - ) -> Result> { + ) -> Result>> { let data =3D queue_data.into_foreign(); let recover_data =3D ScopeGuard::new(|| { // SAFETY: T::QueueData was created by the call to `into_forei= gn()` above @@ -194,10 +196,28 @@ pub fn build( // INVARIANT: `gendisk` was added to the VFS via `device_add_disk`= above. // INVARIANT: `gendisk.queue.queue_data` is set to `data` in the c= all to // `__blk_mq_alloc_disk` above. - Ok(GenDisk { - _tagset: tagset, - gendisk, - }) + let mut disk =3D UniqueArc::new( + GenDisk { + _tagset: tagset, + gendisk, + backref: Arc::pin_init( + // INVARIANT: We break `GenDiskRef` invariant here, bu= t we restore it below. + Revocable::new(GenDiskRef(NonNull::dangling())), + GFP_KERNEL, + )?, + }, + GFP_KERNEL, + )?; + + disk.backref =3D Arc::pin_init( + // INVARIANT: The `GenDisk` in `disk` is a valid for use as a = reference. + Revocable::new(GenDiskRef( + NonNull::new(disk.as_ptr().cast_mut()).expect("Should not = be null"), + )), + GFP_KERNEL, + )?; + + Ok(disk.into()) } } =20 @@ -212,6 +232,14 @@ pub fn build( pub struct GenDisk { _tagset: Arc>, gendisk: *mut bindings::gendisk, + backref: Arc>>, +} + +impl GenDisk { + /// Get a `GenDiskRef` referencing this `GenDisk`. + pub fn get_ref(&self) -> Arc>> { + self.backref.clone() + } } =20 // SAFETY: `GenDisk` is an owned pointer to a `struct gendisk` and an `Arc= ` to a @@ -241,3 +269,25 @@ fn drop(&mut self) { drop(unsafe { T::QueueData::from_foreign(queue_data) }); } } + +/// A reference to a `GenDisk`. +/// +/// # Invariants +/// +/// `self.0` is valid for use as a reference. +pub struct GenDiskRef(NonNull>); + +// SAFETY: It is safe to transfer ownership of `GenDiskRef` across thread = boundaries. +unsafe impl Send for GenDiskRef {} + +// SAFETY: It is safe to share references to `GenDiskRef` across thread bo= undaries. +unsafe impl Sync for GenDiskRef {} + +impl core::ops::Deref for GenDiskRef { + type Target =3D GenDisk; + + fn deref(&self) -> &Self::Target { + // SAFETY: By type invariant, `self.0` is valid for use as a refer= ence. + unsafe { self.0.as_ref() } + } +} --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 BB2BC2DC77A; Sun, 15 Feb 2026 23:43:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199036; cv=none; b=KTCH/EnwJke/npVad6bwCNNoWeYEIxG1s2KX0OTOout3+mu4EVNj9LTQf/oMS+x580Lx76MbacaCyI63BRKGbaaS4uv8PHLPNYdsBWSytMnWw4CBQEjtAGQtIIjbq/1KgLePMzmNvNU8V+fKeIuV2hByemVWYNVUxZ5bavUJsw0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199036; c=relaxed/simple; bh=bBQ8yAcFgCBTwCDBWmKONawvvu9tlBRUpgl5zlaS9nw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=nhDICN7ijL/ZdqUgsU79ksO96kwoQaXnKari++X0mZDgUJP/UeenXro7f/yRmgLNP/ZqGndDO2+mQfsRkGjFNDQJFMsOORzhzxnFkspmGE5j2nU85/tAcwQN+D5n94u46CBNSdkVihSnNyp4cOJP4ikBgz1b4evHfiDLYdfPz6w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=n6UrUbRs; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="n6UrUbRs" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5095FC4CEF7; Sun, 15 Feb 2026 23:43:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199036; bh=bBQ8yAcFgCBTwCDBWmKONawvvu9tlBRUpgl5zlaS9nw=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=n6UrUbRsU/eilmTFtnqrnuOQKsNAklT+OH9G9flbdBGqPlLm0qv+OggfwWEMbrrMB Yn74kcGGmJRJFyn2vrxb4WcIcs9u6tFPY2bo5plpoOLDqGJLuJeTtB3TGaEnVvM8xN etiyNQyo1q3gwIqw/f10uNDPobs0FyEbgvEjP96Ju0L4jKqg5Vl/G6pjUwj/qRuQhf RUhR8lXMzv+MUzYX1Olb2IbPgQkD/S2THEAz620BIdE3w13KFGw2Nc4sTnx7wPOHyC qdRPkNLHooJYpEjaVvo1m4gCyQtT/enAIILBRIpkbbmDYFeJa7nNsFTIj/q1SW03h0 /8hJ9B2n3BpsQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:21 +0100 Subject: [PATCH 34/79] block: rust: introduce an idle type state for `Request` 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: <20260216-rnull-v6-19-rc5-send-v1-34-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=13824; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=bBQ8yAcFgCBTwCDBWmKONawvvu9tlBRUpgl5zlaS9nw=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklg6eftWJsbtc7ccT5eFMt9qD5FJDBCfQB9cw qVSpUuNqMaJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYOgAKCRDhuBo+eShj dxT7EACC2jKTAH6h3LBY12TZ2SyfT6bjGFjPmw/FiOhMk2oeO+YHkqt2kEUTxtWXY1Fyz+kYQVA BwIALV/KI5VtSHgnVABoOv1g9N1To0X/DMhJCQ7v8LjjJ8/Gb+MDgl0RD5m8/4vfZMq49uUmztX PqTGCObV+CZrYmzh3OY0dIjcKu8YSZMJweQ7dKm93/40dwFUVCGRiqYsATIX0+1DKkY8T99xH+s PpbgKRo4U2pjbUOnGc4VEeanV7F/gbwxiU62W3YktWG9uoa4eEH7w4FcqIxRmhqUv+5Q2Ag7qxX ap8tS2XW9hVLV1cwgA+dbatAF1Ta9yZioFY7mJ2Q7vnmDvDZ9vIFbQEI5j4Cg9EwBzvnctdUUem sxSClSrn2/p9JsYZ/Qgd+FNa+xrOIQ4pSIOoLj0X2nYZ6QqIqRkQSSFc+UkTfjUeOuUctrPnuAD x5Fa9OrI+wqK8m0JCSI3olhEdJ/ZXcJe1WCVklaAGFa4rsxjXpoUMRwE+mJxcok6Uxcyt1bRPbp dcC0iTrmW+oWBaXXgawIZmfpBLeUg7RE588FHzeYIn3B10XoMH40boiJrq8OHDrG3/2pt7w+KKb p24ynnpY1kOiqse2xzxQDSijN0+GrNHZH+mYDVjZXUEmM/kxDNqArE53275wlEorov82Im7fgwa uRkoFc1wpUn0LPA== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Block device drivers need to invoke `blk_mq_start_request` on a request to indicate that they have started processing the request. This function may only be called once after a request has been issued to a driver. For Rust block device drivers, the Rust abstractions handle this call. However, in some situations a driver may want to control when a request is started. Thus, expose the start method to Rust block device drivers. To ensure the method is not called more than once, introduce a type state for `Request`. Requests are issued as `IdleRequest` and transition to `Request` when the `start` method is called. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/rnull.rs | 3 +- rust/kernel/block/mq.rs | 5 +- rust/kernel/block/mq/operations.rs | 13 ++-- rust/kernel/block/mq/request.rs | 155 ++++++++++++++++++++++++++++++---= ---- 4 files changed, 136 insertions(+), 40 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index aa59ede72e495..034ddc06eabf9 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -575,9 +575,10 @@ fn new_request_data() -> impl PinInit { fn queue_rq( hw_data: Pin<&SpinLock>, this: Pin<&Self>, - mut rq: Owned>, + rq: Owned>, _is_last: bool, ) -> Result { + let mut rq =3D rq.start(); let mut sectors =3D rq.sectors(); =20 Self::handle_bad_blocks(this.get_ref(), &mut rq, &mut sectors)?; diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index 3eab3ca8f5480..ab493bd91af4c 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -88,10 +88,10 @@ //! fn queue_rq( //! _hw_data: (), //! _queue_data: (), -//! rq: Owned>, +//! rq: Owned>, //! _is_last: bool //! ) -> Result { -//! rq.end_ok(); +//! rq.start().end_ok(); //! Ok(()) //! } //! @@ -130,6 +130,7 @@ pub mod tag_set; =20 pub use operations::Operations; +pub use request::IdleRequest; pub use request::Request; pub use request::RequestTimerHandle; pub use tag_set::TagSet; diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/oper= ations.rs index 6641c340d87f4..fb75d65f67071 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -6,13 +6,13 @@ =20 use crate::{ bindings, - block::mq::{request::RequestDataWrapper, Request}, + block::mq::{request::RequestDataWrapper, IdleRequest, Request}, error::{from_result, Result}, prelude::*, sync::{aref::ARef, atomic::ordering, Refcount}, types::{ForeignOwnable, Owned}, }; -use core::{marker::PhantomData, ptr::NonNull}; +use core::marker::PhantomData; use pin_init::PinInit; =20 type ForeignBorrowed<'a, T> =3D ::Borrowed<'a>; @@ -60,7 +60,7 @@ pub trait Operations: Sized { fn queue_rq( hw_data: ForeignBorrowed<'_, Self::HwData>, queue_data: ForeignBorrowed<'_, Self::QueueData>, - rq: Owned>, + rq: Owned>, is_last: bool, ) -> Result; =20 @@ -132,14 +132,14 @@ impl OperationsVTable { =3D=3D 0 ); =20 + // INVARIANT: By C API contract, `bd.rq` has not been started yet. // SAFETY: // - By API contract, we own the request. // - By the safety requirements of this function, `request` is a = valid // `struct request` and the private data is properly initialize= d. // - `rq` will be alive until `blk_mq_end_request` is called and = is // reference counted by until then. - let mut rq =3D - unsafe { Owned::from_raw(NonNull::>::new_unchecked(= (*bd).rq.cast())) }; + let rq =3D unsafe { IdleRequest::from_raw((*bd).rq) }; =20 // SAFETY: The safety requirement for this function ensure that `h= ctx` // is valid and that `driver_data` was produced by a call to @@ -155,9 +155,6 @@ impl OperationsVTable { // dropped, which happens after we are dropped. let queue_data =3D unsafe { T::QueueData::borrow(queue_data) }; =20 - // SAFETY: We have exclusive access and we just set the refcount a= bove. - unsafe { rq.start_unchecked() }; - let ret =3D T::queue_rq( hw_data, queue_data, diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index 38289b9f966fa..f6addd20624a9 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -15,13 +15,111 @@ time::hrtimer::{ HasHrTimer, HrTimer, HrTimerCallback, HrTimerHandle, HrTimerMode, = HrTimerPointer, }, - types::{Opaque, Ownable, OwnableRefCounted, Owned}, + types::{ForeignOwnable, Opaque, Ownable, OwnableRefCounted, Owned}, }; -use core::{ffi::c_void, marker::PhantomData, ptr::NonNull}; +use core::{ffi::c_void, marker::PhantomData, ops::Deref, ptr::NonNull}; =20 use crate::block::bio::Bio; use crate::block::bio::BioIterator; =20 +/// A [`Request`] that a driver has not yet begun to process. +/// +/// A driver can convert an `IdleRequest` to a [`Request`] by calling [`Id= leRequest::start`]. +/// +/// # Invariants +/// +/// - This request has not been started yet. +#[repr(transparent)] +pub struct IdleRequest(RequestInner); + +impl IdleRequest { + /// Mark the request as processing. + /// + /// This converts the [`IdleRequest`] into a [`Request`]. + pub fn start(self: Owned) -> Owned> { + // SAFETY: By type invariant `self.0.0` is a valid request. Becaus= e we have an `Owned<_>`, + // the refcount is zero. + let mut request =3D unsafe { Request::from_raw(self.0 .0.get()) }; + + debug_assert!( + request + .wrapper_ref() + .refcount() + .as_atomic() + .load(ordering::Acquire) + =3D=3D 0 + ); + + // SAFETY: We have exclusive access and the refcount is 0. By type= invariant `request` was + // not started yet. + unsafe { request.start_unchecked() }; + + request + } + + /// Create a [`Self`] from a raw request pointer. + /// + /// # Safety + /// + /// - The request pointed to by `ptr` must satisfythe invariants of bo= th [`Request`] and + /// [`Self`]. + /// - The refcount of the request pointed to by `ptr` must be 0. + pub(crate) unsafe fn from_raw(ptr: *mut bindings::request) -> Owned { + // SAFETY: By function safety requirements, `ptr` is valid for use= as an `IdleRequest`. + unsafe { Owned::from_raw(NonNull::::new_unchecked(ptr.cast()= )) } + } +} + +// SAFETY: The `release` implementation leaks the `IdleRequest`, which is = a valid state for a +// [`Request`] with refcount 0. +unsafe impl Ownable for IdleRequest { + unsafe fn release(_this: NonNull) {} +} + +impl Deref for IdleRequest { + type Target =3D RequestInner; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +pub struct RequestInner(Opaque, PhantomData); + +impl RequestInner { + /// Get the command identifier for the request + pub fn command(&self) -> u32 { + // SAFETY: By C API contract and type invariant, `cmd_flags` is va= lid for read + unsafe { (*self.0.get()).cmd_flags & ((1 << bindings::REQ_OP_BITS)= - 1) } + } + + /// Get the target sector for the request. + #[inline(always)] + pub fn sector(&self) -> u64 { + // SAFETY: By type invariant of `Self`, `self.0` is valid and live. + unsafe { (*self.0.get()).__sector } + } + + /// Get the size of the request in number of sectors. + #[inline(always)] + pub fn sectors(&self) -> u32 { + self.bytes() >> crate::block::SECTOR_SHIFT + } + + /// Get the size of the request in bytes. + #[inline(always)] + pub fn bytes(&self) -> u32 { + // SAFETY: By type invariant of `Self`, `self.0` is valid and live. + unsafe { (*self.0.get()).__data_len } + } + + /// Borrow the queue data from the request queue associated with this = request. + pub fn queue_data(&self) -> ::Borrowed= <'_> { + // SAFETY: By type invariants of `Request`, `self.0` is a valid re= quest. + unsafe { T::QueueData::borrow((*(*self.0.get()).q).queuedata) } + } +} + /// A wrapper around a blk-mq [`struct request`]. This represents an IO re= quest. /// /// # Implementation details @@ -58,9 +156,28 @@ /// [`struct request`]: srctree/include/linux/blk-mq.h /// #[repr(transparent)] -pub struct Request(Opaque, PhantomData); +pub struct Request(RequestInner); + +impl Deref for Request { + type Target =3D RequestInner; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} =20 impl Request { + /// Create a `Owned` from a request pointer. + /// + /// # Safety + /// + /// - `ptr` must satisfy invariants of `Request`. + /// - The refcount of the request pointed to by `ptr` must be 0. + pub(crate) unsafe fn from_raw(ptr: *mut bindings::request) -> Owned { + // SAFETY: By function safety requirements, `ptr` is valid for use= as `Owned`. + unsafe { Owned::from_raw(NonNull::::new_unchecked(ptr.cast()= )) } + } + /// Create an [`ARef`] from a [`struct request`] pointer. /// /// # Safety @@ -78,12 +195,6 @@ pub(crate) unsafe fn aref_from_raw(ptr: *mut bindings::= request) -> ARef { unsafe { ARef::from_raw(NonNull::new_unchecked(ptr.cast())) } } =20 - /// Get the command identifier for the request - pub fn command(&self) -> u32 { - // SAFETY: By C API contract and type invariant, `cmd_flags` is va= lid for read - unsafe { (*self.0.get()).cmd_flags & ((1 << bindings::REQ_OP_BITS)= - 1) } - } - /// Complete the request by scheduling `Operations::complete` for /// execution. /// @@ -106,7 +217,7 @@ pub fn complete(this: ARef) { pub fn bio(&self) -> Option<&Bio> { // SAFETY: By type invariant of `Self`, `self.0` is valid and the = deref // is safe. - let ptr =3D unsafe { (*self.0.get()).bio }; + let ptr =3D unsafe { (*self.0 .0.get()).bio }; // SAFETY: By C API contract, if `bio` is not null it will have a // positive refcount at least for the duration of the lifetime of // `&self`. @@ -118,7 +229,7 @@ pub fn bio(&self) -> Option<&Bio> { pub fn bio_mut(&mut self) -> Option<&mut Bio> { // SAFETY: By type invariant of `Self`, `self.0` is valid and the = deref // is safe. - let ptr =3D unsafe { (*self.0.get()).bio }; + let ptr =3D unsafe { (*self.0 .0.get()).bio }; // SAFETY: By C API contract, if `bio` is not null it will have a // positive refcount at least for the duration of the lifetime of // `&self`. @@ -132,25 +243,11 @@ pub fn bio_iter_mut<'a>(self: &'a mut Owned) ->= BioIterator<'a> { // `NonNull::new` will return `None` if the pointer is null. BioIterator { // SAFETY: By type invariant `self.0` is a valid `struct reque= st`. - bio: NonNull::new(unsafe { (*self.0.get()).bio.cast() }), + bio: NonNull::new(unsafe { (*self.0 .0.get()).bio.cast() }), _p: PhantomData, } } =20 - /// Get the target sector for the request. - #[inline(always)] - pub fn sector(&self) -> u64 { - // SAFETY: By type invariant of `Self`, `self.0` is valid and live. - unsafe { (*self.0.get()).__sector } - } - - /// Get the size of the request in number of sectors. - #[inline(always)] - pub fn sectors(&self) -> u32 { - // SAFETY: By type invariant of `Self`, `self.0` is valid and live. - (unsafe { (*self.0.get()).__data_len }) >> crate::block::SECTOR_SH= IFT - } - /// Return a pointer to the [`RequestDataWrapper`] stored in the priva= te area /// of the request structure. /// @@ -289,10 +386,10 @@ impl Owned> { /// `self.wrapper_ref().refcount() =3D=3D 0`. /// /// This can only be called once in the request life cycle. - pub(crate) unsafe fn start_unchecked(&mut self) { + pub unsafe fn start_unchecked(&mut self) { // SAFETY: By type invariant, `self.0` is a valid `struct request`= and // we have exclusive access. - unsafe { bindings::blk_mq_start_request(self.0.get()) }; + unsafe { bindings::blk_mq_start_request(self.0 .0.get()) }; } =20 /// Notify the block layer that the request has been completed without= errors. @@ -302,7 +399,7 @@ pub fn end_ok(self) { =20 /// Notify the block layer that the request has been completed. pub fn end(self, status: u8) { - let request_ptr =3D self.0.get().cast(); + let request_ptr =3D self.0 .0.get().cast(); core::mem::forget(self); // SAFETY: By type invariant, `this.0` was a valid `struct request= `. The // existence of `self` guarantees that there are no `ARef`s pointi= ng to --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 DD1672DEA75; Sun, 15 Feb 2026 23:43:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199006; cv=none; b=Ox79QtOY8VyGhLFMzhD9f8xRjFP6vfJm0cYtRQ97TLXm66rOXLvSYmgvfgfvm+/sN7K6ZEv6DjK8D1C5KCSaFKmAl+F2I7ZOcBu2vU2gvqfmo0CHsG36LtDklOQJyw72+L3Bhe4PHNbx8SHGHYf89EjWhp/aCTFvHdJ0AeOFt6E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199006; c=relaxed/simple; bh=GOcUVkfhEDgQ6ums41j+Tcm2cMd8mS+PY9bUD8N/Dz4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=QSBSUWbjutHp82c9DUNUW2tw5z1k12iojr9iI8aHVgJSxznODvYQYe7LRTQ5+ja0vjrmbj97sylJltY3PHgYNFDYDjdwk8tAsU/f8/dhzvvmTteyG48mYwFC1h0BJD67ZMBoZjzw6ZjYrbyh7nboGBoLZIbtXMbyNjij5zFua5U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=CEh1yGSV; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="CEh1yGSV" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 868A8C4CEF7; Sun, 15 Feb 2026 23:43:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199006; bh=GOcUVkfhEDgQ6ums41j+Tcm2cMd8mS+PY9bUD8N/Dz4=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=CEh1yGSV4a2uzg4l1d8GdZvR6yZGxx7P7Qp19et8nDKk87JP6n/W2djZHGYWtxMjg xw+EaAHFULi6Ck4l0KLmPK8pBNcVTQ1HM1Ab5oGi/gq2AUEr7zkVw2ZhQ9LYOXu0VD kjsv2DUxGLa+M7WN+pzvKRqMI9CY7cBRPjqpNc5ntnQ78nmbXR0hVh7VIm7/rVO0IR 1gZwASzgkhoLJTgr/yss18vkijvUai4uQBP52xl7Q8Qv/G7i4FQ4fEay4vViDKa44J L1C5fkCHhFRhJrLwQgyWqtt3jiRYxaHp2A6yB3a2ZGwnM/vgcWwnbtp14j/EEFdhvo /KtnHR0r+p0ew== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:22 +0100 Subject: [PATCH 35/79] block: rust: add a request queue abstraction 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: <20260216-rnull-v6-19-rc5-send-v1-35-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4571; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=GOcUVkfhEDgQ6ums41j+Tcm2cMd8mS+PY9bUD8N/Dz4=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklg7bfZv+rauUSPeVBEpQwOIbacR5CrbTYZXo PobSHvGjUuJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYOwAKCRDhuBo+eShj d3NJEACHSSyaGc+RMfkrlUmWfU1AtFHObfNulQGbxBg6L2E1hcP0cdHbohuIIy2z9/jsqfse9qF FbfpGQewVILxCWZezHYlNWhoF9B3ljJCtWHX8p45y9jpY/cfFzWD0gHqfw0LkgDUlDWuTlWeIwP 7QO4vFDpWeLnyP9BzQbomsIqrMFPk+E8LBE9PPp5B4303uUZ6smVXwjzO+IPqoyHiAwlcg6DYPp FRevzPnyuGu4QJ87rYrEiItLuVPx0hMrFZ1gTa+dUVUll6pT7CndefvgPnodZIE+aTLBtkOtC26 ibv92WGg+ijvchbPSJEr6o19Dk7D5ZPfWkowuIHQQaaHHax1wi3P0c8mHMWWqFN9XkTZJlwDIuh ysBlapM5bvHDWwaUzfDgKgfAwYeZtUypFUvuiSCI2V9fB1wf827jlp2KACfjODUJBdmthe7Psyb qsbnFaoexmSUli3fc3O40lsjpMXXTYdzipL5J1IGaa9pxVoPszg8ktZ3op6kHZrGnvzpc7BoB3G Gp6f5u5qfIuqZDYil4kPxy4jHyvUQ+RmN5spsToBgI5vanTJ+p1ezvCzG856V59k5Gkp06R79vL 7YVT/u4ZZRUkUdezMF26/PyB62LyLS3JL3Oc+RBGEZvLcpUzM8KApphdSE/Fme9GLs806hbi5KL kalmWM7O0IJ19/w== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add the `RequestQueue` type as a Rust abstraction for `struct request_queue`. This type provides methods to access the request queue associated with a `GenDisk` or `Request`. The abstraction exposes queue-related functionality needed by block device drivers. Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq.rs | 2 ++ rust/kernel/block/mq/gen_disk.rs | 8 ++++- rust/kernel/block/mq/request_queue.rs | 57 +++++++++++++++++++++++++++++++= ++++ 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index ab493bd91af4c..a898dda2635e5 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -127,10 +127,12 @@ pub mod gen_disk; mod operations; mod request; +mod request_queue; pub mod tag_set; =20 pub use operations::Operations; pub use request::IdleRequest; pub use request::Request; pub use request::RequestTimerHandle; +pub use request_queue::RequestQueue; pub use tag_set::TagSet; diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_di= sk.rs index 8d39bb70725b0..8a22767f1b916 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -7,7 +7,7 @@ =20 use crate::{ bindings, - block::mq::{Operations, TagSet}, + block::mq::{Operations, RequestQueue, TagSet}, error::{self, from_err_ptr, Result}, fmt::{self, Write}, prelude::*, @@ -240,6 +240,12 @@ impl GenDisk { pub fn get_ref(&self) -> Arc>> { self.backref.clone() } + + /// Get the [`RequestQueue`] associated with this [`GenDisk`]. + pub fn queue(&self) -> &RequestQueue { + // SAFETY: By type invariant, self is a valid gendisk. + unsafe { RequestQueue::from_raw((*self.gendisk).queue) } + } } =20 // SAFETY: `GenDisk` is an owned pointer to a `struct gendisk` and an `Arc= ` to a diff --git a/rust/kernel/block/mq/request_queue.rs b/rust/kernel/block/mq/r= equest_queue.rs new file mode 100644 index 0000000000000..7d846ea423730 --- /dev/null +++ b/rust/kernel/block/mq/request_queue.rs @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 + +use super::Operations; +use crate::types::{ForeignOwnable, Opaque}; +use core::marker::PhantomData; + +/// A structure describing the queues associated with a block device. +/// +/// Owned by a [`GenDisk`]. +/// +/// # Invariants +/// +/// - `self.0` is a valid `bindings::request_queue`. +/// - `self.0.queuedata` is a valid `T::QueueData`. +#[repr(transparent)] +pub struct RequestQueue(Opaque, PhantomData= ); + +impl RequestQueue +where + T: Operations, +{ + /// Create a [`RequestQueue`] from a raw `bindings::request_queue` poi= nter + /// + /// # Safety + /// + /// - `ptr` must be valid for use as a reference for the duration of `= 'a`. + /// - `ptr` must have been initialized as part of [`GenDiskBuilder::bu= ild`]. + pub(crate) unsafe fn from_raw<'a>(ptr: *const bindings::request_queue)= -> &'a Self { + // INVARIANT: + // - By function safety requirements, `ptr` is a valid `request_qu= eue`. + // - By function safety requirement `ptr` was initialized by [`Gen= DiskBuilder::build`], and + // thus `queuedata` was set to point to a valid `T::QueueData`. + // + // SAFETY: By function safety requirements `ptr` is valid for use = as a reference. + unsafe { &*ptr.cast() } + } + + /// Get the driver private data associated with this [`RequestQueue`]. + pub fn queue_data(&self) -> ::Borrowed= <'_> { + // SAFETY: By type invariant, `queuedata` is a valid `T::QueueData= `. + unsafe { T::QueueData::borrow((*self.0.get()).queuedata) } + } + + /// Stop all hardware queues of this [`RequestQueue`]. + pub fn stop_hw_queues(&self) { + // SAFETY: By type invariant, `self.0` is a valid `request_queue`. + unsafe { bindings::blk_mq_stop_hw_queues(self.0.get()) } + } + + /// Start all hardware queues of this [`RequestQueue`]. + /// + /// This function will mark the queues as ready and if necessary, sche= dule the queues to run. + pub fn start_stopped_hw_queues_async(&self) { + // SAFETY: By type invariant, `self.0` is a valid `request_queue`. + unsafe { bindings::blk_mq_start_stopped_hw_queues(self.0.get(), tr= ue) } + } +} --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 6419A2E54BD; Sun, 15 Feb 2026 23:46:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199206; cv=none; b=G12GQkqi9Qo/ip5Jrtl1IdHrpKIrIivzkwsjcBUbMptMN70pSNhuu07lRW0WwMhX4xE1iRpMBVCRYj+cXjQ4liuHzoENjgF3FkdkQ0o6h/hevWPs1Vo6oZdyxIHk75zgUfozMu7kL3Ec3RmeDO56WbH2ntneRVXTHXX8U3WVqbM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199206; c=relaxed/simple; bh=eohGXCOnx46jHczP9dAsSKL7WJgwmz4UPPhsJskp2tU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=p1Ki8uS56dIxe6AXibocalg9EXpkhTwv+75/trR94CFnCoxAi8y9bZF9e0ADB86wzS/M64JrEW8MYTuZfKYKyG0YohPE/gkeIDXnWQkjKrnkOAbloOR3+LPDxXhkirj4HLd8U3J3nwB3UlEOHqKumQy2X85fDFGWLa8BkA6YhRg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Xex8n73Q; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Xex8n73Q" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8CDD6C4CEF7; Sun, 15 Feb 2026 23:46:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199206; bh=eohGXCOnx46jHczP9dAsSKL7WJgwmz4UPPhsJskp2tU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Xex8n73Q8oXjVBQEhoMXO54rl2DX5CR0ALwLFLO6wSWHw9oa+AJtEpumCla9vvHTS cWky+W7GKoCkzcPnxUSUx4Ph6ZGIkmwxIFgqKANolRlRT1Luv49qRTNEXJFWCugtOx keUg99C7sSNkqH+E1HYB79RPxj8RLs8d6IMV9N2BZXfDeUfnpT7tdr/paRgsYBalj1 VSEpSe67DAfItIHRJ0bDN6XoapdfEFibjCXHABNZ51/isypweM5hOwH7LI6ukQ7uHo Qj8l4UB0Wzv/l3+Kf5KEwd2inblhNXXpbr/rSD+NcdSV7QxIzPXz8SgjjWI24xaZ9a KHZjk743vIghw== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:23 +0100 Subject: [PATCH 36/79] block: rust: add a method to get the request queue for a request 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: <20260216-rnull-v6-19-rc5-send-v1-36-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1245; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=eohGXCOnx46jHczP9dAsSKL7WJgwmz4UPPhsJskp2tU=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklg8dg/PhpsHgOFH3l0YMtC6LWUuqH43wi3Py 0nPnS34BySJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYPAAKCRDhuBo+eShj dznwD/sHTnyIp1mixWF4FtiLgwKs1LYUBAJTXKWLW8mnumj2jqbZtIVbjsea2ML8uKpUJ7IbxR5 uEhUV922BCO+J72fsH3SuhWkOsqk7VKvKO2jVlo6XgaDr4KPKkcyCoGMeGxGyRFc5NK0qBqXXNd r++7HEvsG9RWayIwg911zold+v7hJBwXp+zxaYqKMHUlsrTCG3pwZdRNfgESwjZQbVq3quEBgNs WNNnnNoqv9hIxgKG2wcF9Lx/6/OPIGG+hF/4r+jxNsLsmKbKPG/42YG9zKrPx0keXI+AGmLiDSi o7rJSCdbQ8ClM99EgGiiqWzejr8vW2ZIV8qTUh2n7s59hSDakHsS//EgSvfHLVgD7XV2FIZTCJR T7Kb9R+QxyWFoOV2QqEHmSnVcDN4sdlYIqIhl1ETDh8smFzqiw59dOs/tqTK/4LmEqSl6lN/Jr6 S5spzhexPyf9jct1+zxmYdbtTRmAulBiSLhKSe5HpZMwIJgF81wApWjWisB/bSfZkzuWQ11QDSp myA+I+Uh8wnqQ7M/upI1KePq77rKS1xhYGcjus40ZpPcSl6C/o2V4Cn7UIrprwoQ1XuCf/8beoY kexMxbatftJ8+d9HSAo6zePfqczhqFkQxRC91+69ObBzho5w91lgQaMh1TMPt5TgBPYqxee1Gc0 XbSMW0Ka3eI9ldA== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a method to `Request` for obtaining the associated `RequestQueue`. Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq/request.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index f6addd20624a9..d6f3ffde1c5c4 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -19,6 +19,7 @@ }; use core::{ffi::c_void, marker::PhantomData, ops::Deref, ptr::NonNull}; =20 +use super::RequestQueue; use crate::block::bio::Bio; use crate::block::bio::BioIterator; =20 @@ -118,6 +119,12 @@ pub fn queue_data(&self) -> ::Borrowed<'_> { // SAFETY: By type invariants of `Request`, `self.0` is a valid re= quest. unsafe { T::QueueData::borrow((*(*self.0.get()).q).queuedata) } } + + /// Get the request queue associated with this request. + pub fn queue(&self) -> &RequestQueue { + // SAFETY: By type invariant, self.0 is guaranteed to be valid. + unsafe { RequestQueue::from_raw((*self.0.get()).q) } + } } =20 /// A wrapper around a blk-mq [`struct request`]. This represents an IO re= quest. --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 E67973101D2; Sun, 15 Feb 2026 23:45:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199136; cv=none; b=gYJl9zqeaiXo0cWEjXl9b1eCNHERdYVPWv9+RpFNYlr8hclDF9eEmwzEA+4QDpR/9HeSRAuFaWmzQ4SXokDPcWqDt4i5yMByWFPMuhNYRXXo7ZnFHHeCHsgx0DmlJIyRGCtuA/Xne3dixOOx1zLaBCaZE4vEPRlsQsRVFnYlYwE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199136; c=relaxed/simple; bh=eqqiqphigEUutWAOj4Ax0TLofmOBmjKS7MiYG2xrZGY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=XhRIhH4yXbI5FjiPQxaxisqcTLPuzzPe7rAKI7ATrZKplVKRRCkNCtcARXJD3I9MuCwRlrcBc+lscWNvEJJZCq8U7WiCKPJGqj089nPJY/oU0U/442Elrz64cj2DoZEdUw3RT1ZbrfuusUV+MNtFliPk6kS2c9jNclUnYSBtuYQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=PXkyxUVa; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="PXkyxUVa" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 87F62C4CEF7; Sun, 15 Feb 2026 23:45:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199135; bh=eqqiqphigEUutWAOj4Ax0TLofmOBmjKS7MiYG2xrZGY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=PXkyxUVaJ59UKvQfextkryIKOLnj0O7g6KlrZmEkei4tuELxuOVRJCDq31Hg5833h BQZjppCaKVSsRTdhzVgrz1pOTRkTpNb77Mp5u7MeVo582ftkRUXFAY4QCCjbMPMis1 kcrLlapeozPYfWMfHq72Y1NB7LYak5RLftIk0l21qISB5etNhRrh70D92+KyzXadlf tulp0rVMhXnKWttVpro5dbBXp2dRY1R/zp63D7thp81nm1NZJ8xQbr9lMb83zl+hnJ gWx0R0QpQeKOQkI2VsiU8X9LPRc20qQ/NjBE1/+T/f9lMsLNe2qQgK4bpEgCJgZ9FU 1/uEUke51zOFA== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:24 +0100 Subject: [PATCH 37/79] block: rust: introduce `kernel::block::error` 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: <20260216-rnull-v6-19-rc5-send-v1-37-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4556; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=eqqiqphigEUutWAOj4Ax0TLofmOBmjKS7MiYG2xrZGY=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklg9tsnSUeN3yuufEKi8gZGkENrakAQdsa/jp G2Ne5eHOG2JAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYPQAKCRDhuBo+eShj d7ukEADC7oEHfidFv4Xqhl5gUwo5O6pu+bHLU0Tu2FJlcKY5YpbZ5TMIvBIacDygyfwKE/Nxod1 ARcqIZt+gw+56hZchaZ/GBevkPBlChGWVFJ7TJdQA10U4hO4QoAExHS4Uf5Ko3A0b1AmaAPVMyT tJPE66g4mwJqH1y1N2v7liZoXYPNHc4P28xlKJUqZ8n4qfsWTMU2Rb9gEaOwabJaG0CNUR4Q2TN eoy6OxN3spvtReYPPzv25J8YqzD5Sz41ecJU27n825TPm9OfSeNbUOk/nZbUS0mvb3NxVCahHd+ +mEq7rMoIaZ2W7/TkSPCS2zX8dd1yxHBHkXq/bbKARJy290YKNld8UgerTLX5SO8jtuebAdYbWd tyf8pXVkLWqI9JKAWvKBjV1bPWLbZ1crOE2j4AMFd4ad/WZofR73hdC/Dh8ZNP1Xv1MOj9FVGXN 16oVuTZAAN/bekoR+h8JK7BCH077LuvnGPwe/zorwq9YdRnaTDG/GAR0iNrHnV1gCT1H5GpJcFZ gcDiujYahpCQoBSBE16JRIcFLghvHmmg3dM5jDOkvKEu2vwr16G0asEFF/E9C6cLmtGG6ucWWR8 Apy7OwXG9VmOmEoRnLGaAmZiEvupDEJ+sUbOm94GpvmfBSJvVOJ6tWUpNiKVE+8WHa9zKJAiedX aT8eYCeB0te7iaw== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Block layer status codes, represented by `blk_status_t`, are only one byte. This is different from the general kernel error codes. Add `BlkError` and `BlkResult` to handle these status codes. Signed-off-by: Andreas Hindborg --- rust/kernel/block.rs | 94 ++++++++++++++++++++++++++++++++++++++++++++++++= ++++ rust/kernel/error.rs | 3 +- 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/rust/kernel/block.rs b/rust/kernel/block.rs index 19236ab95227b..6cd83dc75a5eb 100644 --- a/rust/kernel/block.rs +++ b/rust/kernel/block.rs @@ -18,3 +18,97 @@ /// The difference between the size of a page and the size of a sector, /// expressed as a power of two. pub const PAGE_SECTORS_SHIFT: u32 =3D bindings::PAGE_SECTORS_SHIFT; + +pub mod error { + //! Block layer errors. + + use core::num::NonZeroU8; + + pub mod code { + //! C compatible error codes for the block subsystem. + macro_rules! declare_err { + ($err:tt $(,)? $($doc:expr),+) =3D> { + $( + #[doc =3D $doc] + )* + pub const $err: super::BlkError =3D + match super::BlkError::try_from_blk_status(crate::bind= ings::$err as u8) { + Some(err) =3D> err, + None =3D> panic!("Invalid errno in `declare_err!`"= ), + }; + }; + } + + declare_err!(BLK_STS_NOTSUPP, "Operation not supported."); + declare_err!(BLK_STS_IOERR, "Generic IO error."); + declare_err!(BLK_STS_DEV_RESOURCE, "Device resource busy. Retry la= ter."); + } + + /// A wrapper around a 1 byte block layer error code. + #[derive(Clone, Copy, PartialEq, Eq)] + pub struct BlkError(NonZeroU8); + + impl BlkError { + /// Create a [`BlkError`] from a `blk_status_t`. + /// + /// If the code is not know, this function will warn and return [`= code::BLK_STS_IOERR`]. + pub fn from_blk_status(status: bindings::blk_status_t) -> Self { + if let Some(error) =3D Self::try_from_blk_status(status) { + error + } else { + kernel::pr_warn!("Attempted to create `BlkError` from inva= lid value"); + code::BLK_STS_IOERR + } + } + + /// Convert `Self` to the underlying type. + pub fn to_blk_status(self) -> bindings::blk_status_t { + self.0.into() + } + + /// Try to create a `Self` form a `blk_status_t`. + /// + /// Returns `None` if the conversion fails. + const fn try_from_blk_status(errno: bindings::blk_status_t) -> Opt= ion { + if errno =3D=3D 0 { + None + } else { + Some(BlkError( + // SAFETY: We just checked that `errno`is nonzero. + unsafe { NonZeroU8::new_unchecked(errno) }, + )) + } + } + } + + impl From for u8 { + fn from(value: BlkError) -> Self { + value.0.into() + } + } + + impl From for u32 { + fn from(value: BlkError) -> Self { + let value: u8 =3D value.0.into(); + value.into() + } + } + + impl From for BlkError { + fn from(_value: kernel::error::Error) -> Self { + code::BLK_STS_IOERR + } + } + + /// A result with a [`BlkError`] error type. + pub type BlkResult =3D Result; + + /// Convert a `blk_status_t` to a `BlkResult`. + pub fn to_result(status: bindings::blk_status_t) -> BlkResult { + if status =3D=3D bindings::BLK_STS_OK { + Ok(()) + } else { + Err(BlkError::from_blk_status(status)) + } + } +} diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index 258b12afdcba3..e656465010d6c 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -162,8 +162,9 @@ pub fn to_errno(self) -> crate::ffi::c_int { self.0.get() } =20 + /// Convert a generic kernel error to a block layer error. #[cfg(CONFIG_BLOCK)] - pub(crate) fn to_blk_status(self) -> bindings::blk_status_t { + pub fn to_blk_status(self) -> bindings::blk_status_t { // SAFETY: `self.0` is a valid error due to its invariant. unsafe { bindings::errno_to_blk_status(self.0.get()) } } --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 ECD9B2D739D; Sun, 15 Feb 2026 23:45:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199156; cv=none; b=OZdDgv5Jla772QWlNqgD0fy0nUn910iwa4OLUjCS6kK1GYP6R/X70l7eakFuRx/C2jArRv1OQ3wF3BGnDxlocqfJeO8LdC919Cy6cEqkJ32wOyYInL6gJ/W/PiHfKP+Cso4S0KKyflm6eNbHagdFgTHpbrhPiVs740IY1tJSQ4w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199156; c=relaxed/simple; bh=wrhcSML+9BrPk1dlrkX3dG3Gv57kLOFA8v0YPzvxLbk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=HMfrOMrfn+HFQFy78X1MElRsfDOHLiksdFVpZuOPRgrB0Vl7AToLqfw4BwwBa7pGmSeGix8jPjDsJhCvBk5LuBo/Ad079xDDCUPmakKLPL/4HC8ZYafBzdCCcyXDdXw2reXG2nX85Bf56vlC1iYC3PwBZm74WqKbgUZneKJrFWA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=fQ+sY5FJ; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="fQ+sY5FJ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 54FADC4CEF7; Sun, 15 Feb 2026 23:45:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199155; bh=wrhcSML+9BrPk1dlrkX3dG3Gv57kLOFA8v0YPzvxLbk=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=fQ+sY5FJdFK7C0bMKDMNQG//TkYOVO2Ma+pP2CqA0jHQ6Kjt5PMd3AHl+pMQZI+vX T/dj3D/hKGzZq5zB1TgW2QBg3UZycnJ0SMt2QSQWpbTrPTNTCAqiUjStS/koUCbY0h q7xd082mA9CIk5ClY4pTkcHa1SWkezlG7fuT+vmHU2lmNpMlmUdH1xc/j70j+vxGIa qt/PRGHV01WoCxvgNhK82st7kTqCemWSlV6QBVTq2WhnJAwZmJXaOK4AMSEZkmXDar hXYO2EYOXPVJNcxs/t6jRyR67CplbyDkJB/WcB/HSUsdxiZccQq56R6O5/n77YhiPC IyA2ggG35mjOQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:25 +0100 Subject: [PATCH 38/79] block: rust: require `queue_rq` to return a `BlkResult` 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: <20260216-rnull-v6-19-rc5-send-v1-38-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=2623; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=wrhcSML+9BrPk1dlrkX3dG3Gv57kLOFA8v0YPzvxLbk=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklg+aD2Q5nTpLbFekOe4kY2SQW+CRM+wzayNW 25s6DTXJzuJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYPgAKCRDhuBo+eShj dxAmEACUQDpqem2zZeoXicjw1rcXE/XDLpB4OxcbpJHvAtlXd8Aeb9D/hJe3GMCXRFn7Cf9loKT ZRWGAJSF7Lc/MHyX7/WAKCNkpAboNTpi2P3T0EkHTOVKJYNLF9dTlpXEsOEvEpzfXOx4pHB+4B4 3RvW3jFJvNl629qfdyos+8dYi+SjXc7ibhRUjIrfplr1X4BYzgxeiVIZiXspMFkz15aZW/9Dzdb u99kjs6xhkzqw5xAM9qd6hExxH0igyyE10VPark7jPgsjbrj1jO2GdTZj3cmG9Jl8Q9EP1Y1zDl FCehWz2ZycZZsH5Xj91UulUhUbRmPujZlrBgnJ/J2rAqjHpGW/hmTA+GowYaxK1iA+zMRpe4El9 ygNqT9CN/pnC4F+YOn+5df7L3i7+UBop86EOW1iyz59sGtsmh0o69MK24skCsoonQ+sGUovhEW+ z5+bl7IFzWgffJNz/XCvM3Gp62t2dLHFfsJrlMa4aZQfuXLP1nZSEPAMmZwb/UXfD9nSBi57+Pd fSFtbGL7IEyAmyBjr/w1lBJ4pgZvyHgrzhjXfp3HwyI+vLDuyMGpGAaXetNTMCDrQJNhwfEvzxA PGHUZnfD5K+N0kzsylNrukF/W6731eIbtPimHorIODHsra0oI2Bn1+zEfGbFrFhshcgWYbF6YHT OTXZstg5sbfAyVg== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Change the return type of `Operations::queue_rq` from `Result` to `BlkResult`. This ensures that drivers return proper block layer status codes that can be translated to the appropriate `blk_status_t` value. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/rnull.rs | 3 ++- rust/kernel/block/mq.rs | 4 ++-- rust/kernel/block/mq/operations.rs | 7 +++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 034ddc06eabf9..55cbdfed25414 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -20,6 +20,7 @@ BadBlocks, // }, bio::Segment, + error::BlkResult, mq::{ self, gen_disk::{ @@ -577,7 +578,7 @@ fn queue_rq( this: Pin<&Self>, rq: Owned>, _is_last: bool, - ) -> Result { + ) -> BlkResult { let mut rq =3D rq.start(); let mut sectors =3D rq.sectors(); =20 diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index a898dda2635e5..02738f52389ba 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -64,7 +64,7 @@ //! ```rust //! use kernel::{ //! bindings, -//! block::mq::{self, *}, +//! block::{error::BlkResult, mq::{self, *}}, //! new_mutex, //! prelude::*, //! sync::{aref::ARef, Arc, Mutex}, @@ -90,7 +90,7 @@ //! _queue_data: (), //! rq: Owned>, //! _is_last: bool -//! ) -> Result { +//! ) -> BlkResult { //! rq.start().end_ok(); //! Ok(()) //! } diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/oper= ations.rs index fb75d65f67071..17468a39af60f 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -6,7 +6,10 @@ =20 use crate::{ bindings, - block::mq::{request::RequestDataWrapper, IdleRequest, Request}, + block::{ + error::BlkResult, + mq::{request::RequestDataWrapper, IdleRequest, Request}, + }, error::{from_result, Result}, prelude::*, sync::{aref::ARef, atomic::ordering, Refcount}, @@ -62,7 +65,7 @@ fn queue_rq( queue_data: ForeignBorrowed<'_, Self::QueueData>, rq: Owned>, is_last: bool, - ) -> Result; + ) -> BlkResult; =20 /// Called by the kernel to indicate that queued requests should be su= bmitted. fn commit_rqs( --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 63BFEEEC0; Sun, 15 Feb 2026 23:41:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198879; cv=none; b=nrYLdKaWUXGiQFIfbvfX8O0YT3qI0+6CyJ9SJN9WerD63gXSKqymrK7HR1OzpFqkYtQa54qfczbh0AualQ9lUfHDRMU62fGBUyUYRQ23p3EUem3tYjyBOBcEiKLrUkj6wrZ4DLcUcPhfmNCzx5lcPCbNu/Aba/wYZGQ53YtXYO4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198879; c=relaxed/simple; bh=ishZSvvB3Z9zb8FDhnjXHtGNUf+npQzs8cRfHAQ8f/g=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=oHbwNcikliocQAguR635+0I8LEE0O6uwsbqT+ZxUQBsnJYdcGKQlBX7HVOvLrPfPNj122OcoQFWPunt965irl1eGyQ5j9+rs6f0yQPHz0rwB9wCcv4IQ54aPSgCVuRVuLPaXjCTwgsS1oUtJAcQ59Rnz1Tz5cJDuzvTMl+YYXQs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=DSaFrKiF; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="DSaFrKiF" Received: by smtp.kernel.org (Postfix) with ESMTPSA id CCAFAC4CEF7; Sun, 15 Feb 2026 23:41:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198879; bh=ishZSvvB3Z9zb8FDhnjXHtGNUf+npQzs8cRfHAQ8f/g=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=DSaFrKiFGB6/amTCrWtVtDcKfgshDfMj5idpSAyBQc4yEmAPlpMjiRG9o7/hxoA+M WTCYeCZIM4P6YzdnFkyfKuLz3UPN9hqAqyYC+wcMYr9Yi0yYLgD0VPRqB+sx5aXIOs C/E6Z8NUcStd5iRUdv9VJcw+ehamkLxBVU38EOSl6xuW56SWqgD83VlJbd/YcFurEx 7LYrG9sJkDwKUScLvbLY+nKP699y2FMURC1capUA2px4hyZG85LvIiqVOA6zvXv4f4 D/DZ1hfai2J4PRkFEsULcN8QKKNp/+gCp27DCBc17FPEwYqWQ9Qsk8dnvf/tlTco8c DVe0LQHWytQlw== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:26 +0100 Subject: [PATCH 39/79] block: rust: add `GenDisk::queue_data` 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: <20260216-rnull-v6-19-rc5-send-v1-39-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1053; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=ishZSvvB3Z9zb8FDhnjXHtGNUf+npQzs8cRfHAQ8f/g=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklg+2aHPwWeYSKM1xwW1IHpYHkn2PT29EkZ7q RTzFzyPMfuJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYPgAKCRDhuBo+eShj d8Z7D/9CVhX2J92c1AOi2x4M0wJNziPWeHyPAG1ev/9oJdujHCMyMp49WszuFgmYujNhRiF4/4c tAHeQSTiybLHs+yKXAnrC+KhwvhsCWCYtQBgBiHRQ3fZpNV9of/AN6cCx8Xzs2nnAvWfHnzjkJx QhMdRd0cykNNVSPrx1CrOqryOPKGdq1KDuacQhb3pKkaAW8sQoE2YZJMLPSftYv1lSm6BSEtBGW vQ4keKzdQ7e5ueIC7V+Ej2aIMn0UmLJ9nEt89YVSVESTsEjh1/5m2dBLns5sOpcwdaBBrzU4TEK ObvgiTy/7knmletLmc3TLdmFa+yzzwQCeG5sZNucLKSPB3QbxFGNMgfXkNvVpH6Vwy62TbQXewA HnyYyaTtno1Dfy0yiWJD6n4TvZEXo1F4ipk7n39r1Blt1O+yqe3Kw5Eo1UCmApquRGRGnqMhwah T165zGyKMU1sQDfDUqP9kOouPyBPxtr8izJ6IHmP4eV/4RWmv8qUtQbO5tsy+HpeITKktMtVVfo vf3U1q9tcqvCGojacdc30nqWkUqso9gnXRqEqX6FcufnzT/nSa3hAojU1ubkKDCZUFabyG9lPSM wGQd0G7QkYQm1rELpA12e1wh5z7GlXx8ePguS5fIAJObeSYnrn2k16JAXQDe7asZ8vwfM+UyBWg iQ/8jHAkGqbPItw== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a method to borrow the private queue data of the queue a `GenDisk` is associated with. Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq/gen_disk.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_di= sk.rs index 8a22767f1b916..fbda2f572e17f 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -246,6 +246,12 @@ pub fn queue(&self) -> &RequestQueue { // SAFETY: By type invariant, self is a valid gendisk. unsafe { RequestQueue::from_raw((*self.gendisk).queue) } } + + /// Get the queue data associated with this [`GenDisk`]. + pub fn queue_data(&self) -> ::Borrowed= <'_> { + // SAFETY: By type invariant, self is a valid gendisk. + unsafe { T::QueueData::borrow((*(*self.gendisk).queue).queuedata) } + } } =20 // SAFETY: `GenDisk` is an owned pointer to a `struct gendisk` and an `Arc= ` to a --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 3BA4630DEAD; Sun, 15 Feb 2026 23:46:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199196; cv=none; b=nOcYyB3DyMeZEFRZcRK34O/jf7rFNiZuIQCrGpiCVt7BaqGQfogZNBgMmrbuuXxWSRWXUjOuSUJck8/CH2hS0RLICJwJkHq++16GUnKocN73/aYdYW7cuIFSZk6e32vwIOs932M5aNh8zu2oluvSaQ8jdjDGSqRYKtaqrG1KfAA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199196; c=relaxed/simple; bh=1D3Qowx2aO/t4IbOvgsohdBDGHVkeFHvWTMo4nJU384=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=TcqSk2mExm4ndubQBypPmlUcf2767sGhJZxUIhlEmAA7ZG2GdXQtdIUxB4fZvkegWFZb5NRKafhFDGhnyvRYb3B43g04jP6wHOYbtbNJW+qa5GWLJnwC9Rlzx7GS2AyTB2S9Ihl7wBpfRxF9QWyMq60iXhfetycGI9KO/W4QT7o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=apYQLKSE; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="apYQLKSE" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7D11CC4CEF7; Sun, 15 Feb 2026 23:46:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199195; bh=1D3Qowx2aO/t4IbOvgsohdBDGHVkeFHvWTMo4nJU384=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=apYQLKSEGKioDBmRLdyp49YhhecKtiE4t5lJ1+rq6Eqnw5uAOhOdqKGstI6n3JrPQ 4jY6cBFbWPOcya15GtWyOaNkjSP1/YVcHiO7iCHqC9+aJBHnbHnPIWpyc8IBFplC+m 2iLz979YOrDEYF3hKy36fH0FY2ZQo+ob8YGuuGOe1iFx31rTm2au3/RJV9in0Mz7Wn VgWrNweaTCoYWM6akq3agJRPDFiLe2IQ1Nwy6cz6Y3VbrOfM6DwCf3/3RXZ2QYhRW+ /8Dzy0ucH1lDoPgbznEKC0tCFtZKBSShiGcL5Wx6Y+rkmJc5jscse5T7uS+tB80Auw qVhe9/gVxleMg== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:27 +0100 Subject: [PATCH 40/79] block: rnull: add bandwidth limiting 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: <20260216-rnull-v6-19-rc5-send-v1-40-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=10640; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=1D3Qowx2aO/t4IbOvgsohdBDGHVkeFHvWTMo4nJU384=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklg/lJ8RWBn2XqZv3u9MAaleDBN4J6c9pL1EJ bQYXASf7kGJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYPwAKCRDhuBo+eShj d6adEAC1AoqKlyjEncpLkoQZDBtLyNnZisBjZMEGbED4jlqtXi+tqor3i/XT+zMdn2i0SSNUM2o ymEeqxJzhJsRd/xOhFQ5tvangLI53sBloxbomvjTtAAk0L6BOLFf9GWHGdHj+Ma9f9PXC6mblIO WEYHUe3pF6ffQdmS1RPsKX4TbNxJ1CYIEShynTnp5yq842QSs620NFz0YKZhHPQL9DZPiyelcX8 PykJ92EkZbLZeRB3ND2+gYpgm7k/55TKcwlDz9JzQhvi4KEhAZIeDEPujmY0+xlgElZNvIwR17P PMrwhJHoN3aeEPx6IK8m2jArV64/JV1qWRF9Taop6aiGribyIY5Ot1tbBIeeaI0Bk0OB6HsK8aH EkJYzXTcxjmZ8mP449ZiB384I+HUc5i0tt/klS6ld0FIgkrtpOl8MV+TT4n4ADEl7HeFHw9cX80 2Z5Ujr6UQa4xck2quT/mYwyOlgx7lohn1s0H4JE7CwSm6rcaoUP+ZhxGWUtpxTuRpocZAc++cAb XswxLn6fVPoF2MHD3ZHeBCDhWlQJi7XZjGe2ARzj5u++0y9wQ8fiQs4BGwjVdBwHYpFJy0Wcqa7 rXAnGj7bUGfIelI24lgLX21Osa+v1fX49660d9PP3dB5jM4OEv+/E/pF/CvmOdYJZi7pp4YO6Sh szGVbzqkiaQle+g== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add bandwidth limiting support to rnull via the `mbps` configfs attribute. When set to a non-zero value, the driver limits I/O throughput to the specified rate in megabytes per second. The implementation uses a token bucket algorithm to enforce the rate limit, delaying request completion when the limit is exceeded. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 7 ++- drivers/block/rnull/rnull.rs | 111 +++++++++++++++++++++++++++++++++++-= ---- 2 files changed, 105 insertions(+), 13 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index e365eb06be6de..d369d8b683c61 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -105,6 +105,7 @@ fn make_group( badblocks_once: 13, badblocks_partial_io: 14, cache_size_mib: 15, + mbps: 16, ], }; =20 @@ -136,6 +137,7 @@ fn make_group( GFP_KERNEL )?, cache_size_mib: 0, + mbps: 0, }), }), core::iter::empty(), @@ -200,6 +202,7 @@ struct DeviceConfigInner { bad_blocks_partial_io: bool, cache_size_mib: u64, disk_storage: Arc, + mbps: u32, } =20 #[vtable] @@ -239,6 +242,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { bad_blocks_once: guard.bad_blocks_once, bad_blocks_partial_io: guard.bad_blocks_partial_io, storage: guard.disk_storage.clone(), + bandwidth_limit: u64::from(guard.mbps) * 2u64.pow(20), })?); guard.powered =3D true; } else if guard.powered && !power_op { @@ -250,7 +254,6 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { } } =20 -// DiskStorage::new(cache_size_mib << 20, block_size as usize), configfs_simple_field!(DeviceConfig, 1, block_size, u32, check GenDiskBuil= der::validate_block_size); configfs_simple_bool_field!(DeviceConfig, 2, rotational); configfs_simple_field!(DeviceConfig, 3, capacity_mib, u64); @@ -457,3 +460,5 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { Ok(()) }) ); + +configfs_simple_field!(DeviceConfig, 16, mbps, u32); diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 55cbdfed25414..16a4f915e59f4 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -25,7 +25,8 @@ self, gen_disk::{ self, - GenDisk, // + GenDisk, + GenDiskRef, // }, Operations, TagSet, // @@ -37,24 +38,31 @@ Result, // }, ffi, + impl_has_hr_timer, new_mutex, new_spinlock, pr_info, prelude::*, + revocable::Revocable, str::CString, sync::{ aref::ARef, atomic::{ ordering, Atomic, // - }, // + }, Arc, + ArcBorrow, Mutex, + SetOnce, SpinLock, - SpinLockGuard, + SpinLockGuard, // }, time::{ hrtimer::{ + self, + ArcHrTimerHandle, + HrTimer, HrTimerCallback, HrTimerCallbackContext, HrTimerPointer, @@ -129,6 +137,10 @@ default: 0, description: "No IO scheduler", }, + mbps: u32 { + default: 0, + description: "Max bandwidth in MiB/s. 0 means no limit.", + }, }, } =20 @@ -174,6 +186,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { bad_blocks_once: false, bad_blocks_partial_io: false, storage: Arc::pin_init(DiskStorage::new(0, block_size = as usize), GFP_KERNEL)?, + bandwidth_limit: u64::from(*module_parameters::mbps.va= lue()) * 2u64.pow(20), })?; disks.push(disk, GFP_KERNEL)?; } @@ -204,6 +217,7 @@ struct NullBlkOptions<'a> { bad_blocks_once: bool, bad_blocks_partial_io: bool, storage: Arc, + bandwidth_limit: u64, } =20 #[pin_data] @@ -216,9 +230,18 @@ struct NullBlkDevice { bad_blocks: Arc, bad_blocks_once: bool, bad_blocks_partial_io: bool, + bandwidth_limit: u64, + #[pin] + bandwidth_timer: HrTimer, + bandwidth_bytes: Atomic, + #[pin] + bandwidth_timer_handle: SpinLock>>, + disk: SetOnce>>>, } =20 impl NullBlkDevice { + const BANDWIDTH_TIMER_INTERVAL: Delta =3D Delta::from_millis(20); + fn new(options: NullBlkOptions<'_>) -> Result>> { let NullBlkOptions { name, @@ -236,6 +259,7 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { bad_blocks_once, bad_blocks_partial_io, storage, + bandwidth_limit, } =3D options; =20 let mut flags =3D mq::tag_set::Flags::default(); @@ -260,7 +284,7 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { GFP_KERNEL, )?; =20 - let queue_data =3D Box::try_pin_init( + let queue_data =3D Arc::try_pin_init( try_pin_init!(Self { storage, irq_mode, @@ -270,6 +294,11 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { bad_blocks, bad_blocks_once, bad_blocks_partial_io, + bandwidth_limit: bandwidth_limit / 50, + bandwidth_timer <- HrTimer::new(), + bandwidth_bytes: Atomic::new(0), + bandwidth_timer_handle <- new_spinlock!(None), + disk: SetOnce::new(), }), GFP_KERNEL, )?; @@ -286,7 +315,10 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { .max_hw_discard_sectors(ffi::c_uint::MAX >> block::SECTOR_= SHIFT); } =20 - builder.build(fmt!("{}", name.to_str()?), tagset, queue_data) + let disk =3D builder.build(fmt!("{}", name.to_str()?), tagset, que= ue_data)?; + let queue_data: ArcBorrow<'_, Self> =3D disk.queue_data(); + queue_data.disk.populate(disk.get_ref()); + Ok(disk) } =20 fn sheaf_size() -> usize { @@ -504,6 +536,36 @@ fn end_request(rq: Owned>) { } } =20 +impl_has_hr_timer! { + impl HasHrTimer for NullBlkDevice { + mode: hrtimer::RelativeHardMode, + field: self.bandwidth_timer, + } +} + +impl HrTimerCallback for NullBlkDevice { + type Pointer<'a> =3D Arc; + + fn run( + this: ArcBorrow<'_, Self>, + mut context: HrTimerCallbackContext<'_, Self>, + ) -> HrTimerRestart { + if this.bandwidth_bytes.load(ordering::Relaxed) =3D=3D 0 { + return HrTimerRestart::NoRestart; + } + + this.disk.as_ref().map(|disk| { + disk.try_access() + .map(|disk| disk.queue().start_stopped_hw_queues_async()) + }); + + this.bandwidth_bytes.store(0, ordering::Relaxed); + + context.forward_now(Self::BANDWIDTH_TIMER_INTERVAL); + HrTimerRestart::Restart + } +} + struct HwQueueContext { page: Option>, } @@ -511,7 +573,7 @@ struct HwQueueContext { #[pin_data] struct Pdu { #[pin] - timer: kernel::time::hrtimer::HrTimer, + timer: HrTimer, error: Atomic, } =20 @@ -560,14 +622,14 @@ fn align_down(value: T, to: T) -> T =20 #[vtable] impl Operations for NullBlkDevice { - type QueueData =3D Pin>; + type QueueData =3D Arc; type RequestData =3D Pdu; type TagSetData =3D (); type HwData =3D Pin>>; =20 fn new_request_data() -> impl PinInit { pin_init!(Pdu { - timer <- kernel::time::hrtimer::HrTimer::new(), + timer <- HrTimer::new(), error: Atomic::new(0), }) } @@ -575,14 +637,39 @@ fn new_request_data() -> impl PinInit { #[inline(always)] fn queue_rq( hw_data: Pin<&SpinLock>, - this: Pin<&Self>, + this: ArcBorrow<'_, Self>, rq: Owned>, _is_last: bool, ) -> BlkResult { - let mut rq =3D rq.start(); let mut sectors =3D rq.sectors(); =20 - Self::handle_bad_blocks(this.get_ref(), &mut rq, &mut sectors)?; + if this.bandwidth_limit !=3D 0 { + if !this.bandwidth_timer.active() { + drop(this.bandwidth_timer_handle.lock().take()); + let arc: Arc<_> =3D this.into(); + *this.bandwidth_timer_handle.lock() =3D + Some(arc.start(Self::BANDWIDTH_TIMER_INTERVAL)); + } + + if this + .bandwidth_bytes + .fetch_add(u64::from(rq.bytes()), ordering::Relaxed) + + u64::from(rq.bytes()) + > this.bandwidth_limit + { + rq.queue().stop_hw_queues(); + if this.bandwidth_bytes.load(ordering::Relaxed) <=3D this.= bandwidth_limit { + rq.queue().start_stopped_hw_queues_async(); + } + + return Err(kernel::block::error::code::BLK_STS_DEV_RESOURC= E); + } + } + + let mut rq =3D rq.start(); + + use core::ops::Deref; + Self::handle_bad_blocks(this.deref(), &mut rq, &mut sectors)?; =20 if this.memory_backed { if rq.command() =3D=3D bindings::req_op_REQ_OP_DISCARD { @@ -604,7 +691,7 @@ fn queue_rq( Ok(()) } =20 - fn commit_rqs(_hw_data: Pin<&SpinLock>, _queue_data: P= in<&Self>) {} + fn commit_rqs(_hw_data: Pin<&SpinLock>, _queue_data: A= rcBorrow<'_, Self>) {} =20 fn init_hctx(_tagset_data: (), _hctx_idx: u32) -> Result= { KBox::pin_init(new_spinlock!(HwQueueContext { page: None }), GFP_K= ERNEL) --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 56EE92E92B7; Sun, 15 Feb 2026 23:46:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199186; cv=none; b=uOauQ4STyU58i6te8QOqgOAEsh8yZ3Zd3V6PWrLhg4ETbZ3Lc2109iROJSaRfXKHAbPb4gBHgYnNxfw9v9LiXITZFy3rALX80ANtkVDwHfNSOfW+pY643t2UWGbx5CN8LDn4FzGDktqilHMJc/XNSPJRJKK4ye1jE7P04UBVUAU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199186; c=relaxed/simple; bh=KwNsjS9BksChOKWlphQ7tBX5Ukevdabgk4N9RSuoPnU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Cjzn4OtMpHf/vbT7i9S1LCODQtIQcvspfSskhsxdCRB3aW0DYuGS8TUKmu3JxigfIr5rgQoJPHDa8DRZyWWFFSmhBnSHp9zcFEMsSwwRZa/1tHAH/dd7B8RtbZf0DCje2SGvxGpQ41X12GXwvFJIeuFxdYzgPccN+OHp2hMwAZQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Tt2dauBM; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Tt2dauBM" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 980BCC4CEF7; Sun, 15 Feb 2026 23:46:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199185; bh=KwNsjS9BksChOKWlphQ7tBX5Ukevdabgk4N9RSuoPnU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Tt2dauBM/91ZcwHIxNjZy56t8ADRDk73M1R5xgyoFIhLuLmwJmT3LGC5PHQHKrRZU UHEMd0NMajM/5cKHDRp50D0h0NngwdfrnD5Gap/LYFz1aW9lktQkW8EPg9j97g9TwP Gcb8+JNOW7U8Gxsr5pJ6Cq+V9Xbmb2v6c6s7Ub6pAzjGLbpJWQ2f1R62YGKpCAVWME ICQIpspC0YXkYRMeFUPjz+mi3UBrNgjteaVDgCAYR7/+xDO0EFkYlXrYxMG8M6XdCX ZLiSWePHfLfXqWmTb9XyN4MPms6F3pxrawtqc67a6UB+m9/r6Vz5lWyEkj8CLW4v1b SGGBfz54SbOew== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:28 +0100 Subject: [PATCH 41/79] block: rnull: add blocking queue mode 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: <20260216-rnull-v6-19-rc5-send-v1-41-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4047; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=KwNsjS9BksChOKWlphQ7tBX5Ukevdabgk4N9RSuoPnU=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhAAmONZuTCFAmTy2QHtO/6KeA3aonRqhccD uZyYAiEM3SJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYQAAKCRDhuBo+eShj d/SdD/4xA7LPhW3edXP28x48xx8Oy40JA8mu40mUSF5x2Tm15UWJyOC4BFixM90yuvg4wTo2zbM 5FjABT+A3GA1+BGStHP8qJFkr/LlankpKRWlAp5QS1fwzoNxiFBtmAzXpEppDrjRCdjb5IO+sTn 0A5pcAJmZCRVPib8vlo5SxL3+A2VBpRxM0BLRiKqgmwDOUoeJxUfMq64NTn21Xyg7QpFKiJuIG1 U5KaR5sY1hftYG38q+wQr2sVXEClQ1+x26DhrIsrneB9Rfr89n36u1ZtawLYg6R64d9xOgsz5Dy CN407ZNK5zGV4OjZVjkHlDK8n950RY/ZRevXXR6IOe07+ymK9yx311faO5rpu+VWEdxBKR6QT+C TWIxrxkT60DXJreOUhL8cnuLfgOhMx6hTBZO0ymjY+kULDyHYZ30QgZxVZkFfNywjxSUCGOMGXK o3jxI4/LUHD8A0tZF50UjiewRjVny1ZvKe5ToPQsRynbkxfzyJrQR07uq3QraGzA9ePn3bn+nZc eFBxwbD2XGyR2cwVAf4hjpQBQjj1GpldPiBqwUSW7Ar7j7kRG0+gGjaB+Vh6HRMAZwRt5Y+Ly6G SBJo8EI5oMVDXucaXmjm0FLx6TtARqzZXTGvr5SW1QfCF7UgcJGhNRr+q7FqMGqdJWkl+dMoRif og+WAb8NDQS7JeQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add support for blocking queue mode via the `blocking` configfs attribute. When enabled, the tag set is created with the `BLK_MQ_F_BLOCKING` flag. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 7 ++++++- drivers/block/rnull/rnull.rs | 9 ++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index d369d8b683c61..640e3de230c0a 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -70,7 +70,7 @@ impl AttributeOperations<0> for Config { let mut writer =3D kernel::str::Formatter::new(page); writer.write_str( "blocksize,size,rotational,irqmode,completion_nsec,memory_back= ed\ - submit_queues,use_per_node_hctx\n", + submit_queues,use_per_node_hctx,discard,blocking\n", )?; Ok(writer.bytes_written()) } @@ -106,6 +106,7 @@ fn make_group( badblocks_partial_io: 14, cache_size_mib: 15, mbps: 16, + blocking: 17, ], }; =20 @@ -138,6 +139,7 @@ fn make_group( )?, cache_size_mib: 0, mbps: 0, + blocking: false, }), }), core::iter::empty(), @@ -203,6 +205,7 @@ struct DeviceConfigInner { cache_size_mib: u64, disk_storage: Arc, mbps: u32, + blocking: bool, } =20 #[vtable] @@ -243,6 +246,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { bad_blocks_partial_io: guard.bad_blocks_partial_io, storage: guard.disk_storage.clone(), bandwidth_limit: u64::from(guard.mbps) * 2u64.pow(20), + blocking: guard.blocking, })?); guard.powered =3D true; } else if guard.powered && !power_op { @@ -462,3 +466,4 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { ); =20 configfs_simple_field!(DeviceConfig, 16, mbps, u32); +configfs_simple_bool_field!(DeviceConfig, 17, blocking); diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 16a4f915e59f4..cfc00c104d9ca 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -141,6 +141,10 @@ default: 0, description: "Max bandwidth in MiB/s. 0 means no limit.", }, + blocking: u8 { + default: 0, + description: "Register as a blocking blk-mq driver device", + }, }, } =20 @@ -187,6 +191,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { bad_blocks_partial_io: false, storage: Arc::pin_init(DiskStorage::new(0, block_size = as usize), GFP_KERNEL)?, bandwidth_limit: u64::from(*module_parameters::mbps.va= lue()) * 2u64.pow(20), + blocking: *module_parameters::blocking.value() !=3D 0, })?; disks.push(disk, GFP_KERNEL)?; } @@ -218,6 +223,7 @@ struct NullBlkOptions<'a> { bad_blocks_partial_io: bool, storage: Arc, bandwidth_limit: u64, + blocking: bool, } =20 #[pin_data] @@ -260,6 +266,7 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { bad_blocks_partial_io, storage, bandwidth_limit, + blocking, } =3D options; =20 let mut flags =3D mq::tag_set::Flags::default(); @@ -267,7 +274,7 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { // TODO: lim.features |=3D BLK_FEAT_WRITE_CACHE; // if (dev->fua) // lim.features |=3D BLK_FEAT_FUA; - if memory_backed { + if blocking || memory_backed { flags |=3D mq::tag_set::Flag::Blocking; } =20 --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 CA65C2EA732; Sun, 15 Feb 2026 23:44:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199066; cv=none; b=mSB4gyr6jrxD/qppTWz2JjSL9YbqK8/Aev8qUfS9CkG75UVK+ls27uCl6EaN9zMQaDH+FJcrycSEsSxOPgSIDGNjtMnGabw+Jl8KiIJpxsz8cNPsKsz/q/RBZ+bxhhKHxMDP4yFCMqPeql7zNU6wAFphfJ75i8DZ0oyUCCnLDg4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199066; c=relaxed/simple; bh=enMteMP5s8/CfnF3wWbknhPBzI+BpA10sTDrdile/IM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Y9WSPpe4ZdC3NFJsJZ2g34PhT/6KEn0xIXHdXcTTRAfNqM+vy+UJ2eQDiK0IDj7jmqhVtaeeYAW4ObYU8iQn8YHzGrU/jL/ks7DsnUAv0EydHqRBLMbI6zt9NQQDCuoxh+Cvf7GMsOo9+yo8DPMfHTO1M/wPgc0qlSBIzgRUP2Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Fi7zEjiX; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Fi7zEjiX" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 31973C4CEF7; Sun, 15 Feb 2026 23:44:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199066; bh=enMteMP5s8/CfnF3wWbknhPBzI+BpA10sTDrdile/IM=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Fi7zEjiXHEcQox5CSzxIu8gEQJNU3drOgLK/ZCUnUl8SRgsygu9o8tlBmDwqzN7tC WWOO4630doCXhemOiYg7oNF2HkVI51FL87JWKO0/H31u+sIF/7pkYT9R+tPZKZmm5C Gj188GWSPqXoNqPlP6pRJacUEs/A1Abnb22H7f2WhTwJuJTZBSTV4gg8d10aVwyGgS 5hJGDBtIgGONZoc4kRRhR358KqdtkaJJYrQJ4zqSLOQ/nwHjSZ4BP5sB9aMPuBxI6N WFeTuesNYUcZcYZO+xnMw702tACBpSU8Y3UdOToaPeyG0lu9yyBnQDd6WT3WrPBxM6 IVjBwkbpuJlDQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:29 +0100 Subject: [PATCH 42/79] block: rnull: add shared tags 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: <20260216-rnull-v6-19-rc5-send-v1-42-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=5495; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=enMteMP5s8/CfnF3wWbknhPBzI+BpA10sTDrdile/IM=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhBL064yZgPJ9mugO4wJgGrxE2244ynwHMHH zlYceC/FKuJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYQQAKCRDhuBo+eShj d5nHD/0ZLzSQ6fLnmrRaqZcDtZxnXn5I8kL8xB9CSs7SomzddSxg5j8EqpW5yTSUgmISr2dFH51 K6ol+CRJQU0TUhxt6cONjzsdJUQPm1Uj/U4+2yhSvj5qoA1uhrw68M49v+XQ70f3IAWj8jukCwc crCE8EZqgAd7BBtAxM8g/0yHpTnZdODTIsbEOS0r5oKT4dJ3wuPDWzTw/qk2BlYK/sccOj8wLFJ 7Ah1FukNn1a921F1Ol+GUn7ZoHWV4XNImSRK/vF79N02eP8VsqvoIRbXpDgM6wsSN/iDmsxCxB8 oLltq6SzHTb7D2bGrgkKN4Vby9JxE8mEpmDrrRiumUJ9nXPBbAmhZdchS+vgrIWMWscFzeDjy0k MnG6aHC7k9+FXMShBK2L3F+Lt9HzGkiknV79V0yLZp25awM1pZFXk+pFHyyJL8iu+9DNMpjo4gP ksm1M+bZtwiaclKVUIGRMMsCUG0oemikiJIyW6jaNrHMqRHdlzvK0ofkWd1MShzrbTs/pvM1yUf DEAWbp7M+t41QAppAgnT98WO8RI2jz7XVS99IDcCZu1hPxINxl/j1kEFD1Rau6O99fRYb0QXWu2 MQ3jxj/5M8PdkSBMdU71L+5fv7p/+FiNhlkwnYnd4Qw5UyiWgwPd86SS6X7nIgXWGJZDNqVWe30 x1YFdvqTGyvgVzg== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add support for sharing tags between multiple rnull devices. When enabled via the `shared_tags` configfs attribute, all devices in the group share a single tag set, reducing memory usage. This feature requires creating a shared `TagSet` that can be referenced by multiple devices. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 7 ++++++- drivers/block/rnull/rnull.rs | 25 +++++++++++++++++++++---- rust/kernel/block/mq/tag_set.rs | 6 ++++++ 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 640e3de230c0a..d1a70fe030cb2 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -70,7 +70,7 @@ impl AttributeOperations<0> for Config { let mut writer =3D kernel::str::Formatter::new(page); writer.write_str( "blocksize,size,rotational,irqmode,completion_nsec,memory_back= ed\ - submit_queues,use_per_node_hctx,discard,blocking\n", + submit_queues,use_per_node_hctx,discard,blocking,shared_tags\= n", )?; Ok(writer.bytes_written()) } @@ -107,6 +107,7 @@ fn make_group( cache_size_mib: 15, mbps: 16, blocking: 17, + shared_tags: 18, ], }; =20 @@ -140,6 +141,7 @@ fn make_group( cache_size_mib: 0, mbps: 0, blocking: false, + shared_tags: false, }), }), core::iter::empty(), @@ -206,6 +208,7 @@ struct DeviceConfigInner { disk_storage: Arc, mbps: u32, blocking: bool, + shared_tags: bool, } =20 #[vtable] @@ -247,6 +250,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { storage: guard.disk_storage.clone(), bandwidth_limit: u64::from(guard.mbps) * 2u64.pow(20), blocking: guard.blocking, + shared_tags: guard.shared_tags, })?); guard.powered =3D true; } else if guard.powered && !power_op { @@ -467,3 +471,4 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { =20 configfs_simple_field!(DeviceConfig, 16, mbps, u32); configfs_simple_bool_field!(DeviceConfig, 17, blocking); +configfs_simple_bool_field!(DeviceConfig, 18, shared_tags); diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index cfc00c104d9ca..84e75a7042214 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -145,6 +145,10 @@ default: 0, description: "Register as a blocking blk-mq driver device", }, + shared_tags: u8 { + default: 0, + description: "Share tag set between devices for blk-mq", + }, }, } =20 @@ -192,6 +196,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { storage: Arc::pin_init(DiskStorage::new(0, block_size = as usize), GFP_KERNEL)?, bandwidth_limit: u64::from(*module_parameters::mbps.va= lue()) * 2u64.pow(20), blocking: *module_parameters::blocking.value() !=3D 0, + shared_tags: *module_parameters::shared_tags.value() != =3D 0, })?; disks.push(disk, GFP_KERNEL)?; } @@ -224,8 +229,11 @@ struct NullBlkOptions<'a> { storage: Arc, bandwidth_limit: u64, blocking: bool, + shared_tags: bool, } =20 +static SHARED_TAG_SET: SetOnce>> =3D SetOnce::ne= w(); + #[pin_data] struct NullBlkDevice { storage: Arc, @@ -267,6 +275,7 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { storage, bandwidth_limit, blocking, + shared_tags, } =3D options; =20 let mut flags =3D mq::tag_set::Flags::default(); @@ -286,10 +295,18 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { return Err(code::EINVAL); } =20 - let tagset =3D Arc::pin_init( - TagSet::new(submit_queues, (), 256, 1, home_node, flags), - GFP_KERNEL, - )?; + let tagset_ctor =3D || -> Result> { + Arc::pin_init( + TagSet::new(submit_queues, (), 256, 1, home_node, flags), + GFP_KERNEL, + ) + }; + + let tagset =3D if shared_tags { + SHARED_TAG_SET.as_ref_or_populate_with(tagset_ctor)?.clone() + } else { + tagset_ctor()? + }; =20 let queue_data =3D Arc::try_pin_init( try_pin_init!(Self { diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set= .rs index f18b51e5217fe..600c9c6249123 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -106,3 +106,9 @@ fn drop(self: Pin<&mut Self>) { unsafe { T::TagSetData::from_foreign(tagset_data) }; } } + +// SAFETY: It is safe to transfer ownership of `TagSet` across thread boun= daries. +unsafe impl Sync for TagSet {} + +// SAFETY: It is safe to share references to `TagSet` across thread bounda= ries. +unsafe impl Send for TagSet {} --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 B6009EEC0; Sun, 15 Feb 2026 23:41:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198869; cv=none; b=p4vnGPEZyzwKDWX7VPAyZ4Ga54Fk2zz5f47YA2ZDGTimGvZg7YMETm1OsvjAR7ptr/Ck3q/Fvoqa0wNGmQa5Fkj6ELU79pPknQUMLPrlMuMCnZyZxykIJovQYqJeIvFBaHm1bR+ITMF18dhT2GcvIRv5ixGdSwBZr0b/AW3cw8Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198869; c=relaxed/simple; bh=tjrMyzA+BVtzT2Wqfa6F8miT38AdN9I/wjO3H7a9lZ0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=X1o+i45jk2/izIfqj/j53ZRWynCfbBsijkOvoAfcq/CYp3dpqZfPUl+zN+eHd6uJvYYtMnMh2CZjSlqST4fLTbwoQ0JIqDfvxUlmhdXEmaPncnggulr3TrNOTqv8pdP58wWvBBT7zpMcT35r1BmQSvCVdvhOghabw74g33G3AiI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=aOYNkd/v; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="aOYNkd/v" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1FBE4C4CEF7; Sun, 15 Feb 2026 23:41:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198869; bh=tjrMyzA+BVtzT2Wqfa6F8miT38AdN9I/wjO3H7a9lZ0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=aOYNkd/vcvbDJk4lE3gOjsaVJabfRblsr07ATuTEdxfmrk67CBg7J5kysXiPjCtlR 2zU9bETXF719U42grp0m7Op3NQLMvhUIekBy6Q/UNyUkAlIs52up01nsIUC3mLHddA upcNqOSeH4D/vgC3ZsOP6rus8KsAknJYAeNFj0gu0oRsqo8NdMfGogMJs2wCRdqpFE gzWlkn55XG3s2Zu3Q6Rgy5RXqZLJleL6lxTWbQ1pct/JtIteQwzVGW62QxUj+poYjA v8guegINKlV/ZYKXtiJDGbvuP+rfMsz5x4zIMnuhqYryNT6caDZF84IRzyORKSK3YD w/mFJr3ABStoQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:30 +0100 Subject: [PATCH 43/79] block: rnull: add queue depth config option 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: <20260216-rnull-v6-19-rc5-send-v1-43-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=3790; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=tjrMyzA+BVtzT2Wqfa6F8miT38AdN9I/wjO3H7a9lZ0=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhCEp6FvtTf5ef2Ki4EejqZQMt6SGvhMnUs4 sC8C3M0ImaJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYQgAKCRDhuBo+eShj d0lGEAClOQg1gPLy7HIVfKapYlBnLDghBIoEapbFs5J5AfZoMFvr3hvNmSvqrGJs7WY+HVl/jWN JAOYy1vdMseKVPdasMgue8+QSbEwM2NF00TAVppVb1LyQvcLHbwDARHwq513LL6yKWLYJ1q9g/9 FYMVEN7axOaqI8F9+jfe8J6WJbJB/RQNEkv41poqsWK5L3bonD0W8iBl8TRB05w/r3SNo0wknhg 15Kw2ZoeXbRLv7/nTSpMUvlGE6VwWpdfjNvP9AHG5YVEyLX3Y38yzh3zyR2FUF2JfCUzwJhGZSa EAwd5V56rOJlRWxGAoXkYIpORkb0PZCdhPLnkpGJtH2MzsMpjoWlikvMVvEAjt+Ed8lVYpsV7fR I/XY2M6/ZDosPtJhUseBVL3o4/+f/cz085lgiZcXEEt3r8o3RJdr4xXaIBSL6xXOT0UGaXDqgbE m3ftZKy6mIPmK3tpDpWsbCMRC4BdQwacV5HrZ+cnIoNeTJ/c6+Ngoect1UuUl794SGLNmfhey4D uVpY6bd0/iZitDW9KKO8deMVEMomcoI42B4jyn/Pok3Vl245uSL5Co0fglN36BjwSwzMfWfTjU8 ZxZuAB28ACB6PnK8rusQ9rG2vTuKWtnb1VAhJcbhLzN9l3MR17TxgtduA3LTS6CkPqcmcziWvth Ky6Q4ks0SwaULHw== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a configfs attribute to configure the queue depth (number of tags) for the rnull block device. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 5 +++++ drivers/block/rnull/rnull.rs | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index d1a70fe030cb2..90f2ecb454285 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -108,6 +108,7 @@ fn make_group( mbps: 16, blocking: 17, shared_tags: 18, + hw_queue_depth: 19 ], }; =20 @@ -142,6 +143,7 @@ fn make_group( mbps: 0, blocking: false, shared_tags: false, + hw_queue_depth: 64, }), }), core::iter::empty(), @@ -209,6 +211,7 @@ struct DeviceConfigInner { mbps: u32, blocking: bool, shared_tags: bool, + hw_queue_depth: u32, } =20 #[vtable] @@ -251,6 +254,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { bandwidth_limit: u64::from(guard.mbps) * 2u64.pow(20), blocking: guard.blocking, shared_tags: guard.shared_tags, + hw_queue_depth: guard.hw_queue_depth, })?); guard.powered =3D true; } else if guard.powered && !power_op { @@ -472,3 +476,4 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { configfs_simple_field!(DeviceConfig, 16, mbps, u32); configfs_simple_bool_field!(DeviceConfig, 17, blocking); configfs_simple_bool_field!(DeviceConfig, 18, shared_tags); +configfs_simple_field!(DeviceConfig, 19, hw_queue_depth, u32); diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 84e75a7042214..3fb19836ce6ff 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -149,6 +149,10 @@ default: 0, description: "Share tag set between devices for blk-mq", }, + hw_queue_depth: u32 { + default: 64, + description: "Queue depth for each hardware queue. Default: 6= 4", + }, }, } =20 @@ -197,6 +201,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { bandwidth_limit: u64::from(*module_parameters::mbps.va= lue()) * 2u64.pow(20), blocking: *module_parameters::blocking.value() !=3D 0, shared_tags: *module_parameters::shared_tags.value() != =3D 0, + hw_queue_depth: *module_parameters::hw_queue_depth.val= ue(), })?; disks.push(disk, GFP_KERNEL)?; } @@ -230,6 +235,7 @@ struct NullBlkOptions<'a> { bandwidth_limit: u64, blocking: bool, shared_tags: bool, + hw_queue_depth: u32, } =20 static SHARED_TAG_SET: SetOnce>> =3D SetOnce::ne= w(); @@ -276,6 +282,7 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { bandwidth_limit, blocking, shared_tags, + hw_queue_depth, } =3D options; =20 let mut flags =3D mq::tag_set::Flags::default(); @@ -297,7 +304,7 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { =20 let tagset_ctor =3D || -> Result> { Arc::pin_init( - TagSet::new(submit_queues, (), 256, 1, home_node, flags), + TagSet::new(submit_queues, (), hw_queue_depth, 1, home_nod= e, flags), GFP_KERNEL, ) }; --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 0F1272D837C; Sun, 15 Feb 2026 23:46:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199216; cv=none; b=ggrZQiZDobe4o2jppYygHXod7EzaqXdpwfKcxFDD3H172XkWwQQtdL+o42+VwuUkN+mtckxv57aoiGC53sXvD+FpGoLp34HczzWJHLIBVEdm7NOhVTBUFGkjzI9g1j1my6PxbYVwXEALd6eolegZnAFg0B1AsJnfVQh5NtEEOSI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199216; c=relaxed/simple; bh=pHTvVkZRFYcjiaGREx9N7FRfc2mqdwNLh9peaN0Ub0Y=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=X0qZ4FwE+ouVN05XTjca3j25MnGd72vZLC7XGrGJ7gt3jl/I5NVCVeEtUGZrYBz/ObraBPcwfMC/aQwwKEPd7jPlmaB68gglx0stCyz9ceBy9Pn0F+6EUWnaWvB1rc8doHy4QseudbmHzbObgsoUrnCl4wQR756VkiATwT4/3XM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=BmQjiPkM; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="BmQjiPkM" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A9CFFC4CEF7; Sun, 15 Feb 2026 23:46:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199215; bh=pHTvVkZRFYcjiaGREx9N7FRfc2mqdwNLh9peaN0Ub0Y=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=BmQjiPkMGbCMCCRWpIRKmUkMrdGCR9uHTSwgOcA660yGX9ktAN30df2PCPV3s1HZk 6CEfKyzgEVdxnIQWZU2sAQGCS/S5FdfVQVx0cD/mKtl8lag7yrZ8mIUEs/tcjl+jas tssEjIRSACRWsCVEMNjHKPKw1d/9YnUX5GO5JjL7vJn6UA0FNu/5BO4te+G6TLLUiQ HzbZ8HZG8ZWn206d5cPypIxHAoQtaq4lhGaTNVkvT1CLAR88iFZsgYvzaUWz3IW4DZ ukKxIk36VgGppMIBD5qzeXu/6eheDOcZsDLM1PwpEyM2bT1EGdmd7VcKD5s0+Beoq5 Pyi7Orb/Ucqhw== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:31 +0100 Subject: [PATCH 44/79] block: rust: add an abstraction for `bindings::req_op` 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: <20260216-rnull-v6-19-rc5-send-v1-44-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=6414; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=pHTvVkZRFYcjiaGREx9N7FRfc2mqdwNLh9peaN0Ub0Y=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhDEeCdW/yi4dywjzU/1Nk+MWm9S2xIIH2Cn V0G9M1w7xSJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYQwAKCRDhuBo+eShj d/D6D/44ecuU5yS9H5GCaAuZSVh+YOzWJCcgz/rVCkktg1rouPSSpD6CxPVUon461IFCjG00DLA G0FKsh7baTRApFzDessKhoxB+9+5mm5uoe3KDBZka+ZtKw6XmL13kwSE6QDe4izJo2fXWh8ZECc T88vr0uJG817P2hEkIKdE7sfywllU2Boyaxc256EgmJko0htOudxKOPi0nX2tGTWC2quFmMbBnj 5ZR4gxFx4OewOOIf2Iyt1vcRNuDuTTJRa38dGXbjwgF/KTExWRKIikmoN/0HKJgOYgsHtGvyCLS NrEInkyirWFL29VlZeTQAEcIuc5yYEBJwCaXeckvuyEk7pivpurH2atUKKpy1Qn/QshXpDuIBFA 5cNc6Vu2ZlFJrGZBXaOjXwcN8Ra7nLJ6wgs82RQJ3RQP+CjK7FjLMFXceHE4cgluDKQtECeD3pl raa/er7bmW6GL7Q5ZQurcZJ6sptVEQC4uq/pWVwnC/IwZfCLGTQjFmhyYBLaxNqrNUx3u40ll9v /8ylEGx1cgYdBcnRaZSV90oIGgrAB8nipQus1bMtJV2hsXIg9jnAEa9dTHZTXuqbK2Aqw/2SmS9 cXooL5FmaBASTDq6ifdv1QwvzLB+mS6rmIPRbTiD7s0baqG2g9Xpk8t7etiq84wChnJIRI1LA76 32fIHtKfsDlyO5g== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add the `Command` enum as a Rust abstraction for block request operation codes. The enum variants correspond to the C `REQ_OP_*` defines and include read, write, flush, discard, and zone management operations. Also add a `command()` method to `Request` to retrieve the operation code. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/rnull.rs | 6 +-- rust/kernel/block/mq.rs | 1 + rust/kernel/block/mq/request.rs | 11 +++++- rust/kernel/block/mq/request/command.rs | 65 +++++++++++++++++++++++++++++= ++++ 4 files changed, 79 insertions(+), 4 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 3fb19836ce6ff..9383b82f9a736 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -508,10 +508,10 @@ fn transfer( .len() .min((end_sector - sector) as u32 >> SECTOR_SHIFT); match command { - bindings::req_op_REQ_OP_WRITE =3D> { + mq::Command::Write =3D> { self.write(&mut tree_guard, &mut hw_data_guard, se= ctor, segment)? } - bindings::req_op_REQ_OP_READ =3D> { + mq::Command::Read =3D> { self.read(&mut tree_guard, &mut hw_data_guard, sec= tor, segment)? } _ =3D> (), @@ -703,7 +703,7 @@ fn queue_rq( Self::handle_bad_blocks(this.deref(), &mut rq, &mut sectors)?; =20 if this.memory_backed { - if rq.command() =3D=3D bindings::req_op_REQ_OP_DISCARD { + if rq.command() =3D=3D mq::Command::Discard { this.discard(&hw_data, rq.sector(), sectors)?; } else { this.transfer(&hw_data, &mut rq, sectors)?; diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index 02738f52389ba..057a5f366be3a 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -131,6 +131,7 @@ pub mod tag_set; =20 pub use operations::Operations; +pub use request::Command; pub use request::IdleRequest; pub use request::Request; pub use request::RequestTimerHandle; diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index d6f3ffde1c5c4..4e47419776e0f 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -23,6 +23,9 @@ use crate::block::bio::Bio; use crate::block::bio::BioIterator; =20 +mod command; +pub use command::Command; + /// A [`Request`] that a driver has not yet begun to process. /// /// A driver can convert an `IdleRequest` to a [`Request`] by calling [`Id= leRequest::start`]. @@ -89,11 +92,17 @@ fn deref(&self) -> &Self::Target { =20 impl RequestInner { /// Get the command identifier for the request - pub fn command(&self) -> u32 { + fn command_raw(&self) -> u32 { // SAFETY: By C API contract and type invariant, `cmd_flags` is va= lid for read unsafe { (*self.0.get()).cmd_flags & ((1 << bindings::REQ_OP_BITS)= - 1) } } =20 + /// Get the command of this request. + pub fn command(&self) -> Command { + // SAFETY: By type invariant of `Self`, `self.0` is valid and live. + unsafe { Command::from_raw(self.command_raw()) } + } + /// Get the target sector for the request. #[inline(always)] pub fn sector(&self) -> u64 { diff --git a/rust/kernel/block/mq/request/command.rs b/rust/kernel/block/mq= /request/command.rs new file mode 100644 index 0000000000000..70a8d67fa35c0 --- /dev/null +++ b/rust/kernel/block/mq/request/command.rs @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0 + +/// Block I/O operation codes. +/// +/// This is the Rust abstraction for the C [`enum req_op`]. +/// +/// Operations common to the bio and request structures. The kernel uses 8= bits +/// for encoding the operation, and the remaining 24 bits for flags. +/// +/// The least significant bit of the operation number indicates the data +/// transfer direction: +/// +/// - If the least significant bit is set, transfers are TO the device. +/// - If the least significant bit is not set, transfers are FROM the devi= ce. +/// +/// If an operation does not transfer data, the least significant bit has = no +/// meaning. +/// +/// [`enum req_op`]: srctree/include/linux/blk_types.h +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum Command { + /// Read sectors from the device. + Read =3D bindings::req_op_REQ_OP_READ, + /// Write sectors to the device. + Write =3D bindings::req_op_REQ_OP_WRITE, + /// Flush the volatile write cache. + Flush =3D bindings::req_op_REQ_OP_FLUSH, + /// Discard sectors. + Discard =3D bindings::req_op_REQ_OP_DISCARD, + /// Securely erase sectors. + SecureErase =3D bindings::req_op_REQ_OP_SECURE_ERASE, + /// Write data at the current zone write pointer. + ZoneAppend =3D bindings::req_op_REQ_OP_ZONE_APPEND, + /// Write zeroes. This allows to implement zeroing for devices that do= n't use either discard + /// with a predictable zero pattern or WRITE SAME of zeroes. + WriteZeroes =3D bindings::req_op_REQ_OP_WRITE_ZEROES, + /// Open a zone. + ZoneOpen =3D bindings::req_op_REQ_OP_ZONE_OPEN, + /// Close a zone. + ZoneClose =3D bindings::req_op_REQ_OP_ZONE_CLOSE, + /// Transition a zone to full. + ZoneFinish =3D bindings::req_op_REQ_OP_ZONE_FINISH, + /// Reset a zone write pointer. + ZoneReset =3D bindings::req_op_REQ_OP_ZONE_RESET, + /// Reset all the zones present on the device. + ZoneResetAll =3D bindings::req_op_REQ_OP_ZONE_RESET_ALL, + /// Driver private request for data transfer to the driver. + DriverIn =3D bindings::req_op_REQ_OP_DRV_IN, + /// Driver private request for data transfer from the driver. + DriverOut =3D bindings::req_op_REQ_OP_DRV_OUT, +} + +impl Command { + /// Creates a [`Command`] from a raw `u32` value. + /// + /// # Safety + /// + /// The value must be a valid `req_op` operation code. + pub unsafe fn from_raw(value: u32) -> Self { + // SAFETY: The caller guarantees that the value is a valid operati= on + // code. + unsafe { core::mem::transmute(value) } + } +} --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 F000E1FE45A; Sun, 15 Feb 2026 23:40:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198840; cv=none; b=W3muOzuqo7KoqwFJAU5Jr9l+jpUMC5ZgzNVuvOfzbCzpFmzJQpSO6sR7kopwvdDXQiIjTsWH/9n20Q/J5yIM0SVMODfbVX7Ny+NVTeIz3iOYWPmT3LFKxR6Dp0LbgLc1yrEIR+s8A8pcO2VkisbcqfGwlwtfoAcbYtDiyAnV1VE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198840; c=relaxed/simple; bh=O3C0OWPFlCYQRkqgbpxphoxzn0AXFyZkFVXVYjg1yE4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=RtXdq7qQQ5fbGiiga+BCcXWMT+wfzol5jD7LNItgCPPnvvgVTYXU/Hugx8XjGGgr4xVcA6jvi47pbF8pgialnjrZo3D+sp8amfr/jI08E8JlT5PT7viSMKyGdzL2fyYXyFYg7xYEABtez3KLLVmFsEG4QWFKfqUdNQoP2YQNiY4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=LcSSioyc; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="LcSSioyc" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2C190C4CEF7; Sun, 15 Feb 2026 23:40:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198839; bh=O3C0OWPFlCYQRkqgbpxphoxzn0AXFyZkFVXVYjg1yE4=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=LcSSioycaHq7sW6v7S8VaMxmPBnrA0XxM/+JfYOZzzNyMOKQ9iJnYTOaI0DgToSTp bB2PKE7e6AniGtKH1YCSTRffWWeIxzuD+r0kRNjm3NnH81MN88AskeW7Wrry71RgCl a3/M2ZTS8ZU/Q/c6edwuFMId2JfKy47xwf7GbEA8V+9ggW4LAExBmGR6o/VUhpP2j4 ihEBh2jyeTbSuOGQn57iigRsghpmM3IlkCAURIzlifsiQ1C3xmUlJU5anvFFLuew+G xu6LFD4QMgW2hvZKcSv15Gqui7zG641/KK0tZGRFSeme74yzer7kJsvhKkFSTXcdad xtSoAtNYXDWpQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:32 +0100 Subject: [PATCH 45/79] block: rust: add a method to set the target sector of a request 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: <20260216-rnull-v6-19-rc5-send-v1-45-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1174; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=O3C0OWPFlCYQRkqgbpxphoxzn0AXFyZkFVXVYjg1yE4=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhEQwaxLvWD0eoBUNtZkrfvjdBAfQM5QFLvA DPqydXXjkmJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYRAAKCRDhuBo+eShj d7HpEACf0tE//nAl5WKx3CMVmGyPVZd06Zxo3HMqJo6lqiyYsbSiJtt9bGZZbPI6/H5CWRAt1m2 szk1Q1ELBF+ypVdLMydJS0WraFl5vKpHY+tWPvkQURN5QOLlSCi6g9kz+gfDgRoKx8a5CfcRAEk uJFZuYIgnptGybIxMuQYiOwAxvwVFYPsI1zEQrvpNUhnq8DLGB6f6lvRE0s4/ZksCGPAxkRzQBy wF1pdl7X2z63pgbZZHh1ngeHc6g+UEfl/dzY9mHeqzwkYUYlUnECxI2FIOrKAF8D7grLtfAL/lc 6/hsl7UtTA1XOMdaHcnwVGjZjglQE7s0jCFbSsnYdJBn1ly3Z56xArWHvtVFcMasgoE7IKE0eOp iHbf9JNaLaCOrrMGicyUsl1f2QZNfrctAe/1c+Iw9Hk08uNCkkkd7i4AGiA1uHX+s1gFKtkikJ0 DHGZVJNZPRHJxRgdVcqfD1jq69/c0R/o1RJhlyuytbRxGcb0ok82GDSGv1r415P48i9IJNuqV4Z FeB9v1ChkeD6VgzxMWKi4xO+P5Tl7ivi9B/OrJHSPQu+Z3swBN9XdU4bZekYmvYojAZZ+uR8iPI Tj//FhDuPVGWtrVxOG06uGngv2m1dzJF9k1FciK+tf73F9L2IirB2Z2060mIt2oBbGHVpg9T4Lt EvIIMlfAMkM/cOA== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a `block::mq::Request::set_sector` to allow setting the target sector of a request. Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq/request.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index 4e47419776e0f..4e7adb10929f0 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -7,6 +7,7 @@ use crate::{ bindings, block::mq::Operations, + prelude::*, sync::{ aref::{ARef, RefCounted}, atomic::ordering, @@ -296,6 +297,13 @@ pub(crate) fn wrapper_ref(&self) -> &RequestDataWrappe= r { pub fn data_ref(&self) -> &T::RequestData { &self.wrapper_ref().data } + + /// Set the target sector for the request. + #[inline(always)] + pub fn set_sector(self: Pin<&mut Self>, sector: u64) { + // SAFETY: By type invariant of `Self`, `self.0` is valid and live. + unsafe { (*self.0 .0.get()).__sector =3D sector } + } } =20 /// A wrapper around data stored in the private area of the C [`struct req= uest`]. --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 345DD30BBAE; Sun, 15 Feb 2026 23:45:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199101; cv=none; b=eBqcGRFsG4sQnHFGl2kUB7w/RRftpaeYLYk5r39kILTyxjZSYhdKGq8RmU2Csxf3l/xQkiWRq42j9MU+0hqJxETeG4Ta1eQYpTawGhk/WtKxF52Dglf+6kB63xVjpnWuifJvBm8eGSPDOtWdx+mCSWI82XCX5Z+KU73fTWz1BH0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199101; c=relaxed/simple; bh=GKW86kl+p+OYSwPf2Q41uaeBW8CpLNtmUrapwK7eNLU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=cW6Hhy7ghFkrtSbb1WzQ4rNJePeTwYZAc3mkLXJ3pmc/CIc7yhe972qL8VNCYP5emGNEE+0ubpeiKbWLkSOqNuBWMJuBCpCwj+T+99sKYz2MRFl9CebsmqXSbSobghZnktyW3kPwn2TQAn5qDsrBtCl/skFVGDOyGuZm+OUEFAA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=h1auz/5i; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="h1auz/5i" Received: by smtp.kernel.org (Postfix) with ESMTPSA id CDD2BC4CEF7; Sun, 15 Feb 2026 23:44:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199101; bh=GKW86kl+p+OYSwPf2Q41uaeBW8CpLNtmUrapwK7eNLU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=h1auz/5iRD896cAXZlpHR+dDUEYCUYbZsj1fP3hBtQQzpz9B9cop2T0CTRCeiWlh2 s4/rS0fdDTV+lx4uRgfCELY/PxZUTSr4/pR7/WjlKCxEniFHNFoZY+egEQRsGC1BhN 6LutD8eT+I7lepVZCB+CKaSCo+78K3+FZInA842/v1H5babY27xW+ymrTouYTZsYFw gKccYcmwjjc/HZ9pbmmkqm4+l3mA9JVGowdU48IG0K8UhNBh9CQJrg1o8pxnR/q0ZB f0WTIsyja+tR07ChjAN6VRS/IDLFvdQljKX5ZrXTF5QA3Hn/nJebLHHK9OL9lAUkPS olq/lZs3NEzUA== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:33 +0100 Subject: [PATCH 46/79] block: rust: move gendisk vtable construction to separate function 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: <20260216-rnull-v6-19-rc5-send-v1-46-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=5340; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=GKW86kl+p+OYSwPf2Q41uaeBW8CpLNtmUrapwK7eNLU=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhFsomGNwQiKqCjwEzFCcRjle0tp7r1mEuBz PnfAoH6wiSJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYRQAKCRDhuBo+eShj d/nNEACRmzsCoK43XTYTgBY9N9dFJ5DpDKa32BQG4qau8nxvTPXcN7Tm1y7kgYUafBtt9fSDNnO nJvRlCSsXQ2XqmZzUhlywwUrmfqqQV/67m6LCTdre7MLyfOpPM79MuAofV4LymRfqnMuhwX35bo /nuJ6oDU5LEj1O0jnZSwxFHquBg/o7g/KAmSitrKqdjKuu1gWlZxTG+qAUj6efPjfOEzXwkvV0i lWda+EkMTJl6SK3eq+6ymSY4adKHrtTYqoCgINxS4uAXxLXwV/19Zb663PJhFESxCmSJimNzt4g AGsJbVd718TSMKQZWr9ZI7yB7Y9+ptrmSiY7pHwzCwhZJsexQZ1TAKTPN2d2L9hyEsDyKwkdyJH 7pSOWLOuGEDH4ZnXL+kfA2vy8gJ/7b4zUyGeQCgTvEWWL9a0AaKbacyM+wU2TFrcF0LhvzIEbRV CUFhDEGmRxmvaqnmmN6jCgO9zEIgGrV2kophX22Pzy639HDEjtWoU3XJWw1UydyDhiBKyP6Sfgh oUXsg68nFb/MckFOA1p8PrbjI5Tx4udUny7Ap0uIctqGN+AG1Bk9tQwx1TsQy3pBNuFffC7OrDq 6Ec4pqI/CyuVhXnpb007xuCJYDoa679rMhRKEOHkcBzuZT+7j9SEMtn5qt8F9IUXGzX+BbI7+nB feQtdV6gwpBTHQg== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Refactor the `GenDiskBuilder::build` method to move the `gendisk` vtable construction into a separate helper function. This prepares for adding zoned block device support which requires conditional vtable setup. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 5 ++- rust/kernel/block/mq/gen_disk.rs | 66 ++++++++++++++++++++++--------------= ---- 2 files changed, 40 insertions(+), 31 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 90f2ecb454285..5dfe7b48af63b 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -266,7 +266,10 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { } } =20 -configfs_simple_field!(DeviceConfig, 1, block_size, u32, check GenDiskBuil= der::validate_block_size); +configfs_simple_field!(DeviceConfig, 1, + block_size, u32, + check GenDiskBuilder::::validate_blo= ck_size +); configfs_simple_bool_field!(DeviceConfig, 2, rotational); configfs_simple_field!(DeviceConfig, 3, capacity_mib, u64); configfs_simple_field!(DeviceConfig, 4, irq_mode, IRQMode); diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_di= sk.rs index fbda2f572e17f..eb980079530bd 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -17,20 +17,21 @@ sync::{Arc, UniqueArc}, types::{ForeignOwnable, ScopeGuard}, }; -use core::ptr::NonNull; +use core::{marker::PhantomData, ptr::NonNull}; =20 /// A builder for [`GenDisk`]. /// /// Use this struct to configure and add new [`GenDisk`] to the VFS. -pub struct GenDiskBuilder { +pub struct GenDiskBuilder { rotational: bool, logical_block_size: u32, physical_block_size: u32, capacity_sectors: u64, max_hw_discard_sectors: u32, + _p: PhantomData, } =20 -impl Default for GenDiskBuilder { +impl Default for GenDiskBuilder { fn default() -> Self { Self { rotational: false, @@ -38,11 +39,12 @@ fn default() -> Self { physical_block_size: bindings::PAGE_SIZE as u32, capacity_sectors: 0, max_hw_discard_sectors: 0, + _p: PhantomData, } } } =20 -impl GenDiskBuilder { +impl GenDiskBuilder { /// Create a new instance. pub fn new() -> Self { Self::default() @@ -109,7 +111,7 @@ pub fn max_hw_discard_sectors(mut self, max_hw_discard_= sectors: u32) -> Self { } =20 /// Build a new `GenDisk` and add it to the VFS. - pub fn build( + pub fn build( self, name: fmt::Arguments<'_>, tagset: Arc>, @@ -141,32 +143,8 @@ pub fn build( ) })?; =20 - const TABLE: bindings::block_device_operations =3D bindings::block= _device_operations { - submit_bio: None, - open: None, - release: None, - ioctl: None, - compat_ioctl: None, - check_events: None, - unlock_native_capacity: None, - getgeo: None, - set_read_only: None, - swap_slot_free_notify: None, - report_zones: None, - devnode: None, - alternative_gpt_sector: None, - get_unique_id: None, - // TODO: Set to THIS_MODULE. Waiting for const_refs_to_static = feature to - // be merged (unstable in rustc 1.78 which is staged for linux= 6.10) - // - owner: core::ptr::null_mut(), - pr_ops: core::ptr::null_mut(), - free_disk: None, - poll_bio: None, - }; - // SAFETY: `gendisk` is a valid pointer as we initialized it above - unsafe { (*gendisk).fops =3D &TABLE }; + unsafe { (*gendisk).fops =3D Self::build_vtable() }; =20 let mut writer =3D NullTerminatedFormatter::new( // SAFETY: `gendisk` points to a valid and initialized instanc= e. We @@ -219,6 +197,34 @@ pub fn build( =20 Ok(disk.into()) } + + const VTABLE: bindings::block_device_operations =3D bindings::block_de= vice_operations { + submit_bio: None, + open: None, + release: None, + ioctl: None, + compat_ioctl: None, + check_events: None, + unlock_native_capacity: None, + getgeo: None, + set_read_only: None, + swap_slot_free_notify: None, + report_zones: None, + devnode: None, + alternative_gpt_sector: None, + get_unique_id: None, + // TODO: Set to THIS_MODULE. Waiting for const_refs_to_static feat= ure to + // be merged (unstable in rustc 1.78 which is staged for linux 6.1= 0) + // + owner: core::ptr::null_mut(), + pr_ops: core::ptr::null_mut(), + free_disk: None, + poll_bio: None, + }; + + pub(crate) const fn build_vtable() -> &'static bindings::block_device_= operations { + &Self::VTABLE + } } =20 /// A generic block device. --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 E4E092BEC4E; Sun, 15 Feb 2026 23:40:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198860; cv=none; b=eIXGF3NrduxGVJXFfnAfEyLJdAzzSzpEuhlM6dvlv5OubOW5RrGeNjLKyltuffMhTNJuQo66AX9Rd8QayKv4DhdLTdGJnd9w2U+rAcy/ZmUb6u9brAYihEwSXmYnGUNPcSVC0SroNDkS+OEUK3t8BwGPILG7u01WFOaCDRs1WQ8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198860; c=relaxed/simple; bh=Y3BHYVknJp7yqDHdJgoOQjOdwC77vJUS39fgNLkKcdI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=I+6sulpIfpzLCShKt6CCHt6gRn4HZ8AdOd9bZDCPdC7b9vqTZKigyKu1UyWQ0VJ95d2qbr9xmjQG7iJKJ/Bn1fQ7kAxYFeTj85/RJ+CSq8CDG/VAFx5H7CIv8OB04jKwErLV4+gWJ5+dZezeStzEkW3ulTfH7TJz8zNdh4BDFV0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Ms5DzXKi; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Ms5DzXKi" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 61AF0C19425; Sun, 15 Feb 2026 23:40:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198859; bh=Y3BHYVknJp7yqDHdJgoOQjOdwC77vJUS39fgNLkKcdI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Ms5DzXKi/d/xecYPMsETnLIURLmAXjUVHfew9lDkm8mTu2Lx2EIODS2d5EH/1EWtY CYd1KARZAi1lEs7tWFtTZNs1ITONN2icIgMAm8+TGP4/yAZaheSiwFxB/CFQ7kCGYl lgxJaAOI+rbbzhQxdyFDpOqEZUfwPPKsX8zlbk2LucEkV4CsM38G5uE8Z3+eQjEprh XsNNvEQF+6I0CJgfPCbZmodqW+agLhgzvc5xqJHHLwHzHtl3MIvBrnkhK1QpS2ikss asFR6D7aJWJ9zEs5H2hdfxwDZqw+GCeB/XnOpmn4gQbN82a7SFCGUznyoI4IlS5Kxl Ha34fEt/jaiyw== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:34 +0100 Subject: [PATCH 47/79] block: rust: add zoned block device support 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: <20260216-rnull-v6-19-rc5-send-v1-47-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=10869; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=Y3BHYVknJp7yqDHdJgoOQjOdwC77vJUS39fgNLkKcdI=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhGUDW7prSJo27C0mxbdYxJEQwAnNTB7yUsd cx/5l1zFDaJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYRgAKCRDhuBo+eShj d3inEACdaB0IAjno84bOvzt+dZs5T80xFhT129GRa5oGVUzDFGz9NdfNjOOYu/Oo3S7TKbDNiWj HM0Z8qIGLxRDlEeSog00YB1ZNYKUBOwq6pNuGgKQPzbUPNUiO13hNHu/fiW9OyQg/BuGW8A8JxT 2/OAhCq1zRtu//wFcZjqkS/njgPRxVPfQdWW/sgak/BzFYCVwkAI6Mnx3gsbjUGdlVD+2p98y68 7cz7Msg8Kmeh0Pq3Hp2LfSQ8aWD9yuMjaRSNCxaOSCccGN3mqgwqlbQLunbKCIk+VyZ8XU/+LwW s01zhY0td/ym4UcpW/ye1B83f0+jV1TUvY0FMrGJoEEMRDyvv2xp0LegdDK6J6IYjCsi9Xwcrb8 vn9FOeYC76EGj0/hzoDr1uBIZTM8N9uCXW2gW7Ew2I3HMITw3WLNksepKsd86UjqwRdYqt6vn5r /4J58RevVcYyyYJggoNehZ1MZHBBGseHvNabSX/eMFEs2EQ2Mlly5pZl0/F/hq0nPcuHI1US9hi i/PisBaA1BGfN8fqq5cPtaWqBMpdkaSP48c8yz5C4g1xkSGWIPn3ARNLmpX/6Nkx3z/Ne3OUvVc G7KFZxA5vsQa4GgxkhS46iYTpBiqaFbX83ZMgB7ndF7t4XFPXbgUg7EeNWcy32ck7fjw9Qt6xet oijO/nTOZgX5Ceg== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add support for zoned block devices to the Rust block layer bindings. This includes the `report_zones` callback in `Operations` and methods in `GenDiskBuilder` to configure zoned device parameters. Drivers can mark a disk as zoned and configure the zone size and maximum zone append size. The `report_zones` callback is invoked by the block layer to query zone information. Signed-off-by: Andreas Hindborg --- rust/bindings/bindings_helper.h | 1 + rust/kernel/block/mq/gen_disk.rs | 96 +++++++++++++++++++++++++++++++++-= ---- rust/kernel/block/mq/operations.rs | 60 ++++++++++++++++++++++-- 3 files changed, 142 insertions(+), 15 deletions(-) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 76b58c3fd1ff1..05133a78ecf95 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -133,6 +133,7 @@ const blk_status_t RUST_CONST_HELPER_BLK_STS_ZONE_ACTIV= E_RESOURCE =3D BLK_STS_ZONE const blk_status_t RUST_CONST_HELPER_BLK_STS_OFFLINE =3D BLK_STS_OFFLINE; const blk_status_t RUST_CONST_HELPER_BLK_STS_DURATION_LIMIT =3D BLK_STS_DU= RATION_LIMIT; const blk_status_t RUST_CONST_HELPER_BLK_STS_INVAL =3D BLK_STS_INVAL; +const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ZONED =3D BLK_FEAT_ZONED; 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; diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_di= sk.rs index eb980079530bd..a6f113ea4bea4 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -7,7 +7,7 @@ =20 use crate::{ bindings, - block::mq::{Operations, RequestQueue, TagSet}, + block::mq::{operations::OperationsVTable, Operations, RequestQueue, Ta= gSet}, error::{self, from_err_ptr, Result}, fmt::{self, Write}, prelude::*, @@ -28,6 +28,12 @@ pub struct GenDiskBuilder { physical_block_size: u32, capacity_sectors: u64, max_hw_discard_sectors: u32, + #[cfg(CONFIG_BLK_DEV_ZONED)] + zoned: bool, + #[cfg(CONFIG_BLK_DEV_ZONED)] + zone_size_sectors: u32, + #[cfg(CONFIG_BLK_DEV_ZONED)] + zone_append_max_sectors: u32, _p: PhantomData, } =20 @@ -39,6 +45,12 @@ fn default() -> Self { physical_block_size: bindings::PAGE_SIZE as u32, capacity_sectors: 0, max_hw_discard_sectors: 0, + #[cfg(CONFIG_BLK_DEV_ZONED)] + zoned: false, + #[cfg(CONFIG_BLK_DEV_ZONED)] + zone_size_sectors: 0, + #[cfg(CONFIG_BLK_DEV_ZONED)] + zone_append_max_sectors: 0, _p: PhantomData, } } @@ -110,6 +122,27 @@ pub fn max_hw_discard_sectors(mut self, max_hw_discard= _sectors: u32) -> Self { self } =20 + /// Mark this device as a zoned block device. + #[cfg(CONFIG_BLK_DEV_ZONED)] + pub fn zoned(mut self, enable: bool) -> Self { + self.zoned =3D enable; + self + } + + /// Set the zone size of this block device. + #[cfg(CONFIG_BLK_DEV_ZONED)] + pub fn zone_size(mut self, sectors: u32) -> Self { + self.zone_size_sectors =3D sectors; + self + } + + /// Set the max zone append size for this block device. + #[cfg(CONFIG_BLK_DEV_ZONED)] + pub fn zone_append_max(mut self, sectors: u32) -> Self { + self.zone_append_max_sectors =3D sectors; + self + } + /// Build a new `GenDisk` and add it to the VFS. pub fn build( self, @@ -130,7 +163,18 @@ pub fn build( lim.physical_block_size =3D self.physical_block_size; lim.max_hw_discard_sectors =3D self.max_hw_discard_sectors; if self.rotational { - lim.features =3D bindings::BLK_FEAT_ROTATIONAL; + lim.features |=3D bindings::BLK_FEAT_ROTATIONAL; + } + + #[cfg(CONFIG_BLK_DEV_ZONED)] + if self.zoned { + if !T::HAS_REPORT_ZONES { + return Err(error::code::EINVAL); + } + + lim.features |=3D bindings::BLK_FEAT_ZONED; + lim.chunk_sectors =3D self.zone_size_sectors; + lim.max_hw_zone_append_sectors =3D self.zone_append_max_sector= s; } =20 // SAFETY: `tagset.raw_tag_set()` points to a valid and initialize= d tag set @@ -160,14 +204,6 @@ pub fn build( // operation, so we will not race. unsafe { bindings::set_capacity(gendisk, self.capacity_sectors) }; =20 - crate::error::to_result( - // SAFETY: `gendisk` points to a valid and initialized instanc= e of - // `struct gendisk`. - unsafe { - bindings::device_add_disk(core::ptr::null_mut(), gendisk, = core::ptr::null_mut()) - }, - )?; - recover_data.dismiss(); =20 // INVARIANT: `gendisk` was initialized above. @@ -195,7 +231,27 @@ pub fn build( GFP_KERNEL, )?; =20 - Ok(disk.into()) + let disk: Arc<_> =3D disk.into(); + + // SAFETY: `disk.gendisk` is valid for write as we initialized it = above. We have exclusive + // access. + unsafe { (*disk.gendisk).private_data =3D Arc::as_ptr(&disk).cast_= mut().cast() }; + + #[cfg(CONFIG_BLK_DEV_ZONED)] + if self.zoned { + // SAFETY: `disk.gendisk` is valid as we initialized it above.= We have exclusive access. + unsafe { bindings::blk_revalidate_disk_zones(gendisk) }; + } + + crate::error::to_result( + // SAFETY: `gendisk` points to a valid and initialized instanc= e of + // `struct gendisk`. + unsafe { + bindings::device_add_disk(core::ptr::null_mut(), gendisk, = core::ptr::null_mut()) + }, + )?; + + Ok(disk) } =20 const VTABLE: bindings::block_device_operations =3D bindings::block_de= vice_operations { @@ -209,7 +265,11 @@ pub fn build( getgeo: None, set_read_only: None, swap_slot_free_notify: None, - report_zones: None, + report_zones: if T::HAS_REPORT_ZONES { + Some(OperationsVTable::::report_zones_callback) + } else { + None + }, devnode: None, alternative_gpt_sector: None, get_unique_id: None, @@ -295,6 +355,18 @@ fn drop(&mut self) { /// `self.0` is valid for use as a reference. pub struct GenDiskRef(NonNull>); =20 +impl GenDiskRef { + /// Create a `GenDiskRef` from a pointer to a `GenDisk`. + /// + /// # Safety + /// + /// `ptr` must be valid for use as a `GenDisk` reference for the lifet= ime of the returned + /// `GenDiskRef`. + pub(crate) unsafe fn from_ptr(ptr: NonNull>) -> GenDiskRef<= T> { + Self(ptr) + } +} + // SAFETY: It is safe to transfer ownership of `GenDiskRef` across thread = boundaries. unsafe impl Send for GenDiskRef {} =20 diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/oper= ations.rs index 17468a39af60f..3f84ebadec86b 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -8,14 +8,14 @@ bindings, block::{ error::BlkResult, - mq::{request::RequestDataWrapper, IdleRequest, Request}, + mq::{gen_disk::GenDiskRef, request::RequestDataWrapper, IdleReques= t, Request}, }, - error::{from_result, Result}, + error::{from_result, to_result, Result}, prelude::*, sync::{aref::ARef, atomic::ordering, Refcount}, types::{ForeignOwnable, Owned}, }; -use core::marker::PhantomData; +use core::{marker::PhantomData, ptr::NonNull}; use pin_init::PinInit; =20 type ForeignBorrowed<'a, T> =3D ::Borrowed<'a>; @@ -87,6 +87,20 @@ fn init_hctx( fn poll(_hw_data: ForeignBorrowed<'_, Self::HwData>) -> bool { build_error!(crate::error::VTABLE_DEFAULT_ERROR) } + + /// Called by the kernel to get a zone report from the driver. + /// + /// The driver must call `callback` once for each zone on `disk` and p= opulate the first argument + /// with a zone descriptor and the second argument when the zone index. + // TODO: We cannot gate this on CONFIG_BLK_DEV_ZONED due to limitation= s of the `vtable` macro. + fn report_zones( + _disk: &GenDiskRef, + _sector: u64, + _nr_zones: u32, + _callback: impl Fn(&bindings::blk_zone, u32) -> Result, + ) -> Result { + Err(ENOTSUPP) + } } =20 /// A vtable for blk-mq to interact with a block device driver. @@ -340,6 +354,46 @@ impl OperationsVTable { unsafe { core::ptr::drop_in_place(pdu) }; } =20 + /// This function is a callback hook for the C kernel. A pointer to th= is function is + /// installed in the `blk_mq_ops` vtable for the driver. + /// + /// # Safety + /// + /// - This function may only be called by blk-mq C infrastructure. + /// - `disk_ptr` must be a pointer to a gendisk initialized by `GenDis= k::build`. + pub(crate) unsafe extern "C" fn report_zones_callback( + disk_ptr: *mut bindings::gendisk, + sector: u64, + nr_zones: u32, + args: *mut bindings::blk_report_zones_args, + ) -> i32 { + // SAFETY: As `disk_ptr` is a gendisk initialized by `GenDisk::bui= ld`, `private_data` is not + // null. + let disk_ref_ptr =3D unsafe { NonNull::new_unchecked((*disk_ptr).p= rivate_data.cast()) }; + + // SAFETY: `disk_ptr.private_data` is a pointer to the `GenDisk` o= wner of `disk_ptr` that we + // installed when we initialized `disk_ptr`. It is valid for use a= s a reference for the + // duration of this call. + let disk =3D unsafe { GenDiskRef::from_ptr(disk_ref_ptr) }; + + from_result(|| { + T::report_zones(&disk, sector, nr_zones, |zone, idx| -> Result= { + to_result( + // SAFETY: `disk_ptr` is valid by function safety requ= irements. + unsafe { + bindings::disk_report_zone( + disk_ptr, + core::ptr::from_ref(zone).cast_mut(), + idx, + args, + ) + }, + ) + }) + .and_then(|v: u32| -> Result<_> { Ok(v.try_into()?) }) + }) + } + const VTABLE: bindings::blk_mq_ops =3D bindings::blk_mq_ops { queue_rq: Some(Self::queue_rq_callback), queue_rqs: None, --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 430772BEC34; Sun, 15 Feb 2026 23:41:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198919; cv=none; b=eZSIlFvlkQJ7dCD0Ty+gRU90brffA2ZqRMHned+qKvQAX83P9/yjELKlqqWqxPDWYr8ve+/oFdSy4vFDhORDEj7hU5mRL6Ca80mh511+ChfWZegzpAS582e0SiG8VEWUZCPlpJZBXGqu9IVgtq4PEvFlg1x+o3Vz5WCZQmmebgQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198919; c=relaxed/simple; bh=XA+ZRMHYjIMd+7vLIFWY0jOWQAXGj1Yr0g3bjUD9UiQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=cBlu67tEWDdMOVe8TTat6U2I+XqMK+Spb4VBL6aEvsOR1zTXhsw+Ttud5vEEQORbxQGnQJsWZDwDfcquxjAIksKRf1NVNyWUZSn8f/xqwBk4LA4dvhOz+vK2Gd4Sqfpns+vndIa8inZZb7p6EnWEGzI4Q39Jx7zH0S7IwAOfOTo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Ll/fJVtB; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Ll/fJVtB" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A1D6AC19422; Sun, 15 Feb 2026 23:41:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198918; bh=XA+ZRMHYjIMd+7vLIFWY0jOWQAXGj1Yr0g3bjUD9UiQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Ll/fJVtBeL4EhJO69U4MKC5o8nM1orCIQTfVNzZujG3mbKSuv36MUMPryibUSeksy SD302ofeYIcqpPsy0QkZTjUHDydyyAwh2ZNaOgIdJYFegIUSslA8G9EKX8xhFB0ARQ KwHT+M/LoiSJf1pwCD8SqNCTp5iuSiLvnlMQBGjU1KZRp+a2E3pK107rth1MupcPZA 4vqP7mAmV2PFaB31L3WpwXwxYgyQpHqeoTWgAkm6/dZFadJP0LbPBHxnJzKKZuSQXY VOibMDT/caD4k7NSQCJdSyb+YBAk2GU6lpbFGHiuRxpTNNUdef4k/WjChe/YlDt6U4 eYbo+vlSLKXaw== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:35 +0100 Subject: [PATCH 48/79] block: rnull: add zoned storage support 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: <20260216-rnull-v6-19-rc5-send-v1-48-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=49033; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=XA+ZRMHYjIMd+7vLIFWY0jOWQAXGj1Yr0g3bjUD9UiQ=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhG4u5RMt08dzvXcwBiXtmKYN5ECCVP4tjt3 Qof2/w93g+JAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYRgAKCRDhuBo+eShj dw3TEACLiSVkhU3ym++xVUD86Ho9NbiYYXDlgvgXQb2GZMD4BLwc/9+aq8Xy1wsUkGBR76Tg91e xvuvzd964aywAmxAAEyE6jQD7cGrSWPFEyUmNe2Ue7PT+Zo6x3rwkQZF5XTkJAzpY7Q21q8Pfcn I4dlSwoMSAtHyZY2rQaYhzltGrFjPfkECWNaeiTBvs+54bY3wcBf4fnSlteyia8LFIZwQFCvD/Y /zApneFOdu+YnxgB8A7uN6Ufb/U+Eae39P03aemtda8qJinN76OLsOM8XYdrg7LrLsSRMT8vRi5 UOdwobqgc3PR8QOMVcGqnB1x3c7C2NR8ZN6BXMUjvJ9k5LbnPZgIPXYwBslOaX8Tqsa1/mtBzeQ pO1NxGIKD7GkyG+DTKBAkZhsur7zPj4VJqNDqe3MnkGh+rfrHQMZm5eOum8Mx/2nsHq8ZECntLG lgRbgnF7g259jUFPlnUKOhgNC68v2yNPWEEKOGxyJi5nfe1wIEc62bxUhwFZIh3nJdgwX6P3Yi4 vt/jnsnfAmWuaWSiZ1fnvZz6yZa6N6tg3+aaIqdlyVWJKUIy3dyY9Ygb/dUxUkfqaL/VufJ67Xl SXFmPpxJ1g2YNw7F7IbUzvYtL08IMOn+XSVroueAy0xTYT2OoBAH8lyF925zINWwyyHxAisktPl BMMmuPUAaJVovxQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add zoned block device emulation to rnull. When enabled via the `zoned` configfs attribute, the driver emulates a zoned storage device with configurable zone size and zone count. The implementation supports zone management operations including zone reset, zone open, zone close, and zone finish. Zone write pointer tracking is maintained for sequential write required zones. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 54 ++- drivers/block/rnull/disk_storage.rs | 34 +- drivers/block/rnull/disk_storage/page.rs | 4 +- drivers/block/rnull/rnull.rs | 233 +++++++---- drivers/block/rnull/util.rs | 65 +++ drivers/block/rnull/zoned.rs | 661 +++++++++++++++++++++++++++= ++++ 6 files changed, 951 insertions(+), 100 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 5dfe7b48af63b..0873d696f80f6 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -70,7 +70,8 @@ impl AttributeOperations<0> for Config { let mut writer =3D kernel::str::Formatter::new(page); writer.write_str( "blocksize,size,rotational,irqmode,completion_nsec,memory_back= ed\ - submit_queues,use_per_node_hctx,discard,blocking,shared_tags\= n", + submit_queues,use_per_node_hctx,discard,blocking,shared_tags,\ + zoned,zone_size,zone_capacity\n", )?; Ok(writer.bytes_written()) } @@ -108,7 +109,14 @@ fn make_group( mbps: 16, blocking: 17, shared_tags: 18, - hw_queue_depth: 19 + hw_queue_depth: 19, + zoned: 20, + zone_size: 21, + zone_capacity: 22, + zone_nr_conv: 23, + zone_max_open: 24, + zone_max_active: 25, + zone_append_max_sectors: 26, ], }; =20 @@ -135,15 +143,19 @@ fn make_group( bad_blocks: Arc::pin_init(BadBlocks::new(false), GFP_K= ERNEL)?, bad_blocks_once: false, bad_blocks_partial_io: false, - disk_storage: Arc::pin_init( - DiskStorage::new(0, block_size as usize), - GFP_KERNEL - )?, + disk_storage: Arc::pin_init(DiskStorage::new(0, block_= size), GFP_KERNEL)?, cache_size_mib: 0, mbps: 0, blocking: false, shared_tags: false, hw_queue_depth: 64, + zoned: false, + zone_size_mib: 256, + zone_capacity_mib: 0, + zone_nr_conv: 0, + zone_max_open: 0, + zone_max_active: 0, + zone_append_max_sectors: u32::MAX, }), }), core::iter::empty(), @@ -212,6 +224,13 @@ struct DeviceConfigInner { blocking: bool, shared_tags: bool, hw_queue_depth: u32, + zoned: bool, + zone_size_mib: u32, + zone_capacity_mib: u32, + zone_nr_conv: u32, + zone_max_open: u32, + zone_max_active: u32, + zone_append_max_sectors: u32, } =20 #[vtable] @@ -237,9 +256,9 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { if !guard.powered && power_op { guard.disk =3D Some(NullBlkDevice::new(crate::NullBlkOptions { name: &guard.name, - block_size: guard.block_size, + block_size_bytes: guard.block_size, rotational: guard.rotational, - capacity_mib: guard.capacity_mib, + device_capacity_mib: guard.capacity_mib, irq_mode: guard.irq_mode, completion_time: guard.completion_time, memory_backed: guard.memory_backed, @@ -255,6 +274,13 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { blocking: guard.blocking, shared_tags: guard.shared_tags, hw_queue_depth: guard.hw_queue_depth, + zoned: guard.zoned, + zone_size_mib: guard.zone_size_mib, + zone_capacity_mib: guard.zone_capacity_mib, + zone_nr_conv: guard.zone_nr_conv, + zone_max_open: guard.zone_max_open, + zone_max_active: guard.zone_max_active, + zone_append_max_sectors: guard.zone_append_max_sectors, })?); guard.powered =3D true; } else if guard.powered && !power_op { @@ -467,10 +493,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { let text =3D core::str::from_utf8(page)?.trim(); let value =3D text.parse::().map_err(|_| EINVAL)?; let mut guard =3D this.data.lock(); - guard.disk_storage =3D Arc::pin_init( - DiskStorage::new(value, guard.block_size as usize), - GFP_KERNEL - )?; + guard.disk_storage =3D Arc::pin_init(DiskStorage::new(value, guard= .block_size), GFP_KERNEL)?; guard.cache_size_mib =3D value; Ok(()) }) @@ -480,3 +503,10 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { configfs_simple_bool_field!(DeviceConfig, 17, blocking); configfs_simple_bool_field!(DeviceConfig, 18, shared_tags); configfs_simple_field!(DeviceConfig, 19, hw_queue_depth, u32); +configfs_simple_bool_field!(DeviceConfig, 20, zoned); +configfs_simple_field!(DeviceConfig, 21, zone_size_mib, u32); +configfs_simple_field!(DeviceConfig, 22, zone_capacity_mib, u32); +configfs_simple_field!(DeviceConfig, 23, zone_nr_conv, u32); +configfs_simple_field!(DeviceConfig, 24, zone_max_open, u32); +configfs_simple_field!(DeviceConfig, 25, zone_max_active, u32); +configfs_simple_field!(DeviceConfig, 26, zone_append_max_sectors, u32); diff --git a/drivers/block/rnull/disk_storage.rs b/drivers/block/rnull/disk= _storage.rs index 8a8a90e1cf0bd..ce3e83671709a 100644 --- a/drivers/block/rnull/disk_storage.rs +++ b/drivers/block/rnull/disk_storage.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 =20 use super::HwQueueContext; +use crate::util::*; use core::pin::Pin; use kernel::{ block, @@ -9,8 +10,12 @@ page::PAGE_SIZE, prelude::*, sync::{ - atomic::{ordering, Atomic}, - SpinLock, SpinLockGuard, + atomic::{ + ordering, + Atomic, // + }, + SpinLock, + SpinLockGuard, // }, uapi::PAGE_SECTORS, xarray::{ @@ -31,11 +36,11 @@ pub(crate) struct DiskStorage { cache_size: u64, cache_size_used: Atomic, next_flush_sector: Atomic, - block_size: usize, + block_size: u32, } =20 impl DiskStorage { - pub(crate) fn new(cache_size: u64, block_size: usize) -> impl PinInit<= Self, Error> { + pub(crate) fn new(cache_size: u64, block_size: u32) -> impl PinInit { try_pin_init!( Self { // TODO: Get rid of the box // https://git.kernel.org/pub/scm/linux/kernel/git/boqun/linux= .git/commit/?h=3Dlocking&id=3Da5d84cafb3e253a11d2e078902c5b090be2f4227 @@ -59,6 +64,27 @@ pub(crate) fn access<'a, 'b, 'c>( pub(crate) fn lock(&self) -> SpinLockGuard<'_, Pin= >> { self.trees.lock() } + + pub(crate) fn discard( + &self, + hw_data: &Pin<&SpinLock>, + mut sector: u64, + sectors: u32, + ) { + let mut tree_guard =3D self.lock(); + let mut hw_data_guard =3D hw_data.lock(); + + let mut access =3D self.access(&mut tree_guard, &mut hw_data_guard= , None); + + let mut remaining_bytes =3D sectors_to_bytes(sectors); + + while remaining_bytes > 0 { + access.free_sector(sector); + let processed =3D remaining_bytes.min(self.block_size); + sector +=3D Into::::into(bytes_to_sectors(processed)); + remaining_bytes -=3D processed; + } + } } =20 pub(crate) struct DiskStorageAccess<'a, 'b, 'c> { diff --git a/drivers/block/rnull/disk_storage/page.rs b/drivers/block/rnull= /disk_storage/page.rs index c2e18502cbdda..a34fe0762724d 100644 --- a/drivers/block/rnull/disk_storage/page.rs +++ b/drivers/block/rnull/disk_storage/page.rs @@ -19,11 +19,11 @@ pub(crate) struct NullBlockPage { page: Owned, status: u64, - block_size: usize, + block_size: u32, } =20 impl NullBlockPage { - pub(crate) fn new(block_size: usize) -> Result> { + pub(crate) fn new(block_size: u32) -> Result> { Ok(KBox::new( Self { page: SafePage::alloc_page(GFP_NOIO | __GFP_ZERO)?, diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 9383b82f9a736..48b2bd598304c 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -2,8 +2,13 @@ =20 //! This is a Rust implementation of the C null block driver. =20 +#![recursion_limit =3D "256"] + mod configfs; mod disk_storage; +mod util; +#[cfg(CONFIG_BLK_DEV_ZONED)] +mod zoned; =20 use configfs::IRQMode; use disk_storage::{ @@ -77,6 +82,7 @@ xarray::XArraySheaf, // }; use pin_init::PinInit; +use util::*; =20 module! { type: NullBlkModule, @@ -153,6 +159,35 @@ default: 64, description: "Queue depth for each hardware queue. Default: 6= 4", }, + zoned: u8 { + default: 0, + description: "Make device as a host-managed zoned block device= . Default: 0", + }, + zone_size: u32 { + default: 256, + description: + "Zone size in MB when block device is zoned. Must be power-of-= two: Default: 256", + }, + zone_capacity: u32 { + default: 0, + description: "Zone capacity in MB when block device is zoned. = Can be less than or equal to zone size. Default: Zone size", + }, + zone_nr_conv: u32 { + default: 0, + description: "Number of conventional zones when block device i= s zoned. Default: 0", + }, + zone_max_open: u32 { + default: 0, + description: "Maximum number of open zones when block device i= s zoned. Default: 0 (no limit)", + }, + zone_max_active: u32 { + default: 0, + description: "Maximum number of active zones when block device= is zoned. Default: 0 (no limit)", + }, + zone_append_max_sectors: u32 { + default: 0, + description: "Maximum size of a zone append command (in 512B s= ectors). Specify 0 for no zone append.", + }, }, } =20 @@ -184,9 +219,9 @@ fn init(_module: &'static ThisModule) -> impl PinInit { let block_size =3D *module_parameters::bs.value(); let disk =3D NullBlkDevice::new(NullBlkOptions { name: &name, - block_size, + block_size_bytes: block_size, rotational: *module_parameters::rotational.value() != =3D 0, - capacity_mib: *module_parameters::gb.value() * 1024, + device_capacity_mib: *module_parameters::gb.value() * = 1024, irq_mode: (*module_parameters::irqmode.value()).try_in= to()?, completion_time: Delta::from_nanos(completion_time), memory_backed: *module_parameters::memory_backed.value= () !=3D 0, @@ -197,11 +232,18 @@ fn init(_module: &'static ThisModule) -> impl PinInit= { bad_blocks: Arc::pin_init(BadBlocks::new(false), GFP_K= ERNEL)?, bad_blocks_once: false, bad_blocks_partial_io: false, - storage: Arc::pin_init(DiskStorage::new(0, block_size = as usize), GFP_KERNEL)?, + storage: Arc::pin_init(DiskStorage::new(0, block_size)= , GFP_KERNEL)?, bandwidth_limit: u64::from(*module_parameters::mbps.va= lue()) * 2u64.pow(20), blocking: *module_parameters::blocking.value() !=3D 0, shared_tags: *module_parameters::shared_tags.value() != =3D 0, hw_queue_depth: *module_parameters::hw_queue_depth.val= ue(), + zoned: *module_parameters::zoned.value() !=3D 0, + zone_size_mib: *module_parameters::zone_size.value(), + zone_capacity_mib: *module_parameters::zone_capacity.v= alue(), + zone_nr_conv: *module_parameters::zone_nr_conv.value(), + zone_max_open: *module_parameters::zone_max_open.value= (), + zone_max_active: *module_parameters::zone_max_active.v= alue(), + zone_append_max_sectors: *module_parameters::zone_appe= nd_max_sectors.value(), })?; disks.push(disk, GFP_KERNEL)?; } @@ -218,9 +260,9 @@ fn init(_module: &'static ThisModule) -> impl PinInit { =20 struct NullBlkOptions<'a> { name: &'a CStr, - block_size: u32, + block_size_bytes: u32, rotational: bool, - capacity_mib: u64, + device_capacity_mib: u64, irq_mode: IRQMode, completion_time: Delta, memory_backed: bool, @@ -236,6 +278,19 @@ struct NullBlkOptions<'a> { blocking: bool, shared_tags: bool, hw_queue_depth: u32, + zoned: bool, + #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), expect(unused_variables))] + zone_size_mib: u32, + #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), expect(unused_variables))] + zone_capacity_mib: u32, + #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), expect(unused_variables))] + zone_nr_conv: u32, + #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), expect(unused_variables))] + zone_max_open: u32, + #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), expect(unused_variables))] + zone_max_active: u32, + #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), expect(unused_variables))] + zone_append_max_sectors: u32, } =20 static SHARED_TAG_SET: SetOnce>> =3D SetOnce::ne= w(); @@ -246,7 +301,7 @@ struct NullBlkDevice { irq_mode: IRQMode, completion_time: Delta, memory_backed: bool, - block_size: usize, + block_size_bytes: u32, bad_blocks: Arc, bad_blocks_once: bool, bad_blocks_partial_io: bool, @@ -257,6 +312,9 @@ struct NullBlkDevice { #[pin] bandwidth_timer_handle: SpinLock>>, disk: SetOnce>>>, + #[cfg(CONFIG_BLK_DEV_ZONED)] + #[pin] + zoned: zoned::ZoneOptions, } =20 impl NullBlkDevice { @@ -265,9 +323,9 @@ impl NullBlkDevice { fn new(options: NullBlkOptions<'_>) -> Result>> { let NullBlkOptions { name, - block_size, + block_size_bytes, rotational, - capacity_mib, + device_capacity_mib, irq_mode, completion_time, memory_backed, @@ -283,6 +341,13 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { blocking, shared_tags, hw_queue_depth, + zoned, + zone_size_mib, + zone_capacity_mib, + zone_nr_conv, + zone_max_open, + zone_max_active, + zone_append_max_sectors, } =3D options; =20 let mut flags =3D mq::tag_set::Flags::default(); @@ -315,13 +380,15 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { tagset_ctor()? }; =20 + let device_capacity_sectors =3D mib_to_sectors(device_capacity_mib= ); + let queue_data =3D Arc::try_pin_init( try_pin_init!(Self { storage, irq_mode, completion_time, memory_backed, - block_size: block_size as usize, + block_size_bytes, bad_blocks, bad_blocks_once, bad_blocks_partial_io, @@ -330,17 +397,42 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { bandwidth_bytes: Atomic::new(0), bandwidth_timer_handle <- new_spinlock!(None), disk: SetOnce::new(), + #[cfg(CONFIG_BLK_DEV_ZONED)] + zoned <- zoned::ZoneOptions::new(zoned::ZoneOptionsArgs { + enable: zoned, + device_capacity_mib, + block_size_bytes: *block_size_bytes, + zone_size_mib, + zone_capacity_mib, + zone_nr_conv, + zone_max_open, + zone_max_active, + zone_append_max_sectors, + })?, }), GFP_KERNEL, )?; =20 let mut builder =3D gen_disk::GenDiskBuilder::new() - .capacity_sectors(capacity_mib << (20 - block::SECTOR_SHIFT)) - .logical_block_size(block_size)? - .physical_block_size(block_size)? + .capacity_sectors(device_capacity_sectors) + .logical_block_size(block_size_bytes)? + .physical_block_size(block_size_bytes)? .rotational(rotational); =20 - if memory_backed && discard { + #[cfg(CONFIG_BLK_DEV_ZONED)] + { + builder =3D builder + .zoned(zoned) + .zone_size(queue_data.zoned.size_sectors) + .zone_append_max(zone_append_max_sectors); + } + + if !cfg!(CONFIG_BLK_DEV_ZONED) && zoned { + return Err(ENOTSUPP); + } + + // TODO: Warn on invalid discard configuration (zoned, memory) + if memory_backed && discard && !zoned { builder =3D builder // Max IO size is u32::MAX bytes .max_hw_discard_sectors(ffi::c_uint::MAX >> block::SECTOR_= SHIFT); @@ -364,13 +456,12 @@ fn sheaf_size() -> usize { fn preload<'b, 'c>( tree_guard: &'b mut SpinLockGuard<'c, Pin>>, hw_data_guard: &'b mut SpinLockGuard<'c, HwQueueContext>, - block_size: usize, + block_size_bytes: u32, ) -> Result { if hw_data_guard.page.is_none() { - hw_data_guard.page =3D - Some(tree_guard.do_unlocked(|| { - hw_data_guard.do_unlocked(|| NullBlockPage::new(block_= size)) - })?); + hw_data_guard.page =3D Some(tree_guard.do_unlocked(|| { + hw_data_guard.do_unlocked(|| NullBlockPage::new(block_size= _bytes)) + })?); } =20 Ok(()) @@ -387,7 +478,7 @@ fn write<'a, 'b, 'c>( let mut sheaf: Option> =3D None; =20 while !segment.is_empty() { - Self::preload(tree_guard, hw_data_guard, self.block_size)?; + Self::preload(tree_guard, hw_data_guard, self.block_size_bytes= )?; =20 match &mut sheaf { Some(sheaf) =3D> { @@ -453,48 +544,23 @@ fn read<'a, 'b, 'c>( sector +=3D segment.copy_from_page(page.page(), page_o= ffset as usize) as u64 >> block::SECTOR_SHIFT; } - None =3D> sector +=3D segment.zero_page() as u64 >> block:= :SECTOR_SHIFT, + None =3D> sector +=3D bytes_to_sectors(segment.zero_page()= as u64), } } =20 Ok(()) } =20 - fn discard( - &self, - hw_data: &Pin<&SpinLock>, - mut sector: u64, - sectors: u32, - ) -> Result { - let mut tree_guard =3D self.storage.lock(); - let mut hw_data_guard =3D hw_data.lock(); - - let mut access =3D self - .storage - .access(&mut tree_guard, &mut hw_data_guard, None); - - let mut remaining_bytes =3D (sectors as usize) << SECTOR_SHIFT; - - while remaining_bytes > 0 { - access.free_sector(sector); - let processed =3D remaining_bytes.min(self.block_size); - sector +=3D (processed >> SECTOR_SHIFT) as u64; - remaining_bytes -=3D processed; - } - - Ok(()) - } - #[inline(never)] fn transfer( &self, hw_data: &Pin<&SpinLock>, rq: &mut Owned>, + command: mq::Command, sectors: u32, ) -> Result { let mut sector =3D rq.sector(); let end_sector =3D sector + >::into(sectors); - let command =3D rq.command(); =20 // TODO: Use `PerCpu` to get rid of this lock let mut hw_data_guard =3D hw_data.lock(); @@ -527,6 +593,26 @@ fn transfer( Ok(()) } =20 + fn handle_regular_command( + &self, + hw_data: &Pin<&SpinLock>, + rq: &mut Owned>, + ) -> Result { + let mut sectors =3D rq.sectors(); + + self.handle_bad_blocks(rq, &mut sectors)?; + + if self.memory_backed { + if rq.command() =3D=3D mq::Command::Discard { + self.storage.discard(hw_data, rq.sector(), sectors); + } else { + self.transfer(hw_data, rq, rq.command(), sectors)?; + } + } + + Ok(()) + } + fn handle_bad_blocks(&self, rq: &mut Owned>, sectors= : &mut u32) -> Result { if self.bad_blocks.enabled() { let start =3D rq.sector(); @@ -542,7 +628,7 @@ fn handle_bad_blocks(&self, rq: &mut Owned>, sectors: &mut u32 } =20 if self.bad_blocks_partial_io { - let block_size_sectors =3D (self.block_size >> SEC= TOR_SHIFT) as u64; + let block_size_sectors =3D u64::from(bytes_to_sect= ors(self.block_size_bytes)); range.start =3D align_down(range.start, block_size= _sectors); if start < range.start { *sectors =3D (range.start - start) as u32; @@ -627,30 +713,6 @@ impl HasHrTimer for Pdu { } } =20 -fn is_power_of_two(value: T) -> bool -where - T: core::ops::Sub, - T: core::ops::BitAnd, - T: core::cmp::PartialOrd, - T: Copy, - T: From, -{ - (value > 0u8.into()) && (value & (value - 1u8.into())) =3D=3D 0u8.into= () -} - -fn align_down(value: T, to: T) -> T -where - T: core::ops::Sub, - T: core::ops::Not, - T: core::ops::BitAnd, - T: core::cmp::PartialOrd, - T: Copy, - T: From, -{ - debug_assert!(is_power_of_two(to)); - value & !(to - 1u8.into()) -} - #[vtable] impl Operations for NullBlkDevice { type QueueData =3D Arc; @@ -672,8 +734,6 @@ fn queue_rq( rq: Owned>, _is_last: bool, ) -> BlkResult { - let mut sectors =3D rq.sectors(); - if this.bandwidth_limit !=3D 0 { if !this.bandwidth_timer.active() { drop(this.bandwidth_timer_handle.lock().take()); @@ -699,17 +759,16 @@ fn queue_rq( =20 let mut rq =3D rq.start(); =20 - use core::ops::Deref; - Self::handle_bad_blocks(this.deref(), &mut rq, &mut sectors)?; - - if this.memory_backed { - if rq.command() =3D=3D mq::Command::Discard { - this.discard(&hw_data, rq.sector(), sectors)?; - } else { - this.transfer(&hw_data, &mut rq, sectors)?; - } + #[cfg(CONFIG_BLK_DEV_ZONED)] + if this.zoned.enabled { + this.handle_zoned_command(&hw_data, &mut rq)?; + } else { + this.handle_regular_command(&hw_data, &mut rq)?; } =20 + #[cfg(not(CONFIG_BLK_DEV_ZONED))] + this.handle_regular_command(&hw_data, &mut rq)?; + match this.irq_mode { IRQMode::None =3D> Self::end_request(rq), IRQMode::Soft =3D> mq::Request::complete(rq.into()), @@ -735,4 +794,14 @@ fn complete(rq: ARef>) { .expect("Failed to complete request"), ) } + + #[cfg(CONFIG_BLK_DEV_ZONED)] + fn report_zones( + disk: &GenDiskRef, + sector: u64, + nr_zones: u32, + callback: impl Fn(&bindings::blk_zone, u32) -> Result, + ) -> Result { + Self::report_zones_internal(disk, sector, nr_zones, callback) + } } diff --git a/drivers/block/rnull/util.rs b/drivers/block/rnull/util.rs new file mode 100644 index 0000000000000..044926c8e2840 --- /dev/null +++ b/drivers/block/rnull/util.rs @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Return true if `value` is a power of two. +pub(crate) fn is_power_of_two(value: T) -> bool +where + T: core::ops::Sub, + T: core::ops::BitAnd, + T: core::cmp::PartialOrd, + T: Copy, + T: From, +{ + (value > 0u8.into()) && (value & (value - 1u8.into())) =3D=3D 0u8.into= () +} + +// Round `value` down to the next multiple of `to`, which must be a power = of +// two. +pub(crate) fn align_down(value: T, to: T) -> T +where + T: core::ops::Sub, + T: core::ops::Not, + T: core::ops::BitAnd, + T: core::cmp::PartialOrd, + T: Copy, + T: From, +{ + debug_assert!(is_power_of_two(to)); + value & !(to - 1u8.into()) +} + +// Round `value` up to the next multiple of `to`, which must be a power of= two. +#[cfg(CONFIG_BLK_DEV_ZONED)] +pub(crate) fn align_up(value: T, to: T) -> T +where + T: core::ops::Sub, + T: core::ops::Add, + T: core::ops::BitAnd, + T: core::ops::BitOr, + T: core::cmp::PartialOrd, + T: Copy, + T: From, +{ + debug_assert!(is_power_of_two(to)); + ((value - 1u8.into()) | (to - 1u8.into())) + 1u8.into() +} + +pub(crate) fn mib_to_sectors(mib: T) -> T +where + T: core::ops::Shl, +{ + mib << (20 - kernel::block::SECTOR_SHIFT) +} + +pub(crate) fn sectors_to_bytes(sectors: T) -> T +where + T: core::ops::Shl, +{ + sectors << kernel::block::SECTOR_SHIFT +} + +pub(crate) fn bytes_to_sectors(bytes: T) -> T +where + T: core::ops::Shl, +{ + bytes << kernel::block::SECTOR_SHIFT +} diff --git a/drivers/block/rnull/zoned.rs b/drivers/block/rnull/zoned.rs new file mode 100644 index 0000000000000..0f15f4cc4e5c3 --- /dev/null +++ b/drivers/block/rnull/zoned.rs @@ -0,0 +1,661 @@ +// SPDX-License-Identifier: GPL-2.0 + +use crate::{ + util::*, + HwQueueContext, // +}; +use kernel::{ + bindings, + block::mq::{ + self, + gen_disk::GenDiskRef, // + }, + new_mutex, + new_spinlock, + prelude::*, + sync::Mutex, + sync::SpinLock, + types::Owned, // +}; + +pub(crate) struct ZoneOptionsArgs { + pub(crate) enable: bool, + pub(crate) device_capacity_mib: u64, + pub(crate) block_size_bytes: u32, + pub(crate) zone_size_mib: u32, + pub(crate) zone_capacity_mib: u32, + pub(crate) zone_nr_conv: u32, + pub(crate) zone_max_open: u32, + pub(crate) zone_max_active: u32, + pub(crate) zone_append_max_sectors: u32, +} + +#[pin_data] +pub(crate) struct ZoneOptions { + pub(crate) enabled: bool, + zones: Pin]>>, + conventional_count: u32, + pub(crate) size_sectors: u32, + append_max_sectors: u32, + max_open: u32, + max_active: u32, + #[pin] + accounting: SpinLock, +} + +impl ZoneOptions { + pub(crate) fn new(args: ZoneOptionsArgs) -> Result> { + let ZoneOptionsArgs { + enable, + device_capacity_mib, + block_size_bytes, + zone_size_mib, + zone_capacity_mib, + mut zone_nr_conv, + mut zone_max_open, + mut zone_max_active, + zone_append_max_sectors, + } =3D args; + + if !is_power_of_two(zone_size_mib) { + return Err(EINVAL); + } + + if zone_capacity_mib > zone_size_mib { + return Err(EINVAL); + } + + let zone_size_sectors =3D mib_to_sectors(zone_size_mib); + let device_capacity_sectors =3D mib_to_sectors(device_capacity_mib= ); + let zone_capacity_sectors =3D mib_to_sectors(zone_capacity_mib); + let zone_count: u32 =3D (align_up(device_capacity_sectors, zone_si= ze_sectors.into()) + >> zone_size_sectors.ilog2()) + .try_into()?; + + if zone_nr_conv >=3D zone_count { + zone_nr_conv =3D zone_count - 1; + pr_info!("changed the number of conventional zones to {zone_nr= _conv}\n"); + } + + let zone_append_max_sectors =3D + align_down(zone_append_max_sectors, bytes_to_sectors(block_siz= e_bytes)) + .min(zone_capacity_sectors); + + let seq_zone_count =3D zone_count - zone_nr_conv; + + if zone_max_active >=3D seq_zone_count { + zone_max_active =3D 0; + pr_info!("zone_max_active limit disabled, limit >=3D zone coun= t\n"); + } + + if zone_max_active !=3D 0 && zone_max_open > zone_max_active { + zone_max_open =3D zone_max_active; + pr_info!("changed the maximum number of open zones to {zone_ma= x_open}\n"); + } else if zone_max_open >=3D seq_zone_count { + zone_max_open =3D 0; + pr_info!("zone_max_open limit disabled, limit >=3D zone count\= n"); + } + + Ok(try_pin_init!(Self { + enabled: enable, + zones: init_zone_descriptors( + zone_size_sectors, + zone_capacity_sectors, + zone_count, + zone_nr_conv, + )?, + size_sectors: zone_size_sectors, + append_max_sectors: zone_append_max_sectors, + max_open: zone_max_open, + max_active: zone_max_active, + accounting <- new_spinlock!(ZoneAccounting { + implicit_open: 0, + explicit_open: 0, + closed: 0, + start_zone: zone_nr_conv, + }), + conventional_count: zone_nr_conv, + })) + } +} + +struct ZoneAccounting { + implicit_open: u32, + explicit_open: u32, + closed: u32, + start_zone: u32, +} + +pub(crate) fn init_zone_descriptors( + zone_size_sectors: u32, + zone_capacity_sectors: u32, + zone_count: u32, + zone_nr_conv: u32, +) -> Result]>>> { + let zone_capacity_sectors =3D if zone_capacity_sectors =3D=3D 0 { + zone_size_sectors + } else { + zone_capacity_sectors + }; + + KBox::pin_slice( + |i| { + let sector =3D i as u64 * Into::::into(zone_size_sectors); + new_mutex!( + if i < zone_nr_conv.try_into().expect("Fewer than 2^32 zon= es") { + ZoneDescriptor { + start_sector: sector, + size_sectors: zone_size_sectors, + capacity_sectors: zone_size_sectors, + kind: ZoneType::Conventional, + write_pointer: sector + Into::::into(zone_siz= e_sectors), + condition: ZoneCondition::NoWritePointer, + } + } else { + ZoneDescriptor { + start_sector: sector, + size_sectors: zone_size_sectors, + capacity_sectors: zone_capacity_sectors, + kind: ZoneType::SequentialWriteRequired, + write_pointer: sector, + condition: ZoneCondition::Empty, + } + } + ) + }, + zone_count as usize, + GFP_KERNEL, + ) +} + +impl super::NullBlkDevice { + pub(crate) fn handle_zoned_command( + &self, + hw_data: &Pin<&SpinLock>, + rq: &mut Owned>, + ) -> Result { + use mq::Command::*; + match rq.command() { + ZoneAppend | Write =3D> self.zoned_write(hw_data, rq)?, + ZoneReset | ZoneResetAll | ZoneOpen | ZoneClose | ZoneFinish = =3D> { + self.zone_management(hw_data, rq)? + } + _ =3D> self.zoned_read(hw_data, rq)?, + } + + Ok(()) + } + + fn zone_management( + &self, + hw_data: &Pin<&SpinLock>, + rq: &mut Owned>, + ) -> Result { + if rq.command() =3D=3D mq::Command::ZoneResetAll { + for zone in self.zoned.zones_iter() { + let mut zone =3D zone.lock(); + use ZoneCondition::*; + match zone.condition { + Empty | ReadOnly | Offline =3D> continue, + _ =3D> self.zoned.reset_zone(&self.storage, hw_data, &= mut zone)?, + } + } + + return Ok(()); + } + + let zone =3D self.zoned.zone(rq.sector())?; + let mut zone =3D zone.lock(); + + if zone.condition =3D=3D ZoneCondition::ReadOnly || zone.condition= =3D=3D ZoneCondition::Offline { + return Err(EIO); + } + + use mq::Command::*; + match rq.command() { + ZoneOpen =3D> self.zoned.open_zone(&mut zone, rq.sector()), + ZoneClose =3D> self.zoned.close_zone(&mut zone), + ZoneReset =3D> self.zoned.reset_zone(&self.storage, hw_data, &= mut zone), + ZoneFinish =3D> self.zoned.finish_zone(&mut zone, rq.sector()), + _ =3D> Err(EIO), + } + } + + fn zoned_read( + &self, + hw_data: &Pin<&SpinLock>, + rq: &mut Owned>, + ) -> Result { + let zone =3D self.zoned.zone(rq.sector())?; + let zone =3D zone.lock(); + if zone.condition =3D=3D ZoneCondition::Offline { + return Err(EINVAL); + } + + zone.check_bounds_read(rq.sector(), rq.sectors())?; + + self.handle_regular_command(hw_data, rq) + } + + fn zoned_write( + &self, + hw_data: &Pin<&SpinLock>, + rq: &mut Owned>, + ) -> Result { + let zone =3D self.zoned.zone(rq.sector())?; + let mut zone =3D zone.lock(); + let append: bool =3D rq.command() =3D=3D mq::Command::ZoneAppend; + + if zone.kind =3D=3D ZoneType::Conventional { + if append { + return Err(EINVAL); + } + + // NOTE: C driver does not check bounds on write. + zone.check_bounds_write(rq.sector(), rq.sectors())?; + + let mut sectors =3D rq.sectors(); + self.handle_bad_blocks(rq, &mut sectors)?; + return self.transfer(hw_data, rq, rq.command(), sectors); + } + + // Check zoned write fits within zone + if zone.write_pointer + Into::::into(rq.sectors()) + > zone.start_sector + Into::::into(zone.capacity_sectors) + { + return Err(EINVAL); + } + + if append { + if self.zoned.append_max_sectors =3D=3D 0 { + return Err(EINVAL); + } + rq.get_pin_mut().set_sector(zone.write_pointer); + } + + // Check write pointer alignment + if !append && rq.sector() !=3D zone.write_pointer { + return Err(EINVAL); + } + + if zone.condition =3D=3D ZoneCondition::Closed || zone.condition = =3D=3D ZoneCondition::Empty { + if self.zoned.use_accounting() { + let mut accounting =3D self.zoned.accounting.lock(); + self.zoned + .check_zone_resources(&mut accounting, &mut zone, rq.s= ector())?; + + if zone.condition =3D=3D ZoneCondition::Closed { + accounting.closed -=3D 1; + accounting.implicit_open +=3D 1; + } else if zone.condition =3D=3D ZoneCondition::Empty { + accounting.implicit_open +=3D 1; + } + } + + zone.condition =3D ZoneCondition::ImplicitOpen; + } + + let mut sectors =3D rq.sectors(); + self.handle_bad_blocks(rq, &mut sectors)?; + + if self.memory_backed { + self.transfer(hw_data, rq, mq::Command::Write, sectors)?; + } + + zone.write_pointer +=3D Into::::into(sectors); + if zone.write_pointer =3D=3D zone.start_sector + Into::::into= (zone.capacity_sectors) { + if self.zoned.use_accounting() { + let mut accounting =3D self.zoned.accounting.lock(); + + if zone.condition =3D=3D ZoneCondition::ExplicitOpen { + accounting.explicit_open -=3D 1; + } else if zone.condition =3D=3D ZoneCondition::ImplicitOpe= n { + accounting.implicit_open -=3D 1; + } + } + + zone.condition =3D ZoneCondition::Full; + } + + Ok(()) + } + + pub(crate) fn report_zones_internal( + disk: &GenDiskRef, + sector: u64, + nr_zones: u32, + callback: impl Fn(&bindings::blk_zone, u32) -> Result, + ) -> Result { + let device =3D disk.queue_data(); + let first_zone =3D sector >> device.zoned.size_sectors.ilog2(); + + let mut count =3D 0; + + for (i, zone) in device + .zoned + .zones + .split_at(first_zone as usize) + .1 + .iter() + .take(nr_zones as usize) + .enumerate() + { + let zone =3D zone.lock(); + let descriptor =3D bindings::blk_zone { + start: zone.start_sector, + len: zone.size_sectors.into(), + wp: zone.write_pointer, + capacity: zone.capacity_sectors.into(), + type_: zone.kind as u8, + cond: zone.condition as u8, + ..bindings::blk_zone::zeroed() + }; + drop(zone); + callback(&descriptor, i as u32)?; + + count +=3D 1; + } + + Ok(count) + } +} + +impl ZoneOptions { + fn zone_no(&self, sector: u64) -> usize { + (sector >> self.size_sectors.ilog2()) as usize + } + + fn zone(&self, sector: u64) -> Result<&Mutex> { + self.zones.get(self.zone_no(sector)).ok_or(EINVAL) + } + + fn zones_iter(&self) -> impl Iterator>= { + self.zones.iter() + } + + fn use_accounting(&self) -> bool { + self.max_active !=3D 0 || self.max_open !=3D 0 + } + + fn try_close_implicit_open_zone(&self, accounting: &mut ZoneAccounting= , sector: u64) -> Result { + let skip =3D self.zone_no(sector) as u32; + + let it =3D Iterator::chain( + self.zones[(accounting.start_zone as usize)..] + .iter() + .enumerate() + .map(|(i, z)| (i + accounting.start_zone as usize, z)), + self.zones[(self.conventional_count as usize)..(accounting.sta= rt_zone as usize)] + .iter() + .enumerate() + .map(|(i, z)| (i + self.conventional_count as usize, z)), + ) + .filter(|(i, _)| *i !=3D skip as usize); + + for (index, zone) in it { + let mut zone =3D zone.lock(); + if zone.condition =3D=3D ZoneCondition::ImplicitOpen { + accounting.implicit_open -=3D 1; + + let index_u32: u32 =3D index.try_into()?; + let next_zone: u32 =3D index_u32 + 1; + accounting.start_zone =3D if next_zone =3D=3D self.zones.l= en().try_into()? { + self.conventional_count + } else { + next_zone + }; + + if zone.write_pointer =3D=3D zone.start_sector { + zone.condition =3D ZoneCondition::Empty; + } else { + zone.condition =3D ZoneCondition::Closed; + accounting.closed +=3D 1; + } + return Ok(()); + } + } + + Err(EINVAL) + } + + fn open_zone(&self, zone: &mut ZoneDescriptor, sector: u64) -> Result { + if zone.kind =3D=3D ZoneType::Conventional { + return Err(EINVAL); + } + + use ZoneCondition::*; + match zone.condition { + ExplicitOpen =3D> return Ok(()), + Empty | ImplicitOpen | Closed =3D> (), + _ =3D> return Err(EIO), + } + + if self.use_accounting() { + let mut accounting =3D self.accounting.lock(); + match zone.condition { + Empty =3D> { + self.check_zone_resources(&mut accounting, zone, secto= r)?; + } + ImplicitOpen =3D> { + accounting.implicit_open -=3D 1; + } + Closed =3D> { + self.check_zone_resources(&mut accounting, zone, secto= r)?; + accounting.closed -=3D 1; + } + _ =3D> (), + } + + accounting.explicit_open +=3D 1; + } + + zone.condition =3D ExplicitOpen; + Ok(()) + } + + fn check_zone_resources( + &self, + accounting: &mut ZoneAccounting, + zone: &mut ZoneDescriptor, + sector: u64, + ) -> Result { + match zone.condition { + ZoneCondition::Empty =3D> { + self.check_active_zones(accounting)?; + self.check_open_zones(accounting, sector) + } + ZoneCondition::Closed =3D> self.check_open_zones(accounting, s= ector), + _ =3D> Err(EIO), + } + } + + fn check_open_zones(&self, accounting: &mut ZoneAccounting, sector: u6= 4) -> Result { + if self.max_open =3D=3D 0 { + return Ok(()); + } + + if self.max_open > accounting.explicit_open + accounting.implicit_= open { + return Ok(()); + } + + if accounting.implicit_open > 0 { + self.check_active_zones(accounting)?; + return self.try_close_implicit_open_zone(accounting, sector); + } + + Err(EBUSY) + } + + fn check_active_zones(&self, accounting: &mut ZoneAccounting) -> Resul= t { + if self.max_active =3D=3D 0 { + return Ok(()); + } + + if self.max_active > accounting.implicit_open + accounting.explici= t_open + accounting.closed + { + return Ok(()); + } + + Err(EBUSY) + } + + fn close_zone(&self, zone: &mut ZoneDescriptor) -> Result { + if zone.kind =3D=3D ZoneType::Conventional { + return Err(EINVAL); + } + + use ZoneCondition::*; + match zone.condition { + Closed =3D> return Ok(()), + ImplicitOpen | ExplicitOpen =3D> (), + _ =3D> return Err(EIO), + } + + if self.use_accounting() { + let mut accounting =3D self.accounting.lock(); + match zone.condition { + ImplicitOpen =3D> accounting.implicit_open -=3D 1, + ExplicitOpen =3D> accounting.explicit_open -=3D 1, + _ =3D> (), + } + + if zone.write_pointer > zone.start_sector { + accounting.closed +=3D 1; + } + } + + if zone.write_pointer =3D=3D zone.start_sector { + zone.condition =3D Empty; + } else { + zone.condition =3D Closed; + } + + Ok(()) + } + + fn finish_zone(&self, zone: &mut ZoneDescriptor, sector: u64) -> Resul= t { + if zone.kind =3D=3D ZoneType::Conventional { + return Err(EINVAL); + } + + if self.use_accounting() { + let mut accounting =3D self.accounting.lock(); + + use ZoneCondition::*; + match zone.condition { + Full =3D> return Ok(()), + Empty =3D> { + self.check_zone_resources(&mut accounting, zone, secto= r)?; + } + ImplicitOpen =3D> accounting.implicit_open -=3D 1, + ExplicitOpen =3D> accounting.explicit_open -=3D 1, + Closed =3D> { + self.check_zone_resources(&mut accounting, zone, secto= r)?; + accounting.closed -=3D 1; + } + _ =3D> return Err(EIO), + } + } + + zone.condition =3D ZoneCondition::Full; + zone.write_pointer =3D zone.start_sector + Into::::into(zone.= size_sectors); + + Ok(()) + } + + fn reset_zone( + &self, + storage: &crate::disk_storage::DiskStorage, + hw_data: &Pin<&SpinLock>, + zone: &mut ZoneDescriptor, + ) -> Result { + if zone.kind =3D=3D ZoneType::Conventional { + return Err(EINVAL); + } + + if self.use_accounting() { + let mut accounting =3D self.accounting.lock(); + + use ZoneCondition::*; + match zone.condition { + ImplicitOpen =3D> accounting.implicit_open -=3D 1, + ExplicitOpen =3D> accounting.explicit_open -=3D 1, + Closed =3D> accounting.closed -=3D 1, + Empty | Full =3D> (), + _ =3D> return Err(EIO), + } + } + + zone.condition =3D ZoneCondition::Empty; + zone.write_pointer =3D zone.start_sector; + + storage.discard(hw_data, zone.start_sector, zone.size_sectors); + + Ok(()) + } +} + +pub(crate) struct ZoneDescriptor { + start_sector: u64, + size_sectors: u32, + kind: ZoneType, + capacity_sectors: u32, + write_pointer: u64, + condition: ZoneCondition, +} + +impl ZoneDescriptor { + fn check_bounds_write(&self, sector: u64, sectors: u32) -> Result { + if sector + Into::::into(sectors) + > self.start_sector + Into::::into(self.capacity_sectors) + { + Err(EIO) + } else { + Ok(()) + } + } + + fn check_bounds_read(&self, sector: u64, sectors: u32) -> Result { + if sector + Into::::into(sectors) > self.write_pointer { + Err(EIO) + } else { + Ok(()) + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[repr(u32)] +enum ZoneType { + Conventional =3D bindings::blk_zone_type_BLK_ZONE_TYPE_CONVENTIONAL, + SequentialWriteRequired =3D bindings::blk_zone_type_BLK_ZONE_TYPE_SEQW= RITE_REQ, + #[expect(dead_code)] + SequentialWritePreferred =3D bindings::blk_zone_type_BLK_ZONE_TYPE_SEQ= WRITE_PREF, +} + +impl ZoneType { + #[expect(dead_code)] + fn as_raw(self) -> u32 { + self as u32 + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[repr(u32)] +enum ZoneCondition { + NoWritePointer =3D bindings::blk_zone_cond_BLK_ZONE_COND_NOT_WP, + Empty =3D bindings::blk_zone_cond_BLK_ZONE_COND_EMPTY, + ImplicitOpen =3D bindings::blk_zone_cond_BLK_ZONE_COND_IMP_OPEN, + ExplicitOpen =3D bindings::blk_zone_cond_BLK_ZONE_COND_EXP_OPEN, + Closed =3D bindings::blk_zone_cond_BLK_ZONE_COND_CLOSED, + Full =3D bindings::blk_zone_cond_BLK_ZONE_COND_FULL, + ReadOnly =3D bindings::blk_zone_cond_BLK_ZONE_COND_READONLY, + Offline =3D bindings::blk_zone_cond_BLK_ZONE_COND_OFFLINE, +} + +impl ZoneCondition { + #[expect(dead_code)] + fn as_raw(self) -> u32 { + self as u32 + } +} --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 1AF3930CD94; Sun, 15 Feb 2026 23:46:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199181; cv=none; b=joUe8DTAyybxEvQ4FHe8I8nt2CaezLrF0LGu8AYul3KNjo5pyX/qwgVi+9S8b1XFkLBhaOcpK1XVAhVK+z93wI9Z9Khd6qDt7UCfz+/MLuPBrojMlHrRCJONTmlm7j/PsQq8ZrCurxOPbSUZ59JazEQmdPRMMH6SUqQm8I87Ptw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199181; c=relaxed/simple; bh=9wLxYaTCPfOG5uonTZ2h8ocU8FWs+hSDrEKcM2sv5z4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=DlaRhCz4p0LLG/VnxxWTt1LPqSpYlNQwiorUfc1hoUiL8uuoP/X+rrCau22mgq5kX37nTTStWbiYKFYgGVozvDUOw2INX/Raic6m+22Meq8adxMvG9Qhjt5oATQ+BFSNHLMnvCwKxVkRl5iIhoe4V2xu8LZmzBjlUroaog+3ooY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=kjCDYQsR; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="kjCDYQsR" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A4FD6C4CEF7; Sun, 15 Feb 2026 23:46:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199181; bh=9wLxYaTCPfOG5uonTZ2h8ocU8FWs+hSDrEKcM2sv5z4=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=kjCDYQsRlhCQN6obSBw1ii95Qd4xqZ2D3oZ1pMKrFWp3LbPJEa068zb6SNYsrLyzl RNtqfQABlx0D/geY5TXUSQ0unzKd7ZTFl8WrcQ6ptjFu3A7zKaqi2rHelGAxlnSd+D J3eLNHh7W6AAppxKG9zzibmJ3iLxifuLzA7d+yJ7P4tFY8tPnGTRQKZ30Y2RvuxtbT 5aAU2fp1DgwaHG2wxFp/FdWtUMtAu/NBUq8CY2UY5lei5riX8DMBInCnZ5ry7D2sfd 9iQrZXLs9GlIXjr3wAh1d/iIpmUWJ6nvaC/yLvGJXKLavrqTYsAvNxCt1mLq6iK4Sp nSEyBV6LaEO/g== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:36 +0100 Subject: [PATCH 49/79] block: rust: add `map_queues` support 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: <20260216-rnull-v6-19-rc5-send-v1-49-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=3879; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=9wLxYaTCPfOG5uonTZ2h8ocU8FWs+hSDrEKcM2sv5z4=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhHteQM6fc3Iu+s7ErTmW5+zxHdbuM7v9MUt WW0qbMjDayJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYRwAKCRDhuBo+eShj d82UEADCFQq4xg0GLRoFbDArGbCjq1pYimjtXweH1jUHAlgJbGGDkikwcOVePhVAnaq0cdE5BuY FST4N7wGwn5uDWVPQjXpCx3F0A00D312QyAmD8cvTc3vOP3J4jJptfyTx18my0TG3l/QpGh2k6+ VRnt/4++OOUgxoE8WyWXsHSiexrgbP5JGRTRiN48GSZzVsD+hMHSp/+pgp7Xcl0BR+8bGdFiCQm LGiYwXAUL5k3Gqj/IYOdhE00o/x0U8DPQMfes514MGFteRl7clkgSvYEFKWth04xf/u7Ko2u7Ku 9RZFRbrxsRa66tuTEpsFIA6IX/RwvMKZoXDegWKZ8M0TsY7LUUI2V7aNC7bnV2Xv0mertjEooNo Ua16efkXqh+Tx/mi18AdzA0+qemfM30vBYaWJ9xQDcAbSVCfSq+gB9EFg/ft+MzN7AS9w2UdGdt EJyQcDaAUyDI4zCU7pPSm/5GHu52+/vVcaHnTHT8vGfMk1tFZQ4t5d9DTiAS11OUgBMHJqhQEPX F2LkCpJ5bsvfp0U5L+/nuiphEGkRmHRxMhE4MQ2vGaAWhI4jUEAeiJC76C5ficBetddzfL4fS+U ZIk5m9JLWOTZyF3nP1DNE0C1fLiAMjBgV3OOwQg8Bp5j7v0btIbGpJUHAd4dzkHYMBmRMjTb/gX QQhP/zLigZgzM0g== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add support for the `map_queues` callback to the Rust block layer bindings. This callback allows drivers to customize the mapping between CPUs and hardware queues. The callback receives a mutable reference to the `TagSet`, and drivers can use the `TagSet::update_maps` method to configure the mappings for each queue type. Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq/operations.rs | 28 ++++++++++++++++++++++++++-- rust/kernel/block/mq/tag_set.rs | 13 +++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/oper= ations.rs index 3f84ebadec86b..017fad010d174 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -8,7 +8,7 @@ bindings, block::{ error::BlkResult, - mq::{gen_disk::GenDiskRef, request::RequestDataWrapper, IdleReques= t, Request}, + mq::{gen_disk::GenDiskRef, request::RequestDataWrapper, IdleReques= t, Request, TagSet}, }, error::{from_result, to_result, Result}, prelude::*, @@ -101,6 +101,11 @@ fn report_zones( ) -> Result { Err(ENOTSUPP) } + + /// Called by the kernel to map submission queues to CPU cores. + fn map_queues(_tag_set: &TagSet) { + build_error!(crate::error::VTABLE_DEFAULT_ERROR) + } } =20 /// A vtable for blk-mq to interact with a block device driver. @@ -394,6 +399,21 @@ impl OperationsVTable { }) } =20 + /// This function is called by the C kernel. A pointer to this functio= n is + /// installed in the `blk_mq_ops` vtable for the driver. + /// + /// # Safety + /// + /// This function may only be called by blk-mq C infrastructure. `tag_= set` + /// must be a pointer to a valid and initialized `TagSet`. The poin= tee + /// must be valid for use as a reference at least the duration of this= call. + unsafe extern "C" fn map_queues_callback(tag_set: *mut bindings::blk_m= q_tag_set) { + // SAFETY: The safety requirements of this function satiesfies the + // requirements of `TagSet::from_ptr`. + let tag_set =3D unsafe { TagSet::from_ptr(tag_set) }; + T::map_queues(tag_set); + } + const VTABLE: bindings::blk_mq_ops =3D bindings::blk_mq_ops { queue_rq: Some(Self::queue_rq_callback), queue_rqs: None, @@ -415,7 +435,11 @@ impl OperationsVTable { exit_request: Some(Self::exit_request_callback), cleanup_rq: None, busy: None, - map_queues: None, + map_queues: if T::HAS_MAP_QUEUES { + Some(Self::map_queues_callback) + } else { + None + }, #[cfg(CONFIG_BLK_DEBUG_FS)] show_rq: None, }; diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set= .rs index 600c9c6249123..330ff28c91507 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -89,6 +89,19 @@ pub fn new( pub(crate) fn raw_tag_set(&self) -> *mut bindings::blk_mq_tag_set { self.inner.get() } + + /// Create a `TagSet` from a raw pointer. + /// + /// # Safety + /// + /// `ptr` must be a pointer to a valid and initialized `TagSet`. Th= ere + /// may be no other mutable references to the tag set. The pointee mus= t be + /// live and valid at least for the duration of the returned lifetime = `'a`. + pub(crate) unsafe fn from_ptr<'a>(ptr: *mut bindings::blk_mq_tag_set) = -> &'a Self { + // SAFETY: By the safety requirements of this function, `ptr` is v= alid + // for use as a reference for the duration of `'a`. + unsafe { &*(ptr.cast::()) } + } } =20 #[pinned_drop] --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 74CA12E5D17; Sun, 15 Feb 2026 23:44:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199091; cv=none; b=hI3wvyY8A9oQj4ckoLqg/XOzHc5nkP8Tr80BdtWe6IBaIVCpOkngT1kV1H0WAjRSWaFPHXA7qJY1uCPPCpYmluAAO3sKHJyktez9BAb/0Opm/yeiv7zFHW4VDSXr7lWfsmbLHqxDIYo9/z/CZiiYM3TdALpDHQsuJ3N03oMZqTA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199091; c=relaxed/simple; bh=KJozLO0lF2FpzqbaUJL2aSa4aBhSF0NKtk7cOClvuLI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=FSq2yzWxmtRclToyo+jqL+NUrJOHwOUw/0oYi9ks58PY1t92uQl8zLwy+LKs3fl5L/wurfCC8NFtcErWg/3Y9UCixFDMhJ7t51doW6RzjnCz7Vxc3Z92I+LEGAcp6SUnWG2dXQomuoN7qZOkY40DwkcPFm78HJknqT5BzpUtRWQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Q+2PSU7K; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Q+2PSU7K" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1E54BC4CEF7; Sun, 15 Feb 2026 23:44:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199091; bh=KJozLO0lF2FpzqbaUJL2aSa4aBhSF0NKtk7cOClvuLI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Q+2PSU7KVK894R2Aztskm49ouQ07YfRkNT67DhLkNKw5XawmZyq6qySRtQpyy+saB mVJCqCqdjTlB1y6P7X24Fu5LTd3da+95pdHalXDcprdYiTnIvLj5J/wxd8ekT5Ai5J Wkjj9njcLJq4qDAQI+Fqc7ArOvP/CP44APlRwKETRtEI42nRovriFsRO1apokavlUs Brt6wdP4c9BHfJ2wgp4LZXKhFO9fTadztmUrOIFg8FRrzweKW0Kn7rc4K6MmS84EaS oKY3LNB4d5MPiSiqKEoWDT0oqp0SqaVrdyzpxexuhFsW7vuKq/n+4QLSpC+rfJZMVl VFlIDixTWGJvA== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:37 +0100 Subject: [PATCH 50/79] block: rust: add an abstraction for `struct blk_mq_queue_map` 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: <20260216-rnull-v6-19-rc5-send-v1-50-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=7024; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=KJozLO0lF2FpzqbaUJL2aSa4aBhSF0NKtk7cOClvuLI=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhIw61luKCnzKfaISJJLxAq6+mHxPJwvpl1v iP3zQiPIySJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYSAAKCRDhuBo+eShj d26FD/4gfr3V2SAN/XvgmWVz1dCEbXu+WeUMPsAAH49GKVtWbtkIiO/6r31TsF16Sb536ocwLdH emuUJDlG0IWhGUn9Lz9qme/Hw7xd1SHBKuWDtnGXTbQA+8jva2+qeMC08WKSP0ygKCLVOPMfnjR Xbv4nOBDi7ElpZ+F6szkqSAPj3wz+fCy+TfpfB4WWZ2hzuoKkcn5Ei1lrvEysMSqg4AwP0VC8/s jTTrcisI392gezwBwij8jQ5Ji3VV+8NDAx4rNLM03hwnqzx7M+6T4rALLxTzW58HWlJjfijKF+G 2+b+6BJn+Lfv3TAi1jWJvgidUL5xAE510CTbU0cSA6mUOvL/Ev2zVViOzI+qvENkta8PH3mAepe 39OpQmA6i1/FKTsEITNCMz51/h8S6I/ua48TThZWl7F6GKVT6lVQj0nn5gZGM/sdfLPris2LhC9 8VxcSXdoG9vr80uOhOLX3m57p78D70sxiEwpL7MrDBDNak7LU0OZxe/DJWvTkkDMmBsEUp2V478 ZWfDU/N81fzpEWo0uvKPRGjIU12NYvlS7tIRVQwdSJUq2VWr8Lx6PJ2ZeWHSbyXJMcH4ACcoPLo 8cWW1iXCBlTAEWGHLpiaU+L8M0k0LjLvPvRMKJDQW9xoxXvBIf8mfaAGGn8ZQ/cV77KB6TRL4sk PZfkxR9kYsQ6KXQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add the `QueueMap` and `QueueType` types as Rust abstractions for CPU to hardware queue mappings. The `QueueMap` type wraps `struct blk_mq_queue_map` and provides methods to set up the mapping between CPUs and hardware queues. `QueueType` represents the different queue types: default, read, and poll queues. Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq.rs | 1 + rust/kernel/block/mq/operations.rs | 10 ++-- rust/kernel/block/mq/tag_set.rs | 96 ++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 102 insertions(+), 5 deletions(-) diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index 057a5f366be3a..cd0bfbcbf317a 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -136,4 +136,5 @@ pub use request::Request; pub use request::RequestTimerHandle; pub use request_queue::RequestQueue; +pub use tag_set::QueueType; pub use tag_set::TagSet; diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/oper= ations.rs index 017fad010d174..28dd4b28d203f 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -102,8 +102,8 @@ fn report_zones( Err(ENOTSUPP) } =20 - /// Called by the kernel to map submission queues to CPU cores. - fn map_queues(_tag_set: &TagSet) { + /// Called by the kernel to map hardware queues to CPU cores. + fn map_queues(_tag_set: Pin<&mut TagSet>) { build_error!(crate::error::VTABLE_DEFAULT_ERROR) } } @@ -408,9 +408,9 @@ impl OperationsVTable { /// must be a pointer to a valid and initialized `TagSet`. The poin= tee /// must be valid for use as a reference at least the duration of this= call. unsafe extern "C" fn map_queues_callback(tag_set: *mut bindings::blk_m= q_tag_set) { - // SAFETY: The safety requirements of this function satiesfies the - // requirements of `TagSet::from_ptr`. - let tag_set =3D unsafe { TagSet::from_ptr(tag_set) }; + // SAFETY: By C API contract `tag_set` is the tag set registered w= ith the `GenDisk` created + // by `GenDiskBuilder`. + let tag_set =3D unsafe { TagSet::from_ptr_mut(tag_set) }; T::map_queues(tag_set); } =20 diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set= .rs index 330ff28c91507..e6edc5bc39312 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -97,11 +97,46 @@ pub(crate) fn raw_tag_set(&self) -> *mut bindings::blk_= mq_tag_set { /// `ptr` must be a pointer to a valid and initialized `TagSet`. Th= ere /// may be no other mutable references to the tag set. The pointee mus= t be /// live and valid at least for the duration of the returned lifetime = `'a`. + #[expect(dead_code)] pub(crate) unsafe fn from_ptr<'a>(ptr: *mut bindings::blk_mq_tag_set) = -> &'a Self { // SAFETY: By the safety requirements of this function, `ptr` is v= alid // for use as a reference for the duration of `'a`. unsafe { &*(ptr.cast::()) } } + + /// Create a `TagSet` from a raw pointer. + /// + /// # Safety + /// + /// `ptr` must be a pointer to a valid and initialized `TagSet`. Th= ere + /// may be no other mutable references to the tag set. The pointee mus= t be + /// live and valid at least for the duration of the returned lifetime = `'a`. + pub(crate) unsafe fn from_ptr_mut<'a>(ptr: *mut bindings::blk_mq_tag_s= et) -> Pin<&'a mut Self> { + // SAFETY: By function safety requirements, `ptr` is valid for use= as a mutable reference. + let mref =3D unsafe { &mut *(ptr.cast::()) }; + + // SAFETY: We never move out of `mref`. + unsafe { Pin::new_unchecked(mref) } + } + + /// Helper function to invoke a closure each hardware queue type suppo= rted. + /// + /// This function invokes `cb` for each variant of [`QueueType`] that = this [`TagSet`] supports. + /// This is helpful for setting up CPU to hardware queue maps in the [= `Operations::map_queues`] + /// callback. + pub fn update_maps(self: Pin<&mut Self>, mut cb: impl FnMut(QueueMap))= -> Result { + // SAFETY: By type invariant, `self.inner` is valid. + let nr_maps =3D unsafe { (*self.inner.get()).nr_maps }; + for i in 0..nr_maps { + cb(QueueMap { + // SAFETY: By type invariant, `self.inner` is valid. + map: unsafe { &raw mut (*self.inner.get()).map[i as usize]= }, + kind: i.try_into()?, + }); + } + + Ok(()) + } } =20 #[pinned_drop] @@ -125,3 +160,64 @@ unsafe impl Sync for TagSet {} =20 // SAFETY: It is safe to share references to `TagSet` across thread bounda= ries. unsafe impl Send for TagSet {} + +/// A [`TagSet`] CPU to hardware queue mapping. +/// +/// # Invariants +/// +/// - `self.map` points to a valid `blk_mq_queue_map` +pub struct QueueMap { + map: *mut bindings::blk_mq_queue_map, + kind: QueueType, +} + +impl QueueMap { + /// Set the number of queues for this mapping kind. + pub fn set_queue_count(&mut self, nr_queues: u32) { + // SAFETY: By type invariant, `self.map` is valid. + unsafe { (*self.map).nr_queues =3D nr_queues } + } + + /// First hardware queue to map this queue kind onto. Used by the PCIe= NVMe driver to map each + /// hardware queue type ([`QueueType`]) onto a distinct set of hardwar= e queues. + pub fn set_offset(&mut self, offset: u32) { + // SAFETY: By type invariant, `self.map` is valid. + unsafe { (*self.map).queue_offset =3D offset } + } + + /// Effectuate the mapping described by [`Self`]. + pub fn map_queues(&self) { + // SAFETY: By type invariant, `self.map` is valid. + unsafe { bindings::blk_mq_map_queues(self.map) } + } + + /// Return the kind of this queue mapping. + pub fn kind(&self) -> QueueType { + self.kind + } +} + +/// Type of hardware queue. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum QueueType { + /// All I/O not otherwise accounted for. + Default =3D bindings::hctx_type_HCTX_TYPE_DEFAULT, + /// Just for READ I/O. + Read =3D bindings::hctx_type_HCTX_TYPE_READ, + /// Polled I/O of any kind. + Poll =3D bindings::hctx_type_HCTX_TYPE_POLL, +} + +impl TryFrom for QueueType { + type Error =3D kernel::error::Error; + + fn try_from(value: u32) -> core::result::Result { + match value { + bindings::hctx_type_HCTX_TYPE_DEFAULT =3D> Ok(QueueType::Defau= lt), + bindings::hctx_type_HCTX_TYPE_READ =3D> Ok(QueueType::Read), + bindings::hctx_type_HCTX_TYPE_POLL =3D> Ok(QueueType::Poll), + _ =3D> Err(kernel::error::code::EINVAL), + } + } +} --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 7B50F2DEA62; Sun, 15 Feb 2026 23:42:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198973; cv=none; b=Z5/oUGuoJZoyEKD7HjJQH/hWiJjV/oQrN4JeZCLx233mKPMysLo5v/nokxgtxtipdsMiuo0ujzm84NX6Q3jlTiUIcg4bVl5H7+VPEf1obVzATyQe+nLlpE8zXBA0JLtNgdNT11sBTZGD9ATOmsprvTQqAAdgEFF22OXvaTB8/X8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198973; c=relaxed/simple; bh=ybh9nCCqsUVynbqxeVyqYZphZTsFNB8zOBEw2xwvxWs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=hSXrxWPdKz98qWn2PGthJnCfNetaYFJyla5ctjyI9CbjivD4N91hKaQnPm37xaoNhuZwbZhkrei+/QX3PdXQhejlWrsEpUoOtsbidQ3vc91koWmzm/E9V98VGCrOx7tV+IpoqtAkWMxQeD3JVU59XKt8W6XS3A5lEkrEwhP5tIY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Rteou1Of; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Rteou1Of" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 23672C4CEF7; Sun, 15 Feb 2026 23:42:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198973; bh=ybh9nCCqsUVynbqxeVyqYZphZTsFNB8zOBEw2xwvxWs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Rteou1OfjI7DKLZqSkBe8PPB/IualqGflbnnBUsS9qsXSD1luGbY3im1XZdknHpCh ObspwCI6zYj8eQvVnxJaOrnI8naEoqRQGGQdi3WfhjZIJIGQMQlVjdk7B2stZz1gJK ch0xXIGkwAtBB/E4wPNKPKMCvVonP+3A2qHyZfyXZAkJz4obksknG9EioW6Ibv4AYi bJe079aEZpDebE/eorwXj6dZO4h6zvF7yOnLRz9u+uWDLVkFkzL8sQR38QBGCwdECZ 1ZsbAjWZn1CuPwdZ0I9OpgeOVqpLAbH09MZV/pP0NBdDW5uYre/tnRtCx3ugieDPjO Zr25lEJigzPDQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:38 +0100 Subject: [PATCH 51/79] block: rust: add polled completion support 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: <20260216-rnull-v6-19-rc5-send-v1-51-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=9024; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=ybh9nCCqsUVynbqxeVyqYZphZTsFNB8zOBEw2xwvxWs=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhJUt9ZG6dMGHexv9X0pUqQqoVvjeHvSSbTC N7CBGOKzqqJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYSQAKCRDhuBo+eShj d0YkD/wM2YmefoyIOH2Avc5M4h+sj8E49GBIT9pw/3UvtWozdRDkLczeYLr9jQGFz0SP2jpZ9zh TJhoqSB97nBs8R5KwAZfCsB6Ox2tM5z1dqFGBfzFwhIlU3YrKjAYKELexy1poD/IXocYw1FFfJu VLH4+RaAg16WK5srruKHjDylJCuj7ATBAIr8Vxtuvwk/8TvtO2NAFN3nQd01MMMj4mt2H+NGdvG EM4SVRCwqUzqhbk22R9jrF7CRPiT7Et3cRX36c2YULLiktSl9hcjVbuLNdQtMbGlf4uyuKDLR5r 1fSBAM1Kjx6SwvgegBSYTCmDDsyCP9Jg17eemC9xeZTUYdIJdeHBl+bKtgvePqqUxRuNM+nxYvv Ke2FOITFh2i6hjr5s2RXeJjxR8sTk1mzvGCQ6qL2P56mKQDzcKuXJSwjxyRGLUN00v5WgKWWuHz l3mee429Irt+8c2fJD/NPYQTI8jLiLHPwqGsNOG+jpo0NTsDj5G13IAugg4terPzoirj1KZygt+ YMR9a+9YRItbUV/BEJjS3NCmGfU2daUz24JpewoCuWjSginBmwyx8Y20jpGTTyiIxBl+4Pkx9Ar A/PXt41xofUhYuAr/OrFFSSrb3lpkh9X21FdITS7I36WQd5oChWpOZM1zLA8ECX2LFAorKHcX42 XOS3K4rjwbL/o0A== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add support for polled I/O completion to the Rust block layer bindings. This includes the `poll` callback in `Operations` and the `IoCompletionBatch` type for batched completions. The `poll` callback is invoked by the block layer when polling for completed requests on poll queues. Drivers can use `IoCompletionBatch` to batch multiple completions efficiently. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/rnull.rs | 1 + rust/helpers/blk.c | 7 +++ rust/kernel/block/mq.rs | 4 +- rust/kernel/block/mq/operations.rs | 104 +++++++++++++++++++++++++++++++++= ++-- rust/kernel/block/mq/request.rs | 5 ++ 5 files changed, 117 insertions(+), 4 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 48b2bd598304c..765bbc8101d10 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -733,6 +733,7 @@ fn queue_rq( this: ArcBorrow<'_, Self>, rq: Owned>, _is_last: bool, + _is_poll: bool, ) -> BlkResult { if this.bandwidth_limit !=3D 0 { if !this.bandwidth_timer.active() { diff --git a/rust/helpers/blk.c b/rust/helpers/blk.c index 53beba8c7782d..91b72131a04cd 100644 --- a/rust/helpers/blk.c +++ b/rust/helpers/blk.c @@ -20,3 +20,10 @@ void rust_helper_bio_advance_iter_single(const struct bi= o *bio, { bio_advance_iter_single(bio, iter, bytes); } + +bool rust_helper_blk_mq_add_to_batch(struct request *req, + struct io_comp_batch *iob, bool is_error, + void (*complete)(struct io_comp_batch *)) +{ + return blk_mq_add_to_batch(req, iob, is_error, complete); +} diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index cd0bfbcbf317a..1d58eea971bd7 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -89,7 +89,8 @@ //! _hw_data: (), //! _queue_data: (), //! rq: Owned>, -//! _is_last: bool +//! _is_last: bool, +//! is_poll: bool //! ) -> BlkResult { //! rq.start().end_ok(); //! Ok(()) @@ -130,6 +131,7 @@ mod request_queue; pub mod tag_set; =20 +pub use operations::IoCompletionBatch; pub use operations::Operations; pub use request::Command; pub use request::IdleRequest; diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/oper= ations.rs index 28dd4b28d203f..bccc1903a0d10 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -65,6 +65,7 @@ fn queue_rq( queue_data: ForeignBorrowed<'_, Self::QueueData>, rq: Owned>, is_last: bool, + is_poll: bool, ) -> BlkResult; =20 /// Called by the kernel to indicate that queued requests should be su= bmitted. @@ -84,7 +85,15 @@ fn init_hctx( =20 /// Called by the kernel to poll the device for completed requests. On= ly /// used for poll queues. - fn poll(_hw_data: ForeignBorrowed<'_, Self::HwData>) -> bool { + /// + /// Should return `Ok(true)` if any requests were completed during the= call, + /// `Ok(false)` if no requests were completed, and `Err(e)` to signal = an + /// error condition. + fn poll( + _hw_data: ForeignBorrowed<'_, Self::HwData>, + _queue_data: ForeignBorrowed<'_, Self::QueueData>, + _batch: &mut IoCompletionBatch, + ) -> Result { build_error!(crate::error::VTABLE_DEFAULT_ERROR) } =20 @@ -168,6 +177,11 @@ impl OperationsVTable { // `into_foreign` in `Self::init_hctx_callback`. let hw_data =3D unsafe { T::HwData::borrow((*hctx).driver_data) }; =20 + let is_poll =3D u32::from( + // SAFETY: `hctx` is valid as required by this function. + unsafe { (*hctx).type_ }, + ) =3D=3D bindings::hctx_type_HCTX_TYPE_POLL; + // SAFETY: `hctx` is valid as required by this function. let queue_data =3D unsafe { (*(*hctx).queue).queuedata }; =20 @@ -184,6 +198,7 @@ impl OperationsVTable { // SAFETY: `bd` is valid as required by the safety requirement= for // this function. unsafe { (*bd).last }, + is_poll, ); =20 if let Err(e) =3D ret { @@ -242,13 +257,32 @@ impl OperationsVTable { /// previously initialized by a call to `init_hctx_callback`. unsafe extern "C" fn poll_callback( hctx: *mut bindings::blk_mq_hw_ctx, - _iob: *mut bindings::io_comp_batch, + iob: *mut bindings::io_comp_batch, ) -> crate::ffi::c_int { // SAFETY: By function safety requirement, `hctx` was initialized = by // `init_hctx_callback` and thus `driver_data` came from a call to // `into_foreign`. let hw_data =3D unsafe { T::HwData::borrow((*hctx).driver_data) }; - T::poll(hw_data).into() + + // SAFETY: `hctx` is valid as required by this function. + let queue_data =3D unsafe { (*(*hctx).queue).queuedata }; + + // SAFETY: `queue.queuedata` was created by `GenDiskBuilder::build= ` with + // a call to `ForeignOwnable::into_foreign` to create `queuedata`. + // `ForeignOwnable::from_foreign` is only called when the tagset is + // dropped, which happens after we are dropped. + let queue_data =3D unsafe { T::QueueData::borrow(queue_data) }; + + let mut batch =3D IoCompletionBatch { + inner: iob, + _p: PhantomData, + }; + + let ret =3D T::poll(hw_data, queue_data, &mut batch); + match ret { + Ok(val) =3D> val.into(), + Err(e) =3D> e.to_errno(), + } } =20 /// This function is called by the C kernel. A pointer to this functio= n is @@ -448,3 +482,67 @@ pub(crate) const fn build() -> &'static bindings::blk_= mq_ops { &Self::VTABLE } } + +/// A batch of I/O completions for polled I/O. +/// +/// This struct wraps the C `struct io_comp_batch` and is used to batch +/// multiple request completions together for improved efficiency during p= olled +/// I/O operations. +/// +/// When the kernel polls for completed requests via [`Operations::poll`],= it +/// passes an `IoCompletionBatch` to collect completed requests. The drive= r can +/// add completed requests to the batch using [`add_request`], allowing the +/// kernel to process multiple completions together rather than one at a t= ime. +/// +/// # Invariants +/// +/// - `inner` must point to a valid `io_comp_batch`. +/// +/// [`add_request`]: IoCompletionBatch::add_request +#[repr(transparent)] +pub struct IoCompletionBatch { + inner: *mut bindings::io_comp_batch, + _p: PhantomData, +} + +impl IoCompletionBatch { + /// Attempt to add a completed request to this batch. + /// + /// This method tries to add `rq` to the batch for deferred completion= . If + /// the request is successfully added, ownership is transferred to the= batch + /// and the request will be completed later when the batch is processe= d. + /// + /// # Arguments + /// + /// - `rq`: The completed request to add to the batch. + /// - `error`: Set to `true` if the request completed with an error. + /// + /// # Return + /// + /// When this method returns `Err`, the caller is responsible for comp= leting + /// the request through other means, such as calling + /// [`Request::complete`](super::Request::complete). + pub fn add_request( + &mut self, + rq: Owned>, + error: bool, + ) -> Result<(), Owned>> { + // SAFETY: By type invariant, `self.inner` is a valid `io_comp_bat= ch`. + let ret =3D unsafe { + bindings::blk_mq_add_to_batch( + rq.as_raw(), + self.inner, + error, + Some(bindings::blk_mq_end_request_batch), + ) + }; + + match ret { + true =3D> { + core::mem::forget(rq); + Ok(()) + } + false =3D> Err(rq), + } + } +} diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index 4e7adb10929f0..b4f5a98a97b16 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -135,6 +135,11 @@ pub fn queue(&self) -> &RequestQueue { // SAFETY: By type invariant, self.0 is guaranteed to be valid. unsafe { RequestQueue::from_raw((*self.0.get()).q) } } + + /// Return a raw pointer to the underlying C structure. + pub fn as_raw(&self) -> *mut bindings::request { + self.0.get() + } } =20 /// A wrapper around a blk-mq [`struct request`]. This represents an IO re= quest. --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 0A74F30E82B; Sun, 15 Feb 2026 23:44:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199072; cv=none; b=Cktr3BtEhO1cEVEKcuOFV9jePYYhPbd7Yp2uVwbHpaWnNi53sV0y6l64Gy864ewuAXIM5PgucQcJV+iaVOFzBuYe4dMYDmywzsGggBwowlgRmDDS6iYjKC0W8ykahoAydtgH2PUPkNk/ApSFk4IiYsIvl/s5eh89Hh4NATXDg1w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199072; c=relaxed/simple; bh=lMkix+l7YL3LsaeozijpZ70ut/nAoZ1bvzW7x8MaTVg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ZTWGp2HGteeWNh6GwWi+rccypX7m7VVHuvOzsgIMrtiSF7mhbhnXVn/xYjpCQswUY5+h1/+1Vys2kTtiHzgGPL2wgGhuMzOPw5u60RakK83lkWiN+DAztq5/xDMUmPWB41GX+Xbrmq2qDok07baYJMAhnmxwlyCBpOmoOSFYO64= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=R/HS3paD; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="R/HS3paD" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 22AEEC4CEF7; Sun, 15 Feb 2026 23:44:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199071; bh=lMkix+l7YL3LsaeozijpZ70ut/nAoZ1bvzW7x8MaTVg=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=R/HS3paDi2eXXvgNc/RC2Gh15brVOf29/uOZWoin9FCssxNtw01OqyYUNZtHcBJnj N0ZIuR24Dz0MDafAvoLvM7OlIkk0Kops5y1M6Rmj6+6JC8Adf6tU2Kx/OzF6EHyg6v Q6lHBxRHdswAmozCKwNl1Yk6Wbl6EY4Yn17UR2P/HNwSChN8JzVddKHggptzdBx6p4 4NY1+fg2SOBwc7lHzoeOPKS6gOXuw5RkwFyDC4Kx2UZVzhHBws6oL1f8YmtXRwo3iK XmPknUyi/LfsuSAYI/GuKk4my1XcGX701xHJ04UM6tTXm3OOSImI5IWoJURQyYw+xG lXMdkNnBwpCeA== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:39 +0100 Subject: [PATCH 52/79] block: rust: add accessors to `TagSet` 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: <20260216-rnull-v6-19-rc5-send-v1-52-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1945; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=lMkix+l7YL3LsaeozijpZ70ut/nAoZ1bvzW7x8MaTVg=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhKcY5nY1KWMizzxuSEORA04SamyEuwDQMar kiUG9Zee1mJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYSgAKCRDhuBo+eShj d5v7EACQiUbG3DpCSqYweozV15pPocS41T8Gc3taZE4wwex28+Dgu0T+ZjyUxhoI5rWG47Sorzl iHOhY9a567WS4In+gqDkVx3HHfzg9GpiDdtrIganFB4y381gFo4nL+x09xDr73iF8BLhdWP2HL5 l7aqqNKU+bIB4oSyL6CsqxHkIKhhFJubVmGr1E1GZxO5yD0CCWtObhTK0MPuq30Ty88uXYjhdCQ QMXWnqTQkCmIzfEftgpvlqABMGH+3i0fSf2UZsQ7gWrJsbpkFGYoh4PdropXXPs8GG5uuiDIoLe xOp8sxKLgd+xFCVDprQNmGyxwGoiNvFmCwpuilkzI2MvCkvYyvN5db+EHfHKGnJ/uc9lqx7pUL6 hqemjwBzYaKc25c9JD1MuvLdnzQ/On3Tsrb7EC5Wfn92fZ+IbOW89ZpE5XEKlrwwzwWRbNl3Gj0 PfJ8nwjnFhPIRv4RsUH034uZpgtMjpqmi35EtgYTNrJxX6jCkj0EzZZLP91tbWYkyL2jMNnJ9g6 m9/gqpSyPokz0MhvpfXKFNwJ6Z+JM6hd5c4iFirSSUhEv3oxuWfRb4nsbcbOTci4ApNGfJls5wr H2R5Vx9SJYZTnGs/tqcRpAeDm+eAlG7T2Kmu978bkDJFsrIGzKj1enxr3EXEK7AizS3QKkgTrdh 7OuWjn0T4XL2IYw== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add `hw_queue_count()` to query the number of hardware queues and `data()` to borrow the private tag set data associated with a `TagSet`. Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq/tag_set.rs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set= .rs index e6edc5bc39312..4d00979a83dbd 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -4,8 +4,6 @@ //! //! C header: [`include/linux/blk-mq.h`](srctree/include/linux/blk-mq.h) =20 -use core::pin::Pin; - use crate::{ bindings, block::mq::{operations::OperationsVTable, request::RequestDataWrapper,= Operations}, @@ -13,7 +11,7 @@ try_pin_init, types::{ForeignOwnable, Opaque}, }; -use core::{convert::TryInto, marker::PhantomData}; +use core::{convert::TryInto, marker::PhantomData, pin::Pin}; use pin_init::{pin_data, pinned_drop, PinInit}; =20 mod flags; @@ -137,6 +135,22 @@ pub fn update_maps(self: Pin<&mut Self>, mut cb: impl = FnMut(QueueMap)) -> Result =20 Ok(()) } + + /// Return the number of hardware queues for this tag set. + pub fn hw_queue_count(&self) -> u32 { + // SAFETY: By type invariant, `self.inner` is valid. + unsafe { (*self.inner.get()).nr_hw_queues } + } + + /// Borrow the [`T::TagSetData`] associated with this tag set. + pub fn data(&self) -> ::Borrowed<'_> { + // SAFETY: By type invariant, `self.inner` is valid. + let ptr =3D unsafe { (*self.inner.get()).driver_data }; + + // SAFETY: `ptr` was created by `into_foreign` during initializati= on and the target is not + // converted back with `from_foreign` while `&self` is live. + unsafe { T::TagSetData::borrow(ptr) } + } } =20 #[pinned_drop] --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 1E4C62DE6FF; Sun, 15 Feb 2026 23:45:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199146; cv=none; b=N1+CCTyKDXrqcqEFCuHMgxiv11ZB32K9i3blrOu5jiPN/k2J9K7Tg3gE3YT8EOnOweyODiNjpCGaemMqHS+gyxokFD7OgMljt7JBIF0iBYrX5BZ5Fe68/y6280z75XM4mUSdZKIp61oDxgCR2aAp+mDvDwN5EXl0swLc0RJS/gQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199146; c=relaxed/simple; bh=SHQHcWrAr56kHNOm4L4h66Q7ENSNd//dP5BDvu4tb6M=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=da/iUjfH/eyh4tpOXW2YxR1YXzEkRJmH8RwnI1wngaCN0CHLmqxh3b18suALiAnY9+48TI3MYcDnD7f+n0fE8a5spCaR6+maZXedZ2NLi9+aFGfaowKGwVyi76O3W2S8cV2xpvVDLF+kkw8KcjIJX2x4qK9FP14gxTRZ/mUV/Ws= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=hPpt3LKx; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="hPpt3LKx" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6F9D6C4CEF7; Sun, 15 Feb 2026 23:45:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199145; bh=SHQHcWrAr56kHNOm4L4h66Q7ENSNd//dP5BDvu4tb6M=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=hPpt3LKx5ODgoYvnFGDMc14a3psBUBdm8tStjS5YgTVFEr+gtMc//bnKnabcdQdT7 I/rS6+/EcCG5iTAq6a3lk6rDMPGzsyC3TYYaS3t9M259/FDw1QplubWXeerUvMD6iB LN6k+te+eC0yHj4VBoMBMMe6PaNnSxdaxBM+B5kfQyWawcr5LgZYkb3mDy5w8NOaGV 3wTNcFGqxqwLhPg9ZriKTYgMaud9NlUCz3brhRzVgXWpRYBO5nYRKmwGXvxWSAw7e/ IESujoisZqCNTtgdFy275L4lm7V5YNO6V+Rxs5YgWRrNr2S6AlI9Z7rG7FFLVOfEM8 ArP9h9+gYnmSA== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:40 +0100 Subject: [PATCH 53/79] block: rnull: add polled completion support 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: <20260216-rnull-v6-19-rc5-send-v1-53-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=10872; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=SHQHcWrAr56kHNOm4L4h66Q7ENSNd//dP5BDvu4tb6M=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhLjsQPQorh27YF42h7pD7jlkOf1PFHTBR0u 3TI7UoofBKJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYSwAKCRDhuBo+eShj d/KSD/0Rq9t9OnKDySf4+B2ol8H/EOjSF++wjSDKMA6Gpxss08a0mQWIH/LyS47nXEwmBggaUy9 tu/RxjMv0xAtNqMH+77fDnl4wQ6Mz71L75tvo5/bdr+r0XqtDpMFSz9/ORtahkvxnUh/fvZaPFi Kw9SMbHJLN7zdXYv3lmjmxORRblYVtx9wjy7fSkr1VIqgXlmF2PdImQjOj/3CiR1dvKRPrxBRTb jg/e3bw0Bylhy3p9JdQDWy45aS+y2Efo87I0xdwGJFHq66oC0imdKawYcAKJWVxN5uLYRaa8yPY DuKjEvfrHiFWnoVf/taQxtIOObpyTuEm9WFZQcc5ab/Bn6Y8c+E3cpmvHfNxZh8XRvElSrOs8gr g+gCRZ5xRBC7ttW7aJ452Nasv/Jjj5nX4W0coMwiJHjCEPU1aBZ16/K6NM3xg30jvGxlwUKxPL5 sHAMc2ZuAWxKc98g3LwKspOP+ec5E+TCQAKUTawX0Hg91XkIiC3J7NrRmL1K9UTchJTepvhfY8X IDu6hjqSwaf1UQDrbaP20U18GBNQQMuC1e8MyCBNhR88ge25oY3qeGV9Omdqw4COBKSMZtTmsZ4 gI6/xPGsy82TJ0P006FIq2h6Nf77XHBzaZ5tj1LD+KeI8k7C61GSP3d6nI8PPTXxRR7j9yb4mqG S4rjwcR/abkmxyA== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add support for polled I/O completion in rnull. This feature requires configuring poll queues via the `poll_queues` attribute. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 19 +++++- drivers/block/rnull/rnull.rs | 130 ++++++++++++++++++++++++++++++++++++= ---- 2 files changed, 136 insertions(+), 13 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 0873d696f80f6..e134e21a6b564 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -71,7 +71,7 @@ impl AttributeOperations<0> for Config { writer.write_str( "blocksize,size,rotational,irqmode,completion_nsec,memory_back= ed\ submit_queues,use_per_node_hctx,discard,blocking,shared_tags,\ - zoned,zone_size,zone_capacity\n", + zoned,zone_size,zone_capacity,poll_queues\n", )?; Ok(writer.bytes_written()) } @@ -117,6 +117,7 @@ fn make_group( zone_max_open: 24, zone_max_active: 25, zone_append_max_sectors: 26, + poll_queues: 27, ], }; =20 @@ -156,6 +157,7 @@ fn make_group( zone_max_open: 0, zone_max_active: 0, zone_append_max_sectors: u32::MAX, + poll_queues: 0, }), }), core::iter::empty(), @@ -231,6 +233,7 @@ struct DeviceConfigInner { zone_max_open: u32, zone_max_active: u32, zone_append_max_sectors: u32, + poll_queues: u32, } =20 #[vtable] @@ -281,6 +284,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { zone_max_open: guard.zone_max_open, zone_max_active: guard.zone_max_active, zone_append_max_sectors: guard.zone_append_max_sectors, + poll_queues: guard.poll_queues, })?); guard.powered =3D true; } else if guard.powered && !power_op { @@ -510,3 +514,16 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { configfs_simple_field!(DeviceConfig, 24, zone_max_open, u32); configfs_simple_field!(DeviceConfig, 25, zone_max_active, u32); configfs_simple_field!(DeviceConfig, 26, zone_append_max_sectors, u32); +configfs_simple_field!( + DeviceConfig, + 27, + poll_queues, + u32, + check | value | { + if value > kernel::num_possible_cpus() { + Err(kernel::error::code::EINVAL) + } else { + Ok(()) + } + } +); diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 765bbc8101d10..92e75f15e02c6 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -33,6 +33,7 @@ GenDisk, GenDiskRef, // }, + IoCompletionBatch, Operations, TagSet, // }, @@ -188,6 +189,10 @@ default: 0, description: "Maximum size of a zone append command (in 512B s= ectors). Specify 0 for no zone append.", }, + poll_queues: u32 { + default: 0, + description: "Number of IOPOLL submission queues.", + }, }, } =20 @@ -244,6 +249,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { zone_max_open: *module_parameters::zone_max_open.value= (), zone_max_active: *module_parameters::zone_max_active.v= alue(), zone_append_max_sectors: *module_parameters::zone_appe= nd_max_sectors.value(), + poll_queues: *module_parameters::poll_queues.value(), })?; disks.push(disk, GFP_KERNEL)?; } @@ -291,6 +297,7 @@ struct NullBlkOptions<'a> { zone_max_active: u32, #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), expect(unused_variables))] zone_append_max_sectors: u32, + poll_queues: u32, } =20 static SHARED_TAG_SET: SetOnce>> =3D SetOnce::ne= w(); @@ -348,6 +355,7 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { zone_max_open, zone_max_active, zone_append_max_sectors, + poll_queues, } =3D options; =20 let mut flags =3D mq::tag_set::Flags::default(); @@ -369,7 +377,21 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { =20 let tagset_ctor =3D || -> Result> { Arc::pin_init( - TagSet::new(submit_queues, (), hw_queue_depth, 1, home_nod= e, flags), + TagSet::new( + submit_queues + poll_queues, + KBox::new( + NullBlkTagsetData { + queue_depth: hw_queue_depth, + submit_queue_count: submit_queues, + poll_queue_count: poll_queues, + }, + GFP_KERNEL, + )?, + hw_queue_depth, + if poll_queues =3D=3D 0 { 1 } else { 3 }, + home_node, + flags, + ), GFP_KERNEL, ) }; @@ -685,6 +707,7 @@ fn run( =20 struct HwQueueContext { page: Option>, + poll_queue: kernel::ringbuffer::RingBuffer>>, } =20 #[pin_data] @@ -713,11 +736,17 @@ impl HasHrTimer for Pdu { } } =20 +struct NullBlkTagsetData { + queue_depth: u32, + submit_queue_count: u32, + poll_queue_count: u32, +} + #[vtable] impl Operations for NullBlkDevice { type QueueData =3D Arc; type RequestData =3D Pdu; - type TagSetData =3D (); + type TagSetData =3D KBox; type HwData =3D Pin>>; =20 fn new_request_data() -> impl PinInit { @@ -733,7 +762,7 @@ fn queue_rq( this: ArcBorrow<'_, Self>, rq: Owned>, _is_last: bool, - _is_poll: bool, + is_poll: bool, ) -> BlkResult { if this.bandwidth_limit !=3D 0 { if !this.bandwidth_timer.active() { @@ -770,13 +799,29 @@ fn queue_rq( #[cfg(not(CONFIG_BLK_DEV_ZONED))] this.handle_regular_command(&hw_data, &mut rq)?; =20 - match this.irq_mode { - IRQMode::None =3D> Self::end_request(rq), - IRQMode::Soft =3D> mq::Request::complete(rq.into()), - IRQMode::Timer =3D> { - OwnableRefCounted::into_shared(rq) - .start(this.completion_time) - .dismiss(); + if is_poll { + // NOTE: We lack the ability to insert `Owned` into a + // `kernel::list::List`, so we use a `RingBuffer` instead. The + // drawback of this is that we have to allocate the space for = the + // ring buffer during drive initialization, and we have to hol= d the + // lock protecting the list until we have processed all the re= quests + // in the list. Change to a linked list when the kernel gets t= his + // ability. + + // NOTE: We are processing requests during submit rather than = during + // poll. This is different from C driver. C driver does proces= sing + // during poll. + + hw_data.lock().poll_queue.push_head(rq)?; + } else { + match this.irq_mode { + IRQMode::None =3D> Self::end_request(rq), + IRQMode::Soft =3D> mq::Request::complete(rq.into()), + IRQMode::Timer =3D> { + OwnableRefCounted::into_shared(rq) + .start(this.completion_time) + .dismiss(); + } } } Ok(()) @@ -784,8 +829,39 @@ fn queue_rq( =20 fn commit_rqs(_hw_data: Pin<&SpinLock>, _queue_data: A= rcBorrow<'_, Self>) {} =20 - fn init_hctx(_tagset_data: (), _hctx_idx: u32) -> Result= { - KBox::pin_init(new_spinlock!(HwQueueContext { page: None }), GFP_K= ERNEL) + fn poll( + hw_data: Pin<&SpinLock>, + _this: ArcBorrow<'_, Self>, + batch: &mut IoCompletionBatch, + ) -> Result { + let mut guard =3D hw_data.lock(); + let mut completed =3D false; + + while let Some(rq) =3D guard.poll_queue.pop_tail() { + let status =3D rq.data_ref().error.load(ordering::Relaxed); + rq.data_ref().error.store(0, ordering::Relaxed); + + // TODO: check error handling via status + if let Err(rq) =3D batch.add_request(rq, status !=3D 0) { + Self::end_request(rq); + } + + completed =3D true; + } + + Ok(completed) + } + + fn init_hctx(tagset_data: &NullBlkTagsetData, _hctx_idx: u32) -> Resul= t { + KBox::pin_init( + new_spinlock!(HwQueueContext { + page: None, + poll_queue: kernel::ringbuffer::RingBuffer::new( + tagset_data.queue_depth.try_into()? + )?, + }), + GFP_KERNEL, + ) } =20 fn complete(rq: ARef>) { @@ -805,4 +881,34 @@ fn report_zones( ) -> Result { Self::report_zones_internal(disk, sector, nr_zones, callback) } + + fn map_queues(tag_set: Pin<&mut TagSet>) { + let mut submit_queue_count =3D tag_set.data().submit_queue_count; + let mut poll_queue_count =3D tag_set.data().poll_queue_count; + + if tag_set.hw_queue_count() !=3D submit_queue_count + poll_queue_c= ount { + pr_warn!( + "tag set has unexpected hardware queue count: {}\n", + tag_set.hw_queue_count() + ); + submit_queue_count =3D 1; + poll_queue_count =3D 0; + } + + let mut offset =3D 0; + tag_set + .update_maps(|mut qmap| { + use mq::QueueType::*; + let queue_count =3D match qmap.kind() { + Default =3D> submit_queue_count, + Read =3D> 0, + Poll =3D> poll_queue_count, + }; + qmap.set_queue_count(queue_count); + qmap.set_offset(offset); + offset +=3D queue_count; + qmap.map_queues(); + }) + .unwrap() + } } --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 00B0A1DF75D; Sun, 15 Feb 2026 23:40:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198835; cv=none; b=Xn71h359cAsfoTMY4MNtOex/5bfrQ210EsyoRtDfbpiGAzN8yU/f9nwaKz2rFwKZZY5v37is40OQy1Nn1aAtcyo3ANUi/8z0MOp2y8RR5kDS8fakBBO69XFJOxhWG5K9FfZ9n2hf2kSY0Tl3Gpo2OYHE1DmhkbQ2tAtzJjd0Df8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198835; c=relaxed/simple; bh=lhLM7VW+elgLHbwAaApu2uDju5ClodzV2lsUXSCKd6w=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Dx+DTKbGHcpRESqwznOpDfs9mZQlx+NzLk1bIOXZRfCuw5fdFRsiNx2aoRf1RFTwIeBSsTUeEub2ERpvqlQ/hlJwaeHg7hmxPKqu8SmkPRCUzZtgLiF+tZWoIBhTMfM+02bGA4aXze9yFdTSkFpeGY7Zx5T57sdUNmBNVTniPt0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=EIVtL4lw; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="EIVtL4lw" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4950AC4CEF7; Sun, 15 Feb 2026 23:40:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198834; bh=lhLM7VW+elgLHbwAaApu2uDju5ClodzV2lsUXSCKd6w=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=EIVtL4lw8JFCfYyLbkOkVEa7JXlNSdcyGo4qOyIIWOqr+nEHnuDiRWmxUe6wzDpdt isgZdMXuUFBbJIUNka6yubc0IlqQeppw0r5J7yihoL0PlRPF3nv1l0ZcM/RjqhXCnQ ZXSayfGGBCotK5nvCVqe7QpRipdTvgbA9iZlPNJlpTWsBUO8LhhVDFR8B1xyV9HWsO degsko85wF5DrlPMznLHzQHKJp7T67C/Lv0943THWRSn45NAVDXxlfNuP9LzNFreQ8 +Vy6bec8YMLhqwhZBRcwIELNj+GoqC52kS9fMO8Nyk5zg9MMVo1UX8+DahqmUIXAz5 Z1ehugPEPKz6A== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:41 +0100 Subject: [PATCH 54/79] block: rnull: add REQ_OP_FLUSH support 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: <20260216-rnull-v6-19-rc5-send-v1-54-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4765; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=lhLM7VW+elgLHbwAaApu2uDju5ClodzV2lsUXSCKd6w=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhM3Jq8YHxrfkqK/oCU6MjwIsMXoUhI4IkTK +/jl05X3saJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYTAAKCRDhuBo+eShj d8KjD/0QbfTC3g4Lbp6rrVquof9Xlz219bbRgWBbI0ddnxfwoBL0oYG9l+U7pgzMJwxChHNGUsW yaXD7XeOVnRfKxNopjmfMpgY8dz4KBRFRPfAI1FOKkjg5Q7pAH68iGn3ytQrqpQ0vOy/qjO8hWX SOdpbBoM5SkJQWgmbRC5eW99He2gVmaNawIruI/N7nMh+H/v7+nDHxodwp+6l16XQ8T4h8L8eoH IsD89YfFpQavHXTBxUv+bhU5YKdy0HNGW8by7aRCmOhpvqP7ctHLNOLeEe8qCfkdEXg0DV/XDYj WqUXjGKZ7ImjxPcNgg6RAYVNerGHKqNus6SnHMgZzSrFspg7SeWJ9M9/BXW5P29HFP+k/lPzTHg J1PMbstlAaUcLEtJMqUkGpUCYYFkMgXeThO1GAnKHp5k+RAFZL6l99kAbEYLl0Cyp66FNYfJiN0 U3CzV14Qz1w1zRUoQKl6uMkCY+A0DIUDKOD7Whm/lR0Zw5xKKxFt+SazbuUIjCjMr4boyqklSy8 WN1vAeenCReskUvStsX6RSMCQEbP+SyNOphfLq15xvx25z1HfP72OFWohynS+RkoOwdhT/4nnse Jb++s4gzj5w8zDYD4uZ486v3p7PLBAoL0/UgxZK2hDpXeRoSMrJzlbvs48ilKYWUSluC5HSjQdz VoZHh/6GYoIaaWA== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add support for handling flush requests in rnull. When memory backing and write cache are enabled, flush requests trigger a cache flush operation that writes all dirty cache pages to the backing store. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/disk_storage.rs | 36 ++++++++++++++++++++++++++++-----= --- drivers/block/rnull/rnull.rs | 31 ++++++++++++++++++++++--------- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/drivers/block/rnull/disk_storage.rs b/drivers/block/rnull/disk= _storage.rs index ce3e83671709a..a613ed5223ba7 100644 --- a/drivers/block/rnull/disk_storage.rs +++ b/drivers/block/rnull/disk_storage.rs @@ -85,6 +85,13 @@ pub(crate) fn discard( remaining_bytes -=3D processed; } } + + pub(crate) fn flush(&self, hw_data: &Pin<&SpinLock>) -= > Result { + let mut tree_guard =3D self.lock(); + let mut hw_data_guard =3D hw_data.lock(); + let mut access =3D self.access(&mut tree_guard, &mut hw_data_guard= , None); + access.flush() + } } =20 pub(crate) struct DiskStorageAccess<'a, 'b, 'c> { @@ -118,13 +125,16 @@ fn to_sector(index: usize) -> u64 { (index << block::PAGE_SECTORS_SHIFT) as u64 } =20 - fn extract_cache_page(&mut self) -> Result> { - let cache_entry =3D self - .cache_guard - .find_next_entry_circular( - self.disk_storage.next_flush_sector.load(ordering::Relaxed= ) as usize - ) - .expect("Expected to find a page in the cache"); + fn extract_cache_page(&mut self) -> Result>= > { + let cache_entry =3D self.cache_guard.find_next_entry_circular( + self.disk_storage.next_flush_sector.load(ordering::Relaxed) as= usize, + ); + + let cache_entry =3D if let Some(entry) =3D cache_entry { + entry + } else { + return Ok(None); + }; =20 let index =3D cache_entry.index(); =20 @@ -168,7 +178,16 @@ fn extract_cache_page(&mut self) -> Result> { } }; =20 - Ok(page) + Ok(Some(page)) + } + + fn flush(&mut self) -> Result { + if self.disk_storage.cache_size > 0 { + while let Some(page) =3D self.extract_cache_page()? { + drop(page); + } + } + Ok(()) } =20 fn get_cache_page(&mut self, sector: u64) -> Result<&mut NullBlockPage= > { @@ -186,6 +205,7 @@ fn get_cache_page(&mut self, sector: u64) -> Result<&mu= t NullBlockPage> { .expect("Expected to have a page available") } else { self.extract_cache_page()? + .expect("Expected to find a page in the cache") }; Ok(self .cache_guard diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 92e75f15e02c6..4870aa3b7a53e 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -673,6 +673,18 @@ fn end_request(rq: Owned>) { _ =3D> rq.end(bindings::BLK_STS_IOERR), } } + + fn complete_request(&self, rq: Owned>) { + match self.irq_mode { + IRQMode::None =3D> Self::end_request(rq), + IRQMode::Soft =3D> mq::Request::complete(rq.into()), + IRQMode::Timer =3D> { + OwnableRefCounted::into_shared(rq) + .start(self.completion_time) + .dismiss(); + } + } + } } =20 impl_has_hr_timer! { @@ -789,6 +801,15 @@ fn queue_rq( =20 let mut rq =3D rq.start(); =20 + if rq.command() =3D=3D mq::Command::Flush { + if this.memory_backed { + this.storage.flush(&hw_data)?; + } + this.complete_request(rq); + + return Ok(()); + } + #[cfg(CONFIG_BLK_DEV_ZONED)] if this.zoned.enabled { this.handle_zoned_command(&hw_data, &mut rq)?; @@ -814,15 +835,7 @@ fn queue_rq( =20 hw_data.lock().poll_queue.push_head(rq)?; } else { - match this.irq_mode { - IRQMode::None =3D> Self::end_request(rq), - IRQMode::Soft =3D> mq::Request::complete(rq.into()), - IRQMode::Timer =3D> { - OwnableRefCounted::into_shared(rq) - .start(this.completion_time) - .dismiss(); - } - } + this.complete_request(rq); } Ok(()) } --=20 2.51.2 From nobody Fri Apr 17 20:22:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 16C342DE6FF; Sun, 15 Feb 2026 23:45:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199151; cv=none; b=JK1YKQ9BEH//DlzVWZybODkMWpgoj7HnjbhpPfqj42thcNjT7mf/tXPAT0wF2CZy81pfozr/3wjZ+ikfIYwq7Sgogtox9Eq8BVxvuQKgDZ0Y8KwcN2bRKNisVjiKXf6A//9Cs3t1sSA+69YIOOOnGPKBZ2My35+VbntSHrNeVMg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199151; c=relaxed/simple; bh=Cd90Kd9HZBS+t07qweteZVt5e7ejNNo7uFQ2B+yelCs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=olLTNMdCTkEZAC92OAILo9bopcr5fDOJy5WfGqyjM8GgMJTrpNxRD98u9YbTUxYdRpys/djZA2OFKbF02J0YwqzpCVvGSCitspQZKf1FrgNVgqXVcyS+dkjZJfPoUjNEJITkVzdfWdbbCG5LCG2t7FqDGmiTQPJtYBuqDATIQmA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=jKuMyOSF; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="jKuMyOSF" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6295DC4CEF7; Sun, 15 Feb 2026 23:45:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199150; bh=Cd90Kd9HZBS+t07qweteZVt5e7ejNNo7uFQ2B+yelCs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=jKuMyOSFwHX0iejhTs6DSzYtDAHi0TVVWOXrm312C8vhFHJSLdQ3Qle0ezUBJQsw/ BSjBS9FT9sjz2iinpGKkXF+uXTaJcMDWxTE1PpXiRwy+F01l/qjyqwi8qoNi9kXhiv z2af+M/GRndl5ZsexUd9sVl++PujRwZvbxYVVw44vVapdsfjxyFKx50JAVRdT/X/ih ZF67rBtRb8vcjHOIa8k9Z7+IEvvKLtMWk/jUoRlMlgxs0DHOpCm2EbmQB/3oJ/Xav2 64j1uyqB/P/qRkcufIMYcor/n3SQyW1s/JdFk8MwOP5FaY3Nq79eBCloOG5bQ0zP1N pdALkPc3DGxsw== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:42 +0100 Subject: [PATCH 55/79] block: rust: add request flags abstraction 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: <20260216-rnull-v6-19-rc5-send-v1-55-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=7161; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=Cd90Kd9HZBS+t07qweteZVt5e7ejNNo7uFQ2B+yelCs=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhN/gQhzHqP+13Aj/SzVGlQW5wV04BcO9cf5 o5yh0IrX8eJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYTQAKCRDhuBo+eShj d17mD/9bbJUdmA/hnedCkeHucvwcMhmYMNx/PApRtG54XNFS/o5+Z4hGXSUFO8b2BSrr3Av7LoK LrfBvkr3pORY0mA4uOssbPoIvhuyYniEuNyjdp7eaujRBCwz+4hA6a2pMu0lRxG3288s/7TnrK0 uM2oZsPIMyw+KIHeMmArlVqsHDo/WycktYLXetrMx9B9ZOgdNmfkOj5sxH2jWzXG1/P1Blxb5DQ 38JvKPe5DiihDyGnTVtK2GYhUuD96kum8ZU4jsS9MYZCIC3N4PhwaaBbmFV/zFiL99/xJh1lqdj QcHNVaiIhCoo071CMXZGCQqySMsiof0WmtZmybW+wS14ubNvIEWv6hkFep1+ji2iR6s0orB5egx jjYO1ZM/K5EIoiN1YPkW4AtySA+kG2GnpuoUnaamv+lLEliQiUjmvRqQzsP1C37SiPiD7gldCVO juSzBOu/wITj/idLDHbNv8IxdV1muTtWds7JzF4gOHBnGHm33L6rIVx/kMgPKhgzB5O8vn2cSIi ABvkS4pBH7SQFR5Ckkl7pescdpJ9+CAcr1Ph/O829WEFjsi2F3DVkqfJZkTBMJnAGvX1v7bT1ip M7ozfp78KMpSkVceNkwDEZOPxXJQLpvbKyqxtuUzxB5l17pLrV2y5dvpQtvFBGgZ1J5MgAIQP9U N888I4Cln+wpFxg== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add the `Flag` enum and `Flags` type as Rust abstractions for the C `REQ_*` request flags. These flags modify how block I/O requests are processed, including sync behavior, priority hints, and integrity settings. Also add a `flags()` method to `Request` to retrieve the flags for a given request. Signed-off-by: Andreas Hindborg --- rust/bindings/bindings_helper.h | 21 ++++++++++++ rust/kernel/block/mq.rs | 2 ++ rust/kernel/block/mq/request.rs | 9 ++++++ rust/kernel/block/mq/request/flag.rs | 62 ++++++++++++++++++++++++++++++++= ++++ 4 files changed, 94 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 05133a78ecf95..b8c2f3eadc730 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -134,6 +134,27 @@ const blk_status_t RUST_CONST_HELPER_BLK_STS_OFFLINE = =3D BLK_STS_OFFLINE; const blk_status_t RUST_CONST_HELPER_BLK_STS_DURATION_LIMIT =3D BLK_STS_DU= RATION_LIMIT; const blk_status_t RUST_CONST_HELPER_BLK_STS_INVAL =3D BLK_STS_INVAL; const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ZONED =3D BLK_FEAT_ZONED; +const blk_opf_t RUST_CONST_HELPER_REQ_FAILFAST_DEV =3D REQ_FAILFAST_DEV; +const blk_opf_t RUST_CONST_HELPER_REQ_FAILFAST_TRANSPORT =3D REQ_FAILFAST_= TRANSPORT; +const blk_opf_t RUST_CONST_HELPER_REQ_FAILFAST_DRIVER =3D REQ_FAILFAST_DRI= VER; +const blk_opf_t RUST_CONST_HELPER_REQ_SYNC =3D REQ_SYNC; +const blk_opf_t RUST_CONST_HELPER_REQ_META =3D REQ_META; +const blk_opf_t RUST_CONST_HELPER_REQ_PRIO =3D REQ_PRIO; +const blk_opf_t RUST_CONST_HELPER_REQ_NOMERGE =3D REQ_NOMERGE; +const blk_opf_t RUST_CONST_HELPER_REQ_IDLE =3D REQ_IDLE; +const blk_opf_t RUST_CONST_HELPER_REQ_INTEGRITY =3D REQ_INTEGRITY; +const blk_opf_t RUST_CONST_HELPER_REQ_FUA =3D REQ_FUA; +const blk_opf_t RUST_CONST_HELPER_REQ_PREFLUSH =3D REQ_PREFLUSH; +const blk_opf_t RUST_CONST_HELPER_REQ_RAHEAD =3D REQ_RAHEAD; +const blk_opf_t RUST_CONST_HELPER_REQ_BACKGROUND =3D REQ_BACKGROUND; +const blk_opf_t RUST_CONST_HELPER_REQ_NOWAIT =3D REQ_NOWAIT; +const blk_opf_t RUST_CONST_HELPER_REQ_POLLED =3D REQ_POLLED; +const blk_opf_t RUST_CONST_HELPER_REQ_ALLOC_CACHE =3D REQ_ALLOC_CACHE; +const blk_opf_t RUST_CONST_HELPER_REQ_SWAP =3D REQ_SWAP; +const blk_opf_t RUST_CONST_HELPER_REQ_DRV =3D REQ_DRV; +const blk_opf_t RUST_CONST_HELPER_REQ_FS_PRIVATE =3D REQ_FS_PRIVATE; +const blk_opf_t RUST_CONST_HELPER_REQ_ATOMIC =3D REQ_ATOMIC; +const blk_opf_t RUST_CONST_HELPER_REQ_NOUNMAP =3D REQ_NOUNMAP; 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; diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index 1d58eea971bd7..5a1c8e914bb9e 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -134,6 +134,8 @@ pub use operations::IoCompletionBatch; pub use operations::Operations; pub use request::Command; +pub use request::Flag as RequestFlag; +pub use request::Flags as RequestFlags; pub use request::IdleRequest; pub use request::Request; pub use request::RequestTimerHandle; diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index b4f5a98a97b16..cebb30fe9b3bc 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -27,6 +27,9 @@ mod command; pub use command::Command; =20 +mod flag; +pub use flag::{Flag, Flags}; + /// A [`Request`] that a driver has not yet begun to process. /// /// A driver can convert an `IdleRequest` to a [`Request`] by calling [`Id= leRequest::start`]. @@ -104,6 +107,12 @@ pub fn command(&self) -> Command { unsafe { Command::from_raw(self.command_raw()) } } =20 + pub fn flags(&self) -> Flags { + // SAFETY: By C API contract and type invariant, `cmd_flags` is va= lid for read + let flags =3D unsafe { (*self.0.get()).cmd_flags & !((1 << binding= s::REQ_OP_BITS) - 1) }; + Flags::try_from(flags).expect("Request should have valid falgs") + } + /// Get the target sector for the request. #[inline(always)] pub fn sector(&self) -> u64 { diff --git a/rust/kernel/block/mq/request/flag.rs b/rust/kernel/block/mq/re= quest/flag.rs new file mode 100644 index 0000000000000..47d25224a1ab4 --- /dev/null +++ b/rust/kernel/block/mq/request/flag.rs @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0 +use kernel::prelude::*; + +impl_flags! { + /// A set of request flags. + /// + /// This type wraps the C `REQ_*` flags and allows combining multiple = flags + /// together. These flags modify how a block I/O request is processed. + #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)] + pub struct Flags(u32); + + /// Individual request flags for block I/O operations. + /// + /// These flags correspond to the C `REQ_*` defines in `linux/blk_type= s.h` + /// and are used to modify the behavior of block I/O requests. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum Flag { + /// No driver retries on device errors. + FailfastDev =3D bindings::REQ_FAILFAST_DEV, + /// No driver retries on transport errors. + FailfastTransport =3D bindings::REQ_FAILFAST_TRANSPORT, + /// No driver retries on driver errors. + FailfastDriver =3D bindings::REQ_FAILFAST_DRIVER, + /// Request is synchronous (sync write or read). + Sync =3D bindings::REQ_SYNC, + /// Metadata I/O request. + Meta =3D bindings::REQ_META, + /// Boost priority in CFQ scheduler. + Priority =3D bindings::REQ_PRIO, + /// Don't merge this request with others. + NoMerge =3D bindings::REQ_NOMERGE, + /// Anticipate more I/O after this one. + Idle =3D bindings::REQ_IDLE, + /// I/O includes block integrity payload. + Integrity =3D bindings::REQ_INTEGRITY, + /// Forced unit access - data must be written to persistent storage + /// before command completion is signaled. + ForcedUnitAccess =3D bindings::REQ_FUA, + /// Request a cache flush before this operation. + Preflush =3D bindings::REQ_PREFLUSH, + /// Read ahead request, can fail anytime. + ReadAhead =3D bindings::REQ_RAHEAD, + /// Background I/O operation. + Background =3D bindings::REQ_BACKGROUND, + /// Don't wait if the request would block. + NoWait =3D bindings::REQ_NOWAIT, + /// Caller polls for completion using `bio_poll`. + Polled =3D bindings::REQ_POLLED, + /// Allocate I/O from cache if available. + AllocCache =3D bindings::REQ_ALLOC_CACHE, + /// Swap I/O operation. + Swap =3D bindings::REQ_SWAP, + /// Reserved for driver use. + Driver =3D bindings::REQ_DRV, + /// Reserved for file system (submitter) use. + FsPrivate =3D bindings::REQ_FS_PRIVATE, + /// Atomic write operation. + Atomic =3D bindings::REQ_ATOMIC, + /// Do not free blocks when zeroing (for write zeroes operations). + NoUnmap =3D bindings::REQ_NOUNMAP, + } +} --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 105312D97A6; Sun, 15 Feb 2026 23:42:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198934; cv=none; b=pvFxRjcFNB92UMelEZNGEUzplMXkuK36v18RL09mOiZhC9m7BVssCYi5g5Bcjm1DbDGirzqHgMqhBG1XPWwoABX/2uwPQU3m5y9fb7xE2TLlsYZcdYiCGM6h9u6cOk+tveXEeZwp795Ykp0qRzgGFSaKZEF+cEuKmjohJ2pTvXE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198934; c=relaxed/simple; bh=h7+bon01UENFNoUz8aPGZ7HCRgNi5L5RQlf/Y/C7XMA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Gtds3KrHqSEnlY2bN2sbSrRR2mMKpkEhPnepHG9m+DZCWRgWv+0lZpamRTSwFogaJ36IgdaOBljniaF2uGAdVyZXoXAf4McmeHIaqpqbpd+/v2oN5rYwp4ST2NyUuUr3Jd0Re2vaCKGo/hOQse63A/p7T9MOwAYluUzauxfJShU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=oTB2Bv0h; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="oTB2Bv0h" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 58C44C4CEF7; Sun, 15 Feb 2026 23:42:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198933; bh=h7+bon01UENFNoUz8aPGZ7HCRgNi5L5RQlf/Y/C7XMA=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=oTB2Bv0htB2pXAXWaZJsudOqYB2WyHJ74Zz2aeHIU3yDKemx2JWmtEf8wyTL84VPL dRyIkzzyIQsjYHgWBkoHd+YUny96mtkaUBkrhDyVio9zCC3+WSHffWQWsoh+MRjLR4 bg3kzp8VNT1qSq6rw75xhyP7k88bU+LbVBNEW7s4bwqJHBotNK2ZiaYPz8yGfEmds9 ZK8r9TrjOvywsNIBH6jnlG7LlQ0At0/TFSeIJpR0Nat1vbvJemEV4/tbyOKw7hfnQN aasdUQPnAkxUS3HqfhHpzAi3DeHoci8f1DnQyHxbxxSnFRtTkO2Kq/BWB8VuVHL3dK OrIlFBmjm6eSA== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:43 +0100 Subject: [PATCH 56/79] block: rust: add abstraction for block queue feature flags 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: <20260216-rnull-v6-19-rc5-send-v1-56-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=6502; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=h7+bon01UENFNoUz8aPGZ7HCRgNi5L5RQlf/Y/C7XMA=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhOREpIyxuvj/IBFaVKOjzP6Ic55E0Q/l0dW MIwADOZnFeJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYTgAKCRDhuBo+eShj d9hoEACpVmpgtnsKcpDvuhvuAPuxqSo5potehm+47IVnGk8liQen6kvUYVGj8MBJ92nUu9SvxuY dtoaM8jPnSp4ocwYgA4nowinOfRuNy7oBVWt8HmD0WQtFC6AKb6bJZotCcb7PmpeJF0YPu6kyOI WUC5JGi3URjTb1Qq7MbfGjXXvvHIqlZT+MuxDboD/0EBn5EJl+EjKjNVYVS9TeA2aq6Q/f03h7z 3xP1Mjrbmj9sXhwYAy6T+wfbC2GrTbcjw/Hob5FFXb297vFUX7d++uNTazraLYmpfi99JcjltAi 0wbe28Jp6hJE9zeqOlufQLn/x3MPMl8Kn+00raZObBz06TiRKXSgOj1Gt5Yri6PznQjjR+ITxXE 3GQeznd6Xd/BLpDQSfNfR/RBxAAkoWf3jfpj4tblQFH3K84gvr7jxwWH2+/qEuXVqmLVgwR/+hB DtwffWbgsHjVSLjItvZuQ3wkWAF1SqLhG+JOkgAU/tP+/uhNCVRLcccxnSsy6xhIlxVpisfG8HZ 0n95Sh6IL2L43khIYnpYfNVuFkCnzD+7AYxe0SgMd9/eAytq1ETsaNMG+6WWQFAo6vqmvjrQ8JE zCpkPZGushAyWA5FLbU0pLekebqK9U98u4uomQZtHfd73cUHlCwIf8lM+PtZTtmEZ9nRrXliYZs yYj1lSCT2uPCd7w== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add the `Feature` enum and `Features` type as Rust abstractions for the C `blk_features_t` bitfield. These types wrap the `BLK_FEAT_*` flags and allow drivers to describe block device capabilities such as write cache support, FUA, rotational media, and DAX. Signed-off-by: Andreas Hindborg --- rust/bindings/bindings_helper.h | 15 ++++++++- rust/kernel/block/mq.rs | 2 ++ rust/kernel/block/mq/feature.rs | 73 +++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 89 insertions(+), 1 deletion(-) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index b8c2f3eadc730..209c53443e9cb 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -113,7 +113,6 @@ const gfp_t RUST_CONST_HELPER_GFP_NOWAIT =3D GFP_NOWAIT; const gfp_t RUST_CONST_HELPER___GFP_ZERO =3D __GFP_ZERO; const gfp_t RUST_CONST_HELPER___GFP_HIGHMEM =3D ___GFP_HIGHMEM; const gfp_t RUST_CONST_HELPER___GFP_NOWARN =3D ___GFP_NOWARN; -const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ROTATIONAL =3D BLK_FEAT_RO= TATIONAL; const blk_status_t RUST_CONST_HELPER_BLK_STS_OK =3D BLK_STS_OK; const blk_status_t RUST_CONST_HELPER_BLK_STS_NOTSUPP =3D BLK_STS_NOTSUPP; const blk_status_t RUST_CONST_HELPER_BLK_STS_TIMEOUT =3D BLK_STS_TIMEOUT; @@ -133,7 +132,21 @@ const blk_status_t RUST_CONST_HELPER_BLK_STS_ZONE_ACTI= VE_RESOURCE =3D BLK_STS_ZONE const blk_status_t RUST_CONST_HELPER_BLK_STS_OFFLINE =3D BLK_STS_OFFLINE; const blk_status_t RUST_CONST_HELPER_BLK_STS_DURATION_LIMIT =3D BLK_STS_DU= RATION_LIMIT; const blk_status_t RUST_CONST_HELPER_BLK_STS_INVAL =3D BLK_STS_INVAL; +const blk_features_t RUST_CONST_HELPER_BLK_FEAT_WRITE_CACHE =3D BLK_FEAT_W= RITE_CACHE; +const blk_features_t RUST_CONST_HELPER_BLK_FEAT_FUA =3D BLK_FEAT_FUA; +const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ROTATIONAL =3D BLK_FEAT_RO= TATIONAL; +const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ADD_RANDOM =3D BLK_FEAT_AD= D_RANDOM; +const blk_features_t RUST_CONST_HELPER_BLK_FEAT_IO_STAT =3D BLK_FEAT_IO_ST= AT; +const blk_features_t RUST_CONST_HELPER_BLK_FEAT_STABLE_WRITES =3D BLK_FEAT= _STABLE_WRITES; +const blk_features_t RUST_CONST_HELPER_BLK_FEAT_SYNCHRONOUS =3D BLK_FEAT_S= YNCHRONOUS; +const blk_features_t RUST_CONST_HELPER_BLK_FEAT_NOWAIT =3D BLK_FEAT_NOWAIT; +const blk_features_t RUST_CONST_HELPER_BLK_FEAT_DAX =3D BLK_FEAT_DAX; +const blk_features_t RUST_CONST_HELPER_BLK_FEAT_POLL =3D BLK_FEAT_POLL; const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ZONED =3D BLK_FEAT_ZONED; +const blk_features_t RUST_CONST_HELPER_BLK_FEAT_PCI_P2PDMA =3D BLK_FEAT_PC= I_P2PDMA; +const blk_features_t RUST_CONST_HELPER_BLK_FEAT_SKIP_TAGSET_QUIESCE =3D BL= K_FEAT_SKIP_TAGSET_QUIESCE; +const blk_features_t RUST_CONST_HELPER_BLK_FEAT_RAID_PARTIAL_STRIPES_EXPEN= SIVE =3D BLK_FEAT_RAID_PARTIAL_STRIPES_EXPENSIVE; +const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ATOMIC_WRITES =3D BLK_FEAT= _ATOMIC_WRITES; const blk_opf_t RUST_CONST_HELPER_REQ_FAILFAST_DEV =3D REQ_FAILFAST_DEV; const blk_opf_t RUST_CONST_HELPER_REQ_FAILFAST_TRANSPORT =3D REQ_FAILFAST_= TRANSPORT; const blk_opf_t RUST_CONST_HELPER_REQ_FAILFAST_DRIVER =3D REQ_FAILFAST_DRI= VER; diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index 5a1c8e914bb9e..b36112f7b22b9 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -125,12 +125,14 @@ //! # Ok::<(), kernel::error::Error>(()) //! ``` =20 +mod feature; pub mod gen_disk; mod operations; mod request; mod request_queue; pub mod tag_set; =20 +pub use feature::{Feature, Features}; pub use operations::IoCompletionBatch; pub use operations::Operations; pub use request::Command; diff --git a/rust/kernel/block/mq/feature.rs b/rust/kernel/block/mq/feature= .rs new file mode 100644 index 0000000000000..6c9f098e10c55 --- /dev/null +++ b/rust/kernel/block/mq/feature.rs @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Block device feature flags. +//! +//! This module provides Rust abstractions for the C `blk_features_t` type= and +//! the associated `BLK_FEAT_*` flags defined in `include/linux/blkdev.h`. + +use kernel::prelude::*; + +impl_flags! { + /// A set of block device feature flags. + /// + /// This type wraps the C `blk_features_t` bitfield and represents a + /// combination of zero or more [`Feature`] flags. It is used to descr= ibe + /// the capabilities of a block device in [`struct queue_limits`]. + /// + /// [`struct queue_limits`]: srctree/include/linux/blkdev.h + #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)] + pub struct Features(u32); + + /// A block device feature flag. + /// + /// Each variant corresponds to a `BLK_FEAT_*` constant defined in + /// `include/linux/blkdev.h`. These flags describe individual capabili= ties + /// or properties of a block device. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum Feature { + /// Supports a volatile write cache. + WriteCache =3D bindings::BLK_FEAT_WRITE_CACHE, + + /// Supports passing on the FUA bit. + ForcedUnitAccess =3D bindings::BLK_FEAT_FUA, + + /// Rotational device (hard drive or floppy). + Rotational =3D bindings::BLK_FEAT_ROTATIONAL, + + /// Contributes to the random number pool. + AddRandom =3D bindings::BLK_FEAT_ADD_RANDOM, + + /// Enables disk/partitions I/O accounting. + IoStat =3D bindings::BLK_FEAT_IO_STAT, + + /// Don't modify data until writeback is done. + StableWrites =3D bindings::BLK_FEAT_STABLE_WRITES, + + /// Always completes in submit context. + Synchronous =3D bindings::BLK_FEAT_SYNCHRONOUS, + + /// Supports REQ_NOWAIT. + Nowait =3D bindings::BLK_FEAT_NOWAIT, + + /// Supports DAX. + Dax =3D bindings::BLK_FEAT_DAX, + + /// Supports I/O polling. + Poll =3D bindings::BLK_FEAT_POLL, + + /// Is a zoned device. + Zoned =3D bindings::BLK_FEAT_ZONED, + + /// Supports PCI(e) p2p requests. + PciP2Pdma =3D bindings::BLK_FEAT_PCI_P2PDMA, + + /// Skips this queue in `blk_mq_(un)quiesce_tagset`. + SkipTagsetQuiesce =3D bindings::BLK_FEAT_SKIP_TAGSET_QUIESCE, + + /// Undocumented magic for bcache. + RaidPartialStripesExpensive =3D bindings::BLK_FEAT_RAID_PARTIAL_ST= RIPES_EXPENSIVE, + + /// Atomic writes enabled. + AtomicWrites =3D bindings::BLK_FEAT_ATOMIC_WRITES, + } +} --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 2844F2E2852; Sun, 15 Feb 2026 23:46:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199176; cv=none; b=iVvjawRVcj630cX4LugLzBhoGGJ6I77FT/qEf6BxbTT22+9R8/PZI/VkF4dhbYA73qj7fRhXkBlJe2MjREo23HottjY3jnPCwbg2SukK4Jb0Bgb038kzuSD45wTw1NXudl3pZVo0DgWOKegpCnXi5EB3AJAL1EJM4ntn7kJEnXs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199176; c=relaxed/simple; bh=SmeQZXFebNfgMhT0PU4tfkkh8SCMyfZDkLAJp5Aj2+g=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=LyfPNv0xwFM5EOzLlQD2IfaRVuLECaNiBNlNLh1OVzm7ixjTyWRnkVgYgEntxcx3EprSR+Bg/ERnHUZgF3RNPzAy9h+pnzBGE0ahjHihSq3SHzJ9aTaZWPGnvc4QjUrzeT1Xy6JQTCUC3L8Sb3dDI9wWbxh5MqZHp//1VeEWXQc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=knbOyE00; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="knbOyE00" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 864ACC4CEF7; Sun, 15 Feb 2026 23:46:11 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199176; bh=SmeQZXFebNfgMhT0PU4tfkkh8SCMyfZDkLAJp5Aj2+g=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=knbOyE00fHy5kmFNl9V+TkJ2VfcplCF4AeFvXCssIlmBjbGFlST02ikxq5VIxwjEU cAQJZulXgIGpfYdvUVCxJROn345beJ63HQacigsH3SmHFtf6p18NRlKq6KUopXzvMP u0cXk/H7K1cqwE1c8TgMcIaQT8JZIJ8HbbWVJrqAUqJoCcQ4AkQ6I2JCMEg6NCpB1V WNJLYtEpctbJO+lrb2+BrZv31E/bm/fLWSwLl1rZbB/+HgYV5MEQO6KnUSgZvFPBRX aOPMGZzY8v3UJ5v7Hs8a/C5B4tt8R42GuU9axuD2s12tJTy5vwFFzj621e6yQ19su5 xUCBuNIC4KuJQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:44 +0100 Subject: [PATCH 57/79] block: rust: allow setting write cache and FUA flags for `GenDisk` 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: <20260216-rnull-v6-19-rc5-send-v1-57-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=3122; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=SmeQZXFebNfgMhT0PU4tfkkh8SCMyfZDkLAJp5Aj2+g=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhPxhVYrbg/jqgQz+TX3wQMfT4hRanolERcq iBGDi6PjuSJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYTwAKCRDhuBo+eShj d8VrD/9iDAv4/R16Jikua6mLzD8+doovIV+CniFnxdRh9EjytOr41KXGvjIE2jpebbEWLXNvnhu J/pLqVjdxVGJkfuYyyOGJZ5xUTfed3f+2oGEwtn/fIyLjQ02fBxX3V9uR5TDvxbMFYEJWCXVInA nheaxy+vjh7rP0DPWqslmsajzqztYr7TD+XYRI2yVR1YD4a66YlslCmcSohocxU9Hvhf1rAJ3Zv 9yedHbMyr0+T1UzL9DwSfvTpA0G4mFNzlt8/mIorrLgbrdodHgrkzJ33KOnZm2oNyVT7rJER5w9 bk7/iOGMYu1bwzy3NiWaruwGoywlJgeMJf73OaUEv5nfFZE2Ed5/6ScUDATU5Opvnru7I+u5H1z awIZnLhwRz1x9qCRe0FTY2+0dlTT7X9hz1CBw4OjNK5rDTIHFw7i2A7NjdikeEowX73PwJJ141u 3dnbRekFt9x72pavXfVUAGCkRjoVYqLtrHVOdYXrmbpit4JiwFkC+h/iRnhcE1YKtY9huAtmyW9 BTvx4eSeANFBm4KjSKnbeIzmztOTXC6zo0U3cwvpe93LeFxvnlMhsr77Kwg6SfWi6oAxNuuaR5f jEIhOrv3llH+ImrALAH8vYA4RBFVYDkvE6igKcpWXNkbTDaBpeijcGuBrbGhYPAbOkUUIahPV7K /dIvSHjKMitbfpQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add methods to `GenDiskBuilder` for enabling the write cache and FUA feature flags. These flags are set in the `queue_limits` structure when building the disk. Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq/gen_disk.rs | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_di= sk.rs index a6f113ea4bea4..c6b9839864012 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -7,7 +7,7 @@ =20 use crate::{ bindings, - block::mq::{operations::OperationsVTable, Operations, RequestQueue, Ta= gSet}, + block::mq::{operations::OperationsVTable, Feature, Operations, Request= Queue, TagSet}, error::{self, from_err_ptr, Result}, fmt::{self, Write}, prelude::*, @@ -34,6 +34,8 @@ pub struct GenDiskBuilder { zone_size_sectors: u32, #[cfg(CONFIG_BLK_DEV_ZONED)] zone_append_max_sectors: u32, + write_cache: bool, + forced_unit_access: bool, _p: PhantomData, } =20 @@ -51,6 +53,8 @@ fn default() -> Self { zone_size_sectors: 0, #[cfg(CONFIG_BLK_DEV_ZONED)] zone_append_max_sectors: 0, + write_cache: false, + forced_unit_access: false, _p: PhantomData, } } @@ -143,6 +147,18 @@ pub fn zone_append_max(mut self, sectors: u32) -> Self= { self } =20 + /// Declare that this device supports forced unit access. + pub fn forced_unit_access(mut self, enable: bool) -> Self { + self.forced_unit_access =3D enable; + self + } + + /// Declare that this device has a write-back cache. + pub fn write_cache(mut self, enable: bool) -> Self { + self.write_cache =3D enable; + self + } + /// Build a new `GenDisk` and add it to the VFS. pub fn build( self, @@ -163,7 +179,7 @@ pub fn build( lim.physical_block_size =3D self.physical_block_size; lim.max_hw_discard_sectors =3D self.max_hw_discard_sectors; if self.rotational { - lim.features |=3D bindings::BLK_FEAT_ROTATIONAL; + lim.features =3D Feature::Rotational.into(); } =20 #[cfg(CONFIG_BLK_DEV_ZONED)] @@ -172,11 +188,19 @@ pub fn build( return Err(error::code::EINVAL); } =20 - lim.features |=3D bindings::BLK_FEAT_ZONED; + //lim.features |=3D request::Feature::Zoned.into(); lim.chunk_sectors =3D self.zone_size_sectors; lim.max_hw_zone_append_sectors =3D self.zone_append_max_sector= s; } =20 + if self.write_cache { + lim.features |=3D Feature::WriteCache; + } + + if self.forced_unit_access { + lim.features |=3D Feature::ForcedUnitAccess; + } + // SAFETY: `tagset.raw_tag_set()` points to a valid and initialize= d tag set let gendisk =3D from_err_ptr(unsafe { bindings::__blk_mq_alloc_disk( --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 2BBD22D7DF1; Sun, 15 Feb 2026 23:42:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198929; cv=none; b=Tc9WcKqGItfOICj9sKUPlrH8JCWnPJBlF2n0Hzyr1l97I9doB2+2Mt4cwKy2XTZCL20cdi21YLj6/lt1oy/FwU8aL+fVte3aGR3+tghtjIIRJ/gn3uWqSdWgMDekIvdk9IWvkbEcDXu8bsQTqmQUKut4+Rcm0Q6T0Iv5AlH6xXw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198929; c=relaxed/simple; bh=uYEwg9qVRQ0NhWZcmlYe0zv8laqBKSDqB2tGW/RMct8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=F3zsPLnIOMnQQktGhXgnhQKyuKrS7LRUcKSRhVXEPUpNu5XTQnFlKwLkR3Z6jCxuW+rnrIyylGVPrj/bsUc5vkAuL5eIOSLY7tbHKOaN5ciHSG0szOPRjMpXL92T+sa4YCGRuqEn5o1KZI34SMpK5G4X1bhEE9+Lejqm3cAqe0I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=CU27ZXms; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="CU27ZXms" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 53D6AC19425; Sun, 15 Feb 2026 23:42:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198928; bh=uYEwg9qVRQ0NhWZcmlYe0zv8laqBKSDqB2tGW/RMct8=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=CU27ZXms6kIrV7G19DfaAaAOwUelhCVipFwQDeCbX4VWn6wVJPDLJ3grDPzSnCtK+ KiuMpnyzd8owICPC2W0nqFdb5DrMTnIxEomPXMeWMm3QGk8Qp3tZcdom0FXNbB1OF3 xwp2ycZjZvwEnm0SbXFmmEx8fPkYkZN5dSSwlKMe1WpNA3bGiOWZ7mHerhivkeXudH otqNDMK90oSKhrl57Wv9F0Iq4PyNxVeEhkJujbCXcvaVMxk2fhM7WPYnmy0J2J2rSd omkM1H18JL/cwXlob4zMsBQ7RMTfe/1XOx5TpA5tlAHEbRBQbiQIXRYlM4rP29ymMY h+cIgNNxzckGQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:45 +0100 Subject: [PATCH 58/79] block: rust: add `Segment::copy_to_page_limit` 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: <20260216-rnull-v6-19-rc5-send-v1-58-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=2840; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=uYEwg9qVRQ0NhWZcmlYe0zv8laqBKSDqB2tGW/RMct8=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhPD1Vd13GaRkq5agX96n72Np4kCvlo5WLCD fk9Egr801WJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYTwAKCRDhuBo+eShj d7efD/wPEHMykdNbgIQEgFjD0g5u2LBnODSA52TqS+8dh9ONb7dkKxO6CM34Xdd5hZN9vKMUaJ5 oyXEeVDHOLxk95g1hPvhVpYZ1QVVxOEySidpbAGXoIucHyW3bNa/75cg0df/ymrsxTT2vsZbVoh aWjyFDqwMSw0k6Dn0E1nMfxMPlP2IGXS/G8AxSz/Y/1NjDs/ln6V27Menk1/1K4/WfWpa2g6jtT i7LiKLwqGTtRBXq/9T4PO6TuRJedJ/2IAUxbftcIT0mUbMSxTM2rnJVl7s5NouEgt6D2YSdgKoV S2I0ax8k7WECCBLHwKmcmPI8WX9CqSjg0X5BLpIVT3c7KNLcrCk88REdnMIszn21mE34J1gVp21 wujZR9oktHlgM7OSn/hcmXEmq7G05hqdOO5nyvDruUebu7pdNJfDrg0czUEbX9hOpJBHOInTLyI ysttQjhHvQkaNthtvcRF3/nI7va/qZfjWTcMZPnv8whDNEXUA+xip5OJ2v0stNbB1kGlkUeQuUx OcJI0uds2mDLTZpGXcOPYOCNutpX8eCzUMGFi9z3oTCF8QhQxfRs9QI9uV0ApIqO9FUsZIAdhG2 Kmzp5QHauxc0nuXi7kddfnCSwPO9b2rR013RDTJA4cu92ljKeaL85sobd0WH5Dc2H0W0HHVTO1K fM4GmOTX+d+pGvQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a method to `block::mq::bio::Segment` to copy a bounded amount of bytes to a page. Signed-off-by: Andreas Hindborg --- rust/kernel/block/bio/vec.rs | 27 ++++++++++++++++++++++++++- rust/kernel/block/mq/request.rs | 3 +-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/rust/kernel/block/bio/vec.rs b/rust/kernel/block/bio/vec.rs index 20cea478050b9..ed1c1374c9c76 100644 --- a/rust/kernel/block/bio/vec.rs +++ b/rust/kernel/block/bio/vec.rs @@ -79,13 +79,38 @@ pub fn advance(&mut self, count: u32) -> Result { /// Returns the number of bytes copied. #[inline(always)] pub fn copy_to_page(&mut self, dst_page: Pin<&mut SafePage>, dst_offse= t: usize) -> usize { + self.copy_to_page_limit(dst_page, dst_offset, 0) + } + + /// Copy data of this segment into `dst_page`. + /// + /// Copies at most `limit` bytes of data from the current offset to th= e next page boundary. That + /// is `PAGE_SIZE - (self.offeset() % PAGE_SIZE)` bytes of data. Data = is placed at offset + /// `self.offset()` in the target page. This call will advance offset = and reduce length of + /// `self`. + /// + /// If `limit` is zero it is ignored. + /// + /// Returns the number of bytes copied. + #[inline(always)] + pub fn copy_to_page_limit( + &mut self, + dst_page: Pin<&mut SafePage>, + dst_offset: usize, + limit: usize, + ) -> usize { // SAFETY: We are not moving out of `dst_page`. let dst_page =3D unsafe { Pin::into_inner_unchecked(dst_page) }; let src_offset =3D self.offset() % PAGE_SIZE; debug_assert!(dst_offset <=3D PAGE_SIZE); - let length =3D (PAGE_SIZE - src_offset) + let mut length =3D (PAGE_SIZE - src_offset) .min(self.len() as usize) .min(PAGE_SIZE - dst_offset); + + if limit > 0 { + length =3D length.min(limit); + } + let page_idx =3D self.offset() / PAGE_SIZE; =20 // SAFETY: self.bio_vec is valid and thus bv_page must be a valid diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index cebb30fe9b3bc..bc655d202ca01 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -7,7 +7,6 @@ use crate::{ bindings, block::mq::Operations, - prelude::*, sync::{ aref::{ARef, RefCounted}, atomic::ordering, @@ -18,7 +17,7 @@ }, types::{ForeignOwnable, Opaque, Ownable, OwnableRefCounted, Owned}, }; -use core::{ffi::c_void, marker::PhantomData, ops::Deref, ptr::NonNull}; +use core::{ffi::c_void, marker::PhantomData, ops::Deref, pin::Pin, ptr::No= nNull}; =20 use super::RequestQueue; use crate::block::bio::Bio; --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 EF2F22D8DD0; Sun, 15 Feb 2026 23:43:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199012; cv=none; b=nArPrvpbe883Aebn6Hc5Q/Pdyg1ZGJtK3gJ3pTjW4yPNsQbVUxJY/t4ndgOMrx5dgUSh8UBIivmLuuy7WmB48PhUVG4SXAt0l426xfCa7+kJXERPzgwr7GmS0dmAzLfDenJ7czLZOt+jOXiaroue4K2RcE+OF8yZ1xcDp3gxcFg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199012; c=relaxed/simple; bh=jfRXWUNk+a84/VFBlikmsBdhxWC79AwOFf4nhCfPiOU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=tNd+Q7OntBYGuFSAbGNqa75O2E75uzPSX7Jz70Uco5tFbKwsaYJVS0faj9k+1ead1ty/SxvDVuMgwh7zIXN+BzRUnW79g/aF4J7fZxh6e2LBek382Wvo9ZkarvMq79LWVqgM7sEngwTgy6fpe+nXFLDCZUbLK5w8iWkU25mh0qk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=npESETd7; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="npESETd7" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 64296C4CEF7; Sun, 15 Feb 2026 23:43:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199011; bh=jfRXWUNk+a84/VFBlikmsBdhxWC79AwOFf4nhCfPiOU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=npESETd7O67DGpYTFb+rL0kbo7BEENUW+9w/BOwekeMkm/SeaN6IXItm7GNjmx7rU T+3+UMgYxtJ6Rc7gv2A+jVkgv6e6hrVbz+2aJIMwNokUqC9WoBl8QES1BFWRNYiZPe sCQ5QCeBYNoiq+3cgbcdrRkavuzL0Yg91OOukbFMeMV/r+0yGvOYYTCV4d32QRLqhg XlvU+Q/ghPOi5IHqTMauyeIjpD6gc39IC0A4SalYLvUKpoDxAshOY/pyVCiVYhLKWo xjV07/Ips5/X4yaVzMRaQ5AXPPClGjxRniU5UqIoU3HPwLow4i6Nvi97fuGNWgHVP3 4VxaEdCNrJt7w== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:46 +0100 Subject: [PATCH 59/79] block: rnull: add fua support 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: <20260216-rnull-v6-19-rc5-send-v1-59-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=9832; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=jfRXWUNk+a84/VFBlikmsBdhxWC79AwOFf4nhCfPiOU=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhQw8ujMRzJ2p7zri55eyF3Vun8A9xlTHqMH W4XsuY97jKJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYUAAKCRDhuBo+eShj dwRcD/96n9UXQln5dGJXotiIswWxCKKyjRXNXiBY86eVdj6gn4Xiy5I01AeeK36jbyoEBeL9QFN flAgUj2rf5gSBGnHBJQQ7J5hfKiTja+/SYdAmNHbBP8PRyPg9/ObY9yw6bNkWQGCKInATEZ65NW arfHYoM7gbAQ18Sf8nGSraizH2JsSShewdCNu/gcepSwAK9WdjfNx3WoIpRysBfFmkVFDZZzo+L 2ShgSOQcbYl8lrZSlLOyLD82C3YV87MRnmUJHYr3JYFLOkd34XJ3vWUlikUF8UnkbUR1040gaXR tkgKJFbJHn4h5Sp28jW4Ee7AD5Av6dkdpG3FuULVw7QzGlFbOVmyiTYM4zNStl931JtvlyxrWny eh0LFTcHlHuzmtcfzZ7Ii3XjbnJ00U0TBwilbM3bLQZUkS5K0WmPJ4cthZcnpPbZJXDOhePWXMz nX+R3tST92NvHt/AFcRLFWVjo0HXZYKhDMtDvPNVReDSEDLA6JivAlYbxeqWELq5xXocWusfDKt 6fLfNGWrW6IYdR6XHP1CXJ5G9z/0ubrFHudCn+i0+YOzLfOLcpE9c2AhpRlsEVXkKEJdyL1Bhq4 D0JonGantAlFjNUmhkILwWwqz3cmiB/hp5YKE2FGwvMwrDOwA+OUNK8xWL7UPZ0AH9iv3TtUrVN saiqiguw56uGiwQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add Forced Unit Access (FUA) support to rnull. When enabled via the `fua` configfs attribute, the driver advertises FUA capability and handles FUA requests by bypassing the volatile cache in the write path. FUA support requires memory backing and write cache to be enabled. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 5 ++++ drivers/block/rnull/disk_storage.rs | 22 +++++++++++++--- drivers/block/rnull/disk_storage/page.rs | 1 + drivers/block/rnull/rnull.rs | 45 ++++++++++++++++++++++++----= ---- 4 files changed, 58 insertions(+), 15 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index e134e21a6b564..816c057f130fc 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -118,6 +118,7 @@ fn make_group( zone_max_active: 25, zone_append_max_sectors: 26, poll_queues: 27, + fua: 28, ], }; =20 @@ -158,6 +159,7 @@ fn make_group( zone_max_active: 0, zone_append_max_sectors: u32::MAX, poll_queues: 0, + fua: true, }), }), core::iter::empty(), @@ -234,6 +236,7 @@ struct DeviceConfigInner { zone_max_active: u32, zone_append_max_sectors: u32, poll_queues: u32, + fua: bool, } =20 #[vtable] @@ -285,6 +288,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { zone_max_active: guard.zone_max_active, zone_append_max_sectors: guard.zone_append_max_sectors, poll_queues: guard.poll_queues, + forced_unit_access: guard.fua, })?); guard.powered =3D true; } else if guard.powered && !power_op { @@ -527,3 +531,4 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { } } ); +configfs_simple_bool_field!(DeviceConfig, 28, fua); diff --git a/drivers/block/rnull/disk_storage.rs b/drivers/block/rnull/disk= _storage.rs index a613ed5223ba7..b2b5eaa783cdc 100644 --- a/drivers/block/rnull/disk_storage.rs +++ b/drivers/block/rnull/disk_storage.rs @@ -92,6 +92,10 @@ pub(crate) fn flush(&self, hw_data: &Pin<&SpinLock>) -> Result { let mut access =3D self.access(&mut tree_guard, &mut hw_data_guard= , None); access.flush() } + + pub(crate) fn cache_enabled(&self) -> bool { + self.cache_size > 0 + } } =20 pub(crate) struct DiskStorageAccess<'a, 'b, 'c> { @@ -190,7 +194,7 @@ fn flush(&mut self) -> Result { Ok(()) } =20 - fn get_cache_page(&mut self, sector: u64) -> Result<&mut NullBlockPage= > { + fn get_or_alloc_cache_page(&mut self, sector: u64) -> Result<&mut Null= BlockPage> { let index =3D Self::to_index(sector); =20 if self.cache_guard.contains_index(index) { @@ -215,6 +219,12 @@ fn get_cache_page(&mut self, sector: u64) -> Result<&m= ut NullBlockPage> { } } =20 + pub(crate) fn get_cache_page(&mut self, sector: u64) -> Option<&mut Nu= llBlockPage> { + let index =3D Self::to_index(sector); + + self.cache_guard.get_mut(index) + } + fn get_disk_page(&mut self, sector: u64) -> Result<&mut NullBlockPage>= { let index =3D Self::to_index(sector); =20 @@ -232,9 +242,13 @@ fn get_disk_page(&mut self, sector: u64) -> Result<&mu= t NullBlockPage> { Ok(page) } =20 - pub(crate) fn get_write_page(&mut self, sector: u64) -> Result<&mut Nu= llBlockPage> { - let page =3D if self.disk_storage.cache_size > 0 { - self.get_cache_page(sector)? + pub(crate) fn get_write_page( + &mut self, + sector: u64, + bypass_cache: bool, + ) -> Result<&mut NullBlockPage> { + let page =3D if self.disk_storage.cache_size > 0 && !bypass_cache { + self.get_or_alloc_cache_page(sector)? } else { self.get_disk_page(sector)? }; diff --git a/drivers/block/rnull/disk_storage/page.rs b/drivers/block/rnull= /disk_storage/page.rs index a34fe0762724d..728073d5dd23d 100644 --- a/drivers/block/rnull/disk_storage/page.rs +++ b/drivers/block/rnull/disk_storage/page.rs @@ -14,6 +14,7 @@ uapi::PAGE_SECTORS, // }; =20 +// TODO: Use rust bitmap const _CHEKC_STATUS_WIDTH: () =3D build_assert!((PAGE_SIZE >> SECTOR_SHIFT= ) <=3D 64); =20 pub(crate) struct NullBlockPage { diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 4870aa3b7a53e..3b7edfe7efe44 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -193,6 +193,10 @@ default: 0, description: "Number of IOPOLL submission queues.", }, + fua: u8 { + default: 1, + description: "Enable/disable FUA support when cache_size is us= ed. Default: 1 (true)", + }, }, } =20 @@ -250,6 +254,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { zone_max_active: *module_parameters::zone_max_active.v= alue(), zone_append_max_sectors: *module_parameters::zone_appe= nd_max_sectors.value(), poll_queues: *module_parameters::poll_queues.value(), + forced_unit_access: *module_parameters::fua.value() != =3D 0, })?; disks.push(disk, GFP_KERNEL)?; } @@ -298,6 +303,7 @@ struct NullBlkOptions<'a> { #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), expect(unused_variables))] zone_append_max_sectors: u32, poll_queues: u32, + forced_unit_access: bool, } =20 static SHARED_TAG_SET: SetOnce>> =3D SetOnce::ne= w(); @@ -356,13 +362,11 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { zone_max_active, zone_append_max_sectors, poll_queues, + forced_unit_access, } =3D options; =20 let mut flags =3D mq::tag_set::Flags::default(); =20 - // TODO: lim.features |=3D BLK_FEAT_WRITE_CACHE; - // if (dev->fua) - // lim.features |=3D BLK_FEAT_FUA; if blocking || memory_backed { flags |=3D mq::tag_set::Flag::Blocking; } @@ -404,9 +408,10 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { =20 let device_capacity_sectors =3D mib_to_sectors(device_capacity_mib= ); =20 + let s =3D storage.clone(); let queue_data =3D Arc::try_pin_init( try_pin_init!(Self { - storage, + storage: s, irq_mode, completion_time, memory_backed, @@ -439,7 +444,9 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { .capacity_sectors(device_capacity_sectors) .logical_block_size(block_size_bytes)? .physical_block_size(block_size_bytes)? - .rotational(rotational); + .rotational(rotational) + .write_cache(storage.cache_enabled()) + .forced_unit_access(forced_unit_access && storage.cache_enable= d()); =20 #[cfg(CONFIG_BLK_DEV_ZONED)] { @@ -496,6 +503,7 @@ fn write<'a, 'b, 'c>( hw_data_guard: &'b mut SpinLockGuard<'c, HwQueueContext>, mut sector: u64, mut segment: Segment<'_>, + bypass_cache: bool, ) -> Result { let mut sheaf: Option> =3D None; =20 @@ -524,12 +532,21 @@ fn write<'a, 'b, 'c>( =20 let mut access =3D self.storage.access(tree_guard, hw_data_gua= rd, sheaf); =20 - let page =3D access.get_write_page(sector)?; + if bypass_cache { + if let Some(page) =3D access.get_cache_page(sector) { + page.set_free(sector); + } + } + + let page =3D access.get_write_page(sector, bypass_cache)?; page.set_occupied(sector); let page_offset =3D (sector & u64::from(block::SECTOR_MASK)) <= < block::SECTOR_SHIFT; =20 - sector +=3D segment.copy_to_page(page.page_mut().get_pin_mut()= , page_offset as usize) - as u64 + sector +=3D segment.copy_to_page_limit( + page.page_mut().get_pin_mut(), + page_offset as usize, + self.block_size_bytes.try_into()?, + ) as u64 >> block::SECTOR_SHIFT; =20 sheaf =3D access.sheaf; @@ -588,6 +605,8 @@ fn transfer( let mut hw_data_guard =3D hw_data.lock(); let mut tree_guard =3D self.storage.lock(); =20 + let skip_cache =3D rq.flags().contains(mq::RequestFlag::ForcedUnit= Access); + for bio in rq.bio_iter_mut() { let segment_iter =3D bio.segment_iter(); for segment in segment_iter { @@ -596,9 +615,13 @@ fn transfer( .len() .min((end_sector - sector) as u32 >> SECTOR_SHIFT); match command { - mq::Command::Write =3D> { - self.write(&mut tree_guard, &mut hw_data_guard, se= ctor, segment)? - } + mq::Command::Write =3D> self.write( + &mut tree_guard, + &mut hw_data_guard, + sector, + segment, + skip_cache, + )?, mq::Command::Read =3D> { self.read(&mut tree_guard, &mut hw_data_guard, sec= tor, segment)? } --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 07861217659; Sun, 15 Feb 2026 23:40:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198845; cv=none; b=gmiuq/3jKlsBi3aQDscGjL7s7nFaHqWuaJ+outi2Q99gxUJaOKJt6n00ON0fjnGw1gM3L0lcWHUDVg7cCC8XpAoeFJTA/5g/wplcaP64oy2B6mjAIuiIok6lZBtfMVTsDhTvo8ht6TAfWGFcUou0wgL9n8IpsjXh3o10fA5/R6Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198845; c=relaxed/simple; bh=PBK7Gy6yOqD93xMD0GKqkyb3it4yG8n9UJvvL1G85v8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=gcG5HKMVDx6FHlIThdVFYOtPWcqSTXci9vOWyl/RP/iORnZKfr2HZz4OLvCxIMCqsKu8yoVtV3GUHnGEtJpk0jmQ0yldHJCaCNUD1Aj2oPWxbcCqJ2TJlsMM6KJ6KWhyg+Dx63jSzKeQUvFdJzhFeX3bL0avSnsqMHoY616fju0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=bxYPJCgE; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="bxYPJCgE" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3D9EFC19422; Sun, 15 Feb 2026 23:40:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198844; bh=PBK7Gy6yOqD93xMD0GKqkyb3it4yG8n9UJvvL1G85v8=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=bxYPJCgEsjiQG6SWt0NwbCdeEoOChcdXDWKWzk9Mn2vlrkVXazcE5sp7XZlRI0B+b XFUCbLfrM1ezrptYH6/psaBuY5loKyUoDtPhVhEcZYwmwBOpDFBDM+2MRNidILppl+ VC0fzKYIgwkljNSdqfAANE0yJ7gp21HICWx0CyJ935vdIsnaotWa7Y/QWoJRRAnNak MY1WeZB7BC106AvJ9+k+xdWrgLkY5n/SEpZP7eCkBgeLXoOk/6qabZpbbkVcJobqHe 2OVFXI6fggXgf6r/bu5p0h0XvgE49CVjosL3Vk/zdP2SzGGKHeAE88IKaLo9Js5QFZ BOk3xbq/GpUtQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:47 +0100 Subject: [PATCH 60/79] block: fix arg type in `blk_mq_update_nr_hw_queues` 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: <20260216-rnull-v6-19-rc5-send-v1-60-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=2537; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=PBK7Gy6yOqD93xMD0GKqkyb3it4yG8n9UJvvL1G85v8=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhRRzjj9sFhsnUHE/Tx66LCHKC5qeomJHUCk Y8jvU2yfAaJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYUQAKCRDhuBo+eShj d+GwEAC1KHf1/pmHLdJDHxI3cCCTlLqWwGumPnUyTFqd3m6XeHO7h86kF21vSosdJrHHb+XDNaz VTv4YxAvrpHaqaNHyZD4vArVYlOcsIJI5Hu284JnIPunXfuqMsSXAhbeDvJgSMxKktW5RlzCyOg yCnx2WhbeRjnv+hjKFH387A0ITVqtytkpT59eXNgagoyGLIa+k01l7K/1/S0+VZ3AM5cQ12qdMY skynG2WSw9hB+Kl+9yFm50dU7Kt/Ab4DtXV6mdjcCq4DkOwWiqJkYbMxu3/FaZrE/jCaBQ09ntV W4mSDcYWA55/eFd/dhWENhnOfGNaBsoRalvJswIVwvZheC1O1AqJeys/Q77bY8PEdaoZEZT7sfA YvVZP6KPs2F43KdFZuM/U4kylbcAlxalbKppHJ+srUQ2MJmk+9tr4Vdw/5H6kO5XZtBvx9cEQAT 96rGiwnga8pPV97JTptFnTZIZmMjUwYUJyZVj/qhZzVMGec6oQjmkKUScUuEOTYFhK5UN173Jn1 msXa41+56KcRX/sDcAIXBzWbmukOeZcVCI+Oh30dbAo5YQYNVUBXSs2wTB+AHqFSFxUajjkDtdf M8fJcZDtfK2c51lYwwrmcyqSRPcuqhYSBF/CkbBh59EXiJr9vL++/DTL/9ffjf7ZxF3HFPzHP9P OQyxV8MpPWhLDmA== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 The type of the argument `nr_hw_queues` in the function `blk_mq_update_nr_hw_queues` is a signed integer. This is wrong, considering the field `nr_hw_queues` of `struct blk_mq_tag_set` is unsigned. Thus, change the type of the parameter to unsigned. Cascade the change to downstream functions. Signed-off-by: Andreas Hindborg --- block/blk-mq.c | 13 +++++++------ include/linux/blk-mq.h | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/block/blk-mq.c b/block/blk-mq.c index a29d8ac9d3e35..6a44ebb6aac43 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -4779,10 +4779,10 @@ static void blk_mq_update_queue_map(struct blk_mq_t= ag_set *set) } =20 static int blk_mq_realloc_tag_set_tags(struct blk_mq_tag_set *set, - int new_nr_hw_queues) + unsigned int new_nr_hw_queues) { struct blk_mq_tags **new_tags; - int i; + unsigned int i; =20 if (set->nr_hw_queues >=3D new_nr_hw_queues) goto done; @@ -5086,12 +5086,12 @@ static int blk_mq_elv_switch_none(struct request_qu= eue *q, } =20 static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, - int nr_hw_queues) + unsigned int nr_hw_queues) { struct request_queue *q; - int prev_nr_hw_queues =3D set->nr_hw_queues; + unsigned int prev_nr_hw_queues =3D set->nr_hw_queues; unsigned int memflags; - int i; + unsigned int i; struct xarray elv_tbl; bool queues_frozen =3D false; =20 @@ -5178,7 +5178,8 @@ static void __blk_mq_update_nr_hw_queues(struct blk_m= q_tag_set *set, __blk_mq_free_map_and_rqs(set, i); } =20 -void blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, int nr_hw_queu= es) +void blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, + unsigned int nr_hw_queues) { down_write(&set->update_nr_hwq_lock); mutex_lock(&set->tag_list_lock); diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index cae9e857aea42..171521e716e49 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -972,7 +972,7 @@ unsigned int blk_mq_num_online_queues(unsigned int max_= queues); void blk_mq_map_queues(struct blk_mq_queue_map *qmap); void blk_mq_map_hw_queues(struct blk_mq_queue_map *qmap, struct device *dev, unsigned int offset); -void blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, int nr_hw_queu= es); +void blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, unsigned int n= r_hw_queues); =20 void blk_mq_quiesce_queue_nowait(struct request_queue *q); =20 --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 8CC762DF137; Sun, 15 Feb 2026 23:45:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199121; cv=none; b=AG5BXMyaEPFiWERRawHslsPjcNrELDTbFq35pobsuKY57FlmMA2dOQx7Zer4fQ+tatGA81YEfZ9mgRC2J1MInvvXKwZiHQMwJG0goPYyPWiyAwaFqKpdI0wqdfSwULSlPO4gMYAIw9ln9vE6szsZjgsYENDELtNUvRaF3VQcHB4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199121; c=relaxed/simple; bh=eIKMKUHsinnjm6IdiHwgx8Cbg+4YOZ96zj8DP30QUB0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=eXc510TgKr+0RzE33lhMl3N5x0OlU8RGr+U/AQPSH4udhgjUc0a1a+pDj0yCwvPGLJeCQ7wSouYEmDhuAcD1Jd3aoO+OhJAzXWSqDMjbq4Z980BTWCF06fCXY0rdI1CgHd34bH+jc/Qh8en1oK8SeXnvT+iv7xy0+upEwJfxahs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Rbc0r5Qr; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Rbc0r5Qr" Received: by smtp.kernel.org (Postfix) with ESMTPSA id BA85AC4CEF7; Sun, 15 Feb 2026 23:45:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199121; bh=eIKMKUHsinnjm6IdiHwgx8Cbg+4YOZ96zj8DP30QUB0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Rbc0r5Qrb7qkrPQNiJhEBZ/acLMM0TcAMN3AMncJp2J+dllypKZuVEnbEuLEdtPqe kAkj28y8tQoORj03TgbhgyHxbtBUgTIg7TnDKh9LvzRQRpPa/kOlStL2AJJJcFlh2G OBYUszJXkIqmN6LV7neooRwL0Oia03CB8TCbPZoSyqIrLVnAL/eY7EHgVrRiIj4jFj cDXes+AKBsTL5FZJTBzNzPntyDprzHoOdcJ9oJcx3u1h7FwlVcfCk5dNe6CKebmC9Y 2IW7RK0jWDli2Qgl6MzZM65u3Uq9OI4AhCNXhfBwPOJ88Y7umYxeJot8XzSHbXVSXN re0k1O/fG1qBQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:48 +0100 Subject: [PATCH 61/79] block: rust: add `GenDisk::tag_set` 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: <20260216-rnull-v6-19-rc5-send-v1-61-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1761; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=eIKMKUHsinnjm6IdiHwgx8Cbg+4YOZ96zj8DP30QUB0=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhSUpM//34bJGuIqeD4kIISe6J2+Xnz/36Oz i3Tue+aVvaJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYUgAKCRDhuBo+eShj d2ACD/9agCA2MGNXTbdM1QI2Fqe0Uv4YftmLwHQmg3ttOb56ej/wO1bKYAhe0GN0qziAWopJau4 o0v2t8yrPv+7WU6DIDSmuRZpgPzPnTRuoGFYQZ6Lh411eX8QrJxB2O7q8ifRdC4mFnbjtwDg2C3 4fPmL3+dZ1q+993ilNfCnY0Z4UlN7HY+SQwoykInhH+N4AE05eaLRn46TUygY45VrAB64SUbfn1 IajI0wymMUHYBZ+HzBY7g3eGoN0VYsdF+3DrIROzEEKGT1H8civF+vq5l5vOERuM/dNgFUfoL2c 5CmmyxBVVkScEaqYEKacICSZyO1E3RYo8K6dway2M/RkKEqDTg1Fx66SAJtm40J6Ey9Aw6ItnVe EEmVReAdxwoOBGi/lmHD/PFLujWLvXcynBCLOzH2RSh7jhwhFUi3GHsYsExziyRzVJUhwG22D8m ld6ezc0wQ4S9YIKtIwU4AGc/KbXTL3kQRQnDff43cWlMCXT7msoIBuOAxCDS4x0QXOUR4/XHONr qbxDBnvXoe84HzyMkXKj7Rl7scESnbv0+LU67zAnYhfYYl4i4QRoTDxUuW17OqbwhbFFEyEkH9c IcqmDeQr/0HbO7kmpejntQgs1BLHmNyIbwFKOYg/soSqoUP/7EWEhaXufcHs+aRrSCpsmseuSe/ Z4FtD6NOI8sKr1w== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a method to `GenDisk` to obtain a reference to the associated `TagSet`. Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq/gen_disk.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_di= sk.rs index c6b9839864012..b5291bc3e051d 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -236,7 +236,7 @@ pub fn build( // `__blk_mq_alloc_disk` above. let mut disk =3D UniqueArc::new( GenDisk { - _tagset: tagset, + tag_set: tagset, gendisk, backref: Arc::pin_init( // INVARIANT: We break `GenDiskRef` invariant here, bu= t we restore it below. @@ -320,7 +320,7 @@ pub(crate) const fn build_vtable() -> &'static bindings= ::block_device_operations /// `bindings::device_add_disk`. /// - `self.gendisk.queue.queuedata` is initialized by a call to `Foreign= Ownable::into_foreign`. pub struct GenDisk { - _tagset: Arc>, + tag_set: Arc>, gendisk: *mut bindings::gendisk, backref: Arc>>, } @@ -342,6 +342,11 @@ pub fn queue_data(&self) -> ::Borrowed<'_> { // SAFETY: By type invariant, self is a valid gendisk. unsafe { T::QueueData::borrow((*(*self.gendisk).queue).queuedata) } } + + /// Get a reference to the `TagSet` used by this `GenDisk`. + pub fn tag_set(&self) -> &Arc> { + &self.tag_set + } } =20 // SAFETY: `GenDisk` is an owned pointer to a `struct gendisk` and an `Arc= ` to a --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 9ED2C30C616; Sun, 15 Feb 2026 23:42:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198968; cv=none; b=sQCgFkzExPopZClOh5bASTAASaKsWrno6qV2GfKBjSFOWt/x3NDG+KMBLWvqyljuWXUEYjY7b5Cmh0cIipj/4/2lMv1boaCJW5/d92Lzbi1XT9NTG8As7TuHnAWc4P2acQaQjq/1ZquvbmIQLXBozLgRYbvKeKXN/08H23fC8Bs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198968; c=relaxed/simple; bh=PclSPlh70GQI0xa5AdeesgMZY73x5FcK7yieZwHyBNg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=i/j04Jlz4xeXPaqskGBN7liWyk47eECxYrWHf0l7CVq/JKc4F6nsCS7GVt8MUCjg7FKY4DbogwtZYRIt7CFlaDV0/z74yl+q5h9ovvQXl2Gv/rd8INEGnyRjQOrlExusqI0KiwLCkNaICqYS+D1ofS7H0K1OR603BUK6IJOly3w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=m+DLwcOZ; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="m+DLwcOZ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1969AC4CEF7; Sun, 15 Feb 2026 23:42:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198968; bh=PclSPlh70GQI0xa5AdeesgMZY73x5FcK7yieZwHyBNg=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=m+DLwcOZ7ztebda4HD5zmzGEd1dxKhN6VIv3Ey90e3U/WBlrx+pf3F2fWIFCLcI5T hldQpB0o8zQTbtheSVXM9rCm2qhYMCI3ogaf2Vx6ppCCM1auAwzwFbNMXljA/fcjqa r3eJu6VrhR+KV7HrXN+OL7YLt+3JPM9t2+oxfpN4jHjTi2uiB8X9ZxE6E8cQblVTds g1AElKe+UUux/V322EArMeHghqO1p3enbonHlviYx7wy63c4QPvX/BYwiZ25ebUMo+ MEbYCOPXyn99/a/fSfZ576dim1A0Oi4TVU3bwG2sQVEg0dsIX2xi4bzSOKmEO3r2Fd GxQsSfgDTR3Tw== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:49 +0100 Subject: [PATCH 62/79] block: rust: add `TagSet::update_hw_queue_count` 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: <20260216-rnull-v6-19-rc5-send-v1-62-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1846; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=PclSPlh70GQI0xa5AdeesgMZY73x5FcK7yieZwHyBNg=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhTMo0te1+0hDGk5iS523JU63TJIgZV6pKQb P0p32p+ORCJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYUwAKCRDhuBo+eShj dz6IEACT5CHYFXwgGMGhVFOsIzIVze2XvGdf11AwPQLHKXovdeWBUGKebfuN/DcQ3OPK9AglXZ5 tm5GgORCPdgssHdGNlfcsNRLHfn6Ln5QKM6AK4Sc7tkaVKu5LYGGWSHcn9GhvVnOx/lpaLszWlN 6mCEKAauIk666pbJOzJ6mlmJSRl04x9sYQfvDzgb9w0tnIihphmkYYMZPWJA31UaUi7v8p4oyMX /pnpFmXu8UwgvIe33s3MB8zKNKeSgG6ZP5uMZV2Qi5iLXqy7Z6+fYFwzqkXLe7OF5ZeC/tTLO+f Xv0AepFCGgEPexiO1cb88/4C3m7gS41joiA8nVtFNYu6d1Su9ckl261r5kyHFZEEEk1jz7W5wa7 YH06lBIytM3mPDDcO010YYmA18RhFXWNv28PxkWHDyobexbUnRr3cVtXGFzODBugO8q2viypqkW cQfJdKYov6juRy+vygspWrsJVhnHaixjGBHrbwA5CVXa5/EMgKj4MDMFibd4KPk8a6Q4b1i12ND La3cQNmfMFAkkWCbMNM2MOd/AuFcgm9CjR3R5NSjKsJ3VMBzsPUiECbEizBCbI8MxBGW5PEcj/n 3bTLvOCGMKucYI/wM4mwXs2D18IMIJ+OhANs+L5A2aZ2HqmA9JcY9ST+ivVR1CR7odYpu55i7r/ xvoD8xIrqD7yQng== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a method to `TagSet` that allows changing the number of hardware queues dynamically. Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq/tag_set.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set= .rs index 4d00979a83dbd..47a162360e628 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -8,6 +8,7 @@ bindings, block::mq::{operations::OperationsVTable, request::RequestDataWrapper,= Operations}, error::{self, Result}, + prelude::*, try_pin_init, types::{ForeignOwnable, Opaque}, }; @@ -34,7 +35,7 @@ pub struct TagSet { } =20 impl TagSet { - /// Try to create a new tag set + /// Try to create a new tag se }t pub fn new( nr_hw_queues: u32, tagset_data: T::TagSetData, @@ -142,6 +143,20 @@ pub fn hw_queue_count(&self) -> u32 { unsafe { (*self.inner.get()).nr_hw_queues } } =20 + /// Update the number of hardware queues for this tag set. + /// + /// This operation may fail if memory for tags cannot be allocated. + pub fn update_hw_queue_count(&self, nr_hw_queues: u32) -> Result { + // SAFETY: blk_mq_update_nr_hw_queues applies internal synchroniza= tion. + unsafe { bindings::blk_mq_update_nr_hw_queues(self.inner.get(), nr= _hw_queues) } + + if self.hw_queue_count() =3D=3D nr_hw_queues { + Ok(()) + } else { + Err(ENOMEM) + } + } + /// Borrow the [`T::TagSetData`] associated with this tag set. pub fn data(&self) -> ::Borrowed<'_> { // SAFETY: By type invariant, `self.inner` is valid. --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 4CC452D739C; Sun, 15 Feb 2026 23:43:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198997; cv=none; b=debj8RkHXyIbUumgvYVxpLGUFa9duKBq/7NuyGcYiaS2pnL/EyZOob0ZYgsZ+h9oH6ojZjcebBD9j60V1MgcPVzz/vZxSSAYyvWXaE/1cG/u333AArcaJLkYmLi+ywDEXS8bbFyozFXTxxEpVm43lmr6DR6VCBdcX2o6y82hfSQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198997; c=relaxed/simple; bh=xGoluFA0is+PW3oIdECTL4BJYJwKP74nRrbzYRfRoWk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=WXNCFGEPpKuTCjiNtJtW+8rSa7xQfex5q0lC/GnxVzEB/b5frc+UZ2nHTue4qMZd0u7JLwL9eXExObx7cBH3Mtezewx7AmigRayn3+f2q9RDtIxJqoGzP/MSoOky7wrtTwSzeSI2vrbqKvx7adnqFODiyKkCEPo9CZG4SuF4FqI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=BI8OtOGP; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="BI8OtOGP" Received: by smtp.kernel.org (Postfix) with ESMTPSA id CA917C4CEF7; Sun, 15 Feb 2026 23:43:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198997; bh=xGoluFA0is+PW3oIdECTL4BJYJwKP74nRrbzYRfRoWk=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=BI8OtOGPWqfX79yRtNzdumXoN1AkPyhoQFxEqPqShE+LORe81VTcXuWju2k2C/TGc 94v3a6l3t7lLWBNdQxuWaRBl6RVAUHBUH1bX++VxWfyuwie9I3bXbP5qgGY8dh2MYc 49s9qkf0lAhzL3BvfGtWH5RE47w84fK1slJ2qS4fbwGLGzdjEc+jGRxRCeP3/jAMUL c1ulLTpr1ab0M7P2DDgpIWlwFDTTBtgWknf92yyle2WFmCzKGMN7ZIlmbS45Jof4d5 mDpWfzkUryuMtnHvJNJ3onRUJW5yqrkitIJ5AJJgPmN2rNqtmUwhMyL7VMntrRCUSo P9CcmdB0E/n+w== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:50 +0100 Subject: [PATCH 63/79] block: rnull: add an option to change the number of hardware queues 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: <20260216-rnull-v6-19-rc5-send-v1-63-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=13710; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=xGoluFA0is+PW3oIdECTL4BJYJwKP74nRrbzYRfRoWk=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhUWEuv7HdW35YDvuGKYE8l1tBjini+J5IAD ksxuawWzxSJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYVAAKCRDhuBo+eShj dwJOEACuhdjHz9OU2pi8f1XJnCz/BujfUae1C49VE34im+iJY74WEEzIyRJePG39SUY/yHjTWDp PRr0Q83+P+XTjDa9VbQpkOucxGVO2Y2ARa6RzF3mAjBvllIYhERY2DwDdaJSmQt0L7Iy45oBYOO Ap96VkzwyGT20pY02BY0DyVLSGlhOjzmSNOfey8zgmkveFxUt0hpi8FvqUyJ9Shs+uNjJxl+29Z itvamOKnpI6rmXyKSFhkjnui6uBRLUw+skWyWENJC6AzRVah2ECXnOqI9uzMs7Wt3BXBpic1Imo 4RTe2ijuixMMxo1wMkN/8SVSzkPGyPwy4zawZ8uCAaG/ZFWBsxoRY9KhWaCWpDmIJr3CS1C1gZX dGwHFMjqh5SX8gEg8xURdJ0+ilSkB66ufkfTMXqZ3vUeR7XhpwQFvx0aXLAgb3KohixMZbsAj7O XjDnfLxwSTJ5KZzVMErIzPNOnGrj+ITRhPtmryIJBni/y9KQ7v9ky8GK17Ga37uEr0jXZhL1ipY kMWKlAdHEZI09rMuIQUGG8XZajrNMJF+TcKIBxglM4XnoPVZxZlEfiMLfy2YsoHr1+2TOgpF+bn gTs1f68yrSF6R52qK5NQ9uirdGAVJfV4YI6StnyeWblfCT4eCX+UiZAtnDfumWpNwklDuILj82b Kz5JtyFk0YPe2ew== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a feature to rnull that allows changing the number of simulated hardware queues during device operation. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 143 +++++++++++++++++++++++++-----------= ---- drivers/block/rnull/rnull.rs | 39 +++++++---- 2 files changed, 116 insertions(+), 66 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 816c057f130fc..424722f01ab8d 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -138,7 +138,13 @@ fn make_group( completion_time: time::Delta::ZERO, name: name.try_into()?, memory_backed: false, - submit_queues: 1, + queue_config: Arc::pin_init( + new_mutex!(QueueConfig { + submit_queues: 1, + poll_queues: 0 + }), + GFP_KERNEL + )?, home_node: bindings::NUMA_NO_NODE, discard: false, no_sched: false, @@ -158,7 +164,6 @@ fn make_group( zone_max_open: 0, zone_max_active: 0, zone_append_max_sectors: u32::MAX, - poll_queues: 0, fua: true, }), }), @@ -215,7 +220,7 @@ struct DeviceConfigInner { completion_time: time::Delta, disk: Option>>, memory_backed: bool, - submit_queues: u32, + queue_config: Arc>, home_node: i32, discard: bool, no_sched: bool, @@ -235,7 +240,6 @@ struct DeviceConfigInner { zone_max_open: u32, zone_max_active: u32, zone_append_max_sectors: u32, - poll_queues: u32, fua: bool, } =20 @@ -268,7 +272,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { irq_mode: guard.irq_mode, completion_time: guard.completion_time, memory_backed: guard.memory_backed, - submit_queues: guard.submit_queues, + queue_config: guard.queue_config.clone(), home_node: guard.home_node, discard: guard.discard, no_sched: guard.no_sched, @@ -287,7 +291,6 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { zone_max_open: guard.zone_max_open, zone_max_active: guard.zone_max_active, zone_append_max_sectors: guard.zone_append_max_sectors, - poll_queues: guard.poll_queues, forced_unit_access: guard.fua, })?); guard.powered =3D true; @@ -300,9 +303,17 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { } } =20 -configfs_simple_field!(DeviceConfig, 1, - block_size, u32, - check GenDiskBuilder::::validate_blo= ck_size +pub(crate) struct QueueConfig { + pub(crate) submit_queues: u32, + pub(crate) poll_queues: u32, +} + +configfs_simple_field!( + DeviceConfig, + 1, + block_size, + u32, + check GenDiskBuilder::::validate_block_size ); configfs_simple_bool_field!(DeviceConfig, 2, rotational); configfs_simple_field!(DeviceConfig, 3, capacity_mib, u64); @@ -349,51 +360,57 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { } } =20 -#[vtable] -impl configfs::AttributeOperations<7> for DeviceConfig { - type Data =3D DeviceConfig; - - fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result { - let mut writer =3D kernel::str::Formatter::new(page); - writer.write_fmt(fmt!("{}\n", this.data.lock().submit_queues))?; - Ok(writer.bytes_written()) - } +configfs_attribute! { + DeviceConfig, + 7, + show: |this, page| show_field(this.data.lock().queue_config.lock().sub= mit_queues, page), + store: |this,page| { + let config_guard =3D this.data.lock(); + let mut queue_config =3D config_guard.queue_config.lock(); =20 - fn store(this: &DeviceConfig, page: &[u8]) -> Result { - if this.data.lock().powered { - return Err(EBUSY); + let text =3D core::str::from_utf8(page)?.trim(); + let value =3D text.parse().map_err(|_| EINVAL)?; + if value > kernel::num_possible_cpus() { + return Err(kernel::error::code::EINVAL) } =20 - let text =3D core::str::from_utf8(page)?.trim(); - let value =3D text - .parse::() - .map_err(|_| kernel::error::code::EINVAL)?; + let old_submit_queues =3D queue_config.submit_queues; + queue_config.submit_queues =3D value; + let total_queue_count =3D queue_config.submit_queues + queue_confi= g.poll_queues; + + let disk =3D config_guard.disk.clone(); + + drop(queue_config); + drop(config_guard); =20 - if value =3D=3D 0 || value > kernel::num_possible_cpus() { - return Err(kernel::error::code::EINVAL); + if let Some(disk) =3D &disk { + if let Err(e) =3D disk.tag_set().update_hw_queue_count(total_q= ueue_count) { + this.data.lock().queue_config.lock().submit_queues =3D old= _submit_queues; + return Err(e); + } } =20 - this.data.lock().submit_queues =3D value; Ok(()) - } + }, } =20 configfs_attribute!(DeviceConfig, 8, - show: |this, page| show_field( - this.data.lock().submit_queues =3D=3D kernel::num_online_nodes(), = page - ), - store: |this, page| store_with_power_check(this, page, |this, page| { - let value =3D core::str::from_utf8(page)? - .trim() - .parse::() - .map_err(|_| kernel::error::code::EINVAL)? - !=3D 0; - - if value { - this.data.lock().submit_queues *=3D kernel::num_online_nodes(); - } - Ok(()) - }) + show: |this, page| show_field( + this.data.lock().queue_config.lock().submit_queues =3D=3D = kernel::num_online_nodes(), + page + ), + store: |this, page| store_with_power_check(this, page, |this, = page| { + let value =3D core::str::from_utf8(page)? + .trim() + .parse::() + .map_err(|_| kernel::error::code::EINVAL)? + !=3D 0; + + if value { + this.data.lock().queue_config.lock().submit_queues *=3D kernel::nu= m_online_nodes(); + } + Ok(()) + }) ); =20 configfs_simple_field!( @@ -518,17 +535,37 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { configfs_simple_field!(DeviceConfig, 24, zone_max_open, u32); configfs_simple_field!(DeviceConfig, 25, zone_max_active, u32); configfs_simple_field!(DeviceConfig, 26, zone_append_max_sectors, u32); -configfs_simple_field!( +configfs_attribute! { DeviceConfig, 27, - poll_queues, - u32, - check | value | { + show: |this, page| show_field(this.data.lock().queue_config.lock().pol= l_queues, page), + store: |this,page| { + let config_guard =3D this.data.lock(); + let mut queue_config =3D config_guard.queue_config.lock(); + + let text =3D core::str::from_utf8(page)?.trim(); + let value =3D text.parse().map_err(|_| EINVAL)?; if value > kernel::num_possible_cpus() { - Err(kernel::error::code::EINVAL) - } else { - Ok(()) + return Err(kernel::error::code::EINVAL) } - } -); + + let old_poll_queues =3D queue_config.poll_queues; + queue_config.poll_queues =3D value; + let total_queue_count =3D queue_config.submit_queues + queue_confi= g.poll_queues; + + let disk =3D config_guard.disk.clone(); + + drop(queue_config); + drop(config_guard); + + if let Some(disk) =3D &disk { + if let Err(e) =3D disk.tag_set().update_hw_queue_count(total_q= ueue_count) { + this.data.lock().queue_config.lock().poll_queues =3D old_p= oll_queues; + return Err(e); + } + } + + Ok(()) + }, +} configfs_simple_bool_field!(DeviceConfig, 28, fua); diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 3b7edfe7efe44..429819bf042ba 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -10,7 +10,10 @@ #[cfg(CONFIG_BLK_DEV_ZONED)] mod zoned; =20 -use configfs::IRQMode; +use configfs::{ + IRQMode, + QueueConfig, // +}; use disk_storage::{ DiskStorage, NullBlockPage, @@ -225,6 +228,8 @@ fn init(_module: &'static ThisModule) -> impl PinInit { *module_parameters::submit_queues.value() }; =20 + let poll_queues =3D *module_parameters::poll_queues.value(= ); + let block_size =3D *module_parameters::bs.value(); let disk =3D NullBlkDevice::new(NullBlkOptions { name: &name, @@ -234,7 +239,13 @@ fn init(_module: &'static ThisModule) -> impl PinInit<= Self, Error> { irq_mode: (*module_parameters::irqmode.value()).try_in= to()?, completion_time: Delta::from_nanos(completion_time), memory_backed: *module_parameters::memory_backed.value= () !=3D 0, - submit_queues, + queue_config: Arc::pin_init( + new_mutex!(QueueConfig { + submit_queues, + poll_queues + }), + GFP_KERNEL, + )?, home_node: *module_parameters::home_node.value(), discard: *module_parameters::discard.value() !=3D 0, no_sched: *module_parameters::no_sched.value() !=3D 0, @@ -253,7 +264,6 @@ fn init(_module: &'static ThisModule) -> impl PinInit { zone_max_open: *module_parameters::zone_max_open.value= (), zone_max_active: *module_parameters::zone_max_active.v= alue(), zone_append_max_sectors: *module_parameters::zone_appe= nd_max_sectors.value(), - poll_queues: *module_parameters::poll_queues.value(), forced_unit_access: *module_parameters::fua.value() != =3D 0, })?; disks.push(disk, GFP_KERNEL)?; @@ -277,7 +287,7 @@ struct NullBlkOptions<'a> { irq_mode: IRQMode, completion_time: Delta, memory_backed: bool, - submit_queues: u32, + queue_config: Arc>, home_node: i32, discard: bool, no_sched: bool, @@ -302,7 +312,6 @@ struct NullBlkOptions<'a> { zone_max_active: u32, #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), expect(unused_variables))] zone_append_max_sectors: u32, - poll_queues: u32, forced_unit_access: bool, } =20 @@ -342,7 +351,7 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { irq_mode, completion_time, memory_backed, - submit_queues, + queue_config, home_node, discard, no_sched, @@ -361,7 +370,6 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { zone_max_open, zone_max_active, zone_append_max_sectors, - poll_queues, forced_unit_access, } =3D options; =20 @@ -379,6 +387,11 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { return Err(code::EINVAL); } =20 + let queue_config_guard =3D queue_config.lock(); + let submit_queues =3D queue_config_guard.submit_queues; + let poll_queues =3D queue_config_guard.poll_queues; + drop(queue_config_guard); + let tagset_ctor =3D || -> Result> { Arc::pin_init( TagSet::new( @@ -386,8 +399,7 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { KBox::new( NullBlkTagsetData { queue_depth: hw_queue_depth, - submit_queue_count: submit_queues, - poll_queue_count: poll_queues, + queue_config, }, GFP_KERNEL, )?, @@ -773,8 +785,7 @@ impl HasHrTimer for Pdu { =20 struct NullBlkTagsetData { queue_depth: u32, - submit_queue_count: u32, - poll_queue_count: u32, + queue_config: Arc>, } =20 #[vtable] @@ -919,8 +930,10 @@ fn report_zones( } =20 fn map_queues(tag_set: Pin<&mut TagSet>) { - let mut submit_queue_count =3D tag_set.data().submit_queue_count; - let mut poll_queue_count =3D tag_set.data().poll_queue_count; + let queue_config =3D tag_set.data().queue_config.lock(); + let mut submit_queue_count =3D queue_config.submit_queues; + let mut poll_queue_count =3D queue_config.poll_queues; + drop(queue_config); =20 if tag_set.hw_queue_count() !=3D submit_queue_count + poll_queue_c= ount { pr_warn!( --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 BB5912DE6FF; Sun, 15 Feb 2026 23:45:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199140; cv=none; b=YacP+ZvUU7gSUuFH87wojQgaOGdfiEwZYQReU0g9ugNzlhIUrUxNBGhfvtyg6JZnnkdoDFZKVvgkkO3lijJCCsno13btq3lcRGeN1w5bWJPXVDd7Rv68dR74Y7u8xdMu3DqZrF5u6KMzvD0qPi1aUETX04fVzsQyU7S/EzkCSVE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199140; c=relaxed/simple; bh=iHJebs10G3KEuSKPd64YYQzEozbVohCdh4WUvdT+gdA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=us5GFfLItM3NjuyzRq1UrzkoGEoIyD/z2zJYY7fIK/JTehxk5043IO6/UVOq1RopiKd3T+ZY4l6aI1jBF5tEWxOMLbmqzEdixtv3cyG7+r7GDLqc2rkBIhyppKy8ZkM6vwsqgV/rEWgBb4Ar5xcCVs3hM+XZubU31KXeCw+ssjI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=OWNqkVSp; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="OWNqkVSp" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6497AC4CEF7; Sun, 15 Feb 2026 23:45:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199140; bh=iHJebs10G3KEuSKPd64YYQzEozbVohCdh4WUvdT+gdA=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=OWNqkVSpuHPfeqJ1obG9KjUAge7I1l0KH3Bd4vcO7D26r2YjI5dQDpC664biAVsof LFGcoc+uoSB3f4UWDWN86wMaA+4RLjrMK04KxYVHi3HbR/g1M2+q6dJsxzwsAmUXf0 MkdCMQfCPZCXbesYKg53FrD3gFepV53oCsnpLSVUlJj1CppafFHN3uj7GrdOBt7HWz gnz5pKgtMyrqfBnP12nE/1Iul/ci552vsJvYYQAyjIFfwPXI2JLXzPhnxLqcng+qQB mvptHf9cmJkdD+nYS69XoZtNMyqtb+EQ73fqh0HFSZdctdRHsTaTiX549NW1gItkDz A8JUVDBoPx4Rg== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:51 +0100 Subject: [PATCH 64/79] block: rust: add an abstraction for `struct rq_list` 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: <20260216-rnull-v6-19-rc5-send-v1-64-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=6135; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=iHJebs10G3KEuSKPd64YYQzEozbVohCdh4WUvdT+gdA=; b=kA0DAAoB4bgaPnkoY3cByyZiAGmSWFWjeowTsFEiINSqulAD21O5tLcZBRTLEu7+cvl0taWOq YkCMwQAAQoAHRYhBBLB+UdWv3wqFdbAEuG4Gj55KGN3BQJpklhVAAoJEOG4Gj55KGN3uDQQAK5S TJnyt6251s9+e3u70mSgIM2EB4xbkhZPSh2nxTo3lEp8w82guHRW0t/howvgJ3nj9a6w4XCwEb+ AV1gxbGx40380Hc0c3wU9N+z4qG5PXCNGGOHSd+OEnl5gVXZnZZtbbVSveZ79khqvzcTJ3gz7d0 7UIWol335/lElOkzzPm/gSQKxUSKOcIt2H+VozTRfqpok96aJx89/jI9g7tI4goDwEh00tbcchK PpHQ0APkpbh09Kb81cZ0I/SXBMWttrK3oF3P8eio0mjOLmq7Eh2Dhvs1kB1OZd3LbsUHXb0xDCa WJEanZ2sRkFI8QAVA5/6loJIUgo1joWAaMu2oUIbvIp5FAlKy2p/crJIYU+hdKE7pm8O+VNmqps rW7N1XxuiUFlFp2SQwlKbxmnshbAz7OvnLl1LvvYfdfv7BQMCIzjBRfdExGQl28u6kCiN2o8YDS dDkxJ+d/dydjryyzj7Mr444ULi3Cl73k+OAm09roYSf9gxMKYBkO2gExZsIg4B0WqdUA5GUkYXi cJn5wgLNpae6d8gxQ7y8KXFpa87YWXO2bxdWn3efgB/cERasAm6EG5izcBgz/WEFLSksxZJsS79 0TL1iD3sJOhx1/dvbZBCVV3oHa/xp6OnRG/JFWt31mFFA0+/9upKGkRAW0okHMgaq2tWDEcgTy1 Cv3+C X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add the `RequestList` type as a safe wrapper around the C `struct rq_list`. This type provides methods to iterate over and manipulate lists of block requests, which is needed for implementing the `queue_rqs` callback. The abstraction includes methods for popping requests from the list, checking if the list is empty, and peeking at the head request. Signed-off-by: Andreas Hindborg --- rust/helpers/blk.c | 26 ++++++++ rust/kernel/block/mq.rs | 2 + rust/kernel/block/mq/request_list.rs | 113 +++++++++++++++++++++++++++++++= ++++ 3 files changed, 141 insertions(+) diff --git a/rust/helpers/blk.c b/rust/helpers/blk.c index 91b72131a04cd..0db567dfc5d53 100644 --- a/rust/helpers/blk.c +++ b/rust/helpers/blk.c @@ -27,3 +27,29 @@ bool rust_helper_blk_mq_add_to_batch(struct request *req, { return blk_mq_add_to_batch(req, iob, is_error, complete); } + +__rust_helper struct request *rust_helper_rq_list_pop(struct rq_list *rl) +{ + return rq_list_pop(rl); +} + +__rust_helper int rust_helper_rq_list_empty(const struct rq_list *rl) +{ + return rq_list_empty(rl); +} + +__rust_helper void rust_helper_rq_list_add_tail(struct rq_list *rl, + struct request *rq) +{ + rq_list_add_tail(rl, rq); +} + +__rust_helper void rust_helper_rq_list_init(struct rq_list *rl) +{ + rq_list_init(rl); +} + +__rust_helper struct request *rust_helper_rq_list_peek(struct rq_list *rl) +{ + return rq_list_peek(rl); +} diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index b36112f7b22b9..786c32f2cb56c 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -129,6 +129,7 @@ pub mod gen_disk; mod operations; mod request; +mod request_list; mod request_queue; pub mod tag_set; =20 @@ -141,6 +142,7 @@ pub use request::IdleRequest; pub use request::Request; pub use request::RequestTimerHandle; +pub use request_list::RequestList; pub use request_queue::RequestQueue; pub use tag_set::QueueType; pub use tag_set::TagSet; diff --git a/rust/kernel/block/mq/request_list.rs b/rust/kernel/block/mq/re= quest_list.rs new file mode 100644 index 0000000000000..60284d9ef6c9c --- /dev/null +++ b/rust/kernel/block/mq/request_list.rs @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0 + +use core::marker::PhantomData; + +use crate::{owned::Owned, types::Opaque}; + +use super::{IdleRequest, Operations}; + +/// A list of [`Request`]. +/// +/// # INVARIANTS +/// +/// - `self.inner` is always a valid list, meaning the `next` and `prev` +/// pointers point to valid requests, or are both null. +/// - All requests in the list are valid for use as `IdleRequest`. +#[repr(transparent)] +pub struct RequestList { + inner: Opaque, + _p: PhantomData, +} + +impl RequestList { + /// Create a new [`RequestList`]. + pub fn new() -> Self { + let this =3D Self { + inner: Opaque::zeroed(), + _p: PhantomData, + }; + + // NOTE: We are actually good to go, but we call the C initializer= for forward + // compatibility. + // SAFETY: `this.inner` is a valid allocation for use as `bindings= ::rq_list!. + unsafe { bindings::rq_list_init(this.inner.get()) } + + //INVARIANT: `self.inner` was initialized above and is empty. + this + } + + /// Create a mutable reference to a [`RequestList`] from a raw pointer. + /// + /// # SAFETY + /// - The list pointed to by `ptr` must satisfy the invariants of `Sel= f`. + /// - The list pointed to by `ptr` must remain valid for use as a muta= ble reference for the + /// duration of `'a`. + pub unsafe fn from_raw<'a>(ptr: *mut bindings::rq_list) -> &'a mut Sel= f { + // SAFETY: + // - RequestList is transparent. + // - By function safety requirements, `ptr` is valid for us as a m= utable reference. + unsafe { &mut (*ptr.cast()) } + } + + /// Check if the list is empty. + pub fn empty(&self) -> bool { + // SAFETY: By type invariant, self.inner is valid. + let ret =3D unsafe { bindings::rq_list_empty(self.inner.get()) }; + ret !=3D 0 + } + + /// Pop a request from the list. + /// + /// Returns [`None`] if the list is empty. + pub fn pop(&mut self) -> Option>> { + // SAFETY: By type invariant `self.inner` is a valid list. + let ptr =3D unsafe { bindings::rq_list_pop(self.inner.get()) }; + + if !ptr.is_null() { + // SAFETY: If `rq_list_pop` returns a non-null pointer, it poi= nts to a valid request. By + // type invariant all requests in this list are valid for use = as `IdleRequest`. + Some(unsafe { IdleRequest::from_raw(ptr) }) + } else { + None + } + } + + /// Push a request on the tail of the list. + pub fn push_tail(&mut self, rq: Owned>) { + let ptr =3D rq.as_raw(); + core::mem::forget(rq); + // INVARIANT: rq is an `IdleRequest`. + // SAFETY: By type invariant, `self.inner` is a valid list. + unsafe { bindings::rq_list_add_tail(self.inner.get(), ptr) }; + } + + /// Peek at the head of the list. + /// + /// Returns a null pointer if the list is empty. + pub fn peek_raw(&self) -> *mut bindings::request { + // SAFETY: By type invariant, `self.inner` is a valid list. + unsafe { bindings::rq_list_peek(self.inner.get()) } + } +} + +impl Default for RequestList { + fn default() -> Self { + Self::new() + } +} + +impl Drop for RequestList { + fn drop(&mut self) { + while let Some(rq) =3D self.pop() { + drop(rq) + } + } +} + +impl Iterator for &mut RequestList { + type Item =3D Owned>; + + fn next(&mut self) -> Option { + self.pop() + } +} --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 EF65A30DED4; Sun, 15 Feb 2026 23:44:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199062; cv=none; b=lEsS06errte+OM1Z9GDokAWV5Ua2d403MQkRSGnJoIM5bOR4o+Byp+RXdX+hKRr1lhRzczE0lH0hW526KKn2laQJrs3uY97+1spAsu/m0w+gZjZqTmdwoOeOMqvTwNrbrLzC50mKQxsE0MUIO76kh+HpcBnFWIcSAfTNLKjVEYw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199062; c=relaxed/simple; bh=kxfk0NxivP7xMNYmKs13HrD3bNE4wuOWBn6+zseKLgk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=sM31WfP+dte0w3F9kyJg1hfLc3DSMsWreqx8guJ0P2GHwm+iF0VtrF4nOs8tbll2xVVKkpwlSSBrFIhCThqxdMYZUWDtjv7hhfTAxfdSJRFj1eDwo5IrQLyUooWN5OmnA3Te3H7AWNb4rG1ONxGrZ/Yxk8hDKRObPQbPc8/2w8M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=FljKbIJu; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="FljKbIJu" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 40614C4CEF7; Sun, 15 Feb 2026 23:44:17 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199061; bh=kxfk0NxivP7xMNYmKs13HrD3bNE4wuOWBn6+zseKLgk=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=FljKbIJuZZV66echQHbgF+iOvpuxT26U4pYgG5yJje98VOFVcvJuhy02vW5XcPTVA bxYXKtNYmHU32oYNfnDOb2EbvpaNaYB5599R+2Jnhfiu16c0NlSkEYER/ncbTcT2B8 fSCYG4AQY4BSXZ0pI9TQzqd7P8SwHOBzkyRw4oXcWDJf3g+g8AX+seX8T9GxtPyZ8I GQTNEXG/zPo90Q9PhF/jSEQyzD4ZQv47uPvGWog8R22euuESIr3z6iOuSWK/Kn5enU u0i12EoV6H1O1hWYHXBioxGfVfKA1YZh2JxUMIkkkdiWn2qSTdN2TaogEfxqJKe1oO KOppbs4tPn9+Q== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:52 +0100 Subject: [PATCH 65/79] block: rust: add `queue_rqs` vtable hook 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: <20260216-rnull-v6-19-rc5-send-v1-65-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=5925; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=kxfk0NxivP7xMNYmKs13HrD3bNE4wuOWBn6+zseKLgk=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhWkfykeGrJtn+Xnt679z12Wv0nstxNgY43F EyZrVwsMdaJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYVgAKCRDhuBo+eShj dxR9D/0b80WZwQzfSaWye9AkT8IHEguI8Qx2vaI9c49aW3ONnRUoCG99C7oEbKQC1dE0BU87+o2 05pBhsiaG0aSEKewssne26p6QXeZkluBdkjIOZqUb0Ul+gaWrY74tiSv+jdv3hO3nWzcvZhISdV i3CaMJDevyGKBbjJNJxJOyHQasm5ftuNatcq1B04oFLV6RSbyeMRkUaqQIlcqOCArM0CPRhCpnK BDgRy8EekJyo+YmhBMT+9S0wIojLfRXKn7O3+2VHXK46if+TUT1w+oQhXMf9XvC9GiHujgEecBZ 85tLpgJ+aJS5e1cbOS6XTpmjXRIoJ5XnKWIpN7SlhaeghSqt9CM3cU1EL2RG3x4ZIQ6XXbtAML+ Gvj5fj/c57My8iy4WkMlhV07XgrnpYKLHK1QJ+iMW0QgzV8yyUf+rIQb7//5G0yjLDh9AznYqn2 Vux4Ysb8OK0gYe/7TIDRZt8KxoL0cts/hsCN7nJh+MVwIPnniT6+Uvhdb7i4h+ut2te9w/K+pYA SGqW7821xeTiYqtrc+zsI8zOtiHfG9N93Z3i3TtbdFCOcSJFReUy3nAUl0VzlieSejw3RKDCJb/ PH0ZLgEh4U0jm8RJhhkmycDkZ13VuCv9zvKlz1g/4fvfzTZF1IxeP7IFcOGmsD62fMV7l9hJKfs iFCzlr4gnyB3iKg== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add support for the `queue_rqs` callback to the Rust block layer bindings. This callback allows drivers to receive multiple requests in a single call, enabling batch processing optimizations. The callback receives a `RequestList` containing the requests to be processed. Drivers should remove successfully processed requests from the list; any remaining requests will be requeued individually. Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq/operations.rs | 61 ++++++++++++++++++++++++++++++++++= +++- rust/kernel/block/mq/request.rs | 26 ++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/oper= ations.rs index bccc1903a0d10..e4de6db807e18 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -18,6 +18,8 @@ use core::{marker::PhantomData, ptr::NonNull}; use pin_init::PinInit; =20 +use super::request_list::RequestList; + type ForeignBorrowed<'a, T> =3D ::Borrowed<'a>; =20 /// Implement this trait to interface blk-mq as block devices. @@ -68,6 +70,15 @@ fn queue_rq( is_poll: bool, ) -> BlkResult; =20 + /// Called by the kernel to queue a list of requests with the driver. + fn queue_rqs( + _hw_data: ForeignBorrowed<'_, Self::HwData>, + _queue_data: ForeignBorrowed<'_, Self::QueueData>, + _requests: &mut RequestList, + ) { + build_error!(crate::error::VTABLE_DEFAULT_ERROR) + } + /// Called by the kernel to indicate that queued requests should be su= bmitted. fn commit_rqs( hw_data: ForeignBorrowed<'_, Self::HwData>, @@ -208,6 +219,50 @@ impl OperationsVTable { } } =20 + /// This function is called by the C kernel to queue a list of new req= uests. + /// + /// Driver is guaranteed that each request belongs to the same queue. = If the + /// driver doesn't empty the `rqlist` completely, then the rest will be + /// queued individually by the block layer upon return. + /// + /// # SAFETY + /// + /// - `requests` must satisfy the safety requirements of `RequestList<= T>` + /// - All requests in `requests` must belong to the same hardware cont= ext. + unsafe extern "C" fn queue_rqs_callback(requests: *mut bindings::rq_li= st) { + // SAFETY: + // - By the safety requirements of this function, `requests` is va= lid for use as a + // `RequestList`. + // - We have exclusive access to `requests` for the duration of th= is function. + let requests =3D unsafe { RequestList::from_raw(requests) }; + + let rq_ptr =3D requests.peek_raw(); + + if rq_ptr.is_null() { + return; + } + + // SAFETY: By function safety requirements, rq_ptr is pointing to a + // valid request. + let hctx =3D unsafe { (*rq_ptr).mq_hctx }; + + // SAFETY: The safety requirement for this function ensure that `h= ctx` + // is valid and that `driver_data` was produced by a call to + // `into_foreign` in `Self::init_hctx_callback`. + let hw_data =3D unsafe { T::HwData::borrow((*hctx).driver_data) }; + + // SAFETY: `hctx` is valid as required by this function. + let queue_data =3D unsafe { (*(*hctx).queue).queuedata }; + + // SAFETY: `queue.queuedata` was created by `GenDiskBuilder::build= ` with + // a call to `ForeignOwnable::into_foreign` to create `queuedata`. + // `ForeignOwnable::from_foreign` is only called when the tagset is + // dropped, which happens after we are dropped. + let queue_data =3D unsafe { T::QueueData::borrow(queue_data) }; + + T::queue_rqs(hw_data, queue_data, requests); + } + /// This function is called by the C kernel. A pointer to this functio= n is /// installed in the `blk_mq_ops` vtable for the driver. /// @@ -450,7 +505,11 @@ impl OperationsVTable { =20 const VTABLE: bindings::blk_mq_ops =3D bindings::blk_mq_ops { queue_rq: Some(Self::queue_rq_callback), - queue_rqs: None, + queue_rqs: if T::HAS_QUEUE_RQS { + Some(Self::queue_rqs_callback) + } else { + None + }, commit_rqs: Some(Self::commit_rqs_callback), get_budget: None, put_budget: None, diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index bc655d202ca01..e22af7ea77bbc 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -148,6 +148,32 @@ pub fn queue(&self) -> &RequestQueue { pub fn as_raw(&self) -> *mut bindings::request { self.0.get() } + + // Return a valid hctx pointer. + fn hctx_raw(&self) -> *mut bindings::blk_mq_hw_ctx { + // SAFETY: The requests is guaranteed to be associated with a hard= ware + // context while we have access to it. + unsafe { (*self.0.get()).mq_hctx } + } + + /// Get a reference to the [`T::HwData`] for the hardware context that= this + /// request is associated with. + pub fn hw_data(&self) -> ::Borrowed<'_> { + let hctx =3D self.hctx_raw(); + + // SAFETY: `hctx` is valid and `driver_data` was produced by a cal= l to + // `into_foreign` in `Operations::init_hctx_callback`. + unsafe { T::HwData::borrow((*hctx).driver_data) } + } + + pub fn is_poll(&self) -> bool { + let hctx =3D self.hctx_raw(); + + u32::from( + // SAFETY: `hctx_raw` returns a valid pointer. + unsafe { (*hctx).type_ }, + ) =3D=3D bindings::hctx_type_HCTX_TYPE_POLL + } } =20 /// A wrapper around a blk-mq [`struct request`]. This represents an IO re= quest. --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 B1A432E1EE7; Sun, 15 Feb 2026 23:44:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199076; cv=none; b=teWRstbyIBPcKrZDR9bzEl9GJFep0QD/eLq095KTRmY1672WAEmXYi5W5P+dE9f8c9NvRHyBKyEcQFW4JRv//FoHoxYKcy5xUOoOD//uxDER8U3/YCenVYRECSUD0gWMTZ/2jPZUnD2l4p/crFXyZ9YIqGPYhEXc9nDHFjy+42o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199076; c=relaxed/simple; bh=HKPTgIJvjqkHxf4DOaN33MBiBgSvGkO/5CG2qjyHOmI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=VPl1olt1285eaum3EGdY8uobjRZN98E3KUGNPsLjg3rW/2PAuUUVc0RvFyzQ2Yv1K9vWLwbkH96a2aCgHItgdkuUI25rzqckNcDaoqmdzBUghKstGvkuEX70GKYdOnwB3/KgiQihyzA6yd/gZ66mayDUQU6yjreIx5AQMSUJ7r8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=HEwbeqMw; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="HEwbeqMw" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3F643C4CEF7; Sun, 15 Feb 2026 23:44:32 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199076; bh=HKPTgIJvjqkHxf4DOaN33MBiBgSvGkO/5CG2qjyHOmI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=HEwbeqMwqhChrUCr0eLVICcsS0kFu3re0MSkS9a7rAap1IsrAEKcKkEc0BoVyz8CP tOt96798R0PqBauyBPJ1bFpvWFtWXVFa8qzPcLPZ8Ld+JV1vCQ28v5arFsYib21ZtN 13fg4gOjKrWZD0zKjm8kS9atSllQkX549FQ2Fw4lS4rqSCef/rmiJn8Kts9a5j1Ps6 M4Bq5zEUiFadeV1YBprYi+RP6zf368p0qbUaE2ke/jQf5uq8U5LtS97eiDQQfhvNkw z2/q4ntwj/WmHNbNz4TpuVQjOoZdtnbtCsebkeeRcCC0poScOEJZlXf5BzgBJAX5vT ag6JVCquNNJHQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:53 +0100 Subject: [PATCH 66/79] block: rnull: support queue_rqs 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: <20260216-rnull-v6-19-rc5-send-v1-66-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=12232; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=HKPTgIJvjqkHxf4DOaN33MBiBgSvGkO/5CG2qjyHOmI=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhXTS1cfVP+RDBqvgSmswBnxZJTKLx89agmK q2pgugwP/eJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYVwAKCRDhuBo+eShj d/3tD/40WsdJWyyYdlKPzEWrD0jdbY+3W83zTWtXtFvD++vJ73emsdfzJGywBkqivcTP2Mw4srN hMaketviLPQhFfp2PZbweUdYsV0U3uoOic9UhqwrAuINKhlv/y7ee9tkIw+wPtN6ZPlfXXdXYF6 hwyz6bI3yH7xSxPNC02QrQyfE7ObOt92sKVJbQjcxKKl57ZKkStWM/QHJusHuaaT5+dljtbHFMF XqSqkNSxbNfh42AS2LRmc7prBMokZCGMUTPgVMejWe8GyXv26jQuFet3FPvwVqr2C31M5cIf4oq d8yph8PhGIDKoUFiAKK0D+a3Cah5jxOwITAlUoTehUeN2uVIAiw+Nf6iiyZIfo2dmuSA9TZSQao AmcsNsOFYuD6LTzmO7KAPrPdZF81HMrIpUupHYIhx/+gfsWWR041QgdwCix5HjNmOCIGGA63Tty bA4l84a1ugbEHBL+dekLmMrWjgrdSp7ZJTnInQ0rAG29XrUijIveoNZcRaOWiJCmtm78+wEWLFk +uhDrBtGlBpviMdoKoqBh/eaRkHXSUlOpZ+jcNKFOJMxJfKugcS5lo42sahjPO28E5jsMFZIVd4 3OCOlMRfahAfl0GN6VPi4Hdc1WtB/j5+dOZnjSE881iJEkvGykDvIzukH8WOd0m4vVbthdP1rSJ qu9CVscK+CEFYAA== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Implement the `queue_rqs` callback for rnull, allowing the block layer to submit multiple requests in a single call. This improves performance by reducing per-request overhead and enabling batch processing. The implementation processes requests from the list one at a time, removing successfully processed requests from the list. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/disk_storage.rs | 34 +++---- drivers/block/rnull/rnull.rs | 180 +++++++++++++++++++++++---------= ---- 2 files changed, 132 insertions(+), 82 deletions(-) diff --git a/drivers/block/rnull/disk_storage.rs b/drivers/block/rnull/disk= _storage.rs index b2b5eaa783cdc..d9f2703957fc0 100644 --- a/drivers/block/rnull/disk_storage.rs +++ b/drivers/block/rnull/disk_storage.rs @@ -86,7 +86,7 @@ pub(crate) fn discard( } } =20 - pub(crate) fn flush(&self, hw_data: &Pin<&SpinLock>) -= > Result { + pub(crate) fn flush(&self, hw_data: &Pin<&SpinLock>) { let mut tree_guard =3D self.lock(); let mut hw_data_guard =3D hw_data.lock(); let mut access =3D self.access(&mut tree_guard, &mut hw_data_guard= , None); @@ -129,16 +129,10 @@ fn to_sector(index: usize) -> u64 { (index << block::PAGE_SECTORS_SHIFT) as u64 } =20 - fn extract_cache_page(&mut self) -> Result>= > { + fn extract_cache_page(&mut self) -> Option> { let cache_entry =3D self.cache_guard.find_next_entry_circular( self.disk_storage.next_flush_sector.load(ordering::Relaxed) as= usize, - ); - - let cache_entry =3D if let Some(entry) =3D cache_entry { - entry - } else { - return Ok(None); - }; + )?; =20 let index =3D cache_entry.index(); =20 @@ -168,11 +162,14 @@ fn extract_cache_page(&mut self) -> Result>> { let mut src =3D cache_entry; let mut offset =3D 0; for _ in 0..PAGE_SECTORS { - src.page_mut().get_pin_mut().copy_to_page( - disk_entry.page_mut().get_pin_mut(), - offset, - block::SECTOR_SIZE as usize, - )?; + src.page_mut() + .get_pin_mut() + .copy_to_page( + disk_entry.page_mut().get_pin_mut(), + offset, + block::SECTOR_SIZE as usize, + ) + .expect("Write to succeed"); offset +=3D block::SECTOR_SIZE as usize; } src.remove() @@ -182,16 +179,15 @@ fn extract_cache_page(&mut self) -> Result>> { } }; =20 - Ok(Some(page)) + Some(page) } =20 - fn flush(&mut self) -> Result { + fn flush(&mut self) { if self.disk_storage.cache_size > 0 { - while let Some(page) =3D self.extract_cache_page()? { + while let Some(page) =3D self.extract_cache_page() { drop(page); } } - Ok(()) } =20 fn get_or_alloc_cache_page(&mut self, sector: u64) -> Result<&mut Null= BlockPage> { @@ -208,7 +204,7 @@ fn get_or_alloc_cache_page(&mut self, sector: u64) -> R= esult<&mut NullBlockPage> .take() .expect("Expected to have a page available") } else { - self.extract_cache_page()? + self.extract_cache_page() .expect("Expected to find a page in the cache") }; Ok(self diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 429819bf042ba..592fbf5790fd2 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -28,7 +28,7 @@ BadBlocks, // }, bio::Segment, - error::BlkResult, + error::{BlkError, BlkResult}, mq::{ self, gen_disk::{ @@ -36,8 +36,10 @@ GenDisk, GenDiskRef, // }, + IdleRequest, IoCompletionBatch, Operations, + RequestList, TagSet, // }, SECTOR_SHIFT, @@ -720,6 +722,104 @@ fn complete_request(&self, rq: Owned>) { } } } + + #[inline(always)] + fn queue_rq_internal( + hw_data: Pin<&SpinLock>, + this: ArcBorrow<'_, Self>, + rq: Owned>, + _is_last: bool, + ) -> Result<(), QueueRequestError> { + if this.bandwidth_limit !=3D 0 { + if !this.bandwidth_timer.active() { + drop(this.bandwidth_timer_handle.lock().take()); + let arc: Arc<_> =3D this.into(); + *this.bandwidth_timer_handle.lock() =3D + Some(arc.start(Self::BANDWIDTH_TIMER_INTERVAL)); + } + + if this + .bandwidth_bytes + .fetch_add(u64::from(rq.bytes()), ordering::Relaxed) + + u64::from(rq.bytes()) + > this.bandwidth_limit + { + rq.queue().stop_hw_queues(); + if this.bandwidth_bytes.load(ordering::Relaxed) <=3D this.= bandwidth_limit { + rq.queue().start_stopped_hw_queues_async(); + } + + return Err(QueueRequestError { request: rq }); + } + } + + let mut rq =3D rq.start(); + + if rq.command() =3D=3D mq::Command::Flush { + if this.memory_backed { + this.storage.flush(&hw_data); + } + this.complete_request(rq); + + return Ok(()); + } + + let status =3D (|| -> Result { + #[cfg(CONFIG_BLK_DEV_ZONED)] + if this.zoned.enabled { + this.handle_zoned_command(&hw_data, &mut rq)?; + } else { + this.handle_regular_command(&hw_data, &mut rq)?; + } + + #[cfg(not(CONFIG_BLK_DEV_ZONED))] + this.handle_regular_command(&hw_data, &mut rq)?; + + Ok(()) + })(); + + if let Err(e) =3D status { + // Do not overwrite existing error. We do not care whether thi= s write fails. + let _ =3D rq + .data_ref() + .error + .cmpxchg(0, e.to_errno(), ordering::Relaxed); + } + + if rq.is_poll() { + // NOTE: We lack the ability to insert `Owned` into a + // `kernel::list::List`, so we use a `RingBuffer` instead. The + // drawback of this is that we have to allocate the space for = the + // ring buffer during drive initialization, and we have to hol= d the + // lock protecting the list until we have processed all the re= quests + // in the list. Change to a linked list when the kernel gets t= his + // ability. + + // NOTE: We are processing requests during submit rather than = during + // poll. This is different from C driver. C driver does proces= sing + // during poll. + + hw_data + .lock() + .poll_queue + .push_head(rq) + .expect("Buffer is sized to hold all in flight requests"); + } else { + this.complete_request(rq); + } + + Ok(()) + } +} + +struct QueueRequestError { + request: Owned>, +} + +impl From for BlkError { + fn from(_value: QueueRequestError) -> Self { + kernel::block::error::code::BLK_STS_IOERR + } } =20 impl_has_hr_timer! { @@ -761,7 +861,7 @@ struct HwQueueContext { struct Pdu { #[pin] timer: HrTimer, - error: Atomic, + error: Atomic, } =20 impl HrTimerCallback for Pdu { @@ -802,76 +902,31 @@ fn new_request_data() -> impl PinInit { }) } =20 - #[inline(always)] fn queue_rq( hw_data: Pin<&SpinLock>, this: ArcBorrow<'_, Self>, rq: Owned>, - _is_last: bool, - is_poll: bool, + is_last: bool, + _is_poll: bool, ) -> BlkResult { - if this.bandwidth_limit !=3D 0 { - if !this.bandwidth_timer.active() { - drop(this.bandwidth_timer_handle.lock().take()); - let arc: Arc<_> =3D this.into(); - *this.bandwidth_timer_handle.lock() =3D - Some(arc.start(Self::BANDWIDTH_TIMER_INTERVAL)); - } + Ok(Self::queue_rq_internal(hw_data, this, rq, is_last)?) + } =20 - if this - .bandwidth_bytes - .fetch_add(u64::from(rq.bytes()), ordering::Relaxed) - + u64::from(rq.bytes()) - > this.bandwidth_limit + fn queue_rqs( + hw_data: Pin<&SpinLock>, + this: ArcBorrow<'_, Self>, + requests: &mut RequestList, + ) { + let mut requeue =3D RequestList::new(); + while let Some(request) =3D requests.pop() { + if let Err(QueueRequestError { request }) =3D + Self::queue_rq_internal(hw_data, this, request, false) { - rq.queue().stop_hw_queues(); - if this.bandwidth_bytes.load(ordering::Relaxed) <=3D this.= bandwidth_limit { - rq.queue().start_stopped_hw_queues_async(); - } - - return Err(kernel::block::error::code::BLK_STS_DEV_RESOURC= E); + requeue.push_tail(request); } } =20 - let mut rq =3D rq.start(); - - if rq.command() =3D=3D mq::Command::Flush { - if this.memory_backed { - this.storage.flush(&hw_data)?; - } - this.complete_request(rq); - - return Ok(()); - } - - #[cfg(CONFIG_BLK_DEV_ZONED)] - if this.zoned.enabled { - this.handle_zoned_command(&hw_data, &mut rq)?; - } else { - this.handle_regular_command(&hw_data, &mut rq)?; - } - - #[cfg(not(CONFIG_BLK_DEV_ZONED))] - this.handle_regular_command(&hw_data, &mut rq)?; - - if is_poll { - // NOTE: We lack the ability to insert `Owned` into a - // `kernel::list::List`, so we use a `RingBuffer` instead. The - // drawback of this is that we have to allocate the space for = the - // ring buffer during drive initialization, and we have to hol= d the - // lock protecting the list until we have processed all the re= quests - // in the list. Change to a linked list when the kernel gets t= his - // ability. - - // NOTE: We are processing requests during submit rather than = during - // poll. This is different from C driver. C driver does proces= sing - // during poll. - - hw_data.lock().poll_queue.push_head(rq)?; - } else { - this.complete_request(rq); - } - Ok(()) + drop(core::mem::replace(requests, requeue)); } =20 fn commit_rqs(_hw_data: Pin<&SpinLock>, _queue_data: A= rcBorrow<'_, Self>) {} @@ -888,7 +943,6 @@ fn poll( let status =3D rq.data_ref().error.load(ordering::Relaxed); rq.data_ref().error.store(0, ordering::Relaxed); =20 - // TODO: check error handling via status if let Err(rq) =3D batch.add_request(rq, status !=3D 0) { Self::end_request(rq); } --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 B7BA8217659; Sun, 15 Feb 2026 23:41:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198889; cv=none; b=aJo1lJSphomtDID0qsaRpFtWecfDSUsqtY3ewp2JeQWGf/YyIRWPvG881TyXyIPagPfdC4kcAaXNC0B/J2t4zXEfhzwYMWLhDwohFdLRzv9NK19TcmCJh+Jl9AKppouOGSpgqRs50MyHGdrTxDPcPXp9Xikw424gKtHK2Mlq4Ws= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198889; c=relaxed/simple; bh=vHPgpZo5uXgA8zL+qju/AiVs11m7b10tJPDeLW3U++M=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=gm6cuXLFa+LDeFGiJvsaoowQhsNFVkOK9e3R6THQT5Ui8Z+ORP0ImDnk//Dehl/sH4gdvXC1BzKcNRZBwl/QELZ16zFUM0Hhdx2tFMEAHqqaqs2ZVQr/4HBVo81+X2PrCyz4gU5O6+gk83S+11LwY4kWwZVqEKImr0R4jE08RnQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=P6gN1JoY; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="P6gN1JoY" Received: by smtp.kernel.org (Postfix) with ESMTPSA id F001DC4CEF7; Sun, 15 Feb 2026 23:41:24 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198889; bh=vHPgpZo5uXgA8zL+qju/AiVs11m7b10tJPDeLW3U++M=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=P6gN1JoYAil+b2m0a49hptXFxRbU6gEBpaZDLQpGcmkMNRl+ts2wQ9IGltjdaNsE2 iHtDP2IDj3SmgZ54TEEtWor4vbjebwuI/6hFw19GH6mKDkyZ5/MgUHV6fb4GGfNk0a xj/p1b2YXIqEcyrLvelc5oC9oRgPZ0LqRgbsXEi4sBF0DJGBovK/t4K8s573ejI5Yl BcQmzROO/AjjnmLo/yornM1A4/oLXUgDFODgqyKCOCTkPW6JlVFoWEiiKGUapcH0oY rpAnGQKoWtCLZMIMHJTEW7jK9xXoDyS/WNUfFbAo1Qy+oAlVBeNulHMiJKsExOCnIS uL0Uz256tvb6Q== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:54 +0100 Subject: [PATCH 67/79] block: rust: remove the `is_poll` parameter from `queue_rq` 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: <20260216-rnull-v6-19-rc5-send-v1-67-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=2486; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=vHPgpZo5uXgA8zL+qju/AiVs11m7b10tJPDeLW3U++M=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhY2K0RYlJtYPGe5XGp2aD/s5dSMAoJeH2Oq zb/CAzQM5GJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYWAAKCRDhuBo+eShj d04tD/97m54PJ4Emn0i+4TXowMxdpppLVqZy2LiOwftpAsi23l98iD5eWe6aKOpyCgEHEXyfl5g Wq1XPUnTt8oQHgE4tpBPLAz8pzNke4DqN/w9WpvKODlITv3lJETQ3zHs4n91c3xL+ROO3F0W5OR w4v1vVMgv8hBmLr/8I4xlPGkyDWtkgEDjOGfqpRRvZBz27n4zfgkdT5ZPhHpjr8R2BteEGWu7v9 R9iax5z1Wt+Sayzq7xXtL0uvd9Rs9Z5HW1nJpNllcV3yh36PdFibMYsi43OhC/kS8geCMoUY6m5 lYDSHBw06dwiSzX2ZqIluikI9wtwZficu8hOzUeX8BIgidJNVZCFIfsDsDpyJSEnIFJJR49FX9H bBdURz9wVMzYy1AillTxxBKXbevqVQNEI1x+TzT9bEte0o1sLcriZZJVafiNVPGUoFXxAxcWQbJ kwTJdtofcJM26rXiDwYwOio1SO/9gpryZjduJrumk4x5OiVIvO/NS1pFuGBRDyEf71kkpz0yo18 TUS3kp81o1NS4kZOad6+f83YVFW2gh5mZNfhbjWDin8MUToyhBJI483FD1Id4cNXA9pwr+HW0X7 Vs/2kVPNqx5hQkKx8DpD/90WbWKLxxAMtD0qnsB4mF7+s3zzBTrn+N7v5HNwGUCHpstS68xrxTN cm8xLRqjLYfocmw== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 The information can now be obtained from `Request::is_poll`. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/rnull.rs | 1 - rust/kernel/block/mq.rs | 1 - rust/kernel/block/mq/operations.rs | 7 ------- 3 files changed, 9 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 592fbf5790fd2..db856f03b78cb 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -907,7 +907,6 @@ fn queue_rq( this: ArcBorrow<'_, Self>, rq: Owned>, is_last: bool, - _is_poll: bool, ) -> BlkResult { Ok(Self::queue_rq_internal(hw_data, this, rq, is_last)?) } diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index 786c32f2cb56c..02d75acfddb3b 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -90,7 +90,6 @@ //! _queue_data: (), //! rq: Owned>, //! _is_last: bool, -//! is_poll: bool //! ) -> BlkResult { //! rq.start().end_ok(); //! Ok(()) diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/oper= ations.rs index e4de6db807e18..efead98767196 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -67,7 +67,6 @@ fn queue_rq( queue_data: ForeignBorrowed<'_, Self::QueueData>, rq: Owned>, is_last: bool, - is_poll: bool, ) -> BlkResult; =20 /// Called by the kernel to queue a list of requests with the driver. @@ -188,11 +187,6 @@ impl OperationsVTable { // `into_foreign` in `Self::init_hctx_callback`. let hw_data =3D unsafe { T::HwData::borrow((*hctx).driver_data) }; =20 - let is_poll =3D u32::from( - // SAFETY: `hctx` is valid as required by this function. - unsafe { (*hctx).type_ }, - ) =3D=3D bindings::hctx_type_HCTX_TYPE_POLL; - // SAFETY: `hctx` is valid as required by this function. let queue_data =3D unsafe { (*(*hctx).queue).queuedata }; =20 @@ -209,7 +203,6 @@ impl OperationsVTable { // SAFETY: `bd` is valid as required by the safety requirement= for // this function. unsafe { (*bd).last }, - is_poll, ); =20 if let Err(e) =3D ret { --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 5804E30C614; Sun, 15 Feb 2026 23:46:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199171; cv=none; b=FTALpC4KNqqV+I1hQrFMUc+6ePwHdAcKNvgI6ZMD33OdIcYStx9JQvf4UrfgURowDk0ZnVTy1X7nCHzTJRVHv7UEDy+h3V3gua+IP4A2QkoEI+XFwD2+MjTdD6ws+JgCfcztC9MPuAG5t7B7uFQGSkrUcSVewoSzxGeLiyHH4d4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199171; c=relaxed/simple; bh=09c7uzZkxTyVrHNvjFvmG71WyMeEUT6Jt3cM3tUyoBs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=FpDxUoBAaVcvhlm3ZS2CCPB2Xsoy6Ar2wQejErkpJNZoyGK9+4K8YOlKeI/wa9I9w7We+ujtf144nV6lIJr1Mor0WdrNmVbjr/UvEIUP5zmRF+L3VtbaVkWm0xG2hKEICUjsM9WPaQh/KrHYcfzHEHgck366Tiv3T9TTpTeT/Vk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=cn925NUd; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="cn925NUd" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5E806C4CEF7; Sun, 15 Feb 2026 23:46:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199170; bh=09c7uzZkxTyVrHNvjFvmG71WyMeEUT6Jt3cM3tUyoBs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=cn925NUdJj4OBEmpfeVpbTqvDSZsJTqZer9GL5rgWPDKXUNrKh7N7CvEZ/8q7ZlB/ JR6Ub+cilzPA0SEV5a66KNxM4O0/p4Il22WIzHeHBk7zGxciQfy3Z8sB8iVgBF2vXx ZbpfOUVueW4pb8ZnXQ4kk91lkCS5qV2wNGWQCOr1uL6xf6/SdglBx+UKcX8lr4kYZY sX0dY0R+f9p48b3UPwm9bD3Ed2cWgsK0BSQfpqcd52jnd7Mz06lcT7GAHEOmF3KRuq 6JwAXz9P/u3kKRE0PAiNqbZhhUyrumt/IzZAimFLzxBHLnY5tsD4nCQciQchOdNp3u Jbs4h+OBc2xgQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:55 +0100 Subject: [PATCH 68/79] block: rust: add a debug assert for refcounts 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: <20260216-rnull-v6-19-rc5-send-v1-68-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1216; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=09c7uzZkxTyVrHNvjFvmG71WyMeEUT6Jt3cM3tUyoBs=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhZFlZdiDql4cqam8az2bqXKyXBUh/nt996f hHOmyZjco2JAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYWQAKCRDhuBo+eShj d+g1D/9Tp63vqvjenlUu+5ZJU1lZ+9dfMcKfq9m3hw47N4hrC9Z4Q6onk654vGOU2FPLlpUNy8O KFHhFrvdjxRcIbLPUsQOFHJGFib9kBLRrM6/JaRWlH5iq6m5ETg0Q5R+WGViG44b169vc4/YW1N JzNKqKwWBx66ko26VSg8maFMMAErumOt7G10lKzpSzcp4cOGXui6ZkKmiQDqJrWSaZQKDbZg1yy 8bhhfuK5uo9d+cDLVDb5Ea1EcGG0W+wP+9g/YDAfR0qF4qjcwLPL9vChJRJXchgei9i5rx7gFth 2xSBY+ot3E1dHVntP9S+ghLoduNyau62Rd9Cc31zYM8heVQM3/HPOed2K4c+KF/TRpoKjvxgHFb YIPbvfyMA6a5YpVqIygvX8iSCGULk5WoA6CP1LzQE6zSK6zEhZPj0vc/zviKoEe8uxcHUScNFqc z+BsrGzv5O2m7ZbGzZgin6qvb5UV/3nqjB/EwntF6hW5J3xMTu6O0f0rw2PdEAVdCVjzYXBCFuj L3ExONihceAiCdO20rH5z/EFwxqcNDWF2fqaXXOR1SB2Z+ZWciJYmnJJV4zgMpJiFQPQKFGPQi4 ICC96uXCC3tdqBoeeBBLLXLPkSwXL+phd4rQNh8+oV2xj7+yqiB/5R6yXRUf04+giFNXYDLK2j5 jxL++PfGAvuA3vg== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a debug assertion in `ARef::dismiss` to verify that the request refcount is at least two when an `ARef` exists. This helps catch reference counting bugs during development. Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq/request.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index e22af7ea77bbc..9a05bce39c64b 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -579,6 +579,17 @@ pub fn dismiss(mut self) { // SAFETY: `inner` is valid for reads and writes, is properly alig= ned and nonnull. We have // exclusive access to `inner` and we do not access `inner` after = this call. unsafe { core::ptr::drop_in_place(inner) }; + + debug_assert!( + self.inner + .wrapper_ref() + .refcount() + .as_atomic() + .load(ordering::Relaxed) + >=3D 2, + "Request refcount must be at least two when an ARef e= xist" + ); + core::mem::forget(self); } } --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 06E8230DEB9; Sun, 15 Feb 2026 23:44:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199057; cv=none; b=THxPRjHO4XQ+NOlWoP5lulDg0goJZxwVisPwGx2e4EZwU66jm9mIiLYyd0W1neuiw6DbmBI46W5+XnMVnreXJ4r9lUzcso3/UJTPUSjlcwQCj3tafIsPCeRpysMmAkAe98ieOVnGTOAfUkAdT6Tnhr3EItmwisL1sgk1NmCJf1E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199057; c=relaxed/simple; bh=xsuee6tClF6ul/R2hOJJBKg96Kg6jLAUBRXV2md3X00=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=XUOS4yhUmGgo3yWVib0PTNowZAQUiV+fcCjobQCaKMoOokThHk46Q+Skglp+e8nkF2Ef8zv/4BdwQTsQdVGyJ6x50MX9Ok/TuMvB6WxQfFU//nDOlIM9HKP9qsmR1k7VrnISp/5fUup+Oqm2pNao2hbd1E89Rwh0oPf7Cc3r6Fw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=hVURXEpM; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="hVURXEpM" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4E680C4CEF7; Sun, 15 Feb 2026 23:44:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199056; bh=xsuee6tClF6ul/R2hOJJBKg96Kg6jLAUBRXV2md3X00=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=hVURXEpMGTCXVTv4Qbca31zfmkrG1iMKdGHLBXWlmZn0eELWfU2kmGrJxfn7LmVs3 dhJZGDMrOPlnWyoGb87RZapywHqdffeccBVFLRyjYT2OybgqJ5pKrPOCIKbp566yk2 7XdTsdcCyJWN0BVB9KdLVAVjSTZyjIl8cFYUVKwtEwngqWYQrdgehuhgfUcYARzeuX azF40DpUsFPaDDLvntX8NfM9EHqHn2Y9+sA7We7+90WcaxBfriS7mawIdjp6sepSIk tF45wFln9tbI0fVOh9AGTrGRDlJbLPZp541Ymdbqi03FI7swQ1ZuCeaeWCj0DTOdSl cpV7xkZPj90bg== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:56 +0100 Subject: [PATCH 69/79] block: rust: add `TagSet::tag_to_rq` 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: <20260216-rnull-v6-19-rc5-send-v1-69-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4873; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=xsuee6tClF6ul/R2hOJJBKg96Kg6jLAUBRXV2md3X00=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklha+tACecSgfGcA74oxyxwExIZbOFlH6RcN8 vImZ0IJU9WJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYWgAKCRDhuBo+eShj d0inD/4kp0vvFFgboOoTGJtTryqDsVSVhkR4f4NyCAQSJe4Mv0PHDrewUkhuP7ETdRCQxOscW0p 4bb/kt7j8g0NZ92mou8bo7g/hxtvcmOVweBwHlcuFJFsDAN4bSjtZyWsCLD9HB+7Q5RX7ULYHMG 8oSrX89dqRNl3Lqg4HyVAbCASaeCkEsggONxCzQYGHxXKUCpJpPJhguYEFvqtasXM0WQvAEnd1y AF2Z4+u3LNrFW0t6BiwSKEz4Jonru68Kh0/PnrGRfu1OIj/d5at0S6ITzVNyNi0i/eEYpw3W3rC ePSEanV2UQCB7nQCiPlASeK4kdrKSsAF8ACEvO5KzIMLKSLBuAX30N925EkU9Dn5fVNtSbgtCHr 8LTLhFp+kUDKn7J8kPbIiqesdRdONnb2n+cHp/fFLn/5sWUJMbSgLC4teV8eFiECYKyMCElw2wn Fx03k/syqs9gj/z1XxuCOWxn6cqdgUoNPU0t1HSR1+VIUBC7ccTbSnRYTF7FC1v1WeAszR08AVc JR3WcUk6WcunP7n9tFmA0uKDOU7POL53QK3WXt+i6ca68WOWRtQZco4AGPNdPoSoZORROiYbzd0 d9C/ihn4wMeheqaU7IMEEGycGw71PxbdX/wmWdZDmCbnQ7LYBXlU9D1BRYjbIOh1m0bZdbtbrst +cDXK7WthYA8csA== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a way for block device drivers to obtain a `Request` from a tag. This is backed by the C `blk_mq_tag_to_rq` but with added checks to ensure memory safety. Signed-off-by: Andreas Hindborg --- rust/helpers/blk.c | 6 ++++ rust/kernel/block/mq/tag_set.rs | 72 +++++++++++++++++++++++++++++++++++++= +--- 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/rust/helpers/blk.c b/rust/helpers/blk.c index 0db567dfc5d53..bb77af6dca9c8 100644 --- a/rust/helpers/blk.c +++ b/rust/helpers/blk.c @@ -53,3 +53,9 @@ __rust_helper struct request *rust_helper_rq_list_peek(st= ruct rq_list *rl) { return rq_list_peek(rl); } + +__rust_helper struct request * +rust_helper_blk_mq_tag_to_rq(struct blk_mq_tags *tags, unsigned int tag) +{ + return blk_mq_tag_to_rq(tags, tag); +} diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set= .rs index 47a162360e628..e7d7217da8922 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -5,12 +5,12 @@ //! C header: [`include/linux/blk-mq.h`](srctree/include/linux/blk-mq.h) =20 use crate::{ - bindings, block::mq::{operations::OperationsVTable, request::RequestDataWrapper,= Operations}, error::{self, Result}, - prelude::*, - try_pin_init, - types::{ForeignOwnable, Opaque}, + pr_warn, + prelude::{ENOMEM, *}, + sync::atomic::ordering, + types::{ARef, ForeignOwnable, Opaque}, }; use core::{convert::TryInto, marker::PhantomData, pin::Pin}; use pin_init::{pin_data, pinned_drop, PinInit}; @@ -19,6 +19,8 @@ pub use flags::Flag; pub use flags::Flags; =20 +use super::Request; + /// A wrapper for the C `struct blk_mq_tag_set`. /// /// `struct blk_mq_tag_set` contains a `struct list_head` and so must be p= inned. @@ -166,6 +168,68 @@ pub fn data(&self) -> ::Borrowed<'_> { // converted back with `from_foreign` while `&self` is live. unsafe { T::TagSetData::borrow(ptr) } } + + /// Obtain a shared reference to a request. + /// + /// This method will hang if the request is not owned by the driver, o= r if + /// the driver holds an [`Ownable`] reference to the request. + pub fn tag_to_rq(&self, qid: u32, tag: u32) -> Option>= > { + if qid >=3D self.hw_queue_count() { + // TODO: Use pr_warn_once! + pr_warn!("Invalid queue id: {qid}\n"); + return None; + } + + // SAFETY: We checked that `qid` is within bounds. + let tags =3D unsafe { *(*self.inner.get()).tags.add(qid as usize) = }; + + // SAFETY: We checked `qid` for overflow above, so `tags` is valid. + let rq_ptr =3D unsafe { bindings::blk_mq_tag_to_rq(tags, tag) }; + if rq_ptr.is_null() { + None + } else { + // SAFETY: if `rq_ptr`is not null, it is a valid request point= er. + let refcount_ptr =3D unsafe { + RequestDataWrapper::refcount_ptr( + Request::wrapper_ptr(rq_ptr.cast::>()).as_p= tr(), + ) + }; + + // SAFETY: The refcount was initialized in `init_request_callb= ack` and is never + // referenced mutably. + let refcount_ref =3D unsafe { &*refcount_ptr }; + + let atomic_ref =3D refcount_ref.as_atomic(); + + // It is possible for an interrupt to arrive faster than the l= ast + // change to the refcount, so retry if the refcount is not wha= t we + // think it should be. + loop { + // Load acquire to sync with store release of `Owned` + // being destroyed (prevent mutable access overlapping sha= red + // access). + let prev =3D atomic_ref.load(ordering::Acquire); + + if prev >=3D 1 { + // Store relaxed as no other operations need to happen= strictly + // before or after the increment. + match atomic_ref.cmpxchg(prev, prev + 1, ordering::Rel= axed) { + Ok(_) =3D> break, + // NOTE: We cannot use the load part of a failed c= mpxchg as it is always + // relaxed. + Err(_) =3D> continue, + } + } else { + // We are probably waiting to observe a refcount incre= ment. + core::hint::spin_loop(); + continue; + }; + } + + // SAFETY: We checked above that `rq_ptr` is valid for use as = an `ARef`. + Some(unsafe { Request::aref_from_raw(rq_ptr) }) + } + } } =20 #[pinned_drop] --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 D41FB30DD07; Sun, 15 Feb 2026 23:44:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199046; cv=none; b=Nwl9RFuIdRnBFQocHrvPugTeUuOKcXwG3yuD8N0LvlnSWwU9xG6fzB5newO64LO5mInspaJbjbO5OaByxbrg757JPzdhuDOZs2FikwGiDNHYThLiAf3xnY7z4tDJwnefP013L7p3U88t1BXv2kAesFDvZpivvlV3J0QBj+oBjuQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199046; c=relaxed/simple; bh=HXXe3ZuaQh0jtawnDxB+W/Q37k4GGbJvn35e1qpXTRo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Idi426K3LPx4pEV+cj3/f7bxxiIk5igqFW1qhBAFceOjnDIq9fU4QOo/4mek706gcKCNcn8J6gSsYgTuHsEPR/IqvG47KaW6VBSvAKhJDO9z485LUBylS/DWFR0g1bL8MR7OElmMIpwHiFkm/u1x2oK1MChChrztKchFH4/xPvg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=X/cAr2ex; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="X/cAr2ex" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 35922C4CEF7; Sun, 15 Feb 2026 23:44:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199046; bh=HXXe3ZuaQh0jtawnDxB+W/Q37k4GGbJvn35e1qpXTRo=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=X/cAr2exmax8lXA+n5QNdVX/hBbQVn347xBA5DUD43Ygvq790YZfIzSVNXjevosvy 5nDZFrviyPAratHgQaIn4vCEBeWKTif9T6X2JcMKahVd2V18L54yXYi75oLxsbBSrP LRGnKMoLmYxBwkVqpgjtEcTNR3LKw6Es2bR7qZAnqxGpzW9s5H9nyAa1UH1xTjJ5NS tb0cBOcjqxT6P8OYpOxQk1Da1x/ZJMh5dvr1W9h6SQvnYIvwOiiUxG7eNB7hS/Fuh1 m64m+TGAtJ70wBFw5h8TcbrjNqsai2TZkT0DoYP9dNZEF8RKP+1ilIakmZT3x2gU6r /KX+laZXIZQ3A== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:57 +0100 Subject: [PATCH 70/79] block: rust: add `Request::queue_index` 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: <20260216-rnull-v6-19-rc5-send-v1-70-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1022; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=HXXe3ZuaQh0jtawnDxB+W/Q37k4GGbJvn35e1qpXTRo=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhbvCV/K7QHpAKO17p9tlhElvwrVj3iR8aTO xSxbX9mMTyJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYWwAKCRDhuBo+eShj d8suD/9uK5x4p4mZa2NHYzVIAjBzj2akl2B5TMECJhzE1rCk8/JBpPTetMD4LGk9ASmRi7Ss2yb XVwBn/7/CqdmjvQvolnlPIgr6WxOJ3Ao5tMDZUSdOQd2g9qJVtCpNxVrZa0Xxp1jGhODM7/ONdm Euyrcd5g5obOyGTHKfmDOq63E4X5xyMawHVsDq4Mg6tCvs/9vyMBLhICEGfGTQNYyu552AbxTGu zWjDlXrTvEnUlU23A5VDDjhOZlbIsUq/jJZYdiO9cI98Ha4xxQm0TLZkkE4pZvi0KI0GvQGz0hL F86Fwk6MT0uBkuhU9RaqP5mkugJPZuDgf0p54aCQvKEmzlZ3v9hQFW8H+Wv1HsueHaNxEYYU7Vm 9Msejz9Ho1DIFwmNj3cSSAKVJGgGR4PDPtpTMaJEVcLoFnaISC0YLCpsZfOHLSNm7L9KS6JfNOF TRFYI5QqRgZNmN8VNoE2xbBznLYLwkTkLgTDOyYIATbqwrPAgn6dPG3opCw5htI3mDmpKOo/Q+c BL5GCwR13fQ0OhJEWc0S40MSWPRsWx11M9x4kGJ4UpYB7jFUIwFd+PjD7mGpiWlCGYZ8Ug/h0vQ XLX0H4zdnBIvqDX2nXLCm/R35mAsWe1U858koRujJFRhUVLyceDIfutFKteIUwCR1YLtXqrQ7Qf cFU/nem3vF4xiqw== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a method to query a request about the index for the hardware queue associated with the request. Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq/request.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index 9a05bce39c64b..e2fafe7737379 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -166,6 +166,13 @@ pub fn hw_data(&self) -> = ::Borrowed<'_> { unsafe { T::HwData::borrow((*hctx).driver_data) } } =20 + /// Get the queue index for the hardware queue associated with this re= quest. + pub fn queue_index(&self) -> u32 { + // SAFETY: The requests is guaranteed to be associated with a hard= ware + // context while we have access to it. + unsafe { (*self.hctx_raw()).queue_num } + } + pub fn is_poll(&self) -> bool { let hctx =3D self.hctx_raw(); =20 --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 3C9E02E54A2; Sun, 15 Feb 2026 23:43:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198987; cv=none; b=RpSa/S+gjQqB72FYYLD1XXJnp2l06cE3Y06oGZVqBFSNjiaTTYpA3/6WHun+fp5IuPUowhAgZYY6pt/yvkmWicNa3mjeHRYoBppfNPHk5fsPRb2WFPxsPi+7pPyVcyu4v1SxrYzttrVWbWx8WI43MrK3SDu9wSfKr1vb+R4pOs0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198987; c=relaxed/simple; bh=g5LW6csT032CGxKoiloM9/8wSOoZndfzpAjeHZm9vX8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=fLbXDXpebxUdrLrRfJYhuXGJNoiF8yzLBM9JCctObu/jI1LGHeTDRHP/jxsFulcZI6ZtSqQmHj66Os1+b5rRDpZfJaGkgCYQlhxbfOXDDOJJaBbg64t/w6diED9LqVp/Exa/vTiY9ZPExsnOk915sQSa6WSmS8+DlOyEsLoY/B4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Zx+uunxp; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Zx+uunxp" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A33F4C4CEF7; Sun, 15 Feb 2026 23:43:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198987; bh=g5LW6csT032CGxKoiloM9/8wSOoZndfzpAjeHZm9vX8=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Zx+uunxpUs5Oi9tE3v9dXqBlUuE1QYCwU1Ow40WRmAr9MQdSYJi2MpEhhs8tBWGtn 0d9tK85IAOB7Z9s8gs1vLmIGSR+CYv8/a9Pk1VHTp7cMi0bUh3RMWHcBQotO6hQAd6 4uRQEt6aqwvrVy75EXGa7oFP6GerMYfBVcTi11Hi+/z21i+wa+vPFKTv+lcUGOs/6B cHgO6GmWStx5QtAphWF8If9g6H4Fumk2nPf+Ldc3RNOE4mbLelFsIHimENQK9cud8A Tc7dt885RbQ+bZaBmQpbnQbWMBlLung72sy8F9AIGqDzASp+MIuph1eS7toRFCc7PY QdJM921qm4+eA== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:58 +0100 Subject: [PATCH 71/79] block: rust: add `Request::requeue` 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: <20260216-rnull-v6-19-rc5-send-v1-71-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1419; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=g5LW6csT032CGxKoiloM9/8wSOoZndfzpAjeHZm9vX8=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhc/Y1g/b0vvwu95fBzmEX7w8cu76KNhIq5l I+j3m5oTruJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYXAAKCRDhuBo+eShj d8KzD/41eNC99szTK+r357aOuZhjbUWcBfaAU6NcRV3rPS7DxRpqHPHuQC4LrldgEw5s2KplPUc qpB/YUxQFdtmK5WjxqzYnASzOhghp33LoecJM4JfsPhkQBI5V+Y2xu2C5yOrDvmpBTsijhB+Yf4 Dktc28xWY5XcaEIwAw+6UWicjNjUducMp3Pns+bIAQqxoYwRenWTqbL/CInb4Gelz7PQc5urccy jDx3Nx3DtaD0cIqKKECShxBdkVE0YLz5kFTv9wm8VO/NbjPckXRQmfQ8+eAOzJMuUx9/YsBffAy rSdbIo6bv7bO01LmxuKi5A7mffoN8lEbts4empUi+Om9PcAzyJEsk6t2d0s2d7SKHM2TvE/ETQR 3jwSjztJ1uR0fOJXYKjciDBETZ1BlyZh1frvzBBQGsM2qHi/gvbr8LaDwL8bBubiEdi6VMiBG0+ 9hOX74LuVZ4xVRXfOpcW4MSyjwBgsW7dcae4G8nvUaTuSrKDX7rYWBIBSJOUqNBdvpfpbHyVdfB M53RHvcf005IKbfO7vK2jK2TAq+S+RMsOrTw2guXnvHfEEzluOMI6nR/m3jGSIli6UqcvfyHbaO 0bkPx31tNWCf59AgggQ0EvIhW7bTah8omTgcUWAodMRNJvct07//69Rj2BPjGGD+jEAi5JlISb0 l9TNEwJtVbfy1jg== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a method on `Request` to requeue the request with the block layer. Drivers can use this method to send a request back to the block layer without processing the request. Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq/request.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index e2fafe7737379..af10dcbd3ffd1 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -75,6 +75,18 @@ pub(crate) unsafe fn from_raw(ptr: *mut bindings::reques= t) -> Owned { // SAFETY: By function safety requirements, `ptr` is valid for use= as an `IdleRequest`. unsafe { Owned::from_raw(NonNull::::new_unchecked(ptr.cast()= )) } } + + /// Requeue this request at the block layer. + /// + /// If `kick_requeue_list` is true, this method will schedule processi= ng of + /// the requeue list on a workqueue. + pub fn requeue(self: Owned, kick_requeue_list: bool) { + let ptr =3D self.0 .0.get(); + core::mem::forget(self); + + // SAFETY: By type invariant, the wrapped request is valid. + unsafe { bindings::blk_mq_requeue_request(ptr, kick_requeue_list) = }; + } } =20 // SAFETY: The `release` implementation leaks the `IdleRequest`, which is = a valid state for a --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 0D4452E1EE7; Sun, 15 Feb 2026 23:44:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199082; cv=none; b=eRkZbbQnSjeID0qYgfTt9cltYMWjf4TIPqtmCdBs4ijVZy/Qxq86fSXtXPMsCdJT3HNXnqlUU/bS9DyaLg/1DHvy+Nxkex8lQ91sSADBAV+xLB+EB7LWZvx4lTQHtOYhVxVvQKxrtPJC/hsCIMDtc/cn1AUNRHVwysebSdvc4gg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199082; c=relaxed/simple; bh=aZRrI7grlZwViJknRtkxdybKDycUBf8fCcK8vfkelAQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=VagrpiKe5/eoJgWr2uXYdhPkqLVUD56O+oRpr1W4eEE8ZF1cYIQUEn+Odl0DzAmoDR7UIko5G9wyW5PMb2vxayn7+GyPDAT+P7EBKz3GEyOlLTsKbqZny36GqmN/KhV9Lwye5Cx+cra1SBomMGUCh+VLC90HCmSmxc4fZRHF/w8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=qt0jb3wr; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="qt0jb3wr" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5BAF7C4CEF7; Sun, 15 Feb 2026 23:44:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199081; bh=aZRrI7grlZwViJknRtkxdybKDycUBf8fCcK8vfkelAQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=qt0jb3wrj4xroz5c6uTfqBoK3nmsYUTL/kzMq1GJfaxJYeSbEjWtNEvp/YH6YvxxM oW6NjXLl7C8Rj5Pxuxu0HBqDBlNkBlq++YKeTPlqVXHEnhf6xOuHXJ9izD5oaOnUGA bvfcoNqNIyjyeJ3tKGGbwYfGMgjcsW6qQWgLB+xyxX0xlUeUvecKVrqc/qDIZMKO02 rIBPdpdLnrCD4GIst3Tp36NKgJOyPPDDZmhtJFaRcaxeIRPYvC8hOwMlXMRnkPpFjI x4L3vS6BXD2lBT9tVFGhMNhJBhpjfTDC8axCWVbAjQyVdhqc7ENJAka1OMZsgTOm0j aSriUStk3f5UQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:35:59 +0100 Subject: [PATCH 72/79] block: rust: add `request_timeout` hook 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: <20260216-rnull-v6-19-rc5-send-v1-72-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=6695; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=aZRrI7grlZwViJknRtkxdybKDycUBf8fCcK8vfkelAQ=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhcFdAYDse1qYjlb88yVrs/2r4YJvu3bHRZ0 KLmw+vrTeOJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYXAAKCRDhuBo+eShj d1XmEACnowsioZfBSvToF9sWGL4GZDs4H83IbFZ/qBJH9HYvC5c5e69trcDeXVyiSY9ncJVk1r9 +4dbvcPZFaNP57M40/imGhDc4zJ11mL8d8w8K+JR5QqewEtHm0bY64a5rtg0fvDJQ1T7pLWnexO yzILWHZaFN8cyTAMTBdZmRNqTSv63VOueQSIDjwDlV2alDpJuLQBrV5PPAe3YGzQ++xTpyMHzgH l9puBa+1SzwBEiFg/MX9RFlnl/nyAiTDKPuOlxTsbVKDAf0OkQ5KuhUzXQPD+/1bG83jG08Lls1 o88X+OX6alrNXkGM/aClhpj4zuYxDTFAWLwdqfidg1HJRNnjVWezpOiEZSCqqZyEmjomMixwC8Q Xxjb6bXisCC9AhmXasx7wV49uCS31HIceuMAP/60fPmgbR6vVBP8Ii3Dd6SJlp3o8tA7SkxWjtH 0wgZMQPo5Iiu8dj3uPFx6w20bBhmzCQ/fCiBc+WGRWrnN02JV+1cTwpXWrmHZGhpcLBtCQO6Jvb PXmQi57majPpveWa4LUn0V6Uh/jELxmACJIt5geSbNXeEcbfzKyDh2Q+SpFgbWe71ETkvQGWkin aT1zM0eN4xnGIyVms91lsDKdTkkPMyFkzHp3SBIoa3RO8AV99kwqt7vD4U2RRg2wTtRahiWYp5D 4Z7dUPkBWqPuQjQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a hook for the request timeout feature. This allows the kernel to call into a block device driver when it decides a request has timed out. Rust block device drivers can now implement `Operations::request_timeout` to respond to request timeouts. Signed-off-by: Andreas Hindborg --- rust/kernel/block.rs | 1 + rust/kernel/block/mq.rs | 1 + rust/kernel/block/mq/operations.rs | 78 ++++++++++++++++++++++++++++++++++= +++- rust/kernel/block/mq/tag_set.rs | 1 - 4 files changed, 79 insertions(+), 2 deletions(-) diff --git a/rust/kernel/block.rs b/rust/kernel/block.rs index 6cd83dc75a5eb..d4f09d6fa9f97 100644 --- a/rust/kernel/block.rs +++ b/rust/kernel/block.rs @@ -42,6 +42,7 @@ macro_rules! declare_err { declare_err!(BLK_STS_NOTSUPP, "Operation not supported."); declare_err!(BLK_STS_IOERR, "Generic IO error."); declare_err!(BLK_STS_DEV_RESOURCE, "Device resource busy. Retry la= ter."); + declare_err!(BLK_STS_TIMEOUT, "Operation timed out."); } =20 /// A wrapper around a 1 byte block layer error code. diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index 02d75acfddb3b..41008f9bb32cd 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -135,6 +135,7 @@ pub use feature::{Feature, Features}; pub use operations::IoCompletionBatch; pub use operations::Operations; +pub use operations::RequestTimeoutStatus; pub use request::Command; pub use request::Flag as RequestFlag; pub use request::Flags as RequestFlags; diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/oper= ations.rs index efead98767196..4767be3ad2a5a 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -125,6 +125,51 @@ fn report_zones( fn map_queues(_tag_set: Pin<&mut TagSet>) { build_error!(crate::error::VTABLE_DEFAULT_ERROR) } + + /// Called by the kernel when a request has been queued with the drive= r for too long. + /// + /// We identify the request by `queue_id` and `tag` as we cannot pass + /// `Owned` or `ARef`. The driver may hold either of= these + /// already. + /// + /// A driver can use [`TagSet::tag_to_rq`] to try to obtain a request = reference. + /// + /// A driver must return [`RequestTimeoutStatus::Completed`] if the re= quest + /// was completed during the call. Otherwise + /// [`RequestTimeoutStatus::RetryLater`] must be returned, and the ker= nel + /// will retry the call later. + fn request_timeout(_tag_set: &TagSet, _queue_id: u32, _tag: u32)= -> RequestTimeoutStatus { + build_error!(crate::error::VTABLE_DEFAULT_ERROR) + } +} + +/// Return value for [`Operations::request_timeout`]. +#[repr(u32)] +pub enum RequestTimeoutStatus { + /// The request was completed. + Completed =3D bindings::blk_eh_timer_return_BLK_EH_DONE, + + /// The request is still processing, retry later. + RetryLater =3D bindings::blk_eh_timer_return_BLK_EH_RESET_TIMER, +} + +impl RequestTimeoutStatus { + /// Create a [`RequestTimeoutStatus`] from an integer. + /// + /// # SAFETY + /// + /// - `value` must be one of the enum values declared for [`bindings::= blk_eh_timer_return`]. + pub unsafe fn from_raw(value: u32) -> Self { + // SAFETY: By function safety requirements, value is usable as `Se= lf`. + unsafe { core::mem::transmute(value) } + } +} + +impl From for u32 { + fn from(value: RequestTimeoutStatus) -> Self { + // SAFETY: All `RequestTimeoutStatus` representations are valid as= `u32`. + unsafe { core::mem::transmute(value) } + } } =20 /// A vtable for blk-mq to interact with a block device driver. @@ -496,6 +541,33 @@ impl OperationsVTable { T::map_queues(tag_set); } =20 + /// This function is called by the block layer when a request has been + /// queued with the driver for too long. + /// + /// # Safety + /// + /// - This function may only be called by blk-mq C infrastructure. + /// - `rq` must point to an initialized and valid `Request`. + unsafe extern "C" fn request_timeout_callback( + rq: *mut bindings::request, + ) -> bindings::blk_eh_timer_return { + // SAFETY: `rq` is valid and initialized. + let hctx =3D unsafe { (*rq).mq_hctx }; + // SAFETY: `rq` is valid and initialized, so `hctx` is also valid = and initialized. + let qid =3D unsafe { (*hctx).queue_num }; + // SAFETY: `rq` is valid and initialized. + let tag =3D unsafe { (*rq).tag } as u32; + // SAFETY: `rq` is valid and initialized, so `hctx` is also valid = and initialized. + let queue =3D unsafe { (*hctx).queue }; + // SAFETY: `rq` is valid and initialized, so is `queue`. + let tag_set =3D unsafe { (*queue).tag_set }; + // SAFETY: As `rq` is valid, so is `tag_set`. We never create muta= ble referennces to a + // `TagSet` without proper locking. + let tag_set: &TagSet =3D unsafe { TagSet::from_ptr(tag_set) }; + + T::request_timeout(tag_set, qid, tag).into() + } + const VTABLE: bindings::blk_mq_ops =3D bindings::blk_mq_ops { queue_rq: Some(Self::queue_rq_callback), queue_rqs: if T::HAS_QUEUE_RQS { @@ -508,7 +580,11 @@ impl OperationsVTable { put_budget: None, set_rq_budget_token: None, get_rq_budget_token: None, - timeout: None, + timeout: if T::HAS_REQUEST_TIMEOUT { + Some(Self::request_timeout_callback) + } else { + None + }, poll: if T::HAS_POLL { Some(Self::poll_callback) } else { diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set= .rs index e7d7217da8922..88a425a70b283 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -98,7 +98,6 @@ pub(crate) fn raw_tag_set(&self) -> *mut bindings::blk_mq= _tag_set { /// `ptr` must be a pointer to a valid and initialized `TagSet`. Th= ere /// may be no other mutable references to the tag set. The pointee mus= t be /// live and valid at least for the duration of the returned lifetime = `'a`. - #[expect(dead_code)] pub(crate) unsafe fn from_ptr<'a>(ptr: *mut bindings::blk_mq_tag_set) = -> &'a Self { // SAFETY: By the safety requirements of this function, `ptr` is v= alid // for use as a reference for the duration of `'a`. --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 E2ADD2DC772; Sun, 15 Feb 2026 23:42:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198964; cv=none; b=X2NHgXn0iJQ5R4zHXbNFqnkMEb9HpmWp4saXVj6SHW9OUqYe9UBYmQvilZHse9yyMKmdHMl7K3j7G+ZtWaoSdTbko3S2ZDO/K/oDPPZO04pvyzqiTkTEHkxPRIQiBbEBhBS8ACU8DLs2kT7tf+n4b4S8xUwXWasdbm3MKbtiEi8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198964; c=relaxed/simple; bh=HabGkFaZxpg0TMJ4bmudZjM7RGpHBrmdEEt4p/Jhb1o=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Y9GVyEJcT1FCb8wWYBsfJihWtgH5CeZKFjtHpGmCnQtDKQCSUSWIywLrtHjd82JxzOUHdXezwxCsNxs4T9/BdNq1pg4Y1t36k7KxC+SR4RkQWK6pV0HwpmJRsKQhkmmUEX5dOUHvTzCv/2fPMIWnSecr+vYRqaz4YNVoteWSG6E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=kXxMYA5k; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="kXxMYA5k" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 36E47C19422; Sun, 15 Feb 2026 23:42:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198963; bh=HabGkFaZxpg0TMJ4bmudZjM7RGpHBrmdEEt4p/Jhb1o=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=kXxMYA5k9qgWa5ikrqf3E7noxTHmJP9lj9IGdrolkpPOiCVw8m6LM03P9KRstoyOG SBx3DNAwt64nWIRtYjejcLoEif6QdvvQ+JCkEMl43dd/jSkrS2EaDfGeiToiW7KiUP /c91dRdES0AtltjsdmESioZDNLCnBERqGERE0NESPSGq/fwZqUYAZaI9YdA523S9RL tRRUhkkiyz3OJB1dClxVmZv03z5Xb5k2u9vNPiZI0rusnYiPwQ3Hk3MiWNDel2DPzM Gpe7mZ+K+bffCphn5RAxasPhNGbBC2/8OzJqRN9Wcg+0pWzq3bAcu1m4VnNlgUGiMX ochoOXMZY/E0w== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:36:00 +0100 Subject: [PATCH 73/79] block: rnull: add fault injection support 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: <20260216-rnull-v6-19-rc5-send-v1-73-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=15513; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=HabGkFaZxpg0TMJ4bmudZjM7RGpHBrmdEEt4p/Jhb1o=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklheNbL2fIfLyru1HT+aFI3GFoq8t4w60izcs N0qtuiwoviJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYXgAKCRDhuBo+eShj dyXYD/0TdBR75C0VGxkSg7cAr4tEIvCg9n37n7uhifxV9p7+QDFfZMT+MrJBAEuR9LwkVzR03aw JaqxDKLPERl6EwnX2EWG8L8OiqKCpWWW58LVgIEu/hl7RQncJKW+ZudBvN0N84x2RYXg75YlTns KsprP4FoPUpH2mhwuQtpGQGt8Pr5mSvbxDdLzzekp4g0BmFGDM9Gnkjmh4naEja1jkDDsRpUM8p //pCmqKU3O7LIVOet9NZqIdCzIxCgcUgw94P7Ovto3jNqoLleIUFGwAh+7PDDPo+xZzP7N/RGte 3d9NWFQgFe14NDSks2v1qD/qt3JW8yyK7DCK/tF01ynqy6wdU9k3GYFBZSr9+q/apCzfSM+QVGH 4d/MiCQa4160O/vWUBSM8O/OYPsrCvlx/qfereQiXKxahEmykl+mPJ9rATmyciWdBNxv8SvVdch K+yKxRQW4m79Sj5JKaoo+jgZ8bt37aQrW2UYhm1mspcL5c+9q/BuWfo1fHtQVG/GM23f9058/RJ Vat/WaWPbGR5VOyy9AakaKcvSXhHuPMO6Er9ecXXUD6xFGSc2H1KnsCIO/yHG5nEbgaWHogBJ0g w6Fjin7g0IGdK/1JQ1TFVs/RfWpcozd861+LSuEqw9vJ6GFKAPotmP/QC4xZnEkGqGRhZosM6gH atlS+mokMm4gDGw== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add fault injection support to rnull using the kernel fault injection infrastructure. When enabled via `CONFIG_FAULT_INJECTION`, users can inject failures into I/O requests through the standard fault injection debugfs interface. The fault injection point is exposed as a configfs default group, allowing per-device fault injection configuration. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/Kconfig | 11 ++++ drivers/block/rnull/configfs.rs | 57 ++++++++++++++++++- drivers/block/rnull/rnull.rs | 120 ++++++++++++++++++++++++++++++++++++= +--- 3 files changed, 179 insertions(+), 9 deletions(-) diff --git a/drivers/block/rnull/Kconfig b/drivers/block/rnull/Kconfig index 7bc5b376c128b..1ade5d8c17997 100644 --- a/drivers/block/rnull/Kconfig +++ b/drivers/block/rnull/Kconfig @@ -11,3 +11,14 @@ config BLK_DEV_RUST_NULL devices that can be configured via various configuration options. =20 If unsure, say N. + +config BLK_DEV_RUST_NULL_FAULT_INJECTION + bool "Support fault injection for Rust Null test block driver" + depends on BLK_DEV_RUST_NULL && FAULT_INJECTION_CONFIGFS + help + Enable fault injection support for the Rust null block driver. This + allows injecting errors into block I/O operations for testing error + handling paths and verifying system resilience. Fault injection is + configured through configfs alongside the null block device settings. + + If unsure, say N. diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 424722f01ab8d..b449ac882d961 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -46,6 +46,9 @@ =20 mod macros; =20 +#[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] +use kernel::fault_injection::FaultConfig; + pub(crate) fn subsystem() -> impl PinInit, Error> { let item_type =3D configfs_attrs! { container: configfs::Subsystem, @@ -122,10 +125,44 @@ fn make_group( ], }; =20 + use kernel::configfs::CDefaultGroup; + + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + let mut default_groups: KVec> =3D KVec::new= (); + + #[cfg(not(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION))] + let default_groups: KVec> =3D KVec::new(); + + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + let timeout_inject =3D Arc::pin_init( + kernel::fault_injection::FaultConfig::new(c"timeout_inject"), + GFP_KERNEL, + )?; + + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + let requeue_inject =3D Arc::pin_init( + kernel::fault_injection::FaultConfig::new(c"requeue_inject"), + GFP_KERNEL, + )?; + + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + let init_hctx_inject =3D Arc::pin_init( + kernel::fault_injection::FaultConfig::new(c"init_hctx_fault_in= ject"), + GFP_KERNEL, + )?; + + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + { + default_groups.push(timeout_inject.clone(), GFP_KERNEL)?; + default_groups.push(requeue_inject.clone(), GFP_KERNEL)?; + default_groups.push(init_hctx_inject.clone(), GFP_KERNEL)?; + } + let block_size =3D 4096; Ok(configfs::Group::new( name.try_into()?, item_type, + // default_groups, // TODO: cannot coerce new_mutex!() to impl PinInit<_, Error>,= so put mutex inside try_pin_init!(DeviceConfig { data <- new_mutex!(DeviceConfigInner { @@ -165,9 +202,15 @@ fn make_group( zone_max_active: 0, zone_append_max_sectors: u32::MAX, fua: true, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + timeout_inject, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + requeue_inject, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + init_hctx_inject, }), }), - core::iter::empty(), + default_groups, )) } } @@ -241,6 +284,12 @@ struct DeviceConfigInner { zone_max_active: u32, zone_append_max_sectors: u32, fua: bool, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + timeout_inject: Arc, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + requeue_inject: Arc, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + init_hctx_inject: Arc, } =20 #[vtable] @@ -292,6 +341,12 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { zone_max_active: guard.zone_max_active, zone_append_max_sectors: guard.zone_append_max_sectors, forced_unit_access: guard.fua, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + requeue_inject: guard.requeue_inject.clone(), + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + init_hctx_inject: guard.init_hctx_inject.clone(), + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + timeout_inject: guard.timeout_inject.clone(), })?); guard.powered =3D true; } else if guard.powered && !power_op { diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index db856f03b78cb..b2b089a657f12 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -40,6 +40,7 @@ IoCompletionBatch, Operations, RequestList, + RequestTimeoutStatus, TagSet, // }, SECTOR_SHIFT, @@ -90,6 +91,9 @@ use pin_init::PinInit; use util::*; =20 +#[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] +use kernel::fault_injection::FaultConfig; + module! { type: NullBlkModule, name: "rnull_mod", @@ -205,6 +209,8 @@ }, } =20 +// TODO: Fault inject via params - requires module_params string support. + #[pin_data] struct NullBlkModule { #[pin] @@ -267,6 +273,15 @@ fn init(_module: &'static ThisModule) -> impl PinInit<= Self, Error> { zone_max_active: *module_parameters::zone_max_active.v= alue(), zone_append_max_sectors: *module_parameters::zone_appe= nd_max_sectors.value(), forced_unit_access: *module_parameters::fua.value() != =3D 0, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + requeue_inject: Arc::pin_init(FaultConfig::new(c"reque= ue_inject"), GFP_KERNEL)?, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + init_hctx_inject: Arc::pin_init( + FaultConfig::new(c"init_hctx_fault_inject"), + GFP_KERNEL, + )?, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + timeout_inject: Arc::pin_init(FaultConfig::new(c"timeo= ut_inject"), GFP_KERNEL)?, })?; disks.push(disk, GFP_KERNEL)?; } @@ -315,6 +330,12 @@ struct NullBlkOptions<'a> { #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), expect(unused_variables))] zone_append_max_sectors: u32, forced_unit_access: bool, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + requeue_inject: Arc, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + init_hctx_inject: Arc, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + timeout_inject: Arc, } =20 static SHARED_TAG_SET: SetOnce>> =3D SetOnce::ne= w(); @@ -339,6 +360,12 @@ struct NullBlkDevice { #[cfg(CONFIG_BLK_DEV_ZONED)] #[pin] zoned: zoned::ZoneOptions, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + requeue_inject: Arc, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + requeue_selector: kernel::sync::atomic::Atomic, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + timeout_inject: Arc, } =20 impl NullBlkDevice { @@ -373,6 +400,13 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { zone_max_active, zone_append_max_sectors, forced_unit_access, + + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + requeue_inject, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + init_hctx_inject, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + timeout_inject, } =3D options; =20 let mut flags =3D mq::tag_set::Flags::default(); @@ -402,6 +436,8 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { NullBlkTagsetData { queue_depth: hw_queue_depth, queue_config, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION= )] + init_hctx_inject, }, GFP_KERNEL, )?, @@ -450,6 +486,12 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { zone_max_active, zone_append_max_sectors, })?, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + requeue_inject, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + requeue_selector: Atomic::new(0), + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + timeout_inject, }), GFP_KERNEL, )?; @@ -680,7 +722,9 @@ fn handle_bad_blocks(&self, rq: &mut Owned>, sectors: &mut u32 badblocks::BlockStatus::None =3D> {} badblocks::BlockStatus::Acknowledged(mut range) | badblocks::BlockStatus::Unacknowledged(mut range) =3D> { - rq.data_ref().error.store(1, ordering::Relaxed); + rq.data_ref() + .error + .store(block::error::code::BLK_STS_IOERR.into(), o= rdering::Relaxed); =20 if self.bad_blocks_once { self.bad_blocks.set_good(range.clone())?; @@ -705,6 +749,7 @@ fn end_request(rq: Owned>) { let status =3D rq.data_ref().error.load(ordering::Relaxed); rq.data_ref().error.store(0, ordering::Relaxed); =20 + // TODO: Use correct error code match status { 0 =3D> rq.end_ok(), _ =3D> rq.end(bindings::BLK_STS_IOERR), @@ -730,6 +775,24 @@ fn queue_rq_internal( rq: Owned>, _is_last: bool, ) -> Result<(), QueueRequestError> { + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + if rq.queue_data().requeue_inject.should_fail(1) { + if rq + .queue_data() + .requeue_selector + .fetch_add(1, ordering::Relaxed) + & 1 + =3D=3D 0 + { + return Err(QueueRequestError { + request: rq, + }); + } else { + rq.requeue(true); + return Ok(()); + } + } + if this.bandwidth_limit !=3D 0 { if !this.bandwidth_timer.active() { drop(this.bandwidth_timer_handle.lock().take()); @@ -755,6 +818,12 @@ fn queue_rq_internal( =20 let mut rq =3D rq.start(); =20 + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + if rq.queue_data().timeout_inject.should_fail(1) { + rq.data_ref().fake_timeout.store(1, ordering::Relaxed); + return Ok(()); + } + if rq.command() =3D=3D mq::Command::Flush { if this.memory_backed { this.storage.flush(&hw_data); @@ -778,12 +847,13 @@ fn queue_rq_internal( Ok(()) })(); =20 - if let Err(e) =3D status { - // Do not overwrite existing error. We do not care whether thi= s write fails. - let _ =3D rq - .data_ref() - .error - .cmpxchg(0, e.to_errno(), ordering::Relaxed); + if status.is_err() { + // Do not overwrite existing error. + let _ =3D rq.data_ref().error.cmpxchg( + 0, + kernel::block::error::code::BLK_STS_IOERR.into(), + ordering::Relaxed, + ); } =20 if rq.is_poll() { @@ -861,7 +931,8 @@ struct HwQueueContext { struct Pdu { #[pin] timer: HrTimer, - error: Atomic, + error: Atomic, + fake_timeout: Atomic, } =20 impl HrTimerCallback for Pdu { @@ -886,6 +957,8 @@ impl HasHrTimer for Pdu { struct NullBlkTagsetData { queue_depth: u32, queue_config: Arc>, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + init_hctx_inject: Arc, } =20 #[vtable] @@ -899,6 +972,7 @@ fn new_request_data() -> impl PinInit { pin_init!(Pdu { timer <- HrTimer::new(), error: Atomic::new(0), + fake_timeout: Atomic::new(0), }) } =20 @@ -953,6 +1027,11 @@ fn poll( } =20 fn init_hctx(tagset_data: &NullBlkTagsetData, _hctx_idx: u32) -> Resul= t { + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + if tagset_data.init_hctx_inject.should_fail(1) { + return Err(EFAULT); + } + KBox::pin_init( new_spinlock!(HwQueueContext { page: None, @@ -1013,4 +1092,29 @@ fn map_queues(tag_set: Pin<&mut TagSet>) { }) .unwrap() } + + fn request_timeout(tag_set: &TagSet, qid: u32, tag: u32) -> Requ= estTimeoutStatus { + if let Some(request) =3D tag_set.tag_to_rq(qid, tag) { + pr_info!("Request timed out\n"); + // Only fail requests that are faking timeouts. Requests that = time + // out due to memory pressure will be completed normally. + if request.data_ref().fake_timeout.load(ordering::Relaxed) != =3D 0 { + request.data_ref().error.store( + block::error::code::BLK_STS_TIMEOUT.into(), + ordering::Relaxed, + ); + request.data_ref().fake_timeout.store(0, ordering::Relaxed= ); + + if let Ok(request) =3D OwnableRefCounted::try_from_shared(= request) { + Self::end_request(request); + return RequestTimeoutStatus::Completed; + } + // TODO: pr_warn_once! + pr_warn!("Timed out request could not be completed\n"); + } + } else { + pr_warn!("Timed out request referenced in timeout handler\n"); + } + RequestTimeoutStatus::RetryLater + } } --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 062F31FE45A; Sun, 15 Feb 2026 23:40:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198850; cv=none; b=Vi8zfFo5HamtqymMcQn3D4glFfbVAZgREa8jJ1MzpUQhsBbdace6DCnbitOa8p7EiFBriM2aaCx+yXRMcD4oLM4DOUrKDY3XPG+U/eLoCg1tZe52Mx6HpeGtGQUIIlQoLbkIE1M7vS4qXOHJmijzEcEwLVwmdUqREEiWlG0QCIU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771198850; c=relaxed/simple; bh=jDbHu1Zje3UqoEo8ZzyBlnA9FXargy0KlZo0vzXHkD8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=mAyqjxFUuZa1cGqA1po2Mrexus5c3ReLBW43U5zlNn4pNZ5DHeRYmQ1NbYqD/k2KS1n6HRseyikXpBcqTP1MUMTIIeLosEDxG1DpMDqN+Vv12Sk/zGVzKZLIx7p8S9oKNWyLtuUDXioraUdWUxVG8+yfhp//NnMkkRSysE1Oc/4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=OOJUz8jH; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="OOJUz8jH" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 47D25C4CEF7; Sun, 15 Feb 2026 23:40:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771198849; bh=jDbHu1Zje3UqoEo8ZzyBlnA9FXargy0KlZo0vzXHkD8=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=OOJUz8jHaC7LP3EtCPD65pnV+OuuUHg0BfVaV60OFiuMPeM7rly6GhALuF7lje2WE iVrI4rr2Vz940v56ePbsz1FEK5OVUdh3FF9wwxV1wTkWj2NAygLoyr4CWMomQIjnMh Rj82CkavoBwm/WoQPLybya11197FGj4lR8QIRRtdma1KUCqceLm0vZ9dil4TWYorw1 /yTkq7IdNoMo69hqDeiunR+YRM5gNd1MkqAfztRamRz/4iJcK3jF21ZmfTxqlmNckg H7tpk+5FJbpAKhphMWEOebbbGY5Hs0H6S/bhrHYtVfhHhyCnINeGvDral+4gSo49oY 7uVPpXwDvEtaQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:36:01 +0100 Subject: [PATCH 74/79] block: rust: add max_sectors option to `GenDiskBuilder` 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: <20260216-rnull-v6-19-rc5-send-v1-74-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1622; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=jDbHu1Zje3UqoEo8ZzyBlnA9FXargy0KlZo0vzXHkD8=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklheeOWsZ5urW4C1UgIxuTPpIyyfBrdvCKsur KSUpEKcmhiJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYXgAKCRDhuBo+eShj dydFEAC1bjAjzG0WwgcS1zrXw0+2c39sfZ4Y4rB+xPXYebButMnzTSTL4dU+tGbyFFq5x4d0g90 j3OCa2gMD8rbHleMZ9yo5J1XCRE/GWd5vhplDWCPHVOcLQaCFzwL4tw90z/FgQ5J0nswbXtg0Su NgHGvdb2an2pEy+z+tYNqQoP3cYGmGUyFXzXHI6iAERegmXrj959KcIKctyA14K2dAxK5EPi3IU FnFqin4bCemG/IK516tUXoZ9Z0N1tV/VzoLHVoc6Es9cEO16U7RzWjrdQPWTZGBo28n0upPGovT 42SPvlzRwb83Btcumna2P4wGLc6yAV7Mu+eafxLfUeZFadRKK3qD2J+06pjJsixpUeItZPiVETe U8it+EgneiTQVhq4WWit6YWSUYhrWZsN6ICUtfKwX0s2PYgjsHfTnBStUd6IteSPR4zi1jw0Z06 W73zvD84pdQ6PxRJphLZm/5LopyrH7MzlvOrBIR+7uwtG80iX9aqC1SRJWy7kltk2HPD+caXBm2 J4plRlTJtkk9avoDHBlpm7+7MiUkzz9RZJIXqVveyHqCJrY5tC4SMHSaX2+S9V5wnoh/c3684OB pQ1sRV/jGYFJ7K5I3ZWZzMeN9BPWcTmZkhbO3kYcsOB+Og/RxCRqAqmBlbziBl3ytAw0XluMvaK NLxY6LENQoYm94w== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Allow drivers to set the maximum I/O size when building a `GenDisk`. Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq/gen_disk.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_di= sk.rs index b5291bc3e051d..b428e3f4207f4 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -36,6 +36,7 @@ pub struct GenDiskBuilder { zone_append_max_sectors: u32, write_cache: bool, forced_unit_access: bool, + max_sectors: u32, _p: PhantomData, } =20 @@ -55,6 +56,7 @@ fn default() -> Self { zone_append_max_sectors: 0, write_cache: false, forced_unit_access: false, + max_sectors: 0, _p: PhantomData, } } @@ -159,6 +161,12 @@ pub fn write_cache(mut self, enable: bool) -> Self { self } =20 + /// Maximum size of a command in 512 byte sectors. + pub fn max_sectors(mut self, sectors: u32) -> Self { + self.max_sectors =3D sectors; + self + } + /// Build a new `GenDisk` and add it to the VFS. pub fn build( self, @@ -178,6 +186,7 @@ pub fn build( lim.logical_block_size =3D self.logical_block_size; lim.physical_block_size =3D self.physical_block_size; lim.max_hw_discard_sectors =3D self.max_hw_discard_sectors; + lim.max_sectors =3D self.max_sectors; if self.rotational { lim.features =3D Feature::Rotational.into(); } --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 F41552E92B7; Sun, 15 Feb 2026 23:46:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199191; cv=none; b=acnKk9zNDLUxau5vSmnWfNm9EGeI1OJxjNwCIG1vrvzyZpdfOHjvQetScQnj60ywVXimBbqsFFaKWLBxMDgbKNOD29LhDa+dzmcvhAPNrwD2NtsYcVO7sVDi3BJPjCuJ7A+oRy0pZtp6JOCbU8N1h4Ttog+HE3JAQqb9RS6F0ZY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199191; c=relaxed/simple; bh=APKT03ZiQU135E3WUwHyPYiO1viCqNZ5coeNcIBfq84=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Ie3w/NdmyXlFs7B+EtQIYdLe19P/4rI3aHi+S9XoyBwZbO9USuER/HdzSQnzMk2Al6RHY5WP4l5EdUi6q/68DKMDu4lgrVPJ8PPhaNY21AS1Y1QqQklH6zN3R8r5NNvJEaKSHdjeJ5K1EsWiIwqpa1vrZziPBa/2oBdfF0rsBxQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=HgADxpSr; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="HgADxpSr" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8ADCEC4CEF7; Sun, 15 Feb 2026 23:46:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199190; bh=APKT03ZiQU135E3WUwHyPYiO1viCqNZ5coeNcIBfq84=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=HgADxpSrEK++iloNXm0dnv+lPw8PVJH6rkUFydSeWsUY3/vjJ7KQzLcayU1lcgIjr Peoe0Ghk8slIlwenivzdI5Z6QI8vSefKN+hFcJP6jzjYtIgsAeOKMWu/0t+9ZJuQIj XCQLAuAavYqo3FyRh5jcob4tOdrSrvbSF4RENPBNaaDOMqUUeWvZ/0TKFKyIiU+pJD O18RmlfqA+4HUxYEuKNhnpZ5vDSHmiK0NsYqk6+hv+50SW70K+hNzCSrRxcq6JoaWT JSNm1Zrr9uRP9arGCa9VkHkv/EBo++kVwqlNVSww3kjVdNqTAqizOKVS8jGJQyiuBf 3WUMyModOsOLg== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:36:02 +0100 Subject: [PATCH 75/79] block: rnull: allow configuration of the maximum IO size 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: <20260216-rnull-v6-19-rc5-send-v1-75-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=3986; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=APKT03ZiQU135E3WUwHyPYiO1viCqNZ5coeNcIBfq84=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhfgXdxK7ttXIOY8hQZaSYd5Mu4euKOGaiXx 5uLWTbYb22JAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYXwAKCRDhuBo+eShj d40HEACZ5T4gkVkYvJNBnOSrIznKGWvjkIyhCyZHxOH9+g7Y0MC6tHeoEYprX5ajdXLwiM57ykV 20YFMSaXWARI/zRNd0iBhRfr2sLliHiEAjhALtiU/exWz84ntqj7x91LQfT68LO2ssjRBhvqW2O DYM9rEfZqTmuCK1cBFkFWPAmcZ0xaZGncNLwz5sK5mgrlydVPiYj/rjQI70Jk/IozMq2lVnn8ol mdx4uQnQ6SneDipQdYxwbHO5fULYmB6KzyVvu/7H5J+nKpwyvJZj6AoJ3XcnutJNMuIH9TwLrH9 DzM6h0CHQI0Oufl1qnf8mZ0cV/pnKKCPE6LftOZ1rAFQ6rvKJPVBOJWLxbJlIvlxqOERfHWyGdY QV+1Bt75TnNe2nufG3fkconWds293dATdw0C625tXnqC7ybMqWuXCYY9v9aRm7GQ/r/814TuHGG SljZZ1Dh2d+I3fl2I0exPjJAREe7FQ6nQVggS7sh7w94+g7oypSNUz/QNrm3VnCZE33gkE+/QM6 a6cDED5Iqkm8vHEkr0+kK9cmufDBHfU47j+XDBLPNTi5Ro7t0T5tlFbX6ee8+kGftqnU2/p5kN0 wmpNcSwKJ7vTiJICdhewvzxAlmuHHosKs18vhHQ0/PTGuknXPTOBLb1Ef7+fdJ7ZIsCadz1Ry0C ilaIYwkI9anAQuQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add module parameter and configfs option for controlling the maximum size of an IO for the emulated block device. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 5 +++++ drivers/block/rnull/rnull.rs | 10 +++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index b449ac882d961..9eaace8b4257e 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -122,6 +122,7 @@ fn make_group( zone_append_max_sectors: 26, poll_queues: 27, fua: 28, + max_sectors: 29, ], }; =20 @@ -208,6 +209,7 @@ fn make_group( requeue_inject, #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] init_hctx_inject, + max_sectors: 0, }), }), default_groups, @@ -290,6 +292,7 @@ struct DeviceConfigInner { requeue_inject: Arc, #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] init_hctx_inject: Arc, + max_sectors: u32, } =20 #[vtable] @@ -347,6 +350,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { init_hctx_inject: guard.init_hctx_inject.clone(), #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] timeout_inject: guard.timeout_inject.clone(), + max_sectors: guard.max_sectors, })?); guard.powered =3D true; } else if guard.powered && !power_op { @@ -624,3 +628,4 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { }, } configfs_simple_bool_field!(DeviceConfig, 28, fua); +configfs_simple_field!(DeviceConfig, 29, max_sectors, u32); diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index b2b089a657f12..495a810f4f4e1 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -206,6 +206,10 @@ default: 1, description: "Enable/disable FUA support when cache_size is us= ed. Default: 1 (true)", }, + max_sectors: u32 { + default: 0, + description: "Maximum size of a command (in 512B sectors)", + }, }, } =20 @@ -282,6 +286,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { )?, #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] timeout_inject: Arc::pin_init(FaultConfig::new(c"timeo= ut_inject"), GFP_KERNEL)?, + max_sectors: *module_parameters::max_sectors.value(), })?; disks.push(disk, GFP_KERNEL)?; } @@ -336,6 +341,7 @@ struct NullBlkOptions<'a> { init_hctx_inject: Arc, #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] timeout_inject: Arc, + max_sectors: u32, } =20 static SHARED_TAG_SET: SetOnce>> =3D SetOnce::ne= w(); @@ -407,6 +413,7 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { init_hctx_inject, #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] timeout_inject, + max_sectors, } =3D options; =20 let mut flags =3D mq::tag_set::Flags::default(); @@ -502,7 +509,8 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { .physical_block_size(block_size_bytes)? .rotational(rotational) .write_cache(storage.cache_enabled()) - .forced_unit_access(forced_unit_access && storage.cache_enable= d()); + .forced_unit_access(forced_unit_access && storage.cache_enable= d()) + .max_sectors(max_sectors); =20 #[cfg(CONFIG_BLK_DEV_ZONED)] { --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 C44642D949F; Sun, 15 Feb 2026 23:43:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199016; cv=none; b=QeeefOs2dfoiqah0xVNDljXHJeOj7+XPn1oeCuPC7vRfBGc5ZetWK6yzqGkYrvWycBPndhkxbzWtbXPdPigW17CDuWHLU3KQDIAKzo7wn3AgsPka6/yA6S1guMf0+VhVp+VpPwJKZevUkCDOu7wKb875L5gSlprnR7Zuz+tUVSQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199016; c=relaxed/simple; bh=c72UaM9t8Nsnn6kAPdjGxkpAM0WUzhWF4XrAzC0RQao=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=A1VCdCb4RYgiky6lPbW4YxvJjkfwApo78xpDoOkVbnbiGX4eVdoVbJxNPOgbb6svvARyaBKRVJjR9wT0QBSnjBH3CIrO3Y2vbbUHSDm2VJhZtjKPFMCCEqKWDN1LwY35mbz8mCa+PBYwbnLDp7ltAherHdsTzU3vjmRPQtcYARk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=no3QM/pF; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="no3QM/pF" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 424BCC4CEF7; Sun, 15 Feb 2026 23:43:32 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199016; bh=c72UaM9t8Nsnn6kAPdjGxkpAM0WUzhWF4XrAzC0RQao=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=no3QM/pFs9zo1QK0k4glZQiApqW0oX1HM3K3pehWTOoTK5+CankPp2HTPq8P+QJZ+ I17e+CXbn9dYJlnxN8fCWL1TnXQZZXQwouZxjV3r2Wn+cQzWWKabGQ5p7t986WqRD2 NDMgiLh3v+c5dRmsYevQgsCk1VJC2rp7r10RHZB6sQBgTjLRKa9UEPr6ThOJGONVdc dFkJuVEwknzqzZBZFc172OIF1y9A/5EGt4KdPDoabnZ5OCeKoZ+8RoWxaxGXRZ1qNo oUTK9FQ7aoNJSveQ7CDjduiwvsbk+HJmyRmdxAquPdJ+4LmgOuN6UFpSppPaQ2205n P9k3NHHKl6caA== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:36:03 +0100 Subject: [PATCH 76/79] block: rust: add `virt_boundary_mask` option to `GenDiskBuilder` 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: <20260216-rnull-v6-19-rc5-send-v1-76-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1933; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=c72UaM9t8Nsnn6kAPdjGxkpAM0WUzhWF4XrAzC0RQao=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhgs4wMS8YB3muuW0aSQ3HICO4ZNqX1ZK51R 3nW17TLDTyJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYYAAKCRDhuBo+eShj d8CpEACJwP4yRNF/vxN+0m7tnWXUQpbGdqaRTYay3DIJwmRNjTfNA7gS8JGyX2CboYyVhnlfOXd FjSGLFlqVkN3pQXpOwbJaiA8fEqbo0/a60mZW9b7TMJal9zLIjyn+kWTUl+Y5vEsNQt73qrqPGO 5vsfhqkiGrG3tKZy10A6l2LP+VEr2pIla3wU5Ze14AgIsFUfxCAYrFXYUMwsdmv6LEKL7zKPyU0 iM6d6XhpXXDMeJpDfX+F58ijvk4e9eNqVZBikMKGvkTDMZAi8moJYOSWmxqETcS/8mm/xW+kgla oksaa54icWULPIQPxed8y8yJdx8GWYsSLAGKDuFjZaw29DGI0ZmbVLf0/NaKc2SZE9DTtaqe9H4 tkO/gfW6QBYKo6H5LK3sxEDslJuwmm4rqiJGW5RJUdsjTBkVUPYbyR19vZbBcLCJzsao9Jng8LX TrnnOlh5wBVGRUEmbuQVjz26NFMO3uVUfrmB5+a3dT39tFxhQbi1hs/R0DeouVUFDv4N5iYKpJc CEAJyu7bNfnrGL4p2EoIjEkCw3HWLXrAA3Cs4TYuVo9N6ES3vxwhbAy+zrdjpp40k5E9Gc80PEh Rq5nV4quXuG0aeW14mY2CfORzn5CxSB4H7jMdFo930kvb5zH7nsKze2ly7FQbQTHmbiQO/lYFmy hb9ZpII2eAkHWYQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Allow Rust device drivers to set the `virt_boundary_mask` property for block devices. Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq/gen_disk.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_di= sk.rs index b428e3f4207f4..bf4775dbd6d25 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -37,6 +37,7 @@ pub struct GenDiskBuilder { write_cache: bool, forced_unit_access: bool, max_sectors: u32, + virt_boundary_mask: usize, _p: PhantomData, } =20 @@ -57,6 +58,7 @@ fn default() -> Self { write_cache: false, forced_unit_access: false, max_sectors: 0, + virt_boundary_mask: 0, _p: PhantomData, } } @@ -167,6 +169,15 @@ pub fn max_sectors(mut self, sectors: u32) -> Self { self } =20 + /// Set the I/O segment memory alignment mask for the block device. I/= O requests to this device + /// will be split between segments wherever either the memory address = of the end of the previous + /// segment or the memory address of the beginning of the current segm= ent is not aligned to + /// virt_boundary_mask + 1 bytes. + pub fn virt_boundary_mask(mut self, mask: usize) -> Self { + self.virt_boundary_mask =3D mask; + self + } + /// Build a new `GenDisk` and add it to the VFS. pub fn build( self, @@ -187,6 +198,7 @@ pub fn build( lim.physical_block_size =3D self.physical_block_size; lim.max_hw_discard_sectors =3D self.max_hw_discard_sectors; lim.max_sectors =3D self.max_sectors; + lim.virt_boundary_mask =3D self.virt_boundary_mask; if self.rotational { lim.features =3D Feature::Rotational.into(); } --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 3C0522DEA75; Sun, 15 Feb 2026 23:43:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199002; cv=none; b=roQJNEI0bcPEtfUuyhzqqOVYRTCPxrKgPuxfV95Z3Kt4zNfAf7k9z+DfYwT1CTsfHvvl6jR5sDh46k4vDJBpnbOTyzPqLFaKg1qMrJXvXJLL798+9aapHcEcDoy2SOZ7SpD9gESd4p9ZyySUby2wIQqwIn8Ln/1gSg/1qPNHE1Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199002; c=relaxed/simple; bh=u9tAIyBFICh0h8M21rtuVjobkuPmRcCa8Z1Q+dHSvbY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=u+3YTljDN7zg1QH1oe0YPDBbIjXCmi3Omh1EsL7PY0YdGwC0cJixdAaWZhvOpmBxFFPqjpHNCsP+70fw62xmG5sAZx0YuZ+AJGN+L5i8zaOGe7Bwfvlrlx4fEyjtJge/0xVOJ339goWaTOnFDE5sIiRPV9krt8jmPoFepeGA5iQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=mn0Sci4k; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="mn0Sci4k" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A90CDC4CEF7; Sun, 15 Feb 2026 23:43:17 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199001; bh=u9tAIyBFICh0h8M21rtuVjobkuPmRcCa8Z1Q+dHSvbY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=mn0Sci4kIejuXJiTiTwdIfMXc3NqoDFy3RaGKisDg44zwaa3hSX3aAZrmGMOb1eec 7x+6SC3q5Z+9cLM/tkFHnalMzYwYLe78vwFaM0XEAnZ8+1lB9viOpgzktWVz+80QZT QSjgz6a5F8xIzO2TJLivOQ5kXo3xCpVQyZLa6f2X+coFC2pTG1ODGvT4GWK+/K94jm 7ynXUlozo6wQAqE2IrfGh6EGsOkn0s0GY+hz9LRGPMxrlT0g5oNpLLgDmCImMpcnly IONfgmAV92usz5sE83QOlBGxkJo1gjwtb1SDOuheDJ6JlmZ5L6opBfWPB60UCYcCWV yH9X/rnEcjlJg== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:36:04 +0100 Subject: [PATCH 77/79] block: rnull: add `virt_boundary` option 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: <20260216-rnull-v6-19-rc5-send-v1-77-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4488; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=u9tAIyBFICh0h8M21rtuVjobkuPmRcCa8Z1Q+dHSvbY=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhhlk7rsPagv2Uwv84amFbhOdhB2bAscIG/l qV12jhD6D2JAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYYQAKCRDhuBo+eShj d39FD/96ZYUm8hNRgfOmyMX5tCjDFNgsfNiS40He5tLPYN/6WQj6DrGgYwv+z1pGIP5EDiWlZ09 laL9bCVVPzdOdm+xGHJM3oRPpcjUgp3vfQlfL1sMp1+gebfGvFcb1pRWX7kDloy/79czcjq8ZZZ Ew2ZOAAd4zYAA8N+l182+vgq8BWrVmS3t/zpZaIvqxD+TjtM1RhHdgQnlEbAEF0CWMBf9GE3mJ0 v8H29/X1YY1Qx+tcEyZ6y0J2cIFtmGzkMTSOMZVs2edeuuBCFYlnHTNDNVqAXxp7iG7CdqFbtiY fxkRP8jEYWWY+GGJJm6Vx4Hsgbwph2axGY1gT+jwIj8zxjIUbteI3xdD8ODOACOLRQjnzF2NG0G nzRtBJa3Ioc1hCdZPK8Ji3+re+kKP1h0aQ9mArwvdEcp7gnkeUXwTn6fFd6DUE1CSDuKr2p82z2 MHFJ3Bc/BSVGBr/tK/GTTEMg/60SSV0z4YUwBDJHFvJ2AkOblvrgm+xvS3mzdYKXrwfc/T29YyK 3TkuOp/0brAa6hgft36PbxJ0BU8NNFnhDc9FLu2y69Wubmb1BobDHlKRuxuL8+fbCePwd6eAAtp SCc2W5iseGlDBlHXIR9WqgzbOkp2HJCUcxX2S6O4WUSXTGW67mJKb56Ha8shPJq/utfHM3KDuH5 CBlxRfFuSrMvDDg== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a configfs attribute to configure the virtual memory boundary mask for the rnull block device. This allows testing how drivers and filesystems handle devices with specific alignment requirements. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 5 +++++ drivers/block/rnull/rnull.rs | 17 ++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 9eaace8b4257e..8b9ad8a1a243b 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -123,6 +123,7 @@ fn make_group( poll_queues: 27, fua: 28, max_sectors: 29, + virt_boundary: 30, ], }; =20 @@ -210,6 +211,7 @@ fn make_group( #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] init_hctx_inject, max_sectors: 0, + virt_boundary: false, }), }), default_groups, @@ -293,6 +295,7 @@ struct DeviceConfigInner { #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] init_hctx_inject: Arc, max_sectors: u32, + virt_boundary: bool, } =20 #[vtable] @@ -351,6 +354,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] timeout_inject: guard.timeout_inject.clone(), max_sectors: guard.max_sectors, + virt_boundary: guard.virt_boundary, })?); guard.powered =3D true; } else if guard.powered && !power_op { @@ -629,3 +633,4 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { } configfs_simple_bool_field!(DeviceConfig, 28, fua); configfs_simple_field!(DeviceConfig, 29, max_sectors, u32); +configfs_simple_bool_field!(DeviceConfig, 30, virt_boundary); diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 495a810f4f4e1..dfdb56bd76f52 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -28,7 +28,10 @@ BadBlocks, // }, bio::Segment, - error::{BlkError, BlkResult}, + error::{ + BlkError, + BlkResult, // + }, mq::{ self, gen_disk::{ @@ -53,6 +56,7 @@ impl_has_hr_timer, new_mutex, new_spinlock, + page::PAGE_SIZE, pr_info, prelude::*, revocable::Revocable, @@ -210,6 +214,10 @@ default: 0, description: "Maximum size of a command (in 512B sectors)", }, + virt_boundary: u8 { + default: 0, + description: "Set alignment requirement for IO buffers to be p= age size.", + }, }, } =20 @@ -287,6 +295,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] timeout_inject: Arc::pin_init(FaultConfig::new(c"timeo= ut_inject"), GFP_KERNEL)?, max_sectors: *module_parameters::max_sectors.value(), + virt_boundary: *module_parameters::virt_boundary.value= () !=3D 0, })?; disks.push(disk, GFP_KERNEL)?; } @@ -342,6 +351,7 @@ struct NullBlkOptions<'a> { #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] timeout_inject: Arc, max_sectors: u32, + virt_boundary: bool, } =20 static SHARED_TAG_SET: SetOnce>> =3D SetOnce::ne= w(); @@ -414,6 +424,7 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] timeout_inject, max_sectors, + virt_boundary, } =3D options; =20 let mut flags =3D mq::tag_set::Flags::default(); @@ -512,6 +523,10 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { .forced_unit_access(forced_unit_access && storage.cache_enable= d()) .max_sectors(max_sectors); =20 + if virt_boundary { + builder =3D builder.virt_boundary_mask(PAGE_SIZE - 1); + } + #[cfg(CONFIG_BLK_DEV_ZONED)] { builder =3D builder --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 DF9E82EA732; Sun, 15 Feb 2026 23:47:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199230; cv=none; b=Jyhjd79JSG6OPEhfI4Kwqj0MsJNrR5uoXeKSIVO5qewiiehuOa9YjQMcB8Ui4S+4ChTCS2ME3b7gQdHJ9pd1Ix+Eq3EJXcuBCW5ggFyqRahTOvtxaaY1tbIuH4gASCca8BgUb1M5NZol5wR2p3iEKLfc7xYZxQFej+bMf0MfDqU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199230; c=relaxed/simple; bh=0D920SHw42YiR49yYlbJhJD7PJMrwKoVYcH+BNnX7MQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=NcJ+db5u2T/4zot72MWTV/nqMR9+qROwqnt5/ErsUrydg/mLv25SVRDWVqU+UfTynGjfvUQOEeUxI4cgRSFlrnNjyK1Xi0GZuRuXSTnkopmoBsU4jyApEz7pUJHIvsz1I9OeuIGsm5U5UND/cjyjoeUbn7x+7dOUxPOOL+a51cs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=fEShHWH1; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="fEShHWH1" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 46BE5C4CEF7; Sun, 15 Feb 2026 23:47:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199230; bh=0D920SHw42YiR49yYlbJhJD7PJMrwKoVYcH+BNnX7MQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=fEShHWH1UvscSbdGktUgMloxWJWkyUCQj3znuqiDrFTbtn6av7N2wWRjQLUxyE9rb jy9mjY8bK3oaMtiuEnib6D/O6wthIYzd+5PMm+ou7WJexXr8qk8T6vGwvDQ+ueciPC imNE5o+5NO39dH8E5UssFNyMSsabrmiR5UU6QKOI4eLN10q/4jEWCzfNNCiZ1E4Y7S ucnRpLXck5OjLMwjgMl89cse59c1ZZ2nWyGG+zYdDidkOigAw+4SxPNvz1I0RBlOh4 rB3fum8FwTBjF8wj0QEsF6qZDl9tXT2ajlbM5mnmQJGTOzOv1BuE5mG7fG8NifUg3O dZotY7KQa6uLQ== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:36:05 +0100 Subject: [PATCH 78/79] block: rnull: add `shared_tag_bitmap` config option 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: <20260216-rnull-v6-19-rc5-send-v1-78-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4667; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=0D920SHw42YiR49yYlbJhJD7PJMrwKoVYcH+BNnX7MQ=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhiKZrX3/G4pA3JkHu1CIOx58kApLAWljhDP YWOJYIICo6JAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYYgAKCRDhuBo+eShj d9SlD/9RmbXJFOgg9x0t/IP45eYn4VRYOpP8Ba54ODjKjkD838myUazuaXpFlcFQXsHueu8qRmy 7vh7NOH2AWPtw8AzQWr05CAjaM4NYWoCMtyr3+nufR4dMnWXLvYi3gxoXla2QriJWBlzlP6vuF3 3U4mAivhbzhQOVyfNF3ZFP0hd9mGj7MLd37B4CePJaQb7eNRjp1liLDNSm28QZb73E0wrAGEYiv gfA5b8Q+DHEoRn9rF22rzxwJ6XouTsZ9iA/48QHS8iE7w+AhOCpAfxDwk2rqX6wH6NPLklduGV9 jq8ar4HB/jervqMppRZNtIv4/qwwGybA0TEP/gEzKO8qu3ScUqEtaWlIZrz9vyUxDV7voQZybPd 3ZSS/StzDmlux6ZmM/tBJDxyHh9kDRIFMqMhroe40bPk2HaedNo93JJOu56JylGRvGLDBeAJ2KR 0rsTkdh1rbLBpxH8/UikssDCRmti5wvgxOzzhW5dv/icaTXJqcbwN87USsY5tlGqUQ22jqdxtEl VmN1pUHOJrWPaLx5yUFlTPY6c0EW+dwmj6H9GqsqXXUnD2s7nyJDfkkz4g7oyVg5mP7QM8Yix8j ocWTju9O6PISgpRNPZJYKht9iULsjgpcnJe2hcHeGtFYQLHp9Dy+YRTdxes9hO1W8sZgbI3fymU KhBc3lLbvPB0bFw== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a configfs attribute and module parameter to enable the `BLK_MQ_F_TAG_HCTX_SHARED` flag for the rnull tag set. When enabled, a tag bitmap is shared across all hardware queues. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 5 +++++ drivers/block/rnull/rnull.rs | 11 +++++++++++ rust/kernel/block/mq/tag_set/flags.rs | 4 ++++ 3 files changed, 20 insertions(+) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 8b9ad8a1a243b..395da68d96dc6 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -124,6 +124,7 @@ fn make_group( fua: 28, max_sectors: 29, virt_boundary: 30, + shared_tag_bitmap: 31, ], }; =20 @@ -212,6 +213,7 @@ fn make_group( init_hctx_inject, max_sectors: 0, virt_boundary: false, + shared_tag_bitmap: false, }), }), default_groups, @@ -296,6 +298,7 @@ struct DeviceConfigInner { init_hctx_inject: Arc, max_sectors: u32, virt_boundary: bool, + shared_tag_bitmap: bool, } =20 #[vtable] @@ -355,6 +358,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { timeout_inject: guard.timeout_inject.clone(), max_sectors: guard.max_sectors, virt_boundary: guard.virt_boundary, + shared_tag_bitmap: guard.shared_tag_bitmap, })?); guard.powered =3D true; } else if guard.powered && !power_op { @@ -634,3 +638,4 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { configfs_simple_bool_field!(DeviceConfig, 28, fua); configfs_simple_field!(DeviceConfig, 29, max_sectors, u32); configfs_simple_bool_field!(DeviceConfig, 30, virt_boundary); +configfs_simple_bool_field!(DeviceConfig, 31, shared_tag_bitmap); diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index dfdb56bd76f52..5bf965908ef63 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -218,6 +218,10 @@ default: 0, description: "Set alignment requirement for IO buffers to be p= age size.", }, + shared_tag_bitmap: u8 { + default: 0, + description: "Use shared tag bitmap for all submission queues = for blk-mq.", + }, }, } =20 @@ -296,6 +300,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { timeout_inject: Arc::pin_init(FaultConfig::new(c"timeo= ut_inject"), GFP_KERNEL)?, max_sectors: *module_parameters::max_sectors.value(), virt_boundary: *module_parameters::virt_boundary.value= () !=3D 0, + shared_tag_bitmap: *module_parameters::shared_tag_bitm= ap.value() !=3D 0, })?; disks.push(disk, GFP_KERNEL)?; } @@ -352,6 +357,7 @@ struct NullBlkOptions<'a> { timeout_inject: Arc, max_sectors: u32, virt_boundary: bool, + shared_tag_bitmap: bool, } =20 static SHARED_TAG_SET: SetOnce>> =3D SetOnce::ne= w(); @@ -425,6 +431,7 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { timeout_inject, max_sectors, virt_boundary, + shared_tag_bitmap, } =3D options; =20 let mut flags =3D mq::tag_set::Flags::default(); @@ -437,6 +444,10 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { flags |=3D mq::tag_set::Flag::NoDefaultScheduler; } =20 + if shared_tag_bitmap { + flags |=3D mq::tag_set::Flag::TagHctxShared; + } + if home_node > kernel::num_online_nodes().try_into()? { return Err(code::EINVAL); } diff --git a/rust/kernel/block/mq/tag_set/flags.rs b/rust/kernel/block/mq/t= ag_set/flags.rs index 1fca4fcb4dd20..cc5f54781cfd8 100644 --- a/rust/kernel/block/mq/tag_set/flags.rs +++ b/rust/kernel/block/mq/tag_set/flags.rs @@ -19,5 +19,9 @@ pub enum Flag { /// Select 'none' during queue registration in case of a single hw= q or shared /// hwqs instead of 'mq-deadline'. NoDefaultScheduler =3D bindings::BLK_MQ_F_NO_SCHED_BY_DEFAULT, + + /// Use shared tag bitmap for all submission queues. + TagHctxShared =3D bindings::BLK_MQ_F_TAG_HCTX_SHARED, + } } --=20 2.51.2 From nobody Fri Apr 17 20:22:42 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 AA7A92BEC34; Sun, 15 Feb 2026 23:44:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199041; cv=none; b=c8zm/U3hcWxNrB2DkzhqWTlbOkGZJI2nqQa50NXZgoR40cKGBK5uite8skj1z3kcYxh95V2qwVIHhTDkXydbgItGBtdj2CO+APn7aC0poTTmeykySoDyhBaOuuTS9aaK2N/x9M6SWed6jXB4KhkhwVC13NyCxc05lHc0o6Nl4fM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771199041; c=relaxed/simple; bh=3/is2sfDJmdRy9QPPG2gvTdPrheCZt7kkxfe/Rrhb04=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=tWF3b6Pl7dM3a2SWavlP884LO1RtzQrS9Bg559mktgGYuyJrz2k99kY13W8yYObrnaQ+iBll5Zu3NuvUqTjcIHRr9yzMrePOYS18cpkTaEj8+56VtYbomOuz73Bs6M0h0CHaAZiFolShi77sdtNOMzJavmn6CIhV6zYh1TMuyjw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=kVqOk658; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="kVqOk658" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 41E13C4CEF7; Sun, 15 Feb 2026 23:43:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771199041; bh=3/is2sfDJmdRy9QPPG2gvTdPrheCZt7kkxfe/Rrhb04=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=kVqOk658tO6pg8KYX7bNxxRB+cTHS2c50HdL/p1++R6v4GnH7HLRcxAuw8IA5riP7 3Jt4IDICWjq7yzM4n5QjAQ8/ig/OqXNTTM3n6zQ52XFn73acEKir+D+ouQNgzERc44 hVOEEdRJHUNE3P7LFU3s65wdgXswhaUvfHjfWnDpUAn6Le/wmlemPdd/nnmTq35F1E ngL8/34WOhSc91d6R/xOShCJl4l6B/bPuHYyjs/qwtS4s8NbMHmI9wNTIYZPQyL0FA TBQSbXL1sFUr2iAXfXoTHHxuhd044WD/AjNe8u0gs6QfQL1C1KR5W1W0ELiKWSUDaz CyuLRbD8VyITw== From: Andreas Hindborg Date: Mon, 16 Feb 2026 00:36:06 +0100 Subject: [PATCH 79/79] block: rnull: add zone offline and readonly configfs files 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: <20260216-rnull-v6-19-rc5-send-v1-79-de9a7af4b469@kernel.org> References: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> In-Reply-To: <20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org> To: Boqun Feng , Jens Axboe , Miguel Ojeda , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Alice Ryhl , Trevor Gross , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Lyude Paul , Thomas Gleixner , Anna-Maria Behnsen , John Stultz , Stephen Boyd , Lorenzo Stoakes , "Liam R. Howlett" Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, Andreas Hindborg X-Mailer: b4 0.15-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=15509; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=3/is2sfDJmdRy9QPPG2gvTdPrheCZt7kkxfe/Rrhb04=; b=owEBbQKS/ZANAwAKAeG4Gj55KGN3AcsmYgBpklhjWcIdNupHJZ+Z1Iqa1OXKENuLiidyv1T3i 5O5U2MYdHyJAjMEAAEKAB0WIQQSwflHVr98KhXWwBLhuBo+eShjdwUCaZJYYwAKCRDhuBo+eShj d8/3EACTdpRzmaQm8jpMo+udlUhbjmqYZQ1GbysxKSwhPMyLWPONRaDgmeKa2b3Rmbe0NV7Zo5Y rIwj38weF3m+Its2XgvUVrahJIiqFSk5NMNQ5HeI+0K+NAQKcKozx5TfEpepA5HdoeaGfJj8b+n JkMOaapjc3sNbmsXCjQPo2o2xg0NND5UUAxe0+z0OLn/Ye9inZkivJzED1jXQFs4c9Vkl47lRzo UTuKDsSk6spxKiGGbtP2JdlHXjRP8iShxxogibBxN3IWVkYmbyngRfTzgnok73Ozq59wTjYzE5S HsuPV36y7jZHTrwnsGiipOeQ0gF6r4E//T+uwJG2yMrszInghK6vLBdJK8TxhAdOwYdEnMdzeVN G/LxoYwndTaA4vOOMj8Rt6WWf1AGu3oV57dmJ56qet8F4ssTJnIira4KNF9WHsFYagJHmUzd5Oh RSUE1Rp/TBTf8aoBQF0i7uC52UL6SLWO3rPiVQGsidikcmpAHYYEAJSVa60nXzdkT8g8X5v0FWm ohKXpgLXGKlIdEgSnROrl/30yR+Gb0F8IO3ZIrmNkncT93pCGE4Cu6Lo6UhcQxzrs0ugJevxH02 h7Qo6Le7yIoYIZLE0eldLg/PTtbB9il8DVQ5sVVDvI7EsI8f/hjx8XmZpUq/VA10kXbtI8Llr+i ZUv36CKP5YOWpDg== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add configfs attributes for managing zone states in the rnull zoned block device emulation. The `zone_offline` and `zone_readonly` attributes allow setting specific zones to offline or read-only states, which is useful for testing how applications handle degraded zones. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/configfs.rs | 80 +++++++++++++++++++++++++++++++++= ++ drivers/block/rnull/disk_storage.rs | 59 +++++++++++++------------- drivers/block/rnull/rnull.rs | 2 +- drivers/block/rnull/zoned.rs | 83 ++++++++++++++++++++++++++-------= ---- 4 files changed, 168 insertions(+), 56 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 395da68d96dc6..1c0d95ded7e9f 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -125,6 +125,8 @@ fn make_group( max_sectors: 29, virt_boundary: 30, shared_tag_bitmap: 31, + zone_offline: 32, + zone_readonly: 33, ], }; =20 @@ -639,3 +641,81 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { configfs_simple_field!(DeviceConfig, 29, max_sectors, u32); configfs_simple_bool_field!(DeviceConfig, 30, virt_boundary); configfs_simple_bool_field!(DeviceConfig, 31, shared_tag_bitmap); + +#[cfg(CONFIG_BLK_DEV_ZONED)] +fn set_zone_condition( + this: &DeviceConfig, + sector: u64, + cb: impl FnOnce( + &crate::zoned::ZoneOptions, + &DiskStorage, + &mut crate::zoned::ZoneDescriptor, + ) -> Result, +) -> Result { + use crate::zoned::ZoneType; + let data_guard =3D this.data.lock(); + let null_disk =3D data_guard.disk.as_ref().ok_or(EBUSY)?.queue_data(); + let storage =3D &null_disk.storage; + let zone_options =3D &null_disk.zoned; + zone_options.enabled.then_some(()).ok_or(EINVAL)?; + let mut zone =3D zone_options.zone(sector)?.lock(); + + if zone.kind =3D=3D ZoneType::Conventional { + return Err(EINVAL); + } + + cb(zone_options, storage, &mut zone) +} + +#[cfg(CONFIG_BLK_DEV_ZONED)] +configfs_attribute!( + DeviceConfig, + 32, + show: |_this, _page| Ok(0), + store: |this,page| { + let text =3D core::str::from_utf8(page)?.trim(); + let sector =3D text.parse().map_err(|_| EINVAL)?; + + set_zone_condition(this, sector, |zone_options, storage, zone| { + zone_options.offline_zone(storage, zone) + })?; + Ok(()) + }, +); + +#[cfg(CONFIG_BLK_DEV_ZONED)] +configfs_attribute!( + DeviceConfig, + 33, + show: |_this, _page| Ok(0), + store: |this,page| { + let text =3D core::str::from_utf8(page)?.trim(); + let sector =3D text.parse().map_err(|_| EINVAL)?; + + set_zone_condition(this, sector, |zone_options, storage, zone| { + zone_options.read_only_zone(storage, zone) + })?; + + Ok(()) + }, +); + +#[cfg(not(CONFIG_BLK_DEV_ZONED))] +configfs_attribute!( + DeviceConfig, + 32, + show: |this, page| {Ok(0)}, + store: |this,page| { + Err(ENOTSUPP) + }, +); + +#[cfg(not(CONFIG_BLK_DEV_ZONED))] +configfs_attribute!( + DeviceConfig, + 33, + show: |this, page| {Ok(0)}, + store: |this,page| { + Err(ENOTSUPP) + }, +); diff --git a/drivers/block/rnull/disk_storage.rs b/drivers/block/rnull/disk= _storage.rs index d9f2703957fc0..802e9534ca2a5 100644 --- a/drivers/block/rnull/disk_storage.rs +++ b/drivers/block/rnull/disk_storage.rs @@ -65,27 +65,45 @@ pub(crate) fn lock(&self) -> SpinLockGuard<'_, Pin>> { self.trees.lock() } =20 - pub(crate) fn discard( - &self, - hw_data: &Pin<&SpinLock>, - mut sector: u64, - sectors: u32, - ) { - let mut tree_guard =3D self.lock(); - let mut hw_data_guard =3D hw_data.lock(); - - let mut access =3D self.access(&mut tree_guard, &mut hw_data_guard= , None); + pub(crate) fn discard(&self, mut sector: u64, sectors: u32) { + let tree_guard =3D self.lock(); + let mut cache_guard =3D tree_guard.cache_tree.lock(); + let mut disk_guard =3D tree_guard.cache_tree.lock(); =20 let mut remaining_bytes =3D sectors_to_bytes(sectors); =20 while remaining_bytes > 0 { - access.free_sector(sector); + self.free_sector(&mut cache_guard, &mut disk_guard, sector); let processed =3D remaining_bytes.min(self.block_size); sector +=3D Into::::into(bytes_to_sectors(processed)); remaining_bytes -=3D processed; } } =20 + fn free_sector_tree(tree_access: &mut xarray::Guard<'_, TreeNode>, sec= tor: u64) { + let index =3D DiskStorageAccess::to_index(sector); + if let Some(page) =3D tree_access.get_mut(index) { + page.set_free(sector); + + if page.is_empty() { + tree_access.remove(index); + } + } + } + + pub(crate) fn free_sector<'a>( + &self, + cache_guard: &mut xarray::Guard<'a, TreeNode>, + disk_guard: &mut xarray::Guard<'a, TreeNode>, + sector: u64, + ) { + if self.cache_size > 0 { + Self::free_sector_tree(cache_guard, sector); + } + + Self::free_sector_tree(disk_guard, sector); + } + pub(crate) fn flush(&self, hw_data: &Pin<&SpinLock>) { let mut tree_guard =3D self.lock(); let mut hw_data_guard =3D hw_data.lock(); @@ -262,25 +280,6 @@ pub(crate) fn get_read_page(&self, sector: u64) -> Opt= ion<&NullBlockPage> { self.disk_guard.get(index) } } - - fn free_sector_tree(tree_access: &mut xarray::Guard<'_, TreeNode>, sec= tor: u64) { - let index =3D Self::to_index(sector); - if let Some(page) =3D tree_access.get_mut(index) { - page.set_free(sector); - - if page.is_empty() { - tree_access.remove(index); - } - } - } - - pub(crate) fn free_sector(&mut self, sector: u64) { - if self.disk_storage.cache_size > 0 { - Self::free_sector_tree(&mut self.cache_guard, sector); - } - - Self::free_sector_tree(&mut self.disk_guard, sector); - } } =20 type Tree =3D XArray; diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 5bf965908ef63..91f8636d74cca 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -739,7 +739,7 @@ fn handle_regular_command( =20 if self.memory_backed { if rq.command() =3D=3D mq::Command::Discard { - self.storage.discard(hw_data, rq.sector(), sectors); + self.storage.discard(rq.sector(), sectors); } else { self.transfer(hw_data, rq, rq.command(), sectors)?; } diff --git a/drivers/block/rnull/zoned.rs b/drivers/block/rnull/zoned.rs index 0f15f4cc4e5c3..3b8ebf5449a8d 100644 --- a/drivers/block/rnull/zoned.rs +++ b/drivers/block/rnull/zoned.rs @@ -178,7 +178,7 @@ pub(crate) fn handle_zoned_command( match rq.command() { ZoneAppend | Write =3D> self.zoned_write(hw_data, rq)?, ZoneReset | ZoneResetAll | ZoneOpen | ZoneClose | ZoneFinish = =3D> { - self.zone_management(hw_data, rq)? + self.zone_management(rq)? } _ =3D> self.zoned_read(hw_data, rq)?, } @@ -186,18 +186,14 @@ pub(crate) fn handle_zoned_command( Ok(()) } =20 - fn zone_management( - &self, - hw_data: &Pin<&SpinLock>, - rq: &mut Owned>, - ) -> Result { + fn zone_management(&self, rq: &mut Owned>) -> Result= { if rq.command() =3D=3D mq::Command::ZoneResetAll { for zone in self.zoned.zones_iter() { let mut zone =3D zone.lock(); use ZoneCondition::*; match zone.condition { Empty | ReadOnly | Offline =3D> continue, - _ =3D> self.zoned.reset_zone(&self.storage, hw_data, &= mut zone)?, + _ =3D> self.zoned.reset_zone(&self.storage, &mut zone)= ?, } } =20 @@ -213,10 +209,10 @@ fn zone_management( =20 use mq::Command::*; match rq.command() { - ZoneOpen =3D> self.zoned.open_zone(&mut zone, rq.sector()), + ZoneOpen =3D> self.zoned.open_zone(&mut zone), ZoneClose =3D> self.zoned.close_zone(&mut zone), - ZoneReset =3D> self.zoned.reset_zone(&self.storage, hw_data, &= mut zone), - ZoneFinish =3D> self.zoned.finish_zone(&mut zone, rq.sector()), + ZoneReset =3D> self.zoned.reset_zone(&self.storage, &mut zone), + ZoneFinish =3D> self.zoned.finish_zone(&mut zone), _ =3D> Err(EIO), } } @@ -282,7 +278,7 @@ fn zoned_write( if self.zoned.use_accounting() { let mut accounting =3D self.zoned.accounting.lock(); self.zoned - .check_zone_resources(&mut accounting, &mut zone, rq.s= ector())?; + .check_zone_resources(&mut accounting, &mut zone)?; =20 if zone.condition =3D=3D ZoneCondition::Closed { accounting.closed -=3D 1; @@ -365,7 +361,7 @@ fn zone_no(&self, sector: u64) -> usize { (sector >> self.size_sectors.ilog2()) as usize } =20 - fn zone(&self, sector: u64) -> Result<&Mutex> { + pub(crate) fn zone(&self, sector: u64) -> Result<&Mutex> { self.zones.get(self.zone_no(sector)).ok_or(EINVAL) } =20 @@ -418,7 +414,7 @@ fn try_close_implicit_open_zone(&self, accounting: &mut= ZoneAccounting, sector: Err(EINVAL) } =20 - fn open_zone(&self, zone: &mut ZoneDescriptor, sector: u64) -> Result { + fn open_zone(&self, zone: &mut ZoneDescriptor) -> Result { if zone.kind =3D=3D ZoneType::Conventional { return Err(EINVAL); } @@ -434,13 +430,13 @@ fn open_zone(&self, zone: &mut ZoneDescriptor, sector= : u64) -> Result { let mut accounting =3D self.accounting.lock(); match zone.condition { Empty =3D> { - self.check_zone_resources(&mut accounting, zone, secto= r)?; + self.check_zone_resources(&mut accounting, zone)?; } ImplicitOpen =3D> { accounting.implicit_open -=3D 1; } Closed =3D> { - self.check_zone_resources(&mut accounting, zone, secto= r)?; + self.check_zone_resources(&mut accounting, zone)?; accounting.closed -=3D 1; } _ =3D> (), @@ -457,14 +453,13 @@ fn check_zone_resources( &self, accounting: &mut ZoneAccounting, zone: &mut ZoneDescriptor, - sector: u64, ) -> Result { match zone.condition { ZoneCondition::Empty =3D> { self.check_active_zones(accounting)?; - self.check_open_zones(accounting, sector) + self.check_open_zones(accounting, zone.start_sector) } - ZoneCondition::Closed =3D> self.check_open_zones(accounting, s= ector), + ZoneCondition::Closed =3D> self.check_open_zones(accounting, z= one.start_sector), _ =3D> Err(EIO), } } @@ -533,7 +528,7 @@ fn close_zone(&self, zone: &mut ZoneDescriptor) -> Resu= lt { Ok(()) } =20 - fn finish_zone(&self, zone: &mut ZoneDescriptor, sector: u64) -> Resul= t { + fn finish_zone(&self, zone: &mut ZoneDescriptor) -> Result { if zone.kind =3D=3D ZoneType::Conventional { return Err(EINVAL); } @@ -545,12 +540,12 @@ fn finish_zone(&self, zone: &mut ZoneDescriptor, sect= or: u64) -> Result { match zone.condition { Full =3D> return Ok(()), Empty =3D> { - self.check_zone_resources(&mut accounting, zone, secto= r)?; + self.check_zone_resources(&mut accounting, zone)?; } ImplicitOpen =3D> accounting.implicit_open -=3D 1, ExplicitOpen =3D> accounting.explicit_open -=3D 1, Closed =3D> { - self.check_zone_resources(&mut accounting, zone, secto= r)?; + self.check_zone_resources(&mut accounting, zone)?; accounting.closed -=3D 1; } _ =3D> return Err(EIO), @@ -566,7 +561,6 @@ fn finish_zone(&self, zone: &mut ZoneDescriptor, sector= : u64) -> Result { fn reset_zone( &self, storage: &crate::disk_storage::DiskStorage, - hw_data: &Pin<&SpinLock>, zone: &mut ZoneDescriptor, ) -> Result { if zone.kind =3D=3D ZoneType::Conventional { @@ -589,16 +583,55 @@ fn reset_zone( zone.condition =3D ZoneCondition::Empty; zone.write_pointer =3D zone.start_sector; =20 - storage.discard(hw_data, zone.start_sector, zone.size_sectors); + storage.discard(zone.start_sector, zone.size_sectors); + + Ok(()) + } + + fn set_zone_condition( + &self, + storage: &crate::disk_storage::DiskStorage, + zone: &mut ZoneDescriptor, + condition: ZoneCondition, + ) -> Result { + if zone.condition =3D=3D condition { + zone.condition =3D ZoneCondition::Empty; + zone.write_pointer =3D zone.start_sector; + storage.discard(zone.start_sector, zone.size_sectors); + } else { + if matches!( + zone.condition, + ZoneCondition::ReadOnly | ZoneCondition::Offline + ) { + self.finish_zone(zone)?; + } =20 + zone.condition =3D ZoneCondition::Offline; + zone.write_pointer =3D u64::MAX; + } Ok(()) } + pub(crate) fn offline_zone( + &self, + storage: &crate::disk_storage::DiskStorage, + zone: &mut ZoneDescriptor, + ) -> Result { + self.set_zone_condition(storage, zone, ZoneCondition::Offline) + } + + pub(crate) fn read_only_zone( + &self, + storage: &crate::disk_storage::DiskStorage, + zone: &mut ZoneDescriptor, + ) -> Result { + self.set_zone_condition(storage, zone, ZoneCondition::ReadOnly) + } } =20 pub(crate) struct ZoneDescriptor { start_sector: u64, size_sectors: u32, - kind: ZoneType, + pub(crate) kind: ZoneType, capacity_sectors: u32, write_pointer: u64, condition: ZoneCondition, @@ -626,7 +659,7 @@ fn check_bounds_read(&self, sector: u64, sectors: u32) = -> Result { =20 #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[repr(u32)] -enum ZoneType { +pub(crate) enum ZoneType { Conventional =3D bindings::blk_zone_type_BLK_ZONE_TYPE_CONVENTIONAL, SequentialWriteRequired =3D bindings::blk_zone_type_BLK_ZONE_TYPE_SEQW= RITE_REQ, #[expect(dead_code)] --=20 2.51.2