This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / fs / fscache / page.c
diff --git a/fs/fscache/page.c b/fs/fscache/page.c
new file mode 100644 (file)
index 0000000..fbb7716
--- /dev/null
@@ -0,0 +1,537 @@
+/* page.c: general filesystem cache cookie management
+ *
+ * Copyright (C) 2004-5 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.
+ */
+
+#include <linux/module.h>
+#include <linux/fscache-cache.h>
+#include <linux/buffer_head.h>
+#include <linux/pagevec.h>
+#include "fscache-int.h"
+
+/*****************************************************************************/
+/*
+ * set the data file size on an object in the cache
+ */
+int __fscache_set_i_size(struct fscache_cookie *cookie, loff_t i_size)
+{
+       struct fscache_object *object;
+       int ret;
+
+       _enter("%p,%llu,", cookie, i_size);
+
+       if (hlist_empty(&cookie->backing_objects)) {
+               _leave(" = -ENOBUFS");
+               return -ENOBUFS;
+       }
+
+       /* not supposed to use this for indexes */
+       BUG_ON(cookie->def->type == FSCACHE_COOKIE_TYPE_INDEX);
+
+       /* prevent the file from being uncached whilst we access it and exclude
+        * read and write attempts on pages
+        */
+       down_write(&cookie->sem);
+
+       ret = -ENOBUFS;
+       if (!hlist_empty(&cookie->backing_objects)) {
+               /* get and pin the backing object */
+               object = hlist_entry(cookie->backing_objects.first,
+                                    struct fscache_object, cookie_link);
+
+               if (test_bit(FSCACHE_IOERROR, &object->cache->flags))
+                       goto out;
+
+               /* prevent the cache from being withdrawn */
+               if (object->cache->ops->set_i_size &&
+                   fscache_operation_lock(object)
+                   ) {
+                       if (object->cache->ops->grab_object(object)) {
+                               /* ask the cache to honour the operation */
+                               ret = object->cache->ops->set_i_size(object,
+                                                                    i_size);
+
+                               object->cache->ops->put_object(object);
+                       }
+
+                       fscache_operation_unlock(object);
+               }
+       }
+
+out:
+       up_write(&cookie->sem);
+       _leave(" = %d", ret);
+       return ret;
+}
+
+EXPORT_SYMBOL(__fscache_set_i_size);
+
+/*****************************************************************************/
+/*
+ * reserve space for an object
+ */
+int __fscache_reserve_space(struct fscache_cookie *cookie, loff_t size)
+{
+       struct fscache_object *object;
+       int ret;
+
+       _enter("%p,%llu,", cookie, size);
+
+       if (hlist_empty(&cookie->backing_objects)) {
+               _leave(" = -ENOBUFS");
+               return -ENOBUFS;
+       }
+
+       /* not supposed to use this for indexes */
+       BUG_ON(cookie->def->type == FSCACHE_COOKIE_TYPE_INDEX);
+
+       /* prevent the file from being uncached whilst we access it and exclude
+        * read and write attempts on pages
+        */
+       down_write(&cookie->sem);
+
+       ret = -ENOBUFS;
+       if (!hlist_empty(&cookie->backing_objects)) {
+               /* get and pin the backing object */
+               object = hlist_entry(cookie->backing_objects.first,
+                                    struct fscache_object, cookie_link);
+
+               if (test_bit(FSCACHE_IOERROR, &object->cache->flags))
+                       goto out;
+
+               if (!object->cache->ops->reserve_space) {
+                       ret = -EOPNOTSUPP;
+                       goto out;
+               }
+
+               /* prevent the cache from being withdrawn */
+               if (fscache_operation_lock(object)) {
+                       if (object->cache->ops->grab_object(object)) {
+                               /* ask the cache to honour the operation */
+                               ret = object->cache->ops->reserve_space(object,
+                                                                       size);
+
+                               object->cache->ops->put_object(object);
+                       }
+
+                       fscache_operation_unlock(object);
+               }
+       }
+
+out:
+       up_write(&cookie->sem);
+       _leave(" = %d", ret);
+       return ret;
+}
+
+EXPORT_SYMBOL(__fscache_reserve_space);
+
+/*****************************************************************************/
+/*
+ * read a page from the cache or allocate a block in which to store it
+ * - we return:
+ *   -ENOMEM   - out of memory, nothing done
+ *   -EINTR    - interrupted
+ *   -ENOBUFS  - no backing object available in which to cache the block
+ *   -ENODATA  - no data available in the backing object 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 *context,
+                                gfp_t gfp)
+{
+       struct fscache_object *object;
+       int ret;
+
+       _enter("%p,{%lu},", cookie, page->index);
+
+       if (hlist_empty(&cookie->backing_objects)) {
+               _leave(" -ENOBUFS [no backing objects]");
+               return -ENOBUFS;
+       }
+
+       /* not supposed to use this for indexes */
+       BUG_ON(cookie->def->type == FSCACHE_COOKIE_TYPE_INDEX);
+
+       /* prevent the file from being uncached whilst we access it */
+       down_read(&cookie->sem);
+
+       ret = -ENOBUFS;
+       if (!hlist_empty(&cookie->backing_objects)) {
+               /* get and pin the backing object */
+               object = hlist_entry(cookie->backing_objects.first,
+                                    struct fscache_object, cookie_link);
+
+               if (test_bit(FSCACHE_IOERROR, &object->cache->flags))
+                       goto out;
+
+               /* prevent the cache from being withdrawn */
+               if (fscache_operation_lock(object)) {
+                       if (object->cache->ops->grab_object(object)) {
+                               /* ask the cache to honour the operation */
+                               ret = object->cache->ops->read_or_alloc_page(
+                                       object,
+                                       page,
+                                       end_io_func,
+                                       context,
+                                       gfp);
+
+                               object->cache->ops->put_object(object);
+                       }
+
+                       fscache_operation_unlock(object);
+               }
+       }
+
+out:
+       up_read(&cookie->sem);
+       _leave(" = %d", ret);
+       return ret;
+}
+
+EXPORT_SYMBOL(__fscache_read_or_alloc_page);
+
+/*****************************************************************************/
+/*
+ * read a list of page from the cache or allocate a block in which to store
+ * them
+ * - we return:
+ *   -ENOMEM   - out of memory, some pages may be being read
+ *   -EINTR    - interrupted, some pages may be being read
+ *   -ENOBUFS  - no backing object or space available in which to cache any
+ *                pages not being read
+ *   -ENODATA  - no data available in the backing object for some or all of
+ *                the pages
+ *   0         - dispatched a read on all pages
+ *
+ * end_io_func() will be called for each page read from the cache as it is
+ * finishes being read
+ *
+ * any pages for which a read is dispatched will be removed from pages and
+ * nr_pages
+ */
+int __fscache_read_or_alloc_pages(struct fscache_cookie *cookie,
+                                 struct address_space *mapping,
+                                 struct list_head *pages,
+                                 unsigned *nr_pages,
+                                 fscache_rw_complete_t end_io_func,
+                                 void *context,
+                                 gfp_t gfp)
+{
+       struct fscache_object *object;
+       int ret;
+
+       _enter("%p,,%d,,,", cookie, *nr_pages);
+
+       if (hlist_empty(&cookie->backing_objects)) {
+               _leave(" -ENOBUFS [no backing objects]");
+               return -ENOBUFS;
+       }
+
+       /* not supposed to use this for indexes */
+       BUG_ON(cookie->def->type == FSCACHE_COOKIE_TYPE_INDEX);
+       BUG_ON(list_empty(pages));
+       BUG_ON(*nr_pages <= 0);
+
+       /* prevent the file from being uncached whilst we access it */
+       down_read(&cookie->sem);
+
+       ret = -ENOBUFS;
+       if (!hlist_empty(&cookie->backing_objects)) {
+               /* get and pin the backing object */
+               object = hlist_entry(cookie->backing_objects.first,
+                                    struct fscache_object, cookie_link);
+
+               if (test_bit(FSCACHE_IOERROR, &object->cache->flags))
+                       goto out;
+
+               /* prevent the cache from being withdrawn */
+               if (fscache_operation_lock(object)) {
+                       if (object->cache->ops->grab_object(object)) {
+                               /* ask the cache to honour the operation */
+                               ret = object->cache->ops->read_or_alloc_pages(
+                                       object,
+                                       mapping,
+                                       pages,
+                                       nr_pages,
+                                       end_io_func,
+                                       context,
+                                       gfp);
+
+                               object->cache->ops->put_object(object);
+                       }
+
+                       fscache_operation_unlock(object);
+               }
+       }
+
+out:
+       up_read(&cookie->sem);
+       _leave(" = %d", ret);
+       return ret;
+}
+
+EXPORT_SYMBOL(__fscache_read_or_alloc_pages);
+
+/*****************************************************************************/
+/*
+ * allocate a block in the cache on which to store a page
+ * - we return:
+ *   -ENOMEM   - out of memory, nothing done
+ *   -EINTR    - interrupted
+ *   -ENOBUFS  - no backing object available in which to cache the block
+ *   0         - block allocated
+ */
+int __fscache_alloc_page(struct fscache_cookie *cookie,
+                        struct page *page,
+                        gfp_t gfp)
+{
+       struct fscache_object *object;
+       int ret;
+
+       _enter("%p,{%lu},", cookie, page->index);
+
+       if (hlist_empty(&cookie->backing_objects)) {
+               _leave(" -ENOBUFS [no backing objects]");
+               return -ENOBUFS;
+       }
+
+       /* not supposed to use this for indexes */
+       BUG_ON(cookie->def->type == FSCACHE_COOKIE_TYPE_INDEX);
+
+       /* prevent the file from being uncached whilst we access it */
+       down_read(&cookie->sem);
+
+       ret = -ENOBUFS;
+       if (!hlist_empty(&cookie->backing_objects)) {
+               /* get and pin the backing object */
+               object = hlist_entry(cookie->backing_objects.first,
+                                    struct fscache_object, cookie_link);
+
+               if (test_bit(FSCACHE_IOERROR, &object->cache->flags))
+                       goto out;
+
+               /* prevent the cache from being withdrawn */
+               if (fscache_operation_lock(object)) {
+                       if (object->cache->ops->grab_object(object)) {
+                               /* ask the cache to honour the operation */
+                               ret = object->cache->ops->allocate_page(object,
+                                                                       page,
+                                                                       gfp);
+
+                               object->cache->ops->put_object(object);
+                       }
+
+                       fscache_operation_unlock(object);
+               }
+       }
+
+out:
+       up_read(&cookie->sem);
+       _leave(" = %d", ret);
+       return ret;
+}
+
+EXPORT_SYMBOL(__fscache_alloc_page);
+
+/*****************************************************************************/
+/*
+ * request a page be stored in the cache
+ * - returns:
+ *   -ENOMEM   - out of memory, nothing done
+ *   -EINTR    - interrupted
+ *   -ENOBUFS  - no backing object 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 *context,
+                        gfp_t gfp)
+{
+       struct fscache_object *object;
+       int ret;
+
+       _enter("%p,{%lu},", cookie, page->index);
+
+       /* not supposed to use this for indexes */
+       BUG_ON(cookie->def->type == FSCACHE_COOKIE_TYPE_INDEX);
+
+       /* prevent the file from been uncached whilst we deal with it */
+       down_read(&cookie->sem);
+
+       ret = -ENOBUFS;
+       if (!hlist_empty(&cookie->backing_objects)) {
+               object = hlist_entry(cookie->backing_objects.first,
+                                    struct fscache_object, cookie_link);
+
+               if (test_bit(FSCACHE_IOERROR, &object->cache->flags))
+                       goto out;
+
+               /* prevent the cache from being withdrawn */
+               if (fscache_operation_lock(object)) {
+                       /* ask the cache to honour the operation */
+                       ret = object->cache->ops->write_page(object,
+                                                            page,
+                                                            end_io_func,
+                                                            context,
+                                                            gfp);
+                       fscache_operation_unlock(object);
+               }
+       }
+
+out:
+       up_read(&cookie->sem);
+       _leave(" = %d", ret);
+       return ret;
+}
+
+EXPORT_SYMBOL(__fscache_write_page);
+
+/*****************************************************************************/
+/*
+ * request several pages be stored in the cache
+ * - returns:
+ *   -ENOMEM   - out of memory, nothing done
+ *   -EINTR    - interrupted
+ *   -ENOBUFS  - no backing object available in which to cache the page
+ *   0         - dispatched a write - it'll call end_io_func() when finished
+ */
+int __fscache_write_pages(struct fscache_cookie *cookie,
+                         struct pagevec *pagevec,
+                         fscache_rw_complete_t end_io_func,
+                         void *context,
+                         gfp_t gfp)
+{
+       struct fscache_object *object;
+       int ret;
+
+       _enter("%p,{%ld},", cookie, pagevec->nr);
+
+       /* not supposed to use this for indexes */
+       BUG_ON(cookie->def->type == FSCACHE_COOKIE_TYPE_INDEX);
+
+       /* prevent the file from been uncached whilst we deal with it */
+       down_read(&cookie->sem);
+
+       ret = -ENOBUFS;
+       if (!hlist_empty(&cookie->backing_objects)) {
+               object = hlist_entry(cookie->backing_objects.first,
+                                    struct fscache_object, cookie_link);
+
+               if (test_bit(FSCACHE_IOERROR, &object->cache->flags))
+                       goto out;
+
+               /* prevent the cache from being withdrawn */
+               if (fscache_operation_lock(object)) {
+                       /* ask the cache to honour the operation */
+                       ret = object->cache->ops->write_pages(object,
+                                                             pagevec,
+                                                             end_io_func,
+                                                             context,
+                                                             gfp);
+                       fscache_operation_unlock(object);
+               }
+       }
+
+out:
+       up_read(&cookie->sem);
+       _leave(" = %d", ret);
+       return ret;
+}
+
+EXPORT_SYMBOL(__fscache_write_pages);
+
+/*****************************************************************************/
+/*
+ * remove a page from the cache
+ */
+void __fscache_uncache_page(struct fscache_cookie *cookie, struct page *page)
+{
+       struct fscache_object *object;
+       struct pagevec pagevec;
+
+       _enter(",{%lu}", page->index);
+
+       /* not supposed to use this for indexes */
+       BUG_ON(cookie->def->type == FSCACHE_COOKIE_TYPE_INDEX);
+
+       if (hlist_empty(&cookie->backing_objects)) {
+               _leave(" [no backing]");
+               return;
+       }
+
+       pagevec_init(&pagevec, 0);
+       pagevec_add(&pagevec, page);
+
+       /* ask the cache to honour the operation */
+       down_read(&cookie->sem);
+
+       if (!hlist_empty(&cookie->backing_objects)) {
+               object = hlist_entry(cookie->backing_objects.first,
+                                    struct fscache_object, cookie_link);
+
+               /* prevent the cache from being withdrawn */
+               if (fscache_operation_lock(object)) {
+                       object->cache->ops->uncache_pages(object, &pagevec);
+                       fscache_operation_unlock(object);
+               }
+       }
+
+       up_read(&cookie->sem);
+
+       _leave("");
+}
+
+EXPORT_SYMBOL(__fscache_uncache_page);
+
+/*****************************************************************************/
+/*
+ * remove a bunch of pages from the cache
+ */
+void __fscache_uncache_pages(struct fscache_cookie *cookie,
+                            struct pagevec *pagevec)
+{
+       struct fscache_object *object;
+
+       _enter(",{%ld}", pagevec->nr);
+
+       BUG_ON(pagevec->nr <= 0);
+       BUG_ON(!pagevec->pages[0]);
+
+       /* not supposed to use this for indexes */
+       BUG_ON(cookie->def->type == FSCACHE_COOKIE_TYPE_INDEX);
+
+       if (hlist_empty(&cookie->backing_objects)) {
+               _leave(" [no backing]");
+               return;
+       }
+
+       /* ask the cache to honour the operation */
+       down_read(&cookie->sem);
+
+       if (!hlist_empty(&cookie->backing_objects)) {
+               object = hlist_entry(cookie->backing_objects.first,
+                                    struct fscache_object, cookie_link);
+
+               /* prevent the cache from being withdrawn */
+               if (fscache_operation_lock(object)) {
+                       object->cache->ops->uncache_pages(object, pagevec);
+                       fscache_operation_unlock(object);
+               }
+       }
+
+       up_read(&cookie->sem);
+
+       _leave("");
+}
+
+EXPORT_SYMBOL(__fscache_uncache_pages);