Commit 84ce182a authored by Anna Schumaker's avatar Anna Schumaker
Browse files

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: default avatarAnna Schumaker <Anna.Schumaker@Netapp.com>
parent 43f0f081
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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
+69 −0
Original line number Diff line number Diff line
@@ -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