404 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			404 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* AFS caching stuff
 | 
						|
 *
 | 
						|
 * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
 | 
						|
 * Written by David Howells (dhowells@redhat.com)
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or
 | 
						|
 * modify it under the terms of the GNU General Public License
 | 
						|
 * as published by the Free Software Foundation; either version
 | 
						|
 * 2 of the License, or (at your option) any later version.
 | 
						|
 */
 | 
						|
 | 
						|
#include <linux/slab.h>
 | 
						|
#include <linux/sched.h>
 | 
						|
#include "internal.h"
 | 
						|
 | 
						|
static uint16_t afs_cell_cache_get_key(const void *cookie_netfs_data,
 | 
						|
				       void *buffer, uint16_t buflen);
 | 
						|
static uint16_t afs_cell_cache_get_aux(const void *cookie_netfs_data,
 | 
						|
				       void *buffer, uint16_t buflen);
 | 
						|
static enum fscache_checkaux afs_cell_cache_check_aux(void *cookie_netfs_data,
 | 
						|
						      const void *buffer,
 | 
						|
						      uint16_t buflen);
 | 
						|
 | 
						|
static uint16_t afs_vlocation_cache_get_key(const void *cookie_netfs_data,
 | 
						|
					    void *buffer, uint16_t buflen);
 | 
						|
static uint16_t afs_vlocation_cache_get_aux(const void *cookie_netfs_data,
 | 
						|
					    void *buffer, uint16_t buflen);
 | 
						|
static enum fscache_checkaux afs_vlocation_cache_check_aux(
 | 
						|
	void *cookie_netfs_data, const void *buffer, uint16_t buflen);
 | 
						|
 | 
						|
static uint16_t afs_volume_cache_get_key(const void *cookie_netfs_data,
 | 
						|
					 void *buffer, uint16_t buflen);
 | 
						|
 | 
						|
static uint16_t afs_vnode_cache_get_key(const void *cookie_netfs_data,
 | 
						|
					void *buffer, uint16_t buflen);
 | 
						|
static void afs_vnode_cache_get_attr(const void *cookie_netfs_data,
 | 
						|
				     uint64_t *size);
 | 
						|
static uint16_t afs_vnode_cache_get_aux(const void *cookie_netfs_data,
 | 
						|
					void *buffer, uint16_t buflen);
 | 
						|
static enum fscache_checkaux afs_vnode_cache_check_aux(void *cookie_netfs_data,
 | 
						|
						       const void *buffer,
 | 
						|
						       uint16_t buflen);
 | 
						|
static void afs_vnode_cache_now_uncached(void *cookie_netfs_data);
 | 
						|
 | 
						|
struct fscache_netfs afs_cache_netfs = {
 | 
						|
	.name			= "afs",
 | 
						|
	.version		= 0,
 | 
						|
};
 | 
						|
 | 
						|
struct fscache_cookie_def afs_cell_cache_index_def = {
 | 
						|
	.name		= "AFS.cell",
 | 
						|
	.type		= FSCACHE_COOKIE_TYPE_INDEX,
 | 
						|
	.get_key	= afs_cell_cache_get_key,
 | 
						|
	.get_aux	= afs_cell_cache_get_aux,
 | 
						|
	.check_aux	= afs_cell_cache_check_aux,
 | 
						|
};
 | 
						|
 | 
						|
struct fscache_cookie_def afs_vlocation_cache_index_def = {
 | 
						|
	.name			= "AFS.vldb",
 | 
						|
	.type			= FSCACHE_COOKIE_TYPE_INDEX,
 | 
						|
	.get_key		= afs_vlocation_cache_get_key,
 | 
						|
	.get_aux		= afs_vlocation_cache_get_aux,
 | 
						|
	.check_aux		= afs_vlocation_cache_check_aux,
 | 
						|
};
 | 
						|
 | 
						|
