</para>
        </listitem>
       </varlistentry>
+
+      <varlistentry id="guc-io-max-concurrency" xreflabel="io_max_concurrency">
+       <term><varname>io_max_concurrency</varname> (<type>integer</type>)
+       <indexterm>
+        <primary><varname>io_max_concurrency</varname> configuration parameter</primary>
+       </indexterm>
+       </term>
+       <listitem>
+        <para>
+         Controls the maximum number of I/O operations that one process can
+         execute simultaneously.
+        </para>
+        <para>
+         The default setting of <literal>-1</literal> selects a number based
+         on <xref linkend="guc-shared-buffers"/> and the maximum number of
+         processes (<xref linkend="guc-max-connections"/>, <xref
+         linkend="guc-autovacuum-worker-slots"/>, <xref
+         linkend="guc-max-worker-processes"/> and <xref
+         linkend="guc-max-wal-senders"/>), but not more than
+         <literal>64</literal>.
+        </para>
+        <para>
+         This parameter can only be set at server start.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-io-method" xreflabel="io_method">
+       <term><varname>io_method</varname> (<type>enum</type>)
+       <indexterm>
+        <primary><varname>io_method</varname> configuration parameter</primary>
+       </indexterm>
+       </term>
+       <listitem>
+        <para>
+         Selects the method for executing asynchronous I/O.
+         Possible values are:
+         <itemizedlist>
+          <listitem>
+           <para>
+            <literal>sync</literal> (execute asynchronous-eligible I/O synchronously)
+           </para>
+          </listitem>
+         </itemizedlist>
+        </para>
+        <para>
+         This parameter can only be set at server start.
+        </para>
+       </listitem>
+      </varlistentry>
+
      </variablelist>
     </sect2>
 
 
 #include "replication/origin.h"
 #include "replication/snapbuild.h"
 #include "replication/syncrep.h"
+#include "storage/aio_subsys.h"
 #include "storage/condition_variable.h"
 #include "storage/fd.h"
 #include "storage/lmgr.h"
                         RESOURCE_RELEASE_BEFORE_LOCKS,
                         true, true);
 
+   AtEOXact_Aio(true);
+
    /* Check we've released all buffer pins */
    AtEOXact_Buffers(true);
 
                         RESOURCE_RELEASE_BEFORE_LOCKS,
                         true, true);
 
+   AtEOXact_Aio(true);
+
    /* Check we've released all buffer pins */
    AtEOXact_Buffers(true);
 
    pgstat_report_wait_end();
    pgstat_progress_end_command();
 
+   pgaio_error_cleanup();
+
    /* Clean up buffer content locks, too */
    UnlockBuffers();
 
        ResourceOwnerRelease(TopTransactionResourceOwner,
                             RESOURCE_RELEASE_BEFORE_LOCKS,
                             false, true);
+       AtEOXact_Aio(false);
        AtEOXact_Buffers(false);
        AtEOXact_RelationCache(false);
        AtEOXact_TypeCache();
 
    pgstat_report_wait_end();
    pgstat_progress_end_command();
+
+   pgaio_error_cleanup();
+
    UnlockBuffers();
 
    /* Reset WAL record construction state */
                             RESOURCE_RELEASE_BEFORE_LOCKS,
                             false, false);
 
+       AtEOXact_Aio(false);
        AtEOSubXact_RelationCache(false, s->subTransactionId,
                                  s->parent->subTransactionId);
        AtEOSubXact_TypeCache();
 
 #include "postmaster/autovacuum.h"
 #include "postmaster/interrupt.h"
 #include "postmaster/postmaster.h"
+#include "storage/aio_subsys.h"
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
 #include "storage/latch.h"
         */
        LWLockReleaseAll();
        pgstat_report_wait_end();
+       pgaio_error_cleanup();
        UnlockBuffers();
        /* this is probably dead code, but let's be safe: */
        if (AuxProcessResourceOwner)
 
 #include "postmaster/auxprocess.h"
 #include "postmaster/bgwriter.h"
 #include "postmaster/interrupt.h"
+#include "storage/aio_subsys.h"
 #include "storage/buf_internals.h"
 #include "storage/bufmgr.h"
 #include "storage/condition_variable.h"
         */
        LWLockReleaseAll();
        ConditionVariableCancelSleep();
+       pgaio_error_cleanup();
        UnlockBuffers();
        ReleaseAuxProcessResources(false);
        AtEOXact_Buffers(false);
 
 #include "postmaster/bgwriter.h"
 #include "postmaster/interrupt.h"
 #include "replication/syncrep.h"
+#include "storage/aio_subsys.h"
 #include "storage/bufmgr.h"
 #include "storage/condition_variable.h"
 #include "storage/fd.h"
        LWLockReleaseAll();
        ConditionVariableCancelSleep();
        pgstat_report_wait_end();
+       pgaio_error_cleanup();
        UnlockBuffers();
        ReleaseAuxProcessResources(false);
        AtEOXact_Buffers(false);
 
 #include "postmaster/interrupt.h"
 #include "postmaster/pgarch.h"
 #include "storage/condition_variable.h"
+#include "storage/aio_subsys.h"
 #include "storage/fd.h"
 #include "storage/ipc.h"
 #include "storage/latch.h"
        LWLockReleaseAll();
        ConditionVariableCancelSleep();
        pgstat_report_wait_end();
+       pgaio_error_cleanup();
        ReleaseAuxProcessResources(false);
        AtEOXact_Files(false);
        AtEOXact_HashTables(false);
 
 #include "postmaster/interrupt.h"
 #include "postmaster/walsummarizer.h"
 #include "replication/walreceiver.h"
+#include "storage/aio_subsys.h"
 #include "storage/fd.h"
 #include "storage/ipc.h"
 #include "storage/latch.h"
        LWLockReleaseAll();
        ConditionVariableCancelSleep();
        pgstat_report_wait_end();
+       pgaio_error_cleanup();
        ReleaseAuxProcessResources(false);
        AtEOXact_Files(false);
        AtEOXact_HashTables(false);
 
 #include "postmaster/auxprocess.h"
 #include "postmaster/interrupt.h"
 #include "postmaster/walwriter.h"
+#include "storage/aio_subsys.h"
 #include "storage/bufmgr.h"
 #include "storage/condition_variable.h"
 #include "storage/fd.h"
        LWLockReleaseAll();
        ConditionVariableCancelSleep();
        pgstat_report_wait_end();
+       pgaio_error_cleanup();
        UnlockBuffers();
        ReleaseAuxProcessResources(false);
        AtEOXact_Buffers(false);
 
 #include "replication/walsender.h"
 #include "replication/walsender_private.h"
 #include "storage/condition_variable.h"
+#include "storage/aio_subsys.h"
 #include "storage/fd.h"
 #include "storage/ipc.h"
 #include "storage/pmsignal.h"
    LWLockReleaseAll();
    ConditionVariableCancelSleep();
    pgstat_report_wait_end();
+   pgaio_error_cleanup();
 
    if (xlogreader != NULL && xlogreader->seg.ws_file >= 0)
        wal_segment_close(xlogreader);
 
 include $(top_builddir)/src/Makefile.global
 
 OBJS = \
+   aio.o \
+   aio_init.o \
    read_stream.o
 
 include $(top_srcdir)/src/backend/common.mk
 
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * aio.c
+ *    AIO - Core Logic
+ *
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *    src/backend/storage/aio/aio.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "lib/ilist.h"
+#include "storage/aio.h"
+#include "storage/aio_subsys.h"
+#include "utils/guc.h"
+#include "utils/guc_hooks.h"
+
+
+/* Options for io_method. */
+const struct config_enum_entry io_method_options[] = {
+   {"sync", IOMETHOD_SYNC, false},
+   {NULL, 0, false}
+};
+
+/* GUCs */
+int            io_method = DEFAULT_IO_METHOD;
+int            io_max_concurrency = -1;
+
+
+
+/*
+ * Release IO handle during resource owner cleanup.
+ */
+void
+pgaio_io_release_resowner(dlist_node *ioh_node, bool on_error)
+{
+}
+
+/*
+ * Perform AIO related cleanup after an error.
+ *
+ * This should be called early in the error recovery paths, as later steps may
+ * need to issue AIO (e.g. to record a transaction abort WAL record).
+ */
+void
+pgaio_error_cleanup(void)
+{
+}
+
+/*
+ * Perform AIO related checks at (sub-)transactional boundaries.
+ *
+ * This should be called late during (sub-)transactional commit/abort, after
+ * all steps that might need to perform AIO, so that we can verify that the
+ * AIO subsystem is in a valid state at the end of a transaction.
+ */
+void
+AtEOXact_Aio(bool is_commit)
+{
+}
+
+void
+assign_io_method(int newval, void *extra)
+{
+}
+
+bool
+check_io_max_concurrency(int *newval, void **extra, GucSource source)
+{
+   if (*newval == -1)
+   {
+       /*
+        * Auto-tuning will be applied later during startup, as auto-tuning
+        * depends on the value of various GUCs.
+        */
+       return true;
+   }
+   else if (*newval == 0)
+   {
+       GUC_check_errdetail("Only -1 or values bigger than 0 are valid.");
+       return false;
+   }
+
+   return true;
+}
 
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * aio_init.c
+ *    AIO - Subsystem Initialization
+ *
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *    src/backend/storage/aio/aio_init.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "storage/aio_subsys.h"
+
+
+
+Size
+AioShmemSize(void)
+{
+   Size        sz = 0;
+
+   return sz;
+}
+
+void
+AioShmemInit(void)
+{
+}
+
+void
+pgaio_init_backend(void)
+{
+}
 
 # Copyright (c) 2024-2025, PostgreSQL Global Development Group
 
 backend_sources += files(
+  'aio.c',
+  'aio_init.c',
   'read_stream.c',
 )
 
 #include "replication/slotsync.h"
 #include "replication/walreceiver.h"
 #include "replication/walsender.h"
