Linux block layer
 help / color / mirror / Atom feed
* Re: [PATCHv3] blk-mq: pop cached request if it is usable
From: Ming Lei @ 2026-05-22  4:12 UTC (permalink / raw)
  To: Keith Busch; +Cc: Keith Busch, axboe, hch, linux-block
In-Reply-To: <ag-1EoopSdp0JZR5@kbusch-mbp>

On Thu, May 21, 2026 at 07:44:50PM -0600, Keith Busch wrote:
> On Fri, May 22, 2026 at 07:33:39AM +0800, Ming Lei wrote:
> > 
> > BTW, as mentioned in v2, the request may be added back in case of merge,
> > but seems not a big deal given blk_mq_free_plug_rqs() doesn't free requests
> > in batch.
> 
> We could introduce a special goto label for the merge case to push it

It can be done simply by replacing the added `blk_mq_free_request` with moving
it back to plug list.

> back to the cached requests, but it's not clear that it's worth it. The
> cached requests are preallocated to match what is about to be dispatched
> (up to a limit), so if a merge happens, there may not be another
> subsequent bio submissions to use it, so it would just get freed as an
> unused request later anyway. And if we block on the split bio allocation
> such that it frees the cached rq list, I think we've already lost the
> optimization battle, so no need to complicate things.

I mean plain sequential IO workload, in which batch allocation is taken, and
just the 1st request is used, all others are freed one by one, which may
be improved to batch release too in theory.


Thanks,
Ming

^ permalink raw reply

* Re: [PATCH] rust: add procedural macro for declaring configfs attributes
From: Andreas Hindborg @ 2026-05-22  6:20 UTC (permalink / raw)
  To: Gary Guo, Malte Wechter, Breno Leitao, Miguel Ojeda, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Jens Axboe
  Cc: linux-kernel, rust-for-linux, linux-block
In-Reply-To: <DIO4B1VH1JZT.27KYCZG39SKDX@garyguo.net>

"Gary Guo" <gary@garyguo.net> writes:

> On Wed May 20, 2026 at 9:40 AM CEST, Malte Wechter wrote:
>> Implement `configfs_attrs!` as a procedural macro using `syn`, this
>> improves readability and maintainability. Remove the old macro and
>> replace all uses with the new macro. Add the new macro implementation
>> file to MAINTAINERS.
>
> The current declarative macro implementation is quite readable and IMO more so
> than this syn rewrite.
>
> Unless there's a compelling use case where it needs to be implememented using
> token munchers with declarative macro but can be easily parsed with proc macro,
> I think it should just stay as decl macro.

I disagree. I think the proc macro is difficult to read and maintain,
and I prefer a procedural parser.

Best regards,
Andreas Hindborg


^ permalink raw reply

* Re: [PATCH] loop: add sync_blockdev() in __loop_clr_fd() to prevent UAF
From: Tetsuo Handa @ 2026-05-22  6:54 UTC (permalink / raw)
  To: Ming Lei
  Cc: Jens Axboe, linux-block, syzbot+cd8a9a308e879a4e2c28,
	syzbot+bc273027d5643e48e5b3, Hongling Zeng, Bart Van Assche,
	Andrew Morton
In-Reply-To: <ag_RZc908B64hTFG@fedora>

On 2026/05/22 12:45, Ming Lei wrote:
> On Fri, May 22, 2026 at 12:28:34PM +0900, Tetsuo Handa wrote:
>> On 2026/05/22 11:54, Ming Lei wrote:
>>> Fix this by adding sync_blockdev() in __loop_clr_fd() to flush all
>>> pending writeback I/O before clearing lo->lo_backing_file. Since the
>>> loop disk is already closed at this point, no new I/O can be submitted
>>> — only writeback remains.
>>
>> Why can you assume that synchronize_rcu() + drain_workqueue(lo->workqueue)
>> is unnecessary? Since we don't know exact commit which is causing this
>> problem, we don't know what has changed.
> 
> When sync_blockdev() returns, there can't be any inflight IO:
> 
> - no one can open this loop disk
> - no dirty page cache any more
> 
> So why do you want to add rcu/drain_workqueue?

The fact that __loop_clr_fd() is called before lo_rw_aio() completes suggests that
some works queued before __loop_clr_fd() is called have not been completed.

I don't know what sync_blockdev() guarantees, but even if sync_blockdev() can guarantee
both "loop_queue_rq() is no longer running" and "all pending AIO requests issued by
lo_rw_aio() have been completed", I think that sync_blockdev() cannot determine whether
lo->lo_device has pending work or not.

Therefore, I guess that at least drain_workqueue() is needed before sync_blockdev().
If sync_blockdev() cannot guarantee "loop_queue_rq() is no longer running",
I guess that both synchronize_rcu() + drain_workqueue() are needed before sync_blockdev().

