From 217ffee38167229568a474051549123713ba3a9e Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Sun, 5 Apr 2026 19:56:00 +0300
Subject: [PATCH v12 11/13] Add alignment option to ShmemRequestStruct()

The buffer blocks (in the next commit) are IO-aligned. This might come
handy in other places too, so make it an explicit feature of
ShmemRequestStruct().

Reviewed-by: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
Reviewed-by: Matthias van de Meent <boekewurm+postgres@gmail.com>
Reviewed-by: Daniel Gustafsson <daniel@yesql.se>
Discussion: https://www.postgresql.org/message-id/CAExHW5vM1bneLYfg0wGeAa=52UiJ3z4vKd3AJ72X8Fw6k3KKrg@mail.gmail.com
---
 src/backend/storage/ipc/shmem.c | 33 +++++++++++++++++++++++----------
 src/include/storage/shmem.h     |  6 ++++++
 2 files changed, 29 insertions(+), 10 deletions(-)

diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 67fad32be82..0f103f17f29 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -136,6 +136,7 @@
 #include "fmgr.h"
 #include "funcapi.h"
 #include "miscadmin.h"
+#include "port/pg_bitutils.h"
 #include "port/pg_numa.h"
 #include "storage/lwlock.h"
 #include "storage/pg_shmem.h"
@@ -236,7 +237,7 @@ typedef struct ShmemAllocatorData
 
 #define ShmemIndexLock (&ShmemAllocator->index_lock)
 
-static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRaw(Size size, Size alignment, Size *allocated_size);
 
 /* shared memory global variables */
 
@@ -340,6 +341,7 @@ ShmemRequestInternal(ShmemStructOpts *options, ShmemRequestKind kind)
 {
 	ShmemRequest *request;
 
+	/* Check the options */
 	if (options->name == NULL)
 		elog(ERROR, "shared memory request is missing 'name' option");
 
@@ -358,6 +360,11 @@ ShmemRequestInternal(ShmemStructOpts *options, ShmemRequestKind kind)
 				 options->size, options->name);
 	}
 
+	if (options->alignment != 0 && pg_nextpower2_size_t(options->alignment) != options->alignment)
+		elog(ERROR, "invalid alignment %zu for shared memory request for \"%s\"",
+			 options->alignment, options->name);
+
+	/* Check that we're in the right state */
 	if (shmem_request_state != SRS_REQUESTING)
 		elog(ERROR, "ShmemRequestStruct can only be called from a shmem_request callback");
 
@@ -399,7 +406,8 @@ ShmemGetRequestedSize(void)
 	{
 		size = add_size(size, request->options->size);
 		/* calculate alignment padding like ShmemAllocRaw() does */
-		size = CACHELINEALIGN(size);
+		size = TYPEALIGN(Max(request->options->alignment, PG_CACHE_LINE_SIZE),
+						 size);
 	}
 
 	return size;
@@ -524,7 +532,9 @@ InitShmemIndexEntry(ShmemRequest *request)
 	 * We inserted the entry to the shared memory index.  Allocate requested
 	 * amount of shared memory for it, and initialize the index entry.
 	 */
-	structPtr = ShmemAllocRaw(request->options->size, &allocated_size);
+	structPtr = ShmemAllocRaw(request->options->size,
+							  request->options->alignment,
+							  &allocated_size);
 	if (structPtr == NULL)
 	{
 		/* out of memory; remove the failed ShmemIndex entry */
@@ -753,7 +763,7 @@ ShmemAlloc(Size size)
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRaw(size, 0, &allocated_size);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -772,7 +782,7 @@ ShmemAllocNoError(Size size)
 {
 	Size		allocated_size;
 
-	return ShmemAllocRaw(size, &allocated_size);
+	return ShmemAllocRaw(size, 0, &allocated_size);
 }
 
 /*
@@ -782,8 +792,9 @@ ShmemAllocNoError(Size size)
  * be equal to the number requested plus any padding we choose to add.
  */
 static void *
-ShmemAllocRaw(Size size, Size *allocated_size)
+ShmemAllocRaw(Size size, Size alignment, Size *allocated_size)
 {
+	Size		rawStart;
 	Size		newStart;
 	Size		newFree;
 	void	   *newSpace;
@@ -799,14 +810,15 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	 * structures out to a power-of-two size - but without this, even that
 	 * won't be sufficient.
 	 */
-	size = CACHELINEALIGN(size);
-	*allocated_size = size;
+	if (alignment < PG_CACHE_LINE_SIZE)
+		alignment = PG_CACHE_LINE_SIZE;
 
 	Assert(ShmemSegHdr != NULL);
 
 	SpinLockAcquire(&ShmemAllocator->shmem_lock);
 
-	newStart = ShmemAllocator->free_offset;
+	rawStart = ShmemAllocator->free_offset;
+	newStart = TYPEALIGN(alignment, rawStart);
 
 	newFree = newStart + size;
 	if (newFree <= ShmemSegHdr->totalsize)
@@ -820,8 +832,9 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	SpinLockRelease(&ShmemAllocator->shmem_lock);
 
 	/* note this assert is okay with newSpace == NULL */
-	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
+	Assert(newSpace == (void *) TYPEALIGN(alignment, newSpace));
 
+	*allocated_size = newFree - rawStart;
 	return newSpace;
 }
 
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 8e0fc29dcac..060a4a8e5d2 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -51,6 +51,12 @@ typedef struct ShmemStructOpts
 	 */
 	ssize_t		size;
 
+	/*
+	 * Alignment of the starting address. If not set, defaults to cacheline
+	 * boundary.  Must be a power of two.
+	 */
+	size_t		alignment;
+
 	/*
 	 * When the shmem area is initialized or attached to, pointer to it is
 	 * stored in *ptr.  It usually points to a global variable, used to access
-- 
2.47.3

