rpc: xdr_truncate_encode
This will be used in the server side in a few cases: - when certain operations (read, readdir, readlink) fail after encoding a partial response. - when we run out of space after encoding a partial response. - in readlink, where we initially reserve PAGE_SIZE bytes for data, then truncate to the actual size. Signed-off-by: J. Bruce Fields <bfields@redhat.com>
This commit is contained in:
		
					parent
					
						
							
								6ac90391c6
							
						
					
				
			
			
				commit
				
					
						3e19ce762b
					
				
			
		
					 2 changed files with 67 additions and 0 deletions
				
			
		|  | @ -215,6 +215,7 @@ typedef int	(*kxdrdproc_t)(void *rqstp, struct xdr_stream *xdr, void *obj); | ||||||
| 
 | 
 | ||||||
| extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p); | extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p); | ||||||
| extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes); | extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes); | ||||||
|  | extern void xdr_truncate_encode(struct xdr_stream *xdr, size_t len); | ||||||
| extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, | extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, | ||||||
| 		unsigned int base, unsigned int len); | 		unsigned int base, unsigned int len); | ||||||
| extern unsigned int xdr_stream_pos(const struct xdr_stream *xdr); | extern unsigned int xdr_stream_pos(const struct xdr_stream *xdr); | ||||||
|  |  | ||||||
|  | @ -508,6 +508,72 @@ __be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes) | ||||||
| } | } | ||||||
| EXPORT_SYMBOL_GPL(xdr_reserve_space); | EXPORT_SYMBOL_GPL(xdr_reserve_space); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * xdr_truncate_encode - truncate an encode buffer | ||||||
|  |  * @xdr: pointer to xdr_stream | ||||||
|  |  * @len: new length of buffer | ||||||
|  |  * | ||||||
|  |  * Truncates the xdr stream, so that xdr->buf->len == len, | ||||||
|  |  * and xdr->p points at offset len from the start of the buffer, and | ||||||
|  |  * head, tail, and page lengths are adjusted to correspond. | ||||||
|  |  * | ||||||
|  |  * If this means moving xdr->p to a different buffer, we assume that | ||||||
|  |  * that the end pointer should be set to the end of the current page, | ||||||
|  |  * except in the case of the head buffer when we assume the head | ||||||
|  |  * buffer's current length represents the end of the available buffer. | ||||||
|  |  * | ||||||
|  |  * This is *not* safe to use on a buffer that already has inlined page | ||||||
|  |  * cache pages (as in a zero-copy server read reply), except for the | ||||||
|  |  * simple case of truncating from one position in the tail to another. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | void xdr_truncate_encode(struct xdr_stream *xdr, size_t len) | ||||||
|  | { | ||||||
|  | 	struct xdr_buf *buf = xdr->buf; | ||||||
|  | 	struct kvec *head = buf->head; | ||||||
|  | 	struct kvec *tail = buf->tail; | ||||||
|  | 	int fraglen; | ||||||
|  | 	int new, old; | ||||||
|  | 
 | ||||||
|  | 	if (len > buf->len) { | ||||||
|  | 		WARN_ON_ONCE(1); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fraglen = min_t(int, buf->len - len, tail->iov_len); | ||||||
|  | 	tail->iov_len -= fraglen; | ||||||
|  | 	buf->len -= fraglen; | ||||||
|  | 	if (tail->iov_len && buf->len == len) { | ||||||
|  | 		xdr->p = tail->iov_base + tail->iov_len; | ||||||
|  | 		/* xdr->end, xdr->iov should be set already */ | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	WARN_ON_ONCE(fraglen); | ||||||
|  | 	fraglen = min_t(int, buf->len - len, buf->page_len); | ||||||
|  | 	buf->page_len -= fraglen; | ||||||
|  | 	buf->len -= fraglen; | ||||||
|  | 
 | ||||||
|  | 	new = buf->page_base + buf->page_len; | ||||||
|  | 	old = new + fraglen; | ||||||
|  | 	xdr->page_ptr -= (old >> PAGE_SHIFT) - (new >> PAGE_SHIFT); | ||||||
|  | 
 | ||||||
|  | 	if (buf->page_len && buf->len == len) { | ||||||
|  | 		xdr->p = page_address(*xdr->page_ptr); | ||||||
|  | 		xdr->end = (void *)xdr->p + PAGE_SIZE; | ||||||
|  | 		xdr->p = (void *)xdr->p + (new % PAGE_SIZE); | ||||||
|  | 		/* xdr->iov should already be NULL */ | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	if (fraglen) | ||||||
|  | 		xdr->end = head->iov_base + head->iov_len; | ||||||
|  | 	/* (otherwise assume xdr->end is already set) */ | ||||||
|  | 	head->iov_len = len; | ||||||
|  | 	buf->len = len; | ||||||
|  | 	xdr->p = head->iov_base + head->iov_len; | ||||||
|  | 	xdr->iov = buf->head; | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(xdr_truncate_encode); | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * xdr_write_pages - Insert a list of pages into an XDR buffer for sending |  * xdr_write_pages - Insert a list of pages into an XDR buffer for sending | ||||||
|  * @xdr: pointer to xdr_stream |  * @xdr: pointer to xdr_stream | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 J. Bruce Fields
				J. Bruce Fields