From 1b9f9b97d5ca74532620cb51c8f8beb23500537f Mon Sep 17 00:00:00 2001
From: David Geier <geidav.pg@gmail.com>
Date: Fri, 5 Dec 2025 08:58:18 +0100
Subject: [PATCH v3 3/3] Add GUC

---
 src/backend/postmaster/postmaster.c       | 11 +++++++++
 src/backend/tcop/postgres.c               | 11 +++++++++
 src/backend/utils/init/postinit.c         |  3 ---
 src/backend/utils/misc/guc_parameters.dat |  7 ++++++
 src/backend/utils/misc/guc_tables.c       | 10 +++++++++
 src/bin/pg_test_timing/pg_test_timing.c   |  2 +-
 src/bin/pgbench/pgbench.c                 |  2 +-
 src/bin/psql/startup.c                    |  2 +-
 src/common/instr_time.c                   | 11 +++++++++
 src/include/portability/instr_time.h      | 27 +++++++++++++----------
 src/include/utils/guc_tables.h            |  1 +
 11 files changed, 69 insertions(+), 18 deletions(-)

diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 921d73226d6..a6dfaf356ee 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -1005,6 +1005,17 @@ PostmasterMain(int argc, char *argv[])
 	 */
 	CreateSharedMemoryAndSemaphores();
 
+	/*
+	 * Initialize high-precision interval timing.
+	 * Raise FATAL error if RDTSC is requested but not available.
+	 */
+	INSTR_TIME_INITIALIZE(true);
+
+#if defined(__x86_64__) && defined(__linux__)
+	if (fast_clock_source == FAST_CLOCK_SOURCE_RDTSC && (!has_rdtsc || !has_rdtscp))
+		ereport(FATAL, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("TSC is not supported as fast clock source")));
+#endif
+
 	/*
 	 * Estimate number of openable files.  This must happen after setting up
 	 * semaphores, because on some platforms semaphores count as open files.
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index e54bf1e760f..e882220b9a0 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -4152,6 +4152,17 @@ PostgresSingleUserMain(int argc, char *argv[],
 	 */
 	CreateSharedMemoryAndSemaphores();
 
+	/*
+	 * Initialize high-precision interval timing.
+	 * Raise FATAL error if RDTSC is requested but not available.
+	 */
+	INSTR_TIME_INITIALIZE(true);
+
+#if defined(__x86_64__) && defined(__linux__)
+	if (fast_clock_source == FAST_CLOCK_SOURCE_RDTSC && (!has_rdtsc || !has_rdtscp))
+		ereport(FATAL, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("TSC is not supported as fast clock source")));
+#endif
+
 	/*
 	 * Estimate number of openable files.  This must happen after setting up
 	 * semaphores, because on some platforms semaphores count as open files.
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index c339c35b25d..3f401faf3de 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -809,9 +809,6 @@ InitPostgres(const char *in_dbname, Oid dboid,
 	/* Initialize portal manager */
 	EnablePortalManager();
 
-	/* initialize high-precision interval timing */
-	INSTR_TIME_INITIALIZE();
-
 	/*
 	 * Load relcache entries for the shared system catalogs.  This must create
 	 * at least entries for pg_database and catalogs used for authentication.
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 7c60b125564..c68b3ff1184 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -1041,6 +1041,13 @@
   max => '3',
 },
 
+{ name => 'fast_clock_source', type => 'enum', context => 'PGC_POSTMASTER', group => 'RESOURCES_TIME',
+  short_desc => 'Use of fast clock source.',
+  variable => 'fast_clock_source',
+  boot_val => 'FAST_CLOCK_SOURCE_TRY',
+  options => 'fast_clock_source_options',
+},
+
 { name => 'file_copy_method', type => 'enum', context => 'PGC_USERSET', group => 'RESOURCES_DISK',
   short_desc => 'Selects the file copy method.',
   variable => 'file_copy_method',
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 73ff6ad0a32..3614aaf1876 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -90,6 +90,7 @@
 #include "tcop/tcopprot.h"
 #include "tsearch/ts_cache.h"
 #include "utils/builtins.h"
+#include "portability/instr_time.h"
 #include "utils/bytea.h"
 #include "utils/float.h"
 #include "utils/guc_hooks.h"
@@ -371,6 +372,15 @@ static const struct config_enum_entry huge_pages_options[] = {
 	{NULL, 0, false}
 };
 
+static const struct config_enum_entry fast_clock_source_options[] = {
+	{"try", FAST_CLOCK_SOURCE_TRY, false},
+	{"off", FAST_CLOCK_SOURCE_OFF, false},
+#if defined(__x86_64__) && defined(__linux__)
+	{"rdtsc", FAST_CLOCK_SOURCE_RDTSC, false},
+#endif
+	{NULL, 0, false}
+};
+
 static const struct config_enum_entry huge_pages_status_options[] = {
 	{"off", HUGE_PAGES_OFF, false},
 	{"on", HUGE_PAGES_ON, false},
diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c
index 62e308dd965..3ffa95769a5 100644
--- a/src/bin/pg_test_timing/pg_test_timing.c
+++ b/src/bin/pg_test_timing/pg_test_timing.c
@@ -174,7 +174,7 @@ test_timing(unsigned int duration, bool fast_timing)
 	char	   *time_source = NULL;
 	bool		fast_timing_available = false;
 
-	INSTR_TIME_INITIALIZE();
+	INSTR_TIME_INITIALIZE(true);
 
 #if !defined(WIN32) && defined(__x86_64__) && defined(__linux__)
 	if (fast_timing && has_rdtsc)
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 7b64fdaed34..78e5b40a7bb 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -7335,7 +7335,7 @@ main(int argc, char **argv)
 	}
 
 	/* initialize high-precision interval timing */
