Commit e6ac0acc authored by Anna Schumaker's avatar Anna Schumaker

SUNRPC: Add an xdr_align_data() function

For now, this function simply aligns the data at the beginning of the
pages. This can eventually be expanded to shift data to the correct
offsets when we're ready.
Signed-off-by: default avatarAnna Schumaker <Anna.Schumaker@Netapp.com>
parent c05eafad
......@@ -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_align_data(struct xdr_stream *, uint64_t, uint32_t);
extern uint64_t xdr_expand_hole(struct xdr_stream *, uint64_t, uint64_t);
/**
......
......@@ -19,6 +19,9 @@
#include <linux/bvec.h>
#include <trace/events/sunrpc.h>
static void _copy_to_pages(struct page **, size_t, const char *, size_t);
/*
* XDR functions for basic NFS types
*/
......@@ -201,6 +204,88 @@ EXPORT_SYMBOL_GPL(xdr_inline_pages);
* Helper routines for doing 'memmove' like operations on a struct xdr_buf
*/
/**
* _shift_data_left_pages
* @pages: vector of pages containing both the source and dest memory area.
* @pgto_base: page vector address of destination
* @pgfrom_base: page vector address of source
* @len: number of bytes to copy
*
* Note: the addresses pgto_base and pgfrom_base are both calculated in
* the same way:
* if a memory area starts at byte 'base' in page 'pages[i]',
* then its address is given as (i << PAGE_CACHE_SHIFT) + base
* Alse note: pgto_base must be < pgfrom_base, but the memory areas
* they point to may overlap.
*/
static void
_shift_data_left_pages(struct page **pages, size_t pgto_base,
size_t pgfrom_base, size_t len)
{
struct page **pgfrom, **pgto;
char *vfrom, *vto;
size_t copy;
BUG_ON(pgfrom_base <= pgto_base);
pgto = pages + (pgto_base >> PAGE_SHIFT);
pgfrom = pages + (pgfrom_base >> PAGE_SHIFT);
pgto_base &= ~PAGE_MASK;
pgfrom_base &= ~PAGE_MASK;
do {
if (pgto_base >= PAGE_SIZE) {
pgto_base = 0;
pgto++;
}
if (pgfrom_base >= PAGE_SIZE){
pgfrom_base = 0;
pgfrom++;
}
copy = len;
if (copy > (PAGE_SIZE - pgto_base))
copy = PAGE_SIZE - pgto_base;
if (copy > (PAGE_SIZE - pgfrom_base))
copy = PAGE_SIZE - pgfrom_base;
vto = kmap_atomic(*pgto);
if (*pgto != *pgfrom) {
vfrom = kmap_atomic(*pgfrom);
memcpy(vto + pgto_base, vfrom + pgfrom_base, copy);
kunmap_atomic(vfrom);
} else
memmove(vto + pgto_base, vto + pgfrom_base, copy);
flush_dcache_page(*pgto);
kunmap_atomic(vto);
pgto_base += copy;
pgfrom_base += copy;
} while ((len -= copy) != 0);
}
static void
_shift_data_left_tail(struct xdr_buf *buf, unsigned int pgto, size_t len)
{
struct kvec *tail = buf->tail;
if (len > tail->iov_len)
len = tail->iov_len;
_copy_to_pages(buf->pages,
buf->page_base + pgto,
(char *)tail->iov_base,
len);
tail->iov_len -= len;
if (tail->iov_len > 0)
memmove((char *)tail->iov_base,
tail->iov_base + len,
tail->iov_len);
}
/**
* _shift_data_right_pages
* @pages: vector of pages containing both the source and dest memory area.
......@@ -1128,6 +1213,42 @@ unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len)
}
EXPORT_SYMBOL_GPL(xdr_read_pages);
uint64_t xdr_align_data(struct xdr_stream *xdr, uint64_t offset, uint32_t length)
{
struct xdr_buf *buf = xdr->buf;
unsigned int from, bytes;
unsigned int shift = 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 (length < bytes)
bytes = length;
/* Move page data to the left */
if (from > offset) {
shift = min_t(unsigned int, bytes, buf->page_len - from);
_shift_data_left_pages(buf->pages,
buf->page_base + offset,
buf->page_base + from,
shift);
bytes -= shift;
/* Move tail data into the pages, if necessary */
if (bytes > 0)
_shift_data_left_tail(buf, offset + shift, bytes);
}
xdr->nwords -= XDR_QUADLEN(length);
xdr_set_page(xdr, from + length, PAGE_SIZE);
return length;
}
EXPORT_SYMBOL_GPL(xdr_align_data);
uint64_t xdr_expand_hole(struct xdr_stream *xdr, uint64_t offset, uint64_t length)
{
struct xdr_buf *buf = xdr->buf;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment