Remove support for 'standalone' rseq registration

Depend on libc to handle the allocation and registration of the rseq
area for the whole process.

Librseq can still be built and run with a libc that lacks the rseq
symbols as it doesn't link on them directly but it will not handle the
registration even if the syscall is available.

User visible changes:

  * Removes per-thread registration API, with libc the registration is
    either enabled for all threads or none.

Change-Id: Ib0cb9aa8ccc8c8786ed0fdd2a50cebe23e4fb1b1
Signed-off-by: Michael Jeanson <mjeanson@efficios.com>
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
diff --git a/include/rseq/abi.h b/include/rseq/abi.h
index 54f008d..9be5659 100644
--- a/include/rseq/abi.h
+++ b/include/rseq/abi.h
@@ -12,6 +12,9 @@
 #include <linux/types.h>
 #include <asm/byteorder.h>
 
+/* Original struct rseq feature size is 20 bytes. */
+#define RSEQ_ABI_ORIG_FEATURE_SIZE		20
+
 /* Original struct rseq allocation size is 32 bytes. */
 #define RSEQ_ABI_ORIG_ALLOC_SIZE		32
 
diff --git a/include/rseq/rseq.h b/include/rseq/rseq.h
index d8c7ef3..4e11ada 100644
--- a/include/rseq/rseq.h
+++ b/include/rseq/rseq.h
@@ -45,6 +45,14 @@
 };
 
 /*
+ * Return values of rseq_init().
+ */
+enum rseq_init_return {
+	RSEQ_INIT_OK = 0,
+	RSEQ_INIT_ERROR_MISSING_SYMBOLS = 1,
+};
+
+/*
  * User code can define RSEQ_GET_ABI_OVERRIDE to override the
  * rseq_get_abi() implementation, for instance to use glibc's symbols
  * directly.
@@ -69,13 +77,10 @@
  * size for the rseq area and the feature size supported by the kernel.
  */
 
-/*
- * Size of the active rseq feature set. 0 if the registration was
- * unsuccessful.
- */
+/* Size of the active rseq features. 0 if the registration failed. */
 extern unsigned int rseq_size;
 
-/* Flags used during rseq registration. */
+/* Flags used at rseq registration. */
 extern unsigned int rseq_flags;
 
 /*
@@ -105,45 +110,51 @@
 #endif
 
 /*
- * Register rseq for the current thread. This needs to be called once
- * by any thread which uses restartable sequences, before they start
- * using restartable sequences, to ensure restartable sequences
- * succeed. A restartable sequence executed from a non-registered
- * thread will always fail.
+ * Initialize librseq, must be called once per process.
  */
-int rseq_register_current_thread(void);
+int rseq_init(void);
 
 /*
- * Unregister rseq for current thread.
- */
-int rseq_unregister_current_thread(void);
-
-/*
- * Restartable sequence fallback for reading the current CPU number.
+ * Slow fallback to get the current CPU number.
  */
 int32_t rseq_fallback_current_cpu(void);
 
 /*
- * Restartable sequence fallback for reading the current node number.
+ * Slow fallback to get the current node number.
  */
 int32_t rseq_fallback_current_node(void);
 
 /*
- * Returns true if rseq is supported.
+ * Returns true if rseq is supported. The query types are:
+ *
+ *   RSEQ_AVAILABLE_QUERY_KERNEL:
+ *     Returns true if the rseq syscall is available.
+ *
+ *   RSEQ_AVAILABLE_QUERY_LIBC:
+ *     Returns true if the libc exposes the rseq symbols.
  */
 bool rseq_available(unsigned int query);
 
+
 /*
- * rseq_get_max_nr_cpus: Get the max_nr_cpus auto-detected value.
- *
+ * Returns true if rseq is registered.
+ */
+static inline __attribute__((always_inline))
+bool rseq_registered(void)
+{
+	return rseq_size > 0;
+}
+
+/*
  * Returns the max_nr_cpus auto-detected at pool creation when invoked
  * with @nr_max_cpus=0 argument.
  */
 int rseq_get_max_nr_cpus(void);
 
 /*
- * Values returned can be either the current CPU number, -1 (rseq is
- * uninitialized), or -2 (rseq initialization has failed).
+ * Get the current CPU number from the rseq area. Values returned can be either
+ * the current CPU number, -1 (rseq is uninitialized), or -2 (rseq
+ * initialization has failed).
  */
 static inline __attribute__((always_inline))
 int32_t rseq_current_cpu_raw(void)
@@ -168,6 +179,9 @@
 	return RSEQ_READ_ONCE(rseq_get_abi()->cpu_id_start);
 }
 
+/*
+ * Get the current CPU number from the rseq area, fallback to a syscall
+ */
 static inline __attribute__((always_inline))
 uint32_t rseq_current_cpu(void)
 {
@@ -179,6 +193,9 @@
 	return cpu;
 }
 
+/*
+ * Returns true if the 'node_id' feature is available.
+ */
 static inline __attribute__((always_inline))
 bool rseq_node_id_available(void)
 {
@@ -186,7 +203,7 @@
 }
 
 /*
- * Current NUMA node number.
+ * Get the current NUMA node number.
  */
 static inline __attribute__((always_inline))
 uint32_t rseq_current_node_id(void)
