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