NFSv4: Fix an atomicity problem in CLOSE
authorTrond Myklebust <trond.myklebust@primarydata.com>
Fri, 23 Jan 2015 20:32:46 +0000 (15:32 -0500)
committerTrond Myklebust <trond.myklebust@primarydata.com>
Sat, 24 Jan 2015 00:22:39 +0000 (19:22 -0500)
If we are to remove the serialisation of OPEN/CLOSE, then we need to
ensure that the stateid sent as part of a CLOSE operation does not
change after we test the state in nfs4_close_prepare.

Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
fs/nfs/nfs4proc.c
fs/nfs/nfs4xdr.c
include/linux/nfs_xdr.h

index c347705b016104de8b0360f163e87c19d9b4d78c..4863dec108652d8db8f1f3247c8d724160e53926 100644 (file)
@@ -2587,6 +2587,11 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
                case -NFS4ERR_OLD_STATEID:
                case -NFS4ERR_BAD_STATEID:
                case -NFS4ERR_EXPIRED:
+                       if (!nfs4_stateid_match(&calldata->arg.stateid,
+                                               &state->stateid)) {
+                               rpc_restart_call_prepare(task);
+                               goto out_release;
+                       }
                        if (calldata->arg.fmode == 0)
                                break;
                default:
@@ -2619,6 +2624,7 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
        is_rdwr = test_bit(NFS_O_RDWR_STATE, &state->flags);
        is_rdonly = test_bit(NFS_O_RDONLY_STATE, &state->flags);
        is_wronly = test_bit(NFS_O_WRONLY_STATE, &state->flags);
+       nfs4_stateid_copy(&calldata->arg.stateid, &state->stateid);
        /* Calculate the change in open mode */
        calldata->arg.fmode = 0;
        if (state->n_rdwr == 0) {
@@ -2757,7 +2763,6 @@ int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait)
        calldata->inode = state->inode;
        calldata->state = state;
        calldata->arg.fh = NFS_FH(state->inode);
-       calldata->arg.stateid = &state->open_stateid;
        /* Serialization for the sequence id */
        calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid, gfp_mask);
        if (calldata->arg.seqid == NULL)
index cb4376b78ed91771b98bf03695eea1c67a5b5889..7e7be5ab70bb6892bcbdf49930ebdb4d5260c304 100644 (file)
@@ -1125,7 +1125,7 @@ static void encode_close(struct xdr_stream *xdr, const struct nfs_closeargs *arg
 {
        encode_op_hdr(xdr, OP_CLOSE, decode_close_maxsz, hdr);
        encode_nfs4_seqid(xdr, arg->seqid);
-       encode_nfs4_stateid(xdr, arg->stateid);
+       encode_nfs4_stateid(xdr, &arg->stateid);
 }
 
 static void encode_commit(struct xdr_stream *xdr, const struct nfs_commitargs *args, struct compound_hdr *hdr)
@@ -1530,7 +1530,7 @@ static void encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_co
 static void encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_closeargs *arg, struct compound_hdr *hdr)
 {
        encode_op_hdr(xdr, OP_OPEN_DOWNGRADE, decode_open_downgrade_maxsz, hdr);
-       encode_nfs4_stateid(xdr, arg->stateid);
+       encode_nfs4_stateid(xdr, &arg->stateid);
        encode_nfs4_seqid(xdr, arg->seqid);
        encode_share_access(xdr, arg->fmode);
 }
index 467c84efb5960f8e79fac918eabc721e5f3722d4..7e38d641236e5c1ecc544913216c34400e36d1f7 100644 (file)
@@ -389,7 +389,7 @@ struct nfs_open_confirmres {
 struct nfs_closeargs {
        struct nfs4_sequence_args       seq_args;
        struct nfs_fh *         fh;
-       nfs4_stateid *          stateid;
+       nfs4_stateid            stateid;
        struct nfs_seqid *      seqid;
        fmode_t                 fmode;
        const u32 *             bitmask;