1 /*
2 * Copyright (C) 2016 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 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/time.h>
28 #include <inttypes.h>
29 #include <pthread.h>
30
31 #include "massert.h"
32 #include "clocks.h"
33
34 #define HASHSIZE 65536
35
36 typedef struct _xattr_cache_value {
37 uint32_t lcnt;
38 const uint8_t *value;
39 } xattr_cache_value;
40
41 typedef struct _xattr_cache_entry {
42 uint32_t hash;
43 uint32_t node;
44 uint32_t uid,gid;
45 uint32_t nleng;
46 uint32_t vleng;
47 int status;
48 const uint8_t *name;
49 xattr_cache_value *value;
50 int64_t utimestamp;
51 struct _xattr_cache_entry *hashnext,**hashprev;
52 struct _xattr_cache_entry *lrunext,**lruprev;
53 } xattr_cache_entry;
54
55 static xattr_cache_entry *lruhead,**lrutail;
56 static xattr_cache_entry **hashtab;
57 static int64_t xattr_cache_timeout;
58 static pthread_mutex_t glock;
59
xattr_cache_hash(uint32_t node,uint32_t nleng,const uint8_t * name)60 static inline uint32_t xattr_cache_hash(uint32_t node,uint32_t nleng,const uint8_t *name) {
61 uint32_t hash;
62 uint32_t i;
63 hash = node * 0x5F2318BD + nleng;
64 for (i=0 ; i<nleng ; i++) {
65 hash = hash*33+name[i];
66 }
67 return hash;
68 }
69
xattr_cache_value_alloc(void)70 static inline xattr_cache_value* xattr_cache_value_alloc(void) {
71 xattr_cache_value *v;
72 v = malloc(sizeof(xattr_cache_value));
73 passert(v);
74 v->lcnt = 1;
75 v->value = NULL;
76 return v;
77 }
78
xattr_cache_value_inc(xattr_cache_value * v)79 static inline void xattr_cache_value_inc(xattr_cache_value *v) {
80 v->lcnt++;
81 }
82
xattr_cache_value_dec(xattr_cache_value * v)83 static inline void xattr_cache_value_dec(xattr_cache_value *v) {
84 v->lcnt--;
85 if (v->lcnt==0) {
86 if (v->value) {
87 free((uint8_t*)(v->value));
88 }
89 free(v);
90 }
91 }
92
xattr_cache_remove_entry(xattr_cache_entry * xce)93 static inline void xattr_cache_remove_entry(xattr_cache_entry *xce) {
94 if (xce->hashnext) {
95 xce->hashnext->hashprev = xce->hashprev;
96 }
97 *(xce->hashprev) = xce->hashnext;
98 if (xce->lrunext) {
99 xce->lrunext->lruprev = xce->lruprev;
100 } else {
101 lrutail = xce->lruprev;
102 }
103 *(xce->lruprev) = xce->lrunext;
104 if (xce->name) {
105 free((uint8_t*)(xce->name));
106 }
107 xattr_cache_value_dec(xce->value);
108 free(xce);
109 }
110
xattr_cache_new(uint32_t node,uint32_t uid,uint32_t gid,uint32_t nleng,const uint8_t * name,const uint8_t * value,uint32_t vleng,int status,int64_t utimestamp)111 static inline void xattr_cache_new(uint32_t node,uint32_t uid,uint32_t gid,uint32_t nleng,const uint8_t *name,const uint8_t *value,uint32_t vleng,int status,int64_t utimestamp) {
112 xattr_cache_entry *xce;
113 uint32_t hash;
114 hash = xattr_cache_hash(node,nleng,name);
115 xce = malloc(sizeof(xattr_cache_entry));
116 passert(xce);
117 xce->hash = hash;
118 xce->node = node;
119 xce->uid = uid;
120 xce->gid = gid;
121 xce->nleng = nleng;
122 if (nleng>0) {
123 xce->name = malloc(nleng);
124 passert(xce->name);
125 memcpy((uint8_t*)(xce->name),name,nleng);
126 } else {
127 xce->name = NULL;
128 }
129 xce->vleng = vleng;
130 xce->value = xattr_cache_value_alloc();
131 if (vleng>0) {
132 xce->value->value = malloc(vleng);
133 passert(xce->value->value);
134 memcpy((uint8_t*)(xce->value->value),value,vleng);
135 }
136 xce->status = status;
137 xce->utimestamp = utimestamp;
138 xce->hashnext = hashtab[hash%HASHSIZE];
139 xce->hashprev = hashtab+(hash%HASHSIZE);
140 if (xce->hashnext) {
141 xce->hashnext->hashprev = &(xce->hashnext);
142 }
143 hashtab[hash%HASHSIZE] = xce;
144 xce->lrunext = NULL;
145 xce->lruprev = lrutail;
146 *lrutail = xce;
147 lrutail = &(xce->lrunext);
148 }
149
xattr_cache_find(uint32_t node,uint32_t uid,uint32_t gid,uint32_t nleng,const uint8_t * name)150 static inline xattr_cache_entry* xattr_cache_find(uint32_t node,uint32_t uid,uint32_t gid,uint32_t nleng,const uint8_t *name) {
151 uint32_t hash;
152 xattr_cache_entry *xce;
153
154 hash = xattr_cache_hash(node,nleng,name);
155 for (xce = hashtab[hash%HASHSIZE] ; xce!=NULL ; xce=xce->hashnext) {
156 if (xce->hash == hash && xce->node == node && xce->uid == uid && xce->gid == gid && xce->nleng == nleng && memcmp(xce->name,name,nleng)==0) {
157 return xce;
158 }
159 }
160 return NULL;
161 }
162
xattr_cache_delete(uint32_t node,uint32_t nleng,const uint8_t * name)163 static inline void xattr_cache_delete(uint32_t node,uint32_t nleng,const uint8_t *name) {
164 uint32_t hash;
165 xattr_cache_entry *xce,*nxce;
166 hash = xattr_cache_hash(node,nleng,name);
167 xce = hashtab[hash%HASHSIZE];
168 while (xce) {
169 nxce = xce->hashnext;
170 if (xce->hash == hash && xce->node == node && xce->nleng == nleng && memcmp(xce->name,name,nleng)==0) {
171 xattr_cache_remove_entry(xce);
172 }
173 xce = nxce;
174 }
175 }
176
xattr_cache_invalidate(int64_t utimestamp)177 static inline void xattr_cache_invalidate(int64_t utimestamp) {
178 xattr_cache_entry *xce,*nxce;
179 xce = lruhead;
180 while (xce!=NULL && xce->utimestamp < utimestamp) {
181 nxce = xce->lrunext;
182 xattr_cache_remove_entry(xce);
183 xce = nxce;
184 }
185 if (lruhead==NULL) {
186 lrutail = &lruhead;
187 }
188 }
189
xattr_cache_get(uint32_t node,uint32_t uid,uint32_t gid,uint32_t nleng,const uint8_t * name,const uint8_t ** value,uint32_t * vleng,int * status)190 void* xattr_cache_get(uint32_t node,uint32_t uid,uint32_t gid,uint32_t nleng,const uint8_t *name,const uint8_t **value,uint32_t *vleng,int *status) {
191 xattr_cache_entry *xce;
192 xattr_cache_value *v;
193 int64_t utimestamp = monotonic_useconds();
194 zassert(pthread_mutex_lock(&glock));
195 xattr_cache_invalidate(utimestamp);
196 xce = xattr_cache_find(node,uid,gid,nleng,name);
197 if (xce==NULL) {
198 v = NULL;
199 } else {
200 *value = xce->value->value;
201 *vleng = xce->vleng;
202 *status = xce->status;
203 v = xce->value;
204 xattr_cache_value_inc(v);
205 }
206 zassert(pthread_mutex_unlock(&glock));
207 return (void*)v;
208 }
209
xattr_cache_set(uint32_t node,uint32_t uid,uint32_t gid,uint32_t nleng,const uint8_t * name,const uint8_t * value,uint32_t vleng,int status)210 void xattr_cache_set(uint32_t node,uint32_t uid,uint32_t gid,uint32_t nleng,const uint8_t *name,const uint8_t *value,uint32_t vleng,int status) {
211 int64_t utimestamp = monotonic_useconds();
212 zassert(pthread_mutex_lock(&glock));
213 xattr_cache_new(node,uid,gid,nleng,name,value,vleng,status,utimestamp+xattr_cache_timeout);
214 zassert(pthread_mutex_unlock(&glock));
215 }
216
xattr_cache_del(uint32_t node,uint32_t nleng,const uint8_t * name)217 void xattr_cache_del(uint32_t node,uint32_t nleng,const uint8_t *name) {
218 zassert(pthread_mutex_lock(&glock));
219 xattr_cache_delete(node,nleng,name);
220 zassert(pthread_mutex_unlock(&glock));
221 }
222
xattr_cache_rel(void * vv)223 void xattr_cache_rel(void *vv) {
224 zassert(pthread_mutex_lock(&glock));
225 xattr_cache_value_dec((xattr_cache_value*)vv);
226 zassert(pthread_mutex_unlock(&glock));
227 }
228
xattr_cache_init(double timeout)229 void xattr_cache_init(double timeout) {
230 uint32_t i;
231 lruhead = NULL;
232 lrutail = &lruhead;
233 hashtab = malloc(sizeof(xattr_cache_entry*)*HASHSIZE);
234 for (i=0; i<HASHSIZE ; i++) {
235 hashtab[i] = NULL;
236 }
237 xattr_cache_timeout = (int64_t)(1000000.0 * timeout);
238 zassert(pthread_mutex_init(&glock,NULL));
239 }
240