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 <kernel.h>
21 : : #include <kernel_structs.h>
22 : :
23 : : #include <toolchain.h>
24 : : #include <wait_q.h>
25 : : #include <sys/dlist.h>
26 : : #include <ksched.h>
27 : : #include <init.h>
28 : : #include <syscall_handler.h>
29 : : #include <tracing/tracing.h>
30 : : #include <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 : 0 : int z_impl_k_sem_init(struct k_sem *sem, unsigned int initial_count,
42 : : unsigned int limit)
43 : : {
44 : : /*
45 : : * Limit cannot be zero and count cannot be greater than limit
46 : : */
47 [ # # # # ]: 0 : CHECKIF(limit == 0U || limit > K_SEM_MAX_LIMIT || initial_count > limit) {
48 : : SYS_PORT_TRACING_OBJ_FUNC(k_sem, init, sem, -EINVAL);
49 : :
50 : 0 : return -EINVAL;
51 : : }
52 : :
53 : 0 : sem->count = initial_count;
54 : 0 : sem->limit = limit;
55 : :
56 : : SYS_PORT_TRACING_OBJ_FUNC(k_sem, init, sem, 0);
57 : :
58 : 0 : z_waitq_init(&sem->wait_q);
59 : : #if defined(CONFIG_POLL)
60 : : sys_dlist_init(&sem->poll_events);
61 : : #endif
62 : 0 : z_object_init(sem);
63 : :
64 : 0 : return 0;
65 : : }
66 : :
67 : : #ifdef CONFIG_USERSPACE
68 : : int z_vrfy_k_sem_init(struct k_sem *sem, unsigned int initial_count,
69 : : unsigned int limit)
70 : : {
71 : : Z_OOPS(Z_SYSCALL_OBJ_INIT(sem, K_OBJ_SEM));
72 : : return z_impl_k_sem_init(sem, initial_count, limit);
73 : : }
74 : : #include <syscalls/k_sem_init_mrsh.c>
75 : : #endif
76 : :
77 : 0 : static inline void handle_poll_events(struct k_sem *sem)
78 : : {
79 : : #ifdef CONFIG_POLL
80 : : z_handle_obj_poll_events(&sem->poll_events, K_POLL_STATE_SEM_AVAILABLE);
81 : : #else
82 : : ARG_UNUSED(sem);
83 : : #endif
84 : 0 : }
85 : :
86 : 0 : void z_impl_k_sem_give(struct k_sem *sem)
87 : : {
88 : 0 : k_spinlock_key_t key = k_spin_lock(&lock);
89 : : struct k_thread *thread;
90 : :
91 : : SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_sem, give, sem);
92 : :
93 : 0 : thread = z_unpend_first_thread(&sem->wait_q);
94 : :
95 [ # # ]: 0 : if (thread != NULL) {
96 : 0 : arch_thread_return_value_set(thread, 0);
97 : 0 : z_ready_thread(thread);
98 : : } else {
99 [ # # ]: 0 : sem->count += (sem->count != sem->limit) ? 1U : 0U;
100 : 0 : handle_poll_events(sem);
101 : : }
102 : :
103 : 0 : z_reschedule(&lock, key);
104 : :
105 : : SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_sem, give, sem);
106 : 0 : }
107 : :
108 : : #ifdef CONFIG_USERSPACE
109 : : static inline void z_vrfy_k_sem_give(struct k_sem *sem)
110 : : {
111 : : Z_OOPS(Z_SYSCALL_OBJ(sem, K_OBJ_SEM));
112 : : z_impl_k_sem_give(sem);
113 : : }
114 : : #include <syscalls/k_sem_give_mrsh.c>
115 : : #endif
116 : :
117 : 0 : int z_impl_k_sem_take(struct k_sem *sem, k_timeout_t timeout)
118 : : {
119 : 0 : int ret = 0;
120 : :
121 [ # # # # ]: 0 : __ASSERT(((arch_is_in_isr() == false) ||
122 : : K_TIMEOUT_EQ(timeout, K_NO_WAIT)), "");
123 : :
124 : 0 : k_spinlock_key_t key = k_spin_lock(&lock);
125 : :
126 : : SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_sem, take, sem, timeout);
127 : :
128 [ # # ]: 0 : if (likely(sem->count > 0U)) {
129 : 0 : sem->count--;
130 : 0 : k_spin_unlock(&lock, key);
131 : 0 : ret = 0;
132 : 0 : goto out;
133 : : }
134 : :
135 [ # # ]: 0 : if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
136 : 0 : k_spin_unlock(&lock, key);
137 : 0 : ret = -EBUSY;
138 : 0 : goto out;
139 : : }
140 : :
141 : : SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_sem, take, sem, timeout);
142 : :
143 : 0 : ret = z_pend_curr(&lock, key, &sem->wait_q, timeout);
144 : :
145 : 0 : out:
146 : : SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_sem, take, sem, timeout, ret);
147 : :
148 : 0 : return ret;
149 : : }
150 : :
151 : 0 : void z_impl_k_sem_reset(struct k_sem *sem)
152 : : {
153 : : struct k_thread *thread;
154 : 0 : k_spinlock_key_t key = k_spin_lock(&lock);
155 : :
156 : : while (true) {
157 : 0 : thread = z_unpend_first_thread(&sem->wait_q);
158 [ # # ]: 0 : if (thread == NULL) {
159 : 0 : break;
160 : : }
161 : 0 : arch_thread_return_value_set(thread, -EAGAIN);
162 : 0 : z_ready_thread(thread);
163 : : }
164 : 0 : sem->count = 0;
165 : :
166 : : SYS_PORT_TRACING_OBJ_FUNC(k_sem, reset, sem);
167 : :
168 : 0 : handle_poll_events(sem);
169 : :
170 : 0 : z_reschedule(&lock, key);
171 : 0 : }
172 : :
173 : : #ifdef CONFIG_USERSPACE
174 : : static inline int z_vrfy_k_sem_take(struct k_sem *sem, k_timeout_t timeout)
175 : : {
176 : : Z_OOPS(Z_SYSCALL_OBJ(sem, K_OBJ_SEM));
177 : : return z_impl_k_sem_take((struct k_sem *)sem, timeout);
178 : : }
179 : : #include <syscalls/k_sem_take_mrsh.c>
180 : :
181 : : static inline void z_vrfy_k_sem_reset(struct k_sem *sem)
182 : : {
183 : : Z_OOPS(Z_SYSCALL_OBJ(sem, K_OBJ_SEM));
184 : : z_impl_k_sem_reset(sem);
185 : : }
186 : : #include <syscalls/k_sem_reset_mrsh.c>
187 : :
188 : : static inline unsigned int z_vrfy_k_sem_count_get(struct k_sem *sem)
189 : : {
190 : : Z_OOPS(Z_SYSCALL_OBJ(sem, K_OBJ_SEM));
191 : : return z_impl_k_sem_count_get(sem);
192 : : }
193 : : #include <syscalls/k_sem_count_get_mrsh.c>
194 : :
195 : : #endif
|