+#include "storage/aio_subsys.h"
 #include "storage/bufmgr.h"
 #include "storage/dsm.h"
 #include "storage/dsm_registry.h"
    size = add_size(size, WaitEventCustomShmemSize());
    size = add_size(size, InjectionPointShmemSize());
    size = add_size(size, SlotSyncShmemSize());
+   size = add_size(size, AioShmemSize());
 
    /* include additional requested shmem from preload libraries */
    size = add_size(size, total_addin_request);
    StatsShmemInit();
    WaitEventCustomShmemInit();
    InjectionPointShmemInit();
+   AioShmemInit();
 }
 
 /*
 
 #include "replication/slot.h"
 #include "replication/slotsync.h"
 #include "replication/walsender.h"
+#include "storage/aio_subsys.h"
 #include "storage/bufmgr.h"
 #include "storage/fd.h"
 #include "storage/ipc.h"
     */
    pgstat_initialize();
 
+   /*
+    * Initialize AIO before infrastructure that might need to actually
+    * execute AIO.
+    */
+   pgaio_init_backend();
+
    /* Do local initialization of storage and buffer managers */
    InitSync();
    smgrinit();
 
 #include "replication/slot.h"
 #include "replication/slotsync.h"
 #include "replication/syncrep.h"
+#include "storage/aio.h"
 #include "storage/bufmgr.h"
 #include "storage/bufpage.h"
 #include "storage/large_object.h"
        NULL, NULL, NULL
    },
 
+   {
+       {"io_max_concurrency",
+           PGC_POSTMASTER,
+           RESOURCES_IO,
+           gettext_noop("Max number of IOs that one process can execute simultaneously."),
+           NULL,
+       },
+       &io_max_concurrency,
+       -1, -1, 1024,
+       check_io_max_concurrency, NULL, NULL
+   },
+
    {
        {"backend_flush_after", PGC_USERSET, RESOURCES_IO,
            gettext_noop("Number of pages after which previously performed writes are flushed to disk."),
        NULL, NULL, NULL
    },
 
+   {
+       {"io_method", PGC_POSTMASTER, RESOURCES_IO,
+           gettext_noop("Selects the method for executing asynchronous I/O."),
+           NULL
+       },
+       &io_method,
+       DEFAULT_IO_METHOD, io_method_options,
+       NULL, assign_io_method, NULL
+   },
+
    /* End-of-list marker */
    {
        {NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL, NULL
 
 #maintenance_io_concurrency = 10   # 1-1000; 0 disables prefetching
 #io_combine_limit = 128kB      # usually 1-32 blocks (depends on OS)
 
+#io_method = sync          # sync (change requires restart)
+#io_max_concurrency = -1       # Max number of IOs that one process
+                   # can execute simultaneously
+                   # -1 sets based on shared_buffers
+                   # (change requires restart)
+
 # - Worker Processes -
 
 #max_worker_processes = 8      # (change requires restart)
 
 
 #include "common/hashfn.h"
 #include "common/int.h"
+#include "lib/ilist.h"
+#include "storage/aio.h"
 #include "storage/ipc.h"
 #include "storage/predicate.h"
 #include "storage/proc.h"
 
    /* The local locks cache. */
    LOCALLOCK  *locks[MAX_RESOWNER_LOCKS];  /* list of owned locks */
+
+   /*
+    * AIO handles need be registered in critical sections and therefore
+    * cannot use the normal ResourceElem mechanism.
+    */
+   dlist_head  aio_handles;
 };
 
 
        parent->firstchild = owner;
    }
 
+   dlist_init(&owner->aio_handles);
+
    return owner;
 }
 
         * so issue warnings.  In the abort case, just clean up quietly.
         */
        ResourceOwnerReleaseAll(owner, phase, isCommit);
+
+       while (!dlist_is_empty(&owner->aio_handles))
+       {
+           dlist_node *node = dlist_head_node(&owner->aio_handles);
+
+           pgaio_io_release_resowner(node, !isCommit);
+       }
    }
    else if (phase == RESOURCE_RELEASE_LOCKS)
    {
    elog(ERROR, "lock reference %p is not owned by resource owner %s",
         locallock, owner->name);
 }
+
+void
+ResourceOwnerRememberAioHandle(ResourceOwner owner, struct dlist_node *ioh_node)
+{
+   dlist_push_tail(&owner->aio_handles, ioh_node);
+}
+
+void
+ResourceOwnerForgetAioHandle(ResourceOwner owner, struct dlist_node *ioh_node)
+{
+   dlist_delete_from(&owner->aio_handles, ioh_node);
+}
 
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * aio.h
+ *    Main AIO interface
+ *
+ *
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/storage/aio.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef AIO_H
+#define AIO_H
+
+
+
+/* Enum for io_method GUC. */
+typedef enum IoMethod
+{
+   IOMETHOD_SYNC = 0,
+} IoMethod;
+
+/* We'll default to synchronous execution. */
+#define DEFAULT_IO_METHOD IOMETHOD_SYNC
+
+
+struct dlist_node;
+extern void pgaio_io_release_resowner(struct dlist_node *ioh_node, bool on_error);
+
+
+/* GUCs */
+extern PGDLLIMPORT int io_method;
+extern PGDLLIMPORT int io_max_concurrency;
+
+
+#endif                         /* AIO_H */
 
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * aio_subsys.h
+ *    Interaction with AIO as a subsystem, rather than actually issuing AIO
+ *
+ * This header is for AIO related functionality that's being called by files
+ * that don't perform AIO, but interact with the AIO subsystem in some
+ * form. E.g. postmaster.c and shared memory initialization need to initialize
+ * AIO but don't perform AIO.
+ *
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/storage/aio_subsys.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef AIO_SUBSYS_H
+#define AIO_SUBSYS_H
+
+
+/* aio_init.c */
+extern Size AioShmemSize(void);
+extern void AioShmemInit(void);
+
+extern void pgaio_init_backend(void);
+
+
+/* aio.c */
+extern void pgaio_error_cleanup(void);
+extern void AtEOXact_Aio(bool is_commit);
+
+#endif                         /* AIO_SUBSYS_H */
 
  */
 extern PGDLLIMPORT const struct config_enum_entry archive_mode_options[];
 extern PGDLLIMPORT const struct config_enum_entry dynamic_shared_memory_options[];
+extern PGDLLIMPORT const struct config_enum_entry io_method_options[];
 extern PGDLLIMPORT const struct config_enum_entry recovery_target_action_options[];
 extern PGDLLIMPORT const struct config_enum_entry wal_level_options[];
 extern PGDLLIMPORT const struct config_enum_entry wal_sync_method_options[];
 
 extern bool check_effective_io_concurrency(int *newval, void **extra,
                                           GucSource source);
 extern bool check_huge_page_size(int *newval, void **extra, GucSource source);
+extern void assign_io_method(int newval, void *extra);
+extern bool check_io_max_concurrency(int *newval, void **extra, GucSource source);
 extern const char *show_in_hot_standby(void);
 extern bool check_locale_messages(char **newval, void **extra, GucSource source);
 extern void assign_locale_messages(const char *newval, void *extra);
 
 extern void ResourceOwnerRememberLock(ResourceOwner owner, struct LOCALLOCK *locallock);
 extern void ResourceOwnerForgetLock(ResourceOwner owner, struct LOCALLOCK *locallock);
 
+/* special support for AIO */
+struct dlist_node;
+extern void ResourceOwnerRememberAioHandle(ResourceOwner owner, struct dlist_node *ioh_node);
+extern void ResourceOwnerForgetAioHandle(ResourceOwner owner, struct dlist_node *ioh_node);
+
 #endif                         /* RESOWNER_H */
 
 InvalMessageArray
 InvalidationInfo
 InvalidationMsgsGroup
+IoMethod
 IpcMemoryId
 IpcMemoryKey
 IpcMemoryState