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