SUNRPC: provide a mechanism for collecting stats in the RPC client

Add a simple mechanism for collecting stats in the RPC client.  Stats are
tabulated during xprt_release.  Note that per_cpu shenanigans are not
required here because the RPC client already serializes on the transport
write lock.

Test plan:
Compile kernel with CONFIG_NFS enabled.  Basic performance regression
testing with high-speed networking and high performance server.

Signed-off-by: Chuck Lever <[email protected]>
Signed-off-by: Trond Myklebust <[email protected]>
This commit is contained in:
Chuck Lever 2006-03-20 13:44:22 -05:00 • committed by Trond Myklebust
commit 11c556b3d8
5 changed files with 192 additions and 4 deletions

View file

@ -28,12 +28,11 @@
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/utsname.h>
#include <linux/workqueue.h>
#include <linux/sunrpc/clnt.h>
#include <linux/workqueue.h>
#include <linux/sunrpc/rpc_pipe_fs.h>
#include <linux/nfs.h>
#include <linux/sunrpc/metrics.h>
#define RPC_SLACK_SPACE (1024) /* total overkill */
@ -147,6 +146,7 @@ rpc_new_client(struct rpc_xprt *xprt, char *servname,
clnt->cl_vers = version->number;
clnt->cl_prot = xprt->prot;
clnt->cl_stats = program->stats;
clnt->cl_metrics = rpc_alloc_iostats(clnt);
rpc_init_wait_queue(&clnt->cl_pmap_default.pm_bindwait, "bindwait");
if (!clnt->cl_port)
@ -245,6 +245,7 @@ rpc_clone_client(struct rpc_clnt *clnt)
if (new->cl_auth)
atomic_inc(&new->cl_auth->au_count);
new->cl_pmap = &new->cl_pmap_default;
new->cl_metrics = rpc_alloc_iostats(clnt);
rpc_init_wait_queue(&new->cl_pmap_default.pm_bindwait, "bindwait");
return new;
out_no_clnt:
@ -315,6 +316,8 @@ rpc_destroy_client(struct rpc_clnt *clnt)
if (clnt->cl_server != clnt->cl_inline_name)
kfree(clnt->cl_server);
out_free:
rpc_free_iostats(clnt->cl_metrics);
clnt->cl_metrics = NULL;
if (clnt->cl_dentry)
dput(clnt->cl_dentry);
kfree(clnt);

View file

@ -21,6 +21,7 @@
#include <linux/seq_file.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/sunrpc/metrics.h>
#define RPCDBG_FACILITY RPCDBG_MISC
@ -106,6 +107,110 @@ void svc_seq_show(struct seq_file *seq, const struct svc_stat *statp) {
}
}
/**
* rpc_alloc_iostats - allocate an rpc_iostats structure
* @clnt: RPC program, version, and xprt
*
*/
struct rpc_iostats *rpc_alloc_iostats(struct rpc_clnt *clnt)
{
unsigned int ops = clnt->cl_maxproc;
size_t size = ops * sizeof(struct rpc_iostats);
struct rpc_iostats *new;
new = kmalloc(size, GFP_KERNEL);
if (new)
memset(new, 0 , size);
return new;
}
EXPORT_SYMBOL(rpc_alloc_iostats);
/**
* rpc_free_iostats - release an rpc_iostats structure
* @stats: doomed rpc_iostats structure
*
*/
void rpc_free_iostats(struct rpc_iostats *stats)
{
kfree(stats);
}
EXPORT_SYMBOL(rpc_free_iostats);
/**
* rpc_count_iostats - tally up per-task stats
* @task: completed rpc_task
*
* Relies on the caller for serialization.
*/
void rpc_count_iostats(struct rpc_task *task)
{
struct rpc_rqst *req = task->tk_rqstp;
struct rpc_iostats *stats = task->tk_client->cl_metrics;
struct rpc_iostats *op_metrics;
long rtt, execute, queue;
if (!stats || !req)
return;
op_metrics = &stats[task->tk_msg.rpc_proc->p_proc];
op_metrics->om_ops++;
op_metrics->om_ntrans += req->rq_ntrans;
op_metrics->om_timeouts += task->tk_timeouts;
op_metrics->om_bytes_sent += task->tk_bytes_sent;
op_metrics->om_bytes_recv += req->rq_received;
queue = (long)req->rq_xtime - task->tk_start;
if (queue < 0)
queue = -queue;
op_metrics->om_queue += queue;
rtt = task->tk_rtt;
if (rtt < 0)
rtt = -rtt;
op_metrics->om_rtt += rtt;
execute = (long)jiffies - task->tk_start;
if (execute < 0)
execute = -execute;
op_metrics->om_execute += execute;
}
#define MILLISECS_PER_JIFFY (1000UL / HZ)
void rpc_print_iostats(struct seq_file *seq, struct rpc_clnt *clnt)
{
struct rpc_iostats *stats = clnt->cl_metrics;
struct rpc_xprt *xprt = clnt->cl_xprt;
unsigned int op, maxproc = clnt->cl_maxproc;
if (!stats)
return;
seq_printf(seq, "\tRPC iostats version: %s ", RPC_IOSTATS_VERS);
seq_printf(seq, "p/v: %u/%u (%s)\n",
clnt->cl_prog, clnt->cl_vers, clnt->cl_protname);
if (xprt)
xprt->ops->print_stats(xprt, seq);
seq_printf(seq, "\tper-op statistics\n");
for (op = 0; op < maxproc; op++) {
struct rpc_iostats *metrics = &stats[op];
seq_printf(seq, "%12u: ", op);
seq_printf(seq, "%lu %lu %lu %Lu %Lu %Lu %Lu %Lu\n",
metrics->om_ops,
metrics->om_ntrans,
metrics->om_timeouts,
metrics->om_bytes_sent,
metrics->om_bytes_recv,
metrics->om_queue * MILLISECS_PER_JIFFY,
metrics->om_rtt * MILLISECS_PER_JIFFY,
metrics->om_execute * MILLISECS_PER_JIFFY);
}
}
EXPORT_SYMBOL(rpc_print_iostats);
/*
* Register/unregister RPC proc files
*/

View file

@ -44,6 +44,7 @@
#include <linux/random.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/metrics.h>
/*
* Local variables
@ -859,6 +860,7 @@ void xprt_release(struct rpc_task *task)
if (!(req = task->tk_rqstp))
return;
rpc_count_iostats(task);
spin_lock_bh(&xprt->transport_lock);
xprt->ops->release_xprt(xprt, task);
if (xprt->ops->release_request)