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