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 <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 : : 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) ? minimum : old_value) + 1; 42 : : } while (atomic_cas(val, old_value, new_value) == 0U); 43 : : 44 : : return old_value; 45 : : } 46 : : 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 : : 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 : : 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 : : 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 108 : 0 : int sys_sem_init(struct sys_sem *sem, unsigned int initial_count, 109 : : unsigned int limit) 110 : : { 111 : 0 : k_sem_init(&sem->kernel_sem, initial_count, limit); 112 : : 113 : 0 : return 0; 114 : : } 115 : : 116 : 0 : int sys_sem_give(struct sys_sem *sem) 117 : : { 118 : 0 : k_sem_give(&sem->kernel_sem); 119 : : 120 : 0 : return 0; 121 : : } 122 : : 123 : 0 : int sys_sem_take(struct sys_sem *sem, k_timeout_t timeout) 124 : : { 125 : 0 : int ret_value = 0; 126 : : 127 : 0 : ret_value = k_sem_take(&sem->kernel_sem, timeout); 128 [ # # ]: 0 : if ((ret_value == -EAGAIN) || (ret_value == -EBUSY)) { 129 : 0 : ret_value = -ETIMEDOUT; 130 : : } 131 : : 132 : 0 : return ret_value; 133 : : } 134 : : 135 : 0 : unsigned int sys_sem_count_get(struct sys_sem *sem) 136 : : { 137 : 0 : return k_sem_count_get(&sem->kernel_sem); 138 : : } 139 : : #endif