This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / fs / nfs / fscache.h
diff --git a/fs/nfs/fscache.h b/fs/nfs/fscache.h
new file mode 100644 (file)
index 0000000..69f0f40
--- /dev/null
@@ -0,0 +1,466 @@
+/* fscache.h: NFS filesystem cache interface definitions
+ *
+ * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _NFS_FSCACHE_H
+#define _NFS_FSCACHE_H
+
+#include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
+#include <linux/nfs4_mount.h>
+
+#ifdef CONFIG_NFS_FSCACHE
+#include <linux/fscache.h>
+
+extern struct fscache_netfs nfs_cache_netfs;
+extern struct fscache_cookie_def nfs_cache_server_index_def;
+extern struct fscache_cookie_def nfs_cache_fh_index_def;
+extern struct vm_operations_struct nfs_fs_vm_operations;
+
+extern void nfs_invalidatepage(struct page *, unsigned long);
+extern int nfs_releasepage(struct page *, gfp_t);
+
+extern atomic_t nfs_fscache_to_pages;
+extern atomic_t nfs_fscache_from_pages;
+extern atomic_t nfs_fscache_uncache_page;
+extern int nfs_fscache_from_error;
+extern int nfs_fscache_to_error;
+
+/*
+ * register NFS for caching
+ */
+static inline int nfs_fscache_register(void)
+{
+       return fscache_register_netfs(&nfs_cache_netfs);
+}
+
+/*
+ * unregister NFS for caching
+ */
+static inline void nfs_fscache_unregister(void)
+{
+       fscache_unregister_netfs(&nfs_cache_netfs);
+}
+
+/*
+ * get the per-client index cookie for an NFS client if the appropriate mount
+ * flag was set
+ * - we always try and get an index cookie for the client, but get filehandle
+ *   cookies on a per-superblock basis, depending on the mount flags
+ */
+static inline void nfs_fscache_get_client_cookie(struct nfs_client *clp)
+{
+       /* create a cache index for looking up filehandles */
+       clp->fscache = fscache_acquire_cookie(nfs_cache_netfs.primary_index,
+                                             &nfs_cache_server_index_def,
+                                             clp);
+       dfprintk(FSCACHE,"NFS: get client cookie (0x%p/0x%p)\n",
+                clp, clp->fscache);
+}
+
+/*
+ * dispose of a per-client cookie
+ */
+static inline void nfs_fscache_release_client_cookie(struct nfs_client *clp)
+{
+       dfprintk(FSCACHE,"NFS: releasing client cookie (0x%p/0x%p)\n",
+               clp, clp->fscache);
+
+       fscache_relinquish_cookie(clp->fscache, 0);
+       clp->fscache = NULL;
+}
+
+/*
+ * indicate the client caching state as readable text
+ */
+static inline const char *nfs_server_fscache_state(struct nfs_server *server)
+{
+       if (server->nfs_client->fscache && (server->flags & NFS_MOUNT_FSCACHE))
+               return "yes";
+       return "no ";
+}
+
+/*
+ * get the per-filehandle cookie for an NFS inode
+ */
+static inline void nfs_fscache_get_fh_cookie(struct super_block *sb,
+                                            struct nfs_inode *nfsi,
+                                            int maycache)
+{
+       nfsi->fscache = NULL;
+       if (maycache && (NFS_SB(sb)->flags & NFS_MOUNT_FSCACHE)) {
+               nfsi->fscache = fscache_acquire_cookie(
+                       NFS_SB(sb)->nfs_client->fscache,
+                       &nfs_cache_fh_index_def,
+                       nfsi);
+
+               fscache_set_i_size(nfsi->fscache, nfsi->vfs_inode.i_size);
+
+               dfprintk(FSCACHE, "NFS: get FH cookie (0x%p/0x%p/0x%p)\n",
+                        sb, nfsi, nfsi->fscache);
+       }
+}
+
+/*
+ * change the filesize associated with a per-filehandle cookie
+ */
+static inline void nfs_fscache_set_size(struct nfs_server *server,
+                                       struct nfs_inode *nfsi,
+                                       loff_t i_size)
+{
+       fscache_set_i_size(nfsi->fscache, i_size);
+}
+
+/*
+ * replace a per-filehandle cookie due to revalidation detecting a file having
+ * changed on the server
+ */
+static inline void nfs_fscache_renew_fh_cookie(struct nfs_server *server,
+                                              struct nfs_inode *nfsi)
+{
+       struct fscache_cookie *old = nfsi->fscache;
+
+       if (nfsi->fscache) {
+               /* retire the current fscache cache and get a new one */
+               fscache_relinquish_cookie(nfsi->fscache, 1);
+
+               nfsi->fscache = fscache_acquire_cookie(
+                       server->nfs_client->fscache,
+                       &nfs_cache_fh_index_def,
+                       nfsi);
+               fscache_set_i_size(nfsi->fscache, nfsi->vfs_inode.i_size);
+
+               dfprintk(FSCACHE,
+                        "NFS: revalidation new cookie (0x%p/0x%p/0x%p/0x%p)\n",
+                        server, nfsi, old, nfsi->fscache);
+       }
+}
+
+/*
+ * release a per-filehandle cookie
+ */
+static inline void nfs_fscache_release_fh_cookie(struct nfs_server *server,
+                                                struct nfs_inode *nfsi)
+{
+       dfprintk(FSCACHE, "NFS: clear cookie (0x%p/0x%p)\n",
+                nfsi, nfsi->fscache);
+
+       fscache_relinquish_cookie(nfsi->fscache, 0);
+       nfsi->fscache = NULL;
+}
+
+/*
+ * retire a per-filehandle cookie, destroying the data attached to it
+ */
+static inline void nfs_fscache_zap_fh_cookie(struct nfs_server *server,
+                                            struct nfs_inode *nfsi)
+{
+       dfprintk(FSCACHE,"NFS: zapping cookie (0x%p/0x%p)\n",
+               nfsi, nfsi->fscache);
+
+       fscache_relinquish_cookie(nfsi->fscache, 1);
+       nfsi->fscache = NULL;
+}
+
+/*
+ * turn off the cache with regard to a filehandle cookie if opened for writing,
+ * invalidating all the pages in the page cache relating to the associated
+ * inode to clear the per-page caching
+ */
+static inline void nfs_fscache_disable_fh_cookie(struct inode *inode)
+{
+       if (NFS_I(inode)->fscache) {
+               dfprintk(FSCACHE,
+                        "NFS: nfsi 0x%p turning cache off\n", NFS_I(inode));
+
+               /* Need to invalided any mapped pages that were read in before
+                * turning off the cache.
+                */
+               if (inode->i_mapping && inode->i_mapping->nrpages)
+                       invalidate_inode_pages2(inode->i_mapping);
+
+               nfs_fscache_zap_fh_cookie(NFS_SERVER(inode), NFS_I(inode));
+       }
+}
+
+/*
+ * install the VM ops for mmap() of an NFS file so that we can hold up writes
+ * to pages on shared writable mappings until the store to the cache is
+ * complete
+ */
+static inline void nfs_fscache_install_vm_ops(struct inode *inode,
+                                             struct vm_area_struct *vma)
+{
+       if (NFS_I(inode)->fscache)
+               vma->vm_ops = &nfs_fs_vm_operations;
+}
+
+/*
+ * release the caching state associated with a page
+ */
+static void nfs_fscache_release_page(struct page *page)
+{
+       if (PageNfsCached(page)) {
+               struct nfs_inode *nfsi = NFS_I(page->mapping->host);
+
+               BUG_ON(nfsi->fscache == NULL);
+
+               dfprintk(FSCACHE, "NFS: fscache releasepage (0x%p/0x%p/0x%p)\n",
+                        nfsi->fscache, page, nfsi);
+
+               wait_on_page_fs_misc(page);
+               fscache_uncache_page(nfsi->fscache, page);
+               atomic_inc(&nfs_fscache_uncache_page);
+               ClearPageNfsCached(page);
+       }
+}
+
+/*
+ * release the caching state associated with a page if undergoing complete page
+ * invalidation
+ */
+static inline void nfs_fscache_invalidate_page(struct page *page,
+                                              struct inode *inode,
+                                              unsigned long offset)
+{
+       if (PageNfsCached(page)) {
+               struct nfs_inode *nfsi = NFS_I(page->mapping->host);
+
+               BUG_ON(!nfsi->fscache);
+
+               dfprintk(FSCACHE,
+                        "NFS: fscache invalidatepage (0x%p/0x%p/0x%p)\n",
+                        nfsi->fscache, page, nfsi);
+
+               if (offset == 0) {
+                       BUG_ON(!PageLocked(page));
+                       if (!PageWriteback(page))
+                               nfs_fscache_release_page(page);
+               }
+       }
+}
+
+/*
+ * store a newly fetched page in fscache
+ */
+extern void nfs_readpage_to_fscache_complete(struct page *, void *, int);
+
+static inline void nfs_readpage_to_fscache(struct inode *inode,
+                                          struct page *page,
+                                          int sync)
+{
+       int ret;
+
+       if (PageNfsCached(page)) {
+               dfprintk(FSCACHE,
+                        "NFS: "
+                        "readpage_to_fscache(fsc:%p/p:%p(i:%lx f:%lx)/%d)\n",
+                        NFS_I(inode)->fscache, page, page->index, page->flags,
+                        sync);
+
+               if (TestSetPageFsMisc(page))
+                       BUG();
+
+               ret = fscache_write_page(NFS_I(inode)->fscache, page,
+                                        nfs_readpage_to_fscache_complete,
+                                        NULL, GFP_KERNEL);
+               dfprintk(FSCACHE,
+                        "NFS:     "
+                        "readpage_to_fscache: p:%p(i:%lu f:%lx) ret %d\n",
+                        page, page->index, page->flags, ret);
+
+               if (ret != 0) {
+                       fscache_uncache_page(NFS_I(inode)->fscache, page);
+                       atomic_inc(&nfs_fscache_uncache_page);
+                       ClearPageNfsCached(page);
+                       end_page_fs_misc(page);
+                       nfs_fscache_to_error = ret;
+               } else {
+                       atomic_inc(&nfs_fscache_to_pages);
+               }
+       }
+}
+
+/*
+ * retrieve a page from fscache
+ */
+extern void nfs_readpage_from_fscache_complete(struct page *, void *, int);
+
+static inline
+int nfs_readpage_from_fscache(struct nfs_open_context *ctx,
+                             struct inode *inode,
+                             struct page *page)
+{
+       int ret;
+
+       if (!NFS_I(inode)->fscache)
+               return 1;
+
+       dfprintk(FSCACHE,
+                "NFS: readpage_from_fscache(fsc:%p/p:%p(i:%lx f:%lx)/0x%p)\n",
+                NFS_I(inode)->fscache, page, page->index, page->flags, inode);
+
+       ret = fscache_read_or_alloc_page(NFS_I(inode)->fscache,
+                                        page,
+                                        nfs_readpage_from_fscache_complete,
+                                        ctx,
+                                        GFP_KERNEL);
+
+       switch (ret) {
+       case 0: /* read BIO submitted (page in fscache) */
+               dfprintk(FSCACHE,
+                        "NFS:    readpage_from_fscache: BIO submitted\n");
+               atomic_inc(&nfs_fscache_from_pages);
+               return ret;
+
+       case -ENOBUFS: /* inode not in cache */
+       case -ENODATA: /* page not in cache */
+               dfprintk(FSCACHE,
+                        "NFS:    readpage_from_fscache error %d\n", ret);
+               return 1;
+
+       default:
+               dfprintk(FSCACHE, "NFS:    readpage_from_fscache %d\n", ret);
+               nfs_fscache_from_error = ret;
+       }
+       return ret;
+}
+
+/*
+ * retrieve a set of pages from fscache
+ */
+static inline int nfs_readpages_from_fscache(struct nfs_open_context *ctx,
+                                            struct inode *inode,
+                                            struct address_space *mapping,
+                                            struct list_head *pages,
+                                            unsigned *nr_pages)
+{
+       int ret, npages = *nr_pages;
+
+       if (!NFS_I(inode)->fscache)
+               return 1;
+
+       dfprintk(FSCACHE,
+                "NFS: nfs_getpages_from_fscache (0x%p/%u/0x%p)\n",
+                NFS_I(inode)->fscache, *nr_pages, inode);
+
+       ret = fscache_read_or_alloc_pages(NFS_I(inode)->fscache,
+                                         mapping, pages, nr_pages,
+                                         nfs_readpage_from_fscache_complete,
+                                         ctx,
+                                         mapping_gfp_mask(mapping));
+
+
+       switch (ret) {
+       case 0: /* read BIO submitted (page in fscache) */
+               BUG_ON(!list_empty(pages));
+               BUG_ON(*nr_pages != 0);
+               dfprintk(FSCACHE,
+                        "NFS: nfs_getpages_from_fscache: BIO submitted\n");
+
+               atomic_add(npages, &nfs_fscache_from_pages);
+               return ret;
+
+       case -ENOBUFS: /* inode not in cache */
+       case -ENODATA: /* page not in cache */
+               dfprintk(FSCACHE,
+                        "NFS: nfs_getpages_from_fscache: no page: %d\n", ret);
+               return 1;
+
+       default:
+               dfprintk(FSCACHE,
+                        "NFS: nfs_getpages_from_fscache: ret  %d\n", ret);
+               nfs_fscache_from_error = ret;
+       }
+
+       return ret;
+}
+
+/*
+ * store an updated page in fscache
+ */
+extern void nfs_writepage_to_fscache_complete(struct page *page, void *data, int error);
+
+static inline void nfs_writepage_to_fscache(struct inode *inode,
+                                           struct page *page)
+{
+       int error;
+
+       if (PageNfsCached(page) && NFS_I(inode)->fscache) {
+               dfprintk(FSCACHE,
+                        "NFS: writepage_to_fscache (0x%p/0x%p/0x%p)\n",
+                        NFS_I(inode)->fscache, page, inode);
+
+               error = fscache_write_page(NFS_I(inode)->fscache, page,
+                                          nfs_writepage_to_fscache_complete,
+                                          NULL, GFP_KERNEL);
+               if (error != 0) {
+                       dfprintk(FSCACHE,
+                                "NFS:    fscache_write_page error %d\n",
+                                error);
+                       fscache_uncache_page(NFS_I(inode)->fscache, page);
+               }
+       }
+}
+
+#else /* CONFIG_NFS_FSCACHE */
+static inline int nfs_fscache_register(void) { return 0; }
+static inline void nfs_fscache_unregister(void) {}
+static inline void nfs_fscache_get_client_cookie(struct nfs_client *clp) {}
+static inline void nfs4_fscache_get_client_cookie(struct nfs_client *clp) {}
+static inline void nfs_fscache_release_client_cookie(struct nfs_client *clp) {}
+static inline const char *nfs_server_fscache_state(struct nfs_server *server) { return "no "; }
+
+static inline void nfs_fscache_get_fh_cookie(struct super_block *sb,
+                                            struct nfs_inode *nfsi,
+                                            int maycache)
+{
+}
+static inline void nfs_fscache_set_size(struct nfs_server *server,
+                                       struct nfs_inode *nfsi,
+                                       loff_t i_size)
+{
+}
+static inline void nfs_fscache_release_fh_cookie(struct nfs_server *server,
+                                                struct nfs_inode *nfsi)
+{
+}
+static inline void nfs_fscache_zap_fh_cookie(struct nfs_server *server, struct nfs_inode *nfsi) {}
+static inline void nfs_fscache_renew_fh_cookie(struct nfs_server *server, struct nfs_inode *nfsi) {}
+static inline void nfs_fscache_disable_fh_cookie(struct inode *inode) {}
+static inline void nfs_fscache_install_vm_ops(struct inode *inode, struct vm_area_struct *vma) {}
+static inline void nfs_fscache_release_page(struct page *page) {}
+static inline void nfs_fscache_invalidate_page(struct page *page,
+                                              struct inode *inode,
+                                              unsigned long offset)
+{
+}
+static inline void nfs_readpage_to_fscache(struct inode *inode, struct page *page, int sync) {}
+static inline int nfs_readpage_from_fscache(struct nfs_open_context *ctx,
+                                           struct inode *inode, struct page *page)
+{
+       return -ENOBUFS;
+}
+static inline int nfs_readpages_from_fscache(struct nfs_open_context *ctx,
+                                            struct inode *inode,
+                                            struct address_space *mapping,
+                                            struct list_head *pages,
+                                            unsigned *nr_pages)
+{
+       return -ENOBUFS;
+}
+
+static inline void nfs_writepage_to_fscache(struct inode *inode, struct page *page)
+{
+       BUG_ON(PageNfsCached(page));
+}
+
+#endif /* CONFIG_NFS_FSCACHE */
+#endif /* _NFS_FSCACHE_H */