pnfs/filelayout: fix race between mark_request_commit and scan_commit_lists
authorPeng Tao <tao.peng@primarydata.com>
Thu, 3 Jul 2014 05:07:45 +0000 (13:07 +0800)
committerTrond Myklebust <trond.myklebust@primarydata.com>
Sat, 12 Jul 2014 22:22:41 +0000 (18:22 -0400)
We need to hold cinfo lock while setting bucket->wlseg and adding req to nwritten
list at the same time. Otherwise there might be a window where nwritten list
is empty yet we set bucket->wlseg, in which case ff_layout_scan_ds_commit_list()
may end up clearing bucket->wlseg incorrectly, casuing client to oops later on.

This was found when testing flexfile layout but filelayout has the same problem.

Signed-off-by: Peng Tao <tao.peng@primarydata.com>
Signed-off-by: Tom Haynes <Thomas.Haynes@primarydata.com>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
fs/nfs/filelayout/filelayout.c

index 504d58a51d3519338c7e57d06f68b520dcef5842..a928f92bcb10c46719051c31a89f320a6935b6bd 100644 (file)
@@ -1035,18 +1035,22 @@ out:
        pnfs_put_lseg(freeme);
 }
 
-static struct list_head *
-filelayout_choose_commit_list(struct nfs_page *req,
-                             struct pnfs_layout_segment *lseg,
-                             struct nfs_commit_info *cinfo)
+static void
+filelayout_mark_request_commit(struct nfs_page *req,
+                              struct pnfs_layout_segment *lseg,
+                              struct nfs_commit_info *cinfo)
+
 {
        struct nfs4_filelayout_segment *fl = FILELAYOUT_LSEG(lseg);
        u32 i, j;
        struct list_head *list;
        struct pnfs_commit_bucket *buckets;
 
-       if (fl->commit_through_mds)
-               return &cinfo->mds->list;
+       if (fl->commit_through_mds) {
+               list = &cinfo->mds->list;
+               spin_lock(cinfo->lock);
+               goto mds_commit;
+       }
 
        /* Note that we are calling nfs4_fl_calc_j_index on each page
         * that ends up being committed to a data server.  An attractive
@@ -1070,19 +1074,22 @@ filelayout_choose_commit_list(struct nfs_page *req,
        }
        set_bit(PG_COMMIT_TO_DS, &req->wb_flags);
        cinfo->ds->nwritten++;
-       spin_unlock(cinfo->lock);
-       return list;
-}
 
-static void
-filelayout_mark_request_commit(struct nfs_page *req,
-                              struct pnfs_layout_segment *lseg,
-                              struct nfs_commit_info *cinfo)
-{
-       struct list_head *list;
-
-       list = filelayout_choose_commit_list(req, lseg, cinfo);
-       nfs_request_add_commit_list(req, list, cinfo);
+mds_commit:
+       /* nfs_request_add_commit_list(). We need to add req to list without
+        * dropping cinfo lock.
+        */
+       set_bit(PG_CLEAN, &(req)->wb_flags);
+       nfs_list_add_request(req, list);
+       cinfo->mds->ncommit++;
+       spin_unlock(cinfo->lock);
+       if (!cinfo->dreq) {
+               inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
+               inc_bdi_stat(page_file_mapping(req->wb_page)->backing_dev_info,
+                            BDI_RECLAIMABLE);
+               __mark_inode_dirty(req->wb_context->dentry->d_inode,
+                                  I_DIRTY_DATASYNC);
+       }
 }
 
 static u32 calc_ds_index_from_commit(struct pnfs_layout_segment *lseg, u32 i)