Merge branch 'bugfix' of git://git.kernel.org/pub/scm/linux/kernel/git/jeremy/xen...
[pandora-kernel.git] / fs / nfsd / nfs4callback.c
index 2509305..3fd23f7 100644 (file)
@@ -140,8 +140,10 @@ struct nfs4_cb_compound_hdr {
        int             status;
        u32             ident;
        u32             nops;
+       __be32          *nops_p;
+       u32             minorversion;
        u32             taglen;
-       char *          tag;
+       char            *tag;
 };
 
 static struct {
@@ -201,21 +203,27 @@ nfs_cb_stat_to_errno(int stat)
  * XDR encode
  */
 
-static int
+static void
 encode_cb_compound_hdr(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr)
 {
        __be32 * p;
 
        RESERVE_SPACE(16);
        WRITE32(0);            /* tag length is always 0 */
-       WRITE32(NFS4_MINOR_VERSION);
+       WRITE32(hdr->minorversion);
        WRITE32(hdr->ident);
+       hdr->nops_p = p;
        WRITE32(hdr->nops);
-       return 0;
 }
 
-static int
-encode_cb_recall(struct xdr_stream *xdr, struct nfs4_delegation *dp)
+static void encode_cb_nops(struct nfs4_cb_compound_hdr *hdr)
+{
+       *hdr->nops_p = htonl(hdr->nops);
+}
+
+static void
+encode_cb_recall(struct xdr_stream *xdr, struct nfs4_delegation *dp,
+               struct nfs4_cb_compound_hdr *hdr)
 {
        __be32 *p;
        int len = dp->dl_fh.fh_size;
@@ -224,10 +232,10 @@ encode_cb_recall(struct xdr_stream *xdr, struct nfs4_delegation *dp)
        WRITE32(OP_CB_RECALL);
        WRITE32(dp->dl_stateid.si_generation);
        WRITEMEM(&dp->dl_stateid.si_opaque, sizeof(stateid_opaque_t));
-       WRITE32(dp->dl_trunc);
+       WRITE32(0); /* truncate optimization not implemented */
        WRITE32(len);
        WRITEMEM(&dp->dl_fh.fh_base, len);
-       return 0;
+       hdr->nops++;
 }
 
 static int
@@ -246,12 +254,13 @@ nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, __be32 *p, struct nfs4_delegation *
        struct xdr_stream xdr;
        struct nfs4_cb_compound_hdr hdr = {
                .ident = args->dl_ident,
-               .nops   = 1,
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_cb_compound_hdr(&xdr, &hdr);
-       return (encode_cb_recall(&xdr, args));
+       encode_cb_recall(&xdr, args, &hdr);
+       encode_cb_nops(&hdr);
+       return 0;
 }
 
 
@@ -494,6 +503,49 @@ nfsd4_probe_callback(struct nfs4_client *clp)
        do_probe_callback(clp);
 }
 
+static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
+{
+       struct nfs4_delegation *dp = calldata;
+       struct nfs4_client *clp = dp->dl_client;
+
+       switch (task->tk_status) {
+       case -EIO:
+               /* Network partition? */
+               atomic_set(&clp->cl_cb_conn.cb_set, 0);
+               warn_no_callback_path(clp, task->tk_status);
+       case -EBADHANDLE:
+       case -NFS4ERR_BAD_STATEID:
+               /* Race: client probably got cb_recall
+                * before open reply granting delegation */
+               break;
+       default:
+               /* success, or error we can't handle */
+               return;
+       }
+       if (dp->dl_retries--) {
+               rpc_delay(task, 2*HZ);
+               task->tk_status = 0;
+               rpc_restart_call(task);
+       } else {
+               atomic_set(&clp->cl_cb_conn.cb_set, 0);
+               warn_no_callback_path(clp, task->tk_status);
+       }
+}
+
+static void nfsd4_cb_recall_release(void *calldata)
+{
+       struct nfs4_delegation *dp = calldata;
+       struct nfs4_client *clp = dp->dl_client;
+
+       nfs4_put_delegation(dp);
+       put_nfs4_client(clp);
+}
+
+static const struct rpc_call_ops nfsd4_cb_recall_ops = {
+       .rpc_call_done = nfsd4_cb_recall_done,
+       .rpc_release = nfsd4_cb_recall_release,
+};
+
 /*
  * called with dp->dl_count inc'ed.
  */
@@ -507,34 +559,13 @@ nfsd4_cb_recall(struct nfs4_delegation *dp)
                .rpc_argp = dp,
                .rpc_cred = clp->cl_cb_conn.cb_cred
        };
-       int retries = 1;
-       int status = 0;
-
-       dp->dl_trunc = 0; /* XXX need to implement truncate optimization */
-
-       status = rpc_call_sync(clnt, &msg, RPC_TASK_SOFT);
-       while (retries--) {
-               switch (status) {
-                       case -EIO:
-                               /* Network partition? */
-                               atomic_set(&clp->cl_cb_conn.cb_set, 0);
-                       case -EBADHANDLE:
-                       case -NFS4ERR_BAD_STATEID:
-                               /* Race: client probably got cb_recall
-                                * before open reply granting delegation */
-                               break;
-                       default:
-                               goto out_put_cred;
-               }
-               ssleep(2);
-               status = rpc_call_sync(clnt, &msg, RPC_TASK_SOFT);
+       int status;
+
+       dp->dl_retries = 1;
+       status = rpc_call_async(clnt, &msg, RPC_TASK_SOFT,
+                               &nfsd4_cb_recall_ops, dp);
+       if (status) {
+               put_nfs4_client(clp);
+               nfs4_put_delegation(dp);
        }
-out_put_cred:
-       /*
-        * Success or failure, now we're either waiting for lease expiration
-        * or deleg_return.
-        */
-       put_nfs4_client(clp);
-       nfs4_put_delegation(dp);
-       return;
 }