We've seen reports from the field panicing in xfs_trans_brelse after an io error for an attribute block. sd 0:0:23:0: [sdx] tag#271 CDB: Read(16) 88 00 00 00 00 00 9b df 5e 78 00 00 00 08 00 00 critical medium error, dev sdx, sector 2615107192 op 0x0:(READ) flags 0x1000 phys_seg 1 prio class 2 XFS (sdx1): metadata I/O error in "xfs_da_read_buf+0xe1/0x140 [xfs]" at daddr 0x9bdf5678 len 8 error 61 BUG: kernel NULL pointer dereference, address: 00000000000000e0 ... RIP: 0010:xfs_trans_brelse+0xb/0xe0 [xfs] ... Call Trace: <TASK> ... ? xfs_trans_brelse+0xb/0xe0 [xfs] xfs_attr_leaf_get+0xb6/0xc0 [xfs] xfs_attr_get+0xa0/0xd0 [xfs] xfs_xattr_get+0x75/0xb0 [xfs] __vfs_getxattr+0x53/0x70 inode_doinit_use_xattr+0x63/0x180 inode_doinit_with_dentry+0x196/0x510 security_d_instantiate+0x2f/0x50 d_splice_alias+0x46/0x2b0 xfs_vn_lookup+0x8b/0xb0 [xfs] __lookup_slow+0x84/0x130 walk_component+0x158/0x1d0 path_lookupat+0x6e/0x1c0 filename_lookup+0xcf/0x1d0 vfs_statx+0x8d/0x170 vfs_fstatat+0x54/0x70 __do_sys_newfstatat+0x26/0x60 This was an unsuccessful attempt to reproduce this using dmerror. It should provoke the problem if a system tap or custom kernel is used to return ENODATA as seen above, and not EIO as returned by dmerror. Signed-off-by: Donald Douwsma <ddouwsma@xxxxxxxxxx> --- tests/xfs/999 | 107 ++++++++++++++++++++++++++++++++++++++++++++++ tests/xfs/999.out | 2 + 2 files changed, 109 insertions(+) create mode 100755 tests/xfs/999 create mode 100644 tests/xfs/999.out diff --git a/tests/xfs/999 b/tests/xfs/999 new file mode 100755 index 00000000..627ca14e --- /dev/null +++ b/tests/xfs/999 @@ -0,0 +1,107 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2025 YOUR NAME HERE. All Rights Reserved. +# +# FS QA Test 601 +# +# what am I here for? +# Test for panic during ioerror reading xattr blocks +# +. ./common/preamble +_begin_fstest auto + +# Override the default cleanup function. +_cleanup() +{ + cd / + rm -f $tmp.* + _dmerror_cleanup +} + +# Import common functions. +# . ./common/filter +. ./common/dmerror + +# real QA test starts here + +# Modify as appropriate. +_require_scratch +_require_dm_target error + +_scratch_mkfs >> $seqres.full +_scratch_mount + +# TODO: Avoid assumptions about inode size using _xfs_get_inode_size and +# _xfs_get_inode_core_bytes, currently assuming 512 byte inodes. + +# Shortform +# Include shortform for completeness, we can only inject errors for attributes +# stored outside of the inode. +testfile="$SCRATCH_MNT/attr_shortform" +touch $testfile +ls -i $testfile >> $seqres.full +setfattr -n user.test_shortform -v $(printf "%0.12d" 12) $testfile + +# leaf with single block extent +testfile="$SCRATCH_MNT/attr_leaf_oneblock" +touch $testfile +ls -i $testfile >> $seqres.full +setfattr -n user.test_leaf -v $(printf "%0.512d" 512) $testfile + +# leaf with single multiple block extent +testfile="$SCRATCH_MNT/attr_leaf_twoblocks" +touch $testfile +ls -i $testfile >> $seqres.full +inode=$(ls -i $testfile|awk '{print $1}') +setfattr -n user.test_leaf -v $(printf "%0.5000d" 5000) $testfile + +# Generalise to for multiple attributes accross many blocks +# size_name= +# size_val=256 # how dows value>block-size work out? +# num_attrs=2022 +# testfile="$SCRATCH_MNT/attr_leaf_manyblocks" +# touch $testfile +# ls -li $testfile +# inode=$(ls -i $testfile|awk '{print $1}') +# +# for n_attr in $(seq 0 $num_attrs); do +# echo $n_attr; +# setfattr -n $(printf "user.test_leaf_%04d" $n_attr) \ +# -v $(printf "%0.${size_val}d" $size_val) \ +# $testfile +# done + +$XFS_IO_PROG -c "bmap -al" $testfile >> $seqres.full +attrblocks=($($XFS_IO_PROG -c "bmap -al" $testfile | awk 'match($3, /[0-9]+/, a) {print a[0]}')) +echo Attribute fork at blocks ${attrblocks[*]} >> $seqres.full + +_scratch_unmount + +echo "Dump inode $inode details with xfs_db" >> $seqres.full +# _scratch_xfs_db -c "inode $inode" -c "print core.aformat core.naextents a" >> $seqres.full +_scratch_xfs_db -c "inode $inode" -c print >> $seqres.full + +_dmerror_init >> $seqres.full 2>&1 +_dmerror_reset_table >> $seqres.full 2>&1 +_dmerror_mount >> $seqres.full 2>&1 + +echo "Setup dm-error when reading the second attribute block ${attrblocks[1]}" >> $seqres.full +_dmerror_mark_range_bad ${attrblocks[1]} 1 $SCRATCH_DEV + +# Debug from tests/xfs/556 +cat >> $seqres.full << ENDL +dmerror after marking bad: +$DMERROR_TABLE +$DMERROR_RTTABLE +<end table> +ENDL + +_dmerror_load_error_table + +# Panic here if failure +echo "Re-read the extended attribute, panics on unandled ioerrors" >> $seqres.full +getfattr -d -m - $testfile >> $seqres.full + +# success, all done +status=0 +exit diff --git a/tests/xfs/999.out b/tests/xfs/999.out new file mode 100644 index 00000000..e7f4becf --- /dev/null +++ b/tests/xfs/999.out @@ -0,0 +1,2 @@ +QA output created by 999 +Silence is golden -- 2.47.3