@@ -195,18 +212,27 @@
 	return RSEQ_READ_ONCE(rseq_get_abi()->node_id);
 }
 
+/*
+ * Returns true if the 'mm_cid' feature is available.
+ */
 static inline __attribute__((always_inline))
 bool rseq_mm_cid_available(void)
 {
 	return (int) rseq_size >= (int) rseq_offsetofend(struct rseq_abi, mm_cid);
 }
 
+/*
+ * Get the current memory map concurrency id.
+ */
 static inline __attribute__((always_inline))
 uint32_t rseq_current_mm_cid(void)
 {
 	return RSEQ_READ_ONCE(rseq_get_abi()->mm_cid);
 }
 
+/*
+ * Clear the rseq_cs pointer.
+ */
 static inline __attribute__((always_inline))
 void rseq_clear_rseq_cs(void)
 {
diff --git a/src/rseq.c b/src/rseq.c
index 7bfaea7..952bc73 100644
--- a/src/rseq.c
+++ b/src/rseq.c
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: MIT
 // SPDX-FileCopyrightText: 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+// SPDX-FileCopyrightText: 2026 Michael Jeanson <mjeanson@efficios.com>
 
 #ifndef _GNU_SOURCE
 #define _GNU_SOURCE
@@ -31,8 +32,10 @@
 # define AT_RSEQ_ALIGN			28
 #endif
 
-static __attribute__((constructor))
-void rseq_init(void);
+
+/*
+ * Private internal variables.
+ */
 
 static pthread_mutex_t init_lock = PTHREAD_MUTEX_INITIALIZER;
 static int init_done;
@@ -41,51 +44,153 @@
 static const unsigned int *libc_rseq_size_p;
 static const unsigned int *libc_rseq_flags_p;
 
-/* Offset from the thread pointer to the rseq area. */
-ptrdiff_t rseq_offset;
 
 /*
- * Size of the active rseq feature set. 0 if the registration was
- * unsuccessful.
+ * Public API variables.
  */
+
+/* Offset from the thread pointer to the rseq area. */
+ptrdiff_t rseq_offset = PTRDIFF_MIN;
+
+/* Size of the active rseq features. 0 if the registration failed. */
 unsigned int rseq_size = -1U;
 
-/* Flags used during rseq registration. */
-unsigned int rseq_flags;
+/* Flags used at rseq registration. */
+unsigned int rseq_flags = 0;
 
-static int rseq_ownership;
-
-/* Allocate a large area for the TLS. */
-#define RSEQ_THREAD_AREA_ALLOC_SIZE	1024
-
-/* Original struct rseq feature size is 20 bytes. */
-#define ORIG_RSEQ_FEATURE_SIZE		20
-
-/* Original struct rseq allocation size is 32 bytes. */
-#define ORIG_RSEQ_ALLOC_SIZE		32
 
 /*
- * The alignment on RSEQ_THREAD_AREA_ALLOC_SIZE guarantees that the
- * rseq_abi structure allocated size is at least
- * RSEQ_THREAD_AREA_ALLOC_SIZE bytes to hold extra space for yet unknown
- * kernel rseq extensions.
+ * Private util functions.
  */
-static
-__thread struct rseq_abi __rseq_abi __attribute__((tls_model("initial-exec"), aligned(RSEQ_THREAD_AREA_ALLOC_SIZE))) = {
-	.cpu_id = RSEQ_ABI_CPU_ID_UNINITIALIZED,
-};
 
-static int sys_rseq(struct rseq_abi *rseq_abi, uint32_t rseq_len,
+/* rseq syscall wrapper. */
+static
+int sys_rseq(struct rseq_abi *rseq_abi, uint32_t rseq_len,
 		    int flags, uint32_t sig)
 {
 	return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig);
 }
 
-static int sys_getcpu(unsigned int *cpu, unsigned int *node)
+/* getcpu syscall wrapper. */
+static
+int sys_getcpu(unsigned int *cpu, unsigned int *node)
 {
 	return syscall(__NR_getcpu, cpu, node, NULL);
 }
 
+/*
+ * Return the feature size supported by the kernel.
+ *
+ * Depending on the value returned by getauxval(AT_RSEQ_FEATURE_SIZE):
+ *
+ * 0:   Return RSEQ_ABI_ORIG_FEATURE_SIZE (20)
+ * > 0: Return the value from getauxval(AT_RSEQ_FEATURE_SIZE).
+ *
+ * It never returns a value below RSEQ_ABI_ORIG_FEATURE_SIZE.
+ */
+static
+unsigned int get_rseq_kernel_feature_size(void)
+{
+	unsigned long auxv_rseq_feature_size = getauxval(AT_RSEQ_FEATURE_SIZE);
+
+	if (auxv_rseq_feature_size)
+		return auxv_rseq_feature_size;
+	else
+		return RSEQ_ABI_ORIG_FEATURE_SIZE;
+}
+
+
+/*
+ * Public API functions.
+ */
+
+/*
+ * Initialize librseq, must be called once per process.
+ */
+int rseq_init(void)
+{
+	/*
+	 * Ensure initialization is only done once. Use load-acquire to
+	 * observe the initialization performed by a concurrently
+	 * running thread.
+	 */
+	if (rseq_smp_load_acquire(&init_done))
+		return RSEQ_INIT_OK;
+
+	/*
+	 * Take the mutex, check the initialization flag again and atomically
+	 * set it to ensure we are the only thread doing the initialization.
+	 */
+	pthread_mutex_lock(&init_lock);
+	if (rseq_smp_load_acquire(&init_done))
+		goto unlock_ok;
+
+	/*
+	 * Get the libc rseq public symbols, all 3 are required for a
+	 * successful initialization.
+	 */
+	libc_rseq_offset_p = dlsym(RTLD_NEXT, "__rseq_offset");
+	libc_rseq_size_p = dlsym(RTLD_NEXT, "__rseq_size");
+	libc_rseq_flags_p = dlsym(RTLD_NEXT, "__rseq_flags");
+	if (!libc_rseq_size_p || !libc_rseq_offset_p || !libc_rseq_flags_p) {
+		pthread_mutex_unlock(&init_lock);
+		return RSEQ_INIT_ERROR_MISSING_SYMBOLS;
+	}
+
+	/*
+	 * Older versions of Glibc expose a rseq feature size of 32 bytes even
+	 * though the kernel only supported 20 bytes initially. Glibc 2.40
+	 * exposes a fixed feature size of 20 bytes, while still allocating a
+	 * 32 bytes area.
+	 *
+	 * Treat both 32 and 20 bytes as special-cases using the following
+	 * value as active feature size:
+	 *
+	 *   rseq_size = min(32, max(20, getauxval(AT_RSEQ_FEATURE_SIZE)));
+	 *
+	 * Otherwise, use the rseq_size from libc directly.
+	 */
+	switch (*libc_rseq_size_p) {
+	case RSEQ_ABI_ORIG_FEATURE_SIZE:	/* Fallthrough. */
+	case RSEQ_ABI_ORIG_ALLOC_SIZE:
+	{
+		unsigned int rseq_kernel_feature_size = get_rseq_kernel_feature_size();
+
+		if (rseq_kernel_feature_size < RSEQ_ABI_ORIG_ALLOC_SIZE)
+			rseq_size = rseq_kernel_feature_size;
+		else
+			rseq_size = RSEQ_ABI_ORIG_ALLOC_SIZE;
+		break;
+	}
+	default:
+		/* Otherwise just use the __rseq_size from libc as rseq_size. */
+		rseq_size = *libc_rseq_size_p;
+		break;
+	}
+
+	/* Copy the libc rseq offset and flags values. */
+	rseq_offset = *libc_rseq_offset_p;
+	rseq_flags = *libc_rseq_flags_p;
+
+	/*
+	 * Set init_done with store-release, to make sure concurrently
+	 * running threads observe the initialized state.
+	 */
+	rseq_smp_store_release(&init_done, 1);
+unlock_ok:
+	pthread_mutex_unlock(&init_lock);
+	return RSEQ_INIT_OK;
+}
+
+/*
+ * Returns true if rseq is supported. The query types are:
+ *
+ *   RSEQ_AVAILABLE_QUERY_KERNEL:
+ *     Returns true if the rseq syscall is available.
+ *
+ *   RSEQ_AVAILABLE_QUERY_LIBC:
+ *     Returns true if the libc exposes the rseq symbols.
+ */
 bool rseq_available(unsigned int query)
 {
 	int rc;
@@ -105,7 +210,10 @@
 		}
 		break;
 	case RSEQ_AVAILABLE_QUERY_LIBC:
-		if (rseq_size && !rseq_ownership)
+		libc_rseq_offset_p = dlsym(RTLD_NEXT, "__rseq_offset");
+		libc_rseq_size_p = dlsym(RTLD_NEXT, "__rseq_size");
+		libc_rseq_flags_p = dlsym(RTLD_NEXT, "__rseq_flags");
+		if (libc_rseq_offset_p && libc_rseq_size_p && libc_rseq_flags_p)
 			return true;
 		break;
 	default:
@@ -114,202 +222,9 @@
 	return false;
 }
 
