Merge tag 'xfs-for-linus-4.5-2' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-block.git] / fs / nfs / pagelist.c
index 452a011ba0d8a33e9e4b9afd858f82858185f57e..8ce4f61cbaa5ffc5488fbe48fcf22b7f066f23b7 100644 (file)
@@ -101,53 +101,18 @@ nfs_page_free(struct nfs_page *p)
        kmem_cache_free(nfs_page_cachep, p);
 }
 
-static void
-nfs_iocounter_inc(struct nfs_io_counter *c)
-{
-       atomic_inc(&c->io_count);
-}
-
-static void
-nfs_iocounter_dec(struct nfs_io_counter *c)
-{
-       if (atomic_dec_and_test(&c->io_count)) {
-               clear_bit(NFS_IO_INPROGRESS, &c->flags);
-               smp_mb__after_atomic();
-               wake_up_bit(&c->flags, NFS_IO_INPROGRESS);
-       }
-}
-
-static int
-__nfs_iocounter_wait(struct nfs_io_counter *c)
-{
-       wait_queue_head_t *wq = bit_waitqueue(&c->flags, NFS_IO_INPROGRESS);
-       DEFINE_WAIT_BIT(q, &c->flags, NFS_IO_INPROGRESS);
-       int ret = 0;
-
-       do {
-               prepare_to_wait(wq, &q.wait, TASK_KILLABLE);
-               set_bit(NFS_IO_INPROGRESS, &c->flags);
-               if (atomic_read(&c->io_count) == 0)
-                       break;
-               ret = nfs_wait_bit_killable(&q.key, TASK_KILLABLE);
-       } while (atomic_read(&c->io_count) != 0 && !ret);
-       finish_wait(wq, &q.wait);
-       return ret;
-}
-
 /**
  * nfs_iocounter_wait - wait for i/o to complete
- * @c: nfs_io_counter to use
+ * @l_ctx: nfs_lock_context with io_counter to use
  *
  * returns -ERESTARTSYS if interrupted by a fatal signal.
  * Otherwise returns 0 once the io_count hits 0.
  */
 int
-nfs_iocounter_wait(struct nfs_io_counter *c)
+nfs_iocounter_wait(struct nfs_lock_context *l_ctx)
 {
-       if (atomic_read(&c->io_count) == 0)
-               return 0;
-       return __nfs_iocounter_wait(c);
+       return wait_on_atomic_t(&l_ctx->io_count, nfs_wait_atomic_killable,
+                       TASK_KILLABLE);
 }
 
 /*
@@ -370,7 +335,7 @@ nfs_create_request(struct nfs_open_context *ctx, struct page *page,
                return ERR_CAST(l_ctx);
        }
        req->wb_lock_context = l_ctx;
-       nfs_iocounter_inc(&l_ctx->io_count);
+       atomic_inc(&l_ctx->io_count);
 
        /* Initialize the request struct. Initially, we assume a
         * long write-back delay. This will be adjusted in
@@ -431,7 +396,8 @@ static void nfs_clear_request(struct nfs_page *req)
                req->wb_page = NULL;
        }
        if (l_ctx != NULL) {
-               nfs_iocounter_dec(&l_ctx->io_count);
+               if (atomic_dec_and_test(&l_ctx->io_count))
+                       wake_up_atomic_t(&l_ctx->io_count);
                nfs_put_lock_context(l_ctx);
                req->wb_lock_context = NULL;
        }
@@ -664,22 +630,11 @@ EXPORT_SYMBOL_GPL(nfs_initiate_pgio);
  * @desc: IO descriptor
  * @hdr: pageio header
  */
