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