Home | History | Annotate | Download | only in fio
      1 /*
      2  * Really simple exclusive file locking based on filename.
      3  * No hash indexing, just a list, so only works well for < 100 files or
      4  * so. But that's more than what fio needs, so should be fine.
      5  */
      6 #include <inttypes.h>
      7 #include <string.h>
      8 #include <unistd.h>
      9 #include <assert.h>
     10 
     11 #include "flist.h"
     12 #include "filelock.h"
     13 #include "smalloc.h"
     14 #include "mutex.h"
     15 #include "hash.h"
     16 #include "log.h"
     17 
     18 struct fio_filelock {
     19 	uint32_t hash;
     20 	struct fio_mutex lock;
     21 	struct flist_head list;
     22 	unsigned int references;
     23 };
     24 
     25 #define MAX_FILELOCKS	128
     26 
     27 static struct filelock_data {
     28 	struct flist_head list;
     29 	struct fio_mutex lock;
     30 
     31 	struct flist_head free_list;
     32 	struct fio_filelock ffs[MAX_FILELOCKS];
     33 } *fld;
     34 
     35 static void put_filelock(struct fio_filelock *ff)
     36 {
     37 	flist_add(&ff->list, &fld->free_list);
     38 }
     39 
     40 static struct fio_filelock *__get_filelock(void)
     41 {
     42 	struct fio_filelock *ff;
     43 
     44 	if (flist_empty(&fld->free_list))
     45 		return NULL;
     46 
     47 	ff = flist_first_entry(&fld->free_list, struct fio_filelock, list);
     48 	flist_del_init(&ff->list);
     49 	return ff;
     50 }
     51 
     52 static struct fio_filelock *get_filelock(int trylock, int *retry)
     53 {
     54 	struct fio_filelock *ff;
     55 
     56 	do {
     57 		ff = __get_filelock();
     58 		if (ff || trylock)
     59 			break;
     60 
     61 		fio_mutex_up(&fld->lock);
     62 		usleep(1000);
     63 		fio_mutex_down(&fld->lock);
     64 		*retry = 1;
     65 	} while (1);
     66 
     67 	return ff;
     68 }
     69 
     70 int fio_filelock_init(void)
     71 {
     72 	int i;
     73 
     74 	fld = smalloc(sizeof(*fld));
     75 	if (!fld)
     76 		return 1;
     77 
     78 	INIT_FLIST_HEAD(&fld->list);
     79 	INIT_FLIST_HEAD(&fld->free_list);
     80 
     81 	if (__fio_mutex_init(&fld->lock, FIO_MUTEX_UNLOCKED))
     82 		goto err;
     83 
     84 	for (i = 0; i < MAX_FILELOCKS; i++) {
     85 		struct fio_filelock *ff = &fld->ffs[i];
     86 
     87 		if (__fio_mutex_init(&ff->lock, FIO_MUTEX_UNLOCKED))
     88 			goto err;
     89 		flist_add_tail(&ff->list, &fld->free_list);
     90 	}
     91 
     92 	return 0;
     93 err:
     94 	fio_filelock_exit();
     95 	return 1;
     96 }
     97 
     98 void fio_filelock_exit(void)
     99 {
    100 	if (!fld)
    101 		return;
    102 
    103 	assert(flist_empty(&fld->list));
    104 	__fio_mutex_remove(&fld->lock);
    105 
    106 	while (!flist_empty(&fld->free_list)) {
    107 		struct fio_filelock *ff;
    108 
    109 		ff = flist_first_entry(&fld->free_list, struct fio_filelock, list);
    110 
    111 		flist_del_init(&ff->list);
    112 		__fio_mutex_remove(&ff->lock);
    113 	}
    114 
    115 	sfree(fld);
    116 	fld = NULL;
    117 }
    118 
    119 static struct fio_filelock *fio_hash_find(uint32_t hash)
    120 {
    121 	struct flist_head *entry;
    122 	struct fio_filelock *ff;
    123 
    124 	flist_for_each(entry, &fld->list) {
    125 		ff = flist_entry(entry, struct fio_filelock, list);
    126 		if (ff->hash == hash)
    127 			return ff;
    128 	}
    129 
    130 	return NULL;
    131 }
    132 
    133 static struct fio_filelock *fio_hash_get(uint32_t hash, int trylock)
    134 {
    135 	struct fio_filelock *ff;
    136 
    137 	ff = fio_hash_find(hash);
    138 	if (!ff) {
    139 		int retry = 0;
    140 
    141 		ff = get_filelock(trylock, &retry);
    142 		if (!ff)
    143 			return NULL;
    144 
    145 		/*
    146 		 * If we dropped the main lock, re-lookup the hash in case
    147 		 * someone else added it meanwhile. If it's now there,
    148 		 * just return that.
    149 		 */
    150 		if (retry) {
    151 			struct fio_filelock *__ff;
    152 
    153 			__ff = fio_hash_find(hash);
    154 			if (__ff) {
    155 				put_filelock(ff);
    156 				return __ff;
    157 			}
    158 		}
    159 
    160 		ff->hash = hash;
    161 		ff->references = 0;
    162 		flist_add(&ff->list, &fld->list);
    163 	}
    164 
    165 	return ff;
    166 }
    167 
    168 static int __fio_lock_file(const char *fname, int trylock)
    169 {
    170 	struct fio_filelock *ff;
    171 	uint32_t hash;
    172 
    173 	hash = jhash(fname, strlen(fname), 0);
    174 
    175 	fio_mutex_down(&fld->lock);
    176 	ff = fio_hash_get(hash, trylock);
    177 	if (ff)
    178 		ff->references++;
    179 	fio_mutex_up(&fld->lock);
    180 
    181 	if (!ff) {
    182 		assert(!trylock);
    183 		return 1;
    184 	}
    185 
    186 	if (!trylock) {
    187 		fio_mutex_down(&ff->lock);
    188 		return 0;
    189 	}
    190 
    191 	if (!fio_mutex_down_trylock(&ff->lock))
    192 		return 0;
    193 
    194 	fio_mutex_down(&fld->lock);
    195 
    196 	/*
    197 	 * If we raced and the only reference to the lock is us, we can
    198 	 * grab it
    199 	 */
    200 	if (ff->references != 1) {
    201 		ff->references--;
    202 		ff = NULL;
    203 	}
    204 
    205 	fio_mutex_up(&fld->lock);
    206 
    207 	if (ff) {
    208 		fio_mutex_down(&ff->lock);
    209 		return 0;
    210 	}
    211 
    212 	return 1;
    213 }
    214 
    215 int fio_trylock_file(const char *fname)
    216 {
    217 	return __fio_lock_file(fname, 1);
    218 }
    219 
    220 void fio_lock_file(const char *fname)
    221 {
    222 	__fio_lock_file(fname, 0);
    223 }
    224 
    225 void fio_unlock_file(const char *fname)
    226 {
    227 	struct fio_filelock *ff;
    228 	uint32_t hash;
    229 
    230 	hash = jhash(fname, strlen(fname), 0);
    231 
    232 	fio_mutex_down(&fld->lock);
    233 
    234 	ff = fio_hash_find(hash);
    235 	if (ff) {
    236 		int refs = --ff->references;
    237 		fio_mutex_up(&ff->lock);
    238 		if (!refs) {
    239 			flist_del_init(&ff->list);
    240 			put_filelock(ff);
    241 		}
    242 	} else
    243 		log_err("fio: file not found for unlocking\n");
    244 
    245 	fio_mutex_up(&fld->lock);
    246 }
    247