SUNRPC: Add the ability to expand holes in data pages

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>
This commit is contained in:
Anna Schumaker 2014-05-28 13:38:53 -04:00
parent 43f0f0816c
commit 84ce182ab8
2 changed files with 70 additions and 0 deletions

View 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

View 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