SUNRPC: Add the ability to expand holes in data pages
authorAnna Schumaker <Anna.Schumaker@Netapp.com>
Wed, 28 May 2014 17:38:53 +0000 (13:38 -0400)
committerAnna Schumaker <Anna.Schumaker@Netapp.com>
Wed, 7 Oct 2020 18:28:39 +0000 (14:28 -0400)
This patch adds the ability to "read a hole" into a set of XDR data
pages by taking the following steps:

1) Shift all data after the current xdr->p to the right, possibly into
   the tail,
2) Zero the specified range, and
3) Update xdr->p to point beyond the hole.

Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
include/linux/sunrpc/xdr.h
net/sunrpc/xdr.c

index 25a68dd87ecfc1d6afa58014b6ef2fb77344c689..f9636d2a6d54e1543267eb1b6edcb11406d0de46 100644 (file)
@@ -250,6 +250,7 @@ extern __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes);
 extern unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len);
 extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len);
 extern int xdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len, int (*actor)(struct scatterlist *, void *), void *data);
+extern uint64_t xdr_expand_hole(struct xdr_stream *, uint64_t, uint64_t);
 
 /**
  * xdr_stream_remaining - Return the number of bytes remaining in the stream
index 10a88a67206a5633d7b1f5c5db635ac83124b49d..1052ccdb4e996720eb777ae1f4b88e85f5166b2a 100644 (file)
@@ -390,6 +390,38 @@ _copy_from_pages(char *p, struct page **pages, size_t pgbase, size_t len)
 }
 EXPORT_SYMBOL_GPL(_copy_from_pages);
 
+/**
+ * _zero_pages
+ * @pages: array of pages
+ * @pgbase: beginning page vector address
+ * @len: length
+ */
+static void
+_zero_pages(struct page **pages, size_t pgbase, size_t len)
+{
+       struct page **page;
+       char *vpage;
+       size_t zero;
+
+       page = pages + (pgbase >> PAGE_SHIFT);
+       pgbase &= ~PAGE_MASK;
+
+       do {
+               zero = PAGE_SIZE - pgbase;
+               if (zero > len)
+                       zero = len;
+
+               vpage = kmap_atomic(*page);
+               memset(vpage + pgbase, 0, zero);
+               kunmap_atomic(vpage);
+
+               flush_dcache_page(*page);
+               pgbase = 0;
+               page++;
+
+       } while ((len -= zero) != 0);
+}
+
 /**
  * xdr_shrink_bufhead
  * @buf: xdr_buf
@@ -1096,6 +1128,43 @@ unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len)
 }
 EXPORT_SYMBOL_GPL(xdr_read_pages);
 
+uint64_t xdr_expand_hole(struct xdr_stream *xdr, uint64_t offset, uint64_t length)
+{
+       struct xdr_buf *buf = xdr->buf;
+       unsigned int bytes;
+       unsigned int from;
+       unsigned int truncated = 0;
+
+       if ((offset + length) < offset ||
+           (offset + length) > buf->page_len)
+               length = buf->page_len - offset;
+
+       xdr_realign_pages(xdr);
+       from = xdr_page_pos(xdr);
+       bytes = xdr->nwords << 2;
+
+       if (offset + length + bytes > buf->page_len) {
+               unsigned int shift = (offset + length + bytes) - buf->page_len;
+               unsigned int res = _shift_data_right_tail(buf, from + bytes - shift, shift);
+               truncated = shift - res;
+               xdr->nwords -= XDR_QUADLEN(truncated);
+               bytes -= shift;
+       }
+
+       /* Now move the page data over and zero pages */
+       if (bytes > 0)
+               _shift_data_right_pages(buf->pages,
+                                       buf->page_base + offset + length,
+                                       buf->page_base + from,
+                                       bytes);
+       _zero_pages(buf->pages, buf->page_base + offset, length);
+
+       buf->len += length - (from - offset) - truncated;
+       xdr_set_page(xdr, offset + length, PAGE_SIZE);
+       return length;
+}
+EXPORT_SYMBOL_GPL(xdr_expand_hole);
+
 /**
  * xdr_enter_page - decode data from the XDR page
  * @xdr: pointer to xdr_stream struct