Commit 3cc50ac0 authored by Linus Torvalds's avatar Linus Torvalds

Merge git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-2.6-fscache

* git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-2.6-fscache: (41 commits)
  NFS: Add mount options to enable local caching on NFS
  NFS: Display local caching state
  NFS: Store pages from an NFS inode into a local cache
  NFS: Read pages from FS-Cache into an NFS inode
  NFS: nfs_readpage_async() needs to be accessible as a fallback for local caching
  NFS: Add read context retention for FS-Cache to call back with
  NFS: FS-Cache page management
  NFS: Add some new I/O counters for FS-Cache doing things for NFS
  NFS: Invalidate FsCache page flags when cache removed
  NFS: Use local disk inode cache
  NFS: Define and create inode-level cache objects
  NFS: Define and create superblock-level objects
  NFS: Define and create server-level objects
  NFS: Register NFS for caching and retrieve the top-level index
  NFS: Permit local filesystem caching to be enabled for NFS
  NFS: Add FS-Cache option bit and debug bit
  NFS: Add comment banners to some NFS functions
  FS-Cache: Make kAFS use FS-Cache
  CacheFiles: A cache that backs onto a mounted filesystem
  CacheFiles: Export things for CacheFiles
  ...
parents d9b9be02 b797cac7
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
================================
ASYNCHRONOUS OPERATIONS HANDLING
================================
By: David Howells <dhowells@redhat.com>
Contents:
(*) Overview.
(*) Operation record initialisation.
(*) Parameters.
(*) Procedure.
(*) Asynchronous callback.
========
OVERVIEW
========
FS-Cache has an asynchronous operations handling facility that it uses for its
data storage and retrieval routines. Its operations are represented by
fscache_operation structs, though these are usually embedded into some other
structure.
This facility is available to and expected to be be used by the cache backends,
and FS-Cache will create operations and pass them off to the appropriate cache
backend for completion.
To make use of this facility, <linux/fscache-cache.h> should be #included.
===============================
OPERATION RECORD INITIALISATION
===============================
An operation is recorded in an fscache_operation struct:
struct fscache_operation {
union {
struct work_struct fast_work;
struct slow_work slow_work;
};
unsigned long flags;
fscache_operation_processor_t processor;
...
};
Someone wanting to issue an operation should allocate something with this
struct embedded in it. They should initialise it by calling:
void fscache_operation_init(struct fscache_operation *op,
fscache_operation_release_t release);
with the operation to be initialised and the release function to use.
The op->flags parameter should be set to indicate the CPU time provision and
the exclusivity (see the Parameters section).
The op->fast_work, op->slow_work and op->processor flags should be set as
appropriate for the CPU time provision (see the Parameters section).
FSCACHE_OP_WAITING may be set in op->flags prior to each submission of the
operation and waited for afterwards.
==========
PARAMETERS
==========
There are a number of parameters that can be set in the operation record's flag
parameter. There are three options for the provision of CPU time in these
operations:
(1) The operation may be done synchronously (FSCACHE_OP_MYTHREAD). A thread
may decide it wants to handle an operation itself without deferring it to
another thread.
This is, for example, used in read operations for calling readpages() on
the backing filesystem in CacheFiles. Although readpages() does an
asynchronous data fetch, the determination of whether pages exist is done
synchronously - and the netfs does not proceed until this has been
determined.
If this option is to be used, FSCACHE_OP_WAITING must be set in op->flags
before submitting the operation, and the operating thread must wait for it
to be cleared before proceeding:
wait_on_bit(&op->flags, FSCACHE_OP_WAITING,
fscache_wait_bit, TASK_UNINTERRUPTIBLE);
(2) The operation may be fast asynchronous (FSCACHE_OP_FAST), in which case it
will be given to keventd to process. Such an operation is not permitted
to sleep on I/O.
This is, for example, used by CacheFiles to copy data from a backing fs
page to a netfs page after the backing fs has read the page in.
If this option is used, op->fast_work and op->processor must be
initialised before submitting the operation:
INIT_WORK(&op->fast_work, do_some_work);
(3) The operation may be slow asynchronous (FSCACHE_OP_SLOW), in which case it
will be given to the slow work facility to process. Such an operation is
permitted to sleep on I/O.
This is, for example, used by FS-Cache to handle background writes of
pages that have just been fetched from a remote server.
If this option is used, op->slow_work and op->processor must be
initialised before submitting the operation:
fscache_operation_init_slow(op, processor)
Furthermore, operations may be one of two types:
(1) Exclusive (FSCACHE_OP_EXCLUSIVE). Operations of this type may not run in
conjunction with any other operation on the object being operated upon.
An example of this is the attribute change operation, in which the file
being written to may need truncation.
(2) Shareable. Operations of this type may be running simultaneously. It's
up to the operation implementation to prevent interference between other
operations running at the same time.
=========
PROCEDURE
=========
Operations are used through the following procedure:
(1) The submitting thread must allocate the operation and initialise it
itself. Normally this would be part of a more specific structure with the
generic op embedded within.
(2) The submitting thread must then submit the operation for processing using
one of the following two functions:
int fscache_submit_op(struct fscache_object *object,
struct fscache_operation *op);
int fscache_submit_exclusive_op(struct fscache_object *object,
struct fscache_operation *op);
The first function should be used to submit non-exclusive ops and the
second to submit exclusive ones. The caller must still set the
FSCACHE_OP_EXCLUSIVE flag.
If successful, both functions will assign the operation to the specified
object and return 0. -ENOBUFS will be returned if the object specified is
permanently unavailable.
The operation manager will defer operations on an object that is still
undergoing lookup or creation. The operation will also be deferred if an
operation of conflicting exclusivity is in progress on the object.
If the operation is asynchronous, the manager will retain a reference to
it, so the caller should put their reference to it by passing it to:
void fscache_put_operation(struct fscache_operation *op);
(3) If the submitting thread wants to do the work itself, and has marked the
operation with FSCACHE_OP_MYTHREAD, then it should monitor
FSCACHE_OP_WAITING as described above and check the state of the object if
necessary (the object might have died whilst the thread was waiting).
When it has finished doing its processing, it should call
fscache_put_operation() on it.
(4) The operation holds an effective lock upon the object, preventing other
exclusive ops conflicting until it is released. The operation can be
enqueued for further immediate asynchronous processing by adjusting the
CPU time provisioning option if necessary, eg:
op->flags &= ~FSCACHE_OP_TYPE;
op->flags |= ~FSCACHE_OP_FAST;
and calling:
void fscache_enqueue_operation(struct fscache_operation *op)
This can be used to allow other things to have use of the worker thread
pools.
=====================
ASYNCHRONOUS CALLBACK
=====================
When used in asynchronous mode, the worker thread pool will invoke the
processor method with a pointer to the operation. This should then get at the
container struct by using container_of():
static void fscache_write_op(struct fscache_operation *_op)
{
struct fscache_storage *op =
container_of(_op, struct fscache_storage, op);
...
}
The caller holds a reference on the operation, and will invoke
fscache_put_operation() when the processor function returns. The processor
function is at liberty to call fscache_enqueue_operation() or to take extra
references.
====================================
SLOW WORK ITEM EXECUTION THREAD POOL
====================================
By: David Howells <dhowells@redhat.com>
The slow work item execution thread pool is a pool of threads for performing
things that take a relatively long time, such as making mkdir calls.
Typically, when processing something, these items will spend a lot of time
blocking a thread on I/O, thus making that thread unavailable for doing other
work.
The standard workqueue model is unsuitable for this class of work item as that
limits the owner to a single thread or a single thread per CPU. For some
tasks, however, more threads - or fewer - are required.
There is just one pool per system. It contains no threads unless something
wants to use it - and that something must register its interest first. When
the pool is active, the number of threads it contains is dynamic, varying
between a maximum and minimum setting, depending on the load.
====================
CLASSES OF WORK ITEM
====================
This pool support two classes of work items:
(*) Slow work items.
(*) Very slow work items.
The former are expected to finish much quicker than the latter.
An operation of the very slow class may do a batch combination of several
lookups, mkdirs, and a create for instance.
An operation of the ordinarily slow class may, for example, write stuff or
expand files, provided the time taken to do so isn't too long.
Operations of both types may sleep during execution, thus tying up the thread
loaned to it.
THREAD-TO-CLASS ALLOCATION
--------------------------
Not all the threads in the pool are available to work on very slow work items.
The number will be between one and one fewer than the number of active threads.
This is configurable (see the "Pool Configuration" section).
All the threads are available to work on ordinarily slow work items, but a
percentage of the threads will prefer to work on very slow work items.
The configuration ensures that at least one thread will be available to work on
very slow work items, and at least one thread will be available that won't work
on very slow work items at all.
=====================
USING SLOW WORK ITEMS
=====================
Firstly, a module or subsystem wanting to make use of slow work items must
register its interest:
int ret = slow_work_register_user();
This will return 0 if successful, or a -ve error upon failure.
Slow work items may then be set up by:
(1) Declaring a slow_work struct type variable:
#include <linux/slow-work.h>
struct slow_work myitem;
(2) Declaring the operations to be used for this item:
struct slow_work_ops myitem_ops = {
.get_ref = myitem_get_ref,
.put_ref = myitem_put_ref,
.execute = myitem_execute,
};
[*] For a description of the ops, see section "Item Operations".
(3) Initialising the item:
slow_work_init(&myitem, &myitem_ops);
or:
vslow_work_init(&myitem, &myitem_ops);
depending on its class.
A suitably set up work item can then be enqueued for processing:
int ret = slow_work_enqueue(&myitem);
This will return a -ve error if the thread pool is unable to gain a reference
on the item, 0 otherwise.
The items are reference counted, so there ought to be no need for a flush
operation. When all a module's slow work items have been processed, and the
module has no further interest in the facility, it should unregister its
interest:
slow_work_unregister_user();
===============
ITEM OPERATIONS
===============
Each work item requires a table of operations of type struct slow_work_ops.
All members are required:
(*) Get a reference on an item:
int (*get_ref)(struct slow_work *work);
This allows the thread pool to attempt to pin an item by getting a
reference on it. This function should return 0 if the reference was
granted, or a -ve error otherwise. If an error is returned,
slow_work_enqueue() will fail.
The reference is held whilst the item is queued and whilst it is being
executed. The item may then be requeued with the same reference held, or
the reference will be released.
(*) Release a reference on an item:
void (*put_ref)(struct slow_work *work);
This allows the thread pool to unpin an item by releasing the reference on
it. The thread pool will not touch the item again once this has been
called.
(*) Execute an item:
void (*execute)(struct slow_work *work);
This should perform the work required of the item. It may sleep, it may
perform disk I/O and it may wait for locks.
==================
POOL CONFIGURATION
==================
The slow-work thread pool has a number of configurables:
(*) /proc/sys/kernel/slow-work/min-threads
The minimum number of threads that should be in the pool whilst it is in
use. This may be anywhere between 2 and max-threads.
(*) /proc/sys/kernel/slow-work/max-threads
The maximum number of threads that should in the pool. This may be
anywhere between min-threads and 255 or NR_CPUS * 2, whichever is greater.
(*) /proc/sys/kernel/slow-work/vslow-percentage
The percentage of active threads in the pool that may be used to execute
very slow work items. This may be between 1 and 99. The resultant number
is bounded to between 1 and one fewer than the number of active threads.
This ensures there is always at least one thread that can process very
slow work items, and always at least one thread that won't.
...@@ -66,6 +66,13 @@ config GENERIC_ACL ...@@ -66,6 +66,13 @@ config GENERIC_ACL
bool bool
select FS_POSIX_ACL select FS_POSIX_ACL
menu "Caches"
source "fs/fscache/Kconfig"
source "fs/cachefiles/Kconfig"
endmenu
if BLOCK if BLOCK
menu "CD-ROM/DVD Filesystems" menu "CD-ROM/DVD Filesystems"
......
...@@ -63,6 +63,7 @@ obj-$(CONFIG_PROFILING) += dcookies.o ...@@ -63,6 +63,7 @@ obj-$(CONFIG_PROFILING) += dcookies.o
obj-$(CONFIG_DLM) += dlm/ obj-$(CONFIG_DLM) += dlm/
# Do not add any filesystems before this line # Do not add any filesystems before this line
obj-$(CONFIG_FSCACHE) += fscache/
obj-$(CONFIG_REISERFS_FS) += reiserfs/ obj-$(CONFIG_REISERFS_FS) += reiserfs/
obj-$(CONFIG_EXT3_FS) += ext3/ # Before ext2 so root fs can be ext3 obj-$(CONFIG_EXT3_FS) += ext3/ # Before ext2 so root fs can be ext3
obj-$(CONFIG_EXT2_FS) += ext2/ obj-$(CONFIG_EXT2_FS) += ext2/
...@@ -116,6 +117,7 @@ obj-$(CONFIG_AFS_FS) += afs/ ...@@ -116,6 +117,7 @@ obj-$(CONFIG_AFS_FS) += afs/
obj-$(CONFIG_BEFS_FS) += befs/ obj-$(CONFIG_BEFS_FS) += befs/
obj-$(CONFIG_HOSTFS) += hostfs/ obj-$(CONFIG_HOSTFS) += hostfs/
obj-$(CONFIG_HPPFS) += hppfs/ obj-$(CONFIG_HPPFS) += hppfs/
obj-$(CONFIG_CACHEFILES) += cachefiles/
obj-$(CONFIG_DEBUG_FS) += debugfs/ obj-$(CONFIG_DEBUG_FS) += debugfs/
obj-$(CONFIG_OCFS2_FS) += ocfs2/ obj-$(CONFIG_OCFS2_FS) += ocfs2/
obj-$(CONFIG_BTRFS_FS) += btrfs/ obj-$(CONFIG_BTRFS_FS) += btrfs/
......
...@@ -19,3 +19,11 @@ config AFS_DEBUG ...@@ -19,3 +19,11 @@ config AFS_DEBUG
See <file:Documentation/filesystems/afs.txt> for more information. See <file:Documentation/filesystems/afs.txt> for more information.
If unsure, say N. 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
...@@ -2,7 +2,10 @@ ...@@ -2,7 +2,10 @@
# Makefile for Red Hat Linux AFS client. # Makefile for Red Hat Linux AFS client.
# #
afs-cache-$(CONFIG_AFS_FSCACHE) := cache.o
kafs-objs := \ kafs-objs := \
$(afs-cache-y) \
callback.o \ callback.o \
cell.o \ cell.o \
cmservice.o \ cmservice.o \
......
This diff is collapsed.
/* AFS local cache management interface /* 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) * Written by David Howells (dhowells@redhat.com)
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
...@@ -9,15 +9,4 @@ ...@@ -9,15 +9,4 @@
* 2 of the License, or (at your option) any later version. * 2 of the License, or (at your option) any later version.
*/ */
#ifndef AFS_CACHE_H #include <linux/fscache.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 */
...@@ -147,12 +147,11 @@ struct afs_cell *afs_cell_create(const char *name, char *vllist) ...@@ -147,12 +147,11 @@ struct afs_cell *afs_cell_create(const char *name, char *vllist)
if (ret < 0) if (ret < 0)
goto error; goto error;
#ifdef AFS_CACHING_SUPPORT #ifdef CONFIG_AFS_FSCACHE
/* put it up for caching */ /* put it up for caching (this never returns an error) */
cachefs_acquire_cookie(afs_cache_netfs.primary_index, cell->cache = fscache_acquire_cookie(afs_cache_netfs.primary_index,
&afs_vlocation_cache_index_def, &afs_cell_cache_index_def,
cell, cell);
&cell->cache);
#endif #endif
/* add to the cell lists */ /* add to the cell lists */
...@@ -362,10 +361,9 @@ static void afs_cell_destroy(struct afs_cell *cell) ...@@ -362,10 +361,9 @@ static void afs_cell_destroy(struct afs_cell *cell)
list_del_init(&cell->proc_link); list_del_init(&cell->proc_link);
up_write(&afs_proc_cells_sem); up_write(&afs_proc_cells_sem);
#ifdef AFS_CACHING_SUPPORT #ifdef CONFIG_AFS_FSCACHE
cachefs_relinquish_cookie(cell->cache, 0); fscache_relinquish_cookie(cell->cache, 0);
#endif #endif
key_put(cell->anonymous_key); key_put(cell->anonymous_key);
kfree(cell); kfree(cell);
......
...@@ -23,6 +23,9 @@ static void afs_invalidatepage(struct page *page, unsigned long offset); ...@@ -23,6 +23,9 @@ static void afs_invalidatepage(struct page *page, unsigned long offset);
static int afs_releasepage(struct page *page, gfp_t gfp_flags); static int afs_releasepage(struct page *page, gfp_t gfp_flags);
static int afs_launder_page(struct page *page); static int afs_launder_page(struct page *page);
static int afs_readpages(struct file *filp, struct address_space *mapping,
struct list_head *pages, unsigned nr_pages);
const struct file_operations afs_file_operations = { const struct file_operations afs_file_operations = {
.open = afs_open, .open = afs_open,
.release = afs_release, .release = afs_release,
...@@ -46,6 +49,7 @@ const struct inode_operations afs_file_inode_operations = { ...@@ -46,6 +49,7 @@ const struct inode_operations afs_file_inode_operations = {
const struct address_space_operations afs_fs_aops = { const struct address_space_operations afs_fs_aops = {
.readpage = afs_readpage, .readpage = afs_readpage,
.readpages = afs_readpages,
.set_page_dirty = afs_set_page_dirty, .set_page_dirty = afs_set_page_dirty,
.launder_page = afs_launder_page, .launder_page = afs_launder_page,
.releasepage = afs_releasepage, .releasepage = afs_releasepage,
...@@ -101,37 +105,18 @@ int afs_release(struct inode *inode, struct file *file) ...@@ -101,37 +105,18 @@ int afs_release(struct inode *inode, struct file *file)
/* /*
* deal with notification that a page was read from the cache * deal with notification that a page was read from the cache
*/ */
#ifdef AFS_CACHING_SUPPORT static void afs_file_readpage_read_complete(struct page *page,
static void afs_readpage_read_complete(void *cookie_data,
struct page *page,
void *data, void *data,
int error) int error)
{ {
_enter("%p,%p,%p,%d", cookie_data, page, data, error); _enter("%p,%p,%d", page, data, error);
if (error) /* if the read completes with an error, we just unlock the page and let
SetPageError(page); * the VM reissue the readpage */
else if (!error)
SetPageUptodate(page); SetPageUptodate(page);
unlock_page(page); unlock_page(page);
}
#endif
/*
* deal with notification that a page was written to the cache
*/
#ifdef AFS_CACHING_SUPPORT
static void afs_readpage_write_complete(void *cookie_data,
struct page *page,
void *data,
int error)
{
_enter("%p,%p,%p,%d", cookie_data, page, data, error);
unlock_page(page);
} }
#endif
/* /*
* AFS read page from file, directory or symlink * AFS read page from file, directory or symlink
...@@ -161,9 +146,9 @@ static int afs_readpage(struct file *file, struct page *page) ...@@ -161,9 +146,9 @@ static int afs_readpage(struct file *file, struct page *page)
if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
goto error; goto error;
#ifdef AFS_CACHING_SUPPORT
/* is it cached? */ /* is it cached? */
ret = cachefs_read_or_alloc_page(vnode->cache, #ifdef CONFIG_AFS_FSCACHE
ret = fscache_read_or_alloc_page(vnode->cache,
page, page,
afs_file_readpage_read_complete, afs_file_readpage_read_complete,
NULL, NULL,
...@@ -171,20 +156,21 @@ static int afs_readpage(struct file *file, struct page *page) ...@@ -171,20 +156,21 @@ static int afs_readpage(struct file *file, struct page *page)
#else #else
ret = -ENOBUFS; ret = -ENOBUFS;
#endif #endif
switch (ret) { switch (ret) {
/* read BIO submitted and wb-journal entry found */
case 1:
BUG(); // TODO - handle wb-journal match
/* read BIO submitted (page in cache) */ /* read BIO submitted (page in cache) */
case 0: case 0:
break; break;
/* no page available in cache */ /* page not yet cached */
case -ENOBUFS:
case -ENODATA: case -ENODATA:
_debug("cache said ENODATA");
goto go_on;
/* page will not be cached */
case -ENOBUFS:
_debug("cache said ENOBUFS");
default: default:
go_on:
offset = page->index << PAGE_CACHE_SHIFT; offset = page->index << PAGE_CACHE_SHIFT;
len = min_t(size_t, i_size_read(inode) - offset, PAGE_SIZE); len = min_t(size_t, i_size_read(inode) - offset, PAGE_SIZE);
...@@ -198,27 +184,25 @@ static int afs_readpage(struct file *file, struct page *page) ...@@ -198,27 +184,25 @@ static int afs_readpage(struct file *file, struct page *page)
set_bit(AFS_VNODE_DELETED, &vnode->flags); set_bit(AFS_VNODE_DELETED, &vnode->flags);
ret = -ESTALE; ret = -ESTALE;
} }
#ifdef AFS_CACHING_SUPPORT
cachefs_uncache_page(vnode->cache, page); #ifdef CONFIG_AFS_FSCACHE
fscache_uncache_page(vnode->cache, page);
#endif #endif
BUG_ON(PageFsCache(page));
goto error; goto error;
} }
SetPageUptodate(page); SetPageUptodate(page);
#ifdef AFS_CACHING_SUPPORT /* send the page to the cache */
if (cachefs_write_page(vnode->cache, #ifdef CONFIG_AFS_FSCACHE
page, if (PageFsCache(page) &&
afs_file_readpage_write_complete, fscache_write_page(vnode->cache, page, GFP_KERNEL) != 0) {
NULL, fscache_uncache_page(vnode->cache, page);
GFP_KERNEL) != 0 BUG_ON(PageFsCache(page));
) {
cachefs_uncache_page(vnode->cache, page);
unlock_page(page);
} }
#else
unlock_page(page);
#endif #endif
unlock_page(page);
} }
_leave(" = 0"); _leave(" = 0");
...@@ -232,34 +216,59 @@ static int afs_readpage(struct file *file, struct page *page) ...@@ -232,34 +216,59 @@ static int afs_readpage(struct file *file, struct page *page)
} }
/* /*
* invalidate part or all of a page * read a set of pages
*/ */
static void afs_invalidatepage(struct page *page, unsigned long offset) static int afs_readpages(struct file *file, struct address_space *mapping,
struct list_head *pages, unsigned nr_pages)
{ {
int ret = 1; struct afs_vnode *vnode;
int ret = 0;
_enter("{%lu},%lu", page->index, offset); _enter(",{%lu},,%d", mapping->host->i_ino, nr_pages);
BUG_ON(!PageLocked(page)); vnode = AFS_FS_I(mapping->host);
if (vnode->flags & AFS_VNODE_DELETED) {
_leave(" = -ESTALE");
return -ESTALE;
}
if (PagePrivate(page)) { /* attempt to read as many of the pages as possible */
/* We release buffers only if the entire page is being #ifdef CONFIG_AFS_FSCACHE
* invalidated. ret = fscache_read_or_alloc_pages(vnode->cache,
* The get_block cached value has been unconditionally mapping,
* invalidated, so real IO is not possible anymore. pages,
*/ &nr_pages,
if (offset == 0) { afs_file_readpage_read_complete,
BUG_ON(!PageLocked(page)); NULL,
mapping_gfp_mask(mapping));
#else
ret = -ENOBUFS;
#endif
ret = 0; switch (ret) {
if (!PageWriteback(page)) /* all pages are being read from the cache */
ret = page->mapping->a_ops->releasepage(page, case 0:
0); BUG_ON(!list_empty(pages));
/* possibly should BUG_ON(!ret); - neilb */ BUG_ON(nr_pages != 0);
} _leave(" = 0 [reading all]");
} return 0;
/* there were pages that couldn't be read from the cache */
case -ENODATA:
case -ENOBUFS:
break;
/* other error */
default:
_leave(" = %d", ret); _leave(" = %d", ret);
return ret;
}
/* load the missing pages from the network */
ret = read_cache_pages(mapping, pages, (void *) afs_readpage, file);
_leave(" = %d [netting]", ret);
return ret;
} }
/* /*
...@@ -273,25 +282,82 @@ static int afs_launder_page(struct page *page) ...@@ -273,25 +282,82 @@ static int afs_launder_page(struct page *page)
} }
/* /*
* release a page and cleanup its private data * invalidate part or all of a page
* - release a page and clean up its private data if offset is 0 (indicating
* the entire page)
*/
static void afs_invalidatepage(struct page *page, unsigned long offset)
{
struct afs_writeback *wb = (struct afs_writeback *) page_private(page);
_enter("{%lu},%lu", page->index, offset);
BUG_ON(!PageLocked(page));
/* we clean up only if the entire page is being invalidated */
if (offset == 0) {
#ifdef CONFIG_AFS_FSCACHE
if (PageFsCache(page)) {
struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
fscache_wait_on_page_write(vnode->cache, page);
fscache_uncache_page(vnode->cache, page);
ClearPageFsCache(page);
}
#endif
if (PagePrivate(page)) {
if (wb && !PageWriteback(page)) {
set_page_private(page, 0);
afs_put_writeback(wb);
}
if (!page_private(page))
ClearPagePrivate(page);
}
}
_leave("");
}
/*
* release a page and clean up its private state if it's not busy
* - return true if the page can now be released, false if not
*/ */
static int afs_releasepage(struct page *page, gfp_t gfp_flags) static int afs_releasepage(struct page *page, gfp_t gfp_flags)
{ {
struct afs_writeback *wb = (struct afs_writeback *) page_private(page);
struct afs_vnode *vnode = AFS_FS_I(page->mapping->host); struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
struct afs_writeback *wb;
_enter("{{%x:%u}[%lu],%lx},%x", _enter("{{%x:%u}[%lu],%lx},%x",
vnode->fid.vid, vnode->fid.vnode, page->index, page->flags, vnode->fid.vid, vnode->fid.vnode, page->index, page->flags,
gfp_flags); gfp_flags);
/* deny if page is being written to the cache and the caller hasn't
* elected to wait */
#ifdef CONFIG_AFS_FSCACHE
if (PageFsCache(page)) {
if (fscache_check_page_write(vnode->cache, page)) {
if (!(gfp_flags & __GFP_WAIT)) {
_leave(" = F [cache busy]");
return 0;
}
fscache_wait_on_page_write(vnode->cache, page);
}
fscache_uncache_page(vnode->cache, page);
ClearPageFsCache(page);
}
#endif
if (PagePrivate(page)) { if (PagePrivate(page)) {
wb = (struct afs_writeback *) page_private(page); if (wb) {
ASSERT(wb != NULL);
set_page_private(page, 0); set_page_private(page, 0);
ClearPagePrivate(page);
afs_put_writeback(wb); afs_put_writeback(wb);
} }
ClearPagePrivate(page);
}
_leave(" = 0"); /* indicate that the page can be released */
return 0; _leave(" = T");
return 1;
} }
...@@ -61,6 +61,11 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key) ...@@ -61,6 +61,11 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
return -EBADMSG; return -EBADMSG;
} }
#ifdef CONFIG_AFS_FSCACHE
if (vnode->status.size != inode->i_size)
fscache_attr_changed(vnode->cache);
#endif
inode->i_nlink = vnode->status.nlink; inode->i_nlink = vnode->status.nlink;
inode->i_uid = vnode->status.owner; inode->i_uid = vnode->status.owner;
inode->i_gid = 0; inode->i_gid = 0;
...@@ -149,15 +154,6 @@ struct inode *afs_iget(struct super_block *sb, struct key *key, ...@@ -149,15 +154,6 @@ struct inode *afs_iget(struct super_block *sb, struct key *key,
return inode; return inode;
} }
#ifdef AFS_CACHING_SUPPORT
/* set up caching before reading the status, as fetch-status reads the
* first page of symlinks to see if they're really mntpts */
cachefs_acquire_cookie(vnode->volume->cache,
NULL,
vnode,
&vnode->cache);
#endif
if (!status) { if (!status) {
/* it's a remotely extant inode */ /* it's a remotely extant inode */
set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
...@@ -183,6 +179,15 @@ struct inode *afs_iget(struct super_block *sb, struct key *key, ...@@ -183,6 +179,15 @@ struct inode *afs_iget(struct super_block *sb, struct key *key,
} }
} }
/* set up caching before mapping the status, as map-status reads the
* first page of symlinks to see if they're really mountpoints */
inode->i_size = vnode->status.size;
#ifdef CONFIG_AFS_FSCACHE
vnode->cache = fscache_acquire_cookie(vnode->volume->cache,
&afs_vnode_cache_index_def,
vnode);
#endif
ret = afs_inode_map_status(vnode, key); ret = afs_inode_map_status(vnode, key);
if (ret < 0) if (ret < 0)
goto bad_inode; goto bad_inode;
...@@ -196,6 +201,10 @@ struct inode *afs_iget(struct super_block *sb, struct key *key, ...@@ -196,6 +201,10 @@ struct inode *afs_iget(struct super_block *sb, struct key *key,
/* failure */ /* failure */
bad_inode: bad_inode:
#ifdef CONFIG_AFS_FSCACHE
fscache_relinquish_cookie(vnode->cache, 0);
vnode->cache = NULL;
#endif
iget_failed(inode); iget_failed(inode);
_leave(" = %d [bad]", ret); _leave(" = %d [bad]", ret);
return ERR_PTR(ret); return ERR_PTR(ret);
...@@ -340,8 +349,8 @@ void afs_clear_inode(struct inode *inode) ...@@ -340,8 +349,8 @@ void afs_clear_inode(struct inode *inode)
ASSERT(list_empty(&vnode->writebacks)); ASSERT(list_empty(&vnode->writebacks));
ASSERT(!vnode->cb_promised); ASSERT(!vnode->cb_promised);
#ifdef AFS_CACHING_SUPPORT #ifdef CONFIG_AFS_FSCACHE
cachefs_relinquish_cookie(vnode->cache, 0); fscache_relinquish_cookie(vnode->cache, 0);
vnode->cache = NULL; vnode->cache = NULL;
#endif #endif
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "afs.h" #include "afs.h"
#include "afs_vl.h" #include "afs_vl.h"
#include "cache.h"
#define AFS_CELL_MAX_ADDRS 15 #define AFS_CELL_MAX_ADDRS 15
...@@ -193,8 +194,8 @@ struct afs_cell { ...@@ -193,8 +194,8 @@ struct afs_cell {
struct key *anonymous_key; /* anonymous user key for this cell */ struct key *anonymous_key; /* anonymous user key for this cell */
struct list_head proc_link; /* /proc cell list link */ struct list_head proc_link; /* /proc cell list link */
struct proc_dir_entry *proc_dir; /* /proc dir for this cell */ struct proc_dir_entry *proc_dir; /* /proc dir for this cell */
#ifdef AFS_CACHING_SUPPORT #ifdef CONFIG_AFS_FSCACHE
struct cachefs_cookie *cache; /* caching cookie */ struct fscache_cookie *cache; /* caching cookie */
#endif #endif
/* server record management */ /* server record management */
...@@ -249,8 +250,8 @@ struct afs_vlocation { ...@@ -249,8 +250,8 @@ struct afs_vlocation {
struct list_head grave; /* link in master graveyard list */ struct list_head grave; /* link in master graveyard list */
struct list_head update; /* link in master update list */ struct list_head update; /* link in master update list */
struct afs_cell *cell; /* cell to which volume belongs */ struct afs_cell *cell; /* cell to which volume belongs */
#ifdef AFS_CACHING_SUPPORT #ifdef CONFIG_AFS_FSCACHE
struct cachefs_cookie *cache; /* caching cookie */ struct fscache_cookie *cache; /* caching cookie */
#endif #endif
struct afs_cache_vlocation vldb; /* volume information DB record */ struct afs_cache_vlocation vldb; /* volume information DB record */
struct afs_volume *vols[3]; /* volume access record pointer (index by type) */ struct afs_volume *vols[3]; /* volume access record pointer (index by type) */
...@@ -302,8 +303,8 @@ struct afs_volume { ...@@ -302,8 +303,8 @@ struct afs_volume {
atomic_t usage; atomic_t usage;
struct afs_cell *cell; /* cell to which belongs (unrefd ptr) */ struct afs_cell *cell; /* cell to which belongs (unrefd ptr) */
struct afs_vlocation *vlocation; /* volume location */ struct afs_vlocation *vlocation; /* volume location */
#ifdef AFS_CACHING_SUPPORT #ifdef CONFIG_AFS_FSCACHE
struct cachefs_cookie *cache; /* caching cookie */ struct fscache_cookie *cache; /* caching cookie */
#endif #endif
afs_volid_t vid; /* volume ID */ afs_volid_t vid; /* volume ID */
afs_voltype_t type; /* type of volume */ afs_voltype_t type; /* type of volume */
...@@ -333,8 +334,8 @@ struct afs_vnode { ...@@ -333,8 +334,8 @@ struct afs_vnode {
struct afs_server *server; /* server currently supplying this file */ struct afs_server *server; /* server currently supplying this file */
struct afs_fid fid; /* the file identifier for this inode */ struct afs_fid fid; /* the file identifier for this inode */
struct afs_file_status status; /* AFS status info for this file */ struct afs_file_status status; /* AFS status info for this file */
#ifdef AFS_CACHING_SUPPORT #ifdef CONFIG_AFS_FSCACHE
struct cachefs_cookie *cache; /* caching cookie */ struct fscache_cookie *cache; /* caching cookie */
#endif #endif
struct afs_permits *permits; /* cache of permits so far obtained */ struct afs_permits *permits; /* cache of permits so far obtained */
struct mutex permits_lock; /* lock for altering permits list */ struct mutex permits_lock; /* lock for altering permits list */
...@@ -427,6 +428,22 @@ struct afs_uuid { ...@@ -427,6 +428,22 @@ struct afs_uuid {
}; };
/*****************************************************************************/ /*****************************************************************************/
/*
* cache.c
*/
#ifdef CONFIG_AFS_FSCACHE
extern struct fscache_netfs afs_cache_netfs;
extern struct fscache_cookie_def afs_cell_cache_index_def;
extern struct fscache_cookie_def afs_vlocation_cache_index_def;
extern struct fscache_cookie_def afs_volume_cache_index_def;
extern struct fscache_cookie_def afs_vnode_cache_index_def;
#else
#define afs_cell_cache_index_def (*(struct fscache_cookie_def *) NULL)
#define afs_vlocation_cache_index_def (*(struct fscache_cookie_def *) NULL)
#define afs_volume_cache_index_def (*(struct fscache_cookie_def *) NULL)
#define afs_vnode_cache_index_def (*(struct fscache_cookie_def *) NULL)
#endif
/* /*
* callback.c * callback.c
*/ */
...@@ -446,9 +463,6 @@ extern void afs_callback_update_kill(void); ...@@ -446,9 +463,6 @@ extern void afs_callback_update_kill(void);
*/ */
extern struct rw_semaphore afs_proc_cells_sem; extern struct rw_semaphore afs_proc_cells_sem;
extern struct list_head afs_proc_cells; extern struct list_head afs_proc_cells;
#ifdef AFS_CACHING_SUPPORT
extern struct cachefs_index_def afs_cache_cell_index_def;
#endif
#define afs_get_cell(C) do { atomic_inc(&(C)->usage); } while(0) #define afs_get_cell(C) do { atomic_inc(&(C)->usage); } while(0)
extern int afs_cell_init(char *); extern int afs_cell_init(char *);
...@@ -554,9 +568,6 @@ extern void afs_clear_inode(struct inode *); ...@@ -554,9 +568,6 @@ extern void afs_clear_inode(struct inode *);
* main.c * main.c
*/ */
extern struct afs_uuid afs_uuid; extern struct afs_uuid afs_uuid;
#ifdef AFS_CACHING_SUPPORT
extern struct cachefs_netfs afs_cache_netfs;
#endif
/* /*
* misc.c * misc.c
...@@ -637,10 +648,6 @@ extern int afs_get_MAC_address(u8 *, size_t); ...@@ -637,10 +648,6 @@ extern int afs_get_MAC_address(u8 *, size_t);
/* /*
* vlclient.c * vlclient.c
*/ */
#ifdef AFS_CACHING_SUPPORT
extern struct cachefs_index_def afs_vlocation_cache_index_def;
#endif
extern int afs_vl_get_entry_by_name(struct in_addr *, struct key *, extern int afs_vl_get_entry_by_name(struct in_addr *, struct key *,
const char *, struct afs_cache_vlocation *, const char *, struct afs_cache_vlocation *,
const struct afs_wait_mode *); const struct afs_wait_mode *);
...@@ -664,12 +671,6 @@ extern void afs_vlocation_purge(void); ...@@ -664,12 +671,6 @@ extern void afs_vlocation_purge(void);
/* /*
* vnode.c * vnode.c
*/ */
#ifdef AFS_CACHING_SUPPORT
extern struct cachefs_index_def afs_vnode_cache_index_def;
#endif
extern struct afs_timer_ops afs_vnode_cb_timed_out_ops;
static inline struct afs_vnode *AFS_FS_I(struct inode *inode) static inline struct afs_vnode *AFS_FS_I(struct inode *inode)
{ {
return container_of(inode, struct afs_vnode, vfs_inode); return container_of(inode, struct afs_vnode, vfs_inode);
...@@ -711,10 +712,6 @@ extern int afs_vnode_release_lock(struct afs_vnode *, struct key *); ...@@ -711,10 +712,6 @@ extern int afs_vnode_release_lock(struct afs_vnode *, struct key *);
/* /*
* volume.c * volume.c
*/ */
#ifdef AFS_CACHING_SUPPORT
extern struct cachefs_index_def afs_volume_cache_index_def;
#endif
#define afs_get_volume(V) do { atomic_inc(&(V)->usage); } while(0) #define afs_get_volume(V) do { atomic_inc(&(V)->usage); } while(0)
extern void afs_put_volume(struct afs_volume *); extern void afs_put_volume(struct afs_volume *);
......
/* AFS client file system /* AFS client file system
* *
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. * Copyright (C) 2002,5 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com) * Written by David Howells (dhowells@redhat.com)
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
...@@ -29,18 +29,6 @@ static char *rootcell; ...@@ -29,18 +29,6 @@ static char *rootcell;
module_param(rootcell, charp, 0); module_param(rootcell, charp, 0);
MODULE_PARM_DESC(rootcell, "root AFS cell name and VL server IP addr list"); MODULE_PARM_DESC(rootcell, "root AFS cell name and VL server IP addr list");
#ifdef AFS_CACHING_SUPPORT
static struct cachefs_netfs_operations afs_cache_ops = {
.get_page_cookie = afs_cache_get_page_cookie,
};
struct cachefs_netfs afs_cache_netfs = {
.name = "afs",
.version = 0,
.ops = &afs_cache_ops,
};
#endif
struct afs_uuid afs_uuid; struct afs_uuid afs_uuid;
/* /*
...@@ -104,10 +92,9 @@ static int __init afs_init(void) ...@@ -104,10 +92,9 @@ static int __init afs_init(void)
if (ret < 0) if (ret < 0)
return ret; return ret;
#ifdef AFS_CACHING_SUPPORT #ifdef CONFIG_AFS_FSCACHE
/* we want to be able to cache */ /* we want to be able to cache */
ret = cachefs_register_netfs(&afs_cache_netfs, ret = fscache_register_netfs(&afs_cache_netfs);
&afs_cache_cell_index_def);
if (ret < 0) if (ret < 0)
goto error_cache; goto error_cache;
#endif #endif
...@@ -142,8 +129,8 @@ static int __init afs_init(void) ...@@ -142,8 +129,8 @@ static int __init afs_init(void)
error_open_socket: error_open_socket:
error_vl_update_init: error_vl_update_init:
error_cell_init: error_cell_init:
#ifdef AFS_CACHING_SUPPORT #ifdef CONFIG_AFS_FSCACHE
cachefs_unregister_netfs(&afs_cache_netfs); fscache_unregister_netfs(&afs_cache_netfs);
error_cache: error_cache:
#endif #endif
afs_callback_update_kill(); afs_callback_update_kill();
...@@ -175,8 +162,8 @@ static void __exit afs_exit(void) ...@@ -175,8 +162,8 @@ static void __exit afs_exit(void)
afs_vlocation_purge(); afs_vlocation_purge();
flush_scheduled_work(); flush_scheduled_work();
afs_cell_purge(); afs_cell_purge();
#ifdef AFS_CACHING_SUPPORT #ifdef CONFIG_AFS_FSCACHE
cachefs_unregister_netfs(&afs_cache_netfs); fscache_unregister_netfs(&afs_cache_netfs);
#endif #endif
afs_proc_cleanup(); afs_proc_cleanup();
rcu_barrier(); rcu_barrier();
......
...@@ -173,9 +173,9 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt) ...@@ -173,9 +173,9 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt)
if (PageError(page)) if (PageError(page))
goto error; goto error;
buf = kmap(page); buf = kmap_atomic(page, KM_USER0);
memcpy(devname, buf, size); memcpy(devname, buf, size);
kunmap(page); kunmap_atomic(buf, KM_USER0);
page_cache_release(page); page_cache_release(page);
page = NULL; page = NULL;
......
...@@ -281,9 +281,8 @@ static void afs_vlocation_apply_update(struct afs_vlocation *vl, ...@@ -281,9 +281,8 @@ static void afs_vlocation_apply_update(struct afs_vlocation *vl,
vl->vldb = *vldb; vl->vldb = *vldb;
#ifdef AFS_CACHING_SUPPORT #ifdef CONFIG_AFS_FSCACHE
/* update volume entry in local cache */ fscache_update_cookie(vl->cache);
cachefs_update_cookie(vl->cache);
#endif #endif
} }
...@@ -304,11 +303,9 @@ static int afs_vlocation_fill_in_record(struct afs_vlocation *vl, ...@@ -304,11 +303,9 @@ static int afs_vlocation_fill_in_record(struct afs_vlocation *vl,
memset(&vldb, 0, sizeof(vldb)); memset(&vldb, 0, sizeof(vldb));
/* see if we have an in-cache copy (will set vl->valid if there is) */ /* see if we have an in-cache copy (will set vl->valid if there is) */
#ifdef AFS_CACHING_SUPPORT #ifdef CONFIG_AFS_FSCACHE
cachefs_acquire_cookie(cell->cache, vl->cache = fscache_acquire_cookie(vl->cell->cache,
&afs_volume_cache_index_def, &afs_vlocation_cache_index_def, vl);
vlocation,
&vl->cache);
#endif #endif
if (vl->valid) { if (vl->valid) {
...@@ -420,6 +417,11 @@ struct afs_vlocation *afs_vlocation_lookup(struct afs_cell *cell, ...@@ -420,6 +417,11 @@ struct afs_vlocation *afs_vlocation_lookup(struct afs_cell *cell,
spin_unlock(&vl->lock); spin_unlock(&vl->lock);
wake_up(&vl->waitq); wake_up(&vl->waitq);
/* update volume entry in local cache */
#ifdef CONFIG_AFS_FSCACHE
fscache_update_cookie(vl->cache);
#endif
/* schedule for regular updates */ /* schedule for regular updates */
afs_vlocation_queue_for_updates(vl); afs_vlocation_queue_for_updates(vl);
goto success; goto success;
...@@ -465,7 +467,7 @@ struct afs_vlocation *afs_vlocation_lookup(struct afs_cell *cell, ...@@ -465,7 +467,7 @@ struct afs_vlocation *afs_vlocation_lookup(struct afs_cell *cell,
spin_unlock(&vl->lock); spin_unlock(&vl->lock);
success: success:
_leave(" = %p",vl); _leave(" = %p", vl);
return vl; return vl;
error_abandon: error_abandon:
...@@ -523,10 +525,9 @@ static void afs_vlocation_destroy(struct afs_vlocation *vl) ...@@ -523,10 +525,9 @@ static void afs_vlocation_destroy(struct afs_vlocation *vl)
{ {
_enter("%p", vl); _enter("%p", vl);
#ifdef AFS_CACHING_SUPPORT #ifdef CONFIG_AFS_FSCACHE
cachefs_relinquish_cookie(vl->cache, 0); fscache_relinquish_cookie(vl->cache, 0);
#endif #endif
afs_put_cell(vl->cell); afs_put_cell(vl->cell);
kfree(vl); kfree(vl);
} }
......
...@@ -124,13 +124,11 @@ struct afs_volume *afs_volume_lookup(struct afs_mount_params *params) ...@@ -124,13 +124,11 @@ struct afs_volume *afs_volume_lookup(struct afs_mount_params *params)
} }
/* attach the cache and volume location */ /* attach the cache and volume location */
#ifdef AFS_CACHING_SUPPORT #ifdef CONFIG_AFS_FSCACHE
cachefs_acquire_cookie(vlocation->cache, volume->cache = fscache_acquire_cookie(vlocation->cache,
&afs_vnode_cache_index_def, &afs_volume_cache_index_def,
volume, volume);
&volume->cache);
#endif #endif
afs_get_vlocation(vlocation); afs_get_vlocation(vlocation);
volume->vlocation = vlocation; volume->vlocation = vlocation;
...@@ -194,8 +192,8 @@ void afs_put_volume(struct afs_volume *volume) ...@@ -194,8 +192,8 @@ void afs_put_volume(struct afs_volume *volume)
up_write(&vlocation->cell->vl_sem); up_write(&vlocation->cell->vl_sem);
/* finish cleaning up the volume */ /* finish cleaning up the volume */
#ifdef AFS_CACHING_SUPPORT #ifdef CONFIG_AFS_FSCACHE
cachefs_relinquish_cookie(volume->cache, 0); fscache_relinquish_cookie(volume->cache, 0);
#endif #endif
afs_put_vlocation(vlocation); afs_put_vlocation(vlocation);
......
...@@ -780,3 +780,24 @@ int afs_fsync(struct file *file, struct dentry *dentry, int datasync) ...@@ -780,3 +780,24 @@ int afs_fsync(struct file *file, struct dentry *dentry, int datasync)
_leave(" = %d", ret); _leave(" = %d", ret);
return ret; return ret;
} }
/*
* notification that a previously read-only page is about to become writable
* - if it returns an error, the caller will deliver a bus error signal
*/
int afs_page_mkwrite(struct vm_area_struct *vma, struct page *page)
{
struct afs_vnode *vnode = AFS_FS_I(vma->vm_file->f_mapping->host);
_enter("{{%x:%u}},{%lx}",
vnode->fid.vid, vnode->fid.vnode, page->index);
/* wait for the page to be written to the cache before we allow it to
* be modified */
#ifdef CONFIG_AFS_FSCACHE
fscache_wait_on_page_write(vnode->cache, page);
#endif
_leave(" = 0");
return 0;
}
config CACHEFILES
tristate "Filesystem caching on files"
depends on FSCACHE && BLOCK
help
This permits use of a mounted filesystem as a cache for other
filesystems - primarily networking filesystems - thus allowing fast
local disk to enhance the speed of slower devices.
See Documentation/filesystems/caching/cachefiles.txt for more
information.
config CACHEFILES_DEBUG
bool "Debug CacheFiles"
depends on CACHEFILES
help
This permits debugging to be dynamically enabled in the filesystem
caching on files module. If this is set, the debugging output may be
enabled by setting bits in /sys/modules/cachefiles/parameter/debug or
by including a debugging specifier in /etc/cachefilesd.conf.
config CACHEFILES_HISTOGRAM
bool "Gather latency information on CacheFiles"
depends on CACHEFILES && PROC_FS
help
This option causes latency information to be gathered on CacheFiles
operation and exported through file:
/proc/fs/cachefiles/histogram
The generation of this histogram adds a certain amount of overhead to
execution as there are a number of points at which data is gathered,
and on a multi-CPU system these may be on cachelines that keep
bouncing between CPUs. On the other hand, the histogram may be
useful for debugging purposes. Saying 'N' here is recommended.
See Documentation/filesystems/caching/cachefiles.txt for more
information.
#
# Makefile for caching in a mounted filesystem
#
cachefiles-y := \
bind.o \
daemon.o \
interface.o \
key.o \
main.o \
namei.o \
rdwr.o \
security.o \
xattr.o
cachefiles-$(CONFIG_CACHEFILES_HISTOGRAM) += proc.o
obj-$(CONFIG_CACHEFILES) := cachefiles.o
/* Bind and unbind a cache from the filesystem backing it
*
* Copyright (C) 2007 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/completion.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/statfs.h>
#include <linux/ctype.h>
#include "internal.h"
static int cachefiles_daemon_add_cache(struct cachefiles_cache *caches);
/*
* bind a directory as a cache
*/
int cachefiles_daemon_bind(struct cachefiles_cache *cache, char *args)
{
_enter("{%u,%u,%u,%u,%u,%u},%s",
cache->frun_percent,
cache->fcull_percent,
cache->fstop_percent,
cache->brun_percent,
cache->bcull_percent,
cache->bstop_percent,
args);
/* start by checking things over */
ASSERT(cache->fstop_percent >= 0 &&
cache->fstop_percent < cache->fcull_percent &&
cache->fcull_percent < cache->frun_percent &&
cache->frun_percent < 100);
ASSERT(cache->bstop_percent >= 0 &&
cache->bstop_percent < cache->bcull_percent &&
cache->bcull_percent < cache->brun_percent &&
cache->brun_percent < 100);
if (*args) {
kerror("'bind' command doesn't take an argument");
return -EINVAL;
}
if (!cache->rootdirname) {
kerror("No cache directory specified");
return -EINVAL;
}
/* don't permit already bound caches to be re-bound */
if (test_bit(CACHEFILES_READY, &cache->flags)) {
kerror("Cache already bound");
return -EBUSY;
}
/* make sure we have copies of the tag and dirname strings */
if (!cache->tag) {
/* the tag string is released by the fops->release()
* function, so we don't release it on error here */
cache->tag = kstrdup("CacheFiles", GFP_KERNEL);
if (!cache->tag)
return -ENOMEM;
}
/* add the cache */
return cachefiles_daemon_add_cache(cache);
}
/*
* add a cache
*/
static int cachefiles_daemon_add_cache(struct cachefiles_cache *cache)
{
struct cachefiles_object *fsdef;
struct nameidata nd;
struct kstatfs stats;
struct dentry *graveyard, *cachedir, *root;
const struct cred *saved_cred;
int ret;
_enter("");
/* we want to work under the module's security ID */
ret = cachefiles_get_security_ID(cache);
if (ret < 0)
return ret;
cachefiles_begin_secure(cache, &saved_cred);
/* allocate the root index object */
ret = -ENOMEM;
fsdef = kmem_cache_alloc(cachefiles_object_jar, GFP_KERNEL);
if (!fsdef)
goto error_root_object;
ASSERTCMP(fsdef->backer, ==, NULL);
atomic_set(&fsdef->usage, 1);
fsdef->type = FSCACHE_COOKIE_TYPE_INDEX;
_debug("- fsdef %p", fsdef);
/* look up the directory at the root of the cache */
memset(&nd, 0, sizeof(nd));
ret = path_lookup(cache->rootdirname, LOOKUP_DIRECTORY, &nd);
if (ret < 0)
goto error_open_root;
cache->mnt = mntget(nd.path.mnt);
root = dget(nd.path.dentry);
path_put(&nd.path);
/* check parameters */
ret = -EOPNOTSUPP;
if (!root->d_inode ||
!root->d_inode->i_op ||
!root->d_inode->i_op->lookup ||
!root->d_inode->i_op->mkdir ||
!root->d_inode->i_op->setxattr ||
!root->d_inode->i_op->getxattr ||
!root->d_sb ||
!root->d_sb->s_op ||
!root->d_sb->s_op->statfs ||
!root->d_sb->s_op->sync_fs)
goto error_unsupported;
ret = -EROFS;
if (root->d_sb->s_flags & MS_RDONLY)
goto error_unsupported;
/* determine the security of the on-disk cache as this governs
* security ID of files we create */
ret = cachefiles_determine_cache_security(cache, root, &saved_cred);
if (ret < 0)
goto error_unsupported;
/* get the cache size and blocksize */
ret = vfs_statfs(root, &stats);
if (ret < 0)
goto error_unsupported;
ret = -ERANGE;
if (stats.f_bsize <= 0)
goto error_unsupported;
ret = -EOPNOTSUPP;
if (stats.f_bsize > PAGE_SIZE)
goto error_unsupported;
cache->bsize = stats.f_bsize;
cache->bshift = 0;
if (stats.f_bsize < PAGE_SIZE)
cache->bshift = PAGE_SHIFT - ilog2(stats.f_bsize);
_debug("blksize %u (shift %u)",
cache->bsize, cache->bshift);
_debug("size %llu, avail %llu",
(unsigned long long) stats.f_blocks,
(unsigned long long) stats.f_bavail);
/* set up caching limits */
do_div(stats.f_files, 100);
cache->fstop = stats.f_files * cache->fstop_percent;
cache->fcull = stats.f_files * cache->fcull_percent;
cache->frun = stats.f_files * cache->frun_percent;
_debug("limits {%llu,%llu,%llu} files",
(unsigned long long) cache->frun,
(unsigned long long) cache->fcull,
(unsigned long long) cache->fstop);
stats.f_blocks >>= cache->bshift;
do_div(stats.f_blocks, 100);
cache->bstop = stats.f_blocks * cache->bstop_percent;
cache->bcull = stats.f_blocks * cache->bcull_percent;
cache->brun = stats.f_blocks * cache->brun_percent;
_debug("limits {%llu,%llu,%llu} blocks",
(unsigned long long) cache->brun,
(unsigned long long) cache->bcull,
(unsigned long long) cache->bstop);
/* get the cache directory and check its type */
cachedir = cachefiles_get_directory(cache, root, "cache");
if (IS_ERR(cachedir)) {
ret = PTR_ERR(cachedir);
goto error_unsupported;
}
fsdef->dentry = cachedir;
fsdef->fscache.cookie = NULL;
ret = cachefiles_check_object_type(fsdef);
if (ret < 0)
goto error_unsupported;
/* get the graveyard directory */
graveyard = cachefiles_get_directory(cache, root, "graveyard");
if (IS_ERR(graveyard)) {
ret = PTR_ERR(graveyard);
goto error_unsupported;
}
cache->graveyard = graveyard;
/* publish the cache */
fscache_init_cache(&cache->cache,
&cachefiles_cache_ops,
"%s",
fsdef->dentry->d_sb->s_id);
fscache_object_init(&fsdef->fscache, NULL, &cache->cache);
ret = fscache_add_cache(&cache->cache, &fsdef->fscache, cache->tag);
if (ret < 0)
goto error_add_cache;
/* done */
set_bit(CACHEFILES_READY, &cache->flags);
dput(root);
printk(KERN_INFO "CacheFiles:"
" File cache on %s registered\n",
cache->cache.identifier);
/* check how much space the cache has */
cachefiles_has_space(cache, 0, 0);
cachefiles_end_secure(cache, saved_cred);
return 0;
error_add_cache:
dput(cache->graveyard);
cache->graveyard = NULL;
error_unsupported:
mntput(cache->mnt);
cache->mnt = NULL;
dput(fsdef->dentry);
fsdef->dentry = NULL;
dput(root);
error_open_root:
kmem_cache_free(cachefiles_object_jar, fsdef);
error_root_object:
cachefiles_end_secure(cache, saved_cred);
kerror("Failed to register: %d", ret);
return ret;
}
/*
* unbind a cache on fd release
*/
void cachefiles_daemon_unbind(struct cachefiles_cache *cache)
{
_enter("");
if (test_bit(CACHEFILES_READY, &cache->flags)) {
printk(KERN_INFO "CacheFiles:"
" File cache on %s unregistering\n",
cache->cache.identifier);
fscache_withdraw_cache(&cache->cache);
}
dput(cache->graveyard);
mntput(cache->mnt);
kfree(cache->rootdirname);
kfree(cache->secctx);
kfree(cache->tag);
_leave("");
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/* Key to pathname encoder
*
* Copyright (C) 2007 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/slab.h>
#include "internal.h"
static const char cachefiles_charmap[64] =
"0123456789" /* 0 - 9 */
"abcdefghijklmnopqrstuvwxyz" /* 10 - 35 */
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* 36 - 61 */
"_-" /* 62 - 63 */
;
static const char cachefiles_filecharmap[256] = {
/* we skip space and tab and control chars */
[33 ... 46] = 1, /* '!' -> '.' */
/* we skip '/' as it's significant to pathwalk */
[48 ... 127] = 1, /* '0' -> '~' */
};
/*
* turn the raw key into something cooked
* - the raw key should include the length in the two bytes at the front
* - the key may be up to 514 bytes in length (including the length word)
* - "base64" encode the strange keys, mapping 3 bytes of raw to four of
* cooked
* - need to cut the cooked key into 252 char lengths (189 raw bytes)
*/
char *cachefiles_cook_key(const u8 *raw, int keylen, uint8_t type)
{
unsigned char csum, ch;
unsigned int acc;
char *key;
int loop, len, max, seg, mark, print;
_enter(",%d", keylen);
BUG_ON(keylen < 2 || keylen > 514);
csum = raw[0] + raw[1];
print = 1;
for (loop = 2; loop < keylen; loop++) {
ch = raw[loop];
csum += ch;
print &= cachefiles_filecharmap[ch];
}
if (print) {
/* if the path is usable ASCII, then we render it directly */
max = keylen - 2;
max += 2; /* two base64'd length chars on the front */
max += 5; /* @checksum/M */
max += 3 * 2; /* maximum number of segment dividers (".../M")
* is ((514 + 251) / 252) = 3
*/
max += 1; /* NUL on end */
} else {
/* calculate the maximum length of the cooked key */
keylen = (keylen + 2) / 3;
max = keylen * 4;
max += 5; /* @checksum/M */
max += 3 * 2; /* maximum number of segment dividers (".../M")
* is ((514 + 188) / 189) = 3
*/
max += 1; /* NUL on end */
}
max += 1; /* 2nd NUL on end */
_debug("max: %d", max);
key = kmalloc(max, GFP_KERNEL);
if (!key)
return NULL;
len = 0;
/* build the cooked key */
sprintf(key, "@%02x%c+", (unsigned) csum, 0);
len = 5;
mark = len - 1;
if (print) {
acc = *(uint16_t *) raw;
raw += 2;
key[len + 1] = cachefiles_charmap[acc & 63];
acc >>= 6;
key[len] = cachefiles_charmap[acc & 63];
len += 2;
seg = 250;
for (loop = keylen; loop > 0; loop--) {
if (seg <= 0) {
key[len++] = '\0';
mark = len;
key[len++] = '+';
seg = 252;
}
key[len++] = *raw++;
ASSERT(len < max);
}
switch (type) {
case FSCACHE_COOKIE_TYPE_INDEX: type = 'I'; break;
case FSCACHE_COOKIE_TYPE_DATAFILE: type = 'D'; break;
default: type = 'S'; break;
}
} else {
seg = 252;
for (loop = keylen; loop > 0; loop--) {
if (seg <= 0) {
key[len++] = '\0';
mark = len;
key[len++] = '+';
seg = 252;
}
acc = *raw++;
acc |= *raw++ << 8;
acc |= *raw++ << 16;
_debug("acc: %06x", acc);
key[len++] = cachefiles_charmap[acc & 63];
acc >>= 6;
key[len++] = cachefiles_charmap[acc & 63];
acc >>= 6;
key[len++] = cachefiles_charmap[acc & 63];
acc >>= 6;
key[len++] = cachefiles_charmap[acc & 63];
ASSERT(len < max);
}
switch (type) {
case FSCACHE_COOKIE_TYPE_INDEX: type = 'J'; break;
case FSCACHE_COOKIE_TYPE_DATAFILE: type = 'E'; break;
default: type = 'T'; break;
}
}
key[mark] = type;
key[len++] = 0;
key[len] = 0;
_leave(" = %p %d", key, len);
return key;
}
/* Network filesystem caching backend to use cache files on a premounted
* filesystem
*
* Copyright (C) 2007 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/completion.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/statfs.h>
#include <linux/sysctl.h>
#include <linux/miscdevice.h>
#include "internal.h"
unsigned cachefiles_debug;
module_param_named(debug, cachefiles_debug, uint, S_IWUSR | S_IRUGO);
MODULE_PARM_DESC(cachefiles_debug, "CacheFiles debugging mask");
MODULE_DESCRIPTION("Mounted-filesystem based cache");
MODULE_AUTHOR("Red Hat, Inc.");
MODULE_LICENSE("GPL");
struct kmem_cache *cachefiles_object_jar;
static struct miscdevice cachefiles_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "cachefiles",
.fops = &cachefiles_daemon_fops,
};
static void cachefiles_object_init_once(void *_object)
{
struct cachefiles_object *object = _object;
memset(object, 0, sizeof(*object));
spin_lock_init(&object->work_lock);
}
/*
* initialise the fs caching module
*/
static int __init cachefiles_init(void)
{
int ret;
ret = misc_register(&cachefiles_dev);
if (ret < 0)
goto error_dev;
/* create an object jar */
ret = -ENOMEM;
cachefiles_object_jar =
kmem_cache_create("cachefiles_object_jar",
sizeof(struct cachefiles_object),
0,
SLAB_HWCACHE_ALIGN,
cachefiles_object_init_once);
if (!cachefiles_object_jar) {
printk(KERN_NOTICE
"CacheFiles: Failed to allocate an object jar\n");
goto error_object_jar;
}
ret = cachefiles_proc_init();
if (ret < 0)
goto error_proc;
printk(KERN_INFO "CacheFiles: Loaded\n");
return 0;
error_proc:
kmem_cache_destroy(cachefiles_object_jar);
error_object_jar:
misc_deregister(&cachefiles_dev);
error_dev:
kerror("failed to register: %d", ret);
return ret;
}
fs_initcall(cachefiles_init);
/*
* clean up on module removal
*/
static void __exit cachefiles_exit(void)
{
printk(KERN_INFO "CacheFiles: Unloading\n");
cachefiles_proc_cleanup();
kmem_cache_destroy(cachefiles_object_jar);
misc_deregister(&cachefiles_dev);
}
module_exit(cachefiles_exit);
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
config FSCACHE
tristate "General filesystem local caching manager"
depends on EXPERIMENTAL
select SLOW_WORK
help
This option enables a generic filesystem caching manager that can be
used by various network and other filesystems to cache data locally.
Different sorts of caches can be plugged in, depending on the
resources available.
See Documentation/filesystems/caching/fscache.txt for more information.
config FSCACHE_STATS
bool "Gather statistical information on local caching"
depends on FSCACHE && PROC_FS
help
This option causes statistical information to be gathered on local
caching and exported through file:
/proc/fs/fscache/stats
The gathering of statistics adds a certain amount of overhead to
execution as there are a quite a few stats gathered, and on a
multi-CPU system these may be on cachelines that keep bouncing
between CPUs. On the other hand, the stats are very useful for
debugging purposes. Saying 'Y' here is recommended.
See Documentation/filesystems/caching/fscache.txt for more information.
config FSCACHE_HISTOGRAM
bool "Gather latency information on local caching"
depends on FSCACHE && PROC_FS
help
This option causes latency information to be gathered on local
caching and exported through file:
/proc/fs/fscache/histogram
The generation of this histogram adds a certain amount of overhead to
execution as there are a number of points at which data is gathered,
and on a multi-CPU system these may be on cachelines that keep
bouncing between CPUs. On the other hand, the histogram may be
useful for debugging purposes. Saying 'N' here is recommended.
See Documentation/filesystems/caching/fscache.txt for more information.
config FSCACHE_DEBUG
bool "Debug FS-Cache"
depends on FSCACHE
help
This permits debugging to be dynamically enabled in the local caching
management module. If this is set, the debugging output may be
enabled by setting bits in /sys/modules/fscache/parameter/debug.
See Documentation/filesystems/caching/fscache.txt for more information.
#
# Makefile for general filesystem caching code
#
fscache-y := \
cache.o \
cookie.o \
fsdef.o \
main.o \
netfs.o \
object.o \
operation.o \
page.o
fscache-$(CONFIG_PROC_FS) += proc.o
fscache-$(CONFIG_FSCACHE_STATS) += stats.o
fscache-$(CONFIG_FSCACHE_HISTOGRAM) += histogram.o
obj-$(CONFIG_FSCACHE) := fscache.o
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -84,3 +84,11 @@ config ROOT_NFS ...@@ -84,3 +84,11 @@ config ROOT_NFS
<file:Documentation/filesystems/nfsroot.txt>. <file:Documentation/filesystems/nfsroot.txt>.
Most people say N here. Most people say N here.
config NFS_FSCACHE
bool "Provide NFS client caching support (EXPERIMENTAL)"
depends on EXPERIMENTAL
depends on NFS_FS=m && FSCACHE || NFS_FS=y && FSCACHE=y
help
Say Y here if you want NFS data to be cached locally on disc through
the general filesystem cache manager
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment