Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / fs / afs / file.c
index 7bb7168..e8e3680 100644 (file)
 #include <linux/slab.h>
 #include <linux/fs.h>
 #include <linux/pagemap.h>
+#include <linux/pagevec.h>
 #include <linux/buffer_head.h>
 #include "volume.h"
 #include "vnode.h"
 #include <rxrpc/call.h>
 #include "internal.h"
 
+#define list_to_page(head) (list_entry((head)->prev, struct page, lru))
+
 #if 0
 static int afs_file_open(struct inode *inode, struct file *file);
 static int afs_file_release(struct inode *inode, struct file *file);
@@ -30,34 +33,74 @@ static int afs_file_release(struct inode *inode, struct file *file);
 static int afs_file_readpage(struct file *file, struct page *page);
 static void afs_file_invalidatepage(struct page *page, unsigned long offset);
 static int afs_file_releasepage(struct page *page, gfp_t gfp_flags);
+static int afs_file_mmap(struct file * file, struct vm_area_struct * vma);
+
+#ifdef CONFIG_AFS_FSCACHE
+static int afs_file_readpages(struct file *filp, struct address_space *mapping,
+                             struct list_head *pages, unsigned nr_pages);
+static int afs_file_page_mkwrite(struct vm_area_struct *vma, struct page *page);
+#endif
 
 struct inode_operations afs_file_inode_operations = {
        .getattr        = afs_inode_getattr,
 };
 
-struct address_space_operations afs_fs_aops = {
+const struct file_operations afs_file_file_operations = {
+       .llseek         = generic_file_llseek,
+       .read           = generic_file_read,
+       .mmap           = afs_file_mmap,
+       .sendfile       = generic_file_sendfile,
+};
+
+const struct address_space_operations afs_fs_aops = {
        .readpage       = afs_file_readpage,
+#ifdef CONFIG_AFS_FSCACHE
+       .readpages      = afs_file_readpages,
+#endif
        .sync_page      = block_sync_page,
        .set_page_dirty = __set_page_dirty_nobuffers,
        .releasepage    = afs_file_releasepage,
        .invalidatepage = afs_file_invalidatepage,
 };
 
+static struct vm_operations_struct afs_fs_vm_operations = {
+       .nopage         = filemap_nopage,
+       .populate       = filemap_populate,
+#ifdef CONFIG_AFS_FSCACHE
+       .page_mkwrite   = afs_file_page_mkwrite,
+#endif
+};
+
+/*****************************************************************************/
+/*
+ * set up a memory mapping on an AFS file
+ * - we set our own VMA ops so that we can catch the page becoming writable for
+ *   userspace for shared-writable mmap
+ */
+static int afs_file_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       _enter("");
+
+       file_accessed(file);
+       vma->vm_ops = &afs_fs_vm_operations;
+       return 0;
+
+} /* end afs_file_mmap() */
+
 /*****************************************************************************/
 /*
  * deal with notification that a page was read from the cache
  */
