summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/nfsd/nfs4proc.c13
-rw-r--r--fs/nfsd/nfs4state.c35
-rw-r--r--fs/nfsd/nfs4xdr.c65
-rw-r--r--include/linux/nfsd/state.h22
-rw-r--r--include/linux/nfsd/xdr4.h2
5 files changed, 111 insertions, 26 deletions
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 80d06158a78d..7d70b85b7f7f 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -664,6 +664,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
break;
case OP_CLOSE:
op->status = nfsd4_close(rqstp, &current_fh, &op->u.close);
+ op->replay = &op->u.close.cl_stateowner->so_replay;
break;
case OP_COMMIT:
op->status = nfsd4_commit(rqstp, &current_fh, &op->u.commit);
@@ -693,12 +694,15 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
break;
case OP_OPEN:
op->status = nfsd4_open(rqstp, &current_fh, &op->u.open);
+ op->replay = &op->u.open.op_stateowner->so_replay;
break;
case OP_OPEN_CONFIRM:
op->status = nfsd4_open_confirm(rqstp, &current_fh, &op->u.open_confirm);
+ op->replay = &op->u.open_confirm.oc_stateowner->so_replay;
break;
case OP_OPEN_DOWNGRADE:
op->status = nfsd4_open_downgrade(rqstp, &current_fh, &op->u.open_downgrade);
+ op->replay = &op->u.open_downgrade.od_stateowner->so_replay;
break;
case OP_PUTFH:
op->status = nfsd4_putfh(rqstp, &current_fh, &op->u.putfh);
@@ -753,8 +757,13 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
}
encode_op:
- nfsd4_encode_operation(resp, op);
- status = op->status;
+ if (op->status == NFSERR_REPLAY_ME) {
+ nfsd4_encode_replay(resp, op);
+ status = op->status = NFS_OK;
+ } else {
+ nfsd4_encode_operation(resp, op);
+ status = op->status;
+ }
}
out:
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index ba2fdbd12dbc..f1b5f1a33466 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -759,6 +759,7 @@ free_stateowner(struct nfs4_stateowner *sop) {
static struct nfs4_stateowner *
alloc_init_stateowner(unsigned int strhashval, struct nfs4_client *clp, struct nfsd4_open *open) {
struct nfs4_stateowner *sop;
+ struct nfs4_replay *rp;
unsigned int idhashval;
if (!(sop = alloc_stateowner(&open->op_owner)))
@@ -776,6 +777,10 @@ alloc_init_stateowner(unsigned int strhashval, struct nfs4_client *clp, struct n
sop->so_client = clp;
sop->so_seqid = open->op_seqid;
sop->so_confirmed = 0;
+ rp = &sop->so_replay;
+ rp->rp_status = NFSERR_SERVERFAULT;
+ rp->rp_buflen = 0;
+ rp->rp_buf = rp->rp_ibuf;
alloc_sowner++;
return sop;
}
@@ -1019,9 +1024,22 @@ nfsd4_process_open1(struct nfsd4_open *open)
strhashval = ownerstr_hashval(clientid->cl_id, open->op_owner);
if (find_stateowner_str(strhashval, open, &sop)) {
open->op_stateowner = sop;
+ /* check for replay */
if (open->op_seqid == sop->so_seqid){
- /* XXX retplay: for now, return bad seqid */
- status = nfserr_bad_seqid;
+ if (!sop->so_replay.rp_buflen) {
+ /*
+ * The original OPEN failed in so spectacularly that we
+ * don't even have replay data saved! Therefore, we
+ * have no choice but to continue processing
+ * this OPEN; presumably, we'll fail again for the same
+ * reason.
+ */
+ dprintk("nfsd4_process_open1: replay with no replay cache\n");
+ status = NFS_OK;
+ goto renew;
+ }
+ /* replay: indicate to calling function */
+ status = NFSERR_REPLAY_ME;
goto out;
}
if (sop->so_confirmed) {
@@ -1033,9 +1051,8 @@ nfsd4_process_open1(struct nfsd4_open *open)
goto out;
}
/* If we get here, we received and OPEN for an unconfirmed
- * nfs4_stateowner. If seqid's are the same then this
- * is a replay.
- * If the sequid's are different, then purge the
+ * nfs4_stateowner.
+ * Since the sequid's are different, purge the
* existing nfs4_stateowner, and instantiate a new one.
*/
clp = sop->so_client;
@@ -1367,8 +1384,6 @@ out:
/*
* Checks for sequence id mutating operations.
- *
- * XXX need to code replay cache logic
*/
int
nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *stateid, int flags, struct nfs4_stateowner **sopp, struct nfs4_stateid **stpp)
@@ -1466,13 +1481,14 @@ no_nfs4_stateid:
}
check_replay:
- status = nfserr_bad_seqid;
if (seqid == sop->so_seqid) {
printk("NFSD: preprocess_seqid_op: retransmission?\n");
- /* XXX will need to indicate replay to calling function here */
+ /* indicate replay to calling function */
+ status = NFSERR_REPLAY_ME;
} else
printk("NFSD: preprocess_seqid_op: bad seqid (expected %d, got %d\n", sop->so_seqid +1, seqid);
+ status = nfserr_bad_seqid;
goto out;
}
@@ -1499,7 +1515,6 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfs
sop->so_confirmed = 1;
update_stateid(&stp->st_stateid);
memcpy(&oc->oc_resp_stateid, &stp->st_stateid, sizeof(stateid_t));
- /* XXX renew the client lease here */
dprintk("NFSD: nfsd4_open_confirm: success, seqid=%d "
"stateid=(%08x/%08x/%08x/%08x)\n", oc->oc_seqid,
stp->st_stateid.si_boot,
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 371f515f9143..bdf83155c625 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -932,6 +932,7 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
for (i = 0; i < argp->opcnt; i++) {
op = &argp->ops[i];
+ op->replay = NULL;
/*
* We can't use READ_BUF() here because we need to handle
@@ -1111,18 +1112,31 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
#define ADJUST_ARGS() resp->p = p
/*
+ * Header routine to setup seqid operation replay cache
+ */
+#define ENCODE_SEQID_OP_HEAD \
+ u32 *p; \
+ u32 *save; \
+ \
+ save = resp->p;
+
+/*
* Routine for encoding the result of a
* "seqid-mutating" NFSv4 operation. This is
- * where seqids are incremented
+ * where seqids are incremented, and the
+ * replay cache is filled.
*/
-#define ENCODE_SEQID_OP_TAIL(stateowner) do { \
- BUG_ON(!stateowner); \
- if (seqid_mutating_err(nfserr) && stateowner) { \
- if (stateowner->so_confirmed) \
- stateowner->so_seqid++; \
- } \
-} while(0)
+#define ENCODE_SEQID_OP_TAIL(stateowner) do { \
+ if (seqid_mutating_err(nfserr) && stateowner) { \
+ if (stateowner->so_confirmed) \
+ stateowner->so_seqid++; \
+ stateowner->so_replay.rp_status = nfserr; \
+ stateowner->so_replay.rp_buflen = \
+ (((char *)(resp)->p - (char *)save)); \
+ memcpy(stateowner->so_replay.rp_buf, save, \
+ stateowner->so_replay.rp_buflen); \
+ } } while(0)
static u32 nfs4_ftypes[16] = {
@@ -1623,7 +1637,7 @@ nfsd4_encode_access(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_acc
static void
nfsd4_encode_close(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_close *close)
{
- ENCODE_HEAD;
+ ENCODE_SEQID_OP_HEAD;
if (!nfserr) {
RESERVE_SPACE(sizeof(stateid_t));
@@ -1631,8 +1645,7 @@ nfsd4_encode_close(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_clos
WRITEMEM(&close->cl_stateid.si_opaque, sizeof(stateid_opaque_t));
ADJUST_ARGS();
}
- if ((close->cl_stateowner) && (close->cl_stateowner->so_confirmed))
- close->cl_stateowner->so_seqid++;
+ ENCODE_SEQID_OP_TAIL(close->cl_stateowner);
}
@@ -1712,7 +1725,7 @@ nfsd4_encode_link(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_link
static void
nfsd4_encode_open(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_open *open)
{
- ENCODE_HEAD;
+ ENCODE_SEQID_OP_HEAD;
if (nfserr)
return;
@@ -1776,7 +1789,7 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_open
static void
nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_open_confirm *oc)
{
- ENCODE_HEAD;
+ ENCODE_SEQID_OP_HEAD;
if (!nfserr) {
RESERVE_SPACE(sizeof(stateid_t));
@@ -1791,7 +1804,7 @@ nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, int nfserr, struct nfs
static void
nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_open_downgrade *od)
{
- ENCODE_HEAD;
+ ENCODE_SEQID_OP_HEAD;
if (!nfserr) {
RESERVE_SPACE(sizeof(stateid_t));
@@ -2170,6 +2183,30 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
*statp = op->status;
}
+/*
+ * Encode the reply stored in the stateowner reply cache
+ *
+ * XDR note: do not encode rp->rp_buflen: the buffer contains the
+ * previously sent already encoded operation.
+ */
+void
+nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
+{
+ ENCODE_HEAD;
+ struct nfs4_replay *rp = op->replay;
+
+ BUG_ON(!rp);
+
+ RESERVE_SPACE(8);
+ WRITE32(op->opnum);
+ WRITE32(NFS_OK);
+ ADJUST_ARGS();
+
+ RESERVE_SPACE(rp->rp_buflen);
+ WRITEMEM(rp->rp_buf, rp->rp_buflen);
+ ADJUST_ARGS();
+}
+
/*
* END OF "GENERIC" ENCODE ROUTINES.
*/
diff --git a/include/linux/nfsd/state.h b/include/linux/nfsd/state.h
index 66422a1c8277..b4d10b7dcc16 100644
--- a/include/linux/nfsd/state.h
+++ b/include/linux/nfsd/state.h
@@ -95,6 +95,27 @@ update_stateid(stateid_t *stateid)
stateid->si_generation++;
}
+/* A reasonable value for REPLAY_ISIZE was estimated as follows:
+ * The OPEN response, typically the largest, requires
+ * 4(status) + 8(stateid) + 20(changeinfo) + 4(rflags) + 8(verifier) +
+ * 4(deleg. type) + 8(deleg. stateid) + 4(deleg. recall flag) +
+ * 20(deleg. space limit) + ~32(deleg. ace) = 112 bytes
+ */
+
+#define NFSD4_REPLAY_ISIZE 112
+
+/*
+ * Replay buffer, where the result of the last seqid-mutating operation
+ * is cached.
+ */
+struct nfs4_replay {
+ u32 rp_status;
+ unsigned int rp_buflen;
+ char *rp_buf;
+ unsigned intrp_allocated;
+ char rp_ibuf[NFSD4_REPLAY_ISIZE];
+};
+
/*
* nfs4_stateowner can either be an open_owner, or (eventually) a lock_owner
*
@@ -111,6 +132,7 @@ struct nfs4_stateowner {
u32 so_seqid;
struct xdr_netobj so_owner; /* open owner name */
int so_confirmed; /* successful OPEN_CONFIRM? */
+ struct nfs4_replay so_replay;
};
/*
diff --git a/include/linux/nfsd/xdr4.h b/include/linux/nfsd/xdr4.h
index 54c3049f6068..254d5d2c5647 100644
--- a/include/linux/nfsd/xdr4.h
+++ b/include/linux/nfsd/xdr4.h
@@ -284,6 +284,7 @@ struct nfsd4_op {
struct nfsd4_verify verify;
struct nfsd4_write write;
} u;
+ struct nfs4_replay * replay;
};
struct nfsd4_compoundargs {
@@ -339,6 +340,7 @@ int nfs4svc_decode_compoundargs(struct svc_rqst *, u32 *,
int nfs4svc_encode_compoundres(struct svc_rqst *, u32 *,
struct nfsd4_compoundres *);
void nfsd4_encode_operation(struct nfsd4_compoundres *, struct nfsd4_op *);
+void nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op);
int nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
struct dentry *dentry, u32 *buffer, int *countp,
u32 *bmval);