]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - fs/nfs/callback_xdr.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / fs / nfs / callback_xdr.c
index 05af212f0edfb1558e238bb0ce95a5ef7688987f..14e0f9371d1463900258ef60c361c3079f55c707 100644 (file)
 #include <linux/nfs4.h>
 #include <linux/nfs_fs.h>
 #include <linux/slab.h>
+#include <linux/sunrpc/bc_xprt.h>
 #include "nfs4_fs.h"
 #include "callback.h"
+#include "internal.h"
 
 #define CB_OP_TAGLEN_MAXSZ     (512)
 #define CB_OP_HDR_RES_MAXSZ    (2 + CB_OP_TAGLEN_MAXSZ)
@@ -22,6 +24,7 @@
 #define CB_OP_RECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ)
 
 #if defined(CONFIG_NFS_V4_1)
+#define CB_OP_LAYOUTRECALL_RES_MAXSZ   (CB_OP_HDR_RES_MAXSZ)
 #define CB_OP_SEQUENCE_RES_MAXSZ       (CB_OP_HDR_RES_MAXSZ + \
                                        4 + 1 + 3)
 #define CB_OP_RECALLANY_RES_MAXSZ      (CB_OP_HDR_RES_MAXSZ)
@@ -33,7 +36,8 @@
 /* Internal error code */
 #define NFS4ERR_RESOURCE_HDR   11050
 
-typedef __be32 (*callback_process_op_t)(void *, void *);
+typedef __be32 (*callback_process_op_t)(void *, void *,
+                                       struct cb_process_state *);
 typedef __be32 (*callback_decode_arg_t)(struct svc_rqst *, struct xdr_stream *, void *);
 typedef __be32 (*callback_encode_res_t)(struct svc_rqst *, struct xdr_stream *, void *);
 
@@ -160,7 +164,7 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound
        hdr->minorversion = ntohl(*p++);
        /* Check minor version is zero or one. */
        if (hdr->minorversion <= 1) {
-               p++;    /* skip callback_ident */
+               hdr->cb_ident = ntohl(*p++); /* ignored by v4.1 */
        } else {
                printk(KERN_WARNING "%s: NFSv4 server callback with "
                        "illegal minor version %u!\n",
@@ -220,6 +224,66 @@ out:
 
 #if defined(CONFIG_NFS_V4_1)
 
+static __be32 decode_layoutrecall_args(struct svc_rqst *rqstp,
+                                      struct xdr_stream *xdr,
+                                      struct cb_layoutrecallargs *args)
+{
+       __be32 *p;
+       __be32 status = 0;
+       uint32_t iomode;
+
+       args->cbl_addr = svc_addr(rqstp);
+       p = read_buf(xdr, 4 * sizeof(uint32_t));
+       if (unlikely(p == NULL)) {
+               status = htonl(NFS4ERR_BADXDR);
+               goto out;
+       }
+
+       args->cbl_layout_type = ntohl(*p++);
+       /* Depite the spec's xdr, iomode really belongs in the FILE switch,
+        * as it is unuseable and ignored with the other types.
+        */
+       iomode = ntohl(*p++);
+       args->cbl_layoutchanged = ntohl(*p++);
+       args->cbl_recall_type = ntohl(*p++);
+
+       if (args->cbl_recall_type == RETURN_FILE) {
+               args->cbl_range.iomode = iomode;
+               status = decode_fh(xdr, &args->cbl_fh);
+               if (unlikely(status != 0))
+                       goto out;
+
+               p = read_buf(xdr, 2 * sizeof(uint64_t));
+               if (unlikely(p == NULL)) {
+                       status = htonl(NFS4ERR_BADXDR);
+                       goto out;
+               }
+               p = xdr_decode_hyper(p, &args->cbl_range.offset);
+               p = xdr_decode_hyper(p, &args->cbl_range.length);
+               status = decode_stateid(xdr, &args->cbl_stateid);
+               if (unlikely(status != 0))
+                       goto out;
+       } else if (args->cbl_recall_type == RETURN_FSID) {
+               p = read_buf(xdr, 2 * sizeof(uint64_t));
+               if (unlikely(p == NULL)) {
+                       status = htonl(NFS4ERR_BADXDR);
+                       goto out;
+               }
+               p = xdr_decode_hyper(p, &args->cbl_fsid.major);
+               p = xdr_decode_hyper(p, &args->cbl_fsid.minor);
+       } else if (args->cbl_recall_type != RETURN_ALL) {
+               status = htonl(NFS4ERR_BADXDR);
+               goto out;
+       }
+       dprintk("%s: ltype 0x%x iomode %d changed %d recall_type %d\n",
+               __func__,
+               args->cbl_layout_type, iomode,
+               args->cbl_layoutchanged, args->cbl_recall_type);
+out:
+       dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
+       return status;
+}
+
 static __be32 decode_sessionid(struct xdr_stream *xdr,
                                 struct nfs4_sessionid *sid)
 {
@@ -574,10 +638,10 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
        case OP_CB_SEQUENCE:
        case OP_CB_RECALL_ANY:
        case OP_CB_RECALL_SLOT:
+       case OP_CB_LAYOUTRECALL:
                *op = &callback_ops[op_nr];
                break;
 
-       case OP_CB_LAYOUTRECALL:
        case OP_CB_NOTIFY_DEVICEID:
        case OP_CB_NOTIFY:
        case OP_CB_PUSH_DELEG:
@@ -593,6 +657,37 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
        return htonl(NFS_OK);
 }
 
+static void nfs4_callback_free_slot(struct nfs4_session *session)
+{
+       struct nfs4_slot_table *tbl = &session->bc_slot_table;
+
+       spin_lock(&tbl->slot_tbl_lock);
+       /*
+        * Let the state manager know callback processing done.
+        * A single slot, so highest used slotid is either 0 or -1
+        */
+       tbl->highest_used_slotid--;
+       nfs4_check_drain_bc_complete(session);
+       spin_unlock(&tbl->slot_tbl_lock);
+}
+
+static void nfs4_cb_free_slot(struct nfs_client *clp)
+{
+       if (clp && clp->cl_session)
+               nfs4_callback_free_slot(clp->cl_session);
+}
+
+/* A single slot, so highest used slotid is either 0 or -1 */
+void nfs4_cb_take_slot(struct nfs_client *clp)
+{
+       struct nfs4_slot_table *tbl = &clp->cl_session->bc_slot_table;
+
+       spin_lock(&tbl->slot_tbl_lock);
+       tbl->highest_used_slotid++;
+       BUG_ON(tbl->highest_used_slotid != 0);
+       spin_unlock(&tbl->slot_tbl_lock);
+}
+
 #else /* CONFIG_NFS_V4_1 */
 
 static __be32
@@ -601,6 +696,9 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
        return htonl(NFS4ERR_MINOR_VERS_MISMATCH);
 }
 
+static void nfs4_cb_free_slot(struct nfs_client *clp)
+{
+}
 #endif /* CONFIG_NFS_V4_1 */
 
 static __be32
@@ -621,7 +719,8 @@ preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op)
 static __be32 process_op(uint32_t minorversion, int nop,
                struct svc_rqst *rqstp,
                struct xdr_stream *xdr_in, void *argp,
-               struct xdr_stream *xdr_out, void *resp, int* drc_status)
+               struct xdr_stream *xdr_out, void *resp,
+               struct cb_process_state *cps)
 {
        struct callback_op *op = &callback_ops[0];
        unsigned int op_nr;
@@ -644,8 +743,8 @@ static __be32 process_op(uint32_t minorversion, int nop,
        if (status)
                goto encode_hdr;
 
-       if (*drc_status) {
-               status = *drc_status;
+       if (cps->drc_status) {
+               status = cps->drc_status;
                goto encode_hdr;
        }
 
@@ -653,16 +752,10 @@ static __be32 process_op(uint32_t minorversion, int nop,
        if (maxlen > 0 && maxlen < PAGE_SIZE) {
                status = op->decode_args(rqstp, xdr_in, argp);
                if (likely(status == 0))
-                       status = op->process_op(argp, resp);
+                       status = op->process_op(argp, resp, cps);
        } else
                status = htonl(NFS4ERR_RESOURCE);
 
-       /* Only set by OP_CB_SEQUENCE processing */
-       if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP)) {
-               *drc_status = status;
-               status = 0;
-       }
-
 encode_hdr:
        res = encode_op_hdr(xdr_out, op_nr, status);
        if (unlikely(res))
@@ -681,8 +774,11 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
        struct cb_compound_hdr_arg hdr_arg = { 0 };
        struct cb_compound_hdr_res hdr_res = { NULL };
        struct xdr_stream xdr_in, xdr_out;
-       __be32 *p;
-       __be32 status, drc_status = 0;
+       __be32 *p, status;
+       struct cb_process_state cps = {
+               .drc_status = 0,
+               .clp = NULL,
+       };
        unsigned int nops = 0;
 
        dprintk("%s: start\n", __func__);
@@ -696,6 +792,12 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
        if (status == __constant_htonl(NFS4ERR_RESOURCE))
                return rpc_garbage_args;
 
+       if (hdr_arg.minorversion == 0) {
+               cps.clp = nfs4_find_client_ident(hdr_arg.cb_ident);
+               if (!cps.clp || !check_gss_callback_principal(cps.clp, rqstp))
+                       return rpc_drop_reply;
+       }
+
        hdr_res.taglen = hdr_arg.taglen;
        hdr_res.tag = hdr_arg.tag;
        if (encode_compound_hdr_res(&xdr_out, &hdr_res) != 0)
@@ -703,7 +805,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
 
        while (status == 0 && nops != hdr_arg.nops) {
                status = process_op(hdr_arg.minorversion, nops, rqstp,
-                                   &xdr_in, argp, &xdr_out, resp, &drc_status);
+                                   &xdr_in, argp, &xdr_out, resp, &cps);
                nops++;
        }
 
@@ -716,6 +818,8 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
 
        *hdr_res.status = status;
        *hdr_res.nops = htonl(nops);
+       nfs4_cb_free_slot(cps.clp);
+       nfs_put_client(cps.clp);
        dprintk("%s: done, status = %u\n", __func__, ntohl(status));
        return rpc_success;
 }
@@ -739,6 +843,12 @@ static struct callback_op callback_ops[] = {
                .res_maxsize = CB_OP_RECALL_RES_MAXSZ,
        },
 #if defined(CONFIG_NFS_V4_1)
+       [OP_CB_LAYOUTRECALL] = {
+               .process_op = (callback_process_op_t)nfs4_callback_layoutrecall,
+               .decode_args =
+                       (callback_decode_arg_t)decode_layoutrecall_args,
+               .res_maxsize = CB_OP_LAYOUTRECALL_RES_MAXSZ,
+       },
        [OP_CB_SEQUENCE] = {
                .process_op = (callback_process_op_t)nfs4_callback_sequence,
                .decode_args = (callback_decode_arg_t)decode_cb_sequence_args,