Introduce PG_IO_ALIGN_SIZE and align all I/O buffers.
authorThomas Munro <[email protected]>
Fri, 7 Apr 2023 22:38:09 +0000 (10:38 +1200)
committerThomas Munro <[email protected]>
Sat, 8 Apr 2023 04:34:50 +0000 (16:34 +1200)
In order to have the option to use O_DIRECT/FILE_FLAG_NO_BUFFERING in a
later commit, we need the addresses of user space buffers to be well
aligned.  The exact requirements vary by OS and file system (typically
sectors and/or memory pages).  The address alignment size is set to
4096, which is enough for currently known systems: it matches modern
sectors and common memory page size.  There is no standard governing
O_DIRECT's requirements so we might eventually have to reconsider this
with more information from the field or future systems.

Aligning I/O buffers on memory pages is also known to improve regular
buffered I/O performance.

Three classes of I/O buffers for regular data pages are adjusted:
(1) Heap buffers are now allocated with the new palloc_aligned() or
MemoryContextAllocAligned() functions introduced by commit 439f6175.
(2) Stack buffers now use a new struct PGIOAlignedBlock to respect
PG_IO_ALIGN_SIZE, if possible with this compiler.  (3) The buffer
pool is also aligned in shared memory.

WAL buffers were already aligned on XLOG_BLCKSZ.  It's possible for
XLOG_BLCKSZ to be configured smaller than PG_IO_ALIGNED_SIZE and thus
for O_DIRECT WAL writes to fail to be well aligned, but that's a
pre-existing condition and will be addressed by a later commit.

BufFiles are not yet addressed (there's no current plan to use O_DIRECT
for those, but they could potentially get some incidental speedup even
in plain buffered I/O operations through better alignment).

If we can't align stack objects suitably using the compiler extensions
we know about, we disable the use of O_DIRECT by setting PG_O_DIRECT to
0.  This avoids the need to consider systems that have O_DIRECT but
can't align stack objects the way we want; such systems could in theory
be supported with more work but we don't currently know of any such
machines, so it's easier to pretend there is no O_DIRECT support
instead.  That's an existing and tested class of system.

Add assertions that all buffers passed into smgrread(), smgrwrite() and
smgrextend() are correctly aligned, unless PG_O_DIRECT is 0 (= stack
alignment tricks may be unavailable) or the block size has been set too
small to allow arrays of buffers to be all aligned.

Author: Thomas Munro <[email protected]>
Author: Andres Freund <[email protected]>
Reviewed-by: Justin Pryzby <[email protected]>
Discussion: https://postgr.es/m/CA+hUKGK1X532hYqJ_MzFWt0n1zt8trz980D79WbjwnT-yYLZpg@mail.gmail.com

26 files changed:
contrib/bloom/blinsert.c
contrib/pg_prewarm/pg_prewarm.c
src/backend/access/gist/gistbuild.c
src/backend/access/hash/hashpage.c
src/backend/access/heap/rewriteheap.c
src/backend/access/nbtree/nbtree.c
src/backend/access/nbtree/nbtsort.c
src/backend/access/spgist/spginsert.c
src/backend/access/transam/generic_xlog.c
src/backend/access/transam/xlog.c
src/backend/catalog/storage.c
src/backend/storage/buffer/buf_init.c
src/backend/storage/buffer/bufmgr.c
src/backend/storage/buffer/localbuf.c
src/backend/storage/file/buffile.c
src/backend/storage/page/bufpage.c
src/backend/storage/smgr/md.c
src/backend/utils/sort/logtape.c
src/bin/pg_checksums/pg_checksums.c
src/bin/pg_rewind/local_source.c
src/bin/pg_upgrade/file.c
src/common/file_utils.c
src/include/c.h
src/include/pg_config_manual.h
src/include/storage/fd.h
src/tools/pgindent/typedefs.list

index dcd81208953be4f42c55944421aced905b293c67..b42b9e6c41f5d35130268192a09879c0ffd71cb8 100644 (file)
@@ -166,7 +166,7 @@ blbuildempty(Relation index)
    Page        metapage;
 
    /* Construct metapage. */
-   metapage = (Page) palloc(BLCKSZ);
+   metapage = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
    BloomFillMetapage(index, metapage);
 
    /*
index 54209924aedc7fbd64e6898283d281cbc3537e86..e464d0d4d2bd14e986bdfb4219348cb1916b7961 100644 (file)
@@ -36,7 +36,7 @@ typedef enum
    PREWARM_BUFFER
 } PrewarmType;
 
-static PGAlignedBlock blockbuffer;
+static PGIOAlignedBlock blockbuffer;
 
 /*
  * pg_prewarm(regclass, mode text, fork text,
index d2f8da5b0261797b2ac7a711652529e26d9f72b2..5e0c1447f9243c929dcd1bc3e72c93b514f38177 100644 (file)
@@ -415,7 +415,7 @@ gist_indexsortbuild(GISTBuildState *state)
     * Write an empty page as a placeholder for the root page. It will be
     * replaced with the real root page at the end.
     */
-   page = palloc0(BLCKSZ);
+   page = palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, MCXT_ALLOC_ZERO);
    smgrextend(RelationGetSmgr(state->indexrel), MAIN_FORKNUM, GIST_ROOT_BLKNO,
               page, true);
    state->pages_allocated++;
