/* * Really simple exclusive file locking based on filename. * No hash indexing, just a list, so only works well for < 100 files or * so. But that's more than what fio needs, so should be fine. */ #include #include #include #include "flist.h" #include "filelock.h" #include "smalloc.h" #include "mutex.h" #include "hash.h" #include "log.h" struct fio_filelock { uint32_t hash; struct fio_mutex lock; struct flist_head list; unsigned int references; }; static struct flist_head *filelock_list; static struct fio_mutex *filelock_lock; int fio_filelock_init(void) { filelock_list = smalloc(sizeof(*filelock_list)); if (!filelock_list) return 1; INIT_FLIST_HEAD(filelock_list); filelock_lock = fio_mutex_init(FIO_MUTEX_UNLOCKED); if (!filelock_lock) { sfree(filelock_list); return 1; } return 0; } void fio_filelock_exit(void) { if (!filelock_list) return; assert(flist_empty(filelock_list)); sfree(filelock_list); filelock_list = NULL; fio_mutex_remove(filelock_lock); filelock_lock = NULL; } static struct fio_filelock *fio_hash_find(uint32_t hash) { struct flist_head *entry; struct fio_filelock *ff; flist_for_each(entry, filelock_list) { ff = flist_entry(entry, struct fio_filelock, list); if (ff->hash == hash) return ff; } return NULL; } static struct fio_filelock *fio_hash_get(uint32_t hash) { struct fio_filelock *ff; ff = fio_hash_find(hash); if (!ff) { ff = smalloc(sizeof(*ff)); ff->hash = hash; __fio_mutex_init(&ff->lock, FIO_MUTEX_UNLOCKED); ff->references = 0; flist_add(&ff->list, filelock_list); } return ff; } int fio_trylock_file(const char *fname) { struct fio_filelock *ff; uint32_t hash; hash = jhash(fname, strlen(fname), 0); fio_mutex_down(filelock_lock); ff = fio_hash_get(hash); ff->references++; fio_mutex_up(filelock_lock); if (!fio_mutex_down_trylock(&ff->lock)) return 0; fio_mutex_down(filelock_lock); /* * If we raced and the only reference to the lock is us, we can * grab it */ if (ff->references != 1) { ff->references--; ff = NULL; } fio_mutex_up(filelock_lock); if (ff) { fio_mutex_down(&ff->lock); return 0; } return 1; } void fio_lock_file(const char *fname) { struct fio_filelock *ff; uint32_t hash; hash = jhash(fname, strlen(fname), 0); fio_mutex_down(filelock_lock); ff = fio_hash_get(hash); ff->references++; fio_mutex_up(filelock_lock); fio_mutex_down(&ff->lock); } void fio_unlock_file(const char *fname) { struct fio_filelock *ff; uint32_t hash; hash = jhash(fname, strlen(fname), 0); fio_mutex_down(filelock_lock); ff = fio_hash_find(hash); if (ff) { ff->references--; fio_mutex_up(&ff->lock); if (!ff->references) { flist_del(&ff->list); sfree(ff); } } else log_err("fio: file not found for unlocking\n"); fio_mutex_up(filelock_lock); }