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 /* common */
26 #include <inttypes.h>
27 #include <stdlib.h>
28 #include <pthread.h>
29 
30 #include "massert.h"
31 #include "portable.h"
32 #include "lwthread.h"
33 #include "clocks.h"
34 
35 #define MAX_LIST_LENGTH 20
36 
37 #define HASH_SIZE 16384
38 
39 typedef struct _sinoparent {
40 	uint32_t inode;
41 	uint32_t parent;
42 	uint32_t validtime;
43 	struct _sinoparent *next;
44 } sinoparent;
45 
46 static sinoparent* sparents_hash[HASH_SIZE];
47 static pthread_mutex_t sparents_lock[HASH_SIZE];
48 
49 #ifndef HAVE___SYNC_FETCH_AND_OP
50 static pthread_mutex_t glock;
51 #endif
52 static pthread_t clthread;
53 static uint8_t term;
54 
sparents_hashfn(uint32_t inode)55 static inline uint32_t sparents_hashfn(uint32_t inode) {
56 	return (inode%HASH_SIZE);
57 }
58 
sparents_add(uint32_t inode,uint32_t parent,uint32_t timeout)59 void sparents_add(uint32_t inode,uint32_t parent,uint32_t timeout) {
60 	uint32_t hash;
61 	uint32_t leng;
62 	sinoparent *oldest;
63 	sinoparent *sip;
64 
65 	hash = sparents_hashfn(inode);
66 	zassert(pthread_mutex_lock(sparents_lock+hash));
67 	sip = sparents_hash[hash];
68 	oldest = NULL;
69 	leng = 0;
70 	while (sip!=NULL) {
71 		if (sip->inode==inode) {
72 			sip->parent = parent;
73 			sip->validtime = monotonic_seconds() + timeout;
74 			zassert(pthread_mutex_unlock(sparents_lock+hash));
75 			return;
76 		}
77 		if (oldest==NULL || oldest->validtime > sip->validtime) {
78 			oldest = sip;
79 		}
80 		leng++;
81 		sip = sip->next;
82 	}
83 	if (leng>=MAX_LIST_LENGTH && oldest!=NULL) { // second condition practically not needed unless somebody define MAX_LIST_LENGTH == 0
84 		sip = oldest;
85 	} else {
86 		sip = malloc(sizeof(sinoparent));
87 		sip->next = sparents_hash[hash];
88 		sparents_hash[hash] = sip;
89 	}
90 	sip->inode = inode;
91 	sip->parent = parent;
92 	sip->validtime = monotonic_seconds() + timeout;
93 	zassert(pthread_mutex_unlock(sparents_lock+hash));
94 }
95 
sparents_get(uint32_t inode)96 uint32_t sparents_get(uint32_t inode) {
97 	uint32_t hash;
98 	uint32_t parent;
99 	sinoparent *sip;
100 
101 	parent = 0;
102 	hash = sparents_hashfn(inode);
103 	zassert(pthread_mutex_lock(sparents_lock+hash));
104 	sip = sparents_hash[hash];
105 	while (sip!=NULL) {
106 		if (sip->inode==inode) {
107 			parent = sip->parent;
108 			break;
109 		}
110 		sip = sip->next;
111 	}
112 	zassert(pthread_mutex_unlock(sparents_lock+hash));
113 	return parent;
114 }
115 
sparents_cleanup(uint32_t hash,uint32_t current_time)116 static void sparents_cleanup(uint32_t hash,uint32_t current_time) {
117 	sinoparent *sip,**sipp;
118 
119 	zassert(pthread_mutex_lock(sparents_lock+hash));
120 	sipp = sparents_hash + hash;
121 	while ((sip=*sipp)!=NULL) {
122 		if (current_time==UINT32_C(0xFFFFFFFF) || sip->validtime < current_time) {
123 			*sipp = sip->next;
124 			free(sip);
125 		} else {
126 			sipp = &(sip->next);
127 		}
128 	}
129 	zassert(pthread_mutex_unlock(sparents_lock+hash));
130 }
131 
sparents_cleanupthread(void * arg)132 static void* sparents_cleanupthread(void *arg) {
133 	uint32_t cuhashpos;
134 	uint32_t current_time;
135 	uint32_t i;
136 	(void)arg;
137 
138 	cuhashpos = 0;
139 	while (1) {
140 		current_time = monotonic_seconds();
141 		for (i=0 ; i<(HASH_SIZE/(10*30)) ; i++) {
142 			sparents_cleanup(cuhashpos,current_time);
143 			cuhashpos = (cuhashpos+1)%HASH_SIZE;
144 		}
145 		portable_usleep(100000);
146 #ifdef HAVE___SYNC_FETCH_AND_OP
147 		if (__sync_fetch_and_or(&term,0)==1) {
148 			return NULL;
149 		}
150 #else
151 		zassert(pthread_mutex_lock(&glock));
152 		if (term==1) {
153 			zassert(pthread_mutex_unlock(&glock));
154 			return NULL;
155 		}
156 		zassert(pthread_mutex_unlock(&glock));
157 #endif
158 	}
159 	return NULL;
160 }
161 
sparents_term(void)162 void sparents_term(void) {
163 	uint32_t hash;
164 #ifdef HAVE___SYNC_FETCH_AND_OP
165 	__sync_fetch_and_or(&term,1);
166 #else
167 	zassert(pthread_mutex_lock(&glock));
168 	term = 1;
169 	zassert(pthread_mutex_unlock(&glock));
170 #endif
171 	pthread_join(clthread,NULL);
172 	for (hash=0 ; hash<HASH_SIZE ; hash++) {
173 		sparents_cleanup(hash,UINT32_C(0xFFFFFFFF));
174 		massert(sparents_hash[hash]==NULL,"structure hasn't been cleaned up");
175 		zassert(pthread_mutex_destroy(sparents_lock+hash));
176 	}
177 
178 #ifndef HAVE___SYNC_FETCH_AND_OP
179 	zassert(pthread_mutex_destroy(&glock));
180 #endif
181 }
182 
sparents_init(void)183 void sparents_init(void) {
184 	uint32_t hash;
185 #ifdef HAVE___SYNC_FETCH_AND_OP
186 	__sync_fetch_and_and(&term,0);
187 #else
188 	zassert(pthread_mutex_init(&glock,NULL));
189 	zassert(pthread_mutex_lock(&glock));
190 	term = 0;
191 	zassert(pthread_mutex_unlock(&glock));
192 #endif
193 	for (hash=0 ; hash<HASH_SIZE ; hash++) {
194 		zassert(pthread_mutex_init(sparents_lock+hash,NULL));
195 		sparents_hash[hash] = NULL;
196 	}
197 	lwt_minthread_create(&clthread,0,sparents_cleanupthread,NULL);
198 }
199