(And I don't know whether sync_blockdev() is appropriate because we don't know what change
made this problem visible. We didn't hit this problem until Linux 7.0.)

> 
> Fixes: 1fe0b1acb14d ("loop: only freeze the queue in __loop_clr_fd when needed")

The Fixes: for these reports should be the commit which made this problem visible.
Or, this patch will be needlessly backported to stable kernels.


^ permalink raw reply

* Re: [PATCH v7 00/43] btrfs: add fscrypt support
From: Daniel Vacek @ 2026-05-22  7:00 UTC (permalink / raw)
  To: Eric Biggers, David Sterba
  Cc: linux-block, linux-fscrypt, linux-btrfs, linux-kernel,
	Chris Mason, Josef Bacik, Theodore Y. Ts'o, Jaegeuk Kim,
	Jens Axboe
In-Reply-To: <20260513085340.3673127-1-neelx@suse.com>

On Wed, 13 May 2026 at 10:54, Daniel Vacek <neelx@suse.com> wrote:
>
> Hello,
>
> These are the remaining parts from former series [1] from Omar, Sweet Tea
> and Josef.  Some bits of it were split into the separate set [2] before.
>
> Notably, at this stage encryption is not supported with RAID5/6 setup
> and send is also disabled for now.
>
> For straight git access you can find this series as the `fscrypt-v7` tag
> e85358ef9fba here:
>
> [0] https://github.com/dvacek/linux-btrfs/tree/fscrypt-v7
>
> Changes since v6 [3]
>  * Rebased onto linux v7.1-rc3.
>  * Adapted to the v7.0 fscrypt API changes, mostly following commit
>    bb8e2019ad613 ("blk-crypto: handle the fallback above the block layer")
>  * Addressed all the review feedback, thanks to Eric Biggers, Chris Mason
>    (and his LLM review prompts) and Neal Gompa.
>  * Adapted to the v7.1 fscrypt API cleanups, using byte offsets as function
>    arguments instead of logical block numbers for newly introduced functions.
>    This should match https://lore.kernel.org/linux-fscrypt/20260218061531.3318130-1-hch@lst.de/
>    As a result btrfs_set_bio_crypt_ctx_from_extent() and btrfs_mergeable_encrypted_bio()
>    helpers were no longer needed and they got removed.

Hi Eric,

This is just a gentle ping.
I was wondering if you had a chance to look at this version?
I believe all your previous feedback has been addressed and this
version is solid.
Please, let me know your thoughts.

Regards,
Daniel

> There are a few changes since v5 [1]:
>  * Rebased to btrfs/for-next branch.  Couple things changed in the last
>    years.  A few patches were dropped as the code cleaned up or refactored.
>    More details in the patches themselves.
>  * As suggested by Qu and Dave, the on-disk format of storing the extent
>    encryption context was re-designed.  Now, a new tree item with dedicated
>    key is inserted instead of extending the file extent item.  As a result
>    a special care needs to be taken when removing the encrypted extents
>    to also remove the related encryption context item.
>  * Fixed bugs found during testing.
>
> Have a nice day,
> Daniel
>
> [1] v5 https://lore.kernel.org/linux-btrfs/cover.1706116485.git.josef@toxicpanda.com/
> [2]    https://lore.kernel.org/linux-btrfs/20251112193611.2536093-1-neelx@suse.com/
> [3] v6 https://lore.kernel.org/linux-btrfs/20260206182336.1397715-1-neelx@suse.com/
>
> Josef Bacik (33):
>   fscrypt: add per-extent encryption support
>   fscrypt: allow inline encryption for extent based encryption
>   fscrypt: add a __fscrypt_file_open helper
>   fscrypt: conditionally don't wipe mk secret until the last active user
>     is done
>   blk-crypto: add a process bio callback
>   fscrypt: add a process_bio hook to fscrypt_operations
>   fscrypt: add documentation about extent encryption
>   btrfs: add infrastructure for safe em freeing
>   btrfs: select encryption dependencies if FS_ENCRYPTION
>   btrfs: add fscrypt_info and encryption_type to ordered_extent
>   btrfs: plumb through setting the fscrypt_info for ordered extents
>   btrfs: populate the ordered_extent with the fscrypt context
>   btrfs: keep track of fscrypt info and orig_start for dio reads
>   btrfs: add extent encryption context tree item type
>   btrfs: pass through fscrypt_extent_info to the file extent helpers
>   btrfs: implement the fscrypt extent encryption hooks
>   btrfs: setup fscrypt_extent_info for new extents
>   btrfs: populate ordered_extent with the orig offset
>   btrfs: set the bio fscrypt context when applicable
>   btrfs: add a bio argument to btrfs_csum_one_bio
>   btrfs: limit encrypted writes to 256 segments
>   btrfs: implement process_bio cb for fscrypt
>   btrfs: implement read repair for encryption
>   btrfs: add test_dummy_encryption support
>   btrfs: make btrfs_ref_to_path handle encrypted filenames
>   btrfs: deal with encrypted symlinks in send
>   btrfs: decrypt file names for send
>   btrfs: load the inode context before sending writes
>   btrfs: set the appropriate free space settings in reconfigure
>   btrfs: support encryption with log replay
>   btrfs: disable auto defrag on encrypted files
>   btrfs: disable encryption on RAID5/6
>   btrfs: disable send if we have encryption enabled
>
> Omar Sandoval (6):
>   fscrypt: expose fscrypt_nokey_name
>   btrfs: start using fscrypt hooks
>   btrfs: add inode encryption contexts
>   btrfs: add new FEATURE_INCOMPAT_ENCRYPT flag
>   btrfs: adapt readdir for encrypted and nokey names
>   btrfs: implement fscrypt ioctls
>
> Sweet Tea Dorminy (4):
>   btrfs: handle nokey names
>   btrfs: add get_devices hook for fscrypt
>   btrfs: set file extent encryption excplicitly
>   btrfs: add fscrypt_info and encryption_type to extent_map
>
>  Documentation/filesystems/fscrypt.rst |  41 +++
>  block/blk-crypto-fallback.c           |  41 +++
>  block/blk-crypto-internal.h           |   8 +
>  block/blk-crypto-profile.c            |   2 +
>  block/blk-crypto.c                    |   6 +-
>  fs/btrfs/Kconfig                      |   4 +
>  fs/btrfs/Makefile                     |   1 +
>  fs/btrfs/accessors.h                  |   2 +
>  fs/btrfs/backref.c                    |  43 ++-
>  fs/btrfs/bio.c                        | 155 +++++++++-
>  fs/btrfs/bio.h                        |  14 +-
>  fs/btrfs/btrfs_inode.h                |   7 +-
>  fs/btrfs/compression.c                |   6 +
>  fs/btrfs/ctree.h                      |   3 +
>  fs/btrfs/defrag.c                     |  14 +
>  fs/btrfs/delayed-inode.c              |  25 +-
>  fs/btrfs/delayed-inode.h              |   5 +-
>  fs/btrfs/dir-item.c                   | 110 ++++++-
>  fs/btrfs/dir-item.h                   |  10 +-
>  fs/btrfs/direct-io.c                  |  28 +-
>  fs/btrfs/disk-io.c                    |   3 +-
>  fs/btrfs/extent_io.c                  | 115 ++++++-
>  fs/btrfs/extent_io.h                  |   3 +
>  fs/btrfs/extent_map.c                 | 102 ++++++-
>  fs/btrfs/extent_map.h                 |  26 ++
>  fs/btrfs/file-item.c                  |  28 +-
>  fs/btrfs/file-item.h                  |   2 +-
>  fs/btrfs/file.c                       |  79 +++++
>  fs/btrfs/fs.h                         |   6 +-
>  fs/btrfs/fscrypt.c                    | 413 ++++++++++++++++++++++++++
>  fs/btrfs/fscrypt.h                    |  86 ++++++
>  fs/btrfs/inode.c                      | 404 +++++++++++++++++++------
>  fs/btrfs/ioctl.c                      |  41 ++-
>  fs/btrfs/ordered-data.c               |  35 ++-
>  fs/btrfs/ordered-data.h               |  14 +
>  fs/btrfs/reflink.c                    |  43 ++-
>  fs/btrfs/root-tree.c                  |   9 +-
>  fs/btrfs/root-tree.h                  |   2 +-
>  fs/btrfs/send.c                       | 134 ++++++++-
>  fs/btrfs/super.c                      |  99 +++++-
>  fs/btrfs/super.h                      |   3 +-
>  fs/btrfs/sysfs.c                      |   6 +
>  fs/btrfs/tree-checker.c               |  64 +++-
>  fs/btrfs/tree-log.c                   |  34 ++-
>  fs/btrfs/volumes.c                    |   5 +
>  fs/crypto/crypto.c                    |  10 +-
>  fs/crypto/fname.c                     |  36 ---
>  fs/crypto/fscrypt_private.h           |  51 +++-
>  fs/crypto/hooks.c                     |  38 ++-
>  fs/crypto/inline_crypt.c              |  91 +++++-
>  fs/crypto/keyring.c                   |  18 +-
>  fs/crypto/keysetup.c                  | 164 ++++++++++
>  fs/crypto/policy.c                    |  47 +++
>  include/linux/blk-crypto.h            |  15 +-
>  include/linux/fscrypt.h               | 127 ++++++++
>  include/uapi/linux/btrfs.h            |   1 +
>  include/uapi/linux/btrfs_tree.h       |  26 +-
>  57 files changed, 2665 insertions(+), 240 deletions(-)
>  create mode 100644 fs/btrfs/fscrypt.c
>  create mode 100644 fs/btrfs/fscrypt.h
>
> --
> 2.53.0
>

^ permalink raw reply

* [PATCH blktests RFC 0/2] add JSON format reporting
From: Shin'ichiro Kawasaki @ 2026-05-22  7:43 UTC (permalink / raw)
  To: linux-block; +Cc: Ted Ts'o, dennis.maisenbacher, Shin'ichiro Kawasaki

During discussions at LSFMMBPF 2026, I learned that there is interest in
machine-readable result reporting for blktests. Ted described how the
machine-readable reporting will be used in this post [1].

[1] https://lore.kernel.org/linux-block/20260511154046.GA21199@macsyma-wired.lan/

This series is an initial attempt to add this feature to blktests. There
are two possible reporting formats: JUnit XML (used by fstests) and JSON.
JSON appears to be more widely used, so this series implements reporting
in JSON format.

The first patch adds the option for JSON format reporting. The report
includes the results of each test case. The second patch adds summary
statistics to the report: counts of passed, failed, and not_run test
cases.

An example command line to use this feature is shown below [2]. The
--json option specifies the JSON file for output. Results for each test
case are reported with the "status" field.

Comments and feedback are welcome as always.

Ted, please review the JSON output to see if it meets your needs when you
have time.

[2] command line example

# ./check --json /tmp/loop.json loop
loop/001 (scan loop device partitions)                       [passed]
    runtime  1.411s  ...  0.820s
loop/002 (try various loop device block sizes)               [passed]
    runtime  0.953s  ...  0.494s
loop/003 (time opening and closing an unbound loop device)   [passed]
    runtime  1.245s  ...  0.685s
loop/004 (combine loop direct I/O mode and a custom block size) [passed]
    runtime  1.624s  ...  1.611s
loop/005 (call LOOP_GET_STATUS{,64} with a NULL arg)         [passed]
    runtime  0.137s  ...  0.065s
loop/006 (change loop backing file while creating/removing another loop device) [passed]
    runtime  31.287s  ...  31.201s
loop/007 (update loop device capacity with filesystem)       [passed]
    runtime  0.532s  ...  0.489s
loop/008 (setup GPT and ESP on a raw disk image)             [passed]
    runtime  0.813s  ...  0.848s
loop/009 (check that LOOP_CONFIGURE sends uevents for partitions) [passed]
    runtime  1.551s  ...  1.407s
loop/010 (check stale loop partition)                        [passed]
    runtime  98.465s  ...  98.253s
loop/011 (Make sure unsupported backing file fallocate does not fill dmesg with errors) [passed]
    runtime  0.395s  ...  0.434s
loop/012 (check for spurious partition removal when partscan is enabled) [passed]
    runtime  6.216s  ...  6.012s
# head -20 /tmp/loop.json
{
  "results": [
    {
      "name": "loop/001",
      "status": "pass",
      "description": "scan loop device partitions",
      "runtime": "0.820s",
      "exit_status": 0
    },
    {
      "name": "loop/002",
      "status": "pass",
      "description": "try various loop device block sizes",
      "runtime": "0.494s",
      "exit_status": 0
    },
    {
      "name": "loop/003",
      "status": "pass",
      "description": "time opening and closing an unbound loop device",
# tail -20 /tmp/loop.json
      "description": "Make sure unsupported backing file fallocate does not fill dmesg with errors",
      "runtime": "0.434s",
      "exit_status": 0
    },
    {
      "name": "loop/012",
      "status": "pass",
      "description": "check for spurious partition removal when partscan is enabled",
      "runtime": "6.012s",
      "exit_status": 0
    }
  ],
  "summary": {
            "total": 12,
            "passed": 12,
            "failed": 0,
            "not run": 0,
            "time": "142.319s"
          }
}


Shin'ichiro Kawasaki (2):
  check: add JSON format output option for test results
  check: add summary statistics to JSON report and --stats option

 check | 297 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 296 insertions(+), 1 deletion(-)

-- 
2.54.0


^ permalink raw reply

* [PATCH blktests RFC 1/2] check: add JSON format output option for test results
From: Shin'ichiro Kawasaki @ 2026-05-22  7:43 UTC (permalink / raw)
  To: linux-block; +Cc: Ted Ts'o, dennis.maisenbacher, Shin'ichiro Kawasaki
In-Reply-To: <20260522074345.227515-1-shinichiro.kawasaki@wdc.com>

Add --json command-line option to write test results in JSON format to a
specified file. The JSON output uses the "results" array containing test
result objects with name, status, description, runtime, and additional
fields like exit_status or skip_reasons depending on the test outcome.

To write result objects separated with commas, it is required to know
the number of the objects to properly handle the comma placement. For
that purpose, use a file-based counter since tests run in subshells and
environment variables don't share state across subshells. This counter
file and the temporary JSON output file are created using mktemp.

Signed-off-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
---
 check | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 176 insertions(+), 1 deletion(-)

diff --git a/check b/check
index c166fae..6ddb064 100755
--- a/check
+++ b/check
@@ -302,6 +302,34 @@ _output_status() {
 	echo "]"
 }
 
+_write_json_test_run() {
+	local device
+	local skip_reasons
+
+	if [[ ${TEST_RUN["status"]} == "not run" && -v SKIP_REASONS ]]; then
+		skip_reasons=$(IFS=';'; echo "${SKIP_REASONS[*]}")
+	fi
+	if [[ "${TEST_DEV:-}" ]]; then
+		device="$(basename "$TEST_DEV")"
+	elif [[ "${TEST_DEV_ARRAY:-}" ]]; then
+		local dev
+		local -a devs
+		for dev in "${TEST_DEV_ARRAY[@]}"; do
+			devs+=("${dev##*/}")
+		done
+		device=$(IFS=' '; echo "${devs[*]}")
+	fi
+
+	_write_json_entry "$TEST_NAME" \
+		"${TEST_RUN["status"]}" \
+		"${TEST_RUN["description"]}" \
+		"${TEST_RUN["runtime"]}" \
+		"${TEST_RUN["reason"]:-}" \
+		"${TEST_RUN["exit_status"]:-}" \
+		"$device" \
+		"$skip_reasons"
+}
+
 _output_skip_reasons()
 {
 	for reason in "${SKIP_REASONS[@]}"; do
@@ -489,6 +517,7 @@ _call_test() {
 
 	_write_test_run
 	_output_test_run
+	_write_json_test_run
 
 	if [[ ${TEST_RUN["status"]} = fail ]]; then
 		case "${TEST_RUN["reason"]}" in
@@ -723,7 +752,10 @@ _check_and_call_test_device() {
 	for TEST_DEV in "${TEST_DEVS[@]}"; do
 		if [[ $TEST_DEV == fallback ]]; then
 			if [[ -v SKIP_REASONS ]]; then
+				local skip_reasons
+				skip_reasons=$(IFS=';'; echo "${SKIP_REASONS[*]}")
 				_output_notrun "$TEST_NAME"
+				_write_json_skip_entry "$TEST_NAME" "$skip_reasons"
 				return 0
 			fi
 
@@ -1019,10 +1051,143 @@ declare -a TEST_DEV_ARRAY
 declare -A TEST_DEV_ARRAY_SYSFS_DIRS
 declare -A TEST_CASE_DEV_ARRAY
 declare -A TEST_CASE_DEV_ARRAY_SYSFS_DIRS
+
+declare JSON_OUTPUT_FILE
+declare JSON_OUTPUT_TEMP
+# plain text file with single number for JSON array comma placement
+declare COUNTER_FILE
+
+_init_json_output() {
+	[[ -z $JSON_OUTPUT ]] && return
+
+	JSON_OUTPUT_FILE="$JSON_OUTPUT"
+	JSON_OUTPUT_TEMP=$(mktemp blktests_json_XXXXXX)
+	COUNTER_FILE=$(mktemp blktests_counter_XXXXXX)
+
+	# Create/truncate the JSON file and write opening structure
+	echo '{' > "$JSON_OUTPUT_TEMP"
+	echo '  "results": [' >> "$JSON_OUTPUT_TEMP"
+
+	echo "0" > "$COUNTER_FILE"
+}
+
+_finalize_json_output() {
+	[[ -z $JSON_OUTPUT ]] && return
+
+	# Close the results array
+	echo '  ]' >> "$JSON_OUTPUT_TEMP"
+	# Close the JSON object
+	echo '}' >> "$JSON_OUTPUT_TEMP"
+
+	cp "$JSON_OUTPUT_TEMP" "$JSON_OUTPUT_FILE"
+	rm -f "$COUNTER_FILE" "$JSON_OUTPUT_TEMP"
+}
+
+_inc_counter() {
+	local counter
+
+	counter=$(<"$COUNTER_FILE")
+	((counter++))
+	echo "$counter" > "$COUNTER_FILE"
+	echo "$counter"
+}
+
+_add_json_entry() {
+	local counter=$1
+	local json_entry="$2"
+
+	# Add comma to previous entry if not the first entry
+	if ((counter > 1 )); then
+		# Add comma after the previous closing brace
+		sed -i '$ s/}/},/' "$JSON_OUTPUT_TEMP"
+	fi
+
+	echo "$json_entry" >> "$JSON_OUTPUT_TEMP"
+}
+
+_write_json_entry() {
+	local test_name="$1"
+	local status="$2"
+	local description="$3"
+	local runtime="$4"
+	local reason="$5"
+	local exit_status="$6"
+	local device="$7"
+	local skip_reasons="$8"
+	local counter
+
+	[[ -z $JSON_OUTPUT ]] && return
+
+	counter=$(_inc_counter)
+
+	local json_entry="    {
+      \"name\": \"$test_name\",
+      \"status\": \"$status\",
+      \"description\": \"$description\",
+      \"runtime\": \"$runtime\""
+
+	if [[ -n $reason ]]; then
+		json_entry+=","
+		json_entry+="
+      \"reason\": \"$reason\""
+	fi
+
+	if [[ -n $exit_status ]]; then
+		json_entry+=","
+		json_entry+="
+      \"exit_status\": $exit_status"
+	fi
+
+	if [[ -n $device ]]; then
+		json_entry+=","
+		json_entry+="
+      \"device\": \"$device\""
+	fi
+
+	if [[ -n $skip_reasons ]]; then
+		json_entry+=","
+		json_entry+="
+      \"skip_reasons\": \"$skip_reasons\""
+	fi
+
+	json_entry+="
+    }"
+
+	_add_json_entry "$counter" "$json_entry"
+}
+
+_write_json_skip_entry() {
+	local test_name="$1"
+	local skip_reasons="$2"
+
+	[[ -z $JSON_OUTPUT ]] && return
+
+	counter=$(_inc_counter)
+
+	local json_entry="    {
+      \"name\": \"$test_name\",
+      \"status\": \"not run\",
+      \"description\": \"\",
+      \"runtime\": \"\""
+
+	if [[ -n $skip_reasons ]]; then
+		json_entry+=","
+		json_entry+="
+      \"skip_reasons\": \"$skip_reasons\""
+	fi
+
+	json_entry+="
+    }"
+
+	_add_json_entry "$counter" "$json_entry"
+}
+
 _check() {
 	# shellcheck disable=SC2034
 	SRCDIR="$(realpath src)"
 
+	_init_json_output
+
 	local test_dev test_name
 	local -a all_test_devs_in_array
 	local km_log="${OUTPUT}/start.kmemleak"
@@ -1087,6 +1252,8 @@ _check() {
 		(($? != 0)) && ret=1
 	fi
 
+	_finalize_json_output
+
 	return $ret
 }
 
@@ -1136,6 +1303,8 @@ Test runs:
   -x, --exclude=TEST	 exclude a test (or test group) from the list of
 			 tests to run
 
+  -j, --json=FILE	 write test results in JSON format to the given file
+
 Miscellaneous:
   -c, --config=FILE	 load configuration from FILE instead of the default
 			 (\"./config\"); if this option is used multiple times,
@@ -1157,7 +1326,7 @@ Miscellaneous:
 
 _check_dependencies
 
-if ! TEMP=$(getopt -o 'do:q::x:c:h' --long 'device-only,quick::,exclude:,output:,config:,help' -n "$0" -- "$@"); then
+if ! TEMP=$(getopt -o 'do:q::x:c:j:h' --long 'device-only,quick::,exclude:,output:,config:,json:,help' -n "$0" -- "$@"); then
 	exit 1
 fi
 
@@ -1186,6 +1355,10 @@ while true; do
 			OPTION_EXCLUDE+=("$2")
 			shift 2
 			;;
+		'-j'|'--json')
+			OPTION_JSON="$2"
+			shift 2
+			;;
 		'-c'|'--config')
 			# shellcheck source=/dev/null
 			. "$2"
@@ -1218,6 +1391,8 @@ DEVICE_ONLY="${OPTION_DEVICE_ONLY:-${DEVICE_ONLY:-0}}"
 
 OUTPUT="${OPTION_OUTPUT:-${OUTPUT:-results}}"
 
+JSON_OUTPUT="${OPTION_JSON:-${JSON_OUTPUT:-}}"
+
 QUICK_RUN="${OPTION_QUICK_RUN:-${QUICK_RUN:-0}}"
 if [[ -n $OPTION_QUICK_RUN && $OPTION_QUICK_RUN -ne 0 ]]; then
 	TIMEOUT="${OPTION_TIMEOUT:-${TIMEOUT:-30}}"
-- 
2.54.0


^ permalink raw reply related

* [PATCH blktests RFC 2/2] check: add summary statistics to JSON report and --stats option
From: Shin'ichiro Kawasaki @ 2026-05-22  7:43 UTC (permalink / raw)
  To: linux-block; +Cc: Ted Ts'o, dennis.maisenbacher, Shin'ichiro Kawasaki
In-Reply-To: <20260522074345.227515-1-shinichiro.kawasaki@wdc.com>

Add summary statistics (total, passed, failed, not run, time) to JSON
output. The JSON now includes a "summary" object with aggregate
statistics.

Add --stats (-s) option to print summary statistics to stdout when
--json option is not specified. This provides a quick way to see test
results summary without generating a full JSON file.

Add STATS_FILE for storing summary statistics and helper functions such
as _write_stats, _increment_stat, and _add_runtime etc, to access the
stats file.

Signed-off-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
---
 check | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 122 insertions(+), 2 deletions(-)

diff --git a/check b/check
index 6ddb064..8dd1b69 100755
--- a/check
+++ b/check
@@ -518,6 +518,7 @@ _call_test() {
 	_write_test_run
 	_output_test_run
 	_write_json_test_run
+	_update_stat
 
 	if [[ ${TEST_RUN["status"]} = fail ]]; then
 		case "${TEST_RUN["reason"]}" in
@@ -1056,6 +1057,95 @@ declare JSON_OUTPUT_FILE
 declare JSON_OUTPUT_TEMP
 # plain text file with single number for JSON array comma placement
 declare COUNTER_FILE
+# plain text file with 5 numbers (total, passed, failed, not_run, time)
+declare STATS_FILE
+
+_init_stats() {
+	[[ -z $STATS && -z $JSON_OUTPUT ]] && return
+
+	STATS_FILE=$(mktemp -p "${TMPDIR:-/tmp}" blktests_stats_XXXXXX)
+	# Initialize summary statistics to 0
+	_write_stats 0 0 0 0 0
+}
+
+_finalize_stats() {
+	 [[ -z $STATS ]] && return
+
+	local -a stats
+	mapfile -t stats < "$STATS_FILE"
+
+	echo "total: ${stats[0]}"
+	echo "passed: ${stats[1]}"
+	echo "failed: ${stats[2]}"
+	echo "not run: ${stats[3]}"
+	echo "time: ${stats[4]}s"
+
+	rm -f "$STATS_FILE"
+}
+
+# write 5 numbers (total, passed, failed, not_run, time)
+_write_stats() {
+	local e
+
+	for e in "${@}"; do
+		echo "${e/ /}"
+	done > "$STATS_FILE"
+}
+
+_increment_stat() {
+	local field=$1
+	local -a stats
+	mapfile -t stats < "$STATS_FILE"
+
+	case "$field" in
+	total)
+		((stats[0]++))
+		;;
+	passed)
+		((stats[1]++))
+		;;
+	failed)
+		((stats[2]++))
+		;;
+	notrun)
+		((stats[3]++))
+		;;
+	esac
+
+	_write_stats "${stats[@]}"
+}
+
+_add_runtime() {
+	local runtime=$1
+	local -a stats
+
+	mapfile -t stats < "$STATS_FILE"
+	stats[4]=$(awk "BEGIN {print ${stats[4]} + ${runtime%s}}")
+	_write_stats "${stats[@]}"
+}
+
+# Update summary statistics in file if stats tracking is enabled
+_update_stat() {
+	[[ -z $STATS_FILE ]] && return
+
+	_increment_stat total
+	case ${TEST_RUN["status"]} in
+	"pass")
+		_increment_stat passed
+		;;
+	"fail")
+		_increment_stat failed
+		;;
+	"not run")
+		_increment_stat notrun
+		;;
+	esac
+
+	# Add runtime to total
+	if [[ -n ${TEST_RUN["runtime"]} ]]; then
+		_add_runtime "${TEST_RUN["runtime"]}"
+	fi
+}
 
 _init_json_output() {
 	[[ -z $JSON_OUTPUT ]] && return
@@ -1075,7 +1165,28 @@ _finalize_json_output() {
 	[[ -z $JSON_OUTPUT ]] && return
 
 	# Close the results array
-	echo '  ]' >> "$JSON_OUTPUT_TEMP"
+	echo '  ],' >> "$JSON_OUTPUT_TEMP"
+
+	# Read summary statistics from file
+	local -a stats
+	mapfile -t stats < "$STATS_FILE"
+	local json_total="${stats[0]}"
+	local json_passed="${stats[1]}"
+	local json_failed="${stats[2]}"
+	local json_notrun="${stats[3]}"
+	local json_total_time="${stats[4]}"
+
+	# Write summary object
+	local summary="  \"summary\": {
+	    \"total\": $json_total,
+	    \"passed\": $json_passed,
+	    \"failed\": $json_failed,
+	    \"not run\": $json_notrun,
+	    \"time\": \"${json_total_time}s\"
+	  }"
+
+	echo "$summary" >> "$JSON_OUTPUT_TEMP"
+
 	# Close the JSON object
 	echo '}' >> "$JSON_OUTPUT_TEMP"
 
@@ -1187,6 +1298,7 @@ _check() {
 	SRCDIR="$(realpath src)"
 
 	_init_json_output
+	_init_stats
 
 	local test_dev test_name
 	local -a all_test_devs_in_array
@@ -1253,6 +1365,7 @@ _check() {
 	fi
 
 	_finalize_json_output
+	_finalize_stats
 
 	return $ret
 }
@@ -1305,6 +1418,8 @@ Test runs:
 
   -j, --json=FILE	 write test results in JSON format to the given file
 
+  -s, --stats		 print summary statistics to stdout
+
 Miscellaneous:
   -c, --config=FILE	 load configuration from FILE instead of the default
 			 (\"./config\"); if this option is used multiple times,
@@ -1326,7 +1441,7 @@ Miscellaneous:
 
 _check_dependencies
 
-if ! TEMP=$(getopt -o 'do:q::x:c:j:h' --long 'device-only,quick::,exclude:,output:,config:,json:,help' -n "$0" -- "$@"); then
+if ! TEMP=$(getopt -o 'do:q::x:c:j:sh' --long 'device-only,quick::,exclude:,output:,config:,json:,stats,help' -n "$0" -- "$@"); then
 	exit 1
 fi
 
@@ -1359,6 +1474,10 @@ while true; do
 			OPTION_JSON="$2"
 			shift 2
 			;;
+		'-s'|'--stats')
+			OPTION_STATS=1
+			shift
+			;;
 		'-c'|'--config')
 			# shellcheck source=/dev/null
 			. "$2"
@@ -1392,6 +1511,7 @@ DEVICE_ONLY="${OPTION_DEVICE_ONLY:-${DEVICE_ONLY:-0}}"
 OUTPUT="${OPTION_OUTPUT:-${OUTPUT:-results}}"
 
 JSON_OUTPUT="${OPTION_JSON:-${JSON_OUTPUT:-}}"
+STATS="${OPTION_STATS:-${STATS:-}}"
 
 QUICK_RUN="${OPTION_QUICK_RUN:-${QUICK_RUN:-0}}"
 if [[ -n $OPTION_QUICK_RUN && $OPTION_QUICK_RUN -ne 0 ]]; then
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH] blk-mq: always take a queue reference in blk_mq_submit_bio
From: Christoph Hellwig @ 2026-05-22  8:56 UTC (permalink / raw)
  To: Jens Axboe; +Cc: Christoph Hellwig, kbusch, linux-block
