Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | /* * Copyright (c) 2010-2016 Wind River Systems, Inc. * * SPDX-License-Identifier: Apache-2.0 */ /** * @file * * @brief Kernel semaphore object. * * The semaphores are of the 'counting' type, i.e. each 'give' operation will * increment the internal count by 1, if no thread is pending on it. The 'init' * call initializes the count to 'initial_count'. Following multiple 'give' * operations, the same number of 'take' operations can be performed without * the calling thread having to pend on the semaphore, or the calling task * having to poll. */ #include <kernel.h> #include <kernel_structs.h> #include <debug/object_tracing_common.h> #include <toolchain.h> #include <linker/sections.h> #include <wait_q.h> #include <sys/dlist.h> #include <ksched.h> #include <init.h> #include <syscall_handler.h> #include <tracing/tracing.h> #include <sys/check.h> /* We use a system-wide lock to synchronize semaphores, which has * unfortunate performance impact vs. using a per-object lock * (semaphores are *very* widely used). But per-object locks require * significant extra RAM. A properly spin-aware semaphore * implementation would spin on atomic access to the count variable, * and not a spinlock per se. Useful optimization for the future... */ static struct k_spinlock lock; #ifdef CONFIG_OBJECT_TRACING struct k_sem *_trace_list_k_sem; /* * Complete initialization of statically defined semaphores. */ static int init_sem_module(struct device *dev) { ARG_UNUSED(dev); Z_STRUCT_SECTION_FOREACH(k_sem, sem) { SYS_TRACING_OBJ_INIT(k_sem, sem); } return 0; } SYS_INIT(init_sem_module, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_OBJECTS); #endif /* CONFIG_OBJECT_TRACING */ int z_impl_k_sem_init(struct k_sem *sem, unsigned int initial_count, unsigned int limit) { /* * Limit cannot be zero and count cannot be greater than limit */ CHECKIF(limit == 0U || initial_count > limit) { return -EINVAL; } sys_trace_void(SYS_TRACE_ID_SEMA_INIT); sem->count = initial_count; sem->limit = limit; z_waitq_init(&sem->wait_q); #if defined(CONFIG_POLL) sys_dlist_init(&sem->poll_events); #endif SYS_TRACING_OBJ_INIT(k_sem, sem); z_object_init(sem); sys_trace_end_call(SYS_TRACE_ID_SEMA_INIT); return 0; } #ifdef CONFIG_USERSPACE int z_vrfy_k_sem_init(struct k_sem *sem, unsigned int initial_count, unsigned int limit) { Z_OOPS(Z_SYSCALL_OBJ_INIT(sem, K_OBJ_SEM)); return z_impl_k_sem_init(sem, initial_count, limit); } #include <syscalls/k_sem_init_mrsh.c> #endif static inline void handle_poll_events(struct k_sem *sem) { #ifdef CONFIG_POLL z_handle_obj_poll_events(&sem->poll_events, K_POLL_STATE_SEM_AVAILABLE); #else ARG_UNUSED(sem); #endif } void z_impl_k_sem_give(struct k_sem *sem) { k_spinlock_key_t key = k_spin_lock(&lock); struct k_thread *thread = z_unpend_first_thread(&sem->wait_q); sys_trace_void(SYS_TRACE_ID_SEMA_GIVE); if (thread != NULL) { arch_thread_return_value_set(thread, 0); z_ready_thread(thread); } else { sem->count += (sem->count != sem->limit) ? 1U : 0U; handle_poll_events(sem); } sys_trace_end_call(SYS_TRACE_ID_SEMA_GIVE); z_reschedule(&lock, key); } #ifdef CONFIG_USERSPACE static inline void z_vrfy_k_sem_give(struct k_sem *sem) { Z_OOPS(Z_SYSCALL_OBJ(sem, K_OBJ_SEM)); z_impl_k_sem_give(sem); } #include <syscalls/k_sem_give_mrsh.c> #endif int z_impl_k_sem_take(struct k_sem *sem, k_timeout_t timeout) { int ret = 0; __ASSERT(((arch_is_in_isr() == false) || K_TIMEOUT_EQ(timeout, K_NO_WAIT)), ""); sys_trace_void(SYS_TRACE_ID_SEMA_TAKE); k_spinlock_key_t key = k_spin_lock(&lock); if (likely(sem->count > 0U)) { sem->count--; k_spin_unlock(&lock, key); ret = 0; goto out; } if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) { k_spin_unlock(&lock, key); ret = -EBUSY; goto out; } ret = z_pend_curr(&lock, key, &sem->wait_q, timeout); out: sys_trace_end_call(SYS_TRACE_ID_SEMA_TAKE); return ret; } #ifdef CONFIG_USERSPACE static inline int z_vrfy_k_sem_take(struct k_sem *sem, k_timeout_t timeout) { Z_OOPS(Z_SYSCALL_OBJ(sem, K_OBJ_SEM)); return z_impl_k_sem_take((struct k_sem *)sem, timeout); } #include <syscalls/k_sem_take_mrsh.c> static inline void z_vrfy_k_sem_reset(struct k_sem *sem) { Z_OOPS(Z_SYSCALL_OBJ(sem, K_OBJ_SEM)); z_impl_k_sem_reset(sem); } #include <syscalls/k_sem_reset_mrsh.c> static inline unsigned int z_vrfy_k_sem_count_get(struct k_sem *sem) { Z_OOPS(Z_SYSCALL_OBJ(sem, K_OBJ_SEM)); return z_impl_k_sem_count_get(sem); } #include <syscalls/k_sem_count_get_mrsh.c> #endif |