NFS/filelayout: Fix racy setting of fl->dsaddr in filelayout_check_deviceid()
authorTrond Myklebust <trond.myklebust@primarydata.com>
Thu, 20 Jul 2017 21:00:02 +0000 (17:00 -0400)
committerAnna Schumaker <Anna.Schumaker@Netapp.com>
Fri, 21 Jul 2017 18:08:45 +0000 (14:08 -0400)
We must set fl->dsaddr once, and once only, even if there are multiple
processes calling filelayout_check_deviceid() for the same layout
segment.

Reported-by: Olga Kornievskaia <kolga@netapp.com>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
fs/nfs/filelayout/filelayout.c

index 080fc6b278bd5bbc813bcaa4dcf828a1789451ea..44c638b7876cfd5824d2d6287731e4411ea02052 100644 (file)
@@ -542,6 +542,10 @@ filelayout_check_deviceid(struct pnfs_layout_hdr *lo,
        struct nfs4_file_layout_dsaddr *dsaddr;
        int status = -EINVAL;
 
+       /* Is the deviceid already set? If so, we're good. */
+       if (fl->dsaddr != NULL)
+               return 0;
+
        /* find and reference the deviceid */
        d = nfs4_find_get_deviceid(NFS_SERVER(lo->plh_inode), &fl->deviceid,
                        lo->plh_lc_cred, gfp_flags);
@@ -553,8 +557,6 @@ filelayout_check_deviceid(struct pnfs_layout_hdr *lo,
        if (filelayout_test_devid_unavailable(&dsaddr->id_node))
                goto out_put;
 
-       fl->dsaddr = dsaddr;
-
        if (fl->first_stripe_index >= dsaddr->stripe_count) {
                dprintk("%s Bad first_stripe_index %u\n",
                                __func__, fl->first_stripe_index);
@@ -570,6 +572,13 @@ filelayout_check_deviceid(struct pnfs_layout_hdr *lo,
                goto out_put;
        }
        status = 0;
+
+       /*
+        * Atomic compare and xchange to ensure we don't scribble
+        * over a non-NULL pointer.
+        */
+       if (cmpxchg(&fl->dsaddr, NULL, dsaddr) != NULL)
+               goto out_put;
 out:
        return status;
 out_put: