Re: [PATCH 2/2] check: capture dmesg of mount failures if test fails

"Darrick J. Wong" <[email protected]>
Newsgroups org.kernel.vger.fstests,org.kernel.vger.linux-xfs
Message-ID <20260414171730.GA149968@frogsfrogsfrogs>
On Tue, Apr 14, 2026 at 12:59:12AM -0700, Christoph Hellwig wrote:
> On Mon, Apr 13, 2026 at 10:51:25AM -0700, Darrick J. Wong wrote:
> > From: Darrick J. Wong <[email protected]>
> > 
> > Capture the kernel output after a mount failure occurs.  If the test
> > itself fails, then keep the logging output for further diagnosis.
> 
> I have to admit I don't really understand what the xunit and selftests
> stuff is doing.  Can you explain that a bit in the commit message?

The xunit.xsd update specifies that there can be a <mount-failure>
element in the xml output, and that its contents will be the
$seqres.mountfail file.

The new selftest practices creating the .mountfail file in the test
output directory after a mount failure.  Annoyingly there's no way for
the test itself to check that, since the .mountfail file is created in
check.

How about the following?

--D

From: Darrick J. Wong <[email protected]>
Subject: [PATCH] check: capture dmesg of mount failures if test fails

Capture the kernel output after a mount failure occurs.  If the test
itself fails, then keep the logging output for further diagnosis.  The
xunit.xsd update adds a <mount-failure> element to the xml output, whose
contents are the mountfail file.

Note that because the .mountfail file is preserved by ./check, the new
selftest requires the user to check for the .mountfail file.  This is a
little awkward.

Signed-off-by: "Darrick J. Wong" <[email protected]>
---
 check                  |   22 +++++++++++++++++++++-
 common/config          |    1 +
 common/rc              |   26 +++++++++++++++++++++++++-
 common/report          |    8 ++++++++
 doc/xunit.xsd          |   12 +++++++++++-
 tests/selftest/008     |   22 ++++++++++++++++++++++
 tests/selftest/008.out |    1 +
 7 files changed, 89 insertions(+), 3 deletions(-)
 create mode 100755 tests/selftest/008
 create mode 100644 tests/selftest/008.out

diff --git a/check b/check
index 6ae89feba7c30a..c27cd8d9b81022 100755
--- a/check
+++ b/check
@@ -608,7 +608,7 @@ _stash_fail_loop_files() {
 	local seq_prefix="${REPORT_DIR}/${1}"
 	local cp_suffix="$2"
 
-	for i in ".full" ".dmesg" ".out.bad" ".notrun" ".core" ".hints"; do
+	for i in ".full" ".dmesg" ".out.bad" ".notrun" ".core" ".hints" ".mountfail"; do
 		rm -f "${seq_prefix}${i}${cp_suffix}"
 		if [ -f "${seq_prefix}${i}" ]; then
 			cp "${seq_prefix}${i}" "${seq_prefix}${i}${cp_suffix}"
@@ -982,6 +982,7 @@ function run_section()
 				      echo -n "	$seqnum -- "
 			cat $seqres.notrun
 			tc_status="notrun"
+			rm -f "$seqres.mountfail?"
 			_stash_test_status "$seqnum" "$tc_status"
 
 			# Unmount the scratch fs so that we can wipe the scratch
@@ -1051,6 +1052,7 @@ function run_section()
 		if [ ! -f $seq.out ]; then
 			_dump_err "no qualified output"
 			tc_status="fail"
+			rm -f "$seqres.mountfail?"
 			_stash_test_status "$seqnum" "$tc_status"
 			continue;
 		fi
@@ -1089,6 +1091,24 @@ function run_section()
 				rm -f $seqres.hints
 			fi
 		fi
+
+		if [ -f "$seqres.mountfail?" ]; then
+			if [ "$tc_status" = "fail" ]; then
+				# Let the user know if there were mount
+				# failures on a test that failed because that
+				# could be interesting.
+				mv "$seqres.mountfail?" "$seqres.mountfail"
+				_dump_err "check: possible mount failures (see $seqres.mountfail)"
+				test -f $seqres.mountfail && \
+					maybe_compress_logfile $seqres.mountfail $MAX_MOUNTFAIL_SIZE
+			else
+				# Don't retain mount failure logs for tests
+				# that pass or were skipped because some tests
+				# intentionally drive mount failures.
+				rm -f "$seqres.mountfail?"
+			fi
+		fi
+
 		_stash_test_status "$seqnum" "$tc_status"
 	done
 
diff --git a/common/config b/common/config
index 2fa9ba44b8ad6c..5462bc061f3664 100644
--- a/common/config
+++ b/common/config
@@ -358,6 +358,7 @@ true "${MAX_OUTPUT_SIZE:=65536}"
 true "${MAX_FULL_SIZE:=$((MAX_OUTPUT_SIZE * 2))}"
 true "${MAX_DMESG_SIZE:=${MAX_OUTPUT_SIZE}}"
 true "${MAX_OUTBAD_SIZE:=${MAX_OUTPUT_SIZE}}"
+true "${MAX_MOUNTFAIL_SIZE:=${MAX_OUTPUT_SIZE}}"
 
 _common_mount_opts()
 {
diff --git a/common/rc b/common/rc
index b11a7d1e404519..7de939d1630dd9 100644
--- a/common/rc
+++ b/common/rc
@@ -288,9 +288,33 @@ _get_hugepagesize()
 	awk '/Hugepagesize/ {print $2 * 1024}' /proc/meminfo
 }
 
+# Does dmesg have a --since flag?
+_dmesg_detect_since()
+{
+	if [ -z "$DMESG_HAS_SINCE" ]; then
+		test "$DMESG_HAS_SINCE" = "yes"
+		return
+	elif dmesg --help | grep -q -- --since; then
+		DMESG_HAS_SINCE=yes
+	else
+		DMESG_HAS_SINCE=no
+	fi
+}
+
 _mount()
 {
-    $MOUNT_PROG $*
+	$MOUNT_PROG $*
+	ret=$?
+	if [ "$ret" -ne 0 ]; then
+		echo "\"$MOUNT_PROG $*\" failed at $(date)" >> "$seqres.mountfail?"
+		if _dmesg_detect_since; then
+			dmesg --since '30s ago' >> "$seqres.mountfail?"
+		else
+			dmesg | tail -n 100 >> "$seqres.mountfail?"
+		fi
+	fi
+
+	return $ret
 }
 
 # Call _mount to do mount operation but also save mountpoint to
diff --git a/common/report b/common/report
index 7128bbebac8b75..a41a58f790b784 100644
--- a/common/report
+++ b/common/report
@@ -199,6 +199,7 @@ _xunit_make_testcase_report()
 		local out_src="${SRC_DIR}/${test_name}.out"
 		local full_file="${REPORT_DIR}/${test_name}.full"
 		local dmesg_file="${REPORT_DIR}/${test_name}.dmesg"
+		local mountfail_file="${REPORT_DIR}/${test_name}.mountfail"
 		local outbad_file="${REPORT_DIR}/${test_name}.out.bad"
 		if [ -z "$_err_msg" ]; then
 			_err_msg="Test $test_name failed, reason unknown"
@@ -225,6 +226,13 @@ _xunit_make_testcase_report()
 			printf ']]>\n'	>>$report
 			echo -e "\t\t</system-err>" >> $report
 		fi
+		if [ -z "$quiet" -a -f "$mountfail_file" ]; then
+			echo -e "\t\t<mount-failure>" >> $report
+			printf	'<![CDATA[\n' >>$report
+			cat "$mountfail_file" | tr -dc '[:print:][:space:]' | encode_cdata >>$report
+			printf ']]>\n'	>>$report
+			echo -e "\t\t</mount-failure>" >> $report
+		fi
 		;;
 	*)
 		echo -e "\t\t<failure message=\"Unknown test_status=$test_status\" type=\"TestFail\"/>" >> $report