-/* The rseq areas need to be at least 32 bytes. */
-static
-unsigned int get_rseq_min_alloc_size(void)
-{
-	unsigned int alloc_size = rseq_size;
-
-	if (alloc_size < ORIG_RSEQ_ALLOC_SIZE)
-		alloc_size = ORIG_RSEQ_ALLOC_SIZE;
-	return alloc_size;
-}
-
 /*
- * Return the feature size supported by the kernel.
- *
- * Depending on the value returned by getauxval(AT_RSEQ_FEATURE_SIZE):
- *
- * 0:   Return ORIG_RSEQ_FEATURE_SIZE (20)
- * > 0: Return the value from getauxval(AT_RSEQ_FEATURE_SIZE).
- *
- * It should never return a value below ORIG_RSEQ_FEATURE_SIZE.
+ * Slow fallback to get the current CPU number.
  */
-static
-unsigned int get_rseq_kernel_feature_size(void)
-{
-	unsigned long auxv_rseq_feature_size, auxv_rseq_align;
-
-	auxv_rseq_align = getauxval(AT_RSEQ_ALIGN);
-	assert(!auxv_rseq_align || auxv_rseq_align <= RSEQ_THREAD_AREA_ALLOC_SIZE);
-
-	auxv_rseq_feature_size = getauxval(AT_RSEQ_FEATURE_SIZE);
-	assert(!auxv_rseq_feature_size || auxv_rseq_feature_size <= RSEQ_THREAD_AREA_ALLOC_SIZE);
-	if (auxv_rseq_feature_size)
-		return auxv_rseq_feature_size;
-	else
-		return ORIG_RSEQ_FEATURE_SIZE;
-}
-
-int rseq_register_current_thread(void)
-{
-	int rc;
-
-	rseq_init();
-
-	if (!rseq_ownership) {
-		/* Treat libc's ownership as a successful registration. */
-		return 0;
-	}
-	rc = sys_rseq(&__rseq_abi, get_rseq_min_alloc_size(), 0, RSEQ_SIG);
-	if (rc) {
-		/*
-		 * After at least one thread has registered successfully
-		 * (rseq_size > 0), the registration of other threads should
-		 * never fail.
-		 */
-		if (RSEQ_READ_ONCE(rseq_size) > 0) {
-			/* Incoherent success/failure within process. */
-			abort();
-		}
-		return -1;
-	}
-	assert(rseq_current_cpu_raw() >= 0);
-
-	/*
-	 * The first thread to register sets the rseq_size to mimic the libc
-	 * behavior.
-	 */
-	if (RSEQ_READ_ONCE(rseq_size) == 0) {
-		RSEQ_WRITE_ONCE(rseq_size, get_rseq_kernel_feature_size());
-	}
-
-	return 0;
-}
-
-int rseq_unregister_current_thread(void)
-{
-	int rc;
-
-	if (!rseq_ownership) {
-		/* Treat libc's ownership as a successful unregistration. */
-		return 0;
-	}
-	rc = sys_rseq(&__rseq_abi, get_rseq_min_alloc_size(), RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG);
-	if (rc)
-		return -1;
-	return 0;
-}
-
-/*
- * Initialize the public symbols for the rseq offset, size, feature size and
- * flags prior to registering threads. If glibc owns the registration, get the
- * values from its public symbols.
- */
-static
-void rseq_init(void)
-{
-	/*
-	 * Ensure initialization is only done once. Use load-acquire to
-	 * observe the initialization performed by a concurrently
-	 * running thread.
-	 */
-	if (rseq_smp_load_acquire(&init_done))
-		return;
-
-	/*
-	 * Take the mutex, check the initialization flag again and atomically
-	 * set it to ensure we are the only thread doing the initialization.
-	 */
-	pthread_mutex_lock(&init_lock);
-	if (init_done)
-		goto unlock;
-
-	/*
-	 * Check for glibc rseq support, if the 3 public symbols are found and
-	 * the rseq_size is not zero, glibc owns the registration.
-	 */
-	libc_rseq_offset_p = dlsym(RTLD_NEXT, "__rseq_offset");
-	libc_rseq_size_p = dlsym(RTLD_NEXT, "__rseq_size");
-	libc_rseq_flags_p = dlsym(RTLD_NEXT, "__rseq_flags");
-	if (libc_rseq_size_p && libc_rseq_offset_p && libc_rseq_flags_p &&
-			*libc_rseq_size_p != 0) {
-		unsigned int libc_rseq_size;
-
-		/* rseq registration owned by glibc */
-		rseq_offset = *libc_rseq_offset_p;
-		libc_rseq_size = *libc_rseq_size_p;
-		rseq_flags = *libc_rseq_flags_p;
-
-		/*
-		 * Previous versions of glibc expose the value
-		 * 32 even though the kernel only supported 20
-		 * bytes initially. Therefore treat 32 as a
-		 * special-case. glibc 2.40 exposes a 20 bytes
-		 * __rseq_size without using getauxval(3) to
-		 * query the supported size, while still allocating a 32
-		 * bytes area. Also treat 20 as a special-case.
-		 *
-		 * Special-cases are handled by using the following
-		 * value as active feature set size:
-		 *
-		 *   rseq_size = min(32, get_rseq_kernel_feature_size())
-		 */
-		switch (libc_rseq_size) {
-		case ORIG_RSEQ_FEATURE_SIZE:	/* Fallthrough. */
-		case ORIG_RSEQ_ALLOC_SIZE:
-		{
-			unsigned int rseq_kernel_feature_size = get_rseq_kernel_feature_size();
-
-			if (rseq_kernel_feature_size < ORIG_RSEQ_ALLOC_SIZE)
-				rseq_size = rseq_kernel_feature_size;
-			else
-				rseq_size = ORIG_RSEQ_ALLOC_SIZE;
-			break;
-		}
-		default:
-			/* Otherwise just use the __rseq_size from libc as rseq_size. */
-			rseq_size = libc_rseq_size;
-			break;
-		}
-		goto init_done;
-	}
-
-	/* librseq owns the registration */
-	rseq_ownership = 1;
-
-	/* Calculate the offset of the rseq area from the thread pointer. */
-	rseq_offset = (uintptr_t)&__rseq_abi - (uintptr_t)rseq_thread_pointer();
-
-	/* rseq flags are deprecated, always set to 0. */
-	rseq_flags = 0;
-
-	/*
-	 * Set the size to 0 until at least one thread registers to mimic the
-	 * libc behavior.
-	 */
-	rseq_size = 0;
-
-init_done:
-	/*
-	 * Set init_done with store-release, to make sure concurrently
-	 * running threads observe the initialized state.
-	 */
-	rseq_smp_store_release(&init_done, 1);
-unlock:
-	pthread_mutex_unlock(&init_lock);
-}
-
-static __attribute__((destructor))
-void rseq_exit(void)
-{
-	if (!rseq_ownership)
-		return;
-	rseq_offset = 0;
-	rseq_size = -1U;
-	rseq_ownership = 0;
-}
-
 int32_t rseq_fallback_current_cpu(void)
 {
 	int32_t cpu;
@@ -322,6 +237,9 @@
 	return cpu;
 }
 
