From 4c1437b6e7ffa37880ec1b7c1a0ac49fc8ff16d5 Mon Sep 17 00:00:00 2001
From: Nazir Bilal Yavuz <byavuz81@gmail.com>
Date: Wed, 9 Sep 2026 15:39:57 +0300
Subject: [PATCH v2 3/8] aio: Add fsync support

The AIO subsystem currently supports only reads and writes (writes are
not used yet). Add PGAIO_OP_FSYNC so callers can submit fsync() and
fdatasync() operations through AIO. This allows callers such as the
checkpointer to keep multiple syncs in flight instead of waiting for
each one in turn.

Let callers select the wait event because fsync targets can represent
different kinds of files. The process that performs the operation
reports that event; io_uring uses the generic AIO wait events because
the kernel performs the operation. (Also see [1])

No callers are converted in this commit; subsequent commits do that.

[1] https://postgr.es/m/CAN55FZ0Rp+94rdQ-zeTX0sF9H1e4uWZxH8r1cVXZX0swfqww3g@mail.gmail.com

Discussion: https://postgr.es/m/CAN55FZ0vLWJQNB%3DHuHXG2wabFjXJd6OWTa3%3DkRzwObdZD9poHQ%40mail.gmail.com
---
 src/backend/storage/aio/aio_funcs.c       |  4 +++
 src/backend/storage/aio/aio_io.c          | 31 +++++++++++++++++++++++
 src/backend/storage/aio/method_io_uring.c |  6 +++++
 src/backend/storage/smgr/smgr.c           |  3 +++
 src/include/storage/aio.h                 | 14 ++++++++--
 5 files changed, 56 insertions(+), 2 deletions(-)

diff --git a/src/backend/storage/aio/aio_funcs.c b/src/backend/storage/aio/aio_funcs.c
index bcdd82318f7..2719556e858 100644
--- a/src/backend/storage/aio/aio_funcs.c
+++ b/src/backend/storage/aio/aio_funcs.c
@@ -191,6 +191,10 @@ retry:
 				values[6] =
 					Int64GetDatum(iov_byte_length(iov_copy, ioh_copy.op_data.write.iov_length));
 				break;
+			case PGAIO_OP_FSYNC:
+				nulls[5] = true;
+				nulls[6] = true;
+				break;
 		}
 
 		/* column: IO's target */
diff --git a/src/backend/storage/aio/aio_io.c b/src/backend/storage/aio/aio_io.c
index 132868130e7..0525643cc2b 100644
--- a/src/backend/storage/aio/aio_io.c
+++ b/src/backend/storage/aio/aio_io.c
@@ -100,6 +100,23 @@ pgaio_io_start_writev(PgAioHandle *ioh,
 	pgaio_io_stage(ioh, PGAIO_OP_WRITEV);
 }
 
+void
+pgaio_io_start_fsync(PgAioHandle *ioh,
+					 int fd, bool datasync, uint32 wait_event_info)
+{
+	pgaio_io_before_start(ioh);
+
+	ioh->op_data.fsync.fd = fd;
+	ioh->op_data.fsync.datasync = datasync;
+	ioh->op_data.fsync.wait_event_info = wait_event_info;
+
+	/* Let the synchronous implementation skip the syscall when fsync is off. */
+	if (!enableFsync)
+		pgaio_io_set_flag(ioh, PGAIO_HF_SYNCHRONOUS);
+
+	pgaio_io_stage(ioh, PGAIO_OP_FSYNC);
+}
+
 
 
 /* --------------------------------------------------------------------------------
@@ -137,6 +154,14 @@ pgaio_io_perform_synchronously(PgAioHandle *ioh)
 								ioh->op_data.write.offset);
 			pgstat_report_wait_end();
 			break;
+		case PGAIO_OP_FSYNC:
+			pgstat_report_wait_start(ioh->op_data.fsync.wait_event_info);
+			if (ioh->op_data.fsync.datasync)
+				result = pg_fdatasync(ioh->op_data.fsync.fd);
+			else
+				result = pg_fsync(ioh->op_data.fsync.fd);
+			pgstat_report_wait_end();
+			break;
 		case PGAIO_OP_INVALID:
 			elog(ERROR, "trying to execute invalid IO operation");
 	}
@@ -189,6 +214,8 @@ pgaio_io_get_op_name(PgAioHandle *ioh)
 			return "readv";
 		case PGAIO_OP_WRITEV:
 			return "writev";
+		case PGAIO_OP_FSYNC:
+			return "fsync";
 	}
 
 	return NULL;				/* silence compiler */
