In order to integrate rseq into user-space applications, add a reference
counter field after the struct rseq TLS ABI so many rseq users can be
linked into the same application (e.g. librseq and glibc). The
reference count ensures that rseq syscall registration/unregistration
happens only for the most early/late user for each thread, thus ensuring
that rseq is registered across the lifetime of all rseq users for a
given thread.

Therefore, struct rseq contains the fields shared between kernel
and user-space, and represents the ABI between kernel and user-space.
The extra field added after struct rseq is an ABI between user-space
executable and libraries, but the kernel does not care about that field,
so it is not part of the Linux uapi.

Signed-off-by: Mathieu Desnoyers <[email protected]>
CC: Shuah Khan <[email protected]>
CC: Carlos O'Donell <[email protected]>
CC: Florian Weimer <[email protected]>
CC: Joseph Myers <[email protected]>
CC: Szabolcs Nagy <[email protected]>
CC: Thomas Gleixner <[email protected]>
CC: Ben Maurer <[email protected]>
CC: Peter Zijlstra <[email protected]>
CC: "Paul E. McKenney" <[email protected]>
CC: Boqun Feng <[email protected]>
CC: Will Deacon <[email protected]>
CC: Dave Watson <[email protected]>
CC: Paul Turner <[email protected]>
CC: [email protected]
---
 tools/testing/selftests/rseq/rseq.c | 32 ++++++++++++++++++++++++--------
 1 file changed, 24 insertions(+), 8 deletions(-)

diff --git a/tools/testing/selftests/rseq/rseq.c 
b/tools/testing/selftests/rseq/rseq.c
index 4847e97ed049..7e9ae973f786 100644
--- a/tools/testing/selftests/rseq/rseq.c
+++ b/tools/testing/selftests/rseq/rseq.c
@@ -30,13 +30,29 @@
 
 #define ARRAY_SIZE(arr)        (sizeof(arr) / sizeof((arr)[0]))
 
-__attribute__((tls_model("initial-exec"))) __thread
-volatile struct rseq __rseq_abi = {
+/*
+ * linux/rseq.h defines struct rseq as aligned on 32 bytes. The kernel ABI
+ * size is 20 bytes. For support of multiple rseq users within a process,
+ * user-space defines an extra 4 bytes field as a reference count, for a
+ * total of 24 bytes.
+ */
+struct libc_rseq {
+       /* kernel-userspace ABI. */
+       __u32 cpu_id_start;
+       __u32 cpu_id;
+       __u64 rseq_cs;
+       __u32 flags;
+       /* user-space ABI. */
+       __u32 refcount;
+} __attribute__((aligned(4 * sizeof(__u64))));
+
+__attribute__((visibility("hidden"))) __thread
+volatile struct libc_rseq __lib_rseq_abi = {
        .cpu_id = RSEQ_CPU_ID_UNINITIALIZED,
 };
 
-static __attribute__((tls_model("initial-exec"))) __thread
-volatile int refcount;
+extern __attribute__((weak, alias("__lib_rseq_abi"))) __thread
+volatile struct rseq __rseq_abi;
 
 static void signal_off_save(sigset_t *oldset)
 {
@@ -70,7 +86,7 @@ int rseq_register_current_thread(void)
        sigset_t oldset;
 
        signal_off_save(&oldset);
-       if (refcount++)
+       if (__lib_rseq_abi.refcount++)
                goto end;
        rc = sys_rseq(&__rseq_abi, sizeof(struct rseq), 0, RSEQ_SIG);
        if (!rc) {
@@ -78,9 +94,9 @@ int rseq_register_current_thread(void)
                goto end;
        }
        if (errno != EBUSY)
-               __rseq_abi.cpu_id = -2;
+               __rseq_abi.cpu_id = RSEQ_CPU_ID_REGISTRATION_FAILED;
        ret = -1;
-       refcount--;
+       __lib_rseq_abi.refcount--;
 end:
        signal_restore(oldset);
        return ret;
@@ -92,7 +108,7 @@ int rseq_unregister_current_thread(void)
        sigset_t oldset;
 
        signal_off_save(&oldset);
-       if (--refcount)
+       if (--__lib_rseq_abi.refcount)
                goto end;
        rc = sys_rseq(&__rseq_abi, sizeof(struct rseq),
                      RSEQ_FLAG_UNREGISTER, RSEQ_SIG);
-- 
2.11.0

Reply via email to