aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorAndy Adamson <andros@netapp.com>2009-04-03 01:28:48 -0400
committerJ. Bruce Fields <bfields@citi.umich.edu>2009-04-03 20:41:20 -0400
commit496c262cf01106a546ffb7df6fea84b8b881ee19 (patch)
treec1b609cd82d7f64a14eedfc308ce21bdf5b05431 /fs
parent6668958fac1d05f55420de702f3678d46c1e93a5 (diff)
nfsd41: check encode size for sessions maxresponse cached
Calculate the space the compound response has taken after encoding the current operation. pad: add on 8 bytes for the next operation's op_code and status so that there is room to cache a failure on the next operation. Compare this length to the session se_fmaxresp_cached and return nfserr_rep_too_big_to_cache if the length is too large. Our se_fmaxresp_cached will always be a multiple of PAGE_SIZE, and so will be at least a page and will therefore hold the xdr_buf head. Signed-off-by: Andy Adamson <andros@netapp.com> [nfsd41: non-page DRC for solo sequence responses] [fixed nfsd4_check_drc_limit cosmetics] Signed-off-by: Benny Halevy <bhalevy@panasas.com> [nfsd41: use cstate session in nfsd4_check_drc_limit] Signed-off-by: Andy Adamson <andros@netapp.com> Signed-off-by: Benny Halevy <bhalevy@panasas.com> Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
Diffstat (limited to 'fs')
-rw-r--r--fs/nfsd/nfs4xdr.c51
1 files changed, 51 insertions, 0 deletions
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index dd81ac1a1c6c..7fdee828f44e 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3078,6 +3078,54 @@ static nfsd4_enc nfsd4_enc_ops[] = {
3078 [OP_RECLAIM_COMPLETE] = (nfsd4_enc)nfsd4_encode_noop, 3078 [OP_RECLAIM_COMPLETE] = (nfsd4_enc)nfsd4_encode_noop,
3079}; 3079};
3080 3080
3081/*
3082 * Calculate the total amount of memory that the compound response has taken
3083 * after encoding the current operation.
3084 *
3085 * pad: add on 8 bytes for the next operation's op_code and status so that
3086 * there is room to cache a failure on the next operation.
3087 *
3088 * Compare this length to the session se_fmaxresp_cached.
3089 *
3090 * Our se_fmaxresp_cached will always be a multiple of PAGE_SIZE, and so
3091 * will be at least a page and will therefore hold the xdr_buf head.
3092 */
3093static int nfsd4_check_drc_limit(struct nfsd4_compoundres *resp)
3094{
3095 int status = 0;
3096 struct xdr_buf *xb = &resp->rqstp->rq_res;
3097 struct nfsd4_compoundargs *args = resp->rqstp->rq_argp;
3098 struct nfsd4_session *session = NULL;
3099 struct nfsd4_slot *slot = resp->cstate.slot;
3100 u32 length, tlen = 0, pad = 8;
3101
3102 if (!nfsd4_has_session(&resp->cstate))
3103 return status;
3104
3105 session = resp->cstate.session;
3106 if (session == NULL || slot->sl_cache_entry.ce_cachethis == 0)
3107 return status;
3108
3109 if (resp->opcnt >= args->opcnt)
3110 pad = 0; /* this is the last operation */
3111
3112 if (xb->page_len == 0) {
3113 length = (char *)resp->p - (char *)xb->head[0].iov_base + pad;
3114 } else {
3115 if (xb->tail[0].iov_base && xb->tail[0].iov_len > 0)
3116 tlen = (char *)resp->p - (char *)xb->tail[0].iov_base;
3117
3118 length = xb->head[0].iov_len + xb->page_len + tlen + pad;
3119 }
3120 dprintk("%s length %u, xb->page_len %u tlen %u pad %u\n", __func__,
3121 length, xb->page_len, tlen, pad);
3122
3123 if (length <= session->se_fmaxresp_cached)
3124 return status;
3125 else
3126 return nfserr_rep_too_big_to_cache;
3127}
3128
3081void 3129void
3082nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op) 3130nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
3083{ 3131{
@@ -3094,6 +3142,9 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
3094 BUG_ON(op->opnum < 0 || op->opnum >= ARRAY_SIZE(nfsd4_enc_ops) || 3142 BUG_ON(op->opnum < 0 || op->opnum >= ARRAY_SIZE(nfsd4_enc_ops) ||
3095 !nfsd4_enc_ops[op->opnum]); 3143 !nfsd4_enc_ops[op->opnum]);
3096 op->status = nfsd4_enc_ops[op->opnum](resp, op->status, &op->u); 3144 op->status = nfsd4_enc_ops[op->opnum](resp, op->status, &op->u);
3145 /* nfsd4_check_drc_limit guarantees enough room for error status */
3146 if (!op->status && nfsd4_check_drc_limit(resp))
3147 op->status = nfserr_rep_too_big_to_cache;
3097status: 3148status:
3098 /* 3149 /*
3099 * Note: We write the status directly, instead of using WRITE32(), 3150 * Note: We write the status directly, instead of using WRITE32(),