Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9b3f26c9 authored by David Howells's avatar David Howells
Browse files

FS-Cache: Make kAFS use FS-Cache



The attached patch makes the kAFS filesystem in fs/afs/ use FS-Cache, and
through it any attached caches.  The kAFS filesystem will use caching
automatically if it's available.

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Acked-by: default avatarSteve Dickson <steved@redhat.com>
Acked-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
Acked-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
Tested-by: default avatarDaire Byrne <Daire.Byrne@framestore.com>
parent 9ae326a6
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -19,3 +19,11 @@ config AFS_DEBUG
	  See <file:Documentation/filesystems/afs.txt> for more information.

	  If unsure, say N.

config AFS_FSCACHE
	bool "Provide AFS client caching support (EXPERIMENTAL)"
	depends on EXPERIMENTAL
	depends on AFS_FS=m && FSCACHE || AFS_FS=y && FSCACHE=y
	help
	  Say Y here if you want AFS data to be cached locally on disk through
	  the generic filesystem cache manager
+3 −0
Original line number Diff line number Diff line
@@ -2,7 +2,10 @@
# Makefile for Red Hat Linux AFS client.
#

afs-cache-$(CONFIG_AFS_FSCACHE) := cache.o

kafs-objs := \
	$(afs-cache-y) \
	callback.o \
	cell.o \
	cmservice.o \
+325 −178
Original line number Diff line number Diff line
/* AFS caching stuff
 *
 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
 * 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
@@ -9,248 +9,395 @@
 * 2 of the License, or (at your option) any later version.
 */

#ifdef AFS_CACHING_SUPPORT
static cachefs_match_val_t afs_cell_cache_match(void *target,
						const void *entry);
static void afs_cell_cache_update(void *source, void *entry);

struct cachefs_index_def afs_cache_cell_index_def = {
	.name			= "cell_ix",
	.data_size		= sizeof(struct afs_cache_cell),
	.keys[0]		= { CACHEFS_INDEX_KEYS_ASCIIZ, 64 },
	.match			= afs_cell_cache_match,
	.update			= afs_cell_cache_update,
#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,
};
#endif

/*
 * match a cell record obtained from the cache
 * set the key for the index entry
 */
#ifdef AFS_CACHING_SUPPORT
static cachefs_match_val_t afs_cell_cache_match(void *target,
						const void *entry)
static uint16_t afs_cell_cache_get_key(const void *cookie_netfs_data,
				       void *buffer, uint16_t bufmax)
{
	const struct afs_cache_cell *ccell = entry;
	struct afs_cell *cell = target;
	const struct afs_cell *cell = cookie_netfs_data;
	uint16_t klen;

	_enter("{%s},{%s}", ccell->name, cell->name);
	_enter("%p,%p,%u", cell, buffer, bufmax);

	if (strncmp(ccell->name, cell->name, sizeof(ccell->name)) == 0) {
		_leave(" = SUCCESS");
		return CACHEFS_MATCH_SUCCESS;
	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;
}

	_leave(" = FAILED");
	return CACHEFS_MATCH_FAILED;
/*
 * 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;
}
#endif

/*****************************************************************************/
/*
 * update a cell record in the cache
 * set the key for the index entry
 */
#ifdef AFS_CACHING_SUPPORT
static void afs_cell_cache_update(void *source, void *entry)
static uint16_t afs_vlocation_cache_get_key(const void *cookie_netfs_data,
					    void *buffer, uint16_t bufmax)
{
	struct afs_cache_cell *ccell = entry;
	struct afs_cell *cell = source;
	const struct afs_vlocation *vlocation = cookie_netfs_data;
	uint16_t klen;

	_enter("%p,%p", source, entry);
	_enter("{%s},%p,%u", vlocation->vldb.name, buffer, bufmax);

	strncpy(ccell->name, cell->name, sizeof(ccell->name));
	klen = strnlen(vlocation->vldb.name, sizeof(vlocation->vldb.name));
	if (klen > bufmax)
		return 0;

	memcpy(ccell->vl_servers,
	       cell->vl_addrs,
	       min(sizeof(ccell->vl_servers), sizeof(cell->vl_addrs)));
	memcpy(buffer, vlocation->vldb.name, klen);

	_leave(" = %u", klen);
	return klen;
}
#endif

#ifdef AFS_CACHING_SUPPORT
static cachefs_match_val_t afs_vlocation_cache_match(void *target,
						     const void *entry);
static void afs_vlocation_cache_update(void *source, void *entry);

struct cachefs_index_def afs_vlocation_cache_index_def = {
	.name		= "vldb",
	.data_size	= sizeof(struct afs_cache_vlocation),
	.keys[0]	= { CACHEFS_INDEX_KEYS_ASCIIZ, 64 },
	.match		= afs_vlocation_cache_match,
	.update		= afs_vlocation_cache_update,
};
#endif

/*
 * match a VLDB record stored in the cache
 * - may also load target from entry
 * provide new auxilliary cache data
 */
#ifdef AFS_CACHING_SUPPORT
static cachefs_match_val_t afs_vlocation_cache_match(void *target,
						     const void *entry)
static uint16_t afs_vlocation_cache_get_aux(const void *cookie_netfs_data,
					    void *buffer, uint16_t bufmax)
{
	const struct afs_cache_vlocation *vldb = entry;
	struct afs_vlocation *vlocation = target;
	const struct afs_vlocation *vlocation = cookie_netfs_data;
	uint16_t dlen;

	_enter("{%s},{%s}", vlocation->vldb.name, vldb->name);
	_enter("{%s},%p,%u", vlocation->vldb.name, buffer, bufmax);

	if (strncmp(vlocation->vldb.name, vldb->name, sizeof(vldb->name)) == 0
	    ) {
		if (!vlocation->valid ||
		    vlocation->vldb.rtime == vldb->rtime
		    ) {
			vlocation->vldb = *vldb;
	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 CACHEFS_MATCH_SUCCESS;
		} else if (memcmp(&vlocation->vldb, vldb, sizeof(*vldb)) != 0) {
			/* delete if VIDs for this name differ */
			if (memcmp(&vlocation->vldb.vid,
				   &vldb->vid,
				   sizeof(vldb->vid)) != 0) {
				_leave(" = DELETE");
				return CACHEFS_MATCH_SUCCESS_DELETE;
		return FSCACHE_CHECKAUX_OKAY;
	}

			_leave(" = UPDATE");
			return CACHEFS_MATCH_SUCCESS_UPDATE;
		} else {
			_leave(" = SUCCESS");
			return CACHEFS_MATCH_SUCCESS;
	/* 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(" = FAILED");
	return CACHEFS_MATCH_FAILED;
	_leave(" = OKAY");
	return FSCACHE_CHECKAUX_OKAY;
}
#endif

/*****************************************************************************/
/*
 * update a VLDB record stored in the cache
 * set the key for the volume index entry
 */
#ifdef AFS_CACHING_SUPPORT
static void afs_vlocation_cache_update(void *source, void *entry)
static uint16_t afs_volume_cache_get_key(const void *cookie_netfs_data,
					void *buffer, uint16_t bufmax)
{
	struct afs_cache_vlocation *vldb = entry;
	struct afs_vlocation *vlocation = source;
	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));

	_enter("");
	_leave(" = %u", klen);
	return klen;

	*vldb = vlocation->vldb;
}
#endif

#ifdef AFS_CACHING_SUPPORT
static cachefs_match_val_t afs_volume_cache_match(void *target,
						  const void *entry);
static void afs_volume_cache_update(void *source, void *entry);

struct cachefs_index_def afs_volume_cache_index_def = {
	.name		= "volume",
	.data_size	= sizeof(struct afs_cache_vhash),
	.keys[0]	= { CACHEFS_INDEX_KEYS_BIN, 1 },
	.keys[1]	= { CACHEFS_INDEX_KEYS_BIN, 1 },
	.match		= afs_volume_cache_match,
	.update		= afs_volume_cache_update,
};
#endif

/*****************************************************************************/
/*
 * match a volume hash record stored in the cache
 * set the key for the index entry
 */
#ifdef AFS_CACHING_SUPPORT
static cachefs_match_val_t afs_volume_cache_match(void *target,
						  const void *entry)
static uint16_t afs_vnode_cache_get_key(const void *cookie_netfs_data,
					void *buffer, uint16_t bufmax)
{
	const struct afs_cache_vhash *vhash = entry;
	struct afs_volume *volume = target;
	const struct afs_vnode *vnode = cookie_netfs_data;
	uint16_t klen;

	_enter("{%u},{%u}", volume->type, vhash->vtype);
	_enter("{%x,%x,%llx},%p,%u",
	       vnode->fid.vnode, vnode->fid.unique, vnode->status.data_version,
	       buffer, bufmax);

	if (volume->type == vhash->vtype) {
		_leave(" = SUCCESS");
		return CACHEFS_MATCH_SUCCESS;
	klen = sizeof(vnode->fid.vnode);
	if (klen > bufmax)
		return 0;

	memcpy(buffer, &vnode->fid.vnode, sizeof(vnode->fid.vnode));

	_leave(" = %u", klen);
	return klen;
}

	_leave(" = FAILED");
	return CACHEFS_MATCH_FAILED;
/*
 * 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;
}
#endif

/*
 * update a volume hash record stored in the cache
 * provide new auxilliary cache data
 */
#ifdef AFS_CACHING_SUPPORT
static void afs_volume_cache_update(void *source, void *entry)
static uint16_t afs_vnode_cache_get_aux(const void *cookie_netfs_data,
					void *buffer, uint16_t bufmax)
{
	struct afs_cache_vhash *vhash = entry;
	struct afs_volume *volume = source;
	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);

	_enter("");
	dlen = sizeof(vnode->fid.unique) + sizeof(vnode->status.data_version);
	if (dlen > bufmax)
		return 0;

	vhash->vtype = volume->type;
	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;
}
#endif

#ifdef AFS_CACHING_SUPPORT
static cachefs_match_val_t afs_vnode_cache_match(void *target,
						 const void *entry);
static void afs_vnode_cache_update(void *source, void *entry);

struct cachefs_index_def afs_vnode_cache_index_def = {
	.name		= "vnode",
	.data_size	= sizeof(struct afs_cache_vnode),
	.keys[0]	= { CACHEFS_INDEX_KEYS_BIN, 4 },
	.match		= afs_vnode_cache_match,
	.update		= afs_vnode_cache_update,
};
#endif

/*
 * match a vnode record stored in the cache
 * check that the auxilliary data indicates that the entry is still valid
 */
#ifdef AFS_CACHING_SUPPORT
static cachefs_match_val_t afs_vnode_cache_match(void *target,
						 const void *entry)
static enum fscache_checkaux afs_vnode_cache_check_aux(void *cookie_netfs_data,
						       const void *buffer,
						       uint16_t buflen)
{
	const struct afs_cache_vnode *cvnode = entry;
	struct afs_vnode *vnode = target;

	_enter("{%x,%x,%Lx},{%x,%x,%Lx}",
	       vnode->fid.vnode,
	       vnode->fid.unique,
	       vnode->status.version,
	       cvnode->vnode_id,
	       cvnode->vnode_unique,
	       cvnode->data_version);

	if (vnode->fid.vnode != cvnode->vnode_id) {
		_leave(" = FAILED");
		return CACHEFS_MATCH_FAILED;
	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 (vnode->fid.unique != cvnode->vnode_unique ||
	    vnode->status.version != cvnode->data_version) {
		_leave(" = DELETE");
		return CACHEFS_MATCH_SUCCESS_DELETE;
	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 CACHEFS_MATCH_SUCCESS;
	return FSCACHE_CHECKAUX_OKAY;
}
#endif

/*
 * update a vnode record stored in the cache
 * 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
 */
#ifdef AFS_CACHING_SUPPORT
static void afs_vnode_cache_update(void *source, void *entry)
static void afs_vnode_cache_now_uncached(void *cookie_netfs_data)
{
	struct afs_cache_vnode *cvnode = entry;
	struct afs_vnode *vnode = source;
	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;

	_enter("");
	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();
	}

	cvnode->vnode_id	= vnode->fid.vnode;
	cvnode->vnode_unique	= vnode->fid.unique;
	cvnode->data_version	= vnode->status.version;
	_leave("");
}
#endif
+2 −13
Original line number Diff line number Diff line
/* AFS local cache management interface
 *
 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
 * 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
@@ -9,15 +9,4 @@
 * 2 of the License, or (at your option) any later version.
 */

#ifndef AFS_CACHE_H
#define AFS_CACHE_H

#undef AFS_CACHING_SUPPORT

#include <linux/mm.h>
#ifdef AFS_CACHING_SUPPORT
#include <linux/cachefs.h>
#endif
#include "types.h"

#endif /* AFS_CACHE_H */
#include <linux/fscache.h>
+7 −9
Original line number Diff line number Diff line
@@ -147,12 +147,11 @@ struct afs_cell *afs_cell_create(const char *name, char *vllist)
	if (ret < 0)
		goto error;

#ifdef AFS_CACHING_SUPPORT
	/* put it up for caching */
	cachefs_acquire_cookie(afs_cache_netfs.primary_index,
			       &afs_vlocation_cache_index_def,
			       cell,
			       &cell->cache);
#ifdef CONFIG_AFS_FSCACHE
	/* put it up for caching (this never returns an error) */
	cell->cache = fscache_acquire_cookie(afs_cache_netfs.primary_index,
					     &afs_cell_cache_index_def,
					     cell);
#endif

	/* add to the cell lists */
@@ -362,10 +361,9 @@ static void afs_cell_destroy(struct afs_cell *cell)
	list_del_init(&cell->proc_link);
	up_write(&afs_proc_cells_sem);

#ifdef AFS_CACHING_SUPPORT
	cachefs_relinquish_cookie(cell->cache, 0);
#ifdef CONFIG_AFS_FSCACHE
	fscache_relinquish_cookie(cell->cache, 0);
#endif

	key_put(cell->anonymous_key);
	kfree(cell);

Loading