@@ -862,3 +862,9 @@ _force_xfsv4_mount_options()
fi
echo "MOUNT_OPTIONS = $MOUNT_OPTIONS" >>$seqres.full
}
+
+# Find AG count of mounted filesystem
+_xfs_mount_agcount()
+{
+ $XFS_INFO_PROG "$1" | grep agcount= | sed -e 's/^.*agcount=\([0-9]*\),.*$/\1/g'
+}
@@ -28,7 +28,7 @@ LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \
attr-list-by-handle-cursor-test listxattr dio-interleaved t_dir_type \
dio-invalidate-cache stat_test t_encrypted_d_revalidate \
attr_replace_test swapon mkswap t_attr_corruption t_open_tmpfiles \
- fscrypt-crypt-util
+ fscrypt-crypt-util bulkstat_null_ocount
SUBDIRS = log-writes perf
new file mode 100644
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * Ensure the kernel returns the new lastip even when ocount is null.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <xfs/xfs.h>
+
+static void die(const char *tag)
+{
+ perror(tag);
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ struct xfs_bstat bstat;
+ __u64 last;
+ struct xfs_fsop_bulkreq bulkreq = {
+ .lastip = &last,
+ .icount = 1,
+ .ubuffer = &bstat,
+ .ocount = NULL,
+ };
+ int ret;
+ int fd;
+
+ fd = open(argv[1], O_RDONLY);
+ if (fd < 0)
+ die(argv[1]);
+
+ last = 0;
+ ret = ioctl(fd, XFS_IOC_FSINUMBERS, &bulkreq);
+ if (ret)
+ die("inumbers");
+
+ if (last == 0)
+ printf("inumbers last = %llu\n", (unsigned long long)last);
+
+ last = 0;
+ ret = ioctl(fd, XFS_IOC_FSBULKSTAT, &bulkreq);
+ if (ret)
+ die("bulkstat");
+
+ if (last == 0)
+ printf("bulkstat last = %llu\n", (unsigned long long)last);
+
+ ret = close(fd);
+ if (ret)
+ die("close");
+
+ return 0;
+}
@@ -63,7 +63,7 @@ for x in `seq 2 64`; do
touch "${TESTFILE}.${x}"
done
inode="$(stat -c '%i' "${TESTFILE}.1")"
-agcount="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep agcount= | sed -e 's/^.*agcount=\([0-9]*\),.*$/\1/g')"
+agcount="$(_xfs_mount_agcount $SCRATCH_MNT)"
umount "${SCRATCH_MNT}"
echo "+ check fs"
@@ -64,7 +64,7 @@ for x in `seq 2 64`; do
touch "${TESTFILE}.${x}"
done
inode="$(stat -c '%i' "${TESTFILE}.1")"
-agcount="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep agcount= | sed -e 's/^.*agcount=\([0-9]*\),.*$/\1/g')"
+agcount="$(_xfs_mount_agcount $SCRATCH_MNT)"
test "${agcount}" -gt 1 || _notrun "Single-AG XFS not supported"
umount "${SCRATCH_MNT}"
@@ -64,7 +64,7 @@ for x in `seq 2 64`; do
touch "${TESTFILE}.${x}"
done
inode="$(stat -c '%i' "${TESTFILE}.1")"
-agcount="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep agcount= | sed -e 's/^.*agcount=\([0-9]*\),.*$/\1/g')"
+agcount="$(_xfs_mount_agcount $SCRATCH_MNT)"
umount "${SCRATCH_MNT}"
echo "+ check fs"
@@ -64,7 +64,7 @@ for x in `seq 2 64`; do
touch "${TESTFILE}.${x}"
done
inode="$(stat -c '%i' "${TESTFILE}.1")"
-agcount="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep agcount= | sed -e 's/^.*agcount=\([0-9]*\),.*$/\1/g')"
+agcount="$(_xfs_mount_agcount $SCRATCH_MNT)"
umount "${SCRATCH_MNT}"
echo "+ check fs"
@@ -64,7 +64,7 @@ for x in `seq 2 64`; do
touch "${TESTFILE}.${x}"
done
inode="$(stat -c '%i' "${TESTFILE}.1")"
-agcount="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep agcount= | sed -e 's/^.*agcount=\([0-9]*\),.*$/\1/g')"
+agcount="$(_xfs_mount_agcount $SCRATCH_MNT)"
umount "${SCRATCH_MNT}"
echo "+ check fs"
@@ -64,7 +64,7 @@ for x in `seq 2 64`; do
touch "${TESTFILE}.${x}"
done
inode="$(stat -c '%i' "${TESTFILE}.1")"
-agcount="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep agcount= | sed -e 's/^.*agcount=\([0-9]*\),.*$/\1/g')"
+agcount="$(_xfs_mount_agcount $SCRATCH_MNT)"
umount "${SCRATCH_MNT}"
echo "+ check fs"
@@ -64,7 +64,7 @@ for x in `seq 2 64`; do
touch "${TESTFILE}.${x}"
done
inode="$(stat -c '%i' "${TESTFILE}.1")"
-agcount="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep agcount= | sed -e 's/^.*agcount=\([0-9]*\),.*$/\1/g')"
+agcount="$(_xfs_mount_agcount $SCRATCH_MNT)"
umount "${SCRATCH_MNT}"
echo "+ check fs"
@@ -67,7 +67,7 @@ for x in `seq 2 64`; do
touch "${TESTFILE}.${x}"
done
inode="$(stat -c '%i' "${TESTFILE}.1")"
-agcount="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep agcount= | sed -e 's/^.*agcount=\([0-9]*\),.*$/\1/g')"
+agcount="$(_xfs_mount_agcount $SCRATCH_MNT)"
umount "${SCRATCH_MNT}"
echo "+ check fs"
@@ -43,7 +43,7 @@ _scratch_mkfs_xfs > /dev/null
echo "+ mount fs image"
_scratch_mount
blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
-agcount="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep agcount= | sed -e 's/^.*agcount=\([0-9]*\),.*$/\1/g')"
+agcount="$(_xfs_mount_agcount $SCRATCH_MNT)"
echo "+ make some files"
_pwrite_byte 0x62 0 $((blksz * 64)) "${SCRATCH_MNT}/file0" >> "$seqres.full"
@@ -41,7 +41,7 @@ _scratch_mkfs_xfs > /dev/null
echo "+ mount fs image"
_scratch_mount
blksz=$(stat -f -c '%s' ${SCRATCH_MNT})
-agcount=$($XFS_INFO_PROG ${SCRATCH_MNT} | grep agcount= | sed -e 's/^.*agcount=\([0-9]*\),.*$/\1/g')
+agcount=$(_xfs_mount_agcount $SCRATCH_MNT)
echo "+ make some files"
_pwrite_byte 0x62 0 $((blksz * 64)) ${SCRATCH_MNT}/file0 >> $seqres.full
@@ -37,7 +37,7 @@ echo "Format and mount"
_scratch_mkfs > "$seqres.full" 2>&1
_scratch_mount
-agcount=$($XFS_INFO_PROG $SCRATCH_MNT | grep agcount= | sed -e 's/^.*agcount=\([0-9]*\),.*$/\1/g')
+agcount=$(_xfs_mount_agcount $SCRATCH_MNT)
echo "Get fsmap" | tee -a $seqres.full
$XFS_IO_PROG -c 'fsmap -v' $SCRATCH_MNT > $TEST_DIR/fsmap
new file mode 100755
@@ -0,0 +1,171 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2014 Red Hat, Inc. All Rights Reserved.
+# Copyright (c) 2019 Oracle, Inc. All Rights Reserved.
+#
+# FS QA Test No. 744
+#
+# Use the xfs_io bulkstat utility to verify bulkstat finds all inodes in a
+# filesystem. Test under various inode counts, inobt record layouts and
+# bulkstat batch sizes.
+#
+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()
+{
+ cd /
+ rm -f $tmp.*
+}
+
+# print the number of inodes counted by bulkstat
+_bstat_count()
+{
+ local batchsize="$1"
+ local tag="$2"
+
+ echo -n "$tag: "
+ $XFS_IO_PROG -c "bulkstat -n $batchsize" $SCRATCH_MNT | grep ino | wc -l
+}
+
+# print the number of inodes counted by per-ag bulkstat
+_bstat_perag_count()
+{
+ local batchsize="$1"
+ local tag="$2"
+
+ local agcount=$(_xfs_mount_agcount $SCRATCH_MNT)
+ echo -n "$tag: "
+ seq 0 $((agcount - 1)) | while read ag; do
+ $XFS_IO_PROG -c "bulkstat -a $ag -n $batchsize" $SCRATCH_MNT
+ done | grep ino | wc -l
+}
+
+# Sum the number of allocated inodes in a fs...
+_inumbers_ag()
+{
+ local agcount="$1"
+ local batchsize="$2"
+ local mount="$3"
+
+ seq 0 $((agcount - 1)) | while read ag; do
+ $XFS_IO_PROG -c "inumbers -a $ag -n $batchsize" $mount
+ done | grep alloccount | awk '{x += $3} END { print(x) }'
+}
+
+# print the number of inodes counted by inumbers
+_inumbers_count()
+{
+ local expect="$1"
+
+ # There probably aren't more than 10 hidden inodes, right?
+ local tolerance=10
+
+ # Force all background inode cleanup
+ _scratch_cycle_mount
+
+ echo -n "inumbers all: "
+ nr=$($XFS_IO_PROG -c "inumbers" $SCRATCH_MNT | grep alloccount | \
+ awk '{x += $3} END { print(x) }')
+ _within_tolerance "inumbers" $nr $expect $tolerance -v
+
+ local agcount=$(_xfs_mount_agcount $SCRATCH_MNT)
+ for batchsize in 64 2 1; do
+ echo -n "inumbers $batchsize: "
+ nr=$(_inumbers_ag $agcount $batchsize $SCRATCH_MNT)
+ _within_tolerance "inumbers" $nr $expect $tolerance -v
+ done
+}
+
+# compare the src/bstat output against the xfs_io bstat output
+_bstat_compare()
+{
+ diff -u <(./src/bstat $SCRATCH_MNT | grep ino | awk '{print $2}') \
+ <($XFS_IO_PROG -c 'bulkstat' $SCRATCH_MNT | grep ino | awk '{print $3}')
+}
+
+# print bulkstat counts using varied batch sizes
+_bstat_test()
+{
+ expect=`find $SCRATCH_MNT -print | wc -l`
+ echo
+ echo "expect $expect"
+
+ for sz in 4096 71 32 1; do
+ _bstat_count $sz "$sz all"
+ _bstat_perag_count $sz "$sz perag"
+ _bstat_compare
+ _inumbers_count $expect
+ done
+}
+
+# get standard environment, filters and checks
+. ./common/rc
+. ./common/filter
+
+_require_scratch
+_require_xfs_io_command bulkstat
+_require_xfs_io_command bulkstat_single
+_require_xfs_io_command inumbers
+
+# real QA test starts here
+
+_supported_fs xfs
+_supported_os Linux
+
+rm -f $seqres.full
+
+DIRCOUNT=8
+INOCOUNT=$((2048 / DIRCOUNT))
+
+_scratch_mkfs "-d agcount=$DIRCOUNT" >> $seqres.full 2>&1 || _fail "mkfs failed"
+_scratch_mount
+
+# who is the root inode?
+bs_root=$($XFS_IO_PROG -c 'bulkstat_single root' $SCRATCH_MNT 2> /dev/null | grep ino | \
+ awk '{print $3}')
+stat_root=$(stat -c '%i' $SCRATCH_MNT)
+if [ -n "$bs_root" ] && [ "$stat_root" -ne "$bs_root" ]; then
+ echo "stat says root is $stat_root but bulkstat says $bs_root"
+fi
+
+# create a set of directories and fill each with a fixed number of files
+for dir in $(seq 1 $DIRCOUNT); do
+ mkdir -p $SCRATCH_MNT/$dir
+ for i in $(seq 1 $INOCOUNT); do
+ touch $SCRATCH_MNT/$dir/$i
+ done
+done
+_bstat_test
+
+# remove every other file from each dir
+for dir in $(seq 1 $DIRCOUNT); do
+ for i in $(seq 2 2 $INOCOUNT); do
+ rm -f $SCRATCH_MNT/$dir/$i
+ done
+done
+_bstat_test
+
+# remove the entire second half of files
+for dir in $(seq 1 $DIRCOUNT); do
+ for i in $(seq $((INOCOUNT / 2)) $INOCOUNT); do
+ rm -f $SCRATCH_MNT/$dir/$i
+ done
+done
+_bstat_test
+
+# remove all regular files
+for dir in $(seq 1 $DIRCOUNT); do
+ rm -f $SCRATCH_MNT/$dir/*
+done
+_bstat_test
+
+# success, all done
+status=0
+exit
new file mode 100644
@@ -0,0 +1,105 @@
+QA output created by 744
+
+expect 2057
+4096 all: 2057
+4096 perag: 2057
+inumbers all: inumbers is in range
+inumbers 64: inumbers is in range
+inumbers 2: inumbers is in range
+inumbers 1: inumbers is in range
+71 all: 2057
+71 perag: 2057
+inumbers all: inumbers is in range
+inumbers 64: inumbers is in range
+inumbers 2: inumbers is in range
+inumbers 1: inumbers is in range
+32 all: 2057
+32 perag: 2057
+inumbers all: inumbers is in range
+inumbers 64: inumbers is in range
+inumbers 2: inumbers is in range
+inumbers 1: inumbers is in range
+1 all: 2057
+1 perag: 2057
+inumbers all: inumbers is in range
+inumbers 64: inumbers is in range
+inumbers 2: inumbers is in range
+inumbers 1: inumbers is in range
+
+expect 1033
+4096 all: 1033
+4096 perag: 1033
+inumbers all: inumbers is in range
+inumbers 64: inumbers is in range
+inumbers 2: inumbers is in range
+inumbers 1: inumbers is in range
+71 all: 1033
+71 perag: 1033
+inumbers all: inumbers is in range
+inumbers 64: inumbers is in range
+inumbers 2: inumbers is in range
+inumbers 1: inumbers is in range
+32 all: 1033
+32 perag: 1033
+inumbers all: inumbers is in range
+inumbers 64: inumbers is in range
+inumbers 2: inumbers is in range
+inumbers 1: inumbers is in range
+1 all: 1033
+1 perag: 1033
+inumbers all: inumbers is in range
+inumbers 64: inumbers is in range
+inumbers 2: inumbers is in range
+inumbers 1: inumbers is in range
+
+expect 521
+4096 all: 521
+4096 perag: 521
+inumbers all: inumbers is in range
+inumbers 64: inumbers is in range
+inumbers 2: inumbers is in range
+inumbers 1: inumbers is in range
+71 all: 521
+71 perag: 521
+inumbers all: inumbers is in range
+inumbers 64: inumbers is in range
+inumbers 2: inumbers is in range
+inumbers 1: inumbers is in range
+32 all: 521
+32 perag: 521
+inumbers all: inumbers is in range
+inumbers 64: inumbers is in range
+inumbers 2: inumbers is in range
+inumbers 1: inumbers is in range
+1 all: 521
+1 perag: 521
+inumbers all: inumbers is in range
+inumbers 64: inumbers is in range
+inumbers 2: inumbers is in range
+inumbers 1: inumbers is in range
+
+expect 9
+4096 all: 9
+4096 perag: 9
+inumbers all: inumbers is in range
+inumbers 64: inumbers is in range
+inumbers 2: inumbers is in range
+inumbers 1: inumbers is in range
+71 all: 9
+71 perag: 9
+inumbers all: inumbers is in range
+inumbers 64: inumbers is in range
+inumbers 2: inumbers is in range
+inumbers 1: inumbers is in range
+32 all: 9
+32 perag: 9
+inumbers all: inumbers is in range
+inumbers 64: inumbers is in range
+inumbers 2: inumbers is in range
+inumbers 1: inumbers is in range
+1 all: 9
+1 perag: 9
+inumbers all: inumbers is in range
+inumbers 64: inumbers is in range
+inumbers 2: inumbers is in range
+inumbers 1: inumbers is in range
new file mode 100755
@@ -0,0 +1,44 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2019 Oracle, Inc. All Rights Reserved.
+#
+# FS QA Test No. 745
+#
+# Regression test for a long-standing bug in BULKSTAT and INUMBERS where
+# the kernel fails to write thew new @lastip value back to userspace if
+# @ocount is NULL.
+#
+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()
+{
+ cd /
+ rm -f $tmp.*
+}
+
+# get standard environment, filters and checks
+. ./common/rc
+. ./common/filter
+
+_require_test_program "bulkstat_null_ocount"
+
+# real QA test starts here
+
+_supported_fs xfs
+_supported_os Linux
+
+rm -f $seqres.full
+
+echo "Silence is golden."
+src/bulkstat_null_ocount $TEST_DIR
+
+# success, all done
+status=0
+exit
new file mode 100644
@@ -0,0 +1,2 @@
+QA output created by 745
+Silence is golden.
@@ -504,4 +504,6 @@
504 auto quick mkfs label
505 auto quick spaceman
506 auto quick health
+744 auto ioctl quick
+745 auto ioctl quick
907 clone