1 /* AFS caching stuff
2  *
3  * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
4  * Written by David Howells (dhowells@redhat.com)
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version
9  * 2 of the License, or (at your option) any later version.
10  */
11 
12 #include <linux/sched.h>
13 #include "internal.h"
14 
15 static uint16_t afs_cell_cache_get_key(const void *cookie_netfs_data,
16 				       void *buffer, uint16_t buflen);
17 static uint16_t afs_cell_cache_get_aux(const void *cookie_netfs_data,
18 				       void *buffer, uint16_t buflen);
19 static enum fscache_checkaux afs_cell_cache_check_aux(void *cookie_netfs_data,
20 						      const void *buffer,
21 						      uint16_t buflen);
22 
23 static uint16_t afs_vlocation_cache_get_key(const void *cookie_netfs_data,
24 					    void *buffer, uint16_t buflen);
25 static uint16_t afs_vlocation_cache_get_aux(const void *cookie_netfs_data,
26 					    void *buffer, uint16_t buflen);
27 static enum fscache_checkaux afs_vlocation_cache_check_aux(
28 	void *cookie_netfs_data, const void *buffer, uint16_t buflen);
29 
30 static uint16_t afs_volume_cache_get_key(const void *cookie_netfs_data,
31 					 void *buffer, uint16_t buflen);
32 
33 static uint16_t afs_vnode_cache_get_key(const void *cookie_netfs_data,
34 					void *buffer, uint16_t buflen);
35 static void afs_vnode_cache_get_attr(const void *cookie_netfs_data,
36 				     uint64_t *size);
37 static uint16_t afs_vnode_cache_get_aux(const void *cookie_netfs_data,
38 					void *buffer, uint16_t buflen);
39 static enum fscache_checkaux afs_vnode_cache_check_aux(void *cookie_netfs_data,
40 						       const void *buffer,
41 						       uint16_t buflen);
42 static void afs_vnode_cache_now_uncached(void *cookie_netfs_data);
43 
44 struct fscache_netfs afs_cache_netfs = {
45 	.name			= "afs",
46 	.version		= 0,
47 };
48 
49 struct fscache_cookie_def afs_cell_cache_index_def = {
50 	.name		= "AFS.cell",
51 	.type		= FSCACHE_COOKIE_TYPE_INDEX,
52 	.get_key	= afs_cell_cache_get_key,
53 	.get_aux	= afs_cell_cache_get_aux,
54 	.check_aux	= afs_cell_cache_check_aux,
55 };
56 
57 struct fscache_cookie_def afs_vlocation_cache_index_def = {
58 	.name			= "AFS.vldb",
59 	.type			= FSCACHE_COOKIE_TYPE_INDEX,
60 	.get_key		= afs_vlocation_cache_get_key,
61 	.get_aux		= afs_vlocation_cache_get_aux,
62 	.check_aux		= afs_vlocation_cache_check_aux,
63 };
64 
65 struct fscache_cookie_def afs_volume_cache_index_def = {
66 	.name		= "AFS.volume",
67 	.type		= FSCACHE_COOKIE_TYPE_INDEX,
68 	.get_key	= afs_volume_cache_get_key,
69 };
70 
71 struct fscache_cookie_def afs_vnode_cache_index_def = {
72 	.name			= "AFS.vnode",
73 	.type			= FSCACHE_COOKIE_TYPE_DATAFILE,
74 	.get_key		= afs_vnode_cache_get_key,
75 	.get_attr		= afs_vnode_cache_get_attr,
76 	.get_aux		= afs_vnode_cache_get_aux,
77 	.check_aux		= afs_vnode_cache_check_aux,
78 	.now_uncached		= afs_vnode_cache_now_uncached,
79 };
80 
81 /*
82  * set the key for the index entry
83  */
afs_cell_cache_get_key(const void * cookie_netfs_data,void * buffer,uint16_t bufmax)84 static uint16_t afs_cell_cache_get_key(const void *cookie_netfs_data,
85 				       void *buffer, uint16_t bufmax)
86 {
87 	const struct afs_cell *cell = cookie_netfs_data;
88 	uint16_t klen;
89 
90 	_enter("%p,%p,%u", cell, buffer, bufmax);
91 
92 	klen = strlen(cell->name);
93 	if (klen > bufmax)
94 		return 0;
95 
96 	memcpy(buffer, cell->name, klen);
97 	return klen;
98 }
99 
100 /*
101  * provide new auxiliary cache data
102  */
afs_cell_cache_get_aux(const void * cookie_netfs_data,void * buffer,uint16_t bufmax)103 static uint16_t afs_cell_cache_get_aux(const void *cookie_netfs_data,
104 				       void *buffer, uint16_t bufmax)
105 {
106 	const struct afs_cell *cell = cookie_netfs_data;
107 	uint16_t dlen;
108 
109 	_enter("%p,%p,%u", cell, buffer, bufmax);
110 
111 	dlen = cell->vl_naddrs * sizeof(cell->vl_addrs[0]);
112 	dlen = min(dlen, bufmax);
113 	dlen &= ~(sizeof(cell->vl_addrs[0]) - 1);
114 
115 	memcpy(buffer, cell->vl_addrs, dlen);
116 	return dlen;
117 }
118 
119 /*
120  * check that the auxiliary data indicates that the entry is still valid
121  */
afs_cell_cache_check_aux(void * cookie_netfs_data,const void * buffer,uint16_t buflen)122 static enum fscache_checkaux afs_cell_cache_check_aux(void *cookie_netfs_data,
123 						      const void *buffer,
124 						      uint16_t buflen)
125 {
126 	_leave(" = OKAY");
127 	return FSCACHE_CHECKAUX_OKAY;
128 }
129 
130 /*****************************************************************************/
131 /*
132  * set the key for the index entry
133  */
afs_vlocation_cache_get_key(const void * cookie_netfs_data,void * buffer,uint16_t bufmax)134 static uint16_t afs_vlocation_cache_get_key(const void *cookie_netfs_data,
135 					    void *buffer, uint16_t bufmax)
136 {
137 	const struct afs_vlocation *vlocation = cookie_netfs_data;
138 	uint16_t klen;
139 
140 	_enter("{%s},%p,%u", vlocation->vldb.name, buffer, bufmax);
141 
142 	klen = strnlen(vlocation->vldb.name, sizeof(vlocation->vldb.name));
143 	if (klen > bufmax)
144 		return 0;
145 
146 	memcpy(buffer, vlocation->vldb.name, klen);
147 
148 	_leave(" = %u", klen);
149 	return klen;
150 }
151 
152 /*
153  * provide new auxiliary cache data
154  */
afs_vlocation_cache_get_aux(const void * cookie_netfs_data,void * buffer,uint16_t bufmax)155 static uint16_t afs_vlocation_cache_get_aux(const void *cookie_netfs_data,
156 					    void *buffer, uint16_t bufmax)
157 {
158 	const struct afs_vlocation *vlocation = cookie_netfs_data;
159 	uint16_t dlen;
160 
161 	_enter("{%s},%p,%u", vlocation->vldb.name, buffer, bufmax);
162 
163 	dlen = sizeof(struct afs_cache_vlocation);
164 	dlen -= offsetof(struct afs_cache_vlocation, nservers);
165 	if (dlen > bufmax)
166 		return 0;
167 
168 	memcpy(buffer, (uint8_t *)&vlocation->vldb.nservers, dlen);
169 
170 	_leave(" = %u", dlen);
171 	return dlen;
172 }
173 
174 /*
175  * check that the auxiliary data indicates that the entry is still valid
176  */
177 static
afs_vlocation_cache_check_aux(void * cookie_netfs_data,const void * buffer,uint16_t buflen)178 enum fscache_checkaux afs_vlocation_cache_check_aux(void *cookie_netfs_data,
179 						    const void *buffer,
180 						    uint16_t buflen)
181 {
182 	const struct afs_cache_vlocation *cvldb;
183 	struct afs_vlocation *vlocation = cookie_netfs_data;
184 	uint16_t dlen;
185 
186 	_enter("{%s},%p,%u", vlocation->vldb.name, buffer, buflen);
187 
188 	/* check the size of the data is what we're expecting */
189 	dlen = sizeof(struct afs_cache_vlocation);
190 	dlen -= offsetof(struct afs_cache_vlocation, nservers);
191 	if (dlen != buflen)
192 		return FSCACHE_CHECKAUX_OBSOLETE;
193 
194 	cvldb = container_of(buffer, struct afs_cache_vlocation, nservers);
195 
196 	/* if what's on disk is more valid than what's in memory, then use the
197 	 * VL record from the cache */
198 	if (!vlocation->valid || vlocation->vldb.rtime == cvldb->rtime) {
199 		memcpy((uint8_t *)&vlocation->vldb.nservers, buffer, dlen);
200 		vlocation->valid = 1;
201 		_leave(" = SUCCESS [c->m]");
202 		return FSCACHE_CHECKAUX_OKAY;
203 	}
204 
205 	/* need to update the cache if the cached info differs */
206 	if (memcmp(&vlocation->vldb, buffer, dlen) != 0) {
207 		/* delete if the volume IDs for this name differ */
208 		if (memcmp(&vlocation->vldb.vid, &cvldb->vid,
209 			   sizeof(cvldb->vid)) != 0
210 		    ) {
211 			_leave(" = OBSOLETE");
212 			return FSCACHE_CHECKAUX_OBSOLETE;
213 		}
214 
215 		_leave(" = UPDATE");
216 		return FSCACHE_CHECKAUX_NEEDS_UPDATE;
217 	}
218 
219 	_leave(" = OKAY");
220 	return FSCACHE_CHECKAUX_OKAY;
221 }
222 
223 /*****************************************************************************/
224 /*
225  * set the key for the volume index entry
226  */
afs_volume_cache_get_key(const void * cookie_netfs_data,void * buffer,uint16_t bufmax)227 static uint16_t afs_volume_cache_get_key(const void *cookie_netfs_data,
228 					void *buffer, uint16_t bufmax)
229 {
230 	const struct afs_volume *volume = cookie_netfs_data;
231 	uint16_t klen;
232 
233 	_enter("{%u},%p,%u", volume->type, buffer, bufmax);
234 
235 	klen = sizeof(volume->type);
236 	if (klen > bufmax)
237 		return 0;
238 
239 	memcpy(buffer, &volume->type, sizeof(volume->type));
240 
241 	_leave(" = %u", klen);
242 	return klen;
243 
244 }
245 
246 /*****************************************************************************/
247 /*
248  * set the key for the index entry
249  */
afs_vnode_cache_get_key(const void * cookie_netfs_data,void * buffer,uint16_t bufmax)250 static uint16_t afs_vnode_cache_get_key(const void *cookie_netfs_data,
251 					void *buffer, uint16_t bufmax)
252 {
253 	const struct afs_vnode *vnode = cookie_netfs_data;
254 	uint16_t klen;
255 
256 	_enter("{%x,%x,%llx},%p,%u",
257 	       vnode->fid.vnode, vnode->fid.unique, vnode->status.data_version,
258 	       buffer, bufmax);
259 
260 	klen = sizeof(vnode->fid.vnode);
261 	if (klen > bufmax)
262 		return 0;
263 
264 	memcpy(buffer, &vnode->fid.vnode, sizeof(vnode->fid.vnode));
265 
266 	_leave(" = %u", klen);
267 	return klen;
268 }
269 
270 /*
271  * provide updated file attributes
272  */
afs_vnode_cache_get_attr(const void * cookie_netfs_data,uint64_t * size)273 static void afs_vnode_cache_get_attr(const void *cookie_netfs_data,
274 				     uint64_t *size)
275 {
276 	const struct afs_vnode *vnode = cookie_netfs_data;
277 
278 	_enter("{%x,%x,%llx},",
279 	       vnode->fid.vnode, vnode->fid.unique,
280 	       vnode->status.data_version);
281 
282 	*size = vnode->status.size;
283 }
284 
285 /*
286  * provide new auxiliary cache data
287  */
afs_vnode_cache_get_aux(const void * cookie_netfs_data,void * buffer,uint16_t bufmax)288 static uint16_t afs_vnode_cache_get_aux(const void *cookie_netfs_data,
289 					void *buffer, uint16_t bufmax)
290 {
291 	const struct afs_vnode *vnode = cookie_netfs_data;
292 	uint16_t dlen;
293 
294 	_enter("{%x,%x,%Lx},%p,%u",
295 	       vnode->fid.vnode, vnode->fid.unique, vnode->status.data_version,
296 	       buffer, bufmax);
297 
298 	dlen = sizeof(vnode->fid.unique) + sizeof(vnode->status.data_version);
299 	if (dlen > bufmax)
300 		return 0;
301 
302 	memcpy(buffer, &vnode->fid.unique, sizeof(vnode->fid.unique));
303 	buffer += sizeof(vnode->fid.unique);
304 	memcpy(buffer, &vnode->status.data_version,
305 	       sizeof(vnode->status.data_version));
306 
307 	_leave(" = %u", dlen);
308 	return dlen;
309 }
310 
311 /*
312  * check that the auxiliary data indicates that the entry is still valid
313  */
afs_vnode_cache_check_aux(void * cookie_netfs_data,const void * buffer,uint16_t buflen)314 static enum fscache_checkaux afs_vnode_cache_check_aux(void *cookie_netfs_data,
315 						       const void *buffer,
316 						       uint16_t buflen)
317 {
318 	struct afs_vnode *vnode = cookie_netfs_data;
319 	uint16_t dlen;
320 
321 	_enter("{%x,%x,%llx},%p,%u",
322 	       vnode->fid.vnode, vnode->fid.unique, vnode->status.data_version,
323 	       buffer, buflen);
324 
325 	/* check the size of the data is what we're expecting */
326 	dlen = sizeof(vnode->fid.unique) + sizeof(vnode->status.data_version);
327 	if (dlen != buflen) {
328 		_leave(" = OBSOLETE [len %hx != %hx]", dlen, buflen);
329 		return FSCACHE_CHECKAUX_OBSOLETE;
330 	}
331 
332 	if (memcmp(buffer,
333 		   &vnode->fid.unique,
334 		   sizeof(vnode->fid.unique)
335 		   ) != 0) {
336 		unsigned unique;
337 
338 		memcpy(&unique, buffer, sizeof(unique));
339 
340 		_leave(" = OBSOLETE [uniq %x != %x]",
341 		       unique, vnode->fid.unique);
342 		return FSCACHE_CHECKAUX_OBSOLETE;
343 	}
344 
345 	if (memcmp(buffer + sizeof(vnode->fid.unique),
346 		   &vnode->status.data_version,
347 		   sizeof(vnode->status.data_version)
348 		   ) != 0) {
349 		afs_dataversion_t version;
350 
351 		memcpy(&version, buffer + sizeof(vnode->fid.unique),
352 		       sizeof(version));
353 
354 		_leave(" = OBSOLETE [vers %llx != %llx]",
355 		       version, vnode->status.data_version);
356 		return FSCACHE_CHECKAUX_OBSOLETE;
357 	}
358 
359 	_leave(" = SUCCESS");
360 	return FSCACHE_CHECKAUX_OKAY;
361 }
362 
363 /*
364  * indication the cookie is no longer uncached
365  * - this function is called when the backing store currently caching a cookie
366  *   is removed
367  * - the netfs should use this to clean up any markers indicating cached pages
368  * - this is mandatory for any object that may have data
369  */
afs_vnode_cache_now_uncached(void * cookie_netfs_data)370 static void afs_vnode_cache_now_uncached(void *cookie_netfs_data)
371 {
372 	struct afs_vnode *vnode = cookie_netfs_data;
373 	struct pagevec pvec;
374 	pgoff_t first;
375 	int loop, nr_pages;
376 
377 	_enter("{%x,%x,%Lx}",
378 	       vnode->fid.vnode, vnode->fid.unique, vnode->status.data_version);
379 
380 	pagevec_init(&pvec, 0);
381 	first = 0;
382 
383 	for (;;) {
384 		/* grab a bunch of pages to clean */
385 		nr_pages = pagevec_lookup(&pvec, vnode->vfs_inode.i_mapping,
386 					  first,
387 					  PAGEVEC_SIZE - pagevec_count(&pvec));
388 		if (!nr_pages)
389 			break;
390 
391 		for (loop = 0; loop < nr_pages; loop++)
392 			ClearPageFsCache(pvec.pages[loop]);
393 
394 		first = pvec.pages[nr_pages - 1]->index + 1;
395 
396 		pvec.nr = nr_pages;
397 		pagevec_release(&pvec);
398 		cond_resched();
399 	}
400 
401 	_leave("");
402 }
403