+/*
+ * Slow fallback to get the current node number.
+ */
 int32_t rseq_fallback_current_node(void)
 {
 	uint32_t cpu_id, node_id;
@@ -335,6 +253,10 @@
 	return (int32_t) node_id;
 }
 
+/*
+ * Returns the max_nr_cpus auto-detected at pool creation when invoked
+ * with @nr_max_cpus=0 argument.
+ */
 int rseq_get_max_nr_cpus(void)
 {
 	return get_possible_cpus_array_len();
diff --git a/tests/basic_percpu_benchmark.c b/tests/basic_percpu_benchmark.c
index 46b431a..c8984b2 100644
--- a/tests/basic_percpu_benchmark.c
+++ b/tests/basic_percpu_benchmark.c
@@ -148,12 +148,6 @@
 
 	set_affinity();
 
-	if (rseq_register_current_thread()) {
-		fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
-			errno, strerror(errno));
-		abort();
-	}
-
 	/*
 	 * Rendez-vous across all threads to make sure the number of
 	 * threads >= number of possible CPUs for the entire test duration.
@@ -201,11 +195,6 @@
 	while (!__atomic_load_n(&test_stop, __ATOMIC_RELAXED))
 		rseq_barrier();
 
-	if (rseq_unregister_current_thread()) {
-		fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
-			errno, strerror(errno));
-		abort();
-	}
 	data->total_time += total_time;
 
 	return NULL;
@@ -272,31 +261,28 @@
 		rand_order[index] = tmp;
 	}
 
-	if (!rseq_available(RSEQ_AVAILABLE_QUERY_KERNEL)) {
-		skip(NR_TESTS, "The rseq syscall is unavailable");
+	/*
+	 * Skip all tests if the libc doesn't have rseq support
+	 */
+	if (!rseq_available(RSEQ_AVAILABLE_QUERY_LIBC)) {
+		skip(NR_TESTS, "The libc doesn't have rseq support");
+	}
+
+	if (rseq_init() == RSEQ_INIT_OK) {
+		pass("Initialized librseq");
+	} else {
+		fail("Initialized librseq")
+		skip(NR_TESTS - 1, "Error: librseq initialization failed");
 		goto end;
 	}
 