-	INSTR_TIME_INITIALIZE();
+	INSTR_TIME_INITIALIZE(false);
 
 	/* opening connection... */
 	con = doConnect();
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index a0b3bafe20c..ec8748002d8 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -329,7 +329,7 @@ main(int argc, char *argv[])
 	PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL);
 
 	/* initialize high-precision interval timing */
-	INSTR_TIME_INITIALIZE();
+	INSTR_TIME_INITIALIZE(false);
 
 	SyncVariables();
 
diff --git a/src/common/instr_time.c b/src/common/instr_time.c
index fdf47699f20..02b57edfb9b 100644
--- a/src/common/instr_time.c
+++ b/src/common/instr_time.c
@@ -24,6 +24,17 @@
 
 #include "portability/instr_time.h"
 
+int fast_clock_source = FAST_CLOCK_SOURCE_TRY;
+
+void
+pg_initialize_timing(bool try_fast)
+{
+#if defined(__x86_64__) && defined(__linux__)
+	if (try_fast)
+		pg_initialize_rdtsc();
+#endif
+}
+
 #ifndef WIN32
 /*
  * Stores what the number of cycles needs to be multiplied with to end up
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 33e506f3526..22ee991d2c9 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -81,6 +81,14 @@ typedef struct instr_time
 #define NS_PER_MS	INT64CONST(1000000)
 #define NS_PER_US	INT64CONST(1000)
 
+typedef enum
+{
+	FAST_CLOCK_SOURCE_TRY,
+	FAST_CLOCK_SOURCE_OFF,
+	FAST_CLOCK_SOURCE_RDTSC
+} FastClockSourceType;
+
+extern int fast_clock_source;
 
 #ifndef WIN32
 
@@ -147,7 +155,7 @@ static inline instr_time
 pg_get_ticks_fast(void)
 {
 #if defined(__x86_64__) && defined(__linux__)
-	if (has_rdtsc)
+	if (likely(has_rdtsc && fast_clock_source != FAST_CLOCK_SOURCE_OFF))
 	{
 		instr_time	now;
 
@@ -163,7 +171,7 @@ static inline instr_time
 pg_get_ticks(void)
 {
 #if defined(__x86_64__) && defined(__linux__)
-	if (has_rdtscp)
+	if (likely(has_rdtscp && fast_clock_source != FAST_CLOCK_SOURCE_OFF))
 	{
 		instr_time	now;
 		uint32		unused;
@@ -212,16 +220,11 @@ pg_ticks_to_ns(int64 ticks)
 	return ns;
 }
 
-static inline void
-pg_initialize_get_ticks()
-{
-#if defined(__x86_64__) && defined(__linux__)
-	pg_initialize_rdtsc();
-#endif
-}
+extern void
+pg_initialize_timing(bool try_fast);
 
-#define INSTR_TIME_INITIALIZE() \
-	pg_initialize_get_ticks()
+#define INSTR_TIME_INITIALIZE(try_fast) \
+	pg_initialize_timing(try_fast)
 
 #define INSTR_TIME_SET_CURRENT_FAST(t) \
 	((t) = pg_get_ticks_fast())
@@ -260,7 +263,7 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
-#define INSTR_TIME_INITIALIZE()
+#define INSTR_TIME_INITIALIZE(try_fast)
 
 #define INSTR_TIME_SET_CURRENT_FAST(t) \
 	((t) = pg_query_performance_counter())
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index 71a80161961..63440b8e36c 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -60,6 +60,7 @@ enum config_group
 	CONN_AUTH_TCP,
 	CONN_AUTH_AUTH,
 	CONN_AUTH_SSL,
+	RESOURCES_TIME,
 	RESOURCES_MEM,
 	RESOURCES_DISK,
 	RESOURCES_KERNEL,
-- 
2.51.0

