blob: 6e84970fc0064cafaeb0945befd5f4355fd9cf7b [file] [log] [blame]
Jens Axboe243bfe12014-04-02 15:46:58 -06001/*
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>
Jens Axboec2e99542015-01-13 21:06:45 -07008#include <unistd.h>
Jens Axboe243bfe12014-04-02 15:46:58 -06009#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
18struct fio_filelock {
19 uint32_t hash;
20 struct fio_mutex lock;
21 struct flist_head list;
22 unsigned int references;
23};
Jens Axboec2e99542015-01-13 21:06:45 -070024
25#define MAX_FILELOCKS 128
Jens Axboe243bfe12014-04-02 15:46:58 -060026
Jens Axboec2e99542015-01-13 21:06:45 -070027static 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
35static void put_filelock(struct fio_filelock *ff)
36{
37 flist_add(&ff->list, &fld->free_list);
38}
39
40static 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
52static 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}
Jens Axboe243bfe12014-04-02 15:46:58 -060069
70int fio_filelock_init(void)
71{
Jens Axboec2e99542015-01-13 21:06:45 -070072 int i;
73
74 fld = smalloc(sizeof(*fld));
75 if (!fld)
Jens Axboe243bfe12014-04-02 15:46:58 -060076 return 1;
77
Jens Axboec2e99542015-01-13 21:06:45 -070078 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);
Jens Axboe243bfe12014-04-02 15:46:58 -060090 }
91
92 return 0;
Jens Axboec2e99542015-01-13 21:06:45 -070093err:
94 fio_filelock_exit();
95 return 1;
Jens Axboe243bfe12014-04-02 15:46:58 -060096}
97
98void fio_filelock_exit(void)
99{
Jens Axboec2e99542015-01-13 21:06:45 -0700100 if (!fld)
Jens Axboe243bfe12014-04-02 15:46:58 -0600101 return;
102
Jens Axboec2e99542015-01-13 21:06:45 -0700103 assert(flist_empty(&fld->list));
Jens Axboe3b9719f2015-02-26 15:38:42 -0700104 __fio_mutex_remove(&fld->lock);
Jens Axboec2e99542015-01-13 21:06:45 -0700105
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);
Jens Axboe3b9719f2015-02-26 15:38:42 -0700112 __fio_mutex_remove(&ff->lock);
Jens Axboec2e99542015-01-13 21:06:45 -0700113 }
114
115 sfree(fld);
116 fld = NULL;
Jens Axboe243bfe12014-04-02 15:46:58 -0600117}
118
119static struct fio_filelock *fio_hash_find(uint32_t hash)
120{
121 struct flist_head *entry;
122 struct fio_filelock *ff;
123
Jens Axboec2e99542015-01-13 21:06:45 -0700124 flist_for_each(entry, &fld->list) {
Jens Axboe243bfe12014-04-02 15:46:58 -0600125 ff = flist_entry(entry, struct fio_filelock, list);
126 if (ff->hash == hash)
127 return ff;
128 }
129
130 return NULL;
131}
132
Jens Axboec2e99542015-01-13 21:06:45 -0700133static struct fio_filelock *fio_hash_get(uint32_t hash, int trylock)
Jens Axboe243bfe12014-04-02 15:46:58 -0600134{
135 struct fio_filelock *ff;
136
137 ff = fio_hash_find(hash);
138 if (!ff) {
Jens Axboec2e99542015-01-13 21:06:45 -0700139 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
Jens Axboe243bfe12014-04-02 15:46:58 -0600160 ff->hash = hash;
Jens Axboe243bfe12014-04-02 15:46:58 -0600161 ff->references = 0;
Jens Axboec2e99542015-01-13 21:06:45 -0700162 flist_add(&ff->list, &fld->list);
Jens Axboe243bfe12014-04-02 15:46:58 -0600163 }
164
165 return ff;
166}
167
Elliott Hugheseda3a602017-05-19 18:53:02 -0700168static bool __fio_lock_file(const char *fname, int trylock)
Jens Axboe243bfe12014-04-02 15:46:58 -0600169{
170 struct fio_filelock *ff;
171 uint32_t hash;
172
173 hash = jhash(fname, strlen(fname), 0);
174
Jens Axboec2e99542015-01-13 21:06:45 -0700175 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);
Elliott Hugheseda3a602017-05-19 18:53:02 -0700183 return true;
Jens Axboec2e99542015-01-13 21:06:45 -0700184 }
185
186 if (!trylock) {
187 fio_mutex_down(&ff->lock);
Elliott Hugheseda3a602017-05-19 18:53:02 -0700188 return false;
Jens Axboec2e99542015-01-13 21:06:45 -0700189 }
Jens Axboe243bfe12014-04-02 15:46:58 -0600190
191 if (!fio_mutex_down_trylock(&ff->lock))
Elliott Hugheseda3a602017-05-19 18:53:02 -0700192 return false;
Jens Axboe243bfe12014-04-02 15:46:58 -0600193
Jens Axboec2e99542015-01-13 21:06:45 -0700194 fio_mutex_down(&fld->lock);
Jens Axboe243bfe12014-04-02 15:46:58 -0600195
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
Jens Axboec2e99542015-01-13 21:06:45 -0700205 fio_mutex_up(&fld->lock);
Jens Axboe243bfe12014-04-02 15:46:58 -0600206
207 if (ff) {
208 fio_mutex_down(&ff->lock);
Elliott Hugheseda3a602017-05-19 18:53:02 -0700209 return false;
Jens Axboe243bfe12014-04-02 15:46:58 -0600210 }
211
Elliott Hugheseda3a602017-05-19 18:53:02 -0700212 return true;
Jens Axboe243bfe12014-04-02 15:46:58 -0600213}
214
Elliott Hugheseda3a602017-05-19 18:53:02 -0700215bool fio_trylock_file(const char *fname)
Jens Axboec2e99542015-01-13 21:06:45 -0700216{
217 return __fio_lock_file(fname, 1);
218}
219
Jens Axboe243bfe12014-04-02 15:46:58 -0600220void fio_lock_file(const char *fname)
221{
Jens Axboec2e99542015-01-13 21:06:45 -0700222 __fio_lock_file(fname, 0);
Jens Axboe243bfe12014-04-02 15:46:58 -0600223}
224
225void 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
Jens Axboec2e99542015-01-13 21:06:45 -0700232 fio_mutex_down(&fld->lock);
Jens Axboe243bfe12014-04-02 15:46:58 -0600233
234 ff = fio_hash_find(hash);
235 if (ff) {
Jens Axboec1803422014-12-18 19:44:18 -0700236 int refs = --ff->references;
Jens Axboe243bfe12014-04-02 15:46:58 -0600237 fio_mutex_up(&ff->lock);
Jens Axboec1803422014-12-18 19:44:18 -0700238 if (!refs) {
Jens Axboec2e99542015-01-13 21:06:45 -0700239 flist_del_init(&ff->list);
240 put_filelock(ff);
Jens Axboe243bfe12014-04-02 15:46:58 -0600241 }
242 } else
243 log_err("fio: file not found for unlocking\n");
244
Jens Axboec2e99542015-01-13 21:06:45 -0700245 fio_mutex_up(&fld->lock);
Jens Axboe243bfe12014-04-02 15:46:58 -0600246}