1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2015 Intel Corporation 3 */ 4 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <stdint.h> 9 #include <stddef.h> 10 #include <limits.h> 11 #include <inttypes.h> 12 #include <unistd.h> 13 #include <pthread.h> 14 #include <fcntl.h> 15 #include <sys/time.h> 16 #include <sys/mman.h> 17 18 #include <rte_per_lcore.h> 19 #include <rte_log.h> 20 #include <rte_spinlock.h> 21 #include <rte_common.h> 22 23 #include "lthread_api.h" 24 #include "lthread_int.h" 25 #include "lthread_mutex.h" 26 #include "lthread_sched.h" 27 #include "lthread_queue.h" 28 #include "lthread_objcache.h" 29 #include "lthread_diag.h" 30 31 /* 32 * Create a mutex 33 */ 34 int 35 lthread_mutex_init(char *name, struct lthread_mutex **mutex, 36 __rte_unused const struct lthread_mutexattr *attr) 37 { 38 struct lthread_mutex *m; 39 40 if (mutex == NULL) 41 return POSIX_ERRNO(EINVAL); 42 43 44 m = _lthread_objcache_alloc((THIS_SCHED)->mutex_cache); 45 if (m == NULL) 46 return POSIX_ERRNO(EAGAIN); 47 48 m->blocked = _lthread_queue_create("blocked queue"); 49 if (m->blocked == NULL) { 50 _lthread_objcache_free((THIS_SCHED)->mutex_cache, m); 51 return POSIX_ERRNO(EAGAIN); 52 } 53 54 if (name == NULL) 55 strncpy(m->name, "no name", sizeof(m->name)); 56 else 57 strncpy(m->name, name, sizeof(m->name)); 58 m->name[sizeof(m->name)-1] = 0; 59 60 m->root_sched = THIS_SCHED; 61 m->owner = NULL; 62 63 rte_atomic64_init(&m->count); 64 65 DIAG_CREATE_EVENT(m, LT_DIAG_MUTEX_CREATE); 66 /* success */ 67 (*mutex) = m; 68 return 0; 69 } 70 71 /* 72 * Destroy a mutex 73 */ 74 int lthread_mutex_destroy(struct lthread_mutex *m) 75 { 76 if ((m == NULL) || (m->blocked == NULL)) { 77 DIAG_EVENT(m, LT_DIAG_MUTEX_DESTROY, m, POSIX_ERRNO(EINVAL)); 78 return POSIX_ERRNO(EINVAL); 79 } 80 81 if (m->owner == NULL) { 82 /* try to delete the blocked queue */ 83 if (_lthread_queue_destroy(m->blocked) < 0) { 84 DIAG_EVENT(m, LT_DIAG_MUTEX_DESTROY, 85 m, POSIX_ERRNO(EBUSY)); 86 return POSIX_ERRNO(EBUSY); 87 } 88 89 /* free the mutex to cache */ 90 _lthread_objcache_free(m->root_sched->mutex_cache, m); 91 DIAG_EVENT(m, LT_DIAG_MUTEX_DESTROY, m, 0); 92 return 0; 93 } 94 /* can't do its still in use */ 95 DIAG_EVENT(m, LT_DIAG_MUTEX_DESTROY, m, POSIX_ERRNO(EBUSY)); 96 return POSIX_ERRNO(EBUSY); 97 } 98 99 /* 100 * Try to obtain a mutex 101 */ 102 int lthread_mutex_lock(struct lthread_mutex *m) 103 { 104 struct lthread *lt = THIS_LTHREAD; 105 106 if ((m == NULL) || (m->blocked == NULL)) { 107 DIAG_EVENT(m, LT_DIAG_MUTEX_LOCK, m, POSIX_ERRNO(EINVAL)); 108 return POSIX_ERRNO(EINVAL); 109 } 110 111 /* allow no recursion */ 112 if (m->owner == lt) { 113 DIAG_EVENT(m, LT_DIAG_MUTEX_LOCK, m, POSIX_ERRNO(EDEADLK)); 114 return POSIX_ERRNO(EDEADLK); 115 } 116 117 for (;;) { 118 rte_atomic64_inc(&m->count); 119 do { 120 if (rte_atomic64_cmpset 121 ((uint64_t *) &m->owner, 0, (uint64_t) lt)) { 122 /* happy days, we got the lock */ 123 DIAG_EVENT(m, LT_DIAG_MUTEX_LOCK, m, 0); 124 return 0; 125 } 126 /* spin due to race with unlock when 127 * nothing was blocked 128 */ 129 } while ((rte_atomic64_read(&m->count) == 1) && 130 (m->owner == NULL)); 131 132 /* queue the current thread in the blocked queue 133 * we defer this to after we return to the scheduler 134 * to ensure that the current thread context is saved 135 * before unlock could result in it being dequeued and 136 * resumed 137 */ 138 DIAG_EVENT(m, LT_DIAG_MUTEX_BLOCKED, m, lt); 139 lt->pending_wr_queue = m->blocked; 140 /* now relinquish cpu */ 141 _suspend(); 142 /* resumed, must loop and compete for the lock again */ 143 } 144 return 0; 145 } 146 147 /* try to lock a mutex but don't block */ 148 int lthread_mutex_trylock(struct lthread_mutex *m) 149 { 150 struct lthread *lt = THIS_LTHREAD; 151 152 if ((m == NULL) || (m->blocked == NULL)) { 153 DIAG_EVENT(m, LT_DIAG_MUTEX_TRYLOCK, m, POSIX_ERRNO(EINVAL)); 154 return POSIX_ERRNO(EINVAL); 155 } 156 157 if (m->owner == lt) { 158 /* no recursion */ 159 DIAG_EVENT(m, LT_DIAG_MUTEX_TRYLOCK, m, POSIX_ERRNO(EDEADLK)); 160 return POSIX_ERRNO(EDEADLK); 161 } 162 163 rte_atomic64_inc(&m->count); 164 if (rte_atomic64_cmpset 165 ((uint64_t *) &m->owner, (uint64_t) NULL, (uint64_t) lt)) { 166 /* got the lock */ 167 DIAG_EVENT(m, LT_DIAG_MUTEX_TRYLOCK, m, 0); 168 return 0; 169 } 170 171 /* failed so return busy */ 172 rte_atomic64_dec(&m->count); 173 DIAG_EVENT(m, LT_DIAG_MUTEX_TRYLOCK, m, POSIX_ERRNO(EBUSY)); 174 return POSIX_ERRNO(EBUSY); 175 } 176 177 /* 178 * Unlock a mutex 179 */ 180 int lthread_mutex_unlock(struct lthread_mutex *m) 181 { 182 struct lthread *lt = THIS_LTHREAD; 183 struct lthread *unblocked; 184 185 if ((m == NULL) || (m->blocked == NULL)) { 186 DIAG_EVENT(m, LT_DIAG_MUTEX_UNLOCKED, m, POSIX_ERRNO(EINVAL)); 187 return POSIX_ERRNO(EINVAL); 188 } 189 190 /* fail if its owned */ 191 if (m->owner != lt || m->owner == NULL) { 192 DIAG_EVENT(m, LT_DIAG_MUTEX_UNLOCKED, m, POSIX_ERRNO(EPERM)); 193 return POSIX_ERRNO(EPERM); 194 } 195 196 rte_atomic64_dec(&m->count); 197 /* if there are blocked threads then make one ready */ 198 while (rte_atomic64_read(&m->count) > 0) { 199 unblocked = _lthread_queue_remove(m->blocked); 200 201 if (unblocked != NULL) { 202 rte_atomic64_dec(&m->count); 203 DIAG_EVENT(m, LT_DIAG_MUTEX_UNLOCKED, m, unblocked); 204 RTE_ASSERT(unblocked->sched != NULL); 205 _ready_queue_insert((struct lthread_sched *) 206 unblocked->sched, unblocked); 207 break; 208 } 209 } 210 /* release the lock */ 211 m->owner = NULL; 212 return 0; 213 } 214 215 /* 216 * return the diagnostic ref val stored in a mutex 217 */ 218 uint64_t 219 lthread_mutex_diag_ref(struct lthread_mutex *m) 220 { 221 if (m == NULL) 222 return 0; 223 return m->diag_ref; 224 } 225