diff options
Diffstat (limited to 'fs/nfsd')
-rw-r--r-- | fs/nfsd/nfs4xdr.c | 51 |
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 | */ | ||
3093 | static 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 | |||
3081 | void | 3129 | void |
3082 | nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op) | 3130 | nfsd4_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; | ||
3097 | status: | 3148 | status: |
3098 | /* | 3149 | /* |
3099 | * Note: We write the status directly, instead of using WRITE32(), | 3150 | * Note: We write the status directly, instead of using WRITE32(), |