DF-2661 / trigger_df2661.sh
#!/bin/sh # DF-2661 trigger (v3): concurrent cold inode loads in the same 64KB DIO # window, with the underlying read failed with EIO (media-fault simulation). # # Bug: _hammer2_io_getblk() completes the INPROG section with error != 0, # leaving dio->bp set and DIO_GOOD clear. Any other thread holding a ref on # the DIO (taken while the first thread was inside the INPROG section) wakes # up, acquires INPROG, and trips KKASSERT(dio->bp == NULL) at # hammer2_io.c:254 -> panic. # # Media prep (once): # golden.img = dd if=/dev/vn2 of=... taken while the fs is still attached # (reads serve from the dirty device buffer cache). NEVER use # `vnconfig -T`: it is O_TRUNC and zeroed the backing file. # Per round: fresh clone of golden.img, attach WITHOUT -T, mount (cold # chains + cold device buffers => real strategy I/O in the INPROG # section), warm the path walk, arm the injector, blast 8 concurrent # cat loops at 8 distinct cold inodes sharing one 64KB window. set -x GOLD=/root/golden.img WORK=/root/work.img MNT=/mnt/h2661 ROUNDS=${1:-5} ARM=${ARM:-1} # ARM=0 -> do not arm the injector (control runs) UMNT=${UMNT:-1} # UMNT=0 -> skip the per-round umount mkgolden() { umount $MNT 2>/dev/null vnconfig -u vn2 2>/dev/null umount $MNT 2>/dev/null vnconfig -u vn2 2>/dev/null rm -f /root/seed.img $GOLD truncate -s 512M /root/seed.img # -T is safe HERE because we want a fresh zero file anyway vnconfig -c -S 512m -T vn2 /root/seed.img || return 1 newfs_hammer2 -L DATA /dev/vn2 || return 1 mkdir -p $MNT mount_hammer2 /dev/vn2@DATA $MNT || return 1 mkdir $MNT/dir || return 1 i=1 while [ $i -le 40 ]; do touch $MNT/dir/f$i || return 1 i=$((i+1)) done ls $MNT/dir | wc -l sync sleep 3 # materialize the live device content (dirty buffers included) dd if=/dev/vn2 of=$GOLD bs=1m count=512 || return 1 umount $MNT || return 1 vnconfig -u vn2 || return 1 rm -f /root/seed.img echo GOLDEN_OK } round() { vnconfig -u vn2 2>/dev/null rm -f $WORK cp $GOLD $WORK || return 2 vnconfig -c -S 512m vn2 $WORK || return 2 mount_hammer2 /dev/vn2@DATA $MNT || return 2 # Warm path-walk chains (root inode, dir inode, dirent chains). # Plain ls does NOT stat the file inodes -> they stay cold. ls $MNT/dir >/dev/null 2>&1 # Arm injector: fail every inode read on the 512MB test volume. [ "$ARM" = "1" ] && sysctl vfs.hammer2.df2661_fail_inode_read=1000000 # 8 concurrent readers of 8 distinct cold inodes (same 64KB window). PIDS="" i=1 while [ $i -le 8 ]; do ( while :; do cat $MNT/dir/f$i >/dev/null 2>&1; done ) & PIDS="$PIDS $!" i=$((i+1)) done sleep 15 for p in $PIDS; do kill $p 2>/dev/null; done wait 2>/dev/null sysctl vfs.hammer2.df2661_fail_inode_read=0 [ "$UMNT" = "1" ] && umount $MNT 2>/dev/null echo ROUND_SURVIVED } case "$1" in golden) mkgolden ;; *) n=1 while [ $n -le $ROUNDS ]; do echo "=== ROUND $n ===" round || break n=$((n+1)) done echo "ALL_ROUNDS_SURVIVED" ;; esac |