@@ -509,7 +509,8 @@ gist_indexsortbuild_levelstate_add(GISTBuildState *state,
            levelstate->current_page++;
 
        if (levelstate->pages[levelstate->current_page] == NULL)
-           levelstate->pages[levelstate->current_page] = palloc(BLCKSZ);
+           levelstate->pages[levelstate->current_page] =
+               palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
 
        newPage = levelstate->pages[levelstate->current_page];
        gistinitpage(newPage, old_page_flags);
@@ -579,7 +580,7 @@ gist_indexsortbuild_levelstate_flush(GISTBuildState *state,
 
        /* Create page and copy data */
        data = (char *) (dist->list);
-       target = palloc0(BLCKSZ);
+       target = palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, MCXT_ALLOC_ZERO);
        gistinitpage(target, isleaf ? F_LEAF : 0);
        for (int i = 0; i < dist->block.num; i++)
        {
@@ -630,7 +631,7 @@ gist_indexsortbuild_levelstate_flush(GISTBuildState *state,
        if (parent == NULL)
        {
            parent = palloc0(sizeof(GistSortedBuildLevelState));
-           parent->pages[0] = (Page) palloc(BLCKSZ);
+           parent->pages[0] = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
            parent->parent = NULL;
            gistinitpage(parent->pages[0], 0);
 
index 6d8af422609afb9cc666e9cb373c6cd94f4505eb..af3a1542667525f6c6e45e5b23ae0960dec496c0 100644 (file)
@@ -992,7 +992,7 @@ static bool
 _hash_alloc_buckets(Relation rel, BlockNumber firstblock, uint32 nblocks)
 {
    BlockNumber lastblock;
-   PGAlignedBlock zerobuf;
+   PGIOAlignedBlock zerobuf;
    Page        page;
    HashPageOpaque ovflopaque;
 
index ae0282a70ee800be8da273abe347877b0829adf4..424958912c75bfb45869a2aa6ad08b593447b2c6 100644 (file)
@@ -255,7 +255,7 @@ begin_heap_rewrite(Relation old_heap, Relation new_heap, TransactionId oldest_xm
 
    state->rs_old_rel = old_heap;
    state->rs_new_rel = new_heap;
-   state->rs_buffer = (Page) palloc(BLCKSZ);
+   state->rs_buffer = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
    /* new_heap needn't be empty, just locked */
    state->rs_blockno = RelationGetNumberOfBlocks(new_heap);
    state->rs_buffer_valid = false;
index 992f84834f836a51b7e330c4ef0b015f9ed5b81b..2df8849858e6e8a903d405384ece4e97e7369b0e 100644 (file)
@@ -154,7 +154,7 @@ btbuildempty(Relation index)
    Page        metapage;
 
    /* Construct metapage. */
-   metapage = (Page) palloc(BLCKSZ);
+   metapage = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
    _bt_initmetapage(metapage, P_NONE, 0, _bt_allequalimage(index, false));
 
    /*
index 1207a496895852cea338ce15ba1c0b70485cb2a9..6ad3f3c54d5fb861ca34647f02a5ee3dc61bff84 100644 (file)
@@ -619,7 +619,7 @@ _bt_blnewpage(uint32 level)
    Page        page;
    BTPageOpaque opaque;
 
-   page = (Page) palloc(BLCKSZ);
+   page = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
 
    /* Zero the page and set up standard page header info */
    _bt_pageinit(page, BLCKSZ);
@@ -660,7 +660,9 @@ _bt_blwritepage(BTWriteState *wstate, Page page, BlockNumber blkno)
    while (blkno > wstate->btws_pages_written)
    {
        if (!wstate->btws_zeropage)
-           wstate->btws_zeropage = (Page) palloc0(BLCKSZ);
+           wstate->btws_zeropage = (Page) palloc_aligned(BLCKSZ,
+                                                         PG_IO_ALIGN_SIZE,
+                                                         MCXT_ALLOC_ZERO);
        /* don't set checksum for all-zero page */
        smgrextend(RelationGetSmgr(wstate->index), MAIN_FORKNUM,
                   wstate->btws_pages_written++,
@@ -1170,7 +1172,7 @@ _bt_uppershutdown(BTWriteState *wstate, BTPageState *state)
     * set to point to "P_NONE").  This changes the index to the "valid" state
     * by filling in a valid magic number in the metapage.
     */
-   metapage = (Page) palloc(BLCKSZ);
+   metapage = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
    _bt_initmetapage(metapage, rootblkno, rootlevel,
                     wstate->inskey->allequalimage);
    _bt_blwritepage(wstate, metapage, BTREE_METAPAGE);
index 718a88335d0ec84a240303b326ae9272a3447325..72d2e1551cd4fd0c749e3eb6afbf436b9a5409f1 100644 (file)
@@ -158,7 +158,7 @@ spgbuildempty(Relation index)
    Page        page;
 
    /* Construct metapage. */
-   page = (Page) palloc(BLCKSZ);
+   page = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
    SpGistInitMetapage(page);
 
    /*
index 9f67d1c1cd583fec502b70175c1869bb57ecaa95..6c68191ca6241a05fe6fc9bd8d5afbabe1dec9bd 100644 (file)
@@ -58,14 +58,17 @@ typedef struct
    char        delta[MAX_DELTA_SIZE];  /* delta between page images */
 } PageData;
 
-/* State of generic xlog record construction */
+/*
+ * State of generic xlog record construction.  Must be allocated at an I/O
+ * aligned address.
+ */
 struct GenericXLogState
 {
+   /* Page images (properly aligned, must be first) */
+   PGIOAlignedBlock images[MAX_GENERIC_XLOG_PAGES];
    /* Info about each page, see above */
    PageData    pages[MAX_GENERIC_XLOG_PAGES];
    bool        isLogged;
-   /* Page images (properly aligned) */
-   PGAlignedBlock images[MAX_GENERIC_XLOG_PAGES];
 };
 
 static void writeFragment(PageData *pageData, OffsetNumber offset,
@@ -269,7 +272,9 @@ GenericXLogStart(Relation relation)
    GenericXLogState *state;
    int         i;
 
-   state = (GenericXLogState *) palloc(sizeof(GenericXLogState));
+   state = (GenericXLogState *) palloc_aligned(sizeof(GenericXLogState),
+                                               PG_IO_ALIGN_SIZE,
+                                               0);
    state->isLogged = RelationNeedsWAL(relation);
 
    for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
index 46821ad60564f1f05e18d3b194d138d49a8fc28f..a5c74fdab8c6285d70a1f37b84c55df788edf47a 100644 (file)
@@ -4506,7 +4506,7 @@ XLOGShmemSize(void)
    /* xlblocks array */
    size = add_size(size, mul_size(sizeof(XLogRecPtr), XLOGbuffers));
    /* extra alignment padding for XLOG I/O buffers */
-   size = add_size(size, XLOG_BLCKSZ);
+   size = add_size(size, Max(XLOG_BLCKSZ, PG_IO_ALIGN_SIZE));
    /* and the buffers themselves */
    size = add_size(size, mul_size(XLOG_BLCKSZ, XLOGbuffers));
 
index af1491aa1d1e5486c3da23fad2ec3c4063bf7fdc..2add0534891ba515954b916d9cf18a36f44f437e 100644 (file)
@@ -451,7 +451,7 @@ void
 RelationCopyStorage(SMgrRelation src, SMgrRelation dst,
                    ForkNumber forkNum, char relpersistence)
 {
-   PGAlignedBlock buf;
+   PGIOAlignedBlock buf;
    Page        page;
    bool        use_wal;
    bool        copying_initfork;
index 20946c47cb4bd4fcf0b60009f9ee77fa1ff97d0c..0057443f0c63b14b9e6b403ec30ae9bafb51ebf0 100644 (file)
@@ -78,9 +78,12 @@ InitBufferPool(void)
                        NBuffers * sizeof(BufferDescPadded),
                        &foundDescs);
 
+   /* Align buffer pool on IO page size boundary. */
    BufferBlocks = (char *)
-       ShmemInitStruct("Buffer Blocks",
-                       NBuffers * (Size) BLCKSZ, &foundBufs);
+       TYPEALIGN(PG_IO_ALIGN_SIZE,
+                 ShmemInitStruct("Buffer Blocks",
+                                 NBuffers * (Size) BLCKSZ + PG_IO_ALIGN_SIZE,
+                                 &foundBufs));
 
    /* Align condition variables to cacheline boundary. */
    BufferIOCVArray = (ConditionVariableMinimallyPadded *)
@@ -163,7 +166,8 @@ BufferShmemSize(void)
    /* to allow aligning buffer descriptors */
    size = add_size(size, PG_CACHE_LINE_SIZE);
 
-   /* size of data pages */
+   /* size of data pages, plus alignment padding */
+   size = add_size(size, PG_IO_ALIGN_SIZE);
    size = add_size(size, mul_size(NBuffers, BLCKSZ));
 
    /* size of stuff controlled by freelist.c */
index a12d0c6c27cdb1c6f59afa08b6d62bbcab3b5aa5..5a237d56063a17f6641f84e6fbd369795a26f56c 100644 (file)
@@ -4250,7 +4250,7 @@ RelationCopyStorageUsingBuffer(RelFileLocator srclocator,
    bool        use_wal;
    BlockNumber nblocks;
    BlockNumber blkno;
-   PGAlignedBlock buf;
+   PGIOAlignedBlock buf;
    BufferAccessStrategy bstrategy_src;
    BufferAccessStrategy bstrategy_dst;
 
index 3d5bc9193d32dcc15be9c9a6abfd3bdce31b1cce..3c6382456a2ada920323a2e7c30cfcb1e59df2f4 100644 (file)
@@ -744,8 +744,11 @@ GetLocalBufferStorage(void)
        /* And don't overflow MaxAllocSize, either */
        num_bufs = Min(num_bufs, MaxAllocSize / BLCKSZ);
 
-       cur_block = (char *) MemoryContextAlloc(LocalBufferContext,
-                                               num_bufs * BLCKSZ);
+       /* Buffers should be I/O aligned. */
+       cur_block = (char *)
+           TYPEALIGN(PG_IO_ALIGN_SIZE,
+                     MemoryContextAlloc(LocalBufferContext,
+                                        num_bufs * BLCKSZ + PG_IO_ALIGN_SIZE));
        next_buf_in_block = 0;
        num_bufs_in_block = num_bufs;
    }
index 37ea8ac6b7c67790f278d624a0665a8f1183a00e..84ead85942abdfa3563148113f078411a85798ab 100644 (file)
@@ -95,6 +95,12 @@ struct BufFile
    off_t       curOffset;      /* offset part of current pos */
    int         pos;            /* next read/write position in buffer */
    int         nbytes;         /* total # of valid bytes in buffer */
+
+   /*
+    * XXX Should ideally us PGIOAlignedBlock, but might need a way to avoid
+    * wasting per-file alignment padding when some users create many
+    * files.
+    */
    PGAlignedBlock buffer;
 };
 
index 92994f8f395c6db8cb35189e90aea5ed5d376548..9a302ddc30e5548339d49b2cc9a9c9a70f263ace 100644 (file)
@@ -1522,7 +1522,10 @@ PageSetChecksumCopy(Page page, BlockNumber blkno)
     * and second to avoid wasting space in processes that never call this.
     */
    if (pageCopy == NULL)
-       pageCopy = MemoryContextAlloc(TopMemoryContext, BLCKSZ);
+       pageCopy = MemoryContextAllocAligned(TopMemoryContext,
+                                            BLCKSZ,
+                                            PG_IO_ALIGN_SIZE,
+                                            0);
 
    memcpy(pageCopy, (char *) page, BLCKSZ);
    ((PageHeader) pageCopy)->pd_checksum = pg_checksum_page(pageCopy, blkno);
index d9d0367c89d95f2749f0726cd7261d9142111189..d1124d46f49500c29f61ecf9e7ed4b9d73e41be0 100644 (file)
@@ -453,6 +453,10 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
    int         nbytes;
    MdfdVec    *v;
 
+   /* If this build supports direct I/O, the buffer must be I/O aligned. */
+   if (PG_O_DIRECT != 0 && PG_IO_ALIGN_SIZE <= BLCKSZ)
+       Assert((uintptr_t) buffer == TYPEALIGN(PG_IO_ALIGN_SIZE, buffer));
+
    /* This assert is too expensive to have on normally ... */
 #ifdef CHECK_WRITE_VS_EXTEND
    Assert(blocknum >= mdnblocks(reln, forknum));
@@ -783,6 +787,10 @@ mdread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
    int         nbytes;
    MdfdVec    *v;
 
+   /* If this build supports direct I/O, the buffer must be I/O aligned. */
+   if (PG_O_DIRECT != 0 && PG_IO_ALIGN_SIZE <= BLCKSZ)
+       Assert((uintptr_t) buffer == TYPEALIGN(PG_IO_ALIGN_SIZE, buffer));
+
    TRACE_POSTGRESQL_SMGR_MD_READ_START(forknum, blocknum,
                                        reln->smgr_rlocator.locator.spcOid,
                                        reln->smgr_rlocator.locator.dbOid,
@@ -848,6 +856,10 @@ mdwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
    int         nbytes;
    MdfdVec    *v;
 
+   /* If this build supports direct I/O, the buffer must be I/O aligned. */
+   if (PG_O_DIRECT != 0 && PG_IO_ALIGN_SIZE <= BLCKSZ)
+       Assert((uintptr_t) buffer == TYPEALIGN(PG_IO_ALIGN_SIZE, buffer));
+
    /* This assert is too expensive to have on normally ... */
 #ifdef CHECK_WRITE_VS_EXTEND
    Assert(blocknum < mdnblocks(reln, forknum));
@@ -1429,7 +1441,8 @@ _mdfd_getseg(SMgrRelation reln, ForkNumber forknum, BlockNumber blkno,
             */
            if (nblocks < ((BlockNumber) RELSEG_SIZE))
            {
-               char       *zerobuf = palloc0(BLCKSZ);
+               char       *zerobuf = palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE,
+                                                    MCXT_ALLOC_ZERO);
 
                mdextend(reln, forknum,
                         nextsegno * ((BlockNumber) RELSEG_SIZE) - 1,
index 64ea237438bcdb20068e0f0af98ceaac79b2cb06..52b8898d5edb4d4ca7e2141d6903cdb8928c2d39 100644 (file)
@@ -252,7 +252,7 @@ ltsWriteBlock(LogicalTapeSet *lts, long blocknum, const void *buffer)
     */
    while (blocknum > lts->nBlocksWritten)
    {
-       PGAlignedBlock zerobuf;
+       PGIOAlignedBlock zerobuf;
 
        MemSet(zerobuf.data, 0, sizeof(zerobuf));
 
index aa2100749766f55796236b432491aaf1ca52cd68..19eb67e4854f7c1dbad01626a6e3cd0977a5e1ba 100644 (file)
@@ -183,7 +183,7 @@ skipfile(const char *fn)
 static void
 scan_file(const char *fn, int segmentno)
 {
-   PGAlignedBlock buf;
+   PGIOAlignedBlock buf;
    PageHeader  header = (PageHeader) buf.data;
    int         f;
    BlockNumber blockno;
index da9d75dccb298d77fafc735e1ae8470cb2094859..4e2a1376c6906e7115946bc81afffe1f2db52a4e 100644 (file)
@@ -77,7 +77,7 @@ static void
 local_queue_fetch_file(rewind_source *source, const char *path, size_t len)
 {
    const char *datadir = ((local_source *) source)->datadir;
-   PGAlignedBlock buf;
+   PGIOAlignedBlock buf;
    char        srcpath[MAXPGPATH];
    int         srcfd;
    size_t      written_len;
@@ -129,7 +129,7 @@ local_queue_fetch_range(rewind_source *source, const char *path, off_t off,
                        size_t len)
 {
    const char *datadir = ((local_source *) source)->datadir;
-   PGAlignedBlock buf;
+   PGIOAlignedBlock buf;
    char        srcpath[MAXPGPATH];
    int         srcfd;
    off_t       begin = off;
index ed874507ff4b8fd66b33dd357bd6a5b0d48dd862..d1736028826f01373296aa665844a9af23679dbf 100644 (file)
@@ -178,8 +178,8 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 {
    int         src_fd;
    int         dst_fd;
-   PGAlignedBlock buffer;
-   PGAlignedBlock new_vmbuf;
+   PGIOAlignedBlock buffer;
+   PGIOAlignedBlock new_vmbuf;
    ssize_t     totalBytesRead = 0;
    ssize_t     src_filesize;
    int         rewriteVmBytesPerPage;
index d568d83b9f633ddf8c00a2b8818dea880fa4100d..74833c4acbbcd8183193087522366618ec2b9678 100644 (file)
@@ -540,8 +540,8 @@ pg_pwritev_with_retry(int fd, const struct iovec *iov, int iovcnt, off_t offset)
 ssize_t
 pg_pwrite_zeros(int fd, size_t size, off_t offset)
 {
-   static const PGAlignedBlock zbuffer = {{0}};    /* worth BLCKSZ */
-   void       *zerobuf_addr = unconstify(PGAlignedBlock *, &zbuffer)->data;
+   static const PGIOAlignedBlock zbuffer = {{0}};  /* worth BLCKSZ */
+   void       *zerobuf_addr = unconstify(PGIOAlignedBlock *, &zbuffer)->data;
    struct iovec iov[PG_IOV_MAX];
    size_t      remaining_size = size;
    ssize_t     total_written = 0;
index 5fe7a97ff03fb5b80613ebf3629002040c60bc08..f69d739be5711e40f3b2081b86bcfd98abd02795 100644 (file)
@@ -1119,14 +1119,11 @@ extern void ExceptionalCondition(const char *conditionName,
 
 /*
  * Use this, not "char buf[BLCKSZ]", to declare a field or local variable
- * holding a page buffer, if that page might be accessed as a page and not
- * just a string of bytes.  Otherwise the variable might be under-aligned,
- * causing problems on alignment-picky hardware.  (In some places, we use
- * this to declare buffers even though we only pass them to read() and
- * write(), because copying to/from aligned buffers is usually faster than
- * using unaligned buffers.)  We include both "double" and "int64" in the
- * union to ensure that the compiler knows the value must be MAXALIGN'ed
- * (cf. configure's computation of MAXIMUM_ALIGNOF).
+ * holding a page buffer, if that page might be accessed as a page.  Otherwise
+ * the variable might be under-aligned, causing problems on alignment-picky
+ * hardware.  We include both "double" and "int64" in the union to ensure that
+ * the compiler knows the value must be MAXALIGN'ed (cf. configure's
+ * computation of MAXIMUM_ALIGNOF).
  */
 typedef union PGAlignedBlock
 {
@@ -1135,9 +1132,30 @@ typedef union PGAlignedBlock
    int64       force_align_i64;
 } PGAlignedBlock;
 
+/*
+ * Use this to declare a field or local variable holding a page buffer, if that
+ * page might be accessed as a page or passed to an SMgr I/O function.  If
+ * allocating using the MemoryContext API, the aligned allocation functions
+ * should be used with PG_IO_ALIGN_SIZE.  This alignment may be more efficient
+ * for I/O in general, but may be strictly required on some platforms when
+ * using direct I/O.
+ */
+typedef union PGIOAlignedBlock
+{
+#ifdef pg_attribute_aligned
+   pg_attribute_aligned(PG_IO_ALIGN_SIZE)
+#endif
+   char        data[BLCKSZ];
+   double      force_align_d;
+   int64       force_align_i64;
+} PGIOAlignedBlock;
+
 /* Same, but for an XLOG_BLCKSZ-sized buffer */
 typedef union PGAlignedXLogBlock
 {
+#ifdef pg_attribute_aligned
+   pg_attribute_aligned(PG_IO_ALIGN_SIZE)
+#endif
    char        data[XLOG_BLCKSZ];
    double      force_align_d;
    int64       force_align_i64;
index b586ee269a0f80ddcfd109ef5263562ad1552ea9..33ec6102c15b36db064be53f166308d705ad5cce 100644 (file)
  */
 #define PG_CACHE_LINE_SIZE     128
 
+/*
+ * Assumed alignment requirement for direct I/O.  4K corresponds to common
+ * sector and memory page size.
+ */
+#define PG_IO_ALIGN_SIZE       4096
+
 /*
  *------------------------------------------------------------------------
  * The following symbols are for enabling debugging code, not for
index daceafd473287ad6fa343f0ec75b3f5c31b4ff51..faac4914fea259d8a00859fc39cc889b0802b01b 100644 (file)
@@ -82,9 +82,10 @@ extern PGDLLIMPORT int max_safe_fds;
  * to the appropriate Windows flag in src/port/open.c.  We simulate it with
  * fcntl(F_NOCACHE) on macOS inside fd.c's open() wrapper.  We use the name
  * PG_O_DIRECT rather than defining O_DIRECT in that case (probably not a good
- * idea on a Unix).
+ * idea on a Unix).  We can only use it if the compiler will correctly align
+ * PGIOAlignedBlock for us, though.
  */
-#if defined(O_DIRECT)
+#if defined(O_DIRECT) && defined(pg_attribute_aligned)
 #define        PG_O_DIRECT O_DIRECT
 #elif defined(F_NOCACHE)
 #define        PG_O_DIRECT 0x80000000
index 494cc66d5b68a1eea68a8a785c67eeaf84b058aa..df960883c5ce3f69338683e58eb07219bb799555 100644 (file)
@@ -1703,6 +1703,7 @@ PGEventResultDestroy
 PGFInfoFunction
 PGFileType
 PGFunction
+PGIOAlignedBlock
 PGLZ_HistEntry
 PGLZ_Strategy
 PGLoadBalanceType