diff options
author | Greg Banks <gnb@sgi.com> | 2009-01-13 05:26:36 -0500 |
---|---|---|
committer | J. Bruce Fields <bfields@citi.umich.edu> | 2009-03-18 17:38:42 -0400 |
commit | 03cf6c9f49a8fea953d38648d016e3f46e814991 (patch) | |
tree | c7e639bd9fb775af1919d2442d0bf2306dfbfca7 | |
parent | 59a252ff8c0f2fa32c896f69d56ae33e641ce7ad (diff) |
knfsd: add file to export stats about nfsd pools
Add /proc/fs/nfsd/pool_stats to export to userspace various
statistics about the operation of rpc server thread pools.
This patch is based on a forward-ported version of
knfsd-add-pool-thread-stats which has been shipping in the SGI
"Enhanced NFS" product since 2006 and which was previously
posted:
http://article.gmane.org/gmane.linux.nfs/10375
It has also been updated thus:
* moved EXPORT_SYMBOL() to near the function it exports
* made the new struct struct seq_operations const
* used SEQ_START_TOKEN instead of ((void *)1)
* merged fix from SGI PV 990526 "sunrpc: use dprintk instead of
printk in svc_pool_stats_*()" by Harshula Jayasuriya.
* merged fix from SGI PV 964001 "Crash reading pool_stats before
nfsds are started".
Signed-off-by: Greg Banks <gnb@sgi.com>
Signed-off-by: Harshula Jayasuriya <harshula@sgi.com>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
-rw-r--r-- | fs/nfsd/nfsctl.c | 12 | ||||
-rw-r--r-- | fs/nfsd/nfssvc.c | 7 | ||||
-rw-r--r-- | include/linux/sunrpc/svc.h | 11 | ||||
-rw-r--r-- | net/sunrpc/svc_xprt.c | 100 |
4 files changed, 129 insertions, 1 deletions
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 3d93b2064ce5..4adebb6312c4 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c | |||
@@ -60,6 +60,7 @@ enum { | |||
60 | NFSD_FO_UnlockFS, | 60 | NFSD_FO_UnlockFS, |
61 | NFSD_Threads, | 61 | NFSD_Threads, |
62 | NFSD_Pool_Threads, | 62 | NFSD_Pool_Threads, |
63 | NFSD_Pool_Stats, | ||
63 | NFSD_Versions, | 64 | NFSD_Versions, |
64 | NFSD_Ports, | 65 | NFSD_Ports, |
65 | NFSD_MaxBlkSize, | 66 | NFSD_MaxBlkSize, |
@@ -172,6 +173,16 @@ static const struct file_operations exports_operations = { | |||
172 | .owner = THIS_MODULE, | 173 | .owner = THIS_MODULE, |
173 | }; | 174 | }; |
174 | 175 | ||
176 | extern int nfsd_pool_stats_open(struct inode *inode, struct file *file); | ||
177 | |||
178 | static struct file_operations pool_stats_operations = { | ||
179 | .open = nfsd_pool_stats_open, | ||
180 | .read = seq_read, | ||
181 | .llseek = seq_lseek, | ||
182 | .release = seq_release, | ||
183 | .owner = THIS_MODULE, | ||
184 | }; | ||
185 | |||
175 | /*----------------------------------------------------------------------------*/ | 186 | /*----------------------------------------------------------------------------*/ |
176 | /* | 187 | /* |
177 | * payload - write methods | 188 | * payload - write methods |
@@ -1246,6 +1257,7 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent) | |||
1246 | [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR}, | 1257 | [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR}, |
1247 | [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR}, | 1258 | [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR}, |
1248 | [NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR}, | 1259 | [NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR}, |
1260 | [NFSD_Pool_Stats] = {"pool_stats", &pool_stats_operations, S_IRUGO}, | ||
1249 | [NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR}, | 1261 | [NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR}, |
1250 | [NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO}, | 1262 | [NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO}, |
1251 | [NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO}, | 1263 | [NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO}, |
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index c3eb0759fd57..ef0a3686639d 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c | |||
@@ -546,3 +546,10 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp) | |||
546 | nfsd_cache_update(rqstp, proc->pc_cachetype, statp + 1); | 546 | nfsd_cache_update(rqstp, proc->pc_cachetype, statp + 1); |
547 | return 1; | 547 | return 1; |
548 | } | 548 | } |
549 | |||
550 | int nfsd_pool_stats_open(struct inode *inode, struct file *file) | ||
551 | { | ||
552 | if (nfsd_serv == NULL) | ||
553 | return -ENODEV; | ||
554 | return svc_pool_stats_open(nfsd_serv, file); | ||
555 | } | ||
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 39ec186a492d..9f9f699dd469 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h | |||
@@ -24,6 +24,15 @@ | |||
24 | */ | 24 | */ |
25 | typedef int (*svc_thread_fn)(void *); | 25 | typedef int (*svc_thread_fn)(void *); |
26 | 26 | ||
27 | /* statistics for svc_pool structures */ | ||
28 | struct svc_pool_stats { | ||
29 | unsigned long packets; | ||
30 | unsigned long sockets_queued; | ||
31 | unsigned long threads_woken; | ||
32 | unsigned long overloads_avoided; | ||
33 | unsigned long threads_timedout; | ||
34 | }; | ||
35 | |||
27 | /* | 36 | /* |
28 | * | 37 | * |
29 | * RPC service thread pool. | 38 | * RPC service thread pool. |
@@ -42,6 +51,7 @@ struct svc_pool { | |||
42 | unsigned int sp_nrthreads; /* # of threads in pool */ | 51 | unsigned int sp_nrthreads; /* # of threads in pool */ |
43 | struct list_head sp_all_threads; /* all server threads */ | 52 | struct list_head sp_all_threads; /* all server threads */ |
44 | int sp_nwaking; /* number of threads woken but not yet active */ | 53 | int sp_nwaking; /* number of threads woken but not yet active */ |
54 | struct svc_pool_stats sp_stats; /* statistics on pool operation */ | ||
45 | } ____cacheline_aligned_in_smp; | 55 | } ____cacheline_aligned_in_smp; |
46 | 56 | ||
47 | /* | 57 | /* |
@@ -396,6 +406,7 @@ struct svc_serv * svc_create_pooled(struct svc_program *, unsigned int, | |||
396 | sa_family_t, void (*shutdown)(struct svc_serv *), | 406 | sa_family_t, void (*shutdown)(struct svc_serv *), |
397 | svc_thread_fn, struct module *); | 407 | svc_thread_fn, struct module *); |
398 | int svc_set_num_threads(struct svc_serv *, struct svc_pool *, int); | 408 | int svc_set_num_threads(struct svc_serv *, struct svc_pool *, int); |
409 | int svc_pool_stats_open(struct svc_serv *serv, struct file *file); | ||
399 | void svc_destroy(struct svc_serv *); | 410 | void svc_destroy(struct svc_serv *); |
400 | int svc_process(struct svc_rqst *); | 411 | int svc_process(struct svc_rqst *); |
401 | int svc_register(const struct svc_serv *, const unsigned short, | 412 | int svc_register(const struct svc_serv *, const unsigned short, |
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 0551b6b6cf8c..1e66f2491460 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c | |||
@@ -318,6 +318,8 @@ void svc_xprt_enqueue(struct svc_xprt *xprt) | |||
318 | goto out_unlock; | 318 | goto out_unlock; |
319 | } | 319 | } |
320 | 320 | ||
321 | pool->sp_stats.packets++; | ||
322 | |||
321 | /* Mark transport as busy. It will remain in this state until | 323 | /* Mark transport as busy. It will remain in this state until |
322 | * the provider calls svc_xprt_received. We update XPT_BUSY | 324 | * the provider calls svc_xprt_received. We update XPT_BUSY |
323 | * atomically because it also guards against trying to enqueue | 325 | * atomically because it also guards against trying to enqueue |
@@ -355,6 +357,7 @@ void svc_xprt_enqueue(struct svc_xprt *xprt) | |||
355 | if (pool->sp_nwaking >= SVC_MAX_WAKING) { | 357 | if (pool->sp_nwaking >= SVC_MAX_WAKING) { |
356 | /* too many threads are runnable and trying to wake up */ | 358 | /* too many threads are runnable and trying to wake up */ |
357 | thread_avail = 0; | 359 | thread_avail = 0; |
360 | pool->sp_stats.overloads_avoided++; | ||
358 | } | 361 | } |
359 | 362 | ||
360 | if (thread_avail) { | 363 | if (thread_avail) { |
@@ -374,11 +377,13 @@ void svc_xprt_enqueue(struct svc_xprt *xprt) | |||
374 | atomic_add(rqstp->rq_reserved, &xprt->xpt_reserved); | 377 | atomic_add(rqstp->rq_reserved, &xprt->xpt_reserved); |
375 | rqstp->rq_waking = 1; | 378 | rqstp->rq_waking = 1; |
376 | pool->sp_nwaking++; | 379 | pool->sp_nwaking++; |
380 | pool->sp_stats.threads_woken++; | ||
377 | BUG_ON(xprt->xpt_pool != pool); | 381 | BUG_ON(xprt->xpt_pool != pool); |
378 | wake_up(&rqstp->rq_wait); | 382 | wake_up(&rqstp->rq_wait); |
379 | } else { | 383 | } else { |
380 | dprintk("svc: transport %p put into queue\n", xprt); | 384 | dprintk("svc: transport %p put into queue\n", xprt); |
381 | list_add_tail(&xprt->xpt_ready, &pool->sp_sockets); | 385 | list_add_tail(&xprt->xpt_ready, &pool->sp_sockets); |
386 | pool->sp_stats.sockets_queued++; | ||
382 | BUG_ON(xprt->xpt_pool != pool); | 387 | BUG_ON(xprt->xpt_pool != pool); |
383 | } | 388 | } |
384 | 389 | ||
@@ -591,6 +596,7 @@ int svc_recv(struct svc_rqst *rqstp, long timeout) | |||
591 | int pages; | 596 | int pages; |
592 | struct xdr_buf *arg; | 597 | struct xdr_buf *arg; |
593 | DECLARE_WAITQUEUE(wait, current); | 598 | DECLARE_WAITQUEUE(wait, current); |
599 | long time_left; | ||
594 | 600 | ||
595 | dprintk("svc: server %p waiting for data (to = %ld)\n", | 601 | dprintk("svc: server %p waiting for data (to = %ld)\n", |
596 | rqstp, timeout); | 602 | rqstp, timeout); |
@@ -676,12 +682,14 @@ int svc_recv(struct svc_rqst *rqstp, long timeout) | |||
676 | add_wait_queue(&rqstp->rq_wait, &wait); | 682 | add_wait_queue(&rqstp->rq_wait, &wait); |
677 | spin_unlock_bh(&pool->sp_lock); | 683 | spin_unlock_bh(&pool->sp_lock); |
678 | 684 | ||
679 | schedule_timeout(timeout); | 685 | time_left = schedule_timeout(timeout); |
680 | 686 | ||
681 | try_to_freeze(); | 687 | try_to_freeze(); |
682 | 688 | ||
683 | spin_lock_bh(&pool->sp_lock); | 689 | spin_lock_bh(&pool->sp_lock); |
684 | remove_wait_queue(&rqstp->rq_wait, &wait); | 690 | remove_wait_queue(&rqstp->rq_wait, &wait); |
691 | if (!time_left) | ||
692 | pool->sp_stats.threads_timedout++; | ||
685 | 693 | ||
686 | xprt = rqstp->rq_xprt; | 694 | xprt = rqstp->rq_xprt; |
687 | if (!xprt) { | 695 | if (!xprt) { |
@@ -1114,3 +1122,93 @@ int svc_xprt_names(struct svc_serv *serv, char *buf, int buflen) | |||
1114 | return totlen; | 1122 | return totlen; |
1115 | } | 1123 | } |
1116 | EXPORT_SYMBOL_GPL(svc_xprt_names); | 1124 | EXPORT_SYMBOL_GPL(svc_xprt_names); |
1125 | |||
1126 | |||
1127 | /*----------------------------------------------------------------------------*/ | ||
1128 | |||
1129 | static void *svc_pool_stats_start(struct seq_file *m, loff_t *pos) | ||
1130 | { | ||
1131 | unsigned int pidx = (unsigned int)*pos; | ||
1132 | struct svc_serv *serv = m->private; | ||
1133 | |||
1134 | dprintk("svc_pool_stats_start, *pidx=%u\n", pidx); | ||
1135 | |||
1136 | lock_kernel(); | ||
1137 | /* bump up the pseudo refcount while traversing */ | ||
1138 | svc_get(serv); | ||
1139 | unlock_kernel(); | ||
1140 | |||
1141 | if (!pidx) | ||
1142 | return SEQ_START_TOKEN; | ||
1143 | return (pidx > serv->sv_nrpools ? NULL : &serv->sv_pools[pidx-1]); | ||
1144 | } | ||
1145 | |||
1146 | static void *svc_pool_stats_next(struct seq_file *m, void *p, loff_t *pos) | ||
1147 | { | ||
1148 | struct svc_pool *pool = p; | ||
1149 | struct svc_serv *serv = m->private; | ||
1150 | |||
1151 | dprintk("svc_pool_stats_next, *pos=%llu\n", *pos); | ||
1152 | |||
1153 | if (p == SEQ_START_TOKEN) { | ||
1154 | pool = &serv->sv_pools[0]; | ||
1155 | } else { | ||
1156 | unsigned int pidx = (pool - &serv->sv_pools[0]); | ||
1157 | if (pidx < serv->sv_nrpools-1) | ||
1158 | pool = &serv->sv_pools[pidx+1]; | ||
1159 | else | ||
1160 | pool = NULL; | ||
1161 | } | ||
1162 | ++*pos; | ||
1163 | return pool; | ||
1164 | } | ||
1165 | |||
1166 | static void svc_pool_stats_stop(struct seq_file *m, void *p) | ||
1167 | { | ||
1168 | struct svc_serv *serv = m->private; | ||
1169 | |||
1170 | lock_kernel(); | ||
1171 | /* this function really, really should have been called svc_put() */ | ||
1172 | svc_destroy(serv); | ||
1173 | unlock_kernel(); | ||
1174 | } | ||
1175 | |||
1176 | static int svc_pool_stats_show(struct seq_file *m, void *p) | ||
1177 | { | ||
1178 | struct svc_pool *pool = p; | ||
1179 | |||
1180 | if (p == SEQ_START_TOKEN) { | ||
1181 | seq_puts(m, "# pool packets-arrived sockets-enqueued threads-woken overloads-avoided threads-timedout\n"); | ||
1182 | return 0; | ||
1183 | } | ||
1184 | |||
1185 | seq_printf(m, "%u %lu %lu %lu %lu %lu\n", | ||
1186 | pool->sp_id, | ||
1187 | pool->sp_stats.packets, | ||
1188 | pool->sp_stats.sockets_queued, | ||
1189 | pool->sp_stats.threads_woken, | ||
1190 | pool->sp_stats.overloads_avoided, | ||
1191 | pool->sp_stats.threads_timedout); | ||
1192 | |||
1193 | return 0; | ||
1194 | } | ||
1195 | |||
1196 | static const struct seq_operations svc_pool_stats_seq_ops = { | ||
1197 | .start = svc_pool_stats_start, | ||
1198 | .next = svc_pool_stats_next, | ||
1199 | .stop = svc_pool_stats_stop, | ||
1200 | .show = svc_pool_stats_show, | ||
1201 | }; | ||
1202 | |||
1203 | int svc_pool_stats_open(struct svc_serv *serv, struct file *file) | ||
1204 | { | ||
1205 | int err; | ||
1206 | |||
1207 | err = seq_open(file, &svc_pool_stats_seq_ops); | ||
1208 | if (!err) | ||
1209 | ((struct seq_file *) file->private_data)->private = serv; | ||
1210 | return err; | ||
1211 | } | ||
1212 | EXPORT_SYMBOL(svc_pool_stats_open); | ||
1213 | |||
1214 | /*----------------------------------------------------------------------------*/ | ||