nfsd-6.7 fixes:
- Fix several long-standing bugs in the duplicate reply cache - Fix a memory leak -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEKLLlsBKG3yQ88j7+M2qzM29mf5cFAmVY6iwACgkQM2qzM29m f5fDqhAAnsHqZNG2I6asqh/5pPoqcp7kXkEe1l+6jr4jz/00R0lg+oLbsC6/S6eY tzGVkQtIkl2OpL8lt4JTgUL/xiO2JaqbdiIuHelnT62l97r7kbKWJ0ALHahLafiX hCQdJWOdud86kZ5x6/cYVfqyO08bhqDLUFvWd7zSLmzW/9U3bG4v6yXvHT3qqnnE dCJtuM9+DUfnDJKHe6+BFIobkyta8+Tpsg4QSgSAu4hg+dTcqtPCOxMeT+YwgNQd uZY1xiIjPLkufsBF86xzC3tyoFNaZc5QhIwv7ZBtmzNUw3906SbuST9hJiWeHeWq m7p0YeWDJrygiyIrvYxv6NDUCqnkoOxbuKTUAniTEj1SsE2gcQCfij0bU1OSRk6r CpT8TdJ6j2zP78+xxMsSIiA/gR7uJtCKs7LABru7DX25+sDkKK2Te9+PXINarJ1k fraeDeuXMQ1cu71WRXUJ3QKGn1/bC8DYGHFVQqcB+gVXqcm3BuKNYGt01nFyCp5s +1jL+lRxUjPydI2J1VKw5g+5jW9rBgLOT0T9xlr+TZDnYADBAakwpbujrBH5Ey+W BswGoNzqsW9B0U4N6o8OP0FA6IxcddX6Uan2czienmqj217w4AaPPT/vuA5EyC9W zNzBAi87rnaqQs4LnWiS09K+RvYIedQl1lJIzwIdQZfnMKmCSEI= =GOU4 -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
bb28378af3
@ -84,8 +84,8 @@ int nfsd_net_reply_cache_init(struct nfsd_net *nn);
|
|||||||
void nfsd_net_reply_cache_destroy(struct nfsd_net *nn);
|
void nfsd_net_reply_cache_destroy(struct nfsd_net *nn);
|
||||||
int nfsd_reply_cache_init(struct nfsd_net *);
|
int nfsd_reply_cache_init(struct nfsd_net *);
|
||||||
void nfsd_reply_cache_shutdown(struct nfsd_net *);
|
void nfsd_reply_cache_shutdown(struct nfsd_net *);
|
||||||
int nfsd_cache_lookup(struct svc_rqst *rqstp,
|
int nfsd_cache_lookup(struct svc_rqst *rqstp, unsigned int start,
|
||||||
struct nfsd_cacherep **cacherep);
|
unsigned int len, struct nfsd_cacherep **cacherep);
|
||||||
void nfsd_cache_update(struct svc_rqst *rqstp, struct nfsd_cacherep *rp,
|
void nfsd_cache_update(struct svc_rqst *rqstp, struct nfsd_cacherep *rp,
|
||||||
int cachetype, __be32 *statp);
|
int cachetype, __be32 *statp);
|
||||||
int nfsd_reply_cache_stats_show(struct seq_file *m, void *v);
|
int nfsd_reply_cache_stats_show(struct seq_file *m, void *v);
|
||||||
|
@ -2804,7 +2804,7 @@ static int client_opens_release(struct inode *inode, struct file *file)
|
|||||||
|
|
||||||
/* XXX: alternatively, we could get/drop in seq start/stop */
|
/* XXX: alternatively, we could get/drop in seq start/stop */
|
||||||
drop_client(clp);
|
drop_client(clp);
|
||||||
return 0;
|
return seq_release(inode, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct file_operations client_states_fops = {
|
static const struct file_operations client_states_fops = {
|
||||||
|
@ -369,33 +369,52 @@ nfsd_reply_cache_scan(struct shrinker *shrink, struct shrink_control *sc)
|
|||||||
return freed;
|
return freed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Walk an xdr_buf and get a CRC for at most the first RC_CSUMLEN bytes
|
* nfsd_cache_csum - Checksum incoming NFS Call arguments
|
||||||
|
* @buf: buffer containing a whole RPC Call message
|
||||||
|
* @start: starting byte of the NFS Call header
|
||||||
|
* @remaining: size of the NFS Call header, in bytes
|
||||||
|
*
|
||||||
|
* Compute a weak checksum of the leading bytes of an NFS procedure
|
||||||
|
* call header to help verify that a retransmitted Call matches an
|
||||||
|
* entry in the duplicate reply cache.
|
||||||
|
*
|
||||||
|
* To avoid assumptions about how the RPC message is laid out in
|
||||||
|
* @buf and what else it might contain (eg, a GSS MIC suffix), the
|
||||||
|
* caller passes us the exact location and length of the NFS Call
|
||||||
|
* header.
|
||||||
|
*
|
||||||
|
* Returns a 32-bit checksum value, as defined in RFC 793.
|
||||||
*/
|
*/
|
||||||
static __wsum
|
static __wsum nfsd_cache_csum(struct xdr_buf *buf, unsigned int start,
|
||||||
nfsd_cache_csum(struct svc_rqst *rqstp)
|
unsigned int remaining)
|
||||||
{
|
{
|
||||||
|
unsigned int base, len;
|
||||||
|
struct xdr_buf subbuf;
|
||||||
|
__wsum csum = 0;
|
||||||
|
void *p;
|
||||||
int idx;
|
int idx;
|
||||||
unsigned int base;
|
|
||||||
__wsum csum;
|
if (remaining > RC_CSUMLEN)
|
||||||
struct xdr_buf *buf = &rqstp->rq_arg;
|
remaining = RC_CSUMLEN;
|
||||||
const unsigned char *p = buf->head[0].iov_base;
|
if (xdr_buf_subsegment(buf, &subbuf, start, remaining))
|
||||||
size_t csum_len = min_t(size_t, buf->head[0].iov_len + buf->page_len,
|
return csum;
|
||||||
RC_CSUMLEN);
|
|
||||||
size_t len = min(buf->head[0].iov_len, csum_len);
|
|
||||||
|
|
||||||
/* rq_arg.head first */
|
/* rq_arg.head first */
|
||||||
csum = csum_partial(p, len, 0);
|
if (subbuf.head[0].iov_len) {
|
||||||
csum_len -= len;
|
len = min_t(unsigned int, subbuf.head[0].iov_len, remaining);
|
||||||
|
csum = csum_partial(subbuf.head[0].iov_base, len, csum);
|
||||||
|
remaining -= len;
|
||||||
|
}
|
||||||
|
|
||||||
/* Continue into page array */
|
/* Continue into page array */
|
||||||
idx = buf->page_base / PAGE_SIZE;
|
idx = subbuf.page_base / PAGE_SIZE;
|
||||||
base = buf->page_base & ~PAGE_MASK;
|
base = subbuf.page_base & ~PAGE_MASK;
|
||||||
while (csum_len) {
|
while (remaining) {
|
||||||
p = page_address(buf->pages[idx]) + base;
|
p = page_address(subbuf.pages[idx]) + base;
|
||||||
len = min_t(size_t, PAGE_SIZE - base, csum_len);
|
len = min_t(unsigned int, PAGE_SIZE - base, remaining);
|
||||||
csum = csum_partial(p, len, csum);
|
csum = csum_partial(p, len, csum);
|
||||||
csum_len -= len;
|
remaining -= len;
|
||||||
base = 0;
|
base = 0;
|
||||||
++idx;
|
++idx;
|
||||||
}
|
}
|
||||||
@ -466,6 +485,8 @@ out:
|
|||||||
/**
|
/**
|
||||||
* nfsd_cache_lookup - Find an entry in the duplicate reply cache
|
* nfsd_cache_lookup - Find an entry in the duplicate reply cache
|
||||||
* @rqstp: Incoming Call to find
|
* @rqstp: Incoming Call to find
|
||||||
|
* @start: starting byte in @rqstp->rq_arg of the NFS Call header
|
||||||
|
* @len: size of the NFS Call header, in bytes
|
||||||
* @cacherep: OUT: DRC entry for this request
|
* @cacherep: OUT: DRC entry for this request
|
||||||
*
|
*
|
||||||
* Try to find an entry matching the current call in the cache. When none
|
* Try to find an entry matching the current call in the cache. When none
|
||||||
@ -479,7 +500,8 @@ out:
|
|||||||
* %RC_REPLY: Reply from cache
|
* %RC_REPLY: Reply from cache
|
||||||
* %RC_DROPIT: Do not process the request further
|
* %RC_DROPIT: Do not process the request further
|
||||||
*/
|
*/
|
||||||
int nfsd_cache_lookup(struct svc_rqst *rqstp, struct nfsd_cacherep **cacherep)
|
int nfsd_cache_lookup(struct svc_rqst *rqstp, unsigned int start,
|
||||||
|
unsigned int len, struct nfsd_cacherep **cacherep)
|
||||||
{
|
{
|
||||||
struct nfsd_net *nn;
|
struct nfsd_net *nn;
|
||||||
struct nfsd_cacherep *rp, *found;
|
struct nfsd_cacherep *rp, *found;
|
||||||
@ -495,7 +517,7 @@ int nfsd_cache_lookup(struct svc_rqst *rqstp, struct nfsd_cacherep **cacherep)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
csum = nfsd_cache_csum(rqstp);
|
csum = nfsd_cache_csum(&rqstp->rq_arg, start, len);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since the common case is a cache miss followed by an insert,
|
* 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,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Copy cached reply to current reply buffer. Should always fit.
|
|
||||||
* FIXME as reply is in a page, we should just attach the page, and
|
|
||||||
* keep a refcount....
|
|
||||||
*/
|
|
||||||
static int
|
static int
|
||||||
nfsd_cache_append(struct svc_rqst *rqstp, struct kvec *data)
|
nfsd_cache_append(struct svc_rqst *rqstp, struct kvec *data)
|
||||||
{
|
{
|
||||||
struct kvec *vec = &rqstp->rq_res.head[0];
|
__be32 *p;
|
||||||
|
|
||||||
if (vec->iov_len + data->iov_len > PAGE_SIZE) {
|
p = xdr_reserve_space(&rqstp->rq_res_stream, data->iov_len);
|
||||||
printk(KERN_WARNING "nfsd: cached reply too large (%zd).\n",
|
if (unlikely(!p))
|
||||||
data->iov_len);
|
return false;
|
||||||
return 0;
|
memcpy(p, data->iov_base, data->iov_len);
|
||||||
}
|
xdr_commit_encode(&rqstp->rq_res_stream);
|
||||||
memcpy((char*)vec->iov_base + vec->iov_len, data->iov_base, data->iov_len);
|
return true;
|
||||||
vec->iov_len += data->iov_len;
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -981,6 +981,8 @@ int nfsd_dispatch(struct svc_rqst *rqstp)
|
|||||||
const struct svc_procedure *proc = rqstp->rq_procinfo;
|
const struct svc_procedure *proc = rqstp->rq_procinfo;
|
||||||
__be32 *statp = rqstp->rq_accept_statp;
|
__be32 *statp = rqstp->rq_accept_statp;
|
||||||
struct nfsd_cacherep *rp;
|
struct nfsd_cacherep *rp;
|
||||||
|
unsigned int start, len;
|
||||||
|
__be32 *nfs_reply;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Give the xdr decoder a chance to change this if it wants
|
* Give the xdr decoder a chance to change this if it wants
|
||||||
@ -988,6 +990,13 @@ int nfsd_dispatch(struct svc_rqst *rqstp)
|
|||||||
*/
|
*/
|
||||||
rqstp->rq_cachetype = proc->pc_cachetype;
|
rqstp->rq_cachetype = proc->pc_cachetype;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ->pc_decode advances the argument stream past the NFS
|
||||||
|
* Call header, so grab the header's starting location and
|
||||||
|
* size now for the call to nfsd_cache_lookup().
|
||||||
|
*/
|
||||||
|
start = xdr_stream_pos(&rqstp->rq_arg_stream);
|
||||||
|
len = xdr_stream_remaining(&rqstp->rq_arg_stream);
|
||||||
if (!proc->pc_decode(rqstp, &rqstp->rq_arg_stream))
|
if (!proc->pc_decode(rqstp, &rqstp->rq_arg_stream))
|
||||||
goto out_decode_err;
|
goto out_decode_err;
|
||||||
|
|
||||||
@ -1001,7 +1010,7 @@ int nfsd_dispatch(struct svc_rqst *rqstp)
|
|||||||
smp_store_release(&rqstp->rq_status_counter, rqstp->rq_status_counter | 1);
|
smp_store_release(&rqstp->rq_status_counter, rqstp->rq_status_counter | 1);
|
||||||
|
|
||||||
rp = NULL;
|
rp = NULL;
|
||||||
switch (nfsd_cache_lookup(rqstp, &rp)) {
|
switch (nfsd_cache_lookup(rqstp, start, len, &rp)) {
|
||||||
case RC_DOIT:
|
case RC_DOIT:
|
||||||
break;
|
break;
|
||||||
case RC_REPLY:
|
case RC_REPLY:
|
||||||
@ -1010,6 +1019,7 @@ int nfsd_dispatch(struct svc_rqst *rqstp)
|
|||||||
goto out_dropit;
|
goto out_dropit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nfs_reply = xdr_inline_decode(&rqstp->rq_res_stream, 0);
|
||||||
*statp = proc->pc_func(rqstp);
|
*statp = proc->pc_func(rqstp);
|
||||||
if (test_bit(RQ_DROPME, &rqstp->rq_flags))
|
if (test_bit(RQ_DROPME, &rqstp->rq_flags))
|
||||||
goto out_update_drop;
|
goto out_update_drop;
|
||||||
@ -1023,7 +1033,7 @@ int nfsd_dispatch(struct svc_rqst *rqstp)
|
|||||||
*/
|
*/
|
||||||
smp_store_release(&rqstp->rq_status_counter, rqstp->rq_status_counter + 1);
|
smp_store_release(&rqstp->rq_status_counter, rqstp->rq_status_counter + 1);
|
||||||
|
|
||||||
nfsd_cache_update(rqstp, rp, rqstp->rq_cachetype, statp + 1);
|
nfsd_cache_update(rqstp, rp, rqstp->rq_cachetype, nfs_reply);
|
||||||
out_cached_reply:
|
out_cached_reply:
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user