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 <assert.h>
9
10#include "flist.h"
11#include "filelock.h"
12#include "smalloc.h"
13#include "mutex.h"
14#include "hash.h"
15#include "log.h"
16
17struct fio_filelock {
18	uint32_t hash;
19	struct fio_mutex lock;
20	struct flist_head list;
21	unsigned int references;
22};
23
24static struct flist_head *filelock_list;
25static struct fio_mutex *filelock_lock;
26
27int fio_filelock_init(void)
28{
29	filelock_list = smalloc(sizeof(*filelock_list));
30	if (!filelock_list)
31		return 1;
32
33	INIT_FLIST_HEAD(filelock_list);
34	filelock_lock = fio_mutex_init(FIO_MUTEX_UNLOCKED);
35	if (!filelock_lock) {
36		sfree(filelock_list);
37		return 1;
38	}
39
40	return 0;
41}
42
43void fio_filelock_exit(void)
44{
45	if (!filelock_list)
46		return;
47
48	assert(flist_empty(filelock_list));
49	sfree(filelock_list);
50	filelock_list = NULL;
51	fio_mutex_remove(filelock_lock);
52	filelock_lock = NULL;
53}
54
55static struct fio_filelock *fio_hash_find(uint32_t hash)
56{
57	struct flist_head *entry;
58	struct fio_filelock *ff;
59
60	flist_for_each(entry, filelock_list) {
61		ff = flist_entry(entry, struct fio_filelock, list);
62		if (ff->hash == hash)
63			return ff;
64	}
65
66	return NULL;
67}
68
69static struct fio_filelock *fio_hash_get(uint32_t hash)
70{
71	struct fio_filelock *ff;
72
73	ff = fio_hash_find(hash);
74	if (!ff) {
75		ff = smalloc(sizeof(*ff));
76		ff->hash = hash;
77		__fio_mutex_init(&ff->lock, FIO_MUTEX_UNLOCKED);
78		ff->references = 0;
79		flist_add(&ff->list, filelock_list);
80	}
81
82	return ff;
83}
84
85int fio_trylock_file(const char *fname)
86{
87	struct fio_filelock *ff;
88	uint32_t hash;
89
90	hash = jhash(fname, strlen(fname), 0);
91
92	fio_mutex_down(filelock_lock);
93	ff = fio_hash_get(hash);
94	ff->references++;
95	fio_mutex_up(filelock_lock);
96
97	if (!fio_mutex_down_trylock(&ff->lock))
98		return 0;
99
100	fio_mutex_down(filelock_lock);
101
102	/*
103	 * If we raced and the only reference to the lock is us, we can
104	 * grab it
105	 */
106	if (ff->references != 1) {
107		ff->references--;
108		ff = NULL;
109	}
110
111	fio_mutex_up(filelock_lock);
112
113	if (ff) {
114		fio_mutex_down(&ff->lock);
115		return 0;
116	}
117
118	return 1;
119}
120
121void fio_lock_file(const char *fname)
122{
123	struct fio_filelock *ff;
124	uint32_t hash;
125
126	hash = jhash(fname, strlen(fname), 0);
127
128	fio_mutex_down(filelock_lock);
129	ff = fio_hash_get(hash);
130	ff->references++;
131	fio_mutex_up(filelock_lock);
132
133	fio_mutex_down(&ff->lock);
134}
135
136void fio_unlock_file(const char *fname)
137{
138	struct fio_filelock *ff;
139	uint32_t hash;
140
141	hash = jhash(fname, strlen(fname), 0);
142
143	fio_mutex_down(filelock_lock);
144
145	ff = fio_hash_find(hash);
146	if (ff) {
147		ff->references--;
148		fio_mutex_up(&ff->lock);
149		if (!ff->references) {
150			flist_del(&ff->list);
151			sfree(ff);
152		}
153	} else
154		log_err("fio: file not found for unlocking\n");
155
156	fio_mutex_up(filelock_lock);
157}
158