In-Reply-To: <89c94510-abb6-485c-87c4-73364c8f6914@kernel.dk>

On Thu, May 21, 2026 at 09:12:05AM -0600, Jens Axboe wrote:
> This is about a 5% regression in peak performance, fwiw. I'm all for
> eliminating this issue, but this kind of big hammer approach is pretty
> crap in terms of adding overhead to the IO path. Maybe we just cover
> blocking split bio case, and look into doing a proper fix for 7.2 that
> isn't such a massive regression?

Well, that assumes we have an idea how to fix it?  So much overhead for a
simple per-cpu counters sounds really weird to start with.  We'd need to
look at the numbers from your setup as I don't have anything equivalent.
 
I'd honestly still prefer to get this right first over optimizations
either way.

^ permalink raw reply

* Re: [PATCHv3] blk-mq: pop cached request if it is usable
From: Christoph Hellwig @ 2026-05-22  9:03 UTC (permalink / raw)
  To: Keith Busch; +Cc: axboe, hch, linux-block, tom.leiming, Keith Busch
In-Reply-To: <20260521190253.242065-1-kbusch@meta.com>

> +static struct request *blk_mq_get_cached_request(struct blk_plug *plug,
>  		struct request_queue *q, blk_opf_t opf)
>  {
>  	enum hctx_type type = blk_mq_get_hctx_type(opf);
> @@ -3093,27 +3093,10 @@ static struct request *blk_mq_peek_cached_request(struct blk_plug *plug,
>  		return NULL;
>  	if (op_is_flush(rq->cmd_flags) != op_is_flush(opf))
>  		return NULL;
> +	rq_list_pop(&plug->cached_rqs);
>  	return rq;
>  }

Please add a comment about not sleeping between the peek and pop as
in my earlier patch here.

> @@ -3257,12 +3243,10 @@ void blk_mq_submit_bio(struct bio *bio)
>  	return;
>  
>  queue_exit:
> -	/*
> -	 * Don't drop the queue reference if we were trying to use a cached
> -	 * request and thus didn't acquire one.
> -	 */
>  	if (!rq)
>  		blk_queue_exit(q);
> +	else
> +		blk_mq_free_request(rq);
>  }

