]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blobdiff - fs/nfs/write.c
NFS: Simplify nfs_request_add_commit_list() arguments
[mirror_ubuntu-jammy-kernel.git] / fs / nfs / write.c
index 7b93164069307346ea43f130e3883959ae6576b6..5754835a288608bd7dc3b58b8f92a2a6ee8a20bc 100644 (file)
@@ -21,6 +21,8 @@
 #include <linux/nfs_page.h>
 #include <linux/backing-dev.h>
 #include <linux/export.h>
+#include <linux/freezer.h>
+#include <linux/wait.h>
 
 #include <asm/uaccess.h>
 
@@ -244,11 +246,9 @@ static int wb_priority(struct writeback_control *wbc)
 {
        int ret = 0;
        if (wbc->for_reclaim)
-               return FLUSH_HIGHPRI | FLUSH_STABLE;
+               return FLUSH_HIGHPRI | FLUSH_COND_STABLE;
        if (wbc->sync_mode == WB_SYNC_ALL)
                ret = FLUSH_COND_STABLE;
-       if (wbc->for_kupdate || wbc->for_background)
-               ret |= FLUSH_LOWPRI;
        return ret;
 }
 
@@ -545,12 +545,22 @@ try_again:
        return head;
 }
 
+static void nfs_write_error_remove_page(struct nfs_page *req)
+{
+       nfs_unlock_request(req);
+       nfs_end_page_writeback(req);
+       nfs_release_request(req);
+       generic_error_remove_page(page_file_mapping(req->wb_page),
+                                 req->wb_page);
+}
+
 /*
  * Find an associated nfs write request, and prepare to flush it out
  * May return an error if the user signalled nfs_wait_on_request().
  */
 static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
-                               struct page *page, bool nonblock)
+                               struct page *page, bool nonblock,
+                               bool launder)
 {
        struct nfs_page *req;
        int ret = 0;
@@ -567,8 +577,21 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
 
        ret = 0;
        if (!nfs_pageio_add_request(pgio, req)) {
-               nfs_redirty_request(req);
                ret = pgio->pg_error;
+               /*
+                * Remove the problematic req upon fatal errors
+                * in launder case, while other dirty pages can
+                * still be around until they get flushed.
+                */
+               if (nfs_error_is_fatal(ret)) {
+                       nfs_context_set_write_error(req->wb_context, ret);
+                       if (launder) {
+                               nfs_write_error_remove_page(req);
+                               goto out;
+                       }
+               }
+               nfs_redirty_request(req);
+               ret = -EAGAIN;
        } else
                nfs_add_stats(page_file_mapping(page)->host,
                                NFSIOS_WRITEPAGES, 1);
@@ -576,12 +599,14 @@ out:
        return ret;
 }
 
-static int nfs_do_writepage(struct page *page, struct writeback_control *wbc, struct nfs_pageio_descriptor *pgio)
+static int nfs_do_writepage(struct page *page, struct writeback_control *wbc,
+                           struct nfs_pageio_descriptor *pgio, bool launder)
 {
        int ret;
 
        nfs_pageio_cond_complete(pgio, page_file_index(page));
-       ret = nfs_page_async_flush(pgio, page, wbc->sync_mode == WB_SYNC_NONE);
+       ret = nfs_page_async_flush(pgio, page, wbc->sync_mode == WB_SYNC_NONE,
+                                  launder);
        if (ret == -EAGAIN) {
                redirty_page_for_writepage(wbc, page);
                ret = 0;
@@ -592,7 +617,9 @@ static int nfs_do_writepage(struct page *page, struct writeback_control *wbc, st
 /*
  * Write an mmapped page to the server.
  */
-static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc)
+static int nfs_writepage_locked(struct page *page,
+                               struct writeback_control *wbc,
+                               bool launder)
 {
        struct nfs_pageio_descriptor pgio;
        struct inode *inode = page_file_mapping(page)->host;
@@ -601,7 +628,7 @@ static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc
        nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGE);
        nfs_pageio_init_write(&pgio, inode, wb_priority(wbc),
                                false, &nfs_async_write_completion_ops);
-       err = nfs_do_writepage(page, wbc, &pgio);
+       err = nfs_do_writepage(page, wbc, &pgio, launder);
        nfs_pageio_complete(&pgio);
        if (err < 0)
                return err;
@@ -614,7 +641,7 @@ int nfs_writepage(struct page *page, struct writeback_control *wbc)
 {
        int ret;
 
-       ret = nfs_writepage_locked(page, wbc);
+       ret = nfs_writepage_locked(page, wbc, false);
        unlock_page(page);
        return ret;
 }
@@ -623,7 +650,7 @@ static int nfs_writepages_callback(struct page *page, struct writeback_control *
 {
        int ret;
 
-       ret = nfs_do_writepage(page, wbc, data);
+       ret = nfs_do_writepage(page, wbc, data, false);
        unlock_page(page);
        return ret;
 }
@@ -803,11 +830,10 @@ EXPORT_SYMBOL_GPL(nfs_request_add_commit_list_locked);
  * holding the nfs_page lock.
  */
 void
-nfs_request_add_commit_list(struct nfs_page *req, struct list_head *dst,
-                           struct nfs_commit_info *cinfo)
+nfs_request_add_commit_list(struct nfs_page *req, struct nfs_commit_info *cinfo)
 {
        spin_lock(cinfo->lock);
-       nfs_request_add_commit_list_locked(req, dst, cinfo);
+       nfs_request_add_commit_list_locked(req, &cinfo->mds->list, cinfo);
        spin_unlock(cinfo->lock);
        nfs_mark_page_unstable(req->wb_page, cinfo);
 }
@@ -865,7 +891,7 @@ nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg,
 {
        if (pnfs_mark_request_commit(req, lseg, cinfo, ds_commit_idx))
                return;
-       nfs_request_add_commit_list(req, &cinfo->mds->list, cinfo);
+       nfs_request_add_commit_list(req, cinfo);
 }
 
 static void
@@ -1128,7 +1154,8 @@ int nfs_flush_incompatible(struct file *file, struct page *page)
                if (req == NULL)
                        return 0;
                l_ctx = req->wb_lock_context;
-               do_flush = req->wb_page != page || req->wb_context != ctx;
+               do_flush = req->wb_page != page ||
+                       !nfs_match_open_context(req->wb_context, ctx);
                /* for now, flush if more than 1 request in page_group */
                do_flush |= req->wb_this_page != req;
                if (l_ctx && flctx &&
@@ -1326,9 +1353,15 @@ static void nfs_async_write_error(struct list_head *head)
        }
 }
 
+static void nfs_async_write_reschedule_io(struct nfs_pgio_header *hdr)
+{
+       nfs_async_write_error(&hdr->pages);
+}
+
 static const struct nfs_pgio_completion_ops nfs_async_write_completion_ops = {
        .error_cleanup = nfs_async_write_error,
        .completion = nfs_write_completion,
+       .reschedule_io = nfs_async_write_reschedule_io,
 };
 
 void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
@@ -1529,27 +1562,21 @@ static void nfs_writeback_result(struct rpc_task *task,
        }
 }
 
-
-static int nfs_commit_set_lock(struct nfs_inode *nfsi, int may_wait)
+static int wait_on_commit(struct nfs_mds_commit_info *cinfo)
 {
-       int ret;
+       return wait_on_atomic_t(&cinfo->rpcs_out,
+                       nfs_wait_atomic_killable, TASK_KILLABLE);
+}
 
-       if (!test_and_set_bit(NFS_INO_COMMIT, &nfsi->flags))
-               return 1;
-       if (!may_wait)
-               return 0;
-       ret = out_of_line_wait_on_bit_lock(&nfsi->flags,
-                               NFS_INO_COMMIT,
-                               nfs_wait_bit_killable,
-                               TASK_KILLABLE);
-       return (ret < 0) ? ret : 1;
+static void nfs_commit_begin(struct nfs_mds_commit_info *cinfo)
+{
+       atomic_inc(&cinfo->rpcs_out);
 }
 
-static void nfs_commit_clear_lock(struct nfs_inode *nfsi)
+static void nfs_commit_end(struct nfs_mds_commit_info *cinfo)
 {
-       clear_bit(NFS_INO_COMMIT, &nfsi->flags);
-       smp_mb__after_atomic();
-       wake_up_bit(&nfsi->flags, NFS_INO_COMMIT);
+       if (atomic_dec_and_test(&cinfo->rpcs_out))
+               wake_up_atomic_t(&cinfo->rpcs_out);
 }
 
 void nfs_commitdata_release(struct nfs_commit_data *data)
@@ -1666,6 +1693,13 @@ void nfs_retry_commit(struct list_head *page_list,
 }
 EXPORT_SYMBOL_GPL(nfs_retry_commit);
 
+static void
+nfs_commit_resched_write(struct nfs_commit_info *cinfo,
+               struct nfs_page *req)
+{
+       __set_page_dirty_nobuffers(req->wb_page);
+}
+
 /*
  * Commit dirty pages
  */
@@ -1687,7 +1721,6 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how,
                                   data->mds_ops, how, 0);
  out_bad:
        nfs_retry_commit(head, NULL, cinfo, 0);
-       cinfo->completion_ops->error_cleanup(NFS_I(inode));
        return -ENOMEM;
 }
 
@@ -1749,8 +1782,7 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data)
                clear_bdi_congested(&nfss->backing_dev_info, BLK_RW_ASYNC);
 
        nfs_init_cinfo(&cinfo, data->inode, data->dreq);
-       if (atomic_dec_and_test(&cinfo.mds->rpcs_out))
-               nfs_commit_clear_lock(NFS_I(data->inode));
+       nfs_commit_end(cinfo.mds);
 }
 
 static void nfs_commit_release(void *calldata)
@@ -1769,7 +1801,7 @@ static const struct rpc_call_ops nfs_commit_ops = {
 
 static const struct nfs_commit_completion_ops nfs_commit_completion_ops = {
        .completion = nfs_commit_release_pages,
-       .error_cleanup = nfs_commit_clear_lock,
+       .resched_write = nfs_commit_resched_write,
 };
 
 int nfs_generic_commit_list(struct inode *inode, struct list_head *head,
@@ -1788,30 +1820,25 @@ int nfs_commit_inode(struct inode *inode, int how)
        LIST_HEAD(head);
        struct nfs_commit_info cinfo;
        int may_wait = how & FLUSH_SYNC;
+       int error = 0;
        int res;
 
-       res = nfs_commit_set_lock(NFS_I(inode), may_wait);
-       if (res <= 0)
-               goto out_mark_dirty;
        nfs_init_cinfo_from_inode(&cinfo, inode);
+       nfs_commit_begin(cinfo.mds);
        res = nfs_scan_commit(inode, &head, &cinfo);
-       if (res) {
-               int error;
-
+       if (res)
                error = nfs_generic_commit_list(inode, &head, how, &cinfo);
-               if (error < 0)
-                       return error;
-               if (!may_wait)
-                       goto out_mark_dirty;
-               error = wait_on_bit_action(&NFS_I(inode)->flags,
-                               NFS_INO_COMMIT,
-                               nfs_wait_bit_killable,
-                               TASK_KILLABLE);
-               if (error < 0)
-                       return error;
-       } else
-               nfs_commit_clear_lock(NFS_I(inode));
+       nfs_commit_end(cinfo.mds);
+       if (error < 0)
+               goto out_error;
+       if (!may_wait)
+               goto out_mark_dirty;
+       error = wait_on_commit(cinfo.mds);
+       if (error < 0)
+               return error;
        return res;
+out_error:
+       res = error;
        /* Note: If we exit without ensuring that the commit is complete,
         * we must mark the inode as dirty. Otherwise, future calls to
         * sync_inode() with the WB_SYNC_ALL flag set will fail to ensure
@@ -1821,6 +1848,7 @@ out_mark_dirty:
        __mark_inode_dirty(inode, I_DIRTY_DATASYNC);
        return res;
 }
+EXPORT_SYMBOL_GPL(nfs_commit_inode);
 
 int nfs_write_inode(struct inode *inode, struct writeback_control *wbc)
 {
@@ -1911,7 +1939,7 @@ int nfs_wb_page_cancel(struct inode *inode, struct page *page)
 /*
  * Write back all requests on one page - we do this before reading it.
  */
-int nfs_wb_page(struct inode *inode, struct page *page)
+int nfs_wb_single_page(struct inode *inode, struct page *page, bool launder)
 {
        loff_t range_start = page_file_offset(page);
        loff_t range_end = range_start + (loff_t)(PAGE_CACHE_SIZE - 1);
@@ -1928,7 +1956,7 @@ int nfs_wb_page(struct inode *inode, struct page *page)
        for (;;) {
                wait_on_page_writeback(page);
                if (clear_page_dirty_for_io(page)) {
-                       ret = nfs_writepage_locked(page, &wbc);
+                       ret = nfs_writepage_locked(page, &wbc, launder);
                        if (ret < 0)
                                goto out_error;
                        continue;