@@ -209,6 +236,8 @@ pgaio_io_uses_fd(PgAioHandle *ioh, int fd)
 			return ioh->op_data.read.fd == fd;
 		case PGAIO_OP_WRITEV:
 			return ioh->op_data.write.fd == fd;
+		case PGAIO_OP_FSYNC:
+			return ioh->op_data.fsync.fd == fd;
 		case PGAIO_OP_INVALID:
 			return false;
 	}
@@ -233,6 +262,8 @@ pgaio_io_get_iovec_length(PgAioHandle *ioh, struct iovec **iov)
 			return ioh->op_data.read.iov_length;
 		case PGAIO_OP_WRITEV:
 			return ioh->op_data.write.iov_length;
+		case PGAIO_OP_FSYNC:
+			return 0;
 		default:
 			pg_unreachable();
 			return 0;
diff --git a/src/backend/storage/aio/method_io_uring.c b/src/backend/storage/aio/method_io_uring.c
index 3ffe5061a20..675a91fb5f7 100644
--- a/src/backend/storage/aio/method_io_uring.c
+++ b/src/backend/storage/aio/method_io_uring.c
@@ -802,6 +802,12 @@ pgaio_uring_sq_from_io(PgAioHandle *ioh, struct io_uring_sqe *sqe)
 
 			break;
 
+		case PGAIO_OP_FSYNC:
+			io_uring_prep_fsync(sqe,
+								ioh->op_data.fsync.fd,
+								ioh->op_data.fsync.datasync ? IORING_FSYNC_DATASYNC : 0);
+			break;
+
 		case PGAIO_OP_INVALID:
 			elog(ERROR, "trying to prepare invalid IO operation for execution");
 	}
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index 5391640d861..69e61ea1661 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -1094,6 +1094,9 @@ smgr_aio_reopen(PgAioHandle *ioh)
 			od->write.fd = smgrfd(reln, sd->smgr.forkNum, sd->smgr.blockNum, &off);
 			Assert(off == od->write.offset);
 			break;
+		case PGAIO_OP_FSYNC:
+			od->fsync.fd = smgrfd(reln, sd->smgr.forkNum, sd->smgr.blockNum, &off);
+			break;
 	}
 }
 
diff --git a/src/include/storage/aio.h b/src/include/storage/aio.h
index ec543b78409..a1d5f49e51a 100644
--- a/src/include/storage/aio.h
+++ b/src/include/storage/aio.h
@@ -91,10 +91,10 @@ typedef enum PgAioOp
 
 	PGAIO_OP_READV,
 	PGAIO_OP_WRITEV,
+	PGAIO_OP_FSYNC,
 
 	/**
 	 * In the near term we'll need at least:
-	 * - fsync / fdatasync
 	 * - flush_range
 	 *
 	 * Eventually we'll additionally want at least:
@@ -104,7 +104,7 @@ typedef enum PgAioOp
 	 **/
 } PgAioOp;
 
-#define PGAIO_OP_COUNT	(PGAIO_OP_WRITEV + 1)
+#define PGAIO_OP_COUNT	(PGAIO_OP_FSYNC + 1)
 
 
 /*
@@ -146,6 +146,13 @@ typedef union
 		uint16		iov_length;
 		uint64		offset;
 	}			write;
+
+	struct
+	{
+		int			fd;
+		bool		datasync;
+		uint32		wait_event_info;
+	}			fsync;
 } PgAioOpData;
 
 
@@ -300,6 +307,9 @@ extern void pgaio_io_start_readv(PgAioHandle *ioh,
 								 int fd, int iovcnt, uint64 offset);
 extern void pgaio_io_start_writev(PgAioHandle *ioh,
 								  int fd, int iovcnt, uint64 offset);
+extern void pgaio_io_start_fsync(PgAioHandle *ioh, int fd, bool datasync,
+								 uint32 wait_event_info);
+
 
 /* functions in aio_target.c */
 extern void pgaio_io_set_target(PgAioHandle *ioh, PgAioTargetID targetid);
-- 
2.47.3