-	if (rseq_register_current_thread()) {
-		fail("rseq_register_current_thread(...) failed(%d): %s\n",
-			errno, strerror(errno));
-		goto end;
-	} else {
-		pass("Registered current thread with rseq");
-	}
 	if (!rseq_validate_cpu_id()) {
-		skip(NR_TESTS - 1, "Error: cpu id getter unavailable");
+		skip(NR_TESTS - 2, "Error: cpu id getter unavailable");
 		goto end;
 	}
+
 	test_percpu_benchmark();
 
-	if (rseq_unregister_current_thread()) {
-		fail("rseq_unregister_current_thread(...) failed(%d): %s\n",
-			errno, strerror(errno));
-		goto end;
-	} else {
-		pass("Unregistered current thread with rseq");
-	}
 end:
 	rseq_mempool_percpu_free(percpudata);
 	rseq_mempool_destroy(mempool);
diff --git a/tests/basic_percpu_ops_test.c b/tests/basic_percpu_ops_test.c
index e2976b2..b94d1de 100644
--- a/tests/basic_percpu_ops_test.c
+++ b/tests/basic_percpu_ops_test.c
@@ -17,7 +17,7 @@
 
 #include "tap.h"
 
-#define NR_TESTS 4
+#define NR_TESTS 3
 
 #define ARRAY_SIZE(arr)	(sizeof(arr) / sizeof((arr)[0]))
 
@@ -116,21 +116,11 @@
 	struct spinlock_test_data *data = (struct spinlock_test_data *) arg;
 	int i, cpu;
 
-	if (rseq_register_current_thread()) {
-		fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
-			errno, strerror(errno));
-		abort();
-	}
 	for (i = 0; i < data->reps; i++) {
 		cpu = rseq_this_cpu_lock(&data->lock);
 		data->c[cpu].count++;
 		rseq_percpu_unlock(&data->lock, cpu);
 	}
-	if (rseq_unregister_current_thread()) {
-		fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
-			errno, strerror(errno));
-		abort();
-	}
 
 	return NULL;
 }