diff --git a/doc/xunit.xsd b/doc/xunit.xsd
index d287eaf5a25fb6..efe0badbb338b5 100644
--- a/doc/xunit.xsd
+++ b/doc/xunit.xsd
@@ -131,7 +131,7 @@
                                 </xs:complexType>
                             </xs:element>
                         </xs:choice>
-                        <xs:choice minOccurs="0" maxOccurs="3">
+                        <xs:choice minOccurs="0" maxOccurs="4">
                             <xs:element name="system-out" minOccurs="0" maxOccurs="1">
                                 <xs:annotation>
                                     <xs:documentation xml:lang="en">Data that was written to the .full log file while the test was executed.</xs:documentation>
@@ -162,6 +162,16 @@
                                     </xs:restriction>
                                 </xs:simpleType>
                             </xs:element>
+                            <xs:element name="mount-failure" minOccurs="0" maxOccurs="1">
+                                <xs:annotation>
+                                    <xs:documentation xml:lang="en">Kernel log recorded when mount failed.</xs:documentation>
+                                </xs:annotation>
+                                <xs:simpleType>
+                                    <xs:restriction base="pre-string">
+                                        <xs:whiteSpace value="preserve"/>
+                                    </xs:restriction>
+                                </xs:simpleType>
+                            </xs:element>
                         </xs:choice>
                     </xs:sequence>
                     <xs:attribute name="name" type="xs:token" use="required">
diff --git a/tests/selftest/008 b/tests/selftest/008
new file mode 100755
index 00000000000000..e6cc87fec99680
--- /dev/null
+++ b/tests/selftest/008
@@ -0,0 +1,22 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024-2026 Oracle.  All Rights Reserved.
+#
+# FS QA Test 008
+#
+# Test mount failure capture.  Test runners will have to look for the
+# 008.mountfail file in the results directory since ./check handles the
+# preservation.
+#
+. ./common/preamble
+_begin_fstest selftest
+
+_require_command "$WIPEFS_PROG" wipefs
+_require_scratch
+
+$WIPEFS_PROG -a $SCRATCH_DEV
+_scratch_mount &>> $seqres.full
+
+# success, all done
+status=0
+exit
diff --git a/tests/selftest/008.out b/tests/selftest/008.out
new file mode 100644
index 00000000000000..aaff95f3f48372
--- /dev/null
+++ b/tests/selftest/008.out
@@ -0,0 +1 @@
+QA output created by 008
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.