Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'nfsd-6.7-1' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux

Pull nfsd fixes from Chuck Lever:

- Fix several long-standing bugs in the duplicate reply cache

- Fix a memory leak

* tag 'nfsd-6.7-1' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux:
NFSD: Fix checksum mismatches in the duplicate reply cache
NFSD: Fix "start of NFS reply" pointer passed to nfsd_cache_update()
NFSD: Update nfsd_cache_append() to use xdr_stream
nfsd: fix file memleak on client_opens_release

+65 -40
+2 -2
fs/nfsd/cache.h
··· 84 84 void nfsd_net_reply_cache_destroy(struct nfsd_net *nn); 85 85 int nfsd_reply_cache_init(struct nfsd_net *); 86 86 void nfsd_reply_cache_shutdown(struct nfsd_net *); 87 - int nfsd_cache_lookup(struct svc_rqst *rqstp, 88 - struct nfsd_cacherep **cacherep); 87 + int nfsd_cache_lookup(struct svc_rqst *rqstp, unsigned int start, 88 + unsigned int len, struct nfsd_cacherep **cacherep); 89 89 void nfsd_cache_update(struct svc_rqst *rqstp, struct nfsd_cacherep *rp, 90 90 int cachetype, __be32 *statp); 91 91 int nfsd_reply_cache_stats_show(struct seq_file *m, void *v);
+1 -1
fs/nfsd/nfs4state.c
··· 2804 2804 2805 2805 /* XXX: alternatively, we could get/drop in seq start/stop */ 2806 2806 drop_client(clp); 2807 - return 0; 2807 + return seq_release(inode, file); 2808 2808 } 2809 2809 2810 2810 static const struct file_operations client_states_fops = {
+50 -35
fs/nfsd/nfscache.c
··· 369 369 return freed; 370 370 } 371 371 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. 374 388 */ 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) 377 391 { 392 + unsigned int base, len; 393 + struct xdr_buf subbuf; 394 + __wsum csum = 0; 395 + void *p; 378 396 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; 386 402 387 403 /* 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 + } 390 409 391 410 /* 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); 397 416 csum = csum_partial(p, len, csum); 398 - csum_len -= len; 417 + remaining -= len; 399 418 base = 0; 400 419 ++idx; 401 420 } ··· 485 466 /** 486 467 * nfsd_cache_lookup - Find an entry in the duplicate reply cache 487 468 * @rqstp: Incoming Call to find 469 + * @start: starting byte in @rqstp->rq_arg of the NFS Call header 470 + * @len: size of the NFS Call header, in bytes 488 471 * @cacherep: OUT: DRC entry for this request 489 472 * 490 473 * Try to find an entry matching the current call in the cache. When none ··· 500 479 * %RC_REPLY: Reply from cache 501 480 * %RC_DROPIT: Do not process the request further 502 481 */ 503 - int nfsd_cache_lookup(struct svc_rqst *rqstp, struct nfsd_cacherep **cacherep) 482 + int nfsd_cache_lookup(struct svc_rqst *rqstp, unsigned int start, 483 + unsigned int len, struct nfsd_cacherep **cacherep) 504 484 { 505 485 struct nfsd_net *nn; 506 486 struct nfsd_cacherep *rp, *found; ··· 517 495 goto out; 518 496 } 519 497 520 - csum = nfsd_cache_csum(rqstp); 498 + csum = nfsd_cache_csum(&rqstp->rq_arg, start, len); 521 499 522 500 /* 523 501 * Since the common case is a cache miss followed by an insert, ··· 663 641 return; 664 642 } 665 643 666 - /* 667 - * Copy cached reply to current reply buffer. Should always fit. 668 - * FIXME as reply is in a page, we should just attach the page, and 669 - * keep a refcount.... 670 - */ 671 644 static int 672 645 nfsd_cache_append(struct svc_rqst *rqstp, struct kvec *data) 673 646 { 674 - struct kvec *vec = &rqstp->rq_res.head[0]; 647 + __be32 *p; 675 648 676 - if (vec->iov_len + data->iov_len > PAGE_SIZE) { 677 - printk(KERN_WARNING "nfsd: cached reply too large (%zd).\n", 678 - data->iov_len); 679 - return 0; 680 - } 681 - memcpy((char*)vec->iov_base + vec->iov_len, data->iov_base, data->iov_len); 682 - vec->iov_len += data->iov_len; 683 - return 1; 649 + p = xdr_reserve_space(&rqstp->rq_res_stream, data->iov_len); 650 + if (unlikely(!p)) 651 + return false; 652 + memcpy(p, data->iov_base, data->iov_len); 653 + xdr_commit_encode(&rqstp->rq_res_stream); 654 + return true; 684 655 } 685 656 686 657 /*
+12 -2
fs/nfsd/nfssvc.c
··· 981 981 const struct svc_procedure *proc = rqstp->rq_procinfo; 982 982 __be32 *statp = rqstp->rq_accept_statp; 983 983 struct nfsd_cacherep *rp; 984 + unsigned int start, len; 985 + __be32 *nfs_reply; 984 986 985 987 /* 986 988 * Give the xdr decoder a chance to change this if it wants ··· 990 988 */ 991 989 rqstp->rq_cachetype = proc->pc_cachetype; 992 990 991 + /* 992 + * ->pc_decode advances the argument stream past the NFS 993 + * Call header, so grab the header's starting location and 994 + * size now for the call to nfsd_cache_lookup(). 995 + */ 996 + start = xdr_stream_pos(&rqstp->rq_arg_stream); 997 + len = xdr_stream_remaining(&rqstp->rq_arg_stream); 993 998 if (!proc->pc_decode(rqstp, &rqstp->rq_arg_stream)) 994 999 goto out_decode_err; 995 1000 ··· 1010 1001 smp_store_release(&rqstp->rq_status_counter, rqstp->rq_status_counter | 1); 1011 1002 1012 1003 rp = NULL; 1013 - switch (nfsd_cache_lookup(rqstp, &rp)) { 1004 + switch (nfsd_cache_lookup(rqstp, start, len, &rp)) { 1014 1005 case RC_DOIT: 1015 1006 break; 1016 1007 case RC_REPLY: ··· 1019 1010 goto out_dropit; 1020 1011 } 1021 1012 1013 + nfs_reply = xdr_inline_decode(&rqstp->rq_res_stream, 0); 1022 1014 *statp = proc->pc_func(rqstp); 1023 1015 if (test_bit(RQ_DROPME, &rqstp->rq_flags)) 1024 1016 goto out_update_drop; ··· 1033 1023 */ 1034 1024 smp_store_release(&rqstp->rq_status_counter, rqstp->rq_status_counter + 1); 1035 1025 1036 - nfsd_cache_update(rqstp, rp, rqstp->rq_cachetype, statp + 1); 1026 + nfsd_cache_update(rqstp, rp, rqstp->rq_cachetype, nfs_reply); 1037 1027 out_cached_reply: 1038 1028 return 1; 1039 1029