@@ -249,12 +239,6 @@
 	int i;
 	struct percpu_list *list = (struct percpu_list *)arg;
 
-	if (rseq_register_current_thread()) {
-		fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
-			errno, strerror(errno));
-		abort();
-	}
-
 	for (i = 0; i < 100000; i++) {
 		struct percpu_list_node *node;
 
@@ -264,12 +248,6 @@
 			this_cpu_list_push(list, node, NULL);
 	}
 
-	if (rseq_unregister_current_thread()) {
-		fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
-			errno, strerror(errno));
-		abort();
-	}
-
 	return NULL;
 }
 
@@ -300,10 +278,12 @@
 		}
 	}
 
+	diag(" Create threads");
 	for (i = 0; i < 200; i++)
 		pthread_create(&test_threads[i], NULL,
 		       test_percpu_list_thread, &list);
 
+	diag(" Join threads");
 	for (i = 0; i < 200; i++)
 		pthread_join(test_threads[i], NULL);
 
@@ -326,32 +306,29 @@
 {
 	plan_tests(NR_TESTS);
 
-	if (!rseq_available(RSEQ_AVAILABLE_QUERY_KERNEL)) {
-		skip(NR_TESTS, "The rseq syscall is unavailable");
+	/*
+	 * Skip all tests if the libc doesn't have rseq support
+	 */
+	if (!rseq_available(RSEQ_AVAILABLE_QUERY_LIBC)) {
+		skip(NR_TESTS, "The libc doesn't have rseq support");
+	}
+
+	if (rseq_init() == RSEQ_INIT_OK) {
+		pass("Initialized librseq");
+	} else {
+		fail("Initialized librseq")
+		skip(NR_TESTS - 1, "Error: librseq initialization failed");
 		goto end;
 	}
 
-	if (rseq_register_current_thread()) {
-		fail("rseq_register_current_thread(...) failed(%d): %s\n",
-			errno, strerror(errno));
-		goto end;
-	} else {
-		pass("Registered current thread with rseq");
-	}
 	if (!rseq_validate_cpu_id()) {
-		skip(NR_TESTS - 1, "Error: cpu id getter unavailable");
+		skip(NR_TESTS - 2, "Error: cpu id getter unavailable");
 		goto end;
 	}
+
 	test_percpu_spinlock();
 	test_percpu_list();
 
-	if (rseq_unregister_current_thread()) {
-		fail("rseq_unregister_current_thread(...) failed(%d): %s\n",
-			errno, strerror(errno));
-		goto end;
-	} else {
-		pass("Unregistered current thread with rseq");
-	}
 end:
 	exit(exit_status());
 }
diff --git a/tests/basic_test.c b/tests/basic_test.c
index 6cbd732..75e7f30 100644
--- a/tests/basic_test.c
+++ b/tests/basic_test.c
@@ -77,34 +77,27 @@
 
 int main(void)
 {
+	diag("Basic test coverage for critical regions and rseq_current_cpu()");
+
 	/*
-	 * Skip all tests if the rseq syscall is unavailable
+	 * Skip all tests if the libc doesn't have rseq support
 	 */
-	if (rseq_available(RSEQ_AVAILABLE_QUERY_KERNEL)) {
+	if (rseq_available(RSEQ_AVAILABLE_QUERY_LIBC)) {
 		plan_no_plan();
 	} else {
-		plan_skip_all("The rseq syscall is unavailable");
+		plan_skip_all("The libc doesn't have rseq support");
 	}
 
-	if (rseq_register_current_thread()) {
-		fail("rseq_register_current_thread(...) failed(%d): %s\n",
-			errno, strerror(errno));
-		goto end;
+	if (rseq_init() == RSEQ_INIT_OK) {
+		pass("Initialized librseq");
 	} else {
-		pass("Registered current thread with rseq");
+		fail("Initialized librseq")
+		goto end;
 	}
 
 	test_registered();
 	test_cpu_pointer();
 
-	if (rseq_unregister_current_thread()) {
-		fail("rseq_unregister_current_thread(...) failed(%d): %s\n",
-			errno, strerror(errno));
-		goto end;
-	} else {
-		pass("Unregistered current thread with rseq");
-	}
-
 end:
 	exit(exit_status());
 }
diff --git a/tests/no_syscall_test.c b/tests/no_syscall_test.c
index b7462b1..71f4116 100644
--- a/tests/no_syscall_test.c
+++ b/tests/no_syscall_test.c
@@ -10,7 +10,7 @@
 
 #include "tap.h"
 
-#define NR_TESTS 4
+#define NR_TESTS 5
 
 /*
  * Ensure the main executable has at least one TLS variable which will be
@@ -34,10 +34,22 @@
 {
 	struct rseq_abi *rseq_abi;
 
+	diag("Test the library init when libc rseq support is present but the syscall is unavailable");
 	plan_tests(NR_TESTS);
 
-	if (rseq_available(RSEQ_AVAILABLE_QUERY_KERNEL)) {
-		fail("The rseq syscall should be unavailable");
+	/*
+	 * Skip all tests if the libc doesn't have rseq support
+	 */
+	if (!rseq_available(RSEQ_AVAILABLE_QUERY_LIBC)) {
+		skip(NR_TESTS, "The libc doesn't have rseq support");
+		goto end;
+	}
+
+	if (rseq_init() == RSEQ_INIT_OK) {
+		pass("Initialize librseq")
+	} else {
+		fail("Initialize librseq")
+		skip(NR_TESTS - 1, "Error: librseq initialization failed");
 		goto end;
 	}
 
@@ -48,8 +60,8 @@
 	ok(rseq_offset != 0, "rseq_offset prior to registration is not 0 (%td)", rseq_offset);
 
 	rseq_abi = rseq_get_abi();
-	ok((int32_t) rseq_abi->cpu_id == RSEQ_ABI_CPU_ID_UNINITIALIZED,
-			"rseq->cpu_id is set to RSEQ_ABI_CPU_ID_UNINITIALIZED (%d)",
+	ok((int32_t) rseq_abi->cpu_id == RSEQ_ABI_CPU_ID_REGISTRATION_FAILED,
+			"rseq->cpu_id is set to RSEQ_ABI_CPU_ID_REGISTRATION_FAILED (%d)",
 			(int32_t) rseq_abi->cpu_id);
 
 end:
diff --git a/tests/param_test.c b/tests/param_test.c
index 5ff9982..ba5d264 100644
--- a/tests/param_test.c
+++ b/tests/param_test.c
@@ -472,9 +472,6 @@
 	struct spinlock_test_data __rseq_percpu *data = thread_data->data;
 	long long i, reps;
 
-	if (!opt_disable_rseq && thread_data->reg &&
-	    rseq_register_current_thread())
-		abort();
 	reps = thread_data->reps;
 	for (i = 0; i < reps; i++) {
 		int cpu = rseq_this_cpu_lock(&data->lock);
@@ -488,9 +485,6 @@
 	}
 	printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
 		       (int) rseq_gettid(), nr_abort, signals_delivered);
-	if (!opt_disable_rseq && thread_data->reg &&
-	    rseq_unregister_current_thread())
-		abort();
 	return NULL;
 }
 
@@ -580,9 +574,6 @@
 	struct inc_test_data __rseq_percpu *data = thread_data->data;
 	long long i, reps;
 
-	if (!opt_disable_rseq && thread_data->reg &&
-	    rseq_register_current_thread())
-		abort();
 	reps = thread_data->reps;
 	for (i = 0; i < reps; i++) {
 		int ret;
@@ -602,9 +593,6 @@
 	}
 	printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
 		       (int) rseq_gettid(), nr_abort, signals_delivered);
-	if (!opt_disable_rseq && thread_data->reg &&
-	    rseq_unregister_current_thread())
-		abort();
 	return NULL;
 }
 
