Skip to content

Commit bb28378

Browse files
committed
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
2 parents 33b63f1 + bf51c52 commit bb28378

File tree

4 files changed

+66
-41
lines changed

4 files changed

+66
-41
lines changed

fs/nfsd/cache.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ int nfsd_net_reply_cache_init(struct nfsd_net *nn);
8484
void nfsd_net_reply_cache_destroy(struct nfsd_net *nn);
8585
int nfsd_reply_cache_init(struct nfsd_net *);
8686
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);
8989
void nfsd_cache_update(struct svc_rqst *rqstp, struct nfsd_cacherep *rp,
9090
int cachetype, __be32 *statp);
9191
int nfsd_reply_cache_stats_show(struct seq_file *m, void *v);

fs/nfsd/nfs4state.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2804,7 +2804,7 @@ static int client_opens_release(struct inode *inode, struct file *file)
28042804

28052805
/* XXX: alternatively, we could get/drop in seq start/stop */
28062806
drop_client(clp);
2807-
return 0;
2807+
return seq_release(inode, file);
28082808
}
28092809

28102810
static const struct file_operations client_states_fops = {

fs/nfsd/nfscache.c

Lines changed: 51 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -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-
*/
649666
static int
650667
nfsd_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
/*

fs/nfsd/nfssvc.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -981,13 +981,22 @@ int nfsd_dispatch(struct svc_rqst *rqstp)
981981
const struct svc_procedure *proc = rqstp->rq_procinfo;
982982
__be32 *statp = rqstp->rq_accept_statp;
983983
struct nfsd_cacherep *rp;
984+
unsigned int start, len;
985+
__be32 *nfs_reply;
984986

985987
/*
986988
* Give the xdr decoder a chance to change this if it wants
987989
* (necessary in the NFSv4.0 compound case)
988990
*/
989991
rqstp->rq_cachetype = proc->pc_cachetype;
990992

993+
/*
994+
* ->pc_decode advances the argument stream past the NFS
995+
* Call header, so grab the header's starting location and
996+
* size now for the call to nfsd_cache_lookup().
997+
*/
998+
start = xdr_stream_pos(&rqstp->rq_arg_stream);
999+
len = xdr_stream_remaining(&rqstp->rq_arg_stream);
9911000
if (!proc->pc_decode(rqstp, &rqstp->rq_arg_stream))
9921001
goto out_decode_err;
9931002

@@ -1001,7 +1010,7 @@ int nfsd_dispatch(struct svc_rqst *rqstp)
10011010
smp_store_release(&rqstp->rq_status_counter, rqstp->rq_status_counter | 1);
10021011

10031012
rp = NULL;
1004-
switch (nfsd_cache_lookup(rqstp, &rp)) {
1013+
switch (nfsd_cache_lookup(rqstp, start, len, &rp)) {
10051014
case RC_DOIT:
10061015
break;
10071016
case RC_REPLY:
@@ -1010,6 +1019,7 @@ int nfsd_dispatch(struct svc_rqst *rqstp)
10101019
goto out_dropit;
10111020
}
10121021

1022+
nfs_reply = xdr_inline_decode(&rqstp->rq_res_stream, 0);
10131023
*statp = proc->pc_func(rqstp);
10141024
if (test_bit(RQ_DROPME, &rqstp->rq_flags))
10151025
goto out_update_drop;
@@ -1023,7 +1033,7 @@ int nfsd_dispatch(struct svc_rqst *rqstp)
10231033
*/
10241034
smp_store_release(&rqstp->rq_status_counter, rqstp->rq_status_counter + 1);
10251035

1026-
nfsd_cache_update(rqstp, rp, rqstp->rq_cachetype, statp + 1);
1036+
nfsd_cache_update(rqstp, rp, rqstp->rq_cachetype, nfs_reply);
10271037
out_cached_reply:
10281038
return 1;
10291039

0 commit comments

Comments
 (0)