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 #if defined(HAVE_CONFIG_H)
22 #  include "config.h"
23 #endif
24 
25 #include <stdlib.h>
26 #include <string.h>
27 #include <pthread.h>
28 #include <inttypes.h>
29 
30 #include "massert.h"
31 #include "mastercomm.h"
32 #include "mfs_fuse.h"
33 #include "clocks.h"
34 #include "portable.h"
35 #include "lwthread.h"
36 
37 #define HASH_SIZE 0x8000
38 #define HASH_MASK 0x7FFF
39 #define MAX_ELEMENTS 10000
40 #define MIN_TIMEOUT 30.0
41 
42 typedef struct _dinval_element {
43 	uint32_t parent;
44 	uint8_t nleng;
45 	uint8_t *name;
46 	uint32_t inode;
47 	double timestamp;
48 	struct _dinval_element *queue_next,**queue_prev;
49 	struct _dinval_element *hash_next,**hash_prev;
50 } dinval_element;
51 
52 static dinval_element **hashtab;
53 static uint32_t elementcnt;
54 static dinval_element *queue_head,**queue_prev;
55 static double main_timeout;
56 
57 static pthread_mutex_t glock;
58 
dinval_calc_hash(uint32_t parent,uint8_t nleng,const uint8_t * name)59 static inline uint32_t dinval_calc_hash(uint32_t parent,uint8_t nleng,const uint8_t *name) {
60 	uint32_t hash=5381;
61 	while (nleng>0) {
62 		hash = ((hash<<5)+hash)^(*name);
63 		name++;
64 		nleng--;
65 	}
66 	hash ^= parent;
67 	return hash & HASH_MASK;
68 }
69 
dinval_calc_elem_hash(dinval_element * dielem)70 static inline uint32_t dinval_calc_elem_hash(dinval_element *dielem) {
71 	return dinval_calc_hash(dielem->parent,dielem->nleng,dielem->name);
72 }
73 
dinval_queue_detach(dinval_element * dielem)74 static inline void dinval_queue_detach(dinval_element *dielem) {
75 	if (dielem->queue_next!=NULL) {
76 		dielem->queue_next->queue_prev = dielem->queue_prev;
77 	} else {
78 		queue_prev = dielem->queue_prev;
79 	}
80 	*(dielem->queue_prev) = dielem->queue_next;
81 }
82 
dinval_element_detach(dinval_element * dielem)83 static inline void dinval_element_detach(dinval_element *dielem) {
84 	if (dielem->queue_next!=NULL) {
85 		dielem->queue_next->queue_prev = dielem->queue_prev;
86 	} else {
87 		queue_prev = dielem->queue_prev;
88 	}
89 	*(dielem->queue_prev) = dielem->queue_next;
90 	if (dielem->hash_next!=NULL) {
91 		dielem->hash_next->hash_prev = dielem->hash_prev;
92 	}
93 	*(dielem->hash_prev) = dielem->hash_next;
94 }
95 
dinval_queue_attach(dinval_element * dielem)96 static inline void dinval_queue_attach(dinval_element *dielem) {
97 	// queue
98 	dielem->queue_next = NULL;
99 	dielem->queue_prev = queue_prev;
100 	*(queue_prev) = dielem;
101 	queue_prev = &(dielem->queue_next);
102 	// timestamp
103 	dielem->timestamp = monotonic_seconds();
104 }
105 
dinval_element_attach(uint32_t hashhint,dinval_element * dielem)106 static inline void dinval_element_attach(uint32_t hashhint,dinval_element *dielem) {
107 	uint32_t hash;
108 
109 	if (hashhint<HASH_SIZE) {
110 		hash = hashhint;
111 	} else {
112 		hash = dinval_calc_elem_hash(dielem);
113 	}
114 	// queue
115 	dielem->queue_next = NULL;
116 	dielem->queue_prev = queue_prev;
117 	*(queue_prev) = dielem;
118 	queue_prev = &(dielem->queue_next);
119 	// hash
120 	dielem->hash_next = hashtab[hash];
121 	if (dielem->hash_next!=NULL) {
122 		dielem->hash_next->hash_prev = &(dielem->hash_next);
123 	}
124 	dielem->hash_prev = hashtab+hash;
125 	hashtab[hash] = dielem;
126 	// timestamp
127 	dielem->timestamp = monotonic_seconds();
128 }
129 
dinval_element_find(uint32_t * hashhint,uint32_t parent,uint8_t nleng,const uint8_t * name)130 static inline dinval_element* dinval_element_find(uint32_t *hashhint,uint32_t parent,uint8_t nleng,const uint8_t *name) {
131 	uint32_t hash;
132 	dinval_element *dielem;
133 
134 	hash = dinval_calc_hash(parent,nleng,name);
135 	for (dielem = hashtab[hash] ; dielem!=NULL ; dielem=dielem->hash_next) {
136 		if (dielem->parent==parent && dielem->nleng==nleng && memcmp(dielem->name,name,nleng)==0) {
137 			return dielem;
138 		}
139 	}
140 	if (hashhint!=NULL) {
141 		*hashhint = hash;
142 	}
143 	return NULL;
144 }
145 
146 // add or refresh time if exists (lookup)
dinval_add(uint32_t parent,uint8_t nleng,const uint8_t * name,uint32_t inode)147 void dinval_add(uint32_t parent,uint8_t nleng,const uint8_t *name,uint32_t inode) {
148 	dinval_element *dielem;
149 	uint32_t hashhint;
150 
151 	pthread_mutex_lock(&glock);
152 	dielem = dinval_element_find(&hashhint,parent,nleng,name);
153 	if (dielem) {
154 		dinval_queue_detach(dielem);
155 		dielem->inode = inode;
156 		dinval_queue_attach(dielem);
157 	} else {
158 		dielem = malloc(sizeof(dinval_element));
159 		passert(dielem);
160 		dielem->parent = parent;
161 		dielem->nleng = nleng;
162 		dielem->name = malloc(nleng+1);
163 		passert(dielem->name);
164 		memcpy(dielem->name,name,nleng);
165 		dielem->name[nleng]=0; // we use it to print
166 		dielem->inode = inode;
167 		dinval_element_attach(hashhint,dielem);
168 		elementcnt++;
169 	}
170 	pthread_mutex_unlock(&glock);
171 }
172 
173 // (rmdir)
dinval_remove(uint32_t parent,uint8_t nleng,const uint8_t * name)174 void dinval_remove(uint32_t parent,uint8_t nleng,const uint8_t *name) {
175 	dinval_element *dielem;
176 	pthread_mutex_lock(&glock);
177 	dielem = dinval_element_find(NULL,parent,nleng,name);
178 	if (dielem) {
179 		dinval_element_detach(dielem);
180 		free(dielem->name);
181 		free(dielem);
182 		elementcnt--;
183 	}
184 	pthread_mutex_unlock(&glock);
185 }
186 
dinval_invalthread(void * arg)187 void* dinval_invalthread(void* arg) {
188 	double timeout;
189 	double now;
190 	uint32_t i;
191 	dinval_element *dielem;
192 
193 	timeout = main_timeout;
194 	while (1) {
195 		now = monotonic_seconds();
196 		pthread_mutex_lock(&glock);
197 		i = 100;
198 		while (i>0 && ((elementcnt>MAX_ELEMENTS && queue_head->timestamp+MIN_TIMEOUT<now) || (queue_head!=NULL && queue_head->timestamp+timeout<now))) {
199 			dielem = queue_head;
200 			if (fs_isopen(dielem->inode)) { // can't invalidate inodes that are still open
201 				dinval_queue_detach(dielem);
202 				dinval_queue_attach(dielem);
203 			} else {
204 				dinval_element_detach(dielem);
205 				pthread_mutex_unlock(&glock);
206 				mfs_dentry_invalidate(dielem->parent,dielem->nleng,(const char*)(dielem->name));
207 				pthread_mutex_lock(&glock);
208 				free(dielem->name);
209 				free(dielem);
210 				elementcnt--;
211 			}
212 			i--;
213 		}
214 		pthread_mutex_unlock(&glock);
215 		portable_usleep(10000);
216 	}
217 	return arg;
218 }
219 
dinval_init(double timeout)220 void dinval_init(double timeout) {
221 	uint32_t i;
222 	pthread_t th;
223 
224 	hashtab = malloc(sizeof(dinval_element*)*HASH_SIZE);
225 	for (i=0 ; i<HASH_SIZE ; i++) {
226 		hashtab[i] = NULL;
227 	}
228 	elementcnt = 0;
229 	queue_head = NULL;
230 	queue_prev = &queue_head;
231 	if (timeout>MIN_TIMEOUT) { // we need time for CWD check (delay in fs_isopen)
232 		main_timeout = timeout;
233 	} else {
234 		main_timeout = MIN_TIMEOUT;
235 	}
236 	zassert(pthread_mutex_init(&glock,NULL));
237 	lwt_minthread_create(&th,1,dinval_invalthread,NULL);
238 }
239