Branch data Line data Source code
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 : :
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 : :
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 : :
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 : :
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 : :
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 : :
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
108 : 0 : int sys_sem_init(struct sys_sem *sem, unsigned int initial_count,
109 : : unsigned int limit)
110 : : {
111 : 0 : k_sem_init(&sem->kernel_sem, initial_count, limit);
112 : :
113 : 0 : return 0;
114 : : }
115 : :
116 : 0 : int sys_sem_give(struct sys_sem *sem)
117 : : {
118 : 0 : k_sem_give(&sem->kernel_sem);
119 : :
120 : 0 : return 0;
121 : : }
122 : :
123 : 0 : int sys_sem_take(struct sys_sem *sem, k_timeout_t timeout)
124 : : {
125 : 0 : int ret_value = 0;
126 : :
127 : 0 : ret_value = k_sem_take(&sem->kernel_sem, timeout);
128 [ # # ]: 0 : if ((ret_value == -EAGAIN) || (ret_value == -EBUSY)) {
129 : 0 : ret_value = -ETIMEDOUT;
130 : : }
131 : :
132 : 0 : return ret_value;
133 : : }
134 : :
135 : 0 : unsigned int sys_sem_count_get(struct sys_sem *sem)
136 : : {
137 : 0 : return k_sem_count_get(&sem->kernel_sem);
138 : : }
139 : : #endif
|