From c8ecdff54fcdbd2cf89ca7888f641db369f207ce Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Thu, 12 Dec 2024 12:57:12 -0500
Subject: [PATCH] md: Report more detail when encountering ENOSPC during
 extension

Author:
Reviewed-by:
Discussion: https://postgr.es/m/
Backpatch:
---
 meson.build                   |  1 +
 configure.ac                  |  1 +
 src/include/pg_config.h.in    |  3 ++
 src/backend/storage/smgr/md.c | 63 +++++++++++++++++++++++++++++++----
 configure                     |  2 +-
 5 files changed, 63 insertions(+), 7 deletions(-)

diff --git a/meson.build b/meson.build
index 4e59feb91da..e644db41ef9 100644
--- a/meson.build
+++ b/meson.build
@@ -2269,6 +2269,7 @@ header_checks = [
   'sys/procctl.h',
   'sys/signalfd.h',
   'sys/ucred.h',
+  'sys/vfs.h',
   'termios.h',
   'ucred.h',
 ]
diff --git a/configure.ac b/configure.ac
index 23add80d8fd..0984949a3b9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1512,6 +1512,7 @@ AC_CHECK_HEADERS(m4_normalize([
 	sys/procctl.h
 	sys/signalfd.h
 	sys/ucred.h
+	sys/vfs.h
 	termios.h
 	ucred.h
 ]))
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index ce3063b2b22..626de538821 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -478,6 +478,9 @@
 /* Define to 1 if you have the <sys/ucred.h> header file. */
 #undef HAVE_SYS_UCRED_H
 
+/* Define to 1 if you have the <sys/vfs.h> header file. */
+#undef HAVE_SYS_VFS_H
+
 /* Define to 1 if you have the <termios.h> header file. */
 #undef HAVE_TERMIOS_H
 
diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c
index fdecbad1709..67c42a69c11 100644
--- a/src/backend/storage/smgr/md.c
+++ b/src/backend/storage/smgr/md.c
@@ -24,6 +24,9 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/file.h>
+#ifdef HAVE_SYS_VFS_H
+#include <sys/vfs.h>
+#endif
 
 #include "access/xlog.h"
 #include "access/xlogutils.h"
@@ -449,6 +452,37 @@ mdunlinkfork(RelFileLocatorBackend rlocator, ForkNumber forknum, bool isRedo)
 	pfree(path);
 }
 
+static void
+report_disk_space(const char *reason, const char *path)
+{
+	/*
+	 * I'm sure there's a way to do this on other OSs too, but for the
+	 * debugging here this should be sufficient.
+	 */
+#ifdef HAVE_SYS_VFS_H
+	int			saved_errno = errno;
+	struct statfs sf;
+	int			ret;
+
+	ret = statfs(path, &sf);
+
+	if (ret != 0)
+		elog(WARNING, "%s: statfs failed: %m", reason);
+	else
+		elog(LOG, "%s: free space for filesystem containing \"%s\" "
+			 "f_blocks: %llu, f_bfree: %llu, f_bavail: %llu "
+			 "f_files: %llu, f_ffree: %llu",
+			 reason, path,
+			 (long long unsigned) sf.f_blocks,
+			 (long long unsigned) sf.f_bfree,
+			 (long long unsigned) sf.f_bavail,
+			 (long long unsigned) sf.f_files,
+			 (long long unsigned) sf.f_ffree);
+
+	errno = saved_errno;
+#endif
+}
+
 /*
  * mdextend() -- Add a block to the specified relation.
  *
@@ -496,11 +530,16 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 
 	if ((nbytes = FileWrite(v->mdfd_vfd, buffer, BLCKSZ, seekpos, WAIT_EVENT_DATA_FILE_EXTEND)) != BLCKSZ)
 	{
+		if (errno == ENOSPC)
+			report_disk_space("mdextend failing with ENOSPC",
+							  FilePathName(v->mdfd_vfd));
+
 		if (nbytes < 0)
 			ereport(ERROR,
 					(errcode_for_file_access(),
-					 errmsg("could not extend file \"%s\": %m",
-							FilePathName(v->mdfd_vfd)),
+					 errmsg("could not extend file \"%s\" from %u to %u blocks: %m",
+							FilePathName(v->mdfd_vfd),
+							blocknum, blocknum + 1),
 					 errhint("Check free disk space.")));
 		/* short write: complain appropriately */
 		ereport(ERROR,
@@ -586,10 +625,15 @@ mdzeroextend(SMgrRelation reln, ForkNumber forknum,
 								WAIT_EVENT_DATA_FILE_EXTEND);
 			if (ret != 0)
 			{
+				if (errno == ENOSPC)
+					report_disk_space("mdzeroextend FileFallocate failing with ENOSPC",
+									  FilePathName(v->mdfd_vfd));
+
 				ereport(ERROR,
 						errcode_for_file_access(),
-						errmsg("could not extend file \"%s\" with FileFallocate(): %m",
-							   FilePathName(v->mdfd_vfd)),
+						errmsg("could not extend file \"%s\" by %u blocks, from %u to %u, using FileFallocate(): %m",
+							   FilePathName(v->mdfd_vfd),
+							   numblocks, segstartblock, segstartblock+numblocks),
 						errhint("Check free disk space."));
 			}
 		}
@@ -608,11 +652,18 @@ mdzeroextend(SMgrRelation reln, ForkNumber forknum,
 						   seekpos, (off_t) BLCKSZ * numblocks,
 						   WAIT_EVENT_DATA_FILE_EXTEND);
 			if (ret < 0)
+			{
+				if (errno == ENOSPC)
+					report_disk_space("mdzeroextend FileZero failing with ENOSPC",
+									  FilePathName(v->mdfd_vfd));
+
 				ereport(ERROR,
 						errcode_for_file_access(),
-						errmsg("could not extend file \"%s\": %m",
-							   FilePathName(v->mdfd_vfd)),
+						errmsg("could not extend file \"%s\" by %u blocks, from %u to %u, using FileZero(): %m",
+							   FilePathName(v->mdfd_vfd),
+							   numblocks, segstartblock, segstartblock+numblocks),
 						errhint("Check free disk space."));
+			}
 		}
 
 		if (!skipFsync && !SmgrIsTemp(reln))
diff --git a/configure b/configure
index 8c2ab3a1973..f62f4f6d3ab 100755
--- a/configure
+++ b/configure
@@ -13768,7 +13768,7 @@ fi
 ## Header files
 ##
 
-for ac_header in atomic.h copyfile.h execinfo.h getopt.h ifaddrs.h langinfo.h mbarrier.h sys/epoll.h sys/event.h sys/personality.h sys/prctl.h sys/procctl.h sys/signalfd.h sys/ucred.h termios.h ucred.h
+for ac_header in atomic.h copyfile.h execinfo.h getopt.h ifaddrs.h langinfo.h mbarrier.h sys/epoll.h sys/event.h sys/personality.h sys/prctl.h sys/procctl.h sys/signalfd.h sys/ucred.h sys/vfs.h termios.h ucred.h
 do :
   as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
 ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
-- 
2.45.2.746.g06e570c0df.dirty

