aboutsummaryrefslogtreecommitdiff
path: root/newlib/libc/sys/amdgcn/lock.c
diff options
context:
space:
mode:
Diffstat (limited to 'newlib/libc/sys/amdgcn/lock.c')
-rw-r--r--newlib/libc/sys/amdgcn/lock.c187
1 files changed, 187 insertions, 0 deletions
diff --git a/newlib/libc/sys/amdgcn/lock.c b/newlib/libc/sys/amdgcn/lock.c
new file mode 100644
index 0000000..dcc93cb
--- /dev/null
+++ b/newlib/libc/sys/amdgcn/lock.c
@@ -0,0 +1,187 @@
+/*
+ * Support file for amdgcn in newlib.
+ * Copyright (c) 2024 BayLibre.
+ *
+ * The authors hereby grant permission to use, copy, modify, distribute,
+ * and license this software and its documentation for any purpose, provided
+ * that existing copyright notices are retained in all copies and that this
+ * notice is included verbatim in any distributions. No written agreement,
+ * license, or royalty fee is required for any of the authorized uses.
+ * Modifications to this software may be copyrighted by their authors
+ * and need not follow the licensing terms described here, provided that
+ * the new terms are clearly indicated on the first page of each file where
+ * they apply.
+ */
+
+/* Lock routines for AMD GPU devices.
+
+ The lock is a 32-bit int:
+ - bits 0-3: wavefront id
+ - bits 4-23: workgroup id (+1, so never zero)
+ - bits 24-31: recursive lock count.
+
+ The purpose of the "relaxed" loads and stores being "atomic" here is
+ mostly just to ensure we punch through the caches consistently.
+
+ Non-recursive locks may be unlocked by any thread. It's an error to
+ attempt to unlock a recursive lock from the wrong thread.
+
+ The DEBUG statements here use sprintf and write to avoid taking locks
+ themselves. */
+
+#include <sys/lock.h>
+#include <assert.h>
+
+#define DEBUG 0
+
+#if DEBUG
+extern void write(int, char *, int);
+#endif
+
+static unsigned
+__gcn_thread_id ()
+{
+ /* Dim(0) is the workgroup ID; range 0 to maybe thousands.
+ Dim(1) is the wavefront ID; range 0 to 15. */
+ return (((__builtin_gcn_dim_pos (0) + 1) << 4)
+ + __builtin_gcn_dim_pos (1));
+}
+
+static int
+__gcn_lock_acquire_int (_LOCK_T *lock_ptr, int _try)
+{
+ int id = __gcn_thread_id ();
+
+#if DEBUG
+ char buf[1000];
+ __builtin_sprintf (buf,"acquire:%p(%d) lock_value:0x%x id:0x%x", lock_ptr,
+ _try, *lock_ptr, id);
+ write (1, buf, __builtin_strlen(buf));
+#endif
+
+ int expected = 0;
+ while (!__atomic_compare_exchange_n (lock_ptr, &expected, id, 0,
+ __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
+ {
+ /* Lock *not* acquired. */
+ if (_try)
+ return 0;
+ else
+ {
+ asm ("s_sleep 64");
+ expected = 0;
+ }
+ }
+
+#if DEBUG
+ __builtin_sprintf (buf,"acquired:%p(%d) lock_value:0x%x id:0x%x", lock_ptr,
+ _try, *lock_ptr, id);
+ write (1, buf, __builtin_strlen(buf));
+#endif
+
+ return 1;
+}
+
+int
+__gcn_try_lock_acquire (_LOCK_T *lock_ptr)
+{
+ return __gcn_lock_acquire_int (lock_ptr, 1);
+}
+
+void
+__gcn_lock_acquire (_LOCK_T *lock_ptr)
+{
+ __gcn_lock_acquire_int (lock_ptr, 0);
+}
+
+static int
+__gcn_lock_acquire_recursive_int (_LOCK_T *lock_ptr, int _try)
+{
+ int id = __gcn_thread_id ();
+
+#if DEBUG
+ char buf[1000];
+ __builtin_sprintf (buf,"acquire recursive:%p(%d) lock_value:0x%x id:0x%x",
+ lock_ptr, _try, *lock_ptr, id);
+ write (1, buf, __builtin_strlen(buf));
+#endif
+
+ unsigned int lock_value = __atomic_load_n (lock_ptr, __ATOMIC_RELAXED);
+ if ((lock_value & 0xffffff) == id)
+ {
+ /* This thread already holds the lock.
+ Increment the recursion counter and update the lock. */
+ int count = lock_value >> 24;
+ lock_value = ((count + 1) << 24) | id;
+ __atomic_store_n (lock_ptr, lock_value, __ATOMIC_RELAXED);
+
+#if DEBUG
+ __builtin_sprintf (buf,
+ "increment recursive:%p(%d) lock_value:0x%x id:0x%x",
+ lock_ptr, _try, *lock_ptr, id);
+ write (1, buf, __builtin_strlen(buf));
+#endif
+
+ return 1;
+ }
+ else
+ return __gcn_lock_acquire_int (lock_ptr, _try);
+}
+
+int
+__gcn_lock_try_acquire_recursive (_LOCK_T *lock_ptr)
+{
+ return __gcn_lock_acquire_recursive_int (lock_ptr, 1);
+}
+
+void
+__gcn_lock_acquire_recursive (_LOCK_T *lock_ptr)
+{
+ __gcn_lock_acquire_recursive_int (lock_ptr, 0);
+}
+
+void
+__gcn_lock_release (_LOCK_T *lock_ptr)
+{
+#if DEBUG
+ char buf[1000];
+ __builtin_sprintf (buf,"release:%p lock_value:0x%x id:0x%x", lock_ptr,
+ *lock_ptr, __gcn_thread_id());
+ write (1, buf, __builtin_strlen(buf));
+#endif
+
+ __atomic_store_n (lock_ptr, 0, __ATOMIC_RELEASE);
+}
+
+void
+__gcn_lock_release_recursive (_LOCK_T *lock_ptr)
+{
+ int id = __gcn_thread_id ();
+ unsigned int lock_value = __atomic_load_n (lock_ptr, __ATOMIC_RELAXED);
+
+#if DEBUG
+ char buf[1000];
+ __builtin_sprintf (buf, "release recursive:%p lock_value:0x%x id:0x%x",
+ lock_ptr, lock_value, id);
+ write (1, buf, __builtin_strlen(buf));
+#endif
+
+ /* It is an error to call this function from the wrong thread. */
+ assert ((lock_value & 0xffffff) == id);
+
+ /* Decrement or release the lock. */
+ int count = lock_value >> 24;
+ if (count > 0)
+ {
+ lock_value = ((count - 1) << 24) | id;
+ __atomic_store_n (lock_ptr, lock_value, __ATOMIC_RELAXED);
+
+#if DEBUG
+ __builtin_sprintf (buf, "decrement recursive:%p lock_value:0x%x id:0x%x",
+ lock_ptr, *lock_ptr, id);
+ write (1, buf, __builtin_strlen(buf));
+#endif
+ }
+ else
+ __gcn_lock_release (lock_ptr);
+}