1 /*
2 * Copyright (c) 2019 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/sys/sem.h>
8 #include <zephyr/internal/syscall_handler.h>
9
10 #ifdef CONFIG_USERSPACE
11 #define SYS_SEM_MINIMUM 0
12 #define SYS_SEM_CONTENDED (SYS_SEM_MINIMUM - 1)
13
bounded_dec(atomic_t * val,atomic_t minimum)14 static inline atomic_t bounded_dec(atomic_t *val, atomic_t minimum)
15 {
16 atomic_t old_value, new_value;
17
18 do {
19 old_value = atomic_get(val);
20 if (old_value < minimum) {
21 break;
22 }
23
24 new_value = old_value - 1;
25 } while (atomic_cas(val, old_value, new_value) == 0);
26
27 return old_value;
28 }
29
bounded_inc(atomic_t * val,atomic_t minimum,atomic_t maximum)30 static inline atomic_t bounded_inc(atomic_t *val, atomic_t minimum,
31 atomic_t maximum)
32 {
33 atomic_t old_value, new_value;
34
35 do {
36 old_value = atomic_get(val);
37 if (old_value >= maximum) {
38 break;
39 }
40
41 new_value = ((old_value < minimum) ? minimum : old_value) + 1;
42 } while (atomic_cas(val, old_value, new_value) == 0U);
43
44 return old_value;
45 }
46
sys_sem_init(struct sys_sem * sem,unsigned int initial_count,unsigned int limit)47 int sys_sem_init(struct sys_sem *sem, unsigned int initial_count,
48 unsigned int limit)
49 {
50 if ((sem == NULL) || (limit == SYS_SEM_MINIMUM) ||
51 (initial_count > limit) || (limit > INT_MAX)) {
52 return -EINVAL;
53 }
54
55 (void)atomic_set(&sem->futex.val, initial_count);
56 sem->limit = limit;
57
58 return 0;
59 }
60
sys_sem_give(struct sys_sem * sem)61 int sys_sem_give(struct sys_sem *sem)
62 {
63 int ret = 0;
64 atomic_t old_value;
65
66 old_value = bounded_inc(&sem->futex.val,
67 SYS_SEM_MINIMUM, sem->limit);
68 if (old_value < 0) {
69 ret = k_futex_wake(&sem->futex, true);
70
71 if (ret > 0) {
72 return 0;
73 }
74 } else if (old_value >= sem->limit) {
75 return -EAGAIN;
76 } else {
77 ;
78 }
79 return ret;
80 }
81
sys_sem_take(struct sys_sem * sem,k_timeout_t timeout)82 int sys_sem_take(struct sys_sem *sem, k_timeout_t timeout)
83 {
84 int ret = 0;
85 atomic_t old_value;
86
87 do {
88 old_value = bounded_dec(&sem->futex.val,
89 SYS_SEM_MINIMUM);
90 if (old_value > 0) {
91 return 0;
92 }
93
94 ret = k_futex_wait(&sem->futex,
95 SYS_SEM_CONTENDED, timeout);
96 } while (ret == 0 || ret == -EAGAIN);
97
98 return ret;
99 }
100
sys_sem_count_get(struct sys_sem * sem)101 unsigned int sys_sem_count_get(struct sys_sem *sem)
102 {
103 int value = atomic_get(&sem->futex.val);
104
105 return (value > SYS_SEM_MINIMUM) ? value : SYS_SEM_MINIMUM;
106 }
107 #else
sys_sem_init(struct sys_sem * sem,unsigned int initial_count,unsigned int limit)108 int sys_sem_init(struct sys_sem *sem, unsigned int initial_count,
109 unsigned int limit)
110 {
111 k_sem_init(&sem->kernel_sem, initial_count, limit);
112
113 return 0;
114 }
115
sys_sem_give(struct sys_sem * sem)116 int sys_sem_give(struct sys_sem *sem)
117 {
118 k_sem_give(&sem->kernel_sem);
119
120 return 0;
121 }
122
sys_sem_take(struct sys_sem * sem,k_timeout_t timeout)123 int sys_sem_take(struct sys_sem *sem, k_timeout_t timeout)
124 {
125 int ret_value = 0;
126
127 ret_value = k_sem_take(&sem->kernel_sem, timeout);
128 if ((ret_value == -EAGAIN) || (ret_value == -EBUSY)) {
129 ret_value = -ETIMEDOUT;
130 }
131
132 return ret_value;
133 }
134
sys_sem_count_get(struct sys_sem * sem)135 unsigned int sys_sem_count_get(struct sys_sem *sem)
136 {
137 return k_sem_count_get(&sem->kernel_sem);
138 }
139 #endif
140