mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	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
					
				
					 4 changed files with 65 additions and 40 deletions
				
			
		| 
						 | 
				
			
			@ -84,8 +84,8 @@ int	nfsd_net_reply_cache_init(struct nfsd_net *nn);
 | 
			
		|||
void	nfsd_net_reply_cache_destroy(struct nfsd_net *nn);
 | 
			
		||||
int	nfsd_reply_cache_init(struct nfsd_net *);
 | 
			
		||||
void	nfsd_reply_cache_shutdown(struct nfsd_net *);
 | 
			
		||||
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);
 | 
			
		||||
void	nfsd_cache_update(struct svc_rqst *rqstp, struct nfsd_cacherep *rp,
 | 
			
		||||
			  int cachetype, __be32 *statp);
 | 
			
		||||
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 */
 | 
			
		||||
	drop_client(clp);
 | 
			
		||||
	return 0;
 | 
			
		||||
	return seq_release(inode, file);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * 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
 | 
			
		||||
nfsd_cache_csum(struct svc_rqst *rqstp)
 | 
			
		||||
static __wsum nfsd_cache_csum(struct xdr_buf *buf, unsigned int start,
 | 
			
		||||
			      unsigned int remaining)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int base, len;
 | 
			
		||||
	struct xdr_buf subbuf;
 | 
			
		||||
	__wsum csum = 0;
 | 
			
		||||
	void *p;
 | 
			
		||||
	int idx;
 | 
			
		||||
	unsigned int base;
 | 
			
		||||
	__wsum csum;
 | 
			
		||||
	struct xdr_buf *buf = &rqstp->rq_arg;
 | 
			
		||||
	const unsigned char *p = buf->head[0].iov_base;
 | 
			
		||||
	size_t csum_len = min_t(size_t, buf->head[0].iov_len + buf->page_len,
 | 
			
		||||
				RC_CSUMLEN);
 | 
			
		||||
	size_t len = min(buf->head[0].iov_len, csum_len);
 | 
			
		||||
 | 
			
		||||
	if (remaining > RC_CSUMLEN)
 | 
			
		||||
		remaining = RC_CSUMLEN;
 | 
			
		||||
	if (xdr_buf_subsegment(buf, &subbuf, start, remaining))
 | 
			
		||||
		return csum;
 | 
			
		||||
 | 
			
		||||
	/* rq_arg.head first */
 | 
			
		||||
	csum = csum_partial(p, len, 0);
 | 
			
		||||
	csum_len -= len;
 | 
			
		||||
	if (subbuf.head[0].iov_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 */
 | 
			
		||||
	idx = buf->page_base / PAGE_SIZE;
 | 
			
		||||
	base = buf->page_base & ~PAGE_MASK;
 | 
			
		||||
	while (csum_len) {
 | 
			
		||||
		p = page_address(buf->pages[idx]) + base;
 | 
			
		||||
		len = min_t(size_t, PAGE_SIZE - base, csum_len);
 | 
			
		||||
	idx = subbuf.page_base / PAGE_SIZE;
 | 
			
		||||
	base = subbuf.page_base & ~PAGE_MASK;
 | 
			
		||||
	while (remaining) {
 | 
			
		||||
		p = page_address(subbuf.pages[idx]) + base;
 | 
			
		||||
		len = min_t(unsigned int, PAGE_SIZE - base, remaining);
 | 
			
		||||
		csum = csum_partial(p, len, csum);
 | 
			
		||||
		csum_len -= len;
 | 
			
		||||
		remaining -= len;
 | 
			
		||||
		base = 0;
 | 
			
		||||
		++idx;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -466,6 +485,8 @@ nfsd_cache_insert(struct nfsd_drc_bucket *b, struct nfsd_cacherep *key,
 | 
			
		|||
/**
 | 
			
		||||
 * nfsd_cache_lookup - Find an entry in the duplicate reply cache
 | 
			
		||||
 * @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
 | 
			
		||||
 *
 | 
			
		||||
 * 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,
 | 
			
		|||
 *   %RC_REPLY: Reply from cache
 | 
			
		||||
 *   %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_cacherep	*rp, *found;
 | 
			
		||||
| 
						 | 
				
			
			@ -495,7 +517,7 @@ int nfsd_cache_lookup(struct svc_rqst *rqstp, struct nfsd_cacherep **cacherep)
 | 
			
		|||
		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,
 | 
			
		||||
| 
						 | 
				
			
			@ -641,24 +663,17 @@ void nfsd_cache_update(struct svc_rqst *rqstp, struct nfsd_cacherep *rp,
 | 
			
		|||
	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
 | 
			
		||||
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) {
 | 
			
		||||
		printk(KERN_WARNING "nfsd: cached reply too large (%zd).\n",
 | 
			
		||||
				data->iov_len);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	memcpy((char*)vec->iov_base + vec->iov_len, data->iov_base, data->iov_len);
 | 
			
		||||
	vec->iov_len += data->iov_len;
 | 
			
		||||
	return 1;
 | 
			
		||||
	p = xdr_reserve_space(&rqstp->rq_res_stream, data->iov_len);
 | 
			
		||||
	if (unlikely(!p))
 | 
			
		||||
		return false;
 | 
			
		||||
	memcpy(p, data->iov_base, data->iov_len);
 | 
			
		||||
	xdr_commit_encode(&rqstp->rq_res_stream);
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -981,6 +981,8 @@ int nfsd_dispatch(struct svc_rqst *rqstp)
 | 
			
		|||
	const struct svc_procedure *proc = rqstp->rq_procinfo;
 | 
			
		||||
	__be32 *statp = rqstp->rq_accept_statp;
 | 
			
		||||
	struct nfsd_cacherep *rp;
 | 
			
		||||
	unsigned int start, len;
 | 
			
		||||
	__be32 *nfs_reply;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * 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;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * ->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))
 | 
			
		||||
		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);
 | 
			
		||||
 | 
			
		||||
	rp = NULL;
 | 
			
		||||
	switch (nfsd_cache_lookup(rqstp, &rp)) {
 | 
			
		||||
	switch (nfsd_cache_lookup(rqstp, start, len, &rp)) {
 | 
			
		||||
	case RC_DOIT:
 | 
			
		||||
		break;
 | 
			
		||||
	case RC_REPLY:
 | 
			
		||||
| 
						 | 
				
			
			@ -1010,6 +1019,7 @@ int nfsd_dispatch(struct svc_rqst *rqstp)
 | 
			
		|||
		goto out_dropit;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nfs_reply = xdr_inline_decode(&rqstp->rq_res_stream, 0);
 | 
			
		||||
	*statp = proc->pc_func(rqstp);
 | 
			
		||||
	if (test_bit(RQ_DROPME, &rqstp->rq_flags))
 | 
			
		||||
		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);
 | 
			
		||||
 | 
			
		||||
	nfsd_cache_update(rqstp, rp, rqstp->rq_cachetype, statp + 1);
 | 
			
		||||
	nfsd_cache_update(rqstp, rp, rqstp->rq_cachetype, nfs_reply);
 | 
			
		||||
out_cached_reply:
 | 
			
		||||
	return 1;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue