#include <linux/list.h>
#include <linux/inet.h>
#include <linux/errno.h>
+#include <linux/delay.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/clnt.h>
#define NFSPROC4_CB_COMPOUND 1
/* declarations */
-static void nfs4_cb_null(struct rpc_task *task);
-extern spinlock_t recall_lock;
+static const struct rpc_call_ops nfs4_cb_null_ops;
/* Index of predefined Linux callback client operations */
{
struct xdr_stream xdr;
struct nfs4_cb_compound_hdr hdr = {
+ .ident = args->cbr_ident,
.nops = 1,
};
if (status)
goto out;
status = decode_cb_op_hdr(&xdr, OP_CB_RECALL);
-out :
+out:
return status;
}
.p_encode = (kxdrproc_t) nfs4_xdr_##argtype, \
.p_decode = (kxdrproc_t) nfs4_xdr_##restype, \
.p_bufsiz = MAX(NFS4_##argtype##_sz,NFS4_##restype##_sz) << 2, \
+ .p_statidx = NFSPROC4_CB_##call, \
+ .p_name = #proc, \
}
-struct rpc_procinfo nfs4_cb_procedures[] = {
+static struct rpc_procinfo nfs4_cb_procedures[] = {
PROC(CB_NULL, NULL, enc_cb_null, dec_cb_null),
PROC(CB_RECALL, COMPOUND, enc_cb_recall, dec_cb_recall),
};
-struct rpc_version nfs_cb_version4 = {
+static struct rpc_version nfs_cb_version4 = {
.number = 1,
- .nrprocs = sizeof(nfs4_cb_procedures)/sizeof(nfs4_cb_procedures[0]),
+ .nrprocs = ARRAY_SIZE(nfs4_cb_procedures),
.procs = nfs4_cb_procedures
};
/*
* Use the SETCLIENTID credential
*/
-struct rpc_cred *
+static struct rpc_cred *
nfsd4_lookupcred(struct nfs4_client *clp, int taskflags)
{
struct auth_cred acred;
struct rpc_clnt *clnt = clp->cl_callback.cb_client;
- struct rpc_cred *ret = NULL;
+ struct rpc_cred *ret;
- if (!clnt)
- goto out;
get_group_info(clp->cl_cred.cr_group_info);
acred.uid = clp->cl_cred.cr_uid;
acred.gid = clp->cl_cred.cr_gid;
clnt->cl_auth->au_ops->au_name);
ret = rpcauth_lookup_credcache(clnt->cl_auth, &acred, taskflags);
put_group_info(clp->cl_cred.cr_group_info);
-out:
return ret;
}
char hostname[32];
int status;
- dprintk("NFSD: probe_callback. cb_parsed %d cb_set %d\n",
- cb->cb_parsed, atomic_read(&cb->cb_set));
- if (!cb->cb_parsed || atomic_read(&cb->cb_set))
+ if (atomic_read(&cb->cb_set))
return;
/* Initialize address */
/* Initialize timeout */
timeparms.to_initval = (NFSD_LEASE_TIME/4) * HZ;
- timeparms.to_retries = 5;
+ timeparms.to_retries = 0;
timeparms.to_maxval = (NFSD_LEASE_TIME/2) * HZ;
timeparms.to_exponential = 1;
/* Create RPC transport */
- if (!(xprt = xprt_create_proto(IPPROTO_TCP, &addr, &timeparms))) {
+ xprt = xprt_create_proto(IPPROTO_TCP, &addr, &timeparms);
+ if (IS_ERR(xprt)) {
dprintk("NFSD: couldn't create callback transport!\n");
goto out_err;
}
/* Initialize rpc_program */
program->name = "nfs4_cb";
program->number = cb->cb_prog;
- program->nrvers = sizeof(nfs_cb_version)/sizeof(nfs_cb_version[0]);
+ program->nrvers = ARRAY_SIZE(nfs_cb_version);
program->version = nfs_cb_version;
program->stats = stat;
* XXX AUTH_UNIX only - need AUTH_GSS....
*/
sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(addr.sin_addr.s_addr));
- if (!(clnt = rpc_create_client(xprt, hostname, program, 1, RPC_AUTH_UNIX))) {
+ clnt = rpc_new_client(xprt, hostname, program, 1, RPC_AUTH_UNIX);
+ if (IS_ERR(clnt)) {
dprintk("NFSD: couldn't create callback client\n");
- goto out_xprt;
+ goto out_err;
}
- clnt->cl_intr = 1;
+ clnt->cl_intr = 0;
clnt->cl_softrtry = 1;
- clnt->cl_chatty = 1;
- cb->cb_client = clnt;
/* Kick rpciod, put the call on the wire. */
goto out_clnt;
}
+ cb->cb_client = clnt;
+
/* the task holds a reference to the nfs4_client struct */
atomic_inc(&clp->cl_count);
msg.rpc_cred = nfsd4_lookupcred(clp,0);
- status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, nfs4_cb_null, NULL);
+ if (IS_ERR(msg.rpc_cred))
+ goto out_rpciod;
+ status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, &nfs4_cb_null_ops, NULL);
+ put_rpccred(msg.rpc_cred);
if (status != 0) {
dprintk("NFSD: asynchronous NFSPROC4_CB_NULL failed!\n");
return;
out_rpciod:
+ atomic_dec(&clp->cl_count);
rpciod_down();
+ cb->cb_client = NULL;
out_clnt:
rpc_shutdown_client(clnt);
- goto out_err;
-out_xprt:
- xprt_destroy(xprt);
out_err:
dprintk("NFSD: warning: no callback path to client %.*s\n",
(int)clp->cl_name.len, clp->cl_name.data);
- cb->cb_client = NULL;
}
static void
-nfs4_cb_null(struct rpc_task *task)
+nfs4_cb_null(struct rpc_task *task, void *dummy)
{
struct nfs4_client *clp = (struct nfs4_client *)task->tk_msg.rpc_argp;
struct nfs4_callback *cb = &clp->cl_callback;
put_nfs4_client(clp);
}
-/*
- * Called with dp->dl_count incremented
- */
-static void
-nfs4_cb_recall_done(struct rpc_task *task)
-{
- struct nfs4_cb_recall *cbr = (struct nfs4_cb_recall *)task->tk_calldata;
- struct nfs4_delegation *dp = cbr->cbr_dp;
- int status;
-
- /* all is well... */
- if (task->tk_status == 0)
- goto out;
-
- /* network partition, retry nfsd4_cb_recall once. */
- if (task->tk_status == -EIO) {
- if (atomic_read(&dp->dl_recall_cnt) == 0)
- goto retry;
- else
- /* callback channel no longer available */
- atomic_set(&dp->dl_client->cl_callback.cb_set, 0);
- }
-
- /* Race: a recall occurred miliseconds after a delegation was granted.
- * Client may have received recall prior to delegation. retry recall
- * once.
- */
- if ((task->tk_status == -EBADHANDLE) || (task->tk_status == -NFS4ERR_BAD_STATEID)){
- if (atomic_read(&dp->dl_recall_cnt) == 0)
- goto retry;
- }
- atomic_set(&dp->dl_state, NFS4_RECALL_COMPLETE);
-
-out:
- if (atomic_dec_and_test(&dp->dl_count))
- atomic_set(&dp->dl_state, NFS4_REAP_DELEG);
- BUG_ON(atomic_read(&dp->dl_count) < 0);
- dprintk("NFSD: nfs4_cb_recall_done: dp %p dl_flock %p dl_count %d\n",dp, dp->dl_flock, atomic_read(&dp->dl_count));
- return;
-
-retry:
- atomic_inc(&dp->dl_recall_cnt);
- /* sleep 2 seconds before retrying recall */
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(2*HZ);
- status = nfsd4_cb_recall(dp);
- dprintk("NFSD: nfs4_cb_recall_done: retry status: %d dp %p dl_flock %p\n",status,dp, dp->dl_flock);
-}
+static const struct rpc_call_ops nfs4_cb_null_ops = {
+ .rpc_call_done = nfs4_cb_null,
+};
/*
* called with dp->dl_count inc'ed.
* nfs4_lock_state() may or may not have been called.
*/
-int
+void
nfsd4_cb_recall(struct nfs4_delegation *dp)
{
- struct nfs4_client *clp;
- struct rpc_clnt *clnt;
+ struct nfs4_client *clp = dp->dl_client;
+ struct rpc_clnt *clnt = clp->cl_callback.cb_client;
+ struct nfs4_cb_recall *cbr = &dp->dl_recall;
struct rpc_message msg = {
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL],
+ .rpc_argp = cbr,
};
- struct nfs4_cb_recall *cbr = &dp->dl_recall;
- int status;
+ int retries = 1;
+ int status = 0;
- dprintk("NFSD: nfsd4_cb_recall NFS4_enc_cb_recall_sz %d NFS4_dec_cb_recall_sz %d \n",NFS4_enc_cb_recall_sz,NFS4_dec_cb_recall_sz);
-
- clp = dp->dl_client;
- clnt = clp->cl_callback.cb_client;
- status = EIO;
if ((!atomic_read(&clp->cl_callback.cb_set)) || !clnt)
- goto out_free;
+ return;
- msg.rpc_argp = cbr;
- msg.rpc_resp = cbr;
- msg.rpc_cred = nfsd4_lookupcred(clp,0);
+ msg.rpc_cred = nfsd4_lookupcred(clp, 0);
+ if (IS_ERR(msg.rpc_cred))
+ goto out;
cbr->cbr_trunc = 0; /* XXX need to implement truncate optimization */
cbr->cbr_dp = dp;
- if ((status = rpc_call_async(clnt, &msg, RPC_TASK_SOFT,
- nfs4_cb_recall_done, cbr ))) {
- dprintk("NFSD: recall_delegation: rpc_call_async failed %d\n",
- status);
- goto out_fail;
+ status = rpc_call_sync(clnt, &msg, RPC_TASK_SOFT);
+ while (retries--) {
+ switch (status) {
+ case -EIO:
+ /* Network partition? */
+ 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);
}
+out_put_cred:
+ put_rpccred(msg.rpc_cred);
out:
- return status;
-out_fail:
- status = nfserrno(status);
- out_free:
- kfree(cbr);
- goto out;
+ if (status == -EIO)
+ atomic_set(&clp->cl_callback.cb_set, 0);
+ /* Success or failure, now we're either waiting for lease expiration
+ * or deleg_return. */
+ dprintk("NFSD: nfs4_cb_recall: dp %p dl_flock %p dl_count %d\n",dp, dp->dl_flock, atomic_read(&dp->dl_count));
+ nfs4_put_delegation(dp);
+ return;
}