rpc: xdr_truncate_encode
[deliverable/linux.git] / net / sunrpc / xdr.c
index dd97ba3c4456d174c694cbbe034c7299acd642b1..352f3b35bbe5c7e8709839b19ced9b8e9909d2d0 100644 (file)
@@ -508,6 +508,72 @@ __be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes)
 }
 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: pointer to xdr_stream
This page took 0.023969 seconds and 5 git commands to generate.