I think keeping a comment here would be nice, a would be avoid
the inversion of the condition for a trivial if/else.

But on a higher level, I really think you should add it back to the
batch list here, otherwise we're doing lots of roundtrips through
blk_mq_free_request for trivially mergable sequential I/O.


^ permalink raw reply

* [PATCH v2] blk-throttle: schedule parent dispatch in tg_flush_bios()
From: Tao Cui @ 2026-05-22  9:15 UTC (permalink / raw)
  To: tj, josef, axboe, cgroups; +Cc: linux-block, Tao Cui, Shin'ichiro Kawasaki

tg_flush_bios() schedules pending_timer on the child tg's own
service_queue, which causes throtl_pending_timer_fn() to dispatch from
the child's pending_tree.  For leaf cgroups this tree is empty, so the
timer fires and exits without dispatching the throttled bio.

The throttled bio sits in the parent's pending_tree with disptime set
to jiffies (THROTL_TG_CANCELING zeroes all dispatch times), but the
parent's timer is never explicitly rescheduled.  The bio only gets
dispatched when the parent timer eventually fires at its previously
scheduled expiry.

Fix by calling throtl_schedule_next_dispatch(sq->parent_sq, true)
instead, matching what tg_set_limit() already does.  This forces the
parent's dispatch cycle to run immediately and flush all canceling
bios without waiting for a stale timer.

For the device deletion path (blk_throtl_cancel_bios), directly
complete throttled bios with EIO via bio_io_error() instead of
dispatching them through the timer -> work -> submission chain.
This avoids a race with the SCSI state machine where bios can reach
the SCSI layer while the device is in SDEV_CANCEL state, causing
ENODEV instead of the expected EIO.

Reported-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Link: https://lore.kernel.org/all/ag2owaQQoigp_fSV@shinmob/
Signed-off-by: Tao Cui <cuitao@kylinos.cn>

---
Changes in v2:
- Rewrite blk_throtl_cancel_bios() to directly complete throttled
  bios with EIO via bio_io_error() instead of dispatching them
  through the timer -> work -> submission chain.  This avoids a
  race with the SCSI state machine during device deletion where
  bios can reach the SCSI layer while the device is in SDEV_CANCEL
  state, causing ENODEV instead of the expected EIO.
- Add Reported-by and Link tags from Shin'ichiro Kawasaki's report.

v1:
  https://lore.kernel.org/all/20260520062420.1762788-1-cuitao@kylinos.cn/
---
 block/blk-throttle.c | 51 ++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 49 insertions(+), 2 deletions(-)

diff --git a/block/blk-throttle.c b/block/blk-throttle.c
index cabf91f0d0dc..88986dde1e18 100644
--- a/block/blk-throttle.c
+++ b/block/blk-throttle.c
@@ -1649,7 +1649,7 @@ static void tg_flush_bios(struct throtl_grp *tg)
 	 */
 	tg_update_disptime(tg);
 
-	throtl_schedule_pending_timer(sq, jiffies + 1);
+	throtl_schedule_next_dispatch(sq->parent_sq, true);
 }
 
 static void throtl_pd_offline(struct blkg_policy_data *pd)
@@ -1668,11 +1668,52 @@ struct blkcg_policy blkcg_policy_throtl = {
 	.pd_free_fn		= throtl_pd_free,
 };
 
