[Linux-cachefs] [PATCH] Split CacheFS into general manager and cache backend bits
David Howells
dhowells at redhat.com
Mon Oct 4 18:24:36 UTC 2004
Content-Type: text/plain; charset=US-ASCIItext/plain; charset=US-ASCII
Well... to make life interesting, I've decided that it's probably a good idea
to split cachefs into two:
(*) FS-Cache
An intermediary layer that sits between netfs's and cache backends. It
doesn't care how a cache operates, as long as it provides certain
operations.
(*) CacheFS
A cache backend that uses a block device for its data storage.
See what you think. The wasn't really much work involved - it mostly split
naturally.
The attached patch include NFS on cachefs. AFS on CacheFS won't work with this
patch yet.
David
--Multipart_Mon_Oct__4_17:32:25_2004-1
Content-Type: text/plain; type=patch; charset=US-ASCII
Content-Disposition: attachment; filename="fscache-269rc2mm4.diff"
Content-Transfer-Encoding: 7bit
diff -uNr linux-2.6.9-rc2-mm4/Documentation/filesystems/cachefs.txt linux-2.6.9-rc2-mm4-fscache/Documentation/filesystems/cachefs.txt
--- linux-2.6.9-rc2-mm4/Documentation/filesystems/cachefs.txt 2004-09-27 11:23:36.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/Documentation/filesystems/cachefs.txt 1970-01-01 01:00:00.000000000 +0100
@@ -1,892 +0,0 @@
- ===========================
- CacheFS: Caching Filesystem
- ===========================
-
-========
-OVERVIEW
-========
-
-CacheFS is a general purpose cache for network filesystems, though it could be
-used for caching other things such as ISO9660 filesystems too.
-
-CacheFS uses a block device directly rather than a bunch of files under an
-already mounted filesystem. For why this is so, see further on. If necessary,
-however, a file can be loopback mounted as a cache.
-
-CacheFS does not follow the idea of completely loading every netfs file opened
-into the cache before it can be operated upon, and then serving the pages out
-of CacheFS rather than the netfs because:
-
- (1) It must be practical to operate without a cache.
-
- (2) The size of any accessible file must not be limited to the size of the
- cache.
-
- (3) The combined size of all opened files (this includes mapped libraries)
- must not be limited to the size of the cache.
-
- (4) The user should not be forced to download an entire file just to do a
- one-off access of a small portion of it.
-
-It rather serves the cache out in PAGE_SIZE chunks as and when requested by
-the netfs('s) using it.
-
-
-CacheFS provides the following facilities:
-
- (1) More than one block device can be mounted as a cache.
-
- (2) Caches can be mounted / unmounted at any time.
-
- (3) The netfs is provided with an interface that allows either party to
- withdraw caching facilities from a file (required for (2)).
-
- (4) The interface to the netfs returns as few errors as possible, preferring
- rather to let the netfs remain oblivious.
-
- (5) Cookies are used to represent files and indexes to the netfs. The simplest
- cookie is just a NULL pointer - indicating nothing cached there.
-
- (6) The netfs is allowed to propose - dynamically - any index hierarchy it
- desires, though it must be aware that the index search function is
- recursive and stack space is limited.
-
- (7) Data I/O is done direct to and from the netfs's pages. The netfs indicates
- that page A is at index B of the data-file represented by cookie C, and
- that it should be read or written. CacheFS may or may not start I/O on
- that page, but if it does, a netfs callback will be invoked to indicate
- completion.
-
- (8) Cookies can be "retired" upon release. At this point CacheFS will mark
- them as obsolete and the index hierarchy rooted at that point will get
- recycled.
-
- (9) The netfs provides a "match" function for index searches. In addition to
- saying whether a match was made or not, this can also specify that an
- entry should be updated or deleted.
-
-(10) All metadata modifications (this includes index contents) are performed
- as journalled transactions. These are replayed on mounting.
-
-
-=============================================
-WHY A BLOCK DEVICE? WHY NOT A BUNCH OF FILES?
-=============================================
-
-CacheFS is backed by a block device rather than being backed by a bunch of
-files on a filesystem. This confers several advantages:
-
- (1) Performance.
-
- Going directly to a block device means that we can DMA directly to/from
- the the netfs's pages. If another filesystem was managing the backing
- store, everything would have to be copied between pages. Whilst DirectIO
- does exist, it doesn't appear easy to make use of in this situation.
-
- New address space or file operations could be added to make it possible to
- persuade a backing discfs to generate block I/O directly to/from disc
- blocks under its control, but that then means the discfs has to keep track
- of I/O requests to pages not under its control.
-
- Furthermore, we only have to do one lot of readahead calculations, not
- two; in the discfs backing case, the netfs would do one and the discfs
- would do one.
-
- (2) Memory.
-
- Using a block device means that we have a lower memory usage - all data
- pages belong to the netfs we're backing. If we used a filesystem, we would
- have twice as many pages at certain points - one from the netfs and one
- from the backing discfs. In the backing discfs model, under situations of
- memory pressure, we'd have to allocate or keep around a discfs page to be
- able to write out a netfs page; or else we'd need to be able to punch a
- hole in the backing file.
-
- Furthermore, whilst we have to keep a CacheFS inode around in memory for
- every netfs inode we're backing, a backing discfs would have to keep the
- dentry and possibly a file struct too.
-
- (3) Holes.
-
- The cache uses holes to indicate to the netfs that it hasn't yet
- downloaded the data for that page.
-
- Since CacheFS is its own filesystem, it can support holes in files
- trivially. Running on top of another discfs would limit us to using ones
- that can support holes.
-
- Furthermore, it would have to be made possible to detect holes in a discfs
- file, rather than just seeing zero filled blocks.
-
- (4) Data Consistency.
-
- Cachefs uses a pair of journals to keep track of the state of the cache
- and all the pages contained therein. This means that it doesn't get into
- an inconsistent state in the on-disc cache and it doesn't lose disc space.
-
- CacheFS takes especial care between the allocation of a block and its
- splicing into the on-disc pointer tree, and the data having been written
- to disc. If power is interrupted and then restored, the journals are
- replayed and if it is seen that a block was allocated but not written it
- is then punched out. Being backed by a discfs, I'm not certain what will
- happen. It may well be possible to mark a discfs's journal, if it has one,
- but how does the discfs deal with those marks? This also limits consistent
- caching to running on journalled discfs's where there's a function to
- write extraordinary marks into the journal.
-
- The alternative would be to keep flags in the superblock, and to
- re-initialise the cache if it wasn't cleanly unmounted.
-
- Knowing that your cache is in a good state is vitally important if you,
- say, put /usr on AFS. Some organisations put everything barring /etc,
- /sbin, /lib and /var on AFS and have an enormous cache on every
- computer. Imagine if the power goes out and renders every cache
- inconsistent, requiring all the computers to re-initialise their caches
- when the power comes back on...
-
- (5) Recycling.
-
- Recycling is simple on CacheFS. It can just scan the metadata index to
- look for inodes that require reclamation/recycling; and it can also build
- up a list of the least recently used inodes so that they can be reclaimed
- later to make space.
-
- Doing this on a discfs would require a search going down through a nest
- of directories, and would probably have to be done in userspace.
-
- (6) Disc Space.
-
- Whilst the block device does set a hard ceiling on the amount of space
- available, CacheFS can guarantee that all that space will be available to
- the cache. On a discfs-backed cache, the administrator would probably want
- to set a cache size limit, but the system wouldn't be able guarantee that
- all that space would be available to the cache - not unless that cache was
- on a partition of its own.
-
- Furthermore, with a discfs-backed cache, if the recycler starts to reclaim
- cache files to make space, the freed blocks may just be eaten directly by
- userspace programs, potentially resulting in the entire cache being
- consumed. Alternatively, netfs operations may end up being held up because
- the cache can't get blocks on which to store the data.
-
- (7) Users.
-
- Users can't so easily go into CacheFS and run amok. The worst they can do
- is cause bits of the cache to be recycled early. With a discfs-backed
- cache, they can do all sorts of bad things to the files belonging to the
- cache, and they can do this quite by accident.
-
-
-On the other hand, there would be some advantages to using a file-based cache
-rather than a blockdev-based cache:
-
- (1) Having to copy to a discfs's page would mean that a netfs could just make
- the copy and then assume its own page is ready to go.
-
- (2) Backing onto a discfs wouldn't require a committed block device. You would
- just nominate a directory and go from there. With CacheFS you have to
- repartition or install an extra drive to make use of it in an existing
- system (though the loopback device offers a way out).
-
- (3) CacheFS requires the netfs to store a key in any pertinent index entry,
- and it also permits a limited amount arbitrary data to be stored there.
-
- A discfs could be requested to store the netfs's data in xattrs, and the
- filename could be used to store the key, though the key would have to be
- rendered as text not binary. Likewise indexes could be rendered as
- directories with xattrs.
-
- (4) You could easily make your cache bigger if the discfs has plenty of space,
- you could even go across multiple mountpoints.
-
-
-======================
-GENERAL ON-DISC LAYOUT
-======================
-
-The filesystem is divided into a number of parts:
-
- 0 +---------------------------+
- | Superblock |
- 1 +---------------------------+
- | Update Journal |
- +---------------------------+
- | Validity Journal |
- +---------------------------+
- | Write-Back Journal |
- +---------------------------+
- | |
- | Data |
- | |
- END +---------------------------+
-
-The superblock contains the filesystem ID tags and pointers to all the other
-regions.
-
-The update journal consists of a set of entries of sector size that keep track
-of what changes have been made to the on-disc filesystem, but not yet
-committed.
-
-The validity journal contains records of data blocks that have been allocated
-but not yet written. Upon journal replay, all these blocks will be detached
-from their pointers and recycled.
-
-The writeback journal keeps track of changes that have been made locally to
-data blocks, but that have not yet been committed back to the server. This is
-not yet implemented.
-
-The journals are replayed upon mounting to make sure that the cache is in a
-reasonable state.
-
-The data region holds a number of things:
-
- (1) Index Files
-
- These are files of entries used by CacheFS internally and by filesystems
- that wish to cache data here (such as AFS) to keep track of what's in
- the cache at any given time.
-
- The first index file (inode 1) is special. It holds the CacheFS-specific
- metadata for every file in the cache (including direct, single-indirect
- and double-indirect block pointers).
-
- The second index file (inode 2) is also special. It has an entry for
- each filesystem that's currently holding data in this cache.
-
- Every allocated entry in an index has an inode bound to it. This inode is
- either another index file or it is a data file.
-
- (2) Cached Data Files
-
- These are caches of files from remote servers. Holes in these files
- represent blocks not yet obtained from the server.
-
- (3) Indirection Blocks
-
- Should a file have more blocks than can be pointed to by the few
- pointers in its storage management record, then indirection blocks will
- be used to point to further data or indirection blocks.
-
- Three levels of indirection are currently supported:
-
- - single indirection
- - double indirection
-
- (4) Allocation Nodes and Free Blocks
-
- The free blocks of the filesystem are kept in two single-branched
- "trees". One tree is the blocks that are ready to be allocated, and the
- other is the blocks that have just been recycled. When the former tree
- becomes empty, the latter tree is decanted across.
-
- Each tree is arranged as a chain of "nodes", each node points to the next
- node in the chain (unless it's at the end) and also up to 1022 free
- blocks.
-
-Note that all blocks are PAGE_SIZE in size. The blocks are numbered starting
-with the superblock at 0. Using 32-bit block pointers, a maximum number of
-0xffffffff blocks can be accessed, meaning that the maximum cache size is ~16TB
-for 4KB pages.
-
-
-========
-MOUNTING
-========
-
-Since CacheFS is actually a quasi-filesystem, it requires a block device behind
-it. The way to give it one is to mount it as cachefs type on a directory
-somewhere. The mounted filesystem will then present the user with a set of
-directories outlining the index structure resident in the cache. Indexes
-(directories) and files can be turfed out of the cache by the sysadmin through
-the use of rmdir and unlink.
-
-For instance, if a cache contains AFS data, the user might see the following:
-
- root>mount -t cachefs /dev/hdg9 /cache-hdg9
- root>ls -1 /cache-hdg9
- afs
- root>ls -1 /cache-hdg9/afs
- cambridge.redhat.com
- root>ls -1 /cache-hdg9/afs/cambridge.redhat.com
- root.afs
- root.cell
-
-However, a block device that's going to be used for a cache must be prepared
-before it can be mounted initially. This is done very simply by:
-
- echo "cachefs___" >/dev/hdg9
-
-During the initial mount, the basic structure will be scribed into the cache,
-and then a background thread will "recycle" the as-yet unused data blocks.
-
-
-======================
-NETWORK FILESYSTEM API
-======================
-
-There is, of course, an API by which a network filesystem can make use of the
-CacheFS facilities. This is based around a number of principles:
-
- (1) Every file and index is represented by a cookie. This cookie may or may
- not have anything associated with it, but the netfs doesn't need to care.
-
- (2) Barring the top-level index (one entry per cached netfs), the index
- hierarchy for each netfs is structured according the whim of the netfs.
-
- (3) Any netfs page being backed by the cache must have a small token
- associated with it (possibly pointed to by page->private) so that CacheFS
- can keep track of it.
-
-This API is declared in <linux/cachefs.h>.
-
-
-NETWORK FILESYSTEM DEFINITION
------------------------------
-
-CacheFS needs a description of the network filesystem. This is specified using
-a record of the following structure:
-
- struct cachefs_netfs {
- const char *name;
- unsigned version;
- struct cachefs_netfs_operations *ops;
- struct cachefs_cookie *primary_index;
- ...
- };
-
-This first three fields should be filled in before registration, and the fourth
-will be filled in by the registration function; any other fields should just be
-ignored and are for internal use only.
-
-The fields are:
-
- (1) The name of the netfs (used as the key in the toplevel index).
-
- (2) The version of the netfs (if the name matches but the version doesn't, the
- entire on-disc hierarchy for this netfs will be scrapped and begun
- afresh).
-
- (3) The operations table is defined as follows:
-
- struct cachefs_netfs_operations {
- struct cachefs_page *(*get_page_cookie)(struct page *page);
- };
-
- The functions here must all be present. Currently the only one is:
-
- (a) get_page_cookie(): Get the token used to bind a page to a block in a
- cache. This function should allocate it if it doesn't exist.
-
- Return -ENOMEM if there's not enough memory and -ENODATA if the page
- just shouldn't be cached.
-
- Set *_page_cookie to point to the token and return 0 if there is now a
- cookie. Note that the netfs must keep track of the cookie itself (and
- free it later). page->private can be used for this (see below).
-
- (4) The cookie representing the primary index will be allocated according to
- another parameter passed into the registration function.
-
-For example, kAFS (linux/fs/afs/) uses the following definitions to describe
-itself:
-
- 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,
- };
-
-
-INDEX DEFINITION
-----------------
-
-Indexes are used for two purposes:
-
- (1) To speed up the finding of a file based on a series of keys (such as AFS's
- "cell", "volume ID", "vnode ID").
-
- (2) To make it easier to discard a subset of all the files cached based around
- a particular key - for instance to mirror the removal of an AFS volume.
-
-However, since it's unlikely that any two netfs's are going to want to define
-their index hierarchies in quite the same way, CacheFS tries to impose as few
-restraints as possible on how an index is structured and where it is placed in
-the tree. The netfs can even mix indexes and data files at the same level, but
-it's not recommended.
-
-There are some limits on indexes:
-
- (1) All entries in any given index must be the same size. An array of such
- entries needn't fit exactly into a page, but they will be not laid across
- a page boundary.
-
- The netfs supplies a blob of data for each index entry, and CacheFS
- provides an inode number and a flag.
-
- (2) The entries in one index can be of a different size to the entries in
- another index.
-
- (3) The entry data must be journallable, and thus must be able to fit into an
- update journal entry - this limits the maximum size to a little over 400
- bytes at present.
-
- (4) The index data must start with the key. The layout of the key is described
- in the index definition, and this is used to display the key in some
- appropriate way.
-
- (5) The depth of the index tree should be judged with care as the search
- function is recursive. Too many layers will run the kernel out of stack.
-
-To define an index, a structure of the following type should be filled out:
-
- struct cachefs_index_def
- {
- uint8_t name[8];
- uint16_t data_size;
- struct {
- uint8_t type;
- uint16_t len;
- } keys[4];
-
- cachefs_match_val_t (*match)(void *target_netfs_data,
- const void *entry);
-
- void (*update)(void *source_netfs_data, void *entry);
- };
-
-This has the following fields:
-
- (1) The name of the index (NUL terminated unless all 8 chars are used).
-
- (2) The size of the data blob provided by the netfs.
-
- (3) A definition of the key(s) at the beginning of the blob. The netfs is
- permitted to specify up to four keys. The total length must not exceed the
- data size. It is assumed that the keys will be laid end to end in order,
- starting at the first byte of the data.
-
- The type field specifies the way the data should be displayed. It can be
- one of:
-
- (*) CACHEFS_INDEX_KEYS_NOTUSED - key field not used
- (*) CACHEFS_INDEX_KEYS_BIN - display byte-by-byte in hex
- (*) CACHEFS_INDEX_KEYS_ASCIIZ - NUL-terminated ASCII
- (*) CACHEFS_INDEX_KEYS_IPV4ADDR - display as IPv4 address
- (*) CACHEFS_INDEX_KEYS_IPV6ADDR - display as IPv6 address
-
- (4) A function to compare an in-page-cache index entry blob with the data
- passed to the cookie acquisition function. This function can also be used
- to extract data from the blob and copy it into the netfs's structures.
-
- The values this function can return are:
-
- (*) CACHEFS_MATCH_FAILED - failed to match
- (*) CACHEFS_MATCH_SUCCESS - successful match
- (*) CACHEFS_MATCH_SUCCESS_UPDATE - successful match, entry needs update
- (*) CACHEFS_MATCH_SUCCESS_DELETE - entry should be deleted
-
- For example, in linux/fs/afs/vnode.c:
-
- static cachefs_match_val_t
- afs_vnode_cache_match(void *target, const void *entry)
- {
- const struct afs_cache_vnode *cvnode = entry;
- struct afs_vnode *vnode = target;
-
- if (vnode->fid.vnode != cvnode->vnode_id)
- return CACHEFS_MATCH_FAILED;
-
- if (vnode->fid.unique != cvnode->vnode_unique ||
- vnode->status.version != cvnode->data_version)
- return CACHEFS_MATCH_SUCCESS_DELETE;
-
- return CACHEFS_MATCH_SUCCESS;
- }
-
- (5) A function to initialise or update an in-page-cache index entry blob from
- netfs data passed to CacheFS by the netfs. This function should not assume
- that there's any data yet in the in-page-cache.
-
- Continuing the above example:
-
- static void afs_vnode_cache_update(void *source, void *entry)
- {
- struct afs_cache_vnode *cvnode = entry;
- struct afs_vnode *vnode = source;
-
- cvnode->vnode_id = vnode->fid.vnode;
- cvnode->vnode_unique = vnode->fid.unique;
- cvnode->data_version = vnode->status.version;
- }
-
-To finish the above example, the index definition for the "vnode" level is as
-follows:
-
- 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,
- };
-
-The first element of struct afs_cache_vnode is the vnode ID.
-
-And for contrast, the cell index definition is:
-
- struct cachefs_index_def afs_cache_cell_index_def = {
- .name = "cell_ix",
- .data_size = sizeof(afs_cell_t),
- .keys[0] = { CACHEFS_INDEX_KEYS_ASCIIZ, 64 },
- .match = afs_cell_cache_match,
- .update = afs_cell_cache_update,
- };
-
-The cell index is the primary index for kAFS.
-
-
-NETWORK FILESYSTEM (UN)REGISTRATION
------------------------------------
-
-The first step is to declare the network filesystem to the cache. This also
-involves specifying the layout of the primary index (for AFS, this would be the
-"cell" level).
-
-The registration function is:
-
- int cachefs_register_netfs(struct cachefs_netfs *netfs,
- struct cachefs_index_def *primary_idef);
-
-It just takes pointers to the netfs definition and the primary index
-definition. It returns 0 or an error as appropriate.
-
-For kAFS, registration is done as follows:
-
- ret = cachefs_register_netfs(&afs_cache_netfs,
- &afs_cache_cell_index_def);
-
-The last step is, of course, unregistration:
-
- void cachefs_unregister_netfs(struct cachefs_netfs *netfs);
-
-
-INDEX REGISTRATION
-------------------
-
-The second step is to inform cachefs about part of an index hierarchy that can
-be used to locate files. This is done by requesting a cookie for each index in
-the path to the file:
-
- struct cachefs_cookie *
- cachefs_acquire_cookie(struct cachefs_cookie *iparent,
- struct cachefs_index_def *idef,
- void *netfs_data);
-
-This function creates an index entry in the index represented by iparent,
-loading the associated blob by calling iparent's update method with the
-supplied netfs_data.
-
-It also creates a new index inode, formatted according to the definition
-supplied in idef. The new cookie is then returned in *_cookie.
-
-Note that this function never returns an error - all errors are handled
-internally. It may also return CACHEFS_NEGATIVE_COOKIE. It is quite acceptable
-to pass this token back to this function as iparent (or even to the relinquish
-cookie, read page and write page functions - see below).
-
-Note also that no indexes are actually created on disc until a data file needs
-to be created somewhere down the hierarchy. Furthermore, an index may be
-created in several different caches independently at different times. This is
-all handled transparently, and the netfs doesn't see any of it.
-
-For example, with AFS, a cell would be added to the primary index. This index
-entry would have a dependent inode containing a volume location index for the
-volume mappings within this cell:
-
- cell->cache =
- cachefs_acquire_cookie(afs_cache_netfs.primary_index,
- &afs_vlocation_cache_index_def,
- cell);
-
-Then when a volume location was accessed, it would be entered into the cell's
-index and an inode would be allocated that acts as a volume type and hash chain
-combination:
-
- vlocation->cache =
- cachefs_acquire_cookie(cell->cache,
- &afs_volume_cache_index_def,
- vlocation);
-
-And then a particular flavour of volume (R/O for example) could be added to
-that index, creating another index for vnodes (AFS inode equivalents):
-
- volume->cache =
- cachefs_acquire_cookie(vlocation->cache,
- &afs_vnode_cache_index_def,
- volume);
-
-
-DATA FILE REGISTRATION
-----------------------
-
-The third step is to request a data file be created in the cache. This is
-almost identical to index cookie acquisition. The only difference is that a
-NULL index definition is passed.
-
- vnode->cache =
- cachefs_acquire_cookie(volume->cache,
- NULL,
- vnode);
-
-
-
-PAGE ALLOC/READ/WRITE
----------------------
-
-And the fourth step is to propose a page be cached. There are two functions
-that are used to do this.
-
-Firstly, the netfs should ask CacheFS to examine the caches and read the
-contents cached for a particular page of a particular file if present, or else
-allocate space to store the contents if not:
-
- typedef
- void (*cachefs_rw_complete_t)(void *cookie_data,
- struct page *page,
- void *end_io_data,
- int error);
-
- int cachefs_read_or_alloc_page(struct cachefs_cookie *cookie,
- struct page *page,
- cachefs_rw_complete_t end_io_func,
- void *end_io_data,
- unsigned long gfp);
-
-The cookie argument must specify a data file cookie, the page specified will
-have the data loaded into it (and is also used to specify the page number), and
-the gfp argument is used to control how any memory allocations made are satisfied.
-
-If the cookie indicates the inode is not cached:
-
- (1) The function will return -ENOBUFS.
-
-Else if there's a copy of the page resident on disc:
-
- (1) The function will submit a request to read the data off the disc directly
- into the page specified.
-
- (2) The function will return 0.
-
- (3) When the read is complete, end_io_func() will be invoked with:
-
- (*) The netfs data supplied when the cookie was created.
-
- (*) The page descriptor.
-
- (*) The data passed to the above function.
-
- (*) An argument that's 0 on success or negative for an error.
-
- If an error occurs, it should be assumed that the page contains no usable
- data.
-
-Otherwise, if there's not a copy available on disc:
-
- (1) A block may be allocated in the cache and attached to the inode at the
- appropriate place.
-
- (2) The validity journal will be marked to indicate this page does not yet
- contain valid data.
-
- (3) The function will return -ENODATA.
-
-
-Secondly, if the netfs changes the contents of the page (either due to an
-initial download or if a user performs a write), then the page should be
-written back to the cache:
-
- int cachefs_write_page(struct cachefs_cookie *cookie,
- struct page *page,
- cachefs_rw_complete_t end_io_func,
- void *end_io_data,
- unsigned long gfp);
-
-The cookie argument must specify a data file cookie, the page specified should
-contain the data to be written (and is also used to specify the page number),
-and the gfp argument is used to control how any memory allocations made are
-satisfied.
-
-If the cookie indicates the inode is not cached then:
-
- (1) The function will return -ENOBUFS.
-
-Else if there's a block allocated on disc to hold this page:
-
- (1) The function will submit a request to write the data to the disc directly
- from the page specified.
-
- (2) The function will return 0.
-
- (3) When the write is complete:
-
- (a) Any associated validity journal entry will be cleared (the block now
- contains valid data as far as CacheFS is concerned).
-
- (b) end_io_func() will be invoked with:
-
- (*) The netfs data supplied when the cookie was created.
-
- (*) The page descriptor.
-
- (*) The data passed to the above function.
-
- (*) An argument that's 0 on success or negative for an error.
-
- If an error happens, it can be assumed that the page has been
- discarded from the cache.
-
-
-PAGE UNCACHING
---------------
-
-To uncache a page, this function should be called:
-
- void cachefs_uncache_page(struct cachefs_cookie *cookie,
- struct page *page);
-
-This detaches the page specified from the data file indicated by the cookie and
-unbinds it from the underlying block.
-
-Note that pages can't be explicitly detached from the a data file. The whole
-data file must be retired (see the relinquish cookie function below).
-
-Furthermore, note that this does not cancel the asynchronous read or write
-operation started by the read/alloc and write functions.
-
-
-INDEX AND DATA FILE UPDATE
---------------------------
-
-To request an update of the index data for an index or data file, the following
-function should be called:
-
- void cachefs_update_cookie(struct cachefs_cookie *cookie);
-
-This function will refer back to the netfs_data pointer stored in the cookie by
-the acquisition function to obtain the data to write into each revised index
-entry. The update method in the parent index definition will be called to
-transfer the data.
-
-
-INDEX AND DATA FILE UNREGISTRATION
-----------------------------------
-
-To get rid of a cookie, this function should be called.
-
- void cachefs_relinquish_cookie(struct cachefs_cookie *cookie,
- int retire);
-
-If retire is non-zero, then the index or file will be marked for recycling, and
-all copies of it will be removed from all active caches in which it is present.
-
-If retire is zero, then the inode may be available again next the the
-acquisition function is called.
-
-One very important note - relinquish must NOT be called unless all "child"
-indexes, files and pages have been relinquished first.
-
-
-PAGE TOKEN MANAGEMENT
----------------------
-
-As previously mentioned, the netfs must keep a token associated with each page
-currently actively backed by the cache. This is used by CacheFS to go from a
-page to the internal representation of the underlying block and back again. It
-is particularly important for managing the withdrawal of a cache whilst it is
-in active service (eg: it got unmounted).
-
-The token is this:
-
- struct cachefs_page {
- ...
- };
-
-Note that all fields are for internal CacheFS use only.
-
-The token only needs to be allocated when CacheFS asks for it. This it will do
-by calling the get_page_cookie() method in the netfs definition ops table. Once
-allocated, the same token should be presented every time the method is called
-again for a particular page.
-
-The token should be retained by the netfs, and should be deleted only after the
-page has been uncached.
-
-One way to achieve this is to attach the token to page->private (and set the
-PG_private bit on the page) once allocated. Shortcut routines are provided by
-CacheFS to do this. Firstly, to retrieve if present and allocate if not:
-
- struct cachefs_page *cachefs_page_get_private(struct page *page,
- unsigned gfp);
-
-Secondly to retrieve if present and BUG if not:
-
- static inline
- struct cachefs_page *cachefs_page_grab_private(struct page *page);
-
-To clean up the tokens, the netfs inode hosting the page should be provided
-with address space operations that circumvent the buffer-head operations for a
-page. For instance:
-
- struct address_space_operations afs_fs_aops = {
- ...
- .sync_page = block_sync_page,
- .set_page_dirty = __set_page_dirty_nobuffers,
- .releasepage = afs_file_releasepage,
- .invalidatepage = afs_file_invalidatepage,
- };
-
- static int afs_file_invalidatepage(struct page *page,
- unsigned long offset)
- {
- struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
- int ret = 1;
-
- BUG_ON(!PageLocked(page));
- if (!PagePrivate(page))
- return 1;
- cachefs_uncache_page(vnode->cache,page);
- if (offset == 0)
- return 1;
- BUG_ON(!PageLocked(page));
- if (PageWriteback(page))
- return 0;
- return page->mapping->a_ops->releasepage(page, 0);
- }
-
- static int afs_file_releasepage(struct page *page, int gfp_flags)
- {
- struct cachefs_page *token;
- struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
-
- if (PagePrivate(page)) {
- cachefs_uncache_page(vnode->cache, page);
- token = (struct cachefs_page *) page->private;
- page->private = 0;
- ClearPagePrivate(page);
- if (token)
- kfree(token);
- }
- return 0;
- }
-
-
-INDEX AND DATA FILE INVALIDATION
---------------------------------
-
-There is no direct way to invalidate an index subtree or a data file. To do
-this, the caller should relinquish and retire the cookie they have, and then
-acquire a new one.
diff -uNr linux-2.6.9-rc2-mm4/Documentation/filesystems/caching/backend-api.txt linux-2.6.9-rc2-mm4-fscache/Documentation/filesystems/caching/backend-api.txt
--- linux-2.6.9-rc2-mm4/Documentation/filesystems/caching/backend-api.txt 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/Documentation/filesystems/caching/backend-api.txt 2004-10-04 17:24:21.770274843 +0100
@@ -0,0 +1,313 @@
+ ==========================
+ FS-CACHE CACHE BACKEND API
+ ==========================
+
+The FS-Cache system provides an API by which actual caches can be supplied to
+FS-Cache for it to then serve out to network filesystems and other interested
+parties.:
+
+This API is declared in <linux/fscache-cache.h>.
+
+
+====================================
+INITIALISING AND REGISTERING A CACHE
+====================================
+
+To start off, a cache definition must be initialised and registered for each
+cache the backend wants to make available. For instance, CacheFS does this in
+the fill_super() operation on mounting.
+
+The cache definition (struct fscache_cache) should be initialised by calling:
+
+ void fscache_init_cache(struct fscache_cache *cache,
+ struct fscache_cache_ops *ops,
+ unsigned fsdef_ino,
+ const char *idfmt,
+ ...)
+
+Where:
+
+ (*) "cache" is a pointer to the cache definition;
+
+ (*) "ops" is a pointer to the table of operations that the backend supports on
+ this cache;
+
+ (*) "fsdef_ino" is the reference number of the FileSystem DEFinition index
+ (the top-level index), which in CacheFS is its inode number;
+
+ (*) and a format and printf-style arguments for constructing a label for the
+ cache.
+
+
+The cache should then be registered with FS-Cache by passing a pointer to the
+previously initialised cache definition to:
+
+ void fscache_add_cache(struct fscache_cache *cache)
+
+
+=====================
+UNREGISTERING A CACHE
+=====================
+
+A cache can be withdrawn from the system by calling this function with a
+pointer to the cache definition:
+
+ void fscache_withdraw_cache(struct fscache_cache *cache)
+
+In CacheFS's case, this is called by put_super().
+
+It is possible to check to see if a cache has been withdrawn by calling:
+
+ int fscache_is_cache_withdrawn(struct fscache_cache *cache)
+
+Which will return non-zero if it has been, zero if it is still active.
+
+
+==================
+FS-CACHE UTILITIES
+==================
+
+FS-Cache provides some utilities that a cache backend may make use of:
+
+ (*) Find parent of node.
+
+ struct fscache_node *fscache_find_parent_node(struct fscache_node *node)
+
+ This allows a backend to find the logical parent of an index or data file
+ in the cache hierarchy.
+
+ (*) Allocate a page token.
+
+ struct fscache_page *fscache_page_get_private(struct page *page,
+ unsigned gfp);
+
+ If the page has a page token attached, then this is returned by this
+ function. If it doesn't have one, then a page token is allocated with the
+ specified allocation flags and attached to the page's private value. The
+ error ENOMEM is returned if there's no memory available.
+
+ (*) Grab an existing page token.
+
+ struct fscache_page *fscache_page_grab_private(struct page *page)
+
+ This function returns a pointer to the page token attached to the page's
+ private value if it exists, and BUG's if it does not.
+
+
+========================
+RELEVANT DATA STRUCTURES
+========================
+
+ (*) Index/Data file FS-Cache representation cookie.
+
+ struct fscache_cookie {
+ struct fscache_index_def *idef;
+ struct fscache_netfs *netfs;
+ void *netfs_data;
+ ...
+ };
+
+ The fields that might be of use to the backend describe the index
+ definition (indexes only), the netfs definition and the netfs's data for
+ this cookie. The index definition contains a number of functions supplied
+ by the netfs for matching index entries; these are required to provide
+ some of the cache operations.
+
+ (*) Cached search result.
+
+ struct fscache_search_result {
+ unsigned ino;
+ ...
+ };
+
+ This is used by FS-Cache to keep track of what nodes it has found in what
+ caches. Some of the cache operations set the "cache node number" held
+ therein.
+
+ (*) In-cache node representation.
+
+ struct fscache_node {
+ struct fscache_cookie *cookie;
+ unsigned long flags;
+ #define FSCACHE_NODE_ISINDEX 0
+ ...
+ };
+
+ Each node contains a pointer to the cookie that represents the index or
+ data file it is backing. It also contains a flag that indicates whether
+ this is an index or not. This should be initialised by calling
+ fscache_node_init(node).
+
+ (*) Filesystem definition (FSDEF) index entry representation.
+
+ struct fscache_fsdef_index_entry {
+ uint8_t name[24]; /* name of netfs */
+ uint32_t version; /* version of layout */
+ };
+
+ This structure defines the layout of the data in the FSDEF index
+ maintained by the FS-Cache facility for distinguishing between the caches
+ for separate netfs's.
+
+
+================
+CACHE OPERATIONS
+================
+
+The cache backend provides FS-Cache with a table of operations that can be
+performed on the denizens of the cache. These are held in a structure of type
+
+ struct fscache_cache_ops
+
+ (*) Name of cache provider [mandatory].
+
+ const char *name
+
+ This isn't strictly an operation, but should be pointed at a string naming
+ the backend.
+
+ (*) Node lookup [mandatory].
+
+ struct fscache_node *(*lookup_node)(struct fscache_cache *cache,
+ unsigned ino)
+
+ This method is used to turn a logical cache node number into a handle on a
+ represention of that node.
+
+ (*) Increment node refcount [mandatory].
+
+ struct fscache_node *(*grab_node)(struct fscache_node *node)
+
+ This method is called to increment the reference count on a node. It may
+ fail (for instance if the cache is being withdrawn).
+
+ (*) Lock/Unlock node [mandatory].
+
+ void (*lock_node)(struct fscache_node *node)
+ void (*unlock_node)(struct fscache_node *node)
+
+ These methods are used to exclusively lock a node. It must be possible to
+ schedule with the lock held, so a spinlock isn't sufficient.
+
+ (*) Unreference node [mandatory].
+
+ void (*put_node)(struct fscache_node *node)
+
+ This method is used to discard a reference to a node. The node may be
+ destroyed when all the references held by FS-Cache are released.
+
+ (*) Search an index [mandatory].
+
+ int (*index_search)(struct fscache_node *index,
+ struct fscache_cookie *cookie,
+ struct fscache_search_result *result)
+
+ This method is called to search an index for a node that matches the
+ criteria attached to the cookie (cookie->netfs_data). This should be
+ matched by calling index->cookie->idef->match().
+
+ The cache backend is responsible for dealing with the match result,
+ including updating or discarding existing index entries. An index entry
+ can be updated by calling index->cookie->idef->update().
+
+ If the search is successful, the node number should be stored in
+ result->ino and zero returned. If not successful, error ENOENT should be
+ returned if no entry was found, or some other error otherwise.
+
+ (*) Create a new node [mandatory].
+
+ int (*index_add)(struct fscache_node *index,
+ struct fscache_cookie *cookie,
+ struct fscache_search_result *result)
+
+ This method is called to create a new node on disc and add an entry for it
+ to the specified index. The index entry for the new node should be
+ obtained by calling index->cookie->idef->update() and passing it the
+ argument cookie.
+
+ If successful, the node number should be stored in result->ino and zero
+ should be returned.
+
+ (*) Update a node [mandatory].
+
+ int (*index_update)(struct fscache_node *index,
+ struct fscache_node *node)
+
+ This is called to update the on-disc index entry for the specified
+ node. The new information should be in node->cookie->netfs_data. This can
+ be obtained by calling index->cookie->idef->update() and passing it
+ node->cookie.
+
+ (*) Synchronise a cache to disc [mandatory].
+
+ void (*sync)(struct fscache_cache *cache)
+
+ This is called to ask the backend to synchronise a cache with disc.
+
+ (*) Dissociate a cache [mandatory].
+
+ void (*dissociate_pages)(struct fscache_cache *cache)
+
+ This is called to ask the cache to dissociate all netfs pages from
+ mappings to disc. It is assumed that the backend cache will have some way
+ of finding all the page tokens that refer to its own blocks.
+
+ (*) Request page be read from cache [mandatory].
+
+ int (*read_or_alloc_page)(struct fscache_node *node,
+ struct page *page,
+ struct fscache_page *pageio,
+ fscache_rw_complete_t end_io_func,
+ void *end_io_data,
+ unsigned long gfp)
+
+ This is called to attempt to read a netfs page from disc, or to allocate a
+ backing block if not. FS-Cache will have done as much checking as it can
+ before calling, but most of the work belongs to the backend.
+
+ If there's no page on disc, then -ENODATA should be returned if the
+ backend managed to allocate a backing block; -ENOBUFS or -ENOMEM if it
+ didn't.
+
+ If there is a page on disc, then a read operation should be queued and 0
+ returned. When the read finishes, end_io_func() should be called with the
+ following arguments:
+
+ (*end_io_func)(node->cookie->netfs_data,
+ page,
+ end_io_data,
+ error);
+
+ (*) Request page be written to cache [mandatory].
+
+ int (*write_page)(struct fscache_node *node,
+ struct page *page,
+ struct fscache_page *pageio,
+ fscache_rw_complete_t end_io_func,
+ void *end_io_data,
+ unsigned long gfp)
+
+ This is called to write from a page on which there was a previously
+ successful read_or_alloc_page() call. FS-Cache filters out pages that
+ don't have mappings.
+
+ If there's no block on disc available, then -ENOBUFS should be returned
+ (or -ENOMEM if there wasn't any memory to be had).
+
+ If the write operation could be queued, then 0 should be returned. When
+ the write completes, end_io_func() should be called with the following
+ arguments:
+
+ (*end_io_func)(node->cookie->netfs_data,
+ page,
+ end_io_data,
+ error);
+
+ (*) Discard mapping [mandatory].
+
+ void (*uncache_page)(struct fscache_node *node,
+ struct fscache_page *page_token)
+
+ This is called when a page is being booted from the pagecache. The cache
+ backend needs to break the links between the page token and whatever
+ internal representations it maintains.
diff -uNr linux-2.6.9-rc2-mm4/Documentation/filesystems/caching/cachefs.txt linux-2.6.9-rc2-mm4-fscache/Documentation/filesystems/caching/cachefs.txt
--- linux-2.6.9-rc2-mm4/Documentation/filesystems/caching/cachefs.txt 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/Documentation/filesystems/caching/cachefs.txt 2004-10-04 17:25:36.878085706 +0100
@@ -0,0 +1,274 @@
+ ===========================
+ CacheFS: Caching Filesystem
+ ===========================
+
+========
+OVERVIEW
+========
+
+CacheFS is a backend for the general filesystem cache facility.
+
+CacheFS uses a block device directly rather than a bunch of files under an
+already mounted filesystem. For why this is so, see further on. If necessary,
+however, a file can be loopback mounted as a cache.
+
+
+CacheFS provides the following facilities:
+
+ (1) More than one block device can be mounted as a cache.
+
+ (2) Caches can be mounted / unmounted at any time.
+
+ (3) All metadata modifications (this includes index contents) are performed
+ as journalled transactions. These are replayed on mounting.
+
+
+=============================================
+WHY A BLOCK DEVICE? WHY NOT A BUNCH OF FILES?
+=============================================
+
+CacheFS is backed by a block device rather than being backed by a bunch of
+files on a filesystem. This confers several advantages:
+
+ (1) Performance.
+
+ Going directly to a block device means that we can DMA directly to/from
+ the the netfs's pages. If another filesystem was managing the backing
+ store, everything would have to be copied between pages. Whilst DirectIO
+ does exist, it doesn't appear easy to make use of in this situation.
+
+ New address space or file operations could be added to make it possible to
+ persuade a backing discfs to generate block I/O directly to/from disc
+ blocks under its control, but that then means the discfs has to keep track
+ of I/O requests to pages not under its control.
+
+ Furthermore, we only have to do one lot of readahead calculations, not
+ two; in the discfs backing case, the netfs would do one and the discfs
+ would do one.
+
+ (2) Memory.
+
+ Using a block device means that we have a lower memory usage - all data
+ pages belong to the netfs we're backing. If we used a filesystem, we would
+ have twice as many pages at certain points - one from the netfs and one
+ from the backing discfs. In the backing discfs model, under situations of
+ memory pressure, we'd have to allocate or keep around a discfs page to be
+ able to write out a netfs page; or else we'd need to be able to punch a
+ hole in the backing file.
+
+ Furthermore, whilst we have to keep a CacheFS inode around in memory for
+ every netfs inode we're backing, a backing discfs would have to keep the
+ dentry and possibly a file struct too.
+
+ (3) Holes.
+
+ The cache uses holes to indicate to the netfs that it hasn't yet
+ downloaded the data for that page.
+
+ Since CacheFS is its own filesystem, it can support holes in files
+ trivially. Running on top of another discfs would limit us to using ones
+ that can support holes.
+
+ Furthermore, it would have to be made possible to detect holes in a discfs
+ file, rather than just seeing zero filled blocks.
+
+ (4) Data Consistency.
+
+ Cachefs uses a pair of journals to keep track of the state of the cache
+ and all the pages contained therein. This means that it doesn't get into
+ an inconsistent state in the on-disc cache and it doesn't lose disc space.
+
+ CacheFS takes especial care between the allocation of a block and its
+ splicing into the on-disc pointer tree, and the data having been written
+ to disc. If power is interrupted and then restored, the journals are
+ replayed and if it is seen that a block was allocated but not written it
+ is then punched out. Being backed by a discfs, I'm not certain what will
+ happen. It may well be possible to mark a discfs's journal, if it has one,
+ but how does the discfs deal with those marks? This also limits consistent
+ caching to running on journalled discfs's where there's a function to
+ write extraordinary marks into the journal.
+
+ The alternative would be to keep flags in the superblock, and to
+ re-initialise the cache if it wasn't cleanly unmounted.
+
+ Knowing that your cache is in a good state is vitally important if you,
+ say, put /usr on AFS. Some organisations put everything barring /etc,
+ /sbin, /lib and /var on AFS and have an enormous cache on every
+ computer. Imagine if the power goes out and renders every cache
+ inconsistent, requiring all the computers to re-initialise their caches
+ when the power comes back on...
+
+ (5) Recycling.
+
+ Recycling is simple on CacheFS. It can just scan the metadata index to
+ look for inodes that require reclamation/recycling; and it can also build
+ up a list of the least recently used inodes so that they can be reclaimed
+ later to make space.
+
+ Doing this on a discfs would require a search going down through a nest
+ of directories, and would probably have to be done in userspace.
+
+ (6) Disc Space.
+
+ Whilst the block device does set a hard ceiling on the amount of space
+ available, CacheFS can guarantee that all that space will be available to
+ the cache. On a discfs-backed cache, the administrator would probably want
+ to set a cache size limit, but the system wouldn't be able guarantee that
+ all that space would be available to the cache - not unless that cache was
+ on a partition of its own.
+
+ Furthermore, with a discfs-backed cache, if the recycler starts to reclaim
+ cache files to make space, the freed blocks may just be eaten directly by
+ userspace programs, potentially resulting in the entire cache being
+ consumed. Alternatively, netfs operations may end up being held up because
+ the cache can't get blocks on which to store the data.
+
+ (7) Users.
+
+ Users can't so easily go into CacheFS and run amok. The worst they can do
+ is cause bits of the cache to be recycled early. With a discfs-backed
+ cache, they can do all sorts of bad things to the files belonging to the
+ cache, and they can do this quite by accident.
+
+
+On the other hand, there would be some advantages to using a file-based cache
+rather than a blockdev-based cache:
+
+ (1) Having to copy to a discfs's page would mean that a netfs could just make
+ the copy and then assume its own page is ready to go.
+
+ (2) Backing onto a discfs wouldn't require a committed block device. You would
+ just nominate a directory and go from there. With CacheFS you have to
+ repartition or install an extra drive to make use of it in an existing
+ system (though the loopback device offers a way out).
+
+ (3) CacheFS requires the netfs to store a key in any pertinent index entry,
+ and it also permits a limited amount arbitrary data to be stored there.
+
+ A discfs could be requested to store the netfs's data in xattrs, and the
+ filename could be used to store the key, though the key would have to be
+ rendered as text not binary. Likewise indexes could be rendered as
+ directories with xattrs.
+
+ (4) You could easily make your cache bigger if the discfs has plenty of space,
+ you could even go across multiple mountpoints.
+
+
+======================
+GENERAL ON-DISC LAYOUT
+======================
+
+The filesystem is divided into a number of parts:
+
+ 0 +---------------------------+
+ | Superblock |
+ 1 +---------------------------+
+ | Update Journal |
+ +---------------------------+
+ | Validity Journal |
+ +---------------------------+
+ | Write-Back Journal |
+ +---------------------------+
+ | |
+ | Data |
+ | |
+ END +---------------------------+
+
+The superblock contains the filesystem ID tags and pointers to all the other
+regions.
+
+The update journal consists of a set of entries of sector size that keep track
+of what changes have been made to the on-disc filesystem, but not yet
+committed.
+
+The validity journal contains records of data blocks that have been allocated
+but not yet written. Upon journal replay, all these blocks will be detached
+from their pointers and recycled.
+
+The writeback journal keeps track of changes that have been made locally to
+data blocks, but that have not yet been committed back to the server. This is
+not yet implemented.
+
+The journals are replayed upon mounting to make sure that the cache is in a
+reasonable state.
+
+The data region holds a number of things:
+
+ (1) Index Files
+
+ These are files of entries used by CacheFS internally and by filesystems
+ that wish to cache data here (such as AFS) to keep track of what's in
+ the cache at any given time.
+
+ The first index file (inode 1) is special. It holds the CacheFS-specific
+ metadata for every file in the cache (including direct, single-indirect
+ and double-indirect block pointers).
+
+ The second index file (inode 2) is also special. It has an entry for
+ each filesystem that's currently holding data in this cache.
+
+ Every allocated entry in an index has an inode bound to it. This inode is
+ either another index file or it is a data file.
+
+ (2) Cached Data Files
+
+ These are caches of files from remote servers. Holes in these files
+ represent blocks not yet obtained from the server.
+
+ (3) Indirection Blocks
+
+ Should a file have more blocks than can be pointed to by the few
+ pointers in its storage management record, then indirection blocks will
+ be used to point to further data or indirection blocks.
+
+ Three levels of indirection are currently supported:
+
+ - single indirection
+ - double indirection
+
+ (4) Allocation Nodes and Free Blocks
+
+ The free blocks of the filesystem are kept in two single-branched
+ "trees". One tree is the blocks that are ready to be allocated, and the
+ other is the blocks that have just been recycled. When the former tree
+ becomes empty, the latter tree is decanted across.
+
+ Each tree is arranged as a chain of "nodes", each node points to the next
+ node in the chain (unless it's at the end) and also up to 1022 free
+ blocks.
+
+Note that all blocks are PAGE_SIZE in size. The blocks are numbered starting
+with the superblock at 0. Using 32-bit block pointers, a maximum number of
+0xffffffff blocks can be accessed, meaning that the maximum cache size is ~16TB
+for 4KB pages.
+
+
+========
+MOUNTING
+========
+
+Since CacheFS is actually a quasi-filesystem, it requires a block device behind
+it. The way to give it one is to mount it as cachefs type on a directory
+somewhere. The mounted filesystem will then present the user with a set of
+directories outlining the index structure resident in the cache. Indexes
+(directories) and files can be turfed out of the cache by the sysadmin through
+the use of rmdir and unlink.
+
+For instance, if a cache contains AFS data, the user might see the following:
+
+ root>mount -t cachefs /dev/hdg9 /cache-hdg9
+ root>ls -1 /cache-hdg9
+ afs
+ root>ls -1 /cache-hdg9/afs
+ cambridge.redhat.com
+ root>ls -1 /cache-hdg9/afs/cambridge.redhat.com
+ root.afs
+ root.cell
+
+However, a block device that's going to be used for a cache must be prepared
+before it can be mounted initially. This is done very simply by:
+
+ echo "cachefs___" >/dev/hdg9
+
+During the initial mount, the basic structure will be scribed into the cache,
+and then a background thread will "recycle" the as-yet unused data blocks.
diff -uNr linux-2.6.9-rc2-mm4/Documentation/filesystems/caching/fscache.txt linux-2.6.9-rc2-mm4-fscache/Documentation/filesystems/caching/fscache.txt
--- linux-2.6.9-rc2-mm4/Documentation/filesystems/caching/fscache.txt 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/Documentation/filesystems/caching/fscache.txt 2004-10-04 15:49:44.108031173 +0100
@@ -0,0 +1,74 @@
+ ==========================
+ General Filesystem Caching
+ ==========================
+
+========
+OVERVIEW
+========
+
+This facility is a general purpose cache for network filesystems, though it
+could be used for caching other things such as ISO9660 filesystems too.
+
+FS-Cache mediates between cache backends (such as CacheFS) and network
+filesystems. FS-Cache does not follow the idea of completely loading every
+netfs file opened in its entirety into a cache before permitting it to be
+accessed and then serving the pages out of that cache rather than the netfs
+inode because:
+
+ (1) It must be practical to operate without a cache.
+
+ (2) The size of any accessible file must not be limited to the size of the
+ cache.
+
+ (3) The combined size of all opened files (this includes mapped libraries)
+ must not be limited to the size of the cache.
+
+ (4) The user should not be forced to download an entire file just to do a
+ one-off access of a small portion of it (such as might be done with the
+ "file" program).
+
+It instead serves the cache out in PAGE_SIZE chunks as and when requested by
+the netfs('s) using it.
+
+
+FS-Cache provides the following facilities:
+
+ (1) More than one cache can be used at once.
+
+ (2) Caches can be added / removed at any time.
+
+ (3) The netfs is provided with an interface that allows either party to
+ withdraw caching facilities from a file (required for (2)).
+
+ (4) The interface to the netfs returns as few errors as possible, preferring
+ rather to let the netfs remain oblivious.
+
+ (5) Cookies are used to represent files and indexes to the netfs. The simplest
+ cookie is just a NULL pointer - indicating nothing cached there.
+
+ (6) The netfs is allowed to propose - dynamically - any index hierarchy it
+ desires, though it must be aware that the index search function is
+ recursive and stack space is limited.
+
+ (7) Data I/O is done direct to and from the netfs's pages. The netfs indicates
+ that page A is at index B of the data-file represented by cookie C, and
+ that it should be read or written. The cache backend may or may not start
+ I/O on that page, but if it does, a netfs callback will be invoked to
+ indicate completion. The I/O may be either synchronous or asynchronous.
+
+ (8) Cookies can be "retired" upon release. At this point FS-Cache will mark
+ them as obsolete and the index hierarchy rooted at that point will get
+ recycled.
+
+ (9) The netfs provides a "match" function for index searches. In addition to
+ saying whether a match was made or not, this can also specify that an
+ entry should be updated or deleted.
+
+
+The netfs API to FS-Cache can be found in:
+
+ Documentation/filesystems/caching/netfs-api.txt
+
+The cache backend API to FS-Cache can be found in:
+
+ Documentation/filesystems/caching/backend-api.txt
diff -uNr linux-2.6.9-rc2-mm4/Documentation/filesystems/caching/netfs-api.txt linux-2.6.9-rc2-mm4-fscache/Documentation/filesystems/caching/netfs-api.txt
--- linux-2.6.9-rc2-mm4/Documentation/filesystems/caching/netfs-api.txt 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/Documentation/filesystems/caching/netfs-api.txt 2004-10-04 16:50:10.373849277 +0100
@@ -0,0 +1,575 @@
+ ===============================
+ FS-CACHE NETWORK FILESYSTEM API
+ ===============================
+
+There's an API by which a network filesystem can make use of the FS-Cache
+facilities. This is based around a number of principles:
+
+ (1) Every file and index is represented by a cookie. This cookie may or may
+ not have anything associated with it, but the netfs doesn't need to care.
+
+ (2) Barring the top-level index (one entry per cached netfs), the index
+ hierarchy for each netfs is structured according the whim of the netfs.
+
+ (3) Any netfs page being backed by the cache must have a small token
+ associated with it (possibly pointed to by page->private) so that FS-Cache
+ can keep track of it.
+
+This API is declared in <linux/fscache.h>.
+
+
+=============================
+NETWORK FILESYSTEM DEFINITION
+=============================
+
+FS-Cache needs a description of the network filesystem. This is specified using
+a record of the following structure:
+
+ struct fscache_netfs {
+ const char *name;
+ unsigned version;
+ struct fscache_netfs_operations *ops;
+ struct fscache_cookie *primary_index;
+ ...
+ };
+
+This first three fields should be filled in before registration, and the fourth
+will be filled in by the registration function; any other fields should just be
+ignored and are for internal use only.
+
+The fields are:
+
+ (1) The name of the netfs (used as the key in the toplevel index).
+
+ (2) The version of the netfs (if the name matches but the version doesn't, the
+ entire on-disc hierarchy for this netfs will be scrapped and begun
+ afresh).
+
+ (3) The operations table is defined as follows:
+
+ struct fscache_netfs_operations {
+ struct fscache_page *(*get_page_cookie)(struct page *page);
+ };
+
+ The functions here must all be present. Currently the only one is:
+
+ (a) get_page_token(): Get the token used to bind a page to a block in a
+ cache. This function should allocate it if it doesn't exist.
+
+ Return -ENOMEM if there's not enough memory and -ENODATA if the page
+ just shouldn't be cached.
+
+ Set *_page_token to point to the token and return 0 if there is now a
+ token. Note that the netfs must keep track of the token itself (and
+ free it later). page->private can be used for this (see below).
+
+ (4) The cookie representing the primary index will be allocated according to
+ another parameter passed into the registration function.
+
+For example, kAFS (linux/fs/afs/) uses the following definitions to describe
+itself:
+
+ static struct fscache_netfs_operations afs_cache_ops = {
+ .get_page_token = afs_cache_get_page_token,
+ };
+
+ struct fscache_netfs afs_cache_netfs = {
+ .name = "afs",
+ .version = 0,
+ .ops = &afs_cache_ops,
+ };
+
+
+================
+INDEX DEFINITION
+================
+
+Indexes are used for two purposes:
+
+ (1) To speed up the finding of a file based on a series of keys (such as AFS's
+ "cell", "volume ID", "vnode ID").
+
+ (2) To make it easier to discard a subset of all the files cached based around
+ a particular key - for instance to mirror the removal of an AFS volume.
+
+However, since it's unlikely that any two netfs's are going to want to define
+their index hierarchies in quite the same way, FS-Cache tries to impose as few
+restraints as possible on how an index is structured and where it is placed in
+the tree. The netfs can even mix indexes and data files at the same level, but
+it's not recommended.
+
+There are some limits on indexes:
+
+ (1) All entries in any given index must be the same size. The netfs supplies a
+ blob of data for each index entry.
+
+ (2) The entries in one index can be of a different size to the entries in
+ another index.
+
+ (3) The entry data must be atomically journallable, so it is limited to 400
+ bytes at present.
+
+ (4) The index data must start with the key. The layout of the key is described
+ in the index definition, and this is used to display the key in some
+ appropriate way.
+
+ (5) The depth of the index tree should be judged with care as the search
+ function is recursive. Too many layers will run the kernel out of stack.
+
+To define an index, a structure of the following type should be filled out:
+
+ struct fscache_index_def
+ {
+ uint8_t name[8];
+ uint16_t data_size;
+ struct {
+ uint8_t type;
+ uint16_t len;
+ } keys[4];
+
+ fscache_match_val_t (*match)(void *target_netfs_data,
+ const void *entry);
+
+ void (*update)(void *source_netfs_data, void *entry);
+ };
+
+This has the following fields:
+
+ (1) The name of the index (NUL terminated unless all 8 chars are used).
+
+ (2) The size of the data blob provided by the netfs.
+
+ (3) A definition of the key(s) at the beginning of the blob. The netfs is
+ permitted to specify up to four keys. The total length must not exceed the
+ data size. It is assumed that the keys will be laid end to end in order,
+ starting at the first byte of the data.
+
+ The type field specifies the way the data should be displayed. It can be
+ one of:
+
+ (*) FSCACHE_INDEX_KEYS_NOTUSED - key field not used
+ (*) FSCACHE_INDEX_KEYS_BIN - display byte-by-byte in hex
+ (*) FSCACHE_INDEX_KEYS_ASCIIZ - NUL-terminated ASCII
+ (*) FSCACHE_INDEX_KEYS_IPV4ADDR - display as IPv4 address
+ (*) FSCACHE_INDEX_KEYS_IPV6ADDR - display as IPv6 address
+
+ (4) A function to compare an in-page-cache index entry blob with the data
+ passed to the cookie acquisition function. This function can also be used
+ to extract data from the blob and copy it into the netfs's structures.
+
+ The values this function can return are:
+
+ (*) FSCACHE_MATCH_FAILED - failed to match
+ (*) FSCACHE_MATCH_SUCCESS - successful match
+ (*) FSCACHE_MATCH_SUCCESS_UPDATE - successful match, entry needs update
+ (*) FSCACHE_MATCH_SUCCESS_DELETE - entry should be deleted
+
+ For example, in linux/fs/afs/vnode.c:
+
+ static fscache_match_val_t
+ afs_vnode_cache_match(void *target, const void *entry)
+ {
+ const struct afs_cache_vnode *cvnode = entry;
+ struct afs_vnode *vnode = target;
+
+ if (vnode->fid.vnode != cvnode->vnode_id)
+ return FSCACHE_MATCH_FAILED;
+
+ if (vnode->fid.unique != cvnode->vnode_unique ||
+ vnode->status.version != cvnode->data_version)
+ return FSCACHE_MATCH_SUCCESS_DELETE;
+
+ return FSCACHE_MATCH_SUCCESS;
+ }
+
+ (5) A function to initialise or update an in-page-cache index entry blob from
+ netfs data passed to FS-Cache by the netfs. This function should not assume
+ that there's any data yet in the in-page-cache.
+
+ Continuing the above example:
+
+ static void afs_vnode_cache_update(void *source, void *entry)
+ {
+ struct afs_cache_vnode *cvnode = entry;
+ struct afs_vnode *vnode = source;
+
+ cvnode->vnode_id = vnode->fid.vnode;
+ cvnode->vnode_unique = vnode->fid.unique;
+ cvnode->data_version = vnode->status.version;
+ }
+
+To finish the above example, the index definition for the "vnode" level is as
+follows:
+
+ struct fscache_index_def afs_vnode_cache_index_def = {
+ .name = "vnode",
+ .data_size = sizeof(struct afs_cache_vnode),
+ .keys[0] = { FSCACHE_INDEX_KEYS_BIN, 4 },
+ .match = afs_vnode_cache_match,
+ .update = afs_vnode_cache_update,
+ };
+
+The first element of struct afs_cache_vnode is the vnode ID.
+
+And for contrast, the cell index definition is:
+
+ struct fscache_index_def afs_cache_cell_index_def = {
+ .name = "cell_ix",
+ .data_size = sizeof(struct afs_cell),
+ .keys[0] = { FSCACHE_INDEX_KEYS_ASCIIZ, 64 },
+ .match = afs_cell_cache_match,
+ .update = afs_cell_cache_update,
+ };
+
+The cell index is the primary index for kAFS.
+
+
+===================================
+NETWORK FILESYSTEM (UN)REGISTRATION
+===================================
+
+The first step is to declare the network filesystem to the cache. This also
+involves specifying the layout of the primary index (for AFS, this would be the
+"cell" level).
+
+The registration function is:
+
+ int fscache_register_netfs(struct fscache_netfs *netfs,
+ struct fscache_index_def *primary_idef);
+
+It just takes pointers to the netfs definition and the primary index
+definition. It returns 0 or an error as appropriate.
+
+For kAFS, registration is done as follows:
+
+ ret = fscache_register_netfs(&afs_cache_netfs,
+ &afs_cache_cell_index_def);
+
+The last step is, of course, unregistration:
+
+ void fscache_unregister_netfs(struct fscache_netfs *netfs);
+
+
+==================
+INDEX REGISTRATION
+==================
+
+The second step is to inform FS-Cache about part of an index hierarchy that can
+be used to locate files. This is done by requesting a cookie for each index in
+the path to the file:
+
+ struct fscache_cookie *
+ fscache_acquire_cookie(struct fscache_cookie *iparent,
+ struct fscache_index_def *idef,
+ void *netfs_data);
+
+This function creates an index entry in the index represented by iparent,
+loading the associated blob by calling iparent's update method with the
+supplied netfs_data.
+
+It also creates a new index inode, formatted according to the definition
+supplied in idef. The new cookie is then returned in *_cookie.
+
+Note that this function never returns an error - all errors are handled
+internally. It may also return FSCACHE_NEGATIVE_COOKIE. It is quite acceptable
+to pass this token back to this function as iparent (or even to the relinquish
+cookie, read page and write page functions - see below).
+
+Note also that no indexes are actually created on disc until a data file needs
+to be created somewhere down the hierarchy. Furthermore, an index may be
+created in several different caches independently at different times. This is
+all handled transparently, and the netfs doesn't see any of it.
+
+For example, with AFS, a cell would be added to the primary index. This index
+entry would have a dependent inode containing a volume location index for the
+volume mappings within this cell:
+
+ cell->cache =
+ fscache_acquire_cookie(afs_cache_netfs.primary_index,
+ &afs_vlocation_cache_index_def,
+ cell);
+
+Then when a volume location was accessed, it would be entered into the cell's
+index and an inode would be allocated that acts as a volume type and hash chain
+combination:
+
+ vlocation->cache =
+ fscache_acquire_cookie(cell->cache,
+ &afs_volume_cache_index_def,
+ vlocation);
+
+And then a particular flavour of volume (R/O for example) could be added to
+that index, creating another index for vnodes (AFS inode equivalents):
+
+ volume->cache =
+ fscache_acquire_cookie(vlocation->cache,
+ &afs_vnode_cache_index_def,
+ volume);
+
+
+======================
+DATA FILE REGISTRATION
+======================
+
+The third step is to request a data file be created in the cache. This is
+almost identical to index cookie acquisition. The only difference is that a
+NULL index definition is passed.
+
+ vnode->cache =
+ fscache_acquire_cookie(volume->cache,
+ NULL,
+ vnode);
+
+
+=====================
+PAGE ALLOC/READ/WRITE
+=====================
+
+And the fourth step is to propose a page be cached. There are two functions
+that are used to do this.
+
+Firstly, the netfs should ask FS-Cache to examine the caches and read the
+contents cached for a particular page of a particular file if present, or else
+allocate space to store the contents if not:
+
+ typedef
+ void (*fscache_rw_complete_t)(void *cookie_data,
+ struct page *page,
+ void *end_io_data,
+ int error);
+
+ int fscache_read_or_alloc_page(struct fscache_cookie *cookie,
+ struct page *page,
+ fscache_rw_complete_t end_io_func,
+ void *end_io_data,
+ unsigned long gfp);
+
+The cookie argument must specify a data file cookie, the page specified will
+have the data loaded into it (and is also used to specify the page number), and
+the gfp argument is used to control how any memory allocations made are satisfied.
+
+If the cookie indicates the inode is not cached:
+
+ (1) The function will return -ENOBUFS.
+
+Else if there's a copy of the page resident on disc:
+
+ (1) The function will submit a request to read the data off the disc directly
+ into the page specified.
+
+ (2) The function will return 0.
+
+ (3) When the read is complete, end_io_func() will be invoked with:
+
+ (*) The netfs data supplied when the cookie was created.
+
+ (*) The page descriptor.
+
+ (*) The data passed to the above function.
+
+ (*) An argument that's 0 on success or negative for an error.
+
+ If an error occurs, it should be assumed that the page contains no usable
+ data.
+
+Otherwise, if there's not a copy available on disc:
+
+ (1) A block may be allocated in the cache and attached to the inode at the
+ appropriate place.
+
+ (2) The validity journal will be marked to indicate this page does not yet
+ contain valid data.
+
+ (3) The function will return -ENODATA.
+
+
+Secondly, if the netfs changes the contents of the page (either due to an
+initial download or if a user performs a write), then the page should be
+written back to the cache:
+
+ int fscache_write_page(struct fscache_cookie *cookie,
+ struct page *page,
+ fscache_rw_complete_t end_io_func,
+ void *end_io_data,
+ unsigned long gfp);
+
+The cookie argument must specify a data file cookie, the page specified should
+contain the data to be written (and is also used to specify the page number),
+and the gfp argument is used to control how any memory allocations made are
+satisfied.
+
+If the cookie indicates the inode is not cached then:
+
+ (1) The function will return -ENOBUFS.
+
+Else if there's a block allocated on disc to hold this page:
+
+ (1) The function will submit a request to write the data to the disc directly
+ from the page specified.
+
+ (2) The function will return 0.
+
+ (3) When the write is complete:
+
+ (a) Any associated validity journal entry will be cleared (the block now
+ contains valid data as far as FS-Cache is concerned).
+
+ (b) end_io_func() will be invoked with:
+
+ (*) The netfs data supplied when the cookie was created.
+
+ (*) The page descriptor.
+
+ (*) The data passed to the above function.
+
+ (*) An argument that's 0 on success or negative for an error.
+
+ If an error happens, it can be assumed that the page has been
+ discarded from the cache.
+
+
+==============
+PAGE UNCACHING
+==============
+
+To uncache a page, this function should be called:
+
+ void fscache_uncache_page(struct fscache_cookie *cookie,
+ struct page *page);
+
+This detaches the page specified from the data file indicated by the cookie and
+unbinds it from the underlying block.
+
+Note that pages can't be explicitly detached from the a data file. The whole
+data file must be retired (see the relinquish cookie function below).
+
+Furthermore, note that this does not cancel the asynchronous read or write
+operation started by the read/alloc and write functions.
+
+
+==========================
+INDEX AND DATA FILE UPDATE
+==========================
+
+To request an update of the index data for an index or data file, the following
+function should be called:
+
+ void fscache_update_cookie(struct fscache_cookie *cookie);
+
+This function will refer back to the netfs_data pointer stored in the cookie by
+the acquisition function to obtain the data to write into each revised index
+entry. The update method in the parent index definition will be called to
+transfer the data.
+
+
+==================================
+INDEX AND DATA FILE UNREGISTRATION
+==================================
+
+To get rid of a cookie, this function should be called.
+
+ void fscache_relinquish_cookie(struct fscache_cookie *cookie,
+ int retire);
+
+If retire is non-zero, then the index or file will be marked for recycling, and
+all copies of it will be removed from all active caches in which it is present.
+
+If retire is zero, then the inode may be available again next the the
+acquisition function is called.
+
+One very important note - relinquish must NOT be called unless all "child"
+indexes, files and pages have been relinquished first.
+
+
+=====================
+PAGE TOKEN MANAGEMENT
+=====================
+
+As previously mentioned, the netfs must keep a token associated with each page
+currently actively backed by the cache. This is used by FS-Cache to go from a
+page to the internal representation of the underlying block and back again. It
+is particularly important for managing the withdrawal of a cache whilst it is
+in active service (eg: it got unmounted).
+
+The token is this:
+
+ struct fscache_page {
+ ...
+ };
+
+Note that all fields are for internal FS-Cache use only.
+
+The token only needs to be allocated when FS-Cache asks for it. This it will do
+by calling the get_page_cookie() method in the netfs definition ops table. Once
+allocated, the same token should be presented every time the method is called
+again for a particular page.
+
+The token should be retained by the netfs, and should be deleted only after the
+page has been uncached.
+
+One way to achieve this is to attach the token to page->private (and set the
+PG_private bit on the page) once allocated. Shortcut routines are provided by
+FS-Cache to do this. Firstly, to retrieve if present and allocate if not:
+
+ struct fscache_page *fscache_page_get_private(struct page *page,
+ unsigned gfp);
+
+Secondly to retrieve if present and BUG if not:
+
+ static inline
+ struct fscache_page *fscache_page_grab_private(struct page *page);
+
+To clean up the tokens, the netfs inode hosting the page should be provided
+with address space operations that circumvent the buffer-head operations for a
+page. For instance:
+
+ struct address_space_operations afs_fs_aops = {
+ ...
+ .sync_page = block_sync_page,
+ .set_page_dirty = __set_page_dirty_nobuffers,
+ .releasepage = afs_file_releasepage,
+ .invalidatepage = afs_file_invalidatepage,
+ };
+
+ static int afs_file_invalidatepage(struct page *page,
+ unsigned long offset)
+ {
+ struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
+ int ret = 1;
+
+ BUG_ON(!PageLocked(page));
+ if (!PagePrivate(page))
+ return 1;
+ fscache_uncache_page(vnode->cache,page);
+ if (offset == 0)
+ return 1;
+ BUG_ON(!PageLocked(page));
+ if (PageWriteback(page))
+ return 0;
+ return page->mapping->a_ops->releasepage(page, 0);
+ }
+
+ static int afs_file_releasepage(struct page *page, int gfp_flags)
+ {
+ struct fscache_page *token;
+ struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
+
+ if (PagePrivate(page)) {
+ fscache_uncache_page(vnode->cache, page);
+ token = (struct fscache_page *) page->private;
+ page->private = 0;
+ ClearPagePrivate(page);
+ if (token)
+ kfree(token);
+ }
+ return 0;
+ }
+
+
+================================
+INDEX AND DATA FILE INVALIDATION
+================================
+
+There is no direct way to invalidate an index subtree or a data file. To do
+this, the caller should relinquish and retire the cookie they have, and then
+acquire a new one.
diff -uNr linux-2.6.9-rc2-mm4/fs/cachefs/block.c linux-2.6.9-rc2-mm4-fscache/fs/cachefs/block.c
--- linux-2.6.9-rc2-mm4/fs/cachefs/block.c 2004-09-27 11:23:55.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/cachefs/block.c 2004-09-30 20:08:55.000000000 +0100
@@ -40,12 +40,12 @@
*/
static int cachefs_block_dummy_filler(void *data, struct page *page)
{
- struct cachefs_page *pageio;
+ struct fscache_page *pageio;
_enter("%p,{%lu}", data, page->index);
/* we need somewhere to note journal ACKs that need to be made */
- pageio = cachefs_page_get_private(page, GFP_KERNEL);
+ pageio = fscache_page_get_private(page, GFP_KERNEL);
if (IS_ERR(pageio))
return PTR_ERR(pageio);
@@ -67,7 +67,7 @@
int cachefs_block_set(struct cachefs_super *super,
struct cachefs_block *block,
struct page *page,
- struct cachefs_page *pageio)
+ struct fscache_page *pageio)
{
DECLARE_WAITQUEUE(myself,current);
@@ -137,7 +137,7 @@
int cachefs_block_set2(struct cachefs_super *super,
cachefs_blockix_t bix,
struct page *page,
- struct cachefs_page *pageio,
+ struct fscache_page *pageio,
struct cachefs_block **_block)
{
struct cachefs_block *block;
@@ -369,7 +369,7 @@
/* duplicate the page if it's flagged copy-on-write */
if (test_bit(CACHEFS_BLOCK_COW, &block->flags)) {
- struct cachefs_page *newpageio;
+ struct fscache_page *newpageio;
mapping = super->imisc->i_mapping;
@@ -378,7 +378,7 @@
if (!newpage)
goto error;
- if (cachefs_page_get_private(newpage, &newpageio,
+ if (fscache_page_get_private(newpage, &newpageio,
mapping_gfp_mask(mapping)) < 0)
goto error_page;
@@ -614,15 +614,18 @@
/*
* withdraw from active service all the blocks residing on a device
*/
-void cachefs_block_withdraw(struct cachefs_super *super)
+void cachefs_block_dissociate(struct fscache_cache *cache)
{
struct cachefs_block *block, *xblock;
- struct cachefs_page *pageio;
+ struct cachefs_super *super;
+ struct fscache_page *pageio;
struct rb_node *node;
unsigned long flags;
DECLARE_WAITQUEUE(myself, current);
+ super = container_of(cache, struct cachefs_super, cache);
+
_enter("");
/* first thing to do is mark all blocks withdrawn
@@ -705,4 +708,4 @@
_leave("");
-} /* end cachefs_block_withdraw() */
+} /* end cachefs_block_dissociate() */
diff -uNr linux-2.6.9-rc2-mm4/fs/cachefs/cachefs-int.h linux-2.6.9-rc2-mm4-fscache/fs/cachefs/cachefs-int.h
--- linux-2.6.9-rc2-mm4/fs/cachefs/cachefs-int.h 2004-09-27 11:23:55.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/cachefs/cachefs-int.h 2004-10-04 16:15:28.481959199 +0100
@@ -12,7 +12,7 @@
#ifndef _LINUX_CACHEFS_INT_H
#define _LINUX_CACHEFS_INT_H
-#include <linux/cachefs.h>
+#include <linux/fscache-cache.h>
#include <linux/timer.h>
#include <linux/bio.h>
#include "cachefs-layout.h"
@@ -28,9 +28,9 @@
struct cachefs_super;
struct cachefs_block;
struct cachefs_inode;
-struct cachefs_search_result;
struct cachefs_transaction;
+extern struct fscache_cache_ops cachefs_cache_ops;
extern struct address_space_operations cachefs_indr_io_addrspace_operations;
extern struct address_space_operations cachefs_linear_io_addrspace_operations;
extern struct file_operations cachefs_root_file_operations;
@@ -46,7 +46,7 @@
extern int cachefs_io_dummy_filler(void *data, struct page *page);
extern int cachefs_indr_io_get_block(struct inode *inode, struct page *page,
- struct cachefs_page *pageio, int create);
+ struct fscache_page *pageio, int create);
struct cachefs_reclaimable {
unsigned ino;
@@ -59,8 +59,8 @@
*/
struct cachefs_super
{
+ struct fscache_cache cache; /* cache handle */
struct super_block *sb;
- struct list_head mnt_link; /* link in list of mounted caches */
struct cachefs_inode *imetadata; /* the metadata records file */
struct inode *imisc; /* an inode covering the whole blkdev */
@@ -70,15 +70,10 @@
#define CACHEFS_SUPER_DO_RECLAIM 2 /* T if should do reclamation */
#define CACHEFS_SUPER_RCM_IMM_SCAN 3 /* T if should scan for immediately
* reclaimable inodes */
-#define CACHEFS_SUPER_WITHDRAWN 4 /* T if cache has been withdrawn */
-#define CACHEFS_SUPER_REPLAYING_UJNL 5 /* T if replaying u-journal */
+#define CACHEFS_SUPER_REPLAYING_UJNL 4 /* T if replaying u-journal */
int bio_wr_barrier; /* command to submit a write barrier BIO */
- /* index management */
- struct list_head ino_list; /* list of data/index inodes */
- spinlock_t ino_list_lock;
-
/* block allocation and recycling management */
struct rb_root blk_tree; /* block mapping tree */
rwlock_t blk_tree_lock;
@@ -191,10 +186,6 @@
struct cachefs_ondisc_superblock *layout;
};
-extern void cachefs_add_cache(struct cachefs_super *super,
- struct cachefs_search_result *srch);
-extern void cachefs_withdraw_cache(struct cachefs_super *super);
-
extern void cachefs_recycle_unready_blocks(struct cachefs_super *super);
extern void cachefs_recycle_transfer_stack(struct cachefs_super *super);
extern void cachefs_recycle_reclaim(struct cachefs_super *super);
@@ -235,7 +226,7 @@
struct list_head batch_link; /* link in batch writer's list */
struct page *page; /* current data for this block */
struct page *writeback; /* source of writeback for this block */
- struct cachefs_page *ref; /* netfs's ref to this page */
+ struct fscache_page *ref; /* netfs's ref to this page */
rwlock_t ref_lock; /* lock governing ref pointer */
struct cachefs_vj_entry *vjentry; /* invalid block record */
};
@@ -254,12 +245,12 @@
extern int cachefs_block_set(struct cachefs_super *super,
struct cachefs_block *block,
struct page *page,
- struct cachefs_page *pageio);
+ struct fscache_page *pageio);
extern int cachefs_block_set2(struct cachefs_super *super,
cachefs_blockix_t bix,
struct page *page,
- struct cachefs_page *pageio,
+ struct fscache_page *pageio,
struct cachefs_block **_block);
extern int cachefs_block_read(struct cachefs_super *super,
@@ -308,7 +299,7 @@
static inline struct cachefs_block *__cachefs_get_page_block(struct page *page)
{
BUG_ON(!PagePrivate(page));
- return ((struct cachefs_page *) page->private)->mapped_block;
+ return ((struct fscache_page *) page->private)->mapped_block;
}
static inline void cachefs_page_modify(struct cachefs_super *super,
@@ -317,38 +308,10 @@
cachefs_block_modify(super, __cachefs_get_page_block(*page), page);
}
-extern void cachefs_block_withdraw(struct cachefs_super *super);
+extern void cachefs_block_dissociate(struct fscache_cache *cache);
-/*****************************************************************************/
-/*
- * data file or index object cookie
- * - a file will only appear in one cache
- * - a request to cache a file may or may not be honoured, subject to
- * constraints such as disc space
- * - indexes files are created on disc just-in-time
- */
-struct cachefs_cookie
-{
- atomic_t usage; /* number of users of this cookie */
- atomic_t children; /* number of children of this cookie */
- struct cachefs_index_def *idef; /* index definition */
- struct cachefs_cookie *iparent; /* index holding this entry */
- struct list_head search_results; /* results of searching iparent */
- struct list_head backing_inodes; /* inode(s) backing this file/index */
- struct rw_semaphore sem;
- struct cachefs_netfs *netfs; /* owner network fs definition */
- void *netfs_data; /* back pointer to netfs */
-};
-
-struct cachefs_search_result {
- struct list_head link; /* link in search_results */
- struct cachefs_super *super; /* superblock searched */
- unsigned ino; /* inode number (or 0 if negative) */
-};
-
-extern kmem_cache_t *cachefs_cookie_jar;
-
-extern void cachefs_cookie_init_once(void *_cookie, kmem_cache_t *cachep, unsigned long flags);
+#define cachefs_mapped_block(PGIO) ((struct cachefs_block *) (PGIO)->mapped_block)
+#define cachefs_mapped_bix(PGIO) (((struct cachefs_block *) (PGIO)->mapped_block)->bix)
/*****************************************************************************/
/*
@@ -357,6 +320,7 @@
struct cachefs_inode
{
struct inode vfs_inode; /* VFS inode record for this file */
+ struct fscache_node node; /* fscache handle */
struct cachefs_block *metadata; /* block containing metadata */
struct page *metadata_page; /* page mapped to metadata block */
@@ -366,16 +330,6 @@
unsigned short index_dsize; /* size of data in each index entry */
unsigned short index_esize; /* size of index entries */
unsigned short index_epp; /* number of index entries per page */
-
- unsigned long flags;
-#define CACHEFS_ACTIVE_INODE_ISINDEX 0 /* T if inode is index file (F if file) */
-#define CACHEFS_ACTIVE_INODE_RELEASING 1 /* T if inode is being released */
-#define CACHEFS_ACTIVE_INODE_RECYCLING 2 /* T if inode is being retired */
-#define CACHEFS_ACTIVE_INODE_WITHDRAWN 3 /* T if inode has been withdrawn */
-
- struct list_head super_link; /* link in super->ino_list */
- struct list_head cookie_link; /* link in cookie->backing_inodes */
- struct cachefs_cookie *cookie; /* netfs's file/index object */
};
extern struct inode_operations cachefs_status_inode_operations;
@@ -466,16 +420,16 @@
extern void cachefs_withdraw_inode(struct cachefs_inode *inode);
-extern int cachefs_index_search(struct cachefs_inode *index,
- struct cachefs_cookie *target,
- unsigned *_entry,
- unsigned *_ino);
-
-extern int cachefs_index_add(struct cachefs_inode *index,
- struct cachefs_cookie *cookie,
- unsigned *_newino);
+extern int cachefs_index_search(struct fscache_node *node,
+ struct fscache_cookie *target,
+ struct fscache_search_result *result);
+
+extern int cachefs_index_add(struct fscache_node *node,
+ struct fscache_cookie *cookie,
+ struct fscache_search_result *result);
-extern int cachefs_index_update(struct cachefs_inode *index);
+extern int cachefs_index_update(struct fscache_node *ixnode,
+ struct fscache_node *node);
extern int cachefs_index_reclaim_one_entry(struct cachefs_super *super,
struct cachefs_transaction **_trans);
@@ -591,7 +545,7 @@
static inline
void cachefs_trans_affects_page(struct cachefs_transaction *trans,
- struct cachefs_page *pageio,
+ struct fscache_page *pageio,
unsigned offset,
unsigned size)
{
@@ -614,7 +568,7 @@
{
struct cachefs_super *super = trans->super;
cachefs_trans_affects_page(trans,
- cachefs_page_grab_private(
+ fscache_page_grab_private(
virt_to_page(super->layout)),
0,
super->sb->s_blocksize);
diff -uNr linux-2.6.9-rc2-mm4/fs/cachefs/cachefs-layout.h linux-2.6.9-rc2-mm4-fscache/fs/cachefs/cachefs-layout.h
--- linux-2.6.9-rc2-mm4/fs/cachefs/cachefs-layout.h 2004-09-27 11:23:55.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/cachefs/cachefs-layout.h 2004-09-30 19:18:08.000000000 +0100
@@ -143,17 +143,6 @@
/*****************************************************************************/
/*
- * on-disc cached network filesystem definition record
- * - each entry resides in its own sector
- */
-struct cachefs_ondisc_fsdef
-{
- uint8_t name[24]; /* name of netfs */
- uint32_t version; /* version of layout */
-};
-
-/*****************************************************************************/
-/*
* Free blocks are kept in pair of a very one sided trees (more horsetail
* plants than trees)
*
diff -uNr linux-2.6.9-rc2-mm4/fs/cachefs/index.c linux-2.6.9-rc2-mm4-fscache/fs/cachefs/index.c
--- linux-2.6.9-rc2-mm4/fs/cachefs/index.c 2004-09-27 11:23:55.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/cachefs/index.c 2004-10-04 15:09:45.926004147 +0100
@@ -20,17 +20,15 @@
*/
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/sched.h>
-#include <linux/completion.h>
#include <linux/slab.h>
#include <linux/bio.h>
#include <linux/circ_buf.h>
#include "cachefs-int.h"
-struct cachefs_index_search_record {
- struct cachefs_cookie *index;
- struct cachefs_cookie *target;
+struct fscache_index_search_record {
+ struct fscache_cookie *index;
+ struct fscache_cookie *target;
struct cachefs_inode *iinode;
unsigned entsize;
unsigned ino;
@@ -42,7 +40,7 @@
* mark an inode/index entry pair for deletion when so requested by the match
* function supplied by the netfs
*/
-static void cachefs_index_search_delete(struct cachefs_index_search_record *rec,
+static void cachefs_index_search_delete(struct fscache_index_search_record *rec,
struct page *ixpage,
unsigned ixentry,
unsigned ixoffset,
@@ -69,7 +67,7 @@
return;
}
- BUG_ON(!list_empty(&inode->cookie_link));
+ BUG_ON(!list_empty(&inode->node.cookie_link));
/* create a transaction to record the reclamation */
ret = -ENOMEM;
@@ -87,7 +85,7 @@
trans->jentry->auxblock = inode->metadata->bix;
trans->jentry->auxentry = inode->metadata_offset;
- cachefs_trans_affects_page(trans, cachefs_page_grab_private(ixpage),
+ cachefs_trans_affects_page(trans, fscache_page_grab_private(ixpage),
ixoffset, sizeof(*xent));
cachefs_trans_affects_inode(trans, inode);
@@ -152,7 +150,7 @@
* mark an inode/index entry pair for deletion when so requested by the match
* function supplied by the netfs
*/
-static void cachefs_index_search_update(struct cachefs_index_search_record *rec,
+static void cachefs_index_search_update(struct fscache_index_search_record *rec,
struct page *ixpage,
unsigned ixentry,
unsigned ixoffset,
@@ -182,7 +180,7 @@
trans->jentry->entry = ixoffset;
trans->jentry->count = rec->iinode->index_dsize;
- cachefs_trans_affects_page(trans, cachefs_page_grab_private(ixpage),
+ cachefs_trans_affects_page(trans, fscache_page_grab_private(ixpage),
ixoffset, sizeof(*xent));
/* have the netfs transcribe the update into the transaction */
@@ -225,14 +223,14 @@
unsigned long offset,
unsigned long size)
{
- struct cachefs_index_search_record *rec;
+ struct fscache_index_search_record *rec;
unsigned long stop, tmp, esize;
void *content;
int ret;
_enter(",{%lu},%lu,%lu", page->index, offset, size);
- rec = (struct cachefs_index_search_record *) desc->arg.buf;
+ rec = (struct fscache_index_search_record *) desc->arg.buf;
ret = size;
/* round up to the first record boundary after the offset */
@@ -257,7 +255,7 @@
for (; offset + esize <= stop; offset += esize) {
struct cachefs_ondisc_index_entry *xent = content + offset;
- cachefs_match_val_t result;
+ fscache_match_val_t result;
unsigned ixentry;
/* ignore invalid entries */
@@ -273,13 +271,13 @@
xent->u.data);
switch (result) {
- case CACHEFS_MATCH_SUCCESS_UPDATE:
+ case FSCACHE_MATCH_SUCCESS_UPDATE:
/* the netfs said that it matched, but needs
* updating */
cachefs_index_search_update(rec, page, ixentry, offset,
xent->ino);
- case CACHEFS_MATCH_SUCCESS:
+ case FSCACHE_MATCH_SUCCESS:
/* the netfs said that it matched */
rec->entry = tmp;
rec->ino = xent->ino;
@@ -299,13 +297,13 @@
ret = 0;
break;
- case CACHEFS_MATCH_SUCCESS_DELETE:
+ case FSCACHE_MATCH_SUCCESS_DELETE:
/* the netfs said that it matched, but this entry
* should be marked obsolete */
cachefs_index_search_delete(rec, page, ixentry, offset,
xent->ino);
- case CACHEFS_MATCH_FAILED:
+ case FSCACHE_MATCH_FAILED:
/* the netfs said there wasn't a valid match */
default:
break;
@@ -330,26 +328,23 @@
* - returns 0 if found, and stores the entry number in *_entry and the inode
* number of the backing file in *_ino
*/
-int cachefs_index_search(struct cachefs_inode *index,
- struct cachefs_cookie *target,
- unsigned *_entry,
- unsigned *_ino)
+int cachefs_index_search(struct fscache_node *node,
+ struct fscache_cookie *target,
+ struct fscache_search_result *result)
{
- struct cachefs_index_search_record rec;
+ struct fscache_index_search_record rec;
+ struct cachefs_inode *index;
struct file_ra_state ra;
read_descriptor_t desc;
loff_t pos;
int ret;
+ index = container_of(node, struct cachefs_inode, node);
+
_enter("{%s,%lu,%Lu}",
- index->cookie->idef->name,
+ index->node.cookie->idef->name,
index->vfs_inode.i_ino,
- i_size_read(index->vfs_inode));
-
- if (_entry)
- *_entry = UINT_MAX;
- if (_ino)
- *_ino = 0;
+ i_size_read(&index->vfs_inode));
ret = -ENOENT;
if (i_size_read(&index->vfs_inode) == 0)
@@ -357,7 +352,7 @@
/* prepare a record of what we want to do */
rec.iinode = index;
- rec.index = index->cookie;
+ rec.index = index->node.cookie;
rec.target = target;
rec.entsize = rec.iinode->index_esize;
rec.entry = UINT_MAX;
@@ -388,11 +383,7 @@
else {
/* we found an entry */
BUG_ON(rec.ino == 0);
-
- if (_entry)
- *_entry = rec.entry;
- if (_ino)
- *_ino = rec.ino;
+ result->ino = rec.ino;
ret = 0;
}
@@ -408,12 +399,12 @@
*/
static int cachefs_index_preinit_page(void *data, struct page *page)
{
- struct cachefs_page *pageio;
+ struct fscache_page *pageio;
_enter(",%p{%lu}", page, page->index);
/* attach a mapping cookie to the page */
- pageio = cachefs_page_get_private(page, GFP_KERNEL);
+ pageio = fscache_page_get_private(page, GFP_KERNEL);
if (IS_ERR(pageio)) {
_leave(" = %ld", PTR_ERR(pageio));
return PTR_ERR(pageio);
@@ -458,7 +449,7 @@
cachefs_metadata_postread(iinode, metadata);
_debug("free entry: %u [size %Lu]",
- newentry, i_size_read(iinode->vfs_inode));
+ newentry, i_size_read(&iinode->vfs_inode));
/* extend the index file if there are no new entries */
if (newentry == UINT_MAX) {
@@ -479,7 +470,7 @@
i_size_read(&iinode->vfs_inode) + PAGE_SIZE);
ret = cachefs_indr_io_get_block(&iinode->vfs_inode, page,
- cachefs_page_grab_private(page),
+ fscache_page_grab_private(page),
1);
if (ret < 0) {
i_size_write(&iinode->vfs_inode,
@@ -561,24 +552,27 @@
* - if an inode is successfully allocated *_newino will be set with the inode
* number
*/
-int cachefs_index_add(struct cachefs_inode *index,
- struct cachefs_cookie *cookie,
- unsigned *_newino)
+int cachefs_index_add(struct fscache_node *node,
+ struct fscache_cookie *cookie,
+ struct fscache_search_result *result)
+// unsigned *_newino)
{
struct cachefs_ondisc_index_entry *xent;
struct cachefs_ondisc_ujnl_index *jindex;
struct cachefs_ondisc_metadata *metadata;
- struct cachefs_search_result *srch;
struct cachefs_transaction *trans;
struct cachefs_super *super;
+ struct cachefs_inode *index;
struct page *inopage, *ixpage;
unsigned ino, ixentry, offset, inonext, ixnext, ino_offset;
int ret, loop;
+ index = container_of(node, struct cachefs_inode, node);
+
_enter("{%lu},{%s},",
- index->vfs_inode.i_ino, index->cookie->idef->name);
+ index->vfs_inode.i_ino, index->node.cookie->idef->name);
- *_newino = 0;
+// *_newino = 0;
super = index->vfs_inode.i_sb->s_fs_info;
inopage = NULL;
@@ -627,9 +621,9 @@
trans->jentry->upblock = index->metadata->bix;
trans->jentry->upentry = index->metadata_offset;
- cachefs_trans_affects_page(trans, cachefs_page_grab_private(ixpage),
+ cachefs_trans_affects_page(trans, fscache_page_grab_private(ixpage),
offset, index->index_esize);
- cachefs_trans_affects_page(trans, cachefs_page_grab_private(inopage),
+ cachefs_trans_affects_page(trans, fscache_page_grab_private(inopage),
ino_offset, super->layout->metadata_size);
cachefs_trans_affects_inode(trans, index);
@@ -642,12 +636,12 @@
jindex->next_ino = inonext;
jindex->next_index = ixnext;
- index->cookie->idef->update(cookie->netfs_data, jindex->data);
+ index->node.cookie->idef->update(cookie->netfs_data, jindex->data);
/* if we're adding a new index, we store its definition in the journal
* too */
if (cookie->idef) {
- struct cachefs_index_def *definition = cookie->idef;
+ struct fscache_index_def *definition = cookie->idef;
jindex->def.dsize = definition->data_size;
jindex->def.esize = definition->data_size;
@@ -725,15 +719,8 @@
cachefs_trans_commit(trans);
trans = NULL;
- /* add the new inode to the cookie's list of search results */
- list_for_each_entry(srch, &cookie->search_results, link) {
- if (srch->super == super) {
- srch->ino = ino;
- break;
- }
- }
-
- *_newino = ino;
+// *_newino = ino;
+ result->ino = ino;
error:
cachefs_trans_put(trans);
@@ -750,38 +737,29 @@
* update the index entry for an index or data file from the associated netfs
* data
*/
-int cachefs_index_update(struct cachefs_inode *inode)
+int cachefs_index_update(struct fscache_node *ixnode,
+ struct fscache_node *node)
{
struct cachefs_ondisc_index_entry *xent;
struct cachefs_ondisc_metadata *meta;
- struct cachefs_cookie *cookie = inode->cookie;
+ struct fscache_cookie *cookie = node->cookie;
struct cachefs_super *super;
- struct cachefs_inode *index;
+ struct cachefs_inode *index, *inode;
struct cachefs_block *block;
struct page *ixpage;
unsigned offs;
int ret;
- _enter("");
+ index = container_of(ixnode, struct cachefs_inode, node);
+ inode = container_of(node, struct cachefs_inode, node);
+
+ _enter(",");
super = inode->vfs_inode.i_sb->s_fs_info;
- if (test_bit(CACHEFS_SUPER_WITHDRAWN, &super->flags))
+ if (fscache_is_cache_withdrawn(&super->cache))
return 0;
- /* the index entry for this inode lives in the parent index inode */
- list_for_each_entry(index,
- &cookie->iparent->backing_inodes,
- cookie_link) {
- if (index->vfs_inode.i_sb == inode->vfs_inode.i_sb)
- goto found_parent_index_inode;
- }
-
- /* hmmm... the parent inode is strangely absent */
- BUG();
- return -ENOENT;
-
- found_parent_index_inode:
/* find the entry number of this inode's index entry */
meta = cachefs_metadata_preread(inode);
offs = meta->pindex_entry;
diff -uNr linux-2.6.9-rc2-mm4/fs/cachefs/indirection-io.c linux-2.6.9-rc2-mm4-fscache/fs/cachefs/indirection-io.c
--- linux-2.6.9-rc2-mm4/fs/cachefs/indirection-io.c 2004-09-27 11:23:55.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/cachefs/indirection-io.c 2004-09-30 17:17:21.000000000 +0100
@@ -36,7 +36,7 @@
struct cachefs_io_block_path {
struct page *page;
- struct cachefs_page *pageio; /* page => block mapping */
+ struct fscache_page *pageio; /* page => block mapping */
cachefs_blockix_t bix; /* block number for this level */
unsigned offset; /* offset into parent pointer block */
@@ -84,7 +84,7 @@
unsigned nr_pages,
sector_t *last_block_in_bio)
{
- struct cachefs_page *pageio;
+ struct fscache_page *pageio;
struct inode *inode = page->mapping->host;
sector_t last_block;
int ret;
@@ -92,7 +92,7 @@
_enter("");
/* get the page mapping cookie */
- pageio = cachefs_page_get_private(page, GFP_KERNEL);
+ pageio = fscache_page_get_private(page, GFP_KERNEL);
if (IS_ERR(pageio)) {
ret = PTR_ERR(pageio);
goto error;
@@ -128,7 +128,7 @@
*/
if (!*_bio)
goto allocate_new_bio;
- else if (*last_block_in_bio + 1 != pageio->mapped_block->bix)
+ else if (*last_block_in_bio + 1 != cachefs_mapped_bix(pageio))
goto dispatch_bio;
/* add the page to the current BIO */
@@ -138,12 +138,12 @@
/* dispatch the BIO immediately if the current page lives on an
* indirection chain boundary */
- if (test_bit(CACHEFS_PAGE_BOUNDARY, &pageio->flags)) {
+ if (test_bit(FSCACHE_PAGE_BOUNDARY, &pageio->flags)) {
submit_bio(READ, *_bio);
*_bio = NULL;
}
else {
- *last_block_in_bio = pageio->mapped_block->bix;
+ *last_block_in_bio = cachefs_mapped_bix(pageio);
}
_leave(" = 0");
@@ -154,7 +154,7 @@
submit_bio(READ, *_bio);
allocate_new_bio:
ret = cachefs_io_alloc(inode->i_sb,
- pageio->mapped_block->bix,
+ cachefs_mapped_bix(pageio),
nr_pages, GFP_KERNEL, _bio);
if (ret < 0) {
*_bio = NULL;
@@ -168,8 +168,7 @@
*/
hole:
ret = -ENODATA;
- if (test_bit(CACHEFS_ACTIVE_INODE_ISINDEX,
- &CACHEFS_FS_I(inode)->flags)) {
+ if (test_bit(FSCACHE_NODE_ISINDEX, &CACHEFS_FS_I(inode)->node.flags)) {
printk("CacheFS: found unexpected hole in index/metadata file:"
" ino=%lu pg=%lu\n",
inode->i_ino, page->index);
@@ -395,7 +394,7 @@
&block, &step->page);
if (ret < 0)
goto error_block;
- step->pageio = cachefs_page_grab_private(step->page);
+ step->pageio = fscache_page_grab_private(step->page);
}
else {
ret = cachefs_block_set2(super, jentry->block,
@@ -581,7 +580,7 @@
* index and must be initialised as part of the final journalling mark
*/
int cachefs_indr_io_get_block(struct inode *vfs_inode, struct page *page,
- struct cachefs_page *pageio, int create)
+ struct fscache_page *pageio, int create)
{
struct cachefs_io_block_path path[4];
struct cachefs_inode *inode = CACHEFS_FS_I(vfs_inode);
@@ -688,10 +687,10 @@
path[pix].offset += inode->metadata_offset;
down_read(&inode->metadata_sem);
- path[pix + 1].pageio = cachefs_page_grab_private(inode->metadata_page);
+ path[pix + 1].pageio = fscache_page_grab_private(inode->metadata_page);
up_read(&inode->metadata_sem);
- path[pix + 1].bix = path[pix + 1].pageio->mapped_block->bix;
+ path[pix + 1].bix = cachefs_mapped_bix(path[pix + 1].pageio);
ret = 0;
for (; pix >= 0; pix--) {
@@ -784,7 +783,7 @@
}
if (!step->pageio) {
- step->pageio = __cachefs_page_grab_private(step->page);
+ step->pageio = __fscache_page_grab_private(step->page);
if (!step->pageio) {
printk("step level %u"
" { ptr={%lu}+%u / bix=%u }",
@@ -812,21 +811,22 @@
return ret;
}
else if (path[0].flags & CACHEFS_BLOCK_INIT_NETFSDATA) {
- set_bit(CACHEFS_BLOCK_NETFSDATA, &pageio->mapped_block->flags);
+ set_bit(CACHEFS_BLOCK_NETFSDATA,
+ &cachefs_mapped_block(pageio)->flags);
}
/* got the block - set the block offset in the page mapping record */
if (path[0].flags & CACHEFS_BLOCK_NEW)
- set_bit(CACHEFS_PAGE_NEW, &pageio->flags);
+ set_bit(FSCACHE_PAGE_NEW, &pageio->flags);
_debug("notboundary = %u", notboundary);
if (!notboundary)
- set_bit(CACHEFS_PAGE_BOUNDARY, &pageio->flags);
+ set_bit(FSCACHE_PAGE_BOUNDARY, &pageio->flags);
_leave(" = 0 [bix=%u %c%c]",
- pageio->mapped_block->bix,
- test_bit(CACHEFS_PAGE_BOUNDARY, &pageio->flags) ? 'b' : '-',
- test_bit(CACHEFS_PAGE_NEW, &pageio->flags) ? 'n' : '-'
+ cachefs_mapped_bix(pageio),
+ test_bit(FSCACHE_PAGE_BOUNDARY, &pageio->flags) ? 'b' : '-',
+ test_bit(FSCACHE_PAGE_NEW, &pageio->flags) ? 'n' : '-'
);
return 0;
diff -uNr linux-2.6.9-rc2-mm4/fs/cachefs/inode.c linux-2.6.9-rc2-mm4-fscache/fs/cachefs/inode.c
--- linux-2.6.9-rc2-mm4/fs/cachefs/inode.c 2004-09-27 11:23:55.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/cachefs/inode.c 2004-10-04 15:18:31.795339335 +0100
@@ -125,7 +125,7 @@
inode->index_esize = inode->index_dsize;
inode->index_epp = PAGE_SIZE / inode->index_esize;
- __set_bit(CACHEFS_ACTIVE_INODE_ISINDEX, &inode->flags);
+ __set_bit(FSCACHE_NODE_ISINDEX, &inode->node.flags);
/* read the block containing this inode's meta-data from disc */
pos = inode->vfs_inode.i_ino << super->layout->metadata_bits;
@@ -262,7 +262,7 @@
inode->vfs_inode.i_op = &cachefs_root_inode_operations;
inode->vfs_inode.i_fop = &cachefs_root_file_operations;
- __set_bit(CACHEFS_ACTIVE_INODE_ISINDEX, &inode->flags);
+ __set_bit(FSCACHE_NODE_ISINDEX, &inode->node.flags);
}
_leave(" = 0");
@@ -297,10 +297,12 @@
/* deal with an existing inode */
if (!(inode->vfs_inode.i_state & I_NEW)) {
- _leave(" = 0 [exist]");
+ _leave(" = %p [exist]", inode);
return inode;
}
+ inode->node.cache = &super->cache;
+
/* new inode - attempt to find in the on-disc catalogue */
switch (ino) {
/* they've asked for the virtual inode that mirrors the
@@ -346,7 +348,7 @@
/* success */
unlock_new_inode(&inode->vfs_inode);
- _leave(" = %p", inode);
+ _leave(" = %p [new]", inode);
return inode;
/* failure */
diff -uNr linux-2.6.9-rc2-mm4/fs/cachefs/interface.c linux-2.6.9-rc2-mm4-fscache/fs/cachefs/interface.c
--- linux-2.6.9-rc2-mm4/fs/cachefs/interface.c 2004-09-27 11:23:55.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/cachefs/interface.c 2004-10-04 17:06:22.775465935 +0100
@@ -1,6 +1,6 @@
-/* interface.c: network FS interface to cache
+/* interface.c: filesystem cache interface
*
- * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells at redhat.com)
*
* This program is free software; you can redistribute it and/or
@@ -10,980 +10,120 @@
*/
#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/bio.h>
#include "cachefs-int.h"
struct cachefs_io_end {
- cachefs_rw_complete_t func;
+ fscache_rw_complete_t func;
void *data;
void *cookie_data;
struct cachefs_block *block;
};
-LIST_HEAD(cachefs_netfs_list);
-LIST_HEAD(cachefs_cache_list);
-DECLARE_RWSEM(cachefs_addremove_sem);
-
-kmem_cache_t *cachefs_cookie_jar;
-
-static cachefs_match_val_t cachefs_fsdef_index_match(void *target,
- const void *entry);
-
-static void cachefs_fsdef_index_update(void *source, void *entry);
-
-static struct cachefs_index_def cachefs_fsdef_index_def = {
- .name = ".fsdef",
- .data_size = sizeof(struct cachefs_ondisc_fsdef),
- .match = cachefs_fsdef_index_match,
- .update = cachefs_fsdef_index_update
-};
-
-static struct cachefs_cookie cachefs_fsdef_index = {
- .usage = ATOMIC_INIT(1),
- .idef = &cachefs_fsdef_index_def,
- .sem = __RWSEM_INITIALIZER(cachefs_fsdef_index.sem),
- .search_results = LIST_HEAD_INIT(cachefs_fsdef_index.search_results),
- .backing_inodes = LIST_HEAD_INIT(cachefs_fsdef_index.backing_inodes),
-};
-
-static void __cachefs_cookie_put(struct cachefs_cookie *cookie);
-static inline void cachefs_cookie_put(struct cachefs_cookie *cookie)
-{
- BUG_ON(atomic_read(&cookie->usage) <= 0);
-
- if (atomic_dec_and_test(&cookie->usage))
- __cachefs_cookie_put(cookie);
-
-}
-
-/*****************************************************************************/
-/*
- * register a network filesystem for caching
- */
-int __cachefs_register_netfs(struct cachefs_netfs *netfs,
- struct cachefs_index_def *primary_idef)
-{
- struct cachefs_netfs *ptr;
- int ret;
-
- _enter("{%s}", netfs->name);
-
- INIT_LIST_HEAD(&netfs->link);
-
- /* allocate a cookie for the primary index */
- netfs->primary_index =
- kmem_cache_alloc(cachefs_cookie_jar, SLAB_KERNEL);
-
- if (!netfs->primary_index) {
- _leave(" = -ENOMEM");
- return -ENOMEM;
- }
-
- /* initialise the primary index cookie */
- memset(netfs->primary_index, 0, sizeof(*netfs->primary_index));
-
- atomic_set(&netfs->primary_index->usage, 1);
- atomic_set(&netfs->primary_index->children, 0);
-
- netfs->primary_index->idef = primary_idef;
- netfs->primary_index->iparent = &cachefs_fsdef_index;
- netfs->primary_index->netfs = netfs;
- netfs->primary_index->netfs_data = netfs;
-
- atomic_inc(&netfs->primary_index->iparent->usage);
- atomic_inc(&netfs->primary_index->iparent->children);
-
- INIT_LIST_HEAD(&netfs->primary_index->search_results);
- INIT_LIST_HEAD(&netfs->primary_index->backing_inodes);
- init_rwsem(&netfs->primary_index->sem);
-
- /* check the netfs type is not already present */
- down_write(&cachefs_addremove_sem);
-
- ret = -EEXIST;
- list_for_each_entry(ptr, &cachefs_netfs_list,link) {
- if (strcmp(ptr->name, netfs->name) == 0)
- goto already_registered;
- }
-
- list_add(&netfs->link, &cachefs_netfs_list);
- ret = 0;
-
- printk("CacheFS: netfs '%s' registered for caching\n", netfs->name);
-
- already_registered:
- up_write(&cachefs_addremove_sem);
-
- if (ret < 0) {
- kmem_cache_free(cachefs_cookie_jar, netfs->primary_index);
- netfs->primary_index = NULL;
- }
-
- _leave(" = %d", ret);
- return ret;
-
-} /* end __cachefs_register_netfs() */
-
-EXPORT_SYMBOL(__cachefs_register_netfs);
-
-/*****************************************************************************/
-/*
- * unregister a network filesystem from the cache
- * - all cookies must have been released first
- */
-void __cachefs_unregister_netfs(struct cachefs_netfs *netfs)
-{
- _enter("{%s.%u}", netfs->name, netfs->version);
-
- down_write(&cachefs_addremove_sem);
-
- list_del(&netfs->link);
- cachefs_relinquish_cookie(netfs->primary_index, 0);
-
- up_write(&cachefs_addremove_sem);
-
- printk("CacheFS: netfs '%s' unregistered from caching\n", netfs->name);
-
- _leave("");
-
-} /* end __cachefs_unregister_netfs() */
-
-EXPORT_SYMBOL(__cachefs_unregister_netfs);
-
-/*****************************************************************************/
-/*
- * declare a mounted cache as being open for business
- * - try not to allocate memory as disposing of the superblock is a pain
- */
-void cachefs_add_cache(struct cachefs_super *super,
- struct cachefs_search_result *srch)
-{
- struct cachefs_inode *ifsdef;
-
- _enter("");
-
- /* prepare an active-inode record for the FSDEF index of this cache */
- ifsdef = cachefs_iget(super, CACHEFS_INO_FSDEF_CATALOGUE);
- if (IS_ERR(ifsdef))
- /* there shouldn't be an error as FSDEF is the root dir of the
- * FS and so should already be in core */
- BUG();
-
- if (!cachefs_igrab(ifsdef))
- BUG();
-
- ifsdef->cookie = &cachefs_fsdef_index;
-
- srch->super = super;
- srch->ino = CACHEFS_INO_FSDEF_CATALOGUE;
-
- down_write(&cachefs_addremove_sem);
-
- /* add the superblock to the list */
- list_add(&super->mnt_link, &cachefs_cache_list);
-
- /* add the cache's netfs definition index inode to the superblock's
- * list */
- spin_lock(&super->ino_list_lock);
- list_add_tail(&ifsdef->super_link, &super->ino_list);
- spin_unlock(&super->ino_list_lock);
-
- /* add the cache's netfs definition index inode to the top level index
- * cookie as a known backing inode */
- down_write(&cachefs_fsdef_index.sem);
-
- list_add_tail(&srch->link, &cachefs_fsdef_index.search_results);
- list_add_tail(&ifsdef->cookie_link,
- &cachefs_fsdef_index.backing_inodes);
- atomic_inc(&cachefs_fsdef_index.usage);
-
- up_write(&cachefs_fsdef_index.sem);
-
- up_write(&cachefs_addremove_sem);
-
- _leave("");
-
-} /* end cachefs_add_cache() */
-
/*****************************************************************************/
/*
- * withdraw an unmounted cache from the active service
+ * look up the nominated node for this cache
*/
-void cachefs_withdraw_cache(struct cachefs_super *super)
+static struct fscache_node *cachefs_lookup_node(struct fscache_cache *cache,
+ unsigned ino)
{
+ struct cachefs_super *super;
struct cachefs_inode *inode;
- _enter("");
-
- /* make the cache unavailable for cookie acquisition */
- set_bit(CACHEFS_SUPER_WITHDRAWN, &super->flags);
-
- down_write(&cachefs_addremove_sem);
- list_del_init(&super->mnt_link);
- up_write(&cachefs_addremove_sem);
-
- /* mark all inodes as being withdrawn */
- spin_lock(&super->ino_list_lock);
- list_for_each_entry(inode, &super->ino_list, super_link) {
- set_bit(CACHEFS_ACTIVE_INODE_WITHDRAWN, &inode->flags);
- }
- spin_unlock(&super->ino_list_lock);
-
- /* make sure all pages pinned by operations on behalf of the netfs are
- * written to disc */
- cachefs_trans_sync(super, CACHEFS_TRANS_SYNC_WAIT_FOR_ACK);
-
- /* mark all active blocks as being withdrawn */
- cachefs_block_withdraw(super);
-
- /* we now have to destroy all the active inodes pertaining to this
- * superblock */
- spin_lock(&super->ino_list_lock);
-
- while (!list_empty(&super->ino_list)) {
- inode = list_entry(super->ino_list.next, struct cachefs_inode,
- super_link);
- list_del(&inode->super_link);
- spin_unlock(&super->ino_list_lock);
-
- /* we've extracted an active inode from the tree - now dispose
- * of it */
- cachefs_withdraw_inode(inode);
- cachefs_iput(inode);
-
- spin_lock(&super->ino_list_lock);
- }
-
- spin_unlock(&super->ino_list_lock);
-
- _leave("");
-
-} /* end cachefs_withdraw_cache() */
-
-/*****************************************************************************/
-/*
- * withdraw an inode from active service
- * - need break the links to a cached object cookie
- * - called under two situations:
- * (1) recycler decides to reclaim an in-use inode
- * (2) a cache is unmounted
- * - have to take care as the cookie can be being relinquished by the netfs
- * simultaneously
- * - the active inode is pinned by the caller holding a refcount on it
- */
-void cachefs_withdraw_inode(struct cachefs_inode *inode)
-{
- struct cachefs_search_result *srch;
- struct cachefs_cookie *cookie, *xcookie = NULL;
-
- _enter("{ino=%lu cnt=%u}",
- inode->vfs_inode.i_ino, atomic_read(&inode->vfs_inode.i_count));
-
- /* first of all we have to break the links between the inode and the
- * cookie
- * - we have to hold both semaphores BUT we have to get the cookie sem
- * FIRST
- */
- down(&inode->vfs_inode.i_sem);
-
- cookie = inode->cookie;
- if (cookie) {
- /* pin the cookie so that is doesn't escape */
- atomic_inc(&cookie->usage);
-
- /* re-order the locks to avoid deadlock */
- up(&inode->vfs_inode.i_sem);
- down_write(&cookie->sem);
- down(&inode->vfs_inode.i_sem);
-
- /* erase references from the inode to the cookie */
- list_del_init(&inode->cookie_link);
-
- xcookie = inode->cookie;
- inode->cookie = NULL;
-
- /* delete the search result record for this inode from the
- * cookie's list */
- list_for_each_entry(srch, &cookie->search_results, link) {
- if (srch->super == inode->vfs_inode.i_sb->s_fs_info)
- break;
- }
-
- list_del(&srch->link);
- dbgfree(srch);
- kfree(srch);
-
- up_write(&cookie->sem);
- }
-
- up(&inode->vfs_inode.i_sem);
-
- /* we've broken the links between cookie and inode */
- if (xcookie) {
- cachefs_cookie_put(xcookie);
- cachefs_iput(inode);
- }
-
- /* unpin the cookie */
- if (cookie)
- cachefs_cookie_put(cookie);
-
- _leave("");
-
-} /* end cachefs_withdraw_inode() */
-
-/*****************************************************************************/
-/*
- * search for representation of an object in its parent cache
- * - the cookie must be locked by the caller
- * - returns -ENODATA if the object or one of its ancestors doesn't exist
- */
-static int cachefs_search_for_object(struct cachefs_cookie *cookie,
- struct cachefs_super *super)
-{
- struct cachefs_search_result *srch;
- struct cachefs_cookie *iparent;
- struct cachefs_inode *ipinode, *inode;
- int ret;
-
- iparent = cookie->iparent;
- if (!iparent)
- return 0; /* FSDEF entries don't have a parent */
-
- _enter("{%s/%s},",
- iparent->idef->name,
- cookie->idef ? (char *) cookie->idef->name : "<file>");
-
- /* see if there's a search result for this object already */
- list_for_each_entry(srch, &cookie->search_results, link) {
- _debug("check entry %p x %p [ino %u]",
- cookie, super, srch->ino);
-
- if (srch->super == super) {
- _debug("found entry");
-
- if (srch->ino) {
- _leave(" = 0 [found ino %u]", srch->ino);
- return 0;
- }
-
- /* entry is negative */
- _leave(" = -ENODATA");
- return -ENODATA;
- }
- }
-
- /* allocate an initially negative entry for this object */
- _debug("alloc entry %p x %p", cookie, super);
-
- srch = kmalloc(sizeof(*srch), GFP_KERNEL);
- if (!srch) {
- _leave(" = -ENOMEM");
- return -ENOMEM;
- }
-
- memset(srch, 0, sizeof(*srch));
-
- srch->super = super;
- srch->ino = 0;
- INIT_LIST_HEAD(&srch->link);
-
- /* we need see if there's an entry for this cache in this object's
- * parent index, so the first thing to do is to see if the parent index
- * is represented on disc
- */
- down_read(&iparent->sem);
-
- ret = cachefs_search_for_object(iparent, super);
- if (ret < 0) {
- if (ret != -ENODATA)
- goto error;
-
- /* set a negative entry */
- list_add_tail(&srch->link, &cookie->search_results);
- goto done;
- }
-
- /* find the parent's backing inode */
- list_for_each_entry(ipinode, &iparent->backing_inodes, cookie_link) {
- if (ipinode->vfs_inode.i_sb->s_fs_info == super)
- goto found_parent_entry;
- }
-
- BUG();
-
- found_parent_entry:
- _debug("found_parent_entry");
-
- /* search the parent index for a reference compatible with this
- * object */
- ret = cachefs_index_search(ipinode, cookie, NULL, &srch->ino);
- switch (ret) {
- default:
- goto error;
-
- case 0:
- /* found - allocate an inode */
- inode = cachefs_iget(super, srch->ino);
- if (IS_ERR(inode)) {
- ret = PTR_ERR(inode);
- goto error;
- }
-
- down(&inode->vfs_inode.i_sem);
+ _enter("%p,%d", cache, ino);
- BUG_ON(!list_empty(&inode->cookie_link));
-
- /* attach the inode to the superblock's inode list */
- if (list_empty(&inode->super_link)) {
- if (!cachefs_igrab(inode))
- goto igrab_failed_upput;
-
- spin_lock(&super->ino_list_lock);
- list_add_tail(&inode->super_link, &super->ino_list);
- spin_unlock(&super->ino_list_lock);
- }
-
- /* attach the inode to the cookie */
- inode->cookie = cookie;
- list_add_tail(&srch->link, &cookie->search_results);
- list_add_tail(&inode->cookie_link, &cookie->backing_inodes);
- atomic_inc(&cookie->usage);
-
- up(&inode->vfs_inode.i_sem);
- break;
-
- case -ENOENT:
- /* we can at least set a valid negative entry */
- list_add_tail(&srch->link, &cookie->search_results);
- ret = -ENODATA;
- break;
- }
-
- done:
- up_read(&iparent->sem);
- _leave(" = %d", ret);
- return ret;
-
- igrab_failed_upput:
- up(&inode->vfs_inode.i_sem);
- cachefs_iput(inode);
- ret = -ENOENT;
- error:
- up_read(&iparent->sem);
- dbgfree(srch);
- kfree(srch);
- _leave(" = %d", ret);
- return ret;
-
-} /* end cachefs_search_for_object() */
-
-/*****************************************************************************/
-/*
- * instantiate the object in the specified cache
- * - the cookie must be write-locked by the caller
- * - search must have been performed first (so lists of search results are
- * filled out)
- * - all parent index objects are instantiated if necessary
- */
-static int cachefs_instantiate_object(struct cachefs_cookie *cookie,
- struct cachefs_super *super)
-{
- struct cachefs_search_result *srch;
- struct cachefs_cookie *iparent;
- struct cachefs_inode *ipinode, *inode;
- int ret;
+ super = container_of(cache, struct cachefs_super, cache);
- iparent = cookie->iparent;
- if (!iparent)
- return 0; /* FSDEF entries don't have a parent */
-
- _enter("{%s/%s},",
- iparent->idef->name,
- cookie->idef ? (char *) cookie->idef->name : "<file>");
-
- /* find the search result for this object */
- list_for_each_entry(srch, &cookie->search_results, link) {
- if (srch->super == super)
- goto found_search_result;
- }
-
- BUG();
-
- found_search_result:
- if (srch->ino) {
- /* it was instantiated already */
- _leave(" = 0 [found ino %u]", srch->ino);
- return 0;
- }
-
- /* we need to insert an entry for this cache in the object's parent
- * index, so the first thing to do is make sure that the parent index
- * is represented on disc
- */
- down_write(&iparent->sem);
-
- ret = cachefs_instantiate_object(iparent, super);
- if (ret < 0)
- goto error;
-
- /* the parent index's inode should now be available */
- list_for_each_entry(ipinode, &iparent->backing_inodes, cookie_link) {
- if (ipinode->vfs_inode.i_sb->s_fs_info == super)
- goto found_parent_inode;
- }
-
- BUG();
-
- found_parent_inode:
- _debug("found_parent_inode: ino=%lu", ipinode->vfs_inode.i_ino);
-
- BUG_ON(ipinode->cookie != iparent);
-
- /* allocate an entry within the parent index inode */
- ret = cachefs_index_add(ipinode, cookie, &srch->ino);
- if (ret < 0)
- goto error;
-
- /* we're going to need an in-memory reflection of the inode too */
- inode = cachefs_iget(super, srch->ino);
+ inode = cachefs_iget(super, ino);
if (IS_ERR(inode)) {
- ret = PTR_ERR(inode);
- goto error_x; /* uh-oh... our search record is now wrong */
- }
-
- /* keep track of it */
- down(&inode->vfs_inode.i_sem);
-
- BUG_ON(!list_empty(&inode->cookie_link));
-
- /* attach to the superblock's inode list */
- if (list_empty(&inode->super_link)) {
- if (!cachefs_igrab(inode))
- goto error_xi;
-
- spin_lock(&super->ino_list_lock);
- list_add_tail(&inode->super_link, &super->ino_list);
- spin_unlock(&super->ino_list_lock);
- }
-
- /* attach to the cookie's search result list */
- inode->cookie = cookie;
- list_add_tail(&inode->cookie_link, &cookie->backing_inodes);
- atomic_inc(&cookie->usage);
-
- /* done */
- up(&inode->vfs_inode.i_sem);
- up_write(&iparent->sem);
- _leave(" = 0 [new]");
- return 0;
-
- /* if we get an error after having instantiated an inode on disc, just
- * discard the search record so we find it next time */
- error_xi:
- up(&inode->vfs_inode.i_sem);
- cachefs_iput(inode);
- ret = -ENOENT;
- error_x:
- list_del(&srch->link);
- dbgfree(srch);
- kfree(srch);
- srch = NULL;
- error:
- up_write(&iparent->sem);
- _leave(" = %d", ret);
- return ret;
-
-} /* end cachefs_instantiate_object() */
-
-/*****************************************************************************/
-/*
- * select a cache on which to store a file
- * - the cache addremove semaphore must be at least read-locked by the caller
- */
-static struct cachefs_super *cachefs_select_cache_for_file(void)
-{
- struct cachefs_super *super;
-
- _enter("");
-
- /* TODO: make more intelligent than just choosing the first cache */
- super = NULL;
- if (!list_empty(&cachefs_cache_list))
- super = list_entry(cachefs_cache_list.next,
- struct cachefs_super,
- mnt_link);
-
- _leave(" = %p", super);
- return super;
-
-} /* end cachefs_select_cache_for_file() */
-
-/*****************************************************************************/
-/*
- * request a cookie to represent a data file or an index
- * - iparent specifies the parent index to pin in memory
- * - the top level index cookie for each netfs is stored in the cachefs_netfs
- * struct upon registration
- * - idef is NULL for a data file
- * - idef points to the definition for an index
- * - the netfs_data will be passed to the functions pointed to in *idef
- * - all attached caches will be searched to see if they contain this object
- * - index objects aren't stored on disc until there's a dependent file that
- * needs storing
- * - file objects are stored in a selected cache immediately, and all the
- * indexes forming the path to it are instantiated if necessary
- * - we never let on to the netfs about errors
- * - we may set a negative cookie pointer, but that's okay
- */
-struct cachefs_cookie *__cachefs_acquire_cookie(struct cachefs_cookie *iparent,
- struct cachefs_index_def *idef,
- void *netfs_data)
-{
- struct cachefs_cookie *cookie;
- struct cachefs_super *super;
- int ret = 0;
-
- _enter("{%s},{%s},%p",
- iparent ? (char *) iparent->idef->name : "<no-parent>",
- idef ? (char *) idef->name : "<file>",
- netfs_data);
-
- /* if it's going to be an index then validate the index data */
- if (idef) {
- int dsize;
- int loop;
-
- if (!idef->name[0]) {
- printk("CacheFS: %s.%s.%p: nameless index\n",
- iparent->netfs->name,
- iparent->idef->name,
- idef);
- return CACHEFS_NEGATIVE_COOKIE;
- }
-
- dsize = CACHEFS_ONDISC_UJNL_MIN_REC_SIZE -
- sizeof(struct cachefs_ondisc_update_journal);
-
- if (idef->data_size > dsize) {
- printk("CacheFS: %s.%s.%s:"
- " index data size exceeds maximum %u>%d\n",
- iparent->netfs->name,
- iparent->idef->name,
- idef->name,
- idef->data_size,
- dsize);
- return CACHEFS_NEGATIVE_COOKIE;
- }
-
- for (loop = 0; loop < 4; loop++) {
- if (idef->keys[loop].type >=
- CACHEFS_INDEX_KEYS__LAST) {
- printk("CacheFS: %s.%s.%s:"
- " index type %u unsupported\n",
- iparent->netfs->name,
- iparent->idef->name,
- idef->name,
- idef->keys[loop].type);
- return CACHEFS_NEGATIVE_COOKIE;
- }
-
- dsize -= idef->keys[loop].len;
- if (dsize < 0) {
- printk("CacheFS: %s.%s.%s:"
- " index key size exceeds data size\n",
- iparent->netfs->name,
- iparent->idef->name,
- idef->name);
- return CACHEFS_NEGATIVE_COOKIE;
- }
- }
- }
-
- /* if there's no parent cookie, then we don't create one here either */
- if (iparent == CACHEFS_NEGATIVE_COOKIE) {
- _leave(" [no parent]");
- return CACHEFS_NEGATIVE_COOKIE;
- }
-
- /* allocate and initialise a cookie */
- cookie = kmem_cache_alloc(cachefs_cookie_jar, SLAB_KERNEL);
- if (!cookie) {
- _leave(" [ENOMEM]");
- return CACHEFS_NEGATIVE_COOKIE;
+ _leave(" = %ld [error]", PTR_ERR(inode));
+ return ERR_PTR(PTR_ERR(inode));
}
- atomic_set(&cookie->usage, 1);
- atomic_set(&cookie->children, 0);
+ _leave(" = %p", &inode->node);
+ return &inode->node;
- atomic_inc(&iparent->usage);
- atomic_inc(&iparent->children);
-
- cookie->idef = idef;
- cookie->iparent = iparent;
- cookie->netfs = iparent->netfs;
- cookie->netfs_data = netfs_data;
-
- /* now we need to see whether the backing objects for this cookie yet
- * exist, if not there'll be nothing to search */
- down_read(&cachefs_addremove_sem);
-
- if (list_empty(&cachefs_cache_list)) {
- up_read(&cachefs_addremove_sem);
- _leave(" [no caches]");
- return cookie;
- }
-
- down_write(&cookie->sem);
-
- /* search every cache we know about to see if the object is already
- * present */
- list_for_each_entry(super, &cachefs_cache_list, mnt_link) {
- ret = cachefs_search_for_object(cookie, super);
- switch (ret) {
- case 0:
- if (!cookie->idef)
- break; /* only want the first file entry */
- case -ENODATA:
- ret = 0;
- continue;
- default:
- goto error;
- }
- }
-
- /* if the object is a cookie then we need do nothing more here - we
- * create indexes on disc when we need them as an index may exist in
- * multiple caches */
- if (cookie->idef)
- goto done;
-
- /* the object is a file - we need to select a cache in which to store
- * it */
- ret = -ENOMEDIUM;
- super = cachefs_select_cache_for_file();
- if (!super)
- goto error; /* couldn't decide on a cache */
-
- /* create a file index entry on disc, along with all the indexes
- * required to find it again later */
- ret = cachefs_instantiate_object(cookie, super);
- if (ret == 0)
- goto done;
-
- error:
- printk("CacheFS: error from cache fs: %d\n", ret);
- if (cookie) {
- kmem_cache_free(cachefs_cookie_jar, cookie);
- cookie = CACHEFS_NEGATIVE_COOKIE;
- atomic_dec(&iparent->usage);
- atomic_dec(&iparent->children);
- }
-
- done:
- up_write(&cookie->sem);
- up_read(&cachefs_addremove_sem);
- _leave(" = %p", cookie);
- return cookie;
-
-} /* end __cachefs_acquire_cookie() */
-
-EXPORT_SYMBOL(__cachefs_acquire_cookie);
+} /* end cachefs_lookup_node() */
/*****************************************************************************/
/*
- * release a cookie back to the cache
- * - the object will be marked as recyclable on disc if retire is true
- * - all dependents of this cookie must have already been unregistered
- * (indexes/files/pages)
+ * increment the usage count on this inode (may fail if unmounting)
*/
-void __cachefs_relinquish_cookie(struct cachefs_cookie *cookie, int retire)
+static struct fscache_node *cachefs_grab_node(struct fscache_node *node)
{
struct cachefs_inode *inode;
+ struct fscache_node *ret;
- _enter("{%s},%d",
- cookie && cookie->idef ? (char *) cookie->idef->name : "<file>",
- retire);
-
- if (cookie == CACHEFS_NEGATIVE_COOKIE) {
- _leave(" [no cookie]");
- return;
- }
+ _enter("%p", node);
- if (atomic_read(&cookie->children) != 0) {
- printk("CacheFS: cookie still has children\n");
- BUG();
- }
-
- /* detach pointers back to netfs */
- down_write(&cookie->sem);
-
- cookie->netfs_data = NULL;
- cookie->idef = NULL;
-
- /* queue retired objects for recycling */
- if (retire) {
- list_for_each_entry(inode,
- &cookie->backing_inodes,
- cookie_link) {
- set_bit(CACHEFS_ACTIVE_INODE_RECYCLING, &inode->flags);
- }
- }
-
- /* break links with all the active inodes */
- while (!list_empty(&cookie->backing_inodes)) {
- inode = list_entry(cookie->backing_inodes.next,
- struct cachefs_inode,
- cookie_link);
-
- /* detach each cache inode from the object cookie */
- set_bit(CACHEFS_ACTIVE_INODE_RELEASING, &inode->flags);
-
- list_del_init(&inode->cookie_link);
-
- down(&inode->vfs_inode.i_sem);
- inode->cookie = NULL;
- up(&inode->vfs_inode.i_sem);
-
- if (atomic_dec_and_test(&cookie->usage))
- /* the cookie refcount shouldn't be reduced to 0 yet */
- BUG();
+ inode = container_of(node, struct cachefs_inode, node);
+ inode = cachefs_igrab(inode);
+ ret = (inode ? &inode->node : NULL);
- cachefs_iput(inode);
- }
-
- up_write(&cookie->sem);
-
- if (cookie->iparent)
- atomic_dec(&cookie->iparent->children);
-
- /* finally dispose of the cookie */
- cachefs_cookie_put(cookie);
-
- _leave("");
-
-} /* end __cachefs_relinquish_cookie() */
+ _leave(" = %p", ret);
+ return ret;
-EXPORT_SYMBOL(__cachefs_relinquish_cookie);
+} /* end cachefs_grab_node() */
/*****************************************************************************/
/*
- * update the index entries backing a cookie
+ * lock a semaphore on a node
*/
-void __cachefs_update_cookie(struct cachefs_cookie *cookie)
+static void cachefs_lock_node(struct fscache_node *node)
{
struct cachefs_inode *inode;
- _enter("{%s}",
- cookie &&
- cookie->idef ? (char *) cookie->idef->name : "<file>");
-
- if (cookie == CACHEFS_NEGATIVE_COOKIE) {
- _leave(" [no cookie]");
- return;
- }
-
- down_read(&cookie->sem);
- down_read(&cookie->iparent->sem);
-
- /* update the index entry on disc in each cache backing this cookie */
- list_for_each_entry(inode, &cookie->backing_inodes, cookie_link) {
- cachefs_index_update(inode);
- }
-
- up_read(&cookie->iparent->sem);
- up_read(&cookie->sem);
- _leave("");
-
-} /* end __cachefs_update_cookie() */
-
-EXPORT_SYMBOL(__cachefs_update_cookie);
+ _enter("%p", node);
-/*****************************************************************************/
-/*
- * see if the netfs definition matches
- */
-static cachefs_match_val_t cachefs_fsdef_index_match(void *target,
- const void *entry)
-{
- const struct cachefs_ondisc_fsdef *fsdef = entry;
- struct cachefs_netfs *netfs = target;
-
- _enter("%p,%p", target, entry);
-
- /* name and version must both match with what's on disc */
- _debug("{%s.%u},{%s.%u}",
- netfs->name, netfs->version, fsdef->name, fsdef->version);
-
- if (strncmp(netfs->name, fsdef->name, sizeof(fsdef->name)) != 0) {
- _leave(" = FAILED");
- return CACHEFS_MATCH_FAILED;
- }
-
- if (netfs->version == fsdef->version) {
- _leave(" = SUCCESS");
- return CACHEFS_MATCH_SUCCESS;
- }
-
- /* an entry of the same name but different version is scheduled for
- * deletion */
- _leave(" = SUCCESS_DELETE");
- return CACHEFS_MATCH_SUCCESS_DELETE;
+ inode = container_of(node, struct cachefs_inode, node);
+ down(&inode->vfs_inode.i_sem);
-} /* end cachefs_fsdef_index_match() */
+} /* end cachefs_lock_node() */
/*****************************************************************************/
/*
- * update the netfs definition to be stored on disc
+ * unlock a semaphore on a node
*/
-static void cachefs_fsdef_index_update(void *source, void *entry)
+static void cachefs_unlock_node(struct fscache_node *node)
{
- struct cachefs_ondisc_fsdef *fsdef = entry;
- struct cachefs_netfs *netfs = source;
-
- _enter("{%s.%u},", netfs->name, netfs->version);
+ struct cachefs_inode *inode;
- /* install the netfs name and version in the top-level index entry */
- strncpy(fsdef->name, netfs->name, sizeof(fsdef->name));
+ _enter("%p", node);
- fsdef->version = netfs->version;
+ inode = container_of(node, struct cachefs_inode, node);
+ up(&inode->vfs_inode.i_sem);
-} /* end cachefs_fsdef_index_update() */
+} /* end cachefs_unlock_node() */
/*****************************************************************************/
/*
- * destroy a cookie
+ * dispose of a reference to a node
*/
-static void __cachefs_cookie_put(struct cachefs_cookie *cookie)
+static void cachefs_put_node(struct fscache_node *node)
{
- _enter("");
-
- if (cookie->iparent)
- cachefs_cookie_put(cookie->iparent);
-
- kmem_cache_free(cachefs_cookie_jar, cookie);
+ _enter("%p", node);
- _leave("");
+ if (node)
+ cachefs_iput(container_of(node, struct cachefs_inode, node));
-} /* end __cachefs_cookie_put() */
+} /* end cachefs_put_node() */
/*****************************************************************************/
/*
- * initialise an cookie jar slab element prior to any use
+ * sync a cache
*/
-void cachefs_cookie_init_once(void *_cookie, kmem_cache_t *cachep,
- unsigned long flags)
+static void cachefs_sync(struct fscache_cache *cache)
{
- struct cachefs_cookie *cookie = _cookie;
+ _enter("%p", cache);
- if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
- SLAB_CTOR_CONSTRUCTOR) {
- memset(cookie, 0, sizeof(*cookie));
- INIT_LIST_HEAD(&cookie->search_results);
- INIT_LIST_HEAD(&cookie->backing_inodes);
- init_rwsem(&cookie->sem);
- }
+ /* make sure all pages pinned by operations on behalf of the netfs are
+ * written to disc */
+ cachefs_trans_sync(container_of(cache, struct cachefs_super, cache),
+ CACHEFS_TRANS_SYNC_WAIT_FOR_ACK);
-} /* end cachefs_cookie_init_once() */
+} /* end cachefs_sync() */
/*****************************************************************************/
/*
@@ -1024,9 +164,7 @@
/*****************************************************************************/
/*
* read a page from the cache or allocate a block in which to store it
- * - if the cookie is not backed by a file:
- * - -ENOBUFS will be returned and nothing more will be done
- * - else if the page is backed by a block in the cache:
+ * - if the page is backed by a block in the cache:
* - a read will be started which will call end_io_func on completion
* - the wb-journal will be searched for an entry pertaining to this block
* - if an entry is found:
@@ -1038,44 +176,22 @@
* - the v-journal will be marked to note the block contains invalid data
* - -ENODATA will be returned
*/
-int __cachefs_read_or_alloc_page(struct cachefs_cookie *cookie,
- struct page *page,
- cachefs_rw_complete_t end_io_func,
- void *end_io_data,
- unsigned long gfp)
+static int cachefs_read_or_alloc_page(struct fscache_node *node,
+ struct page *page,
+ struct fscache_page *pageio,
+ fscache_rw_complete_t end_io_func,
+ void *end_io_data,
+ unsigned long gfp)
{
struct cachefs_io_end *end_io = NULL;
struct cachefs_inode *inode;
- struct cachefs_block *block;
- struct cachefs_page *pageio;
+ struct cachefs_block *block = NULL;
struct bio *bio = NULL;
int ret;
- _enter("%p,{%lu},", cookie, page->index);
-
- if (cookie == CACHEFS_NEGATIVE_COOKIE) {
- _leave(" -ENOBUFS [no cookie]");
- return -ENOBUFS; /* no actual cookie */
- }
-
- BUG_ON(cookie->idef); /* not supposed to use this for indexes */
-
- /* get the cache-cookie for this page */
- pageio = cookie->netfs->ops->get_page_cookie(page);
- if (IS_ERR(pageio)) {
- _leave(" = %ld", PTR_ERR(pageio));
- return PTR_ERR(pageio);
- }
-
- /* prevent the file from being uncached whilst we access it */
- block = NULL;
- down_read(&cookie->sem);
+ _enter("");
- /* if there's no disc space whatsoever backing this file, then leave
- * now */
- ret = -ENOBUFS;
- if (list_empty(&cookie->backing_inodes))
- goto error;
+ inode = container_of(node, struct cachefs_inode, node);
/* handle the case of there already being a mapping,
* - must protect against cache removal
@@ -1084,22 +200,14 @@
read_lock(&pageio->lock);
block = pageio->mapped_block;
- if (block && !test_bit(CACHEFS_SUPER_WITHDRAWN, &block->super->flags))
+ if (block && !fscache_is_cache_withdrawn(&block->super->cache))
goto available_on_disc; /* already mapped */
read_unlock(&pageio->lock);
block = NULL;
/* we don't know of a backing page, but there may be one recorded on
- * disc... and if there isn't we'll request one be allocated */
- _debug("igrab");
- inode = cachefs_igrab(list_entry(cookie->backing_inodes.next,
- struct cachefs_inode,
- cookie_link));
- ret = -ENOBUFS;
- if (!inode)
- goto error;
-
+ * disc... and if there isn't we'll request that one be allocated */
_debug("get block");
down(&inode->vfs_inode.i_sem);
@@ -1109,13 +217,13 @@
if (ret < 0)
goto error_i;
- if (!test_and_clear_bit(CACHEFS_PAGE_NEW, &pageio->flags)) {
+ if (!test_and_clear_bit(FSCACHE_PAGE_NEW, &pageio->flags)) {
/* there was data - pin the block underlying it and read */
read_lock(&pageio->lock);
block = pageio->mapped_block;
if (block &&
- !test_bit(CACHEFS_SUPER_WITHDRAWN, &block->super->flags))
+ !fscache_is_cache_withdrawn(&block->super->cache))
goto available_on_disc_i;
/* it went out of service for some reason */
@@ -1127,15 +235,13 @@
/* we allocated a new block, but didn't assign any data to it */
up(&inode->vfs_inode.i_sem);
- cachefs_iput(inode);
/* point the mapped block at its referencer */
- write_lock(&pageio->mapped_block->ref_lock);
- pageio->mapped_block->ref = pageio;
- write_unlock(&pageio->mapped_block->ref_lock);
+ write_lock(&cachefs_mapped_block(pageio)->ref_lock);
+ cachefs_mapped_block(pageio)->ref = pageio;
+ write_unlock(&cachefs_mapped_block(pageio)->ref_lock);
- _debug("no data [bix=%u ref=%p]", pageio->mapped_block->bix, pageio);
- up_read(&cookie->sem);
+ _debug("no data [bix=%u ref=%p]", cachefs_mapped_bix(pageio), pageio);
/* tell the caller we've allocated a block, but we don't have any data
* for them */
@@ -1147,7 +253,6 @@
available_on_disc_i:
_debug("available_i");
up(&inode->vfs_inode.i_sem);
- cachefs_iput(inode);
available_on_disc:
_debug("available");
@@ -1166,7 +271,7 @@
end_io->func = end_io_func;
end_io->data = end_io_data;
- end_io->cookie_data = cookie->netfs_data;
+ end_io->cookie_data = node->cookie->netfs_data;
end_io->block = block;
/* dispatch an operation to the block device */
@@ -1187,7 +292,6 @@
submit_bio(READ, bio);
_debug("done");
- up_read(&cookie->sem);
/* point the mapped block at its referencer */
write_lock(&block->ref_lock);
@@ -1205,10 +309,8 @@
error_i:
_debug("error_i");
up(&inode->vfs_inode.i_sem);
- cachefs_iput(inode);
error:
_debug("error");
- up_read(&cookie->sem);
cachefs_block_put(block);
if (bio)
bio_put(bio);
@@ -1219,9 +321,7 @@
_leave(" = %d", ret);
return ret;
-} /* end __cachefs_read_or_alloc_page() */
-
-EXPORT_SYMBOL(__cachefs_read_or_alloc_page);
+} /* end cachefs_read_or_alloc_page() */
/*****************************************************************************/
/*
@@ -1282,41 +382,25 @@
* be erased
* - returns 0
*/
-int __cachefs_write_page(struct cachefs_cookie *cookie,
- struct page *page,
- cachefs_rw_complete_t end_io_func,
- void *end_io_data,
- unsigned long gfp)
+static int cachefs_write_page(struct fscache_node *node,
+ struct page *page,
+ struct fscache_page *pageio,
+ fscache_rw_complete_t end_io_func,
+ void *end_io_data,
+ unsigned long gfp)
{
struct cachefs_io_end *end_io = NULL;
struct cachefs_block *block;
- struct cachefs_page *pageio;
struct bio *bio = NULL;
int ret;
- _enter("%p,{%lu},", cookie, page->index);
-
- if (cookie == CACHEFS_NEGATIVE_COOKIE) {
- _leave(" -ENOBUFS [no cookie]");
- return -ENOBUFS; /* no actual cookie */
- }
-
- BUG_ON(cookie->idef); /* not supposed to use this for indexes */
-
- /* get the cache-cookie for this page */
- pageio = cookie->netfs->ops->get_page_cookie(page);
- if (IS_ERR(pageio)) {
- _leave(" = %ld", PTR_ERR(pageio));
- return PTR_ERR(pageio);
- }
+ _enter("");
- /* prevent the file from been uncached whilst we deal with it */
- down_read(&cookie->sem);
read_lock(&pageio->lock);
/* only write if there's somewhere to write to */
- block = pageio->mapped_block;
- if (!block || test_bit(CACHEFS_SUPER_WITHDRAWN, &block->super->flags))
+ block = cachefs_mapped_block(pageio);
+ if (!block || fscache_is_cache_withdrawn(&block->super->cache))
goto no_block;
/* pin the block and drop the lock */
@@ -1334,7 +418,7 @@
end_io->func = end_io_func;
end_io->data = end_io_data;
- end_io->cookie_data = cookie->netfs_data;
+ end_io->cookie_data = node->cookie->netfs_data;
end_io->block = block;
/* dispatch an operation to the block device */
@@ -1355,11 +439,10 @@
if (!bio_add_page(bio, page, PAGE_SIZE, 0))
BUG();
- //dump_bio(bio,1);
+ //dump_bio(bio, 1);
submit_bio(WRITE, bio);
/* tell the caller it's in progress */
- up_read(&cookie->sem);
_leave(" = 0");
return 0;
@@ -1368,7 +451,6 @@
clear_bit(CACHEFS_BLOCK_NETFSBUSY, &block->flags);
wake_up(&block->writewq);
cachefs_block_put(block);
- up_read(&cookie->sem);
if (bio)
bio_put(bio);
if (end_io) {
@@ -1381,40 +463,23 @@
/* tell the caller there wasn't a block to write into */
no_block:
read_unlock(&pageio->lock);
- up_read(&cookie->sem);
_leave(" = -ENOBUFS");
return -ENOBUFS;
-} /* end __cachefs_write_page() */
-
-EXPORT_SYMBOL(__cachefs_write_page);
+} /* end cachefs_write_page() */
/*****************************************************************************/
/*
- * remove a page from the cache
+ * detach a backing block from a page
* - if the block backing the page still has a vjentry then the block will be
* recycled
*/
-void __cachefs_uncache_page(struct cachefs_cookie *cookie, struct page *page)
+static void cachefs_uncache_page(struct fscache_node *node,
+ struct fscache_page *pageio)
{
struct cachefs_block *block, *xblock;
- struct cachefs_page *pageio;
-
- _enter(",{%lu}", page->index);
-
- if (cookie == CACHEFS_NEGATIVE_COOKIE) {
- _leave(" [no cookie]");
- return;
- }
- BUG_ON(cookie->idef); /* not supposed to use this for indexes */
-
- /* get the cache-cookie for this page */
- pageio = cookie->netfs->ops->get_page_cookie(page);
- if (IS_ERR(pageio)) {
- _leave(" [get_page_cookie() = %ld]", PTR_ERR(pageio));
- return;
- }
+ _enter("");
/* un-cross-link the page cookie and the block */
xblock = NULL;
@@ -1448,8 +513,22 @@
}
_leave("");
- return;
-} /* end __cachefs_uncache_page() */
+} /* end cachefs_uncache_page() */
-EXPORT_SYMBOL(__cachefs_uncache_page);
+struct fscache_cache_ops cachefs_cache_ops = {
+ .name = "cachefs",
+ .lookup_node = cachefs_lookup_node,
+ .grab_node = cachefs_grab_node,
+ .lock_node = cachefs_lock_node,
+ .unlock_node = cachefs_unlock_node,
+ .put_node = cachefs_put_node,
+ .index_search = cachefs_index_search,
+ .index_add = cachefs_index_add,
+ .index_update = cachefs_index_update,
+ .sync = cachefs_sync,
+ .dissociate_pages = cachefs_block_dissociate,
+ .read_or_alloc_page = cachefs_read_or_alloc_page,
+ .write_page = cachefs_write_page,
+ .uncache_page = cachefs_uncache_page,
+};
diff -uNr linux-2.6.9-rc2-mm4/fs/cachefs/journal.c linux-2.6.9-rc2-mm4-fscache/fs/cachefs/journal.c
--- linux-2.6.9-rc2-mm4/fs/cachefs/journal.c 2004-09-27 11:23:55.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/cachefs/journal.c 2004-10-04 15:18:28.975573470 +0100
@@ -430,7 +430,7 @@
offset = (trans->index << super->sb->s_blocksize_bits) & ~PAGE_MASK;
jentry = kmap_atomic(trans->jpage, KM_USER0) + offset;
memcpy(jentry, trans->jentry, super->sb->s_blocksize);
- kunmap_atomic(trans->jpage, KM_USER0);
+ kunmap_atomic(jentry, KM_USER0);
SetPageWriteback(trans->jpage);
@@ -1678,11 +1678,11 @@
unsigned int bytes_done,
int error)
{
- kenter("%p{%lx},%u,%d", bio, bio->bi_flags, bytes_done, error);
+ _enter("%p{%lx},%u,%d", bio, bio->bi_flags, bytes_done, error);
/* we're only interested in completion */
if (bio->bi_size > 0) {
- kleave(" = 1");
+ _leave(" = 1");
return 1;
}
@@ -1690,7 +1690,7 @@
end_page_writeback(bio->bi_io_vec[0].bv_page);
bio_put(bio);
- kleave(" = 0");
+ _leave(" = 0");
return 0;
} /* end cachefs_trans_ack_written() */
diff -uNr linux-2.6.9-rc2-mm4/fs/cachefs/linear-io.c linux-2.6.9-rc2-mm4-fscache/fs/cachefs/linear-io.c
--- linux-2.6.9-rc2-mm4/fs/cachefs/linear-io.c 2004-09-27 11:23:55.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/cachefs/linear-io.c 2004-09-30 17:18:42.000000000 +0100
@@ -57,14 +57,14 @@
cachefs_blockix_t *last_block_in_bio)
{
struct cachefs_block *block;
- struct cachefs_page *pageio;
+ struct fscache_page *pageio;
struct inode *inode = page->mapping->host;
int ret;
_enter("");
/* get the page mapping cookie */
- pageio = cachefs_page_get_private(page, GFP_KERNEL);
+ pageio = fscache_page_get_private(page, GFP_KERNEL);
if (IS_ERR(pageio)) {
ret = PTR_ERR(pageio);
goto error;
@@ -176,7 +176,7 @@
int cachefs_linear_io_readpage(struct file *file, struct page *page)
{
struct cachefs_block *block;
- struct cachefs_page *pageio;
+ struct fscache_page *pageio;
struct inode *inode = page->mapping->host;
struct bio *bio;
int ret;
@@ -184,7 +184,7 @@
_enter(",{%lu}", page->index);
/* get the page mapping cookie */
- pageio = cachefs_page_get_private(page, GFP_KERNEL);
+ pageio = fscache_page_get_private(page, GFP_KERNEL);
if (IS_ERR(pageio)) {
_leave(" = %ld [pgp]", PTR_ERR(pageio));
return PTR_ERR(pageio);
diff -uNr linux-2.6.9-rc2-mm4/fs/cachefs/main.c linux-2.6.9-rc2-mm4-fscache/fs/cachefs/main.c
--- linux-2.6.9-rc2-mm4/fs/cachefs/main.c 2004-09-27 11:23:56.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/cachefs/main.c 2004-09-30 17:19:20.000000000 +0100
@@ -36,21 +36,8 @@
{
int ret;
- /* create ourselves a cookie jar and a block jar */
+ /* create a block jar */
ret = -ENOMEM;
- cachefs_cookie_jar =
- kmem_cache_create("cachefs_cookie_jar",
- sizeof(struct cachefs_cookie),
- 0,
- SLAB_HWCACHE_ALIGN,
- cachefs_cookie_init_once,
- NULL);
- if (!cachefs_cookie_jar) {
- printk(KERN_NOTICE
- "CacheFS: Failed to allocate a cookie jar\n");
- goto error;
- }
-
cachefs_block_jar =
kmem_cache_create("cachefs_block_jar",
sizeof(struct cachefs_block),
@@ -61,7 +48,7 @@
if (!cachefs_block_jar) {
printk(KERN_NOTICE
"CacheFS: Failed to allocate a block jar\n");
- goto error_cookie_jar;
+ goto error;
}
/* initialise the filesystem */
@@ -75,8 +62,6 @@
error_block_jar:
kmem_cache_destroy(cachefs_block_jar);
- error_cookie_jar:
- kmem_cache_destroy(cachefs_cookie_jar);
error:
printk(KERN_ERR "CacheFS: failed to register: %d\n", ret);
return ret;
@@ -92,7 +77,6 @@
cachefs_fs_exit();
kmem_cache_destroy(cachefs_block_jar);
- kmem_cache_destroy(cachefs_cookie_jar);
} /* end cachefs_exit() */
diff -uNr linux-2.6.9-rc2-mm4/fs/cachefs/misc.c linux-2.6.9-rc2-mm4-fscache/fs/cachefs/misc.c
--- linux-2.6.9-rc2-mm4/fs/cachefs/misc.c 2004-09-27 11:23:56.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/cachefs/misc.c 2004-09-30 19:06:08.000000000 +0100
@@ -30,10 +30,10 @@
* get a page caching token from for a page, allocating it and attaching it to
* the page's private pointer if it doesn't exist
*/
-struct cachefs_page * __cachefs_page_get_private(struct page *page,
+struct fscache_page * __cachefs_page_get_private(struct page *page,
unsigned gfp_flags)
{
- struct cachefs_page *pageio = (struct cachefs_page *) page->private;
+ struct fscache_page *pageio = (struct fscache_page *) page->private;
if (!pageio) {
pageio = kmalloc(sizeof(*pageio), gfp_flags);
@@ -145,7 +145,7 @@
*/
int cachefs_invalidatepage(struct page *page, unsigned long offset)
{
- struct cachefs_page *pageio;
+ struct fscache_page *pageio;
int ret = 1;
_enter("{%lu},%lu", page->index, offset);
@@ -153,7 +153,7 @@
BUG_ON(!PageLocked(page));
if (PagePrivate(page)) {
- pageio = (struct cachefs_page *) page->private;
+ pageio = (struct fscache_page *) page->private;
pageio->flags = 0;
/* we release page attachments only if the entire page is being
@@ -179,14 +179,14 @@
int cachefs_releasepage(struct page *page, int gfp_flags)
{
struct cachefs_block *block;
- struct cachefs_page *pageio;
+ struct fscache_page *pageio;
_enter("{%lu},%x", page->index, gfp_flags);
/* detach the page mapping cookie and mapped block */
if (PagePrivate(page)) {
/* detach the mapped block from the page if there is one */
- pageio = (struct cachefs_page *) page->private;
+ pageio = (struct fscache_page *) page->private;
page->private = 0;
ClearPagePrivate(page);
diff -uNr linux-2.6.9-rc2-mm4/fs/cachefs/recycling.c linux-2.6.9-rc2-mm4-fscache/fs/cachefs/recycling.c
--- linux-2.6.9-rc2-mm4/fs/cachefs/recycling.c 2004-09-27 11:23:56.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/cachefs/recycling.c 2004-09-30 17:37:41.000000000 +0100
@@ -561,7 +561,7 @@
cachefs_metadata_postread(iinode, metadata);
cachefs_trans_affects_page(trans,
- cachefs_page_grab_private(ixpage),
+ fscache_page_grab_private(ixpage),
trans->jentry->entry,
trans->jentry->count);
diff -uNr linux-2.6.9-rc2-mm4/fs/cachefs/replay.c linux-2.6.9-rc2-mm4-fscache/fs/cachefs/replay.c
--- linux-2.6.9-rc2-mm4/fs/cachefs/replay.c 2004-09-27 11:23:56.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/cachefs/replay.c 2004-10-04 13:33:04.681820540 +0100
@@ -1638,7 +1638,7 @@
cachefs_trans_replays_effect(trans, ptrblock, "ptr");
}
- kunmap_atomic(ptrpage, KM_USER0);
+ kunmap_atomic(pbix, KM_USER0);
}
/* make sure the vjournal entry is cleared */
diff -uNr linux-2.6.9-rc2-mm4/fs/cachefs/rootdir.c linux-2.6.9-rc2-mm4-fscache/fs/cachefs/rootdir.c
--- linux-2.6.9-rc2-mm4/fs/cachefs/rootdir.c 2004-09-27 11:23:56.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/cachefs/rootdir.c 2004-09-30 17:48:40.000000000 +0100
@@ -722,7 +722,7 @@
trans->jentry->block = __cachefs_get_page_block(ixpage)->bix;
cachefs_trans_affects_inode(trans, inode);
- cachefs_trans_affects_page(trans, cachefs_page_grab_private(ixpage),
+ cachefs_trans_affects_page(trans, fscache_page_grab_private(ixpage),
trans->jentry->entry, sizeof(*xent));
/* write the transaction mark to the journal */
diff -uNr linux-2.6.9-rc2-mm4/fs/cachefs/super.c linux-2.6.9-rc2-mm4-fscache/fs/cachefs/super.c
--- linux-2.6.9-rc2-mm4/fs/cachefs/super.c 2004-09-27 11:23:56.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/cachefs/super.c 2004-10-04 17:20:14.014693842 +0100
@@ -133,6 +133,13 @@
sb = get_sb_bdev(fs_type, flags, dev_name, options,
cachefs_fill_super);
+ _debug("backing nodes %p: %p,%p -> %p,%p",
+ &fscache_fsdef_index.backing_nodes,
+ fscache_fsdef_index.backing_nodes.next,
+ fscache_fsdef_index.backing_nodes.prev,
+ fscache_fsdef_index.backing_nodes.next->next,
+ fscache_fsdef_index.backing_nodes.next->prev);
+
_leave(" = %p", sb);
return sb;
@@ -222,7 +229,6 @@
*/
static int cachefs_fill_super(struct super_block *sb, void *_data, int silent)
{
- struct cachefs_search_result *srch = NULL;
struct cachefs_super *super = NULL;
struct cachefs_inode *inode = NULL, *inode2;
struct dentry *root = NULL;
@@ -267,10 +273,6 @@
super->vjnl_count = CACHEFS_ONDISC_VJNL_ENTS;
- srch = kmalloc(sizeof(*srch), GFP_KERNEL);
- if (!srch)
- goto error;
-
/* initialise the superblock */
sb->s_magic = CACHEFS_FS_MAGIC;
sb->s_op = &cachefs_super_ops;
@@ -278,10 +280,13 @@
super->sb = sb;
super->ujnl_step = bdev_hardsect_size(super->sb->s_bdev);
- INIT_LIST_HEAD(&super->mnt_link);
-
- INIT_LIST_HEAD(&super->ino_list);
- spin_lock_init(&super->ino_list_lock);
+ fscache_init_cache(&super->cache,
+ &cachefs_cache_ops,
+ CACHEFS_INO_FSDEF_CATALOGUE,
+ "%02x:%02x",
+ MAJOR(sb->s_dev),
+ MINOR(sb->s_dev)
+ );
rwlock_init(&super->blk_tree_lock);
@@ -455,17 +460,12 @@
goto error;
}
- cachefs_add_cache((struct cachefs_super *) sb->s_fs_info, srch);
+ fscache_add_cache(&super->cache);
_leave(" = 0 [super=%p]", super);
return 0;
error:
- if (srch) {
- dbgfree(srch);
- kfree(srch);
- }
-
if (super) {
if (super->dmn_task) {
super->dmn_die = 1;
@@ -628,7 +628,7 @@
metadata->mtime = CURRENT_TIME.tv_sec;
metadata->atime = CURRENT_TIME.tv_sec;
- metadata->index.dsize = sizeof(struct cachefs_ondisc_fsdef);
+ metadata->index.dsize = sizeof(struct fscache_fsdef_index_entry);
metadata->index.esize = sizeof(struct cachefs_ondisc_index_entry);
metadata->index.esize += metadata->index.dsize;
metadata->index.keys[0] = CACHEFS_ONDISC_INDEXKEY_ASCIIZ | 24;
@@ -805,7 +805,7 @@
BUG_ON(!super);
/* detach the cache from all cookies that reference it */
- cachefs_withdraw_cache(super);
+ fscache_withdraw_cache(&super->cache);
/* wait for validity journalling to be sorted */
if (!list_empty(&super->vjnl_unallocq) ||
@@ -897,14 +897,14 @@
{
struct cachefs_inode *inode = _inode;
+ _enter("%p,,1", _inode);
+
if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
SLAB_CTOR_CONSTRUCTOR) {
memset(inode, 0, sizeof(*inode));
inode_init_once(&inode->vfs_inode);
init_rwsem(&inode->metadata_sem);
-
- INIT_LIST_HEAD(&inode->cookie_link);
- INIT_LIST_HEAD(&inode->super_link);
+ fscache_node_init(&inode->node);
}
} /* end cachefs_i_init_once() */
@@ -922,6 +922,7 @@
if (!inode)
return NULL;
+ _leave(" = %p", &inode->vfs_inode);
return &inode->vfs_inode;
} /* end cachefs_alloc_inode() */
diff -uNr linux-2.6.9-rc2-mm4/fs/cachefs/vjournal.c linux-2.6.9-rc2-mm4-fscache/fs/cachefs/vjournal.c
--- linux-2.6.9-rc2-mm4/fs/cachefs/vjournal.c 2004-09-27 11:23:56.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/cachefs/vjournal.c 2004-10-04 13:34:40.114592740 +0100
@@ -307,7 +307,7 @@
ptr = kmap_atomic(vjentry->vpage, KM_USER0);
memset(ptr + vjentry->ventry, 0,
sizeof(struct cachefs_ondisc_validity_journal));
- kunmap_atomic(vjentry->vpage, KM_USER0);
+ kunmap_atomic(ptr, KM_USER0);
/* queue the transaction to be written to disc */
cachefs_trans_commit(trans);
@@ -380,7 +380,7 @@
struct cachefs_ondisc_validity_journal *vjmark;
struct cachefs_vj_entry *vjentry;
struct cachefs_super *super = (struct cachefs_super *) desc->arg.buf;
- struct cachefs_page *pageio;
+ struct fscache_page *pageio;
unsigned long stop;
void *data;
int ret;
@@ -395,7 +395,7 @@
stop = offset + size;
- pageio = cachefs_page_grab_private(page);
+ pageio = fscache_page_grab_private(page);
cachefs_block_set(super, pageio->mapped_block, page, pageio);
data = kmap(page);
@@ -483,7 +483,7 @@
/* validate it */
ret = -EINVAL;
- if (inode->flags & CACHEFS_ACTIVE_INODE_ISINDEX) {
+ if (inode->node.flags & FSCACHE_NODE_ISINDEX) {
printk("CacheFS: Index inode %x has block in v-journal\n",
vjentry->ino);
goto error2;
@@ -606,10 +606,10 @@
/* get the block number for this level */
if (!step->bix) {
- u8 *data = kmap(step[1].page);
+ u8 *data = kmap_atomic(step[1].page, KM_USER0);
step->bix =
*(cachefs_blockix_t *)(data + step->offset);
- kunmap(step[1].page);
+ kunmap_atomic(data, KM_USER0);
}
/* allocate this block if necessary */
diff -uNr linux-2.6.9-rc2-mm4/fs/fscache/cookie.c linux-2.6.9-rc2-mm4-fscache/fs/fscache/cookie.c
--- linux-2.6.9-rc2-mm4/fs/fscache/cookie.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/fscache/cookie.c 2004-10-04 15:10:07.165240574 +0100
@@ -0,0 +1,1000 @@
+/* cookie.c: general filesystem cache cookie management
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at 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/module.h>
+#include "fscache-int.h"
+
+LIST_HEAD(fscache_netfs_list);
+LIST_HEAD(fscache_cache_list);
+DECLARE_RWSEM(fscache_addremove_sem);
+
+kmem_cache_t *fscache_cookie_jar;
+
+static void fscache_withdraw_node(struct fscache_cache *cache,
+ struct fscache_node *node);
+
+/*****************************************************************************/
+/*
+ * register a network filesystem for caching
+ */
+int __fscache_register_netfs(struct fscache_netfs *netfs,
+ struct fscache_index_def *primary_idef)
+{
+ struct fscache_netfs *ptr;
+ int ret;
+
+ _enter("{%s}", netfs->name);
+
+ INIT_LIST_HEAD(&netfs->link);
+
+ /* allocate a cookie for the primary index */
+ netfs->primary_index =
+ kmem_cache_alloc(fscache_cookie_jar, SLAB_KERNEL);
+
+ if (!netfs->primary_index) {
+ _leave(" = -ENOMEM");
+ return -ENOMEM;
+ }
+
+ /* initialise the primary index cookie */
+ memset(netfs->primary_index, 0, sizeof(*netfs->primary_index));
+
+ atomic_set(&netfs->primary_index->usage, 1);
+ atomic_set(&netfs->primary_index->children, 0);
+
+ netfs->primary_index->idef = primary_idef;
+ netfs->primary_index->iparent = &fscache_fsdef_index;
+ netfs->primary_index->netfs = netfs;
+ netfs->primary_index->netfs_data = netfs;
+
+ atomic_inc(&netfs->primary_index->iparent->usage);
+ atomic_inc(&netfs->primary_index->iparent->children);
+
+ rwlock_init(&netfs->primary_index->lock);
+ init_rwsem(&netfs->primary_index->sem);
+ INIT_LIST_HEAD(&netfs->primary_index->search_results);
+ INIT_LIST_HEAD(&netfs->primary_index->backing_nodes);
+
+ /* check the netfs type is not already present */
+ down_write(&fscache_addremove_sem);
+
+ ret = -EEXIST;
+ list_for_each_entry(ptr, &fscache_netfs_list, link) {
+ if (strcmp(ptr->name, netfs->name) == 0)
+ goto already_registered;
+ }
+
+ list_add(&netfs->link, &fscache_netfs_list);
+ ret = 0;
+
+ printk("Fscache: netfs '%s' registered for caching\n", netfs->name);
+
+ already_registered:
+ up_write(&fscache_addremove_sem);
+
+ if (ret < 0) {
+ netfs->primary_index->iparent = NULL;
+ __fscache_cookie_put(netfs->primary_index);
+ netfs->primary_index = NULL;
+ }
+
+ _leave(" = %d", ret);
+ return ret;
+
+} /* end __fscache_register_netfs() */
+
+EXPORT_SYMBOL(__fscache_register_netfs);
+
+/*****************************************************************************/
+/*
+ * unregister a network filesystem from the cache
+ * - all cookies must have been released first
+ */
+void __fscache_unregister_netfs(struct fscache_netfs *netfs)
+{
+ _enter("{%s.%u}", netfs->name, netfs->version);
+
+ down_write(&fscache_addremove_sem);
+
+ list_del(&netfs->link);
+ fscache_relinquish_cookie(netfs->primary_index, 0);
+
+ up_write(&fscache_addremove_sem);
+
+ printk("Fscache: netfs '%s' unregistered from caching\n", netfs->name);
+
+ _leave("");
+
+} /* end __fscache_unregister_netfs() */
+
+EXPORT_SYMBOL(__fscache_unregister_netfs);
+
+/*****************************************************************************/
+/*
+ * initialise a cache record
+ */
+void fscache_init_cache(struct fscache_cache *cache,
+ struct fscache_cache_ops *ops,
+ unsigned fsdef_ino,
+ const char *idfmt,
+ ...)
+{
+ va_list va;
+
+ memset(cache, 0, sizeof(*cache));
+
+ cache->ops = ops;
+
+ va_start(va, idfmt);
+ vsnprintf(cache->identifier, sizeof(cache->identifier), idfmt, va);
+ va_end(va);
+
+ INIT_LIST_HEAD(&cache->link);
+ INIT_LIST_HEAD(&cache->node_list);
+ spin_lock_init(&cache->node_list_lock);
+
+ INIT_LIST_HEAD(&cache->fsdef_srch.link);
+ cache->fsdef_srch.cache = cache;
+ cache->fsdef_srch.ino = fsdef_ino;
+
+} /* end fscache_init_cache() */
+
+EXPORT_SYMBOL(fscache_init_cache);
+
+/*****************************************************************************/
+/*
+ * declare a mounted cache as being open for business
+ */
+void fscache_add_cache(struct fscache_cache *cache)
+{
+ struct fscache_node *ifsdef;
+
+ BUG_ON(!cache->ops);
+
+ _enter("{%s.%s}", cache->ops->name, cache->identifier);
+
+ /* prepare an active-node record for the FSDEF index of this cache */
+ ifsdef = cache->ops->lookup_node(cache, cache->fsdef_srch.ino);
+ BUG_ON(IS_ERR(ifsdef)); /* there shouldn't be an error as FSDEF is the
+ * root dir of the FS and so should already be
+ * in core */
+
+ if (!cache->ops->grab_node(ifsdef))
+ BUG();
+
+ ifsdef->cookie = &fscache_fsdef_index;
+
+ down_write(&fscache_addremove_sem);
+
+ /* add the cache to the list */
+ list_add(&cache->link, &fscache_cache_list);
+
+ /* add the cache's netfs definition index node to the cache's
+ * list */
+ spin_lock(&cache->node_list_lock);
+ list_add_tail(&ifsdef->cache_link, &cache->node_list);
+ spin_unlock(&cache->node_list_lock);
+
+ /* add the cache's netfs definition index node to the top level index
+ * cookie as a known backing node */
+ down_write(&fscache_fsdef_index.sem);
+
+ list_add_tail(&cache->fsdef_srch.link,
+ &fscache_fsdef_index.search_results);
+ list_add_tail(&ifsdef->cookie_link,
+ &fscache_fsdef_index.backing_nodes);
+
+ atomic_inc(&fscache_fsdef_index.usage);
+
+ /* done */
+ up_write(&fscache_fsdef_index.sem);
+ up_write(&fscache_addremove_sem);
+ _leave("");
+
+} /* end fscache_add_cache() */
+
+EXPORT_SYMBOL(fscache_add_cache);
+
+/*****************************************************************************/
+/*
+ * withdraw an unmounted cache from the active service
+ */
+void fscache_withdraw_cache(struct fscache_cache *cache)
+{
+ struct fscache_node *node;
+
+ _enter("");
+
+ /* make the cache unavailable for cookie acquisition */
+ set_bit(FSCACHE_CACHE_WITHDRAWN, &cache->flags);
+
+ down_write(&fscache_addremove_sem);
+ list_del_init(&cache->link);
+ up_write(&fscache_addremove_sem);
+
+ /* mark all nodes as being withdrawn */
+ spin_lock(&cache->node_list_lock);
+ list_for_each_entry(node, &cache->node_list, cache_link) {
+ set_bit(FSCACHE_NODE_WITHDRAWN, &node->flags);
+ }
+ spin_unlock(&cache->node_list_lock);
+
+ /* make sure all pages pinned by operations on behalf of the netfs are
+ * written to disc */
+ cache->ops->sync(cache);
+
+ /* dissociate all the netfs pages backed by this cache from the block
+ * mappings in the cache */
+ cache->ops->dissociate_pages(cache);
+
+ /* we now have to destroy all the active nodes pertaining to this
+ * cache */
+ spin_lock(&cache->node_list_lock);
+
+ while (!list_empty(&cache->node_list)) {
+ node = list_entry(cache->node_list.next, struct fscache_node,
+ cache_link);
+ list_del(&node->cache_link);
+ spin_unlock(&cache->node_list_lock);
+
+ /* we've extracted an active node from the tree - now dispose
+ * of it */
+ fscache_withdraw_node(cache, node);
+ cache->ops->put_node(node);
+
+ spin_lock(&cache->node_list_lock);
+ }
+
+ spin_unlock(&cache->node_list_lock);
+
+ _leave("");
+
+} /* end fscache_withdraw_cache() */
+
+EXPORT_SYMBOL(fscache_withdraw_cache);
+
+/*****************************************************************************/
+/*
+ * withdraw an node from active service
+ * - need break the links to a cached object cookie
+ * - called under two situations:
+ * (1) recycler decides to reclaim an in-use node
+ * (2) a cache is unmounted
+ * - have to take care as the cookie can be being relinquished by the netfs
+ * simultaneously
+ * - the active node is pinned by the caller holding a refcount on it
+ */
+static void fscache_withdraw_node(struct fscache_cache *cache,
+ struct fscache_node *node)
+{
+ struct fscache_search_result *srch;
+ struct fscache_cookie *cookie, *xcookie = NULL;
+
+ _enter("");
+
+ /* first of all we have to break the links between the node and the
+ * cookie
+ * - we have to hold both semaphores BUT we have to get the cookie sem
+ * FIRST
+ */
+ cache->ops->lock_node(node);
+
+ cookie = node->cookie;
+ if (cookie) {
+ /* pin the cookie so that is doesn't escape */
+ atomic_inc(&cookie->usage);
+
+ /* re-order the locks to avoid deadlock */
+ cache->ops->unlock_node(node);
+ down_write(&cookie->sem);
+ cache->ops->lock_node(node);
+
+ /* erase references from the node to the cookie */
+ list_del_init(&node->cookie_link);
+
+ xcookie = node->cookie;
+ node->cookie = NULL;
+
+ /* delete the search result record for this node from the
+ * cookie's list */
+ list_for_each_entry(srch, &cookie->search_results, link) {
+ if (srch->cache == cache)
+ goto found_record;
+ }
+ BUG();
+
+ found_record:
+ list_del_init(&srch->link);
+
+ if (srch != &cache->fsdef_srch) {
+ dbgfree(srch);
+ kfree(srch);
+ }
+
+ up_write(&cookie->sem);
+ }
+
+ cache->ops->unlock_node(node);
+
+ /* we've broken the links between cookie and node */
+ _debug("broken links");
+
+ if (xcookie) {
+ fscache_cookie_put(xcookie);
+ cache->ops->put_node(node);
+ }
+
+ /* unpin the cookie */
+ if (cookie)
+ fscache_cookie_put(cookie);
+
+ _leave("");
+
+} /* end fscache_withdraw_node() */
+
+/*****************************************************************************/
+/*
+ * search for representation of an object in its parent cache
+ * - the cookie must be locked by the caller
+ * - returns -ENODATA if the object or one of its ancestors doesn't exist
+ */
+static int fscache_search_for_object(struct fscache_cookie *cookie,
+ struct fscache_cache *cache)
+{
+ struct fscache_search_result *srch;
+ struct fscache_cookie *iparent;
+ struct fscache_node *ipnode, *node;
+ int ret;
+
+ iparent = cookie->iparent;
+ if (!iparent) {
+ /* FSDEF entries don't have a parent */
+ _enter("{.fsdef},%s.%s",
+ cache->ops->name, cache->identifier);
+ BUG_ON(list_empty(&cookie->backing_nodes));
+ BUG_ON(list_empty(&cookie->search_results));
+ _leave(" = 0 [.fsdef]");
+ return 0;
+ }
+
+ _enter("{%s/%s},%s.%s",
+ iparent->idef->name,
+ cookie->idef ? (char *) cookie->idef->name : "<file>",
+ cache->ops->name, cache->identifier);
+
+ /* see if there's a search result for this object already */
+ list_for_each_entry(srch, &cookie->search_results, link) {
+ _debug("check entry %p x %p [ino %u]",
+ cookie, cache, srch->ino);
+
+ if (srch->cache == cache) {
+ _debug("found entry");
+
+ if (srch->ino) {
+ _leave(" = 0 [found ino %u]", srch->ino);
+ return 0;
+ }
+
+ /* entry is negative */
+ _leave(" = -ENODATA");
+ return -ENODATA;
+ }
+ }
+
+ /* allocate an initially negative entry for this object */
+ _debug("alloc entry %p x %p", cookie, cache);
+
+ srch = kmalloc(sizeof(*srch), GFP_KERNEL);
+ if (!srch) {
+ _leave(" = -ENOMEM");
+ return -ENOMEM;
+ }
+
+ srch->cache = cache;
+ srch->ino = 0;
+ INIT_LIST_HEAD(&srch->link);
+
+ /* we need see if there's an entry for this cache in this object's
+ * parent index, so the first thing to do is to see if the parent index
+ * is represented on disc
+ */
+ down_read(&iparent->sem);
+
+ _debug("backing nodes %p: %p,%p -> %p,%p",
+ &iparent->backing_nodes,
+ iparent->backing_nodes.next,
+ iparent->backing_nodes.prev,
+ iparent->backing_nodes.next->next,
+ iparent->backing_nodes.next->prev);
+
+ ret = fscache_search_for_object(iparent, cache);
+ if (ret < 0) {
+ if (ret != -ENODATA)
+ goto error;
+
+ /* set a negative entry */
+ list_add_tail(&srch->link, &cookie->search_results);
+ goto done;
+ }
+
+ /* find the parent's backing node */
+ _debug("X backing nodes %p: %p,%p -> %p,%p",
+ &iparent->backing_nodes,
+ iparent->backing_nodes.next,
+ iparent->backing_nodes.prev,
+ iparent->backing_nodes.next->next,
+ iparent->backing_nodes.next->prev
+ );
+
+ _debug("X search results %p: %p,%p -> %p,%p",
+ &iparent->search_results,
+ iparent->search_results.next,
+ iparent->search_results.prev,
+ iparent->search_results.next->next,
+ iparent->search_results.next->prev);
+
+ read_lock(&iparent->lock);
+ list_for_each_entry(ipnode, &iparent->backing_nodes, cookie_link) {
+ _debug("bnode %p -> %p", ipnode, ipnode->cache);
+
+ if (ipnode->cache == cache)
+ goto found_parent_entry;
+ }
+
+ BUG();
+
+ found_parent_entry:
+ read_unlock(&iparent->lock);
+ _debug("found_parent_entry");
+
+ /* search the parent index for a reference compatible with this
+ * object */
+ ret = cache->ops->index_search(ipnode, cookie, srch);
+ switch (ret) {
+ default:
+ goto error;
+
+ case 0:
+ /* found - allocate an node */
+ node = cache->ops->lookup_node(cache, srch->ino);
+ if (IS_ERR(node)) {
+ ret = PTR_ERR(node);
+ goto error;
+ }
+
+ cache->ops->lock_node(node);
+
+ BUG_ON(!list_empty(&node->cookie_link));
+
+ /* attach the node to the cache's node list */
+ if (list_empty(&node->cache_link)) {
+ if (!cache->ops->grab_node(node))
+ goto igrab_failed_upput;
+
+ spin_lock(&cache->node_list_lock);
+ list_add_tail(&node->cache_link, &cache->node_list);
+ spin_unlock(&cache->node_list_lock);
+ }
+
+ /* attach the node to the cookie */
+ node->cookie = cookie;
+ atomic_inc(&cookie->usage);
+
+ write_lock(&iparent->lock);
+ list_add_tail(&srch->link, &cookie->search_results);
+ list_add_tail(&node->cookie_link, &cookie->backing_nodes);
+ write_unlock(&iparent->lock);
+
+ cache->ops->unlock_node(node);
+ break;
+
+ case -ENOENT:
+ /* we can at least set a valid negative entry */
+ list_add_tail(&srch->link, &cookie->search_results);
+ ret = -ENODATA;
+ break;
+ }
+
+ done:
+ up_read(&iparent->sem);
+ _leave(" = %d", ret);
+ return ret;
+
+ igrab_failed_upput:
+ cache->ops->unlock_node(node);
+ cache->ops->put_node(node);
+ ret = -ENOENT;
+ error:
+ up_read(&iparent->sem);
+ dbgfree(srch);
+ kfree(srch);
+ _leave(" = %d", ret);
+ return ret;
+
+} /* end fscache_search_for_object() */
+
+/*****************************************************************************/
+/*
+ * instantiate the object in the specified cache
+ * - the cookie must be write-locked by the caller
+ * - search must have been performed first (so lists of search results are
+ * filled out)
+ * - all parent index objects are instantiated if necessary
+ */
+static int fscache_instantiate_object(struct fscache_cookie *cookie,
+ struct fscache_cache *cache)
+{
+ struct fscache_search_result *srch;
+ struct fscache_cookie *iparent;
+ struct fscache_node *ipnode, *node;
+ int ret;
+
+ iparent = cookie->iparent;
+ if (!iparent)
+ return 0; /* FSDEF entries don't have a parent */
+
+ _enter("{%s/%s},",
+ iparent->idef->name,
+ cookie->idef ? (char *) cookie->idef->name : "<file>");
+
+ /* find the search result for this object */
+ list_for_each_entry(srch, &cookie->search_results, link) {
+ if (srch->cache == cache)
+ goto found_search_result;
+ }
+
+ BUG();
+
+ found_search_result:
+ if (srch->ino) {
+ /* it was instantiated already */
+ _leave(" = 0 [found ino %u]", srch->ino);
+ return 0;
+ }
+
+ /* we need to insert an entry for this cache in the object's parent
+ * index, so the first thing to do is make sure that the parent index
+ * is represented on disc
+ */
+ down_write(&iparent->sem);
+
+ ret = fscache_instantiate_object(iparent, cache);
+ if (ret < 0)
+ goto error;
+
+ /* the parent index's node should now be available */
+ list_for_each_entry(ipnode, &iparent->backing_nodes, cookie_link) {
+ if (ipnode->cache == cache)
+ goto found_parent_node;
+ }
+
+ BUG();
+
+ found_parent_node:
+ _debug("found_parent_node: node=%p", ipnode);
+
+ BUG_ON(ipnode->cookie != iparent);
+
+ /* allocate an entry within the parent index node */
+ ret = cache->ops->index_add(ipnode, cookie, srch);
+ if (ret < 0)
+ goto error;
+
+ /* we're going to need an in-memory reflection of the node too */
+ node = cache->ops->lookup_node(cache, srch->ino);
+ if (IS_ERR(node)) {
+ ret = PTR_ERR(node);
+ goto error_x; /* uh-oh... our search record is now wrong */
+ }
+
+ /* keep track of it */
+ cache->ops->lock_node(node);
+
+ BUG_ON(!list_empty(&node->cookie_link));
+
+ /* attach to the cache's node list */
+ if (list_empty(&node->cache_link)) {
+ if (!cache->ops->grab_node(node))
+ goto error_xi;
+
+ spin_lock(&cache->node_list_lock);
+ list_add_tail(&node->cache_link, &cache->node_list);
+ spin_unlock(&cache->node_list_lock);
+ }
+
+ /* attach to the cookie's search result list */
+ node->cookie = cookie;
+ atomic_inc(&cookie->usage);
+ list_add_tail(&node->cookie_link, &cookie->backing_nodes);
+
+ /* done */
+ cache->ops->unlock_node(node);
+ up_write(&iparent->sem);
+ _leave(" = 0 [new]");
+ return 0;
+
+ /* if we get an error after having instantiated an node on disc, just
+ * discard the search record so we find it next time */
+ error_xi:
+ cache->ops->unlock_node(node);
+ cache->ops->put_node(node);
+ ret = -ENOENT;
+ error_x:
+ list_del(&srch->link);
+ dbgfree(srch);
+ kfree(srch);
+ srch = NULL;
+ error:
+ up_write(&iparent->sem);
+ _leave(" = %d", ret);
+ return ret;
+
+} /* end fscache_instantiate_object() */
+
+/*****************************************************************************/
+/*
+ * select a cache on which to store a file
+ * - the cache addremove semaphore must be at least read-locked by the caller
+ */
+static struct fscache_cache *fscache_select_cache_for_file(void)
+{
+ struct fscache_cache *cache;
+
+ _enter("");
+
+ /* TODO: make more intelligent than just choosing the first cache */
+ cache = NULL;
+ if (!list_empty(&fscache_cache_list))
+ cache = list_entry(fscache_cache_list.next,
+ struct fscache_cache,
+ link);
+
+ _leave(" = %p", cache);
+ return cache;
+
+} /* end fscache_select_cache_for_file() */
+
+/*****************************************************************************/
+/*
+ * request a cookie to represent a data file or an index
+ * - iparent specifies the parent index to pin in memory
+ * - the top level index cookie for each netfs is stored in the fscache_netfs
+ * struct upon registration
+ * - idef is NULL for a data file
+ * - idef points to the definition for an index
+ * - the netfs_data will be passed to the functions pointed to in *idef
+ * - all attached caches will be searched to see if they contain this object
+ * - index objects aren't stored on disc until there's a dependent file that
+ * needs storing
+ * - file objects are stored in a selected cache immediately, and all the
+ * indexes forming the path to it are instantiated if necessary
+ * - we never let on to the netfs about errors
+ * - we may set a negative cookie pointer, but that's okay
+ */
+struct fscache_cookie *__fscache_acquire_cookie(struct fscache_cookie *iparent,
+ struct fscache_index_def *idef,
+ void *netfs_data)
+{
+ struct fscache_cookie *cookie;
+ struct fscache_cache *cache;
+ int ret = 0;
+
+ _enter("{%s},{%s},%p",
+ iparent ? (char *) iparent->idef->name : "<no-parent>",
+ idef ? (char *) idef->name : "<file>",
+ netfs_data);
+
+ _debug("backing nodes %p: %p,%p -> %p,%p",
+ &fscache_fsdef_index.backing_nodes,
+ fscache_fsdef_index.backing_nodes.next,
+ fscache_fsdef_index.backing_nodes.prev,
+ fscache_fsdef_index.backing_nodes.next->next,
+ fscache_fsdef_index.backing_nodes.next->prev);
+
+ /* if there's no parent cookie, then we don't create one here either */
+ if (iparent == FSCACHE_NEGATIVE_COOKIE) {
+ _leave(" [no parent]");
+ return FSCACHE_NEGATIVE_COOKIE;
+ }
+
+ /* if it's going to be an index then validate the index data */
+ if (idef) {
+ size_t dsize;
+ int loop;
+
+ if (!idef->name[0]) {
+ printk("Fscache: %s.%s.%p: nameless index\n",
+ iparent->netfs->name,
+ iparent->idef->name,
+ idef);
+ return FSCACHE_NEGATIVE_COOKIE;
+ }
+
+ dsize = idef->data_size;
+
+ for (loop = 0; loop < 4; loop++) {
+ if (idef->keys[loop].type >=
+ FSCACHE_INDEX_KEYS__LAST) {
+ printk("Fscache: %s.%s.%s:"
+ " index type %u unsupported\n",
+ iparent->netfs->name,
+ iparent->idef->name,
+ idef->name,
+ idef->keys[loop].type);
+ return FSCACHE_NEGATIVE_COOKIE;
+ }
+
+ dsize += idef->keys[loop].len;
+ }
+
+ if (dsize > 400) {
+ printk("Fscache: %s.%s.%s:"
+ " index entry size exceeds maximum %u>400\n",
+ iparent->netfs->name,
+ iparent->idef->name,
+ idef->name,
+ dsize);
+ return FSCACHE_NEGATIVE_COOKIE;
+ }
+ }
+
+ /* allocate and initialise a cookie */
+ cookie = kmem_cache_alloc(fscache_cookie_jar, SLAB_KERNEL);
+ if (!cookie) {
+ _leave(" [ENOMEM]");
+ return FSCACHE_NEGATIVE_COOKIE;
+ }
+
+ atomic_set(&cookie->usage, 1);
+ atomic_set(&cookie->children, 0);
+
+ atomic_inc(&iparent->usage);
+ atomic_inc(&iparent->children);
+
+ cookie->idef = idef;
+ cookie->iparent = iparent;
+ cookie->netfs = iparent->netfs;
+ cookie->netfs_data = netfs_data;
+
+ /* now we need to see whether the backing objects for this cookie yet
+ * exist, if not there'll be nothing to search */
+ down_read(&fscache_addremove_sem);
+
+ if (list_empty(&fscache_cache_list)) {
+ up_read(&fscache_addremove_sem);
+ _leave(" [no caches]");
+ return cookie;
+ }
+
+ down_write(&cookie->sem);
+
+ /* search every cache we know about to see if the object is already
+ * present */
+ list_for_each_entry(cache, &fscache_cache_list, link) {
+ ret = fscache_search_for_object(cookie, cache);
+ switch (ret) {
+ case 0:
+ if (!cookie->idef)
+ break; /* only want the first file entry */
+ case -ENODATA:
+ ret = 0;
+ continue;
+ default:
+ goto error;
+ }
+ }
+
+ /* if the object is a cookie then we need do nothing more here - we
+ * create indexes on disc when we need them as an index may exist in
+ * multiple caches */
+ if (cookie->idef)
+ goto done;
+
+ /* the object is a file - we need to select a cache in which to store
+ * it */
+ ret = -ENOMEDIUM;
+ cache = fscache_select_cache_for_file();
+ if (!cache)
+ goto error; /* couldn't decide on a cache */
+
+ /* create a file index entry on disc, along with all the indexes
+ * required to find it again later */
+ ret = fscache_instantiate_object(cookie, cache);
+ if (ret == 0)
+ goto done;
+
+ error:
+ printk("Fscache: error from cache fs: %d\n", ret);
+ if (cookie) {
+ __fscache_cookie_put(cookie);
+ cookie = FSCACHE_NEGATIVE_COOKIE;
+ atomic_dec(&iparent->children);
+ }
+
+ done:
+ up_write(&cookie->sem);
+ up_read(&fscache_addremove_sem);
+ _leave(" = %p", cookie);
+ return cookie;
+
+} /* end __fscache_acquire_cookie() */
+
+EXPORT_SYMBOL(__fscache_acquire_cookie);
+
+/*****************************************************************************/
+/*
+ * release a cookie back to the cache
+ * - the object will be marked as recyclable on disc if retire is true
+ * - all dependents of this cookie must have already been unregistered
+ * (indexes/files/pages)
+ */
+void __fscache_relinquish_cookie(struct fscache_cookie *cookie, int retire)
+{
+ struct fscache_cache *cache;
+ struct fscache_node *node;
+
+ _enter("%p{%s},%d",
+ cookie,
+ cookie && cookie->idef ? (char *) cookie->idef->name : "<file>",
+ retire);
+
+ if (cookie == FSCACHE_NEGATIVE_COOKIE) {
+ _leave(" [no cookie]");
+ return;
+ }
+
+ if (atomic_read(&cookie->children) != 0) {
+ printk("Fscache: cookie still has children\n");
+ BUG();
+ }
+
+ /* detach pointers back to netfs */
+ down_write(&cookie->sem);
+
+ cookie->netfs_data = NULL;
+ cookie->idef = NULL;
+
+ read_lock(&cookie->lock);
+
+ /* queue retired objects for recycling */
+ if (retire) {
+ list_for_each_entry(node,
+ &cookie->backing_nodes,
+ cookie_link) {
+ set_bit(FSCACHE_NODE_RECYCLING, &node->flags);
+ }
+ }
+
+ /* break links with all the active nodes */
+ while (!list_empty(&cookie->backing_nodes)) {
+ node = list_entry(cookie->backing_nodes.next,
+ struct fscache_node,
+ cookie_link);
+
+ /* detach each cache node from the object cookie */
+ set_bit(FSCACHE_NODE_RELEASING, &node->flags);
+
+ list_del_init(&node->cookie_link);
+ read_unlock(&cookie->lock);
+
+ cache = node->cache;
+ cache->ops->lock_node(node);
+ node->cookie = NULL;
+ cache->ops->unlock_node(node);
+
+ if (atomic_dec_and_test(&cookie->usage))
+ /* the cookie refcount shouldn't be reduced to 0 yet */
+ BUG();
+
+ cache->ops->put_node(node);
+
+ read_lock(&cookie->lock);
+ }
+
+ read_unlock(&cookie->lock);
+ up_write(&cookie->sem);
+
+ if (cookie->iparent)
+ atomic_dec(&cookie->iparent->children);
+
+ /* finally dispose of the cookie */
+ fscache_cookie_put(cookie);
+
+ _leave("");
+
+} /* end __fscache_relinquish_cookie() */
+
+EXPORT_SYMBOL(__fscache_relinquish_cookie);
+
+/*****************************************************************************/
+/*
+ * update the index entries backing a cookie
+ */
+void __fscache_update_cookie(struct fscache_cookie *cookie)
+{
+ struct fscache_node *ixnode, *node;
+
+ _enter("{%s}",
+ cookie &&
+ cookie->idef ? (char *) cookie->idef->name : "<file>");
+
+ if (cookie == FSCACHE_NEGATIVE_COOKIE) {
+ _leave(" [no cookie]");
+ return;
+ }
+
+ down_write(&cookie->sem);
+ down_write(&cookie->iparent->sem);
+
+ /* update the index entry on disc in each cache backing this cookie */
+ list_for_each_entry(node, &cookie->backing_nodes, cookie_link) {
+ ixnode = fscache_find_parent_node(node);
+ node->cache->ops->index_update(ixnode, node);
+ }
+
+ up_write(&cookie->iparent->sem);
+ up_write(&cookie->sem);
+ _leave("");
+
+} /* end __fscache_update_cookie() */
+
+EXPORT_SYMBOL(__fscache_update_cookie);
+
+/*****************************************************************************/
+/*
+ * destroy a cookie
+ */
+void __fscache_cookie_put(struct fscache_cookie *cookie)
+{
+ struct fscache_search_result *srch;
+
+ _enter("%p", cookie);
+
+ if (cookie->iparent)
+ fscache_cookie_put(cookie->iparent);
+
+ /* dispose of any cached search results */
+ while (!list_empty(&cookie->search_results)) {
+ srch = list_entry(cookie->search_results.next,
+ struct fscache_search_result,
+ link);
+
+ list_del(&srch->link);
+ kfree(srch);
+ }
+
+ BUG_ON(!list_empty(&cookie->search_results));
+ BUG_ON(!list_empty(&cookie->backing_nodes));
+ kmem_cache_free(fscache_cookie_jar, cookie);
+
+ _leave("");
+
+} /* end __fscache_cookie_put() */
+
+/*****************************************************************************/
+/*
+ * initialise an cookie jar slab element prior to any use
+ */
+void fscache_cookie_init_once(void *_cookie, kmem_cache_t *cachep,
+ unsigned long flags)
+{
+ struct fscache_cookie *cookie = _cookie;
+
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR) {
+ memset(cookie, 0, sizeof(*cookie));
+ rwlock_init(&cookie->lock);
+ init_rwsem(&cookie->sem);
+ INIT_LIST_HEAD(&cookie->search_results);
+ INIT_LIST_HEAD(&cookie->backing_nodes);
+ }
+
+} /* end fscache_cookie_init_once() */
diff -uNr linux-2.6.9-rc2-mm4/fs/fscache/fscache-int.h linux-2.6.9-rc2-mm4-fscache/fs/fscache/fscache-int.h
--- linux-2.6.9-rc2-mm4/fs/fscache/fscache-int.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/fscache/fscache-int.h 2004-09-30 13:50:20.000000000 +0100
@@ -0,0 +1,81 @@
+/* fscache-int.h: internal definitions
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at 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.
+ */
+
+#ifndef _FSCACHE_INT_H
+#define _FSCACHE_INT_H
+
+#include <linux/fscache-cache.h>
+#include <linux/timer.h>
+#include <linux/bio.h>
+
+extern kmem_cache_t *fscache_cookie_jar;
+
+extern struct fscache_cookie fscache_fsdef_index;
+
+extern void fscache_cookie_init_once(void *_cookie, kmem_cache_t *cachep, unsigned long flags);
+
+extern void __fscache_cookie_put(struct fscache_cookie *cookie);
+
+static inline void fscache_cookie_put(struct fscache_cookie *cookie)
+{
+ BUG_ON(atomic_read(&cookie->usage) <= 0);
+
+ if (atomic_dec_and_test(&cookie->usage))
+ __fscache_cookie_put(cookie);
+
+}
+
+/*****************************************************************************/
+/*
+ * debug tracing
+ */
+#define dbgprintk(FMT,...) \
+ printk("[%-6.6s] "FMT"\n",current->comm ,##__VA_ARGS__)
+#define _dbprintk(FMT,...) do { } while(0)
+
+#define kenter(FMT,...) dbgprintk("==> %s("FMT")",__FUNCTION__ ,##__VA_ARGS__)
+#define kleave(FMT,...) dbgprintk("<== %s()"FMT"",__FUNCTION__ ,##__VA_ARGS__)
+#define kdebug(FMT,...) dbgprintk(FMT ,##__VA_ARGS__)
+
+#define kjournal(FMT,...) _dbprintk(FMT ,##__VA_ARGS__)
+
+#define dbgfree(ADDR) _dbprintk("%p:%d: FREEING %p",__FILE__,__LINE__,ADDR)
+
+#define dbgpgalloc(PAGE) \
+do { \
+ _dbprintk("PGALLOC %s:%d: %p {%lx,%lu}\n", \
+ __FILE__,__LINE__, \
+ (PAGE),(PAGE)->mapping->host->i_ino,(PAGE)->index \
+ ); \
+} while(0)
+
+#define dbgpgfree(PAGE) \
+do { \
+ if ((PAGE)) \
+ _dbprintk("PGFREE %s:%d: %p {%lx,%lu}\n", \
+ __FILE__,__LINE__, \
+ (PAGE), \
+ (PAGE)->mapping->host->i_ino, \
+ (PAGE)->index \
+ ); \
+} while(0)
+
+#ifdef __KDEBUG
+#define _enter(FMT,...) kenter(FMT,##__VA_ARGS__)
+#define _leave(FMT,...) kleave(FMT,##__VA_ARGS__)
+#define _debug(FMT,...) kdebug(FMT,##__VA_ARGS__)
+#else
+#define _enter(FMT,...) do { } while(0)
+#define _leave(FMT,...) do { } while(0)
+#define _debug(FMT,...) do { } while(0)
+#endif
+
+#endif /* _FSCACHE_INT_H */
diff -uNr linux-2.6.9-rc2-mm4/fs/fscache/fsdef.c linux-2.6.9-rc2-mm4-fscache/fs/fscache/fsdef.c
--- linux-2.6.9-rc2-mm4/fs/fscache/fsdef.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/fscache/fsdef.c 2004-10-04 15:09:45.928003981 +0100
@@ -0,0 +1,87 @@
+/* fsdef.c: filesystem index definition
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at 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/module.h>
+#include "fscache-int.h"
+
+static fscache_match_val_t fscache_fsdef_index_match(void *target,
+ const void *entry);
+
+static void fscache_fsdef_index_update(void *source, void *entry);
+
+static struct fscache_index_def fscache_fsdef_index_def = {
+ .name = ".fsdef",
+ .data_size = sizeof(struct fscache_fsdef_index_entry),
+ .match = fscache_fsdef_index_match,
+ .update = fscache_fsdef_index_update
+};
+
+struct fscache_cookie fscache_fsdef_index = {
+ .usage = ATOMIC_INIT(1),
+ .idef = &fscache_fsdef_index_def,
+ .lock = RW_LOCK_UNLOCKED,
+ .sem = __RWSEM_INITIALIZER(fscache_fsdef_index.sem),
+ .search_results = LIST_HEAD_INIT(fscache_fsdef_index.search_results),
+ .backing_nodes = LIST_HEAD_INIT(fscache_fsdef_index.backing_nodes),
+};
+
+EXPORT_SYMBOL(fscache_fsdef_index);
+
+/*****************************************************************************/
+/*
+ * see if the netfs definition matches
+ */
+static fscache_match_val_t fscache_fsdef_index_match(void *target,
+ const void *entry)
+{
+ const struct fscache_fsdef_index_entry *fsdef = entry;
+ struct fscache_netfs *netfs = target;
+
+ _enter("%p,%p", target, entry);
+
+ /* name and version must both match with what's on disc */
+ _debug("{%s.%u},{%s.%u}",
+ netfs->name, netfs->version, fsdef->name, fsdef->version);
+
+ if (strncmp(netfs->name, fsdef->name, sizeof(fsdef->name)) != 0) {
+ _leave(" = FAILED");
+ return FSCACHE_MATCH_FAILED;
+ }
+
+ if (netfs->version == fsdef->version) {
+ _leave(" = SUCCESS");
+ return FSCACHE_MATCH_SUCCESS;
+ }
+
+ /* an entry of the same name but different version is scheduled for
+ * deletion */
+ _leave(" = SUCCESS_DELETE");
+ return FSCACHE_MATCH_SUCCESS_DELETE;
+
+} /* end fscache_fsdef_index_match() */
+
+/*****************************************************************************/
+/*
+ * update the netfs definition to be stored on disc
+ */
+static void fscache_fsdef_index_update(void *source, void *entry)
+{
+ struct fscache_fsdef_index_entry *fsdef = entry;
+ struct fscache_netfs *netfs = source;
+
+ _enter("{%s.%u},", netfs->name, netfs->version);
+
+ /* install the netfs name and version in the top-level index entry */
+ strncpy(fsdef->name, netfs->name, sizeof(fsdef->name));
+
+ fsdef->version = netfs->version;
+
+} /* end fscache_fsdef_index_update() */
diff -uNr linux-2.6.9-rc2-mm4/fs/fscache/main.c linux-2.6.9-rc2-mm4-fscache/fs/fscache/main.c
--- linux-2.6.9-rc2-mm4/fs/fscache/main.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/fscache/main.c 2004-09-30 19:11:35.000000000 +0100
@@ -0,0 +1,111 @@
+/* main.c: general filesystem caching manager
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at 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/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <linux/slab.h>
+#include "fscache-int.h"
+
+int fscache_debug = 0;
+
+static int fscache_init(void);
+static void fscache_exit(void);
+
+fs_initcall(fscache_init);
+module_exit(fscache_exit);
+
+MODULE_DESCRIPTION("FS Cache Manager");
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL");
+
+/*****************************************************************************/
+/*
+ * initialise the fs caching module
+ */
+static int fscache_init(void)
+{
+ fscache_cookie_jar =
+ kmem_cache_create("fscache_cookie_jar",
+ sizeof(struct fscache_cookie),
+ 0,
+ SLAB_HWCACHE_ALIGN,
+ fscache_cookie_init_once,
+ NULL);
+
+ if (!fscache_cookie_jar) {
+ printk(KERN_NOTICE
+ "Fscache: Failed to allocate a cookie jar\n");
+ return -ENOMEM;
+ }
+
+ printk(KERN_INFO "fscache: general fs caching registered\n");
+ return 0;
+
+} /* end fscache_init() */
+
+/*****************************************************************************/
+/*
+ * clean up on module removal
+ */
+static void __exit fscache_exit(void)
+{
+ printk(KERN_INFO "Fscache: general fs caching unregistering\n");
+
+ kmem_cache_destroy(fscache_cookie_jar);
+
+} /* end fscache_exit() */
+
+/*****************************************************************************/
+/*
+ * clear the dead space between task_struct and kernel stack
+ * - called by supplying -finstrument-functions to gcc
+ */
+#if 0
+void __cyg_profile_func_enter (void *this_fn, void *call_site)
+__attribute__((no_instrument_function));
+
+void __cyg_profile_func_enter (void *this_fn, void *call_site)
+{
+ asm volatile(" movl %%esp,%%edi \n"
+ " andl %0,%%edi \n"
+ " addl %1,%%edi \n"
+ " movl %%esp,%%ecx \n"
+ " subl %%edi,%%ecx \n"
+ " shrl $2,%%ecx \n"
+ " movl $0xedededed,%%eax \n"
+ " rep stosl \n"
+ :
+ : "i"(~(THREAD_SIZE-1)), "i"(sizeof(struct thread_info))
+ : "eax", "ecx", "edi", "memory", "cc"
+ );
+}
+
+void __cyg_profile_func_exit(void *this_fn, void *call_site)
+__attribute__((no_instrument_function));
+
+void __cyg_profile_func_exit(void *this_fn, void *call_site)
+{
+ asm volatile(" movl %%esp,%%edi \n"
+ " andl %0,%%edi \n"
+ " addl %1,%%edi \n"
+ " movl %%esp,%%ecx \n"
+ " subl %%edi,%%ecx \n"
+ " shrl $2,%%ecx \n"
+ " movl $0xdadadada,%%eax \n"
+ " rep stosl \n"
+ :
+ : "i"(~(THREAD_SIZE-1)), "i"(sizeof(struct thread_info))
+ : "eax", "ecx", "edi", "memory", "cc"
+ );
+}
+#endif
diff -uNr linux-2.6.9-rc2-mm4/fs/fscache/Makefile linux-2.6.9-rc2-mm4-fscache/fs/fscache/Makefile
--- linux-2.6.9-rc2-mm4/fs/fscache/Makefile 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/fscache/Makefile 2004-09-30 13:40:00.000000000 +0100
@@ -0,0 +1,13 @@
+#
+# Makefile for general filesystem caching code
+#
+
+#CFLAGS += -finstrument-functions
+
+fscache-objs := \
+ cookie.o \
+ fsdef.o \
+ main.o \
+ page.o
+
+obj-$(CONFIG_FSCACHE) := fscache.o
diff -uNr linux-2.6.9-rc2-mm4/fs/fscache/page.c linux-2.6.9-rc2-mm4-fscache/fs/fscache/page.c
--- linux-2.6.9-rc2-mm4/fs/fscache/page.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/fscache/page.c 2004-10-04 17:06:39.304098924 +0100
@@ -0,0 +1,231 @@
+/* page.c: general filesystem cache cookie management
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at 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/module.h>
+#include <linux/fscache-cache.h>
+#include <linux/buffer_head.h>
+#include "fscache-int.h"
+
+/*****************************************************************************/
+/*
+ * read a page from the cache or allocate a block in which to store it
+ * - we return:
+ * -ENOMEM - out of memory, nothing done
+ * -ENOBUFS - no backing node available in which to cache the block
+ * -ENODATA - no data available in the backing node for this block
+ * 0 - dispatched a read - it'll call end_io_func() when finished
+ */
+int __fscache_read_or_alloc_page(struct fscache_cookie *cookie,
+ struct page *page,
+ fscache_rw_complete_t end_io_func,
+ void *end_io_data,
+ unsigned long gfp)
+{
+ struct fscache_node *node;
+ struct fscache_page *pageio;
+ int ret;
+
+ _enter("%p,{%lu},", cookie, page->index);
+
+ if (cookie == FSCACHE_NEGATIVE_COOKIE) {
+ _leave(" -ENOBUFS [no cookie]");
+ return -ENOBUFS;
+ }
+
+ if (list_empty(&cookie->backing_nodes)) {
+ _leave(" -ENOBUFS [no backing nodes]");
+ return -ENOBUFS;
+ }
+
+ BUG_ON(cookie->idef); /* not supposed to use this for indexes */
+
+ /* get the cache-cookie for this page */
+ pageio = cookie->netfs->ops->get_page_token(page);
+ if (IS_ERR(pageio)) {
+ _leave(" = %ld", PTR_ERR(pageio));
+ return PTR_ERR(pageio);
+ }
+
+ /* prevent the file from being uncached whilst we access it */
+ down_read(&cookie->sem);
+
+ ret = -ENOBUFS;
+ if (!list_empty(&cookie->backing_nodes)) {
+ /* get and pin the backing node */
+ node = list_entry(cookie->backing_nodes.next,
+ struct fscache_node,
+ cookie_link);
+
+ if (node->cache->ops->grab_node(node)) {
+ /* ask the cache to honour the operation */
+ ret = node->cache->ops->read_or_alloc_page(node,
+ page,
+ pageio,
+ end_io_func,
+ end_io_data,
+ gfp);
+
+ node->cache->ops->put_node(node);
+ }
+
+ }
+ up_read(&cookie->sem);
+ _leave(" = %d", ret);
+ return ret;
+
+} /* end __fscache_read_or_alloc_page() */
+
+EXPORT_SYMBOL(__fscache_read_or_alloc_page);
+
+/*****************************************************************************/
+/*
+ * request a page be stored in the cache
+ * - returns:
+ * -ENOMEM - out of memory, nothing done
+ * -ENOBUFS - no backing node available in which to cache the page
+ * 0 - dispatched a write - it'll call end_io_func() when finished
+ */
+int __fscache_write_page(struct fscache_cookie *cookie,
+ struct page *page,
+ fscache_rw_complete_t end_io_func,
+ void *end_io_data,
+ unsigned long gfp)
+{
+ struct fscache_page *pageio;
+ struct fscache_node *node;
+ int ret;
+
+ _enter("%p,{%lu},", cookie, page->index);
+
+ if (cookie == FSCACHE_NEGATIVE_COOKIE) {
+ _leave(" -ENOBUFS [no cookie]");
+ return -ENOBUFS; /* no actual cookie */
+ }
+
+ BUG_ON(cookie->idef); /* not supposed to use this for indexes */
+
+ /* get the cache-cookie for this page */
+ pageio = cookie->netfs->ops->get_page_token(page);
+ if (IS_ERR(pageio)) {
+ _leave(" = %ld", PTR_ERR(pageio));
+ return PTR_ERR(pageio);
+ }
+
+ /* prevent the file from been uncached whilst we deal with it */
+ down_read(&cookie->sem);
+
+ ret = -ENOBUFS;
+ if (!list_empty(&cookie->backing_nodes) && pageio->mapped_block) {
+ node = list_entry(cookie->backing_nodes.next,
+ struct fscache_node,
+ cookie_link);
+
+ /* ask the cache to honour the operation */
+ ret = node->cache->ops->write_page(node,
+ page,
+ pageio,
+ end_io_func,
+ end_io_data,
+ gfp);
+ }
+
+ up_read(&cookie->sem);
+ _leave(" = %d", ret);
+ return ret;
+
+} /* end __fscache_write_page() */
+
+EXPORT_SYMBOL(__fscache_write_page);
+
+/*****************************************************************************/
+/*
+ * remove a page from the cache
+ * - if the block backing the page still has a vjentry then the block will be
+ * recycled
+ */
+void __fscache_uncache_page(struct fscache_cookie *cookie, struct page *page)
+{
+ struct fscache_page *pageio;
+ struct fscache_node *node;
+
+ _enter(",{%lu}", page->index);
+
+ if (cookie == FSCACHE_NEGATIVE_COOKIE) {
+ _leave(" [no cookie]");
+ return;
+ }
+
+ BUG_ON(cookie->idef); /* not supposed to use this for indexes */
+
+ /* get the cache-cookie for this page */
+ pageio = cookie->netfs->ops->get_page_token(page);
+ if (IS_ERR(pageio)) {
+ _leave(" [get_page_cookie() = %ld]", PTR_ERR(pageio));
+ return;
+ }
+
+ if (list_empty(&cookie->backing_nodes)) {
+ BUG_ON(pageio->mapped_block);
+ _leave(" [no backing]");
+ return;
+ }
+
+ if (!pageio->mapped_block) {
+ _leave(" [no mapping]");
+ return;
+ }
+
+ /* ask the cache to honour the operation */
+ down_read(&cookie->sem);
+
+ if (!list_empty(&cookie->backing_nodes) && pageio->mapped_block) {
+ node = list_entry(cookie->backing_nodes.next,
+ struct fscache_node,
+ cookie_link);
+
+ node->cache->ops->uncache_page(node, pageio);
+ }
+
+ up_read(&cookie->sem);
+
+ _leave("");
+ return;
+
+} /* end __fscache_uncache_page() */
+
+EXPORT_SYMBOL(__fscache_uncache_page);
+
+/*****************************************************************************/
+/*
+ * get a page caching token from for a page, allocating it and attaching it to
+ * the page's private pointer if it doesn't exist
+ */
+struct fscache_page * __fscache_page_get_private(struct page *page,
+ unsigned gfp_flags)
+{
+ struct fscache_page *pageio = (struct fscache_page *) page->private;
+
+ if (!pageio) {
+ pageio = kmalloc(sizeof(*pageio), gfp_flags);
+ if (!pageio)
+ return ERR_PTR(-ENOMEM);
+
+ memset(pageio, 0, sizeof(*pageio));
+ rwlock_init(&pageio->lock);
+
+ page->private = (unsigned long) pageio;
+ SetPagePrivate(page);
+ }
+
+ return pageio;
+} /* end __fscache_page_get_private() */
+
+EXPORT_SYMBOL(__fscache_page_get_private);
diff -uNr linux-2.6.9-rc2-mm4/fs/Kconfig linux-2.6.9-rc2-mm4-fscache/fs/Kconfig
--- linux-2.6.9-rc2-mm4/fs/Kconfig 2004-09-27 11:23:57.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/Kconfig 2004-09-30 21:06:13.000000000 +0100
@@ -485,10 +485,21 @@
menu "Caches"
-config CACHEFS
- tristate "Filesystem caching support"
+config FSCACHE
+ tristate "General filesystem cache manager"
depends on EXPERIMENTAL
help
+ This option enables a generic filesystem caching manager that can be
+ used by various network and other filesystems to cache data
+ locally. Diffent sorts of caches can be plugged in, depending on the
+ resources available.
+
+ See Documentation/filesystems/fscache.txt for more information.
+
+config CACHEFS
+ tristate "Filesystem caching filesystem"
+ depends on FSCACHE
+ help
This filesystem acts as a cache for other filesystems - primarily
networking filesystems - rather than thus allowing fast local disc to
enhance the speed of slower devices.
@@ -1482,6 +1493,13 @@
If unsure, say N.
+config NFS_FSCACHE
+ bool "Provide NFS client caching support"
+ depends on NFS_FS && FSCACHE && EXPERIMENTAL
+ help
+ Say Y here if you want NFS data to be cached locally on disc through
+ the general filesystem cache manager
+
config NFS_DIRECTIO
bool "Allow direct I/O on NFS files (EXPERIMENTAL)"
depends on NFS_FS && EXPERIMENTAL
@@ -1811,12 +1829,18 @@
If unsure, say N.
-config AFS_CACHEFS
- bool "Provide AFS client caching support through CacheFS"
- depends on AFS_FS && CACHEFS && EXPERIMENTAL
+config AFS_KEYS
+ bool "Provide AFS authentication & security key support"
+ depends on AFS_FS && KEYS && EXPERIMENTAL
+ help
+ Say Y here if you want AFS to attempt to use authentication.
+
+config AFS_FSCACHE
+ bool "Provide AFS client caching support"
+ depends on AFS_FS && FSCACHE && EXPERIMENTAL
help
- Say Y here if you want AFS data to be cached locally on disc through
- the CacheFS filesystem.
+ Say Y here if you want AFS data to be cached locally on through the
+ generic filesystem cache manager
config RXRPC
tristate
diff -uNr linux-2.6.9-rc2-mm4/fs/Makefile linux-2.6.9-rc2-mm4-fscache/fs/Makefile
--- linux-2.6.9-rc2-mm4/fs/Makefile 2004-09-27 11:23:57.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/Makefile 2004-09-30 13:34:53.000000000 +0100
@@ -45,6 +45,8 @@
obj-$(CONFIG_PROFILING) += dcookies.o
# Do not add any filesystems before this line
+obj-$(CONFIG_FSCACHE) += fscache/
+obj-$(CONFIG_CACHEFS) += cachefs/
obj-$(CONFIG_REISERFS_FS) += reiserfs/
obj-$(CONFIG_REISER4_FS) += reiser4/
obj-$(CONFIG_EXT3_FS) += ext3/ # Before ext2 so root fs can be ext3
@@ -94,4 +96,3 @@
obj-$(CONFIG_BEFS_FS) += befs/
obj-$(CONFIG_HOSTFS) += hostfs/
obj-$(CONFIG_HPPFS) += hppfs/
-obj-$(CONFIG_CACHEFS) += cachefs/
diff -uNr linux-2.6.9-rc2-mm4/fs/nfs/file.c linux-2.6.9-rc2-mm4-fscache/fs/nfs/file.c
--- linux-2.6.9-rc2-mm4/fs/nfs/file.c 2004-09-16 12:08:07.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/nfs/file.c 2004-09-30 18:50:15.000000000 +0100
@@ -27,9 +27,11 @@
#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/smp_lock.h>
+#include <linux/buffer_head.h>
#include <asm/uaccess.h>
#include <asm/system.h>
+#include "nfs-fscache.h"
#include "delegation.h"
@@ -240,6 +242,61 @@
return status;
}
+#ifdef CONFIG_NFS_FSCACHE
+static int nfs_invalidatepage(struct page *page, unsigned long offset)
+{
+ int ret = 1;
+ struct nfs_server *server = NFS_SERVER(page->mapping->host);
+
+ BUG_ON(!PageLocked(page));
+
+ if (server->flags & NFS_MOUNT_FSCACHE) {
+ if (PagePrivate(page)) {
+ struct nfs_inode *nfsi = NFS_I(page->mapping->host);
+ fscache_uncache_page(nfsi->fscache, page);
+
+ if (offset == 0) {
+ BUG_ON(!PageLocked(page));
+ ret = 0;
+ if (!PageWriteback(page))
+ ret = page->mapping->a_ops->releasepage(page, 0);
+ }
+ }
+ } else
+ ret = 0;
+
+ return ret;
+}
+static int nfs_releasepage(struct page *page, int gfp_flags)
+{
+ struct fscache_page *pageio;
+ struct nfs_server *server = NFS_SERVER(page->mapping->host);
+
+ if (server->flags & NFS_MOUNT_FSCACHE && PagePrivate(page)) {
+ struct nfs_inode *nfsi = NFS_I(page->mapping->host);
+ fscache_uncache_page(nfsi->fscache, page);
+ pageio = (struct fscache_page *) page->private;
+ page->private = 0;
+ ClearPagePrivate(page);
+
+ if (pageio)
+ kfree(pageio);
+ }
+
+ return 0;
+}
+static int nfs_mkwrite(struct page *page)
+{
+ wait_on_page_fs_misc(page);
+ return 0;
+}
+#endif
+
+/*
+ * since we use page->private for our own nefarious purposes when using fscache, we have to
+ * override extra address space ops to prevent fs/buffer.c from getting confused, even though we
+ * may not have asked its opinion
+ */
struct address_space_operations nfs_file_aops = {
.readpage = nfs_readpage,
.readpages = nfs_readpages,
@@ -251,6 +308,12 @@
#ifdef CONFIG_NFS_DIRECTIO
.direct_IO = nfs_direct_IO,
#endif
+#ifdef CONFIG_NFS_FSCACHE
+ .sync_page = block_sync_page,
+ .releasepage = nfs_releasepage,
+ .invalidatepage = nfs_invalidatepage,
+ .page_mkwrite = nfs_mkwrite,
+#endif
};
/*
diff -uNr linux-2.6.9-rc2-mm4/fs/nfs/inode.c linux-2.6.9-rc2-mm4-fscache/fs/nfs/inode.c
--- linux-2.6.9-rc2-mm4/fs/nfs/inode.c 2004-09-27 11:23:57.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/nfs/inode.c 2004-09-30 18:51:00.000000000 +0100
@@ -41,6 +41,8 @@
#include "delegation.h"
+#include "nfs-fscache.h"
+
#define NFSDBG_FACILITY NFSDBG_VFS
#define NFS_PARANOIA 1
@@ -140,7 +142,7 @@
/*
* For the moment, the only task for the NFS clear_inode method is to
- * release the mmap credential
+ * release the mmap credential and release the inode's on-disc cache
*/
static void
nfs_clear_inode(struct inode *inode)
@@ -153,6 +155,15 @@
cred = nfsi->cache_access.cred;
if (cred)
put_rpccred(cred);
+
+#ifdef CONFIG_NFS_FSCACHE
+ if (NFS_SERVER(inode)->flags & NFS_MOUNT_FSCACHE) {
+ dprintk("nfs_clear_inode: fscache 0x%p\n", nfsi->fscache);
+ fscache_relinquish_cookie(nfsi->fscache, 0);
+ nfsi->fscache = NULL;
+ }
+#endif
+
BUG_ON(atomic_read(&nfsi->data_updates) != 0);
}
@@ -462,6 +473,19 @@
server->namelen = NFS2_MAXNAMLEN;
}
+#ifdef CONFIG_NFS_FSCACHE
+ /* create a cache index for looking up filehandles */
+ server->fscache = NULL;
+ if (server->flags & NFS_MOUNT_FSCACHE) {
+ server->fscache = fscache_acquire_cookie(nfs_cache_netfs.primary_index,
+ &nfs_cache_fh_index_def, server);
+ if (server->fscache == NULL) {
+ server->flags &= ~NFS_MOUNT_FSCACHE;
+ printk(KERN_WARNING "NFS: No Fscache cookie. Turning Fscache off!\n");
+ }
+ }
+#endif
+
sb->s_op = &nfs_sops;
return nfs_sb_init(sb, authflavor);
}
@@ -518,7 +542,7 @@
} nfs_info[] = {
{ NFS_MOUNT_SOFT, ",soft", ",hard" },
{ NFS_MOUNT_INTR, ",intr", "" },
- { NFS_MOUNT_POSIX, ",posix", "" },
+ { NFS_MOUNT_POSIX, ",fscache", "" },
{ NFS_MOUNT_TCP, ",tcp", ",udp" },
{ NFS_MOUNT_NOCTO, ",nocto", "" },
{ NFS_MOUNT_NOAC, ",noac", "" },
@@ -568,6 +592,14 @@
nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
else
nfsi->flags |= NFS_INO_INVALID_ATTR;
+
+#ifdef CONFIG_NFS_FSCACHE
+ if (NFS_SERVER(inode)->flags & NFS_MOUNT_FSCACHE) {
+ fscache_relinquish_cookie(nfsi->fscache, 1);
+ nfsi->fscache = NULL;
+ }
+#endif
+
}
/*
@@ -705,6 +737,16 @@
memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));
nfsi->cache_access.cred = NULL;
+#ifdef CONFIG_NFS_FSCACHE
+{
+ struct nfs_server *server = NFS_SB(sb);
+ if (server->flags & NFS_MOUNT_FSCACHE) {
+ nfsi->fscache = fscache_acquire_cookie(server->fscache, NULL, nfsi);
+ /* XXX: Add warning when NULL is returned */
+ dprintk("nfs_fhget: fscache 0x%p\n", nfsi->fscache);
+ }
+}
+#endif
unlock_new_inode(inode);
} else
nfs_refresh_inode(inode, fattr);
@@ -1009,6 +1051,18 @@
(long long)NFS_FILEID(inode));
/* This ensures we revalidate dentries */
nfsi->cache_change_attribute++;
+
+#ifdef CONFIG_NFS_FSCACHE
+ if (server->flags & NFS_MOUNT_FSCACHE) {
+ struct fscache_cookie *old = nfsi->fscache;
+
+ /* retire the current fscache cache and get a new one */
+ fscache_relinquish_cookie(nfsi->fscache, 1);
+ nfsi->fscache = fscache_acquire_cookie(server->fscache, NULL, nfsi);
+ dfprintk(PAGECACHE,"NFS: fscache: old 0x%p new 0x%p\n",
+ old, nfsi->fscache);
+ }
+#endif
}
dfprintk(PAGECACHE, "NFS: (%s/%Ld) revalidation complete\n",
inode->i_sb->s_id,
@@ -1417,6 +1471,14 @@
return ERR_PTR(-EINVAL);
}
+#ifndef CONFIG_NFS_FSCACHE
+ if (data->flags & NFS_MOUNT_FSCACHE) {
+ printk(KERN_WARNING "NFS: kernel not compiled with CONFIG_NFS_FSCACHE\n");
+ kfree(server);
+ return ERR_PTR(-EINVAL);
+ }
+#endif
+
s = sget(fs_type, nfs_compare_super, nfs_set_super, server);
if (IS_ERR(s) || s->s_root) {
@@ -1449,6 +1511,11 @@
kill_anon_super(s);
+#ifdef CONFIG_NFS_FSCACHE
+ if (server->flags & NFS_MOUNT_FSCACHE)
+ fscache_relinquish_cookie(server->fscache, 0);
+#endif
+
nfs4_renewd_prepare_shutdown(server);
if (server->client != NULL && !IS_ERR(server->client))
@@ -1768,6 +1835,20 @@
s = ERR_PTR(-EIO);
goto out_free;
}
+#ifdef TODO
+#ifdef CONFIG_NFS_FSCACHE
+ /* create a cache index for looking up filehandles */
+ server->fscache = NULL;
+ if (server->flags & NFS_MOUNT_FSCACHE) {
+ server->fscache = fscache_acquire_cookie(nfs_cache_netfs.primary_index,
+ &nfs_cache_fh_index_def, server);
+ if (server->fscache == NULL) {
+ server->flags &= ~NFS_MOUNT_FSCACHE;
+ printk(KERN_WARNING "NFS: No Fscache cookie. Turning Fscache off!\n");
+ }
+ }
+#endif
+#endif
error = nfs4_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0);
if (error) {
@@ -1888,6 +1969,14 @@
{
int err;
+#ifdef CONFIG_NFS_FSCACHE
+ /* we want to be able to cache */
+ err = fscache_register_netfs(&nfs_cache_netfs,
+ &nfs_cache_server_index_def);
+ if (err < 0)
+ goto out5;
+#endif
+
err = nfs_init_nfspagecache();
if (err)
goto out4;
@@ -1923,6 +2012,10 @@
out3:
nfs_destroy_nfspagecache();
out4:
+#ifdef CONFIG_NFS_FSCACHE
+ fscache_unregister_netfs(&nfs_cache_netfs);
+out5:
+#endif
return err;
}
@@ -1932,6 +2025,9 @@
nfs_destroy_readpagecache();
nfs_destroy_inodecache();
nfs_destroy_nfspagecache();
+#ifdef CONFIG_NFS_FSCACHE
+ fscache_unregister_netfs(&nfs_cache_netfs);
+#endif
#ifdef CONFIG_PROC_FS
rpc_proc_unregister("nfs");
#endif
diff -uNr linux-2.6.9-rc2-mm4/fs/nfs/Makefile linux-2.6.9-rc2-mm4-fscache/fs/nfs/Makefile
--- linux-2.6.9-rc2-mm4/fs/nfs/Makefile 2004-09-16 12:08:07.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/nfs/Makefile 2004-09-30 18:47:36.000000000 +0100
@@ -12,4 +12,5 @@
delegation.o idmap.o \
callback.o callback_xdr.o callback_proc.o
nfs-$(CONFIG_NFS_DIRECTIO) += direct.o
+nfs-$(CONFIG_NFS_FSCACHE) += nfs-fscache.o
nfs-objs := $(nfs-y)
diff -uNr linux-2.6.9-rc2-mm4/fs/nfs/nfs-fscache.c linux-2.6.9-rc2-mm4-fscache/fs/nfs/nfs-fscache.c
--- linux-2.6.9-rc2-mm4/fs/nfs/nfs-fscache.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/nfs/nfs-fscache.c 2004-09-30 21:33:45.000000000 +0100
@@ -0,0 +1,206 @@
+/* nfs-fscache.c: NFS filesystem cache interface
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at 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/config.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_fs_sb.h>
+
+#include "nfs-fscache.h"
+
+#define NFS_CACHE_FH_INDEX_SIZE sizeof(struct nfs_fh)
+
+#if 0
+#define kleave(FMT,...) \
+ printk("[%-6.6s] <== %s()"FMT"\n",current->comm,__FUNCTION__ ,##__VA_ARGS__)
+#else
+#define kleave(FMT,...) \
+ do {} while(0)
+#endif
+
+/*
+ * the root index is
+ */
+static struct fscache_page *nfs_cache_get_page_token(struct page *page);
+
+static struct fscache_netfs_operations nfs_cache_ops = {
+ .get_page_token = nfs_cache_get_page_token,
+};
+
+struct fscache_netfs nfs_cache_netfs = {
+ .name = "nfs",
+ .version = 0,
+ .ops = &nfs_cache_ops,
+};
+
+/*
+ * the root index for the filesystem is defined by nfsd IP address and ports
+ */
+static fscache_match_val_t nfs_cache_server_match(void *target,
+ const void *entry);
+static void nfs_cache_server_update(void *source, void *entry);
+
+struct fscache_index_def nfs_cache_server_index_def = {
+ .name = "servers",
+ .data_size = 18,
+ .keys[0] = { FSCACHE_INDEX_KEYS_IPV6ADDR, 16 },
+ .keys[1] = { FSCACHE_INDEX_KEYS_BIN, 2 },
+ .match = nfs_cache_server_match,
+ .update = nfs_cache_server_update,
+};
+
+/*
+ * the primary index for each server is simply made up of a series of NFS file
+ * handles
+ */
+static fscache_match_val_t nfs_cache_fh_match(void *target, const void *entry);
+static void nfs_cache_fh_update(void *source, void *entry);
+
+struct fscache_index_def nfs_cache_fh_index_def = {
+ .name = "fh",
+ .data_size = NFS_CACHE_FH_INDEX_SIZE,
+ .keys[0] = { FSCACHE_INDEX_KEYS_BIN,
+ sizeof(struct nfs_fh) },
+ .match = nfs_cache_fh_match,
+ .update = nfs_cache_fh_update,
+};
+
+/*
+ * get a page token for the specified page
+ * - the token will be attached to page->private and PG_private will be set on
+ * the page
+ */
+static struct fscache_page *nfs_cache_get_page_token(struct page *page)
+{
+ return fscache_page_get_private(page, GFP_NOIO);
+}
+
+static const uint8_t nfs_cache_ipv6_wrapper_for_ipv4[12] = {
+ [0 ... 9] = 0x00,
+ [10 ... 11] = 0xff
+};
+
+/*
+ * match a server record obtained from the cache
+ */
+static fscache_match_val_t nfs_cache_server_match(void *target,
+ const void *entry)
+{
+ struct nfs_server *server = target;
+ const uint8_t *data = entry;
+
+ switch (server->addr.sin_family) {
+ case AF_INET:
+ if (memcmp(data + 0,
+ &nfs_cache_ipv6_wrapper_for_ipv4,
+ 12) != 0)
+ break;
+
+ if (memcmp(data + 12, &server->addr.sin_addr, 4) != 0)
+ break;
+
+ if (memcmp(data + 16, &server->addr.sin_port, 2) != 0)
+ break;
+
+ kleave(" = SUCCESS");
+ return FSCACHE_MATCH_SUCCESS;
+
+ case AF_INET6:
+ if (memcmp(data + 0, &server->addr.sin_addr, 16) != 0)
+ break;
+
+ if (memcmp(data + 16, &server->addr.sin_port, 2) != 0)
+ break;
+
+ kleave(" = SUCCESS");
+ return FSCACHE_MATCH_SUCCESS;
+
+ default:
+ break;
+ }
+
+ kleave(" = FAILED");
+ return FSCACHE_MATCH_FAILED;
+}
+
+/*
+ * update a server record in the cache
+ */
+static void nfs_cache_server_update(void *source, void *entry)
+{
+ struct nfs_server *server = source;
+ uint8_t *data = entry;
+
+ switch (server->addr.sin_family) {
+ case AF_INET:
+ memcpy(data + 0, &nfs_cache_ipv6_wrapper_for_ipv4, 12);
+ memcpy(data + 12, &server->addr.sin_addr, 4);
+ memcpy(data + 16, &server->addr.sin_port, 2);
+ return;
+
+ case AF_INET6:
+ memcpy(data + 0, &server->addr.sin_addr, 16);
+ memcpy(data + 16, &server->addr.sin_port, 2);
+ return;
+
+ default:
+ return;
+ }
+}
+
+/*
+ * match a file handle record obtained from the cache
+ */
+static fscache_match_val_t nfs_cache_fh_match(void *target, const void *entry)
+{
+ struct nfs_inode *nfsi = target;
+ const uint8_t *data = entry;
+ int loop;
+
+ /* check the file handle matches */
+ if (memcmp(data, &nfsi->fh, sizeof(nfsi->fh)) == 0) {
+
+ /* check the auxilliary data matches (if any) */
+ for (loop = sizeof(nfsi->fh);
+ loop < NFS_CACHE_FH_INDEX_SIZE;
+ loop++)
+ if (data[loop]) {
+ kleave(" = FAILED");
+ return FSCACHE_MATCH_FAILED;
+ }
+
+ kleave(" = SUCCESS");
+ return FSCACHE_MATCH_SUCCESS;
+ }
+
+ kleave(" = FAILED");
+ return FSCACHE_MATCH_FAILED;
+}
+
+/*
+ * update a fh record in the cache
+ */
+static void nfs_cache_fh_update(void *source, void *entry)
+{
+ struct nfs_inode *nfsi = source;
+ uint8_t *data = entry;
+
+ /* set the file handle */
+ memcpy(data, &nfsi->fh, sizeof(nfsi->fh));
+
+ /* just clear the auxilliary data for now */
+ memset(data + sizeof(nfsi->fh),
+ 0,
+ NFS_CACHE_FH_INDEX_SIZE - sizeof(nfsi->fh));
+}
diff -uNr linux-2.6.9-rc2-mm4/fs/nfs/nfs-fscache.h linux-2.6.9-rc2-mm4-fscache/fs/nfs/nfs-fscache.h
--- linux-2.6.9-rc2-mm4/fs/nfs/nfs-fscache.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/nfs/nfs-fscache.h 2004-09-30 21:12:13.000000000 +0100
@@ -0,0 +1,27 @@
+/* nfs-fscache.h: NFS filesystem cache interface definitions
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at 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.
+ */
+
+#ifndef _NFS_FSCACHE_H
+#define _NFS_FSCACHE_H
+
+#include <linux/fscache.h>
+
+#ifdef CONFIG_NFS_FSCACHE
+#ifndef CONFIG_FSCACHE
+#error "CONFIG_NFS_FSCACHE is defined but not CONFIG_FSCACHE"
+#endif
+
+extern struct fscache_netfs nfs_cache_netfs;
+extern struct fscache_index_def nfs_cache_server_index_def;
+extern struct fscache_index_def nfs_cache_fh_index_def;
+
+#endif
+#endif /* _NFS_FSCACHE_H */
diff -uNr linux-2.6.9-rc2-mm4/fs/nfs/read.c linux-2.6.9-rc2-mm4-fscache/fs/nfs/read.c
--- linux-2.6.9-rc2-mm4/fs/nfs/read.c 2004-09-16 12:08:07.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/nfs/read.c 2004-09-30 18:48:35.000000000 +0100
@@ -28,6 +28,7 @@
#include <linux/sunrpc/clnt.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_page.h>
+#include <linux/nfs_mount.h>
#include <linux/smp_lock.h>
#include <asm/system.h>
@@ -89,6 +90,53 @@
}
/*
+ * store a newly fetched page in fscache
+ */
+#ifdef CONFIG_NFS_FSCACHE
+struct nfs_fscache_stats_t {
+ int read_from_calls;
+ int read_from_misses;
+ int read_from_hits;
+ int write_to_calls;
+ int write_to_errors;
+ int write_to_completes;
+} nfs_fscache_stats = {0, 0, 0, 0, 0, 0};
+
+static void
+nfs_readpage_to_fscache_complete(void *cookie_data, struct page *page, void *data, int error)
+{
+nfs_fscache_stats.write_to_completes++;
+ end_page_fs_misc(page);
+}
+
+static inline void
+nfs_readpage_to_fscache(struct inode *inode, struct page *page, int sync)
+{
+nfs_fscache_stats.write_to_calls++;
+ SetPageFsMisc(page);
+ if (fscache_write_page(NFS_I(inode)->fscache,
+ page,
+ nfs_readpage_to_fscache_complete,
+ NULL,
+ GFP_KERNEL) != 0
+ ) {
+nfs_fscache_stats.write_to_errors++;
+ fscache_uncache_page(NFS_I(inode)->fscache, page);
+ ClearPageFsMisc(page);
+ }
+
+ unlock_page(page);
+}
+#else
+static inline void
+nfs_readpage_to_fscache(struct inode *inode, struct page *page, int sync)
+{
+ BUG();
+}
+#endif
+
+
+/*
* Read a page synchronously.
*/
static int nfs_readpage_sync(struct nfs_open_context *ctx, struct inode *inode,
@@ -164,6 +212,13 @@
ClearPageError(page);
result = 0;
+ if (NFS_SERVER(inode)->flags & NFS_MOUNT_FSCACHE)
+ nfs_readpage_to_fscache(inode, page, 1);
+ else
+ unlock_page(page);
+
+ return result;
+
io_error:
unlock_page(page);
nfs_readdata_free(rdata);
@@ -196,6 +251,14 @@
static void nfs_readpage_release(struct nfs_page *req)
{
+#ifdef CONFIG_NFS_FSCACHE
+ struct inode *d_inode = req->wb_context->dentry->d_inode;
+
+ if ((NFS_SERVER(d_inode)->flags & NFS_MOUNT_FSCACHE) &&
+ PageUptodate(req->wb_page))
+ nfs_readpage_to_fscache(d_inode, req->wb_page, 0);
+ else
+#endif
unlock_page(req->wb_page);
nfs_clear_request(req);
@@ -494,6 +557,57 @@
data->complete(data, status);
}
+
+/*
+ * Read a page through the on-disc cache if possible
+ */
+#ifdef CONFIG_NFS_FSCACHE
+static void
+nfs_readpage_from_fscache_complete(void *cookie_data, struct page *page, void *data, int error)
+{
+ if (error)
+ SetPageError(page);
+ else
+ SetPageUptodate(page);
+ unlock_page(page);
+}
+
+static inline int
+nfs_readpage_from_fscache(struct inode *inode, struct page *page)
+{
+ struct fscache_page *pageio;
+ int ret;
+
+nfs_fscache_stats.read_from_calls++;
+ pageio = fscache_page_get_private(page, GFP_NOIO);
+ if (IS_ERR(pageio))
+ return PTR_ERR(pageio);
+
+ ret = fscache_read_or_alloc_page(NFS_I(inode)->fscache,
+ page,
+ nfs_readpage_from_fscache_complete,
+ NULL,
+ GFP_KERNEL);
+
+ switch (ret) {
+ case 1: /* read BIO submitted and wb-journal entry found */
+ BUG();
+
+ case 0: /* read BIO submitted (page in fscache) */
+nfs_fscache_stats.read_from_hits++;
+ return ret;
+
+ case -ENOBUFS: /* inode not in cache */
+ case -ENODATA: /* page not in cache */
+nfs_fscache_stats.read_from_misses++;
+ return 1;
+
+ default:
+ return ret;
+ }
+}
+#endif
+
/*
* Read a page over NFS.
* We read the page synchronously in the following case:
@@ -527,6 +641,15 @@
ctx = get_nfs_open_context((struct nfs_open_context *)
file->private_data);
if (!IS_SYNC(inode)) {
+#ifdef CONFIG_NFS_FSCACHE
+ if (NFS_SERVER(inode)->flags & NFS_MOUNT_FSCACHE) {
+ error = nfs_readpage_from_fscache(inode, page);
+ if (error < 0)
+ goto out_error;
+ if (error == 0)
+ return error;
+ }
+#endif
error = nfs_readpage_async(ctx, inode, page);
goto out;
}
@@ -557,6 +680,18 @@
unsigned int len;
nfs_wb_page(inode, page);
+
+#ifdef CONFIG_NFS_FSCACHE
+ if (NFS_SERVER(inode)->flags & NFS_MOUNT_FSCACHE) {
+ int error = nfs_readpage_from_fscache(inode, page);
+ if (error < 0)
+ return error;
+ if (error == 0) {
+ return error;
+ }
+ }
+#endif
+
len = nfs_page_length(inode, page);
if (len == 0)
return nfs_return_empty_page(page);
diff -uNr linux-2.6.9-rc2-mm4/fs/nfs/write.c linux-2.6.9-rc2-mm4-fscache/fs/nfs/write.c
--- linux-2.6.9-rc2-mm4/fs/nfs/write.c 2004-09-16 12:08:07.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/fs/nfs/write.c 2004-09-30 20:58:23.000000000 +0100
@@ -273,6 +273,28 @@
}
/*
+ * store an updated page in fscache
+ */
+#ifdef CONFIG_NFS_FSCACHE
+static void
+nfs_writepage_to_fscache_complete(void *cookie_data, struct page *page, void *data, int error)
+{
+ /* really need to synchronise the end of writeback, probably using a page flag */
+}
+static inline void
+nfs_writepage_to_fscache(struct inode *inode, struct page *page)
+{
+ if (fscache_write_page(NFS_I(inode)->fscache,
+ page,
+ nfs_writepage_to_fscache_complete,
+ NULL,
+ GFP_KERNEL) != 0
+ )
+ fscache_uncache_page(NFS_I(inode)->fscache, page);
+}
+#endif
+
+/*
* Write an mmapped page to the server.
*/
int nfs_writepage(struct page *page, struct writeback_control *wbc)
@@ -317,6 +339,12 @@
err = -EBADF;
goto out;
}
+
+#ifdef CONFIG_NFS_FSCACHE
+ if (NFS_SERVER(inode)->flags & NFS_MOUNT_FSCACHE)
+ nfs_writepage_to_fscache(inode, page);
+#endif
+
lock_kernel();
if (!IS_SYNC(inode) && inode_referenced) {
err = nfs_writepage_async(ctx, inode, page, 0, offset);
@@ -1322,6 +1350,7 @@
(long long)NFS_FILEID(req->wb_context->dentry->d_inode),
req->wb_bytes,
(long long)req_offset(req));
+
if (task->tk_status < 0) {
req->wb_context->error = task->tk_status;
nfs_inode_remove_request(req);
diff -uNr linux-2.6.9-rc2-mm4/include/linux/cachefs.h linux-2.6.9-rc2-mm4-fscache/include/linux/cachefs.h
--- linux-2.6.9-rc2-mm4/include/linux/cachefs.h 2004-09-27 11:24:04.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/include/linux/cachefs.h 1970-01-01 01:00:00.000000000 +0100
@@ -1,351 +0,0 @@
-/* cachefs.h: general filesystem caching interface
- *
- * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells at 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.
- */
-
-#ifndef _LINUX_CACHEFS_H
-#define _LINUX_CACHEFS_H
-
-#include <linux/config.h>
-#include <linux/fs.h>
-#include <linux/list.h>
-#include <linux/pagemap.h>
-
-#ifdef CONFIG_CACHEFS_MODULE
-#define CONFIG_CACHEFS
-#endif
-
-struct cachefs_cookie;
-struct cachefs_netfs;
-struct cachefs_netfs_operations;
-struct cachefs_page;
-
-#define CACHEFS_NEGATIVE_COOKIE NULL
-
-typedef void (*cachefs_rw_complete_t)(void *cookie_data,
- struct page *page,
- void *data,
- int error);
-
-/* result of index entry comparison */
-typedef enum {
- /* no match */
- CACHEFS_MATCH_FAILED,
-
- /* successful match */
- CACHEFS_MATCH_SUCCESS,
-
- /* successful match, entry requires update */
- CACHEFS_MATCH_SUCCESS_UPDATE,
-
- /* successful match, entry requires deletion */
- CACHEFS_MATCH_SUCCESS_DELETE,
-} cachefs_match_val_t;
-
-/*****************************************************************************/
-/*
- * cachefs index definition
- * - each index file contains a number of fixed size entries
- * - they don't have to fit exactly into a page, but if they don't, the gap
- * at the end of the page will not be used
- */
-struct cachefs_index_def
-{
- /* name of index */
- uint8_t name[8];
-
- /* size of data to be stored in index */
- uint16_t data_size;
-
- /* key description (for displaying in cache mountpoint) */
- struct {
- uint8_t type;
- uint16_t len;
- } keys[4];
-
-#define CACHEFS_INDEX_KEYS_NOTUSED 0
-#define CACHEFS_INDEX_KEYS_BIN 1
-#define CACHEFS_INDEX_KEYS_ASCIIZ 2
-#define CACHEFS_INDEX_KEYS_IPV4ADDR 3
-#define CACHEFS_INDEX_KEYS_IPV6ADDR 4
-#define CACHEFS_INDEX_KEYS__LAST CACHEFS_INDEX_KEYS_IPV6ADDR
-
- /* see if entry matches the specified key
- * - the netfs data from the cookie being used as the target is
- * presented
- * - entries that aren't in use will not be presented for matching
- */
- cachefs_match_val_t (*match)(void *target_netfs_data,
- const void *entry);
-
- /* update entry from key
- * - the netfs data from the cookie being used as the source is
- * presented
- */
- void (*update)(void *source_netfs_data, void *entry);
-};
-
-#ifdef CONFIG_CACHEFS
-extern struct cachefs_cookie *__cachefs_acquire_cookie(struct cachefs_cookie *iparent,
- struct cachefs_index_def *idef,
- void *netfs_data);
-
-extern void __cachefs_relinquish_cookie(struct cachefs_cookie *cookie,
- int retire);
-
-extern void __cachefs_update_cookie(struct cachefs_cookie *cookie);
-#endif
-
-static inline
-struct cachefs_cookie *cachefs_acquire_cookie(struct cachefs_cookie *iparent,
- struct cachefs_index_def *idef,
- void *netfs_data)
-{
-#ifdef CONFIG_CACHEFS
- if (iparent != CACHEFS_NEGATIVE_COOKIE)
- return __cachefs_acquire_cookie(iparent, idef, netfs_data);
-#endif
- return CACHEFS_NEGATIVE_COOKIE;
-}
-
-static inline
-void cachefs_relinquish_cookie(struct cachefs_cookie *cookie,
- int retire)
-{
-#ifdef CONFIG_CACHEFS
- if (cookie != CACHEFS_NEGATIVE_COOKIE)
- __cachefs_relinquish_cookie(cookie, retire);
-#endif
-}
-
-static inline
-void cachefs_update_cookie(struct cachefs_cookie *cookie)
-{
-#ifdef CONFIG_CACHEFS
- if (cookie != CACHEFS_NEGATIVE_COOKIE)
- __cachefs_update_cookie(cookie);
-#endif
-}
-
-/*****************************************************************************/
-/*
- * cachefs cached network filesystem type
- * - name, version and ops must be filled in before registration
- * - all other fields will be set during registration
- */
-struct cachefs_netfs
-{
- const char *name; /* filesystem name */
- unsigned version; /* indexing version */
- struct cachefs_cookie *primary_index;
- struct cachefs_netfs_operations *ops;
- struct list_head link; /* internal link */
-};
-
-struct cachefs_netfs_operations
-{
- /* get page-to-block mapping cookie for a page
- * - one should be allocated if it doesn't exist
- * - returning -ENODATA will cause this page to be ignored
- * - typically, the struct will be attached to page->private
- */
- struct cachefs_page *(*get_page_cookie)(struct page *page);
-};
-
-#ifdef CONFIG_CACHEFS
-extern int __cachefs_register_netfs(struct cachefs_netfs *netfs,
- struct cachefs_index_def *primary_idef);
-extern void __cachefs_unregister_netfs(struct cachefs_netfs *netfs);
-#endif
-
-static inline
-int cachefs_register_netfs(struct cachefs_netfs *netfs,
- struct cachefs_index_def *primary_idef)
-{
-#ifdef CONFIG_CACHEFS
- return __cachefs_register_netfs(netfs, primary_idef);
-#else
- return 0;
-#endif
-}
-
-static inline
-void cachefs_unregister_netfs(struct cachefs_netfs *netfs)
-{
-#ifdef CONFIG_CACHEFS
- __cachefs_unregister_netfs(netfs);
-#endif
-}
-
-/*****************************************************************************/
-/*
- * page mapping cookie
- * - stores the mapping of a page to a block in the cache (may also be null)
- * - note that the mapping may be removed without notice if a cache is removed
- */
-struct cachefs_page
-{
- struct cachefs_block *mapped_block; /* block mirroring this page */
- rwlock_t lock;
-
- unsigned long flags;
-#define CACHEFS_PAGE_BOUNDARY 0 /* next block has a different
- * indirection chain */
-#define CACHEFS_PAGE_NEW 1 /* this is a newly allocated block */
-};
-
-/*
- * read a page from the cache or allocate a block in which to store it
- * - if the cookie is not backed by a file:
- * - -ENOBUFS will be returned and nothing more will be done
- * - else if the page is backed by a block in the cache:
- * - a read will be started which will call end_io_func on completion
- * - the wb-journal will be searched for an entry pertaining to this block
- * - if an entry is found:
- * - 1 will be returned [not yet supported]
- * else
- * - 0 will be returned
- * - else if the page is unbacked:
- * - a block will be allocated and attached
- * - the validity journal will be marked to note the block does not yet
- * contain valid data
- * - -ENODATA will be returned
- */
-#ifdef CONFIG_CACHEFS
-extern int __cachefs_read_or_alloc_page(struct cachefs_cookie *cookie,
- struct page *page,
- cachefs_rw_complete_t end_io_func,
- void *end_io_data,
- unsigned long gfp);
-#endif
-
-static inline
-int cachefs_read_or_alloc_page(struct cachefs_cookie *cookie,
- struct page *page,
- cachefs_rw_complete_t end_io_func,
- void *end_io_data,
- unsigned long gfp)
-{
-#ifdef CONFIG_CACHEFS
- if (cookie != CACHEFS_NEGATIVE_COOKIE)
- return __cachefs_read_or_alloc_page(cookie, page, end_io_func,
- end_io_data, gfp);
-#endif
- return -ENOBUFS;
-}
-
-/*
- * request a page be stored in the cache
- * - this request may be ignored if no cache block is currently attached, in
- * which case it:
- * - returns -ENOBUFS
- * - if a cache block was already allocated:
- * - the page cookie will be updated to reflect the block selected
- * - a BIO will be dispatched to write the page (end_io_func will be called
- * from the completion function)
- * - end_io_func can be NULL, in which case a default function will just
- * clear the writeback bit on the page
- * - any associated validity journal entry will be cleared
- * - returns 0
- */
-#ifdef CONFIG_CACHEFS
-extern int __cachefs_write_page(struct cachefs_cookie *cookie,
- struct page *page,
- cachefs_rw_complete_t end_io_func,
- void *end_io_data,
- unsigned long gfp);
-#endif
-
-static inline
-int cachefs_write_page(struct cachefs_cookie *cookie,
- struct page *page,
- cachefs_rw_complete_t end_io_func,
- void *end_io_data,
- unsigned long gfp)
-{
-#ifdef CONFIG_CACHEFS
- if (cookie != CACHEFS_NEGATIVE_COOKIE)
- return __cachefs_write_page(cookie, page, end_io_func,
- end_io_data, gfp);
-#endif
- return -ENOBUFS;
-}
-
-/*
- * indicate that caching is no longer required on a page
- * - note: cannot cancel any outstanding BIOs between this page and the cache
- */
-#ifdef CONFIG_CACHEFS
-extern void __cachefs_uncache_page(struct cachefs_cookie *cookie,
- struct page *page);
-#endif
-
-static inline
-void cachefs_uncache_page(struct cachefs_cookie *cookie,
- struct page *page)
-{
-#ifdef CONFIG_CACHEFS
- __cachefs_uncache_page(cookie, page);
-#endif
-}
-
-/*
- * keep track of pages changed locally but not yet committed
- */
-#if 0 /* TODO */
-extern void cachefs_writeback_prepare(struct cachefs_cookie *cookie,
- struct page *page,
- unsigned short from,
- unsigned short to);
-
-extern void cachefs_writeback_committed(struct cachefs_cookie *cookie,
- struct page *page,
- unsigned short from,
- unsigned short to);
-
-extern void cachefs_writeback_aborted(struct cachefs_cookie *cookie,
- struct page *page,
- unsigned short from,
- unsigned short to);
-#endif
-
-/*
- * convenience routines for mapping page->private directly to a struct
- * cachefs_page
- */
-static inline
-struct cachefs_page *__cachefs_page_grab_private(struct page *page)
-{
- return (struct cachefs_page *) (PagePrivate(page) ? page->private : 0);
-}
-
-#define cachefs_page_grab_private(X) \
-({ \
- BUG_ON(!PagePrivate(X)); \
- __cachefs_page_grab_private(X); \
-})
-
-
-#ifdef CONFIG_CACHEFS
-extern struct cachefs_page *__cachefs_page_get_private(struct page *page,
- unsigned gfp);
-#endif
-
-static inline
-struct cachefs_page *cachefs_page_get_private(struct page *page,
- unsigned gfp)
-{
-#ifdef CONFIG_CACHEFS
- return __cachefs_page_get_private(page, gfp);
-#else
- return ERR_PTR(-EIO);
-#endif
-}
-
-#endif /* _LINUX_CACHEFS_H */
diff -uNr linux-2.6.9-rc2-mm4/include/linux/fscache-cache.h linux-2.6.9-rc2-mm4-fscache/include/linux/fscache-cache.h
--- linux-2.6.9-rc2-mm4/include/linux/fscache-cache.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/include/linux/fscache-cache.h 2004-10-04 17:23:28.357675703 +0100
@@ -0,0 +1,205 @@
+/* fscache-cache.h: general filesystem caching backing cache interface
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at 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.
+ */
+
+#ifndef _LINUX_FSCACHE_CACHE_H
+#define _LINUX_FSCACHE_CACHE_H
+
+#include <linux/fscache.h>
+
+struct fscache_cache;
+struct fscache_cache_ops;
+struct fscache_node;
+struct fscache_search_result;
+
+struct fscache_search_result {
+ struct list_head link; /* link in search_results */
+ struct fscache_cache *cache; /* cache searched */
+ unsigned ino; /* node ID (or 0 if negative) */
+};
+
+struct fscache_cache {
+ struct fscache_cache_ops *ops;
+ struct list_head link; /* link in list of caches */
+ size_t max_index_size; /* maximum size of index data */
+ unsigned long flags;
+#define FSCACHE_CACHE_WITHDRAWN 0 /* T if cache has been withdrawn */
+
+ char identifier[32]; /* cache label */
+
+ /* node management */
+ struct list_head node_list; /* list of data/index nodes */
+ spinlock_t node_list_lock;
+ struct fscache_search_result fsdef_srch; /* search result for the fsdef index */
+};
+
+extern void fscache_init_cache(struct fscache_cache *cache,
+ struct fscache_cache_ops *ops,
+ unsigned fsdef_ino,
+ const char *idfmt,
+ ...) __attribute__ ((format (printf,4,5)));
+
+extern void fscache_add_cache(struct fscache_cache *cache);
+extern void fscache_withdraw_cache(struct fscache_cache *cache);
+
+/* see if a cache has been withdrawn */
+static inline int fscache_is_cache_withdrawn(struct fscache_cache *cache)
+{
+ return test_bit(FSCACHE_CACHE_WITHDRAWN, &cache->flags);
+}
+
+/*****************************************************************************/
+/*
+ * cache operations
+ */
+struct fscache_cache_ops {
+ /* name of cache provider */
+ const char *name;
+
+ /* look up the nominated node for this cache */
+ struct fscache_node *(*lookup_node)(struct fscache_cache *cache, unsigned ino);
+
+ /* increment the usage count on this inode (may fail if unmounting) */
+ struct fscache_node *(*grab_node)(struct fscache_node *node);
+
+ /* lock a semaphore on a node */
+ void (*lock_node)(struct fscache_node *node);
+
+ /* unlock a semaphore on a node */
+ void (*unlock_node)(struct fscache_node *node);
+
+ /* dispose of a reference to a node */
+ void (*put_node)(struct fscache_node *node);
+
+ /* search an index for an inode to back a cookie
+ * - the "inode number" should be set in result->ino
+ */
+ int (*index_search)(struct fscache_node *node, struct fscache_cookie *cookie,
+ struct fscache_search_result *result);
+
+ /* create a new file or inode, with an entry in the named index
+ * - the "inode number" should be set in result->ino
+ */
+ int (*index_add)(struct fscache_node *node, struct fscache_cookie *cookie,
+ struct fscache_search_result *result);
+
+ /* update the index entry for a node
+ * - the netfs's update operation should be called
+ */
+ int (*index_update)(struct fscache_node *ixnode,
+ struct fscache_node *node);
+
+ /* sync a cache */
+ void (*sync)(struct fscache_cache *cache);
+
+ /* dissociate a cache from all the pages it was backing */
+ void (*dissociate_pages)(struct fscache_cache *cache);
+
+ /* request a backing block for a page be read or allocated in the
+ * cache */
+ int (*read_or_alloc_page)(struct fscache_node *node,
+ struct page *page,
+ struct fscache_page *pageio,
+ fscache_rw_complete_t end_io_func,
+ void *end_io_data,
+ unsigned long gfp);
+
+ /* write a page to its backing block in the cache */
+ int (*write_page)(struct fscache_node *node,
+ struct page *page,
+ struct fscache_page *pageio,
+ fscache_rw_complete_t end_io_func,
+ void *end_io_data,
+ unsigned long gfp);
+
+ /* detach a backing block from a page */
+ void (*uncache_page)(struct fscache_node *node,
+ struct fscache_page *pageio);
+};
+
+/*****************************************************************************/
+/*
+ * data file or index object cookie
+ * - a file will only appear in one cache
+ * - a request to cache a file may or may not be honoured, subject to
+ * constraints such as disc space
+ * - indexes files are created on disc just-in-time
+ */
+struct fscache_cookie {
+ atomic_t usage; /* number of users of this cookie */
+ atomic_t children; /* number of children of this cookie */
+ rwlock_t lock; /* list access lock */
+ struct rw_semaphore sem; /* list creation vs scan lock */
+ struct list_head search_results; /* results of searching iparent */
+ struct list_head backing_nodes; /* node(s) backing this file/index */
+ struct fscache_index_def *idef; /* index definition */
+ struct fscache_cookie *iparent; /* index holding this entry */
+ struct fscache_netfs *netfs; /* owner network fs definition */
+ void *netfs_data; /* back pointer to netfs */
+};
+
+extern struct fscache_cookie fscache_fsdef_index;
+
+/*****************************************************************************/
+/*
+ * on-disc cache file or index handle
+ */
+struct fscache_node {
+ unsigned long flags;
+#define FSCACHE_NODE_ISINDEX 0 /* T if inode is index file (F if file) */
+#define FSCACHE_NODE_RELEASING 1 /* T if inode is being released */
+#define FSCACHE_NODE_RECYCLING 2 /* T if inode is being retired */
+#define FSCACHE_NODE_WITHDRAWN 3 /* T if inode has been withdrawn */
+
+ struct list_head cache_link; /* link in cache->node_list */
+ struct list_head cookie_link; /* link in cookie->backing_nodes */
+ struct fscache_cache *cache; /* cache that supplied this node */
+ struct fscache_cookie *cookie; /* netfs's file/index object */
+};
+
+static inline
+void fscache_node_init(struct fscache_node *node)
+{
+ node->flags = 0;
+ INIT_LIST_HEAD(&node->cache_link);
+ INIT_LIST_HEAD(&node->cookie_link);
+ node->cache = NULL;
+ node->cookie = NULL;
+}
+
+/* find the parent index node for a node */
+static inline
+struct fscache_node *fscache_find_parent_node(struct fscache_node *node)
+{
+ struct fscache_cookie *cookie = node->cookie;
+ struct fscache_cache *cache = node->cache;
+ struct fscache_node *parent;
+
+ list_for_each_entry(parent,
+ &cookie->iparent->backing_nodes,
+ cookie_link
+ ) {
+ if (parent->cache == cache)
+ return parent;
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************/
+/*
+ * definition of the contents of an FSDEF index entry
+ */
+struct fscache_fsdef_index_entry {
+ uint8_t name[24]; /* name of netfs */
+ uint32_t version; /* version of layout */
+};
+
+#endif /* _LINUX_FSCACHE_CACHE_H */
diff -uNr linux-2.6.9-rc2-mm4/include/linux/fscache.h linux-2.6.9-rc2-mm4-fscache/include/linux/fscache.h
--- linux-2.6.9-rc2-mm4/include/linux/fscache.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/include/linux/fscache.h 2004-09-30 17:27:30.000000000 +0100
@@ -0,0 +1,351 @@
+/* fscache.h: general filesystem caching interface
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at 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.
+ */
+
+#ifndef _LINUX_FSCACHE_H
+#define _LINUX_FSCACHE_H
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/pagemap.h>
+
+#ifdef CONFIG_FSCACHE_MODULE
+#define CONFIG_FSCACHE
+#endif
+
+struct fscache_cookie;
+struct fscache_netfs;
+struct fscache_netfs_operations;
+struct fscache_page;
+
+#define FSCACHE_NEGATIVE_COOKIE NULL
+
+typedef void (*fscache_rw_complete_t)(void *cookie_data,
+ struct page *page,
+ void *data,
+ int error);
+
+/* result of index entry comparison */
+typedef enum {
+ /* no match */
+ FSCACHE_MATCH_FAILED,
+
+ /* successful match */
+ FSCACHE_MATCH_SUCCESS,
+
+ /* successful match, entry requires update */
+ FSCACHE_MATCH_SUCCESS_UPDATE,
+
+ /* successful match, entry requires deletion */
+ FSCACHE_MATCH_SUCCESS_DELETE,
+} fscache_match_val_t;
+
+/*****************************************************************************/
+/*
+ * fscache index definition
+ * - each index file contains a number of fixed size entries
+ * - they don't have to fit exactly into a page, but if they don't, the gap
+ * at the end of the page will not be used
+ */
+struct fscache_index_def
+{
+ /* name of index */
+ uint8_t name[8];
+
+ /* size of data to be stored in index */
+ uint16_t data_size;
+
+ /* key description (for displaying in cache mountpoint) */
+ struct {
+ uint8_t type;
+ uint16_t len;
+ } keys[4];
+
+#define FSCACHE_INDEX_KEYS_NOTUSED 0
+#define FSCACHE_INDEX_KEYS_BIN 1
+#define FSCACHE_INDEX_KEYS_ASCIIZ 2
+#define FSCACHE_INDEX_KEYS_IPV4ADDR 3
+#define FSCACHE_INDEX_KEYS_IPV6ADDR 4
+#define FSCACHE_INDEX_KEYS__LAST FSCACHE_INDEX_KEYS_IPV6ADDR
+
+ /* see if entry matches the specified key
+ * - the netfs data from the cookie being used as the target is
+ * presented
+ * - entries that aren't in use will not be presented for matching
+ */
+ fscache_match_val_t (*match)(void *target_netfs_data,
+ const void *entry);
+
+ /* update entry from key
+ * - the netfs data from the cookie being used as the source is
+ * presented
+ */
+ void (*update)(void *source_netfs_data, void *entry);
+};
+
+#ifdef CONFIG_FSCACHE
+extern struct fscache_cookie *__fscache_acquire_cookie(struct fscache_cookie *iparent,
+ struct fscache_index_def *idef,
+ void *netfs_data);
+
+extern void __fscache_relinquish_cookie(struct fscache_cookie *cookie,
+ int retire);
+
+extern void __fscache_update_cookie(struct fscache_cookie *cookie);
+#endif
+
+static inline
+struct fscache_cookie *fscache_acquire_cookie(struct fscache_cookie *iparent,
+ struct fscache_index_def *idef,
+ void *netfs_data)
+{
+#ifdef CONFIG_FSCACHE
+ if (iparent != FSCACHE_NEGATIVE_COOKIE)
+ return __fscache_acquire_cookie(iparent, idef, netfs_data);
+#endif
+ return FSCACHE_NEGATIVE_COOKIE;
+}
+
+static inline
+void fscache_relinquish_cookie(struct fscache_cookie *cookie,
+ int retire)
+{
+#ifdef CONFIG_FSCACHE
+ if (cookie != FSCACHE_NEGATIVE_COOKIE)
+ __fscache_relinquish_cookie(cookie, retire);
+#endif
+}
+
+static inline
+void fscache_update_cookie(struct fscache_cookie *cookie)
+{
+#ifdef CONFIG_FSCACHE
+ if (cookie != FSCACHE_NEGATIVE_COOKIE)
+ __fscache_update_cookie(cookie);
+#endif
+}
+
+/*****************************************************************************/
+/*
+ * fscache cached network filesystem type
+ * - name, version and ops must be filled in before registration
+ * - all other fields will be set during registration
+ */
+struct fscache_netfs
+{
+ const char *name; /* filesystem name */
+ unsigned version; /* indexing version */
+ struct fscache_cookie *primary_index;
+ struct fscache_netfs_operations *ops;
+ struct list_head link; /* internal link */
+};
+
+struct fscache_netfs_operations
+{
+ /* get page-to-block mapping token for a page
+ * - one should be allocated if it doesn't exist
+ * - returning -ENODATA will cause this page to be ignored
+ * - typically, the struct will be attached to page->private
+ */
+ struct fscache_page *(*get_page_token)(struct page *page);
+};
+
+#ifdef CONFIG_FSCACHE
+extern int __fscache_register_netfs(struct fscache_netfs *netfs,
+ struct fscache_index_def *primary_idef);
+extern void __fscache_unregister_netfs(struct fscache_netfs *netfs);
+#endif
+
+static inline
+int fscache_register_netfs(struct fscache_netfs *netfs,
+ struct fscache_index_def *primary_idef)
+{
+#ifdef CONFIG_FSCACHE
+ return __fscache_register_netfs(netfs, primary_idef);
+#else
+ return 0;
+#endif
+}
+
+static inline
+void fscache_unregister_netfs(struct fscache_netfs *netfs)
+{
+#ifdef CONFIG_FSCACHE
+ __fscache_unregister_netfs(netfs);
+#endif
+}
+
+/*****************************************************************************/
+/*
+ * page mapping cookie
+ * - stores the mapping of a page to a block in the cache (may also be null)
+ * - note that the mapping may be removed without notice if a cache is removed
+ */
+struct fscache_page
+{
+ void *mapped_block; /* block mirroring this page */
+ rwlock_t lock;
+
+ unsigned long flags;
+#define FSCACHE_PAGE_BOUNDARY 0 /* next block has a different
+ * indirection chain */
+#define FSCACHE_PAGE_NEW 1 /* this is a newly allocated block */
+};
+
+/*
+ * read a page from the cache or allocate a block in which to store it
+ * - if the cookie is not backed by a file:
+ * - -ENOBUFS will be returned and nothing more will be done
+ * - else if the page is backed by a block in the cache:
+ * - a read will be started which will call end_io_func on completion
+ * - the wb-journal will be searched for an entry pertaining to this block
+ * - if an entry is found:
+ * - 1 will be returned [not yet supported]
+ * else
+ * - 0 will be returned
+ * - else if the page is unbacked:
+ * - a block will be allocated and attached
+ * - the validity journal will be marked to note the block does not yet
+ * contain valid data
+ * - -ENODATA will be returned
+ */
+#ifdef CONFIG_FSCACHE
+extern int __fscache_read_or_alloc_page(struct fscache_cookie *cookie,
+ struct page *page,
+ fscache_rw_complete_t end_io_func,
+ void *end_io_data,
+ unsigned long gfp);
+#endif
+
+static inline
+int fscache_read_or_alloc_page(struct fscache_cookie *cookie,
+ struct page *page,
+ fscache_rw_complete_t end_io_func,
+ void *end_io_data,
+ unsigned long gfp)
+{
+#ifdef CONFIG_FSCACHE
+ if (cookie != FSCACHE_NEGATIVE_COOKIE)
+ return __fscache_read_or_alloc_page(cookie, page, end_io_func,
+ end_io_data, gfp);
+#endif
+ return -ENOBUFS;
+}
+
+/*
+ * request a page be stored in the cache
+ * - this request may be ignored if no cache block is currently attached, in
+ * which case it:
+ * - returns -ENOBUFS
+ * - if a cache block was already allocated:
+ * - the page cookie will be updated to reflect the block selected
+ * - a BIO will be dispatched to write the page (end_io_func will be called
+ * from the completion function)
+ * - end_io_func can be NULL, in which case a default function will just
+ * clear the writeback bit on the page
+ * - any associated validity journal entry will be cleared
+ * - returns 0
+ */
+#ifdef CONFIG_FSCACHE
+extern int __fscache_write_page(struct fscache_cookie *cookie,
+ struct page *page,
+ fscache_rw_complete_t end_io_func,
+ void *end_io_data,
+ unsigned long gfp);
+#endif
+
+static inline
+int fscache_write_page(struct fscache_cookie *cookie,
+ struct page *page,
+ fscache_rw_complete_t end_io_func,
+ void *end_io_data,
+ unsigned long gfp)
+{
+#ifdef CONFIG_FSCACHE
+ if (cookie != FSCACHE_NEGATIVE_COOKIE)
+ return __fscache_write_page(cookie, page, end_io_func,
+ end_io_data, gfp);
+#endif
+ return -ENOBUFS;
+}
+
+/*
+ * indicate that caching is no longer required on a page
+ * - note: cannot cancel any outstanding BIOs between this page and the cache
+ */
+#ifdef CONFIG_FSCACHE
+extern void __fscache_uncache_page(struct fscache_cookie *cookie,
+ struct page *page);
+#endif
+
+static inline
+void fscache_uncache_page(struct fscache_cookie *cookie,
+ struct page *page)
+{
+#ifdef CONFIG_FSCACHE
+ __fscache_uncache_page(cookie, page);
+#endif
+}
+
+/*
+ * keep track of pages changed locally but not yet committed
+ */
+#if 0 /* TODO */
+extern void fscache_writeback_prepare(struct fscache_cookie *cookie,
+ struct page *page,
+ unsigned short from,
+ unsigned short to);
+
+extern void fscache_writeback_committed(struct fscache_cookie *cookie,
+ struct page *page,
+ unsigned short from,
+ unsigned short to);
+
+extern void fscache_writeback_aborted(struct fscache_cookie *cookie,
+ struct page *page,
+ unsigned short from,
+ unsigned short to);
+#endif
+
+/*
+ * convenience routines for mapping page->private directly to a struct
+ * fscache_page
+ */
+static inline
+struct fscache_page *__fscache_page_grab_private(struct page *page)
+{
+ return (struct fscache_page *) (PagePrivate(page) ? page->private : 0);
+}
+
+#define fscache_page_grab_private(X) \
+({ \
+ BUG_ON(!PagePrivate(X)); \
+ __fscache_page_grab_private(X); \
+})
+
+
+#ifdef CONFIG_FSCACHE
+extern struct fscache_page *__fscache_page_get_private(struct page *page,
+ unsigned gfp);
+#endif
+
+static inline
+struct fscache_page *fscache_page_get_private(struct page *page,
+ unsigned gfp)
+{
+#ifdef CONFIG_FSCACHE
+ return __fscache_page_get_private(page, gfp);
+#else
+ return ERR_PTR(-EIO);
+#endif
+}
+
+#endif /* _LINUX_FSCACHE_H */
diff -uNr linux-2.6.9-rc2-mm4/include/linux/nfs_fs.h linux-2.6.9-rc2-mm4-fscache/include/linux/nfs_fs.h
--- linux-2.6.9-rc2-mm4/include/linux/nfs_fs.h 2004-09-16 12:08:13.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/include/linux/nfs_fs.h 2004-09-30 13:43:00.000000000 +0100
@@ -30,6 +30,7 @@
#include <linux/nfs_xdr.h>
#include <linux/rwsem.h>
#include <linux/workqueue.h>
+#include <linux/fscache.h>
/*
* Enable debugging support for nfs client.
@@ -189,6 +190,10 @@
struct rw_semaphore rwsem;
#endif /* CONFIG_NFS_V4*/
+#ifdef CONFIG_NFS_FSCACHE
+ struct fscache_cookie *fscache;
+#endif
+
struct inode vfs_inode;
};
diff -uNr linux-2.6.9-rc2-mm4/include/linux/nfs_fs_sb.h linux-2.6.9-rc2-mm4-fscache/include/linux/nfs_fs_sb.h
--- linux-2.6.9-rc2-mm4/include/linux/nfs_fs_sb.h 2004-09-16 12:08:13.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/include/linux/nfs_fs_sb.h 2004-09-30 13:43:12.000000000 +0100
@@ -3,6 +3,7 @@
#include <linux/list.h>
#include <linux/backing-dev.h>
+#include <linux/fscache.h>
/*
* NFS client parameters stored in the superblock.
@@ -46,6 +47,10 @@
that are supported on this
filesystem */
#endif
+
+#ifdef CONFIG_NFS_FSCACHE
+ struct fscache_cookie *fscache; /* cache cookie */
+#endif
};
/* Server capabilities */
diff -uNr linux-2.6.9-rc2-mm4/include/linux/nfs_mount.h linux-2.6.9-rc2-mm4-fscache/include/linux/nfs_mount.h
--- linux-2.6.9-rc2-mm4/include/linux/nfs_mount.h 2004-06-18 13:42:15.000000000 +0100
+++ linux-2.6.9-rc2-mm4-fscache/include/linux/nfs_mount.h 2004-09-30 18:48:28.000000000 +0100
@@ -60,6 +60,7 @@
#define NFS_MOUNT_BROKEN_SUID 0x0400 /* 4 */
#define NFS_MOUNT_STRICTLOCK 0x1000 /* reserved for NFSv4 */
#define NFS_MOUNT_SECFLAVOUR 0x2000 /* 5 */
+#define NFS_MOUNT_FSCACHE NFS_MOUNT_POSIX
#define NFS_MOUNT_FLAGMASK 0xFFFF
#endif
--Multipart_Mon_Oct__4_17:32:25_2004-1--
More information about the Linux-cachefs
mailing list