* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <linux/config.h>
#include <linux/module.h>
#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 */
/*
* Generic encode routines from fs/nfs/nfs4xdr.c
*/
-static inline u32 *
-xdr_writemem(u32 *p, const void *ptr, int nbytes)
+static inline __be32 *
+xdr_writemem(__be32 *p, const void *ptr, int nbytes)
{
int tmp = XDR_QUADLEN(nbytes);
if (!tmp)
#define READ_BUF(nbytes) do { \
p = xdr_inline_decode(xdr, nbytes); \
if (!p) { \
- dprintk("NFSD: %s: reply buffer overflowed in line %d.", \
+ dprintk("NFSD: %s: reply buffer overflowed in line %d.\n", \
__FUNCTION__, __LINE__); \
return -EIO; \
} \
static int
encode_cb_compound_hdr(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr)
{
- u32 * p;
+ __be32 * p;
RESERVE_SPACE(16);
WRITE32(0); /* tag length is always 0 */
static int
encode_cb_recall(struct xdr_stream *xdr, struct nfs4_cb_recall *cb_rec)
{
- u32 *p;
+ __be32 *p;
int len = cb_rec->cbr_fhlen;
RESERVE_SPACE(12+sizeof(cb_rec->cbr_stateid) + len);
}
static int
-nfs4_xdr_enc_cb_null(struct rpc_rqst *req, u32 *p)
+nfs4_xdr_enc_cb_null(struct rpc_rqst *req, __be32 *p)
{
struct xdr_stream xdrs, *xdr = &xdrs;
}
static int
-nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, u32 *p, struct nfs4_cb_recall *args)
+nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, __be32 *p, struct nfs4_cb_recall *args)
{
struct xdr_stream xdr;
struct nfs4_cb_compound_hdr hdr = {
+ .ident = args->cbr_ident,
.nops = 1,
};
static int
decode_cb_compound_hdr(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr){
- u32 *p;
+ __be32 *p;
READ_BUF(8);
READ32(hdr->status);
static int
decode_cb_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
{
- u32 *p;
+ __be32 *p;
u32 op;
int32_t nfserr;
}
static int
-nfs4_xdr_dec_cb_null(struct rpc_rqst *req, u32 *p)
+nfs4_xdr_dec_cb_null(struct rpc_rqst *req, __be32 *p)
{
return 0;
}
static int
-nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, u32 *p)
+nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, __be32 *p)
{
struct xdr_stream xdr;
struct nfs4_cb_compound_hdr hdr;
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;
}
{
struct sockaddr_in addr;
struct nfs4_callback *cb = &clp->cl_callback;
- struct rpc_timeout timeparms;
- struct rpc_xprt * xprt;
+ struct rpc_timeout timeparms = {
+ .to_initval = (NFSD_LEASE_TIME/4) * HZ,
+ .to_retries = 5,
+ .to_maxval = (NFSD_LEASE_TIME/2) * HZ,
+ .to_exponential = 1,
+ };
struct rpc_program * program = &cb->cb_program;
- struct rpc_stat * stat = &cb->cb_stat;
- struct rpc_clnt * clnt;
+ struct rpc_create_args args = {
+ .protocol = IPPROTO_TCP,
+ .address = (struct sockaddr *)&addr,
+ .addrsize = sizeof(addr),
+ .timeout = &timeparms,
+ .servername = clp->cl_name.data,
+ .program = program,
+ .version = nfs_cb_version[1]->number,
+ .authflavor = RPC_AUTH_UNIX, /* XXX: need AUTH_GSS... */
+ .flags = (RPC_CLNT_CREATE_NOPING),
+ };
struct rpc_message msg = {
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
.rpc_argp = clp,
};
- 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 */
addr.sin_port = htons(cb->cb_port);
addr.sin_addr.s_addr = htonl(cb->cb_addr);
- /* Initialize timeout */
- timeparms.to_initval = (NFSD_LEASE_TIME/4) * HZ;
- timeparms.to_retries = 5;
- timeparms.to_maxval = (NFSD_LEASE_TIME/2) * HZ;
- timeparms.to_exponential = 1;
-
- /* Create RPC transport */
- if (!(xprt = xprt_create_proto(IPPROTO_TCP, &addr, &timeparms))) {
- 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;
+ program->stats = &cb->cb_stat;
/* Initialize rpc_stat */
- memset(stat, 0, sizeof(struct rpc_stat));
- stat->program = program;
-
- /* Create RPC client
- *
- * 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))) {
+ memset(program->stats, 0, sizeof(cb->cb_stat));
+ program->stats->program = program;
+
+ /* Create RPC client */
+ cb->cb_client = rpc_create(&args);
+ if (IS_ERR(cb->cb_client)) {
dprintk("NFSD: couldn't create callback client\n");
- goto out_xprt;
+ goto out_err;
}
- clnt->cl_intr = 1;
- clnt->cl_softrtry = 1;
- clnt->cl_chatty = 1;
- cb->cb_client = clnt;
/* Kick rpciod, put the call on the wire. */
-
- if (rpciod_up() != 0) {
- dprintk("nfsd: couldn't start rpciod for callbacks!\n");
+ if (rpciod_up() != 0)
goto out_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(cb->cb_client, &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();
out_clnt:
- rpc_shutdown_client(clnt);
- goto out_err;
-out_xprt:
- xprt_destroy(xprt);
+ rpc_shutdown_client(cb->cb_client);
out_err:
+ cb->cb_client = NULL;
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;
- u32 addr = htonl(cb->cb_addr);
+ __be32 addr = htonl(cb->cb_addr);
dprintk("NFSD: nfs4_cb_null task->tk_status %d\n", task->tk_status);
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;
-
- 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);
+ int retries = 1;
+ int status = 0;
- 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;
}