+static void tg_cancel_writeback_bios(struct throtl_grp *tg,
+				      struct bio_list *cancel_bios)
+{
+	struct throtl_service_queue *sq = &tg->service_queue;
+	struct throtl_data *td = sq_to_td(sq);
+	int rw;
+
+	if (tg->flags & THROTL_TG_CANCELING)
+		return;
+	tg->flags |= THROTL_TG_CANCELING;
+
+	for (rw = READ; rw <= WRITE; rw++) {
+		struct throtl_qnode *qn, *tmp;
+		unsigned int nr_bios = 0;
+
+		list_for_each_entry_safe(qn, tmp, &sq->queued[rw], node) {
+			struct bio *bio;
+
+			while ((bio = bio_list_pop(&qn->bios_iops))) {
+				sq->nr_queued_iops[rw]--;
+				bio_list_add(&cancel_bios[rw], bio);
+				nr_bios++;
+			}
+			while ((bio = bio_list_pop(&qn->bios_bps))) {
+				sq->nr_queued_bps[rw]--;
+				bio_list_add(&cancel_bios[rw], bio);
+				nr_bios++;
+			}
+
+			list_del_init(&qn->node);
+			blkg_put(tg_to_blkg(qn->tg));
+		}
+
+		td->nr_queued[rw] -= nr_bios;
+	}
+
+	throtl_dequeue_tg(tg);
+}
+
 void blk_throtl_cancel_bios(struct gendisk *disk)
 {
 	struct request_queue *q = disk->queue;
 	struct cgroup_subsys_state *pos_css;
 	struct blkcg_gq *blkg;
+	struct bio_list cancel_bios[2] = { };
+	int rw;
 
 	if (!blk_throtl_activated(q))
 		return;
@@ -1693,10 +1734,16 @@ void blk_throtl_cancel_bios(struct gendisk *disk)
 		 * Cancel bios here to ensure no bios are inflight after
 		 * del_gendisk.
 		 */
-		tg_flush_bios(blkg_to_tg(blkg));
+		tg_cancel_writeback_bios(blkg_to_tg(blkg), cancel_bios);
 	}
 	rcu_read_unlock();
 	spin_unlock_irq(&q->queue_lock);
+
+	for (rw = READ; rw <= WRITE; rw++) {
+		struct bio *bio;
+		while ((bio = bio_list_pop(&cancel_bios[rw])))
+			bio_io_error(bio);
+	}
 }
 
 static bool tg_within_limit(struct throtl_grp *tg, struct bio *bio, bool rw)
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH v7 32/43] btrfs: implement process_bio cb for fscrypt
From: Christoph Hellwig @ 2026-05-22  9:19 UTC (permalink / raw)
  To: Daniel Vacek
  Cc: Chris Mason, Josef Bacik, Eric Biggers, Theodore Y. Ts'o,
	Jaegeuk Kim, Jens Axboe, David Sterba, linux-block, linux-fscrypt,
	linux-btrfs, linux-kernel
In-Reply-To: <20260513085340.3673127-33-neelx@suse.com>

On Wed, May 13, 2026 at 10:53:06AM +0200, Daniel Vacek wrote:
> From: Josef Bacik <josef@toxicpanda.com>
> 
> We are going to be checksumming the encrypted data, so we have to
> implement the ->process_bio fscrypt callback.  This will provide us with
> the original bio and the encrypted bio to do work on.  For WRITE's this
> will happen after the encrypted bio has been encrypted.  For READ's this
> will happen after the read has completed and before the decryption step
> is done.
> 
> For write's this is straightforward, we can just pass in the encrypted
> bio to btrfs_csum_one_bio and then the csums will be added to the bbio
> as normal.
> 
> For read's this is relatively straightforward, but requires some care.
> We assume (because that's how it works currently) that the encrypted bio
> match the original bio, this is important because we save the iter of
> the bio before we submit.  If this changes in the future we'll need a
> hook to give us the bi_iter of the decryption bio before it's submitted.
> We check the csums before decryption.  If it doesn't match we simply
> error out and we let the normal path handle the repair work.
> 
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> Signed-off-by: Daniel Vacek <neelx@suse.com>
> ---
> 
> v7 changes:
>  * Fixed array overflow stack corruption for bios > max blocksize (>64KiB)
>    as reported by Chris' AI review.
> v6 changes:
>  * Adapt to btrfs_data_csum_ok() changes for bs > ps.  Mostly follow
>    what was done in 052fd7a5cace ("btrfs: make read verification
>    handle bs > ps cases without large folios").
>  * Rename bbio::csum_done to csum_ok due to name collision.
>    With upstream, member name csum_done was used for async csums.
> v5: https://lore.kernel.org/linux-btrfs/ca32684b01ff8c252be515509137e0a4a0e5db7a.1706116485.git.josef@toxicpanda.com/
> ---
>  fs/btrfs/bio.c       | 44 +++++++++++++++++++++++++++++++++++++++++++-
>  fs/btrfs/bio.h       |  3 +++
>  fs/btrfs/file-item.c | 14 ++++++++++++--
>  fs/btrfs/fscrypt.c   | 29 +++++++++++++++++++++++++++++
>  4 files changed, 87 insertions(+), 3 deletions(-)
> 
> diff --git a/fs/btrfs/bio.c b/fs/btrfs/bio.c
> index 3e2ee19aab50..729c5aff5c3d 100644
> --- a/fs/btrfs/bio.c
> +++ b/fs/btrfs/bio.c
> @@ -301,6 +301,40 @@ static struct btrfs_failed_bio *repair_one_sector(struct btrfs_bio *failed_bbio,
>  	return fbio;
>  }
>  
> +blk_status_t btrfs_check_encrypted_read_bio(struct btrfs_bio *bbio, struct bio *enc_bio)
> +{
> +	struct btrfs_inode *inode = bbio->inode;
> +	struct btrfs_fs_info *fs_info = inode->root->fs_info;
> +	struct bvec_iter iter = bbio->saved_iter;
> +	struct btrfs_device *dev = bbio->bio.bi_private;
> +	const u32 blocksize = fs_info->sectorsize;
> +	const u32 step = min(blocksize, PAGE_SIZE);
> +	const u32 nr_steps = iter.bi_size / step;
> +	phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
> +	phys_addr_t paddr;
> +	unsigned int slot = 0;
> +	u32 offset = 0;
> +
> +	/*
> +	 * We have to use a copy of iter in case there's an error,
> +	 * btrfs_check_read_bio will handle submitting the repair bios.
> +	 */
> +	btrfs_bio_for_each_block(paddr, enc_bio, &iter, step) {
> +		ASSERT(slot < nr_steps);
> +		paddrs[slot] = paddr;
> +		slot++;
> +		offset += step;
> +		if (IS_ALIGNED(offset, blocksize)) {
> +			if (!btrfs_data_csum_ok(bbio, dev, offset - blocksize, paddrs))
> +				return BLK_STS_IOERR;
> +			slot = 0;
> +		}
> +	}
> +
> +	bbio->csum_ok = true;
> +	return BLK_STS_OK;
> +}
> +
>  static void btrfs_check_read_bio(struct btrfs_bio *bbio, struct btrfs_device *dev)
>  {
>  	struct btrfs_inode *inode = bbio->inode;
> @@ -330,6 +364,10 @@ static void btrfs_check_read_bio(struct btrfs_bio *bbio, struct btrfs_device *de
>  	/* Clear the I/O error. A failed repair will reset it. */
>  	bbio->bio.bi_status = BLK_STS_OK;
>  
> +	/* This was an encrypted bio and we've already done the csum check. */
> +	if (status == BLK_STS_OK && bbio->csum_ok)
> +		goto out;
> +
>  	btrfs_bio_for_each_block(paddr, &bbio->bio, iter, step) {
>  		paddrs[(offset / step) % nr_steps] = paddr;
>  		offset += step;
> @@ -341,6 +379,7 @@ static void btrfs_check_read_bio(struct btrfs_bio *bbio, struct btrfs_device *de
>  							 paddrs, fbio);
>  		}
>  	}
> +out:
>  	if (bbio->csum != bbio->csum_inline)
>  		kvfree(bbio->csum);
>  
> @@ -859,10 +898,13 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio, int mirror_num)
>  		/*
>  		 * Csum items for reloc roots have already been cloned at this
>  		 * point, so they are handled as part of the no-checksum case.
> +		 *
> +		 * Encrypted inodes are csum'ed via the ->process_bio callback.
>  		 */
>  		if (!(inode->flags & BTRFS_INODE_NODATASUM) &&
>  		    !test_bit(BTRFS_FS_STATE_NO_DATA_CSUMS, &fs_info->fs_state) &&
> -		    !btrfs_is_data_reloc_root(inode->root) && !bbio->is_remap) {
> +		    !btrfs_is_data_reloc_root(inode->root) && !bbio->is_remap &&
> +		    !IS_ENCRYPTED(&inode->vfs_inode)) {
>  			if (should_async_write(bbio) &&
>  			    btrfs_wq_submit_bio(bbio, bioc, &smap, mirror_num))
>  				goto done;
> diff --git a/fs/btrfs/bio.h b/fs/btrfs/bio.h
> index 43f7544029ac..456d32db9e9e 100644
> --- a/fs/btrfs/bio.h
> +++ b/fs/btrfs/bio.h
> @@ -43,6 +43,7 @@ struct btrfs_bio {
>  		struct {
>  			u8 *csum;
>  			u8 csum_inline[BTRFS_BIO_INLINE_CSUM_SIZE];
> +			bool csum_ok;
>  			struct bvec_iter saved_iter;
>  		};
>  
> @@ -130,5 +131,7 @@ void btrfs_submit_repair_write(struct btrfs_bio *bbio, int mirror_num, bool dev_
>  int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 fileoff,
>  			    u32 length, u64 logical, const phys_addr_t paddrs[],
>  			    unsigned int step, int mirror_num);
> +blk_status_t btrfs_check_encrypted_read_bio(struct btrfs_bio *bbio,
> +					    struct bio *enc_bio);
>  
>  #endif
> diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
> index 986914078708..72d9d3243460 100644
> --- a/fs/btrfs/file-item.c
> +++ b/fs/btrfs/file-item.c
> @@ -338,6 +338,14 @@ static int search_csum_tree(struct btrfs_fs_info *fs_info,
>  	return ret;
>  }
>  
> +static inline bool inode_skip_csum(struct btrfs_inode *inode)
> +{
> +	struct btrfs_fs_info *fs_info = inode->root->fs_info;
> +
> +	return (inode->flags & BTRFS_INODE_NODATASUM) ||
> +		test_bit(BTRFS_FS_STATE_NO_DATA_CSUMS, &fs_info->fs_state);
> +}
> +
>  /*
>   * Lookup the checksum for the read bio in csum tree.
>   *
> @@ -357,8 +365,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio)
>  	int ret = 0;
>  	u32 bio_offset = 0;
>  
> -	if ((inode->flags & BTRFS_INODE_NODATASUM) ||
> -	    test_bit(BTRFS_FS_STATE_NO_DATA_CSUMS, &fs_info->fs_state))
> +	if (inode_skip_csum(inode))
>  		return 0;
>  
>  	/*
> @@ -817,6 +824,9 @@ int btrfs_csum_one_bio(struct btrfs_bio *bbio, struct bio *bio, bool async)
>  	struct btrfs_ordered_sum *sums;
>  	unsigned nofs_flag;
>  
> +	if (inode_skip_csum(inode))
> +		return 0;
> +
>  	nofs_flag = memalloc_nofs_save();
>  	sums = kvzalloc(btrfs_ordered_sum_size(fs_info, bio->bi_iter.bi_size),
>  		       GFP_KERNEL);
> diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
> index 5d34a8b94da5..924ee3df7f32 100644
> --- a/fs/btrfs/fscrypt.c
> +++ b/fs/btrfs/fscrypt.c
> @@ -16,6 +16,7 @@
>  #include "transaction.h"
>  #include "volumes.h"
>  #include "xattr.h"
> +#include "file-item.h"
>  
>  /*
>   * From a given location in a leaf, read a name into a qstr (usually a
> @@ -212,6 +213,33 @@ static struct block_device **btrfs_fscrypt_get_devices(struct super_block *sb,
>  	return devs;
>  }
>  
> +static blk_status_t btrfs_process_encrypted_bio(struct bio *orig_bio,
> +						struct bio *enc_bio)
> +{
> +	struct btrfs_bio *bbio;
> +
> +	/*
> +	 * If our bio is from the normal fs_bio_set then we know this is a
> +	 * mirror split and we can skip it, we'll get the real bio on the last
> +	 * mirror and we can process that one.
> +	 */
> +	if (orig_bio->bi_pool == &fs_bio_set)
> +		return BLK_STS_OK;
> +
> +	bbio = btrfs_bio(orig_bio);
> +
> +	if (bio_op(orig_bio) == REQ_OP_READ) {
> +		/*
> +		 * We have ->saved_iter based on the orig_bio, so if the block
> +		 * layer changes we need to notice this asap so we can update
> +		 * our code to handle the new world order.
> +		 */
> +		ASSERT(orig_bio == enc_bio);
> +		return btrfs_check_encrypted_read_bio(bbio, enc_bio);
> +	}
> +	return btrfs_csum_one_bio(bbio, enc_bio, false);

Honestly, all this shows that the architecture of the I/O path in this
series is pretty broken.  It needs all this magic detection, and the
passing of arguments that mixes the bbio for state and the lower
encrypted bio without the btrfs context shows something doesn't work
well.

So let's take a step back, if we think of the I/O pipeline, it should do
things in this order for writes:

 - encrypt data
 - generate checksums
 - do mirroring/striping/parity

and reverse for reads.

All this suggest that the btrfs_bio needs to exist for the encrypted
data.  So I think you'll need to and refactor this, preferably with the
really annoying two-level callbacks that this really hard to follow (or
implement).  Your caller is in the file system, and it should be able to
call fscrypt as helpers instead of going two layers down using direct
calls and then two layers back up using indirect calls.  The recent
refactoring that moves the fscrypt fallback above the block layer
instead of calling it from the bottom should help a lot with that.


^ permalink raw reply

* Re: [PATCH v7 17/43] btrfs: add get_devices hook for fscrypt
From: Christoph Hellwig @ 2026-05-22  9:19 UTC (permalink / raw)
  To: Daniel Vacek
  Cc: Chris Mason, Josef Bacik, Eric Biggers, Theodore Y. Ts'o,
	Jaegeuk Kim, Jens Axboe, David Sterba, linux-block, linux-fscrypt,
	linux-btrfs, linux-kernel, Sweet Tea Dorminy
In-Reply-To: <20260513085340.3673127-18-neelx@suse.com>

On Wed, May 13, 2026 at 10:52:51AM +0200, Daniel Vacek wrote:
> From: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> 
> Since extent encryption requires inline encryption, even though we
> expect to use the inlinecrypt software fallback most of the time, we
> need to enumerate all the devices in use by btrfs.

How does this handled adding/removing devices at runtime?


^ permalink raw reply

* Re: [PATCH] loop: add sync_blockdev() in __loop_clr_fd() to prevent UAF
From: Ming Lei @ 2026-05-22  9:33 UTC (permalink / raw)
  To: Tetsuo Handa
  Cc: Jens Axboe, linux-block, syzbot+cd8a9a308e879a4e2c28,
	syzbot+bc273027d5643e48e5b3, Hongling Zeng, Bart Van Assche,
	Andrew Morton
In-Reply-To: <7ef76131-d077-4cfc-80ab-69720a214a5c@I-love.SAKURA.ne.jp>

On Fri, May 22, 2026 at 03:54:54PM +0900, Tetsuo Handa wrote:
> On 2026/05/22 12:45, Ming Lei wrote:
> > On Fri, May 22, 2026 at 12:28:34PM +0900, Tetsuo Handa wrote:
> >> On 2026/05/22 11:54, Ming Lei wrote:
> >>> Fix this by adding sync_blockdev() in __loop_clr_fd() to flush all
> >>> pending writeback I/O before clearing lo->lo_backing_file. Since the
> >>> loop disk is already closed at this point, no new I/O can be submitted
> >>> — only writeback remains.
> >>
> >> Why can you assume that synchronize_rcu() + drain_workqueue(lo->workqueue)
> >> is unnecessary? Since we don't know exact commit which is causing this
> >> problem, we don't know what has changed.
> > 
> > When sync_blockdev() returns, there can't be any inflight IO:
> > 
> > - no one can open this loop disk
> > - no dirty page cache any more
> > 
> > So why do you want to add rcu/drain_workqueue?
> 
> The fact that __loop_clr_fd() is called before lo_rw_aio() completes suggests that
> some works queued before __loop_clr_fd() is called have not been completed.
> 
> I don't know what sync_blockdev() guarantees, but even if sync_blockdev() can guarantee
> both "loop_queue_rq() is no longer running" and "all pending AIO requests issued by
> lo_rw_aio() have been completed", I think that sync_blockdev() cannot determine whether
> lo->lo_device has pending work or not.

Please do take a look at the commit log of this one and commit 1fe0b1acb14d

> 
> Therefore, I guess that at least drain_workqueue() is needed before sync_blockdev().
> If sync_blockdev() cannot guarantee "loop_queue_rq() is no longer running",
> I guess that both synchronize_rcu() + drain_workqueue() are needed before sync_blockdev().

Not at all.

What matters is that resources cleared in __loop_clr_fd() are not used any
more, and the resource is only accessed when handling io command from both
submission and completion path.

Again:

When the loop disk is closed, no foreground IO is possible, meantime
no one can open it because of ->open_mutex. So the only remained IO are
writeback, sync_blockdev() writes out all dirty pages and waits until
all are done, then there can't be any new IO, and it is safe to clear
these resources.

work function may still be run, but command list includes nothing, so
resources to be cleared won't be touched.

> 
> (And I don't know whether sync_blockdev() is appropriate because we don't know what change
> made this problem visible. We didn't hit this problem until Linux 7.0.)
> 
> > 
> > Fixes: 1fe0b1acb14d ("loop: only freeze the queue in __loop_clr_fd when needed")
> 
> The Fixes: for these reports should be the commit which made this problem visible.
> Or, this patch will be needlessly backported to stable kernels.

Will add it in V2.


Thanks,
Ming

^ permalink raw reply

* Re: [PATCH] loop: add sync_blockdev() in __loop_clr_fd() to prevent UAF
From: Tetsuo Handa @ 2026-05-22  9:54 UTC (permalink / raw)
  To: Ming Lei
  Cc: Jens Axboe, linux-block, syzbot+cd8a9a308e879a4e2c28,
	syzbot+bc273027d5643e48e5b3, Hongling Zeng, Bart Van Assche,
	Andrew Morton
In-Reply-To: <ahAjBvh8IfPCreRG@fedora>

On 2026/05/22 18:33, Ming Lei wrote:
> Please do take a look at the commit log of this one and commit 1fe0b1acb14d

My question is what commit broke "->release is only called after all outstanding
I/O has completed" in the patch description of commit 1fe0b1acb14d ("loop: only
freeze the queue in __loop_clr_fd when needed").

>>> Fixes: 1fe0b1acb14d ("loop: only freeze the queue in __loop_clr_fd when needed")
>>
>> The Fixes: for these reports should be the commit which made this problem visible.
>> Or, this patch will be needlessly backported to stable kernels.
> 
> Will add it in V2.

Commit 1fe0b1acb14d relies on commits before 1fe0b1acb14d. An not-yet-identified commit
which went to the merge window for 7.1 broke what commit 1fe0b1acb14d relies on.
"Fixes: 1fe0b1acb14d" is wrong, and "Fixes: not-yet-identified-commit which went to
the merge window for 7.1" is expected.


^ permalink raw reply

* Re: [PATCH blktests] nbd/rc: redirect nbd-server stderr
From: Shin'ichiro Kawasaki @ 2026-05-22 10:54 UTC (permalink / raw)
  To: linux-block, nbd
In-Reply-To: <20260515053047.64075-1-shinichiro.kawasaki@wdc.com>

On May 15, 2026 / 14:30, Shin'ichiro Kawasaki wrote:
> Recent nbd commit da5e07c057ab ("Reimplement daemonize() without using
> daemon()") changed how nbd-server daemonizes itself. After this commit,
> nbd-server prints messages to stderr after daemonization. This caused
> nbd test cases to fail due to unexpected stderr output.
[...]
> To avoid the failures, redirect nbd-server stderr to .full files.
> 
> Link: https://github.com/NetworkBlockDevice/nbd/commit/da5e07c057ab
> Signed-off-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>

FYI, I applied the patch.

^ permalink raw reply

* [PATCH] block: propagate in_flight to whole disk on partition I/O
From: Tang Yizhou @ 2026-05-22 11:37 UTC (permalink / raw)
  To: axboe, hch; +Cc: yukuai, linux-block, linux-kernel, Tang Yizhou, Leon Hwang

From: Tang Yizhou <yizhou.tang@shopee.com>

Now when I/O is submitted to a partition, the per-CPU in_flight[]
counter is incremented only on the partition's block_device, not on the
underlying whole disk. This leads to a problem which can be shown by a
fio test:

lsblk
  NAME     MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
  mydev    252:1    0   20G  0 disk
  └─mydev1 259:0    0   10G  0 part

iostat -xp 1
  Device       r/s        rkB/s      ... aqu-sz   %util
  mydev    128153.00  512612.00      ...  13.22   72.20
  mydev1   128154.00  512616.00      ...  13.22  100.00

%util is different between mydev and mydev1, which is unexpected.

This is the cumulative effect of a series of patches.  The key step is
commit 10ec5e86f9b8 ("block: merge part_{inc,dev}_in_flight into their
only callers"), which folded the whole-disk in_flight accounting into
generic_start_io_acct() and generic_end_io_acct().  Those two helpers
were then removed by commit e722fff238bb ("block: remove
generic_{start,end}_io_acct"), and from that point on the whole disk's
in_flight is no longer accounted at all.

Fix it by restoring the whole-disk in_flight accounting.

Fixes: e722fff238bb ("block: remove generic_{start,end}_io_acct")

Suggested-by: Leon Hwang <leon.huangfu@shopee.com>
Signed-off-by: Tang Yizhou <yizhou.tang@shopee.com>
---
 block/blk-core.c | 4 ++++
 block/blk-mq.c   | 6 ++++++
 2 files changed, 10 insertions(+)

diff --git a/block/blk-core.c b/block/blk-core.c
index 17450058ea6d..03f4b7015e69 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -1043,6 +1043,8 @@ unsigned long bdev_start_io_acct(struct block_device *bdev, enum req_op op,
 	part_stat_lock();
 	update_io_ticks(bdev, start_time, false);
 	part_stat_local_inc(bdev, in_flight[op_is_write(op)]);
+	if (bdev_is_partition(bdev))
+		part_stat_local_inc(bdev_whole(bdev), in_flight[op_is_write(op)]);
 	part_stat_unlock();
 
 	return start_time;
@@ -1074,6 +1076,8 @@ void bdev_end_io_acct(struct block_device *bdev, enum req_op op,
 	part_stat_add(bdev, sectors[sgrp], sectors);
 	part_stat_add(bdev, nsecs[sgrp], jiffies_to_nsecs(duration));
 	part_stat_local_dec(bdev, in_flight[op_is_write(op)]);
+	if (bdev_is_partition(bdev))
+		part_stat_local_dec(bdev_whole(bdev), in_flight[op_is_write(op)]);
 	part_stat_unlock();
 }
 EXPORT_SYMBOL(bdev_end_io_acct);
diff --git a/block/blk-mq.c b/block/blk-mq.c
index d0c37daf568f..60ead16f1496 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -1084,6 +1084,9 @@ static inline void blk_account_io_done(struct request *req, u64 now)
 		part_stat_add(req->part, nsecs[sgrp], now - req->start_time_ns);
 		part_stat_local_dec(req->part,
 				    in_flight[op_is_write(req_op(req))]);
+		if (bdev_is_partition(req->part))
+			part_stat_local_dec(bdev_whole(req->part),
+					    in_flight[op_is_write(req_op(req))]);
 		part_stat_unlock();
 	}
 }
@@ -1144,6 +1147,9 @@ static inline void blk_account_io_start(struct request *req)
 	part_stat_lock();
 	update_io_ticks(req->part, jiffies, false);
 	part_stat_local_inc(req->part, in_flight[op_is_write(req_op(req))]);
+	if (bdev_is_partition(req->part))
+		part_stat_local_inc(bdev_whole(req->part),
+				    in_flight[op_is_write(req_op(req))]);
 	part_stat_unlock();
 }
 
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH] loop: add sync_blockdev() in __loop_clr_fd() to prevent UAF
From: Ming Lei @ 2026-05-22 11:50 UTC (permalink / raw)
  To: Tetsuo Handa
  Cc: Jens Axboe, linux-block, syzbot+cd8a9a308e879a4e2c28,
	syzbot+bc273027d5643e48e5b3, Hongling Zeng, Bart Van Assche,
	Andrew Morton
In-Reply-To: <3001ff83-b48c-47d0-aaf3-81a8d58a0a44@I-love.SAKURA.ne.jp>

On Fri, May 22, 2026 at 06:54:39PM +0900, Tetsuo Handa wrote:
> On 2026/05/22 18:33, Ming Lei wrote:
> > Please do take a look at the commit log of this one and commit 1fe0b1acb14d
> 
> My question is what commit broke "->release is only called after all outstanding
> I/O has completed" in the patch description of commit 1fe0b1acb14d ("loop: only
> freeze the queue in __loop_clr_fd when needed").

oops, 1fe0b1acb14d ("loop: only freeze the queue in __loop_clr_fd when needed") is actually
correct.

The issue must be from somewhere else(writeback?), given blkdev_put_whole() does
run blkdev_flush_mapping() before calling ->release(). If you or anyone
can reproduce it, the following debug patch may provide some hint:

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 0000913f7efc..85f140c7f0a4 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1855,6 +1855,8 @@ static blk_status_t loop_queue_rq(struct blk_mq_hw_ctx *hctx,
        struct loop_cmd *cmd = blk_mq_rq_to_pdu(rq);
        struct loop_device *lo = rq->q->queuedata;
 
+       WARN_ON_ONCE(!lo->lo_backing_file);
+
        blk_mq_start_request(rq);
 
        if (data_race(READ_ONCE(lo->lo_state)) != Lo_bound)


Thanks,
Ming

^ permalink raw reply related

* [PATCH] block: avoid use-after-free in disk_free_zone_resources()
From: Damien Le Moal @ 2026-05-22 11:56 UTC (permalink / raw)
  To: Jens Axboe, linux-block; +Cc: Christoph Hellwig

The function disk_update_zone_resources() may call
disk_free_zone_resources() in case of error, and following this,
blk_revalidate_disk_zones() will again calls disk_free_zone_resources() if
disk_update_zone_resources() failed. If a zone worker thread is being used
(which is the default for a rotational media zoned device),
disk_free_zone_resources() will try to stop the zone worker thread twice
because disk->zone_wplugs_worker is not reset to NULL when the worker
thread is stopped the first time.

In disk_free_zone_resources(), fix this by correctly clearing
disk->zone_wplugs_worker to NULL when the worker thread is stopped.

And while at it, since disk_free_zone_resources() is always called after a
failed call to disk_update_zone_resources(), remove the unnecessary call
to disk_free_zone_resources() in disk_update_zone_resources().

Fixes: 1365b6904fd0 ("block: allow submitting all zone writes from a single context")
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
---
 block/blk-zoned.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index 42ef830054dc..6a221c180889 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -2001,8 +2001,10 @@ static void disk_set_zones_cond_array(struct gendisk *disk, u8 *zones_cond)
 
 void disk_free_zone_resources(struct gendisk *disk)
 {
-	if (disk->zone_wplugs_worker)
+	if (disk->zone_wplugs_worker) {
 		kthread_stop(disk->zone_wplugs_worker);
+		disk->zone_wplugs_worker = NULL;
+	}
 	WARN_ON_ONCE(!list_empty(&disk->zone_wplugs_list));
 
 	if (disk->zone_wplugs_wq) {
@@ -2135,9 +2137,6 @@ static int disk_update_zone_resources(struct gendisk *disk,
 	ret = queue_limits_commit_update(q, &lim);
 
 unfreeze:
-	if (ret)
-		disk_free_zone_resources(disk);
-
 	blk_mq_unfreeze_queue(q, memflags);
 
 	return ret;
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH 00/12] Block storage copy offloading
From: Shin'ichiro Kawasaki @ 2026-05-22 12:00 UTC (permalink / raw)
  To: Bart Van Assche
  Cc: Jens Axboe, linux-block, linux-scsi, linux-nvme,
	Christoph Hellwig, Nitesh Shetty
In-Reply-To: <20260424224201.1949243-1-bvanassche@acm.org>

On Apr 24, 2026 / 15:41, Bart Van Assche wrote:
> Hi Jens,
> 
> This patch series implements a new approach for copy offloading. Compared to
> Nitesh' approach, the differences are as follows:
>  - Two new limits have been introduced representing the maximum number of source
>    and destination ranges. Support for multiple source ranges for the NVMe Copy
>    command has been added.
>  - The blkdev_copy_offload() function can now submit multiple copy offload
>    commands instead of only one.
>  - The implementation no longer depends on block layer plugging.
> 
> This patch series includes copy offloading support for the Linux kernel block
> layer core, the device mapper core, the null_blk and the NVMe and nvmet drivers.
> Support for the scsi_debug and SCSI core will follow later.
> 
> Test scripts are available here:
> https://github.com/bvanassche/blktests/tree/copy-offloading
> 
> See also Bart Van Assche, [LSF/MM/BPF TOPIC] Block storage copy offloading,
> January 2026
> (https://lore.kernel.org/all/0cfe6fe2-3865-4dc2-92a7-74b1240f7b63@acm.org/).
> 
> See also Nitesh Shetty, Implement copy offload support, May 2024
> (https://lore.kernel.org/linux-block/20240520102033.9361-1-nj.shetty@samsung.com/).

FYI, blktests CI trial run detected that this patch series triggers nvme/018
failure. I manually applied this series on top of the v7.1-rc4 kernel and
observed the failure is recreated in stable manner.

nvme/018 (tr=loop) (unit test NVMe-oF out of range access on a file backend) [failed]
    runtime  1.208s  ...  1.189s
    --- tests/nvme/018.out      2025-04-22 13:13:27.738873155 +0900
    +++ /home/shin/Blktests/blktests/results/nodev_tr_loop/nvme/018.out.bad     2026-05-22 20:57:31.060000000 +0900
    @@ -1,3 +1,4 @@
     Running nvme/018
    +ERROR: nvme read for out of range LBA was not rejected
     disconnected 1 controller(s)
     Test complete

^ permalink raw reply

* Re: [PATCH v7 17/43] btrfs: add get_devices hook for fscrypt
From: Daniel Vacek @ 2026-05-22 12:00 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Chris Mason, Josef Bacik, Eric Biggers, Theodore Y. Ts'o,
	Jaegeuk Kim, Jens Axboe, David Sterba, linux-block, linux-fscrypt,
	linux-btrfs, linux-kernel, Sweet Tea Dorminy
In-Reply-To: <ahAfvPa_yl6AKZoW@infradead.org>

On Fri, 22 May 2026 at 11:19, Christoph Hellwig <hch@infradead.org> wrote:
> On Wed, May 13, 2026 at 10:52:51AM +0200, Daniel Vacek wrote:
> > From: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> >
> > Since extent encryption requires inline encryption, even though we
> > expect to use the inlinecrypt software fallback most of the time, we
> > need to enumerate all the devices in use by btrfs.
>
> How does this handled adding/removing devices at runtime?

When called, this callback returns the list of bdevs opened by the
given superblock. If devices are added or removed, this function
returns a different list.
In other words it always returns a valid list.

This is called from `fscrypt_get_devices()`, which is called from
`fscrypt_select_encryption_impl()` or
`fscrypt_prepare_inline_crypt_key()` or
`fscrypt_destroy_inline_crypt_key()`. All these functions walk the
returned list and discard it immediately afterwards.

Note that with btrfs at this point we're only using the inline crypto fallback.
Is there any particular reason you asked this question?

--nX

^ permalink raw reply

* Re: [PATCH] block: avoid use-after-free in disk_free_zone_resources()
From: Christoph Hellwig @ 2026-05-22 12:09 UTC (permalink / raw)
  To: Damien Le Moal; +Cc: Jens Axboe, linux-block, Christoph Hellwig
In-Reply-To: <20260522115622.588535-1-dlemoal@kernel.org>

Looks good:

Reviewed-by: Christoph Hellwig <hch@lst.de>

^ permalink raw reply

* Re: [PATCH] block: propagate in_flight to whole disk on partition I/O
From: Christoph Hellwig @ 2026-05-22 12:12 UTC (permalink / raw)
  To: Tang Yizhou; +Cc: axboe, hch, yukuai, linux-block, linux-kernel, Leon Hwang
In-Reply-To: <20260522113751.171630-1-yizhou.tang@shopee.com>

On Fri, May 22, 2026 at 07:37:51PM +0800, Tang Yizhou wrote:
> --- a/block/blk-core.c
> +++ b/block/blk-core.c
> @@ -1043,6 +1043,8 @@ unsigned long bdev_start_io_acct(struct block_device *bdev, enum req_op op,
>  	part_stat_lock();
>  	update_io_ticks(bdev, start_time, false);
>  	part_stat_local_inc(bdev, in_flight[op_is_write(op)]);
> +	if (bdev_is_partition(bdev))
> +		part_stat_local_inc(bdev_whole(bdev), in_flight[op_is_write(op)]);

overly lone line.

> +	if (bdev_is_partition(bdev))
> +		part_stat_local_dec(bdev_whole(bdev), in_flight[op_is_write(op)]);

Same.

>  }
> @@ -1144,6 +1147,9 @@ static inline void blk_account_io_start(struct request *req)
>  	part_stat_lock();
>  	update_io_ticks(req->part, jiffies, false);
>  	part_stat_local_inc(req->part, in_flight[op_is_write(req_op(req))]);
> +	if (bdev_is_partition(req->part))
> +		part_stat_local_inc(bdev_whole(req->part),
> +				    in_flight[op_is_write(req_op(req))]);

and tis duplicates the above logic.  Mabye factor the common code
into two little helpers?


^ permalink raw reply

* Re: [PATCH v7 17/43] btrfs: add get_devices hook for fscrypt
From: Christoph Hellwig @ 2026-05-22 12:17 UTC (permalink / raw)
  To: Daniel Vacek
  Cc: Christoph Hellwig, Chris Mason, Josef Bacik, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, Jens Axboe, David Sterba,
	linux-block, linux-fscrypt, linux-btrfs, linux-kernel,
	Sweet Tea Dorminy
In-Reply-To: <CAPjX3FeoGzNOBnmY5ie34irNaAOZiFqV_Ccf7dXFrZJeUXh8PA@mail.gmail.com>

On Fri, May 22, 2026 at 02:00:28PM +0200, Daniel Vacek wrote:
> > How does this handled adding/removing devices at runtime?
> 
> When called, this callback returns the list of bdevs opened by the
> given superblock. If devices are added or removed, this function
> returns a different list.
> In other words it always returns a valid list.
> 
> This is called from `fscrypt_get_devices()`, which is called from
> `fscrypt_select_encryption_impl()` or
> `fscrypt_prepare_inline_crypt_key()` or
> `fscrypt_destroy_inline_crypt_key()`. All these functions walk the
> returned list and discard it immediately afterwards.
> 
> Note that with btrfs at this point we're only using the inline crypto fallback.
> Is there any particular reason you asked this question?

Well, assume you have a single device fs, and then you add a device
later, you will not get the blk_crypto_config_supported call for this
device, and it will not be taken into account.

Now can btrfs even support hardware inline encryption?  The way the bio
processing is special cased I somehow doubt it.  But the concept of a
static device list just doesn't work for btrfs, so I think the fscrypt
side of this will need refactoring not to rely on it.  If we never
support hardware inline encryption on such dynamic file systems that
would be relative easy, if we need to support that case things might
get a lot more complicated.


^ permalink raw reply

* Re: [PATCHv3] blk-mq: pop cached request if it is usable
From: Keith Busch @ 2026-05-22 12:23 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: Keith Busch, axboe, linux-block, tom.leiming
In-Reply-To: <20260522090340.GA7331@lst.de>

On Fri, May 22, 2026 at 11:03:40AM +0200, Christoph Hellwig wrote:
> > @@ -3257,12 +3243,10 @@ void blk_mq_submit_bio(struct bio *bio)
> >  	return;
> >  
> >  queue_exit:
> > -	/*
> > -	 * Don't drop the queue reference if we were trying to use a cached
> > -	 * request and thus didn't acquire one.
> > -	 */
> >  	if (!rq)
> >  		blk_queue_exit(q);
> > +	else
> > +		blk_mq_free_request(rq);
> >  }
> 
> I think keeping a comment here would be nice, a would be avoid
> the inversion of the condition for a trivial if/else.
> 
> But on a higher level, I really think you should add it back to the
> batch list here, otherwise we're doing lots of roundtrips through
> blk_mq_free_request for trivially mergable sequential I/O.

That gets compicated when we did sleep. This request may be the last
reference that's holding up a freeze. We really want to re-enter the
queue in that case because the driver is trying to change the queue
limits. Unless this is measurably harming perforamnce, it's a corner
case that's just easier to not think about.

^ permalink raw reply

* [PATCH v2] block: propagate in_flight to whole disk on partition I/O
From: Tang Yizhou @ 2026-05-22 12:34 UTC (permalink / raw)
  To: axboe, hch; +Cc: yukuai, linux-block, linux-kernel, Tang Yizhou, Leon Hwang

From: Tang Yizhou <yizhou.tang@shopee.com>

Now when I/O is submitted to a partition, the per-CPU in_flight[]
counter is incremented only on the partition's block_device, not on the
underlying whole disk. This leads to a problem which can be shown by a
fio test:

lsblk
  NAME     MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
  mydev    252:1    0   20G  0 disk
  └─mydev1 259:0    0   10G  0 part

iostat -xp 1
  Device       r/s        rkB/s      ... aqu-sz   %util
  mydev    128153.00  512612.00      ...  13.22   72.20
  mydev1   128154.00  512616.00      ...  13.22  100.00

%util is different between mydev and mydev1, which is unexpected.

This is the cumulative effect of a series of patches. The root cause is
commit e016b78201a2 ("block: return just one value from part_in_flight"),
which deleted the branch in part_in_flight() that aggregated the whole-disk
in_flight count on top of the partition's. Then the second commit is
commit 10ec5e86f9b8 ("block: merge part_{inc,dev}_in_flight into their
only callers"), which folded the whole-disk in_flight accounting into
generic_start_io_acct() and generic_end_io_acct(). Those two helpers
were then removed by commit e722fff238bb ("block: remove
generic_{start,end}_io_acct"), and from that point on the whole disk's
in_flight is no longer accounted at all.

In update_io_ticks(), if calling bdev_count_inflight() finds that the
inflight value of the whole device is 0, the accumulation of io_ticks will
be skipped, causing the reported util% value to be underestimated.

Fix it by restoring the whole-disk in_flight accounting.

Fixes: e016b78201a2 ("block: return just one value from part_in_flight")

Suggested-by: Leon Hwang <leon.huangfu@shopee.com>
Signed-off-by: Tang Yizhou <yizhou.tang@shopee.com>
---
v2: Update commit message.
 block/blk-core.c | 4 ++++
 block/blk-mq.c   | 6 ++++++
 2 files changed, 10 insertions(+)

diff --git a/block/blk-core.c b/block/blk-core.c
index 17450058ea6d..03f4b7015e69 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -1043,6 +1043,8 @@ unsigned long bdev_start_io_acct(struct block_device *bdev, enum req_op op,
 	part_stat_lock();
 	update_io_ticks(bdev, start_time, false);
 	part_stat_local_inc(bdev, in_flight[op_is_write(op)]);
+	if (bdev_is_partition(bdev))
+		part_stat_local_inc(bdev_whole(bdev), in_flight[op_is_write(op)]);
 	part_stat_unlock();
 
 	return start_time;
@@ -1074,6 +1076,8 @@ void bdev_end_io_acct(struct block_device *bdev, enum req_op op,
 	part_stat_add(bdev, sectors[sgrp], sectors);
 	part_stat_add(bdev, nsecs[sgrp], jiffies_to_nsecs(duration));
 	part_stat_local_dec(bdev, in_flight[op_is_write(op)]);
+	if (bdev_is_partition(bdev))
+		part_stat_local_dec(bdev_whole(bdev), in_flight[op_is_write(op)]);
 	part_stat_unlock();
 }
 EXPORT_SYMBOL(bdev_end_io_acct);
diff --git a/block/blk-mq.c b/block/blk-mq.c
index d0c37daf568f..60ead16f1496 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -1084,6 +1084,9 @@ static inline void blk_account_io_done(struct request *req, u64 now)
 		part_stat_add(req->part, nsecs[sgrp], now - req->start_time_ns);
 		part_stat_local_dec(req->part,
 				    in_flight[op_is_write(req_op(req))]);
+		if (bdev_is_partition(req->part))
+			part_stat_local_dec(bdev_whole(req->part),
+					    in_flight[op_is_write(req_op(req))]);
 		part_stat_unlock();
 	}
 }
@@ -1144,6 +1147,9 @@ static inline void blk_account_io_start(struct request *req)
 	part_stat_lock();
 	update_io_ticks(req->part, jiffies, false);
 	part_stat_local_inc(req->part, in_flight[op_is_write(req_op(req))]);
+	if (bdev_is_partition(req->part))
+		part_stat_local_inc(bdev_whole(req->part),
+				    in_flight[op_is_write(req_op(req))]);
 	part_stat_unlock();
 }
 
-- 
2.43.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox