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 #include <inttypes.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <pthread.h>
25 
26 #include "massert.h"
27 
28 // #define CHUNKS_MAX_ENTRIES 1000000
29 // #define CHUNKS_MAX_TIME 60
30 
31 #define CHUNKS_INODE_HASH_SIZE 65536
32 #define CHUNKS_DATA_HASH_SIZE 524288
33 
34 struct _chunks_inode_entry;
35 
36 typedef struct _chunks_data_entry {
37 	uint32_t inode;
38 	uint32_t chindx;
39 	uint64_t chunkid;
40 	uint32_t version;
41 	uint8_t csdataver;
42 	uint8_t *csdata;
43 	uint32_t csdatasize;
44 	struct _chunks_inode_entry *parent;
45 	struct _chunks_data_entry **previnode,*nextinode;
46 	struct _chunks_data_entry **prevdata,*nextdata;
47 //	struct _chunks_data_entry **prevlru,*nextlru;
48 } chunks_data_entry;
49 
50 typedef struct _chunks_inode_entry {
51 	uint32_t inode;
52 	struct _chunks_data_entry *data_head;
53 	struct _chunks_inode_entry **prev,*next;
54 } chunks_inode_entry;
55 
56 static chunks_inode_entry **chunks_inode_hash;
57 static chunks_data_entry **chunks_data_hash;
58 //static chunks_data_entry *lruhead,**lrutail;
59 static pthread_mutex_t lock;
60 
61 //static uint32_t entries;
62 
chunks_inode_hash_fn(uint32_t inode)63 static inline uint32_t chunks_inode_hash_fn(uint32_t inode) {
64 	return ((inode*0x72B5F387U)&(CHUNKS_INODE_HASH_SIZE-1));
65 }
66 
chunks_data_hash_fn(uint32_t inode,uint32_t chindx)67 static inline uint32_t chunks_data_hash_fn(uint32_t inode,uint32_t chindx) {
68 	return ((((inode*0x72B5F387U)+chindx)*0x56BF7623U)&(CHUNKS_DATA_HASH_SIZE-1));
69 }
70 
chunks_try_remove_inode(chunks_inode_entry * ih)71 static inline void chunks_try_remove_inode(chunks_inode_entry *ih) {
72 	if (ih->data_head==NULL) {
73 		*(ih->prev) = ih->next;
74 		if (ih->next) {
75 			ih->next->prev = ih->prev;
76 		}
77 		free(ih);
78 	}
79 }
80 
chunks_remove_entry(chunks_data_entry * ca)81 static inline void chunks_remove_entry(chunks_data_entry *ca) {
82 	*(ca->previnode) = ca->nextinode;
83 	if (ca->nextinode) {
84 		ca->nextinode->previnode = ca->previnode;
85 	}
86 	*(ca->prevdata) = ca->nextdata;
87 	if (ca->nextdata) {
88 		ca->nextdata->prevdata = ca->prevdata;
89 	}
90 //	*(ca->prevlru) = ca->nextlru;
91 //	if (ca->nextlru) {
92 //		ca->nextlru->prevlru = ca->prevlru;
93 //	} else {
94 //		lrutail = ca->prevlru;
95 //	}
96 	if (ca->csdata) {
97 		free(ca->csdata);
98 	}
99 	chunks_try_remove_inode(ca->parent);
100 	free(ca);
101 //	entries--;
102 }
103 
chunks_new_entry(uint32_t inode,uint32_t chindx)104 static inline chunks_data_entry* chunks_new_entry(uint32_t inode,uint32_t chindx) {
105 	chunks_inode_entry *ih;
106 	chunks_data_entry *ca;
107 	uint32_t hash,ihash;
108 
109 	ihash = chunks_inode_hash_fn(inode);
110 	for (ih = chunks_inode_hash[ihash] ; ih && ih->inode!=inode ; ih = ih->next) {}
111 
112 	if (ih==NULL) {
113 		ih = malloc(sizeof(chunks_inode_entry));
114 		ih->inode = inode;
115 		ih->data_head = NULL;
116 		ih->next = chunks_inode_hash[ihash];
117 		if (ih->next) {
118 			ih->next->prev = &(ih->next);
119 		}
120 		ih->prev = chunks_inode_hash + ihash;
121 		chunks_inode_hash[ihash] = ih;
122 	}
123 	hash = chunks_data_hash_fn(inode,chindx);
124 	ca = malloc(sizeof(chunks_data_entry));
125 	ca->inode = inode;
126 	ca->chindx = chindx;
127 	ca->chunkid = 0;
128 	ca->version = 0;
129 	ca->csdata = NULL;
130 	ca->csdatasize = 0;
131 	ca->csdataver = 0;
132 	ca->parent = ih;
133 	ca->nextinode = ih->data_head;
134 	if (ca->nextinode) {
135 		ca->nextinode->previnode = &(ca->nextinode);
136 	}
137 	ca->previnode = &(ih->data_head);
138 	ih->data_head = ca;
139 	ca->nextdata = chunks_data_hash[hash];
140 	if (ca->nextdata) {
141 		ca->nextdata->prevdata = &(ca->nextdata);
142 	}
143 	ca->prevdata = chunks_data_hash + hash;
144 	chunks_data_hash[hash] = ca;
145 //	*lrutail = ca;
146 //	ca->prevlru = lrutail;
147 //	ca->nextlru = NULL;
148 //	lrutail = &(ca->nextlru);
149 	return ca;
150 }
151 /*
152 static inline void chunks_lru_move(chunks_data_entry *ca) {
153 	if (ca->nextlru==NULL) {
154 		return;
155 	}
156 	*(ca->prevlru) = ca->nextlru;
157 	ca->nextlru->prevlru = ca->prevlru;
158 	*lrutail = ca;
159 	ca->prevlru = lrutail;
160 	ca->nextlru = NULL;
161 	lrutail = &(ca->nextlru);
162 }
163 */
164 
165 // clears all entries with chindx higher or equal to given
chunksdatacache_clear_inode(uint32_t inode,uint32_t chindx)166 void chunksdatacache_clear_inode(uint32_t inode,uint32_t chindx) {
167 	chunks_inode_entry *ih,*ihn;
168 	chunks_data_entry *ca,*can;
169 
170 	pthread_mutex_lock(&lock);
171 	ih = chunks_inode_hash[chunks_inode_hash_fn(inode)];
172 	while (ih) {
173 		ihn = ih->next;
174 		if (ih->inode==inode) {
175 			ca = ih->data_head;
176 			while (ca) {
177 				can = ca->nextinode;
178 				if (ca->chindx>=chindx) {
179 					chunks_remove_entry(ca);
180 				}
181 				ca = can;
182 			}
183 		}
184 		ih = ihn;
185 	}
186 	pthread_mutex_unlock(&lock);
187 }
188 
chunksdatacache_invalidate(uint32_t inode,uint32_t chindx)189 void chunksdatacache_invalidate(uint32_t inode,uint32_t chindx) {
190 	chunks_data_entry *ca;
191 	uint32_t hash;
192 
193 	pthread_mutex_lock(&lock);
194 	hash = chunks_data_hash_fn(inode,chindx);
195 	for (ca = chunks_data_hash[hash] ; ca ; ca=ca->nextdata) {
196 		if (ca->inode==inode && ca->chindx==chindx) {
197 			chunks_remove_entry(ca);
198 			pthread_mutex_unlock(&lock);
199 			return;
200 		}
201 	}
202 	pthread_mutex_unlock(&lock);
203 }
204 
chunksdatacache_check(uint32_t inode,uint32_t chindx,uint64_t chunkid,uint32_t version)205 uint8_t chunksdatacache_check(uint32_t inode,uint32_t chindx,uint64_t chunkid,uint32_t version) {
206 	chunks_data_entry *ca;
207 	uint32_t hash;
208 
209 	pthread_mutex_lock(&lock);
210 	hash = chunks_data_hash_fn(inode,chindx);
211 	for (ca = chunks_data_hash[hash] ; ca ; ca=ca->nextdata) {
212 		if (ca->inode==inode && ca->chindx==chindx) {
213 			if (ca->chunkid==chunkid && ca->version==version) {
214 				pthread_mutex_unlock(&lock);
215 				return 1;
216 			} else {
217 				pthread_mutex_unlock(&lock);
218 				return 0;
219 			}
220 		}
221 	}
222 	pthread_mutex_unlock(&lock);
223 	return 0;
224 }
225 
chunksdatacache_change(uint32_t inode,uint32_t chindx,uint64_t chunkid,uint32_t version)226 void chunksdatacache_change(uint32_t inode,uint32_t chindx,uint64_t chunkid,uint32_t version) {
227 	chunks_data_entry *ca;
228 	uint32_t hash;
229 
230 	pthread_mutex_lock(&lock);
231 	hash = chunks_data_hash_fn(inode,chindx);
232 	for (ca = chunks_data_hash[hash] ; ca ; ca=ca->nextdata) {
233 		if (ca->inode==inode && ca->chindx==chindx) {
234 			ca->chunkid = chunkid;
235 			ca->version = version;
236 			pthread_mutex_unlock(&lock);
237 			return;
238 		}
239 	}
240 	pthread_mutex_unlock(&lock);
241 }
242 
chunksdatacache_insert(uint32_t inode,uint32_t chindx,uint64_t chunkid,uint32_t version,uint8_t csdataver,const uint8_t * csdata,uint32_t csdatasize)243 void chunksdatacache_insert(uint32_t inode,uint32_t chindx,uint64_t chunkid,uint32_t version,uint8_t csdataver,const uint8_t *csdata,uint32_t csdatasize) {
244 	chunks_data_entry *ca;
245 	uint32_t hash;
246 
247 	pthread_mutex_lock(&lock);
248 	hash = chunks_data_hash_fn(inode,chindx);
249 	for (ca = chunks_data_hash[hash] ; ca ; ca=ca->nextdata) {
250 		if (ca->inode==inode && ca->chindx==chindx) {
251 //			chunks_lru_move(ca);
252 			break;
253 		}
254 	}
255 
256 	if (ca==NULL) {
257 //		while (entries>=CHUNKS_MAX_ENTRIES) {
258 //			chunks_remove_entry(lruhead);
259 //		}
260 		ca = chunks_new_entry(inode,chindx);
261 	}
262 
263 	ca->chunkid = chunkid;
264 	ca->version = version;
265 	ca->csdataver = csdataver;
266 	if (ca->csdatasize==csdatasize) {
267 		if (csdatasize>0) {
268 			memcpy(ca->csdata,csdata,csdatasize);
269 		}
270 	} else {
271 		if (ca->csdata) {
272 			free(ca->csdata);
273 		}
274 		if (csdatasize>0) {
275 			ca->csdata = malloc(csdatasize);
276 			memcpy(ca->csdata,csdata,csdatasize);
277 		} else {
278 			ca->csdata = NULL;
279 		}
280 		ca->csdatasize = csdatasize;
281 	}
282 	pthread_mutex_unlock(&lock);
283 }
284 
chunksdatacache_find(uint32_t inode,uint32_t chindx,uint64_t * chunkid,uint32_t * version,uint8_t * csdataver,uint8_t * csdata,uint32_t * csdatasize)285 uint8_t chunksdatacache_find(uint32_t inode,uint32_t chindx,uint64_t *chunkid,uint32_t *version,uint8_t *csdataver,uint8_t *csdata,uint32_t *csdatasize) {
286 	chunks_data_entry *ca;
287 	uint32_t hash;
288 
289 	pthread_mutex_lock(&lock);
290 	hash = chunks_data_hash_fn(inode,chindx);
291 	for (ca = chunks_data_hash[hash] ; ca ; ca=ca->nextdata) {
292 		if (ca->inode==inode && ca->chindx==chindx) {
293 //			chunks_lru_move(ca);
294 			if (*csdatasize < ca->csdatasize) { // not enough space in external buffer
295 				pthread_mutex_unlock(&lock);
296 				return 0;
297 			}
298 			*chunkid = ca->chunkid;
299 			*version = ca->version;
300 			*csdataver = ca->csdataver;
301 			memcpy(csdata,ca->csdata,ca->csdatasize);
302 			*csdatasize = ca->csdatasize;
303 			pthread_mutex_unlock(&lock);
304 			return 1;
305 		}
306 	}
307 	pthread_mutex_unlock(&lock);
308 	return 0;
309 }
310 
chunksdatacache_cleanup(void)311 void chunksdatacache_cleanup(void) {
312 	chunks_inode_entry *ih,*ihn;
313 	chunks_data_entry *ca,*can;
314 	uint32_t hash;
315 
316 	pthread_mutex_lock(&lock);
317 	for (hash = 0 ; hash < CHUNKS_INODE_HASH_SIZE ; hash++) {
318 		ih = chunks_inode_hash[hash];
319 		while (ih) {
320 			ihn = ih->next;
321 			free(ih);
322 			ih = ihn;
323 		}
324 		chunks_inode_hash[hash] = NULL;
325 	}
326 	for (hash = 0 ; hash < CHUNKS_DATA_HASH_SIZE ; hash++) {
327 		ca = chunks_data_hash[hash];
328 		while (ca) {
329 			can = ca->nextdata;
330 			if (ca->csdata) {
331 				free(ca->csdata);
332 			}
333 			free(ca);
334 			ca = can;
335 		}
336 		chunks_data_hash[hash] = NULL;
337 	}
338 //	lruhead = NULL;
339 //	lrutail = &lruhead;
340 //	entries = 0;
341 	pthread_mutex_unlock(&lock);
342 }
343 
chunksdatacache_term(void)344 void chunksdatacache_term(void) {
345 	chunksdatacache_cleanup();
346 	free(chunks_inode_hash);
347 	free(chunks_data_hash);
348 	pthread_mutex_destroy(&lock);
349 }
350 
chunksdatacache_init(void)351 void chunksdatacache_init(void) {
352 	uint32_t hash;
353 
354 	chunks_inode_hash = malloc(sizeof(chunks_inode_entry*)*CHUNKS_INODE_HASH_SIZE);
355 	passert(chunks_inode_hash);
356 
357 	chunks_data_hash = malloc(sizeof(chunks_data_entry*)*CHUNKS_DATA_HASH_SIZE);
358 	passert(chunks_data_hash);
359 
360 	for (hash = 0 ; hash < CHUNKS_INODE_HASH_SIZE ; hash++) {
361 		chunks_inode_hash[hash] = NULL;
362 	}
363 	for (hash = 0 ; hash < CHUNKS_DATA_HASH_SIZE ; hash++) {
364 		chunks_data_hash[hash] = NULL;
365 	}
366 //	lruhead = NULL;
367 //	lrutail = &lruhead;
368 //	entries = 0;
369 	pthread_mutex_init(&lock,NULL);
370 }
371