struct fscache_cookie_def afs_volume_cache_index_def = {
 | 
						|
	.name		= "AFS.volume",
 | 
						|
	.type		= FSCACHE_COOKIE_TYPE_INDEX,
 | 
						|
	.get_key	= afs_volume_cache_get_key,
 | 
						|
};
 | 
						|
 | 
						|
struct fscache_cookie_def afs_vnode_cache_index_def = {
 | 
						|
	.name			= "AFS.vnode",
 | 
						|
	.type			= FSCACHE_COOKIE_TYPE_DATAFILE,
 | 
						|
	.get_key		= afs_vnode_cache_get_key,
 | 
						|
	.get_attr		= afs_vnode_cache_get_attr,
 | 
						|
	.get_aux		= afs_vnode_cache_get_aux,
 | 
						|
	.check_aux		= afs_vnode_cache_check_aux,
 | 
						|
	.now_uncached		= afs_vnode_cache_now_uncached,
 | 
						|
};
 | 
						|
 | 
						|
/*
 | 
						|
 * set the key for the index entry
 | 
						|
 */
 | 
						|
static uint16_t afs_cell_cache_get_key(const void *cookie_netfs_data,
 | 
						|
				       void *buffer, uint16_t bufmax)
 | 
						|
{
 | 
						|
	const struct afs_cell *cell = cookie_netfs_data;
 | 
						|
	uint16_t klen;
 | 
						|
 | 
						|
	_enter("%p,%p,%u", cell, buffer, bufmax);
 | 
						|
 | 
						|
	klen = strlen(cell->name);
 | 
						|
	if (klen > bufmax)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	memcpy(buffer, cell->name, klen);
 | 
						|
	return klen;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * provide new auxilliary cache data
 | 
						|
 */
 | 
						|
static uint16_t afs_cell_cache_get_aux(const void *cookie_netfs_data,
 | 
						|
				       void *buffer, uint16_t bufmax)
 | 
						|
{
 | 
						|
	const struct afs_cell *cell = cookie_netfs_data;
 | 
						|
	uint16_t dlen;
 | 
						|
 | 
						|
	_enter("%p,%p,%u", cell, buffer, bufmax);
 | 
						|
 | 
						|
	dlen = cell->vl_naddrs * sizeof(cell->vl_addrs[0]);
 | 
						|
	dlen = min(dlen, bufmax);
 | 
						|
	dlen &= ~(sizeof(cell->vl_addrs[0]) - 1);
 | 
						|
 | 
						|
	memcpy(buffer, cell->vl_addrs, dlen);
 | 
						|
	return dlen;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * check that the auxilliary data indicates that the entry is still valid
 | 
						|
 */
 | 
						|
static enum fscache_checkaux afs_cell_cache_check_aux(void *cookie_netfs_data,
 | 
						|
						      const void *buffer,
 | 
						|
						      uint16_t buflen)
 | 
						|
{
 | 
						|
	_leave(" = OKAY");
 | 
						|
	return FSCACHE_CHECKAUX_OKAY;
 | 
						|
}
 | 
						|
 | 
						|
/*****************************************************************************/
 | 
						|
/*
 | 
						|
 * set the key for the index entry
 | 
						|
 */
 | 
						|
static uint16_t afs_vlocation_cache_get_key(const void *cookie_netfs_data,
 | 
						|
					    void *buffer, uint16_t bufmax)
 | 
						|
{
 | 
						|
	const struct afs_vlocation *vlocation = cookie_netfs_data;
 | 
						|
	uint16_t klen;
 | 
						|
 | 
						|
	_enter("{%s},%p,%u", vlocation->vldb.name, buffer, bufmax);
 | 
						|
 | 
						|
	klen = strnlen(vlocation->vldb.name, sizeof(vlocation->vldb.name));
 | 
						|
	if (klen > bufmax)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	memcpy(buffer, vlocation->vldb.name, klen);
 | 
						|
 | 
						|
	_leave(" = %u", klen);
 | 
						|
	return klen;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * provide new auxilliary cache data
 | 
						|
 */
 | 
						|
static uint16_t afs_vlocation_cache_get_aux(const void *cookie_netfs_data,
 | 
						|
					    void *buffer, uint16_t bufmax)
 | 
						|
{
 | 
						|
	const struct afs_vlocation *vlocation = cookie_netfs_data;
 | 
						|
	uint16_t dlen;
 | 
						|
 | 
						|
	_enter("{%s},%p,%u", vlocation->vldb.name, buffer, bufmax);
 | 
						|
 | 
						|
	dlen = sizeof(struct afs_cache_vlocation);
 | 
						|
	dlen -= offsetof(struct afs_cache_vlocation, nservers);
 | 
						|
	if (dlen > bufmax)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	memcpy(buffer, (uint8_t *)&vlocation->vldb.nservers, dlen);
 | 
						|
 | 
						|
	_leave(" = %u", dlen);
 | 
						|
	return dlen;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * check that the auxilliary data indicates that the entry is still valid
 | 
						|
 */
 | 
						|
static
 | 
						|
enum fscache_checkaux afs_vlocation_cache_check_aux(void *cookie_netfs_data,
 | 
						|
						    const void *buffer,
 | 
						|
						    uint16_t buflen)
 | 
						|
{
 | 
						|
	const struct afs_cache_vlocation *cvldb;
 | 
						|
	struct afs_vlocation *vlocation = cookie_netfs_data;
 | 
						|
	uint16_t dlen;
 | 
						|
 | 
						|
	_enter("{%s},%p,%u", vlocation->vldb.name, buffer, buflen);
 | 
						|
 | 
						|
	/* check the size of the data is what we're expecting */
 | 
						|
	dlen = sizeof(struct afs_cache_vlocation);
 | 
						|
	dlen -= offsetof(struct afs_cache_vlocation, nservers);
 | 
						|
	if (dlen != buflen)
 | 
						|
		return FSCACHE_CHECKAUX_OBSOLETE;
 | 
						|
 | 
						|
	cvldb = container_of(buffer, struct afs_cache_vlocation, nservers);
 | 
						|
 | 
						|
	/* if what's on disk is more valid than what's in memory, then use the
 | 
						|
	 * VL record from the cache */
 | 
						|
	if (!vlocation->valid || vlocation->vldb.rtime == cvldb->rtime) {
 | 
						|
		memcpy((uint8_t *)&vlocation->vldb.nservers, buffer, dlen);
 | 
						|
		vlocation->valid = 1;
 | 
						|
		_leave(" = SUCCESS [c->m]");
 | 
						|
		return FSCACHE_CHECKAUX_OKAY;
 | 
						|
	}
 | 
						|
 | 
						|
	/* need to update the cache if the cached info differs */
 | 
						|
	if (memcmp(&vlocation->vldb, buffer, dlen) != 0) {
 | 
						|
		/* delete if the volume IDs for this name differ */
 | 
						|
		if (memcmp(&vlocation->vldb.vid, &cvldb->vid,
 | 
						|
			   sizeof(cvldb->vid)) != 0
 | 
						|
		    ) {
 | 
						|
			_leave(" = OBSOLETE");
 | 
						|
			return FSCACHE_CHECKAUX_OBSOLETE;
 | 
						|
		}
 | 
						|
 | 
						|
		_leave(" = UPDATE");
 | 
						|
		return FSCACHE_CHECKAUX_NEEDS_UPDATE;
 | 
						|
	}
 | 
						|
 | 
						|
	_leave(" = OKAY");
 | 
						|
	return FSCACHE_CHECKAUX_OKAY;
 | 
						|
}
 | 
						|
 | 
						|
/*****************************************************************************/
 | 
						|
/*
 | 
						|
 * set the key for the volume index entry
 | 
						|
 */
 | 
						|
static uint16_t afs_volume_cache_get_key(const void *cookie_netfs_data,
 | 
						|
					void *buffer, uint16_t bufmax)
 | 
						|
{
 | 
						|
	const struct afs_volume *volume = cookie_netfs_data;
 | 
						|
	uint16_t klen;
 | 
						|
 | 
						|
	_enter("{%u},%p,%u", volume->type, buffer, bufmax);
 | 
						|
 | 
						|
	klen = sizeof(volume->type);
 | 
						|
	if (klen > bufmax)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	memcpy(buffer, &volume->type, sizeof(volume->type));
 | 
						|
 | 
						|
	_leave(" = %u", klen);
 | 
						|
	return klen;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/*****************************************************************************/
 | 
						|
/*
 | 
						|
 * set the key for the index entry
 | 
						|
 */
 | 
						|
static uint16_t afs_vnode_cache_get_key(const void *cookie_netfs_data,
 | 
						|
					void *buffer, uint16_t bufmax)
 | 
						|
{
 | 
						|
	const struct afs_vnode *vnode = cookie_netfs_data;
 | 
						|
	uint16_t klen;
 | 
						|
 | 
						|
	_enter("{%x,%x,%llx},%p,%u",
 | 
						|
	       vnode->fid.vnode, vnode->fid.unique, vnode->status.data_version,
 | 
						|
	       buffer, bufmax);
 | 
						|
 | 
						|
	klen = sizeof(vnode->fid.vnode);
 | 
						|
	if (klen > bufmax)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	memcpy(buffer, &vnode->fid.vnode, sizeof(vnode->fid.vnode));
 | 
						|
 | 
						|
	_leave(" = %u", klen);
 | 
						|
	return klen;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * provide updated file attributes
 | 
						|
 */
 | 
						|
static void afs_vnode_cache_get_attr(const void *cookie_netfs_data,
 | 
						|
				     uint64_t *size)
 | 
						|
{
 | 
						|
	const struct afs_vnode *vnode = cookie_netfs_data;
 | 
						|
 | 
						|
	_enter("{%x,%x,%llx},",
 | 
						|
	       vnode->fid.vnode, vnode->fid.unique,
 | 
						|
	       vnode->status.data_version);
 | 
						|
 | 
						|
	*size = vnode->status.size;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * provide new auxilliary cache data
 | 
						|
 */
 | 
						|
static uint16_t afs_vnode_cache_get_aux(const void *cookie_netfs_data,
 | 
						|
					void *buffer, uint16_t bufmax)
 | 
						|
{
 | 
						|
	const struct afs_vnode *vnode = cookie_netfs_data;
 | 
						|
	uint16_t dlen;
 | 
						|
 | 
						|
	_enter("{%x,%x,%Lx},%p,%u",
 | 
						|
	       vnode->fid.vnode, vnode->fid.unique, vnode->status.data_version,
 | 
						|
	       buffer, bufmax);
 | 
						|
 | 
						|
	dlen = sizeof(vnode->fid.unique) + sizeof(vnode->status.data_version);
 | 
						|
	if (dlen > bufmax)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	memcpy(buffer, &vnode->fid.unique, sizeof(vnode->fid.unique));
 | 
						|
	buffer += sizeof(vnode->fid.unique);
 | 
						|
	memcpy(buffer, &vnode->status.data_version,
 | 
						|
	       sizeof(vnode->status.data_version));
 | 
						|
 | 
						|
	_leave(" = %u", dlen);
 | 
						|
	return dlen;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * check that the auxilliary data indicates that the entry is still valid
 | 
						|
 */
 | 
						|
static enum fscache_checkaux afs_vnode_cache_check_aux(void *cookie_netfs_data,
 | 
						|
						       const void *buffer,
 | 
						|
						       uint16_t buflen)
 | 
						|
{
 | 
						|
	struct afs_vnode *vnode = cookie_netfs_data;
 | 
						|
	uint16_t dlen;
 | 
						|
 | 
						|
	_enter("{%x,%x,%llx},%p,%u",
 | 
						|
	       vnode->fid.vnode, vnode->fid.unique, vnode->status.data_version,
 | 
						|
	       buffer, buflen);
 | 
						|
 | 
						|
	/* check the size of the data is what we're expecting */
 | 
						|
	dlen = sizeof(vnode->fid.unique) + sizeof(vnode->status.data_version);
 | 
						|
	if (dlen != buflen) {
 | 
						|
		_leave(" = OBSOLETE [len %hx != %hx]", dlen, buflen);
 | 
						|
		return FSCACHE_CHECKAUX_OBSOLETE;
 | 
						|
	}
 | 
						|
 | 
						|
	if (memcmp(buffer,
 | 
						|
		   &vnode->fid.unique,
 | 
						|
		   sizeof(vnode->fid.unique)
 | 
						|
		   ) != 0) {
 | 
						|
		unsigned unique;
 | 
						|
 | 
						|
		memcpy(&unique, buffer, sizeof(unique));
 | 
						|
 | 
						|
		_leave(" = OBSOLETE [uniq %x != %x]",
 | 
						|
		       unique, vnode->fid.unique);
 | 
						|
		return FSCACHE_CHECKAUX_OBSOLETE;
 | 
						|
	}
 | 
						|
 | 
						|
	if (memcmp(buffer + sizeof(vnode->fid.unique),
 | 
						|
		   &vnode->status.data_version,
 | 
						|
		   sizeof(vnode->status.data_version)
 | 
						|
		   ) != 0) {
 | 
						|
		afs_dataversion_t version;
 | 
						|
 | 
						|
		memcpy(&version, buffer + sizeof(vnode->fid.unique),
 | 
						|
		       sizeof(version));
 | 
						|
 | 
						|
		_leave(" = OBSOLETE [vers %llx != %llx]",
 | 
						|
		       version, vnode->status.data_version);
 | 
						|
		return FSCACHE_CHECKAUX_OBSOLETE;
 | 
						|
	}
 | 
						|
 | 
						|
	_leave(" = SUCCESS");
 | 
						|
	return FSCACHE_CHECKAUX_OKAY;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * indication the cookie is no longer uncached
 | 
						|
 * - this function is called when the backing store currently caching a cookie
 | 
						|
 *   is removed
 | 
						|
 * - the netfs should use this to clean up any markers indicating cached pages
 | 
						|
 * - this is mandatory for any object that may have data
 | 
						|
 */
 | 
						|
static void afs_vnode_cache_now_uncached(void *cookie_netfs_data)
 | 
						|
{
 | 
						|
	struct afs_vnode *vnode = cookie_netfs_data;
 | 
						|
	struct pagevec pvec;
 | 
						|
	pgoff_t first;
 | 
						|
	int loop, nr_pages;
 | 
						|
 | 
						|
	_enter("{%x,%x,%Lx}",
 | 
						|
	       vnode->fid.vnode, vnode->fid.unique, vnode->status.data_version);
 | 
						|
 | 
						|
	pagevec_init(&pvec, 0);
 | 
						|
	first = 0;
 | 
						|
 | 
						|
	for (;;) {
 | 
						|
		/* grab a bunch of pages to clean */
 | 
						|
		nr_pages = pagevec_lookup(&pvec, vnode->vfs_inode.i_mapping,
 | 
						|
					  first,
 | 
						|
					  PAGEVEC_SIZE - pagevec_count(&pvec));
 | 
						|
		if (!nr_pages)
 | 
						|
			break;
 | 
						|
 | 
						|
		for (loop = 0; loop < nr_pages; loop++)
 | 
						|
			ClearPageFsCache(pvec.pages[loop]);
 | 
						|
 | 
						|
		first = pvec.pages[nr_pages - 1]->index + 1;
 | 
						|
 | 
						|
		pvec.nr = nr_pages;
 | 
						|
		pagevec_release(&pvec);
 | 
						|
		cond_resched();
 | 
						|
	}
 | 
						|
 | 
						|
	_leave("");
 | 
						|
}
 |