]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - fs/nfs/pagelist.c
NFS: Don't run wake_up_bit() when nobody is waiting...
[karo-tx-linux.git] / fs / nfs / pagelist.c
index 6e629b856a00f2ebe52b4ba6badb356d49f380e6..de9066a92c0d27cb9e397233fb5b92116c8dc127 100644 (file)
 static struct kmem_cache *nfs_page_cachep;
 static const struct rpc_call_ops nfs_pgio_common_ops;
 
-static bool nfs_pgarray_set(struct nfs_page_array *p, unsigned int pagecount)
-{
-       p->npages = pagecount;
-       if (pagecount <= ARRAY_SIZE(p->page_array))
-               p->pagevec = p->page_array;
-       else {
-               p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_KERNEL);
-               if (!p->pagevec)
-                       p->npages = 0;
-       }
-       return p->pagevec != NULL;
-}
-
 struct nfs_pgio_mirror *
 nfs_pgio_current_mirror(struct nfs_pageio_descriptor *desc)
 {
@@ -63,8 +50,8 @@ void nfs_pgheader_init(struct nfs_pageio_descriptor *desc,
        hdr->cred = hdr->req->wb_context->cred;
        hdr->io_start = req_offset(hdr->req);
        hdr->good_bytes = mirror->pg_count;
+       hdr->io_completion = desc->pg_io_completion;
        hdr->dreq = desc->pg_dreq;
-       hdr->layout_private = desc->pg_layout_private;
        hdr->release = release;
        hdr->completion_ops = desc->pg_completion_ops;
        if (hdr->completion_ops->init_hdr)
@@ -115,6 +102,35 @@ nfs_iocounter_wait(struct nfs_lock_context *l_ctx)
                        TASK_KILLABLE);
 }
 
+/**
+ * nfs_async_iocounter_wait - wait on a rpc_waitqueue for I/O
+ * to complete
+ * @task: the rpc_task that should wait
+ * @l_ctx: nfs_lock_context with io_counter to check
+ *
+ * Returns true if there is outstanding I/O to wait on and the
+ * task has been put to sleep.
+ */
+bool
+nfs_async_iocounter_wait(struct rpc_task *task, struct nfs_lock_context *l_ctx)
+{
+       struct inode *inode = d_inode(l_ctx->open_context->dentry);
+       bool ret = false;
+
+       if (atomic_read(&l_ctx->io_count) > 0) {
+               rpc_sleep_on(&NFS_SERVER(inode)->uoc_rpcwaitq, task, NULL);
+               ret = true;
+       }
+
+       if (atomic_read(&l_ctx->io_count) == 0) {
+               rpc_wake_up_queued_task(&NFS_SERVER(inode)->uoc_rpcwaitq, task);
+               ret = false;
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(nfs_async_iocounter_wait);
+
 /*
  * nfs_page_group_lock - lock the head of the page group
  * @req - request in group that is to be locked
@@ -139,9 +155,12 @@ nfs_page_group_lock(struct nfs_page *req, bool nonblock)
        if (!test_and_set_bit(PG_HEADLOCK, &head->wb_flags))
                return 0;
 
-       if (!nonblock)
+       if (!nonblock) {
+               set_bit(PG_CONTENDED1, &head->wb_flags);
+               smp_mb__after_atomic();
                return wait_on_bit_lock(&head->wb_flags, PG_HEADLOCK,
                                TASK_UNINTERRUPTIBLE);
+       }
 
        return -EAGAIN;
 }
@@ -159,6 +178,10 @@ nfs_page_group_lock_wait(struct nfs_page *req)
 
        WARN_ON_ONCE(head != head->wb_head);
 
+       if (!test_bit(PG_HEADLOCK, &head->wb_flags))
+               return;
+       set_bit(PG_CONTENDED1, &head->wb_flags);
+       smp_mb__after_atomic();
        wait_on_bit(&head->wb_flags, PG_HEADLOCK,
                TASK_UNINTERRUPTIBLE);
 }
@@ -177,6 +200,8 @@ nfs_page_group_unlock(struct nfs_page *req)
        smp_mb__before_atomic();
        clear_bit(PG_HEADLOCK, &head->wb_flags);
        smp_mb__after_atomic();
+       if (!test_bit(PG_CONTENDED1, &head->wb_flags))
+               return;
        wake_up_bit(&head->wb_flags, PG_HEADLOCK);
 }
 
@@ -367,6 +392,8 @@ void nfs_unlock_request(struct nfs_page *req)
        smp_mb__before_atomic();
        clear_bit(PG_BUSY, &req->wb_flags);
        smp_mb__after_atomic();
+       if (!test_bit(PG_CONTENDED2, &req->wb_flags))
+               return;
        wake_up_bit(&req->wb_flags, PG_BUSY);
 }
 
@@ -398,8 +425,11 @@ static void nfs_clear_request(struct nfs_page *req)
                req->wb_page = NULL;
        }
        if (l_ctx != NULL) {
-               if (atomic_dec_and_test(&l_ctx->io_count))
+               if (atomic_dec_and_test(&l_ctx->io_count)) {
                        wake_up_atomic_t(&l_ctx->io_count);
+                       if (test_bit(NFS_CONTEXT_UNLOCK, &ctx->flags))
+                               rpc_wake_up(&NFS_SERVER(d_inode(ctx->dentry))->uoc_rpcwaitq);
+               }
                nfs_put_lock_context(l_ctx);
                req->wb_lock_context = NULL;
        }
@@ -446,6 +476,10 @@ void nfs_release_request(struct nfs_page *req)
 int
 nfs_wait_on_request(struct nfs_page *req)
 {
+       if (!test_bit(PG_BUSY, &req->wb_flags))
+               return 0;
+       set_bit(PG_CONTENDED2, &req->wb_flags);
+       smp_mb__after_atomic();
        return wait_on_bit_io(&req->wb_flags, PG_BUSY,
                              TASK_UNINTERRUPTIBLE);
 }
@@ -677,7 +711,8 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
                     const struct nfs_pgio_completion_ops *compl_ops,
                     const struct nfs_rw_ops *rw_ops,
                     size_t bsize,
-                    int io_flags)
+                    int io_flags,
+                    gfp_t gfp_flags)
 {
        struct nfs_pgio_mirror *new;
        int i;
@@ -690,8 +725,8 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
        desc->pg_ioflags = io_flags;
        desc->pg_error = 0;
        desc->pg_lseg = NULL;
+       desc->pg_io_completion = NULL;
        desc->pg_dreq = NULL;
-       desc->pg_layout_private = NULL;
        desc->pg_bsize = bsize;
 
        desc->pg_mirror_count = 1;
@@ -701,7 +736,7 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
                /* until we have a request, we don't have an lseg and no
                 * idea how many mirrors there will be */
                new = kcalloc(NFS_PAGEIO_DESCRIPTOR_MIRROR_MAX,
-                             sizeof(struct nfs_pgio_mirror), GFP_KERNEL);
+                             sizeof(struct nfs_pgio_mirror), gfp_flags);
                desc->pg_mirrors_dynamic = new;
                desc->pg_mirrors = new;
 
@@ -754,13 +789,25 @@ int nfs_generic_pgio(struct nfs_pageio_descriptor *desc,
                                *last_page;
        struct list_head *head = &mirror->pg_list;
        struct nfs_commit_info cinfo;
+       struct nfs_page_array *pg_array = &hdr->page_array;
        unsigned int pagecount, pageused;
+       gfp_t gfp_flags = GFP_KERNEL;
 
        pagecount = nfs_page_array_len(mirror->pg_base, mirror->pg_count);
-       if (!nfs_pgarray_set(&hdr->page_array, pagecount)) {
-               nfs_pgio_error(hdr);
-               desc->pg_error = -ENOMEM;
-               return desc->pg_error;
+       pg_array->npages = pagecount;
+
+       if (pagecount <= ARRAY_SIZE(pg_array->page_array))
+               pg_array->pagevec = pg_array->page_array;
+       else {
+               if (hdr->rw_mode == FMODE_WRITE)
+                       gfp_flags = GFP_NOIO;
+               pg_array->pagevec = kcalloc(pagecount, sizeof(struct page *), gfp_flags);
+               if (!pg_array->pagevec) {
+                       pg_array->npages = 0;
+                       nfs_pgio_error(hdr);
+                       desc->pg_error = -ENOMEM;
+                       return desc->pg_error;
+               }
        }
 
        nfs_init_cinfo(&cinfo, desc->pg_inode, desc->pg_dreq);
@@ -1202,6 +1249,7 @@ int nfs_pageio_resend(struct nfs_pageio_descriptor *desc,
 {
        LIST_HEAD(failed);
 
+       desc->pg_io_completion = hdr->io_completion;
        desc->pg_dreq = hdr->dreq;
        while (!list_empty(&hdr->pages)) {
                struct nfs_page *req = nfs_list_entry(hdr->pages.next);
@@ -1256,8 +1304,10 @@ void nfs_pageio_cond_complete(struct nfs_pageio_descriptor *desc, pgoff_t index)
                mirror = &desc->pg_mirrors[midx];
                if (!list_empty(&mirror->pg_list)) {
                        prev = nfs_list_entry(mirror->pg_list.prev);
-                       if (index != prev->wb_index + 1)
-                               nfs_pageio_complete_mirror(desc, midx);
+                       if (index != prev->wb_index + 1) {
+                               nfs_pageio_complete(desc);
+                               break;
+                       }
                }
        }
 }