-static int nfs_pgio_error(struct nfs_pageio_descriptor *desc,
-                         struct nfs_pgio_header *hdr)
+static void nfs_pgio_error(struct nfs_pgio_header *hdr)
 {
-       struct nfs_pgio_mirror *mirror;
-       u32 midx;
-
        set_bit(NFS_IOHDR_REDO, &hdr->flags);
        nfs_pgio_data_destroy(hdr);
        hdr->completion_ops->completion(hdr);
-       /* TODO: Make sure it's right to clean up all mirrors here
-        *       and not just hdr->pgio_mirror_idx */
-       for (midx = 0; midx < desc->pg_mirror_count; midx++) {
-               mirror = &desc->pg_mirrors[midx];
-               desc->pg_completion_ops->error_cleanup(&mirror->pg_list);
-       }
-       return -ENOMEM;
 }
 
 /**
@@ -800,8 +755,11 @@ int nfs_generic_pgio(struct nfs_pageio_descriptor *desc,
        unsigned int pagecount, pageused;
 
        pagecount = nfs_page_array_len(mirror->pg_base, mirror->pg_count);
-       if (!nfs_pgarray_set(&hdr->page_array, pagecount))
-               return nfs_pgio_error(desc, hdr);
+       if (!nfs_pgarray_set(&hdr->page_array, pagecount)) {
+               nfs_pgio_error(hdr);
+               desc->pg_error = -ENOMEM;
+               return desc->pg_error;
+       }
 
        nfs_init_cinfo(&cinfo, desc->pg_inode, desc->pg_dreq);
        pages = hdr->page_array.pagevec;
@@ -819,8 +777,11 @@ int nfs_generic_pgio(struct nfs_pageio_descriptor *desc,
                        *pages++ = last_page = req->wb_page;
                }
        }
-       if (WARN_ON_ONCE(pageused != pagecount))
-               return nfs_pgio_error(desc, hdr);
+       if (WARN_ON_ONCE(pageused != pagecount)) {
+               nfs_pgio_error(hdr);
+               desc->pg_error = -EINVAL;
+               return desc->pg_error;
+       }
 
        if ((desc->pg_ioflags & FLUSH_COND_STABLE) &&
            (desc->pg_moreio || nfs_reqs_to_commit(&cinfo)))
@@ -835,18 +796,13 @@ EXPORT_SYMBOL_GPL(nfs_generic_pgio);
 
 static int nfs_generic_pg_pgios(struct nfs_pageio_descriptor *desc)
 {
-       struct nfs_pgio_mirror *mirror;
        struct nfs_pgio_header *hdr;
        int ret;
 
-       mirror = nfs_pgio_current_mirror(desc);
-
        hdr = nfs_pgio_header_alloc(desc->pg_rw_ops);
        if (!hdr) {
-               /* TODO: make sure this is right with mirroring - or
-                *       should it back out all mirrors? */
-               desc->pg_completion_ops->error_cleanup(&mirror->pg_list);
-               return -ENOMEM;
+               desc->pg_error = -ENOMEM;
+               return desc->pg_error;
        }
        nfs_pgheader_init(desc, hdr, nfs_pgio_header_free);
        ret = nfs_generic_pgio(desc, hdr);
@@ -874,6 +830,9 @@ static int nfs_pageio_setup_mirroring(struct nfs_pageio_descriptor *pgio,
 
        mirror_count = pgio->pg_ops->pg_get_mirror_count(pgio, req);
 
+       if (pgio->pg_error < 0)
+               return pgio->pg_error;
+
        if (!mirror_count || mirror_count > NFS_PAGEIO_DESCRIPTOR_MIRROR_MAX)
                return -EINVAL;
 
@@ -903,12 +862,6 @@ static void nfs_pageio_cleanup_mirroring(struct nfs_pageio_descriptor *pgio)
        pgio->pg_mirrors_dynamic = NULL;
 }
 
-static bool nfs_match_open_context(const struct nfs_open_context *ctx1,
-               const struct nfs_open_context *ctx2)
-{
-       return ctx1->cred == ctx2->cred && ctx1->state == ctx2->state;
-}
-
 static bool nfs_match_lock_context(const struct nfs_lock_context *l1,
                const struct nfs_lock_context *l2)
 {
@@ -982,6 +935,8 @@ static int nfs_pageio_do_add_request(struct nfs_pageio_descriptor *desc,
        } else {
                if (desc->pg_ops->pg_init)
                        desc->pg_ops->pg_init(desc, req);
+               if (desc->pg_error < 0)
+                       return 0;
                mirror->pg_base = req->wb_pgbase;
        }
        if (!nfs_can_coalesce_requests(prev, req, desc))
@@ -1147,6 +1102,8 @@ int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
        bytes = req->wb_bytes;
 
        nfs_pageio_setup_mirroring(desc, req);
+       if (desc->pg_error < 0)
+               goto out_failed;
 
        for (midx = 0; midx < desc->pg_mirror_count; midx++) {
                if (midx) {
@@ -1163,7 +1120,8 @@ int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
 
                        if (IS_ERR(dupreq)) {
                                nfs_page_group_unlock(req);
-                               return 0;
+                               desc->pg_error = PTR_ERR(dupreq);
+                               goto out_failed;
                        }
 
                        nfs_lock_request(dupreq);
@@ -1176,10 +1134,32 @@ int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
                if (nfs_pgio_has_mirroring(desc))
                        desc->pg_mirror_idx = midx;
                if (!nfs_pageio_add_request_mirror(desc, dupreq))
-                       return 0;
+                       goto out_failed;
        }
 
        return 1;
+
+out_failed:
+       /*
+        * We might have failed before sending any reqs over wire.
+        * Clean up rest of the reqs in mirror pg_list.
+        */
+       if (desc->pg_error) {
+               struct nfs_pgio_mirror *mirror;
+               void (*func)(struct list_head *);
+
+               /* remember fatal errors */
+               if (nfs_error_is_fatal(desc->pg_error))
+                       mapping_set_error(desc->pg_inode->i_mapping,
+                                         desc->pg_error);
+
+               func = desc->pg_completion_ops->error_cleanup;
+               for (midx = 0; midx < desc->pg_mirror_count; midx++) {
+                       mirror = &desc->pg_mirrors[midx];
+                       func(&mirror->pg_list);
+               }
+       }
+       return 0;
 }
 
 /*
@@ -1232,7 +1212,7 @@ int nfs_pageio_resend(struct nfs_pageio_descriptor *desc,
        nfs_pageio_complete(desc);
        if (!list_empty(&failed)) {
                list_move(&failed, &hdr->pages);
-               return -EIO;
+               return desc->pg_error < 0 ? desc->pg_error : -EIO;
        }
        return 0;
 }