1 #include <stdio.h> 2 #include <string.h> 3 #include <unistd.h> 4 #include <stdlib.h> 5 #include <fcntl.h> 6 #include <time.h> 7 #include <errno.h> 8 #include <pthread.h> 9 #include <sys/mman.h> 10 #include <assert.h> 11 12 #include "fio.h" 13 #include "log.h" 14 #include "mutex.h" 15 #include "arch/arch.h" 16 #include "os/os.h" 17 #include "helpers.h" 18 #include "fio_time.h" 19 #include "gettime.h" 20 21 void __fio_mutex_remove(struct fio_mutex *mutex) 22 { 23 assert(mutex->magic == FIO_MUTEX_MAGIC); 24 pthread_cond_destroy(&mutex->cond); 25 26 /* 27 * Ensure any subsequent attempt to grab this mutex will fail 28 * with an assert, instead of just silently hanging. 29 */ 30 memset(mutex, 0, sizeof(*mutex)); 31 } 32 33 void fio_mutex_remove(struct fio_mutex *mutex) 34 { 35 __fio_mutex_remove(mutex); 36 munmap((void *) mutex, sizeof(*mutex)); 37 } 38 39 int cond_init_pshared(pthread_cond_t *cond) 40 { 41 pthread_condattr_t cattr; 42 int ret; 43 44 ret = pthread_condattr_init(&cattr); 45 if (ret) { 46 log_err("pthread_condattr_init: %s\n", strerror(ret)); 47 return ret; 48 } 49 50 #ifdef CONFIG_PSHARED 51 ret = pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED); 52 if (ret) { 53 log_err("pthread_condattr_setpshared: %s\n", strerror(ret)); 54 return ret; 55 } 56 #endif 57 ret = pthread_cond_init(cond, &cattr); 58 if (ret) { 59 log_err("pthread_cond_init: %s\n", strerror(ret)); 60 return ret; 61 } 62 63 return 0; 64 } 65 66 int mutex_init_pshared(pthread_mutex_t *mutex) 67 { 68 pthread_mutexattr_t mattr; 69 int ret; 70 71 ret = pthread_mutexattr_init(&mattr); 72 if (ret) { 73 log_err("pthread_mutexattr_init: %s\n", strerror(ret)); 74 return ret; 75 } 76 77 /* 78 * Not all platforms support process shared mutexes (FreeBSD) 79 */ 80 #ifdef CONFIG_PSHARED 81 ret = pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED); 82 if (ret) { 83 log_err("pthread_mutexattr_setpshared: %s\n", strerror(ret)); 84 return ret; 85 } 86 #endif 87 ret = pthread_mutex_init(mutex, &mattr); 88 if (ret) { 89 log_err("pthread_mutex_init: %s\n", strerror(ret)); 90 return ret; 91 } 92 93 return 0; 94 } 95 96 int mutex_cond_init_pshared(pthread_mutex_t *mutex, pthread_cond_t *cond) 97 { 98 int ret; 99 100 ret = mutex_init_pshared(mutex); 101 if (ret) 102 return ret; 103 104 ret = cond_init_pshared(cond); 105 if (ret) 106 return ret; 107 108 return 0; 109 } 110 111 int __fio_mutex_init(struct fio_mutex *mutex, int value) 112 { 113 int ret; 114 115 mutex->value = value; 116 mutex->magic = FIO_MUTEX_MAGIC; 117 118 ret = mutex_cond_init_pshared(&mutex->lock, &mutex->cond); 119 if (ret) 120 return ret; 121 122 return 0; 123 } 124 125 struct fio_mutex *fio_mutex_init(int value) 126 { 127 struct fio_mutex *mutex = NULL; 128 129 mutex = (void *) mmap(NULL, sizeof(struct fio_mutex), 130 PROT_READ | PROT_WRITE, 131 OS_MAP_ANON | MAP_SHARED, -1, 0); 132 if (mutex == MAP_FAILED) { 133 perror("mmap mutex"); 134 return NULL; 135 } 136 137 if (!__fio_mutex_init(mutex, value)) 138 return mutex; 139 140 fio_mutex_remove(mutex); 141 return NULL; 142 } 143 144 static bool mutex_timed_out(struct timeval *t, unsigned int msecs) 145 { 146 struct timeval now; 147 148 gettimeofday(&now, NULL); 149 return mtime_since(t, &now) >= msecs; 150 } 151 152 int fio_mutex_down_timeout(struct fio_mutex *mutex, unsigned int msecs) 153 { 154 struct timeval tv_s; 155 struct timespec t; 156 int ret = 0; 157 158 assert(mutex->magic == FIO_MUTEX_MAGIC); 159 160 gettimeofday(&tv_s, NULL); 161 t.tv_sec = tv_s.tv_sec; 162 t.tv_nsec = tv_s.tv_usec * 1000; 163 164 t.tv_sec += msecs / 1000; 165 t.tv_nsec += ((msecs * 1000000ULL) % 1000000000); 166 if (t.tv_nsec >= 1000000000) { 167 t.tv_nsec -= 1000000000; 168 t.tv_sec++; 169 } 170 171 pthread_mutex_lock(&mutex->lock); 172 173 mutex->waiters++; 174 while (!mutex->value && !ret) { 175 /* 176 * Some platforms (FreeBSD 9?) seems to return timed out 177 * way too early, double check. 178 */ 179 ret = pthread_cond_timedwait(&mutex->cond, &mutex->lock, &t); 180 if (ret == ETIMEDOUT && !mutex_timed_out(&tv_s, msecs)) 181 ret = 0; 182 } 183 mutex->waiters--; 184 185 if (!ret) { 186 mutex->value--; 187 pthread_mutex_unlock(&mutex->lock); 188 return 0; 189 } 190 191 pthread_mutex_unlock(&mutex->lock); 192 return ret; 193 } 194 195 bool fio_mutex_down_trylock(struct fio_mutex *mutex) 196 { 197 bool ret = true; 198 199 assert(mutex->magic == FIO_MUTEX_MAGIC); 200 201 pthread_mutex_lock(&mutex->lock); 202 if (mutex->value) { 203 mutex->value--; 204 ret = false; 205 } 206 pthread_mutex_unlock(&mutex->lock); 207 208 return ret; 209 } 210 211 void fio_mutex_down(struct fio_mutex *mutex) 212 { 213 assert(mutex->magic == FIO_MUTEX_MAGIC); 214 215 pthread_mutex_lock(&mutex->lock); 216 217 while (!mutex->value) { 218 mutex->waiters++; 219 pthread_cond_wait(&mutex->cond, &mutex->lock); 220 mutex->waiters--; 221 } 222 223 mutex->value--; 224 pthread_mutex_unlock(&mutex->lock); 225 } 226 227 void fio_mutex_up(struct fio_mutex *mutex) 228 { 229 int do_wake = 0; 230 231 assert(mutex->magic == FIO_MUTEX_MAGIC); 232 233 pthread_mutex_lock(&mutex->lock); 234 read_barrier(); 235 if (!mutex->value && mutex->waiters) 236 do_wake = 1; 237 mutex->value++; 238 pthread_mutex_unlock(&mutex->lock); 239 240 if (do_wake) 241 pthread_cond_signal(&mutex->cond); 242 } 243 244 void fio_rwlock_write(struct fio_rwlock *lock) 245 { 246 assert(lock->magic == FIO_RWLOCK_MAGIC); 247 pthread_rwlock_wrlock(&lock->lock); 248 } 249 250 void fio_rwlock_read(struct fio_rwlock *lock) 251 { 252 assert(lock->magic == FIO_RWLOCK_MAGIC); 253 pthread_rwlock_rdlock(&lock->lock); 254 } 255 256 void fio_rwlock_unlock(struct fio_rwlock *lock) 257 { 258 assert(lock->magic == FIO_RWLOCK_MAGIC); 259 pthread_rwlock_unlock(&lock->lock); 260 } 261 262 void fio_rwlock_remove(struct fio_rwlock *lock) 263 { 264 assert(lock->magic == FIO_RWLOCK_MAGIC); 265 munmap((void *) lock, sizeof(*lock)); 266 } 267 268 struct fio_rwlock *fio_rwlock_init(void) 269 { 270 struct fio_rwlock *lock; 271 pthread_rwlockattr_t attr; 272 int ret; 273 274 lock = (void *) mmap(NULL, sizeof(struct fio_rwlock), 275 PROT_READ | PROT_WRITE, 276 OS_MAP_ANON | MAP_SHARED, -1, 0); 277 if (lock == MAP_FAILED) { 278 perror("mmap rwlock"); 279 lock = NULL; 280 goto err; 281 } 282 283 lock->magic = FIO_RWLOCK_MAGIC; 284 285 ret = pthread_rwlockattr_init(&attr); 286 if (ret) { 287 log_err("pthread_rwlockattr_init: %s\n", strerror(ret)); 288 goto err; 289 } 290 #ifdef CONFIG_PSHARED 291 ret = pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); 292 if (ret) { 293 log_err("pthread_rwlockattr_setpshared: %s\n", strerror(ret)); 294 goto destroy_attr; 295 } 296 297 ret = pthread_rwlock_init(&lock->lock, &attr); 298 #else 299 ret = pthread_rwlock_init(&lock->lock, NULL); 300 #endif 301 302 if (ret) { 303 log_err("pthread_rwlock_init: %s\n", strerror(ret)); 304 goto destroy_attr; 305 } 306 307 pthread_rwlockattr_destroy(&attr); 308 309 return lock; 310 destroy_attr: 311 pthread_rwlockattr_destroy(&attr); 312 err: 313 if (lock) 314 fio_rwlock_remove(lock); 315 return NULL; 316 } 317