@@ -369,33 +369,52 @@ nfsd_reply_cache_scan(struct shrinker *shrink, struct shrink_control *sc)
369369 return freed ;
370370}
371371
372- /*
373- * Walk an xdr_buf and get a CRC for at most the first RC_CSUMLEN bytes
372+ /**
373+ * nfsd_cache_csum - Checksum incoming NFS Call arguments
374+ * @buf: buffer containing a whole RPC Call message
375+ * @start: starting byte of the NFS Call header
376+ * @remaining: size of the NFS Call header, in bytes
377+ *
378+ * Compute a weak checksum of the leading bytes of an NFS procedure
379+ * call header to help verify that a retransmitted Call matches an
380+ * entry in the duplicate reply cache.
381+ *
382+ * To avoid assumptions about how the RPC message is laid out in
383+ * @buf and what else it might contain (eg, a GSS MIC suffix), the
384+ * caller passes us the exact location and length of the NFS Call
385+ * header.
386+ *
387+ * Returns a 32-bit checksum value, as defined in RFC 793.
374388 */
375- static __wsum
376- nfsd_cache_csum ( struct svc_rqst * rqstp )
389+ static __wsum nfsd_cache_csum ( struct xdr_buf * buf , unsigned int start ,
390+ unsigned int remaining )
377391{
392+ unsigned int base , len ;
393+ struct xdr_buf subbuf ;
394+ __wsum csum = 0 ;
395+ void * p ;
378396 int idx ;
379- unsigned int base ;
380- __wsum csum ;
381- struct xdr_buf * buf = & rqstp -> rq_arg ;
382- const unsigned char * p = buf -> head [0 ].iov_base ;
383- size_t csum_len = min_t (size_t , buf -> head [0 ].iov_len + buf -> page_len ,
384- RC_CSUMLEN );
385- size_t len = min (buf -> head [0 ].iov_len , csum_len );
397+
398+ if (remaining > RC_CSUMLEN )
399+ remaining = RC_CSUMLEN ;
400+ if (xdr_buf_subsegment (buf , & subbuf , start , remaining ))
401+ return csum ;
386402
387403 /* rq_arg.head first */
388- csum = csum_partial (p , len , 0 );
389- csum_len -= len ;
404+ if (subbuf .head [0 ].iov_len ) {
405+ len = min_t (unsigned int , subbuf .head [0 ].iov_len , remaining );
406+ csum = csum_partial (subbuf .head [0 ].iov_base , len , csum );
407+ remaining -= len ;
408+ }
390409
391410 /* Continue into page array */
392- idx = buf -> page_base / PAGE_SIZE ;
393- base = buf -> page_base & ~PAGE_MASK ;
394- while (csum_len ) {
395- p = page_address (buf -> pages [idx ]) + base ;
396- len = min_t (size_t , PAGE_SIZE - base , csum_len );
411+ idx = subbuf . page_base / PAGE_SIZE ;
412+ base = subbuf . page_base & ~PAGE_MASK ;
413+ while (remaining ) {
414+ p = page_address (subbuf . pages [idx ]) + base ;
415+ len = min_t (unsigned int , PAGE_SIZE - base , remaining );
397416 csum = csum_partial (p , len , csum );
398- csum_len -= len ;
417+ remaining -= len ;
399418 base = 0 ;
400419 ++ idx ;
401420 }
@@ -466,6 +485,8 @@ nfsd_cache_insert(struct nfsd_drc_bucket *b, struct nfsd_cacherep *key,
466485/**
467486 * nfsd_cache_lookup - Find an entry in the duplicate reply cache
468487 * @rqstp: Incoming Call to find
488+ * @start: starting byte in @rqstp->rq_arg of the NFS Call header
489+ * @len: size of the NFS Call header, in bytes
469490 * @cacherep: OUT: DRC entry for this request
470491 *
471492 * Try to find an entry matching the current call in the cache. When none
@@ -479,7 +500,8 @@ nfsd_cache_insert(struct nfsd_drc_bucket *b, struct nfsd_cacherep *key,
479500 * %RC_REPLY: Reply from cache
480501 * %RC_DROPIT: Do not process the request further
481502 */
482- int nfsd_cache_lookup (struct svc_rqst * rqstp , struct nfsd_cacherep * * cacherep )
503+ int nfsd_cache_lookup (struct svc_rqst * rqstp , unsigned int start ,
504+ unsigned int len , struct nfsd_cacherep * * cacherep )
483505{
484506 struct nfsd_net * nn ;
485507 struct nfsd_cacherep * rp , * found ;
@@ -495,7 +517,7 @@ int nfsd_cache_lookup(struct svc_rqst *rqstp, struct nfsd_cacherep **cacherep)
495517 goto out ;
496518 }
497519
498- csum = nfsd_cache_csum (rqstp );
520+ csum = nfsd_cache_csum (& rqstp -> rq_arg , start , len );
499521
500522 /*
501523 * Since the common case is a cache miss followed by an insert,
@@ -641,24 +663,17 @@ void nfsd_cache_update(struct svc_rqst *rqstp, struct nfsd_cacherep *rp,
641663 return ;
642664}
643665
644- /*
645- * Copy cached reply to current reply buffer. Should always fit.
646- * FIXME as reply is in a page, we should just attach the page, and
647- * keep a refcount....
648- */
649666static int
650667nfsd_cache_append (struct svc_rqst * rqstp , struct kvec * data )
651668{
652- struct kvec * vec = & rqstp -> rq_res .head [0 ];
653-
654- if (vec -> iov_len + data -> iov_len > PAGE_SIZE ) {
655- printk (KERN_WARNING "nfsd: cached reply too large (%zd).\n" ,
656- data -> iov_len );
657- return 0 ;
658- }
659- memcpy ((char * )vec -> iov_base + vec -> iov_len , data -> iov_base , data -> iov_len );
660- vec -> iov_len += data -> iov_len ;
661- return 1 ;
669+ __be32 * p ;
670+
671+ p = xdr_reserve_space (& rqstp -> rq_res_stream , data -> iov_len );
672+ if (unlikely (!p ))
673+ return false;
674+ memcpy (p , data -> iov_base , data -> iov_len );
675+ xdr_commit_encode (& rqstp -> rq_res_stream );
676+ return true;
662677}
663678
664679/*
0 commit comments