Skip to content

Commit 3e19ce7

Browse files
author
J. Bruce Fields
committed
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>
1 parent 6ac9039 commit 3e19ce7

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

include/linux/sunrpc/xdr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ typedef int (*kxdrdproc_t)(void *rqstp, struct xdr_stream *xdr, void *obj);
215215

216216
extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p);
217217
extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes);
218+
extern void xdr_truncate_encode(struct xdr_stream *xdr, size_t len);
218219
extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages,
219220
unsigned int base, unsigned int len);
220221
extern unsigned int xdr_stream_pos(const struct xdr_stream *xdr);

net/sunrpc/xdr.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,72 @@ __be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes)
508508
}
509509
EXPORT_SYMBOL_GPL(xdr_reserve_space);
510510

511+
/**
512+
* xdr_truncate_encode - truncate an encode buffer
513+
* @xdr: pointer to xdr_stream
514+
* @len: new length of buffer
515+
*
516+
* Truncates the xdr stream, so that xdr->buf->len == len,
517+
* and xdr->p points at offset len from the start of the buffer, and
518+
* head, tail, and page lengths are adjusted to correspond.
519+
*
520+
* If this means moving xdr->p to a different buffer, we assume that
521+
* that the end pointer should be set to the end of the current page,
522+
* except in the case of the head buffer when we assume the head
523+
* buffer's current length represents the end of the available buffer.
524+
*
525+
* This is *not* safe to use on a buffer that already has inlined page
526+
* cache pages (as in a zero-copy server read reply), except for the
527+
* simple case of truncating from one position in the tail to another.
528+
*
529+
*/
530+
void xdr_truncate_encode(struct xdr_stream *xdr, size_t len)
531+
{
532+
struct xdr_buf *buf = xdr->buf;
533+
struct kvec *head = buf->head;
534+
struct kvec *tail = buf->tail;
535+
int fraglen;
536+
int new, old;
537+
538+
if (len > buf->len) {
539+
WARN_ON_ONCE(1);
540+
return;
541+
}
542+
543+
fraglen = min_t(int, buf->len - len, tail->iov_len);
544+
tail->iov_len -= fraglen;
545+
buf->len -= fraglen;
546+
if (tail->iov_len && buf->len == len) {
547+
xdr->p = tail->iov_base + tail->iov_len;
548+
/* xdr->end, xdr->iov should be set already */
549+
return;
550+
}
551+
WARN_ON_ONCE(fraglen);
552+
fraglen = min_t(int, buf->len - len, buf->page_len);
553+
buf->page_len -= fraglen;
554+
buf->len -= fraglen;
555+
556+
new = buf->page_base + buf->page_len;
557+
old = new + fraglen;
558+
xdr->page_ptr -= (old >> PAGE_SHIFT) - (new >> PAGE_SHIFT);
559+
560+
if (buf->page_len && buf->len == len) {
561+
xdr->p = page_address(*xdr->page_ptr);
562+
xdr->end = (void *)xdr->p + PAGE_SIZE;
563+
xdr->p = (void *)xdr->p + (new % PAGE_SIZE);
564+
/* xdr->iov should already be NULL */
565+
return;
566+
}
567+
if (fraglen)
568+
xdr->end = head->iov_base + head->iov_len;
569+
/* (otherwise assume xdr->end is already set) */
570+
head->iov_len = len;
571+
buf->len = len;
572+
xdr->p = head->iov_base + head->iov_len;
573+
xdr->iov = buf->head;
574+
}
575+
EXPORT_SYMBOL(xdr_truncate_encode);
576+
511577
/**
512578
* xdr_write_pages - Insert a list of pages into an XDR buffer for sending
513579
* @xdr: pointer to xdr_stream

0 commit comments

Comments
 (0)