Home | History | Annotate | Download | only in fio
      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