From 4c1437b6e7ffa37880ec1b7c1a0ac49fc8ff16d5 Mon Sep 17 00:00:00 2001 From: Nazir Bilal Yavuz 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