]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - fs/nfs/pagelist.c
Merge branch 'master' into devel and apply fixup from Stephen Rothwell:
[karo-tx-linux.git] / fs / nfs / pagelist.c
index 18449f43c568313f67abc036036fa7110b19d0e7..b60970cc7f1f0f688c80b80674848b940265625c 100644 (file)
@@ -230,7 +230,7 @@ EXPORT_SYMBOL_GPL(nfs_generic_pg_test);
  */
 void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
                     struct inode *inode,
-                    int (*doio)(struct nfs_pageio_descriptor *),
+                    const struct nfs_pageio_ops *pg_ops,
                     size_t bsize,
                     int io_flags)
 {
@@ -240,13 +240,12 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
        desc->pg_bsize = bsize;
        desc->pg_base = 0;
        desc->pg_moreio = 0;
+       desc->pg_recoalesce = 0;
        desc->pg_inode = inode;
-       desc->pg_doio = doio;
+       desc->pg_ops = pg_ops;
        desc->pg_ioflags = io_flags;
        desc->pg_error = 0;
        desc->pg_lseg = NULL;
-       desc->pg_test = nfs_generic_pg_test;
-       pnfs_pageio_init(desc, inode);
 }
 
 /**
@@ -276,7 +275,7 @@ static bool nfs_can_coalesce_requests(struct nfs_page *prev,
                return false;
        if (prev->wb_pgbase + prev->wb_bytes != PAGE_CACHE_SIZE)
                return false;
-       return pgio->pg_test(pgio, prev, req);
+       return pgio->pg_ops->pg_test(pgio, prev, req);
 }
 
 /**
@@ -297,6 +296,8 @@ static int nfs_pageio_do_add_request(struct nfs_pageio_descriptor *desc,
                if (!nfs_can_coalesce_requests(prev, req, desc))
                        return 0;
        } else {
+               if (desc->pg_ops->pg_init)
+                       desc->pg_ops->pg_init(desc, req);
                desc->pg_base = req->wb_pgbase;
        }
        nfs_list_remove_request(req);
@@ -311,7 +312,7 @@ static int nfs_pageio_do_add_request(struct nfs_pageio_descriptor *desc,
 static void nfs_pageio_doio(struct nfs_pageio_descriptor *desc)
 {
        if (!list_empty(&desc->pg_list)) {
-               int error = desc->pg_doio(desc);
+               int error = desc->pg_ops->pg_doio(desc);
                if (error < 0)
                        desc->pg_error = error;
                else
@@ -331,7 +332,7 @@ static void nfs_pageio_doio(struct nfs_pageio_descriptor *desc)
  * Returns true if the request 'req' was successfully coalesced into the
  * existing list of pages 'desc'.
  */
-int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
+static int __nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
                           struct nfs_page *req)
 {
        while (!nfs_pageio_do_add_request(desc, req)) {
@@ -340,17 +341,67 @@ int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
                if (desc->pg_error < 0)
                        return 0;
                desc->pg_moreio = 0;
+               if (desc->pg_recoalesce)
+                       return 0;
        }
        return 1;
 }
 
+static int nfs_do_recoalesce(struct nfs_pageio_descriptor *desc)
+{
+       LIST_HEAD(head);
+
+       do {
+               list_splice_init(&desc->pg_list, &head);
+               desc->pg_bytes_written -= desc->pg_count;
+               desc->pg_count = 0;
+               desc->pg_base = 0;
+               desc->pg_recoalesce = 0;
+
+               while (!list_empty(&head)) {
+                       struct nfs_page *req;
+
+                       req = list_first_entry(&head, struct nfs_page, wb_list);
+                       nfs_list_remove_request(req);
+                       if (__nfs_pageio_add_request(desc, req))
+                               continue;
+                       if (desc->pg_error < 0)
+                               return 0;
+                       break;
+               }
+       } while (desc->pg_recoalesce);
+       return 1;
+}
+
+int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
+               struct nfs_page *req)
+{
+       int ret;
+
+       do {
+               ret = __nfs_pageio_add_request(desc, req);
+               if (ret)
+                       break;
+               if (desc->pg_error < 0)
+                       break;
+               ret = nfs_do_recoalesce(desc);
+       } while (ret);
+       return ret;
+}
+
 /**
  * nfs_pageio_complete - Complete I/O on an nfs_pageio_descriptor
  * @desc: pointer to io descriptor
  */
 void nfs_pageio_complete(struct nfs_pageio_descriptor *desc)
 {
-       nfs_pageio_doio(desc);
+       for (;;) {
+               nfs_pageio_doio(desc);
+               if (!desc->pg_recoalesce)
+                       break;
+               if (!nfs_do_recoalesce(desc))
+                       break;
+       }
 }
 
 /**
@@ -369,7 +420,7 @@ void nfs_pageio_cond_complete(struct nfs_pageio_descriptor *desc, pgoff_t index)
        if (!list_empty(&desc->pg_list)) {
                struct nfs_page *prev = nfs_list_entry(desc->pg_list.prev);
                if (index != prev->wb_index + 1)
-                       nfs_pageio_doio(desc);
+                       nfs_pageio_complete(desc);
        }
 }