1 /*
2 * Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 /*
8 * @file linux/mutex.h
9 * @brief Linux mutex primitives for libmetal.
10 */
11
12 #ifndef __METAL_MUTEX__H__
13 #error "Include metal/mutex.h instead of metal/linux/mutex.h"
14 #endif
15
16 #ifndef __METAL_LINUX_MUTEX__H__
17 #define __METAL_LINUX_MUTEX__H__
18
19 #include <unistd.h>
20 #include <sys/syscall.h>
21 #include <linux/futex.h>
22
23 #include <metal/atomic.h>
24
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28
29 typedef struct {
30 atomic_int v;
31 } metal_mutex_t;
32
33 /*
34 * METAL_MUTEX_INIT - used for initializing an mutex element in a static struct
35 * or global
36 */
37 #define METAL_MUTEX_INIT(m) { ATOMIC_VAR_INIT(0) }
38 /*
39 * METAL_MUTEX_DEFINE - used for defining and initializing a global or
40 * static singleton mutex
41 */
42 #define METAL_MUTEX_DEFINE(m) metal_mutex_t m = METAL_MUTEX_INIT(m)
43
__metal_mutex_cmpxchg(metal_mutex_t * mutex,int exp,int val)44 static inline int __metal_mutex_cmpxchg(metal_mutex_t *mutex,
45 int exp, int val)
46 {
47 atomic_compare_exchange_strong(&mutex->v, (int *)&exp, val);
48 return exp;
49 }
50
__metal_mutex_init(metal_mutex_t * mutex)51 static inline void __metal_mutex_init(metal_mutex_t *mutex)
52 {
53 atomic_store(&mutex->v, 0);
54 }
55
__metal_mutex_deinit(metal_mutex_t * mutex)56 static inline void __metal_mutex_deinit(metal_mutex_t *mutex)
57 {
58 (void)mutex;
59 }
60
__metal_mutex_try_acquire(metal_mutex_t * mutex)61 static inline int __metal_mutex_try_acquire(metal_mutex_t *mutex)
62 {
63 int val = 0;
64
65 return atomic_compare_exchange_strong(&mutex->v, &val, 1);
66 }
67
__metal_mutex_acquire(metal_mutex_t * mutex)68 static inline void __metal_mutex_acquire(metal_mutex_t *mutex)
69 {
70 int c = 0;
71
72 if (atomic_compare_exchange_strong(&mutex->v, &c, 1))
73 return;
74 if (c != 2)
75 c = atomic_exchange(&mutex->v, 2);
76 while (c != 0) {
77 syscall(SYS_futex, &mutex->v, FUTEX_WAIT, 2, NULL, NULL, 0);
78 c = atomic_exchange(&mutex->v, 2);
79 }
80 }
81
__metal_mutex_release(metal_mutex_t * mutex)82 static inline void __metal_mutex_release(metal_mutex_t *mutex)
83 {
84 if (atomic_fetch_sub(&mutex->v, 1) != 1) {
85 atomic_store(&mutex->v, 0);
86 syscall(SYS_futex, &mutex->v, FUTEX_WAKE, 1, NULL, NULL, 0);
87 }
88 }
89
__metal_mutex_is_acquired(metal_mutex_t * mutex)90 static inline int __metal_mutex_is_acquired(metal_mutex_t *mutex)
91 {
92 return atomic_load(&mutex->v);
93 }
94
95 #ifdef __cplusplus
96 }
97 #endif
98
99 #endif /* __METAL_LINUX_MUTEX__H__ */
100