@@ -771,9 +759,6 @@
 	long long i, reps;
 	struct percpu_list __rseq_percpu *list = (struct percpu_list __rseq_percpu *)arg;
 
-	if (!opt_disable_rseq && rseq_register_current_thread())
-		abort();
-
 	reps = opt_reps;
 	for (i = 0; i < reps; i++) {
 		struct percpu_list_node *node;
@@ -787,8 +772,6 @@
 
 	printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
 		       (int) rseq_gettid(), nr_abort, signals_delivered);
-	if (!opt_disable_rseq && rseq_unregister_current_thread())
-		abort();
 
 	return NULL;
 }
@@ -982,9 +965,6 @@
 	long long i, reps;
 	struct percpu_buffer __rseq_percpu *buffer = (struct percpu_buffer __rseq_percpu *)arg;
 
-	if (!opt_disable_rseq && rseq_register_current_thread())
-		abort();
-
 	reps = opt_reps;
 	for (i = 0; i < reps; i++) {
 		struct percpu_buffer_node *node;
@@ -1002,8 +982,6 @@
 
 	printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
 		       (int) rseq_gettid(), nr_abort, signals_delivered);
-	if (!opt_disable_rseq && rseq_unregister_current_thread())
-		abort();
 
 	return NULL;
 }
@@ -1225,9 +1203,6 @@
 	long long i, reps;
 	struct percpu_memcpy_buffer __rseq_percpu *buffer = (struct percpu_memcpy_buffer __rseq_percpu *)arg;
 
-	if (!opt_disable_rseq && rseq_register_current_thread())
-		abort();
-
 	reps = opt_reps;
 	for (i = 0; i < reps; i++) {
 		struct percpu_memcpy_buffer_node item;
@@ -1246,8 +1221,6 @@
 
 	printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n",
 		       (int) rseq_gettid(), nr_abort, signals_delivered);
-	if (!opt_disable_rseq && rseq_unregister_current_thread())
-		abort();
 
 	return NULL;
 }
@@ -1422,12 +1395,6 @@
 	const long long iters = opt_reps;
 	long long i;
 
-	if (rseq_register_current_thread()) {
-		fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
-			errno, strerror(errno));
-		abort();
-	}
-
 	/* Wait for initialization. */
 	while (!rseq_smp_load_acquire(&args->percpu_list_ptr)) { }
 
@@ -1444,11 +1411,6 @@
 		} while (rseq_unlikely(ret));
 	}
 
