1 /*
2 * Sysctl operations for Coda filesystem
3 * Original version: (C) 1996 P. Braam and M. Callahan
4 * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
5 *
6 * Carnegie Mellon encourages users to contribute improvements to
7 * the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
8 *
9 * CODA operation statistics
10 * (c) March, 1998 Zhanyong Wan <zhanyong.wan@yale.edu>
11 *
12 */
13
14 #include <linux/config.h>
15 #include <linux/sched.h>
16 #include <linux/mm.h>
17 #include <linux/sysctl.h>
18 #include <linux/swapctl.h>
19 #include <linux/proc_fs.h>
20 #include <linux/slab.h>
21 #include <linux/stat.h>
22 #include <linux/ctype.h>
23 #include <asm/bitops.h>
24 #include <asm/uaccess.h>
25 #include <linux/utsname.h>
26 #define __NO_VERSION__
27 #include <linux/module.h>
28
29 #include <linux/coda.h>
30 #include <linux/coda_linux.h>
31 #include <linux/coda_fs_i.h>
32 #include <linux/coda_psdev.h>
33 #include <linux/coda_cache.h>
34 #include <linux/coda_proc.h>
35
36 static struct ctl_table_header *fs_table_header;
37
38 #define FS_CODA 1 /* Coda file system */
39
40 #define CODA_DEBUG 1 /* control debugging */
41 #define CODA_ENTRY 2 /* control enter/leave pattern */
42 #define CODA_TIMEOUT 3 /* timeout on upcalls to become intrble */
43 #define CODA_MC 4 /* use/do not use the access cache */
44 #define CODA_HARD 5 /* mount type "hard" or "soft" */
45 #define CODA_VFS 6 /* vfs statistics */
46 #define CODA_UPCALL 7 /* upcall statistics */
47 #define CODA_PERMISSION 8 /* permission statistics */
48 #define CODA_CACHE_INV 9 /* cache invalidation statistics */
49 #define CODA_FAKE_STATFS 10 /* don't query venus for actual cache usage */
50
51 static ctl_table coda_table[] = {
52 {CODA_DEBUG, "debug", &coda_debug, sizeof(int), 0644, NULL, &proc_dointvec},
53 {CODA_MC, "accesscache", &coda_access_cache, sizeof(int), 0644, NULL, &proc_dointvec},
54 {CODA_TIMEOUT, "timeout", &coda_timeout, sizeof(int), 0644, NULL, &proc_dointvec},
55 {CODA_HARD, "hard", &coda_hard, sizeof(int), 0644, NULL, &proc_dointvec},
56 {CODA_VFS, "vfs_stats", NULL, 0, 0644, NULL, &do_reset_coda_vfs_stats},
57 {CODA_UPCALL, "upcall_stats", NULL, 0, 0644, NULL, &do_reset_coda_upcall_stats},
58 {CODA_PERMISSION, "permission_stats", NULL, 0, 0644, NULL, &do_reset_coda_permission_stats},
59 {CODA_CACHE_INV, "cache_inv_stats", NULL, 0, 0644, NULL, &do_reset_coda_cache_inv_stats},
60 {CODA_FAKE_STATFS, "fake_statfs", &coda_fake_statfs, sizeof(int), 0600, NULL, &proc_dointvec},
61 { 0 }
62 };
63
64 static ctl_table fs_table[] = {
65 {FS_CODA, "coda", NULL, 0, 0555, coda_table},
66 {0}
67 };
68
69 struct coda_vfs_stats coda_vfs_stat;
70 struct coda_permission_stats coda_permission_stat;
71 struct coda_cache_inv_stats coda_cache_inv_stat;
72 struct coda_upcall_stats_entry coda_upcall_stat[CODA_NCALLS];
73 struct coda_upcallstats coda_callstats;
74 int coda_upcall_timestamping = 0;
75
76 /* keep this in sync with coda.h! */
77 char *coda_upcall_names[] = {
78 "totals ", /* 0 */
79 "- ", /* 1 */
80 "root ", /* 2 */
81 "open_by_fd ", /* 3 */
82 "open ", /* 4 */
83 "close ", /* 5 */
84 "ioctl ", /* 6 */
85 "getattr ", /* 7 */
86 "setattr ", /* 8 */
87 "access ", /* 9 */
88 "lookup ", /* 10 */
89 "create ", /* 11 */
90 "remove ", /* 12 */
91 "link ", /* 13 */
92 "rename ", /* 14 */
93 "mkdir ", /* 15 */
94 "rmdir ", /* 16 */
95 "readdir ", /* 17 */
96 "symlink ", /* 18 */
97 "readlink ", /* 19 */
98 "fsync ", /* 20 */
99 "- ", /* 21 */
100 "vget ", /* 22 */
101 "signal ", /* 23 */
102 "replace ", /* 24 */
103 "flush ", /* 25 */
104 "purgeuser ", /* 26 */
105 "zapfile ", /* 27 */
106 "zapdir ", /* 28 */
107 "- ", /* 29 */
108 "purgefid ", /* 30 */
109 "open_by_path", /* 31 */
110 "resolve ", /* 32 */
111 "reintegrate ", /* 33 */
112 "statfs ", /* 34 */
113 "store ", /* 35 */
114 "release " /* 36 */
115 };
116
117
reset_coda_vfs_stats(void)118 void reset_coda_vfs_stats( void )
119 {
120 memset( &coda_vfs_stat, 0, sizeof( coda_vfs_stat ) );
121 }
122
reset_coda_upcall_stats(void)123 void reset_coda_upcall_stats( void )
124 {
125 memset( &coda_upcall_stat, 0, sizeof( coda_upcall_stat ) );
126 }
127
reset_coda_permission_stats(void)128 void reset_coda_permission_stats( void )
129 {
130 memset( &coda_permission_stat, 0, sizeof( coda_permission_stat ) );
131 }
132
reset_coda_cache_inv_stats(void)133 void reset_coda_cache_inv_stats( void )
134 {
135 memset( &coda_cache_inv_stat, 0, sizeof( coda_cache_inv_stat ) );
136 }
137
138
do_time_stats(struct coda_upcall_stats_entry * pentry,unsigned long runtime)139 void do_time_stats( struct coda_upcall_stats_entry * pentry,
140 unsigned long runtime )
141 {
142 unsigned long time = runtime; /* time in us */
143 CDEBUG(D_SPECIAL, "time: %ld\n", time);
144
145 if ( pentry->count == 0 ) {
146 pentry->time_sum = pentry->time_squared_sum = 0;
147 }
148
149 pentry->count++;
150 pentry->time_sum += time;
151 pentry->time_squared_sum += time*time;
152 }
153
154
155
coda_upcall_stats(int opcode,long unsigned runtime)156 void coda_upcall_stats(int opcode, long unsigned runtime)
157 {
158 struct coda_upcall_stats_entry * pentry;
159
160 if ( opcode < 0 || opcode > CODA_NCALLS - 1) {
161 printk("Nasty opcode %d passed to coda_upcall_stats\n",
162 opcode);
163 return;
164 }
165
166 pentry = &coda_upcall_stat[opcode];
167 do_time_stats(pentry, runtime);
168
169 /* fill in the totals */
170 pentry = &coda_upcall_stat[0];
171 do_time_stats(pentry, runtime);
172
173 }
174
get_time_average(const struct coda_upcall_stats_entry * pentry)175 unsigned long get_time_average( const struct coda_upcall_stats_entry * pentry )
176 {
177 return ( pentry->count == 0 ) ? 0 : pentry->time_sum / pentry->count;
178 }
179
absolute(unsigned long x)180 static inline unsigned long absolute( unsigned long x )
181 {
182 return x >= 0 ? x : -x;
183 }
184
sqr_root(unsigned long x)185 static unsigned long sqr_root( unsigned long x )
186 {
187 unsigned long y = x, r;
188 int n_bit = 0;
189
190 if ( x == 0 )
191 return 0;
192 if ( x < 0)
193 x = -x;
194
195 while ( y ) {
196 y >>= 1;
197 n_bit++;
198 }
199
200 r = 1 << (n_bit/2);
201
202 while ( 1 ) {
203 r = (r + x/r)/2;
204 if ( r*r <= x && x < (r+1)*(r+1) )
205 break;
206 }
207
208 return r;
209 }
210
get_time_std_deviation(const struct coda_upcall_stats_entry * pentry)211 unsigned long get_time_std_deviation( const struct coda_upcall_stats_entry * pentry )
212 {
213 unsigned long time_avg;
214
215 if ( pentry->count <= 1 )
216 return 0;
217
218 time_avg = get_time_average( pentry );
219
220 return sqr_root( (pentry->time_squared_sum / pentry->count) -
221 time_avg * time_avg );
222 }
223
do_reset_coda_vfs_stats(ctl_table * table,int write,struct file * filp,void * buffer,size_t * lenp)224 int do_reset_coda_vfs_stats( ctl_table * table, int write, struct file * filp,
225 void * buffer, size_t * lenp )
226 {
227 if ( write ) {
228 reset_coda_vfs_stats();
229
230 filp->f_pos += *lenp;
231 } else {
232 *lenp = 0;
233 }
234
235 return 0;
236 }
237
do_reset_coda_upcall_stats(ctl_table * table,int write,struct file * filp,void * buffer,size_t * lenp)238 int do_reset_coda_upcall_stats( ctl_table * table, int write,
239 struct file * filp, void * buffer,
240 size_t * lenp )
241 {
242 if ( write ) {
243 if (*lenp > 0) {
244 char c;
245 if (get_user(c, (char *)buffer))
246 return -EFAULT;
247 coda_upcall_timestamping = (c == '1');
248 }
249 reset_coda_upcall_stats();
250
251 filp->f_pos += *lenp;
252 } else {
253 *lenp = 0;
254 }
255
256 return 0;
257 }
258
do_reset_coda_permission_stats(ctl_table * table,int write,struct file * filp,void * buffer,size_t * lenp)259 int do_reset_coda_permission_stats( ctl_table * table, int write,
260 struct file * filp, void * buffer,
261 size_t * lenp )
262 {
263 if ( write ) {
264 reset_coda_permission_stats();
265
266 filp->f_pos += *lenp;
267 } else {
268 *lenp = 0;
269 }
270
271 return 0;
272 }
273
do_reset_coda_cache_inv_stats(ctl_table * table,int write,struct file * filp,void * buffer,size_t * lenp)274 int do_reset_coda_cache_inv_stats( ctl_table * table, int write,
275 struct file * filp, void * buffer,
276 size_t * lenp )
277 {
278 if ( write ) {
279 reset_coda_cache_inv_stats();
280
281 filp->f_pos += *lenp;
282 } else {
283 *lenp = 0;
284 }
285
286 return 0;
287 }
288
coda_vfs_stats_get_info(char * buffer,char ** start,off_t offset,int length)289 int coda_vfs_stats_get_info( char * buffer, char ** start, off_t offset,
290 int length)
291 {
292 int len=0;
293 off_t begin;
294 struct coda_vfs_stats * ps = & coda_vfs_stat;
295
296 /* this works as long as we are below 1024 characters! */
297 len += sprintf( buffer,
298 "Coda VFS statistics\n"
299 "===================\n\n"
300 "File Operations:\n"
301 "\topen\t\t%9d\n"
302 "\tflush\t\t%9d\n"
303 "\trelease\t\t%9d\n"
304 "\tfsync\t\t%9d\n\n"
305 "Dir Operations:\n"
306 "\treaddir\t\t%9d\n\n"
307 "Inode Operations\n"
308 "\tcreate\t\t%9d\n"
309 "\tlookup\t\t%9d\n"
310 "\tlink\t\t%9d\n"
311 "\tunlink\t\t%9d\n"
312 "\tsymlink\t\t%9d\n"
313 "\tmkdir\t\t%9d\n"
314 "\trmdir\t\t%9d\n"
315 "\trename\t\t%9d\n"
316 "\tpermission\t%9d\n",
317
318 /* file operations */
319 ps->open,
320 ps->flush,
321 ps->release,
322 ps->fsync,
323
324 /* dir operations */
325 ps->readdir,
326
327 /* inode operations */
328 ps->create,
329 ps->lookup,
330 ps->link,
331 ps->unlink,
332 ps->symlink,
333 ps->mkdir,
334 ps->rmdir,
335 ps->rename,
336 ps->permission);
337
338 begin = offset;
339 *start = buffer + begin;
340 len -= begin;
341
342 if ( len > length )
343 len = length;
344 if ( len < 0 )
345 len = 0;
346
347 return len;
348 }
349
coda_upcall_stats_get_info(char * buffer,char ** start,off_t offset,int length)350 int coda_upcall_stats_get_info( char * buffer, char ** start, off_t offset,
351 int length)
352 {
353 int len=0;
354 int i;
355 off_t begin;
356 off_t pos = 0;
357 char tmpbuf[80];
358 int tmplen = 0;
359
360 /* this works as long as we are below 1024 characters! */
361 if ( offset < 80 )
362 len += sprintf( buffer,"%-79s\n", "Coda upcall statistics");
363 if ( offset < 160)
364 len += sprintf( buffer + len,"%-79s\n", "======================");
365 if ( offset < 240)
366 len += sprintf( buffer + len,"%-79s\n", "upcall count avg time(us) std deviation(us)");
367 if ( offset < 320)
368 len += sprintf( buffer + len,"%-79s\n", "------ ----- ------------ -----------------");
369 pos = 320;
370 for ( i = 0 ; i < CODA_NCALLS ; i++ ) {
371 tmplen += sprintf(tmpbuf,"%s %9d %10ld %10ld",
372 coda_upcall_names[i],
373 coda_upcall_stat[i].count,
374 get_time_average(&coda_upcall_stat[i]),
375 coda_upcall_stat[i].time_squared_sum);
376 pos += 80;
377 if ( pos < offset )
378 continue;
379 len += sprintf(buffer + len, "%-79s\n", tmpbuf);
380 if ( len >= length )
381 break;
382 }
383
384 begin = len- (pos - offset);
385 *start = buffer + begin;
386 len -= begin;
387
388 if ( len > length )
389 len = length;
390 if ( len < 0 )
391 len = 0;
392
393 return len;
394 }
395
coda_permission_stats_get_info(char * buffer,char ** start,off_t offset,int length)396 int coda_permission_stats_get_info( char * buffer, char ** start, off_t offset,
397 int length)
398 {
399 int len=0;
400 off_t begin;
401 struct coda_permission_stats * ps = & coda_permission_stat;
402
403 /* this works as long as we are below 1024 characters! */
404 len += sprintf( buffer,
405 "Coda permission statistics\n"
406 "==========================\n\n"
407 "count\t\t%9d\n"
408 "hit count\t%9d\n",
409
410 ps->count,
411 ps->hit_count );
412
413 begin = offset;
414 *start = buffer + begin;
415 len -= begin;
416
417 if ( len > length )
418 len = length;
419 if ( len < 0 )
420 len = 0;
421
422 return len;
423 }
424
coda_cache_inv_stats_get_info(char * buffer,char ** start,off_t offset,int length)425 int coda_cache_inv_stats_get_info( char * buffer, char ** start, off_t offset,
426 int length)
427 {
428 int len=0;
429 off_t begin;
430 struct coda_cache_inv_stats * ps = & coda_cache_inv_stat;
431
432 /* this works as long as we are below 1024 characters! */
433 len += sprintf( buffer,
434 "Coda cache invalidation statistics\n"
435 "==================================\n\n"
436 "flush\t\t%9d\n"
437 "purge user\t%9d\n"
438 "zap_dir\t\t%9d\n"
439 "zap_file\t%9d\n"
440 "zap_vnode\t%9d\n"
441 "purge_fid\t%9d\n"
442 "replace\t\t%9d\n",
443 ps->flush,
444 ps->purge_user,
445 ps->zap_dir,
446 ps->zap_file,
447 ps->zap_vnode,
448 ps->purge_fid,
449 ps->replace );
450
451 begin = offset;
452 *start = buffer + begin;
453 len -= begin;
454
455 if ( len > length )
456 len = length;
457 if ( len < 0 )
458 len = 0;
459
460 return len;
461 }
462
463
464 #ifdef CONFIG_PROC_FS
465
466 /*
467 target directory structure:
468 /proc/fs (see linux/fs/proc/root.c)
469 /proc/fs/coda
470 /proc/fs/coda/{vfs_stats,
471
472 */
473
474 struct proc_dir_entry* proc_fs_coda;
475
476 #endif
477
478 #define coda_proc_create(name,get_info) \
479 create_proc_info_entry(name, 0, proc_fs_coda, get_info)
480
coda_sysctl_init()481 void coda_sysctl_init()
482 {
483 memset(&coda_callstats, 0, sizeof(coda_callstats));
484 reset_coda_vfs_stats();
485 reset_coda_upcall_stats();
486 reset_coda_permission_stats();
487 reset_coda_cache_inv_stats();
488
489 #ifdef CONFIG_PROC_FS
490 proc_fs_coda = proc_mkdir("coda", proc_root_fs);
491 if (proc_fs_coda) {
492 proc_fs_coda->owner = THIS_MODULE;
493 coda_proc_create("vfs_stats", coda_vfs_stats_get_info);
494 coda_proc_create("upcall_stats", coda_upcall_stats_get_info);
495 coda_proc_create("permission_stats", coda_permission_stats_get_info);
496 coda_proc_create("cache_inv_stats", coda_cache_inv_stats_get_info);
497 }
498 #endif
499
500 #ifdef CONFIG_SYSCTL
501 if ( !fs_table_header )
502 fs_table_header = register_sysctl_table(fs_table, 0);
503 #endif
504 }
505
coda_sysctl_clean()506 void coda_sysctl_clean()
507 {
508
509 #ifdef CONFIG_SYSCTL
510 if ( fs_table_header ) {
511 unregister_sysctl_table(fs_table_header);
512 fs_table_header = 0;
513 }
514 #endif
515
516 #if CONFIG_PROC_FS
517 remove_proc_entry("cache_inv_stats", proc_fs_coda);
518 remove_proc_entry("permission_stats", proc_fs_coda);
519 remove_proc_entry("upcall_stats", proc_fs_coda);
520 remove_proc_entry("vfs_stats", proc_fs_coda);
521 remove_proc_entry("coda", proc_root_fs);
522 #endif
523 }
524