From nobody Fri Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0CC614DA549; Tue, 9 Jun 2026 19:13:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032397; cv=none; b=I1a08JkAN0hPxJtAfo+8VnuPBIOFuRdrl7mYXemMhUg8KUsl3xAzHoA4CFP7V0qy9hlqjiD0CM4ZKmuB5oAi9F8WpZLXk6Yz65bFgqmWg4FiRFFft66MCTnJMHcF71LU3JfMBkJ01Y4dMjylQB90LFEDcf648hu+NhJtvnUDyxg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032397; c=relaxed/simple; bh=j94rzYXVcMOYMwkP1BfZKPpHsVdQeUm+yqM3C4KWNns=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=JzhLkFgocFqREBOvtvbHYdphaTI/Kk4oN7psN7aIOzgYr87TZJFdgQEaQ48643hKr/ezVkrb9DmW4eBxeyQ5M1mdS81u2VsVOpWMI1MUZ/fYGA0OCGaXPyYsW3Kbg37TbYZlR7cn2HaQJdlM49kq6jgZqjCu6jIAAYjpKmu1MuU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ALQ4MDJ3; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ALQ4MDJ3" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C1E211F00898; Tue, 9 Jun 2026 19:13:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032395; bh=+SB8unmwrAO9mUO9xf4AbinL27K1XIFUUebCtsNMw1k=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=ALQ4MDJ39UaQi4Xcs0/po7LiGIPPyP5hBFLR0xeILyrKkWGyU2T13VYHajc/84m52 lOhsZ9Pl4J6iG2SNWK3KrVrLIWeN70xM8JrHv8jjZT95NKJylLX/a4NyuzVsKmkpHy POtU8lbn05to8Jz8D3CVT0tNcIvCqSXxWURs/EBmyeax80BEVIpQpdZOhEPfZzpDqL mJTh7ZGuFe0ZL8NckFGbZBnqNXKJqtCawKadfffdzXFCSMQuysSbS65b+HiLlW7FQG AbfFHuLz0h+ogiRgdj9JcjXHUzXrvhVbruVMLTmR9JpVPmyh0exfPcItLKtQlu2Jcq 2Ee86TSyLLBJg== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:07:40 +0200 Subject: [PATCH v2 01/83] block: rust: fix `Send` bound 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: <20260609-rnull-v6-19-rc5-send-v2-1-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org, Yuan Tan X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1957; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=j94rzYXVcMOYMwkP1BfZKPpHsVdQeUm+yqM3C4KWNns=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGS115ZnPcBcAbPrOuPuRVqGsLxm3lnTlZcjW axvRMhOjeuJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihktQAKCRD6UCkIqsW9 0GV+D/4l9Bq3nvQRAYx6wrVRSnUhKN8gQ453qln/Ip/dk0q8mtRhyZ0Za/ISiSc5OIZp+4G3xPb 2d0KQ6dpftTRPG0isURFnasRIjo30OxcCpEH2ICK4N21I/RiyGMOqsAQMteSdFPQPRWgh9HiJRz hFfXXLzIdGxd3T+tpvOsfMnYe4PI+JS8C7gO/8Y6s+jimfh3vU+oKyHs89kOsEShkPnQfDSx3fZ x50Plys3MjPb5vaimaBRtnziqYsi9IkRmhkyzDKXl+RSZ+Tl6xCWucnlzQoBkxTrWld6IYjdhkA aTyWkcib7SjvbwEQqu43KtlhmHL/CAoNPgXSh9uNz+6n+KXwSzZSxoVWb/5A6elZZeUtcrcgvw2 aB4OrN4TnINzJBneIGB5REjg0Do+AFBoJKPtP5BYa026fLRymtQVd50v6eUlvHvNLgNli9X8QFu KJiurZBg81YdReVkJQezYm1dgGfyPtZxiRhPgN8UbxJPxXMHE+1s4jnnVM2ATmmdAfGbR+Ugrgz 8B/32qMGDi/1zCDNIrR1WnMQ7dyDus5zLM6m56VLTtW0nGq1dLGwiDsK+Mzh6rIszwtFkuo/2G4 Glk/yxf5td/ojCS22TRv4n+bUDGP3zuQU8u77H2mjPPIKLfJCY07oq0xFlM3a3zLfhXlsVJjHbb K4TkagJxdHac3MA== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 The `Send` implementation for `GenDisk` was conditioned on `T: Send`. This constrains the wrong type. `T` is the `Operations` implementation, which is typically a zero-sized marker type that carries no data, so `T: Send` says nothing about whether the data a `GenDisk` actually owns can be moved to another thread. A `GenDisk` owns the queue data `T::QueueData` (stored as the `gendisk`'s `queuedata` and dropped when the `GenDisk` is dropped) and an `Arc>`. These are the values transferred when a `GenDisk` is sent across a thread boundary, so the `Send` bound must constrain exactly them. Bound `T::QueueData: Send` and `Arc>: Send` instead. Fixes: 3253aba3408a ("rust: block: introduce `kernel::block::mq` module") Suggested-by: Yuan Tan Signed-off-by: Andreas Hindborg Reported-by: Dylan Zueck Reported-by: Priya Bala Govindasamy --- Please take patch from Yuan instead of this one, if they send a fixed version [1]. [1] https://lore.kernel.org/r/8839ddc5ff54bf454d508cde91d27d00fc3e2dd8.1780= 633578.git.ytan089@ucr.edu --- rust/kernel/block/mq/gen_disk.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_di= sk.rs index 912cb805caf5..b36d24382cc3 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -199,8 +199,14 @@ pub struct GenDisk { } =20 // SAFETY: `GenDisk` is an owned pointer to a `struct gendisk` and an `Arc= ` to a -// `TagSet` It is safe to send this to other threads as long as T is Send. -unsafe impl Send for GenDisk {} +// `TagSet`. It is safe to send this to other threads as long as these two= are `Send`. +unsafe impl Send for GenDisk +where + T: Operations, + T::QueueData: Send, + Arc>: Send, +{ +} =20 impl Drop for GenDisk { fn drop(&mut self) { --=20 2.51.2 From nobody Fri Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 39A5A4DB563; Tue, 9 Jun 2026 19:11:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032310; cv=none; b=eDurSOFJFw0iOPjd9M5EOnErP361LpVKNiKhOq79Ks/3Zi3lmuy8haZ0oWeSSSSU8b+SZ+4SRhirptLfIB+Y+DsFT2IVp3nMyiPTNUPpUIY7ED92+o9daHD9NitJse3uHSlazzF9+KG7MLuT1IAgAif4dQ5oq0mhny/7JED4lr8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032310; c=relaxed/simple; bh=7twqnJezwG7YFrJ5o1U16sXUnbxUjCBfg374lo5njEE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=rUKEb9ffCvDLnupbdJOjEKddo+y6gGd7kxJwY6uCCYuWdCy+7rg3RN7u/GTh4sxnIHuJ1uPn7izKChUAGWU48PyvslzIZuuPWLyc3vKs0SLHepdZd6p4xwSyMdW/mch89TZdw/qc9kiV6toB3U8reD1dcGJcCXMMk8DkZCAAAVM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=FyL0vqob; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="FyL0vqob" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A0BAF1F00898; Tue, 9 Jun 2026 19:11:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032307; bh=v4Z8AHRxPgKpZNYlJlm3R0WTIJzVY9sjt95caEWA3FU=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=FyL0vqobNgq2bJN3QVgiuWZUEWwnm69ZsAY5sgnbeMg4yqz+QOfCMS+26Svt+DOpq Cz3uYdyQ8aG1yRMMpq6b/gMhMLkX9LIgeIXYkv9s2y0WxY5dPPKTDvbldzIKegimJ0 9aVRSnJM3R2DAZUsUXxX8o+y7dZ47IEIVzbiDSB0GIcIn7THLwe+LVLojJJg3BqONc BhShP9FKlzUrT+jbgTYNIlWeGZafiIRtdR+RuJn3JCP1iw/9kqMFnLxOuxOR+55ipu wfHxFLfyiDEizPOJqe/qTl3/uMDVaEkjzlSSa8Ijzf9PLHvVHCtJassHUiUktCISQY KBMl9pVHvvpGA== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:07:41 +0200 Subject: [PATCH v2 02/83] rust: block: rename `SECTOR_MASK` to `PAGE_SECTOR_MASK` 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: <20260609-rnull-v6-19-rc5-send-v2-2-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1073; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=7twqnJezwG7YFrJ5o1U16sXUnbxUjCBfg374lo5njEE=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGS2jkBlMXprmSKrEyFaF9EB3OxN7q7deJAwW z8X8xGygpOJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihktgAKCRD6UCkIqsW9 0KohEAC0G5926iVZdDlAI/QAadxwjGDcLBe6MxfhB04nVnEWPurh4URtNIDfql0VvjgnK5GoOGI vUvYMWMait7VM/0VSbi5NUNtEktgdyfc01g9BNEEmEjc8fXmQKhaD88Gy/qmNvpga+KEu53mQQ8 AnAGw3F+t55amoJeOaRirIwlRzmj6VY42w+7Msrcs1/AEWaRsOnA9UcO2SzlgswDxWRFdN2LGUq N675ReEzy0SX3LF5dTJEdOqdkT4mNbWuSS9X1JhjxwDjVN825N8NL75u2K2yZxETb8d+qs7qAIF CkycmdpYuK8Lpbx0cnoryLdKVVGWfVhRvxBXHJLJWTUIf7NAeKkBxTaOABSkoqwDJAyY0S8iTaz hShmSJlT5PlhDfp1CJSA2rZyHgE6Ij2n6EhewL22FU91gvQp+PZauBRRg0xXX+X3ZQ86WQRUCc8 GcDnCpI2BP0fdkLsXuXFO5Qp+yfDmiGTPvh3IKul0AB9EUEOMOfXq/4hf6E8MBacORz2XNjCsl+ wZj1sd7CA4P2Su0LqkYz0/8f5E7LYbxXLKfSyp2fGuGZGXzZZTGVd67X8iQVs9SknplyNNkj+iQ Tngch73oPcV4Varpq9ZaCgtSYfO1NX7Os7BBsOm+s3/JoCT7WKdATreu537hhNSwTcQv43zxAJw 8nxoutHdmOPcWEA== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 The constant exposes `bindings::SECTOR_MASK`, which masks the sector index within a page (`PAGE_SIZE / SECTOR_SIZE - 1`), not `SECTOR_SIZE` itself as the original docstring suggested. The misleading name made it easy for callers to reach for it when they wanted a byte-level sector mask. Rename the Rust constant to `PAGE_SECTOR_MASK` and fix the docstring. The C binding is unchanged. Signed-off-by: Andreas Hindborg --- rust/kernel/block.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/kernel/block.rs b/rust/kernel/block.rs index 32c8d865afb6..b120e83d9425 100644 --- a/rust/kernel/block.rs +++ b/rust/kernel/block.rs @@ -4,8 +4,8 @@ =20 pub mod mq; =20 -/// Bit mask for masking out [`SECTOR_SIZE`]. -pub const SECTOR_MASK: u32 =3D bindings::SECTOR_MASK; +/// Bit mask for masking out the sector index in a page. +pub const PAGE_SECTOR_MASK: u32 =3D bindings::SECTOR_MASK; =20 /// Sectors are size `1 << SECTOR_SHIFT`. pub const SECTOR_SHIFT: u32 =3D bindings::SECTOR_SHIFT; --=20 2.51.2 From nobody Fri Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 75CE84BCAA2; Tue, 9 Jun 2026 19:10:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032258; cv=none; b=JFSV4ZlgQCF6+eRLD2nXNmDwu1qSfANTJGaqvZSgEnHszaWLOykGxwLSuFCRAEK1QOhIlNpekCdpdsdPC1TYMoQjV9UT+9HSxFJcGs/vf7dE/jM3WUMD7koNyw8AQaSjQGyvixCmQ4eGkkgrLE9EXAnDH48v9D268idnYJ17/aY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032258; c=relaxed/simple; bh=4NVXyfJD6Wc3B91hTYpUVWCQ5xmIczODt3j5Z23HY6s=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=QlGmZPNkCiOHs22HNF5PpvFJJ8IxoOhQZaMl/DoguXFIWhSaxV+wMa/BLhuaLTKFWhTSDsJ97SrDNKOr4J+Ef8MQxqyWr100i3B6HoJvhtlElw1Go0Gs+uXvivti0GxwRclSxA7Gltei8cb5kgoUfgIOchcLbvRmdnab0ZiYCoc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=PyT24jGX; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="PyT24jGX" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 317391F00893; Tue, 9 Jun 2026 19:10:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032257; bh=z3StJ1SDVAol/xVojKQU2uRTOblocEMLOnL3mqoENfg=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=PyT24jGXtIi3d0UF04FVHwiZclVhw/6Ja+SJ0WHyKYnIz34KeUUX8ad3sCHCDHAKI /n1gD3j6hBu41FBHAfylAUdeDpuzs/GQkt+EqsM8aezKqsnqi8i7Zz9H0Q0amI9ghX bZUFwofKeBJuAGg1pJS6stvUEGb+n8sRJGcRs5ZApZw90maEIicpz/WebeBGlZ+Nr/ PRM/9VNUd6DluM5kek4MBKALBdIHMyBfBuqBp19OeWGjiJnoA1bUubIuQntNfBLQ5z gpBAQGsEJ5ZlWDqFcTuX4C04foUANCTNaw+daq0g9pVXGwS49d62pEHwJdfWHcSYgO Uy12ml271T44w== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:07:42 +0200 Subject: [PATCH v2 03/83] 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: <20260609-rnull-v6-19-rc5-send-v2-3-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1057; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=4NVXyfJD6Wc3B91hTYpUVWCQ5xmIczODt3j5Z23HY6s=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGS3eaUcUB7ex1Rk0/NrKNjqPU45h3WR6lcRF 3h/f82EGjWJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihktwAKCRD6UCkIqsW9 0G5wD/908gLu/c/luttZa3fhus/yQ22Ibyd1bQaE7Go+HRNeE0PP5PijB26fEiwMq+wZapKjfdF cD0lxtPlkAJVWuWuZMEQvHzv6J0+ty/3lmAAZH0EYuNh91xn2ME9AtGQe4sqk8YZefRw/t4glgD Ok4VQbhmdyOTfo7LVrggMkNcY0hlyGoBf98O3LYmyavBT1HnoY/DxeRyK+iUTWA0bA4IkgSZsNV 4sbHm2WLjLP2yXmdWfa4BNfU0ASOfzEHa1Yp1EIOPicsDZTrKrmGz1VEDUjpe8+vV8hukWazy00 Na+XzMEtJ/leSsLYEIaOY9CBnjoWCjH/oHPAXpLuLrd6lOfpXEtg4MSWfkKn9yldpPZzUzidEdW uGHTkxc3guVO56mTrk51YkCcsjDa0//MCCEK2VY5ToUCbCUYZdyq9qe1VpUcW3kJp6m83AuLMrf 881PPqrOdhirescgirjUkUSYVc7V/BpVtpIhrXbB0ll2MD5ve9oMY3mDNkwr/Pj5O82VakOQcfP moq5YSm46RG3ISMDFJF5hDRDbzbKrJ852TWNAv4rdk6/TUMKY8frEIdz1goOCaB6afbhDaQ+nhv ByCqFxHXnAGfwSm8MMWzExl3veZcnaO4TvvjJJV+4tVPBJdGRsfAppZUQ1x3YZNhN4dNctHK2je 3oVTGh1mKkqd5dA== 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. Reviewed-by: Alice Ryhl Signed-off-by: Andreas Hindborg --- 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 0ca8715febe8..d58d2c4c5f63 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -10,12 +10,19 @@ self, mq::{ self, - gen_disk::{self, GenDisk}, - Operations, TagSet, + gen_disk::{ + self, + GenDisk, // + }, + Operations, + TagSet, // }, }, prelude::*, - sync::{aref::ARef, Arc}, + sync::{ + aref::ARef, + Arc, // + }, }; =20 module! { --=20 2.51.2 From nobody Fri Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B17884D8D96; Tue, 9 Jun 2026 19:12:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032347; cv=none; b=rusEJm0aLxWqOmPUh7qCqxAMONYGzjTUoMGDpBYeCcATmszolTnFAg6JmOZPWotyOn8a1lRsitid4v/Gh8Ds9YapKwa3MYMZ2gBWE0CYaG0bSaAFaDzwMgu/Jd9Y7A/G4jAEXr+CvUCSETAt/RfjkN3xWtxEacjeADC0O70Y1Jw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032347; c=relaxed/simple; bh=NlaHBwDAyKNiiMVG0I8yNPspfiq5/k+1/1/ufIcyrYs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=NgDKrMBE660I48/2tB78knEsWswMpb6jPHFVWHaf/8+6snoehk24YVfz6eQJely17+fXBclQfXGDxYp67NVFBDKJOT//64H60qZJuqmbEV1uODUrH8QfZtpIYjkE4L1K9D5hIybPoz5GKXVNcFUQNhWf6eUVe22KdQa1+wC8/4E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=OCuQ//BL; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="OCuQ//BL" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6AD2B1F00893; Tue, 9 Jun 2026 19:12:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032346; bh=3hqK4pNL6S2JPG/y7ZeGzzjDNeUcKa36Uf2X4yV6K44=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=OCuQ//BLNvPCd7LUKo8DdrDPmpPEjSN9j54i865qFnGncdfHVKv/F835XUWzh5fmy 1JqGuqfN0CppEcDMWMOKkw6y5kScMLdiIu6c0TWVFWXh1zMNTmAWIc55NOsYCb+vaH doQxW//kLze1PHhaTv32DdIJB9OTNJsV3VSELUTo4Rn/mVcD4nrDYWqyk2I/iKy8Wd msbDktIdNVuE5R4eHhFgVlT4tfoSTIg5NcQ5Hl46AxYz6fWJsXTGuS9O9SHKbatsUB /hZh+nP0fG2dcpB2BkxCTvQCgya+YTpvERbdLpOTXd0GsiM0SAatO9BdhX4vfMJS2G A65ui9BiHP91g== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:07:43 +0200 Subject: [PATCH v2 04/83] 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: <20260609-rnull-v6-19-rc5-send-v2-4-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=3121; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=NlaHBwDAyKNiiMVG0I8yNPspfiq5/k+1/1/ufIcyrYs=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGS4fPIeIol1rMY3VBolkA8u3anuZt9v8wBBi QcjjYrJYrOJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkuAAKCRD6UCkIqsW9 0HJ6D/4r6Srm+0SmXV4+YiMjaedQ8+3HXt5ttbJCmfe6w8sTEOmVS4RG3Ad9QUVLBtvt+SGICwl X40VacPNolbomVbsQJVcRJxX2TW6uuLVLT9l07xKio8sYaVcpPzDH+fwcFuGygsFCsH/O1j3OGh LrhgVvAG4wN3MIUXnb8lGwObPBrX1ep1DYt1UXSF6kjIyYxifSvvRQFvlbGBO9gSaoPbrPUJtuB g+zWGGFXFMp44VS22ZC5Go2OMIFtt4b4h+scV5r5d0c4pvf0/MermHK1kNeD9akvoL447uDrTn5 AlBLrCDd3t70vrObkRGn6zzCT/SDFZPxlXOXT6HTDeGN+oOR0+LnR4pJzMJLg97b1ATobsnzINz BEwkx5fSncNaS8cHptLLk4+jPGES7hwmzSRE7G35rcoBMBOgYSi2lvAunsY0qFIZXe0wNYf+GaJ Azf0qrluNYhjkGZ7enOmevTOYJcHWM8N/sRH6/p8P30s+qbMus3yMKmVkxYYACBpdpZrzVQINmT 9ksQpRsnzpJyABIgID+RNrNL4r++AWi6eevjw6K78gOrs31gbs7n55dUnATPUb+g9Bf026YXp9K qukh4y9TLviUUojh2BBgjMK1hm+T5LBsWEqf95Wr+on7CHJBLcBAwqokliJpr62JH+VR9XUyZyf 6XZhZ0jatT97k9g== 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 | 50 ++++++++++++++++++++++++++++++++++++++++= +++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index d58d2c4c5f63..77ccc6850961 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -18,10 +18,14 @@ TagSet, // }, }, + error::Result, + new_mutex, pr_info, prelude::*, + str::CString, sync::{ aref::ARef, - Arc, // + Arc, + Mutex, // }, }; =20 @@ -31,20 +35,64 @@ 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: bool { + default: false, + description: "Set the rotational feature for the device.", + }, + 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(), + 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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A42B623D2A4; Tue, 9 Jun 2026 19:11:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032275; cv=none; b=BuTXj9vNC5VgRgGtnSOt29In8KDnwVz7lfg+kmJpjrnFs93wrfOqyWJXYCCnIs3/4lsm71VdyhcUbZuWyJW1ImDrLQdqoZ4HAu2ax3IuwZeixxeiFsIpmVAM3dLYfDNWNftqVY1WhuaOE7eBFAqmuMesAa105jwhc3wJ9ixyFok= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032275; c=relaxed/simple; bh=mkT4eThBe4It8KWqsiOtBJt/DANWdnHxd0qXiVgZJI8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Y0R4jaNk3wjhlRa1nrneNiC+BxhIS15XnGvX/IgtM1jUXsxY5XyM5MZZiRV3sZK/W2ZaZh6lnLDQjJJ9E7s3U4EkG4UpXlnqnHrAgflIIrokJiCLqO7bQtw6rPbwgbX2efw8clPA1+yK5P78bz6fKw8tKVvsx3JbGJREFx18k88= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=UbhsD/Lb; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="UbhsD/Lb" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4CA321F00898; Tue, 9 Jun 2026 19:11:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032274; bh=DgBQwxbMweV6Zkbj2/J7QySjjyAJfiQJ6Oi1laC2E2E=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=UbhsD/LbUCehg1QTO7E5Unt9lyPSrvNa7/2GuoaAkStPWOTqPv2ABuVxIOA3fMdGz PUmt65Gh9wmh+FDCTdDSm6xGptUyjtZfAh6ushZmjXEYaFbKoNQ84HqArEiiu/pGgO IzE7/CCC08113x9AP/PMci8kj5k/PelI1CBIvTY72zPpiHxaN3k9++cT7G2BFljOXx 5kDtjFj+btkuK2OeAiqRyrihgMOr2ydXNQhu1kLWjW7edaFcja0gIf/iQq8e5vfP9i wgQ4IzeN03/FI4i4ZCxBQyvdta6M5wv8rs1Q2zUCPw2/7Fgdq8QfRIGwixJEmrWfiz HPjudMd9DXgRQ== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:07:44 +0200 Subject: [PATCH v2 05/83] 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: <20260609-rnull-v6-19-rc5-send-v2-5-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=10514; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=mkT4eThBe4It8KWqsiOtBJt/DANWdnHxd0qXiVgZJI8=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGS4cw7+kfXwUnAvj3tdI1tU/JIPYwTDkhIsj 0/J6tjx/T+JAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkuAAKCRD6UCkIqsW9 0MZID/4ymdYkqB7ckKLRVkyTg+DrfULQJOo33DREmgb9MUDfl2wUZ/DkUXOCE01NnuGswQZjgbL dKGyJQQBAMDis8nIFLT33hauvbtk8OerUfQwTJv0RLgjFVKK/h3rWpZngwnM64b8DTjwYnOW7Dy chJISfMmP/raWJa/87NdX9ES8DxjfPRD4uuoSKPBceZFiYQkQVThHd8zCrSrvdQBqo4VdtLhAZ5 JtnFr9220oUuKE5iYa1CboZ1/eiGdCBNYRxToFBRirop+UVeZAR+07wqYBZjNjfYgBuD8uudK4K p7mSFt3CDKVCTclJ0X+XnBelhsPMLosVjcBO+ytI6MadUXzPxg+2TSgkIHMSkJV4I3yJroqZI2N wcmkDk9XWBp6/I20EnJwDqhAkzlkXMECPjd5CQhDC3wZIqN3XmGWsmM95MqQPH9pWKfClmJKfyn 1lBrz6wjnn1QMNqvOCDZgiSHP0BMqutOQ9g6MoQdNoDIh2FSHYxbjMnA37YPm+N50MNeEDWsm6h bqDP7wDHNuXJQAomQg0dknkgdRR4q6bxe9kugzIUNc6CatP9dMKxytHJR7BW7WmJFwXRfgV2Fmq t2ZizLRiNVaJNejR1mBk7VZM+LrbMJa3Vg3DMQ0imfCu8d3Uwchte0SN6zDpVLkDoGU5CiK/r9p qAL5XeLz/iM9UaQ== 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 | 143 +++++++++++++++++++++++++++++= ++++ 2 files changed, 179 insertions(+), 98 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index b165347e9413..fd309fc17e66 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -1,18 +1,39 @@ // 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}, - configfs::{self, AttributeOperations}, + block::mq::gen_disk::{ + GenDisk, + GenDiskBuilder, // + }, + 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, // }; =20 +mod macros; + pub(crate) fn subsystem() -> impl PinInit, Error> { let item_type =3D configfs_attrs! { container: configfs::Subsystem, @@ -164,99 +185,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 000000000000..30bb32238457 --- /dev/null +++ b/drivers/block/rnull/configfs/macros.rs @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0 + +use super::{ + DeviceConfig, + DeviceConfigInner, // +}; +use core::str::FromStr; +use kernel::{ + fmt::{ + self, + Write, // + }, + 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()) +} + +// The lock guard is passed to `store_fn` so the powered check and the +// store happen atomically. Releasing the lock between the two would +// allow another writer to power the device on in the gap. +pub(crate) fn store_with_power_check(this: &DeviceConfig, page: &[u8], = store_fn: F) -> Result +where + F: FnOnce(&mut DeviceConfigInner, &[u8]) -> Result, +{ + let mut guard =3D this.data.lock(); + if guard.powered { + return Err(EBUSY); + } + store_fn(&mut guard, page) +} + +pub(crate) fn store_number_with_power_check( + this: &DeviceConfig, + page: &[u8], + store_fn: F, +) -> Result +where + F: FnOnce(&mut DeviceConfigInner, T) -> Result, + T: FromStr, +{ + let text =3D core::str::from_utf8(page)?.trim(); + let value =3D text.parse::().map_err(|_| EINVAL)?; + + let mut guard =3D this.data.lock(); + if guard.powered { + return Err(EBUSY); + } + + store_fn(&mut guard, 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, = |data, page| { + data.$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, + |data, value: $field_type| { + data.$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, + |data, value: $field_type| { + data.$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, + |data, value: $field_type| { + $check(value)?; + data.$field =3D value; + Ok(()) + } + ) + ); + }; +} +pub(crate) use configfs_simple_field; --=20 2.51.2 From nobody Fri Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C716B403B00; Tue, 9 Jun 2026 19:15:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032560; cv=none; b=ctvcP3qVcJoZxjuzRa5nMLf3Ad/1/CAQrlliGeZe2+/6NLDpZL53u/IcnAThuLannVjEF96geRnXbPzSzlk+D2HRG5DDdVyqrD81StC+QOcn7QB87G9zgnSs81NvuMi6/pjchkSzF7QmeA6GjwPJ52WALVM8YF9aP5k8j7eFTmg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032560; c=relaxed/simple; bh=AEQaJe7SK7Z/x0RCHvYjLfe6Z+31poYBBz6CEFZucYI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=M6CeLpq4HPreke2K6b0K166W73Nbjj/1f0pLEP4caX+28Vmnh+xhlVr0vJ6BD/Aih1kQMkZ/Xr33ZhAwRnV65tJnVu0VbXFshe7Ufctrq7XbLmDkLoLUNVBLYTKpHCn70pB+eBj86GSADSJ2tP0gvLWg3Q2ZDq6KuwqhRVnRbnY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ORLbq2rd; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ORLbq2rd" Received: by smtp.kernel.org (Postfix) with ESMTPSA id BD8FE1F00898; Tue, 9 Jun 2026 19:15:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032559; bh=kylCIHTIqWZVVqg1E9SzeJnum0Ofj/qvsUr/FLAXqYA=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=ORLbq2rd9IsQ0KM6VJkQSIi0/UUW9JcgQsqWlCtS4dFjSwrUkEpFwdwYMFe4wqwbs j8XR9XS1sWdSqZc3q5QMLfS1VJ2GWM+8PHe61CAnVcaRdypxKxtu0bbTZAZDWMk7kL RdLkzJvjZUir7vsU1jowtfq23armyfMPakzgnD9IEYceS+TvXUpHxTmj3RbmcGnewr 5OBNEN8zLRIBWq0IZl3v+Q3CuK9Wn7ka3SPM9EzJlLfNUJelFXnSb9USQY3Gu9A2be RM80E96/gwdcWp+a0vekE8wnKQ3CpnoId2e6HEj5Yr4A8JGbmiu0nHM9flEL3oXBVR 6ALdqJmtFh44A== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:07:45 +0200 Subject: [PATCH v2 06/83] 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: <20260609-rnull-v6-19-rc5-send-v2-6-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4558; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=AEQaJe7SK7Z/x0RCHvYjLfe6Z+31poYBBz6CEFZucYI=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGS5MZAEtPyseKnhLu3ha3HCPw+XFEI2sVTNN RpPLZ/VhfaJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkuQAKCRD6UCkIqsW9 0E9bD/9uFjqpyDnP6ArF+C4Ps9g7VX+5ehlZp5Wge0BITznbNerYO0QBEofxj5R+p+7QOSQHlcM lWX2y4KUeqwioR8dtZiFjPNq2s/EWY0ceeUALCorjFMAoW6JTl0eTX99wUJ92mS4nmTwtemI69L K2fc3Pis9rGscgDoFurnNvd4Szph5Kg4jWAmwX+TL7BBNhPvjM42oEEYQXHVbL4T/zarMe1AsDM 27nefnw/tFHEd3nls3Yvx4eTAYjVwPzD6nPuL37oiXi7++NTGHGBnSe2I/9sjlx5nDRYeFf32Mi URUpGsp0xyV+TUmTyiR3eXTXvbgVBdFutTpQBCifsFsKu9R1KrUi7FXsocbqZuc48QPxQW3G7lc wQXOLbg7i/0AbwVWFFyvH+OzJMYADyt9dU3QbtKtPcCjv7QoyzrjSmpvT+urO7SKRHBBM63IRzb CDGf4gMv3qb5a+jGPzQ14la92avZLuIEj/XrtAcxlQYwyWlYWxiuDNCixROoUFqAqhnnv6FZssx 4DMOjqAhpG09dp3qkjCU9pmGMzpJtNQpe4Hy6XT8omwBYv8faeCttn/JsIXIWHr/Di0Ji9e8+fM WqXl4V39o4hHqpXQy+nu3vVHsB3C3RwHOt7IUAWCzyBUy7K+1O2UTBQPdbsgjKrlGqlHkpItsZK 8Par3G2IHTfF3CA== 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. Reviewed-by: Alice Ryhl Signed-off-by: Andreas Hindborg --- rust/bindgen_parameters | 6 ++++++ rust/bindings/bindings_helper.h | 19 +++++++++++++++++++ rust/kernel/block/mq/operations.rs | 17 +++++++++++++---- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/rust/bindgen_parameters b/rust/bindgen_parameters index 6f02d9720ad2..128731e84775 100644 --- a/rust/bindgen_parameters +++ b/rust/bindgen_parameters @@ -5,6 +5,12 @@ --blocklist-type __kernel_s?size_t --blocklist-type __kernel_ptrdiff_t =20 +# Bindgen cannot extract values from the `((__force blk_status_t)N)` +# CPP-macro form used by most of these and emits the few it can extract +# as `u32`. Block them entirely; the `RUST_CONST_HELPER_BLK_STS_*` +# definitions in `bindings_helper.h` expose them as `blk_status_t`. +--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 9da216faad51..b1fb3afee4ca 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -119,6 +119,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 89029f468f44..6b2fcd76372e 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -6,10 +6,19 @@ =20 use crate::{ bindings, - block::mq::{request::RequestDataWrapper, Request}, - error::{from_result, Result}, + block::mq::{ + request::RequestDataWrapper, + Request, // + }, + error::{ + from_result, + Result, // + }, prelude::*, - sync::{aref::ARef, Refcount}, + sync::{ + aref::ARef, + Refcount, // + }, types::ForeignOwnable, }; use core::marker::PhantomData; @@ -124,7 +133,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0051F3E44F7; Tue, 9 Jun 2026 19:13:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032409; cv=none; b=dEQwPWeTVw79grVTRSn5l5ew6xZnQ2ivhsup46IOIMSV2FVtrJ+DSbvHHjfgkJSDdIE708/lOxUl9jWZVE7Fw5T9TDVfs9m3salN7tS0YThWFwbD9xPrF8outQp982pVf4OvitxnbLLTOkTOA1SytSh8cfc9yIvJILwC0rL0vTc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032409; c=relaxed/simple; bh=OdnzBNU31O2ZnfIiXoHm34uCgI6yyOystzxqjcmD7w0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=V8dvGvaynU7zyX5HfjL8V8s01T9u75LOuxWIyQltbh9m4fizLZl2+hBSiN8iiSgpBv5HRyx62T1EPWRK0HH2gpd91iRTSakXD9ykyQ2zxZiWc+mhr0GQhIdjqcc2+jCv3FIr8tC9Um/edTDjzFRqZqyNFp1PF7tMwM8tQEpZ5ig= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=HR+32Ab6; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="HR+32Ab6" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 59F731F00898; Tue, 9 Jun 2026 19:13:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032407; bh=xna1WpbAQAdejm6/MlRFJZMGV7MPhzRJAHMLP9ok4LQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=HR+32Ab6FJNQWMIb7U6W0t84SUfNsrnH6DGh+nZ5jR3jKeOZDMoi2NN7ru0iGIivv ojFZBQZyObM0wn0qIkh1Ik7/AkFCbtSKDLhXp0BqbkAV6hYL56hn3qJAmJcmBP/GYb DiAkf0wFTGLKrUYKRDCITHXPSTdQc3br+nCd0d8Vvo0aTGKdYldr5x2PESNkkIEATu J7CzUGWP7O4/NrpxW+RR4r6dMlyKnFQ1Oae6hadu09e/9iMWby7zz1V9O1P2RXxI7g pNlpfUdilM9Znj9uzxr9U8OiQI5KWU8qkcpUfrDB0o6BKYHAmK/ptKPj51djE83+aY WYlcNcCyylXHw== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:07:46 +0200 Subject: [PATCH v2 07/83] 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: <20260609-rnull-v6-19-rc5-send-v2-7-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=18998; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=OdnzBNU31O2ZnfIiXoHm34uCgI6yyOystzxqjcmD7w0=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGS67cn9N194bV1gIXZ48CMQNOsMI0JrrRTqg m7uBzydPyKJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkugAKCRD6UCkIqsW9 0EJXD/wIYrkihr2QLiwhOeo6NswKQa+vORjWQxmP1BRHjf7Xee9gLeBWtQorc6SXtfFK8kegEOo 4ogIkzViCmtG5hMI9u5GKt38TyUy7LqOjZY5v/5Q4SZNGs6orjDbyW0SWM1D3xic/RZs32aBHWy N8XYyYN7oy+SiUdYPVIHCkEC4ZNbprMRd4aNQH5ddnkxpUwtCyddEBZ4w/G8OcO/g+6B3rjDvhz 9Xw36Skbfd7s+wRo00JctLwsEYAlItWN/Ky4wzrx25S237xa8Lo7SueZ4uuIqjkZT/T6ndhFYK2 bUGOyo44TcF9NOATDxLJB9xPec6nF/TF3UOD8qLxtBt8Zc7nij/rBRL+UjUBXyLxzfnp5Ksbr7P ExnB9Zn+C8qM4rLX54pbsmD5WkHKfSyQVTnInQQEiP/irWwTtqAt3VK//+qr1Qwu0QiupQkp5mx TGS1XnmBfeRcYYGWhWjl8sUWIFbYlDtuc4Lwo7zpes6Mq8oRhHsmFbtKvAUMk/6Yb5uUrpT1hdg i36z/hsFcqzQpAgIonwFU6LWfec9x1WCSX/fR/ayB/SWJScGrH+bth71ZmcCgHeh0/PHvOwsOb4 ZyqpLSqMrGtLqNQfw5NFeDhG/zebTIizuBmqYfr473kTETlMHXjr7wbtLQngj4e2F0S7N85VnJk aNIlTh7JZ72Ng/Q== 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 | 32 +++-- rust/kernel/block/mq/request.rs | 231 ++++++++++++++++++++++-----------= ---- 4 files changed, 176 insertions(+), 123 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 77ccc6850961..69cf62475446 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, // + }, // }; =20 module! { @@ -129,15 +134,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(()) } @@ -145,11 +145,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 1fd0d54dd549..b8ecd69abe98 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 6b2fcd76372e..bb23a32f3983 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -17,11 +17,18 @@ prelude::*, sync::{ aref::ARef, + atomic::ordering, Refcount, // }, - types::ForeignOwnable, + types::{ + ForeignOwnable, + Owned, // + }, +}; +use core::{ + marker::PhantomData, + ptr::NonNull, // }; -use core::marker::PhantomData; =20 type ForeignBorrowed<'a, T> =3D ::Borrowed<'a>; =20 @@ -45,7 +52,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 @@ -99,16 +106,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 }; @@ -120,7 +134,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 cf013b9e2cac..7444de3c8522 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -7,39 +7,45 @@ 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, // }; -use core::{marker::PhantomData, ptr::NonNull}; =20 /// A wrapper around a blk-mq [`struct request`]. This represents an IO re= quest. /// /// # Implementation details /// -/// There are four 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). +/// There are three states for a request that the Rust bindings care about: /// +/// - 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 ensure we fail tag to request conversions = for -/// requests that are not owned by the driver. +/// 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 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 +72,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 +83,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 +175,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) }; } } =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 Ownable for Request { + // The `release` implementation frees the underlying request according= to the reference + // counting scheme for `Request`. + unsafe fn release(&mut self) { + // SAFETY: The safety requirements of this function guarantee that= `self` + // is valid for read. + let wrapper_ptr =3D unsafe { Self::wrapper_ptr(self).as_ptr() }; + // 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>`" + ); + } +} + +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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7C87940ADA6; Tue, 9 Jun 2026 19:17:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032678; cv=none; b=ZeaJUm5yUio+QRoHX/Ob+B+zaGiNmoZ90vCmQEHjGZ/FXq54yH8drDqI1/PLHWxkb7ZOOXN2srh2oNSLUrsTEtL6MXOk5PLit0N7gAzDrHqjbYVSGzh8ArBnhSOs6IIb5/CkzX/2bs8x/3XlsfgbHQNhMdpu4ddQaA1t90SPDvE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032678; c=relaxed/simple; bh=L6FvQRzSXtXDkmSJy+tkngQbsHtKH4Hgr0ZGlG8PxDw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=HQn/JRYBugV+PmKax9gh2xxz/ISVjndmca7zseDx294lsgFqZgo91fy0vLal3f54Go/N9khX7anc9VyhlIAfhyATmXEcIKURI0qaZ9EGcp3lsGTfOaii5xDHXVFMbBuaiuES21w5lN8SH0E6z0jkXmlMdf/9Uj0flAkfdrjSdHA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=f6x+HKtj; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="f6x+HKtj" Received: by smtp.kernel.org (Postfix) with ESMTPSA id BE2B81F00898; Tue, 9 Jun 2026 19:17:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032677; bh=qkLI2fdYByXyCgDm4iAAw5nvnTz6NbS2AU9RKI9kMBw=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=f6x+HKtjJQa//HVdP+jdTpzgQjMzAXX/8DUi4w8cMw/3BsbtWRGn6bd2e49kjJLr5 MCqb+PhjDxHazy6xuaIa+TQF0xQ5bPBUJQCwGH08tJ8HseG3ZXIZ5P7I4j+ILQPg7c P77wIouKbJuIMS9bOJ9JRPVQ216+621pDd2wUQmBx9/wiZFhl0m1fc0gYsN0clMt8x 4tSiZAZp1nt/nGi1UAanZhW267SFA5ipsbZGlbhyEoBMuiT6tdDTBk+TBk42hpAuNA GHqlY+n/YJbdj52UmMDovdi/cmva7RBh/7TR7syAiDLxJxugc9wUY2/WmV7OLRe98Y LKRSWXqiSEXkA== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:07:47 +0200 Subject: [PATCH v2 08/83] 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: <20260609-rnull-v6-19-rc5-send-v2-8-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org, Andreas Hindborg X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=9400; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=IAlWkCq69WO4onn9FqDheU5v7rVQqR0ixWrEuSku+Rs=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGS7NyFEmzZd5N67NuhGtgFIfmFZn7yqauVsq hBmYwRv/xuJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkuwAKCRD6UCkIqsW9 0LPiD/9L2omkcWDTbakIiMk5Ihy3WJcxnZhFx+yd0Aw6J6PCE1tGLdx8egOh8hEmDqKO4LSpjCS HwuaOKBz1zq7J1wqo83FMDQ3ihDLZShdQ5bVAzZ+SDREyie0Tf7tln/nyMdZk84VykRn05tI3wY KtGWxuwh1LDwjPCzvbRUsNEfGfXt/izPMZsRj8CAx2F/R90al+BiQgPKBuPAMvoVpvxK5IHgH+4 oVf7qrNw0SmhClSKn4UeYDhMjnj7eVm2zVVVhB+0z8zAuXZByKMdD5/wKkNZbotpEr60e7dXjAz bdT+b4dUlZ/TJNuXTzuI9kmXNd0iRwl7il8KPNwoX/wfeEyzoOUefYWsGhEnpzRSz6x8eYBWwdz RON96H7Z7Pza5TakQkhStQdmVkiMHGKSM7YSuuG2vHtvYHhf2zyKCRRptBcjlbKRNuPPlC8UDIC LT1VHlg2nlnTDQkugFTiLJYX9p+mqPdYKoWb5XD1P2kqA7YaZfDaM4W5vwwjw+XJLFN9DF3jrCg A+4vK44bZesXVrZUgG0GVrDly1Gg6+n/IKnZiE7UyImj8/RqxOk3SjbKRImkCIdjQGF9EKKb5BN BV+B0LxuV1V61KuZ1UC3Y0GgXktYCDk75RjGNw0oMXutl51YvoKIy5fkMYS1OdKoVpVMUvY+erF Z+yXmonrvVQnC0A== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 From: Andreas Hindborg 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 | 26 +++++++++++++++++++++++++- rust/kernel/block/mq/request.rs | 24 +++++++++++++++++++----- rust/kernel/block/mq/tag_set.rs | 24 +++++++++++++++++++----- 5 files changed, 74 insertions(+), 11 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 69cf62475446..dd7a30519870 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -132,6 +132,11 @@ struct QueueData { #[vtable] impl Operations for NullBlkDevice { type QueueData =3D KBox; + type RequestData =3D (); + + fn new_request_data() -> impl PinInit { + Ok(()) + } =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 b8ecd69abe98..7718b106eb49 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<()> { +//! Ok(()) +//! } +//! //! 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 bb23a32f3983..c49ca2e8bbb2 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -29,6 +29,7 @@ marker::PhantomData, ptr::NonNull, // }; +use pin_init::PinInit; =20 type ForeignBorrowed<'a, T> =3D ::Borrowed<'a>; =20 @@ -44,10 +45,27 @@ /// [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`. + /// + /// Requests may be cleaned up by a thread different from the allocati= ng thread, so + /// `RequestData` must be `Send`. + /// + /// 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 + Send; + /// Data associated with the `struct request_queue` that is allocated = for /// the `GenDisk` associated with this `Operations` implementation. type QueueData: ForeignOwnable + Sync; =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( @@ -252,6 +270,12 @@ 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 not to move th= e pointee of `pdu`. + unsafe { initializer.__pinned_init(RequestDataWrapper::data_pt= r(pdu.as_ptr()))? }; + Ok(0) }) } @@ -271,7 +295,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 7444de3c8522..1882d697dcf3 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -107,12 +107,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) } @@ -120,7 +120,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 @@ -132,16 +132,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 { @@ -159,6 +162,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 dae9df408a86..ec5cac48b83f 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -8,13 +8,27 @@ =20 use crate::{ bindings, - block::mq::{operations::OperationsVTable, request::RequestDataWrapper,= Operations}, - error::{self, Result}, + block::mq::{ + operations::OperationsVTable, + request::RequestDataWrapper, + Operations, // + }, + error::{ + self, + Result, // + }, prelude::try_pin_init, types::Opaque, }; -use core::{convert::TryInto, marker::PhantomData}; -use pin_init::{pin_data, pinned_drop, PinInit}; +use core::{ + convert::TryInto, + marker::PhantomData, // +}; +use pin_init::{ + pin_data, + pinned_drop, + PinInit, // +}; =20 /// A wrapper for the C `struct blk_mq_tag_set`. /// @@ -39,7 +53,7 @@ pub fn new( num_maps: u32, ) -> impl PinInit { let tag_set: bindings::blk_mq_tag_set =3D pin_init::zeroed(); - let tag_set: Result<_> =3D core::mem::size_of::() + let tag_set: Result<_> =3D size_of::>() .try_into() .map(|cmd_size| { bindings::blk_mq_tag_set { --=20 2.51.2 From nobody Fri Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A6D813749E0; Tue, 9 Jun 2026 19:13:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032437; cv=none; b=Tbb9ZpAnOv6OIm6qJy33/0m4/3+f2kZyPZ/3QpJpnyfrA/jBE/+8cniUJm/8e6hFUNHek9bPWgqrnNREl/GjvIWKLXNTzhCEz4FuqtG7jSruiFLAkROSuCC9woFumT/AhPHafzsoQ7HuO+Rd+t0KMdlkul7GnQhEIr0vzz8Fvcs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032437; c=relaxed/simple; bh=WeYmUyzlnXNFxatIkPpAFWbysNLEuc10mgSTySvU0tw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Ylte/wn0bTZ9abEsh6dPaeBcW+W7EOlk57QeGpSJ2gtyv2zdcosAovquvNzDakRGSjJ2q23Exrtfm141l+hvaXCtBBcZwUjxQI4K7Bgt3rIFVp5DonnOXUrrB2PwWiOlo7txsuWc53XdVmFSuuMPk+VN1xqC5KoMnt5Qz0UP1nk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=kfcOINeh; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="kfcOINeh" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5B3FF1F00893; Tue, 9 Jun 2026 19:13:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032436; bh=75VKKvc6NJYD5SbM7TBq2nTJ46z7L7NmdWlGRZUewiQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=kfcOINehWKcp3xHtTXGSLHWSOG52K0OZctFq88FEdbCXodQGd98cSiTeLARe08H/I WZdb4CcyA44JkRtJQOnH/HoVzi84FyydSlaaxTVzXONcu7HTe4IMNMs9m192s1V4OF 1QhvpFsnOU7Th4JJa/thaqDZrlpgAfjaUMzEFoGsvpqs0Hm/qDgndrXyKY8tDQy/IQ xQElXKLaImnBu5kviUwfTySaGPDXwGWQfFcjblEEUttXyW68n8jo2WCSM1ZxyGYD6y W7ZYebArVcSCDN5wNYEE3mToENk/qla4+cSl5PVFg5tTDZe7McNe4oS5YEC6wJdRqE Co35lL4onwnaA== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:07:48 +0200 Subject: [PATCH v2 09/83] block: rust: document the lifetime of `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: <20260609-rnull-v6-19-rc5-send-v2-9-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=2300; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=WeYmUyzlnXNFxatIkPpAFWbysNLEuc10mgSTySvU0tw=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGS8pbk0dWGZmS3+sy2e+s65BD8zbm6UgUtCy OvQvHcZviCJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkvAAKCRD6UCkIqsW9 0GOND/oClSp+fyePfjxASjbnzpRkbpOcYzsPIfJIFLzOEbuxRBKqUBX5gb6z9iM9ekAtle7EjDm LgmLhm0/UWbN/qEG/RzRfvXrZkkzRon67UBiFRqCaTGOdCAYMbrJSuUyxdCsf3Tr3lU2rundNb3 ZxcH9LxUz0Bt2ojKj+vWsVfflJtJzKjap3viiFj8LecPgipAQqexSqDuIn8JC4hOFb2MuvCMuBY 1j/QRQ/ZH5DxEfl9E172ZsadOzLm4CNgQeHRznsTqMcqi5iLdG6u1SJ6ReQozO53l0/faq7WhdV BF/L0lwriH76mvPK9gueQ4jM0vFUhD6CwrMwHHvEzgq1oSJNITpx9or18C6Dr7o2/AssXrvOz7I hc1I/qc5eslTGSkvxp1ftgksTj+cp0ckdVu9Hoo9Ok+UaB7cUHnFxEpmvRj7/x1Z0AiiFimQaJl uyLRyDmCazphHvIP3oZ7CwYBfed03bT6/5vKQfqWZQ3nZCShOG6Lq2WKTKkN4NCBGng4/TOdJ3q prIb/yRoyDjqzavaK/zyHafEQsUdcKTbO1lzIhC7McNRlpKUf5bt16+nGRdSXbEgfVPiKRd3c60 vARab+Z3NoVCOPqseBUmafgMa3ay/XiiOb1f6d14r9Jv161VFFrwaww5vGDsLalB+c7IHagEWly JokXC674ET8kROQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 The `struct request` objects backing a `Request` are not allocated and freed for each IO. Instead, a fixed pool of requests is allocated when the tag set is initialized, and each request is reused to service many distinct IO operations over the lifetime of the request queue. It is easy to assume from the existing documentation that a request, and in particular its private data, is fresh for each IO. Add a `Lifetime` section to the `Request` documentation describing this reuse and its consequence for the lifetime of the request private data. Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq/request.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index 1882d697dcf3..a6e757d8755d 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -29,6 +29,24 @@ =20 /// A wrapper around a blk-mq [`struct request`]. This represents an IO re= quest. /// +/// # Lifetime +/// +/// The [`struct request`] backing a [`Request`] is not allocated and free= d for +/// each IO. Instead, a fixed pool of requests is allocated up front when = the +/// [`TagSet`](crate::block::mq::TagSet) is initialized, with one request = per +/// available tag. A single request allocation is then reused to service m= any +/// distinct IO operations over the lifetime of the request queue: when the +/// block layer needs to process an IO, it assigns a free tag and hands the +/// driver the associated request, and once that IO completes the request = is +/// returned to the pool to later be handed out again for an unrelated IO. +/// +/// The private data area of the request, which holds the driver defined +/// [`Operations::RequestData`], shares this lifetime. It is initialized o= nce +/// when the request pool is allocated and dropped once when the pool is t= orn +/// down - not once per IO. As a result, [`Operations::RequestData`] persi= sts +/// across the many IO operations that reuse the same request, and a drive= r must +/// not assume that it is reset to a fresh value at the start of each IO. +/// /// # Implementation details /// /// There are three states for a request that the Rust bindings care about: --=20 2.51.2 From nobody Fri Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2488A4D90D7; Tue, 9 Jun 2026 19:13:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032426; cv=none; b=jz0+RVGLOs0RPGrivG9MTXDfrexAStdlhBjqCc6t22VaU7bw3JpMJ4EGBy+suDUeVbDh4ETFgZ9sfKwN2ulZ4tOx82cvI+daXeYXb5d/LfP7x21spa6NOz6kJ2//2s5mZkckvCW26BgdCkvebWgoHpmip1gDAkrBuOiezOXK39I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032426; c=relaxed/simple; bh=MxU/avvgu0ObO+hSiIzPv5Gh1hPwXbxknG4ZgL5JR2I=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=EGLCMZIrgg6QK9QVtaEbtW2iex2/0tsCjJx4RxssPELNm5tYkt4qcySOSw/8wnYcXSoAhWUTRRXlCZrUiD6y6mSV38ALXWCUGMD2nyAd6fCrRmebatnkXjkJESFKaVRbPTpGDgoSOsrtKWnyQZ58QStt/dEvADLYmvvAE7faCxg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=AfnLzzTx; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="AfnLzzTx" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 01E3A1F00893; Tue, 9 Jun 2026 19:13:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032425; bh=XPdx1oCo+cDGKHpfIjN0PVTLlY+JG6MdSS1fHOHa1sM=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=AfnLzzTxPZjO9ehKPb9b2SbLpNUEFeuH3N5AStrAtc1iU7GQOokbmWJ0vrkEep7me LYGxii8ZJ1Lweycf+53XWBXkQT4k/aN1MjrknxAdDKgx1zDZfW+Ogg4XyOgy11UwHp uEzRtPDDHT+hjlz+GCUjrAE9d4qnc8G1pgLrgXeb2f1p6ZZuJT22q5ISlmqFLe4TGv Mf1Dxv0FEx96l56ZNL9N9o+rMsa7uf9L2sWaWrK/2P6N1PKnbZ8l/9/r4qglqLBJ+G aPyiTFjSGKLYnBoSMjZwdUHOo/7pANnZ7IJMwayibLplmz+zpKji9NvtoV4xOd+5nt RvpDS3daO9tjA== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:07:49 +0200 Subject: [PATCH v2 10/83] 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: <20260609-rnull-v6-19-rc5-send-v2-10-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=8262; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=MxU/avvgu0ObO+hSiIzPv5Gh1hPwXbxknG4ZgL5JR2I=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGS9QazYsxgfYMLVHCHgKKVjzrf3FJbLdn8ww +/TN93edpaJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkvQAKCRD6UCkIqsW9 0FJGD/97Fixr3jGIlPgmEP6cWYHvaI4ONk7ITRFh7/tfq5iF7O4OiQV58e9R+Br+J80W8kv8t72 a9tF07TjgAHAz5j2CtgAghAX2Dog0PE2Y9kPETYarm8PqqG56T43ctr0Z3icnQUGTx8v9OAPJFY HezJ/hX7TrE7h/AZlpX02lgY4DAXCpXkqbmoKvTubJRoau5weN4ESUs0OwmZ9hk2fnCgZab8FDN eOASHKr+srZAMT8DQM7RU1cfj1UlwYE6smitL195w03eRdDC16SM4O/AbUgEJ+hRq7R2Rq5Fk4D DMlI5L9fow6PzL0kU/7IdYYZiF85VPgTqdHwEsRjmYVDlGT1SfgAxlmvsuJdheqAFU0UojAdiAs hofFoQj18th6a//f9AtAZMupTeMIF7pM0rGb5LGqyG4A8f94XgtCA0tTNbeJziyQJLR7KcCmQiw /fgLN/DbRyt0lojLLFTZMDqGdAi3y8VdlvVxY2txEXn+eL/4n1nVIXQ4F/X5wsAdHdM3yLWS6ri NAF6l8ns0rcGkHdC1xNkIwVR6FeAjV661vXf9xEyStqI3OOygyGV6GYCZeA2G3EvlpMMyjLt6wt gzFUzDE2E8DVZdZddM9+dzNCXN28y51+7uIjXQF6iJ6O7NMjYY2GbJVl62xBqcCfbDdnxmcmn0E CWQNJg/g0Pwbo6w== 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 | 5 +- rust/kernel/block/mq/request.rs | 142 ++++++++++++++++++++++++++++++++++++= ++++ rust/kernel/time/hrtimer.rs | 5 +- 3 files changed, 149 insertions(+), 3 deletions(-) diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index 7718b106eb49..a03d46d274a5 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -107,5 +107,8 @@ mod tag_set; =20 pub use operations::Operations; -pub use request::Request; +pub use request::{ + Request, + RequestTimerHandle, // +}; pub use tag_set::TagSet; diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index a6e757d8755d..0b14f584c9d9 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -15,6 +15,14 @@ atomic::ordering, Refcount, }, + time::hrtimer::{ + HasHrTimer, + HrTimer, + HrTimerCallback, + HrTimerHandle, + HrTimerMode, + HrTimerPointer, // + }, types::{ Opaque, Ownable, @@ -23,6 +31,7 @@ }, }; use core::{ + ffi::c_void, marker::PhantomData, ptr::NonNull, // }; @@ -145,6 +154,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`]. @@ -329,3 +343,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 d57276496ed6..096b18523c73 100644 --- a/rust/kernel/time/hrtimer.rs +++ b/rust/kernel/time/hrtimer.rs @@ -496,7 +496,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 @@ -900,7 +900,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 83FBC4DD6CC; Tue, 9 Jun 2026 19:16:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032571; cv=none; b=KcZIYfcnPgSs8g/Z+GUB+A1zA33XtH7xwIwDhusSs0PRwRvOpEDa/5sJmBA5Xa+OgWLvtiFTg+JQmhI/HuSShHpHERtoVqV7AyX62exp9qObyQM8uEwgTjFDXXY0masNsScoYGI6pafR09vWedk1RJ+XY8CfrtHky//0evHlCfk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032571; c=relaxed/simple; bh=2WihwLY3BKsR/bUh+8xVFcjC68ymvUSpcl2aNu7m3ro=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=qAe4GP86f4SJXjl8is57UzJnOSprPigMX75zpgGXepxAMSh+cPi1TimmkNUdck6hrgKJO6sVQ9O2rTNiKV7+52VHmXVtou/tHfD+HAi9Du6dBSDRpg8BgVkoD4NvkZKR0sU2ejEeHRp316VkIWNcwMSoUW+kV5dY2wbKSpuHFnM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=RUzkqU+Z; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="RUzkqU+Z" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A48B81F00898; Tue, 9 Jun 2026 19:16:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032570; bh=Orhut8IhA4k/fI1g8VtdPP5GiujeWv5wMA2yYIWY1p8=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=RUzkqU+ZXawZrmlyk04BLnOhiuJzHkfl61joHhZcBaJzAK6ZcL1k6CE2FsibWxiU7 RYBL/UjhqKpDI+/39m/Z9JiKYUdcO9PK8ohduC4fYu3E0T7i5TCnvfraScXLOaEGh3 S+gZoTn3QrqlDt0k4RgvmC3st+7ihZ7uo8hy0+zJrYWIuqmhltbPlwr+FbaM63QyRz 11bRCpQFv0VBLxZiB3u+NuaeVqyC7/FHEQu/r+IjOwfRffZHpGz6sep1y/Xcc7GZ4X 3+pEleRTX5HmupMZswDkaL2Y8vLRrOkd4yCOBevKooC1A4X0p6ue4AtmVgoE3jCKKY vxwxONaV/5lkA== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:07:50 +0200 Subject: [PATCH v2 11/83] 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: <20260609-rnull-v6-19-rc5-send-v2-11-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=8400; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=2WihwLY3BKsR/bUh+8xVFcjC68ymvUSpcl2aNu7m3ro=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGS+rU1gDuulIyezDBGUgrXsFtIS6RTlCiVRL 3EBNU/is9GJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkvgAKCRD6UCkIqsW9 0KnwD/0bwBHMPBBCjhyRdIxobUfjyBnsEtn2wXnswCENj7OIZnPKIiR4pzsIijIprkOLi+Xh/rR Aj2+xTHMdRGVHPz/gZX2rsrde9a3ar0Eqv3xd/8WtXbW6lFynQTgEqBsDxD4xFHDvegdth1FFFT zX6Qs7QhtK9QoQvbrizt2qP/+Rc6QCoLB7CKXr4UhWV1BIC4TQjVZMlhGvF+VTW5UU6lOTQ72IR aLsHVbe03W2nuO8HOhl6d/ZqzPAeifmI5KvH6ep0ARDTCIGK+7g/TLrNKlSScLmMUI5MQ5H0XYb X8n+mA5zynpdOnPAuFv8N0LTtN7TaOokE8mGIiTXgswZXdxqzx/mGIRxNY6iQ3yOhyyCw0wAwCS Cbr/JD8gjj8qUQcI2yThBBSvXn9mJOxuqVJSYT4QAeemEQE6w7nKrCITN5OZ/6haENVWg3jHt+W I+nckkSVS3F8v8ZU42L/H9IHaekvx3yaQuGlA5lZtiHrGSPojoJevRkJNlMc7gbaLoOfyPYU0oa YF8Q4ELRoA2VIIp1QMDK4jKssuV2eDGgH3QC2E76jp0oKV7BGb2ul+h1T6nqQjPxfua+9H0TXal /QJfn3BY4k+XN9vv4Dlvo2mk+mIUBCPqRTcdN1t+d8CDzAJBpIT00omYkYnAesldTy4wyE3fiEs /ZAVOJ55sXdCcww== 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 | 34 ++++++++++++++++++++-- drivers/block/rnull/rnull.rs | 63 +++++++++++++++++++++++++++++++++++++= +--- 2 files changed, 90 insertions(+), 7 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index fd309fc17e66..83b474f6da60 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -25,11 +25,15 @@ kstrtobool_bytes, CString, // }, - sync::Mutex, // + sync::Mutex, + time, // }; use macros::{ + configfs_attribute, configfs_simple_bool_field, - configfs_simple_field, // + configfs_simple_field, + show_field, + store_number_with_power_check, // }; =20 mod macros; @@ -56,7 +60,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()) } } @@ -79,6 +83,7 @@ fn make_group( rotational: 2, size: 3, irqmode: 4, + completion_nsec: 5, ], }; =20 @@ -94,6 +99,7 @@ fn make_group( disk: None, capacity_mib: 4096, irq_mode: IRQMode::None, + completion_time: time::Delta::ZERO, name: name.try_into()?, }), }), @@ -106,6 +112,7 @@ fn make_group( pub(crate) enum IRQMode { None, Soft, + Timer, } =20 impl TryFrom for IRQMode { @@ -115,6 +122,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), } } @@ -125,11 +133,22 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Res= ult { match self { Self::None =3D> f.write_str("0")?, Self::Soft =3D> f.write_str("1")?, + Self::Timer =3D> f.write_str("2")?, } Ok(()) } } =20 +/// Wraps [`time::Delta`] to render the value as a bare nanosecond count f= or +/// configfs attributes that historically used this format. +struct DeltaDisplay(time::Delta); + +impl kernel::fmt::Display for DeltaDisplay { + fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Resu= lt { + f.write_fmt(kernel::prelude::fmt!("{}", self.0.as_nanos())) + } +} + #[pin_data] pub(crate) struct DeviceConfig { #[pin] @@ -144,6 +163,7 @@ struct DeviceConfigInner { rotational: bool, capacity_mib: u64, irq_mode: IRQMode, + completion_time: time::Delta, disk: Option>, } =20 @@ -174,6 +194,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 { @@ -189,6 +210,13 @@ 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_attribute!(DeviceConfig, 5, + show: |this, page| show_field(DeltaDisplay(this.data.lock().completion= _time), page), + store: |this, page| store_number_with_power_check(this, page, |data, v= alue: i64| { + data.completion_time =3D time::Delta::from_nanos(value); + Ok(()) + }) +); =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 dd7a30519870..3e7a47e6d0e5 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, // @@ -59,7 +68,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", }, }, } @@ -79,6 +92,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_nse= c.value().try_into()?; for i in 0..module_parameters::nr_devices.value() { let name =3D CString::try_from_fmt(fmt!("rnullb{}", i))?; =20 @@ -88,6 +102,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { module_parameters::rotational.value(), module_parameters::gb.value() * 1024, module_parameters::irqmode.value().try_into()?, + Delta::from_nanos(completion_time), )?; disks.push(disk, GFP_KERNEL)?; } @@ -111,10 +126,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)) @@ -127,15 +149,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 { - Ok(()) + pin_init!(Pdu { + timer <- kernel::time::hrtimer::HrTimer::new(), + }) } =20 #[inline(always)] @@ -143,6 +193,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E2D704169EC; Tue, 9 Jun 2026 19:16:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032577; cv=none; b=ksEVNbYOrpecxYQxV85T1GUVaVTCPjQw3K8u4pgylwV0FWuBtAuzlFyBx6liTSsGo4ILgVuXFDho/3iUXHhAwof7WrjpUE2cr1bqCFF4QTq5jFW/jDNiK1wXXOEXvkHnEW/wRtHP3B7UT/M+zup8oKZ5a7Q5FgzlXbUmpEO4FZg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032577; c=relaxed/simple; bh=MD4KthFPqC9yqNG7lwJ7FKyEFdf1M3xtRHuNlziU+/4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=b2+QqKptKQ2pSMVGaNfnV4JDGscUKaq6q8tanvLAOvhUcX4MD85A/C1jjakiSJ/sRTvJ002j28P5+8y4ccVd8Q/P46Ja4DOv1aF0K2U2tt1cezMeTJ+hDJDmxe4BUNhfpXjSzIH/PIBaHt+wrcqQne6cu90TUmpUH85fSW0NpNk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=RCcKjJ1H; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="RCcKjJ1H" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 0CCC31F00893; Tue, 9 Jun 2026 19:16:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032575; bh=kvKtbMgKXL7TKURKnSmGyfkyvz09Oj1eTG63xB9PZNE=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=RCcKjJ1HGChIO9qP+6RrfwQR2NL7Cna/HkYCocVsHaBqaPSdZC/+8eJWwOjX082dB oRmKm5X63ENy3wecfZlQMF3Lg1ysUYkS2H+isv27Gs2ws4CCB7l+NvFlUyr3crLgF3 XKeaO1IBhr1E6vgID3JIpKdFHezYKaBUbN41YqB0bKNUZ5dgcSSUlxhQ15jaOqZ1Uj 5/+/unVAJ5rkmzq1MPLOYLegw8X2hzgwr05kXNHGkocIja6iFqbHMiipL7dagZhtZy S+Vsy7uz+zFbv4AYeR77pKLMz1GSO9N3xbsmf59yipdP4fH9OGld9B/FOp/H0AzRrD v3zbOFOnTX80A== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:07:51 +0200 Subject: [PATCH v2 12/83] 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: <20260609-rnull-v6-19-rc5-send-v2-12-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=26863; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=MD4KthFPqC9yqNG7lwJ7FKyEFdf1M3xtRHuNlziU+/4=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGS/kV6kl5jr8mSNQOXdMEOoxVRwwlXVIyaFt gsw3kFYWdWJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkvwAKCRD6UCkIqsW9 0BliD/kBO545tHG18FZOvXIBiLFK0Zxi2O6ZJR0ejyMz1UTO3dGfY/rVkQUcTUj+vZ/PLRR3iJB mvWHEgqpEzVwyKGWjZXcje+u+yofFdAQcugEI0CkpcoHbYvLikHQpxm9LheZXTKYC48DSBAiez/ Ordtcz8Z8LdMYH+UUAWAUnRgs7V0B72qpJ+WsrKcqwwHLzbNOCN8C8q33ZEKr3PfRrxaKbZwzbi FDYnSS5Mpyp13JFUKmd1IF02dd5VmSK2ZyZBFFmtmL0iuczsQXQlNogPT5eR5zw8uD8LPaXWW5J qWzrv1s747H8TciOpt7pFU7WBTn9IeASfg57IRYFBUR1ZsK00UZYXLH8nNybIfaDPsnDJrSuouP a74NVTVDX3kRkfOASgexXaugjOTbfbKm5D4Dst8Ky7Ar3NXR2u5sYMEKKem9KWDu/JVucc4uqWh xg5LlXIvtIIFbgpBk6aw9zPIYehTmD2eOgykAtFZGZSpVq8M8usU6rh902TkW4MFuUvL9k2slOj vngU3+i4gn6ZNOfxfflyG46MDzAKQijByzt6VgP+rIqZwIrgLd0G5BcNpB2V+ntGsyRSOB0uoMY KUzf/Kimi4KjXRKi/XHLJQW7PDoIqrPMCTZJLI05/he6yY5Z6VSOOt80FYKUu1rdAyQoI2Hsn9V 5gN1BjeJElPPsSw== 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 | 147 ++++++++++++++ rust/kernel/block/bio/vec.rs | 411 ++++++++++++++++++++++++++++++++++++= ++++ rust/kernel/block/mq/request.rs | 49 +++++ rust/kernel/page.rs | 2 +- 6 files changed, 617 insertions(+), 1 deletion(-) diff --git a/rust/helpers/blk.c b/rust/helpers/blk.c index 20c512e46a7a..6a70e1306a3a 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 @@ __rust_helper struct request *rust_helper_blk_mq_rq_from= _pdu(void *pdu) { return blk_mq_rq_from_pdu(pdu); } + +__rust_helper void rust_helper_bio_advance_iter_single(const struct bio *b= io, + 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 b120e83d9425..eb512dad031b 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 the sector index in a page. diff --git a/rust/kernel/block/bio.rs b/rust/kernel/block/bio.rs new file mode 100644 index 000000000000..af84f94a85fe --- /dev/null +++ b/rust/kernel/block/bio.rs @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Types for working with the bio layer. +//! +//! C header: [`include/linux/blk_types.h`](srctree/include/linux/blk_type= s.h) + +use crate::{ + fmt, + types::Opaque, // +}; +use core::{ + marker::PhantomData, + pin::Pin, + ptr::NonNull, // +}; + +mod vec; + +pub use vec::{ + BioSegmentIterator, + Segment, // +}; + +/// 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`]. +#[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(self: Pin<&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 function, `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> { + // SAFETY: by the safety requirement of this function, `ptr` is + // valid for read for the duration of the returned lifetime. + let bio =3D unsafe { &mut *NonNull::new(ptr)?.as_ptr().cast::= () }; + + // SAFETY: `bindings::bio` is pinned. + Some(unsafe { Pin::new_unchecked(bio) }) + } +} + +impl fmt::Display for Bio { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let iter =3D self.raw_iter(); + write!( + f, + "Bio({:?}, vcnt: {}, idx: {}, size: 0x{:x}, completed: 0x{:x})= ", + self.0.get(), + self.io_vec_count(), + iter.bi_idx, + iter.bi_size, + 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 Pin<&'a mut Bio>; + + #[inline(always)] + fn next(&mut self) -> Option> { + 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 reference. + // - `bindings::bio` is pinned. + Some(unsafe { Pin::new_unchecked(current.as_mut()) }) + } +} diff --git a/rust/kernel/block/bio/vec.rs b/rust/kernel/block/bio/vec.rs new file mode 100644 index 000000000000..99ab164d4038 --- /dev/null +++ b/rust/kernel/block/bio/vec.rs @@ -0,0 +1,411 @@ +// 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, // + }, + page::{ + Page, + SafePage, + PAGE_SIZE, // + }, + prelude::*, // +}; +use core::{ + fmt, + 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 might 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` +/// +/// The iterator takes a copy of the bio's `bvec_iter` when it is created = and +/// advances that copy as it yields [`Segment`]s, leaving the `Bio` untouc= hed. A +/// `struct bvec_iter` is a standalone cursor into the bio's `bio_vec` arr= ay: as +/// described in the kernel's [immutable biovecs] documentation, the `bio_= vec` +/// array is immutable once the bio is submitted and all of the position t= hat +/// changes while iterating is held in the `bvec_iter`, not in the array. = Such an +/// iterator can therefore be freely copied and moved, and advancing one c= opy +/// affects neither the `Bio` nor any other copy of the iterator. +/// +/// [immutable biovecs]: srctree/Documentation/block/biovecs.rst +/// +/// # Invariants +/// +/// If `iter.bi_size` > 0, `iter` must always index a valid `bio_vec` in `= bio.io_vec()`. +pub struct BioSegmentIterator<'a> { + bio: Pin<&'a mut Bio>, + iter: bindings::bvec_iter, +} + +impl<'a> BioSegmentIterator<'a> { + /// Create a new segment iterator for iterating the segments of `bio`.= The + /// iterator starts at the beginning of `bio`. + #[inline(always)] + pub(crate) fn new(bio: Pin<&'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(), + &raw 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 0b14f584c9d9..98e54f0586d1 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -33,9 +33,15 @@ use core::{ ffi::c_void, marker::PhantomData, + pin::Pin, ptr::NonNull, // }; =20 +use crate::block::bio::{ + Bio, + BioIterator, // +}; + /// A wrapper around a blk-mq [`struct request`]. This represents an IO re= quest. /// /// # Lifetime @@ -127,6 +133,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(self: Pin<&mut Self>) -> Option> { + // 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 + // `&mut self`. + unsafe { Bio::from_raw_mut(ptr) } + } + + /// Get an iterator over all bio structures 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/page.rs b/rust/kernel/page.rs index e4585e1dba0c..a3473dabf587 100644 --- a/rust/kernel/page.rs +++ b/rust/kernel/page.rs @@ -282,7 +282,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E42A84DB568; Tue, 9 Jun 2026 19:16:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032582; cv=none; b=in+wEXynmgW0erQKSJEuA0Oa5C8KuoU8JeWoVq0K/WAKUUdJDh5UyWdgT3y83jNEJkLuI47b8uYuthFc/VOK6aJ3JEGao6T+XkQnVbix9uSfygjmZG54b2G7SNPXOekzGSH3jtLrx6ixlS8fst6Y7XRH9FGKkseZNuo9hIAGWHs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032582; c=relaxed/simple; bh=VH2F2EOtPdHOtGslg/ieQKsqzCG4W9fZoz+7ESvbugA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=hPlryEbJjPxzvQauUukl5KBQo7+Cm+9JR4/Sc5qEB1tj7asjKAc8frP9/pp4nOaG//5DGOxB4T9SiyzlSNlyTEcWaO86FOFKoR+q6ZyMuyp7AjjmDutsbVTSKZNSp7DjKTdMv9uVrIfItxWrdwVtVhJtGZ0IlhlXWklPr7vvHZk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=oErhkH5/; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="oErhkH5/" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6D7FA1F00898; Tue, 9 Jun 2026 19:16:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032581; bh=e+XDXLChurzP2r5iP0BK/Xmgv4b9xTF7r8tpuv8eUXo=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=oErhkH5/RvsU0pYxaGvsH8J04eqib3UlDmfBu006mx+qHGpOK7zmrbYXnd8jITMuF pc81Wo2vAhP5ShprAawD0YzrZr87uu5PIeaaLzwRLlCVGDDSBs9TH2ljfGejE9QFLo 1njvvfMDcPJU8TCJTTuhAe7mvxLaofUY6xXDExPkk+GqRIHJMnXa4IJQ2I64+mQ72l k+yiINMEiMIk1sgMwmop7aLKxDLB8ovJCfSPMlRtQWXqeB9LZRg9L+e3S6ekcNJT2b QutGQosDKhYG0taub6tEw+i183GSKcSUN19YbgznxPNE29KMY1fZzL4VKNXx8/NsZX +JGLZ8fby/Z0Q== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:07:52 +0200 Subject: [PATCH v2 13/83] 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: <20260609-rnull-v6-19-rc5-send-v2-13-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org, Andreas Hindborg X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1258; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=JgJ/K9nxltyXzGsFxBuvnKQJ52RCZyxIiod6jnOKKd4=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTAUkk0LcDHa3vjJ0+TBKsyJketYcEgtB4lQ W0HgArxGNuJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkwAAKCRD6UCkIqsW9 0IBMD/9LTXBuBuAUnixSWlmlKgnI0lwXtwNfEAaUVoVY0DQ7Lsr+Hb5gWYPkTVBCmyKN8FWHT9j o4tQWP+BaptnALclsraBmmGiV7+m6J2mJZtJFRJosJIaDUkJ99PN0+Nw5GxTUl/vAcoKJiM82hD Femk99RwRCgWkb9zu3RpDebOw2ZP3ssLMoxeKASj7yTQpV0bBaiuWMCfQTYBhnOebVQd96l9URP sPHS1MuQeUcebhigYWJn2EfeIke8EljTgjcQvQf1Kf+lkqWvM5TRf44GXxQZT1mZPjtsBwrBwKu tfWGwgloAAj6FQjS/nZqrJfmV+gjIOEwQosq3LNCAkwFhxW572m/UN47kUc0dvq5qDSnrI8J3JM yuLJODtcJjkUvj5ewwqZpcYa16o+/bS6jo1rEG/EU7Neoni+FE+5qw0Cf8TU70WqqoVMdimrQ74 eR1SmgLCp1Q4/dXLKctTGI5wSOB/MxtUw+ecU75dVlvSjGQpwDOx727LR2bhbBRg7TJy/KFw9lC iTRExSEHadwSnr1F9vreBiS2LyvcUVkNiQp0w3s/lNHw3hYtCiCUD3NNPyWUp3WzvJ/6HBm9rtW +3oIR8BySUbSN40gULMBY64GsLjcYE39eEbdR606WEgnuNvqhOvfals2ZGW2mmHiEPNV7wEivcG GGuPyYMYUFbkwUQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 From: Andreas Hindborg 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. Reviewed-by: Alice Ryhl 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 98e54f0586d1..19bdf17de166 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -116,6 +116,13 @@ 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 { + use core::ops::BitAnd; + // SAFETY: By C API contract and type invariant, `cmd_flags` is va= lid for read + unsafe { (*self.0.get()).cmd_flags }.bitand((1u32 << bindings::REQ= _OP_BITS) - 1) + } + /// Complete the request by scheduling `Operations::complete` for /// execution. /// --=20 2.51.2 From nobody Fri Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 74D884BCAA2; Tue, 9 Jun 2026 19:10:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032241; cv=none; b=PXpHLZE1kI756FS1oWWlEra3j/bxLkHJYHJCrzHyYu23O9Q4NE26mHbQwamEtjO0NN/EBOoSIyqNHeqk4BoInDW51ePvTZhubZ7c6eTB639rlzVyUhDUqgv6xKO3O6dlFu6tCMAaH2nTav6f2frvDsoMJWPXbP9nzPd/sCDCEfg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032241; c=relaxed/simple; bh=s+4gufW3UhlqKWqsXkBFJk+U/u784ZMLj0EBgYGeaY4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=i3zditjiS/SFKgUFEUgkkGJHHlg7wZCP9HK2dqxfe3oIf/0KipJYrlzwRtB3foC/AdUQGxNOKwvAxsYHlLpF60vgai90JtjtG80wF6tb3nMj4lkv+LXGxJg+nI1UQkW6yBo81eFVp62Zz3WHHUb1iLEQ3kOwMf11EuNQxFo7YS8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=lm1doXFR; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="lm1doXFR" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D8B451F00898; Tue, 9 Jun 2026 19:10:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032240; bh=/7g3lYXs2grCtTPuxxxiBCjUGdtPSieCEyjdPX9TpUo=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=lm1doXFR+4S/8kmMih5UCQi0ymkFJpYbFdiQ4qX5gYG0t+4lOjfYlBb4TgoD2nRjO GTwRJXGjsTwXU8o4zK2wI28qJksbQVaW05XjXPDEktcjkR3z30IYnbVFTM1/oKOv8t MnI+RmR2Q7V1c7JbjKAL6d1u19hu9S4WiPdhxfg2ry+sJikFqEW49XDa3wL4muXkah vpX8HfJYx81CFuVlZkVNJ+NESf/fTdhMXi+W1NuIbUufSA6BNZmazZbjVhnsr+h+ar e8p/gcX28taHZSurKCisc2oDXGkk1gRO/bxsoUYZHTDxiJ89WUbomMm2zJ5ND9cijY U3nyLU577Amtw== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:07:53 +0200 Subject: [PATCH v2 14/83] 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: <20260609-rnull-v6-19-rc5-send-v2-14-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1117; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=s+4gufW3UhlqKWqsXkBFJk+U/u784ZMLj0EBgYGeaY4=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTBPbl9h3oqRHD3qmw+YLlvzYSsXXClOB/ws R1mq9pxblqJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkwQAKCRD6UCkIqsW9 0KnlEACzvlyksB6u2aU1pLlwZ/fl8WaqAxGeKb94S1jgEr3YgSy44Du05LDxbJTqrljEY9Nw89z JfCzmr9MkUKrJFoBp6X6gVMw1lICFvyQw1eStDW86Z4pEJAeqQlqegnCf3bmCxi4Oi2ePveenVd QYmQ3M2//o3r3izL8vSGEMrcxWTWDaQyLy77WjI8ThjP10CwsrUb7wPwQi6Mrai76pZZoZP3d1f Gk4ghY/2l162hUvogAKsQboBvTdP0vMvdN0Qdlx2mACa7Z1EBwxIGT0WOVChatkbRrXPCVL0+f1 UGRNkA4J0iLTdZ1HRQGsmbDzSDs4F/+nOMCJjO017Hi2dM94inwsm/eXoEJtryuLj7JC2fbQHgF ma50rc5KnKtYYuA4x/dNRz4OCwWjqok9TBht6fYIuhCXT11ZCmdUiZO2VqIqN/0WBRc6bhSNnjG sduuyzMviPUz7oITP214t2u25YFK3sAXB4X5y9BT9v5C1YfHc57kfpNJ13ALLgvhIQ7pWgDVWLp huVyio+8RPWJvvNe03euJ2nD5PCi14wActvblauFVVBb7Bor5eGmz8Sp/zAzT8RXQwQ/E7AzIPd 24Ip+kB0VvL/Uy2Q6OsuQ+b1VHbSblRGX8KbOyXakb/krEyDBw65i0uFYG9WioBneDLlANLenZm rdFLIcZjtTAwNwA== 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. Reviewed-by: Alice Ryhl Signed-off-by: Andreas Hindborg --- 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 a03d46d274a5..23660817df29 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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5BD614BCAA2; Tue, 9 Jun 2026 19:11:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032264; cv=none; b=n2ER+mqCC539XKwebZsHomOjZYUYsIJ0Vck+i8eCY/sf0/d70mvo9vKlwKKHOBDF2B0Ma7fix359wB7W7vYjoT2fEMyL6dmaSHXlumDGDEizFQH689N0j/BLuOEQdZj+kb++ie/6F2P/QpHKdMmJX56PR1j0aR6TPCa+S6xPnfU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032264; c=relaxed/simple; bh=DINncKwSuL7mJVmDgVRSZcLatYZj5+iron/JtJAzd08=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=BjAYIrgJ8+fVpTSnb1hjy3zPJekJZhg9GkS+a82unIBKspQ3Dn9l128S80ZiZYKvmZesVC+uHNrQZ9C2k8o8odSNCLZgFgPuY2alR39ZSnOkvC6tpPqtlisBOCId2W+RF8x2LmIZTb3QECWYHCLzqLlfLfnPpvnl+lqVlrENcBc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Q00rT/I4; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Q00rT/I4" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 052B21F00898; Tue, 9 Jun 2026 19:10:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032263; bh=PS2VkUNd369JJf7ucum93WJqs0QoezRjkkbireGl6Qc=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=Q00rT/I4IRrjIQkppmOTatg2ODde300ODDsVgZck3pnItEIKRYQ1fjMjWRjEg5jKy hs3C97xkNLaYC8fB1xltqdWpZPJBIGJYQwDMx145u1ZoC66OHw/EGu2ety+Zo+vGVG LMJX7ngWW36omMtZKnfh1rUf0IvCsxrNkHUyFe4CSWA6UaiSm71LhlbsDT3ULR1wcS jOJxSN0qiMTrMLW1tOdtuDMxZEaTXDchUKarZTJbXn1eIMd2oD1SU3eXK6SZeDNrXN yZ5+tUohuQp1vCZEjKKWBvGNmO4qTIHITHAQ3zvzW7Y2bItALkXpZxxt3QDUj0aNMw XuqEs7rTXHwWw== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:07:54 +0200 Subject: [PATCH v2 15/83] 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: <20260609-rnull-v6-19-rc5-send-v2-15-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4659; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=DINncKwSuL7mJVmDgVRSZcLatYZj5+iron/JtJAzd08=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTCt2lO9pNrd9xgsB1ZZBoV+xLyOTEIyMITV qniUELa4O6JAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkwgAKCRD6UCkIqsW9 0KLZD/0QWgWovCM4gbcQhcPZLiCELO1QvVZhElbrfYyNm/BG39ys+cP95Ms0QeFoszLrvo2utWP o8jUQ7WsPfvhFyho27hGT8W3RxxdMzeZIaPeLI/ukpoxn6tQDbalVECoeMj7R6v7dHvIo+IvuI9 n5gkRjBh18GmuOrb/w8TsnTU0dUTC1KDS7XhvmXJKzd935LFDwavGqrw5hOkl+t6ZdGPuyfbBbW tSP2xid/fY5AuWkDYzEmAKFu1v79Cvn+FPej52mDFLWvOmrUmSZtCtiWS9eXWxa+Xoad2qpMsPC gG9Rao+SSazio3jF/VfQW7HOyWgLQSn/WEtengNJTG9kVTuyckt0bISduRzdSDHVDX2zwKXG/Im BT8yi0IS37hL225IWmRukLCc5IeoWAPlrkuAmh0bck35x91bImVG0oTFkSp+0A+WWwQvXDCwg/+ fsiFOI+m7ltGPF1R2XxCBpgHL7TUB2vNV63oGWHH+ZSjrA7cMmNvnxyOtIVEZjGvk7Mp8LeXr3B 0u2BCV39WJ+D3wvhsuMIMluYRb2XBkQg4m4bgBhfvVc6ovo7+zhA5RCyzoySk03Ne2HGZYmZeL7 R+tpuZ99WJc1Xj2RpK/MJUmYujM4yhf9RihfZfBtKXVzrZmpFMOZ9tHBDtFqfLUaddbf5i4WWAx 9p4Xn7qvIOow7Iw== 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. Reviewed-by: Alice Ryhl Signed-off-by: Andreas Hindborg --- drivers/block/rnull/rnull.rs | 5 ++++- rust/kernel/block/mq.rs | 6 +++--- rust/kernel/block/mq/tag_set.rs | 13 ++++++++++--- rust/kernel/block/mq/tag_set/flags.rs | 21 +++++++++++++++++++++ 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 3e7a47e6d0e5..746ddadd11f0 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -128,7 +128,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 23660817df29..e556b3bb1191 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::{ diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set= .rs index ec5cac48b83f..5b1a5bcc978d 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -17,7 +17,7 @@ self, Result, // }, - prelude::try_pin_init, + prelude::*, types::Opaque, }; use core::{ @@ -30,6 +30,12 @@ PinInit, // }; =20 +mod flags; +pub use flags::{ + Flag, + 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. @@ -51,6 +57,7 @@ pub fn new( nr_hw_queues: u32, num_tags: u32, num_maps: u32, + flags: Flags, ) -> impl PinInit { let tag_set: bindings::blk_mq_tag_set =3D pin_init::zeroed(); let tag_set: Result<_> =3D size_of::>() @@ -63,8 +70,8 @@ pub fn new( numa_node: bindings::NUMA_NO_NODE, queue_depth: num_tags, cmd_size, - flags: 0, - driver_data: core::ptr::null_mut::= (), + flags: flags.into(), + 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 000000000000..b7eaccd200a2 --- /dev/null +++ b/rust/kernel/block/mq/tag_set/flags.rs @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 + +use crate::{ + bindings, + impl_flags, // +}; + +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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CA34D4D2EDE; Tue, 9 Jun 2026 19:11:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032320; cv=none; b=bOrK8VJzW+mjeSNIXE0f/ZeEDms4UpVJEc1NSekNKKRj3huqWP6XcPU1VvVQuWZqkyGWjpmpdvQflJkEznavd2sxhU+uglJ9kM/nWjZb/nbr7IaafAf/9Q9AB63Cu9Hx/N3EQV36Pf+0OZiTQhYurNQRE23HA3sRnMK0cKlj0DY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032320; c=relaxed/simple; bh=5tWrOfwAoTdtiugODrPJwqvRH5XTpwTVzL/RkoxFmKQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=T4X/wvcMdLnhdJuMSkuUej6/SASkJdHMMaosgyx8ANo6mVmB0P/Jse3ZUHg9p4ygt29GlO/J5rjICEvFgrTPoNY9YD5tri5Rd+Ihr1xSSnuY367Gk6rxrIx8MhQQpislmjb8TUJc80jo+nutnsC2KCvux+3qMGhKRb2kRs5I6UA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=HbkJriII; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="HbkJriII" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A8EE11F00898; Tue, 9 Jun 2026 19:11:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032318; bh=IkD0QE1JNG+5G4E9lQ6Nh6HgjvnmUhj4qhwVEK7b4Kw=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=HbkJriII3mozpClgOgi65m6h2a4N5QW7igQRqNhtcRg/1/9VCyekam92oxNs83Vv2 8ZvF5ZsjHQnVxNDCtEQdRGKyzgJ3/iPPhkHsDnJO+d61HEqeSOpIM+myPDxNVkTr+A A5e2liydG4NK1SLts+w88+lMJ0AErM2/Q16rZP4xSQYJv0CKTs6i8cLvXKWT6uJYCR eM5CSuk0AtUQk0qW2NMFSjq8VMNMyRNP/XsYZZKEME84eDwrVSf+OgloaEqROe8fah gCpp5t3MPXJ+StljDASOVhFgkcGDd514WrNe5/cHXTO8/VK95jGEbXiwFKKzldxg/b +92RzJ2tyv7ew== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:07:55 +0200 Subject: [PATCH v2 16/83] 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: <20260609-rnull-v6-19-rc5-send-v2-16-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=9051; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=5tWrOfwAoTdtiugODrPJwqvRH5XTpwTVzL/RkoxFmKQ=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTDlpQLAb1Gdie0mQdmixKF/RmwXPiQ/Q1mE yviretqzF+JAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkwwAKCRD6UCkIqsW9 0E5BD/kBzAHZRaW7Q6j0G0U3xNqihtUwFAbCJwp+84ZCx2vnJuP6bH2v801ATOF/7Ik6Gips0mP +46GLnccPBOI5WabhrpnL2CZ2ZSDBbGUoOm2v2n8HsGRyh61KptIriN+blMlb9soc+VrzGNW1To g1B1kBaqYTeVEAPC8iJ5gYzcZTmEwNEIYkHEwT4DlU+OBQpA8/+2cMyJuWrIHXBKe3/z7LgcdnP VFkuPPsHkxklFDpZDCuCWYRH4wKwx4kn3XToD8jS7B4AFDlh4eDimVOzFOr33BnmbPceBK+zQon bBhjFJH4D6gbSmcZ/uCBYq1aPS4Vj3grlTBCX/KmBfBAY3+VJB5i4cuB/am8pIR+CF1J/8JbS6a SO10KADB1axzJrNZLOTOOKQpzRWNvqu7eFtAgdnxkeqaa4Wg4iyrJzvFJSSe1mDYzMYAj2WJOOj 1uXy8L1k6bYhn16XKayO5bwak8fjQMHOpFUAj6HKxLIXMqRDx3emfaDqNTndTkKYQGaf+drMJh6 xhn5HyA7xZdrkfBE5n/gQ9j37eojuawnQPcEiGQfIAWuBVX9aFgv/BT5u07a4pD+Z/CBrpOdpDr VTPbIr3B7RLh0Z/hJrmf31ZOEUxysfmNaWOSxYcaPC5gBdCXlYfbJjrWvId0h+DW6PVqkl9a0Pb cmr4YJ0LMImhP7w== 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 | 8 ++- drivers/block/rnull/rnull.rs | 126 ++++++++++++++++++++++++++++++++++--= ---- 2 files changed, 116 insertions(+), 18 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 83b474f6da60..8daf2ca409ba 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -60,7 +60,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()) } } @@ -84,6 +84,7 @@ fn make_group( size: 3, irqmode: 4, completion_nsec: 5, + memory_backed: 6, ], }; =20 @@ -101,6 +102,7 @@ fn make_group( irq_mode: IRQMode::None, completion_time: time::Delta::ZERO, name: name.try_into()?, + memory_backed: false, }), }), core::iter::empty(), @@ -165,6 +167,7 @@ struct DeviceConfigInner { irq_mode: IRQMode, completion_time: time::Delta, disk: Option>, + memory_backed: bool, } =20 #[vtable] @@ -195,6 +198,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 { @@ -226,3 +230,5 @@ fn from_str(s: &str) -> Result { value.try_into() } } + +configfs_simple_bool_field!(DeviceConfig, 6, memory_backed); diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 746ddadd11f0..8e4d2b270bcf 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, + memalloc_scope, 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, }; =20 module! { @@ -74,6 +74,10 @@ default: 10_000, description: "Time in ns to complete a request in hardware. D= efault: 10,000ns", }, + memory_backed: bool { + default: false, + description: "Create a memory-backed block device.", + }, }, } =20 @@ -103,6 +107,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(), )?; disks.push(disk, GFP_KERNEL)?; } @@ -127,17 +132,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 @@ -148,11 +159,72 @@ fn new( .rotational(rotational) .build(fmt!("{}", name.to_str()?), tagset, queue_data) } + + #[inline(always)] + fn write(tree: &XArray, mut sector: usize, mut segment: Segm= ent<'_>) -> Result { + while !segment.is_empty() { + let page =3D SafePage::alloc_page(GFP_KERNEL)?; + 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_KERNEL)?; + tree.get_mut(page_idx).unwrap() + }; + + let page_offset =3D (sector & block::PAGE_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: &XArray, mut sector: usize, mut segment: Segme= nt<'_>) -> Result { + 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::PAGE_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: &XArray, + 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; + +#[pin_data] struct QueueData { + #[pin] + tree: XArray, irq_mode: IRQMode, completion_time: Delta, + memory_backed: bool, } =20 #[pin_data] @@ -182,7 +254,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 { @@ -192,7 +264,27 @@ 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 { + memalloc_scope!(let _noio: NoIo); + 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()), @@ -205,7 +297,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 487B648B389; Tue, 9 Jun 2026 19:11:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032292; cv=none; b=ojD3wxANc2cF/B2xsSSHMWdfe7a92krZ+BrgCJuY+kalElkpbU/OXPs6gR+PWNCxVSgKtrXV9MN7L5dowrPeYbPB7WyfmSqRkquFgq7W5O8VoMyMsKZ12LwRJ8yi/A+6fDF5pSkeojxVO7caReqXeq06L/sRbJ1nX7YHQZlpO7Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032292; c=relaxed/simple; bh=lzez8j8zqRLV6gbk6iWV9jQDc4roUyUsLUzz7LgTQpc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=RtNTCzdL8xu99mgvwfHEaQkww7xEwme8rye9+SW3l/BoTh7aBi7/aMSODMbMO2DYkC1Yx6EdTGexoO8yEV7eYDSvWvJJAvk4jKHt9/3m5xXjkj5c2NNrlwRxuUmp1nUXvuwNm/bIJTPbt/8j+qdRCbuQObMR6VyW3cgpgdzEe3g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=EZHUEfJY; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="EZHUEfJY" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5B8D21F00893; Tue, 9 Jun 2026 19:11:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032291; bh=GI7bMkhPaaNJq9fuPFFaiFn3pKn1655aVcxzZP2OKa8=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=EZHUEfJYGN1jW1sAkKwr559XDdZdWoWVCrAILg5iUtNDYxlfsJNdrRbWicD1yB5U6 TqvbVW1DOFFNkiF95/STtQ2qQ3lxZaSw/prEon1gGhr6ejYbhr6Z4uX1xLVAsqj6SA E5X+4oDlhNpgd5O7Z+Wl4uwsP/WWaa6Vlnf6hEybMLT560soImnS9AvaU/2C4pXUa6 gmDjya5H7oMAy9ZIJmjxQubrEgO0jRt/Zv4H6OBZCY9HGIvVxL1/dSRjIXT16lrXGu mlSGYbAu6Uq7rzxdps74pGMWhKiMsxSxvvcpeByzHKlYNVj/9iP9fDuoW9U4jmpuhk BAv/+lIVO3QPw== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:07:56 +0200 Subject: [PATCH v2 17/83] 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: <20260609-rnull-v6-19-rc5-send-v2-17-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=7246; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=lzez8j8zqRLV6gbk6iWV9jQDc4roUyUsLUzz7LgTQpc=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTEugFhxWOtTsK/8FP5iDzSz6aLCztmYQWKh q35rokSuQKJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkxAAKCRD6UCkIqsW9 0AeqD/wNZq1pWUrULFGmIEbOx73Cl6XyiQcErKBUzZVdec3C6xECJjipuL9gjVjAUQI/Q6B0QLj 0UFnRtYmRP8OMrfo2AYryyW6ZcWJFuzRqkm3d36ZkBgIwY3GZ6pIwb2SBH5S2GmvxokxEJdO7z8 n6k0M8Z4pUta9Cw8dDy34OwP1axygPMoVzy5hX7Hh5spwxuuxhb/8YhgtD+J15wxS+BPzdxtdRc oEcTbUilzEqMGUxPfL+AwSTo+vZDOSjWpekfkO1n6bH/N6nuhrpp/2Ftyy/XTSAEL7C7ZlXINmE lZoyLq+Cdnw99vQFKX7orrRhflx7aRrtTPthwzudOpVJcX2ecadD3fd69Cjz1E5V1hCJi0wyNvY qUlV0dv7zQrvbIrQbSmHd6C5LGHEa5RPzfn8L0QH8CSo3rSXFHeeX47TjRN1xFt8Fr2m5q+C+6D 67O/5bHx2QnGRoOcBaLKxn1Prtnzod9OyXk/Kv5r97GWwXCNuCp9sarwHMqRTg4DkK4OV7rypKB AZ6S1TaR+FtmyZfDUt/TyGeRrA0PIBanT4xsYsAgH4B5AL7wQTwpD3tgUzrlBK8PC90wzncRnmM vZIbR+CxAXXWtMbaMYOVGiCPo9SlisFxFRwYQtfUSj+s/rx9AR/pr818kvGmPgvvUxfBeLC9SD6 C61RVrxw2QMRGnA== 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 8daf2ca409ba..0dea92a9079b 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -60,7 +60,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()) } } @@ -85,6 +88,7 @@ fn make_group( irqmode: 4, completion_nsec: 5, memory_backed: 6, + submit_queues: 7, ], }; =20 @@ -103,6 +107,7 @@ fn make_group( completion_time: time::Delta::ZERO, name: name.try_into()?, memory_backed: false, + submit_queues: 1, }), }), core::iter::empty(), @@ -168,6 +173,7 @@ struct DeviceConfigInner { completion_time: time::Delta, disk: Option>, memory_backed: bool, + submit_queues: u32, } =20 #[vtable] @@ -191,15 +197,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()); @@ -232,3 +239,32 @@ fn from_str(s: &str) -> Result { } =20 configfs_simple_bool_field!(DeviceConfig, 6, memory_backed); + +#[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::cpu::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 8e4d2b270bcf..a7c35f33631a 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -78,6 +78,10 @@ default: false, description: "Create a memory-backed block device.", }, + submit_queues: u32 { + default: 1, + description: "Number of submission queues", + }, }, } =20 @@ -100,15 +104,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(), - module_parameters::gb.value() * 1024, - module_parameters::irqmode.value().try_into()?, - Delta::from_nanos(completion_time), - module_parameters::memory_backed.value(), - )?; + let disk =3D NullBlkDevice::new(NullBlkOptions { + name: &name, + block_size: module_parameters::bs.value(), + rotational: module_parameters::rotational.value(), + capacity_mib: module_parameters::gb.value() * 1024, + irq_mode: module_parameters::irqmode.value().try_into(= )?, + completion_time: Delta::from_nanos(completion_time), + memory_backed: module_parameters::memory_backed.value(= ), + submit_queues: module_parameters::submit_queues.value(= ), + })?; disks.push(disk, GFP_KERNEL)?; } =20 @@ -122,25 +127,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B4D5E4D90D2; Tue, 9 Jun 2026 19:12:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032341; cv=none; b=Hubltu8gTEt4Ic2QSlfyMMgsndjwaWJ213qiiTddzZLZQL2KTujglPvlCPCwbNmgkMZGpfg/5yPZyylCr026ypcBsLij241EJij4VE8bnwoEs5/tW9jZLXqmW1ce8nbcWGfVJI3QJrapqnaENVvSREmxEykCxc7+h7JrkBn/2hI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032341; c=relaxed/simple; bh=W6NPFvanCyRxN5s1t/T7s5V5cnK6/DFM8N+7PAduKdA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=AGD+26GvhEh0kt51MI1/MY6AE81VLZ0cdruE/uJXB2e9p+SK8EgPPOcPX0st2y9M0fDcAQYpjqvpmEoyZDkoBvKm99BWSaUisipSaUeZD2+YxyvRmCSn9mUvGvApAm6PKKnqSZEuoMMDYEmJnntjn0MD1ORcAJnFwnAWN4uJOoo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=C+s/R9Z7; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="C+s/R9Z7" Received: by smtp.kernel.org (Postfix) with ESMTPSA id CDF061F00899; Tue, 9 Jun 2026 19:12:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032340; bh=EJTHJQtN5OQlA3kXWXe5gEXmVH0+1Vi2EqXteO8lhZw=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=C+s/R9Z74OZNoJ8c+zWo3+tXPkSQ+ixbl0fg0/issp3wrRbBBBDjbljXa2uPO40b8 lvDSlTFdE5WDffemG8zFJBUWVVw52K4d2aRHgQnSFGoJ/DesZRKu4t66tJUj5oHqUw rIVFHikAFWiatp5W/5/ePqMuoCZ3RSRsCpB4VLPBgGifZwPobcsuI4F3Y1RuISna9J /wrG+6sor58g/3TrfZwA64XDDFuxNDieIV5kBqswTFCyD+R9eUPzcazGTxsk9T4ath yqICpu5rd+iAj1RXksr36w/9V1BUsLsgu+Rzt9TQiGiJiKxbzs/jiWPMaFsdf70n99 pLLnrIV/zc78Q== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:07:57 +0200 Subject: [PATCH v2 18/83] 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: <20260609-rnull-v6-19-rc5-send-v2-18-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4856; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=W6NPFvanCyRxN5s1t/T7s5V5cnK6/DFM8N+7PAduKdA=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTEij8XwF+frBqMC0Gcp9AC7AnB2pwS4JrQH ek+Hp3GUFiJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkxAAKCRD6UCkIqsW9 0ItzD/4oGYrGoLcxFeTDDdsyisJ3mvbEwRaynStL7lKIivphzirl5FLUxqssIjKJqijnFMCvoGY QjYl5mD44FSDo8XdWpx9SsDLRQOjuenhzIKIUlTHH7uoUaDLly3ibLB30xRnzVN8rtmm0ePch06 JdWR5Cy+yHWNuxPK/kHnwFqIRpEiGXHr2yVYCAICPrHt61+T7YzgBRjRC//48c/Bm4GzeJhJkzf d/80AbRIcQWyWKjc4AttPoAzJiS+WFbtuZyy/5o4xoAffsyViZBd4mE9fdC3Fx2xxx1gTw1zIls o+ZG41gtBClHyAiIergk19jmGs6WAYbe0E/egoNw21XVfvuGZHO9TzkEKhUZ2/TDWOB9Xjal6Eu G4hioZgaPf+eTZndWSHitjaME2pR+HMoc4wqPyqPJJyeVExFS31HQTw8x9WN7EblmK0LdWucfxy j/zICLMl1crwW62S+enQaUlrdQTvA6sKs5akQDG6kR+nk2gTv/mnCsLp0Kus6V/1EqDGypSGgEA 5xkde6SImoDB7LjH6ySd0zpP8404Y059fXRD2PYoQ+npTmyUJVRVMdrMesppj0zJC0MK+qzBRAA zoWEecfczYKXWNIGs63icFgBLyZ256kJMhEytcjoSQITNRfvjEvC/WJ6GH6T88IW9tFuBrzAb6h Hg/g7XV17XnlawQ== 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 | 24 ++++++++++++++++++++++-- drivers/block/rnull/rnull.rs | 28 ++++++++++++++++++++++------ 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 0dea92a9079b..71b38373be33 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -33,7 +33,8 @@ configfs_simple_bool_field, configfs_simple_field, show_field, - store_number_with_power_check, // + store_number_with_power_check, + store_with_power_check, // }; =20 mod macros; @@ -62,7 +63,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()) } @@ -89,6 +90,7 @@ fn make_group( completion_nsec: 5, memory_backed: 6, submit_queues: 7, + use_per_node_hctx: 8, ], }; =20 @@ -268,3 +270,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::numa::num_online_nod= es(), page + ), + store: |this, page| store_with_power_check(this, page, |data, page| { + let value =3D core::str::from_utf8(page)? + .trim() + .parse::() + .map_err(|_| kernel::error::code::EINVAL)? + !=3D 0; + + if value { + data.submit_queues =3D kernel::numa::num_online_nodes(); + } + Ok(()) + }) +); diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index a7c35f33631a..30de022146ec 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -21,12 +21,18 @@ }, }, error::Result, - memalloc_scope, new_mutex, new_xarray, + memalloc_scope, + 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 +46,7 @@ OwnableRefCounted, Owned, // }, - xarray::XArray, + xarray::XArray, // }; =20 module! { @@ -71,8 +77,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: bool { default: false, @@ -82,6 +89,10 @@ default: 1, description: "Number of submission queues", }, + use_per_node_hctx: bool { + default: false, + description: "Use per-node allocation for hardware context que= ues.", + }, }, } =20 @@ -104,6 +115,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_h= ctx.value() { + kernel::numa::num_online_nodes() + } else { + module_parameters::submit_queues.value() + }; let disk =3D NullBlkDevice::new(NullBlkOptions { name: &name, block_size: module_parameters::bs.value(), @@ -112,7 +128,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { irq_mode: module_parameters::irqmode.value().try_into(= )?, completion_time: Delta::from_nanos(completion_time), memory_backed: module_parameters::memory_backed.value(= ), - submit_queues: module_parameters::submit_queues.value(= ), + submit_queues, })?; disks.push(disk, GFP_KERNEL)?; } --=20 2.51.2 From nobody Fri Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2B5B9408A39; Tue, 9 Jun 2026 19:14:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032461; cv=none; b=mNNIm0nmhg/mUDmeGswGiIzTudt6wR9sxkWWyLqtmwl924ctzP7bTzCyPTbJKtn60MHscECEePl3TbuTs6Qhwu+R3gqujn/YK7ZCGHaryzqiHn0wLGCYwaRn6gmTRB2EzPCt6MOio/It0s77zQPcI01mwR8oyUxtlH9ky8UlFMw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032461; c=relaxed/simple; bh=rKHqlbFiDQot+ZmlglcKCo/ua2u/3jUgradS2liawPs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ZiN5AgPvF9rx4jB/02bpegc3Y6cyKhQCNpMIg5yyiXR5po1vunz06YAMO2FNUR/oa0g0C6jedSBJXbRAvxc0OC7RlZFAdDcY59/LOwIAOdw7M1iA3ohC9vzXfmvZ7oQch20vgr6Sipp2GQERUUlFAjZwWQYVgdaA1Gvo0i0t+hM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=UK8qLP51; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="UK8qLP51" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D48C11F00893; Tue, 9 Jun 2026 19:14:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032460; bh=aU3etQORaY46jYoSyqScYvf6b4+RP+R/4DsnBtOMojA=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=UK8qLP51dGiAHHqYJNrAAu4gqz9LbJnT1abn0iB1tN10CWSgMOXM2WHUkBuVqwprn DAVdOdrbUNzNNmXxmIMyFVrnn7/AsWnCmjFeJpZjs/O2nRVbntEp81Z30KMiAZSAIB 998Hsvm0FhjYz2KnJlg9AvjGA1eBiiucYgpW8+K0xooG5sDie9pGXGGtVBTWdcY7KP ZEmvHizkHOwxqG5BOIebpXaZe7OTSSOBKGfU6WH0jYzLTW4MUSt9B+B/XqjTaCuPr6 DWLt3B4V7u4OrSIqyhrvnRLh9YNFYfiWcbfHtfE9tAL/V2/28zcuCpeBxei5KXA47L LjCTTS5k/lt4Q== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:07:58 +0200 Subject: [PATCH v2 19/83] 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: <20260609-rnull-v6-19-rc5-send-v2-19-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=3058; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=rKHqlbFiDQot+ZmlglcKCo/ua2u/3jUgradS2liawPs=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTFc9bXf/EXOOGGprjUbeDTFs4TcTazosGSi AAATe1MXLSJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkxQAKCRD6UCkIqsW9 0EwTEACc0x57IjxoSWw17YFWmZ1nV5wtXGPR5ov9KpAor1CoF8ZnBF3cyjr2dewCH7eSzjwEA9F Yfk+ESoiBRMnHdyje1lz3SCriO/4Vb8YConPJcu4AY8HUuhcepOJkl6VMLTYd33cAYVhIku7h+H OB1Emtf9lOVAMHkHFTftNCMu3+4MK8nUZQNf4mUXV3BpsyGGJ6CXdAj5gqA/osH2pwU/G4u4D5L A/GYIrZKUyrIvLkjSiDV29/5dBBin5+vDLhpUVY6X3OnhtToq3CnXEbje+rxGbCqLZ9VlB+lu1m CH/VEHjcI+V3Fb1lLBhpy7id7+DrNqJ24GdvMGVoy8XwfQpAFt7f0Q93gHdSPIFEmC51KHiYAgN YJgR2OZAWcpPmRTym9OGXl1cFRMUz37yjNyYJNUD+Ev9406YcJ126JM02/O/TfumwdIO4hSeEqm Iq6IihHZ9BYODl1oDDSKwtIaPmrss2DD9th4AsvlnaSSO4wxwRQJFOy7a2aZ2FMnGl81IuoXOpW V+uYlzSqrUX7DFW828I7UOUsYr/aFziabmI90FsDQ0V2yiiO0AQV1OqNv6F+9UKaMNCdMTUMoUu ZNkSD5gMmFewkyEHVpnbi45VlDgxxer+vQdKV26NDyRrNpM1at0y0DAiMwzbNp9OGZ2nYKU2vTv Zj7VqK0IxKW8Upw== 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 | 11 ++++++++++- rust/kernel/block/mq.rs | 5 ++++- rust/kernel/block/mq/tag_set.rs | 4 +++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 30de022146ec..6323327d4a5a 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -174,7 +174,16 @@ 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, + kernel::alloc::NumaNode::NO_NODE, + flags, + ), + 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 e556b3bb1191..bac15b509d90 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -57,6 +57,7 @@ //! //! ```rust //! use kernel::{ +//! alloc::NumaNode, //! 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, NumaNode::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/tag_set.rs b/rust/kernel/block/mq/tag_set= .rs index 5b1a5bcc978d..d6d104adf4aa 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -7,6 +7,7 @@ use core::pin::Pin; =20 use crate::{ + alloc::NumaNode, bindings, block::mq::{ operations::OperationsVTable, @@ -57,6 +58,7 @@ pub fn new( nr_hw_queues: u32, num_tags: u32, num_maps: u32, + numa_node: NumaNode, flags: Flags, ) -> impl PinInit { let tag_set: bindings::blk_mq_tag_set =3D pin_init::zeroed(); @@ -67,7 +69,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: numa_node.id(), queue_depth: num_tags, cmd_size, flags: flags.into(), --=20 2.51.2 From nobody Fri Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 00A264C9004; Tue, 9 Jun 2026 19:10:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032247; cv=none; b=eKOcBgpMP7CuqDu75PKRcCKbLf+VUkRJiQCnzQ39jeqAW3D56/sD1JjIdjcxTI2Husx8MMQ8QKAOlOm1GV9r8gdI34EtiHZwJh705BD2T6th6CkfI80N1gLk7SKUJ4uSkKEhYF8hsXoDcQ6bvUkzWnAYTWDmyX6b2ugmJfgFm4I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032247; c=relaxed/simple; bh=bVLUGu7Yg0gIIoubIKQz4YBEP/nNvOd82422m1mRrnQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=jvAgYVYGc0wA9VHrfbkHcffV6oebrPI6it+0aze90i0l9o2y4pKRLUi8Ze2EtC3gXvSpuDaxxBiC2nKRqp7VotGWJLodqR9gdc2X1PnVURLdlqTtxpcPAeTi0vYF5LHcOL/5dokHcZi49eoraVe+xeGDdpBCdJqRXyVY70RHVNo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=dz4cqmJl; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="dz4cqmJl" Received: by smtp.kernel.org (Postfix) with ESMTPSA id AC7DC1F00893; Tue, 9 Jun 2026 19:10:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032245; bh=ViEAmKmawxcF7N2WCFkvitTjmdUVh88IGEYKrsZa4rs=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=dz4cqmJlGK6zP1GJh8CiPq58n1tvdzDUWuVqpvqJF83oQPnB2lhpcMK1Re1PTMp8n W+Tfe9M9q/+WpSZE0J8Zsw1M4i+EXly/2tqH4GDRNUz30ZxVUImUfW9qiTCD4F66tp VHldJTtRunXv1ARJB8J8gXzUQy3k9u9Rx5CucVgt1SFADAh+8w3WIxebdYeGLLtdAW W7Ta8dhN1wz1x2ZIexM54QH9ifybplFHKE49uDjQ+q9EoBv+vK2xXmUsN07JCGcL5x NzH7/G/zrmcJPXemmxdQSdviLFCysSDnKAUvwi8Ol/fllT+LHQ5Qv9omDYKQVFrJq3 RSU0pk3Bt/xrw== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:07:59 +0200 Subject: [PATCH v2 20/83] 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: <20260609-rnull-v6-19-rc5-send-v2-20-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4728; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=bVLUGu7Yg0gIIoubIKQz4YBEP/nNvOd82422m1mRrnQ=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTGbf6fmgZgdL2qiDikI8Dlf9jeRNjBJ2UPJ UzTC72Je16JAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkxgAKCRD6UCkIqsW9 0J1rD/9EI6gaU8QWYs3K+I+msNoR8AihBwE6+XAin0W8lX+Fh2dpKy2KgPS0aDv3XhPrVQVApMc CeW3HxCugLxc42a0yRckCX8VLxWGm873dyJdDq/N06Vy0wQf65U22ubt9BKzjMPs6bjHguDd8mK IJAVG3SlTVU/RnsCOnJitaVqi3BIc07MzbXdbX956GIIdaAsD0/KjUqTGWIoU9VXJFUWavG+uG1 fkfGJGCPH54b0WBCgOvrV1lzVfERmYGQ7I//4z0ZUBp17Iq0SgXwSpqLAm7iI91F2kx63LT6X7O 51Mr2gtaIcmO433FOh082r47ymS63Xwh802eJimAyOL2pwbZHA11DTjSq587JCN4DTHkUU1AwZM DLpL5Yrr6BQz7JkRAKdqpmLxip4JGZczzaCc7W/lV6/e5aTl+KsWdPKR3tRmryh7Ify+/lo2pK0 /hd1dUgVc7GzOehIvxoMETyGagOW2cpc1bkID57Rk5XFpeZSi2L/aM/z8WzWreDE3H3G44vTQpi uFStrF7v5ihgAR2f+DHyTP7DgFBuCqKl9Aa+pAj+HUjFFEOaA0PwY+oNxWt3ABrfLmCOW8MCq/P Y5Rx3N+9hGz8fsnacW0kRXPdvnlqrZ00rz8LAZtPwaJTpn3F6xc1Itomhod5p307PhVOPXPUR21 OmL/ScaWsC86CUg== 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 | 30 ++++++++++++++++++++++-------- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 71b38373be33..2f3fa81ea121 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, // @@ -91,6 +92,7 @@ fn make_group( memory_backed: 6, submit_queues: 7, use_per_node_hctx: 8, + home_node: 9, ], }; =20 @@ -110,6 +112,7 @@ fn make_group( name: name.try_into()?, memory_backed: false, submit_queues: 1, + home_node: bindings::NUMA_NO_NODE, }), }), core::iter::empty(), @@ -176,6 +179,7 @@ struct DeviceConfigInner { disk: Option>, memory_backed: bool, submit_queues: u32, + home_node: i32, } =20 #[vtable] @@ -208,6 +212,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 { @@ -288,3 +293,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::numa::num_online_nodes().t= ry_into()? { + Err(kernel::error::code::EINVAL) + } else { + Ok(()) + } + }) +); diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 6323327d4a5a..1d0faf524f5c 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -20,7 +20,10 @@ TagSet, // }, }, - error::Result, + error::{ + code, + Result, // + }, memalloc_scope, new_mutex, new_xarray, @@ -93,6 +96,10 @@ default: false, description: "Use per-node allocation for hardware context que= ues.", }, + home_node: i32 { + default: -1, + description: "Home node for the device. Default: -1 (no node)", + }, }, } =20 @@ -129,6 +136,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { completion_time: Delta::from_nanos(completion_time), memory_backed: module_parameters::memory_backed.value(= ), submit_queues, + home_node: module_parameters::home_node.value(), })?; disks.push(disk, GFP_KERNEL)?; } @@ -152,6 +160,7 @@ struct NullBlkOptions<'a> { completion_time: Delta, memory_backed: bool, submit_queues: u32, + home_node: i32, } struct NullBlkDevice; =20 @@ -166,6 +175,7 @@ fn new(options: NullBlkOptions<'_>) -> Result> { completion_time, memory_backed, submit_queues, + home_node, } =3D options; =20 let flags =3D if memory_backed { @@ -174,14 +184,18 @@ fn new(options: NullBlkOptions<'_>) -> Result> { mq::tag_set::Flags::default() }; =20 + if home_node > kernel::numa::num_online_nodes().try_into()? { + return Err(code::EINVAL); + } + + let numa_node =3D if home_node =3D=3D -1 { + kernel::alloc::NumaNode::NO_NODE + } else { + kernel::alloc::NumaNode::new(home_node)? + }; + let tagset =3D Arc::pin_init( - TagSet::new( - submit_queues, - 256, - 1, - kernel::alloc::NumaNode::NO_NODE, - flags, - ), + TagSet::new(submit_queues, 256, 1, numa_node, flags), GFP_KERNEL, )?; =20 --=20 2.51.2 From nobody Fri Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DF7CB3E44FD; Tue, 9 Jun 2026 19:15:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032549; cv=none; b=L+HPPuMMmxzaDcA+5oVsClDPfwo3dDHM6+9dBmf6QfyMgVVXN9u2vwfeuOnF4dCpxFbCKInVAzA5f+B4TMirW7l6zhsxfjQCI8Trr7veV2uDt1kemd8bD6BrILremNYSZ9Exd1mpag3C/cYEDw0eiIG/mias1r8HXikFHLdvAkA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032549; c=relaxed/simple; bh=saeAx929MEumoW6R7srpYWt0jl7c4t9NDx7UHc+Auss=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=SHMMu1Iy4Hb5M/83e2Qa7zb/WNiqvRW4D+XOKPJBl5ROMEZLTMMSeUO+9XC7PdcomC9GmwNa8r7iuaSPNPc4Tfrth/qbY/6ZesTPUMv2oeZ+GCoKEm507zMTK+A/7nYPflZiupnZ3kBJev2B4jI0JRDkcx2vt3OAxMxpOeAbA6E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=jfEuyFm/; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="jfEuyFm/" Received: by smtp.kernel.org (Postfix) with ESMTPSA id CBB0A1F00898; Tue, 9 Jun 2026 19:15:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032548; bh=1hPzuRGlGHR6F8zQSGoWCISEe22ewkDpnFmVeBMxzdI=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=jfEuyFm/hAx6MS5urtW/zDAEkpRUKAk4cdbEVABbdlVrV+t5b+q5Qbld29ZT1yWnU lfPpLpHjmMHV/OqnAiK+dvaoXKcbK+L8adV0xvRMa9m3w5GSKvvr7OeAX4dg4IOi8M n1T3enn2PrweRL0/wGuXqXoVdHkPBREFWSnpQCJiIE6W7Q3GA2aZY/HGEcjOIptEyY k024u37TRsmV6aSkYZc/m89BCBfYzNbBckqf56ZK2/4NgaKtMZ3VGGmo+RADdia2/i ynsR4htHT6lBqCY8Za76lYCknx6AWTYz64mv16A1eg6mhOnQk//SII2Ww+bfNILznH NhELfBySmcNNQ== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:00 +0200 Subject: [PATCH v2 21/83] 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: <20260609-rnull-v6-19-rc5-send-v2-21-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1028; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=saeAx929MEumoW6R7srpYWt0jl7c4t9NDx7UHc+Auss=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTH0CYyD0Jxnz7sIleuTxhsvjwvNVqKG9as3 3F8Reqpp6+JAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkxwAKCRD6UCkIqsW9 0DhdEACqiXEmIVrM/wsxmp7AywwJPAcsP9j6NfegAc3bKCA/HCuBa3Y/zaNeXx5o//XHLGK9BJV O/naUSzVBIptU36gVfx2L62+wP24jh/mKBc8IFEZWqL+HvVZUKpdiwnG0ZHygZ//gsPrgNd0WEk Y3Kg4i7ossqWoy77DyoKcHagMdPztQ79Q1kDy+PioTtteSRFbSKtHkv7JB6wfvSqB1GhfKqQW3V ERsPUYYtuhV87vQ0GAGIp3VZwX90RxofIcdZ+3qR93QhhcRdha6p7Jh94AUFLDgyKu+bD/JvRNA bkBao4jIQxms245dn2k7sXJVgaufRLa9Qy7GgiOWpN7aUeM/JXNC1MiEQucYYOBQ8uwckuU1bom B6+1Y3kQDpi5G8PaTZlVMeuT0fImCQWvldoYZB5xqW7Ctf4yZ1O4/aPWcBO9fu4/cZqqaYQEVg6 oqwWB+hpBNG2ea8vNo30qSobQN9qfBCLVBsCPd/nskjTP3+k/mbudUkEEsZAvKVcRLiYow791P0 8QuW0znNVuCnL1T5hPxjtK/7wvEcvqILu09syvrh3FLg7bX7+a4mbGKQg7mdukiBDsErixKBsUm Q/myXiwyirEyQMN+IhzeVbUewjW5CN5l+eIUY/sd2rpgkZhdhz95CExMKCjVRTXGrHvfW7GItAG N2IkpJWR3O2Imiw== 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. Reviewed-by: Alice Ryhl 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 19bdf17de166..54fe580b7b42 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -183,6 +183,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4CE224EA39C; Tue, 9 Jun 2026 19:14:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032500; cv=none; b=hMnM5jjewhbowQIt2ieieJ7wo83Jn3YVUVpdsqi2OoQlSNjmLtubaGV2Jfvpx4+GorynExwO/rCsisMdxNfJMQcDdL+csu1Q4JJkXFkdp+dmqkjbTVFnODA6WS0YcnFKC5LMUVFGmQW0InBzt2OOu2LX143vnfuFqlfRtAWz+jQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032500; c=relaxed/simple; bh=mKAY/8gcmYnV4CeK9ErSKMBIuQ/5EOAccvACmxqa5ao=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=jGJDnlXZVPsKYx2b8F60UuWQr4b9Pf5WUe+gQS9oRwSGwrvog6ssX1Du4aCb4YshVTLNAbDGxJY/NOZwXHZJqxQVIj2MGIIgv6ESemw3fFlGYoYS383p1UH+nCu41t8yeI0Y6Iruzj/bLKAR9U41uRW2Gi0LtTBFcXUwGKtvZEM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=HHAC1mMS; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="HHAC1mMS" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 366131F00898; Tue, 9 Jun 2026 19:14:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032499; bh=mrJG8KiUxKUHfvXwMNkNGZhqS4gsQJoMju1kYZYGQYk=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=HHAC1mMSrZnRSEnk724VpwowU20c+RummkN3EX1Wk7AmLhuBnHQwz4ky+nR7DScgd sUIpXnqZuhzZmyH4K56+t0kuwhbQpfND8xQDxHmMrOu9OYvXCsZat41IvOYIVn90st mPoG82a+TP6gJ8uXM8wtqwfwJFnllYZRYBsmhEssGX1sVIEesB9M2rpsakkaz/jm9V lZDBM8NHls/nCoAJWk6Yrw0OSbFxsaAoNRgNyHy+3P3MBKBcZ2Driuiky8/i/IfnjV kDV86hLZ+dBFl7ijvCoZP3X2BIEbsFWmvPdYAmx3/glcjO8IxuLGNHO0FZtxGMVjaz DrbPd4rWTf6ew== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:01 +0200 Subject: [PATCH v2 22/83] 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: <20260609-rnull-v6-19-rc5-send-v2-22-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=2897; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=mKAY/8gcmYnV4CeK9ErSKMBIuQ/5EOAccvACmxqa5ao=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTIgHAgY6l2Ttz2UGa4fGnWqQhoUxmiEEF/z 54xXUGsE+mJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkyAAKCRD6UCkIqsW9 0Bj5D/96wH2IqvfrEeXl1taIlDXI8bvB6U+cgPclaot3GKF7pKW5w0nNNbMXmAuzSnikbTtXAwN 3FZjaf2kUe4pntQwnEQqVRw2Yg199oPewGn4uyUpmTRdT13CQ2+SzkBRevnwKW7cywDI0oIgOHW qRkdpzkSUK8nzHXsXCqA9EOdzKCmCuiFwQZQ9lKF/eXMVpQXdvuOWSRRNHV2xtFk1n1ALFsxAdw spdqpARcO9SjoZrIM9I4wi3VWfSjgPP2/6QLumakyvOtXTNASaDUY71bxSHA/tkuDUR4jhLwV18 tAEBl/iwJQxOlAQRcyU0rd1mToig1Y+SLoeq+8LXTW+IWTbj3zxWj6hJB6FRN324/4scjob/j94 8g7pLaVTjlxeiLl51KFrNwnmxrqB/p/IUA9RTxUXbkbjsCzE8ppjX9zY8yJCs8/PxCdpXE389sI LRUKc/I34zz0ttv7WAyjTau8f++O5Elj/wfnJn0fx/8c4QBpBXk/8az74W1QDwrIqVTg/D+plXo u13K7NvAzzAQw1Hvgqv8Do5vbuG/Q9ijMn8qCuYLn1vDgCeSrTxGwT33vmKUegXVVojj0coZank s/R5IjGAl2SvsSdbu0Lk+8xboJZvOi/Cl5KSCtyBBYlZYnd7OZA6tfT1KlzDxT2VksKI/y2Mq4q WXaE27QIK7CyMYA== 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. Reviewed-by: Alice Ryhl Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq/gen_disk.rs | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_di= sk.rs index b36d24382cc3..2b204b0ed49a 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -7,14 +7,27 @@ =20 use crate::{ bindings, - block::mq::{Operations, TagSet}, - error::{self, from_err_ptr, Result}, - fmt::{self, Write}, + block::mq::{ + Operations, + TagSet, // + }, + error::{ + self, + from_err_ptr, + Result, // + }, + fmt::{ + self, + Write, // + }, prelude::*, static_lock_class, str::NullTerminatedFormatter, sync::Arc, - types::{ForeignOwnable, ScopeGuard}, + types::{ + ForeignOwnable, + ScopeGuard, // + }, }; =20 /// A builder for [`GenDisk`]. @@ -25,6 +38,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 +48,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 +109,16 @@ pub fn capacity_sectors(mut self, capacity: u64) -> Sel= f { 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, @@ -111,6 +136,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2E4E64D2EFE; Tue, 9 Jun 2026 19:12:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032375; cv=none; b=KO63x71YrkIEtMj95WNLH9CQbGr+3B2U1yALzwEN3W1OtSTV9gmEKCM//H4U2PisCPgiqNCuqrUK7iUa6V447mqfgIMOmR1O2wW9CEl6ohHfDMzcFHBmD+VPhK2NlMbWJplhWygG1vVQknvApCbkxcoXKxrwSgcQkYSc7HF8Aow= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032375; c=relaxed/simple; bh=ZSZBUb37pDZIK0wkAHNaDzISlaNSNKMcCAAG2JILBR0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=edR7c+cUd39u49HzPseDpJ2UEU53Vv3kQPMPTXsbJt0zYp2ZelZHJTIuZOK57Qfr9qFAxHWq1AAW1/JO13P+dJpIr3tz6ULHPmjXu0Y0SblM0bhCaFC23PgWDIKUcrZqaNrIlNdne86DlTztL9YAicAbcHl5aAgSgXlUf8I543s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=XmBpTvYQ; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="XmBpTvYQ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 203821F00898; Tue, 9 Jun 2026 19:12:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032373; bh=Ja5KkFyfig0piS8ccuJVk2LRov2nAywPKm3RKnAniXM=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=XmBpTvYQGNYex0G9cDf7mrTcQkE6zSx5BlSiKxiE8zXF7YoKSvcptVWP0ax9KkZpp WDsBf5/5MNWAIaBKh84619ohb+eA4IgID7EGmF4UaNsE24NYDYjBX9TY1UETt8tFmE FjBXyZ6dGR/zZuKNKjt70yPp4Oxj1fDfRQmf4m1VG/bCnUo1c6CNdW/uP2n3IYoax9 TXyHequ1+NZ0UDgWekOATHJFJyk20uG/hE99699Ac+RH2ZNPZ5wl9E3MDtfJdyQpIX S+yIas+hwXklF/uWnIuzkXwNm0Y5yhh3qQBYD5zp++qqDqo7f/c+OrJzKydbHF6Yqy LbGLPfpgyM7WA== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:02 +0200 Subject: [PATCH v2 23/83] 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: <20260609-rnull-v6-19-rc5-send-v2-23-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=10082; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=ZSZBUb37pDZIK0wkAHNaDzISlaNSNKMcCAAG2JILBR0=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTJ8uX2fM3Snwr3kQptrqZsMmQScXM8+be4R pd3o904NGyJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkyQAKCRD6UCkIqsW9 0CgLD/4uNP8wyJjti62HDNzWfcHXuYIe0fxsRoadLQySp324zQzosIaVo4Da3NKHryJDW2/PsWe G4Y+n/rpLSJqi7Xhko5+pXFGvE14CBRxN9pUVppbtUzcaFnObe1627rQxutetQyAUD5yN4Va9gK hie9Ah2+DaDh6vzNKVhw75qoLIJn9SbsRVxxfH745isLstt6Nuys/8LgMHpmKtpCR+mUVPxQsKV CZIh9ylHjWoKeuDvr0lfFtG5sSrQZ4ZmgJ1iZPiCn4OoMD3ICvHA+EVRXn0CPZlC+nukExNaqIY nYtqiNY4C6BNegpLk7ndh2if26Iir/ddeqWem8OO7zXy2fzD3ZPSSmFIIs1NlEwrCjXfpRLXKfl tJeUfUOl+rruTUB/zKxgfBRU3cnJvr8jjn7dIgdjtVcLGQzlMs6zciaqhVstqgeQXP7Arp6TZe+ O0GpmbqqxShxg0bP0LR4MFISa+IzPm+OF58yM1vbccbmmcU3LELjWg2U1V073ke6DjfpaMI18OS M6qXn0QLQNPwt8AQbLIL0Ewf5Rtwl9BBK/4OoAOmwAWeRf4+800gx+pt0/Im4ZKFiSFQ7N52Spc y6gTGRsf9Q8kgF8xk/jQjdLC2mv9BPvlpWTetaXqo8mMwkZKnmS4zIZAoMAz5R7HMid0BhZuUdd FZTC5qe9Qyzk8ig== 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 | 120 +++++++++++++++++++++++++++++++++++-= ---- 2 files changed, 121 insertions(+), 14 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 2f3fa81ea121..e47399cd45a4 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -93,6 +93,7 @@ fn make_group( submit_queues: 7, use_per_node_hctx: 8, home_node: 9, + discard: 10, ], }; =20 @@ -113,6 +114,7 @@ fn make_group( memory_backed: false, submit_queues: 1, home_node: bindings::NUMA_NO_NODE, + discard: false, }), }), core::iter::empty(), @@ -180,6 +182,7 @@ struct DeviceConfigInner { memory_backed: bool, submit_queues: u32, home_node: i32, + discard: bool, } =20 #[vtable] @@ -213,6 +216,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 { @@ -307,3 +311,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, |data, page| { + if !data.memory_backed { + return Err(EINVAL); + } + data.discard =3D kstrtobool_bytes(page)?; + Ok(()) + }) +); diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 1d0faf524f5c..bdc05b3f6072 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -19,15 +19,20 @@ Operations, TagSet, // }, + PAGE_SECTOR_MASK, SECTOR_SHIFT, }, error::{ code, Result, // }, + ffi, memalloc_scope, new_mutex, new_xarray, - page::SafePage, + page::{ + SafePage, // + PAGE_SIZE, + }, pr_info, prelude::*, str::CString, @@ -100,6 +105,11 @@ default: -1, description: "Home node for the device. Default: -1 (no node)", }, + discard: bool { + default: false, + description: + "Support discard operations (requires memory-backed null_b= lk device).", + }, }, } =20 @@ -137,6 +147,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { memory_backed: module_parameters::memory_backed.value(= ), submit_queues, home_node: module_parameters::home_node.value(), + discard: module_parameters::discard.value(), })?; disks.push(disk, GFP_KERNEL)?; } @@ -161,6 +172,7 @@ struct NullBlkOptions<'a> { memory_backed: bool, submit_queues: u32, home_node: i32, + discard: bool, } struct NullBlkDevice; =20 @@ -176,6 +188,7 @@ fn new(options: NullBlkOptions<'_>) -> Result> { memory_backed, submit_queues, home_node, + discard, } =3D options; =20 let flags =3D if memory_backed { @@ -205,22 +218,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: &XArray, mut sector: usize, mut segment: Segm= ent<'_>) -> Result { while !segment.is_empty() { - let page =3D SafePage::alloc_page(GFP_KERNEL)?; + let page =3D NullBlockPage::new()?; let mut tree =3D tree.lock(); =20 let page_idx =3D sector >> block::PAGE_SECTORS_SHIFT; @@ -232,8 +253,10 @@ fn write(tree: &XArray, mut sector: usize, m= ut segment: Segment<'_>) - tree.get_mut(page_idx).unwrap() }; =20 + page.set_occupied(sector); let page_offset =3D (sector & block::PAGE_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.as_pin_mut(), page_offset) = >> block::SECTOR_SHIFT; } Ok(()) } @@ -248,7 +271,7 @@ fn read(tree: &XArray, mut sector: usize, mut= segment: Segment<'_>) -> if let Some(page) =3D tree.get(idx) { let page_offset =3D (sector & block::PAGE_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; } @@ -257,6 +280,37 @@ fn read(tree: &XArray, mut sector: usize, mu= t segment: Segment<'_>) -> Ok(()) } =20 + fn discard( + tree: &XArray, + 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, @@ -273,7 +327,40 @@ fn transfer( } } =20 -type TreeNode =3D Owned; +static_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_KERNEL | __GFP_ZERO)?, + status: 0, + }, + GFP_KERNEL, + )?) + } + + fn set_occupied(&mut self, sector: usize) { + let idx =3D sector & PAGE_SECTOR_MASK as usize; + self.status |=3D 1 << idx; + } + + fn set_free(&mut self, sector: usize) { + let idx =3D sector & PAGE_SECTOR_MASK as usize; + self.status &=3D !(1 << idx); + } + + fn is_empty(&self) -> bool { + self.status =3D=3D 0 + } +} + +type TreeNode =3D KBox; =20 #[pin_data] struct QueueData { @@ -282,6 +369,7 @@ struct QueueData { irq_mode: IRQMode, completion_time: Delta, memory_backed: bool, + block_size: usize, } =20 #[pin_data] @@ -332,12 +420,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7ADFF4D90D7; Tue, 9 Jun 2026 19:13:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032420; cv=none; b=Zt9Hf8JOsbRamIqQ+6bHN3C5QuYzwx2jh66n3IYd6f1Dc3+QlqZ42k0Ucdxcyuup6l5aH6HfcFCDdCz61vAbvGsx1WssaywUG8LesZjQrhWd+ptIxpjQsnasYtsUTscfe1gnsfY+Skx8DRC4PWvxIzGEjMO2u6M1wFPiRoYOslo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032420; c=relaxed/simple; bh=QkTc+Yc4D89ONNykWVRFoz8tGJV4akO/AW521uoK1/4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=pTmICzr3W62IpMCuW/8wREWHkB9uzJbzAkkX2dp6D5/uB7dhe5fhGgHW5XYwlM6QUezq61mEmlxR3W0qIE6gOZdmT057Ae6uCVSYKdi9ezhjeb3NlkHzPiN0NH12wq1eKaMIwG4fRQv+o0MsNLkhzBMloAZL9Hw7Rk8TMwOOlhE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=eV4PtLBl; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="eV4PtLBl" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 16D241F00898; Tue, 9 Jun 2026 19:13:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032419; bh=UznLXv+yDoXlv0q3+7o1af2gG6KRdEpR67pAYXvOmSw=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=eV4PtLBlcmIv0Rs9zY+RH0AQ1KpL1zwypAt5Izt++/RuDL53D/zlqHzbUx3BjTbH4 Rwez+JUusnq6LFb1LdHpyHaDhTmIrSSTVvGikutOfjjEzfOP1dPerlzCtMPtBTmxRA Evy2QrFrR7hKO3WPo3IdfJjp8PV6lS6LVgHS3ujbFx5Tpqqt0sALERzM3pGrQdzjrt 6oprQDgRaavUxEPY1tVPAPVMc9G6C/+9oeXr8FKTKiqFtmLhRESG55UtwjXTXgvJcd JmgcrTXETRxfnZpM7qU2qItMwIT2F3KGiQjv+5mfAIl6l+pzqY6LcC2cpBEXwgjOmZ SsxmY8pzAd0VA== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:03 +0200 Subject: [PATCH v2 24/83] 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: <20260609-rnull-v6-19-rc5-send-v2-24-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1046; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=QkTc+Yc4D89ONNykWVRFoz8tGJV4akO/AW521uoK1/4=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTK0UzZL4I383mSkVK2Rk14L1Tqwr6Iq1PY8 mxrWR8hc9yJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkygAKCRD6UCkIqsW9 0KW1D/4zp50ASbhhUmvVy8hfrATcAApjnZ/OtWhyYLIBc0YBryID6W/1ii1xw60AQJCUxU6QH1U u+AKjeUPAVylMVse+ivWCG1wwLfFXBir86ERXb7sYtoT6e4ZrX5+2FgtU7If/0jW6PeDE+snU1M /Q3XLlMSNDjqNWE+RNDRg8Q+CsI0dJZthUz052XvCHTCV2XU66tblMnZg6Rdd2Gsx4z5hFxQ6qh sCjRq21uw0CVTV9/NNaC3r9WOH5c2kfq9l/yurslB3I712ZQjr/Ou9YLI8Dwm78of+YmaKS1/8x HF3L9PKpielTT/sp1K1nYGTGv0tIjEQk/SKwJDgGI87P08y5Npo0vBZDcXGAZKqlHwvrcDgXtQl Y8Ulc62/bstRDtqz3S6t1471dWYgZf268gPoK/il2sEwIg3xq7x7MC03Czd6WV1yK/UKJ5DbFlo Xf/tWC1jUbUQCQOXOqRRDmCHzGjI8lsdfvEvNglhey478SygvWqeshKVPz4KEpVTP2MLPhZrmPD twIUqTwCDS3AcW0gXF7i/Ceu/Xq3rPxCc3oO+xVYGzG2ysuJfEzfiVUP5GpgHzd8NfxkZPd/2o3 22JPrke71+Ss9WHskK1ZzucpQX52QsE0xgCunb6v9fbaTVVxZjVKIQTOameD2U0M9Z9xoxG9XC5 1zlZ9whoVdLOkiQ== 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 b7eaccd200a2..2561d7090c49 100644 --- a/rust/kernel/block/mq/tag_set/flags.rs +++ b/rust/kernel/block/mq/tag_set/flags.rs @@ -17,5 +17,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6241C408A0B; Tue, 9 Jun 2026 19:17:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032642; cv=none; b=nurx5bKByJBkpeWciFweJzKw7SESah86NyYXdIB7tLBNw+2+BikuBLOX9NjTuH9Hj2/xT5bydtKR2GzZAqwS9QbApBfegzPaD0aF6XZFEBh4iY5eK38udb7rK/Pn+QmIycLuEp+efQoWIzyOAPEI8MpzCK3LZNwdJsqRzPh3Qxk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032642; c=relaxed/simple; bh=O+jl2F2xzeizmKeEFp6OfGRrahz8fj9S64rA8TqH02s=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=efceYAvUtc8lMH+E+Vz1NuUJQ15vRgTGHXNqEd+RgL5G1AfE/taX9fJWeeFnSRKal12++Tv+BofT2heDkzEXbCLolPYucqifN3H1Iwjz0A/cDL4nSCaC1LeFTJvzLHEanWIhVK3wt5O/u16FrpN+Ec3gzhOGxPkNurIPBw1iwXY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=XRZMzO2n; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="XRZMzO2n" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 13E801F00893; Tue, 9 Jun 2026 19:17:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032641; bh=67YOcpbRpSvyeMAWDdHjO8J84Lo+TPp/ZYE9AcqGdYc=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=XRZMzO2nzYm/oXccW/HVI5Dk5QvbHo+TtdY3/UR6W3/B08le5NVKnk551rf9JD85Y 5LTO9PZTcuWeC8lUNCkMTwYZazubh9vN0OCO78aOBhXmKkds3ilmrKFObR0ZDY865T yZJz7FAbsCVk1N3mDDlXCcHJg+liy+vXk2QRmWD7flbbPJO/mJT37SlvO3+xHV9DvV XnqNOhi3ANXS3m/GB1H+XxwB1BYDfiuIaywBqfOWrDsRuSUijJEGoWVNKWyIu9VTIs kjqF+7h2oqA9wncI7Smy3PRK1TQnKdSv+yrdMXHMx1g3Z34NfCrdb5adK8sXvtzyxD xO0iGaW3vjokg== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:04 +0200 Subject: [PATCH v2 25/83] 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: <20260609-rnull-v6-19-rc5-send-v2-25-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4147; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=O+jl2F2xzeizmKeEFp6OfGRrahz8fj9S64rA8TqH02s=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTL8JCj0D2bsrjx8If5DE09F4g67MwSLHApp 73TkQ+1MHWJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkywAKCRD6UCkIqsW9 0J9pD/0ZofgGrMZ//q3Xy/zRBs9l2Vn3GJKNw929z8MAETbFVoorBU4y/U2yUDQdOC/XDmdXoWe 4Nad5uh/xRXZLpOBiImot9EG5hP6Thp9W3yxSZid30wvFnEQmd6AFD4aeT6XxuZCKQo39OI4dVM ARB1aecc9qrZoBbKU1ENcIcdx62S/9bYRzzNb4rHA2k2ud8A1qLJOpTHSY/nhLm/tUd7RyyT4wg +R+I6e0hBErrB8mvUDaWQqwH5PJ3XbO/ZpJLnPzjyTgaIO0rOvRdnSBAs3tc5Rv1rKWDSSk3tDs G8g+HH+/i6eikJIifHkiCzoz57AUBNGhuoSdcPuXIKF93tV78JMecTIeTt1zHa/F8ww5WE9GFlz 0weMV4RIuJDvJ8j2DibdYfEBBit84BFLl3cDiqKVDcdwn0fyrPPatCCMWKnO/uEf4LbW950oFWT YAmEGjCQrE3TEs8aB6Y8MR1fUOvEkT4MVsHWlTL3LKbgfImUIaZFxI5rgVYF3f/QImdzmCgbFhF JMOmNxi6kPiilGy/70601nmr17+xmJ438F5FASSqQ0w5UPM/xinWQNrjAiyQcZHaVukg/uVQ4ni VnVwDn8bBaoX5wZc618giL/7vqrVikGfPITYl6S9Zw7UC9rA8WMOlGk3O41AWuSmGlIj1mDiKu8 aypOiV//2X6dgCA== 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 | 6 ++++++ drivers/block/rnull/rnull.rs | 25 ++++++++++++++++++------- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index e47399cd45a4..d9aead646ae0 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -94,6 +94,7 @@ fn make_group( use_per_node_hctx: 8, home_node: 9, discard: 10, + no_sched:11, ], }; =20 @@ -115,6 +116,7 @@ fn make_group( submit_queues: 1, home_node: bindings::NUMA_NO_NODE, discard: false, + no_sched: false, }), }), core::iter::empty(), @@ -183,6 +185,7 @@ struct DeviceConfigInner { submit_queues: u32, home_node: i32, discard: bool, + no_sched: bool, } =20 #[vtable] @@ -217,6 +220,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 { @@ -322,3 +326,5 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { Ok(()) }) ); + +configfs_simple_bool_field!(DeviceConfig, 11, no_sched); diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index bdc05b3f6072..cb5b642f68e5 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -30,8 +30,8 @@ new_mutex, new_xarray, page::{ - SafePage, // - PAGE_SIZE, + SafePage, + PAGE_SIZE, // }, pr_info, prelude::*, @@ -110,6 +110,10 @@ description: "Support discard operations (requires memory-backed null_b= lk device).", }, + no_sched: bool { + default: false, + description: "No IO scheduler", + }, }, } =20 @@ -148,6 +152,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { submit_queues, home_node: module_parameters::home_node.value(), discard: module_parameters::discard.value(), + no_sched: module_parameters::no_sched.value(), })?; disks.push(disk, GFP_KERNEL)?; } @@ -173,6 +178,7 @@ struct NullBlkOptions<'a> { submit_queues: u32, home_node: i32, discard: bool, + no_sched: bool, } struct NullBlkDevice; =20 @@ -189,13 +195,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::numa::num_online_nodes().try_into()? { return Err(code::EINVAL); --=20 2.51.2 From nobody Fri Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D51D84D8DB1; Tue, 9 Jun 2026 19:17:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032625; cv=none; b=FkEJE6lQSeMZCFuMP07B5I97LbAFKSpDRauJ8J7feMTFN8Iw10kOTFpnmi/WjsmVl4d04gw2mNzXLirgdc7ShqfazFkcCttVZ890LaxeBMPzADLHCxJR/E4RDY3ChZbyhOu02KyzfvOg9w/J81g8gQrQvSv1MjpqNQYw5faXoXo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032625; c=relaxed/simple; bh=v0XQhwyOc620vsCHIPz3I6cOTlfWRB5GqFO9hLn8R5k=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=r1fOtyFsvv/U+cwcIW6tFu2ffiSWIRAr19q0VodBmyolh3CJ6/3sFQGKX5Qm+5zWT3zz4fSuD3Y+eiKVKXvZJ3q8j6jpjfyqTjCRGvJyHHmxXrS3Bk0KBZyXOZXIJVGihsEiBWQWVaCvepgLXqIJ6BgB0xOCuvGoS1yv2Nfh4ns= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Ln4G1iif; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Ln4G1iif" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 387E71F0089A; Tue, 9 Jun 2026 19:16:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032623; bh=umeS9rSQGXG+kg9FrmoD3TXShidPIQhNP8+xWx74Hs8=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=Ln4G1iifeGEWneCXz36TrX17jEw24VQfjkiYMflm5rTiRv+zy/knoxB8xHHVS5WE2 NHACg8+KORXckGrd+3swtQYGvjFt/Bun0rrRKsvOCwuLZ9vcsyn9SP8+f2zIQK+i14 gRL9NebxrpbEKYpduWvdDoEX4tayxg/lX5D5p55PgwFfsQabdy47tBoHDSLf12UFbY SEfSN/O9lj/CTOlCQdRTxYgbzJYxIXcTnp9zV6MR9LyFJTYskw2lo3Hm3DmWxkG+fw NvdaVeMUaefgcNsgT0DVQ60raX2nPJ0LhBGjHCM9V3nMt8FDGPJ1wWxlBraQ6HEkBD 0mKxdmgJ00z4A== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:05 +0200 Subject: [PATCH v2 26/83] 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: <20260609-rnull-v6-19-rc5-send-v2-26-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=8857; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=v0XQhwyOc620vsCHIPz3I6cOTlfWRB5GqFO9hLn8R5k=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTMD0m8HIb02rdULtOHdHfQabhQNf3AvX2lP d4k2ie8Ml6JAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkzAAKCRD6UCkIqsW9 0DoXEACElQZIRs2ATiX3r5JUC1LZWpwkeycYVxgmZiH95ILTB6Vo8aNShxNEGxNIX6iLJyN8g3e /7+Zy/eDbFG0rdcuAmp4g2zxNhdMCkniFPEx4HueAgAsD5ODQdTD8gD7/+kSz4s7s8SopZEsq3j GDkOd0as0fJqfOscklxZ5+IzLMt1r+G4Ir/RMFzV/0c67wWdXDJQOF764/vzGab5ptBGvNpSiyk KTog832s4Sb5kVjc0fCU3W2JbZyvmYteFCxX/qdWFkHmNsAdmOMzBtS+Da3ka8yRck0BSlmOiC8 sEpN364gTrW4uPxZNnUXGGHQ4z87EDUXbzIjikzmNqj9MwVEHBvqHD5EU6y4RLXpa4RohOR3oTc 34UmtfhVsp/a2UgjKCkSyQNt8vlpC1Guh+Pj3/Hse6EnO/ZPudSgZuoaRLCiXQGcJ5im+ChuUbK Lv4kup8LjfrA6C0HIBuWQDQ7j+8RK0oSqu/lcH8g7ToP3yY9gNvOYUsEJwyANWdaY/QfwwMR/Af qYHmqO11qROCmvxB64tjPfaKuq8zsG9WWPKZdKnR0VWMNtAIztfuwXraXOQ8MmSllcaRTJ6dcxA JgyOpET+kgIEnvZfEK6vWb9G0iPTYsTfB3mXk3oreP58i5jXbXltXyJfMLRsMHq+oDaD+HedqIi xAa6WY4KstRaB2w== 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 | 71 +++++++++++++++++++++++++------------= ---- rust/kernel/block/mq/request.rs | 8 ++--- 2 files changed, 47 insertions(+), 32 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index cb5b642f68e5..73f14d6e379f 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -218,6 +218,13 @@ fn new(options: NullBlkOptions<'_>) -> Result> { kernel::alloc::NumaNode::new(home_node)? }; =20 + let capacity_sectors =3D capacity_mib << (20 - block::SECTOR_SHIFT= ); + + // Prevent overflow in usize/u64 casts + if usize::BITS =3D=3D 32 && capacity_sectors > u32::MAX.into() { + return Err(code::EINVAL); + } + let tagset =3D Arc::pin_init( TagSet::new(submit_queues, 256, 1, numa_node, flags), GFP_KERNEL, @@ -229,13 +236,13 @@ fn new(options: NullBlkOptions<'_>) -> Result> { irq_mode, completion_time, memory_backed, - block_size: block_size as usize, + block_size: block_size.into(), }), GFP_KERNEL, )?; =20 let mut builder =3D gen_disk::GenDiskBuilder::new() - .capacity_sectors(capacity_mib << (20 - block::SECTOR_SHIFT)) + .capacity_sectors(capacity_sectors) .logical_block_size(block_size)? .physical_block_size(block_size)? .rotational(rotational); @@ -250,12 +257,13 @@ fn new(options: NullBlkOptions<'_>) -> Result> { } =20 #[inline(always)] - fn write(tree: &XArray, mut sector: usize, mut segment: Segm= ent<'_>) -> Result { + fn write(tree: &XArray, mut sector: u64, mut segment: Segmen= t<'_>) -> Result { 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; + // CAST: Device size limited during setup to (2^32)-1 on 32 bi= t systems. + let page_idx =3D (sector >> block::PAGE_SECTORS_SHIFT) as usiz= e; =20 let page =3D if let Some(page) =3D tree.get_mut(page_idx) { page @@ -265,43 +273,50 @@ fn write(tree: &XArray, mut sector: usize, = mut segment: Segment<'_>) - }; =20 page.set_occupied(sector); - let page_offset =3D (sector & block::PAGE_SECTOR_MASK as usize= ) << block::SECTOR_SHIFT; - sector +=3D - segment.copy_to_page(page.page.as_pin_mut(), page_offset) = >> block::SECTOR_SHIFT; + + // CAST: Page offset always fits in 32 bits. + let page_offset =3D + ((sector & u64::from(block::PAGE_SECTOR_MASK)) << block::S= ECTOR_SHIFT) as usize; + + // CAST: Casting from `usize` to `u64` never overflows. + sector +=3D segment.copy_to_page(page.page.as_pin_mut(), page_= offset) as u64 + >> block::SECTOR_SHIFT; } Ok(()) } =20 #[inline(always)] - fn read(tree: &XArray, mut sector: usize, mut segment: Segme= nt<'_>) -> Result { + fn read(tree: &XArray, mut sector: u64, mut segment: Segment= <'_>) -> Result { let tree =3D tree.lock(); =20 while !segment.is_empty() { - let idx =3D sector >> block::PAGE_SECTORS_SHIFT; + // CAST: Device size limited during setup to (2^32)-1 on 32 bi= t systems. + let page_idx =3D (sector >> block::PAGE_SECTORS_SHIFT) as usiz= e; =20 - if let Some(page) =3D tree.get(idx) { + if let Some(page) =3D tree.get(page_idx) { + // CAST: Page offset always fits in 32 bits. let page_offset =3D - (sector & block::PAGE_SECTOR_MASK as usize) << block::= SECTOR_SHIFT; - sector +=3D segment.copy_from_page(&page.page, page_offset= ) >> block::SECTOR_SHIFT; + ((sector & u64::from(block::PAGE_SECTOR_MASK)) << bloc= k::SECTOR_SHIFT) as usize; + + // CAST: Casting from `usize` to `u64` never overflows. + sector +=3D + segment.copy_from_page(&page.page, page_offset) as u64= >> block::SECTOR_SHIFT; } else { - sector +=3D segment.zero_page() >> block::SECTOR_SHIFT; + // CAST: Casting from `usize` to `u64` never overflows. + sector +=3D segment.zero_page() as u64 >> block::SECTOR_SH= IFT; } } =20 Ok(()) } =20 - fn discard( - tree: &XArray, - mut sector: usize, - sectors: usize, - block_size: usize, - ) -> Result { + fn discard(tree: &XArray, mut sector: u64, sectors: u64, blo= ck_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; + // CAST: Device size limited during setup to (2^32)-1 on 32 bi= t systems. + let page_idx =3D (sector >> block::PAGE_SECTORS_SHIFT) as usiz= e; let mut remove =3D false; if let Some(page) =3D tree.get_mut(page_idx) { page.set_free(sector); @@ -326,7 +341,7 @@ fn discard( fn transfer( command: bindings::req_op, tree: &XArray, - sector: usize, + sector: u64, segment: Segment<'_>, ) -> Result { match command { @@ -356,13 +371,13 @@ fn new() -> Result> { )?) } =20 - fn set_occupied(&mut self, sector: usize) { - let idx =3D sector & PAGE_SECTOR_MASK as usize; + fn set_occupied(&mut self, sector: u64) { + let idx =3D sector & u64::from(PAGE_SECTOR_MASK); self.status |=3D 1 << idx; } =20 - fn set_free(&mut self, sector: usize) { - let idx =3D sector & PAGE_SECTOR_MASK as usize; + fn set_free(&mut self, sector: u64) { + let idx =3D sector & u64::from(PAGE_SECTOR_MASK); self.status &=3D !(1 << idx); } =20 @@ -380,7 +395,7 @@ struct QueueData { irq_mode: IRQMode, completion_time: Delta, memory_backed: bool, - block_size: usize, + block_size: u64, } =20 #[pin_data] @@ -432,14 +447,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 54fe580b7b42..9e176f015ab8 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -178,16 +178,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 106B9418AA5; Tue, 9 Jun 2026 19:16:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032589; cv=none; b=QuTmkE6MrCabK0i7FX4mBZ4T2NHEaT+MW/XpwVV6k91i3k1Kpx7/x3Zt2YvFOwb8M5WmBJDO0J8pQYmPzNDGXGJ43tvYXX34LqkRee/iQjaITfI2UgLA2jD7krWdfdfrfFLeUllFVgnEXe/GDpdhudyCLIslB0TCK8yNsBJFjCE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032589; c=relaxed/simple; bh=HRywcrCiLmX+1/nAPCLYKHPcnwsmzxh2+t1mg8Q6G+4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=gVSx/a4FbB4kfGdd7ZH5kd6c9AWh/bnrng/vdvO8lybrSWioLMrId+tQ/bt3S9Pu/CMf9IJbFxvuROa5xLmqud4oRLRp5VhNR4lP20n5Gt2Zup+tLP0A29PZzqoMZO6kVw04pyF6St4o/xoF4debwol0HyXWm7uWTFNToBjkk5A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=XFDwz+8a; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="XFDwz+8a" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2DFB51F00893; Tue, 9 Jun 2026 19:16:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032587; bh=yzcaFz5Bg8WRjLkioMq8mphYDCXE2WyS5ZdwN19/ASA=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=XFDwz+8aG2tLl+iFwjOAOOHYcBryvvwr+IFisaQlHPfpRsG5dNB3iLsviMhu53KN9 NdYVfD70bTu+OG/owt1ThMpL8FgtCNSDbbuEdWmUq77hs8fKz2QzvskaH34YmtW6L8 CVe1yI1Kw0+az0cwWcIIpdcsfVemT3GUih9bEI8up6owif0+7w9mf3yxgODnRm1yHz kjhkr/3VjszDPcFlj/DT8FVT7n319hcT4AcINP8xSqDlZiZs03cA8NHuB4zFbG4xX4 WH18+FMYpPdqqp6mF5e4CUdKpRvGLD58Nep2SP2hzDQDWXoI0szanzSxFxp38S+ylU TfVsNQNdVwtEw== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:06 +0200 Subject: [PATCH v2 27/83] 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: <20260609-rnull-v6-19-rc5-send-v2-27-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=30818; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=HRywcrCiLmX+1/nAPCLYKHPcnwsmzxh2+t1mg8Q6G+4=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTNs3/+5XGtdPDnxKPbZiO56hmUsKptMikhD qmcUMw6YlOJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkzQAKCRD6UCkIqsW9 0LoSD/9ALSkvxB75gCzTGawPodKBjAfK+bzqj+DY8fSrlUepgNjmJz3IAbMY8MTF0wHhObGzqPm K3Co6lZwdOxjF2Us5M3kJtAcB5BLjy9UPF24F6hVaE81VoyAUQ5EkvaQBi5P+qbaUptY6w8+lOo QqUFkwl0sNfQF3n9DEZdUqG2kbpYWgbZKJclDQ/O7geo1H7d42Ejnav3PZqxulSA6eKUpifQSFL s+afvPXHVqji/yVS6V6/Rlqx+1pF0BJOxlIJZ9PhqVHfSY52LgKGvBjoTnyJthQEzkRloFbZFvD wwJD4IhWSoK5l6bcPN8m3j3PVj2lfLf9Ds4EoLz9ejgnb7b8aW3EG+3rUFtdd4HgIxQkSt6zd3P ZFbzCzSeyVl1Ls6cIPp12LPp3BU/RTT7Kts9ByXOawE8m72SMxr7aNz64Sybha2nyxH0Eo4lsoC pC0DrznNoILcB0swdoMPe6jXn1wtA2PnKBQ8y6YqwjakY1Gaiw/41fMKz6xfEC4ot/Bl6Bvfn2X /WWqXtNyp7hCeiwtRrQcR73m0QB21dwRhuUgkCKHbQG7unjx0C53ig3dl8hSr9O5XSY8aTyg2l2 lC/fmtY8p/Kd6gwlSv8P2AM4T9S24g2xeNY4ELlQASJUcpIfBCLQNy9WU7lDpJ39amc/NeVE+iM 7i/I01/K4LUJM5A== 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 | 716 ++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 718 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index b1fb3afee4ca..eaf05d60dda9 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/block.rs b/rust/kernel/block.rs index eb512dad031b..96e48a2e6116 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 000000000000..0aab661ed7be --- /dev/null +++ b/rust/kernel/block/badblocks.rs @@ -0,0 +1,716 @@ +// 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. +//! +//! C header: [`include/linux/fault-inject.h`](srctree/include/linux/fault= -inject.h). + +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, enable.i= nto()) }) + }), + }) + } + + 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. + /// + /// # 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. + let status =3D unsafe { + bindings::badblocks_set( + self.blocks.get(), + range.start, + range.end - range.start, + if acknowledged { 1 } else { 0 }, + ) + }; + + if status { + Ok(()) + } else { + Err(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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E670D4D90CA; Tue, 9 Jun 2026 19:13:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032402; cv=none; b=WApNq9D+NCCQ6Ry19ZEpBNZXTX73cEAXJRkbywStj9AnFgghMWRJgDmVwCs8Ta9qw6RujZrpbYGeMissH9a+2wBRhdgpCXHvPGmrzuGwmmlPsZbvZhUCJe5FXOKrUBorQNF/zq2QsKVBHPu5Qf9wvGxu14eyQG0kS354x4NGl7U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032402; c=relaxed/simple; bh=cO3QVWHBp3bOOCRfL0F0a19BcOtYRij0a3oTZkjsymw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=t64rpdS4tsXuTPYT8Vj5GiPND6UQKqFObV5V8pYaWvYB9+FJDudF+rB39ioVSS6b6DJxufFryvtD5dTe/b+/dy1Not1qytQmpLLnJMtzkyIePusonaMOiAgXwnpRwcvrUtXpDFDfTPt5IoU2ozwiNauq3dLPLDrkLBtW1ERDjMQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ID+9rl9i; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ID+9rl9i" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8B9CB1F00893; Tue, 9 Jun 2026 19:13:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032401; bh=/9ybO/t8zOvRCr2bRBHoQTxr/dQJgA17JKTcxpSIg3s=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=ID+9rl9i5FcVp4DSVY6a+jf6ixiMKI5GWzj4dZM8z3TyyuSTQ/lmM2T1i5l9+u98b GlgxxBGXBxmGMysQbxzci7NbKQL0jEI9AzKOOD4egRna6tx7JQ7Dg1gpktL1N82g1v k5SSzdNm2rD2uR8jVLQ+4Ey/lpiEzn9Zt7QsX4oi7+W63hc6KlHTUHIUc7mhXbqJYb 6wiwyUcjUMaFU+kjtxsbP7MjqRxX5b+UvHGP8qT870Ix6wvfZTxIt3vEV3TYTBKjEJ 5/ANL1p0rITS6eTWvkUkcZWrDZfLuWRR2DdS6ydQYMaquJyIxUn7cI3B3yKaNJz36g 0BkwYrWU9CVXA== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:07 +0200 Subject: [PATCH v2 28/83] 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: <20260609-rnull-v6-19-rc5-send-v2-28-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1413; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=cO3QVWHBp3bOOCRfL0F0a19BcOtYRij0a3oTZkjsymw=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTOwOJ+Mxk74baYbuWET2T8HjDFCqOB+l0b0 r0Fytyjv8eJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkzgAKCRD6UCkIqsW9 0Jx3EACqq6sM1Fp2PN6SLigvCgaPbaiBkyHfgHxbnJcEjrYb2k+0u13lhvpIorRTR+3R62TV+WX /vBPhKnYOd6xObFSdUh1k5B0wEcyk2Zrp5yfnkOR1caJbTWxLdaks4KSPxWS9tbRdEorKs8J0me NowWypzsEXQ7s0MbXagvt7Xvm4cEXmSMeIbwKIesyDuEfpMM5ukqYjbuOX3gScAAB0SSKlerGRw OFKJdRvAZJ98xGMK4mWh917bVXcXM7vzqReCJ3sv+TBLHBGadx9bEEx/FSU4AtIUzWivyEpsLYH Uh9ntbu39dSC6fY9WX0vrIbO1EmpG3VeYS3X39MqsXBpNy2oyzdulzmwtd6goSEK2lvCwJXLW/M 1CC5h/qmsIirn4D7Wa0HIXu7TUD6TR8/kULT0mBlHRtV93Wz5B2HmUU2PZ6FehGubZouGWl42E7 MQO2zsb2s6YR4v0sBDtLrR38Bjufs4UHrAIxEkP/tK6hbFFfpe476bilYL0bFPBcz40Zx49rPBY xUioWLibTAF6J7imjQ52yI7AAOOM+M9sRW4nDooWS7kUvDSrQRsAnN0OeAaCo9gvh66otumNJqW 6Pq5obt3DY/mWq0ctpr3BiEuz7ofHbpQ2HpsmQO5s0KRwM6AjJ2BmeQ7SrJaTw1m67uO+55Idly 3tXDmex+9KNr7ew== 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. Reviewed-by: Alice Ryhl Signed-off-by: Andreas Hindborg --- 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 9e176f015ab8..c06907dfe5b5 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -336,13 +336,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CA329373BFE; Tue, 9 Jun 2026 19:13:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032432; cv=none; b=GdtFA/rTvFwqnIz7yAGEvV+/4eT3JF4uN9NgWpIdoco6XEl+s+/tbV0p3LdBEu/bP6dUtpVa1cC8L8oc8zyMAO2B1X1Yf7CNobjxrWy8s4b7QQzMMcRSRIC0e6hmAeEaNPcXHsLv5kBlQlCTrk/Q7+eevV0KXN6eVE97/1xznOc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032432; c=relaxed/simple; bh=Z4bTvtitOiRUpVB/vtnTPuYJk3qDeC9v435ynCbwhKQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=oe+nQbWMdcZJGNQheBNRBPJvFR/Mhuz47nz6hzouvMu5gA1uVMhj79s/9oBaM5S2gADOSqo4yOfzLWmL7HRGJVt2ttFX6U/m6mUaV/N07Xmb8IeZ+YkA5vB4Mipo44R1ez49oBElKJxYgayOsF93GXnvVeLh6Dbnvb7qxbB01JM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Uo6CXLUE; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Uo6CXLUE" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A95A51F00898; Tue, 9 Jun 2026 19:13:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032430; bh=RwDIVImKkZ54sd2q6hPX1oX6gadqlWc4TQjYIjlzw7g=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=Uo6CXLUE1iYXFFn5qXQl5CLsZDNmGC+xqboP+YL55rAJeKIsTQk/GWivS1AXxfkq2 1kH7PaVlLurPRERGkgTZr3YZnqh76Crpc8XcBnoEdSfLgUaJNye6TyQeK8e3/Ts/hq QvXYSOgwW+NzirJRNfPZ415rJL6il3evGUnDY8WpvVrXrZndDotRqov2kvQLpZMwXB YpCwL4Vw+OSWK3PCX6Sz8TWyDnDuGoIi0oUxE/jDikq8u7xKMZHllfVgtrBF8pAEv1 5VH3VoPXnhsATsggoSRNz9osQUqshFhQQMJDH49p/vdWSEwTaWFMeBZJN00fawHoqa 0QLq3Ytsf8r7Q== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:08 +0200 Subject: [PATCH v2 29/83] 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: <20260609-rnull-v6-19-rc5-send-v2-29-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=8576; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=Z4bTvtitOiRUpVB/vtnTPuYJk3qDeC9v435ynCbwhKQ=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTO4SWpDWOTQpKuppQyjtocpGDBfs4l6cpI1 YmC6ruiDU2JAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkzgAKCRD6UCkIqsW9 0BLTD/4pE5l+jd7Un+rj/Z4vBWnQ2DGFDngPPPqu33XdKQf5Y0H0s2EDGJiLAS66NfHmv9Y3VQy xww7+gqD4l5tCDuKPlt3aDym1hhf9Zn+JY3Tx+RSkJs9yNfPdVyzVTVlzyywVXJ2zdV01JXeN6w cqIK+XCCfOxuOSdObW9Lf6hh3wfobK4V2yAMWJzeYK5c0qvIzQZv94rfupDTcOJXVICOM/2+caW 8b5kyqE4PFOL72KXnavqStdpp9927ASRkZOf2R5iD74pND012qGLwWKokU1E8EoA2DLLyMDXrxz ISK7vfkrfbHWKJUFzdSuMQA06hhccZzWWb7nYo0MBqMIfkVUybvZ/oMNDTqvnEVnakgQjjBYi5m 4d8w7IeN8aqzHhmYR+zWPFSh+YDjrxxnOR7jS1qPRL1+cMutR8B0uLM8Bu2Ibg74/ovVf15aQW4 jVwIP5aTUOjNklDtmnFdGnv8XNlTn/PgulHfYdsagNTyUv4R66SkmaCBgeV+iTkOLWvHvki2dOU Dti4t9OJbJpAh6U9Fdw4hSh8hadMvwJVMK+0x6q0WZd410UprMULv3rxZ1IhOjkWVxsGaU/XJ6c 3gwATWIJS8qbbeKEVoX9Bm590uxQULT1/USttL7kxlSueG0cItM4nfP6IPjE6GWLu8Pk872bI1g 6C4dFIesc5G5haw== 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 d9aead646ae0..4db3ba26c2d1 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, // + }, // }, configfs::{ self, @@ -26,7 +29,10 @@ kstrtobool_bytes, CString, // }, - sync::Mutex, + sync::{ + Arc, + Mutex, // + }, time, // }; use macros::{ @@ -95,6 +101,7 @@ fn make_group( home_node: 9, discard: 10, no_sched:11, + badblocks: 12, ], }; =20 @@ -117,6 +124,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(), @@ -186,6 +194,7 @@ struct DeviceConfigInner { home_node: i32, discard: bool, no_sched: bool, + bad_blocks: Arc, } =20 #[vtable] @@ -221,6 +230,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 { @@ -328,3 +338,48 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { ); =20 configfs_simple_bool_field!(DeviceConfig, 11, no_sched); + +#[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 73f14d6e379f..90dbf318c2f8 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, @@ -38,6 +39,10 @@ str::CString, sync::{ aref::ARef, + atomic::{ + ordering, + Atomic, // + }, Arc, Mutex, // }, @@ -153,6 +158,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { home_node: module_parameters::home_node.value(), discard: module_parameters::discard.value(), no_sched: module_parameters::no_sched.value(), + bad_blocks: Arc::pin_init(BadBlocks::new(false), GFP_K= ERNEL)?, })?; disks.push(disk, GFP_KERNEL)?; } @@ -179,6 +185,7 @@ struct NullBlkOptions<'a> { home_node: i32, discard: bool, no_sched: bool, + bad_blocks: Arc, } struct NullBlkDevice; =20 @@ -196,6 +203,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(); @@ -237,6 +245,7 @@ fn new(options: NullBlkOptions<'_>) -> Result> { completion_time, memory_backed, block_size: block_size.into(), + bad_blocks, }), GFP_KERNEL, )?; @@ -351,6 +360,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 static_assert!((PAGE_SIZE >> SECTOR_SHIFT) <=3D 64); @@ -396,12 +415,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 { @@ -431,6 +452,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 @@ -440,6 +462,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 { memalloc_scope!(let _noio: NoIo); let tree =3D &queue_data.tree; @@ -461,7 +496,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) @@ -475,9 +510,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1F25A4D2ED3; Tue, 9 Jun 2026 19:11:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032287; cv=none; b=UoOTvj4/SbD1PPE3fJOzufVI+p7qz6aLJZosAcw/fA9MaqYBErth42AKWcKjc5xhgWMjaVsf9cQ5R9ZV16mWiVeGnLgi4aKr/tjQqLVtE++XEVlnvIYihbhERp5H6Px7In0mFcWfI508x4+IZi3zgPWqtF0o/5pBWqsbKIh5GCU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032287; c=relaxed/simple; bh=qZlgs0sK+HX7M/TrC9q/wYE8rUGAPlyTT8AOUXdX4Yk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=DD0MLGYqa7x99oWEo2zFaT2blP5iOg1ICvgNin6U4O8kVsQqkQTCyA1wdMm/CqbjFox0hnPC/yjOibTSrgIuHS4/9tA1I7Ny0N+vgsy5FfCZbS0XmhUzCd3nNXHzbJGVoPg3WBMjJItZTaYChTG/aVmlrit+C0FIMBcKzrGwbDA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ZXDYoeVZ; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ZXDYoeVZ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E5C771F00898; Tue, 9 Jun 2026 19:11:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032285; bh=Genwro56cY+UIXWxa1GkqiC9jUD33al3dVJcd/tkiNo=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=ZXDYoeVZKI30KjnBvSohk8mjBZd6jwRPYFqkUn6nNY0zNisn6gre1LlbDLVxtgxmf RcGpnkt3BgKupb9mb0Y/848Os7fo6XAmFreBrYorbRAogCKdFwYtGRqH3fEVOMntj6 o+Hgu7cbfGCyJiY7bN7CQFgvxaCns0ZUIeGv5p64GnQBuOIWZhw73oAJw4xnaTOka6 3S7I+6Z4RB8UWZtVdQAzUBXdDzRVMhf/4yz3nMPJTt0znjKbaD+Z6y720cdZo8Cvzm IJZwVFC0UFK+a6xGbaWv75tNxuAwnyeBy8tn/fRC7fkY47aYPTjyG7xaf9Cgc2kN8B oiS3/7pGuo5hA== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:09 +0200 Subject: [PATCH v2 30/83] 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: <20260609-rnull-v6-19-rc5-send-v2-30-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4985; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=qZlgs0sK+HX7M/TrC9q/wYE8rUGAPlyTT8AOUXdX4Yk=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTPGfHlAiKSZ2KoOX7dAG1VcEApOjObDwOa2 1pRADm+b/iJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihkzwAKCRD6UCkIqsW9 0LTZD/wKvry2ERjhKHe30Tz0FYhDCvD5njsXCp7hpiJ+vTZbjExI1h7XCB2ebt/+VCJz/3m856v A8nO+NsPm8ezrty4X/Pv++6IRkoxFQJI8NDKjqNQde1FrPl1UY/5uxlhtXgEKqDy57jDFjs3Wfc y3n6Xae60waSpqvVrOH5T9Fkk6AQI7loOr+L/4CQuSDeFKVblEhugCUFC7voOocOx6sMSvC+6gS n7LI/bqtdL7SOoe3iD7APjUqn0ulJ/7/imTZqsGIEPaSdD8UE8ft2709LldeVqHMVtduZm9cROI +qXh4ariZkXnuOengqFXoPHFSUfiboSdjFmuyf6gk+xN8ZFfopfuwDLBjzzwG30KFbFcjzFFzcq SrVliLC8au1PDN1SPHSquoeHq4RL0wZEMLt8B/HhyV53jqgVJMRL58iL9a0dACn1eRG7w7gPwo9 NihFdSZNh2q5gBHaE9e4D3hp84UqHfcPQnFiyimsfjVujTs0KIFsC7o5TzZ60v2W6ZRtwFGhDJQ fw31v3XM/B3kVAc3oaK8S30Ioy/ed8H7xbRSv8eFWVn1+vTc5qnF/OGzhJSBtPvpZCJpuJHyJm/ wL8myMhgNUsDHAw90318RVeRb76yA0xxrb2DWdBLEDcfMQ5NWCqAQmTcG2jGnUlXhxV3JrqT5u1 xeFKzNyVK8MqfHQ== 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 | 6 ++++++ drivers/block/rnull/rnull.rs | 21 +++++++++++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 4db3ba26c2d1..05229ba9173a 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -102,6 +102,7 @@ fn make_group( discard: 10, no_sched:11, badblocks: 12, + badblocks_once: 13, ], }; =20 @@ -125,6 +126,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(), @@ -195,6 +197,7 @@ struct DeviceConfigInner { discard: bool, no_sched: bool, bad_blocks: Arc, + bad_blocks_once: bool, } =20 #[vtable] @@ -231,6 +234,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 { @@ -383,3 +387,5 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { Ok(()) } } + +configfs_simple_bool_field!(DeviceConfig, 13, bad_blocks_once); diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 90dbf318c2f8..5486eb6dd921 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -159,6 +159,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { discard: module_parameters::discard.value(), no_sched: module_parameters::no_sched.value(), bad_blocks: Arc::pin_init(BadBlocks::new(false), GFP_K= ERNEL)?, + bad_blocks_once: false, })?; disks.push(disk, GFP_KERNEL)?; } @@ -186,6 +187,7 @@ struct NullBlkOptions<'a> { discard: bool, no_sched: bool, bad_blocks: Arc, + bad_blocks_once: bool, } struct NullBlkDevice; =20 @@ -204,6 +206,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(); @@ -246,6 +249,7 @@ fn new(options: NullBlkOptions<'_>) -> Result> { memory_backed, block_size: block_size.into(), bad_blocks, + bad_blocks_once, }), GFP_KERNEL, )?; @@ -416,6 +420,7 @@ struct QueueData { memory_backed: bool, block_size: u64, bad_blocks: Arc, + bad_blocks_once: bool, } =20 #[pin_data] @@ -465,12 +470,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6CDA923D2A4; Tue, 9 Jun 2026 19:11:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032281; cv=none; b=uAiUR/RPRRk6+hps6QaTMDfmgt1iVFa6P1uKZwumTF3iOcRtor/CRiS0V8zb9TdivLnu23Ygv/cdfj/cld2MD90p7e0/g6Yk5xSI3gjAG2EZipDAklaI2fo3WvkPBOf3Cv+ty1SOb+y3rka3mIakIxflQreMCpMHNOWHCFEGJuA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032281; c=relaxed/simple; bh=mBVFER4nWrZs+8ynhjMfZaNZazMthVQxUgWM919F7I4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=D+y75GUSZO+DiKd+yoMOFrCQRvHgOXtn7nYykhd8IP1in9sxp6/GtsFZBq0Soo5M2IAder4rN3UIGzyqtV5U3HhjxxobdXtwqNu5KaEs1j5NM07bFltzwbrEWARDI5rjfeBemrgBiKYtL8P/thsAqceSgsJo5sYp51NT8A1rCMI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=BjLr6wq4; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="BjLr6wq4" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E8C241F00893; Tue, 9 Jun 2026 19:11:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032280; bh=KilJT6sKvydSYR5+me7VI/DGNTEVkZbaYVToN0IHPfQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=BjLr6wq4e25sVTou0OaA6fD+9JF++b1zX3fzRdWtlKGizoHcOiMZQ7eSaLZkaQ/GT qlTFftploMEN10YzFDj6Edo+qC4MJjG/cS7KqxpRyAgugcKFr1bAHTYjHFmhiZxIee IlxcTGJBV0ZgmVmbWTjj4jgMP2krdboUiOMkxqG9mIMYBzPrYmcyt44gapjfWXziGY 5tkX0g+Ps3sNKE9uTRpBnvAYCAzQEGfLHiq60I8zC0e++1WYve+FV8N0az+ouabHUO /OB1c8SRM45bd5k/jAqLzJ8GddId96lhURhdpoqgOpe04dFueNadbtUcjipLX8cOP6 MqpMgzu99sw8g== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:10 +0200 Subject: [PATCH v2 31/83] block: rust: add `Segment::truncate` 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: <20260609-rnull-v6-19-rc5-send-v2-31-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1671; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=mBVFER4nWrZs+8ynhjMfZaNZazMthVQxUgWM919F7I4=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTQNzHBxSzi8DrV8H8g1Ugz2ZxPBx/cKvwuy 5a8kGpX0RWJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk0AAKCRD6UCkIqsW9 0Hw5EACkjHqDIlzn7uZ2gAggaV5oUwQ+rp2jrz3Nma9IjCrnBK4Q9p6cn/1CrpI8nyc7uVlIwbC 5+Vn+PielpQcAGzBTH55vXrrepSbx+zx4Kue04PX0vNq2Q2lIVlBDPvGg5FajY03X3MtMtTEV6B UQSEKX7OUt7j0VUA+wwaLwxw6NZJFvQbIwsfqo7qFPSUakUotBvyxwP0bdpdulzWgxhUgjd4ppx nQqzGSG1Pd2TfvNDBubKFRLcOpbSQAtifywnvaNPTHWb2YVEDj541UArVxVoqOCMGJnrQWqf+Xu W9QjQoA/HEi8cQ3JMt3JBZlaIWjLT0BagtBWUhfyhaUL62UpsnYP+5WKag7xRI5J8No+Nt2yaQp S1pstT1rf7lbQmqjjjX6yEo4+XAiT77Uhc0eMF29yjfUJtfPcN3xs4nwosWa0WIbKr5cSnNH3Wv p/9C1a0gK2oCFoYlrkiasejuWjM5YCff8+uI5R90KnFWGrkpuw73yo0ybUed5y61Y0BYpGtk1KN t0BHh3h2x8DTu33oax6VhJoH4DKqunh/6ax+B4o8Bqks6KRA3y+KLhZpQTlY2/SobAzBnPH1QCW uaaA43Wzb2Uf7FBC7GR1tdXKuKsqs0Y85bpkP5PGeEpINnbwXwpuATZuvfQWVCLzmNYgLVvXgrV ts+xMNrZWroS58Q== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a method that limits the remaining length of a `Segment` without moving its offset. This complements `Segment::advance`, which can skip data at the front but cannot trim data at the back, and gives callers a way to clip a segment to a maximum byte count before handing it to the existing `copy_to_page` / `copy_from_page` / `zero_page` helpers, which already bound themselves by `Segment::len()`. This is needed by rnull's partial bad-block I/O path, which needs to clamp per-segment work to a sector boundary computed from the bad-block range. Signed-off-by: Andreas Hindborg --- rust/kernel/block/bio/vec.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/rust/kernel/block/bio/vec.rs b/rust/kernel/block/bio/vec.rs index 99ab164d4038..61d83a07397f 100644 --- a/rust/kernel/block/bio/vec.rs +++ b/rust/kernel/block/bio/vec.rs @@ -81,6 +81,18 @@ pub fn advance(&mut self, count: u32) -> Result { Ok(()) } =20 + /// Limit the remaining length of the segment. + /// + /// Shortens the segment to at most `new_len` bytes. If `new_len` is + /// greater than or equal to the current remaining length, the segment= is + /// left unchanged. The offset is not modified, so subsequent copy + /// operations still start from the current position. + pub fn truncate(&mut self, new_len: u32) { + if new_len < self.len() { + self.bio_vec.bv_len =3D new_len; + } + } + /// Copy data of this segment into `dst_page`. /// /// Copies data from the current offset to the next page boundary. Tha= t is `PAGE_SIZE - --=20 2.51.2 From nobody Fri Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BC8B44CA277; Tue, 9 Jun 2026 19:11:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032270; cv=none; b=lOJg2IwSFbJTSA9gFTUhjLl4XzlyISb7rhjF6Jp2c88nlIbOowNennbZIi45Fff2kt708Ahu44Uzcw5JH396TruALbHqiOp8rxZchMeLkpmgP44YRF/wXeAd1rlpg57I3AoMoEcq/687Fe0Toq/olFkpfXI9w5XiicP1doZqg78= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032270; c=relaxed/simple; bh=xV+OONEs9Ld1Dxqfh4dzHUhstJCVgf4f6VVBNqpaotw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=FkgvRLIQ2KR3hUruGjjiq2OOswIdqxVTw5ypTc9OyKamk1w6wkC5mLrjzz3FU5GLKTqLMUoGIRWY0NcvtIM5i7TknJ3KIfiAY/bjuJWoMzVEKDCmC4eT+Ikog/2zFVMNx7GVj/hNmvvOJuh7O0v8GO8mQ4YS0/YHfEMdVf+UqUY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=nuQYPx1P; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="nuQYPx1P" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A38621F00893; Tue, 9 Jun 2026 19:11:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032268; bh=zLr5CLPfIXCjUx/yqmdeB165LH9EbFQ5aO9LrGL70xQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=nuQYPx1PsxsnailNXG1ho0LHPrMkIZkpy4NqAJ2mJ3oDsasqR+f+Y45zebfqY0SKo 3MZHGKSdBGXJHJaC+DeBrNboCdqknkVI2VtYFqa5/1F8lKDhRK9SSHb7vnjQPLm9ZI slMrD5cqEHyaCYtkuX0B0UI0kTPXkx9zdJ+Jte5HDuJAT9pDi0+/h/fe245OGiCrwr nCUOYC1bPBh06pwygqjUYtGx6SoQxz7aQvswDE14j3GXQ7pIXsqRr5tEOY/9BYtax+ Cgb5SmQGjitdFRQKJsjXVePlqLAOy46TMhPF5WWytp/ZpDTcS62f6aZ/bmRawL5OWA hMLzNU/zYF+Bg== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:11 +0200 Subject: [PATCH v2 32/83] 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: <20260609-rnull-v6-19-rc5-send-v2-32-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=9670; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=xV+OONEs9Ld1Dxqfh4dzHUhstJCVgf4f6VVBNqpaotw=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTREVg3urPjuSLlm/682SoGEi1sYXPkiTpgi iPWSVRqemCJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk0QAKCRD6UCkIqsW9 0AwsD/0fOiEtoWSB/RGdXRgEjP+HqCgBV+3fN5unUlchYeBh/g7LbqhGVP3GBg5VFh2/9a6BxFf lU8h9LrhpM3DGOiGdDsM+6VVEBxn48QZmHRuVVnJNeOvaSdKM/XHwAHI2xU/Nc5t1eZaPEOLPXN OYnbe17eArn7XEOIkEw4Bw3YO7oh1yt1dYEdmbj/W5iHO6CoBkT3n6VSVQGmjUIsTotQPBlCTSX buTVNuq1/GDiG2L3YpRRoT1FubfStQzxixG65DvAlCewwKPdpTS4HZZLZrMuvQ8gCaqYb++ljXy YHB1wwwDB3pClCLJcW+cPEl27Ipasuz+ICo3dS52yYrPHnONAQLMvcuhUWeEliK3Dn/lL+X27g9 xQZc5skJdlXcQ9k1OFinMN/JHkag2jqgJVI+jAIsiejYnasdJ/62JsUeh8S1VNghrsuA6lU+wpQ uS7ZpI84EjkmYE1bP4jZVc02AsvF7DcVO/FPTHi6jvVeBKgj5CUOumYcklayr3q+heTzta2jWY3 QL8PUpNTIerN/kAAyEZo7bSS8d7sIcvviYI3TE/+VnhuFn3f8+nS2XN7zP6YnVqI2L6ea/QaOib 4DI6hyKdSw45yjWQCl9BnJ77bDSF0Z3mN6gAn3X8jftuYNdLuuz2Rdx0+fF1wpQs6zc076Filsj dBauCb+4T77Pa/Q== 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 | 5 ++ drivers/block/rnull/rnull.rs | 126 +++++++++++++++++++++++++++++-------= ---- 2 files changed, 97 insertions(+), 34 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 05229ba9173a..0e9fe8cdc07f 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -103,6 +103,7 @@ fn make_group( no_sched:11, badblocks: 12, badblocks_once: 13, + badblocks_partial_io: 14, ], }; =20 @@ -127,6 +128,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(), @@ -198,6 +200,7 @@ struct DeviceConfigInner { no_sched: bool, bad_blocks: Arc, bad_blocks_once: bool, + bad_blocks_partial_io: bool, } =20 #[vtable] @@ -235,6 +238,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 { @@ -389,3 +393,4 @@ 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); diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 5486eb6dd921..be0b4bd25e53 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -160,6 +160,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { no_sched: module_parameters::no_sched.value(), 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)?; } @@ -188,6 +189,7 @@ struct NullBlkOptions<'a> { no_sched: bool, bad_blocks: Arc, bad_blocks_once: bool, + bad_blocks_partial_io: bool, } struct NullBlkDevice; =20 @@ -207,6 +209,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(); @@ -250,6 +253,7 @@ fn new(options: NullBlkOptions<'_>) -> Result> { block_size: block_size.into(), bad_blocks, bad_blocks_once, + bad_blocks_partial_io, }), GFP_KERNEL, )?; @@ -352,15 +356,66 @@ fn discard(tree: &XArray, mut sector: u64, = sectors: u64, block_size: u =20 #[inline(never)] fn transfer( - command: bindings::req_op, + rq: &mut Owned>, tree: &XArray, - sector: u64, - segment: Segment<'_>, + max_sectors: 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> (), + let mut sector =3D rq.sector(); + let max_end_sector =3D sector + >::into(max_secto= rs); + let command =3D rq.command(); + + for bio in rq.bio_iter_mut() { + let segment_iter =3D bio.segment_iter(); + for mut segment in segment_iter { + // Length might be limited by bad blocks. + let segment_length_sectors =3D segment.len() >> SECTOR_SHI= FT; + let max_remaining_sectors =3D (max_end_sector - sector) as= u32; + let length_sectors_allowed =3D segment_length_sectors.min(= max_remaining_sectors); + segment.truncate(length_sectors_allowed << 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_sectors_allowed); + + if sector >=3D max_end_sector { + return Ok(()); + } + } + } + Ok(()) + } + + fn handle_bad_blocks( + rq: &mut Owned>, + queue_data: &QueueData, + sectors: &mut u32, + ) -> Result { + 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(()) } @@ -421,6 +476,7 @@ struct QueueData { block_size: u64, bad_blocks: Arc, bad_blocks_once: bool, + bad_blocks_partial_io: bool, } =20 #[pin_data] @@ -449,6 +505,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>; @@ -467,40 +547,18 @@ 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 { memalloc_scope!(let _noio: NoIo); 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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 855483E4513; Tue, 9 Jun 2026 19:13:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032414; cv=none; b=t19XL0Kl+E8Bv9E4RvVQ5WyJFOr8FdF9Uet2IIYrHosL2+n5AagrProsI04kipUxuududbiCbGeG3JpCmsWDmvaCagYIUubfE8hN5bN0/zKPbirg+9b1m5NlcDuPNirINLstXYnLmnmRDjtlY8FRF13NkU/TXvIGC+ZHMHUYO54= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032414; c=relaxed/simple; bh=sJLbN5KEUkCPHK8xRsQjgnA4YwvknpnDUCxdTCWNd1I=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=a05BMa2VUsdDHaDcF43bggGvWtxjNlYUXxrvfIYLrPj67bHMbb5OaFToN17c+LBSrHZ2oHlMkf20FCbjgnkTVvTLE6kouSa3hOAKkHCr6fJA77SZuGr3s9oPKl8qEbz0hjF3+7D0Y05JA+gMcxiYD2L/B5sxlcCxbcwVwetbSIo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=XG+ezTAn; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="XG+ezTAn" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3767A1F00893; Tue, 9 Jun 2026 19:13:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032413; bh=MQiHlNRAHcG0zCplCfubwH1heBFHeJMR1Ghfy65SyHo=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=XG+ezTAn3i1xqHdZPtdFpCrSiMPocPWl4XuHVxwbumaNYCNa7ClDAIbBNh5b2lukE misM/dXVckb5it86sWzB5ZkLc1PL+RWzMTweV01gvsSZS+q4jteYytyzA+i+pqRXsx Tf0gjOwSp52RvKmCY+jaFJtXIToGzapEcixnTv1RbYIRIoEq2vAx+L20z85Qr/n15a iIgy1waClKBNfPxwKazUMxsvlrexnBAL4byc9fhhLND8oE/V+i0DEqfQaVkTgYqdA4 BrQMNEi4z8izlVAE2/HEDB3oRSZrTpSn36RPtcad5hLtXw8UDinfZErDAsW8OJjrwf mfa7UOUgqakpw== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:12 +0200 Subject: [PATCH v2 33/83] 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: <20260609-rnull-v6-19-rc5-send-v2-33-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org, Andreas Hindborg X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=5734; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=3g5+YHpLQZtUQxpZtQ2/w4vImmZD3/qhcfuMUlvuqQc=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTSdtbD1l7vP9EyaG6jBiQ3jFqAW4K54QXX3 hnEnGAciQqJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk0gAKCRD6UCkIqsW9 0HJED/9N+0BqKQHS/fK1I2gbsYIMS8fTYQC9/oy1w2ldDx6XoxRqHv68II+u+A84sakszQZjrry nUk3j7FAqdeCks7LeDrW2U/w/cjCJVqJPU4uXR60K4xowPGgdiWvSBpjUw7+Zr8G5A+gjNbhoFR BNpthODIrjQsn4+AYmz8T1Az7y4r6jEQs7mKAjpHB2kaRIbEEy8Prxg4Yz91bDAzWD3K3A6XsW4 +v3i+KskX2LGXBrdsTu51JDJ1vGhAXDS7XGUocRvpYmFzjUAGyUynZebFu9Bn5sMQQshgWtY0Bi CUE/+WvRAcMljkDv/ha8LtcMNgbECjUhzwEMYB/WcWAJ7em+2PW5bUSQVflvoLJD86cOH9xe/Kf 4d491j0ovtSQ3eq7qp7/9i2qakGwACVppYf//cg68DAi+438MqhvsehwTzCo6B+oqRkS1QW63YW GK88ffUCkNnuEfZwlKVZbPY1wfRT5mSXvXqe1D2+b9ohflfK/QxjfenRsSgSlWYv+9Xrr86K0Oh vP73viiPWDBIfq56mCsGfJx/rKR0h8KVcJJbwyjb7M4jBBr1eEF4GFYB5NpIx1hjsjG0iUvPWLC c326mxqiqd6Rvk5LIUzUH5odg8ocrT79Vwe68mcQ2VYrJgBMs0YVPlkRKVnP7QKNIjvugXz0gLR 0LRZdzS1Ak40/oQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 From: Andreas Hindborg 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 | 26 ++++++++++++++++++++++---- 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index be0b4bd25e53..ad26a4a8dbbe 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -240,7 +240,7 @@ fn new(options: NullBlkOptions<'_>) -> Result> { } =20 let tagset =3D Arc::pin_init( - TagSet::new(submit_queues, 256, 1, numa_node, flags), + TagSet::new(submit_queues, (), 256, 1, numa_node, flags), GFP_KERNEL, )?; =20 @@ -533,6 +533,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 bac15b509d90..28cee0d60846 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, NumaNode::NO_NODE, mq::tag_set::Flags::= default()), -//! GFP_KERNEL)?; +//! TagSet::new(1, (), 256, 1, NumaNode::NO_NODE, mq::tag_set::Fla= gs::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 c49ca2e8bbb2..093bb21fa1b2 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -63,6 +63,10 @@ pub trait Operations: Sized { /// the `GenDisk` associated with this `Operations` implementation. type QueueData: ForeignOwnable + Sync; =20 + /// Data associated with a `TagSet`. This is stored as a pointer in `s= truct + /// blk_mq_tag_set`. + type TagSetData: ForeignOwnable + Sync; + /// 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 d6d104adf4aa..bfb8f8af4ee1 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -19,7 +19,10 @@ Result, // }, prelude::*, - types::Opaque, + types::{ + ForeignOwnable, + Opaque, // + }, }; use core::{ convert::TryInto, @@ -56,6 +59,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: NumaNode, @@ -73,7 +77,7 @@ pub fn new( queue_depth: num_tags, cmd_size, flags: flags.into(), - driver_data: core::ptr::null_mut::(), + driver_data: tagset_data.into_foreign(), nr_maps: num_maps, ..tag_set } @@ -86,7 +90,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, }) @@ -102,7 +113,14 @@ pub(crate) fn raw_tag_set(&self) -> *mut bindings::blk= _mq_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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A17EB40C20B; Tue, 9 Jun 2026 19:14:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032479; cv=none; b=Cr8BFmBYqhC0oHaDsQ7e5ytT89SbU8Qi4ohOMWBMP1zETcDfDWCbjoUC0QJI7aaxj3qaFZVrECxoMoq1vK8VUXOtVcNfGgMsm8PuDKiiR5AHgwGiwqm/qGtWcSEPAcsRgbZOYdlhx/toXF4rOr5CaH95Mi2/sj9Vra6zsmbO3hE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032479; c=relaxed/simple; bh=VSjbR308Y3W8tVGZAOb4Go9QtJqPNxvDl4+QWOFR8HQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=BUwmwdJcmaY57/Ap+IxGO18ys3fHlfwAqczChb7pFj7eIfvQXLbJHcftZ5CcBwPzqEMntOFFKs1DIbo918mtUPiyMMZ2b60wYiHwRB7a+WAai33RMEAUas17FBcmCTX+5j/cvRKM+3TuE0GsUT2ECo/9tRpO3PgaLM9i9sJ4P9A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=CnPtpzyI; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="CnPtpzyI" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2B0831F00898; Tue, 9 Jun 2026 19:14:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032477; bh=RUaG/t4BDKUDpP/85NUbptm2D8pKhwGgeSEJZQeUeww=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=CnPtpzyI6JJwiF89/OVcBwVdCgzSMStGy76LCrLcV2v+1pJCJ3u/cfYj3UmJ/txA0 d/F7MGakQeCukt7wcUPT+S7HGaXMpdGiGc+0Qz4Ur78myT/Uh4uTHzGocNTkRjsjnJ dIv9KjhTJTv04bu6G96Dej7Reso0hHguGGB9YhLPUh/gzwMUbks5XPB+vCBLO+UyKL QxeMZtK3isd8uolnozzIUuR5PbDgpdvyuxuTYWZRr8GzujIwExJwL+vJOMbYVAA6Q4 BKfsExARjUkMoG+roR+q74XSanjl4R10QPhHfLB5GDB9xLCqF0toNq+EDm/2A2RE67 bU6qNL2PmhlLg== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:13 +0200 Subject: [PATCH v2 34/83] 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: <20260609-rnull-v6-19-rc5-send-v2-34-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org, Andreas Hindborg X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=11924; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=RnxsNBO4KO4zAfn6JI4Hivn6bXj0kWEGZsxUPnRjRL8=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTT31y62ICIlg+/98VZqV/cU4EFmXJR9XD0z hQSjPqeT+2JAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk0wAKCRD6UCkIqsW9 0JRUD/4wIz/6c4NdjCaY6LJ9sWTiesBhrg6ORxtGqQfBUZbTDLhJc+Bb9URi0WPsT+s1Dl5170Q LeP06P5iO9om3yjr095jzb1jWks1Y3xHEVQbQ840PUfmSWKgzjfix8V9cX3z8j806favdqneebp Sj4zDr2MCHBAJqpqgF42aRRWntwL/BNIqnyn4RaCEJuQh1wji1HAmKxS9rtat/QOeNkextJX+jZ NudJugI8vkPYDiqspVI6DIPuZaHhurfhqnZsAMx9Ann7ZLS/8dwzzyEkzltl3KjfZ/GlN8kcxuS +dUBkY9ZQTvocxDPk+C7w89SdC9Q1UJ4Ggw6bvjNRxLH8dboDNgXnL1Squ/5BdIlSuJEsV9O4v+ ixqJ5I3ZBQmmNST4LI+F/FPFeeMOyDwLCmCIRwjB2YD/BrSbeQglVz0Ke0hSi9YEOG4E6fLBl9o XRY+xRfltKW2HhfC4g/wzInFbRvnIE5O01PdM0pjmcU3tW/S1X8g689CNtGKUCqkLNzPLWGKe5a GldkkBaIv/pUdPHMbf039XyDfoxEBjs/ItgSaphYMdWIlJUFS3EUWBHSBKIjjpGBEaIkC4TdnCe TyfUhbCEbEmmuf6qVx2XV24clbPM/7x4MGtHAiVQSWrFAAfx8n6xFii+bOeUpHVFMS0w2am5R8g Z4fes14xD76AyIA== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 From: Andreas Hindborg 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 | 88 +++++++++++++++++++++++++++++++---= ---- 3 files changed, 100 insertions(+), 19 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index ad26a4a8dbbe..0c1bc2f5ae9c 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -534,6 +534,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 { @@ -544,6 +545,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, @@ -575,7 +577,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 28cee0d60846..b095cc7f51ce 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 @@ //! Ok(()) //! } //! -//! 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 093bb21fa1b2..1b20df25d6df 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -63,6 +63,13 @@ pub trait Operations: Sized { /// the `GenDisk` associated with this `Operations` implementation. type QueueData: ForeignOwnable + Sync; =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. + /// + /// Hardware contexts may be cleaned up by a thread different from the= allocating thread, so + /// `HwData` must be `Send`. + type HwData: ForeignOwnable + Sync + Send; + /// Data associated with a `TagSet`. This is stored as a pointer in `s= truct /// blk_mq_tag_set`. type TagSetData: ForeignOwnable + Sync; @@ -73,20 +80,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) } } @@ -146,6 +163,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 @@ -159,6 +181,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 @@ -181,6 +204,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 @@ -189,7 +216,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 @@ -213,12 +240,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 @@ -226,15 +259,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 once 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 c_void, + hctx_idx: c_uint, + ) -> 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 requirements 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 @@ -242,11 +289,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6376641FC6F; Tue, 9 Jun 2026 19:18:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032691; cv=none; b=nDidsLwTDUcIA91txPNYWZNXbeDmZLvgzPslVDlBi9Hatt8dpn0qs7qMWJcc6uQ1cTQxKbSO46eDmXDhWTPEtf1YC0z78bmQoU/OztQK/22RHgHuK4R8hePSeTJRo+93Gi0aZMCBmCyH5PRyhC41njGm4rzhY5rFD0lcN/c+NfI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032691; c=relaxed/simple; bh=3yz+SrCPHbaB54NgYDQ68+N5dlDgZu/1MA+s5KWPMjo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Soohnx8F8oPtINy6UQArDSffwbVlZXr0VcazXQ2To9kR4gOg+dzFMsrNVt7PCQ1LY4xUu88wpj1oLCy9sbq4JaNz0GUbTNx0ZAgvAqpdz2eEBt0TR8PxjuzaZQnTaxLG0VZWtAVsep/60r+TDNLOLkSrFpLFAF11MNgGnep95Lk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=XBBv6Gp6; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="XBBv6Gp6" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A42BC1F008A0; Tue, 9 Jun 2026 19:18:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032689; bh=YHB1zFLDwKHODEh3lmkWrJX5tEeRvBklzvizAuQ1Beo=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=XBBv6Gp6EE0ANqOYfEXJeDfeuyrNi+ULc25y6fgn4C9ijf8pm6e1CYO/Z9MwYJCfG ixC0ZRyL+ecQJyernoxXCIpNg+N1Z49wweTc3HrpHfEkU2fz1+BXBOTm0OBBQ0keim o5CIklivVsfmSo2vi22L71JRmOj9zYNFw9de55uTMT6qmAZJ52ECP5f1ReZU+AljdM anQjfj7x8GcBv82CKcF8ix6SBEiyJ3ixVrGUAnEvgbBDSsp+C4YrTyXQd4pf3xhpfw o8IL3h9ZO1EKlhUymYPMikPEV6SSpm3dnheJWI0QGFYMXCfl93QXwvAGUXgnZFLiB5 OU4X1Y+beTYww== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:14 +0200 Subject: [PATCH v2 35/83] 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: <20260609-rnull-v6-19-rc5-send-v2-35-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=34846; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=3yz+SrCPHbaB54NgYDQ68+N5dlDgZu/1MA+s5KWPMjo=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTUSa7JTIavrDEXTxMV9StFT4OhxfwAhN6gu wUW7m0J+VmJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk1AAKCRD6UCkIqsW9 0PUGEACQf/36sFXSAcij3iel+EYCrcpIK3gqtkL6wH7YF09YEtA0f4dlkgFAJzXhbn23J3fMF8G t2jgeY8qavkatzqc2za4m/Gt+5RytuAvjIsFc1cgUefcci+KP2YaphXwQghg5/Bf1pLatm7rsfr 55xmSllPFFzl4gu2kH/e1brv/PG0nD2zB/Mn7LEPB9igho6Si5sbZCPDEj55Jrwu1eAlfmK7XZP x2KmlobMvpHmJ6IlOr1hFytmkN9V+mjMMBfrPLW+QForcZfyzv6fZ1caa28EUOkAnN6IaJz32QJ 3wLDVnI0BIolAHaV2oPLvK8BhP0gUZhPjt9jJ4n7F3L35KGgTN+eA5PZyqvjzqK/pU0OA/KqR/W B8a6tuuasu3mB6o9Ah5Z55VhvjS5Wh8wOEePt9h+cHJS57s/VyhX/hk4A8TQk/z+lzuF40csLyl cOA9QaVkMqdYizoyznw4TcruVhokUs9L8AQsVEu+1WYoRzt7gSCqObxqFmQTjsuVLasSBJu+oCM NAANyu73GHAVSrmYxWXMs/QwMJEsa0Wk2FjP0brO+tv419ctdb40q9uTIa7QjYr4A4ju0j1+Gxn NH5zEFCJj33q/kEY8Ofsq5gp+1SIEa11gvlRkfyf9Rbxibt0EIPahblv4lfxFg1NarGSCFF6JWS fZwpf8Xy1Pu1LIg== 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 | 35 +++- drivers/block/rnull/disk_storage.rs | 260 +++++++++++++++++++++++++ drivers/block/rnull/disk_storage/page.rs | 77 ++++++++ drivers/block/rnull/rnull.rs | 314 ++++++++++++++++++---------= ---- 4 files changed, 545 insertions(+), 141 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 0e9fe8cdc07f..504bb477c2d0 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::{ @@ -18,10 +20,7 @@ AttributeOperations, // }, configfs_attrs, - fmt::{ - self, - Write as _, // - }, + fmt, new_mutex, page::PAGE_SIZE, prelude::*, @@ -104,17 +103,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, @@ -129,6 +130,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(), @@ -201,6 +207,8 @@ struct DeviceConfigInner { bad_blocks: Arc, bad_blocks_once: bool, bad_blocks_partial_io: bool, + cache_size_mib: u64, + disk_storage: Arc, } =20 #[vtable] @@ -239,6 +247,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 { @@ -250,6 +259,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); @@ -394,3 +404,16 @@ 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, |data, page| { + let text =3D core::str::from_utf8(page)?.trim(); + let value =3D text.parse::().map_err(|_| EINVAL)?; + data.disk_storage =3D Arc::pin_init( + DiskStorage::new(value, data.block_size as usize), + GFP_KERNEL + )?; + data.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 000000000000..b8fef411fffe --- /dev/null +++ b/drivers/block/rnull/disk_storage.rs @@ -0,0 +1,260 @@ +// 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 { + // CAST: Device size limited during setup to (2^32)-1 on 32 bit sy= stems. + (sector >> block::PAGE_SECTORS_SHIFT) as usize + } + + fn to_sector(index: usize) -> u64 { + // CAST: Casting from `usize` to `u64` never overflows. + (index << block::PAGE_SECTORS_SHIFT) as u64 + } + + fn extract_cache_page_inner<'g>( + cache_guard: &mut xarray::Guard<'g, TreeNode>, + disk_guard: &mut xarray::Guard<'g, TreeNode>, + disk_storage: &DiskStorage, + hw_data: &mut HwQueueContext, + sheaf: Option<&mut XArraySheaf<'_>>, + ) -> Result> { + let cache_entry =3D cache_guard + .find_next_entry_circular( + 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(); + + disk_storage + .next_flush_sector + .store(Self::to_sector(index).wrapping_add(1), ordering::Relax= ed); + + disk_storage.cache_size_used.store( + disk_storage.cache_size_used.load(ordering::Relaxed) - PAGE_SI= ZE as u64, + ordering::Relaxed, + ); + + let page =3D match disk_guard.entry(index) { + xarray::Entry::Vacant(disk_entry) =3D> { + disk_entry + .insert(cache_entry.remove(), sheaf) + .expect("Preload is set up to allow insert without fai= lure"); + hw_data.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().as_pin_mut().copy_to_page( + disk_entry.page_mut().as_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); + + match self.cache_guard.entry(index) { + xarray::Entry::Occupied(occupied_entry) =3D> Ok(occupied_entry= .into_mut()), + xarray::Entry::Vacant(vacant_entry) =3D> { + let cache_guard =3D vacant_entry.into_guard(); + let page =3D if self.disk_storage.cache_size_used.load(ord= ering::Relaxed) + < self.disk_storage.cache_size + { + self.hw_data_guard + .page + .take() + .expect("Expected to have a page available") + } else { + Self::extract_cache_page_inner( + cache_guard, + &mut self.disk_guard, + self.disk_storage, + self.hw_data_guard, + self.sheaf.as_mut(), + )? + }; + let xarray::Entry::Vacant(vacant_entry) =3D cache_guard.en= try(index) else { + unreachable!("slot was vacant and we hold the lock") + }; + Ok(vacant_entry + .insert(page, self.sheaf.as_mut()) + .expect("Should be able to insert")) + } + } + } + + 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 TreeNode =3D KBox; + +#[pin_data] +pub(crate) struct TreeContainer { + #[pin] + disk_tree: XArray, + #[pin] + cache_tree: XArray, +} + +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 000000000000..bc78973ad5d4 --- /dev/null +++ b/drivers/block/rnull/disk_storage/page.rs @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0 + +use kernel::{ + block::{ + PAGE_SECTOR_MASK, + SECTOR_SHIFT, // + }, + memalloc_scope, + page::{ + SafePage, + PAGE_SIZE, // + }, + prelude::*, + types::Owned, + uapi::PAGE_SECTORS, // +}; + +static_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> { + memalloc_scope!(let _noio: NoIo); + Ok(KBox::new( + Self { + page: SafePage::alloc_page(__GFP_ZERO)?, + status: 0, + block_size, + }, + GFP_KERNEL, + )?) + } + + pub(crate) fn set_occupied(&mut self, sector: u64) { + let idx =3D sector & u64::from(PAGE_SECTOR_MASK); + self.status |=3D 1 << idx; + } + + pub(crate) fn set_free(&mut self, sector: u64) { + let idx =3D sector & u64::from(PAGE_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 0c1bc2f5ae9c..877683dba0ac 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, // }, - PAGE_SECTOR_MASK, SECTOR_SHIFT, + SECTOR_SHIFT, }, error::{ code, @@ -29,11 +38,7 @@ ffi, memalloc_scope, new_mutex, - new_xarray, - page::{ - SafePage, - PAGE_SIZE, // - }, + new_spinlock, pr_info, prelude::*, str::CString, @@ -42,9 +47,11 @@ atomic::{ ordering, Atomic, // - }, + }, // Arc, - Mutex, // + Mutex, + SpinLock, + SpinLockGuard, }, time::{ hrtimer::{ @@ -59,7 +66,7 @@ OwnableRefCounted, Owned, // }, - xarray::XArray, // + xarray::XArraySheaf, // }; =20 module! { @@ -146,9 +153,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(), capacity_mib: module_parameters::gb.value() * 1024, irq_mode: module_parameters::irqmode.value().try_into(= )?, @@ -161,6 +170,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)?; } @@ -190,8 +200,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> { @@ -210,6 +232,7 @@ 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(); @@ -244,13 +267,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, @@ -273,22 +296,68 @@ 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, + sheaf: &'b mut Option>, + ) -> Result { + match sheaf { + Some(sheaf) =3D> { + tree_guard.do_unlocked(|| { + hw_data_guard.do_unlocked(|| sheaf.refill(GFP_KERNEL, = 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_KERNEL) + }) + }))?, + ); + } + } + + // Another thread may get the lock after we allocate. If this happ= ens, retry. + while 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: &XArray, mut sector: u64, mut segment: Segmen= t<'_>) -> Result { - while !segment.is_empty() { - let page =3D NullBlockPage::new()?; - let mut tree =3D tree.lock(); + 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; =20 - // CAST: Device size limited during setup to (2^32)-1 on 32 bi= t systems. - let page_idx =3D (sector >> block::PAGE_SECTORS_SHIFT) as usiz= e; + while !segment.is_empty() { + Self::preload(tree_guard, hw_data_guard, self.block_size, &mut= sheaf)?; =20 - let page =3D if let Some(page) =3D tree.get_mut(page_idx) { - page - } else { - tree.store(page_idx, page, GFP_KERNEL)?; - tree.get_mut(page_idx).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); =20 // CAST: Page offset always fits in 32 bits. @@ -296,58 +365,73 @@ fn write(tree: &XArray, mut sector: u64, mu= t segment: Segment<'_>) -> ((sector & u64::from(block::PAGE_SECTOR_MASK)) << block::S= ECTOR_SHIFT) as usize; =20 // CAST: Casting from `usize` to `u64` never overflows. - sector +=3D segment.copy_to_page(page.page.as_pin_mut(), page_= offset) as u64 + sector +=3D segment.copy_to_page(page.page_mut().as_pin_mut(),= page_offset) 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: &XArray, mut sector: u64, mut segment: Segment= <'_>) -> Result { - 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() { - // CAST: Device size limited during setup to (2^32)-1 on 32 bi= t systems. - let page_idx =3D (sector >> block::PAGE_SECTORS_SHIFT) as usiz= e; + let page =3D access.get_read_page(sector); =20 - if let Some(page) =3D tree.get(page_idx) { - // CAST: Page offset always fits in 32 bits. - let page_offset =3D - ((sector & u64::from(block::PAGE_SECTOR_MASK)) << bloc= k::SECTOR_SHIFT) as usize; + match page { + Some(page) =3D> { + // CAST: Page offset always fits in 32 bits. + let page_offset =3D ((sector & u64::from(block::PAGE_S= ECTOR_MASK)) + << block::SECTOR_SHIFT) as usize; =20 + // CAST: Casting from `usize` to `u64` never overflows. + sector +=3D segment.copy_from_page(page.page(), page_o= ffset) as u64 + >> block::SECTOR_SHIFT; + } // CAST: Casting from `usize` to `u64` never overflows. - sector +=3D - segment.copy_from_page(&page.page, page_offset) as u64= >> block::SECTOR_SHIFT; - } else { - // CAST: Casting from `usize` to `u64` never overflows. - sector +=3D segment.zero_page() as u64 >> block::SECTOR_SH= IFT; + None =3D> sector +=3D segment.zero_page() as u64 >> block:= :SECTOR_SHIFT, } } =20 Ok(()) } =20 - fn discard(tree: &XArray, mut sector: u64, sectors: u64, blo= ck_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 { - // CAST: Device size limited during setup to (2^32)-1 on 32 bi= t systems. - let page_idx =3D (sector >> block::PAGE_SECTORS_SHIFT) as usiz= e; - 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; - } - } + let mut access =3D self + .storage + .access(&mut tree_guard, &mut hw_data_guard, None); =20 - if remove { - drop(tree.remove(page_idx)) - } + 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 @@ -356,14 +440,19 @@ fn discard(tree: &XArray, mut sector: u64, = sectors: u64, block_size: u =20 #[inline(never)] fn transfer( + &self, + hw_data: &Pin<&SpinLock>, rq: &mut Owned>, - tree: &XArray, max_sectors: u32, ) -> Result { let mut sector =3D rq.sector(); let max_end_sector =3D sector + >::into(max_secto= rs); 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 mut segment in segment_iter { @@ -373,8 +462,12 @@ fn transfer( let length_sectors_allowed =3D segment_length_sectors.min(= max_remaining_sectors); segment.truncate(length_sectors_allowed << 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_sectors_allowed); @@ -384,29 +477,26 @@ fn transfer( } } } + 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; @@ -431,52 +521,8 @@ fn end_request(rq: Owned>) { } } =20 -static_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_KERNEL | __GFP_ZERO)?, - status: 0, - }, - GFP_KERNEL, - )?) - } - - fn set_occupied(&mut self, sector: u64) { - let idx =3D sector & u64::from(PAGE_SECTOR_MASK); - self.status |=3D 1 << idx; - } - - fn set_free(&mut self, sector: u64) { - let idx =3D sector & u64::from(PAGE_SECTOR_MASK); - self.status &=3D !(1 << idx); - } - - fn is_empty(&self) -> bool { - self.status =3D=3D 0 - } -} - -type TreeNode =3D KBox; - -#[pin_data] -struct QueueData { - #[pin] - tree: XArray, - 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] @@ -531,10 +577,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 { @@ -545,42 +591,40 @@ 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)?; + Self::handle_bad_blocks(this.get_ref(), &mut rq, &mut sectors)?; =20 - if queue_data.memory_backed { + if this.memory_backed { memalloc_scope!(let _noio: NoIo); - let tree =3D &queue_data.tree; - 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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 69FA4408A3D; Tue, 9 Jun 2026 19:17:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032666; cv=none; b=H1L+7/OM5kxyMsdM84DX5w9XMoI8ecaJT9NZ5kun2f0tC/MtA3dqai/5GRJb4ahIlZYRG7PFI3VjgJthOs8E0XqocPdGwsfUBNxcS8yr4jkIbYvv8nUEiF7xFig6/6l38Yfa1h7NkBJLa3dLxAKwvVLNj8l8Ov+QQ2xeAoTY6P4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032666; c=relaxed/simple; bh=6A74Jcx8EHUn7wYuZ9sgvdf2Nhmf1F3bFtPD29rpOao=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=A5LaewhsPNWK8j40jV2YtIV6acZi9FWp3pYga5y4Wa+/RLHRT61cnyagupbo+dt0nUAMju91V8xJGiv8hzxa4mj5MyBQSsKFu7haI+tz+AHMkcSM/cvQKxddyc3WRQNq91vK+jFQlDY1JDqJ3KHz8PqPQqOCFME2Eb9dWy3Jmc4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Ef6/ao6J; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Ef6/ao6J" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C80781F00899; Tue, 9 Jun 2026 19:17:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032665; bh=yhhiPZN9dpoZ4xyJGnlIqf2UGsG9mC/RzPjjGnQZ8J8=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=Ef6/ao6J/2x2P2Q0Ul+w5+fI3fcDAeHUvj8joaMLKxAOVfCBVdPNot/Dls8xWkw8p v+JkUcacIS0nQQkJ/g5mHuqZ6LTZavpNlH7Vq7SckIE2cKU70qtoKIoJSOBQsKxLX6 EsGwOEGpGG8IWiJH4xnBt5uAgSQDGPmrzm5TAyXHdJ3csKbYVR2KJyH8LD8yeOaKMB eO/8GZHy0QdHegguUdWDWs0DcCD1TiXd+Mfco4iYtoI3Z/sXFZ+TpLtsba4Tbw3+7V cjGLu+gU5awGymhS6I9SChG4YS4j6jqfuQPBSnfW4CFpyz81vaDHIOJ0LNmGEUMY9a NQ0qZYbWXBpcw== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:15 +0200 Subject: [PATCH v2 36/83] 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: <20260609-rnull-v6-19-rc5-send-v2-36-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1041; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=6A74Jcx8EHUn7wYuZ9sgvdf2Nhmf1F3bFtPD29rpOao=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTVV4QA6fjC6s5QJaM+DuAAdyzosiERGRI2a nDPiCvCHpqJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk1QAKCRD6UCkIqsW9 0ONmD/4z/u7pdgX5Wmx6bwNyWB7SgoBcZbYO0GePl/pxDjGYrsXBAKCaR+jIq7gru/rtRnshdS/ S0P4TMMhpGCOhEBoc5NpThiIJBzW3N3u/kL4X9FOxsEs7jdy1tCyUPZrlxWzi5QVbSowIVdo/Mb WVTCRroH4sw+/02YE2fKpBLUhNyzc0GRG5Qu0DdFMrHxJRDsOK0EAFlRvzJ2gb4E16xFBJv8rFg 0qEcfUc+f/5FKhLO8QHeS1qOSPQFC9/AANVaJr8MdbO1NY8BTnLXMf+yuyjr0VdynNTDtInejC5 7+OsySI1f8LWISEwii89pTJ+aey3e9JTXUB44v56mcrUrAA5HwbIxe0QrEZePJWfllIIBUVXK8j SRmyAz1KO6c7xMEgiKtdNeB+3wpH44dMtkiOUIQKUD2Gbt6pJD2T3iBhMv8+hOJlZQHsEOj2LbV GXOrsB75EiLe9qmROJ3RTxojwEA/O3HyReIJhIveUu/5JJSEqTEmUkDMtoexgHbVnK2hD5UGOmM 3TquGBebSsc0PsF7c4v/tR71vnCbhOIcchnkbWsyk3DDItKVKIToZQ7sC3FVAHezHNxEdZS44r3 oCQ0x6gzZjYl3a6jO0cPAREwRMDet2vkSIgEqB09MIEIXMHeUl5DGM50Sll8XyZGhVhrIUNr4Nq XxKWA13bCrjnvFw== 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 | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_di= sk.rs index 2b204b0ed49a..94af85fe1716 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -234,6 +234,17 @@ 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 if the `Arc` and the `gen= disk` private data is +// `Sync`. +unsafe impl Sync for GenDisk +where + T: Operations, + T::QueueData: Sync, + Arc>: Sync, +{ +} + 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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5DA1D4D2EFC; Tue, 9 Jun 2026 19:11:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032303; cv=none; b=SCNbhiX8CQFKgl7m0k/APb7SHtMcCdHwXKoU5vU2wM4cLDR/XpPLPcoMT/5fK88CfBLNJObELbe6gSSV7etO5uvOaSN/gjgc2LnRmNLNJESKJmWpmdmcqgc2OzFZEmEHjHy6XTD/eT8d8j/Qmfbo8X4FBJf9UAIiDN654drtPSc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032303; c=relaxed/simple; bh=Pgrl0umFQHfxEgGBhL3ORYr61hxvp3mCX9/WUL8WuTA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=d655Ad+eVBiYB2htH1rW8+OJURg0r2EEUl1mBxVkdI9Z1Z66G8Cta5Ia/Y9Yu66rujT6ig6u+EmUYMvD4ZAtB7LH4OONqGB/Mck4eCanCsFcRWoHHGfuhfbKRC9belWIsbdXfG95aCkFeX4iml6tj4tiKg9X3G4VDhM+QLteDNw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=iWscA1Ru; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="iWscA1Ru" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 37CBD1F00893; Tue, 9 Jun 2026 19:11:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032302; bh=cTMq75LVYGONeqD6aceZFLDHKDcK1Q7SK0j/d7sqNo0=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=iWscA1RuPcNWUzsAri0x7rO9uKR2nATOTwgMdkliYE9DcAtE72IIAJLsDeIhftwio JnhJp/MlW7V+wTCacbdY57b98GVq2ddUnxCrnWEi4KctzJ8ENaaUVrc/37X8NP11m9 HNQkWQQKwF0mPjOexVkyNhIoAe7rDLeWi1OtUuyQl8WJjJK8Kxr2sherQ1/SYBfFyP bJXYW0Gjq74pBTnh2Abmk2nUXM/RdeLEB4O7pjXNqFOaUeT7Aw2QX2QxDyVtS8PpnO pK3O4Y6jwzL068fD3Foi0NDofU3aGNR9iFoLtxVX6WyvshYjH20iefhjvY914dZYyF QhquVR/aCZuDg== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:16 +0200 Subject: [PATCH v2 37/83] 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: <20260609-rnull-v6-19-rc5-send-v2-37-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=5601; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=Pgrl0umFQHfxEgGBhL3ORYr61hxvp3mCX9/WUL8WuTA=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTWPDkbTCiF4AD045KZczuURzT6Ht4dFueuB WWPpi9oI4yJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk1gAKCRD6UCkIqsW9 0NUHEACfPdxlqQtedQBoWcauh+uupMtyzebSZu/9dyy/KhNqkuWRHfrh7tskkROLw4OetVFM+85 FIRjRx5EzlxXdiS8XjDCiRqCHDO5mXxwUDG6mUJjAKEvbT/xPdM/ZFr3EHO+1VWOUkdt8tb2tWE 1CCV2dTvwd2RqPLZxVJOMSn869PyQJGNBXNUAcTwUwFqGNccu/faYuwjpvNWzUQXT7rHdtL4ugI RplKUy3PWkgIEukdwtUd1YvfsQAEYpRRKLyvJqKYtM5PPwJNzRXRbXyEsd+65znGhMnYJgf66S8 6QxRdnbSQOHoRXKA/XsvwXD5nNsJEbpceZurQyjKafth8w3D2ejKhohwfD4cRmLWnd6taUNgxRX /95y3Fy4RJ8pwbnRxq5pAX6DRHbIbzn6gxydhJJM97TsiYAEx7BTgx1v10ktqkdmrlo5xXyLsOH LMpvBp7w0Bdk3/Y+jqn1Vlq/CP01y5SkFejkmA7C9D6ejGbcZbYvABTEh8b/0lATzLaE/L7A3JJ WEw4YURvDp5LZ6M505NRA9l0DiYOxKu0OJm+HNwZxqjQ8dwpFjODFVMR/waJG+Wc1fQQzQ473NP nQgH0XyDErWgWKxHViVDUbu7i7XIRAyke4TIAbBl997x5fB8ErWdzZ0G5ozTPgleIcgnrdO7ZYF YDjGmpDibn2T6Sw== 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 | 65 ++++++++++++++++++++++++++++++++++++= ---- 3 files changed, 62 insertions(+), 9 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 504bb477c2d0..4df0b748596a 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -198,7 +198,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 877683dba0ac..fd9b770965a6 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -134,7 +134,7 @@ struct NullBlkModule { #[pin] configfs_subsystem: kernel::configfs::Subsystem, #[pin] - param_disks: Mutex>>, + param_disks: Mutex>>>, } =20 impl kernel::InPlaceModule for NullBlkModule { @@ -216,7 +216,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 94af85fe1716..f51bccb0d2ef 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -21,14 +21,19 @@ 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`]. /// @@ -125,7 +130,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 @@ -204,10 +209,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(UniqueArc::as_ptr(&disk).cast_mut()).expect("= Should not be null"), + )), + GFP_KERNEL, + )?; + + Ok(disk.into()) } } =20 @@ -222,6 +245,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 @@ -264,3 +295,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9B2F83E44E6; Tue, 9 Jun 2026 19:15:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032534; cv=none; b=N7fBR5l+ndlD4QFmhO4OcC/CoJCarvqejzWAufgztq3P+MLENM0hXSVZr0zCC/Hdxk7ZdXLq355jZZpK7lGBgZnWYTaDwtAez6YpmkF1ZNV8LofUq+4W4aQ8e5PPX/BHCgwvf6CcpPCpPzx3LcLkon/INpm2ZFNV2BDPjj9fYrQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032534; c=relaxed/simple; bh=e88GUFL7aWGGbG4bgQFnEUpaUQZNG/CxzSPfdvkzNv0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=GCWCan4RktIU7/MTSGTp1J/1SuFiFpo/jyrKKu749+dqrqPaiv6UWJGgCavhVSI9L1rDGfwatcOkErVAxpgA6fplKZMfQju5FPenOCdb40oFAxWXB5kXkCOYHurid6YQndEQRtVREnQJMVg64mvloDBk1n1Q/uASgbKcXL27wHA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=l88p8bP6; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="l88p8bP6" Received: by smtp.kernel.org (Postfix) with ESMTPSA id B61991F00893; Tue, 9 Jun 2026 19:15:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032532; bh=uM6bRZfyW/+v1bt/o770rG+SNBq/AlbWF2/41XM/k+A=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=l88p8bP6SypH1B3G9l7zVj9b/Pcn5qkGcYpYy46zP4wfP8aE311oYvWFaRV4RO94S oHmeW5e/OcBjzKZ4ITwXpOFOkXKiELIvdD9/6UubvXMlI1Hc36Z/XtHvo7V418iSRM +LFo1uWgrYARxP6CHGO4I9MOsTIjrOPJq+EDIg5+GQE4u7CVyQe63cGa7E53/G4hH7 cZluIg6rJosMrDFE8puMXRQt6FswTjEzufiFSaL2/1whSv9zpw06WX/3EhmFbQtJmF Lmdy6b4gbeYjt+NaHpxf/30qFl8rkMv0qKdZ0hQs+K9Nbaq5EBhGu/7pyT5P26nXIb Nmw2PAcqkkE4A== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:17 +0200 Subject: [PATCH v2 38/83] 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: <20260609-rnull-v6-19-rc5-send-v2-38-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=13481; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=e88GUFL7aWGGbG4bgQFnEUpaUQZNG/CxzSPfdvkzNv0=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTXV79hhxFxeQyl4m6URu3O1sAL2/W5HbrDn t0kpZ/CGCuJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk1wAKCRD6UCkIqsW9 0LRcD/9DzJC8KbV9dHlUFIVG30iAFMsShgJ3JYDCft8sbI3kFYTWZ8dDgxFoIb3LNqW0f0+buvy SQBiqEjWo5N/X1z8pt8GEISdjmlx65DCP6YzlyOaligEtYRTWb+WsuzpH1QNoZXb/cjZCv1SH5h 0VnQs+O+bCv0E2hPnmRs/AszUWrqGhE1zUL45q8RFEP3cr74XC3CD3yYeA/s+3DnaThsgxoB5+e tsU+LyHu5aobYPzpXceJq3YMnGyBmMkZEA15DRM6xsCWQaQfeMcmsiY6FLBZZpmSpAHd1zELrqD iuB0rADphtTBR48h7NgpLjdZunRG/Ml60tZq0J4lJgIpp2sh4hoefPyLGCRsR9bkmgth7awc3dY 49FF42wxvUXgEjVdJK2e+2I13aSxEKnR6Jp1V1DGiUfY/QTjDVSARLVR5JkwuVUyt2k2FkdaHhP L9f3jEqZm+tKqFGAXXykd4xPz69V2I4C3MY9gupyaqVxx73oXBxF7lrdYoJkCw68V2+nd4/nC2D nc9YMrk2n2+p1Y5Jl0bgmO/2iyHVef+PCJQlMmpNFaxXFZtrT5NFlZkfKiZd3CgccNlAKy6pRmv MHEyHDR/euz69oewTesoEzGe4wCb238AKO2qcjUTGdThaoOTAkVUTSONhUyNj/RbQwxcmaQcvKf q9CI1PkB0P4i7ww== 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 | 15 ++-- rust/kernel/block/mq/request.rs | 149 +++++++++++++++++++++++++++++++--= ---- 4 files changed, 137 insertions(+), 35 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index fd9b770965a6..bb8c4df08218 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -593,9 +593,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 b095cc7f51ce..77e3593e8626 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(()) //! } //! @@ -131,6 +131,7 @@ =20 pub use operations::Operations; pub use request::{ + IdleRequest, Request, RequestTimerHandle, // }; diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/oper= ations.rs index 1b20df25d6df..01917ef213d1 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -8,6 +8,7 @@ bindings, block::mq::{ request::RequestDataWrapper, + IdleRequest, Request, // }, error::{ @@ -25,10 +26,7 @@ Owned, // }, }; -use core::{ - marker::PhantomData, - ptr::NonNull, // -}; +use core::marker::PhantomData; use pin_init::PinInit; =20 type ForeignBorrowed<'a, T> =3D ::Borrowed<'a>; @@ -82,7 +80,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 @@ -154,14 +152,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 @@ -177,9 +175,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 c06907dfe5b5..f94e9c2181d0 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -24,6 +24,7 @@ HrTimerPointer, // }, types::{ + ForeignOwnable, Opaque, Ownable, OwnableRefCounted, @@ -33,6 +34,7 @@ use core::{ ffi::c_void, marker::PhantomData, + ops::Deref, pin::Pin, ptr::NonNull, // }; @@ -42,6 +44,104 @@ 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()= )) } + } +} + +impl Ownable for IdleRequest { + // The `release` implementation leaks the `IdleRequest`, which is a va= lid state for a + // [`Request`] with refcount 0. + unsafe fn release(&mut self) {} +} + +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. /// /// # Lifetime @@ -96,9 +196,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 @@ -120,7 +239,7 @@ pub(crate) unsafe fn aref_from_raw(ptr: *mut bindings::= request) -> ARef { pub fn command(&self) -> u32 { use core::ops::BitAnd; // SAFETY: By C API contract and type invariant, `cmd_flags` is va= lid for read - unsafe { (*self.0.get()).cmd_flags }.bitand((1u32 << bindings::REQ= _OP_BITS) - 1) + unsafe { (*self.0 .0.get()).cmd_flags }.bitand((1u32 << bindings::= REQ_OP_BITS) - 1) } =20 /// Complete the request by scheduling `Operations::complete` for @@ -145,7 +264,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`. @@ -157,7 +276,7 @@ pub fn bio(&self) -> Option<&Bio> { pub fn bio_mut(self: Pin<&mut Self>) -> Option> { // 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 // `&mut self`. @@ -171,25 +290,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. /// @@ -328,10 +433,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. @@ -341,7 +446,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A49DB41B7F3; Tue, 9 Jun 2026 19:16:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032618; cv=none; b=dJgZv/irUxJKsaG3efBzgrhByrbYMfFKAnuYMBv5BlXlqwmsR11GoROoW4y/sEzCeo9FnVFFLZay1jS+5aDL9tn5oUnJARmP90ZWdhFK2Vg2IHvX092wNIxoNbussB2YoJngYMR/Gu3sORh11DnBKZ6MNDeq5mnB2pU9IDA+nZU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032618; c=relaxed/simple; bh=DDAoobr8iByPKaXTjyWraJH4CgIk1Bn8L7BjryKL7CA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=VctnlDkwQQvGhKVj1itV63FHlGgvbnlhdNYr+NdTBbC2CI1ygQaAWwaoeqrM+l0LDaHSJLnUomfpulDRumB0WGEwiZ1o3NdyKkpX5Xn5TSDd2BiK4P6tlgQD0DVbFEMnktz+uw45/DTePNJYNATTibU5Xmma8k8rCCbOj/7z4EA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=grUeoCqF; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="grUeoCqF" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 548A91F00899; Tue, 9 Jun 2026 19:16:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032617; bh=bVoeMqM+4x+DRGTfgue4mO0tQcRwgKzm2C1LaDMHuSY=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=grUeoCqFABZLR5KpiONwSvirHR19FPTkamv0cIZjpX0X/gJFsc6J8DAxT3mHYNz/s K+WKEyBCxStr9qDhA4AKiYMvggcCNrO6v6UnzkrnISiu10rCWqg7to8tRjhM9xj6sV K9lHLn35p3o/MFw0Y4A9fl00SQ/MEhLpXih7Ql/SZ9C6HEECFy8qB8GvXfD4NNW0av pWuKNQL7J10m3WgHCKMGYvEPnvYbGDAT5pNK4j+B4GQs+PTiLLV5BFoTXxky0YD9Nc oLgtUe6zgkWCwiq2YfUKd6hVyKuvs75kSN2ef1VTy7Bphc5TTpoQ+Hn5hxFQlX00K3 m+L/lnEHwqixQ== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:18 +0200 Subject: [PATCH v2 39/83] 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: <20260609-rnull-v6-19-rc5-send-v2-39-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4451; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=DDAoobr8iByPKaXTjyWraJH4CgIk1Bn8L7BjryKL7CA=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTY5J/OppOq84fVsvUc70Gv8I7RWlb+IcQ5b n+xAAkuUZGJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk2AAKCRD6UCkIqsW9 0JAYD/wNyX5xXqqubNgadeKihqrsjtqz3ydvG9+mnr/DgIpKbh2MXJBs2ZbxSup1esNlSyz3azm wbRDBoj9rDXqvqzvH9ysMyCWYxS3Sn4rgwQZaXXCN42BtGM/DBDmIxsHTvAhY7J/zB/bouVZLzb q028kd3X0GCrSQCgfrEwBWMLLm24Bb4Slv5vBT5q2Deev7kRhBl0VCntwaRL1I1nCQB9oAMoarB 1ZGFqBzMGTshGhaJhTyVdRB7f2ReA2hWS+hj/0TXTgCtrJ5S+i/mS8sOekEOl2hSM1bcpcJzSSV IyAyy3RQLQ74RYnO6a1kYNVLV2K+BEZcmwA/1BXTbgkS7DmmPvMf4ew5bf+UhaMIZ0dYmyPABrt 0r1owc46JL1Q6OGsB26VudnHZc33zirn0gPeZLO/xJedrSMxEQNReQtBysGuDaH41D+niqYba0/ 1pp1sCDcdSVdoy8L9rJR3fHictVriecfMxz+zYIRPVFIUct3uOdV/WYdPmcVstBFfEEGSjnJ674 qxsle7FItS1YuxIFTXLzHCD+mUXvetTRshMhcDzK7uvYW3ChfHzPU6Wzj5k5RhocqFYITVvY9R/ xzww5zYaJaffKh/KubF/t3/ygNbCtd5r8Sg6PDQ6BWZkf+Ot3OkD6h/sAVanI5YNrKTJoFrg3Yi k4HtIwPlH1gz6aQ== 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 | 7 ++++ rust/kernel/block/mq/request_queue.rs | 60 +++++++++++++++++++++++++++++++= ++++ 3 files changed, 69 insertions(+) diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index 77e3593e8626..e89eb394001f 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -127,6 +127,7 @@ pub mod gen_disk; mod operations; mod request; +mod request_queue; pub mod tag_set; =20 pub use operations::Operations; @@ -135,4 +136,5 @@ 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 f51bccb0d2ef..6ba8d88f63a9 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -9,6 +9,7 @@ bindings, block::mq::{ Operations, + RequestQueue, TagSet, // }, error::{ @@ -253,6 +254,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 000000000000..45fb55b1a310 --- /dev/null +++ b/rust/kernel/block/mq/request_queue.rs @@ -0,0 +1,60 @@ +// 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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 187A8411686; Tue, 9 Jun 2026 19:15:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032517; cv=none; b=FitTek9pjBFKhs94ajsn0cyXtQ9tJrruJGC92lYPyfhiaJfuzsgOqk4ekfffFg/5TWa7JYIbig/gS/dl3Cdjr3oEChJCN2ssK1a/bVRrsHu2wRtxpB62NjXv3yNOgZURLT0UUC0w3whe1L65uiADLYfXkuljRtceECQaGmA61X0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032517; c=relaxed/simple; bh=xXqJf7F04NCf34hZ6dXAld8MDblLWnRBzQzNDZIowmk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=KT/3mdRCGPspMIDzoZ74YvuttzYTmUZsdWErKl8We/BWZHHM8jHEIz/02XY/d+Tzth4+/ifO2vYPPxnsIY1x/FPgSOnxrKT4a8XVyuTxKkIpV0gl6VVIPLLLyVxw6g7LG5HVsLw9SR/R6Slgvqu+Mm4LzaMLD+CnEVSD4VomzCI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ggWIxrcj; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ggWIxrcj" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E89F21F00899; Tue, 9 Jun 2026 19:15:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032516; bh=zKR6MXUHWDbRDFl3ImkDVNtbroS5Q/HyfNFCf+FwxNM=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=ggWIxrcjs4A43SLr4s3hdHt917/ecfOv5m8cp9wSvjzF3JoBFOTJjldDMjroXwZ3i bcahTsZaHnpTB4FjNrTNOcxc4P3jrUQOT3nBLICKS25EdvNdDsPaV0Ny0LfZ9RTPKI WuRlLJ8Jr0lhaJBGabII72L6YC/k6hmX8MmumqGxjgPCqpAqK6EjcRSn3x3Dzy4EMs Z42iSrbZQqAaJqJ/mvHM7BAoBKnkpOVW++PQ2L6tJfTFud3OTOtQr21jP6mASeFpk4 vqmPlRCGToJQSUCQoDS502fwI7NztczrgB3wvuHanA1ppG3ZvmLj5XxVShp1riWTWe bqOizZ2xFh9bw== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:19 +0200 Subject: [PATCH v2 40/83] 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: <20260609-rnull-v6-19-rc5-send-v2-40-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1181; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=xXqJf7F04NCf34hZ6dXAld8MDblLWnRBzQzNDZIowmk=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTYKpj79Bj8kwYs3EbPRsKivGoKybBXNZCRR xXW9AqPvSSJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk2AAKCRD6UCkIqsW9 0OcBEACoS6o6SVWxtT7ZyVgvB7JIj4BBlPg28VWTaXBFnfHlyKAgn1i0oqQ16TElCSKGc+Puqpc NnZdm712Ow2whWUmmGO5Nw2NeFcpJtejoAzJYqy8igr4mIL+wHmRtVYyLmCmUralCpN5yPb9XF9 KmqkynCMpexMbGVmDRglQzv/9Q/d80FO7N6TlYcds68fLYxUSF0zOHR/KrH1rybJTcOtsTIQwmt QEMhL1nZEWdBxGK/xsG8RAfHSDqgdlvlgwZvGQLi+X6BjWYsuMbezvOeLw/jNB8sccYddF4o61Z zol2JdhWi4f0QD36nLqujap2uKiK0P4CjTU8U4EgxbyS7TJ8Kkflbg1RI3UbP3xO/VHYNuJq5gk ry3kS9L3CuhrYH3vBH9qznVjsvZfL7C9k+WuqF/TIpMKuAGltj1wz6BHXpMIp0W30phm+0yFYyT j7NfXKf0V0X7a/TiGXhM+HMCgj2kvSWeuFgqpzCvncumdPgNhatqZXURov/OWOx80hbVw6nnxVO QawPayOekOTnzzMDbIknfPLXOTuw4zvoHkgSvWpaVpotOSNV2bgUxv/QbO7+c5OkMZV1oXuHrMa Z+a8e5+mEofXCEblsREqhZ0nbAKv7U3y8IK8rsVVchi4UHWqvuov2dTiYVJYjd7oZQZE8BvveCJ MTSOmiL8Nhitrgg== 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 f94e9c2181d0..a05df2351c2c 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -39,6 +39,7 @@ ptr::NonNull, // }; =20 +use super::RequestQueue; use crate::block::bio::{ Bio, BioIterator, // @@ -140,6 +141,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6ABEE406073; Tue, 9 Jun 2026 19:17:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032630; cv=none; b=lfincPUne0ya4NEv1JiSLYSYgqVSvnASOFtN3DmQZUsUUGhe0ZBno2cNElviR+oYC1hzAZPJzH91F6zVqySK+toO3ZqxO3X2jmP/kW32QkfhYviEs9sBLUJ/BcsYqpXDI6SJNef6mjY9YX7reRch6vMlN/8yj6Uy5zPlxG2O+FY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032630; c=relaxed/simple; bh=NIOSFhSPNxZhzeKeGIxP5rwZxvn86AQcjaih/HmSERI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=frzpjzAx4UOJ8wSzYt/Tix6Jr0cUK31rk3TJH5QcbzBo3k+2aVrK667EP85TWWA4O5Nen5bZEJQeKH8ZHBk6GKpm7BE5DgbCuzXtu3rv15Xs4sRPwTiarnDKLa2hvk4HMOrKHF3aYpnhnGsZwNNAYeEV2JLG97YNlYMyxfnBp2U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=dMBuXHKj; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="dMBuXHKj" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1D36D1F00893; Tue, 9 Jun 2026 19:17:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032629; bh=WOpj7e1Nhzq+smZTE7xHDrYEYXBQOG4Wg4O9/mQv0/U=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=dMBuXHKj7RBMnrn8vqr8h9A0cqlKkwuGkdYwYlk5HTqvGLjz5KyjqENj1lJ0wBl6c 67Koi+t68S/qgWgzIaTGps9fGoTf+rEnGeetWs2XB4tLee1Q29kYYzFkRNke9lZ7TG F2LfbPPnohHoYTJmZxkiXkyIFiUVquvh/atI+hSocOxWAgUREe7//Ydi6jmh+0ZBz9 aaSV208dDkez7DyZrzCT1tqrZCdkDFqlfLSQ/puHn434pX159mMhJdG98G5JAAkhEf U/kbPDSxMX7Vz8z/PRNLH2V+XO/7a6gbfCKFZQr2d4k7tQiqyQklJ43UbLiFX3VyS2 k+Z8/7PoKuN9Q== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:20 +0200 Subject: [PATCH v2 41/83] 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: <20260609-rnull-v6-19-rc5-send-v2-41-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4552; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=NIOSFhSPNxZhzeKeGIxP5rwZxvn86AQcjaih/HmSERI=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTZOFzdFIMSshGFudWmfHLuQiMwx6tHAFLj3 aRHUI8FQHSJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk2QAKCRD6UCkIqsW9 0MT5D/49nDByyphJs16CpsUBJQfuY/Jfg9V9KHly+Mo6HIhh2UrcaH1n80P875Kyz0m+sNCeXu1 B/QAaoAb35Y/RULsjofHll4JlMDxuYjoE4lppDLn0F2aa5Kz1P9ujoe0arE+dGAh8dxKYicCUSC /Q/Iem7dwXnhJSx0gscIiB8w24DN8yA1YqRDuwAMxLKos2HweBg4PqdPbe9IrSdQXlSAATUsDFl DtmKEDrI1hfdNsZU+GPNc7c42c+RLfo8kQRrwMZOlg4c/eXjL9wlaWN1ZnD/nsmlYXO5x7cXqzg 5wET2xLrgxkTFlgvYWJRW/TN6kvOyZhCgEAyIIQ2WFthIPzYwJbrVkKldHIfxyxZdlAaL4HaVcp DHnO57hsHbvg0aacNhmxK2VkUhax27Rc2Jl1NNpCGUJNcmmFw9XukCn6G2tP8eZZ6ZTOW68OiPF wPW/eB21y1mFa8CP4kXQPHuPPy77RLx3UswOMpYdPhvzZD4dYsCBVnTBFYr3ZdD6+kFJMRE5fII 8G1eQ2BbBZyhxr/TleLh8MGjjYH9mC9g27dZ7Wfms2y5thH0c0bhyYAW390TAQq7DGsNQmSuiCh suf9YEpWUTCcDoJZrvOngWRV6dCPk4KLyjNHSaWoVeVcTzs7mf6u9V3caF9/5j9yB4GMGxrnAO/ rVeZt73bEmgtG0A== 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 96e48a2e6116..b3578f28871a 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 05cf869ac090..6dd14a72526f 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -163,8 +163,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CD8C53C10B9; Tue, 9 Jun 2026 19:14:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032449; cv=none; b=o6nFjUYd5LhCHQdn2gDj2eKyq/5NznpGw0CTl5c2AyHUOyLHnCpKCk/gQQFA22UdChVsXm1GvNE85eD/y+rK3yH470n0t7wm1NaEE9ecwY0ilxPUzyYKdSjzH2qJHccRSy0DbI9cmq9T1IbKMfe/1hDhTV7EpSB84wxZsnIP8HE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032449; c=relaxed/simple; bh=DdV/NLJR9VTmPqZyVaI6uzyHec586erqdAzY/0l2Nn8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=MD6V9IO2k+ot5KBaDik0mzhBG+40oc4Tn4ZuxCoZgxhrXJ1DJwj6edYDqvabPdAgnUrFHrAkZ0lrR+Ct6che0+ya462Emmm7WmqVLdqy8bSq4bNTizCzol9Cxt9v1YcBrZOd612aNDPQzCyWYun6/SzYokGpnIhFYnrDYb0gEU0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Zjlyuyx8; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Zjlyuyx8" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4EB8C1F00893; Tue, 9 Jun 2026 19:14:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032448; bh=x2PxxA3MSPDESp6PPRlNFBFe70vLv/38gregfZpEU1k=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=Zjlyuyx8bYL/2Qw4pNRLmAEHI0GjtXrDc1K8a2ffnCDtsJKoZFEUWFTPw5PIPVTCX 5+Dt7qmtIZ183zEAPDo/azahSlF81GVi6ROKQcaRe0nw3b2a+2p9gjQqbjky35oFLO XD7KRmiIGvuYUSIN/vxnscSb0sVUIo0MtsZQ/BVEFxwxHoNvSSQx+1t3iHQ7wTlw1n LktGX1Rw5Fu9Firh01MDlOSQuf4Pgng5clcQPr37BPO/TSXsABkdM/VzMgjrxRhf3S E6m0gNa8NKgkt4f6tNYgeDngM246bQ9W/ZRLYeCZ5wTaSqnO9EKfafJ25tHWnTfAtj GuAEmxBHcXrFA== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:21 +0200 Subject: [PATCH v2 42/83] 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: <20260609-rnull-v6-19-rc5-send-v2-42-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=2656; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=DdV/NLJR9VTmPqZyVaI6uzyHec586erqdAzY/0l2Nn8=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTaruS+IOkE14zFQtyt0+NF5/WYzKnu8ZDWM tiGv7qIRIaJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk2gAKCRD6UCkIqsW9 0B7yD/wOR8PPlgBri1+hwHQY+XDM3u3f1pRkc8cj7TuKaXgHixWBdzbLkqsIc5cdFkOgiBSAc5N pfnFEgZ7b267KmCIrTUViibhwJ9jmlH2J5D/bdZlWKKLLrBhoxoqh2OEiMO0feNI76lzcNmps/C Hqcmlql6GDiMD2qzQbpOmwMTv7VTPB7y5F3rUIE82SFcSk723PdvakjbKmpLTXp8lScT5QRnEuY 06m5hQPXoRHNuNnrIZcVB1k/8A2DkXoy8xvRcmh5/aP35iEe483vHo02SMTutUEyaxQSsANxbmW c/dIWCHfMHesodP5gH3QGt7HTnKYnzoJYfnscdsnepM0oGXrQIoW0tQnLVzWLNlnMN/Ekj5ti/b Jov7ZRf577FRhmu+xXe/8oT0boTu1rRoj6DePhfwAoXOuTzZFH8/xzdckSiNEy/dQOa0r8zRYOf SSfMFSQylDoWRPmBjRXMPATHYLcf5Z2CxZj5w8T6dU+uahBumZbiIKRlgzOQcjLCAKOajj5V1+Y uL8mYOZmcIBJilixB9XJhB7x+Wp1RZJtJlNsvLs55CH6c68XWISE8iMMeptSBBNCWram6eBqQr0 ugbcSrBoHnaNa3lniFssKZqkH/8FXV9JljrSy/X4gZCoAWLR+U+vUpBSghlDSMJiVh9xOSlocNB hx5UUvhdKFemNiw== 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 | 13 ++++++++----- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index bb8c4df08218..6ceba23a4d3e 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::{ @@ -595,7 +596,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 e89eb394001f..503623267b19 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -64,7 +64,7 @@ //! ```rust //! use kernel::{ //! alloc::NumaNode, -//! 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 01917ef213d1..b9a2bf6592b3 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -6,10 +6,13 @@ =20 use crate::{ bindings, - block::mq::{ - request::RequestDataWrapper, - IdleRequest, - Request, // + block::{ + error::BlkResult, + mq::{ + request::RequestDataWrapper, + IdleRequest, + Request, // + }, }, error::{ from_result, @@ -82,7 +85,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 27B8248B389; Tue, 9 Jun 2026 19:10:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032224; cv=none; b=WpmR6ReC7s2RD2UK2UKmR43DFqxZzNFAZ8yhe/LkRlCt2SoxDSG6Qep4Fht5kKTOHufblW7K8rMkJd0AhA/EGiWrT69Fu2Pih3mIIZH8ag4IUCaZ/p2Luoaodv9/mSgix2+k9j1xab5vnkMgTpMBRUGIlT2sOyCUmyaMm256gFY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032224; c=relaxed/simple; bh=QdUIpX955IjWtpvoketZafjzxxc6bEWigg7uBvUHVaM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Lg/3wQiAhQ1dmQtKj4+K9YsSXYWqCxByttnNs4gfhlOt2gUNYzrHDJy+uT8jVmfcqAoslO0PNzxgya8ZDitzy9gLHNGnQ7ikPa1Jed2Y+dqX+cmdVY37qhygtoruMq88NoVQsu98GENKy6glvwdJ7vwie1S5lRwacFJZNY7Y9hY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=I4EdEHZ+; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="I4EdEHZ+" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 86C511F00893; Tue, 9 Jun 2026 19:10:17 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032222; bh=iHY5maGdy9Y0fuMpxRd+GJYueXrmulzdgPUuxeFdpc4=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=I4EdEHZ+4mOmI52HAVmdSd2wciDtFR0/Q+5iTwm0ly5YvhTwazYPxYmIXCZDfXVgG /yWbJXOrQ8VVEfrF56ldHexNv1vKEoGR/BDVNXFAoRcdUFs0hIg33NJWhSorfpLJhj KiHE791tF/dKvrwu49a0YvcEPMDV0g1LLZ69Rgy4nxvZzFtmKsH/BG/5DxmOk8zyjL YtlEV63v70Nv13rR2eBVB5qP8EKyqc9II4cD/gxONQFburypTp7oMl+KJx/nBgyLoP Gcpx/6A/AnoCjUyYM4QxsG50NUdBe46PL8in5Olq8ABlsP/rKX+6L//CUhJwFeu0rI k4QpjPTh30YVg== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:22 +0200 Subject: [PATCH v2 43/83] 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: <20260609-rnull-v6-19-rc5-send-v2-43-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1051; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=QdUIpX955IjWtpvoketZafjzxxc6bEWigg7uBvUHVaM=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTbpS7sC8gUW2Adx4HtZ/fpiNOjSfvNGjo6B NBvkfxPGy6JAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk2wAKCRD6UCkIqsW9 0GkKD/4jKQPzH7UvTzlymlTkzO6nFr0uD+r+lxJMwBCVa3UJ7O/Hiw74mJ5raWq/ELYiqGwZ17q BK1XKroXvE7lo3/W2fjw41bqrmg1v56WWQQfNlCe/w5rXtisEs7WSUsxgwCTsH8MlMbBxAYiVZG BIgAp9fMcFefidQZ1rioOGGQK7fWOKhtfN5V26APj4FSH2klG8pNPYv8Qwnn1QRljsT6htLvVtA 8agBETAJEBn2/gcReU6RDaUgyv5WTdYOjApi8mzoW0qVVEbCIpAAAAbKOQS2rWMdXdg9kq5mYnG Tw2PhohLdqYMM8qX1sSrGUawgIxfRckb+2DrR21SUtQ1lCAu64PxoOsK1xd79HwdUVxCeO4FJcv uby53gHiQsCDK5U8guolyHcVWzSBlfgq21+1KxTxw+IlDlBA3knAIa5qrr9P3+5XI6mugMLJtyX qtx1NZcFEUEgzSOv8x8/9oKtpIIaV5eiaMsrd2jP9evv3IeFMKt6Tfd9297ONwbX8A6l5J+WgHB FDG2ITMtxPEO99CIBhv3hFdzJeDlUZfp8VC5npQDoZrc4UyRyEVrzgE/1AWgs+JCqqZw2PPQdgO 6KXUT/c+OsYDx+LwGjkGzaUQk9zkDIJyJQMTzczdk2TAJAZyLPhBaQec/R0MrXSL/IxGvyOBlzr Le8kjM8gfkCjTvQ== 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 6ba8d88f63a9..49ce5ac4774d 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -260,6 +260,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4384C3E44FD; Tue, 9 Jun 2026 19:15:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032555; cv=none; b=rNp9LxwSouxzsUZkFNLyrZ65v4/+8jZmp3v8QBDy1ciwZM7gSbqxqHx+ssPCc58nZ7H96XCGG4bzhxplBBBFZU7G2+3lBUyXCoxqxTGPtV8z1R1H4qFCUBC0+BJuWGeckd00XEzEr/IfM/KU0mKo3q430TQd5QDYKVH6S/BonkE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032555; c=relaxed/simple; bh=pjTJV6lpFGJdbnqxGVPCelvpzcvZdwrgNcXyn+EPT0s=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=bYcdYmm/NxVKSHygFlCrq9mCPCi74qHgQYbzSkIssYQzY7cqRvol7qH9J9zsi3oCkbYTmUJyDLWzacef5GI0orX9Uet4ZnbnSAa6nzrnMZaCr64MfzUaUWj0ng17U1JU59HQ5iETlD7gulx8jO2juYvTI3LEF0NwwxKJZT2T8Wo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=PJRuMeub; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="PJRuMeub" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 67BD01F00893; Tue, 9 Jun 2026 19:15:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032554; bh=XwkzM7VKd2QXL9hc4jVTBaLQ2ofiUVQa/wzCehzlLfg=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=PJRuMeubNxh1PQSzxL1DyB41AIrvdek8nDKPtSKhm5o0NviyKSTiVrhDLu3iguEBd 0xplh6V9VIdWerS8y8DtTZmmThWVU95nzC9QpGylXKU46KuVuhXlvX1CkzsOu+JoCe MjbNF4bZ8B7n25zVRSn0JlV/mgNqTLFHn0oOt9AFFFqYQwcGNwNUe07KPJxHyFvlxZ Uu7ZwCkEGreSa0hJGaoJKGkcUPY8MxZzIjviUr2EHCVUDXUEgt3mRmbGBZP/66UZ2W OV/8H3/4WZpxm6Pd3jfQqPZJXa4soGpN8YNs6Y1td/O2Drf+0BrX00P4V/lDtDuomn uR2Eh6VVq7z5g== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:23 +0200 Subject: [PATCH v2 44/83] 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: <20260609-rnull-v6-19-rc5-send-v2-44-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=10642; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=pjTJV6lpFGJdbnqxGVPCelvpzcvZdwrgNcXyn+EPT0s=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTc6+ODZ65AqoAf371RvHz/OnyK5XGNwDbnU VCqT77xgpuJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk3AAKCRD6UCkIqsW9 0JWaD/9mQPWw4JE9i9ltAG5BKZTUckOkIUd7RZ4WGZ4lMe6tJZhAq3cT8nktOiW9M0bpzIgKIxn zvuvxpqbwe8dQplaQYysdwCAGcn+HP0sBEBtscs8KVgMMokJ9nBgmFvYRs6YJ8Tcb+p19D2kyWd LTX2g/vNcZpNfuMKQXgkE28gwts3AruCAUXNY9LdMN1JGBp0gCNdVCxArt9xgwk0bXStMYm8e4/ PvKOSHSdDspS/YiNaqzcXvFuDqiEos9gX6gOGqIo3jw963sZZH3IHmpaQD7MMbUHnfOJqPVrKYh vk/0QP3ErfWjqJA2N1BmOVmoOLN0zmzfxNI/tKk2kBrn9mxR7XBVPZA5cJaoVCR2HC9KasgmsOP rXaf/U4Mmm5Qxl3koLOoY/6DIXTR6RL8YYB1Hdp0hlI8KjvMoAyVOJ2EmMMq6QjV1hTdSQns+wj oMsdvt+nwsXit4Nldq6dvzXVHZYT+5cVPCI8s/B/W1yDFENImrG+c9LwvAjh7lbkpIIbR7jcDiy /+u6dLn7ukZ4PLM7XGvfLq5xcPqQterwMaq/Egacm65c5lpeGPrRhcSDdm6YgtIgr57sc2JUkkH ytSxT7XAWiHYky9m5qKgtQhOr5Z5/7ST297dsw9YFwT2lLyok596jljmWq8MHMJa83kwIaqKOUA GUJBbFBDOVZunyQ== 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 4df0b748596a..59217d75f46b 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -104,6 +104,7 @@ fn make_group( badblocks_once: 13, badblocks_partial_io: 14, cache_size_mib: 15, + mbps: 16, ], }; =20 @@ -135,6 +136,7 @@ fn make_group( GFP_KERNEL )?, cache_size_mib: 0, + mbps: 0, }), }), core::iter::empty(), @@ -209,6 +211,7 @@ struct DeviceConfigInner { bad_blocks_partial_io: bool, cache_size_mib: u64, disk_storage: Arc, + mbps: u32, } =20 #[vtable] @@ -248,6 +251,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 { @@ -259,7 +263,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); @@ -417,3 +420,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 6ceba23a4d3e..1dda8d717b95 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,25 +38,32 @@ Result, // }, ffi, + impl_has_hr_timer, memalloc_scope, 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, @@ -127,6 +135,10 @@ default: false, description: "No IO scheduler", }, + mbps: u32 { + default: 0, + description: "Max bandwidth in MiB/s. 0 means no limit.", + }, }, } =20 @@ -172,6 +184,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.val= ue()) * 2u64.pow(20), })?; disks.push(disk, GFP_KERNEL)?; } @@ -202,6 +215,7 @@ struct NullBlkOptions<'a> { bad_blocks_once: bool, bad_blocks_partial_io: bool, storage: Arc, + bandwidth_limit: u64, } =20 #[pin_data] @@ -214,9 +228,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, @@ -234,6 +257,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(); @@ -268,7 +292,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, @@ -278,6 +302,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, )?; @@ -294,7 +323,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 { @@ -522,6 +554,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>, } @@ -529,7 +591,7 @@ struct HwQueueContext { #[pin_data] struct Pdu { #[pin] - timer: kernel::time::hrtimer::HrTimer, + timer: HrTimer, error: Atomic, } =20 @@ -578,14 +640,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), }) } @@ -593,14 +655,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 { memalloc_scope!(let _noio: NoIo); @@ -623,7 +710,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BBDC24BCAA2; Tue, 9 Jun 2026 19:10:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032229; cv=none; b=QJC7gugGXpX2iWqFAcQ6QwIrfA/ySwCiuXTQLI5XcVGHu0P5HUvlvyDC5WZpGJ0M2WwrrvPZZgJk75w2HFJfHtsRC4UuLjvWOI0GYalxsn9wPlhJf90oU7dfdOIm2TBhCjNxnt2hl1851oZw2wMt3cUcHZ3UkO5RlyhgMoMsRYc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032229; c=relaxed/simple; bh=YZ/AfN8Kndq1dH1klIaGEGbAb5qaoYZQKEwqGVYQjkM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=p/p0NCzPPFmjObgl07b9k3H039+oRzadJWuVTCezg5Rinjj7rasGFdLDIWupy4tl0sf9FWwRLj0Xpk2BmNwsKkroOupQQNaHplPl3KFi60up3UlTEtY3+ciNhLI+oPWkL5ZkD1NSnkQY6gUhIe4/VPJj1hqAZymG3Px6RROyqK8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=iyVfnxIT; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="iyVfnxIT" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 68B611F00898; Tue, 9 Jun 2026 19:10:23 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032228; bh=a/0/Vaq6P+ehn9fD0MH6e+Cla87NJ0pjAGFS/FD6Uz0=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=iyVfnxITOTX17YdbikJ+OjiLKUylrJ+piLYQesB6V3/nNJ0TcR04Wh1F7wRvaDNTP pbnXpydkDDpZm7HBeQKMVRrpWxoueQTtoYjkddxqV4k8yy0l5xZXuaVzvwaiJhTWya vvokoPwjXD2EW+F/buj61BNjGL7VQ/XR2xMfWZNMTRKikwaHeF1jKNPBOJvChU1KuI hhIooOt9bYl3ZbzXzBu+Iecm7FTafUB8N1sIleSSyQIPsBI1V9aV0tcJHdwod3kEQd BOxPGksCSQqXwLLUwL87RVDIdNc/sV5JoEIj/orDNnRw8sGq9T9AHMbEhyxRB6/jrZ /ZCQavcFjWhug== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:24 +0200 Subject: [PATCH v2 45/83] 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: <20260609-rnull-v6-19-rc5-send-v2-45-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=3831; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=YZ/AfN8Kndq1dH1klIaGEGbAb5qaoYZQKEwqGVYQjkM=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTdp0x0IRIof4+hYdVikG2ZXkHe5WzQAFj+a fipjeP29fKJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk3QAKCRD6UCkIqsW9 0AeeD/9aLlgjZ/YxFTP6ATkG1QnKkssNiKjananf2paDujmui75oDOJ3XtOQ2+e6g0zWLX2kcr1 UpmZ8N2wd0jDgTVnsWobBsGfGVPpbRt2UH42VQQTZARKg9PF7RxBZPOJdAFRdervRPIVVRjhd9r O0JF5jOwKslo9hb8uu/Cdj1rL8PMXEx6TMPUE5OwFjoR4wP21AI9vAVPvKOkdZEf1Zec5+nboXb 4JdPTKYAdSIFEalOguSL3KTPMljFp0FKDBEAcSXvVudk2rrcSpidVhuek7Knoe1uojyFFVaj8jJ woGhhNWlnntQyVcGCdbkV+pDgfhEqrDF6Q8g7d8Nn1P1VbDWcMgt5ws83KUyS4Jgwg4YUUUH2gI bWlfm+OnGjNqTbd0o+h0gsKMXikkvLj5T6An3TeVaw7OWSLl8GuQVe+Ely/KppzmhGZgdHQHKSR BKYcTSWemqX+aFp5pj3IAvmhaqJD4xfZyoIuUJKV1Rn22pPecj0AcCpE70s6t3DXDsiYv/1Zptj Q8SXFlAqzvqpOk5zy5sXxcyY9Y0VsKWJrRbekkMZ9tmcefsc/C+snQhnBKpCbESQ4kqRULDn+tz yd9L2IkSk22YqTmmQDGEeTjDxLy+C6in/VrSNgQb5mtkjskynCMyCZvg6vGTPkDaBGdPyjqct7N Jjs6wnX/pYuGKZQ== 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 59217d75f46b..5e6bcf9d31d8 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -69,7 +69,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()) } @@ -105,6 +105,7 @@ fn make_group( badblocks_partial_io: 14, cache_size_mib: 15, mbps: 16, + blocking: 17, ], }; =20 @@ -137,6 +138,7 @@ fn make_group( )?, cache_size_mib: 0, mbps: 0, + blocking: false, }), }), core::iter::empty(), @@ -212,6 +214,7 @@ struct DeviceConfigInner { cache_size_mib: u64, disk_storage: Arc, mbps: u32, + blocking: bool, } =20 #[vtable] @@ -252,6 +255,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 { @@ -422,3 +426,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 1dda8d717b95..181fce551a91 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -139,6 +139,10 @@ default: 0, description: "Max bandwidth in MiB/s. 0 means no limit.", }, + blocking: bool { + default: false, + description: "Register as a blocking blk-mq driver device", + }, }, } =20 @@ -185,6 +189,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.val= ue()) * 2u64.pow(20), + blocking: module_parameters::blocking.value(), })?; disks.push(disk, GFP_KERNEL)?; } @@ -216,6 +221,7 @@ struct NullBlkOptions<'a> { bad_blocks_partial_io: bool, storage: Arc, bandwidth_limit: u64, + blocking: bool, } =20 #[pin_data] @@ -258,11 +264,12 @@ 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(); =20 - if memory_backed { + if blocking || memory_backed { flags |=3D mq::tag_set::Flag::Blocking; } =20 --=20 2.51.2 From nobody Fri Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1E57B4EA37F; Tue, 9 Jun 2026 19:13:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032386; cv=none; b=iD01UGiYjlGamaIIQFVV93EE9tS4WR/008Gk2QDEgAEFaXu5eIfWR1dFy5tvHWVUk18nqePQkpO0K9AMSLLVcw50+XXX55sYujHFSKiEtO1y4eWpUuTCfnJv58LvdXd5HSo3/oYIyAv7ZgccU6C59KZy4IQ8eTfare+Byc6mOnQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032386; c=relaxed/simple; bh=OyYo3h9FH7tddSUSU2yGqcG1rcOMZa8+hV0gLvVXT30=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=djSVfR2YmnToY8L5g2oFUucOu/aT/ToAO9PNv9fpOjB5EdhhiBqfBZlg8PAeOAYX9DflXaXfioRCUEU4r5BcQZ1e2RIvK8YYhHEec+PeM1TXjFNNQ7bt4jIDArMzrLFwerAqffjNpSWKtbfs5yG0HJlWwt4OfdoyROo+dWlqsJk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=TFpYDTDB; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="TFpYDTDB" Received: by smtp.kernel.org (Postfix) with ESMTPSA id EA1051F00898; Tue, 9 Jun 2026 19:12:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032384; bh=JnD8AYBlSFJHV8ifo4XwIzlj5eOON9Q0z1EH0iWmsCE=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=TFpYDTDBuIAzpa+0TjHG5dMSItuqqqLZhFGeQ34qoDBgGbY2MoHw2glhTaoX81oCm 9z0VXjifWppnmUn0b/0WnuVlIJ/msxO6TdXR2BXu0PffY8j17xDH0BLMyNEONtfaa5 0b0MveTNIZzwhgPjhPSz024RZcIvQd7gj+d/h3U7rX+T2oT+TRUe6R9PPvaDucAlo3 dhgUYoDWmMPr6lEelcLBQi9AtCdCLExLm7X/WjKemb0TbVpLy6lOWzE9nDibDxjxrJ rKsjZGaDcoo4HrDKmvyXeaniKekTP9LHlqPsMQ7A5+RAgenOJURmR0Y/El9KRA68M3 nSA6T58L5V6UA== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:25 +0200 Subject: [PATCH v2 46/83] 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: <20260609-rnull-v6-19-rc5-send-v2-46-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=13841; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=OyYo3h9FH7tddSUSU2yGqcG1rcOMZa8+hV0gLvVXT30=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTeznS9taKg4dSs5gVp6USJA2+jTJFwZidH4 aBNBag7rUaJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk3gAKCRD6UCkIqsW9 0HtxEACstjqG92HymGqbWHGNy9sEvKNUzNlbVl7NQKWv5rxeAIN2/U3xOvzo9HHjRbXBbQrz3Hq /ygvkPv1e/v6mOOFMSHLLL3rwlfgGXZ1JSZykGsCYNc+ggl6+cGbuOetVXEt4E0LHzqNLBi1/IY 4E3qoazdTGvD7Ms4eycgZ/Q9WpPM1SzmnGZz6iucfO0/zumZhVEWty6ofpwVDXWA2pMjJNFymCx lVC7/sgmJfQzvMMLAskuJwCBQ57FncfWBQKlyUT8TWJdmtQCVjyNFRH0D/pafBn7M0nYTwi217H z45FxqGkhzSe0AOZJgfmWf4yE3DgJ95LqlxaG3pXKulcf7m2L9LehCoEfOKVVF0Wvb55/pAiPu1 ofTBQ2sJKjf8pNdm8/R3UVcO44WJDbll32vo6/h4tIpH/P4STXAW/SnZPN23Q2T4M945He/g21E d1hK4m6b4ljc6hjIh92kokd0Xn2nVnajcqZpNhCxSG98at29luZcwruLAag6M2KtV+1uimnIKP2 mAiMahiFIBKe+4ZW9KIKuScsxOkaQ6nwg6XckyaCxi16iIC6CJ7HQ7Ix5xu/ndi9o/2pcp5kc4+ JS4G2uI7K7ZdKlm2BQq93oxC4S2WvjB28W1v9h8A0C+6Mlafa8QUaVhUF4EOvUXAWSg2+xy7v/7 GS9TiRF96HEb1pg== 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 | 44 +++++++++---- drivers/block/rnull/rnull.rs | 136 +++++++++++++++++++++++++-----------= ---- rust/kernel/block/mq/tag_set.rs | 18 ++++++ 3 files changed, 136 insertions(+), 62 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 5e6bcf9d31d8..a84854e7c358 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -10,9 +10,12 @@ bindings, block::{ badblocks::BadBlocks, - mq::gen_disk::{ - GenDisk, - GenDiskBuilder, // + mq::{ + gen_disk::{ + GenDisk, + GenDiskBuilder, // + }, + TagSet, // }, // }, configfs::{ @@ -45,7 +48,9 @@ =20 mod macros; =20 -pub(crate) fn subsystem() -> impl PinInit, Error> { +pub(crate) fn subsystem( + shared_tag_set: Arc>, +) -> impl PinInit, Error> { let item_type =3D configfs_attrs! { container: configfs::Subsystem, data: Config, @@ -55,11 +60,17 @@ pub(crate) fn subsystem() -> impl PinInit, E ], }; =20 - kernel::configfs::Subsystem::new(c"rnull", item_type, try_pin_init!(Co= nfig {})) + kernel::configfs::Subsystem::new( + c"rnull", + item_type, + try_pin_init!(Config { shared_tag_set }), + ) } =20 #[pin_data] -pub(crate) struct Config {} +pub(crate) struct Config { + shared_tag_set: Arc>, +} =20 #[vtable] impl AttributeOperations<0> for Config { @@ -69,7 +80,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()) } @@ -106,6 +117,7 @@ fn make_group( cache_size_mib: 15, mbps: 16, blocking: 17, + shared_tags: 18, ], }; =20 @@ -139,6 +151,8 @@ fn make_group( cache_size_mib: 0, mbps: 0, blocking: false, + shared_tags: false, + shared_tag_set: self.shared_tag_set.clone(), }), }), core::iter::empty(), @@ -215,6 +229,8 @@ struct DeviceConfigInner { disk_storage: Arc, mbps: u32, blocking: bool, + shared_tags: bool, + shared_tag_set: Arc>, } =20 #[vtable] @@ -245,17 +261,20 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { 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, - home_node: guard.home_node, discard: guard.discard, - 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, storage: guard.disk_storage.clone(), bandwidth_limit: u64::from(guard.mbps) * 2u64.pow(20), - blocking: guard.blocking, + shared_tag_set: guard.shared_tags.then(|| guard.shared_tag= _set.clone()), + tag_set: crate::TagSetOptions { + submit_queues: guard.submit_queues, + home_node: guard.home_node, + blocking: guard.blocking, + memory_backed: guard.memory_backed, + no_sched: guard.no_sched, + }, })?); guard.powered =3D true; } else if guard.powered && !power_op { @@ -427,3 +446,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 181fce551a91..bcf6a85f1cbc 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -143,6 +143,10 @@ default: false, description: "Register as a blocking blk-mq driver device", }, + shared_tags: bool { + default: false, + description: "Share tag set between devices for blk-mq", + }, }, } =20 @@ -158,19 +162,30 @@ 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(); + pin_init::pin_init_scope(move || -> Result<_, Error> { + let submit_queues =3D if module_parameters::use_per_node_hctx.= value() { + kernel::numa::num_online_nodes() + } else { + module_parameters::submit_queues.value() + }; + let home_node =3D module_parameters::home_node.value(); + let blocking =3D module_parameters::blocking.value(); + let memory_backed =3D module_parameters::memory_backed.value(); + let no_sched =3D module_parameters::no_sched.value(); + + let shared_tag_set =3D NullBlkDevice::build_tag_set(TagSetOpti= ons { + submit_queues, + home_node, + blocking, + memory_backed, + no_sched, + })?; =20 - let defer_init =3D move || -> Result<_, Error> { + let mut disks =3D KVec::new(); let completion_time: i64 =3D module_parameters::completion_nse= c.value().try_into()?; 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_h= ctx.value() { - kernel::numa::num_online_nodes() - } else { - module_parameters::submit_queues.value() - }; - let block_size =3D module_parameters::bs.value(); let disk =3D NullBlkDevice::new(NullBlkOptions { name: &name, @@ -179,27 +194,30 @@ fn init(_module: &'static ThisModule) -> impl PinInit= { capacity_mib: module_parameters::gb.value() * 1024, irq_mode: module_parameters::irqmode.value().try_into(= )?, completion_time: Delta::from_nanos(completion_time), - memory_backed: module_parameters::memory_backed.value(= ), - submit_queues, - home_node: module_parameters::home_node.value(), discard: module_parameters::discard.value(), - no_sched: module_parameters::no_sched.value(), 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)?, bandwidth_limit: u64::from(module_parameters::mbps.val= ue()) * 2u64.pow(20), - blocking: module_parameters::blocking.value(), + shared_tag_set: module_parameters::shared_tags + .value() + .then(|| shared_tag_set.clone()), + tag_set: TagSetOptions { + submit_queues, + home_node, + blocking, + memory_backed, + no_sched, + }, })?; disks.push(disk, GFP_KERNEL)?; } =20 - Ok(disks) - }; - - try_pin_init!(Self { - configfs_subsystem <- configfs::subsystem(), - param_disks <- new_mutex!(defer_init()?), + Ok(try_pin_init!(Self { + configfs_subsystem <- configfs::subsystem(shared_tag_set), + param_disks <- new_mutex!(disks), + })) }) } } @@ -211,17 +229,14 @@ struct NullBlkOptions<'a> { capacity_mib: u64, irq_mode: IRQMode, completion_time: Delta, - memory_backed: bool, - submit_queues: u32, - home_node: i32, discard: bool, - no_sched: bool, bad_blocks: Arc, bad_blocks_once: bool, bad_blocks_partial_io: bool, storage: Arc, bandwidth_limit: u64, - blocking: bool, + shared_tag_set: Option>>, + tag_set: TagSetOptions, } =20 #[pin_data] @@ -243,9 +258,50 @@ struct NullBlkDevice { disk: SetOnce>>>, } =20 +struct TagSetOptions { + submit_queues: u32, + home_node: i32, + blocking: bool, + memory_backed: bool, + no_sched: bool, +} + impl NullBlkDevice { const BANDWIDTH_TIMER_INTERVAL: Delta =3D Delta::from_millis(20); =20 + fn build_tag_set(options: TagSetOptions) -> Result>> { + let TagSetOptions { + submit_queues, + home_node, + blocking, + memory_backed, + no_sched, + } =3D options; + + if home_node > kernel::numa::num_online_nodes().try_into()? { + return Err(code::EINVAL); + } + + let numa_node =3D if home_node =3D=3D -1 { + kernel::alloc::NumaNode::NO_NODE + } else { + kernel::alloc::NumaNode::new(home_node)? + }; + + let mut flags =3D mq::tag_set::Flags::default(); + if blocking || memory_backed { + flags |=3D mq::tag_set::Flag::Blocking; + } + if no_sched { + flags |=3D mq::tag_set::Flag::NoDefaultScheduler; + } + + Arc::pin_init( + TagSet::new(submit_queues, (), 256, 1, numa_node, flags), + GFP_KERNEL, + ) + } + fn new(options: NullBlkOptions<'_>) -> Result>> { let NullBlkOptions { name, @@ -254,37 +310,22 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { capacity_mib, irq_mode, completion_time, - memory_backed, - submit_queues, - home_node, discard, - no_sched, bad_blocks, bad_blocks_once, bad_blocks_partial_io, storage, bandwidth_limit, - blocking, + shared_tag_set, + tag_set, } =3D options; =20 - let mut flags =3D mq::tag_set::Flags::default(); + let memory_backed =3D tag_set.memory_backed; =20 - if blocking || memory_backed { - flags |=3D mq::tag_set::Flag::Blocking; - } - - if no_sched { - flags |=3D mq::tag_set::Flag::NoDefaultScheduler; - } - - if home_node > kernel::numa::num_online_nodes().try_into()? { - return Err(code::EINVAL); - } - - let numa_node =3D if home_node =3D=3D -1 { - kernel::alloc::NumaNode::NO_NODE + let tagset =3D if let Some(shared) =3D shared_tag_set { + shared } else { - kernel::alloc::NumaNode::new(home_node)? + Self::build_tag_set(tag_set)? }; =20 let capacity_sectors =3D capacity_mib << (20 - block::SECTOR_SHIFT= ); @@ -294,11 +335,6 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { return Err(code::EINVAL); } =20 - let tagset =3D Arc::pin_init( - TagSet::new(submit_queues, (), 256, 1, numa_node, flags), - GFP_KERNEL, - )?; - let queue_data =3D Arc::try_pin_init( try_pin_init!(Self { storage, diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set= .rs index bfb8f8af4ee1..5359e60fb5a5 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -124,3 +124,21 @@ fn drop(self: Pin<&mut Self>) { unsafe { T::TagSetData::from_foreign(tagset_data) }; } } + +// SAFETY: It is safe to share references to `TagSet` across thread bounda= ries as long as +// `TagSetData` is `Sync`. +unsafe impl Sync for TagSet +where + T: Operations, + T::TagSetData: Sync, +{ +} + +// SAFETY: It is safe to transfer ownership of `TagSet` across thread boun= daries if the associated +// private data is `Send` (it will be dropped with the `TagSet`). +unsafe impl Send for TagSet +where + T: Operations, + T::TagSetData: Send, +{ +} --=20 2.51.2 From nobody Fri Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C725040C22B; Tue, 9 Jun 2026 19:14:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032483; cv=none; b=b8ppvdZH9isyKUwE1zdsNnEostXKKHiDUswjm++AIX/3PPhh+wDl6GOYTyK8+6s4su3aDEQu27anjEw7wrtBa5bbM2d2QQ3Ta8ctHp8nOPW42ABz9s68q1As9e8/Zd63ZtyWHJlYqp6gLhJ2j3cHjpED/vKCFJ0rFHb7XRflw9E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032483; c=relaxed/simple; bh=RiK2MFDfZueTIoC29KqxTo1erC5StZym/slB3abdziY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Clg6hyz0j70EdU6iLJykQHXgocOzy5sBSl2LcvZibiGp+30qSCOZNZhf9dIDtS6wmLPHg0ylDawai2IKALNu+8vZNkHerlYQYpM/mZ5H+zrkznlFt35utodd6nhTQwsW3bM9vfCYZqb3zuOV1bhtLkt4xhcHSdp4LCx33mdquxk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=m6F0lnJA; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="m6F0lnJA" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E1EC61F00893; Tue, 9 Jun 2026 19:14:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032482; bh=yh/dWdgKvT0zkP6Uc7e51/m9fxvSh52AeREuWUz3HD0=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=m6F0lnJAGS/NLRHer47scYdfbR7jhR0fJPP8y9mzicldIup+EVOxTgoiHv3hOReei iNCg0ypYBdJTCsIg5js6JoOQE0YWIezzBCRQM2w95OIieOipKY1xsA81zzAaTzH81Z iJW1UEHtzD7v1ZHHjKK7py8KD9lHnDvUiNcGOJQVnUsCPdLqAWoCbjNbDBYcmMEcvC Yy5rAilaH1QQM7iYFNfMZoTsRIld6dZXm5eNZv74k+fZmt+bV0hKPbpQ5bfVnbX+4L /j+UVHQIR2nsWsMHO+F0slrgeSE5y0aOfn+bStGbRixytYc/7CudYimCUwlmIbI1gp FVTSpmMZ6ZNWg== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:26 +0200 Subject: [PATCH v2 47/83] 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: <20260609-rnull-v6-19-rc5-send-v2-47-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4312; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=RiK2MFDfZueTIoC29KqxTo1erC5StZym/slB3abdziY=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTfmemXeroNyNXumJBaGxIPH3vPurRUz3Bt/ aFVV1AUQoiJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk3wAKCRD6UCkIqsW9 0OVPEAC1vgGJz1ZISUXdjGr19xkHpGJq9tLAlpJ+QS1EMUsAC+PCYIi91hDyDYZIJ5Xi/ppekge 4RvZDlVOQTvD8UzQMx14InQPciiuSo1izTTyv8VCBoaNLpL6/stmmZpRwqOoGDwZcAcftl9e01r oB157JRV7quK8BuYsE7TpkDqZpw0CP6tqrRG+iCCAdQ0/nwmIiHLQnQfJsqSl3+uz0KbzimP2fX txU7vr9Ua1zV9trOfeq9I6td5NXKxdiCHqf2UUMO3qNG8/d+E/lNFYxbPj+v7Z4v/+Mo0ih+uF0 eL2C1Hdq2DngCceeMc/FytdwQcs9PUr9EF4bqOHdr0dyavntauC335TpOPjuWwP954JGl2nK8i3 PE3kg1gIq9TDzIowvz7aN/a4n9PMCeMcyDPUKlf0M4yX/BsDSfUIjyXeEaP52idAmtxPFUb/JIw HeXak9dimV1Yp56H1JQ8c86LJq0XrhLiGZxQeIW/69R0UCy4HqP7PN+XwP2K4fk0ake1G4djGi1 TWdTJaB/02NzQyC1ZFR2fkhVf2fVZf/x5hYLA25MEskOF5JVY4YerRa0x3iAV0l8BF94zYDDFQ1 QWZgXeq1/j/N/wTiyHPJSNSfefCKl40g+4pCjN8n30UEHVvYVeG47NJwvrwn4v6Ag6nT5EjTpde cInJmkYZ4C2fsWw== 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 | 11 ++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index a84854e7c358..2dfc87dff66a 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -118,6 +118,7 @@ fn make_group( mbps: 16, blocking: 17, shared_tags: 18, + hw_queue_depth: 19 ], }; =20 @@ -153,6 +154,7 @@ fn make_group( blocking: false, shared_tags: false, shared_tag_set: self.shared_tag_set.clone(), + hw_queue_depth: 64, }), }), core::iter::empty(), @@ -231,6 +233,7 @@ struct DeviceConfigInner { blocking: bool, shared_tags: bool, shared_tag_set: Arc>, + hw_queue_depth: u32, } =20 #[vtable] @@ -274,6 +277,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { blocking: guard.blocking, memory_backed: guard.memory_backed, no_sched: guard.no_sched, + hw_queue_depth: guard.hw_queue_depth, }, })?); guard.powered =3D true; @@ -447,3 +451,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 bcf6a85f1cbc..491979daa50e 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -147,6 +147,10 @@ default: false, 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 @@ -172,6 +176,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { let blocking =3D module_parameters::blocking.value(); let memory_backed =3D module_parameters::memory_backed.value(); let no_sched =3D module_parameters::no_sched.value(); + let hw_queue_depth =3D module_parameters::hw_queue_depth.value= (); =20 let shared_tag_set =3D NullBlkDevice::build_tag_set(TagSetOpti= ons { submit_queues, @@ -179,6 +184,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { blocking, memory_backed, no_sched, + hw_queue_depth, })?; =20 let mut disks =3D KVec::new(); @@ -209,6 +215,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { blocking, memory_backed, no_sched, + hw_queue_depth, }, })?; disks.push(disk, GFP_KERNEL)?; @@ -264,6 +271,7 @@ struct TagSetOptions { blocking: bool, memory_backed: bool, no_sched: bool, + hw_queue_depth: u32, } =20 impl NullBlkDevice { @@ -276,6 +284,7 @@ fn build_tag_set(options: TagSetOptions) -> Result>> { blocking, memory_backed, no_sched, + hw_queue_depth, } =3D options; =20 if home_node > kernel::numa::num_online_nodes().try_into()? { @@ -297,7 +306,7 @@ fn build_tag_set(options: TagSetOptions) -> Result>> { } =20 Arc::pin_init( - TagSet::new(submit_queues, (), 256, 1, numa_node, flags), + TagSet::new(submit_queues, (), hw_queue_depth, 1, numa_node, f= lags), GFP_KERNEL, ) } --=20 2.51.2 From nobody Fri Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8DF6E4DD6C5; Tue, 9 Jun 2026 19:12:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032336; cv=none; b=LEyUdp4RMpsjwi5BvlktVlDKlzNSErzBBTKEw0k3LYQuYv5fjTPcODk1L8RqUrQR8T8eO0lW79AdGP7BGfB55ZEAnT/zFPOqK/bgEZzesZv7yDjRD9egilGaUCQPbwU9YJHq4mcosS7M5yaC5GKzpt1eRjPnDNPgyZmm/98d49k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032336; c=relaxed/simple; bh=cGlJTpA6F6McVlRWYLS3sRK+tr1wvcKBW9jQZlkWj+Q=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=C3pcL2wtLKDkoqaa4A/ZBPRb/XvqQFqKIIJr1b462qLxfM1UoSyD8NaSdzpczOcePn7DJIyCp8jD3Ss5T1OlrQjey/CFaL0NLWrEBKtADBfXhIm+ovV+10WfgwlvgT/A+Ba+tTJK8NVT7NnTIhukGUijOe2nq/e+q1YVBDub9g0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=NYqk0bMX; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="NYqk0bMX" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 691CD1F00893; Tue, 9 Jun 2026 19:12:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032335; bh=a3jbJNIkhOwxGOU8DfYtnfANfNVtK2eFGYBWmXLcybk=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=NYqk0bMXBfzUx40NJEmhqEjCWQBmirujxeYo80SxHV6ohWkmH97SeEqumLNc7LQyZ dbp+zuf7GJroLJhHQj69de+gY7gADe9ctRVyXOn4H90BqAX8MFSZxsMoWFs3jY3UOX Poq2EMSX3lstrGtLG8lzXVIBH6ccWxPvMWoaLYGCLG4brPsT8jEGhpMGAUuL4N3V1T qpsExMA2wKtorI0q19REki41bI63shSxBDRzKFtkWvlyEfL5zDA5bLcC5prlbBNb4S zIaWeVCu+hhBhSRDz9KnLwandfGruIhCw45gX6Q+IVEw9YscNKiHPr7S34u3KTd9t+ UjXvFYZShbjhA== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:27 +0200 Subject: [PATCH v2 48/83] 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: <20260609-rnull-v6-19-rc5-send-v2-48-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=6975; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=cGlJTpA6F6McVlRWYLS3sRK+tr1wvcKBW9jQZlkWj+Q=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTgxZOVEJMK7I40lvqteErFqI2DYzSNcD5mb 96fFobyPEqJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk4AAKCRD6UCkIqsW9 0AcoEACvXt+Fpn1SP/zeWsAm6kzE6hADdYR61zFif7WrXX2R8MnVpdfi44Qm1pQX+NC8/NrD51P iL6dJThG+xn7f327yeKNTKSCjQ5Z4VDpFWft3dFT3LuuOG+d71+vhEQduwMJPsicGf7D/uchYXa 832XLg5qdGT/7yZWn9VmuabH1J54pYc5T47AOXypstsyyXW25mNE53JWmegX8zsPET3dkW9tGiQ HBCvk2qLSnvzgU0L5ms4SwMiq46GRHjkZJKDq5hPPLNy4M2l9085b53eYe+cyTWJIssY17OctLM grWIcE7baIw7oE0EJ3sk2RNRvNJ2e/eZi5ECOISb7Fk5r2OMoUJpVSdmo5cJeuESQ++Lu0gQDro NfSkpM6Ltj3lwMgw/OuEd34FJtzaM/eYk3qXtLS1VWvft0mp4r31kcU/3bIKzkz3Vyfp9D2ilpw XR2ljhF0TCR+sBwU5oRtGEnDSVdjSbTdFgBcURfXEjJiryySP5BxbXU/M0lhxRyrD/KPAVOSu7T Y7YYq5PSBKuBFlGYF6T0jguUcHFINNQ4hpP2Hsbr4yoUN78SjWzWtzk7uROW3CXkn+y3avJiy1/ bJXaDSAcnSJOXt4r9MFq9d8zdiCh57OAgpIA4GDUYnESFSqOyv5qO9JMwqAwVrQy5Izqiw1C+7j V1uHlVY3vP7PZqg== 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 | 18 +++++---- rust/kernel/block/mq/request/command.rs | 65 +++++++++++++++++++++++++++++= ++++ 4 files changed, 79 insertions(+), 11 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 491979daa50e..5ec17a2674b7 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -547,10 +547,10 @@ fn transfer( let length_sectors_allowed =3D segment_length_sectors.min(= max_remaining_sectors); segment.truncate(length_sectors_allowed << 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> (), @@ -743,7 +743,7 @@ fn queue_rq( =20 if this.memory_backed { memalloc_scope!(let _noio: NoIo); - 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 503623267b19..5bf2cf2736a5 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -132,6 +132,7 @@ =20 pub use operations::Operations; pub use request::{ + Command, IdleRequest, Request, RequestTimerHandle, // diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index a05df2351c2c..63e248970ab1 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -45,6 +45,9 @@ 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`]. @@ -111,11 +114,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 { @@ -242,13 +251,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 { - use core::ops::BitAnd; - // SAFETY: By C API contract and type invariant, `cmd_flags` is va= lid for read - unsafe { (*self.0 .0.get()).cmd_flags }.bitand((1u32 << bindings::= REQ_OP_BITS) - 1) - } - /// Complete the request by scheduling `Operations::complete` for /// execution. /// diff --git a/rust/kernel/block/mq/request/command.rs b/rust/kernel/block/mq= /request/command.rs new file mode 100644 index 000000000000..70a8d67fa35c --- /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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9650E418AC6; Tue, 9 Jun 2026 19:16:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032594; cv=none; b=lMZWTsNGAtddXRTQFU/U2GXL1gwPjYHltWDnYzUDLUeKO7B0e34alBAk9EwaWVsmmpziBacu/552HkQlxlpFG0d97B3UBvvELV+KIH2mbJAaiJmU9bjGCTTUpG3c247okw2UlAbScFchiP6N901mvebKgoq6awi8I5gXiNrb9Ao= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032594; c=relaxed/simple; bh=3a7iopDDpVfSb79+JJXmsLPYQF2HcbnYwDnDegNwY+w=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=W7MPqyT00ZUmvbWVlN4pdC2ZVGJWB9cztx4hvl0gPEdcZLduUA+seeqJo4pkeWY6utuQZ+9m1W2+hcueEDFnilRBrusgVt4/f3C14RFYXXooWF4ZJxV5kPkKc2F5Jh4Y77KSF4lmY8DnvRrNL3nXgPce9BD7KsvNsVJkKWUWHD0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=eA1jDN6Y; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="eA1jDN6Y" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C12CC1F00898; Tue, 9 Jun 2026 19:16:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032593; bh=ceBxrKoS+wJKiaFN4cLOiMODjlL/11h7U+S96uTWBDA=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=eA1jDN6YyELOCOa8X5oEIDSxxoh5dC0rltB56ExFbZ7xhWcDACGDg+vg4eMfLW1ET NWoBCbja/8RFTQF+QMnfmLVWDfx3MldlTK4JaZEbBX5aeM10MzN5AYbRpvSC1NCveN DViSi/M/y3DpyqKYQf7Kn4PprdZIDbKCsCfZdufaul72qH6nYaIQAMzNQeohzPmVFE /H2PKBIHT6sci1oEEKP5vYHfAxKY8VmVAqBYVYmc3DNRDkYGXIaxAL51qBa+I9O/2y 0+MUdzBiLK/kiZqXF994Qoz+611BRXTEZmZeFxy/cKKQheDoZTvuD4cmaLNfkZNwVH SK72mVDE7jsjg== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:28 +0200 Subject: [PATCH v2 49/83] 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: <20260609-rnull-v6-19-rc5-send-v2-49-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=998; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=3a7iopDDpVfSb79+JJXmsLPYQF2HcbnYwDnDegNwY+w=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTg+ZmtVXy+AJz3uWxsiQGOSHwamJdP+GKsj 3jamG5JdFyJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk4AAKCRD6UCkIqsW9 0OdoD/9PtqqOjDx+7cpfrfEDQvEryz7mcRZz+rSjYP/0BQE+XiVpvXAUCDvPnbakbh4lpNSp0VY 5TOPyaxt7f0nx6Y5KkuI9jpjdRMzDp7V2DUB71Gx3N7A1CoNfPojHm7qXu82Jgu8LkUGRkYf+vS eutgaL12rgKVIr+eD4tH7q/gqnKRhltLWxX+xpQruEQU0VTjEg4EUwMxxBfXp1bKMJr+k2khdsC 0gMw2N1TN2bxlIwlrhhfg4ev14b+4VFLIqcx2MjGd46RopjOKwEWCnOmbcdH3E3XSWKtaFk009z AxH7CgutGjDiXF6MWMNqau2JgWEPSvjh8UHDHhrfy+Ut6KXVbl3nGrO9cBTkR00GX1UzUqHhqz9 yNTKz58qbnFNbQzdyJp4wxhOu0Kx2g8IW07gVOwMDj30VzUUAYHs9cw/g4TpxX+HElDNqS/wHn/ APr0iZ2wWuFoGmaC6+q9vEHvU7fuIrGzrzWa/fr6jd4RsnHQxbfoQw4CLFFGqkHyI7ZZCS89ak9 9mdwJ4B+6sfUwCexGx9msOrWewmkVUvqyol8OvrpYevPG0rvmXCkMZ+2fzZK6zw9QeVDzBq0c/i 5qKwdLNPIQRC+5Dpy0/hyuG5YHG8PAZLYEZ8vjXYa06A0woFUj+FAVOnL7aoMyVIa0GXk0BH60l HEgqudcUfq/MFbQ== 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 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index 63e248970ab1..66ef2493c448 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -336,6 +336,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4107D408A25; Tue, 9 Jun 2026 19:17:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032660; cv=none; b=LmdhQI2TGNuj5t67ZGhIc6fcOqHQtHdcUWwT10VM+DG7No4PsR7nS3P4+ZCdhQCAc9QZAXUKYgvHOMNNVjnA1rCjS4XIcSvSElXKubTCy43a8jdgMNTiS4OeAzoMu6UKJTvu3HdMKC90nlTOZE03f0/XFeGvPvlzWg6a9SvCwWs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032660; c=relaxed/simple; bh=nWZZSuCE7eRuBTdr1Tw1jnn7BhOeTAryxo5+j0nH9IQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=u5y2v7dHD8q9PplomT4M+esTAOEBYZgnVmdSpGHq8VT34YWJYbyrJpwjvGR4HbLFE4ovaH9N5K0C9BWfljgE0gUnlaCzljqpO29s0uzU8y3+jdwGKsBljit3ZLyby11v/PqTXOsB5KO2OdSM2MfagaA2AHS605NXHLhzLlAG7fA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Yh4qFHgw; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Yh4qFHgw" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 06B2B1F00898; Tue, 9 Jun 2026 19:17:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032659; bh=F3cUw+ZLEEIEYCWFefqNhKQoFd9lfmv4Djxg5dSyh6Y=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=Yh4qFHgwl9dPDE+Dd804+hehfek3T3KktZ4KLW8E0ND8oPBDHwHPhZK4oxN4Jt9cG EE0iARjt5TMMkCAoYKrdklnyXyi+i7wv319+qcsz6IBM0GK99bDE5MzRYlBgv1ZmO1 j1YuADV5qkveEai4nkTjSJI6iL0kWqkODtqVr6bj6SaJ5OgO6XjFXWcs6IohWfvxLU ZGgYe2DY+DClniqoBYYOtq/3PZc7lpl6YRqYN7x2SQCCr3MdD8lyK9N5SABAnhEWVT zKebynE+sZt7YwTmDKGLRm8D6GCuWXU+uqH+p6zleDLieCbFam4bWKcqtcX7DryVTn YZ22OhmrgDUcQ== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:29 +0200 Subject: [PATCH v2 50/83] 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: <20260609-rnull-v6-19-rc5-send-v2-50-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=5125; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=nWZZSuCE7eRuBTdr1Tw1jnn7BhOeTAryxo5+j0nH9IQ=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGThwYPLrFFscsf6EbG3+a5iaTayyAq94Nxgz iFI6puKAgmJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk4QAKCRD6UCkIqsW9 0J3GD/9P/NG4qDSfiZLT6kaSKJVYbPTGQp/wYmerkXDHyXttYDguhsRf0Qbec3e33McsoMnGXdn FDVdkp9zxo2TQ9yg/KTko4Kfk/r8uOuKf8LQF1kLfAS7vV5Y5h0WHp/lN3VsGTMD0Oh1TQbr3+t mnOhDCph7RJk/hDPa+AQBTo+q6GWMOuATQ9WMJCDb2vmPNVP2y/kyfZTA5Y2RgjJqlGyrA4vkkI ibGl+WdC6oa+1nv9sGtlNBDUb76uMcDz8gwvNRtQTDW86JB+Dv5KrdWr/lPgAxgbB+ivkG1iAVl wnnitGsYMBoqLT8x53Cgd72es41XV/ZIxXrjHOOvvLrs14Iw3HFDrHQjP/qTdjoAkRfOydVsYT+ XpiV4YZSkQbdfPIyraHZBqaWmWgb7CvprxvkSNSLJv0WheMyLt0kTBijoO26x7ogyYEEDNrh6Lz ZeICGdPddeacasO0sobyJl/FZ4AGx4OdqsBW586ctFoIwwQHQj0SgQKIlvI2IGMxME5pryXnvtT np0gGKYMTz63Nqy2l0eOyWgzxyPGL2b/5g6dKZJ2T56cWODWW/Gxnq9bBnVZH2i9JLWm1eB3uM9 kZusZVyLHqvdEjAJRF5qvdnu67LxpdjEDeb+P8xy2g3S1+b+hYG8TGJ1bogFAC3hn2/3P4kwyMj 0EgP1DUWmyYA8zg== 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 | 67 +++++++++++++++++++++++-------------= ---- 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 2dfc87dff66a..8fa16dbc2a75 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -290,7 +290,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 49ce5ac4774d..79a67b545eca 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -34,20 +34,24 @@ 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, @@ -55,11 +59,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() @@ -126,7 +131,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>, @@ -157,30 +162,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`. - 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 @@ -233,6 +216,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 42A5C3EB0F2; Tue, 9 Jun 2026 19:12:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032331; cv=none; b=oCPE8ePgDmp1bSDpRAZDDwUeUrVxg5Pi6iWHB7E1muZaxU94nE0dUBQeuHu9RV8qz7fp7zaKMJfurI0LdtDApeV18nOgvszM1F5sZruuiRg4wErw6NsPWOjJABbd4KjL1oasgTVQkdTSr/h2SG+A04AG3Nw5o3z3m458aTuq9I8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032331; c=relaxed/simple; bh=a+eWU5zLlvyoRr0+a9110st2xDfdKu1tkbHU0hHggYA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=RD1AXBf+ZQa8tR8Ek3PGkS6wtaSb+2KvqK+eiudKAQ8fSH6LOCvmKxA6Z3uo+q3YMhcADqUpPAGMry3Az/tmo86hYumBLix74cfcfdCyTImOG8wXrCY9NlNl6uQEtKg7B+fDk2jr8yd6DjSi1p09raqXPhDg2ijSh1sTeOIxiUY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=fzjF7g8h; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="fzjF7g8h" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 02C511F00898; Tue, 9 Jun 2026 19:12:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032329; bh=nqc86KcUVxZTdVaOG+SDVXI6QBFEXbnIR9/IB4zof7s=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=fzjF7g8hqCBIQqu0hkw28HiFPgTi6wEP/2SThQdUdkG8bwJ+e2esJGkua58Bpc663 eQ0mccMeWcLtwJ7jL4pVOHLXXsn6vcb1OUNRqKFIzDcH2spcjyO1xsZJuF6cts2xQ8 KrKKa2oFJPy2LvVoAZaTqp8DSh75OjA70KiXf85Em3EPDyuZV4Zs/ozZLjIAmBRYMl 0C7nznhMYSNT4GktfNZNw2+gXY4Wf17jndEkxQ9Y3YX2jdNQczeJ8EPM1y8wUiVn0L ynbpYZuDP4Ix83K5AQy7HzFSi52+FsYssztH7cNPf9qMW4BuGdlsEzz70YdrP4lRCs ye/y/XEvZcxdg== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:30 +0200 Subject: [PATCH v2 51/83] 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: <20260609-rnull-v6-19-rc5-send-v2-51-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=10741; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=a+eWU5zLlvyoRr0+a9110st2xDfdKu1tkbHU0hHggYA=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTiFZEVhsuDWxvjg8aek7snzhrQ8IDiU8X+K 3NI4zNl2h6JAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk4gAKCRD6UCkIqsW9 0J14D/409i6f2gq2UFsEg83A5npLQ7XUUl/lMbAv8if9BRE1FRGBbo9zszd4Hb+sq7JUp3ytZE2 +bRsL3nhWR+BC4X1Jsj/F4bETG/5o66JlOpSXSl8F8dBnLXhkbWqoiyzHMLOLFHV0+gIpQZrj9Q tnu41D7jOnya4/4lUw6dPK77WGB/HJcitwAUenDhsl75BBzipcnKpvneAEUsuEVxgkNC5Yr03HO 5yQ2lsdhbIGsOEbWWIosSwdCIuiKgARwiJfMWIALm3zYp6jXpA5TDQHaat7mxCPOR2U7t059CZY sgt8D8sy7onJF//G7N+N7eMRxra0zi0deRrasV1zQMLL9e4zbjLYBI91QDnA0TsDM7O3+dclNcd ziUPx5hMNIxNLnP/Sv8tzNJABzk9K8fJ9LaFmXzfhl9EZFOIheSTWDA+lOX1Udft8aexw1pBqvq x9RUemeiQfJhV5dqBD1OiX0Vc/t+q4zOXoYhsrBlTvo54OK4tPUt/35T+SdVjdr5iIleeu5YcBH ySyUbu+HIHCjmhCWLGVqAE27gnB/RJkjUcwSWVXsNMn64rt3nWNsp7AZ+8pG8rnkC15hyb47+0F PZ0qhAj8A0IDv2MmuicLy0lkD+d2XyBbpYqRkhiyf/bAjRTP+F9UZ8klhY4bGWAJLLtsu8l0Zju TtfAW7rKnkP+klQ== 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 | 95 +++++++++++++++++++++++++++++++++-= ---- rust/kernel/block/mq/operations.rs | 61 +++++++++++++++++++++++- 3 files changed, 145 insertions(+), 12 deletions(-) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index eaf05d60dda9..2a69c17bf271 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -139,6 +139,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 79a67b545eca..eedba691e167 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -8,6 +8,7 @@ use crate::{ bindings, block::mq::{ + operations::OperationsVTable, Operations, RequestQueue, TagSet, // @@ -48,6 +49,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 @@ -59,6 +66,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, } } @@ -130,6 +143,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, @@ -149,7 +183,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 @@ -179,14 +224,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. @@ -214,7 +251,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 { @@ -228,7 +285,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, @@ -327,6 +388,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 b9a2bf6592b3..71d4192d627f 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -9,6 +9,7 @@ block::{ error::BlkResult, mq::{ + gen_disk::GenDiskRef, request::RequestDataWrapper, IdleRequest, Request, // @@ -16,6 +17,7 @@ }, error::{ from_result, + to_result, Result, // }, prelude::*, @@ -29,7 +31,10 @@ Owned, // }, }; -use core::marker::PhantomData; +use core::{ + marker::PhantomData, + ptr::NonNull, // +}; use pin_init::PinInit; =20 type ForeignBorrowed<'a, T> =3D ::Borrowed<'a>; @@ -107,6 +112,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. @@ -359,6 +378,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CE9104D8DAE; Tue, 9 Jun 2026 19:12:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032358; cv=none; b=sackz7LJtb1vhnyEjKAiTuyqG3gODJelBMA5Dw6hUc+8kGNMXP8m4VNfFyz74R5tlf50IqVutnpggL7YOoDK02JSoth260dTqDwW2M2v5Zms9xkIN45JqA5Vhbhds4U/XUurGt/Vk3Z+bnMsosgUTy/5HpMcr7fjOdLh5aGjuAo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032358; c=relaxed/simple; bh=OGr4o32gLgytplFNTefSvmBphLU6/cXTR/X1CTCtpbs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=NtR0KHwcfmNHMRTdlvWWuoLD/r1/6YtI0MmE+8G4CfX/jhDL/ymxlwLq5qPXOmIrfrmRHWOZsIH1rTbLQ24QtIpBQ/ljdQ98q9nXu8SVNnAa34g+/SpHcb9NbqIU3h6LNqK3BbWD22BtU/YiXMp2g6D8mc6wlG4BV44eyWsZsQI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=hSLqirSm; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="hSLqirSm" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6DC361F00893; Tue, 9 Jun 2026 19:12:32 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032357; bh=SHThiWRFl6eX+o7HfToI0BveZqoyq24Iln96WP9m9/Q=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=hSLqirSmMdfe7JHeDMJEbaGk2xH+r8zJCJzvpqX2KhAAr9i8+gTGMZyAN1O1wjZFC 87zQtjSJAnw4CigqubVw8wKx94QKtNmMrfN5c7at91IeS592SC+4RIGtymDHm1YwCa YLJUWRbTanGImSCTPh9eGyzpeIrCOd2UbjnURp/RndyphhSh+uuhENiu2XosWeNgGW CdVJAtOFqRqSx44VleaAHHBq96ylbwR33okKwYWrDDhCCpyXGW8l/aCJ10fBnnoNeF gDyNOm1J8ES8pByB/3zTYzR5n0qqCLrmYO2IeqK/tGMJ6+8bvXHby5oTUt8Hb2HC8z /PO7x6UNlMlnw== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:31 +0200 Subject: [PATCH v2 52/83] 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: <20260609-rnull-v6-19-rc5-send-v2-52-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1127; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=OGr4o32gLgytplFNTefSvmBphLU6/cXTR/X1CTCtpbs=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTjD/SsS15S2X0k9iUPabdH4odGbYwDe1N6N 6f6Z6npMbGJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk4wAKCRD6UCkIqsW9 0BqjEACmNfk+sSeVLWEKg7qqrjEhETKAJdKa0iscz0fFFu5BCZYtskqN1Pe+gzH4+dxsr0nPBZp wFoG2ahBVLl1ZFM6xUD5wlUwnvbCqawttYwP9tRmHVlVHeDArVaWUnXcapkxPDUNo+RWr3nsPSQ TM3hKexhjGLQDNoOtZwP6z4n4J58mZ0FeHYSZgx6TOD3SOMpR0vOLZ8+opSfcFQDXnoo9T9xkHk EqbddpiRIe17zJHcW1XA6kUthmI2JZab72b+IjuBR/NQqr+cg5oRSFafIj0EjDD/IdeVQ+DRqNd 8vUgnW+2fAyuIbgxeq+/bnuy+Yp0MsopvmFj9siA3Kpr1l8LgSJ6Qw6D9AbQ0CnL1Hrg3DgGDVH fF4DCvpOb7/bEaKGo6OQ5hsY8kPb13y1q0p4XM94MvgobrunqFlJNuWulMDtz6cBJewScWUyKoc 9c4dGEBHCEJokswtZYoAvoQJDhfOQGzI/5k1jUwapW44qpjVhWS4OlRZD8qPJKoa0iI8dm+m5Vl S3+RawjqCptgUdxVWG0VUXAaEpWzQ+ddyLGHOK4WV1kP77VB6GlWEprAoNoj4fhj7s1SciGYsa3 cv85EOjJFomaU8MELbJfwoihhJgdcgv8QPmNTICa+Zinnxx9Zir+POqXejgqcP07lZVrtdQPIi9 vu2Y6Bmvw6jKhQQ== X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 Add a way for block device drivers to query the flags that a `TagSet` was configured with. This is needed so drivers can inspect properties such as whether the tag set uses blocking queues. Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq/tag_set.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set= .rs index 5359e60fb5a5..157c47f64334 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -107,6 +107,15 @@ pub fn new( pub(crate) fn raw_tag_set(&self) -> *mut bindings::blk_mq_tag_set { self.inner.get() } + + /// Return the [`Flags`] that this tag set was configured with. + pub fn flags(&self) -> Flags { + let this =3D self.raw_tag_set(); + // SAFETY: By type invariant, `this` points to a valid and initial= ized + // `blk_mq_tag_set`. + let flags_raw =3D unsafe { (*this).flags }; + Flags::try_from(flags_raw).expect("Expected valid flags from C str= uct") + } } =20 #[pinned_drop] --=20 2.51.2 From nobody Fri Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C81DF41B7DE; Tue, 9 Jun 2026 19:16:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032614; cv=none; b=KsavlVBc0SClO3hMssCEIacuX66uEtEjQ3SxqEsVoHqYMN5QF3AQrc354tLkAmBUFwJIv0s8OtvKhoaxcA27aiWv5UBzPK6R8QDph1JZYU4W3P3bijIjMW4J24yK3UWmchr2dCeloj95ZJMyYi2S1Py4i7xmfdjCzWGKHVip4u8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032614; c=relaxed/simple; bh=54Ir0JYD9OtCshYrOmORT50bWLTfAnbAqnZGtUhdkb4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Z0ZpNdmZG8Rx54RNcN/JwgPi8oIHT93EzF8G/9XqkFR3k3j1n9oxn69jOiOfe49aV4Ip9XFUabfLs5mfOJ5hi96R3hU/Cqv0m7fYvJczdUNr63rpw0wxlZcnY8X+uVYDf0iOEDAwAq0oiAs/r2Blhl6OV+v9djgD9RbzwWVAS1g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Z5kDg10y; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Z5kDg10y" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4080F1F00893; Tue, 9 Jun 2026 19:16:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032611; bh=8+aQYbUEeB2wWpPE0Kkc70BP8QREHNkdbeNV0sZI8Ec=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=Z5kDg10ySRIH6Q0XSS0fYiwMB7rfEZqjLr0pZXuQKgwYrtRtDBxYXz+EoYF1lDT0g bBhAsKph9TgcjwJvCGSDRWQi+ZM6y552BlKZo9/8YJ8Xus9zz2ZSsT1UexUa17DlYt DnhHjpwpBney5US//f3OmKEXSxHymYdD7W48VJGGEIKUmomoS7AWa79Ca5INnfTotX 7F0/K6ls6aIgmknUa/3Wgmm/Q4YzBG7qqHZNhvCFm0i/jKGzWME8stbCVMyLDfVLgn eXLTg4ELCUYN6cYYudD6oBD5yFJcrWH+m89c5yFC5Iyiz4w9zoGEa1b+uyfPc0VjRZ uWPO0F+lYFvFQ== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:32 +0200 Subject: [PATCH v2 53/83] 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: <20260609-rnull-v6-19-rc5-send-v2-53-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=50709; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=54Ir0JYD9OtCshYrOmORT50bWLTfAnbAqnZGtUhdkb4=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTkR1oxAPh1bGbAiVeMf3iVb4oGeLtW2sIxJ zwsA89rwdaJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk5AAKCRD6UCkIqsW9 0MqTD/4zxDaeSp+Xd66k1yvqUWvNcM0tZVeygkIgw58KFG8jojjGfNCSCXhbo5ifDqDrdJ7Mxzu 0u/hSPiH1bvHlv8KZLmXM/Z13tazlOB8dfBtFLVgOhvbwNL96rz4oXlvN6tv04EnpW2fJEais0k IZh+WMealEw7YL9pZ3HPTWMHBczHKvMluEXI9b9CdvWWTdOajUC315rXB71yeu2P0r+g4nc+49M qyvHMcHSwrVdtfajbWq1ilMi/McEWnMwHQ+2fg80EzDqdVeNndv/R9xFPbnDChQ+rVQhR/eYnyC z5m4RxmXBUsGrjXxQ9phDcRhC3/KfZlkfIDAvCUj6ba89TomZ5J/nAb9L+kQvfTTszmQ2WVo9ep PnNFjkh/CueMwNCrnNvM8tUphBNbGcQIa7W/6+NWe1/QFFA55LtJ4Qrzq4/xeHCsOGEMu4vw11i 9Vicnj1lMD3ohB2/0OhHU2+N+buOp+vmInvqsm5i3C1bYo63sYA4ygnT4sh0hscUDB6j251lqZh 43bxdKXgtiDYd36696t93GIsySis+U7p6Gj82HX5xkxicmdmRyzWvGQtZ898ghDaeWgo125Yz/Y 6J0GOCpnYemDh4vSv3cTdOeypMaGfTgpMfNdlearAunVw4z0NAjdU55twifJ83d+ldsS3SIfj6m 1y02KxkZB/S8FDg== 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 | 67 +++- drivers/block/rnull/disk_storage.rs | 34 +- drivers/block/rnull/disk_storage/page.rs | 4 +- drivers/block/rnull/rnull.rs | 243 +++++++---- drivers/block/rnull/util.rs | 65 +++ drivers/block/rnull/zoned.rs | 663 +++++++++++++++++++++++++++= ++++ 6 files changed, 973 insertions(+), 103 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 8fa16dbc2a75..f866595a263c 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -80,7 +80,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()) } @@ -118,7 +119,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 @@ -145,16 +153,20 @@ 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, shared_tag_set: self.shared_tag_set.clone(), 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(), @@ -234,6 +246,13 @@ struct DeviceConfigInner { shared_tags: bool, shared_tag_set: Arc>, 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] @@ -257,11 +276,24 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { let mut guard =3D this.data.lock(); =20 if !guard.powered && power_op { + // We protect zone state with a mutex, so we require blocking = queues for zone emulation. + if guard.shared_tags && guard.zoned { + if !guard + .shared_tag_set + .flags() + .contains(kernel::block::mq::tag_set::Flag::Blocking) + { + return Err(EINVAL); + } + } else if guard.zoned && !guard.blocking { + return Err(EINVAL); + } + 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, discard: guard.discard, @@ -279,6 +311,13 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { no_sched: guard.no_sched, 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 { @@ -442,10 +481,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { store: |this, page| store_with_power_check(this, page, |data, page| { let text =3D core::str::from_utf8(page)?.trim(); let value =3D text.parse::().map_err(|_| EINVAL)?; - data.disk_storage =3D Arc::pin_init( - DiskStorage::new(value, data.block_size as usize), - GFP_KERNEL - )?; + data.disk_storage =3D Arc::pin_init(DiskStorage::new(value, data.b= lock_size), GFP_KERNEL)?; data.cache_size_mib =3D value; Ok(()) }) @@ -455,3 +491,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 b8fef411fffe..82de1f656f68 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 bc78973ad5d4..88dc9a2476bd 100644 --- a/drivers/block/rnull/disk_storage/page.rs +++ b/drivers/block/rnull/disk_storage/page.rs @@ -20,11 +20,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> { memalloc_scope!(let _noio: NoIo); Ok(KBox::new( Self { diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 5ec17a2674b7..6fb307e33263 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 util::*; =20 module! { type: NullBlkModule, @@ -151,6 +157,35 @@ default: 64, description: "Queue depth for each hardware queue. Default: 6= 4", }, + zoned: bool { + default: false, + description: "Make device as a host-managed zoned block device= .", + }, + 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 @@ -195,16 +230,16 @@ 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(), - capacity_mib: module_parameters::gb.value() * 1024, + device_capacity_mib: module_parameters::gb.value() * 1= 024, irq_mode: module_parameters::irqmode.value().try_into(= )?, completion_time: Delta::from_nanos(completion_time), discard: module_parameters::discard.value(), 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.val= ue()) * 2u64.pow(20), shared_tag_set: module_parameters::shared_tags .value() @@ -217,6 +252,13 @@ fn init(_module: &'static ThisModule) -> impl PinInit<= Self, Error> { no_sched, hw_queue_depth, }, + zoned: module_parameters::zoned.value(), + zone_size_mib: module_parameters::zone_size.value(), + zone_capacity_mib: module_parameters::zone_capacity.va= lue(), + 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.va= lue(), + zone_append_max_sectors: module_parameters::zone_appen= d_max_sectors.value(), })?; disks.push(disk, GFP_KERNEL)?; } @@ -231,9 +273,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, discard: bool, @@ -244,6 +286,19 @@ struct NullBlkOptions<'a> { bandwidth_limit: u64, shared_tag_set: Option>>, tag_set: TagSetOptions, + zoned: bool, + #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), allow(dead_code))] + zone_size_mib: u32, + #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), allow(dead_code))] + zone_capacity_mib: u32, + #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), allow(dead_code))] + zone_nr_conv: u32, + #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), allow(dead_code))] + zone_max_open: u32, + #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), allow(dead_code))] + zone_max_active: u32, + #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), allow(dead_code))] + zone_append_max_sectors: u32, } =20 #[pin_data] @@ -252,7 +307,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, @@ -263,6 +318,9 @@ struct NullBlkDevice { #[pin] bandwidth_timer_handle: SpinLock>>, disk: SetOnce>>>, + #[cfg(CONFIG_BLK_DEV_ZONED)] + #[pin] + zoned: zoned::ZoneOptions, } =20 struct TagSetOptions { @@ -314,9 +372,9 @@ fn build_tag_set(options: TagSetOptions) -> Result>> { fn new(options: NullBlkOptions<'_>) -> Result>> { let NullBlkOptions { name, - block_size, + block_size_bytes, rotational, - capacity_mib, + device_capacity_mib, irq_mode, completion_time, discard, @@ -327,6 +385,19 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { bandwidth_limit, shared_tag_set, tag_set, + zoned, + #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), allow(unused_variables))] + zone_size_mib, + #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), allow(unused_variables))] + zone_capacity_mib, + #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), allow(unused_variables))] + zone_nr_conv, + #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), allow(unused_variables))] + zone_max_open, + #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), allow(unused_variables))] + zone_max_active, + #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), allow(unused_variables))] + zone_append_max_sectors, } =3D options; =20 let memory_backed =3D tag_set.memory_backed; @@ -337,10 +408,10 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { Self::build_tag_set(tag_set)? }; =20 - let capacity_sectors =3D capacity_mib << (20 - block::SECTOR_SHIFT= ); + let device_capacity_sectors =3D mib_to_sectors(device_capacity_mib= ); =20 // Prevent overflow in usize/u64 casts - if usize::BITS =3D=3D 32 && capacity_sectors > u32::MAX.into() { + if usize::BITS =3D=3D 32 && device_capacity_sectors > u32::MAX.int= o() { return Err(code::EINVAL); } =20 @@ -350,7 +421,7 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { irq_mode, completion_time, memory_backed, - block_size: block_size as usize, + block_size_bytes, bad_blocks, bad_blocks_once, bad_blocks_partial_io, @@ -359,17 +430,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_sectors) - .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); @@ -393,7 +489,7 @@ 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, sheaf: &'b mut Option>, ) -> Result { match sheaf { @@ -418,10 +514,9 @@ fn preload<'b, 'c>( =20 // Another thread may get the lock after we allocate. If this happ= ens, retry. while 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(()) @@ -438,7 +533,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, &mut= sheaf)?; + Self::preload(tree_guard, hw_data_guard, self.block_size_bytes= , &mut sheaf)?; =20 let mut access =3D self.storage.access(tree_guard, hw_data_gua= rd, sheaf); =20 @@ -491,48 +586,23 @@ fn read<'a, 'b, 'c>( >> block::SECTOR_SHIFT; } // CAST: Casting from `usize` to `u64` never overflows. - 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, max_sectors: u32, ) -> Result { let mut sector =3D rq.sector(); let max_end_sector =3D sector + >::into(max_secto= rs); - let command =3D rq.command(); =20 // TODO: Use `PerCpu` to get rid of this lock let mut hw_data_guard =3D hw_data.lock(); @@ -566,6 +636,27 @@ 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 { + memalloc_scope!(let _noio: NoIo); + 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(); @@ -581,7 +672,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; @@ -666,30 +757,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; @@ -711,8 +778,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()); @@ -738,18 +803,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 { - memalloc_scope!(let _noio: NoIo); - 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()), @@ -775,4 +838,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 000000000000..044926c8e284 --- /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 000000000000..808449cc49e1 --- /dev/null +++ b/drivers/block/rnull/zoned.rs @@ -0,0 +1,663 @@ +// SPDX-License-Identifier: GPL-2.0 + +use crate::{ + util::*, + HwQueueContext, // +}; +use kernel::{ + bindings, + block::mq::{ + self, + gen_disk::GenDiskRef, // + }, + memalloc_scope, + 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.as_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 { + memalloc_scope!(let _noio: NoIo); + 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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7E7B7403B19; Tue, 9 Jun 2026 19:16:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032566; cv=none; b=F0kNd8JExZJ6DrxOCWYnFZB+ECIMb9Fm1S8SXFv2QJZyM4EHr7ZahiyOO5qM5VloSUZmKdk4HzkeMVo68kQRRRs6vMqMX75JEcBM5eKI3pIlbgKWqug7DdDOPR+jNbfRmPB8TRjFOwfs/zXa+3Iv6v5u4yl8EukcS8Aix3iUIHs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032566; c=relaxed/simple; bh=v7M+SqjjYm4ifPlbRD5RF/J8lCqCoX2siEspuGsi6JU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=f1FYLpgQUfLKYjJZ9tO+LtrsDCMhzAjMST4Vu8pEWqXatQ4YM8qDXjEZVv0oYNVpOYp+4tAd4HUAkPlYMvORQrCATJ6NStxRTUtmtusYtLcl+h9b0ANRDdwTorbjHr2JcVdcaTsOLeSj0SkeXO5Vl/iLyAms5wypS4vQXYLARZ4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=jw3H/Q3g; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="jw3H/Q3g" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1E3CC1F0089A; Tue, 9 Jun 2026 19:15:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032565; bh=G3lgi4BvnuP8dvWHNylWqJ5utFJtojJuMjE3Nz2Xk8c=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=jw3H/Q3g3yHtjPIAShUuvFoegF8tqdbx/yiX64UeAWL7gLz2R6aeB/6bcgMAFYfX+ qdr4L1CerL0er728ZHZ4Vr6lsEleYCtd3dnM1gVcuPNSHRNCKoJDUGyhJOPl29r8Wr 1Oil0uJnHHH+WNKeCm+hmTHJVTv4dLZnonRPThd6ol3KUnHqnD/ZzgZeIZ1k2pGy/O yRgp4CsOogjtHlhnDlBLw7GnctxuUYzeFbRPQxvLoZCdzkSLN4TI855YFEqxLge1F/ SLFwSDUDMLgj8nG8tj09nSbVLo7TTTl65FtYZyDwBUa1kUk0ivIFg+ayFQh/AHQ5HO Plw+Zl29cAUAA== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:33 +0200 Subject: [PATCH v2 54/83] 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: <20260609-rnull-v6-19-rc5-send-v2-54-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org, Andreas Hindborg X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=3830; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=1/oGMHKJngamxy/YHU0uRCSEeVu7aFtuzPGZ9TvHG3k=; b=kA0DAAoB+lApCKrFvdAByyZiAGooZOWj3cCG2U8+E3n3Tx3zuIx6/yQjgqJBN0irwutd75dDd okCMwQAAQoAHRYhBFeK2cjZZnYmKsBqhvpQKQiqxb3QBQJqKGTlAAoJEPpQKQiqxb3Ql6oQAIXd 9lWv1cDgGI7fP3R1gNts3WYj4RA+OhgzhbTerMr1sIBoD0a6gKcGvV0+p6o1sVFZN1iqIka++11 pSozYjaes478Tbr6cJMx3Q+rvpZfWhuSWzT0kIO3Gx858LHrufA6VpiyzQEMqYfdKM4N8YYZ3kn I31DkpEe0tsQ1kphWJ5OeqzzVSLqLmmNhwTtLMVQoLlIiv9L+btIHGNvOyL1FLNxqt5KFnpsg2Q FDbbKUSUEC2poyjuCxLGx7X96fCyCoodylGu1pGpedoVJrJx5zxgYLhrbqTYvxsX7S9hO6cuXnj wdXrYDMIay2dzq4IEv2RxfIgInhaYr5oQfYZginSxw6xpMlFUOAkpv58qPy1sdd8obSwM/JkwSY JfC3oi7LqjzLVuwSoJPMZL/TLG1j5h9/wYTeeJhnws+FmdbuaD8lAQhyqt24+0N285pOEaupAY8 1aplUuy556oWZH0R36MfNMevlodFy7+Na0Bu8SZr6YrBc6lJmkCcF0LJzEnd4VEDKyIID99DLza WO+RHsJnYLWflvvnRqoX7Sv/98qPDWQ7pqmdO4e/wtVyr2MGTw2IiC+piaHEhJqv0ToWuXfNiSe ieHwW6vuyMEuinefgYAI8anN6jcQgvPRUEWxdNiltSWXqYW5lDP++LXCzr/ITiruskuIJ5FnOMU e+Sce X-Developer-Key: i=a.hindborg@kernel.org; a=openpgp; fpr=3108C10F46872E248D1FB221376EB100563EF7A7 From: Andreas Hindborg 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 | 29 +++++++++++++++++++++++++++-- rust/kernel/block/mq/tag_set.rs | 13 +++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/oper= ations.rs index 71d4192d627f..8a418bf0f3ba 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -12,7 +12,8 @@ gen_disk::GenDiskRef, request::RequestDataWrapper, IdleRequest, - Request, // + Request, + TagSet, // }, }, error::{ @@ -126,6 +127,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. @@ -418,6 +424,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, @@ -439,7 +460,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 157c47f64334..d3e93ad98b6e 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -116,6 +116,19 @@ pub fn flags(&self) -> Flags { let flags_raw =3D unsafe { (*this).flags }; Flags::try_from(flags_raw).expect("Expected valid flags from C str= uct") } + + /// 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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AEA054D8D96; Tue, 9 Jun 2026 19:11:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032297; cv=none; b=cvMGFMMWYBTrEHohCKSQoSj39yaasA1ebKMQuXS0s6kHWrHqI3H0BJxn/SWVUzj4rYN90oYUCQmMh4CYxDVBe301JC0BvoZSmSLnPh8AnBNyovBtJimo6/Qt/MMPETAtYxh+gB6cSYJY6LPjSosl14CRTow27WN7KL8TidHygtk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032297; c=relaxed/simple; bh=ektQL/aHChpH2COAEoc+T67ae92mSUZiOEMhrybycnM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Sw7Q0rvANG0cD3hNdvynURpIyYG/6gj4xoLfgZ6mQdEMCA4oArR6q9Iip3YKb+uz0zfsc/41uYnyefOnnmTZ894fGf2m6zy0EBdzpAL721+Dmxfa5o2FWD0JUuEkAuE9ji0bDwueR7SdZuQGVo8mHJDc5KKwz1PESEime37i1XE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=c1cG7zd1; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="c1cG7zd1" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C4FF81F00898; Tue, 9 Jun 2026 19:11:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032296; bh=BNgt6NEUNStjnj6HIxW0BxY2rf9f+JxrNGSNIXiRUfI=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=c1cG7zd10jQTjOFbyKBTS2Xr3GepDjhaSf9h61vP7FI4V27AfuIdSci/J2vDfc43d LlMbXoI+wLxIyLsvITXIf+mrwQ/jNL7Bis+MyUnkchyKZN9s0ROJ2UvDDUz9aM6VI2 jtCkgsT0uoPPQLQKux0+VR6hSWH0feyU4mhX1FAuuqTQK4KHANY+LNkj2bn1EHfw2K jjtpjuncCJG3tGpCzvURJp5b3bhujvNbnPe5nVsUv8yQKuFebRPVRfa4e3nbDt45bn 8ZH/C8lu1W1wXyKs95rG4JXc5Qu3hE6IX81Mx0YVB4lZ59sP+dMhKzHblGbDTV+T29 TpprcYZGMcQIg== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:34 +0200 Subject: [PATCH v2 55/83] 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: <20260609-rnull-v6-19-rc5-send-v2-55-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=6865; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=ektQL/aHChpH2COAEoc+T67ae92mSUZiOEMhrybycnM=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTmL3Mfx97RHoZzsqxrmDoBER68EUD3nOm+m V2jwyRIXWuJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk5gAKCRD6UCkIqsW9 0EZUD/9krINdRjcBpOaNIsActzD16iPZ7v0RNTmF0EsM7APNv3HU7Y34DmFAgNgTNTi7pfqHtH5 jZrv7s9P49bzKoloCxFs3RAa8r5ThTj1q7gwGhUpn9gGLhTaC87KeIG6WOsu2UfBlBahhNW+GeP 6FPTO6sN1gqjVHRruzT9TSUmdc47nJX2B73Wr0CVMoaBngH/9YdAd7dWKvRP7eOaPIq2DeTxPkC DXfg7LUSpCfIFYR3rBrOHFuegLl1ge6gqqFmMcqrwzclI1Th9LOnYMmaXVI16UPawAfcs7114de 2XHhzORXqAq1eN0ADdsx3RRM6IYZUONy9I6bTPgyIStvg5Ole1myIqQ0wPmHDnxH2Ps5RJ9IMze ejuJ8QplYt7AI433kAmjy3AW4VcNjK4aPsk8QuLK37XXyn+QfhvGSjlfuZrrR5FrkAkh8MgNdLr lfjm79kMoynNJmT/y+5W1rtiawpty4qYkWTgPg7lRJIma3Eejn0K7khxTtOp49Gj7/4fhDUqeZ7 btwxisYW+0u87V9ULrCbRoLLNPu/2D1TvvtN4EXv3CrNOltcV/RnY2zxepVyNNfpMe6KA9QYZ+2 koiWqsTAp9qZtFA6GNn+1lexur+ODHeGeF3dmIA43bq/hRNdQDK9BfL1IaPrPWPs2cPJrB3QUYJ WDh/LwnMojBKxCg== 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 | 5 +- rust/kernel/block/mq/operations.rs | 10 ++-- rust/kernel/block/mq/tag_set.rs | 96 ++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 105 insertions(+), 6 deletions(-) diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index 5bf2cf2736a5..e9bea19d684b 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -138,4 +138,7 @@ RequestTimerHandle, // }; pub use request_queue::RequestQueue; -pub use tag_set::TagSet; +pub use tag_set::{ + QueueType, + TagSet, // +}; diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/oper= ations.rs index 8a418bf0f3ba..06faf5647aaa 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -128,8 +128,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) } } @@ -433,9 +433,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 d3e93ad98b6e..e62dfd267fd9 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -124,11 +124,46 @@ pub fn flags(&self) -> Flags { /// `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] @@ -164,3 +199,64 @@ unsafe impl Send for TagSet T::TagSetData: Send, { } + +/// 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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 343324D8DAE; Tue, 9 Jun 2026 19:12:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032353; cv=none; b=iIEoICehqxVcsKH7zfBh0UunxsMM6QEDXd7itnT6IiZlGrC0QxJJOhW1VsoypvUw7NYUGJvf2gwMNJsDkrGEy+FthxUH8KquvYSoyEcbeky2VUsAj39auatpBWAkWqe6K4AMcgNxNFHuuLsql8HrqHSeShih8OHJKZ/uth4AcGY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032353; c=relaxed/simple; bh=jvXphF1O+UsOfmqE9+2fr5qdFdUFp0Su15fp0KlmVtk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=sJDqxBptsR4XKMiNFqAy70cr6z7BgcPRhxfJQMlEvvX0Mac9e8wTY1GSgMlsDKGwc+gho2xbvV8r7aTR8OOFEmIG4B0fEd/sFCxzGAeGisO9X/EOFVhth8zb/5fll2BaVioVaqyfsvpvYSdjkxwDX3PkkAtenRRA0gi7IvgG+7I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=GSy6Rg6j; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="GSy6Rg6j" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 07DA61F00898; Tue, 9 Jun 2026 19:12:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032351; bh=SUQEGk99smJ7Pbi43ZJOTsp1NaneMcUaITRFaSewA98=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=GSy6Rg6jO9B6Pu9mRJmo9lpMn4MHZaZXKvOsHJMJZqrl0/9cXG/yrrRVMOqQ7W87l GiUWGJNvYjqngYMk72RS6nSLp3zONB0bG0jR3YWL5IxOjJiuvpUrmILbpH2uZppNTH n3KArJ4YGmyNi+s4H0689MlC3U9DX3pew9vU6WpKAu6aBGu7Z8RQ3DyRB0rUfs0CPv W5z/K2xDOQkn7e87vjgwEJe5fkIEIz//dz1kVWDPT2kxq4VEdLtGDuiLCFbB+kUE/T PEs4w6x3n1slayCv0OduXPCEhcxWExt79xQU8JBTmtJdt8C2g4t11aA4iYqDvl4okt s/JfVUmD/SZtw== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:35 +0200 Subject: [PATCH v2 56/83] 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: <20260609-rnull-v6-19-rc5-send-v2-56-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=9061; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=jvXphF1O+UsOfmqE9+2fr5qdFdUFp0Su15fp0KlmVtk=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTnPCCkwXRZ7EM9IraNe3j7/IC/hd8SjG0uX 8HAWb/QjDSJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk5wAKCRD6UCkIqsW9 0FghD/4oHjyv4trDJ9ykJMai09g225CGdRs7Dd98wURQnYWZKMmo9oed9LXHMjZVQW0cEf0PCzS HZ5MGb1R2DCg9UTe620dVw6qHYiu8lUMsmyPGaoisZXkbcAi+Q2OvJiz4qzMZxZlpj00uVtEXll QQ35Curz4haeD8eXkVtOMOedpf4ghknAgQv11Bg1hnvNjrm9xPXOjl0ivnVEZc69wc+Yzeii3EU iR5P5it8A1Tt1UYHzyfdRJo02wJ2WK99Q3jognRcqOItoD6H4k8MzC7O1OERHAwY75XEWHaZkce CX2KM16gjTlV1NrEZsJO2g/MwRN/kA25H5DY0dJcg0Q8GZ7O+nJrlSZHYoY2uE3ZB54AQvBSzyR OkriCEAIl5vmtij+j5EPP0svxA47FWs6cSqlVUCf0E+vxKHPWlpv/Fne8f847nuc6zDuUZ45W9y uBwTMp4kCLh/k071tJnNmoXP7PEeWTlQDIe3+l2z1if9u9axOfJCn3XZ4LRxXuh0Xi6AdIUewU8 tRE6IXnI+3Ewdgy6zWkoAEGrY924o9lzO6kAhlEuG7ogID5bHePO9e8zmzmAssC0nm31AElbE0R +5ZqCoGRkQaIx1GjqWl65QS1LnDRFdtnCOjRmkBntX19JjqN8uysKkCmrhChvd4aV4KPxD7n/xm NLAhazSaBz+Lvvw== 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 | 8 ++- rust/kernel/block/mq/operations.rs | 104 +++++++++++++++++++++++++++++++++= ++-- rust/kernel/block/mq/request.rs | 5 ++ 5 files changed, 120 insertions(+), 5 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 6fb307e33263..076493f92516 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -777,6 +777,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 6a70e1306a3a..500e3c6fd951 100644 --- a/rust/helpers/blk.c +++ b/rust/helpers/blk.c @@ -20,3 +20,10 @@ __rust_helper void rust_helper_bio_advance_iter_single(c= onst struct bio *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 e9bea19d684b..23bf95136bc1 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,7 +131,10 @@ mod request_queue; pub mod tag_set; =20 -pub use operations::Operations; +pub use operations::{ + IoCompletionBatch, + Operations, // +}; pub use request::{ Command, IdleRequest, diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/oper= ations.rs index 06faf5647aaa..1be4695ca944 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -91,6 +91,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. @@ -110,7 +111,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 @@ -194,6 +203,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 @@ -210,6 +224,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 { @@ -268,13 +283,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 @@ -473,3 +507,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 66ef2493c448..dbe657a80324 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -156,6 +156,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CCA8537FF63; Tue, 9 Jun 2026 19:15:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032522; cv=none; b=uHxuykTZYHLZZYcWIBDpw1tJOo/8R6FY43up1ogThpaPlJp128+1n6NBeKGUwAiMvmlp1pg2bdOA6vijF5aJ73GHr32azoRw0atgYZ1Hw5nDH2KMWeF6vyv3wisVjVKonUaUjw4kA8d3IyO2FgWYrCyU4dl95HidwhfuYGvCehk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032522; c=relaxed/simple; bh=DgqXpos7C80lClbWnsjqwY/+BJEIchncSKiJddAVFzY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=LozoHf0j4/z0O9nCTHQ9MUWB7b7Sgi9L7A6mcRnZI6H4c7b8OPfBngvZ+4nl3POBmtWepDlIwjjPYVAnhEnYGiPoVX4zxU6w1DRbJ1EoKPrOdvzA1xAoDyHGWwdI8LTiIT07SFjks69NoIGYtFa9hOSmNW1fAaoFlINE05ykJGI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=lnGNp+U7; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="lnGNp+U7" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 90C9A1F00893; Tue, 9 Jun 2026 19:15:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032521; bh=DZGnmBi8mHh4p9y0/gnRSVsorGqqRp78wd3TOoNIUWI=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=lnGNp+U7lAm5TT7NragGdHyHIpj5W/ggjDukYYZfxoLkmwB/zHaT09MQy68txK4dP RXCl0hNOempY7pxNynN6Su//rED5AGL4Cd0Tc5n1yLrm5xHFJRN1SHv89LAX8ubz7a 1qCZWTesGzjQf/0kVeaDe0Aus51szcX4jATCD1gTxNbx0+LIzY5eLHxn+h74dlQamx ZuvDLtY4kQLwBQScw9NMu49hlkOoJpeNtQxL/0Ow4P505SyOmmGPdpgiJL4talvB+D seoV3GintN5UCwVYOP+EzaHkep7a+IFnFChahUdWddmZZqbcw0a/G8BiexrddvdLf7 WWpKSQYu59U9A== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:36 +0200 Subject: [PATCH v2 57/83] 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: <20260609-rnull-v6-19-rc5-send-v2-57-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1789; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=DgqXpos7C80lClbWnsjqwY/+BJEIchncSKiJddAVFzY=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGToQokKIiIK5c0rRnI8tRT1EM+BSmSLw36zh gEkP49VKD6JAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk6AAKCRD6UCkIqsW9 0KkYEAC5XUOUpCMv00hmZWvvpzfwCR6OoLx2WY3L4hGfe1HPjTDBH+Rs7HujdMlbqnVGEam8I7a MCW9s9FDf9sKjmX+T+bNrONZsuyPKQ8baL+2xE2zZcH8lsBd2aWU16gagSgfRKiNAUQGq+EcTwz /2IjccxOcK6fS4L9NIdKbQLqrBf9TSAL1+/HJg6m8Obl8BkqIxvFsPRS9+cJGrbjfvtMJ1CdakM SqH8o8A043MXmCjUlEpayeJyIpzhjXn2EUoEegAHXO9wM0F9ADFDzjA1ykrWwUi9kiGSr2WOX9I KH+dqr6K3zDHu4bzRrnyeAaY8oCJMM6wTrCW80OEePgqvhraTsKTf7KEYFLNQR0r/ZsW9FtI8vq yS7OWDzyWFIiDQmlB+ZtVp5jpw7PFgpqug+os3nbKsFhXEvYMavRRMgCkeqsQx2zQma6bJhHd6s +F/e/jgQxhqe0kqQIIp0FaZ/UhI4pV3F+PHRrXmmVqPJSTqGNplMi+ubYnfa4jh3yRE0S/R7q5m QfFU+95OZf6/SwiSA5EunitjI/O+1lNqwmDfwl9YFXZ4Fo86iiIksafQGeixEJPI3+wCYVRySv2 MoNQipcDq+e5qSu/cjpAQgZ/xpSiYaqJHSOV8dvnY0N6fd0KwScumMCub7RwiWN0nZR43g2GSKv Yovc8yWTHPWru5g== 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 | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set= .rs index e62dfd267fd9..858c1b952b00 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::{ alloc::NumaNode, bindings, @@ -26,7 +24,8 @@ }; use core::{ convert::TryInto, - marker::PhantomData, // + marker::PhantomData, + pin::Pin, // }; use pin_init::{ pin_data, @@ -164,6 +163,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ABAEE4D90B3; Tue, 9 Jun 2026 19:14:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032494; cv=none; b=PfekR4wBDQ1E45a7gSWylZaz5mtery5BO+rRh+5ZXwaa6OTS6YzrDiizSX3sECTWphP7Z5Oc+2pvwzs1P4cFLeUE+1u7/yXUlAcdfyUhuKX3K8Vvf5YjoqCfIXZl/MKeMIdMYj1X/SUVJuRMHrcU2KuiTXqvVBheZLZ8maLY8vg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032494; c=relaxed/simple; bh=QPO62OaNBIAfYDSzpo0UED4W/ZvhuueaX47bHuuBkyo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=bENFMTsruEAHtMXVy9ZcIrRXQ4gJM2IE7MyNJsJEljmGdTA4n7lH6KYesLLKUUW/CzkYmGBXXTZljQgEaLLyuysyvwIA67FK2g8mhOwbwTstKVySr6SZiZSjFo58mq6AsPJyls4NM03rKajisxsUh89lLw0+UrjcYu5g64Hwi1I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=EyN9ApUf; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="EyN9ApUf" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C1B4D1F00893; Tue, 9 Jun 2026 19:14:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032493; bh=UouDyN+bv+pg1LIszIBRKxLF6COncO6rj93swX5VWIQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=EyN9ApUfLL7GV5unCKn9nFGRDRGHNM1W5clT9YVptBVEqFjAbCGjIBHUbnHjVVhfn d5uVEg1A/JZvhLoyetw6k5KAZCtYdLSAbmbb8yJjzr323cvRoCQ7VkrB064O56K11E hS5S4KlWrLH9jGXzqfywsP2fv61lQQBKlpiMql83NeIV3k7vcnOlWALLQfPaI+tBAl SdIK0ERHvxsCdU1Vfeex19y09guma31EZbBvtwmLSe0ievVd7CWLznlewGDo9nSbUq 2nrW3VYJV5vMvkApzTI7Goqeiw7fcVjshw/Z6RJwm07L85IH+/VAXtgpprwxF6fcMV 2kPx10Q0vCYTw== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:37 +0200 Subject: [PATCH v2 58/83] 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: <20260609-rnull-v6-19-rc5-send-v2-58-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=11432; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=QPO62OaNBIAfYDSzpo0UED4W/ZvhuueaX47bHuuBkyo=; b=kA0DAAoB+lApCKrFvdAByyZiAGooZOnIPDXi/2iD3GYkBFxkVpu58B/kp89G/GaHEdTrd59Bl YkCMwQAAQoAHRYhBFeK2cjZZnYmKsBqhvpQKQiqxb3QBQJqKGTpAAoJEPpQKQiqxb3QW0MP/1uA Mf80s3MI/u69Kg8Js4t4bnVVcYYpKrOYyg5QbzOwZtuWMtPpcrT4p0tUAbZJXwV5jcallrMAijS 9/NgFGS/iJxUfsI94X9nDW7h7UFCE9Hf/h1sKDMrPtngmzlq8jbdgrPZQCAhkXb+TAs9DEd2y3N Opjf2z8Q99n93ZvTR6ae3urfkxV4A61jJXRoMI3zaP2XLy4AqMcmHPuH93OEuMBk0UJMHDCLJgo 5ksHNOnU8cK3NlXRX4s61C97aaHU17fkjB3uQrD1QP0m7vWk0sFy32vN458lFze9C8q6wEmA0VD /PIhUK9WAmIY+eNG8UsLRI0TknpR5yZDLZXHM3KCZLijMYMhyHVY3d0TEvWymy1sQyMABTM60pk PYx5hfX0F+fOuuv0XD5G20jX4PytlMbO7wgI675kHDgwC2D6jnvKF43Wg3eYkx118ZFJ2WDALD9 Gpa98cqnyRozMPuNdMQNHmhB0JMPzt81KCB9tUmDN/y66Tct91BSe0pXjpz7ca3IK06j/JF9Zo5 AVJ+Cu5BU1bjHtFk8kdhppC6OtqUFgpINvDqQYkEjubhKzk/EwxSSz2zkBEOEhWgb9CoUxOX2ME gZ5qPQ7MMPlOh3K70OA6GFpsiIh7fRhDabpH+XOrRW1l8jyEVQa7vSf1i/eJmS9m6zug8wmD0KX 8kr8g 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 | 133 ++++++++++++++++++++++++++++++++++++= ---- 2 files changed, 139 insertions(+), 13 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index f866595a263c..0637c1e0ab22 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -81,7 +81,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()) } @@ -127,6 +127,7 @@ fn make_group( zone_max_open: 24, zone_max_active: 25, zone_append_max_sectors: 26, + poll_queues: 27, ], }; =20 @@ -167,6 +168,7 @@ fn make_group( zone_max_open: 0, zone_max_active: 0, zone_append_max_sectors: u32::MAX, + poll_queues: 0, }), }), core::iter::empty(), @@ -253,6 +255,7 @@ struct DeviceConfigInner { zone_max_open: u32, zone_max_active: u32, zone_append_max_sectors: u32, + poll_queues: u32, } =20 #[vtable] @@ -305,6 +308,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { shared_tag_set: guard.shared_tags.then(|| guard.shared_tag= _set.clone()), tag_set: crate::TagSetOptions { submit_queues: guard.submit_queues, + poll_queues: guard.poll_queues, home_node: guard.home_node, blocking: guard.blocking, memory_backed: guard.memory_backed, @@ -498,3 +502,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::cpu::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 076493f92516..edb4ef53d6ad 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -33,6 +33,7 @@ GenDisk, GenDiskRef, // }, + IoCompletionBatch, Operations, TagSet, // }, @@ -186,6 +187,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 @@ -207,6 +212,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { } else { module_parameters::submit_queues.value() }; + let poll_queues =3D module_parameters::poll_queues.value(); let home_node =3D module_parameters::home_node.value(); let blocking =3D module_parameters::blocking.value(); let memory_backed =3D module_parameters::memory_backed.value(); @@ -215,6 +221,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { =20 let shared_tag_set =3D NullBlkDevice::build_tag_set(TagSetOpti= ons { submit_queues, + poll_queues, home_node, blocking, memory_backed, @@ -246,6 +253,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { .then(|| shared_tag_set.clone()), tag_set: TagSetOptions { submit_queues, + poll_queues, home_node, blocking, memory_backed, @@ -325,6 +333,7 @@ struct NullBlkDevice { =20 struct TagSetOptions { submit_queues: u32, + poll_queues: u32, home_node: i32, blocking: bool, memory_backed: bool, @@ -338,6 +347,7 @@ impl NullBlkDevice { fn build_tag_set(options: TagSetOptions) -> Result>> { let TagSetOptions { submit_queues, + poll_queues, home_node, blocking, memory_backed, @@ -364,7 +374,21 @@ fn build_tag_set(options: TagSetOptions) -> Result>> { } =20 Arc::pin_init( - TagSet::new(submit_queues, (), hw_queue_depth, 1, numa_node, f= lags), + 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 }, + numa_node, + flags, + ), GFP_KERNEL, ) } @@ -729,6 +753,7 @@ fn run( =20 struct HwQueueContext { page: Option>, + poll_queue: kernel::alloc::ringbuffer::KRingBuffer>>, } =20 #[pin_data] @@ -757,11 +782,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 { @@ -777,7 +808,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() { @@ -814,13 +845,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(()) @@ -828,8 +875,40 @@ 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::alloc::ringbuffer::KRingBuffer::new( + tagset_data.queue_depth.try_into()?, + GFP_KERNEL, + )?, + }), + GFP_KERNEL, + ) } =20 fn complete(rq: ARef>) { @@ -849,4 +928,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9B4184D90B3; Tue, 9 Jun 2026 19:14:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032489; cv=none; b=teZsNjuxveyEkto4GOeKqltqd7k6qGR4dpUBv1SmA0j6ydsjmDXACdk1JV+xVenn2Ask9iDSKBZPTMuQQPeNIYKr7ZOw37JHV74n0tO2F78OZRrlhuRrBcqkX5PTMadVi+n9obiKIqOlsYCNHxl4/FwhQoz6A8CtsZQqROYA9R8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032489; c=relaxed/simple; bh=UpX4gG7L/JrWoAycLPQmU4GCqRdexz34zVS4aoqb37s=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=POBtf4vEFQxhbebR1uXx9ZnvEFiJ+O8M7Imxq6FHW5CG3S4NIR6rmsn/1GKmJnzF3njLEnBZ0yuNP7rz9qkIh0Q4SvqSHQ1Et5CXF7XZdOikwZEw1x+LQiwgY16O1E0P7N9sw3XbCnZWH48DIFp8Zo4OVkRoTUtmRvVYHQR6WkM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=bWyNwyB6; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="bWyNwyB6" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 514EE1F00898; Tue, 9 Jun 2026 19:14:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032488; bh=RYYbnA7Q4dh7igQ57Y011k9mdRZ1hokxHpxllxzwi44=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=bWyNwyB6adFUkYFJfv842UmUDVq0mrAXsfUn0bPtMclR/u/umgM7MP+B06igld1bs A8aF5kdlvfIVyuifXdsLkQJ5BlWOp0vvmZp+qAXiSEB4tku4WBWdZrTBKKX1d2V4tB uePjAyngdh3WYxTybFWC9GYOOtOR9BhqiccMF4nQD44JnDrMHbVmJzWsU2TWuoZAPL JJHlWih9Lk6CLwvEtBCb+yU1e6thapVD3IXtcq5sJjNUJOmnodZy72Wuth/G1ChhtY WKL5i0PuRHvPbk3tsU08jIdiM5ftr0A4R59YU+sFsWb5nFqEnte6gSU10Rs7Q8tNDR 5aPXC7YSFa3aw== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:38 +0200 Subject: [PATCH v2 59/83] 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: <20260609-rnull-v6-19-rc5-send-v2-59-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=5324; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=UpX4gG7L/JrWoAycLPQmU4GCqRdexz34zVS4aoqb37s=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTqZ+E1LBttgu8oVGQJ213mTDB9ii02KysEx sxYZnUwv0yJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk6gAKCRD6UCkIqsW9 0FqcEACHRLYMIzbzsj377pL0AVUKQNA3v/IIbHjqKDbwp9jFhJpcFsShNwdi6+Hjxi08g+CN5NU 8zyMZmvY68JQTlN3j1tJyIWbb+YLnV7yEhnF+6Kciv0dToFZShpYI1xT0Hbv5CajqybY3yWWEyV Ev4DvIXdeyD3Gkx5ecpI8hem4cqX4aCGEtLMAR4XqPDhNWGAs5t3eFl3WKvFUlOCqApIlo1pRlb FQAhufO6qJ3b7UEvOgNfUa46TxXVOqImPa6Q9yMwa8F2OsaH0W5g2Xzm/9ilLUgw1abLPLYVwwN CMTPDvwL2zUjg9Ut27Rfri6ImqCQnDJzciTuO9e5JjO6PLTORMJvTwJ0Ni5sH40NaxDV4Pkisqx Fb7uBy9CE5KniRLf/r3cNgsLFhe+/bf2P+/tHJp6ocaX7itDUgrNKlbxlI7qO0H0f6a5VRsXcy2 azZCK/Wit2MRrSFWhft9Gpcw/9o/Yunh9KSHEy2di6neFyT6gIQbJO6XXCHtwuHuQw7qi80Ujo2 Z87t/RqnNrQOd76aHnJ6tes3nJPkgz+kSbKflkg4Rh+zhRlFdI9LRYCczPHhn9u70PmLBjsHezE iVfFX5lp/AjhEXY6fL/EBW0YXEkX3pNYRmv0gua/hty2NwyQOaBXqPfzpOmGDH0HIjLPgd99Eyk 9hyJetaPpyfZ38A== 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 | 45 +++++++++++++++++++++++++++++++--= ---- drivers/block/rnull/rnull.rs | 31 +++++++++++++++++-------- 2 files changed, 60 insertions(+), 16 deletions(-) diff --git a/drivers/block/rnull/disk_storage.rs b/drivers/block/rnull/disk= _storage.rs index 82de1f656f68..7667830bd616 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> { @@ -120,18 +127,32 @@ fn to_sector(index: usize) -> u64 { (index << block::PAGE_SECTORS_SHIFT) as u64 } =20 + fn extract_cache_page(&mut self) -> Result>= > { + Self::extract_cache_page_inner( + &mut self.cache_guard, + &mut self.disk_guard, + self.disk_storage, + self.hw_data_guard, + self.sheaf.as_mut(), + ) + } + fn extract_cache_page_inner<'g>( cache_guard: &mut xarray::Guard<'g, TreeNode>, disk_guard: &mut xarray::Guard<'g, TreeNode>, disk_storage: &DiskStorage, hw_data: &mut HwQueueContext, sheaf: Option<&mut XArraySheaf<'_>>, - ) -> Result> { - let cache_entry =3D cache_guard - .find_next_entry_circular( - disk_storage.next_flush_sector.load(ordering::Relaxed) as = usize - ) - .expect("Expected to find a page in the cache"); + ) -> Result>> { + let cache_entry =3D cache_guard.find_next_entry_circular( + disk_storage.next_flush_sector.load(ordering::Relaxed) as usiz= e, + ); + + let cache_entry =3D if let Some(entry) =3D cache_entry { + entry + } else { + return Ok(None); + }; =20 let index =3D cache_entry.index(); =20 @@ -172,7 +193,16 @@ fn extract_cache_page_inner<'g>( } }; =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= > { @@ -197,6 +227,7 @@ fn get_cache_page(&mut self, sector: u64) -> Result<&mu= t NullBlockPage> { self.hw_data_guard, self.sheaf.as_mut(), )? + .expect("Expected to find a page in the cache") }; let xarray::Entry::Vacant(vacant_entry) =3D cache_guard.en= try(index) else { unreachable!("slot was vacant and we hold the lock") diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index edb4ef53d6ad..0695cbd07f1d 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -719,6 +719,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! { @@ -835,6 +847,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)?; @@ -860,15 +881,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 634B84D9907; Tue, 9 Jun 2026 19:14:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032467; cv=none; b=fTAva7y7XYfP1mCVMZsyhzi9+XUwEnrnQzqEjeMxnP8rso7Kq+6lvidqEeqhlnLaoJWHT9PvWC5gaKkelvjbYZMQ2fzLswqn0QrUFzeTN8CYCNgw+szRprTyin2VwmPbLq6P/nfLjknSR1RIipqNYaYHCTJFRJSi2HjPjNzmBGE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032467; c=relaxed/simple; bh=fLt09kZRej3ghugW4pGzb6NaQOUJAAVoOulRRHYHWZE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=L5FqGE9ZjFeISPeFCinNGtTgfNDadRO/PdXKx/Ib9Z3hGLhqV+tNkh4c1HpdThpeetlJ9pX8csXwhp0WAU/GyMNRBGCsgRYnYswcnoli/cmLGCk12gCaRM/TcBk9Usn1lXIHEUu8RI0UOtqRFhr/UnGeXfo1DXha1wb7NnTkxns= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=jeEioYzD; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="jeEioYzD" Received: by smtp.kernel.org (Postfix) with ESMTPSA id AF62F1F00898; Tue, 9 Jun 2026 19:14:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032465; bh=rKt+bDrtCj1DKU9/OW9168On24x2YpG08Y7m0jWTFgI=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=jeEioYzDsBVnFZz1f7uZchjHzy2ohBa9zs7YA/cJD5kOF1lrs7CVudTkTHKw+6hV5 ihTcY5u8I7KzaFuX9xZgRL4zh/x9C6gAUqcWRDY/CWHHg+wE4aJKA/cDlR3Dc63bVZ px203EI+zb6fNR6NoWi7+gKpyo/4HT/ybi9l+wERZlK5JufBBc0VwnfJCzaMb+rb8k LYRf6wbOg8GZ10omn9TvIP0bkn09NN+46i/o+3pbiEPqzkJPCGG+2A4xxEfEjkRuJs D25J+L/BWlw13P0lQ4vfSDwS9Bi+9DNjiu7sKjoOqrcpYj4/+mdbpBzh9auoUy6aFa +ejbEWXuXUUgQ== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:39 +0200 Subject: [PATCH v2 60/83] 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: <20260609-rnull-v6-19-rc5-send-v2-60-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=7083; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=fLt09kZRej3ghugW4pGzb6NaQOUJAAVoOulRRHYHWZE=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTq7g3559Rf/Oj1KcJo1QtcNpHwg/t23j1fe 80+a4/wDNWJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk6gAKCRD6UCkIqsW9 0EWBD/wNxmiVpxV8R1QZyoHjba4mwA5SmLxaiVRZLxopUWiap1d7rLXHJZleSQqtdRwARZXPZ7V 2XMXiq0hUyS41VXfsW7mPfKTx3zxQd6m1E7cPXQ6Xt0TKGrNlfb/WZKssXw5iRTEqjU/2GKs2oD fHYxFiANODiKaOA7xK7L6i6+HBovzHlVm2+aXfgry1DLm3XLQ0UELzcOqCWlBP/p0rziOZY9sAj P1pB8AEymKThXy0WPRHUHbAKwYD3p7C0vD9NGDysXOQN7WwhFSYwt8oc4MqimlJtSNb86MSMHLV sxx0jJR9VBo87/mzgMNa4zUl/LmCRBJhy8uNr35/xdsqo1ieMs/O9a1JQb86r8nmLzvSl42mE0L uPCdWaeLPcq2gyhr6gCcQs8Ber/iQv6ZlO6Ebeh4lOS/RsHmiWzaKysllWEwA5+u9DsECUDKy3m 913rU11wXlUPcUi9+2L+MfcOnnSe0ZnEhMxURzqz9nPaZtNHtxhoACsKWtTBpI26fVUm4JzW4OV I9akjGe0wvT71EvQ25iVIz7Umj+deMF12pPAjfZWSzqK2YvjkG2KBOj+78ySP/97oUooS1tUb4j uGTmvuCBtPghmG/1Yxcp6Y3s5iZsd+XQwJOAMGZSRbuxJglhee0D+RBF8F0yBl6+m/UyYQFUW7R e+QntWip+kGV0uQ== 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 | 12 +++++++ rust/kernel/block/mq/request/flag.rs | 65 ++++++++++++++++++++++++++++++++= ++++ 4 files changed, 100 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 2a69c17bf271..7acda3ae9725 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -140,6 +140,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 23bf95136bc1..9bad95d79230 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -137,6 +137,8 @@ }; pub use request::{ Command, + Flag as RequestFlag, + Flags as RequestFlags, IdleRequest, Request, RequestTimerHandle, // diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request= .rs index dbe657a80324..84f8b2c17f85 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -48,6 +48,12 @@ 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`]. @@ -125,6 +131,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 flags") + } + /// 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 000000000000..01f249269803 --- /dev/null +++ b/rust/kernel/block/mq/request/flag.rs @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0 +use crate::{ + bindings, + impl_flags, // +}; + +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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ACD4D4EA37C; Tue, 9 Jun 2026 19:16:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032606; cv=none; b=jYgo+CG/ydg3K8IM8l9aiPVjWJs0fSWyGSDpNY+CgHTPtsHxYqQvoFW2tBj4fq2aN2S5+8HTe3QUf6vOLdgGhqzX89BJPPNrkuVw9aBJjV63gACfNU4CK8jLy4VAFMsakTZyOzX/Uq0gs6AbvEfvhaMDXO/c14JEx4PsbUTLR28= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032606; c=relaxed/simple; bh=qsUKYYdo8WvQ3OUet+Sg4krK1e3oT616Y3hnM04WUUw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=bboaW2N5wGxP2Trm72Cn5hLlH39TQR770AkMdHqaOPxcZZLMCjyb0YfqtO4NyyWUiIgXM24ChhFtzSFF1xGbqt+kKiSyOK19t0Uht6/cACBLKdIfJuWru0JN7FCcDMIKls+d8Jl4Jm83NWbbV3LCOVUTdO4aG+68aBBSHohSnhs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=U3DogMGq; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="U3DogMGq" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5DB021F00898; Tue, 9 Jun 2026 19:16:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032605; bh=GG7pmCmWFnp9jZvdbQDmbhPtuDfh4M2FF6gkwIDeo6M=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=U3DogMGqDWWdylVM8ryJHz4W/HitJOCJjFjN9ZH1NIs6FFemXBoa14zSHP1SBJS9d cr8IeXNqWGNOZROEB1cbSIInFGiOLLSSEN68l+yoclpfARRcmlqO6lCzpy0h7v/c0b cBiVM7CKqTgNbNC+U2Iet4SwMLGTQkn9Kz8rwVPvttMAmL0qMZnUU8sV43ErrofAiB AO8Oixsx47go3UAwuQOb8ZfV4Z/YdeHeIqntz2/3wuKqYwhCO5G1kBWZpv9o+YOj0J f5CnzN4ANDC7Z7mOIYAOV5Hg3UqgfGlzD4y1OLIZMy3Q+Y+x5UHy+O+D8UhdUbkyZ6 Dnjg13bJGwE9w== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:40 +0200 Subject: [PATCH v2 61/83] 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: <20260609-rnull-v6-19-rc5-send-v2-61-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=6514; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=qsUKYYdo8WvQ3OUet+Sg4krK1e3oT616Y3hnM04WUUw=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTrIKkFcKUQRufLll6LHGGeCFub5v0tIEjJD nrNF9QzWL6JAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk6wAKCRD6UCkIqsW9 0EMuD/9JZn80xXpH8G01ZlOumy1Qv9DSLMJusdiBBVjQ4ehhVnNNQmYZgMeaMJpmdfx2UMuji3w TagrVTOjw5j1cXfZM+Zv/AZ1HHhVcZ65ViBRMvvCPvPlE1yHkR1iKSNpO5uW+gQpb9cD0Pb6ub5 5mJSkdY0d7CD1fuPmjcrxzXHsgDpYdI87HDz9o4EIITcmUsGxx1JdfCUUFDFuZxvp/o378UIifn x9QDj6kyvewr8pWnzSd6N76zEkV3X32H5NXb6HR8Fd5tFDU/h8MaS58JnPnZax4yTIKQ5MeR5+N 0PFzibj8zOJdS1aYCLQaMFfpYurdIdV3AUFTDbrUv63pPp12ZBC8b3UxeJ84BzE0JNpe6dQnkQU IJaMBq2CLQc8y6JyHOgh/Lpek3O/eGkTCBaGnHSu9/XY+PP4AGAAzk8kJ9tN+ai+qe1uTKtfasZ lpFhvYqmO4E0k4y6p5fIoXP+2UxKXVfCltw+wFwotF+1/TDqIppkLoWuhS9ki6jZAHQZSwytYr6 Zv3PCY9AZQzElEiylUsnQ/GNR6LWLT+I3VMDy4F5bVXJMQwu0ZMwJ/2lUnbNf6S+kigkovlQejB 1y8cH6N/Xm3Y2jRqzUEj1BWOvtxsXXrwzdW6Lz2Ci6FPoIkTZvh8glP7lzEEypQPPmEgHZiUUGG ozwvqWcbh3qu60Q== 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 | 5 +++ rust/kernel/block/mq/feature.rs | 76 +++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 7acda3ae9725..af0330b9e491 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -119,7 +119,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; @@ -139,7 +138,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 9bad95d79230..7c346be843e1 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -125,12 +125,17 @@ //! # 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, Operations, // diff --git a/rust/kernel/block/mq/feature.rs b/rust/kernel/block/mq/feature= .rs new file mode 100644 index 000000000000..015d7925d5f0 --- /dev/null +++ b/rust/kernel/block/mq/feature.rs @@ -0,0 +1,76 @@ +// 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 crate::{ + bindings, + impl_flags, // +}; + +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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EFC44374A01; Tue, 9 Jun 2026 19:14:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032444; cv=none; b=E3OnceKiOxYgTKecZm1efkG4YEXfbZOguocXcEgYuzr/IsNVDMCb7+nafU6WmVh92TzAj+a+ogaAltx7SWHgr+VCIjtvjEHRU7YyQyGdt3xCPq/WLR4knFPy3abTpe9xTb5tARd/+xDPL0MDaqepNtWvHGnK/iN8Sa9YXN4zb6s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032444; c=relaxed/simple; bh=4KyhpI/ipNtBWo4VfFmPMWE541B+ewaPC0dhOaA747E=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=eZwiBXXGNC15PaRdJf1THLdRkVg56drtJrcMq1BbJNc/Bt0RWHNoAkwrEoZtqjML+Qh2r+dgp+3Gl8tuj1TqIAlM+WN/LM+1p2KEUEwXnACLZ35EAM69IjF80TtX2HsLdK1Qq3xttiaACvmIpyg0MisoHn3/I6e/EEmLhlhASZ8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=GkzrKIe2; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="GkzrKIe2" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 391DF1F00898; Tue, 9 Jun 2026 19:13:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032442; bh=07OL9kcN1ts1JXzzO+dUFMGY2+vD2vNCDEhWdbU7mT4=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=GkzrKIe2u3GY0JbJewS0RuhVNBjw7yRlApgAHzBncBKj0XnPuXad13OLnC7CRwaXG iMgQ1BzKirKGjzmSiTCjo/1Fs9/L8PDVtB/u0nDJbC37bCWViphuSoV3Cvd3UmrvDj pME6QGR8NxzFmm0HDLEQWprWgzzml6RxsrCNYnyYa/HOjeT5IflAHk4Gc+lccHR5Vi VOthBvAe25P+FT/ToGxNyMYpxjHPvckRZAzs9NU/Jcy5K5VwCW8qQLK31VymURNStI NDGvTJie/NIGPH6KxtT/P0RBcLko0qNzcD+/4wPU5peW1CRKFAmJ+OMnnA6UGIRTP+ XfcBzTDei3a0Q== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:41 +0200 Subject: [PATCH v2 62/83] 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: <20260609-rnull-v6-19-rc5-send-v2-62-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=2966; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=4KyhpI/ipNtBWo4VfFmPMWE541B+ewaPC0dhOaA747E=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTs54UDr8c4HG8t9kjVRyMVGYuBltQmu35Fx kzOC0ur7sGJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk7AAKCRD6UCkIqsW9 0MLED/9uhTnck2NzwFArogG6VlzaQyne7VPrBkfmw6Nv9UnoWpTNUkQfe/CsDpQGsQxLtCmae/z E1C6XowvFBrQtmwJ2gsPnNOe5tmkJuNQOAnrSXGO7sC5OeqlVJ0tgiEO5QGEfp1EQfsLt4qmIvu 3ZEioOmYoItty3EBEvv5H3WyjCSGEdo4pKOewZ+FNfE/rJj0G8lpwsZs24HlwBUaXB847igdfCg Ww3FMnCeM7z37GSPdxmB6OaqZQM91MVDQrBzEQLu3NYjs5SSWknEe948agXVpRo+mI1nr44JdwO ha/ZiVWE5418t6/WrHnzw6UKoj0ALQ00jtulT74X883V9UJqgpSMtGRT/Zx28YdWJCNlG+HkSiJ ZUnN7IyNz5oRD5heUlK+3ymtP/gsAougCbARycu9ziTyMX6e5af8ofNWexv8+7LHjiwhlcCEMp2 fUpW47RaTUxN7poQqnXe5JgXfBtdNmAk49uVTVHcFb2OE/FiYzqEJxqQ7fDA3vUS7Vw8fVGI8ti RAf2z/EcyjbByabxIQpiWqLQROy4g8G+IHdgVs+Bu8glPNHc0jzafKewS/RD19YKxcVZcwo+HR+ 37VssMaG+PTuLzYSpas3h8u7UljZO0tMrD6jZWTBYCgRlaz8Y1vLONVCt1MMtPYLPiZXT93duaC cq8ynYTAiX1BKag== 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 | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_di= sk.rs index eedba691e167..5367ca92b7aa 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -9,6 +9,7 @@ bindings, block::mq::{ operations::OperationsVTable, + Feature, Operations, RequestQueue, TagSet, // @@ -55,6 +56,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 @@ -72,6 +75,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, } } @@ -164,6 +169,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, @@ -183,7 +200,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)] @@ -192,11 +209,19 @@ pub fn build( return Err(error::code::EINVAL); } =20 - lim.features |=3D bindings::BLK_FEAT_ZONED; + lim.features |=3D Feature::Zoned; 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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BE9934D2EDE; Tue, 9 Jun 2026 19:15:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032511; cv=none; b=QEJjpK0PyvOsQKVWHfafG9hz2ItOQomGoa/g0Ok72MRsfA1QJvJdhjXHvy8EXYMZwEK+UX4SbOZGooGTQEACXlMP/BD7RPeS3cX1TcL8l5yxAxo8TJzSUL9eNFl+3ajjoJnV/VIO+CaBssGMCVKk1r21g6a6pYUJ2Z9RlgaWXew= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032511; c=relaxed/simple; bh=5+GUPrHafhi7e06njalxCX3qEtJzRjQWZ+jqQPaKrWg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=hjav/kASQxaweoDcANSNnt5QmSOarzkFGiOHHqzponrBSaV/J9LO1W1cb/YF4XI5lavm8EH0fTC/2awxY6bdr10NnMr4gqPlnmqDwxdSkxcEO71E9TqlJ6rLSH88BnYasq216NyXwN84n/b/Bk1MIpXuCBKBOw1l82BUXJQ4cPk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ILJ4ZGV3; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ILJ4ZGV3" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4970E1F00898; Tue, 9 Jun 2026 19:15:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032510; bh=64hvSmLNolq6thR/XE1WahjnyPiD4J8xEpWXph8IhxU=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=ILJ4ZGV3IqhZKH9TQnw/S+0cy9b56XT90Qwagujd3q8x0Dr0hMVaLhsl/3XdWRHVq E6koHy5xCOlv8iDzC1IwtRwNRrWU412XAyZNC8ne/hwGDhILazbZ8i3t09gJpDalis wJ0JqcmxEp07yCSIsELbbLuyWnexgc30pw09eSenYfYBeYqqBKkesHqwHL/6pKdmsb 4Sbo+qipLgem/kIYtOsmUWP+yPGyjBPF5WBQ5LvaCYixLpsvj0//8c6ssvZYqUxK/J s47/hjd4zM0UDTuo3X9bC5ZK1Bv+5XlqZ0pa4vGdSaN99upOFsPJQfyX19HJGdE77r hnZc2RwqC5h6w== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:42 +0200 Subject: [PATCH v2 63/83] 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: <20260609-rnull-v6-19-rc5-send-v2-63-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=2086; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=5+GUPrHafhi7e06njalxCX3qEtJzRjQWZ+jqQPaKrWg=; b=kA0DAAoB+lApCKrFvdAByyZiAGooZO2hozbAw63hLJZ4uFn9jmNCLHArRdSgJaa5AlVUL8B6Q 4kCMwQAAQoAHRYhBFeK2cjZZnYmKsBqhvpQKQiqxb3QBQJqKGTtAAoJEPpQKQiqxb3Qx+cP/2/W ms5Tmyn1Z/jTN07+FyjyTUSksEZV2eNtSnPfn+USQ3MWIf63EoTMejbL93puBw4G9QuRs9mezAZ jdtRrCILq3VtPqdFtdYRIrjZzeSHKZy9u2l+9QmI8uAIkMl6TwHxswM1XjGYB09WNl3jvTpysFa QLVzHdICSrtPOK1TvUCJUs3zA92cxKEY84XPRVpGh1jnWleCYI7ZEtiuzlLseEIFgGhv3oSguWL xdEKUluwi/j+kLqo1LMR856+wcpzl8ujsESFklsm5nBW6Wd1AygX8RFUTzD+DuemwWhAhiCzeO+ JEdcwfO3MNzTi3roXxSHuglsRaol4BJO5dV+JXx/q37ns0vuxc5dBdJ+4xh1w4IL3az5kB6Gdsi DABd7XeiTBCf3ZUicvObJ+p/w1jBtoZ5TE2SU5UH7c/DDnoBlF5/WIXJwkKWUvq0Mj1xQ1U1EF8 mfH26FpRa5F4Ol3QOooJVp5hJqtthHVXlyjJqdOuWi+A/PDtqQ1LUNuEALgRDO/ec+C13MDo1ZN MpzCaFWHq3AA0UEJ4qtTLO8TToZ6QmTvhMgYFDelcOErKCXqcpFViSa3qcKH9aMY61sNysMxuUE Ay2NQVXelzeffKyV8c8Lg2+DWJhhwbCwUT/XKAJ/4at764te4+OUcIfOUzMrcxMjfiNzpP9Urm6 +DfSb 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 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/rust/kernel/block/bio/vec.rs b/rust/kernel/block/bio/vec.rs index 61d83a07397f..82e89a1d17c3 100644 --- a/rust/kernel/block/bio/vec.rs +++ b/rust/kernel/block/bio/vec.rs @@ -102,13 +102,38 @@ pub fn truncate(&mut self, new_len: u32) { /// 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 --=20 2.51.2 From nobody Fri Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BF67740F33E; Tue, 9 Jun 2026 19:15:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032506; cv=none; b=eIhg+VMBfaKUEYVoS1VAcatkOR3VmKYruDcvDvOZMoOr8bcaOAVfdJehPkIF56Vk7gZyUpukxa8h/jqI0bhiHP493hkdTXHdUONT3XuTdTv32TqYYgaRdhcFw7KFYRtm5PInf0E+bc9chAgQ9q7EcoQ2VOsSSzouFIVUUINUjqc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032506; c=relaxed/simple; bh=5ZDAw2d4+wjEV9WBL84Feycxe65EY0rEcU1DrNk0Mw4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=jC0eUZBDCQHz/xqpuD8GriDBp4fKLN9Z4RXG17q8u2FafZZURBvuHwvL/wZNGZKSw6aWVhj6zrPf6TK+4WBS2QQduZXWYmGIB+QfSSstfOBeIaOGTN9P9msj35qsRXY20gDQxff3m/fKxgkmlCx2ikLfutfTHG8YdqHiiuc4QV0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=gkYwzbAL; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="gkYwzbAL" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C89CC1F00893; Tue, 9 Jun 2026 19:14:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032504; bh=q3myJXRVGS6pYA1Jmu7PlFD54lLFsKGWr7dOlhEJ36M=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=gkYwzbAL3t+TSX+8S2eqNxw5+5hJ2hR7DgJKhsh5ygCRD0DYuIGmtF3S5OWlI+DVD ILLbpjWp+pDxlHlOYGcnblEmUuPPIRTHjRvTiFOyHiunLV/QJOnjkxMjAWwRA9TK9i KWbpl5H8TVsheFt6uMxykY9rMLURh3tpn5+QokX/tXdadPoqfceb8qqQnSxLsoDBmv BqyjGIiAteBuMUtxlAN6yt8up8N2DksEUKxVNizJE+q1L05eomwtP7QxORbiItN+OH 3ggY4CnFywEKYgfiiKjHDLixFHHp/OjC8w6hWJw97swZxczPO3y0UWEup+I0fEMABd JKkTHWq9iIPUQ== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:43 +0200 Subject: [PATCH v2 64/83] 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: <20260609-rnull-v6-19-rc5-send-v2-64-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=9701; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=5ZDAw2d4+wjEV9WBL84Feycxe65EY0rEcU1DrNk0Mw4=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTuAz1cLMPwLLtPPjzuLS1WhD7/D4OChQeKr pc/tbbTv1uJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk7gAKCRD6UCkIqsW9 0J+LD/9XjmhK/H/XcSIv69e4TN+NKqL0T1jXIEseBLH5bopZMhMPXWkyh1lge+JOmGGhxTVWf06 v1sTI+3NDDjThUEBexq+7f04qpcqDniA5Afj0/4q80EkTnrDxvxvJ2K6nInRKPOQlKXv8PzeaEO xNB0HJ4a9Q/kkqA4SKicjU2Pe7CJxNQ5C4pcveQXfk4y0lvHPyFKUV8XpZlp8W+XVMmBRwxG3qz ZVFn2dEEMFRumjbRpmX4RR4DvXCn8RbjAfNX2pPToQELTWesuFzNuIw0nZ6pJ9TbbSN55DnFjz7 pNu18c1OrN9SDAZ5GoGESpohSljq2tgIsr3vWJhJ7Ca04rreJC3JmFFCxYu0wqQzDScSQNDe5lI Hv3MI0PMPcwh5Fu6b1PyULbRSJ3zdQcUiOT4o9mLA12NFyT6FMw6LqkRJZAazr8vi7JPKvajAwW P5gLITw5nAK0qaw18VeDRmOy2bLb1zkUD98+76ABmpu3wwQ75avD6rtrQtHYVz878xBn7EJwTKP A3LQnUm+8l8IlxbBcvzIi7ffkuzxmXRzHBg7qnRrLNPfEpp4Ol1O1nd3Pyz0b24W62iKTipVbW/ MSC9TH0wSw8vZMNsICXBL4zx2PJ/2VU9q3p43i9asDn2CGoD9gJ417TwpytDtCuRJTgTtWfnxPP 2c0V/Y8qBmsVUgw== 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 | 41 ++++++++++++++++++++++++++--= ---- 4 files changed, 58 insertions(+), 11 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 0637c1e0ab22..8195d645ecc6 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -128,6 +128,7 @@ fn make_group( zone_max_active: 25, zone_append_max_sectors: 26, poll_queues: 27, + fua: 28, ], }; =20 @@ -169,6 +170,7 @@ fn make_group( zone_max_active: 0, zone_append_max_sectors: u32::MAX, poll_queues: 0, + fua: true, }), }), core::iter::empty(), @@ -256,6 +258,7 @@ struct DeviceConfigInner { zone_max_active: u32, zone_append_max_sectors: u32, poll_queues: u32, + fua: bool, } =20 #[vtable] @@ -322,6 +325,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, + forced_unit_access: guard.fua, })?); guard.powered =3D true; } else if guard.powered && !power_op { @@ -515,3 +519,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 7667830bd616..4a9bf480221f 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> { @@ -205,7 +209,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 match self.cache_guard.entry(index) { @@ -239,6 +243,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 @@ -256,9 +266,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 88dc9a2476bd..846269d31c63 100644 --- a/drivers/block/rnull/disk_storage/page.rs +++ b/drivers/block/rnull/disk_storage/page.rs @@ -15,6 +15,7 @@ uapi::PAGE_SECTORS, // }; =20 +// TODO: Use rust bitmap static_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 0695cbd07f1d..c3126b923367 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -191,6 +191,10 @@ default: 0, description: "Number of IOPOLL submission queues.", }, + fua: bool { + default: true, + description: "Enable/disable FUA support when cache_size is us= ed.", + }, }, } =20 @@ -267,6 +271,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.va= lue(), zone_append_max_sectors: module_parameters::zone_appen= d_max_sectors.value(), + forced_unit_access: module_parameters::fua.value(), })?; disks.push(disk, GFP_KERNEL)?; } @@ -307,6 +312,7 @@ struct NullBlkOptions<'a> { zone_max_active: u32, #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), allow(dead_code))] zone_append_max_sectors: u32, + forced_unit_access: bool, } =20 #[pin_data] @@ -422,6 +428,7 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { zone_max_active, #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), allow(unused_variables))] zone_append_max_sectors, + forced_unit_access, } =3D options; =20 let memory_backed =3D tag_set.memory_backed; @@ -439,9 +446,10 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { return Err(code::EINVAL); } =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, @@ -474,7 +482,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)] { @@ -553,6 +563,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 @@ -561,7 +572,13 @@ 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); =20 // CAST: Page offset always fits in 32 bits. @@ -569,7 +586,11 @@ fn write<'a, 'b, 'c>( ((sector & u64::from(block::PAGE_SECTOR_MASK)) << block::S= ECTOR_SHIFT) as usize; =20 // CAST: Casting from `usize` to `u64` never overflows. - sector +=3D segment.copy_to_page(page.page_mut().as_pin_mut(),= page_offset) as u64 + sector +=3D segment.copy_to_page_limit( + page.page_mut().as_pin_mut(), + page_offset, + self.block_size_bytes.try_into()?, + ) as u64 >> block::SECTOR_SHIFT; =20 sheaf =3D access.sheaf; @@ -632,6 +653,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 mut segment in segment_iter { @@ -641,9 +664,13 @@ fn transfer( let length_sectors_allowed =3D segment_length_sectors.min(= max_remaining_sectors); segment.truncate(length_sectors_allowed << 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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C53144D2EDE; Tue, 9 Jun 2026 19:12:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032325; cv=none; b=XAzp3Jfk5YwYIza+gIxhxi9aqNUt8B4MZXhcD7Qwpvm/jvBY7FOIVsQ1/SMcUp4E+UFaB2XqYM0ELdt7ByvAPFJAeN/PeUjqwM3fKWbFtHCFYwk6eHOleIvDsPcyOCZAbyidX26Do506ytiCpcGdpu8Khnw/EDvKLpAULj8kiss= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032325; c=relaxed/simple; bh=W0O2CczxZDQEXRc15dJETKMQ+dkKiMZR7U8tm411g7s=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=RZfOPN/gL5RJhemES6nb87deyXPZPmiBLYG4teDjSOAplr+5pw2IgU/Lsn4kPBZrwCPoeiY0pfkuzddB6RhmVgm9oi3VDqc+5kiqzuy6FOE3r+xnu3h+gOJQBIvsUniKv2DdURHD8N+/gics+e7rw8XdXnKsm5+UlIXD7A9/zWU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=KWh84ZQZ; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="KWh84ZQZ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1CC0C1F00893; Tue, 9 Jun 2026 19:11:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032324; bh=+YoDlaRXxHkBdL9A3Iy3eyhP/tykMNVFwls11O+PbGw=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=KWh84ZQZbd6+abJoZIFIxRUn4c5TrwdxMM/86SAfgZTUh6LojigcMZcS5+SAofC+j 43O9jA6Q0PVLSoLmT5cekTXpstMmziu34IY4oicvKtd4/I28lKCaBG4zG1eWiBasq8 ZmXp24Rb7GmggbZSBZPaT217AwW0dz5zs8OnOgZ+OV6xFD11SFStdpDWVzIkolj5Op y557TTH3PeayehmmC7aY6efh5NWG00BEEznzp3sEYMtY2EK27SPUMGD97Z8DfGPlO5 V1XXt3jl+BFVTR/wq5KnKladB0fOdbR9OpNv51kuH3rMDxygWm+MUVtecbA37wwcJj 4KGuinCXguuQw== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:44 +0200 Subject: [PATCH v2 65/83] 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: <20260609-rnull-v6-19-rc5-send-v2-65-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1759; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=W0O2CczxZDQEXRc15dJETKMQ+dkKiMZR7U8tm411g7s=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTvQ1j9ShlTi9k2Sxb2Wf0dabZIrqUisKav9 O2siS1ba22JAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk7wAKCRD6UCkIqsW9 0FeWD/9yDXxLKGaEABjjYFcFzRClbH4obCRuAURGM/QGNN9G68bjR5xvZgZBY2ro33xcNTNJAJL 40PULazu/7YNQvvPEommrLYYtyhDfbp/IgLzBIOTzp6jwSAfSHAIivLG98wyH/amIbR1vH8yCql SBDdg/alHTtXGCU0q2i+pbsClfWDluhNWB7x4H4UG8+LC5nW6Jud2OXoy3VYZcluv7qanvjUJUg itVhIUBCc0CsZakZ3bAicLz9qx324CAigGnYNBC0XecSxh0jmsIopTMS5Vn3bSQjY8ffpVWI+zz +3K99Hgnhv6uX1VODIAC7V7dp13ZPw9pq1/w8tFFhXa83i42bKeqH4EMDwtArYia/r5UN+8Hxru W/jUOiqtMyP+/6yvN2WWEGIDkCP90nS/ITnMdwqplALX6quStpjIrtpEWHDcJvqGupxXlRnmxoS vxlRuEN9Hci+7AlgG121BQDKaTq/6gR9fZfOpOSa3pO37+JcWv3qNI9wGHa6QY8xtYDF3RWWaAs jOM6ahj8m58s1GqtrBlSzSyFs1JhsSdZoWOgQ1VHhOxANpkNHBqTIuyuEzo9Krvs74mm5LO5KEG jM8F3FHT/n5ck6W8SkIAJahMQV/vvVUYxBdGI6n1+VJuZqib6n1gmfGQWb74cml/ePNiQby0wb8 g+x3smGKALfVxkw== 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 5367ca92b7aa..a50ba7b605d7 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -257,7 +257,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. @@ -341,7 +341,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>>, } @@ -363,6 +363,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 18B8040C208; Tue, 9 Jun 2026 19:18:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032696; cv=none; b=MEtPRnpDLdyw6pTuZ8YmpYPvkNOmeFYwd0QiPkJgWx4lLiIgJQM3eEhx8VC19l+GsSyshvQLqnqCFIrs3aUDLq8YuYzzOjVTKMCQPdr4aqFLrX0GUJK2DSzpPgtKjtT3tHwRZaJ1iDXCIXH/7MRsiYr4sFSd9VFqwMSV1d5r+Wc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032696; c=relaxed/simple; bh=lmxiQlFN4F/4ZyV264h2eAjCgTfWmHi9M8lfy1C7H1w=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=r5xYU2kXhIYAwo5mRnpcefYbP3EqAdBJDVNCDCImvaz7Y/HBKg56plbzbmqFXKamKDMii8ZYXtd7uQIp8m4Tf/+wHYOxSfywFBbzYD0xKKRwF+Ig8IYQXrjP6G7JpLsblYWw3JPXuZ1KrRRLzbttecXVNVbuaxSaWl6ZdfAhekk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Avj6ZWt9; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Avj6ZWt9" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9F7501F00893; Tue, 9 Jun 2026 19:18:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032695; bh=uscdNJwwl8XqBYBhj9sseM+ELakppynPqvLUaYkMsEM=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=Avj6ZWt9pyUdKva4jyLrRWrEGIc5qrLQcx1ciDPuZRvwlPxdmI+rdUb48SJ3Cytjk U529xgXBl/pmejgxhzzGCvpCLn8XYDOIKug3ZUHdtGbztugR3slzzeLs/NScyu2Bua 0GWaP64jk/bcpXYC51pJynhkoMj/t5VdA3viPk9JiqVFuIgBTA26EgN7fxGUYKDdYv 9JRcuLKV6Eo0u7OWd0Wv4us8J2/Sd4IPpq+UAD9fE7m/ptbvJt/bkZFBBFJ+7yjH5V a0XELaraRhArqjPXP+zACi/VPsIv05NW2S/1OQOV9zmmVy9Tg2jyy1T/lPF1lS7ARt QVQH1dK4yF5QQ== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:45 +0200 Subject: [PATCH v2 66/83] 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: <20260609-rnull-v6-19-rc5-send-v2-66-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1334; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=lmxiQlFN4F/4ZyV264h2eAjCgTfWmHi9M8lfy1C7H1w=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTwVD2rB/lbhBXJY9WqF8y/XOfulLI3AS2U/ 50nQzCG89CJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk8AAKCRD6UCkIqsW9 0KpZD/9Etz799ndlt+Bh6QO59f5AYdztn2sHE5grM0Rq44vTgyQrPIcUtZ0k7gHcl9ORVH+I8wI JsLwUzfhmM0YMr8e42dMLGbaZizg50DtAgDNquDXaDbTyyBa3eVSrcgzEEX3BYWDnr6aHong4aQ iXz/x6pvWXstmacukGPKrcoHwL3bqLa57k8IotdrqAhkoNhEMe36n90krLBxrydyPWzGira8QQr NZkPon5yJR4rb3yCTn1JC76RZ1l3lUATCz0Ht1Z/rcyHHM3SFkWp6tiOh6YG7+eHtxfrczoVVtV 9KsMcvW2WnxX65N/96qL4kFSh6h01gOkJ0Ip1E23+onzt3VyGS4S8Xjs3s4wXP92hXnO3s+/dOv J9INh46w8FRpbHngW+g7ncoGB284Nyek22DW5F72WzkRwyB4sOevBaxuTxWDgatBH8U/qwyunUq NjmaQBTOjui8AEBizBKibIcF0hlGFW/v9aUg8dDAKnSHapcT7KhUixSwaiuZCfgJQtZgkmpYFhx 9iWpJ22epXsC7zkmsWm6yCJRrDdDXqufL94+6QSEeu3PAr5lJZjsc3L9oRdCQTxbV2QvP34Q4/L Wupt5tIpG/XJv0eb+1kvVN+E5vJ23uQUHgOCByzlxPxVKG1qGqlWyN/sX8Jed74UiEd6ln3Xnz6 pgSBJ+DnvEbhkEw== 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 | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set= .rs index 858c1b952b00..e89c76987b54 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -170,6 +170,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8DB0C408A19; Tue, 9 Jun 2026 19:14:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032455; cv=none; b=fMzManZLTT2uZYLxSHfPU6e5Qx+NGvQykcBGY1a2cA5wqP63TELxBhKRbpjzvskX9qlU54t9FMoQedrT/Mhmr5nalKi5oY7/knZ414Lzrj6/mrPXj7tdURVvJTAxxQKwqE7IhTvkU7nqxGRZJXQlvGNFHydzYpVpveYRacBrfvM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032455; c=relaxed/simple; bh=7zQettVVHdmYaGqWkJYUonz9KPKSfJ5OcoVNXtzcUHo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=qFUOMyMaT+DFgKPK/JzScjdXX09sjCB240FVDy+KYXZNOWxszkQApPwFpS9mX0CoFeYzfbS3/HWHvvBuL5VFT3y0a7KE75bOM/ZM8O82XOtFBhEIWVMdw5DeYWrvw7Nyhprr6EGm8YTlxFgMpK049GQ4eFCsPXUeQ77HQ7tFAss= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=kSgxWhKq; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="kSgxWhKq" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 306531F00899; Tue, 9 Jun 2026 19:14:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032454; bh=jxOzQ6UxSv5lSwC8v6zCHNH1kojG7yWT9JQ+45S4GSQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=kSgxWhKqzTG8qOB3fXj50ITpeAwOaC/pRYz1PppMxkMYDPPx0nu2Cl12jIU8/ctq1 lvg9jQdGIhiVwX4dEDE+G7rveBOVMJAM48+5CrRKr3nTRiwmPDIvij2dlMfSaiUuME TIadX0aBwnEPF91l4ocBBNLl6DSM+Zcyk5bHao6Mpa52xFkWGlc+msEKdtV4BCYlH1 H1upFhHpU9vKCzDkUvkG/SfywICCp4C/mtQJNum2baFWf+QpjAJ7rOJNycRqg5Yl+z oMveEJwIXmTlx0p3wSRsDeyhD7sba+F9dEgCTjEOE4r8ph3Cm8D40YbvNuOxvqhTHT 66EOKtknms32Q== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:46 +0200 Subject: [PATCH v2 67/83] 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: <20260609-rnull-v6-19-rc5-send-v2-67-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=11986; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=7zQettVVHdmYaGqWkJYUonz9KPKSfJ5OcoVNXtzcUHo=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTxT9gH0dHBCKVhfGgJYCHI0gPbLH/3uApdr Pj7/fHqrUmJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk8QAKCRD6UCkIqsW9 0D7ZD/98jV+hTmerVF71nmBv/TWQvvm9FV2jfHts8FFuwWnBEOlK4JF1WXexgKfwSi5XGmP350t jcRIuZZmCFEVuuSI/awo/oDxU40sGL1y7oAEhPR/v4zUm4WV3f4xbEQ8C7CQDx0tLvPrT7Dm/UN LWIR5TLicCXRADWdpiTlNAJEomvqmOpwMxERISwevyOHaBztFsV6q++3gAZlYpfVIn/zTavG/lp oPzkOdiNfgkMbNAjiBdPh9Yly7rhQEeGkiu8L/mIJ04WPiZHSA9I3Zd7/b5JNb849k7G7+BPCDB c5wvPV3S4+ugFENKvvvphKmKLRrJtfb306tyeSA/tErYpu5A00ftUL10JB1X4clK83XwMxJbbq1 AtGuD13CbLXneYAvIeOxC2W8v6955UZiGqV+ytzILjcYVbeU5B3c5gCIG3/cDGkfOwJQpN1wo+V 5FfCAxu+gqgpiaxgGVEHBwa/A4nzkg9l4X3L9oAyQAUaW2u0ouukoIvokIxDv0v+SdvglLBd5iP 2MuLy01HVGobAFqlc9NMSE3nEeMcY84WR8AZ4vIA9VZVZddSDrCf728rOcvUJ1TOTLtv/42RW1N cvVr1UW0Oyud30wNVxt8dbSsPn/icsggyX1bGUaGUIZUJY6brtIb6qSMbOOnu2wXcWyNyFLBSvc S2hEAEupv9/HQVg== 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 | 117 ++++++++++++++++++++++++++----------= ---- drivers/block/rnull/rnull.rs | 46 ++++++++++------ 2 files changed, 108 insertions(+), 55 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 8195d645ecc6..d9246b9150f4 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -148,7 +148,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, @@ -169,7 +175,6 @@ fn make_group( zone_max_open: 0, zone_max_active: 0, zone_append_max_sectors: u32::MAX, - poll_queues: 0, fua: true, }), }), @@ -236,7 +241,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, @@ -257,7 +262,6 @@ struct DeviceConfigInner { zone_max_open: u32, zone_max_active: u32, zone_append_max_sectors: u32, - poll_queues: u32, fua: bool, } =20 @@ -310,9 +314,8 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { bandwidth_limit: u64::from(guard.mbps) * 2u64.pow(20), shared_tag_set: guard.shared_tags.then(|| guard.shared_tag= _set.clone()), tag_set: crate::TagSetOptions { - submit_queues: guard.submit_queues, - poll_queues: guard.poll_queues, home_node: guard.home_node, + queue_config: guard.queue_config.clone(), blocking: guard.blocking, memory_backed: guard.memory_backed, no_sched: guard.no_sched, @@ -337,9 +340,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); @@ -363,38 +374,44 @@ fn from_str(s: &str) -> Result { =20 configfs_simple_bool_field!(DeviceConfig, 6, memory_backed); =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::cpu::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::cpu::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::numa::num_online_nod= es(), page + this.data.lock().queue_config.lock().submit_queues =3D=3D kernel::= numa::num_online_nodes(), + page ), store: |this, page| store_with_power_check(this, page, |data, page| { let value =3D core::str::from_utf8(page)? @@ -404,7 +421,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { !=3D 0; =20 if value { - data.submit_queues =3D kernel::numa::num_online_nodes(); + data.queue_config.lock().submit_queues =3D kernel::numa::num_o= nline_nodes(); } Ok(()) }) @@ -506,17 +523,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::cpu::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 c3126b923367..6653db5c069b 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, @@ -224,9 +227,14 @@ fn init(_module: &'static ThisModule) -> impl PinInit<= Self, Error> { let hw_queue_depth =3D module_parameters::hw_queue_depth.value= (); =20 let shared_tag_set =3D NullBlkDevice::build_tag_set(TagSetOpti= ons { - submit_queues, - poll_queues, home_node, + queue_config: Arc::pin_init( + new_mutex!(QueueConfig { + submit_queues, + poll_queues, + }), + GFP_KERNEL, + )?, blocking, memory_backed, no_sched, @@ -256,9 +264,14 @@ fn init(_module: &'static ThisModule) -> impl PinInit<= Self, Error> { .value() .then(|| shared_tag_set.clone()), tag_set: TagSetOptions { - submit_queues, - poll_queues, home_node, + queue_config: Arc::pin_init( + new_mutex!(QueueConfig { + submit_queues, + poll_queues, + }), + GFP_KERNEL, + )?, blocking, memory_backed, no_sched, @@ -338,9 +351,8 @@ struct NullBlkDevice { } =20 struct TagSetOptions { - submit_queues: u32, - poll_queues: u32, home_node: i32, + queue_config: Arc>, blocking: bool, memory_backed: bool, no_sched: bool, @@ -352,9 +364,8 @@ impl NullBlkDevice { =20 fn build_tag_set(options: TagSetOptions) -> Result>> { let TagSetOptions { - submit_queues, - poll_queues, home_node, + queue_config, blocking, memory_backed, no_sched, @@ -379,14 +390,18 @@ fn build_tag_set(options: TagSetOptions) -> Result>> { flags |=3D mq::tag_set::Flag::NoDefaultScheduler; } =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); + Arc::pin_init( TagSet::new( submit_queues + poll_queues, KBox::new( NullBlkTagsetData { queue_depth: hw_queue_depth, - submit_queue_count: submit_queues, - poll_queue_count: poll_queues, + queue_config, }, GFP_KERNEL, )?, @@ -823,8 +838,7 @@ impl HasHrTimer for Pdu { =20 struct NullBlkTagsetData { queue_depth: u32, - submit_queue_count: u32, - poll_queue_count: u32, + queue_config: Arc>, } =20 #[vtable] @@ -970,8 +984,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3AD024DA54B; Tue, 9 Jun 2026 19:15:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032528; cv=none; b=U20jTQaG2MQ4QkyeXdFf1PBRVhRWFkA35/o5X9jpevQEAqm7Emv+5jO6Nq65g7yApJJpmAvqXFvRYyzOyzXGC4ycLKUVyTDCqFiNHhuESQR6ybS8+wGqCpHxN+B1wrsQU5JjZOOioQxvbvZBOD3MGnmCIOjWeJhRAmw2d8j1BUY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032528; c=relaxed/simple; bh=3zWHoh8lTGLJ6X5+DtdbMgAgdt2Ms+a+4ebccRKtMVI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=FUgNiwPuhRJ1zRVXKF7mXG0X8TfRsimE143sS7ZpuO8sUVeMczb+eZ0e262Iz0dw1czXb5m4ua/PJtzXvF8mKVcVjZ5ByMTHalfcsv6qTJ+SRLng38Bd0FGy1kvGnKA/yDlqHp2Y8fpPTYdHG24TlON7tfJ1nJC3QoUGxb5m7yk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=KFB0bEyM; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="KFB0bEyM" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 599521F00898; Tue, 9 Jun 2026 19:15:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032527; bh=A7C6J/4qobSt2YJVzP9B516t3v7OvSeSUX63/tzsSD8=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=KFB0bEyMs7EiTXFSctBJyJparalcPcsZroXU0a4EB0GdEUSVJQ1jmm52IMGG/3Vi3 615Mi+9jnabtiCfiUIdE0HHvBQzXk+eVl2kDCU2Jn829Q13GPFbwUvRWMMoyHJFhNe WBFpvGMDMX/B/MAXYHe0J6rqBAqeEfhflFt0xgQGtQPwTKI3MjMHBRkHA1TuvCgaLe aH+c8py+8ZbIskQkBfi6q6HuI648MgN4TaNNmfpQW2E+kv+WZr/ic68NDKSYK0vCSX 3yMAWKPPy6efyPU+hAgNzcs2cy0w2F0NLB5WEeBa8IYgwMg5JgnD7AF17aA6rtp/cf /1lND+VxKezog== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:47 +0200 Subject: [PATCH v2 68/83] 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: <20260609-rnull-v6-19-rc5-send-v2-68-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=6100; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=3zWHoh8lTGLJ6X5+DtdbMgAgdt2Ms+a+4ebccRKtMVI=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTyrI+QXei6rTMP8iiiEZKr1484LBHMD95oG XNTJoWDgPKJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk8gAKCRD6UCkIqsW9 0OPxD/9ibPYIYnDR73NPhBD6/PgXSyhfABFOOMxGgykL5SxPrfRMpIsel+KLRDMxOtTwfZgWPqm 7x4eXk/w0qBS7J1T4uP1v997OT3s7wNqiANIuX6w4EjLVagJ4RIH5vG+KYhBRcB28QURFuvYNxN 44u7vqyn29dU8ioYK6DSBn6xB3NQILfY0/W18UedMoGKU2AUmptQKhDOqAO5iy9VJUloWW0wZLe 7nyeHWAix0ofNz46wCcan2obRujcatIVZx+V0+2E9VbiBlPc0C01r2ELIwXImeWG0MWoyAeFTPO LrZsuuMMITkKmD9Mdy4UsKtc/n7aFKgRVuRuUNqfh4tecyVBphzrmE3iETMC8PLo4BddR1MrDF9 Cc06NF5KA1jiS9FM5FwCOrr99HLkOMNGfJCbiDgPPgvENDjTCdd5Iz7A5s2y9fCM6IUQ8ykUy7F bu0AOfVa3f+5Y8mrqunMsHHy5vEJDHjoGTvXeXcxBa0x/pjgmPWfrF6JQPQ+nDpsvUNn9GhczhO Weyrad1n73SqnseczMeKxYX08ZNWHG639XShzc+XdTlHq55IzqkojBqOo0gtCYRh/0h4RILIqys RgA1/8PhvFvL4e1N9lo2ifsQxargLpvuwK4YsnypCJ9HKZTXRapEGnZEhHX+7WTR5vFVqTz86rI kld1/DFJXWO1kXQ== 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 | 119 +++++++++++++++++++++++++++++++= ++++ 3 files changed, 147 insertions(+) diff --git a/rust/helpers/blk.c b/rust/helpers/blk.c index 500e3c6fd951..422289d617ae 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 7c346be843e1..e8f0d03f2ff7 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 @@ -148,6 +149,7 @@ Request, RequestTimerHandle, // }; +pub use request_list::RequestList; pub use request_queue::RequestQueue; pub use tag_set::{ QueueType, diff --git a/rust/kernel/block/mq/request_list.rs b/rust/kernel/block/mq/re= quest_list.rs new file mode 100644 index 000000000000..82e6005126f7 --- /dev/null +++ b/rust/kernel/block/mq/request_list.rs @@ -0,0 +1,119 @@ +// 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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 95EC14DA53A; Tue, 9 Jun 2026 19:13:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032391; cv=none; b=htVVlVcSH8LMWje3zW/j2TgsZ5+uLgm6tR+jkaEYhE1bDpwd32Q2jb1tc6kbwbfhuOE6uui4zs7KpOGCU/MKLareeWG00Mm7G71t5KiFLQlaqnQtV2y7ri1iQyx7p/fqP9TrrqUyiXe5yZ51FvoLV30SQn5ZFv5psdW4acbXlew= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032391; c=relaxed/simple; bh=lKoE7hlYKrSKMR83Axydp/oZ8JxoEi389NiL+WfBOgg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=SfmXWL6K90nzo/9tCJ4zgTLRfDdxQQ0M/HmqvFwHLXwuB0Ep8eOeTXMemguucTwYhtCMTneMSDvXMD+dqXVONx3R4AcYLcPg2PVsu63s9nxosnUbNGXgDWYia/0j8o4wIl8+3bIiFtvKGmQf0lA0hraqSk2AEu3Y9gj9OxeGiAo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=QtO4XsWE; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="QtO4XsWE" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5AD981F00893; Tue, 9 Jun 2026 19:13:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032390; bh=dn/nL+HhqyPcXgr06oBLyldQr9p2+VExm2qADQKL/c4=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=QtO4XsWENZS24EnRtrb4kQ3MF43G5qGXqVTqEsOStmos6LyIm8rXYbjg3lMFCJUOU ihtIQzB+NNni2RjRBE1Qfv/ob2U1TIWZPG8WOCFxX2DrSlzCwKd35Ca4aL7tuxREix Y+b6m4mcolq3oWycMqsbHU6eHuuCBsLhNNHpoCHSnk4Pj+P4cjU1PF5GdJTD3uczsZ lOBfFiuPyeTvOzqyqQUVZ0uSqO4pG6J06+G+Y70cH5//tRpiUDayhFDW7y9l4Ixip0 hT3WsVKSf45F6NNBgyaqg8pu3E9WQ1dSduQQ4e4y7o2vdbPF3dhdme52QfBBuiMu3+ XU1QjPGs0022w== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:48 +0200 Subject: [PATCH v2 69/83] 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: <20260609-rnull-v6-19-rc5-send-v2-69-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=5877; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=lKoE7hlYKrSKMR83Axydp/oZ8JxoEi389NiL+WfBOgg=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGTz/hgE7XP0crSwcvBFBVmKr+7qEbSg5PbSp rkcv2WYM66JAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk8wAKCRD6UCkIqsW9 0B3mEACAadrp+g4DQkLAhdcEFJwFR75ItPB0hUabotPIhZK5V5Q5SvRq/T5bCACBqPPZNgOqfr1 OhGSpXn865qPZ9ouSWUKid24gGCK2utocnxXKRHVVZRFGeODLCTrEd3NEO65tqy7Zdk6D26k8LW ssZ90yNJr6SfKJMu40r+lJB2EoGK8BpbP4GXxDgXT8X4/v/j7r4sbTioXpSlUjAaGiOghB+tv1A hy7jbRN0LrnJoL8IXRDQJVFaphqnlHbLp0y1naclIJC03+TTX/rutvScSOwmrsqs2um/YyWzlyh mwXnGv/uFB6wkAZ4ay2oIveL/WvYqGeaPmP3t896vdNch1iR4MeQRvu80BqffDTkHkDOgmlGuWA IF+aa+mGIR0ISuGBothqKGPTXPzBHDNXEljaorTHRLUtwDaUBUayTOjgZwPKxrpZ9mf83b57UCv orrVTck6xR9WbwJhGP3vEYWPsaUqPC6nR2XKnxJ8vIqQfoze5AGWccN36OwGk6ZcdOIYgnAg2d1 pXxuMJvSmgonvpmN4xHw+HfdozFeUOWdVCjTY0keLijweNbKgC4HJvRA4WYpOxST3TxQvbRnwCD ypWTlnSYyEJ3Sfskk/teOGnrZ36Qd2jrpgncwNWShm4cuFdWoL5OR/zvJfd66LgDMBiSFRuofXW KCDInu/jCoWWJYw== 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 1be4695ca944..505e7d2b2253 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -38,6 +38,8 @@ }; 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. @@ -94,6 +96,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>, @@ -234,6 +245,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. /// @@ -475,7 +530,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 84f8b2c17f85..9c451583e75d 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -173,6 +173,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 98C5123D2A4; Tue, 9 Jun 2026 19:10:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032235; cv=none; b=Mf36gWFUdt/skA1KGkorig2NqA/8QoO6c8D8O+FWOtYQhX5OQe2ifJlj8+gg2pHyb2cZDUtaydD2c5Ag3We9KxAV+jmvUC+yDHHNdM+3QBk5VgJFCLRYptLPTcf2ejJZ6DyWP45YO5pXGeSRSJybmLuECpgqJ+Pqc0on0nJEqZk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032235; c=relaxed/simple; bh=HAe34xfCsUoHt6x/RPtlMZRmBGzEmDCCiUB3Z6xwSqQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=NZNdTcAYKMqPlZZhdqiWiKPRzm6qNk0tll+ATDzxOJbo3nft9Q9Art3h3DxcCSIyTs7UlvVPIh89t1WoKB2mmDp1hoTHcAfX0YyPRhpHMUS6r/OrpuM5HSa9L1yEjtJRTzT9dx+wMLBA1o3p2vIm6DhrTe/+0oTuDE6OZ8kk/0A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=DHfR6GEK; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="DHfR6GEK" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 0F3181F00893; Tue, 9 Jun 2026 19:10:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032234; bh=n7KJ52vVknn+HZxloD2rw0BcCGYGEtC34QZpg9ZQ/io=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=DHfR6GEKhSRqH51YDspmHwYVmBeWfn0CFJ/sAWvISIn+W4A9qsA7B1nseHmI9fiHF fZ006O6symB4+yUn2j81GvONJQmwZxHnj/cLWbXxYDvferpNv0TrcPzwvhEVsyeaI7 DJ+2svd/J2oDkl4QLUXMSlIBwsrmmUp/uPc+Os7h/VPVSd31f9NoRnMFMSubPoszuA SRfRxPag60Tu/3j905lr8UWXI3WTFD/IK8qMW6Itr4ZBTB4ryLxVIoHVfytCuBFdhf aJknQP6B8UQBj+JRBnRcvEBmZWcBnsTCNNNHkwYHwiGKnaZc+i1TYX/J1jzkOvYm6y 8jVegc5ufeA4g== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:49 +0200 Subject: [PATCH v2 70/83] 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: <20260609-rnull-v6-19-rc5-send-v2-70-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=12569; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=HAe34xfCsUoHt6x/RPtlMZRmBGzEmDCCiUB3Z6xwSqQ=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGT0dEhhxLsMeCG5/JhmGIrhDtwxcH+HffVaZ VUvAm0Dw4qJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk9AAKCRD6UCkIqsW9 0Nr4D/9RKrd5HFQ7814Jyv+pbynsFomOHkYK6cDSuf2Vza5IIU7h6Ha59MUhEldPZvuKHYS/b0w Ak6vgufUIxiEU1Rw3BQtOyBRrwvHuJrMS6+g6MVJ53nlr4tPdnEQzI5fUGg6NmXH7e8UaSZFqNF o5fYoOcFgfeP/7HiusbwsMOeFcKG3hqKCFfOLwE0MFKJDYXpStrl3RTttqlvDugrHB2jiFLoyEk tHd91XqBTKqvDf5rlHWRO0096yLfkoSROlBMwOWeu5VgAQfaSvMXLbiMJFc6jb2I48SjxsGomk4 cKT6ggfDBWvWKQQ+u1+tzez4mswknQrIx5ndWVD6xVUSomSnkgMHDI350YFsWWKYFPCygsYlUpV /lT6rdCcL3qoD+Z2iJRbH8XeyOOXXI+7lEXEQOI2yAfHrg77SRux0KCIcR9lRNXu1gxsLfsJdoh 67yj+m+biE+807KtgYaYe3Ohf5/J0gAyAhcSMXDJsAAhBDSgzlVkKNZ0T9oW+6N/onQ0HXSwfHv uLDNeF7aU3ulKhQIQ0bOFyqHNGAUtxiZdVdSbHIb5g3g0P6YOUyG/mZRNkfwsIUGdz2IQepDAo1 xLKtyh5y1oS2pL/sXZxAlok8wWtLhVfFOWZcLVQ0D6Mvdm0f43pYuY/jhS7e7plZqvND7ji1uBB hmuxkWh6ftahgbw== 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 | 36 ++++---- drivers/block/rnull/rnull.rs | 180 +++++++++++++++++++++++---------= ---- 2 files changed, 133 insertions(+), 83 deletions(-) diff --git a/drivers/block/rnull/disk_storage.rs b/drivers/block/rnull/disk= _storage.rs index 4a9bf480221f..6797b7996da3 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); @@ -131,7 +131,7 @@ 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> { Self::extract_cache_page_inner( &mut self.cache_guard, &mut self.disk_guard, @@ -147,16 +147,10 @@ fn extract_cache_page_inner<'g>( disk_storage: &DiskStorage, hw_data: &mut HwQueueContext, sheaf: Option<&mut XArraySheaf<'_>>, - ) -> Result>> { + ) -> Option> { let cache_entry =3D cache_guard.find_next_entry_circular( disk_storage.next_flush_sector.load(ordering::Relaxed) as usiz= e, - ); - - let cache_entry =3D if let Some(entry) =3D cache_entry { - entry - } else { - return Ok(None); - }; + )?; =20 let index =3D cache_entry.index(); =20 @@ -183,11 +177,14 @@ fn extract_cache_page_inner<'g>( let mut src =3D cache_entry; let mut offset =3D 0; for _ in 0..PAGE_SECTORS { - src.page_mut().as_pin_mut().copy_to_page( - disk_entry.page_mut().as_pin_mut(), - offset, - block::SECTOR_SIZE as usize, - )?; + src.page_mut() + .as_pin_mut() + .copy_to_page( + disk_entry.page_mut().as_pin_mut(), + offset, + block::SECTOR_SIZE as usize, + ) + .expect("Write to succeed"); offset +=3D block::SECTOR_SIZE as usize; } src.remove() @@ -197,16 +194,15 @@ fn extract_cache_page_inner<'g>( } }; =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> { @@ -230,7 +226,7 @@ fn get_or_alloc_cache_page(&mut self, sector: u64) -> R= esult<&mut NullBlockPage> self.disk_storage, self.hw_data_guard, self.sheaf.as_mut(), - )? + ) .expect("Expected to find a page in the cache") }; let xarray::Entry::Vacant(vacant_entry) =3D cache_guard.en= try(index) else { diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 6653db5c069b..32af69bbf8f0 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, @@ -773,6 +775,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! { @@ -814,7 +914,7 @@ struct HwQueueContext { struct Pdu { #[pin] timer: HrTimer, - error: Atomic, + error: Atomic, } =20 impl HrTimerCallback for Pdu { @@ -855,76 +955,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>) {} @@ -941,7 +996,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7A04340AD8B; Tue, 9 Jun 2026 19:17:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032672; cv=none; b=mrqqV5oAJp3xaW7W/V5w1b8vN2+5CZHSN7YwxS6YnVwYMp6CfB8PpzAYBWa5KnfecPTZRtiaRwgXsBLK4hDH+Hz+gcCN/HNlMlhBKaiinMsMblhRuaNszYVFFXmrXM3QtUpGWbauX8bClMmZlxutMBplS9L/gx4T0eAxqcDKJY4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032672; c=relaxed/simple; bh=xd/altTiEmLts+ET/xrl2RTBlfY83eAmYmlDKop0Y4U=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=W4H5F+/GUyM+0P1piDFkq5y7hiWrKp4WMNgyXuuaKKj6y6Q5uYE+xLJAcZ1A0u58arLiP2JNzj+LoYZMw3f9bPyM/xDy0MtbLSGD9QHWCvDdY4ast0uenVm/rvF6WevQ2jWCrWkf6yDE0TtTKqhc2Oh9wBEpF0GDvHOqeTthf44= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=BOpkIGy0; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="BOpkIGy0" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C3A021F00893; Tue, 9 Jun 2026 19:17:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032671; bh=dAPErZub35CZRx6cRiD4tDxfNHqt4EuvLpyZZOzwYBc=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=BOpkIGy0vWKfnVPuc32W+RgiwQEKWwVFlLjhaG3yLag7kuk3JZrioSTHUZazNEX9V UYOWlrhQf01weTRZYF+AWKbbC8q2gM+G5u15fl51lLINl16hXrFlqkS7lgNRODpgE+ nOTTpXBhf+f30wu/FazulF8lUv3UGupacuXC1k9KlNDZH2AvDBjxupV1PZz/0LgBg5 UNxNy0Uaflf3dLD4ei4zWmAUPXm2HYky5KcP5wgkS0qfxeMAvZ4DuIdECWQL9Iic1z cVDdL7uFG8iWEaAuzUCLfzeslTJtOUcRK8AvbyKyzUS8/RUKHTTAkfYS5iPQcz5zLa IMb36Yp7Os6eA== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:50 +0200 Subject: [PATCH v2 71/83] 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: <20260609-rnull-v6-19-rc5-send-v2-71-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=2480; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=xd/altTiEmLts+ET/xrl2RTBlfY83eAmYmlDKop0Y4U=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGT027BJxlk5QPCxItS21m2vrFHxGANnDAgd3 IbcDwh3OPWJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk9AAKCRD6UCkIqsW9 0LF9D/923dP0veZGR2zfMjDu/gMAkrMiM1R4hyi+SVGsVcewgN1D1luGHrk+H6j9V9TNVrzS3m6 gAu8+oplAnEp2FcSumoCdp/D73QHljzxq14myEC0wZOTOWbipS6Luej/4wgrDTMJO+UdnzdaRAG tKWS5Rw8c55Far19Gncm90UVqlxCq9AoO48teIcKl1MVaXKxWKoIwLh9ditcZ/Qg+S/JHr2SFGm POz0yCG2RJFhL/2heewf+s22ZeNWiUR6MaamuAMoylXhwM91kP9LpEm8uFYPqU7ouq9olV85igQ H8j8K2posTmlHNBupPFh5/oMBvs849KJZGeAIF+V6iIgP3qE9bCtJSPhshNO9c7YO1OAsOdMNKO EOLU0Jc+EgcjqVQQmvW8Ze+pokll8AGRSI5y+BQ8eDqnhsw+eh3Ptn6EzgDBCObgcaDPXbMJtDJ LKdbN8DtBmwp9llutBwp6TGpT1ceS4Ph8/TYLzq+oPZPK5skpRNp9tFVRGrARV3dSMR/TX4jJzI /odctllp+jTuef2Z3pKiFTukcgCXrHCbsOuSnrQvkM+4m9/SGaGaqX1/PVHEjBG0kkuXEB3v6fz juSBiv5FucmvVh4zkjKh6Hen50NFyv70eNX1r4Wb+JJKe6Gc3Z4uQt5bqylYSF/DCZLDcoQxLwN 8UH8FXhYWmjQsOA== 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 32af69bbf8f0..8e17b2b17a66 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -960,7 +960,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 e8f0d03f2ff7..47e1f860c6ba 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 505e7d2b2253..d28af9a5e006 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -93,7 +93,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. @@ -214,11 +213,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 @@ -235,7 +229,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 015794D9907; Tue, 9 Jun 2026 19:14:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032472; cv=none; b=Q/eD2Iy5myS/UqIt6H7jjcZIyOo41LFpQyvshYvU3NTxvsqrCCiTWYccNuZA2pFg6ccq6s9VFq6/GdqwsZykCed/PYII79H8MWLWlenCT7njR9DJsjMkV3rjXaMnjmgzr7yKTIH1a2qJs+1DWTuQNXI/qTAloU89Nilo6G3Aihc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032472; c=relaxed/simple; bh=dEPmlUxwcO+Z1aUp8x6Cur6OyCq6SpGxV6GyA9LbAqo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=VpJ7IzYwep88TAbTEdspYJbJIj4o4RCzC1WKCgZtozPqAqBshlhTN85XASbF0qoX0dKTlp36RajJrTOLVh2sktDExqJcwBzhs5Ev1GvvgNNafLnGudLLqDnvqfV4rsPwy1OXQohvlpPrM3fZa0DiQTTiueh3olQxKtBFdBrwfWs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=dUOTO/5d; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="dUOTO/5d" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 861FB1F00893; Tue, 9 Jun 2026 19:14:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032471; bh=yCdEZ68IJNlYPAWBV8MghxbvXE9NFFUFrCiuA8Oc9Y8=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=dUOTO/5dgtxbFMdbxqoipkYQeeWf8MyOeQdKWhPUr0eI7maU7Fi4OjvUNtNGPrfzA VJyzqcM3ZAuTq+okb8Jf5Xqk4RghniTH+a5ursvS/slGyK4fY8WvJoM08LQ59mCwz6 cAJHfKOn1AixaoCzxEyYeVSg+ijkUwfDC6DkCyzmV/Q+Cu1yIVR6wKMuuBaTtXNUwB PxqBv3/7ZLl16DL9aeX2HIKxNjPAt+s0UZFlWv4nn1tfj+FxIe6k/Vi3JcJEw6uDbG 0TW1ekT50kNXN4mxwC08P6YQs7JrrayFslQnpWJO2f1xZ4JqJ905syUyQHDq01ZYNK L+ktlL75QCAYQ== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:51 +0200 Subject: [PATCH v2 72/83] 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: <20260609-rnull-v6-19-rc5-send-v2-72-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1313; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=dEPmlUxwcO+Z1aUp8x6Cur6OyCq6SpGxV6GyA9LbAqo=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGT1t5B7OMznwXk8t33ak/UoKnSo82sNtKPE4 l1ctAFhDBGJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk9QAKCRD6UCkIqsW9 0J9CD/9f9tEnTGtTO3lz4bba9E7Rk81Gu6FqZ3nTV+ku9eLPqem/tv5hDKXD6QLR6I0OuSqvaBY ty/gCLetdWBvzLW0dGdDvRlbMPNShJUycx24awKp0NF3w7c8KxZ54aVMelLfnVL1N+ZhL2T3P4G r5cYQPdu2/asYUvO2BM8cqe0hcWn4tn+0H5Q8iSsEW4gZkZTjbuvkaN2I0a5mIbdy47Qmmjbur5 Q29HxY16fLW2NAPN6e1slr4/LWaUkhfggan73RC0kA8eGcSG4Y+4no49aqK8SYaPAhgk6ntbyY+ MJZbIlepcJA07j8Lsi1JlKVtmiw/gmOMv8S+kZrQIfbSptnBHBiinuDt5v7wIXBKsOMpvdi48Op m3WvEEO6D+ClOMYhT+sO7OJwwS+9LBBMncaUmKOhCtyOFOlc/jxK7toJMQlP5sOJAmo9nhIUVDX By70DQOGP/Iz5xDcZEgkFrPz0hapvzSlXJGfg0+TIqTAPwreJV7P8Dvc8sNg3+nAi5xJQrAVL1u r3wpA+ug/ZZNExmQASxAruvhPt+nT/ZyVJMzabVkjYq62zCIK7VCBuqdIH/1zo/mchWvy3K/uWM q4kJCjGW2z9SJICFwfA9UUtG8vkTCI/qPTEoVjpqf3e8p+xnKXfAQmWd+wkOxsisgyobBEYRKsL vIUoFDMOIrrvLHw== 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 9c451583e75d..05b167dfc6c6 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -619,9 +619,20 @@ impl RequestTimerHandle pub fn dismiss(mut self) { let inner =3D core::ptr::from_mut(&mut self.inner); =20 + 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" + ); + // 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); } } --=20 2.51.2 From nobody Fri Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EEA40412E86; Tue, 9 Jun 2026 19:15:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032539; cv=none; b=qTavVDc89abDbb3bEb4Rvo72WK55ruZvmUJRpM63vd6/laRwIsTw9aYsQMVtvxTsVl2v9j91KAXd3B30QuiXlymmCkZYxyabIzRKJY9+atuiWb9DyCxt1Y8OuHGkh0qU0xqftgOuhtuE4bGzig8XP3zxI72EXwgEdii9ohEsGig= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032539; c=relaxed/simple; bh=4OiBFQGmKFx0gHrsLZFCxFqfXlZh7LnoMq7hJUvpKfY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Ro/MsFjzGw1nIXL5hDpfSI2o3BVvPz+cNXcMDA9T0OLpjGmx1HFUGze7HyjUnqTNV8w897sWkishp0cla1Mp875j/irDJAKh1ejFdVoCltUgn+AuOZFzKoscR9fP/wAiANfhJc+GlR8/ILXScHFU3GbP2vwHc+8sGBhhjrV0b6Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=XCNyr4Ju; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="XCNyr4Ju" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 22A811F00898; Tue, 9 Jun 2026 19:15:32 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032537; bh=DQaitxpNA51/3rHHdgAAaeOKOYDYafiFasueOGXY3OQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=XCNyr4JurIsv41OBXAtAuO/k84pBtoNQEWQ5PF3mC/KuhrTmfNh5K1S/BqKvioNbC 3h/BKwqGFI7MVQWDF3UzHSS7cP83EsnGqPHLzg3uulZr8lkLuF3OJXqPgyQIH29etr V3Ii4X5e6Q/l1xyPtDiIkSTWSAzFDX9Tbs8NEEiy2YwiYfH2QzHaQGJPJE5SXAdfvJ lnuZ03AeL8IJyt2sTbfiWzB6vXEal4kxHh40iqhNrd63JQuUlMGQw/GpVSuoBtvq86 oRzljUW8hmoqECP1aptnMXf35hEaFnUJff4mwCOWJU5iIbzAh68yjaymwv0ZMun4Of UkT9UXb/qIieA== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:52 +0200 Subject: [PATCH v2 73/83] 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: <20260609-rnull-v6-19-rc5-send-v2-73-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4610; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=4OiBFQGmKFx0gHrsLZFCxFqfXlZh7LnoMq7hJUvpKfY=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGT2zkh/Q8CgSEFyUkKpGV9RCrunuLVdbVgq9 vD6EgNAjT2JAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk9gAKCRD6UCkIqsW9 0HP4D/wOTaFywqxSb/E8W41kcuSGxbImFnwDDJAFeKlSlQ5jetbEzO3M98Gk6NOpFL8zha+hBPd 5wuvIgS4Gknn6JxldU/yvZl2RUKkZrG15tNNEezVsvzVz2ZmmFQ5JWs+RE+bQ1D6ZtMY7q5kbE9 kMIJSK2g7DTFR1kdC1Q1xJ517sy+wq12RlrhepNpA2x8rYLwTsLpIWS1Rxdw0bYH9u6xwSYiWG+ qE7CvRB1Z1RylJ1aSURv5iyltcrpqdsRQ7D5s1dVmWENhW/6EodN4WAqkVw9xJpKz3qfHS6v2ec ePTDI3X5aFW+k6HWR1JB7B+D8DaBAUOeqz0pmsU1PBrd8S3QH8+qugx8HU0464yvCnGzruf/STC kuXPhBvxMkT5Wf/X9h0yXeskbKt9gRQBAv3hb7Zfal7kDsdRPh9fQ3M3jT9xdmz0F3JckxEeF2O UV1oI3XcxgWOcGFHWRSHNHITtVjSx9SDDJPiNsSzYdcSZFLZoIJzwxQZXfb5A6Fwr4vQ9d2606X da6Cj6Q0DolEIA2oaxmwNw9xlLMh4Y9Bu93LaBRuoedijwXl7BUOQEi6knVCcJ26Oo9dLoSPzFS Kmt2Gyr7hhKxp50XsSLvbnRXsV0SlXU56U1kgS99dg6iwOXN4KIVKLw8W2b3H7kkB4S3sioYtNQ AoEfd5Rj6pG+HrA== 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 | 66 +++++++++++++++++++++++++++++++++++++= +++- 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/rust/helpers/blk.c b/rust/helpers/blk.c index 422289d617ae..1f3e5c661096 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 e89c76987b54..66b6a30a9e66 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -6,7 +6,6 @@ =20 use crate::{ alloc::NumaNode, - bindings, block::mq::{ operations::OperationsVTable, request::RequestDataWrapper, @@ -17,7 +16,9 @@ Result, // }, prelude::*, + sync::atomic::ordering, types::{ + ARef, ForeignOwnable, Opaque, // }, @@ -39,6 +40,8 @@ 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. @@ -193,6 +196,67 @@ 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() { + kernel::pr_warn_once!("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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B79BD41D9E8; Tue, 9 Jun 2026 19:17:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032648; cv=none; b=fr/AFT7i7+AZhWTNYTZUvQfFPc5gbclhVx8IjnF2kPivRoJ4uKzwV27odkW1fFoylorLSmkvBwt0rkC6fHKtgZxVFyjyvFgN5sI7za0QJ9Cd0Fo1MGwGETOI+VVjDDT/sn9upEq8b4kiGaQFce1GtPk0acA2eOV/J/VY9J2bCTc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032648; c=relaxed/simple; bh=fop9S+9mzVcsTkO1rMOxG+AfrBnmsW7E8Z0RHs803s4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=T4J/hjoT0UzgPbHGTOGxkt3YZOQXrRB7vrjTMwJPSghW5eOQo2IZi4BQF6xoSBS9MG42fFQrPYAvBfkqnHWkn0UIsymFEkUTolVeGdMqClMeiaaN8ON2ZEor1ae4GfHNMCRos896jY1O3NLFU8n1ezBWYQFW4TXUM+PqdE6Pths= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=NlrvzFZv; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="NlrvzFZv" Received: by smtp.kernel.org (Postfix) with ESMTPSA id EBF751F00898; Tue, 9 Jun 2026 19:17:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032647; bh=l4agbTirNlyUYcdjQT8pmN2Uz+Tb+GEZRsrovxY+04g=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=NlrvzFZvPGR4r0FNlUqsKm++UKOhHK9QP0yTaBU7RQVTO9eHAX6zE73w7SOEXOqzz HbqiiTpXR9TGlS3R1TpzwnAtw6OLHW7j1knJpzQuF1e2fd/qEFd7Ucy//TBEc/0zG0 /85I1zCRbhg2xHXdMU4mUkMdbQBEOI611kkbd/C4oOPDg7eDylgpbUd/NxpHdeZ63U xF4Z34AwNl4fAkpkmCVUBZLoKhABDFEbUZI+/VPNNgi1tPJErzbOhbadAj10o4G+lp 9PS5nNaobsnkaCbtJ/Pa8O3hsxb0Ft3qvZ3psLw2KYXw71L9zYW6exHzoYng1bQ8bS rZNA+z5ZE6EZA== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:53 +0200 Subject: [PATCH v2 74/83] 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: <20260609-rnull-v6-19-rc5-send-v2-74-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1020; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=fop9S+9mzVcsTkO1rMOxG+AfrBnmsW7E8Z0RHs803s4=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGT3vhakzgq4tXf5rIzfy3B6X1NktI4mR2vyG W7qLn2llZGJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk9wAKCRD6UCkIqsW9 0Bg7D/4snWv9goqos2qzgRQYcA8FkUKrKYnV6R6LAk7i9bC3v34ZUm4z7VyF+eJWfWf7o9V2Er8 TbQZy5aVqp6ptRU1JCmcHZifV+9oQ6ywjAbputtg8TgQHjUQ2c28RZzZdwJDHRFpT6qUYkeJ7lc 5nWrAhMhkxiFs8f4jZiFnU3P63hgN3dyOWS5mrO41E13MtvQ0/Dq89IlRTRGvihZbagHHD1IKsA TSoIEhfng5ttW+FQLZft2CpwPoz8IjnlflIGn5JTM+adojBxi2le3/MYoso83fC/8YEMQC6f9zM sEeY0E03bg1r8Pc4KPyG1QHTsaQyO6/xpFZAR2hT0fZubKdABYnBt1ESu8EcWmU0XqZDbG+VGw0 Y0Mb3xXr3s0mEZ6VnY3A8wBem47djoGLzkoiDukouoN40CfeWvBodvM5ZFb69nLL3imY+xE0b7k L1/cjFAzaR3sBLT0vCHzjSFo0HV8KzTzOtPlt77fSHtXRGfK92wysvVWo1ZUClsdTKXa/tAPfZv 0v/lusk/BplPsNb9+xgGQvAwcjQ9rNu9ZE587fvULiyig1jEbu0RTZONQSjXLN6H2iinQ65N7NZ 0MRGFoQqvNbkKm8pGOgr3vIvmgbIGYlT7iKFtWGmqA2AhYDsz1OhXxGaEgB2jfRT0rkCV7eydxd HtfYGWM0XD9snvQ== 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 05b167dfc6c6..54b5202567f8 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -191,6 +191,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 95BCA4DBD8D; Tue, 9 Jun 2026 19:12:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032369; cv=none; b=lhQ3tOxmo9yupJxOngvLZtgoTbFb+z0WenAUvLNfBPvbZA/XkwoRxYBLPA+ZmtOi0FY7crY88Sjo3w8C+ebYa9AbnAITJ0csI85ElF6GB2dk/aPGKaBSm/TJI4vh7KEM5vhBpkQfotLdFGtgxgB3F0x3ZtLhpGwgxjHYlFjSAtA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032369; c=relaxed/simple; bh=scALI3Of9papCU7IjOqZJ1u1Kulf0qIwjxsjg5iYtNw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=cYNWoKNehFVN7Hj+CyaPobjLqNbsr4QgxXa4SqzNiee2vMPYj6+VaMMmOOKrG+1OCzwE0Ad/aZIQbDePyzRbTGIf0yAh+CeKHyN7Z6jN7H3qEAKhJmh5W1LnF0xCcsm2jp6y+XjlNa/KVQCjG833V5HPLxP4NAdXuKrE8kqJh0U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=cV+8i+U4; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="cV+8i+U4" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7AE171F00893; Tue, 9 Jun 2026 19:12:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032368; bh=E4zTF3buoXlpdVNSPu+eDYHPwLwVvZ/+F/dMjwaTG3c=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=cV+8i+U43n6XAVH0qKkpGqpEw8THPoG5GDx+77Vr2ca87tK5ut2BVqSJULdHo1hL2 p141gWkNxfeG+5Mp+0//5KGEv9Ew8Es2Wg2t0JcC0kWYLoBPXxGylq46Rbn++CsN2C rxRVwg2gLrybVWOZzFw/p1pquEa8GbekwyeJNuK0ZRP/FSAmbjb+yCzjvUogsJBlQt z8ObVLIcZlAamjFkoxSImz5RQ0HosmKZe56bJZwHqpKYXNk+zUVqyNknLi1wepHEnA 5nY9UbuI1j5int48wYdOuU0RwIBC1XMNTg7FIYdFlvDav+85pC0rfutBWdBWXAsGBz eF1kGp0QexmlA== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:54 +0200 Subject: [PATCH v2 75/83] 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: <20260609-rnull-v6-19-rc5-send-v2-75-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1374; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=scALI3Of9papCU7IjOqZJ1u1Kulf0qIwjxsjg5iYtNw=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGT47losJJyzKx25dxqwhmsAjiaceinSmISuB 74LtHjCMIuJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk+AAKCRD6UCkIqsW9 0LVCD/0f5aMN4ivKJwIUmvoBpEA1oTSTTcWwTQ16DMSlB5Da4QnuLb8vhomM9pCloc5XmQfBJVe qfLrZ5PEe94IN8vCQ9jUEmNPGg8kZai0Z2UmGyu3saUkwWfk35EPalRebaU3bFakJe6Zux3Qn+C BCA1441jxGPWzc5hT1zAYwETUK1gDEEKvXgWWZYByEBtyvtkSwHkbMw9Abc21zj4l2Whif5mdt/ p2E7/Ua4A4a8xt8TzoI6cUe0EtH4VjqAlhBpMl1OSy2GPdt4BKGdA74sZFsePhuN0+m36iX32Fr l9ulhIZ06xNRCWeDAZQuGVHa6IFUBTv+ttHLvkmOAygMpix6R6KEY1jCEij+OgWFeMuIQ3cLYiY Q1+XFRy6t5fRHV2U8l3jB8bwJ9e4oqpOGWtxMthi/JNx1k06WvYkvrZtgb815J39s/qrSB79ek0 NdQxosr1F95TF2BCXp5PJ+6+r1rS3EWQXxkXrY0X4s5Ohqnb1+mP4veGTa3jC3Dwo1XEGnbdoKd yZBgHkUrRaDXNEalYdKUMv1k5ht0gKKOD2EDZxuSqjhtc4hkixpQ9DzFIFEL7GnPLf2zObrUTFt l/FDNHE0kaVd9ziq2D0/SxRMg19xpq/cFQjhPzccSgFYHbvH+z4wESovJwNK3B9azUnXASAq91T jXMzYewRKtk7MLw== 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 54b5202567f8..bf6d58139ab4 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -100,6 +100,18 @@ pub(crate) unsafe fn from_raw(ptr: *mut bindings::requ= est) -> 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 impl Ownable for IdleRequest { --=20 2.51.2 From nobody Fri Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 60AB641FC57; Tue, 9 Jun 2026 19:18:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032684; cv=none; b=uUi4zKDlM4D30tTsHp6FNcgZpIqNy8O+uzw2s1JBB375Ys9MsXkXIVTaEOp+0QAiChjFu4gua2CmTbijgD4va8BbcKiRKNwzws3YyBfwvbKzCIVuBCCZG0RsOtmbb0bBFWjYVy3RbPrGf17Hbx+FmFeaaZhX9lqslBhcWcU/LNo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032684; c=relaxed/simple; bh=97jxH/QmtmH/CWohF6JLit6bM5t2zk5/a3nktYO+2ZM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Cx1jPRNp6S+gculzT/FSUy03o9o96U/HrGDFL7dMWLjgxVDM8+VXLsshVeKOz5qR29sZRdbH764EJf0lmIFOHhnM7IwCJgM0a0cjrwUPFlTIgQA7pU0F1g53cbr1yGvdRNKBUbiHVI+rNiQ2vgQxaQk15Ggfy47IMx+9oJDjRqA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=X6rVcgBb; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="X6rVcgBb" Received: by smtp.kernel.org (Postfix) with ESMTPSA id BDF811F00893; Tue, 9 Jun 2026 19:17:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032683; bh=OomWwZ2E/uUmvbXfccoW6uFSslUSkDomnKFoGy4lNf4=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=X6rVcgBbqqcy6A9FVMlgU1UJoH0xU01VsG5i8Ygg4IsKChRY/9VOAsjBR3iUedsxo Brp/4f7kIscPxi3a1CuvX4BzmBWghtHS229HEPJSstGiaeSS/JiR9mi98eyr+FeDXT 3z3F3KO77EcCY+EKLfnozl19/RjLbZ1WwF8FfcIHw0fYtgYnuQz6mDa8LQY+mLQrxO FdUHyNX5GzlF8CTW0PVbwtZUtBT4YaHZOWx9VCiBjIHTZW8yTt1n2nNwgpOH5nHVh5 s/u6t9qIAdIawLWUS3Pw15SYD6Ar1e9qz2td99E6lHfJ7i8iL+fmVF7nFhX+MqfMQi yNelwjegkjg9g== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:55 +0200 Subject: [PATCH v2 76/83] 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: <20260609-rnull-v6-19-rc5-send-v2-76-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=6548; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=97jxH/QmtmH/CWohF6JLit6bM5t2zk5/a3nktYO+2ZM=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGT5RrlseORLgOiPT447AgxzjasPLzO3FtBsB GDuy3HwtJ2JAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk+QAKCRD6UCkIqsW9 0FU6D/4w74QvjVjFXSovnx/Stq9wkAc4CPI43xP8x1Grj/P5qSz0FJqXX1dizqKmTuaLX3ClnB+ pR5tp9eY0sOz0ri5A9awMYIcBqbQvFR9cdXUhDeLczI4iIMaG3/hG86CGS0tlsRXpv8ixwhgUa5 LXSBEwLccPUHos62AEBrJqmi428crWlgbNHmWRun4/vFduQi2pNd9CrtV+2BmcKXmT43/9toGJS 1w4lvTIqkwkCvSq7lKF4yMDtko4L8peJuF8y9zUaMmsA9io/FqVpXvjNGAXyoxwUHiSnx6ZdWDW Q+6IBUXA6DqGRZyRaqg+rtyVPeA6EmPNdqH5Et1iGA+AlBulTUMSGmaqxvCGnAmaF9ApiuoPO4J UQ9wyk21GdV8G/q5F55DXkpIzswLIfZkn4aTca+A9ktr+lCosJf9N8XGeHEJWC+oZOSOHI67Bqd tJyUzCSqWpUxusCek7oAup3sgT7AqqMPS0rxVPxIuZtwStw2NCoSvPtFZNtMbCjyj5PJeJe6wrg SzouRr+/KKNuQsBEXIxx+0KRfhu3qmnZ5Bb0rCJ4qrPX7/twL8eksXNu9+4cSI/rkrWd6NZAl51 mBgbi4cUrutsWQQ+ZznN2lQJvrApVfnqUeDgtDLP7aqMUV5wSXdb+g5UgQfYLy81cBY8/FoJRMM fCuMkCEyeqmZKAg== 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 | 3 +- rust/kernel/block/mq/operations.rs | 78 ++++++++++++++++++++++++++++++++++= +++- rust/kernel/block/mq/tag_set.rs | 1 - 4 files changed, 80 insertions(+), 3 deletions(-) diff --git a/rust/kernel/block.rs b/rust/kernel/block.rs index b3578f28871a..23795dbe08c3 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 47e1f860c6ba..a306181d88ce 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -138,7 +138,8 @@ }; pub use operations::{ IoCompletionBatch, - Operations, // + Operations, + RequestTimeoutStatus, // }; pub use request::{ Command, diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/oper= ations.rs index d28af9a5e006..2b340675f976 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -151,6 +151,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. @@ -521,6 +566,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 references 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 { @@ -533,7 +605,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 66b6a30a9e66..6d3882c01d9d 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -126,7 +126,6 @@ pub fn flags(&self) -> Flags { /// `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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4D495412EAF; Tue, 9 Jun 2026 19:15:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032544; cv=none; b=fDv1wStAQ3Iz/4dZmTWqhOJS/kVb7Eb2IP8F/DzcZPAUr0yNLSe2R8iCM9ygZbxRvM2O61bC58m9cmG2F3neG2gfl/I+PKHMhXv7ByLqbldAFPU67wLivMcXkwqXOIHjL/SAqR/bTpklCmq0MSX+q4LUw6eQ2bKLpE+S6F5WIoA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032544; c=relaxed/simple; bh=LKRO/kdnz0HdXDtAJ98fqv3Q8kP2DI6d0zTWOdCxz60=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=jXAH+SmsXIJfSXorMHQ6aa2kZD/FLVigBZo2oP1vo1HxlSHMFMoJ0ksqMM5WGpvvxA7HuwpziynHSzE849mhMx4Nws5zBKO2FTg3hz7n7dhZHpHLzxmeLEXYH6gvRP/1/20zQ+XXJDycZZ31OcYfD93Gk9MioLMkyGNPAlmfNqw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=g54eXIsj; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="g54eXIsj" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7680A1F00893; Tue, 9 Jun 2026 19:15:38 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032543; bh=ObZUiZGkmGKCKDmrRrIYTd2UdzvwSBsSQswoI/qC1L4=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=g54eXIsjeu7SPnLFCcPLpvO9MXyFiWh1dt+cdGb6rkoOX6apHL8ot8TbdBnlmoSfl GKjNRNH2Jm8lgMIXCs+aaLz+pTLJfO1M3cugjTZNbKia9OrzQHZlFkQ4TBNYkTR4Da LRJ2K/cVic6wAJzw2MLhRc/O0jX9+Ul9ecnZWcXKC8b4ZpfkHPCTJwnqrDUEBsz4JB XauxiseH1Kv3CSNuvwChqKe4pD98+7SUab6/ikwPPNX3+x2VmN/+35tgR0c7VqMdN7 srCoEuU0EnlppSaWYHTQpnqdfr+fT9f7/A2LoZ5lPPtP6OP9yPd6gv6bvol5spfONo AcR7MBeDt716g== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:56 +0200 Subject: [PATCH v2 77/83] 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: <20260609-rnull-v6-19-rc5-send-v2-77-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=16656; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=LKRO/kdnz0HdXDtAJ98fqv3Q8kP2DI6d0zTWOdCxz60=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGT6fFbzOOyZeh9I5VN2kkchzacCjx9+cOsVD uuoBYVbif2JAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk+gAKCRD6UCkIqsW9 0MOhEACvYoJ26WQA8ahQCGMgqawwcYnBZkeu75ZVq0YqxzJrZ/Gj5pIIMboKx42YdAFcN+FJll0 7sdjBKs/utevmwzgDaWq6PlyZt9WgxBsRECdsKlBkPL3EDwwufQK6Wv3eo8HFIAk7k6HW1N0afb sIyACn4PJIfWb5yTHQ28S9ARWlQtYPMeE0eDQyJWQ42HRYdl/nzoaybHpLthYsNNXn3OOUt0fmH 8X1LuzLnDXkg0x7mwiNnJT0nEa7RtcQOyquR2+N9ze+eXp5lc5/koWKD34oDTM42h13rB89hKxB HbV3nZhKO2fhnJHpFBgJNDTilNH3IlJZo2airpD/72VR20BOVfxN9kD10g5QwwJ9Z45xB0kkNB8 yGkmPb12JyLaMLT7dZV+VtPtKtUJc+MYV0Z8kXvrZPZ0N2SrKYK5wZmS4EwlQouDbCC9eDVngl2 E8DzV30j83LgMz87el8Q01vqfBDE+5YToJhj/3QG8hVfGC8faeCgd5AZr+tVwm0kswWz6iXyk3W 8oPyJm4SSKpMyj4/T433JbtTcqYorR/AUC05Q4jx1m6mQgDv+83sh3ef/joxR8CvXCG4WO4rrc+ hdE5Ic4sOMPfsLiImjriEj9lnm+7Csas2KQeNIOYpBOWN2NJQ0MinQL8Bd/3ya+7Gi6VLCPSfNh 1vp2COdxIRUsDAA== 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 | 121 ++++++++++++++++++++++++++++++++++++= +--- 3 files changed, 180 insertions(+), 9 deletions(-) diff --git a/drivers/block/rnull/Kconfig b/drivers/block/rnull/Kconfig index 7bc5b376c128..1ade5d8c1799 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 d9246b9150f4..eaa7617e5ffa 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -48,6 +48,9 @@ =20 mod macros; =20 +#[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] +use kernel::fault_injection::FaultConfig; + pub(crate) fn subsystem( shared_tag_set: Arc>, ) -> impl PinInit, Error> { @@ -132,10 +135,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 { @@ -176,9 +213,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, )) } } @@ -263,6 +306,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] @@ -320,6 +369,8 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { memory_backed: guard.memory_backed, no_sched: guard.no_sched, hw_queue_depth: guard.hw_queue_depth, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + init_hctx_inject: guard.init_hctx_inject.clone(), }, zoned: guard.zoned, zone_size_mib: guard.zone_size_mib, @@ -329,6 +380,10 @@ 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)] + 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 8e17b2b17a66..f909360ec70d 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 util::*; =20 +#[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] +use kernel::fault_injection::FaultConfig; + module! { type: NullBlkModule, name: "rnull_mod", @@ -203,6 +207,8 @@ }, } =20 +// TODO: Fault inject via params - requires module_params string support. + #[pin_data] struct NullBlkModule { #[pin] @@ -241,6 +247,11 @@ fn init(_module: &'static ThisModule) -> impl PinInit<= Self, Error> { memory_backed, no_sched, hw_queue_depth, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + init_hctx_inject: Arc::pin_init( + FaultConfig::new(c"init_hctx_fault_inject"), + GFP_KERNEL, + )?, })?; =20 let mut disks =3D KVec::new(); @@ -278,6 +289,11 @@ fn init(_module: &'static ThisModule) -> impl PinInit<= Self, Error> { memory_backed, no_sched, hw_queue_depth, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + init_hctx_inject: Arc::pin_init( + FaultConfig::new(c"init_hctx_fault_inject"), + GFP_KERNEL, + )?, }, zoned: module_parameters::zoned.value(), zone_size_mib: module_parameters::zone_size.value(), @@ -287,6 +303,10 @@ fn init(_module: &'static ThisModule) -> impl PinInit<= Self, Error> { zone_max_active: module_parameters::zone_max_active.va= lue(), zone_append_max_sectors: module_parameters::zone_appen= d_max_sectors.value(), forced_unit_access: module_parameters::fua.value(), + #[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)] + timeout_inject: Arc::pin_init(FaultConfig::new(c"timeo= ut_inject"), GFP_KERNEL)?, })?; disks.push(disk, GFP_KERNEL)?; } @@ -328,6 +348,10 @@ struct NullBlkOptions<'a> { #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), allow(dead_code))] 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)] + timeout_inject: Arc, } =20 #[pin_data] @@ -350,6 +374,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 struct TagSetOptions { @@ -359,6 +389,8 @@ struct TagSetOptions { memory_backed: bool, no_sched: bool, hw_queue_depth: u32, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + init_hctx_inject: Arc, } =20 impl NullBlkDevice { @@ -372,6 +404,8 @@ fn build_tag_set(options: TagSetOptions) -> Result>> { memory_backed, no_sched, hw_queue_depth, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + init_hctx_inject, } =3D options; =20 if home_node > kernel::numa::num_online_nodes().try_into()? { @@ -404,6 +438,8 @@ fn build_tag_set(options: TagSetOptions) -> Result>> { NullBlkTagsetData { queue_depth: hw_queue_depth, queue_config, + #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] + init_hctx_inject, }, GFP_KERNEL, )?, @@ -446,6 +482,11 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { #[cfg_attr(not(CONFIG_BLK_DEV_ZONED), allow(unused_variables))] 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)] + timeout_inject, } =3D options; =20 let memory_backed =3D tag_set.memory_backed; @@ -491,6 +532,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, )?; @@ -733,7 +780,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())?; @@ -783,6 +832,22 @@ 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()); @@ -808,6 +873,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); @@ -831,12 +902,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() { @@ -914,7 +986,8 @@ struct HwQueueContext { struct Pdu { #[pin] timer: HrTimer, - error: Atomic, + error: Atomic, + fake_timeout: Atomic, } =20 impl HrTimerCallback for Pdu { @@ -939,6 +1012,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] @@ -952,6 +1027,7 @@ fn new_request_data() -> impl PinInit { pin_init!(Pdu { timer <- HrTimer::new(), error: Atomic::new(0), + fake_timeout: Atomic::new(0), }) } =20 @@ -1006,6 +1082,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, @@ -1067,4 +1148,28 @@ 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; + } + kernel::pr_warn_once!("Timed out request could not be comp= leted\n"); + } + } else { + kernel::pr_warn_once!("Timed out request referenced in timeout= handler\n"); + } + RequestTimeoutStatus::RetryLater + } } --=20 2.51.2 From nobody Fri Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C2489406072; Tue, 9 Jun 2026 19:17:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032636; cv=none; b=jskpnxrNfLBTdkY9TN/XLGgvwvmRI7s6BRB4Uof5AkIqz+qFxBTV5N3cnLTMPGugWKGYftcszIFP8vT90AIkf/DcXjmsJhpHxvhOuV7vZRPohOCFjmIa0VemtEimjEr8k7orT4o6M5KMboFibukv5D3cPRSF//Nj5BsfJxYMZZE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032636; c=relaxed/simple; bh=HoPPP8LHyfFhtce3K0cUJuW8NUYoe+z4X7dgaxFLGfE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=A3WuSDlvVHK8rsh6X5Z7jPGS5xoTTB78DLmcEdliqpVZLK8OVW8rnGHUPEpjS+lZb5rAGcTJzwuZLihp+0JRSW0Z9zdSuHmtZU8XKBYhgwIQl2YQ9oytvduZJoPxyRaCW2t0xoyE4pJWfZ/Ej2lrGhwuNpKU3LryoZVlIp+ac34= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=QJGWcflN; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="QJGWcflN" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 01AD21F00898; Tue, 9 Jun 2026 19:17:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032635; bh=0vBcgswxHnJVjRfzfe4sol9/FUdXWuk9h/h15LXryYU=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=QJGWcflNSh6sOYC4ukSS5Gx/oNMYV0HgjwWjG0RF2pFsvZU8qk1Klls5Ps0LskIVr QZAdRfPu5eTf1wFPIVYIg5jfHlJ4v2vjSSHik2KzVdv02pYwOnwv1IEfzma9XETfdP w9ECu9XPIWVQfwA/bU6cQqXn5jZqvetAYF2/Z4jfRqZpDeTUvGJXJC///PmnT59Bx4 p1Ya9/0kdz9iVuA6Tk4ThQfJ6t7uFQHnf/jEqy6HbQVvBb/pGPTQ0wtHd9U3Q3O768 m80m/joroPPzC9tzm9LeWFs+dv7j0VChFsMQLhEsViifiouT96slfEkJj06Ba6nscQ TyIqJQnGJYuCw== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:57 +0200 Subject: [PATCH v2 78/83] 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: <20260609-rnull-v6-19-rc5-send-v2-78-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1620; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=HoPPP8LHyfFhtce3K0cUJuW8NUYoe+z4X7dgaxFLGfE=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGT7S1gTtW+R1WhBEPArVYTcHteX7mgWXqCHu TgIUJ9j4RmJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk+wAKCRD6UCkIqsW9 0MPdD/0diJL/A2gSYUNUwHklP3bHcD/77AQjbTl7KVNQrN/kmQvABFD9xTfID5LimPN5gwnDeBK vm2AJdxSZ3PbOyQs2aBEXnm0c7ossw/opjWZ2lMbc7uxV55+wNurDPo9CjVtqzNCjy7QIs21dBs Y2bUvKFpQsbNkgiuGSF7HEGuaFdpabmHDTrYyR7YM+Pmx4vLs0qiWiQygWTNIwn7iIIN0fpmjkA VrBc4LfNcHsCMtNb10VS1TQzSJa+77ceu08WfF9acCVJ44jhF/BDMu8lSgD0fPmS7jZqWKZTuIe jmMxx4XRYFIHfgD/5DRd3HnGSvA9q800AlCTrcxyYdVIrAR4/4eCobhxFyXnkLYojGJiy20O/9Y wwwcCEiHuHYiBvmXA0nMhkAvfshZcJu1raF9gtUDBgxyDSDrHaDPDP8ay+spS+UYdTP3u6yMiYi pGeYRQzkhA4ZnhcGZy45GadpiPZ8sf2z/4/6xJliKLX9N/yDPJ04Mwq6mvwTRYOpdzr8URH6jHW ygCAqRZpjDI3sF4jpVMaF2VDsPfgH7fRycsHCc5c6vhaSHWfSXJkxUc+B5I0YzQryV5UIo3mtPZ vWnoO1tFE6ZjT7pMk31qA+VnVKnOk+7BstKDYXsMhgynKA6amnzncerTtDgswXJLlwANnaXwddT T2INwiJ1HtR0AiA== 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 a50ba7b605d7..6d760dafade5 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -58,6 +58,7 @@ pub struct GenDiskBuilder { zone_append_max_sectors: u32, write_cache: bool, forced_unit_access: bool, + max_sectors: u32, _p: PhantomData, } =20 @@ -77,6 +78,7 @@ fn default() -> Self { zone_append_max_sectors: 0, write_cache: false, forced_unit_access: false, + max_sectors: 0, _p: PhantomData, } } @@ -181,6 +183,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, @@ -199,6 +207,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F12584D8DB6; Tue, 9 Jun 2026 19:12:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032364; cv=none; b=EAi2kcq0AyP4z7+WeCDOFMRvsSobCSb/VAcc/0pSNnNbnK7te6GPwZJm4gGR0iNoogO2lyXk6zasRr0zSSIV/tVeukRitWToN3q4J9NjZT5c3GSifxstD9ZI5mTU+PNnQOtZ81odv7+WbqH6GL7kxDttR3kGFrQUC8JaVypgohM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032364; c=relaxed/simple; bh=JQ3kG/WGGmu6/EYTDHQbX/u4S5Z3d/R/pgMZd3cP0HQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=aScR0FAWQy7smwgDF6IZUA4l3eGK9qNQcyzpWeevojm1KvfoLPKut2BVlGai23d+44NxlAZAgk7i9g2n84Tni8mlf2EypjZrFHpDLWFIWmWcZgsb1STXEijlbxlcUd6Ea17ng6gfztKoONv2QpGdBXF93A+cF5tQv+NvwQJZM0w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Dy16hqjQ; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Dy16hqjQ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 13EAE1F00898; Tue, 9 Jun 2026 19:12:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032362; bh=z9SH0w8iIvKNdd0B99CMlOWkeY6yYgzCEx1TAgow6z0=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=Dy16hqjQw2Mog1tvdwcHBgxNe6GgyDQM9lfuJiH/uSL8HAFAWfLPU6tA8x9VRqQwd k+3SzKdu754K2Qb2Vyyl+iCqoQz5wlixX76y9mxsGH+LOLu23YCNSnl2t0wBebYnRz oF/vaWFXAdOFWQX3Zl5UAFkKbK2R5pR9gPPIkKpTpEpNhwjhrh0XOrnay2hDQYlF3m BIi2jA5ulZsvWpQsOrZmxd2LWCfCbHRd5+N3kYu+LV783dC4sl4qhmAHF40BN7rfBB Jjg+bT3idl2VsnrwL9xR2aaZUsQu3DLq1qcvG9nTflg/a20RuBa7EpB/Rn6NLLWVh6 Q9NcMxrDuw+cw== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:58 +0200 Subject: [PATCH v2 79/83] 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: <20260609-rnull-v6-19-rc5-send-v2-79-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=3966; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=JQ3kG/WGGmu6/EYTDHQbX/u4S5Z3d/R/pgMZd3cP0HQ=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGT8rNaIp8s3qv+4COUoMMnibZPjUIMIFeJDh oso6l2MqniJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk/AAKCRD6UCkIqsW9 0PsFD/4xE6M+uoXN4PZ2IfrksGAEmNVSk7zRi9n1KitGIRUO+UXtc5+N2QRfjVe6sYx7Tp7EqAs RWiPCK8XoQeu59WSbcfLQ6Uiw6+OnhcWG4IDQbZeiAmkq5lLXGEMMjjCFXyVE/P2oXHqkSHL65u mDxAorO3h2M2aPim/kUBZ+OYSg4flgEeSi6cQlnv/O5PjQwJpDiQVAs4n5gcq6M39Iayt3dZWgv JO4rZFnYQNdCiVv5rO4clM2Y0y+V5YHquDcJvYq0Oq3PkNhSh8qeJ63Qe/2H/6rhoc/GaNlzGQp o2UVDDj3YdOvRIiUJrbMTZeTzySTO6BhLPRqXnreFIoK3Ek7loiI2whFBNS5OiOa8lqgwuWg5WS ZDcru6GX3keiMByCTYc5cBeicOZE7LVFuTxIk/kpX85QvuOc2Egr2xcgF9za/QGg2hTJ+zF/Ary OWqTvc335xTd7d6HXnj0Ab2Guam7+eCfzPNROf3FkFhh0fFO+T3U6AKk7/s73rIttGmhrGCX4sl HNulUFvD1lcl6anYjUPepd7whnquIe0VJKe4iy06/U3LG/vQo7Y20FXtVs9roAu63NfhByKGIaY bJ6U/vhYVBd/Edt5kZth5o2Zv2P/vSkHHDxXS8uwwqkdeevhuVh8vvI/I9K/xkmHUlm97NHGq0S /IRjhjK6XdwyWCA== 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 eaa7617e5ffa..5ab217e43e2b 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -132,6 +132,7 @@ fn make_group( zone_append_max_sectors: 26, poll_queues: 27, fua: 28, + max_sectors: 29, ], }; =20 @@ -219,6 +220,7 @@ fn make_group( requeue_inject, #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] init_hctx_inject, + max_sectors: 0, }), }), default_groups, @@ -312,6 +314,7 @@ struct DeviceConfigInner { requeue_inject: Arc, #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] init_hctx_inject: Arc, + max_sectors: u32, } =20 #[vtable] @@ -384,6 +387,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { requeue_inject: guard.requeue_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 { @@ -612,3 +616,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 f909360ec70d..15b8c365b9fa 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -204,6 +204,10 @@ default: true, description: "Enable/disable FUA support when cache_size is us= ed.", }, + max_sectors: u32 { + default: 0, + description: "Maximum size of a command (in 512B sectors)", + }, }, } =20 @@ -307,6 +311,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { requeue_inject: Arc::pin_init(FaultConfig::new(c"reque= ue_inject"), GFP_KERNEL)?, #[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)?; } @@ -352,6 +357,7 @@ struct NullBlkOptions<'a> { requeue_inject: Arc, #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] timeout_inject: Arc, + max_sectors: u32, } =20 #[pin_data] @@ -487,6 +493,7 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { requeue_inject, #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] timeout_inject, + max_sectors, } =3D options; =20 let memory_backed =3D tag_set.memory_backed; @@ -548,7 +555,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EC9724BCAA2; Tue, 9 Jun 2026 19:10:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032252; cv=none; b=IH7B7IagJphf9sw6jv4R0GdAzLxPMATcKRtZpehppe0JEnMxdx7jdi2E3Yu8ygxSrsgciZWcook7P7buPqPAlqoAWj7gyCjCdKIAVK6Rsz4rZ3FAuIARejL18a80vrURdV020n1E8qXYrkZPAgWx81N0v4dodHoRHWf1Hpo0brQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032252; c=relaxed/simple; bh=/gyZSLKa2TzHdBLdP1ZJPQOHQ8gDmiUSAZ/ZBCdP9G0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=PtFvB7Vw3VU4QHDmJNHe3qD9XlKlidFQMx4022lJq505zrKtoAliH5xTr+N3mtez3UT3686X1o5VuByjN+ErOtg+DBzVOA0IJO0LUOyQMn44rq3Mrk9rjMa3vwn3QcEZp+NiL7mfDCEENBck+M4p+UIXoxVaQ61U0db3GDhdn/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=l6D/bcpS; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="l6D/bcpS" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 562B01F00898; Tue, 9 Jun 2026 19:10:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032251; bh=6HqbIExdtN+AFhFGHKtY0qW2xobLkHXMaLIXspxSgS8=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=l6D/bcpSBHJLSkUvVBypuKACqnuIrkZBk+O3bR/NcXh4yg6VprxqYRJdD8mi33lzi uQ2HmLjWQqdo6F5rNTZU+sGh4hTf2xdX8xiX82z9pTwLYFa17wFMl/CfwGTu51KZNu k5EzAHtnLS7/2Na+8Wc30PUDOcGdKvFfIt0YU+ECG8oFIWdIeHzgXhCFy9bfRDBa1A pMN97/jjWoi+KGZv7D1v9lbz/cAaz89L1UgVDxSbNLBo39tLNclsRpF0Vb1OZvqNbE gXo9gX1PS6ug3TwCVOwX5e3ytkJm1EpTQ5EilMEVMW/C6sQSt1kXK+wW/w/WJT+KfZ L4KPp2m0QiRgQ== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:08:59 +0200 Subject: [PATCH v2 80/83] 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: <20260609-rnull-v6-19-rc5-send-v2-80-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1931; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=/gyZSLKa2TzHdBLdP1ZJPQOHQ8gDmiUSAZ/ZBCdP9G0=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGT9TT0R4aUtBtSeuFFz1LbiE1VCD8K7ODM7E HRL8FuebEOJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk/QAKCRD6UCkIqsW9 0AZRD/9o4BTmJllPti16gUC3pq38UjvIoqBZxFPKYSys1RmxO+GlJRTZhPxaMmMvC3xZiw4wO3D WoiymkCJM16OQvte0AYdP3F7/GsJBlASsCR26pDFFbbFp8xG1UnwouPXwA/gJjWfQ5IhjA14IC8 MVjQ8guFgpMDjzcrdOlbrq+x62bDx6Bh3eZImuQnZJ0vRGnFWM5J2H9GTGbSEUJ7cMNxAY7I64A hlcjpdPTpTbrEQjf07jqihO+UQ+1TE/45VcWtwdqFzws1IocOuarZVN+UQRjTULDzjh6ZbO8E6G QJCxvd7NoofCN7ykAra33D2JQlM9WOK9uRzcM5T3/UJZCGJ/9ddd6QKq7zZqPDWB+uGIbLPpAI8 vKEaFpCx84GwO4Q6S/z4DIq0AJRwfvCYqzn4YocMN/RSajWK/n4Zsh27llz/xmi8Fwpexy4um1d ZfdlHNnc+N9fzzAw7msyM3cM5JcW2hKp7Ld4qtDOxZ3V6EHSGfyIN3wcoYThPZxYOT99SwZF2c7 cqen7CXW6JUdqbdgpnskGdzXm6b73Jxjozc0Mf+ZGn/un8dkRV7R9dKbyy71NV6rOTrWqusoqxo wyU+nt1pe/SKYe1opyuU+H9rGDE4wg7Mo6LH8H0c87jgCScnJ1N+Y8iVSziHlxesRcv52u5Bcqb n8L8bBljBkJo53g== 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 6d760dafade5..38057ebc0878 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -59,6 +59,7 @@ pub struct GenDiskBuilder { write_cache: bool, forced_unit_access: bool, max_sectors: u32, + virt_boundary_mask: usize, _p: PhantomData, } =20 @@ -79,6 +80,7 @@ fn default() -> Self { write_cache: false, forced_unit_access: false, max_sectors: 0, + virt_boundary_mask: 0, _p: PhantomData, } } @@ -189,6 +191,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, @@ -208,6 +219,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A986741D9E8; Tue, 9 Jun 2026 19:17:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032654; cv=none; b=W4HdcNlGPt4dPMMrSbFl6cuVEh64xjpQgL1cp8R7k16zBZcO9FgjmfnoYr6cSoG2Zy3z1kd3cLxytuCZRkEzuOE4MobjX4mG8nb8lzegV0+bt+o9pLX8CQThxh3XcsxbpG8TXAGuobui5DJCqnpwpfjz9U9nS7uF2n35QATAXqY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032654; c=relaxed/simple; bh=mQLcR4pADqmwISKMJS96CwmsvxCUmL6qoCmsZgQdi8k=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=G3lqT+CWNosHFTMKor0saTQ9Eq+rKpBpTAyD/GTbnZnwgWrHNpuBzWAbymeFq5raurOUYgV4mIWTAljs62RfMVB5ppIv09JQjaLovFfzutnaFKHzMXlnGFtO7UPdWaJ+sjCwNBAa9b1ZpE5PvNPc6XEEFqPmmhFqM4y9nj0/1MQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=LruU/bVU; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="LruU/bVU" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 37F381F00893; Tue, 9 Jun 2026 19:17:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032653; bh=p4d5I6NPrj9QC3rwhT3LQJHa+sFgEUUsxiN1Rw94+DE=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=LruU/bVU77MdbFwvtz6tVmVrLebbYltpAyDnwaI/L/LiUSYzHPzOHjRpQGG1B0B8k nn5Jmd8IxW3BhKtJQUSXx/YNYEZVj6eYpbutYbHnFIoEImSTZejbs2amCQM0+nlr71 L1MxooAh3o3g/sGwX4ONYXHiqpysjm4P2HZ7o/tt3CpyHSTQ3rtzCNmHxMVoQsrAls 3zgdYKdnNLIbrSlrsqieFvEbnj81xd3gOtI0GLc0GV92gyVjECNoQBQeWGNCA8kMCI 0LsjhRO3fgTgr/xfHgcQ4fASYVAOdRJ8zEtVO/PvfoYfqjXeIJ16HgtURboUMq2lGZ ZfHKDcLs8VkIw== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:09:00 +0200 Subject: [PATCH v2 81/83] 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: <20260609-rnull-v6-19-rc5-send-v2-81-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4411; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=mQLcR4pADqmwISKMJS96CwmsvxCUmL6qoCmsZgQdi8k=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGT9ToLPR7x5aepZTCfCm4HkjbgUTRQRr8bi+ tSPr1Q92iOJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk/QAKCRD6UCkIqsW9 0Aj8EAC2EoIkwX9KZomazlQrN/N8C7wXO55qgCiGfevd0Arj+x9lodAq2aTUUrDM01s/qoJZ2q1 LvRpFf+WmwsdVW7UFlwZgCfaiUBQdMT94rxnN0WMm8GVVE9DMC3ma4u/SVC8GLNgpzSU9UQ2gcw aZHwZTzw5/e80I9Qvin84j+ml5alD3PfvBvSkknSX3jgwp5XRcCva7H7XFUs68rvu+yV4p/cc6b Hg8LxEPI+7/ZdrOBLtEJ9pn5HhJqp6wZ5T5eMvnurirWHXzwBm4tEtsQNUsN1kUIM236MIR4rcf HeJ1LqEecW1kzwAUFEcz3z230Vuud6OmaaU6fkwqbDg3TkrPPfHbsO2gm92OzbvMv1l5u5V8PRt gMtAfjC+RS4XtVmilxUn6adKKG88lqpHr9K5pvPpBAPQkUAZnumxDvtiW1+KBeVP/Tx64vBGa/j sacJzs2KDVCTn/7sXKDoFunqaH0m34aby1033yoKawIwnK33+FGLHeFSU7UG78KubP2CyDWEu60 E/gt2qz4kn34tHBwPT6rVfUDomeePgXkKN9mqA62vZn5lmRHagfDZdnZxJHxd/CjXtl4q28PilY kfMdnyG5t5tf1lnoCf4rEq/0osNjf1KQBD9CXKMHFEJvIjbMq9Dda/UhHEg+rYAZZHW8R/uPmMu 6v6OG2Elg7F5Y4g== 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 5ab217e43e2b..3e054339226c 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -133,6 +133,7 @@ fn make_group( poll_queues: 27, fua: 28, max_sectors: 29, + virt_boundary: 30, ], }; =20 @@ -221,6 +222,7 @@ fn make_group( #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] init_hctx_inject, max_sectors: 0, + virt_boundary: false, }), }), default_groups, @@ -315,6 +317,7 @@ struct DeviceConfigInner { #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] init_hctx_inject: Arc, max_sectors: u32, + virt_boundary: bool, } =20 #[vtable] @@ -388,6 +391,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 { @@ -617,3 +621,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 15b8c365b9fa..147dc8498c3a 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::{ @@ -54,6 +57,7 @@ memalloc_scope, new_mutex, new_spinlock, + page::PAGE_SIZE, pr_info, prelude::*, revocable::Revocable, @@ -208,6 +212,10 @@ default: 0, description: "Maximum size of a command (in 512B sectors)", }, + virt_boundary: bool { + default: false, + description: "Set alignment requirement for IO buffers to be p= age size.", + }, }, } =20 @@ -312,6 +320,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(= ), })?; disks.push(disk, GFP_KERNEL)?; } @@ -358,6 +367,7 @@ struct NullBlkOptions<'a> { #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] timeout_inject: Arc, max_sectors: u32, + virt_boundary: bool, } =20 #[pin_data] @@ -494,6 +504,7 @@ fn new(options: NullBlkOptions<'_>) -> Result>> { #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] timeout_inject, max_sectors, + virt_boundary, } =3D options; =20 let memory_backed =3D tag_set.memory_backed; @@ -558,6 +569,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 29E4C4D2EE9; Tue, 9 Jun 2026 19:11:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032314; cv=none; b=QbPNV1hNVrdSXGvrxAtXA2CTbLlJ0oNsSgfc5Ss5sNhSe20kjdUd7G8g4rTk7BK0LvTnKMeKUD6RBcRNtdUXoBDD0vFd2oBcuSKQOcewFondcAsi1lhh7Nwhkh8gZbrgrbDia7ATHezWKm9rP+/nb5wTtycmMq2i7mVORDYAcdM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032314; c=relaxed/simple; bh=aC+DAIoXKVhLZjQ6VtxK/CU+nypY/dW+t6e//OC/LfI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ST0t+Q858cGPYXkX5ZwwKifaHaRrMrzyQTK7b9LLUj6NLjqAIfVAq2526cqzTFtZTY4jueeTreY2+JZv4MeKaXXU8pT/b6pUXbpSolppXMgaz/18Yd5WcMkUIDH3nP0iIRUdBTmcZp4iot9gvc73y9sBA4D7AzvXGx6v6pb+4dI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=BBqeDFBO; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="BBqeDFBO" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3E8E21F00893; Tue, 9 Jun 2026 19:11:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032313; bh=LG6tMe5IWSj2wlms7qY7Y93GPHV0FPQd/hj6Ivr47HU=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=BBqeDFBO6IhOZgXsVP1UWzizuRgZi3kU7sfun64CmXti2K6oPjtIBK9ig3BdDxI99 cF7sX5GSOB9a6RMswzwGJ7G7wZWyz/OfM3ww7kqrGDbMJUrfhm2MfUAy4dhiIJfUv9 lxKN4uCMP2DKqoRBLiWW+W+lxD3bzRPoS1unHHzrTSIUA5h5UlVsTL+dXBA1j4FZLV rMUy57xguGXz2SWZBBNK+OUjIdOGj04TXu2YFAKrqi0ypCd7uemPexNeQWMWarv446 GXbWEZYV7iPTK5o/ZILzn8GsZmANF8B20sLgQqjMB8ZeqEKOEZdqbn6/zR5YMgJkYr bqqCWK/4xzSTA== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:09:01 +0200 Subject: [PATCH v2 82/83] 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: <20260609-rnull-v6-19-rc5-send-v2-82-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=5599; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=aC+DAIoXKVhLZjQ6VtxK/CU+nypY/dW+t6e//OC/LfI=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGT+NlnHQn5rbaLOqNWNJZ4pO68g/2hZz04u7 eduX1XgsBqJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk/gAKCRD6UCkIqsW9 0PeWD/90gWv84QyKgx1/RYvYIvXyS8hP7dIG5obg9kqAuuG0GqZasGqXWQwPi7fElVX7xT3U2jr HPB7ByYGppRrwvyHt6ai6tGJAAY8No0tir/Lf5sW+HIZy4rJmWSid+N9fhnWj0yft0TGKqmMx/v ucDqJO9i3hfO6qGo/0nFAgB2f4ao7DZ66A3Z494exLc3UvYGIbhgo6Ux1GcY8EZvKmD4Sz2mKDq U7Sgv/YbBaacDWVZSopGuy6V3KwESVPjmEUFwVaXxKvgK5enyB+DryohgVsTKzrcZB2Kxi0dIoT 42UzjEo+Kw6pNiADr2FvTSzmGfpdGbJta9bdVt/ehnJPf4RYMiaASV3JkY1W5bhaUtqyU6nsvca YnW0ymLhTvxVkaaUHODoEyhbMYRpPKoDd/VYXcW37a/QATqEYS0NX5O9j8j/H55gpDRt/SiN9qc BsTP/kqQAk3962iTwxTiDEeNfcGbZ/jSszC4svAyMcqMC21DBdq41SrV8fhKOeigFaWuzeilq9U PO6lsfFeREpMMJkPbT8hyElT3qnFzQ4qk7MgfpEp+XqZCqyFqKnISg+ECIm8gwcsEYFD3FNukZk gqKc75NFApUc40oqvG4e76V8QyYJNVoK6Tg38XcH59mRvwQibccjYe/xcrGVJ7MGeTZmzB4Zo9T rtv+TYGXxJnL7zg== 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 | 12 ++++++++++++ rust/kernel/block/mq/tag_set/flags.rs | 4 ++++ 3 files changed, 21 insertions(+) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 3e054339226c..1bab38c55698 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -134,6 +134,7 @@ fn make_group( fua: 28, max_sectors: 29, virt_boundary: 30, + shared_tag_bitmap: 31, ], }; =20 @@ -223,6 +224,7 @@ fn make_group( init_hctx_inject, max_sectors: 0, virt_boundary: false, + shared_tag_bitmap: false, }), }), default_groups, @@ -318,6 +320,7 @@ struct DeviceConfigInner { init_hctx_inject: Arc, max_sectors: u32, virt_boundary: bool, + shared_tag_bitmap: bool, } =20 #[vtable] @@ -374,6 +377,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result { blocking: guard.blocking, memory_backed: guard.memory_backed, no_sched: guard.no_sched, + shared_tag_bitmap: guard.shared_tag_bitmap, hw_queue_depth: guard.hw_queue_depth, #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] init_hctx_inject: guard.init_hctx_inject.clone(), @@ -622,3 +626,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 147dc8498c3a..81f9e2d03f31 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -216,6 +216,10 @@ default: false, description: "Set alignment requirement for IO buffers to be p= age size.", }, + shared_tag_bitmap: bool { + default: false, + description: "Use shared tag bitmap for all submission queues = for blk-mq.", + }, }, } =20 @@ -245,6 +249,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { let memory_backed =3D module_parameters::memory_backed.value(); let no_sched =3D module_parameters::no_sched.value(); let hw_queue_depth =3D module_parameters::hw_queue_depth.value= (); + let shared_tag_bitmap =3D module_parameters::shared_tag_bitmap= .value(); =20 let shared_tag_set =3D NullBlkDevice::build_tag_set(TagSetOpti= ons { home_node, @@ -258,6 +263,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { blocking, memory_backed, no_sched, + shared_tag_bitmap, hw_queue_depth, #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] init_hctx_inject: Arc::pin_init( @@ -300,6 +306,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit { blocking, memory_backed, no_sched, + shared_tag_bitmap, hw_queue_depth, #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] init_hctx_inject: Arc::pin_init( @@ -404,6 +411,7 @@ struct TagSetOptions { blocking: bool, memory_backed: bool, no_sched: bool, + shared_tag_bitmap: bool, hw_queue_depth: u32, #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] init_hctx_inject: Arc, @@ -419,6 +427,7 @@ fn build_tag_set(options: TagSetOptions) -> Result>> { blocking, memory_backed, no_sched, + shared_tag_bitmap, hw_queue_depth, #[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)] init_hctx_inject, @@ -441,6 +450,9 @@ fn build_tag_set(options: TagSetOptions) -> Result>> { if no_sched { flags |=3D mq::tag_set::Flag::NoDefaultScheduler; } + if shared_tag_bitmap { + flags |=3D mq::tag_set::Flag::TagHctxShared; + } =20 let queue_config_guard =3D queue_config.lock(); let submit_queues =3D queue_config_guard.submit_queues; diff --git a/rust/kernel/block/mq/tag_set/flags.rs b/rust/kernel/block/mq/t= ag_set/flags.rs index 2561d7090c49..afc9d31ed998 100644 --- a/rust/kernel/block/mq/tag_set/flags.rs +++ b/rust/kernel/block/mq/tag_set/flags.rs @@ -21,5 +21,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 Jun 12 04:44:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6DA384EA37B; Tue, 9 Jun 2026 19:12:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032380; cv=none; b=UkBSZN8zicKF/gOB1VRpGYYllezF9cLdcwQXzCj5EE0sQAV3KPzlWWF2+unvukCni5xbPuwLfgQDczjZWjZangh/ConKgn1+Iks8V43qquoucZcf/3NxTNINLZpB/orRsV7l+3tjBdy9xIlptbxc1Cv5/TiNB2khV7Bq1ia/Grg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781032380; c=relaxed/simple; bh=T3pkCjSoqlz/5eAmoml0zuQJKOlZemt616lr74wbC6g=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=FuOtRjKbMAHsUpi9/H6ewYOFTYXMBavEuITna+94yG3iTsYgPVxh6pTc5uXnhm7Fe8VcxU59YkFT4uk5B5X8kezUNiDrOf4u6teMncwJ4keTgUnvagzL+wwORGbYAP27lUja+YNfHDAiA9eqW2kCjfQ8jd4n1vup8s+oz2rSYSs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=H/lEsSss; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="H/lEsSss" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 84E301F00893; Tue, 9 Jun 2026 19:12:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781032379; bh=as5ubVXy9pDah6a38S1Rm853FewwW17NrvS6Iq2Oams=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=H/lEsSssWyII8rY8RleQpGtSmi85awUyW7eU8iflg601qoIsT4qvYikr89gNkQ9En kpHtW+3/uHGG0TQLBmeLNw4LNRWw9HxF1h1SsrzJVmefWnkA0GJBdqgBqcSDdifXK+ BJ3OGGtsAmojbqYNtD2QRYQoxG2VwRH3b5UaRJp6C1csUcgiQ42EG3BcXVWWfwxJIe BmF2GyFgl5qXK0j/NGKK8XWulaEixftONEsoWE9ClN+AXkmLxtx7Pfx3/ejBB3vDxX tneJSr04kv1HM5s95zRZlMrMR7bhLMt3o5nGzJpRo1QEht8RBBtk504u3WHLdFQf6g 4U6JzxI2Ar/Jg== From: Andreas Hindborg Date: Tue, 09 Jun 2026 21:09:02 +0200 Subject: [PATCH v2 83/83] 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: <20260609-rnull-v6-19-rc5-send-v2-83-82c7404542e2@kernel.org> References: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> In-Reply-To: <20260609-rnull-v6-19-rc5-send-v2-0-82c7404542e2@kernel.org> To: "Liam R. Howlett" , Alice Ryhl , Anna-Maria Behnsen , Benno Lossin , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Boqun Feng , Danilo Krummrich , FUJITA Tomonori , Frederic Weisbecker , Gary Guo , Jens Axboe , John Stultz , Lorenzo Stoakes , Lyude Paul , Miguel Ojeda , Stephen Boyd , Thomas Gleixner , Trevor Gross , "Liam R. Howlett" , Boqun Feng , Lorenzo Stoakes Cc: Andreas Hindborg , linux-block@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=15517; i=a.hindborg@kernel.org; h=from:subject:message-id; bh=T3pkCjSoqlz/5eAmoml0zuQJKOlZemt616lr74wbC6g=; b=owEBbQKS/ZANAwAKAfpQKQiqxb3QAcsmYgBqKGT/2B21XD7rkanWLOYqJi6E0g5N/32wBgJiA UCG8Z2zaGOJAjMEAAEKAB0WIQRXitnI2WZ2JirAaob6UCkIqsW90AUCaihk/wAKCRD6UCkIqsW9 0A+vD/4/R6jsny4DtPvTLS1Zr0bhgTMsff+cmB0ZHGOBF9MtJTa/RQYLe0TMvKsNc/IkT+Kgmvm A5i9CAXdoH9F+qJN5qNlaD2zK7Z1OuHFiEP//e3oPSP6dML1WQDU+tQL/podg/speszIl1GvbcY BvDSKpJuEWoNNjKCqTRjJD8LNKMNjTrags41FXp45aiVXyDpR6c3e7nOgjOBHbxMNSnL3D9aEzr zXaxnRtik7GXFMqqggZ9npK1ksk33uqIBLNVWz9TTnlwt7lPph4vfOjBreeKPI5slUKaXv0dOb1 9LBh+pu4tvgbpXJoC1qxZv51uD1g3fP6nXyxBZL5iSLBJGNBRUy8ytmLuAToScyG+NMXMSQymn7 L/1gju6Hg7GQWX1PDXvkGpFZhYwZcgq8uH59xstBP80I79wcq3bWBqBbobtIKsfMCEsFxt23ylr JaOG1XYJ0t89feKumt9mtu32Eavil0dSQG2KICfpsAUhDEQFIR4BoiwlMxmOgnO3AMEyWmLupe8 3Mp4lwR3QoWLERLfx1owl6xHJKDTNyjY+IaccJTfGP0cK2ONl56Fe4s26NwF/CwXoKD18W8zbtJ pGeyYuKoT6AppvcmtiANY3tEOldxVP9uH5e8HeoYdl4NnQnS8v35lObymH6iOA+ZUtPAhdPWaC0 B5YQyVZzCWRu3zQ== 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 | 76 +++++++++++++++++++++++++++++++++ drivers/block/rnull/disk_storage.rs | 59 +++++++++++++------------- drivers/block/rnull/rnull.rs | 2 +- drivers/block/rnull/zoned.rs | 83 ++++++++++++++++++++++++++-------= ---- 4 files changed, 164 insertions(+), 56 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs= .rs index 1bab38c55698..43d01b419579 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -135,6 +135,8 @@ fn make_group( max_sectors: 29, virt_boundary: 30, shared_tag_bitmap: 31, + zone_offline: 32, + zone_readonly: 33, ], }; =20 @@ -627,3 +629,77 @@ 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 6797b7996da3..879dd5d96e65 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(); @@ -286,25 +304,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 TreeNode =3D KBox; diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 81f9e2d03f31..b6371fe4ebeb 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -798,7 +798,7 @@ fn handle_regular_command( if self.memory_backed { memalloc_scope!(let _noio: NoIo); 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 808449cc49e1..cf0eb5d31840 100644 --- a/drivers/block/rnull/zoned.rs +++ b/drivers/block/rnull/zoned.rs @@ -179,7 +179,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)?, } @@ -187,18 +187,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 @@ -214,10 +210,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), } } @@ -283,7 +279,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; @@ -367,7 +363,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 @@ -420,7 +416,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); } @@ -436,13 +432,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> (), @@ -459,14 +455,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), } } @@ -535,7 +530,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); } @@ -547,12 +542,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), @@ -568,7 +563,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 { @@ -591,16 +585,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, @@ -628,7 +661,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