-#ifdef AFS_CACHING_SUPPORT
-static void afs_file_readpage_read_complete(void *cookie_data,
-                                           struct page *page,
+#ifdef CONFIG_AFS_FSCACHE
+static void afs_file_readpage_read_complete(struct page *page,
                                            void *data,
                                            int error)
 {
-       _enter("%p,%p,%p,%d", cookie_data, page, data, error);
+       _enter("%p,%p,%d", page, data, error);
 
-       if (error)
-               SetPageError(page);
-       else
+       /* if the read completes with an error, we just unlock the page and let
+        * the VM reissue the readpage */
+       if (!error)
                SetPageUptodate(page);
        unlock_page(page);
 
@@ -68,15 +111,16 @@ static void afs_file_readpage_read_complete(void *cookie_data,
 /*
  * deal with notification that a page was written to the cache
  */
-#ifdef AFS_CACHING_SUPPORT
-static void afs_file_readpage_write_complete(void *cookie_data,
-                                            struct page *page,
+#ifdef CONFIG_AFS_FSCACHE
+static void afs_file_readpage_write_complete(struct page *page,
                                             void *data,
                                             int error)
 {
-       _enter("%p,%p,%p,%d", cookie_data, page, data, error);
+       _enter("%p,%p,%d", page, data, error);
 
-       unlock_page(page);
+       /* note that the page has been written to the cache and can now be
+        * modified */
+       end_page_fs_misc(page);
 
 } /* end afs_file_readpage_write_complete() */
 #endif
@@ -88,16 +132,13 @@ static void afs_file_readpage_write_complete(void *cookie_data,
 static int afs_file_readpage(struct file *file, struct page *page)
 {
        struct afs_rxfs_fetch_descriptor desc;
-#ifdef AFS_CACHING_SUPPORT
-       struct cachefs_page *pageio;
-#endif
        struct afs_vnode *vnode;
        struct inode *inode;
        int ret;
 
        inode = page->mapping->host;
 
-       _enter("{%lu},{%lu}", inode->i_ino, page->index);
+       _enter("{%lu},%p{%lu}", inode->i_ino, page, page->index);
 
        vnode = AFS_FS_I(inode);
 
@@ -107,13 +148,9 @@ static int afs_file_readpage(struct file *file, struct page *page)
        if (vnode->flags & AFS_VNODE_DELETED)
                goto error;
 
-#ifdef AFS_CACHING_SUPPORT
-       ret = cachefs_page_get_private(page, &pageio, GFP_NOIO);
-       if (ret < 0)
-               goto error;
-
+#ifdef CONFIG_AFS_FSCACHE
        /* is it cached? */
-       ret = cachefs_read_or_alloc_page(vnode->cache,
+       ret = fscache_read_or_alloc_page(vnode->cache,
                                         page,
                                         afs_file_readpage_read_complete,
                                         NULL,
@@ -123,18 +160,20 @@ static int afs_file_readpage(struct file *file, struct page *page)
 #endif
 
        switch (ret) {
-               /* read BIO submitted and wb-journal entry found */
-       case 1:
-               BUG(); // TODO - handle wb-journal match
-
                /* read BIO submitted (page in cache) */
        case 0:
                break;
 
-               /* no page available in cache */
-       case -ENOBUFS:
+               /* page not yet cached */
        case -ENODATA:
+               _debug("cache said ENODATA");
+               goto go_on;
+
+               /* page will not be cached */
+       case -ENOBUFS:
+               _debug("cache said ENOBUFS");
        default:
+       go_on:
                desc.fid        = vnode->fid;
                desc.offset     = page->index << PAGE_CACHE_SHIFT;
                desc.size       = min((size_t) (inode->i_size - desc.offset),
@@ -148,34 +187,40 @@ static int afs_file_readpage(struct file *file, struct page *page)
                ret = afs_vnode_fetch_data(vnode, &desc);
                kunmap(page);
                if (ret < 0) {
-                       if (ret==-ENOENT) {
-                               _debug("got NOENT from server"
+                       if (ret == -ENOENT) {
+                               kdebug("got NOENT from server"
                                       " - marking file deleted and stale");
                                vnode->flags |= AFS_VNODE_DELETED;
                                ret = -ESTALE;
                        }
 
-#ifdef AFS_CACHING_SUPPORT
-                       cachefs_uncache_page(vnode->cache, page);
+#ifdef CONFIG_AFS_FSCACHE
+                       fscache_uncache_page(vnode->cache, page);
+                       ClearPagePrivate(page);
 #endif
                        goto error;
                }
 
                SetPageUptodate(page);
 
-#ifdef AFS_CACHING_SUPPORT
-               if (cachefs_write_page(vnode->cache,
-                                      page,
-                                      afs_file_readpage_write_complete,
-                                      NULL,
-                                      GFP_KERNEL) != 0
-                   ) {
-                       cachefs_uncache_page(vnode->cache, page);
-                       unlock_page(page);
+               /* send the page to the cache */
+#ifdef CONFIG_AFS_FSCACHE
+               if (PagePrivate(page)) {
+                       if (TestSetPageFsMisc(page))
+                               BUG();
+                       if (fscache_write_page(vnode->cache,
+                                              page,
+                                              afs_file_readpage_write_complete,
+                                              NULL,
+                                              GFP_KERNEL) != 0
+                           ) {
+                               fscache_uncache_page(vnode->cache, page);
+                               ClearPagePrivate(page);
+                               end_page_fs_misc(page);
+                       }
                }
-#else
-               unlock_page(page);
 #endif
+               unlock_page(page);
        }
 
        _leave(" = 0");
@@ -192,20 +237,63 @@ static int afs_file_readpage(struct file *file, struct page *page)
 
 /*****************************************************************************/
 /*
- * get a page cookie for the specified page
+ * read a set of pages
  */
-#ifdef AFS_CACHING_SUPPORT
-int afs_cache_get_page_cookie(struct page *page,
-                             struct cachefs_page **_page_cookie)
+#ifdef CONFIG_AFS_FSCACHE
+static int afs_file_readpages(struct file *filp, struct address_space *mapping,
+                             struct list_head *pages, unsigned nr_pages)
 {
-       int ret;
+       struct afs_vnode *vnode;
+#if 0
+       struct pagevec lru_pvec;
+       unsigned page_idx;
+#endif
+       int ret = 0;
 
-       _enter("");
-       ret = cachefs_page_get_private(page,_page_cookie, GFP_NOIO);
+       _enter(",{%lu},,%d", mapping->host->i_ino, nr_pages);
 
-       _leave(" = %d", ret);
+       vnode = AFS_FS_I(mapping->host);
+       if (vnode->flags & AFS_VNODE_DELETED) {
+               _leave(" = -ESTALE");
+               return -ESTALE;
+       }
+
+       /* attempt to read as many of the pages as possible */
+       ret = fscache_read_or_alloc_pages(vnode->cache,
+                                         mapping,
+                                         pages,
+                                         &nr_pages,
+                                         afs_file_readpage_read_complete,
+                                         NULL,
+                                         mapping_gfp_mask(mapping));
+
+       switch (ret) {
+               /* all pages are being read from the cache */
+       case 0:
+               BUG_ON(!list_empty(pages));
+               BUG_ON(nr_pages != 0);
+               _leave(" = 0 [reading all]");
+               return 0;
+
+               /* there were pages that couldn't be read from the cache */
+       case -ENODATA:
+       case -ENOBUFS:
+               break;
+
+               /* other error */
+       default:
+               _leave(" = %d", ret);
+               return ret;
+       }
+
+       /* load the missing pages from the network */
+       ret = read_cache_pages(mapping, pages,
+                              (void *) afs_file_readpage, NULL);
+
+       _leave(" = %d [netting]", ret);
        return ret;
-} /* end afs_cache_get_page_cookie() */
+
+} /* end afs_file_readpages() */
 #endif
 
 /*****************************************************************************/
@@ -214,35 +302,22 @@ int afs_cache_get_page_cookie(struct page *page,
  */
 static void afs_file_invalidatepage(struct page *page, unsigned long offset)
 {
-       int ret = 1;
-
        _enter("{%lu},%lu", page->index, offset);
 
        BUG_ON(!PageLocked(page));
 
        if (PagePrivate(page)) {
-#ifdef AFS_CACHING_SUPPORT
-               struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
-               cachefs_uncache_page(vnode->cache,page);
-#endif
-
                /* We release buffers only if the entire page is being
                 * invalidated.
                 * The get_block cached value has been unconditionally
                 * invalidated, so real IO is not possible anymore.
                 */
-               if (offset == 0) {
-                       BUG_ON(!PageLocked(page));
-
-                       ret = 0;
-                       if (!PageWriteback(page))
-                               ret = page->mapping->a_ops->releasepage(page,
-                                                                       0);
-                       /* possibly should BUG_ON(!ret); - neilb */
-               }
+               if (offset == 0 && !PageWriteback(page))
+                       page->mapping->a_ops->releasepage(page, 0);
        }
 
-       _leave(" = %d", ret);
+       _leave("");
+
 } /* end afs_file_invalidatepage() */
 
 /*****************************************************************************/
@@ -251,23 +326,30 @@ static void afs_file_invalidatepage(struct page *page, unsigned long offset)
  */
 static int afs_file_releasepage(struct page *page, gfp_t gfp_flags)
 {
-       struct cachefs_page *pageio;
-
        _enter("{%lu},%x", page->index, gfp_flags);
 
-       if (PagePrivate(page)) {
-#ifdef AFS_CACHING_SUPPORT
-               struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
-               cachefs_uncache_page(vnode->cache, page);
+#ifdef CONFIG_AFS_FSCACHE
+       wait_on_page_fs_misc(page);
+       fscache_uncache_page(AFS_FS_I(page->mapping->host)->cache, page);
+       ClearPagePrivate(page);
 #endif
 
-               pageio = (struct cachefs_page *) page_private(page);
-               set_page_private(page, 0);
-               ClearPagePrivate(page);
+       /* indicate that the page can be released */
+       _leave(" = 1");
+       return 1;
 
-               kfree(pageio);
-       }
+} /* end afs_file_releasepage() */
 
-       _leave(" = 0");
+/*****************************************************************************/
+/*
+ * wait for the disc cache to finish writing before permitting modification of
+ * our page in the page cache
+ */
+#ifdef CONFIG_AFS_FSCACHE
+static int afs_file_page_mkwrite(struct vm_area_struct *vma, struct page *page)
+{
+       wait_on_page_fs_misc(page);
        return 0;
-} /* end afs_file_releasepage() */
+
+} /* end afs_file_page_mkwrite() */
+#endif