| #! /bin/bash |
| # SPDX-License-Identifier: GPL-2.0 |
| # Copyright (C) 2024 SUSE Linux Products GmbH. All Rights Reserved. |
| # |
| # FS QA Test 366 |
| # |
| # Test if mixed direct read, direct write and buffered write on the same file will |
| # hang the filesystem. |
| # |
| # This is exposed by an incoming btrfs feature, which allows a folio to be |
| # partial uptodate if the buffered write range is block aligned but not yet |
| # full folio aligned. |
| # |
| # Such behavior makes btrfs to hang reliably under generic/095. |
| # This is the extracted minimal reproducer for 4k block size and 64K page size. |
| # |
| . ./common/preamble |
| _begin_fstest auto quick rw |
| |
| . ./common/filter |
| |
| _require_scratch |
| _require_odirect 512 # see fio job1 config below |
| _require_aio |
| |
| [ "$FSTYP" = "btrfs" ] && _fixed_by_kernel_commit xxxxxxxxxxxx \ |
| "btrfs: avoid deadlock when reading a partial uptodate folio" |
| |
| iterations=$((32 * LOAD_FACTOR)) |
| |
| fio_config=$tmp.fio |
| fio_out=$tmp.fio.out |
| blksz=`$here/src/min_dio_alignment $SCRATCH_MNT $SCRATCH_DEV` |
| cat >$fio_config <<EOF |
| [global] |
| bs=8k |
| iodepth=1 |
| randrepeat=1 |
| size=256k |
| directory=$SCRATCH_MNT |
| numjobs=1 |
| [job1] |
| ioengine=sync |
| bs=512 |
| direct=1 |
| rw=randread |
| filename=file1 |
| [job2] |
| ioengine=libaio |
| rw=randwrite |
| direct=1 |
| filename=file1 |
| [job3] |
| ioengine=posixaio |
| rw=randwrite |
| filename=file1 |
| EOF |
| _require_fio $fio_config |
| |
| for (( i = 0; i < $iterations; i++)); do |
| _scratch_mkfs >>$seqres.full 2>&1 |
| _scratch_mount |
| # There's a known EIO failure to report collisions between directio and buffered |
| # writes to userspace, refer to upstream linux 5a9d929d6e13. So ignore EIO error |
| # at here. |
| # |
| # And for btrfs if sector size < page size, if we have a partial |
| # uptodate folio caused by a buffered write, e.g: |
| # |
| # 0 16K 32K 48K 64K |
| # | |///////////| |
| # \- sector Uptodate|Dirty |
| # |
| # Then writeback happens and finished, but btrfs' ordered extent not |
| # yet finished. |
| # In that case, the folio can be released from the page cache (since |
| # the folio is not under IO/lock). |
| # |
| # Then new buffered writes into the folio happened, re-dirty the folio: |
| # 0 16K 32K 48K 64K |
| # |//////////| |///////////| |
| # \- sector Uptodate|Dirty \- No sector flags |
| # extent map PINNED |
| # OE still here |
| # |
| # Now read is triggered on that folio. |
| # Btrfs will need to wait for any existing ordered extents in the folio range, |
| # that wait will also trigger writeback if the folio is dirty. |
| # That writeback will happen for range [48K, 64K), but since the whole folio |
| # is locked for read, writeback will also try to lock the same folio, causing |
| # a deadlock. |
| $FIO_PROG $fio_config --ignore_error=,EIO --output=$fio_out |
| # umount before checking dmesg in case umount triggers any WARNING or Oops |
| _scratch_unmount |
| |
| _check_dmesg _filter_aiodio_dmesg |
| |
| echo "=== fio $i/$iterations ===" >> $seqres.full |
| cat $fio_out >> $seqres.full |
| done |
| |
| echo "Silence is golden" |
| |
| # success, all done |
| status=0 |
| exit |