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 <sys/sem.h> 8 : : #include <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 ? 42 : : minimum + 1 : old_value + 1; 43 : : } while (atomic_cas(val, old_value, new_value) == 0U); 44 : : 45 : : return old_value; 46 : : } 47 : : 48 : : int sys_sem_init(struct sys_sem *sem, unsigned int initial_count, 49 : : unsigned int limit) 50 : : { 51 : : if (sem == NULL || limit == SYS_SEM_MINIMUM || 52 : : initial_count > limit || limit > INT_MAX) { 53 : : return -EINVAL; 54 : : } 55 : : 56 : : atomic_set(&sem->futex.val, initial_count); 57 : : sem->limit = limit; 58 : : 59 : : return 0; 60 : : } 61 : : 62 : : int sys_sem_give(struct sys_sem *sem) 63 : : { 64 : : int ret = 0; 65 : : atomic_t old_value; 66 : : 67 : : old_value = bounded_inc(&sem->futex.val, 68 : : SYS_SEM_MINIMUM, sem->limit); 69 : : if (old_value < 0) { 70 : : ret = k_futex_wake(&sem->futex, true); 71 : : 72 : : if (ret > 0) { 73 : : return 0; 74 : : } 75 : : } else if (old_value >= sem->limit) { 76 : : return -EAGAIN; 77 : : } else { 78 : : ; 79 : : } 80 : : return ret; 81 : : } 82 : : 83 : : int sys_sem_take(struct sys_sem *sem, k_timeout_t timeout) 84 : : { 85 : : int ret = 0; 86 : : atomic_t old_value; 87 : : 88 : : do { 89 : : old_value = bounded_dec(&sem->futex.val, 90 : : SYS_SEM_MINIMUM); 91 : : if (old_value > 0) { 92 : : return 0; 93 : : } 94 : : 95 : : ret = k_futex_wait(&sem->futex, 96 : : SYS_SEM_CONTENDED, timeout); 97 : : } while (ret == 0 || ret == -EAGAIN); 98 : : 99 : : return ret; 100 : : } 101 : : 102 : : unsigned int sys_sem_count_get(struct sys_sem *sem) 103 : : { 104 : : int value = atomic_get(&sem->futex.val); 105 : : 106 : : return value > SYS_SEM_MINIMUM ? value : SYS_SEM_MINIMUM; 107 : : } 108 : : #else 109 : 0 : int sys_sem_init(struct sys_sem *sem, unsigned int initial_count, 110 : : unsigned int limit) 111 : : { 112 : 0 : k_sem_init(&sem->kernel_sem, initial_count, limit); 113 : : 114 : 0 : return 0; 115 : : } 116 : : 117 : 0 : int sys_sem_give(struct sys_sem *sem) 118 : : { 119 : 0 : k_sem_give(&sem->kernel_sem); 120 : : 121 : 0 : return 0; 122 : : } 123 : : 124 : 0 : int sys_sem_take(struct sys_sem *sem, k_timeout_t timeout) 125 : : { 126 : 0 : int ret_value = 0; 127 : : 128 : 0 : ret_value = k_sem_take(&sem->kernel_sem, timeout); 129 [ # # # # ]: 0 : if (ret_value == -EAGAIN || ret_value == -EBUSY) { 130 : 0 : ret_value = -ETIMEDOUT; 131 : : } 132 : : 133 : 0 : return ret_value; 134 : : } 135 : : 136 : 0 : unsigned int sys_sem_count_get(struct sys_sem *sem) 137 : : { 138 : 0 : return k_sem_count_get(&sem->kernel_sem); 139 : : } 140 : : #endif