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) |