diff options
| author | Chuck Lever <cel@netapp.com> | 2006-03-20 13:44:22 -0500 |
|---|---|---|
| committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-03-20 13:44:22 -0500 |
| commit | 11c556b3d8d481829ab5f9933a25d29b00913b5a (patch) | |
| tree | d1127fa5d22aeb035ca9977abbc73a29d5436fbe | |
| parent | ef759a2e54ed434b2f72b52a14edecd6d4eadf74 (diff) | |
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 <cel@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
| -rw-r--r-- | include/linux/sunrpc/clnt.h | 3 | ||||
| -rw-r--r-- | include/linux/sunrpc/metrics.h | 77 | ||||
| -rw-r--r-- | net/sunrpc/clnt.c | 9 | ||||
| -rw-r--r-- | net/sunrpc/stats.c | 105 | ||||
| -rw-r--r-- | net/sunrpc/xprt.c | 2 |
5 files changed, 192 insertions, 4 deletions
diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index f147e6b84332..0f3662002ffc 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h | |||
| @@ -45,7 +45,8 @@ struct rpc_clnt { | |||
| 45 | char * cl_server; /* server machine name */ | 45 | char * cl_server; /* server machine name */ |
| 46 | char * cl_protname; /* protocol name */ | 46 | char * cl_protname; /* protocol name */ |
| 47 | struct rpc_auth * cl_auth; /* authenticator */ | 47 | struct rpc_auth * cl_auth; /* authenticator */ |
| 48 | struct rpc_stat * cl_stats; /* statistics */ | 48 | struct rpc_stat * cl_stats; /* per-program statistics */ |
| 49 | struct rpc_iostats * cl_metrics; /* per-client statistics */ | ||
| 49 | 50 | ||
| 50 | unsigned int cl_softrtry : 1,/* soft timeouts */ | 51 | unsigned int cl_softrtry : 1,/* soft timeouts */ |
| 51 | cl_intr : 1,/* interruptible */ | 52 | cl_intr : 1,/* interruptible */ |
diff --git a/include/linux/sunrpc/metrics.h b/include/linux/sunrpc/metrics.h new file mode 100644 index 000000000000..8f96e9dc369a --- /dev/null +++ b/include/linux/sunrpc/metrics.h | |||
| @@ -0,0 +1,77 @@ | |||
| 1 | /* | ||
| 2 | * linux/include/linux/sunrpc/metrics.h | ||
| 3 | * | ||
| 4 | * Declarations for RPC client per-operation metrics | ||
| 5 | * | ||
| 6 | * Copyright (C) 2005 Chuck Lever <cel@netapp.com> | ||
| 7 | * | ||
| 8 | * RPC client per-operation statistics provide latency and retry | ||
| 9 | * information about each type of RPC procedure in a given RPC program. | ||
| 10 | * These statistics are not for detailed problem diagnosis, but simply | ||
| 11 | * to indicate whether the problem is local or remote. | ||
| 12 | * | ||
| 13 | * These counters are not meant to be human-readable, but are meant to be | ||
| 14 | * integrated into system monitoring tools such as "sar" and "iostat". As | ||
| 15 | * such, the counters are sampled by the tools over time, and are never | ||
| 16 | * zeroed after a file system is mounted. Moving averages can be computed | ||
| 17 | * by the tools by taking the difference between two instantaneous samples | ||
| 18 | * and dividing that by the time between the samples. | ||
| 19 | * | ||
| 20 | * The counters are maintained in a single array per RPC client, indexed | ||
| 21 | * by procedure number. There is no need to maintain separate counter | ||
| 22 | * arrays per-CPU because these counters are always modified behind locks. | ||
| 23 | */ | ||
| 24 | |||
| 25 | #ifndef _LINUX_SUNRPC_METRICS_H | ||
| 26 | #define _LINUX_SUNRPC_METRICS_H | ||
| 27 | |||
| 28 | #include <linux/seq_file.h> | ||
| 29 | |||
| 30 | #define RPC_IOSTATS_VERS "1.0" | ||
| 31 | |||
| 32 | struct rpc_iostats { | ||
| 33 | /* | ||
| 34 | * These counters give an idea about how many request | ||
| 35 | * transmissions are required, on average, to complete that | ||
| 36 | * particular procedure. Some procedures may require more | ||
| 37 | * than one transmission because the server is unresponsive, | ||
| 38 | * the client is retransmitting too aggressively, or the | ||
| 39 | * requests are large and the network is congested. | ||
| 40 | */ | ||
| 41 | unsigned long om_ops, /* count of operations */ | ||
| 42 | om_ntrans, /* count of RPC transmissions */ | ||
| 43 | om_timeouts; /* count of major timeouts */ | ||
| 44 | |||
| 45 | /* | ||
| 46 | * These count how many bytes are sent and received for a | ||
| 47 | * given RPC procedure type. This indicates how much load a | ||
| 48 | * particular procedure is putting on the network. These | ||
| 49 | * counts include the RPC and ULP headers, and the request | ||
| 50 | * payload. | ||
| 51 | */ | ||
| 52 | unsigned long long om_bytes_sent, /* count of bytes out */ | ||
| 53 | om_bytes_recv; /* count of bytes in */ | ||
| 54 | |||
| 55 | /* | ||
| 56 | * The length of time an RPC request waits in queue before | ||
| 57 | * transmission, the network + server latency of the request, | ||
| 58 | * and the total time the request spent from init to release | ||
| 59 | * are measured. | ||
| 60 | */ | ||
| 61 | unsigned long long om_queue, /* jiffies queued for xmit */ | ||
| 62 | om_rtt, /* jiffies for RPC RTT */ | ||
| 63 | om_execute; /* jiffies for RPC execution */ | ||
| 64 | } ____cacheline_aligned; | ||
| 65 | |||
| 66 | struct rpc_task; | ||
| 67 | struct rpc_clnt; | ||
| 68 | |||
| 69 | /* | ||
| 70 | * EXPORTed functions for managing rpc_iostats structures | ||
| 71 | */ | ||
| 72 | struct rpc_iostats * rpc_alloc_iostats(struct rpc_clnt *); | ||
| 73 | void rpc_count_iostats(struct rpc_task *); | ||
| 74 | void rpc_print_iostats(struct seq_file *, struct rpc_clnt *); | ||
| 75 | void rpc_free_iostats(struct rpc_iostats *); | ||
| 76 | |||
| 77 | #endif /* _LINUX_SUNRPC_METRICS_H */ | ||
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 84eb5b4565fc..0bb23e8e9d0c 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c | |||
| @@ -28,12 +28,11 @@ | |||
| 28 | #include <linux/mm.h> | 28 | #include <linux/mm.h> |
| 29 | #include <linux/slab.h> | 29 | #include <linux/slab.h> |
| 30 | #include <linux/utsname.h> | 30 | #include <linux/utsname.h> |
| 31 | #include <linux/workqueue.h> | ||
| 31 | 32 | ||
| 32 | #include <linux/sunrpc/clnt.h> | 33 | #include <linux/sunrpc/clnt.h> |
| 33 | #include <linux/workqueue.h> | ||
| 34 | #include <linux/sunrpc/rpc_pipe_fs.h> | 34 | #include <linux/sunrpc/rpc_pipe_fs.h> |
| 35 | 35 | #include <linux/sunrpc/metrics.h> | |
| 36 | #include <linux/nfs.h> | ||
| 37 | 36 | ||
| 38 | 37 | ||
| 39 | #define RPC_SLACK_SPACE (1024) /* total overkill */ | 38 | #define RPC_SLACK_SPACE (1024) /* total overkill */ |
| @@ -147,6 +146,7 @@ rpc_new_client(struct rpc_xprt *xprt, char *servname, | |||
| 147 | clnt->cl_vers = version->number; | 146 | clnt->cl_vers = version->number; |
| 148 | clnt->cl_prot = xprt->prot; | 147 | clnt->cl_prot = xprt->prot; |
| 149 | clnt->cl_stats = program->stats; | 148 | clnt->cl_stats = program->stats; |
| 149 | clnt->cl_metrics = rpc_alloc_iostats(clnt); | ||
| 150 | rpc_init_wait_queue(&clnt->cl_pmap_default.pm_bindwait, "bindwait"); | 150 | rpc_init_wait_queue(&clnt->cl_pmap_default.pm_bindwait, "bindwait"); |
| 151 | 151 | ||
| 152 | if (!clnt->cl_port) | 152 | if (!clnt->cl_port) |
| @@ -245,6 +245,7 @@ rpc_clone_client(struct rpc_clnt *clnt) | |||
| 245 | if (new->cl_auth) | 245 | if (new->cl_auth) |
| 246 | atomic_inc(&new->cl_auth->au_count); | 246 | atomic_inc(&new->cl_auth->au_count); |
| 247 | new->cl_pmap = &new->cl_pmap_default; | 247 | new->cl_pmap = &new->cl_pmap_default; |
| 248 | new->cl_metrics = rpc_alloc_iostats(clnt); | ||
| 248 | rpc_init_wait_queue(&new->cl_pmap_default.pm_bindwait, "bindwait"); | 249 | rpc_init_wait_queue(&new->cl_pmap_default.pm_bindwait, "bindwait"); |
| 249 | return new; | 250 | return new; |
| 250 | out_no_clnt: | 251 | out_no_clnt: |
| @@ -315,6 +316,8 @@ rpc_destroy_client(struct rpc_clnt *clnt) | |||
| 315 | if (clnt->cl_server != clnt->cl_inline_name) | 316 | if (clnt->cl_server != clnt->cl_inline_name) |
| 316 | kfree(clnt->cl_server); | 317 | kfree(clnt->cl_server); |
| 317 | out_free: | 318 | out_free: |
| 319 | rpc_free_iostats(clnt->cl_metrics); | ||
| 320 | clnt->cl_metrics = NULL; | ||
| 318 | if (clnt->cl_dentry) | 321 | if (clnt->cl_dentry) |
| 319 | dput(clnt->cl_dentry); | 322 | dput(clnt->cl_dentry); |
| 320 | kfree(clnt); | 323 | kfree(clnt); |
diff --git a/net/sunrpc/stats.c b/net/sunrpc/stats.c index 4979f226e285..24ac7163b9c7 100644 --- a/net/sunrpc/stats.c +++ b/net/sunrpc/stats.c | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | #include <linux/seq_file.h> | 21 | #include <linux/seq_file.h> |
| 22 | #include <linux/sunrpc/clnt.h> | 22 | #include <linux/sunrpc/clnt.h> |
| 23 | #include <linux/sunrpc/svcsock.h> | 23 | #include <linux/sunrpc/svcsock.h> |
| 24 | #include <linux/sunrpc/metrics.h> | ||
| 24 | 25 | ||
| 25 | #define RPCDBG_FACILITY RPCDBG_MISC | 26 | #define RPCDBG_FACILITY RPCDBG_MISC |
| 26 | 27 | ||
| @@ -106,6 +107,110 @@ void svc_seq_show(struct seq_file *seq, const struct svc_stat *statp) { | |||
| 106 | } | 107 | } |
| 107 | } | 108 | } |
| 108 | 109 | ||
| 110 | /** | ||
| 111 | * rpc_alloc_iostats - allocate an rpc_iostats structure | ||
| 112 | * @clnt: RPC program, version, and xprt | ||
| 113 | * | ||
| 114 | */ | ||
| 115 | struct rpc_iostats *rpc_alloc_iostats(struct rpc_clnt *clnt) | ||
| 116 | { | ||
| 117 | unsigned int ops = clnt->cl_maxproc; | ||
| 118 | size_t size = ops * sizeof(struct rpc_iostats); | ||
| 119 | struct rpc_iostats *new; | ||
| 120 | |||
| 121 | new = kmalloc(size, GFP_KERNEL); | ||
| 122 | if (new) | ||
| 123 | memset(new, 0 , size); | ||
| 124 | return new; | ||
| 125 | } | ||
| 126 | EXPORT_SYMBOL(rpc_alloc_iostats); | ||
| 127 | |||
| 128 | /** | ||
| 129 | * rpc_free_iostats - release an rpc_iostats structure | ||
| 130 | * @stats: doomed rpc_iostats structure | ||
| 131 | * | ||
| 132 | */ | ||
| 133 | void rpc_free_iostats(struct rpc_iostats *stats) | ||
| 134 | { | ||
| 135 | kfree(stats); | ||
| 136 | } | ||
| 137 | EXPORT_SYMBOL(rpc_free_iostats); | ||
| 138 | |||
| 139 | /** | ||
| 140 | * rpc_count_iostats - tally up per-task stats | ||
| 141 | * @task: completed rpc_task | ||
| 142 | * | ||
| 143 | * Relies on the caller for serialization. | ||
| 144 | */ | ||
| 145 | void rpc_count_iostats(struct rpc_task *task) | ||
| 146 | { | ||
| 147 | struct rpc_rqst *req = task->tk_rqstp; | ||
| 148 | struct rpc_iostats *stats = task->tk_client->cl_metrics; | ||
| 149 | struct rpc_iostats *op_metrics; | ||
| 150 | long rtt, execute, queue; | ||
| 151 | |||
| 152 | if (!stats || !req) | ||
| 153 | return; | ||
| 154 | op_metrics = &stats[task->tk_msg.rpc_proc->p_proc]; | ||
| 155 | |||
| 156 | op_metrics->om_ops++; | ||
| 157 | op_metrics->om_ntrans += req->rq_ntrans; | ||
| 158 | op_metrics->om_timeouts += task->tk_timeouts; | ||
| 159 | |||
| 160 | op_metrics->om_bytes_sent += task->tk_bytes_sent; | ||
| 161 | op_metrics->om_bytes_recv += req->rq_received; | ||
| 162 | |||
| 163 | queue = (long)req->rq_xtime - task->tk_start; | ||
| 164 | if (queue < 0) | ||
| 165 | queue = -queue; | ||
| 166 | op_metrics->om_queue += queue; | ||
| 167 | |||
| 168 | rtt = task->tk_rtt; | ||
| 169 | if (rtt < 0) | ||
| 170 | rtt = -rtt; | ||
| 171 | op_metrics->om_rtt += rtt; | ||
| 172 | |||
| 173 | execute = (long)jiffies - task->tk_start; | ||
| 174 | if (execute < 0) | ||
| 175 | execute = -execute; | ||
| 176 | op_metrics->om_execute += execute; | ||
| 177 | } | ||
| 178 | |||
| 179 | #define MILLISECS_PER_JIFFY (1000UL / HZ) | ||
| 180 | |||
| 181 | void rpc_print_iostats(struct seq_file *seq, struct rpc_clnt *clnt) | ||
| 182 | { | ||
| 183 | struct rpc_iostats *stats = clnt->cl_metrics; | ||
| 184 | struct rpc_xprt *xprt = clnt->cl_xprt; | ||
| 185 | unsigned int op, maxproc = clnt->cl_maxproc; | ||
| 186 | |||
| 187 | if (!stats) | ||
| 188 | return; | ||
| 189 | |||
| 190 | seq_printf(seq, "\tRPC iostats version: %s ", RPC_IOSTATS_VERS); | ||
| 191 | seq_printf(seq, "p/v: %u/%u (%s)\n", | ||
| 192 | clnt->cl_prog, clnt->cl_vers, clnt->cl_protname); | ||
| 193 | |||
| 194 | if (xprt) | ||
| 195 | xprt->ops->print_stats(xprt, seq); | ||
| 196 | |||
| 197 | seq_printf(seq, "\tper-op statistics\n"); | ||
| 198 | for (op = 0; op < maxproc; op++) { | ||
| 199 | struct rpc_iostats *metrics = &stats[op]; | ||
| 200 | seq_printf(seq, "%12u: ", op); | ||
| 201 | seq_printf(seq, "%lu %lu %lu %Lu %Lu %Lu %Lu %Lu\n", | ||
| 202 | metrics->om_ops, | ||
| 203 | metrics->om_ntrans, | ||
| 204 | metrics->om_timeouts, | ||
| 205 | metrics->om_bytes_sent, | ||
| 206 | metrics->om_bytes_recv, | ||
| 207 | metrics->om_queue * MILLISECS_PER_JIFFY, | ||
| 208 | metrics->om_rtt * MILLISECS_PER_JIFFY, | ||
| 209 | metrics->om_execute * MILLISECS_PER_JIFFY); | ||
| 210 | } | ||
| 211 | } | ||
| 212 | EXPORT_SYMBOL(rpc_print_iostats); | ||
| 213 | |||
| 109 | /* | 214 | /* |
| 110 | * Register/unregister RPC proc files | 215 | * Register/unregister RPC proc files |
| 111 | */ | 216 | */ |
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index c6241976a6ee..eb5a262e024e 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c | |||
| @@ -44,6 +44,7 @@ | |||
| 44 | #include <linux/random.h> | 44 | #include <linux/random.h> |
| 45 | 45 | ||
| 46 | #include <linux/sunrpc/clnt.h> | 46 | #include <linux/sunrpc/clnt.h> |
| 47 | #include <linux/sunrpc/metrics.h> | ||
| 47 | 48 | ||
| 48 | /* | 49 | /* |
| 49 | * Local variables | 50 | * Local variables |
| @@ -859,6 +860,7 @@ void xprt_release(struct rpc_task *task) | |||
| 859 | 860 | ||
| 860 | if (!(req = task->tk_rqstp)) | 861 | if (!(req = task->tk_rqstp)) |
| 861 | return; | 862 | return; |
| 863 | rpc_count_iostats(task); | ||
| 862 | spin_lock_bh(&xprt->transport_lock); | 864 | spin_lock_bh(&xprt->transport_lock); |
| 863 | xprt->ops->release_xprt(xprt, task); | 865 | xprt->ops->release_xprt(xprt, task); |
| 864 | if (xprt->ops->release_request) | 866 | if (xprt->ops->release_request) |
