NFSv4.1: Ensure that we free existing layout segments if we get a new layout
authorTrond Myklebust <trond.myklebust@primarydata.com>
Wed, 12 Feb 2014 15:02:27 +0000 (10:02 -0500)
committerTrond Myklebust <trond.myklebust@primarydata.com>
Thu, 20 Feb 2014 02:21:06 +0000 (21:21 -0500)
If the server returns a completely new layout stateid in response to our
LAYOUTGET, then make sure to free any existing layout segments.

Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
fs/nfs/pnfs.c

index 6e67ada6c22cfdd391472f845ea936a7d373de71..cb53d450ae321e4464e9dee7d9a4aa6ef2074b9e 100644 (file)
@@ -665,6 +665,17 @@ static bool pnfs_seqid_is_newer(u32 s1, u32 s2)
        return (s32)(s1 - s2) > 0;
 }
 
+static void
+pnfs_verify_layout_stateid(struct pnfs_layout_hdr *lo,
+               const nfs4_stateid *new,
+               struct list_head *free_me_list)
+{
+       if (nfs4_stateid_match_other(&lo->plh_stateid, new))
+               return;
+       /* Layout is new! Kill existing layout segments */
+       pnfs_mark_matching_lsegs_invalid(lo, free_me_list, NULL);
+}
+
 /* update lo->plh_stateid with new if is more recent */
 void
 pnfs_set_layout_stateid(struct pnfs_layout_hdr *lo, const nfs4_stateid *new,
@@ -1315,6 +1326,7 @@ pnfs_layout_process(struct nfs4_layoutget *lgp)
        struct nfs4_layoutget_res *res = &lgp->res;
        struct pnfs_layout_segment *lseg;
        struct inode *ino = lo->plh_inode;
+       LIST_HEAD(free_me);
        int status = 0;
 
        /* Inject layout blob into I/O device driver */
@@ -1341,6 +1353,8 @@ pnfs_layout_process(struct nfs4_layoutget *lgp)
                goto out_forget_reply;
        }
 
+       /* Check that the new stateid matches the old stateid */
+       pnfs_verify_layout_stateid(lo, &res->stateid, &free_me);
        /* Done processing layoutget. Set the layout stateid */
        pnfs_set_layout_stateid(lo, &res->stateid, false);
 
@@ -1355,6 +1369,7 @@ pnfs_layout_process(struct nfs4_layoutget *lgp)
        }
 
        spin_unlock(&ino->i_lock);
+       pnfs_free_lseg_list(&free_me);
        return lseg;
 out:
        return ERR_PTR(status);