Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2010-2016 Wind River Systems, Inc.
3 : : *
4 : : * SPDX-License-Identifier: Apache-2.0
5 : : */
6 : :
7 : : /**
8 : : * @file
9 : : *
10 : : * @brief Kernel semaphore object.
11 : : *
12 : : * The semaphores are of the 'counting' type, i.e. each 'give' operation will
13 : : * increment the internal count by 1, if no thread is pending on it. The 'init'
14 : : * call initializes the count to 'initial_count'. Following multiple 'give'
15 : : * operations, the same number of 'take' operations can be performed without
16 : : * the calling thread having to pend on the semaphore, or the calling task
17 : : * having to poll.
18 : : */
19 : :
20 : : #include <zephyr/kernel.h>
21 : : #include <zephyr/kernel_structs.h>
22 : :
23 : : #include <zephyr/toolchain.h>
24 : : #include <wait_q.h>
25 : : #include <zephyr/sys/dlist.h>
26 : : #include <ksched.h>
27 : : #include <zephyr/init.h>
28 : : #include <zephyr/internal/syscall_handler.h>
29 : : #include <zephyr/tracing/tracing.h>
30 : : #include <zephyr/sys/check.h>
31 : :
32 : : /* We use a system-wide lock to synchronize semaphores, which has
33 : : * unfortunate performance impact vs. using a per-object lock
34 : : * (semaphores are *very* widely used). But per-object locks require
35 : : * significant extra RAM. A properly spin-aware semaphore
36 : : * implementation would spin on atomic access to the count variable,
37 : : * and not a spinlock per se. Useful optimization for the future...
38 : : */
39 : : static struct k_spinlock lock;
40 : :
41 : : #ifdef CONFIG_OBJ_CORE_SEM
42 : : static struct k_obj_type obj_type_sem;
43 : : #endif /* CONFIG_OBJ_CORE_SEM */
44 : :
45 : 0 : int z_impl_k_sem_init(struct k_sem *sem, unsigned int initial_count,
46 : : unsigned int limit)
47 : : {
48 : : /*
49 : : * Limit cannot be zero and count cannot be greater than limit
50 : : */
51 [ # # ]: 0 : CHECKIF(limit == 0U || initial_count > limit) {
52 : : SYS_PORT_TRACING_OBJ_FUNC(k_sem, init, sem, -EINVAL);
53 : :
54 : : return -EINVAL;
55 : : }
56 : :
57 : 0 : sem->count = initial_count;
58 : 0 : sem->limit = limit;
59 : :
60 : 0 : SYS_PORT_TRACING_OBJ_FUNC(k_sem, init, sem, 0);
61 : :
62 : 0 : z_waitq_init(&sem->wait_q);
63 : : #if defined(CONFIG_POLL)
64 : : sys_dlist_init(&sem->poll_events);
65 : : #endif /* CONFIG_POLL */
66 : 0 : k_object_init(sem);
67 : :
68 : : #ifdef CONFIG_OBJ_CORE_SEM
69 : : k_obj_core_init_and_link(K_OBJ_CORE(sem), &obj_type_sem);
70 : : #endif /* CONFIG_OBJ_CORE_SEM */
71 : :
72 : 0 : return 0;
73 : : }
74 : :
75 : : #ifdef CONFIG_USERSPACE
76 : : int z_vrfy_k_sem_init(struct k_sem *sem, unsigned int initial_count,
77 : : unsigned int limit)
78 : : {
79 : : K_OOPS(K_SYSCALL_OBJ_INIT(sem, K_OBJ_SEM));
80 : : return z_impl_k_sem_init(sem, initial_count, limit);
81 : : }
82 : : #include <zephyr/syscalls/k_sem_init_mrsh.c>
83 : : #endif /* CONFIG_USERSPACE */
84 : :
85 : 2 : static inline bool handle_poll_events(struct k_sem *sem)
86 : : {
87 : : #ifdef CONFIG_POLL
88 : : z_handle_obj_poll_events(&sem->poll_events, K_POLL_STATE_SEM_AVAILABLE);
89 : : return true;
90 : : #else
91 : 2 : ARG_UNUSED(sem);
92 : 2 : return false;
93 : : #endif /* CONFIG_POLL */
94 : : }
95 : :
96 : 6 : void z_impl_k_sem_give(struct k_sem *sem)
97 : : {
98 : 6 : k_spinlock_key_t key = k_spin_lock(&lock);
99 : 6 : struct k_thread *thread;
100 : 6 : bool resched = true;
101 : :
102 : 6 : SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_sem, give, sem);
103 : :
104 : 6 : thread = z_unpend_first_thread(&sem->wait_q);
105 : :
106 [ + + ]: 6 : if (thread != NULL) {
107 : 4 : arch_thread_return_value_set(thread, 0);
108 : 4 : z_ready_thread(thread);
109 : : } else {
110 [ - + ]: 2 : sem->count += (sem->count != sem->limit) ? 1U : 0U;
111 : 2 : resched = handle_poll_events(sem);
112 : : }
113 : :
114 [ - + ]: 6 : if (resched) {
115 : 4 : z_reschedule(&lock, key);
116 : : } else {
117 : 2 : k_spin_unlock(&lock, key);
118 : : }
119 : :
120 : 6 : SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_sem, give, sem);
121 : 6 : }
122 : :
123 : : #ifdef CONFIG_USERSPACE
124 : : static inline void z_vrfy_k_sem_give(struct k_sem *sem)
125 : : {
126 : : K_OOPS(K_SYSCALL_OBJ(sem, K_OBJ_SEM));
127 : : z_impl_k_sem_give(sem);
128 : : }
129 : : #include <zephyr/syscalls/k_sem_give_mrsh.c>
130 : : #endif /* CONFIG_USERSPACE */
131 : :
132 : 6 : int z_impl_k_sem_take(struct k_sem *sem, k_timeout_t timeout)
133 : : {
134 : 6 : int ret;
135 : :
136 [ - + - - ]: 6 : __ASSERT(((arch_is_in_isr() == false) ||
137 : : K_TIMEOUT_EQ(timeout, K_NO_WAIT)), "");
138 : :
139 : 6 : k_spinlock_key_t key = k_spin_lock(&lock);
140 : :
141 : 6 : SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_sem, take, sem, timeout);
142 : :
143 [ + + ]: 6 : if (likely(sem->count > 0U)) {
144 : 2 : sem->count--;
145 : 2 : k_spin_unlock(&lock, key);
146 : 2 : ret = 0;
147 : 2 : goto out;
148 : : }
149 : :
150 [ - + ]: 4 : if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
151 : 0 : k_spin_unlock(&lock, key);
152 : 0 : ret = -EBUSY;
153 : 0 : goto out;
154 : : }
155 : :
156 : 4 : SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_sem, take, sem, timeout);
157 : :
158 : 4 : ret = z_pend_curr(&lock, key, &sem->wait_q, timeout);
159 : :
160 : 6 : out:
161 : 6 : SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_sem, take, sem, timeout, ret);
162 : :
163 : 6 : return ret;
164 : : }
165 : :
166 : 0 : void z_impl_k_sem_reset(struct k_sem *sem)
167 : : {
168 : 0 : struct k_thread *thread;
169 : 0 : k_spinlock_key_t key = k_spin_lock(&lock);
170 : :
171 : 0 : while (true) {
172 : 0 : thread = z_unpend_first_thread(&sem->wait_q);
173 [ # # ]: 0 : if (thread == NULL) {
174 : : break;
175 : : }
176 : 0 : arch_thread_return_value_set(thread, -EAGAIN);
177 : 0 : z_ready_thread(thread);
178 : : }
179 : 0 : sem->count = 0;
180 : :
181 : 0 : SYS_PORT_TRACING_OBJ_FUNC(k_sem, reset, sem);
182 : :
183 : 0 : handle_poll_events(sem);
184 : :
185 : 0 : z_reschedule(&lock, key);
186 : 0 : }
187 : :
188 : : #ifdef CONFIG_USERSPACE
189 : : static inline int z_vrfy_k_sem_take(struct k_sem *sem, k_timeout_t timeout)
190 : : {
191 : : K_OOPS(K_SYSCALL_OBJ(sem, K_OBJ_SEM));
192 : : return z_impl_k_sem_take(sem, timeout);
193 : : }
194 : : #include <zephyr/syscalls/k_sem_take_mrsh.c>
195 : :
196 : : static inline void z_vrfy_k_sem_reset(struct k_sem *sem)
197 : : {
198 : : K_OOPS(K_SYSCALL_OBJ(sem, K_OBJ_SEM));
199 : : z_impl_k_sem_reset(sem);
200 : : }
201 : : #include <zephyr/syscalls/k_sem_reset_mrsh.c>
202 : :
203 : : static inline unsigned int z_vrfy_k_sem_count_get(struct k_sem *sem)
204 : : {
205 : : K_OOPS(K_SYSCALL_OBJ(sem, K_OBJ_SEM));
206 : : return z_impl_k_sem_count_get(sem);
207 : : }
208 : : #include <zephyr/syscalls/k_sem_count_get_mrsh.c>
209 : :
210 : : #endif /* CONFIG_USERSPACE */
211 : :
212 : : #ifdef CONFIG_OBJ_CORE_SEM
213 : : static int init_sem_obj_core_list(void)
214 : : {
215 : : /* Initialize semaphore object type */
216 : :
217 : : z_obj_type_init(&obj_type_sem, K_OBJ_TYPE_SEM_ID,
218 : : offsetof(struct k_sem, obj_core));
219 : :
220 : : /* Initialize and link statically defined semaphores */
221 : :
222 : : STRUCT_SECTION_FOREACH(k_sem, sem) {
223 : : k_obj_core_init_and_link(K_OBJ_CORE(sem), &obj_type_sem);
224 : : }
225 : :
226 : : return 0;
227 : : }
228 : :
229 : : SYS_INIT(init_sem_obj_core_list, PRE_KERNEL_1,
230 : : CONFIG_KERNEL_INIT_PRIORITY_OBJECTS);
231 : : #endif /* CONFIG_OBJ_CORE_SEM */
|