FS-Cache: Fix lock misorder in fscache_write_op()
[safe/jmp/linux-2.6] / fs / fscache / page.c
index e6f2e61..3ea8897 100644 (file)
@@ -45,16 +45,26 @@ EXPORT_SYMBOL(__fscache_wait_on_page_write);
 /*
  * note that a page has finished being written to the cache
  */
-static void fscache_end_page_write(struct fscache_cookie *cookie, struct page *page)
+static void fscache_end_page_write(struct fscache_object *object,
+                                  struct page *page)
 {
-       struct page *xpage;
+       struct fscache_cookie *cookie;
+       struct page *xpage = NULL;
 
-       spin_lock(&cookie->lock);
-       xpage = radix_tree_delete(&cookie->stores, page->index);
-       spin_unlock(&cookie->lock);
-       ASSERT(xpage != NULL);
-
-       wake_up_bit(&cookie->flags, 0);
+       spin_lock(&object->lock);
+       cookie = object->cookie;
+       if (cookie) {
+               /* delete the page from the tree if it is now no longer
+                * pending */
+               spin_lock(&cookie->stores_lock);
+               fscache_stat(&fscache_n_store_radix_deletes);
+               xpage = radix_tree_delete(&cookie->stores, page->index);
+               spin_unlock(&cookie->stores_lock);
+               wake_up_bit(&cookie->flags, 0);
+       }
+       spin_unlock(&object->lock);
+       if (xpage)
+               page_cache_release(xpage);
 }
 
 /*
@@ -591,7 +601,7 @@ static void fscache_write_op(struct fscache_operation *_op)
        struct fscache_storage *op =
                container_of(_op, struct fscache_storage, op);
        struct fscache_object *object = op->op.object;
-       struct fscache_cookie *cookie = object->cookie;
+       struct fscache_cookie *cookie;
        struct page *page;
        unsigned n;
        void *results[1];
@@ -601,16 +611,17 @@ static void fscache_write_op(struct fscache_operation *_op)
 
        fscache_set_op_state(&op->op, "GetPage");
 
-       spin_lock(&cookie->lock);
        spin_lock(&object->lock);
+       cookie = object->cookie;
 
-       if (!fscache_object_is_active(object)) {
+       if (!fscache_object_is_active(object) || !cookie) {
                spin_unlock(&object->lock);
-               spin_unlock(&cookie->lock);
                _leave("");
                return;
        }
 
+       spin_lock(&cookie->stores_lock);
+
        fscache_stat(&fscache_n_store_calls);
 
        /* find a page to store */
@@ -621,23 +632,25 @@ static void fscache_write_op(struct fscache_operation *_op)
                goto superseded;
        page = results[0];
        _debug("gang %d [%lx]", n, page->index);
-       if (page->index > op->store_limit)
+       if (page->index > op->store_limit) {
+               fscache_stat(&fscache_n_store_pages_over_limit);
                goto superseded;
+       }
 
        radix_tree_tag_clear(&cookie->stores, page->index,
                             FSCACHE_COOKIE_PENDING_TAG);
 
+       spin_unlock(&cookie->stores_lock);
        spin_unlock(&object->lock);
-       spin_unlock(&cookie->lock);
 
        if (page) {
                fscache_set_op_state(&op->op, "Store");
+               fscache_stat(&fscache_n_store_pages);
                fscache_stat(&fscache_n_cop_write_page);
                ret = object->cache->ops->write_page(op, page);
                fscache_stat_d(&fscache_n_cop_write_page);
                fscache_set_op_state(&op->op, "EndWrite");
-               fscache_end_page_write(cookie, page);
-               page_cache_release(page);
+               fscache_end_page_write(object, page);
                if (ret < 0) {
                        fscache_set_op_state(&op->op, "Abort");
                        fscache_abort_object(object);
@@ -653,9 +666,9 @@ superseded:
        /* this writer is going away and there aren't any more things to
         * write */
        _debug("cease");
+       spin_unlock(&cookie->stores_lock);
        clear_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags);
        spin_unlock(&object->lock);
-       spin_unlock(&cookie->lock);
        _leave("");
 }
 
@@ -731,6 +744,7 @@ int __fscache_write_page(struct fscache_cookie *cookie,
        /* add the page to the pending-storage radix tree on the backing
         * object */
        spin_lock(&object->lock);
+       spin_lock(&cookie->stores_lock);
 
        _debug("store limit %llx", (unsigned long long) object->store_limit);
 
@@ -751,6 +765,7 @@ int __fscache_write_page(struct fscache_cookie *cookie,
        if (test_and_set_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags))
                goto already_pending;
 
+       spin_unlock(&cookie->stores_lock);
        spin_unlock(&object->lock);
 
        op->op.debug_id = atomic_inc_return(&fscache_op_debug_id);
@@ -772,6 +787,7 @@ int __fscache_write_page(struct fscache_cookie *cookie,
 already_queued:
        fscache_stat(&fscache_n_stores_again);
 already_pending:
+       spin_unlock(&cookie->stores_lock);
        spin_unlock(&object->lock);
        spin_unlock(&cookie->lock);
        radix_tree_preload_end();
@@ -781,7 +797,9 @@ already_pending:
        return 0;
 
 submit_failed:
+       spin_lock(&cookie->stores_lock);
        radix_tree_delete(&cookie->stores, page->index);
+       spin_unlock(&cookie->stores_lock);
        page_cache_release(page);
        ret = -ENOBUFS;
        goto nobufs;