NFS: added FREE_STATEID call
Bryan Schumaker [Thu, 2 Jun 2011 18:59:09 +0000 (14:59 -0400)]
FREE_STATEID is used to tell the server that we want to free a stateid
that no longer has any locks associated with it.  This allows the client
to reclaim locks without encountering edge conditions documented in
section 8.4.3 of RFC 5661.

Signed-off-by: Bryan Schumaker <bjschuma@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>

fs/nfs/nfs4proc.c
fs/nfs/nfs4xdr.c
include/linux/nfs4.h
include/linux/nfs_xdr.h

index 5612ba9..197ad0a 100644 (file)
@@ -6043,6 +6043,36 @@ static int nfs41_test_stateid(struct nfs_server *server, struct nfs4_state *stat
        } while (exception.retry);
        return err;
 }
+
+static int _nfs4_free_stateid(struct nfs_server *server, struct nfs4_state *state)
+{
+       int status;
+       struct nfs41_free_stateid_args args = {
+               .stateid = &state->stateid,
+       };
+       struct nfs41_free_stateid_res res;
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FREE_STATEID],
+               .rpc_argp = &args,
+               .rpc_resp = &res,
+       };
+
+       args.seq_args.sa_session = res.seq_res.sr_session = NULL;
+       status = nfs4_call_sync_sequence(server->client, server, &msg, &args.seq_args, &res.seq_res, 0, 1);
+       return status;
+}
+
+static int nfs41_free_stateid(struct nfs_server *server, struct nfs4_state *state)
+{
+       struct nfs4_exception exception = { };
+       int err;
+       do {
+               err = nfs4_handle_exception(server,
+                               _nfs4_free_stateid(server, state),
+                               &exception);
+       } while (exception.retry);
+       return err;
+}
 #endif /* CONFIG_NFS_V4_1 */
 
 struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = {
index 7d914d9..c191a9b 100644 (file)
@@ -348,6 +348,9 @@ static int nfs4_stat_to_errno(int);
 #define encode_test_stateid_maxsz      (op_encode_hdr_maxsz + 2 + \
                                         XDR_QUADLEN(NFS4_STATEID_SIZE))
 #define decode_test_stateid_maxsz      (op_decode_hdr_maxsz + 2 + 1)
+#define encode_free_stateid_maxsz      (op_encode_hdr_maxsz + 1 + \
+                                        XDR_QUADLEN(NFS4_STATEID_SIZE))
+#define decode_free_stateid_maxsz      (op_decode_hdr_maxsz + 1)
 #else /* CONFIG_NFS_V4_1 */
 #define encode_sequence_maxsz  0
 #define decode_sequence_maxsz  0
@@ -791,6 +794,12 @@ static int nfs4_stat_to_errno(int);
 #define NFS4_dec_test_stateid_sz       (compound_decode_hdr_maxsz + \
                                         decode_sequence_maxsz + \
                                         decode_test_stateid_maxsz)
+#define NFS4_enc_free_stateid_sz       (compound_encode_hdr_maxsz + \
+                                        encode_sequence_maxsz + \
+                                        encode_free_stateid_maxsz)
+#define NFS4_dec_free_stateid_sz       (compound_decode_hdr_maxsz + \
+                                        decode_sequence_maxsz + \
+                                        decode_free_stateid_maxsz)
 
 const u32 nfs41_maxwrite_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
                                      compound_encode_hdr_maxsz +
@@ -1985,6 +1994,18 @@ static void encode_test_stateid(struct xdr_stream *xdr,
        hdr->nops++;
        hdr->replen += decode_test_stateid_maxsz;
 }