-	if (rseq_unregister_current_thread()) {
-		fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
-			errno, strerror(errno));
-		abort();
-	}
 	return NULL;
 }
 
@@ -1537,12 +1499,6 @@
 	args->max_nr_cpus = rseq_mempool_get_max_nr_cpus(mempool);
 	args->mempool = mempool;
 
-	if (rseq_register_current_thread()) {
-		fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
-			errno, strerror(errno));
-		abort();
-	}
-
 	/* Init lists. */
 	list_a = test_membarrier_alloc_percpu_list(mempool);
 	assert(list_a);
@@ -1612,11 +1568,6 @@
 	test_membarrier_free_percpu_list(args, list_a);
 	test_membarrier_free_percpu_list(args, list_b);
 
-	if (rseq_unregister_current_thread()) {
-		fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n",
-			errno, strerror(errno));
-		abort();
-	}
 	ret = rseq_mempool_destroy(mempool);
 	if (ret) {
 		perror("rseq_mempool_destroy");
@@ -1863,6 +1814,11 @@
 		}
 	}
 
+	if (rseq_init() != RSEQ_INIT_OK) {
+		printf_verbose("librseq initialization failed\n");
+		goto no_rseq;
+	}
+
 	loop_cnt_1 = loop_cnt[1];
 	loop_cnt_2 = loop_cnt[2];
 	loop_cnt_3 = loop_cnt[3];
@@ -1873,8 +1829,6 @@
 	if (set_signal_handler())
 		goto error;
 
-	if (!opt_disable_rseq && rseq_register_current_thread())
-		goto error;
 	if (!opt_disable_rseq && !rseq_validate_cpu_id()) {
 		printf_verbose("The rseq cpu id getter is unavailable\n");
 		goto no_rseq;
@@ -1905,8 +1859,6 @@
 		test_membarrier();
 		break;
 	}
-	if (!opt_disable_rseq && rseq_unregister_current_thread())
-		abort();
 end:
 	return 0;
 
diff --git a/tests/unregistered_test.c b/tests/unregistered_test.c
index 5aed17f..e722980 100644
--- a/tests/unregistered_test.c
+++ b/tests/unregistered_test.c
@@ -10,7 +10,7 @@
 
 #include "tap.h"
 
-#define NR_TESTS 4
+#define NR_TESTS 8
 
 /*
  * Ensure the main executable has at least one TLS variable which will be
@@ -32,22 +32,40 @@
 {
 	struct rseq_abi *rseq_abi;
 
+	diag("Test the library init when libc rseq support is present but disabled by tunable");
 	plan_tests(NR_TESTS);
 
-	if (!rseq_available(RSEQ_AVAILABLE_QUERY_KERNEL)) {
-		skip(NR_TESTS, "rseq syscall unavailable");
+	/*
+	 * Skip all tests if the libc doesn't have rseq support
+	 */
+	if (!rseq_available(RSEQ_AVAILABLE_QUERY_LIBC)) {
+		skip(NR_TESTS, "The libc doesn't have rseq support");
 		goto end;
 	}
 
-	/* The syscall is available but the current thread is not registered. */
+	/* Check the state of the library symbols before initialization. */
+	ok(rseq_flags == 0, "rseq_flags prior to library initialization is 0 (%d)", rseq_flags);
+	ok(rseq_size == -1U, "rseq_size prior to library initialization is -1U (%d)", rseq_size);
+	ok(rseq_offset == PTRDIFF_MIN, "rseq_offset prior to library initialization is PTRDIFF_MIN (%td)", rseq_offset);
 
-	ok(rseq_flags == 0, "rseq_flags prior to registration is 0 (%d)", rseq_flags);
-	ok(rseq_size == 0, "rseq_size prior to registration is 0 (%d)", rseq_size);
-	ok(rseq_offset != 0, "rseq_offset prior to registration is not 0 (%td)", rseq_offset);
+	/* Initialize librseq */
+	if (rseq_init() == RSEQ_INIT_OK) {
+		pass("Initialize librseq")
+	} else {
+		fail("Initialize librseq")
+		skip(NR_TESTS - 1, "Error: librseq initialization failed");
+		goto end;
+	}
 
+	/* Check the state of the library symbols after initialization. */
+	ok(rseq_flags == 0, "rseq_flags after library initialization is 0 (%d)", rseq_flags);
+	ok(rseq_size == 0, "rseq_size after library initialization is 0 (%d)", rseq_size);
+	ok(rseq_offset != 0, "rseq_offset after library initialization is not 0 (%td)", rseq_offset);
+
+	/* Check the state of the rseq area. */
 	rseq_abi = rseq_get_abi();
-	ok((int32_t) rseq_abi->cpu_id == RSEQ_ABI_CPU_ID_UNINITIALIZED,
-			"rseq->cpu_id is set to RSEQ_ABI_CPU_ID_UNINITIALIZED (%d)",
+	ok((int32_t) rseq_abi->cpu_id == RSEQ_ABI_CPU_ID_REGISTRATION_FAILED,
+			"rseq->cpu_id is set to RSEQ_ABI_CPU_ID_REGISTRATION_FAILED (%d)",
 			(int32_t) rseq_abi->cpu_id);
 
 end: