NFSv4.1/pNFS: Don't queue up a new commit if the layout segment is invalid
authorTrond Myklebust <trond.myklebust@primarydata.com>
Thu, 31 Dec 2015 14:28:06 +0000 (09:28 -0500)
committerTrond Myklebust <trond.myklebust@primarydata.com>
Thu, 31 Dec 2015 20:55:35 +0000 (15:55 -0500)
If the layout segment is invalid, then we should not be adding more
write requests to the commit list. Instead, those writes should be
replayed after requesting a new layout.

Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
fs/nfs/callback_proc.c
fs/nfs/direct.c
fs/nfs/pnfs.c
fs/nfs/pnfs.h
fs/nfs/pnfs_nfs.c
fs/nfs/write.c
include/linux/nfs_xdr.h

index e4dbab5..2be8b25 100644 (file)
@@ -233,6 +233,8 @@ static u32 initiate_file_draining(struct nfs_client *clp,
 unlock:
        spin_unlock(&ino->i_lock);
        pnfs_free_lseg_list(&free_me_list);
+       /* Free all lsegs that are attached to commit buckets */
+       nfs_commit_inode(ino, 0);
        pnfs_put_layout_hdr(lo);
        trace_nfs4_cb_layoutrecall_file(clp, &args->cbl_fh, ino,
                        &args->cbl_stateid, -rv);
index 14f77df..a9a9392 100644 (file)
@@ -721,8 +721,20 @@ static void nfs_direct_commit_complete(struct nfs_commit_data *data)
                nfs_direct_write_complete(dreq, data->inode);
 }
 
+static void nfs_direct_resched_write(struct nfs_commit_info *cinfo,
+               struct nfs_page *req)
+{
+       struct nfs_direct_req *dreq = cinfo->dreq;
+
+       spin_lock(&dreq->lock);
+       dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
+       spin_unlock(&dreq->lock);
+       nfs_mark_request_commit(req, NULL, cinfo, 0);
+}
+
 static const struct nfs_commit_completion_ops nfs_direct_commit_completion_ops = {
        .completion = nfs_direct_commit_complete,
+       .resched_write = nfs_direct_resched_write,
 };
 
 static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq)
index 360fe95..6593be7 100644 (file)
@@ -703,6 +703,8 @@ pnfs_layout_free_bulk_destroy_list(struct list_head *layout_list,
                        ret = -EAGAIN;
                spin_unlock(&inode->i_lock);
                pnfs_free_lseg_list(&lseg_list);
+               /* Free all lsegs that are attached to commit buckets */
+               nfs_commit_inode(inode, 0);
                pnfs_put_layout_hdr(lo);
                iput(inode);
        }
@@ -1811,6 +1813,7 @@ void pnfs_error_mark_layout_for_return(struct inode *inode,
        pnfs_mark_matching_lsegs_return(lo, &free_me, &range);
        spin_unlock(&inode->i_lock);
        pnfs_free_lseg_list(&free_me);
+       nfs_commit_inode(inode, 0);
 }
 EXPORT_SYMBOL_GPL(pnfs_error_mark_layout_for_return);
 
index d93c2eb..4bd7faf 100644 (file)
@@ -412,6 +412,12 @@ pnfs_get_lseg(struct pnfs_layout_segment *lseg)
        return lseg;
 }
 
+static inline bool
+pnfs_is_valid_lseg(struct pnfs_layout_segment *lseg)
+{
+       return test_bit(NFS_LSEG_VALID, &lseg->pls_flags) != 0;
+}
+
 /* Return true if a layout driver is being used for this mountpoint */
 static inline int pnfs_enabled_sb(struct nfs_server *nfss)
 {
index 3c8e3a4..81ac648 100644 (file)
@@ -868,6 +868,11 @@ pnfs_layout_mark_request_commit(struct nfs_page *req,
        buckets = cinfo->ds->buckets;
        list = &buckets[ds_commit_idx].written;
        if (list_empty(list)) {
+               if (!pnfs_is_valid_lseg(lseg)) {
+                       spin_unlock(cinfo->lock);
+                       cinfo->completion_ops->resched_write(cinfo, req);
+                       return;
+               }
                /* Non-empty buckets hold a reference on the lseg.  That ref
                 * is normally transferred to the COMMIT call and released
                 * there.  It could also be released if the last req is pulled
index ae29f08..0aa8d6f 100644 (file)
@@ -1676,6 +1676,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
  */
@@ -1777,6 +1784,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,
+       .resched_write = nfs_commit_resched_write,
 };
 
 int nfs_generic_commit_list(struct inode *inode, struct list_head *head,
@@ -1823,6 +1831,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)
 {
index a8905b7..bee3e60 100644 (file)
@@ -1420,10 +1420,12 @@ struct nfs_mds_commit_info {
        struct list_head        list;
 };
 
+struct nfs_commit_info;
 struct nfs_commit_data;
 struct nfs_inode;
 struct nfs_commit_completion_ops {
        void (*completion) (struct nfs_commit_data *data);
+       void (*resched_write) (struct nfs_commit_info *, struct nfs_page *);
 };
 
 struct nfs_commit_info {