1 /*
2 * Copyright (C) 2021 Jakub Kruszona-Zawadzki, Core Technology Sp. z o.o.
3 *
4 * This file is part of MooseFS.
5 *
6 * MooseFS is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, version 2 (only).
9 *
10 * MooseFS is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with MooseFS; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301, USA
18 * or visit http://www.gnu.org/licenses/gpl-2.0.html
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25
26 #ifdef HAVE_ATOMICS
27 #include <stdatomic.h>
28 #undef HAVE_ATOMICS
29 #define HAVE_ATOMICS 1
30 #else
31 #define HAVE_ATOMICS 0
32 #endif
33
34 #if defined(HAVE___SYNC_OP_AND_FETCH) && defined(HAVE___SYNC_BOOL_COMPARE_AND_SWAP)
35 #define HAVE_SYNCS 1
36 #else
37 #define HAVE_SYNCS 0
38 #endif
39
40
41 #define BUCKETS_MT_MMAP_ALLOC 1
42
43
44 #include <stdlib.h>
45 #include <inttypes.h>
46 #include <pthread.h>
47
48 #include "massert.h"
49 #include "buckets_mt.h"
50
51
52 #define INOLENG_HASHSIZE 1024
53 #define INOLENG_HASH(inode) ((inode)%INOLENG_HASHSIZE)
54
55 typedef struct _ileng {
56 uint32_t inode;
57 #if HAVE_ATOMICS
58 _Atomic uint32_t refcnt;
59 _Atomic uint64_t fleng;
60 #elif HAVE_SYNCS
61 volatile uint32_t refcnt;
62 volatile uint64_t fleng;
63 #else
64 volatile uint32_t refcnt;
65 volatile uint64_t fleng;
66 #define USE_LOCK 1
67 pthread_mutex_t lock;
68 #endif
69 // rwlock
70 uint8_t writing;
71 uint32_t readers_cnt;
72 uint32_t writers_cnt;
73 pthread_mutex_t rwlock;
74 pthread_cond_t rwcond;
75 //
76 struct _ileng *next;
77 } ileng;
78
79 CREATE_BUCKET_MT_ALLOCATOR(ileng,ileng,500)
80
81 static ileng *inolenghashtab[INOLENG_HASHSIZE];
82 static pthread_mutex_t hashlock[INOLENG_HASHSIZE];
83
inoleng_acquire(uint32_t inode)84 void* inoleng_acquire(uint32_t inode) {
85 uint32_t h;
86 ileng *ilptr;
87
88 h = INOLENG_HASH(inode);
89 zassert(pthread_mutex_lock(hashlock+h));
90 for (ilptr = inolenghashtab[h] ; ilptr!=NULL ; ilptr=ilptr->next) {
91 if (ilptr->inode==inode) {
92 #if HAVE_ATOMICS
93 atomic_fetch_add(&(ilptr->refcnt),1);
94 #elif HAVE_SYNCS
95 __sync_add_and_fetch(&(ilptr->refcnt),1);
96 #else
97 zassert(pthread_mutex_lock(&(ilptr->lock)));
98 ilptr->refcnt++;
99 zassert(pthread_mutex_unlock(&(ilptr->lock)));
100 #endif
101 zassert(pthread_mutex_unlock(hashlock+h));
102 return (void*)ilptr;
103 }
104 }
105 ilptr = ileng_malloc();
106 ilptr->inode = inode;
107 #if HAVE_ATOMICS
108 atomic_init(&(ilptr->refcnt),1);
109 atomic_init(&(ilptr->fleng),0);
110 #else
111 ilptr->refcnt = 1;
112 ilptr->fleng = 0;
113 #ifdef USE_LOCK
114 zassert(pthread_mutex_init(&(ilptr->lock),NULL));
115 #endif
116 #endif
117 ilptr->writing = 0;
118 ilptr->writers_cnt = 0;
119 ilptr->readers_cnt = 0;
120 zassert(pthread_mutex_init(&(ilptr->rwlock),NULL));
121 zassert(pthread_cond_init(&(ilptr->rwcond),NULL));
122 ilptr->next = inolenghashtab[h];
123 inolenghashtab[h] = ilptr;
124 zassert(pthread_mutex_unlock(hashlock+h));
125 return (void*)ilptr;
126 }
127
inoleng_release(void * ptr)128 void inoleng_release(void *ptr) {
129 uint32_t h;
130 ileng *ilptr,**ilpptr;
131 ileng *il = (ileng*)ptr;
132
133 #if HAVE_ATOMICS
134 if (atomic_fetch_sub(&(il->refcnt),1)==1) { // returns value held previously
135 #elif HAVE_SYNCS
136 if (__sync_sub_and_fetch(&(il->refcnt),1)==0) {
137 #else
138 zassert(pthread_mutex_lock(&(il->lock)));
139 il->refcnt--;
140 if (il->refcnt==0) {
141 zassert(pthread_mutex_unlock(&(il->lock)));
142 #endif
143 h = INOLENG_HASH(il->inode);
144 zassert(pthread_mutex_lock(hashlock+h));
145 #if HAVE_ATOMICS
146 if (atomic_load(&(il->refcnt))==0) { // still zero after lock
147 #elif HAVE_SYNCS
148 if (__sync_add_and_fetch(&(il->refcnt),0)==0) {
149 #else
150 zassert(pthread_mutex_lock(&(il->lock)));
151 if (il->refcnt==0) {
152 zassert(pthread_mutex_unlock(&(il->lock)));
153 #endif
154 ilpptr = inolenghashtab + h;
155 while ((ilptr=*ilpptr)!=NULL) {
156 if (il==ilptr) {
157 *ilpptr = ilptr->next;
158 #ifdef USE_LOCK
159 zassert(pthread_mutex_destroy(&(ilptr->lock)));
160 #endif
161 zassert(pthread_mutex_destroy(&(ilptr->rwlock)));
162 zassert(pthread_cond_destroy(&(ilptr->rwcond)));
163 ileng_free(ilptr);
164 } else {
165 ilpptr = &(ilptr->next);
166 }
167 }
168 #if HAVE_ATOMICS || HAVE_SYNCS
169 }
170 #else
171 } else {
172 zassert(pthread_mutex_unlock(&(il->lock)));
173 }
174 #endif
175 zassert(pthread_mutex_unlock(hashlock+h));
176 }
177 }
178
179 uint64_t inoleng_getfleng(void *ptr) {
180 ileng *il = (ileng*)ptr;
181 #if HAVE_ATOMICS
182 return atomic_load_explicit(&(il->fleng),memory_order_relaxed);
183 #elif HAVE_SYNCS
184 return __sync_add_and_fetch(&(il->fleng),0);
185 #else
186 uint64_t ret;
187 zassert(pthread_mutex_lock(&(il->lock)));
188 ret = il->fleng;
189 zassert(pthread_mutex_unlock(&(il->lock)));
190 return ret;
191 #endif
192 }
193
194 void inoleng_setfleng(void *ptr,uint64_t fleng) {
195 ileng *il = (ileng*)ptr;
196 #if HAVE_ATOMICS
197 atomic_store_explicit(&(il->fleng),fleng,memory_order_relaxed);
198 #elif HAVE_SYNCS
199 for (;;) {
200 uint64_t ofleng = __sync_add_and_fetch(&(il->fleng),0);
201 if (__sync_bool_compare_and_swap(&(il->fleng),ofleng,fleng)) {
202 return;
203 }
204 }
205 #else
206 zassert(pthread_mutex_lock(&(il->lock)));
207 il->fleng = fleng;
208 zassert(pthread_mutex_unlock(&(il->lock)));
209 #endif
210 }
211
212 void inoleng_update_fleng(uint32_t inode,uint64_t fleng) {
213 uint32_t h;
214 ileng *ilptr;
215
216 h = INOLENG_HASH(inode);
217 zassert(pthread_mutex_lock(hashlock+h));
218 for (ilptr = inolenghashtab[h] ; ilptr!=NULL ; ilptr=ilptr->next) {
219 if (ilptr->inode==inode) {
220 #if HAVE_ATOMICS
221 atomic_store_explicit(&(ilptr->fleng),fleng,memory_order_relaxed);
222 #elif HAVE_SYNCS
223 for (;;) {
224 uint64_t ofleng = __sync_add_and_fetch(&(ilptr->fleng),0);
225 if (__sync_bool_compare_and_swap(&(ilptr->fleng),ofleng,fleng)) {
226 break;
227 }
228 }
229 #else
230 zassert(pthread_mutex_lock(&(ilptr->lock)));
231 ilptr->fleng = fleng;
232 zassert(pthread_mutex_unlock(&(ilptr->lock)));
233 #endif
234 }
235 }
236 zassert(pthread_mutex_unlock(hashlock+h));
237 }
238
239 void inoleng_write_start(void *ptr) {
240 ileng *il = (ileng*)ptr;
241
242 zassert(pthread_mutex_lock(&(il->rwlock)));
243 il->writers_cnt++;
244 while (il->readers_cnt | il->writing) {
245 zassert(pthread_cond_wait(&(il->rwcond),&(il->rwlock)));
246 }
247 il->writers_cnt--;
248 il->writing = 1;
249 zassert(pthread_mutex_unlock(&(il->rwlock)));
250 }
251
252 void inoleng_write_end(void *ptr) {
253 ileng *il = (ileng*)ptr;
254
255 zassert(pthread_mutex_lock(&(il->rwlock)));
256 il->writing = 0;
257 zassert(pthread_cond_broadcast(&(il->rwcond)));
258 zassert(pthread_mutex_unlock(&(il->rwlock)));
259 }
260
261 void inoleng_read_start(void *ptr) {
262 ileng *il = (ileng*)ptr;
263
264 zassert(pthread_mutex_lock(&(il->rwlock)));
265 while (il->writing | il->writers_cnt) {
266 zassert(pthread_cond_wait(&(il->rwcond),&(il->rwlock)));
267 }
268 il->readers_cnt++;
269 zassert(pthread_mutex_unlock(&(il->rwlock)));
270 }
271
272 void inoleng_read_end(void *ptr) {
273 ileng *il = (ileng*)ptr;
274
275 zassert(pthread_mutex_lock(&(il->rwlock)));
276 il->readers_cnt--;
277 if (il->readers_cnt==0) {
278 zassert(pthread_cond_broadcast(&(il->rwcond)));
279 }
280 zassert(pthread_mutex_unlock(&(il->rwlock)));
281 }
282
283 void inoleng_io_wait(void *ptr) {
284 ileng *il = (ileng*)ptr;
285
286 zassert(pthread_mutex_lock(&(il->rwlock)));
287 while (il->readers_cnt | il->writers_cnt | il->writing) {
288 zassert(pthread_cond_wait(&(il->rwcond),&(il->rwlock)));
289 }
290 zassert(pthread_mutex_unlock(&(il->rwlock)));
291 }
292
293 void inoleng_term(void) {
294 ileng *ilptr,*ilnptr;
295 uint32_t refcnt;
296 uint32_t h;
297
298 for (h=0 ; h<INOLENG_HASHSIZE ; h++) {
299 zassert(pthread_mutex_lock(hashlock+h));
300 for (ilptr = inolenghashtab[h] ; ilptr!=NULL ; ilptr=ilnptr) {
301 ilnptr = ilptr->next;
302 #if HAVE_ATOMICS
303 refcnt = atomic_load(&(ilptr->refcnt));
304 #elif HAVE_SYNCS
305 refcnt = __sync_add_and_fetch(&(ilptr->refcnt),0);
306 #else
307 zassert(pthread_mutex_lock(&(ilptr->lock)));
308 refcnt = ilptr->refcnt;
309 zassert(pthread_mutex_unlock(&(ilptr->lock)));
310 #endif
311 syslog(LOG_WARNING,"inode fleng data structure leftovers (ino: %"PRIu32" ; refcnt: %"PRIu32")",ilptr->inode,refcnt);
312 ileng_free(ilptr);
313 }
314
315 zassert(pthread_mutex_unlock(hashlock+h));
316 zassert(pthread_mutex_destroy(hashlock+h));
317 }
318 ileng_free_all();
319 }
320
321 void inoleng_init(void) {
322 uint32_t h;
323
324 (void)ileng_getusage; // functions that are defined by CREATE_BUCKET_MT_ALLOCATOR macro but not used
325
326 for (h=0 ; h<INOLENG_HASHSIZE ; h++) {
327 inolenghashtab[h] = NULL;
328 zassert(pthread_mutex_init(hashlock+h,NULL));
329 }
330 }
331