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