| #! /bin/bash |
| # SPDX-License-Identifier: GPL-2.0-or-later |
| # Copyright (c) 2021 Oracle. All Rights Reserved. |
| # |
| # FS QA Test No. 631 |
| # |
| # Reproducer for a deadlock in xfs_rename reported by Wenli Xie. |
| # |
| # When overlayfs is running on top of xfs and the user unlinks a file in the |
| # overlay, overlayfs will create a whiteout inode and ask us to "rename" the |
| # whiteout file atop the one being unlinked. If the file being unlinked loses |
| # its one nlink, we then have to put the inode on the unlinked list. |
| # |
| # This requires us to grab the AGI buffer of the whiteout inode to take it |
| # off the unlinked list (which is where whiteouts are created) and to grab |
| # the AGI buffer of the file being deleted. If the whiteout was created in |
| # a higher numbered AG than the file being deleted, we'll lock the AGIs in |
| # the wrong order and deadlock. |
| # |
| # Note that this test doesn't do anything xfs-specific so it's a generic test. |
| # This is a regression test for commit 6da1b4b1ab36 ("xfs: fix an ABBA deadlock |
| # in xfs_rename"). |
| |
| seq=`basename $0` |
| seqres=$RESULT_DIR/$seq |
| echo "QA output created by $seq" |
| |
| here=`pwd` |
| tmp=/tmp/$$ |
| status=1 # failure is the default! |
| trap "_cleanup; exit \$status" 0 1 2 3 15 |
| |
| _cleanup() |
| { |
| stop_workers |
| cd / |
| rm -f $tmp.* |
| } |
| |
| # get standard environment, filters and checks |
| . ./common/rc |
| . ./common/attr |
| |
| # real QA test starts here |
| _supported_fs generic |
| _require_scratch |
| _require_attrs |
| test "$FSTYP" = "overlay" && _notrun "Test does not apply to overlayfs." |
| _require_extra_fs overlay |
| |
| rm -f $seqres.full |
| |
| _scratch_mkfs >> $seqres.full |
| _scratch_mount |
| _supports_filetype $SCRATCH_MNT || _notrun "overlayfs test requires d_type" |
| |
| mkdir $SCRATCH_MNT/lowerdir |
| mkdir $SCRATCH_MNT/lowerdir1 |
| mkdir $SCRATCH_MNT/lowerdir/etc |
| mkdir $SCRATCH_MNT/workers |
| echo salts > $SCRATCH_MNT/lowerdir/etc/access.conf |
| touch $SCRATCH_MNT/running |
| |
| stop_workers() { |
| test -e $SCRATCH_MNT/running || return |
| rm -f $SCRATCH_MNT/running |
| |
| while [ "$(ls $SCRATCH_MNT/workers/ | wc -l)" -gt 0 ]; do |
| wait |
| done |
| } |
| |
| worker() { |
| local tag="$1" |
| local mergedir="$SCRATCH_MNT/merged$tag" |
| local l="lowerdir=$SCRATCH_MNT/lowerdir:$SCRATCH_MNT/lowerdir1" |
| local u="upperdir=$SCRATCH_MNT/upperdir$tag" |
| local w="workdir=$SCRATCH_MNT/workdir$tag" |
| local i="index=off" |
| |
| touch $SCRATCH_MNT/workers/$tag |
| while test -e $SCRATCH_MNT/running; do |
| rm -rf $SCRATCH_MNT/merged$tag |
| rm -rf $SCRATCH_MNT/upperdir$tag |
| rm -rf $SCRATCH_MNT/workdir$tag |
| mkdir $SCRATCH_MNT/merged$tag |
| mkdir $SCRATCH_MNT/workdir$tag |
| mkdir $SCRATCH_MNT/upperdir$tag |
| |
| mount -t overlay overlay -o "$l,$u,$w,$i" $mergedir |
| mv $mergedir/etc/access.conf $mergedir/etc/access.conf.bak |
| touch $mergedir/etc/access.conf |
| mv $mergedir/etc/access.conf $mergedir/etc/access.conf.bak |
| touch $mergedir/etc/access.conf |
| umount $mergedir |
| done |
| rm -f $SCRATCH_MNT/workers/$tag |
| } |
| |
| for i in $(seq 0 $((4 + LOAD_FACTOR)) ); do |
| worker $i & |
| done |
| |
| sleep $((30 * TIME_FACTOR)) |
| stop_workers |
| |
| echo Silence is golden. |
| # success, all done |
| status=0 |
| exit |