Commit 7be9cea3 authored by Chuck Lever's avatar Chuck Lever Committed by Anna Schumaker

SUNRPC: Add trace event that reports reply page vector alignment

We don't want READ payloads that are partially in the head iovec and
in the page buffer because this requires pull-up, which can be
expensive.

The NFS/RPC client tries hard to predict the size of the head iovec
so that the incoming READ data payload lands only in the page
vector, but it doesn't always get it right. To help diagnose such
problems, add a trace point in the logic that decodes READ-like
operations that reports whether pull-up is being done.
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
Signed-off-by: default avatarAnna Schumaker <Anna.Schumaker@Netapp.com>
parent 5582863f
...@@ -321,6 +321,65 @@ TRACE_EVENT(rpc_xdr_overflow, ...@@ -321,6 +321,65 @@ TRACE_EVENT(rpc_xdr_overflow,
) )
); );
TRACE_EVENT(rpc_xdr_alignment,
TP_PROTO(
const struct xdr_stream *xdr,
size_t offset,
unsigned int copied
),
TP_ARGS(xdr, offset, copied),
TP_STRUCT__entry(
__field(unsigned int, task_id)
__field(unsigned int, client_id)
__field(int, version)
__field(size_t, offset)
__field(unsigned int, copied)
__field(const void *, head_base)
__field(size_t, head_len)
__field(const void *, tail_base)
__field(size_t, tail_len)
__field(unsigned int, page_len)
__field(unsigned int, len)
__string(progname,
xdr->rqst->rq_task->tk_client->cl_program->name)
__string(procedure,
xdr->rqst->rq_task->tk_msg.rpc_proc->p_name)
),
TP_fast_assign(
const struct rpc_task *task = xdr->rqst->rq_task;
__entry->task_id = task->tk_pid;
__entry->client_id = task->tk_client->cl_clid;
__assign_str(progname,
task->tk_client->cl_program->name)
__entry->version = task->tk_client->cl_vers;
__assign_str(procedure, task->tk_msg.rpc_proc->p_name)
__entry->offset = offset;
__entry->copied = copied;
__entry->head_base = xdr->buf->head[0].iov_base,
__entry->head_len = xdr->buf->head[0].iov_len,
__entry->page_len = xdr->buf->page_len,
__entry->tail_base = xdr->buf->tail[0].iov_base,
__entry->tail_len = xdr->buf->tail[0].iov_len,
__entry->len = xdr->buf->len;
),
TP_printk(
"task:%u@%u %sv%d %s offset=%zu copied=%u xdr=[%p,%zu]/%u/[%p,%zu]/%u\n",
__entry->task_id, __entry->client_id,
__get_str(progname), __entry->version, __get_str(procedure),
__entry->offset, __entry->copied,
__entry->head_base, __entry->head_len,
__entry->page_len,
__entry->tail_base, __entry->tail_len,
__entry->len
)
);
/* /*
* First define the enums in the below macros to be exported to userspace * First define the enums in the below macros to be exported to userspace
* via TRACE_DEFINE_ENUM(). * via TRACE_DEFINE_ENUM().
......
...@@ -347,13 +347,15 @@ EXPORT_SYMBOL_GPL(_copy_from_pages); ...@@ -347,13 +347,15 @@ EXPORT_SYMBOL_GPL(_copy_from_pages);
* 'len' bytes. The extra data is not lost, but is instead * 'len' bytes. The extra data is not lost, but is instead
* moved into the inlined pages and/or the tail. * moved into the inlined pages and/or the tail.
*/ */
static void static unsigned int
xdr_shrink_bufhead(struct xdr_buf *buf, size_t len) xdr_shrink_bufhead(struct xdr_buf *buf, size_t len)
{ {
struct kvec *head, *tail; struct kvec *head, *tail;
size_t copy, offs; size_t copy, offs;
unsigned int pglen = buf->page_len; unsigned int pglen = buf->page_len;
unsigned int result;
result = 0;
tail = buf->tail; tail = buf->tail;
head = buf->head; head = buf->head;
...@@ -367,6 +369,7 @@ xdr_shrink_bufhead(struct xdr_buf *buf, size_t len) ...@@ -367,6 +369,7 @@ xdr_shrink_bufhead(struct xdr_buf *buf, size_t len)
copy = tail->iov_len - len; copy = tail->iov_len - len;
memmove((char *)tail->iov_base + len, memmove((char *)tail->iov_base + len,
tail->iov_base, copy); tail->iov_base, copy);
result += copy;
} }
/* Copy from the inlined pages into the tail */ /* Copy from the inlined pages into the tail */
copy = len; copy = len;
...@@ -377,11 +380,13 @@ xdr_shrink_bufhead(struct xdr_buf *buf, size_t len) ...@@ -377,11 +380,13 @@ xdr_shrink_bufhead(struct xdr_buf *buf, size_t len)
copy = 0; copy = 0;
else if (copy > tail->iov_len - offs) else if (copy > tail->iov_len - offs)
copy = tail->iov_len - offs; copy = tail->iov_len - offs;
if (copy != 0) if (copy != 0) {
_copy_from_pages((char *)tail->iov_base + offs, _copy_from_pages((char *)tail->iov_base + offs,
buf->pages, buf->pages,
buf->page_base + pglen + offs - len, buf->page_base + pglen + offs - len,
copy); copy);
result += copy;
}
/* Do we also need to copy data from the head into the tail ? */ /* Do we also need to copy data from the head into the tail ? */
if (len > pglen) { if (len > pglen) {
offs = copy = len - pglen; offs = copy = len - pglen;
...@@ -391,6 +396,7 @@ xdr_shrink_bufhead(struct xdr_buf *buf, size_t len) ...@@ -391,6 +396,7 @@ xdr_shrink_bufhead(struct xdr_buf *buf, size_t len)
(char *)head->iov_base + (char *)head->iov_base +
head->iov_len - offs, head->iov_len - offs,
copy); copy);
result += copy;
} }
} }
/* Now handle pages */ /* Now handle pages */
...@@ -406,12 +412,15 @@ xdr_shrink_bufhead(struct xdr_buf *buf, size_t len) ...@@ -406,12 +412,15 @@ xdr_shrink_bufhead(struct xdr_buf *buf, size_t len)
_copy_to_pages(buf->pages, buf->page_base, _copy_to_pages(buf->pages, buf->page_base,
(char *)head->iov_base + head->iov_len - len, (char *)head->iov_base + head->iov_len - len,
copy); copy);
result += copy;
} }
head->iov_len -= len; head->iov_len -= len;
buf->buflen -= len; buf->buflen -= len;
/* Have we truncated the message? */ /* Have we truncated the message? */
if (buf->len > buf->buflen) if (buf->len > buf->buflen)
buf->len = buf->buflen; buf->len = buf->buflen;
return result;
} }
/** /**
...@@ -423,14 +432,16 @@ xdr_shrink_bufhead(struct xdr_buf *buf, size_t len) ...@@ -423,14 +432,16 @@ xdr_shrink_bufhead(struct xdr_buf *buf, size_t len)
* 'len' bytes. The extra data is not lost, but is instead * 'len' bytes. The extra data is not lost, but is instead
* moved into the tail. * moved into the tail.
*/ */
static void static unsigned int
xdr_shrink_pagelen(struct xdr_buf *buf, size_t len) xdr_shrink_pagelen(struct xdr_buf *buf, size_t len)
{ {
struct kvec *tail; struct kvec *tail;
size_t copy; size_t copy;
unsigned int pglen = buf->page_len; unsigned int pglen = buf->page_len;
unsigned int tailbuf_len; unsigned int tailbuf_len;
unsigned int result;
result = 0;
tail = buf->tail; tail = buf->tail;
BUG_ON (len > pglen); BUG_ON (len > pglen);
...@@ -448,18 +459,22 @@ xdr_shrink_pagelen(struct xdr_buf *buf, size_t len) ...@@ -448,18 +459,22 @@ xdr_shrink_pagelen(struct xdr_buf *buf, size_t len)
if (tail->iov_len > len) { if (tail->iov_len > len) {
char *p = (char *)tail->iov_base + len; char *p = (char *)tail->iov_base + len;
memmove(p, tail->iov_base, tail->iov_len - len); memmove(p, tail->iov_base, tail->iov_len - len);
result += tail->iov_len - len;
} else } else
copy = tail->iov_len; copy = tail->iov_len;
/* Copy from the inlined pages into the tail */ /* Copy from the inlined pages into the tail */
_copy_from_pages((char *)tail->iov_base, _copy_from_pages((char *)tail->iov_base,
buf->pages, buf->page_base + pglen - len, buf->pages, buf->page_base + pglen - len,
copy); copy);
result += copy;
} }
buf->page_len -= len; buf->page_len -= len;
buf->buflen -= len; buf->buflen -= len;
/* Have we truncated the message? */ /* Have we truncated the message? */
if (buf->len > buf->buflen) if (buf->len > buf->buflen)
buf->len = buf->buflen; buf->len = buf->buflen;
return result;
} }
void void
...@@ -959,13 +974,17 @@ static unsigned int xdr_align_pages(struct xdr_stream *xdr, unsigned int len) ...@@ -959,13 +974,17 @@ static unsigned int xdr_align_pages(struct xdr_stream *xdr, unsigned int len)
struct kvec *iov; struct kvec *iov;
unsigned int nwords = XDR_QUADLEN(len); unsigned int nwords = XDR_QUADLEN(len);
unsigned int cur = xdr_stream_pos(xdr); unsigned int cur = xdr_stream_pos(xdr);
unsigned int copied, offset;
if (xdr->nwords == 0) if (xdr->nwords == 0)
return 0; return 0;
/* Realign pages to current pointer position */ /* Realign pages to current pointer position */
iov = buf->head; iov = buf->head;
if (iov->iov_len > cur) { if (iov->iov_len > cur) {
xdr_shrink_bufhead(buf, iov->iov_len - cur); offset = iov->iov_len - cur;
copied = xdr_shrink_bufhead(buf, offset);
trace_rpc_xdr_alignment(xdr, offset, copied);
xdr->nwords = XDR_QUADLEN(buf->len - cur); xdr->nwords = XDR_QUADLEN(buf->len - cur);
} }
...@@ -977,7 +996,9 @@ static unsigned int xdr_align_pages(struct xdr_stream *xdr, unsigned int len) ...@@ -977,7 +996,9 @@ static unsigned int xdr_align_pages(struct xdr_stream *xdr, unsigned int len)
len = buf->page_len; len = buf->page_len;
else if (nwords < xdr->nwords) { else if (nwords < xdr->nwords) {
/* Truncate page data and move it into the tail */ /* Truncate page data and move it into the tail */
xdr_shrink_pagelen(buf, buf->page_len - len); offset = buf->page_len - len;
copied = xdr_shrink_pagelen(buf, offset);
trace_rpc_xdr_alignment(xdr, offset, copied);
xdr->nwords = XDR_QUADLEN(buf->len - cur); xdr->nwords = XDR_QUADLEN(buf->len - cur);
} }
return len; return len;
......
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