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
15static uint16_t afs_cell_cache_get_key(const void *cookie_netfs_data,
16				       void *buffer, uint16_t buflen);
17static uint16_t afs_cell_cache_get_aux(const void *cookie_netfs_data,
18				       void *buffer, uint16_t buflen);
19static enum fscache_checkaux afs_cell_cache_check_aux(void *cookie_netfs_data,
20						      const void *buffer,
21						      uint16_t buflen);
22
23static uint16_t afs_vlocation_cache_get_key(const void *cookie_netfs_data,
24					    void *buffer, uint16_t buflen);
25static uint16_t afs_vlocation_cache_get_aux(const void *cookie_netfs_data,
26					    void *buffer, uint16_t buflen);
27static enum fscache_checkaux afs_vlocation_cache_check_aux(
28	void *cookie_netfs_data, const void *buffer, uint16_t buflen);
29
30static uint16_t afs_volume_cache_get_key(const void *cookie_netfs_data,
31					 void *buffer, uint16_t buflen);
32
33static uint16_t afs_vnode_cache_get_key(const void *cookie_netfs_data,
34					void *buffer, uint16_t buflen);
35static void afs_vnode_cache_get_attr(const void *cookie_netfs_data,
36				     uint64_t *size);
37static uint16_t afs_vnode_cache_get_aux(const void *cookie_netfs_data,
38					void *buffer, uint16_t buflen);
39static enum fscache_checkaux afs_vnode_cache_check_aux(void *cookie_netfs_data,
40						       const void *buffer,
41						       uint16_t buflen);
42static void afs_vnode_cache_now_uncached(void *cookie_netfs_data);
43
44struct fscache_netfs afs_cache_netfs = {
45	.name			= "afs",
46	.version		= 0,
47};
48
49struct 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
57struct 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
65struct 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
71struct 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 */
84static 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 */
103static 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 */
122static 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 */
134static 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 */
155static 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 */
177static
178enum 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 */
227static 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 */
250static 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 */
273static 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 */
288static 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 */
314static 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 */
370static 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