+
+static void encode_free_stateid(struct xdr_stream *xdr,
+                               struct nfs41_free_stateid_args *args,
+                               struct compound_hdr *hdr)
+{
+       __be32 *p;
+       p = reserve_space(xdr, 4 + NFS4_STATEID_SIZE);
+       *p++ = cpu_to_be32(OP_FREE_STATEID);
+       xdr_encode_opaque_fixed(p, args->stateid->data, NFS4_STATEID_SIZE);
+       hdr->nops++;
+       hdr->replen += decode_free_stateid_maxsz;
+}
 #endif /* CONFIG_NFS_V4_1 */
 
 /*
@@ -2873,6 +2894,23 @@ static void nfs4_xdr_enc_test_stateid(struct rpc_rqst *req,
        encode_test_stateid(xdr, args, &hdr);
        encode_nops(&hdr);
 }
+
+/*
+ *  Encode FREE_STATEID request
+ */
+static void nfs4_xdr_enc_free_stateid(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
+                                    struct nfs41_free_stateid_args *args)
+{
+       struct compound_hdr hdr = {
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
+       };
+
+       encode_compound_hdr(xdr, req, &hdr);
+       encode_sequence(xdr, &args->seq_args, &hdr);
+       encode_free_stateid(xdr, args, &hdr);
+       encode_nops(&hdr);
+}
 #endif /* CONFIG_NFS_V4_1 */
 
 static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
@@ -5440,6 +5478,26 @@ out_overflow:
 out:
        return -EIO;
 }
+
+static int decode_free_stateid(struct xdr_stream *xdr,
+                              struct nfs41_free_stateid_res *res)
+{
+       __be32 *p;
+       int status;
+
+       status = decode_op_hdr(xdr, OP_FREE_STATEID);
+       if (status)
+               return status;
+
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(!p))
+               goto out_overflow;
+       res->status = be32_to_cpup(p++);
+       return res->status;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
 #endif /* CONFIG_NFS_V4_1 */
 
 /*
@@ -6624,6 +6682,27 @@ static int nfs4_xdr_dec_test_stateid(struct rpc_rqst *rqstp,
 out:
        return status;
 }
+
+/*
+ * Decode FREE_STATEID response
+ */
+static int nfs4_xdr_dec_free_stateid(struct rpc_rqst *rqstp,
+                                    struct xdr_stream *xdr,
+                                    struct nfs41_free_stateid_res *res)
+{
+       struct compound_hdr hdr;
+       int status;
+
+       status = decode_compound_hdr(xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
+       if (status)
+               goto out;
+       status = decode_free_stateid(xdr, res);
+out:
+       return status;
+}
 #endif /* CONFIG_NFS_V4_1 */
 
 /**
@@ -6828,6 +6907,7 @@ struct rpc_procinfo       nfs4_procedures[] = {
        PROC(LAYOUTRETURN,      enc_layoutreturn,       dec_layoutreturn),
        PROC(SECINFO_NO_NAME,   enc_secinfo_no_name,    dec_secinfo_no_name),
        PROC(TEST_STATEID,      enc_test_stateid,       dec_test_stateid),
+       PROC(FREE_STATEID,      enc_free_stateid,       dec_free_stateid),
 #endif /* CONFIG_NFS_V4_1 */
 };
 
index efe07f7..a3c4bc8 100644 (file)
@@ -565,6 +565,7 @@ enum {
        NFSPROC4_CLNT_LAYOUTRETURN,
        NFSPROC4_CLNT_SECINFO_NO_NAME,
        NFSPROC4_CLNT_TEST_STATEID,
+       NFSPROC4_CLNT_FREE_STATEID,
 };
 
 /* nfs41 types */
index e696c1f..2094555 100644 (file)
@@ -1102,6 +1102,16 @@ struct nfs41_test_stateid_res {
        struct nfs4_sequence_res        seq_res;
 };
 
+struct nfs41_free_stateid_args {
+       nfs4_stateid                    *stateid;
+       struct nfs4_sequence_args       seq_args;
+};
+
+struct nfs41_free_stateid_res {
+       unsigned int                    status;
+       struct nfs4_sequence_res        seq_res;
+};
+
 #endif /* CONFIG_NFS_V4_1 */
 
 struct nfs_page;