Whamcloud - gitweb
Branch: b1_4
[fs/lustre-release.git] / lustre / obdfilter / lproc_obdfilter.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *  Copyright (C) 2002, 2003 Cluster File Systems, Inc.
5  *
6  *   This file is part of Lustre, http://www.lustre.org.
7  *
8  *   Lustre is free software; you can redistribute it and/or
9  *   modify it under the terms of version 2 of the GNU General Public
10  *   License as published by the Free Software Foundation.
11  *
12  *   Lustre is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with Lustre; if not, write to the Free Software
19  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  */
22 #define DEBUG_SUBSYSTEM S_CLASS
23
24 #include <linux/version.h>
25 #include <linux/lprocfs_status.h>
26 #include <linux/obd.h>
27 #include <linux/seq_file.h>
28 #include <linux/version.h>
29
30 #include "filter_internal.h"
31
32 #ifndef LPROCFS
33 static struct lprocfs_vars lprocfs_obd_vars[]  = { {0} };
34 static struct lprocfs_vars lprocfs_module_vars[] = { {0} };
35 #else
36
37 static int lprocfs_filter_rd_groups(char *page, char **start, off_t off,
38                                     int count, int *eof, void *data)
39 {
40         *eof = 1;
41         return snprintf(page, count, "%u\n", FILTER_GROUPS);
42 }
43
44 static int lprocfs_filter_rd_tot_dirty(char *page, char **start, off_t off,
45                                        int count, int *eof, void *data)
46 {
47         struct obd_device *obd = (struct obd_device *)data;
48
49         LASSERT(obd != NULL);
50         *eof = 1;
51         return snprintf(page, count, LPU64"\n", obd->u.filter.fo_tot_dirty);
52 }
53
54 static int lprocfs_filter_rd_tot_granted(char *page, char **start, off_t off,
55                                          int count, int *eof, void *data)
56 {
57         struct obd_device *obd = (struct obd_device *)data;
58
59         LASSERT(obd != NULL);
60         *eof = 1;
61         return snprintf(page, count, LPU64"\n", obd->u.filter.fo_tot_granted);
62 }
63
64 static int lprocfs_filter_rd_tot_pending(char *page, char **start, off_t off,
65                                          int count, int *eof, void *data)
66 {
67         struct obd_device *obd = (struct obd_device *)data;
68
69         LASSERT(obd != NULL);
70         *eof = 1;
71         return snprintf(page, count, LPU64"\n", obd->u.filter.fo_tot_pending);
72 }
73
74 static int lprocfs_filter_rd_mntdev(char *page, char **start, off_t off,
75                                     int count, int *eof, void *data)
76 {
77         struct obd_device *obd = (struct obd_device *)data;
78
79         LASSERT(obd != NULL);
80         LASSERT(obd->u.filter.fo_vfsmnt->mnt_devname);
81         *eof = 1;
82         return snprintf(page, count, "%s\n",
83                         obd->u.filter.fo_vfsmnt->mnt_devname);
84 }
85
86 static int lprocfs_filter_rd_last_id(char *page, char **start, off_t off,
87                                      int count, int *eof, void *data)
88 {
89         struct obd_device *obd = data;
90
91         if (obd == NULL)
92                 return 0;
93
94         return snprintf(page, count, LPU64"\n",
95                         filter_last_id(&obd->u.filter, 0));
96 }
97
98 int lprocfs_filter_rd_readcache(char *page, char **start, off_t off, int count,
99                                 int *eof, void *data)
100 {
101         struct obd_device *obd = data;
102         int rc;
103
104         rc = snprintf(page, count, LPU64"\n",
105                       obd->u.filter.fo_readcache_max_filesize);
106         return rc;
107 }
108
109 int lprocfs_filter_wr_readcache(struct file *file, const char *buffer,
110                                 unsigned long count, void *data)
111 {
112         struct obd_device *obd = data;
113         __u64 val;
114         int rc;
115
116         rc = lprocfs_write_u64_helper(buffer, count, &val);
117         if (rc)
118                 return rc;
119
120         obd->u.filter.fo_readcache_max_filesize = val;
121         return count;
122 }
123
124 static int lprocfs_filter_rd_bunit(char *page, char **start, off_t off, int count, 
125                                    int *eof, void *data)
126 {
127         struct obd_device *obd = (struct obd_device *)data;
128         LASSERT(obd != NULL);
129
130         return snprintf(page, count, "%lu\n", 
131                         obd->u.filter.fo_quota_ctxt.lqc_bunit_sz);
132 }
133
134 static int lprocfs_filter_rd_iunit(char *page, char **start, off_t off, int count, 
135                                    int *eof, void *data)
136 {
137         struct obd_device *obd = (struct obd_device *)data;
138         LASSERT(obd != NULL);
139
140         return snprintf(page, count, "%lu\n", 
141                         obd->u.filter.fo_quota_ctxt.lqc_iunit_sz);
142 }
143
144 static int lprocfs_filter_wr_bunit(struct file *file, const char *buffer,
145                                    unsigned long count, void *data)
146 {
147         struct obd_device *obd = (struct obd_device *)data;
148         int val, rc = 0;
149         LASSERT(obd != NULL);
150
151         rc = lprocfs_write_helper(buffer, count, &val);
152         if (rc)
153                 return rc;
154
155         if (val % QUOTABLOCK_SIZE ||
156             val <= obd->u.filter.fo_quota_ctxt.lqc_btune_sz)
157                 return -EINVAL;
158
159         obd->u.filter.fo_quota_ctxt.lqc_bunit_sz = val;
160         return count;
161 }
162
163 static int lprocfs_filter_wr_iunit(struct file *file, const char *buffer,
164                                    unsigned long count, void *data)
165 {
166         struct obd_device *obd = (struct obd_device *)data;
167         int val, rc = 0;
168         LASSERT(obd != NULL);
169
170         rc = lprocfs_write_helper(buffer, count, &val);
171         if (rc)
172                 return rc;
173
174         if (val <= obd->u.filter.fo_quota_ctxt.lqc_itune_sz)
175                 return -EINVAL;
176
177         obd->u.filter.fo_quota_ctxt.lqc_iunit_sz = val;
178         return count;
179 }
180
181 static int lprocfs_filter_rd_btune(char *page, char **start, off_t off, int count, 
182                                    int *eof, void *data)
183 {
184         struct obd_device *obd = (struct obd_device *)data;
185         LASSERT(obd != NULL);
186
187         return snprintf(page, count, "%lu\n", 
188                         obd->u.filter.fo_quota_ctxt.lqc_btune_sz);
189 }
190
191 static int lprocfs_filter_rd_itune(char *page, char **start, off_t off, int count, 
192                                    int *eof, void *data)
193 {
194         struct obd_device *obd = (struct obd_device *)data;
195         LASSERT(obd != NULL);
196
197         return snprintf(page, count, "%lu\n", 
198                         obd->u.filter.fo_quota_ctxt.lqc_itune_sz);
199 }
200
201 static int lprocfs_filter_wr_btune(struct file *file, const char *buffer,
202                                    unsigned long count, void *data)
203 {
204         struct obd_device *obd = (struct obd_device *)data;
205         int val, rc = 0;
206         LASSERT(obd != NULL);
207
208         rc = lprocfs_write_helper(buffer, count, &val);
209         if (rc)
210                 return rc;
211         
212         if (val <= QUOTABLOCK_SIZE * MIN_QLIMIT || val % QUOTABLOCK_SIZE || 
213             val >= obd->u.filter.fo_quota_ctxt.lqc_bunit_sz)
214                 return -EINVAL;
215
216         obd->u.filter.fo_quota_ctxt.lqc_btune_sz = val;
217         return count;
218 }
219
220 static int lprocfs_filter_wr_itune(struct file *file, const char *buffer,
221                                    unsigned long count, void *data)
222 {
223         struct obd_device *obd = (struct obd_device *)data;
224         int val, rc = 0;
225         LASSERT(obd != NULL);
226
227         rc = lprocfs_write_helper(buffer, count, &val);
228         if (rc)
229                 return rc;
230         
231         if (val <= MIN_QLIMIT || 
232             val >= obd->u.filter.fo_quota_ctxt.lqc_iunit_sz)
233                 return -EINVAL;
234
235         obd->u.filter.fo_quota_ctxt.lqc_itune_sz = val;
236         return count;
237 }
238
239 static struct lprocfs_vars lprocfs_obd_vars[] = {
240         { "uuid",         lprocfs_rd_uuid,          0, 0 },
241         { "blocksize",    lprocfs_rd_blksize,       0, 0 },
242         { "kbytestotal",  lprocfs_rd_kbytestotal,   0, 0 },
243         { "kbytesfree",   lprocfs_rd_kbytesfree,    0, 0 },
244         { "kbytesavail",  lprocfs_rd_kbytesavail,   0, 0 },
245         { "filestotal",   lprocfs_rd_filestotal,    0, 0 },
246         { "filesfree",    lprocfs_rd_filesfree,     0, 0 },
247         { "filegroups",   lprocfs_filter_rd_groups, 0, 0 },
248         { "fstype",       lprocfs_rd_fstype,        0, 0 },
249         { "mntdev",       lprocfs_filter_rd_mntdev, 0, 0 },
250         { "last_id",      lprocfs_filter_rd_last_id,0, 0 },
251         { "tot_dirty",    lprocfs_filter_rd_tot_dirty,   0, 0 },
252         { "tot_pending",  lprocfs_filter_rd_tot_pending, 0, 0 },
253         { "tot_granted",  lprocfs_filter_rd_tot_granted, 0, 0 },
254         { "recovery_status", lprocfs_obd_rd_recovery_status, 0, 0 },
255         { "evict_client", 0, lprocfs_wr_evict_client, 0 },
256         { "num_exports",  lprocfs_rd_num_exports,   0, 0 },
257         { "readcache_max_filesize",
258                           lprocfs_filter_rd_readcache,
259                           lprocfs_filter_wr_readcache, 0 },
260         { "quota_bunit_sz", lprocfs_filter_rd_bunit, 
261                             lprocfs_filter_wr_bunit, 0},
262         { "quota_btune_sz", lprocfs_filter_rd_btune,
263                             lprocfs_filter_wr_btune, 0},
264         { "quota_iunit_sz", lprocfs_filter_rd_iunit, 
265                             lprocfs_filter_wr_iunit, 0},
266         { "quota_itune_sz", lprocfs_filter_rd_itune,
267                             lprocfs_filter_wr_itune, 0},
268
269         { 0 }
270 };
271
272 static struct lprocfs_vars lprocfs_module_vars[] = {
273         { "num_refs",     lprocfs_rd_numrefs,       0, 0 },
274         { 0 }
275 };
276
277 void filter_tally_write(struct filter_obd *filter, struct page **pages,
278                      int nr_pages, unsigned long *blocks, int blocks_per_page)
279 {
280         struct page *last_page = NULL;
281         unsigned long *last_block = NULL;
282         unsigned long discont_pages = 0;
283         unsigned long discont_blocks = 0;
284         int i;
285
286         if (nr_pages == 0)
287                 return;
288
289         lprocfs_oh_tally_log2(&filter->fo_w_pages, nr_pages);
290
291         while (nr_pages-- > 0) {
292                 if (last_page && (*pages)->index != (last_page->index + 1))
293                         discont_pages++;
294                 last_page = *pages;
295                 pages++;
296                 for (i = 0; i < blocks_per_page; i++) {
297                         if (last_block && *blocks != (*last_block + 1))
298                                 discont_blocks++;
299                         last_block = blocks++;
300                 }
301         }
302
303         lprocfs_oh_tally(&filter->fo_w_discont_pages, discont_pages);
304         lprocfs_oh_tally(&filter->fo_w_discont_blocks, discont_blocks);
305 }
306
307 void filter_tally_read(struct filter_obd *filter, struct niobuf_local *lnb,
308                        int niocount)
309 {
310         struct niobuf_local *end;
311         struct page *last_page = NULL;
312         unsigned long discont_pages = 0;
313         unsigned long discont_blocks = 0;
314
315         if (niocount == 0)
316                 return;
317
318         for (end = lnb + niocount; lnb < end && lnb->page; lnb++) {
319                 struct page *page = lnb->page;
320                 if (last_page) {
321                        if (page->index != (last_page->index + 1))
322                                 discont_pages++;
323                         /* XXX not so smart for now */
324 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
325                         if ((page->buffers && last_page->buffers) &&
326                             (page->buffers->b_blocknr !=
327                              (last_page->buffers->b_blocknr + 1)))
328                                 discont_blocks++;
329 #else
330 #warning "port on 2.6 -bzzz"
331 #endif
332                 }
333                 last_page = page;
334         }
335
336         lprocfs_oh_tally_log2(&filter->fo_r_pages, niocount);
337         lprocfs_oh_tally(&filter->fo_r_discont_pages, discont_pages);
338         lprocfs_oh_tally(&filter->fo_r_discont_blocks, discont_blocks);
339 }
340
341 #define pct(a,b) (b ? a * 100 / b : 0)
342
343 static int filter_brw_stats_seq_show(struct seq_file *seq, void *v)
344 {
345         struct timeval now;
346         struct obd_device *dev = seq->private;
347         struct filter_obd *filter = &dev->u.filter;
348         unsigned long read_tot = 0, write_tot = 0, read_cum, write_cum;
349         int i;
350
351         do_gettimeofday(&now);
352
353         /* this sampling races with updates */
354
355         seq_printf(seq, "snapshot_time:         %lu.%lu (secs.usecs)\n",
356                    now.tv_sec, now.tv_usec);
357
358         seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
359         seq_printf(seq, "pages per brw         brws   %% cum %% |");
360         seq_printf(seq, "       rpcs   %% cum %%\n");
361
362         read_tot = lprocfs_oh_sum(&filter->fo_r_pages);
363         write_tot = lprocfs_oh_sum(&filter->fo_w_pages);
364
365         read_cum = 0;
366         write_cum = 0;
367         for (i = 0; i < OBD_HIST_MAX; i++) {
368                 unsigned long r = filter->fo_r_pages.oh_buckets[i];
369                 unsigned long w = filter->fo_w_pages.oh_buckets[i];
370                 read_cum += r;
371                 write_cum += w;
372                 seq_printf(seq, "%u:\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
373                                  1 << i, r, pct(r, read_tot),
374                                  pct(read_cum, read_tot), w,
375                                  pct(w, write_tot),
376                                  pct(write_cum, write_tot));
377                 if (read_cum == read_tot && write_cum == write_tot)
378                         break;
379         }
380
381         seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
382         seq_printf(seq, "discont pages        rpcs   %% cum %% |");
383         seq_printf(seq, "       rpcs   %% cum %%\n");
384
385         read_tot = lprocfs_oh_sum(&filter->fo_r_discont_pages);
386         write_tot = lprocfs_oh_sum(&filter->fo_w_discont_pages);
387
388         read_cum = 0;
389         write_cum = 0;
390
391         for (i = 0; i < OBD_HIST_MAX; i++) {
392                 unsigned long r = filter->fo_r_discont_pages.oh_buckets[i];
393                 unsigned long w = filter->fo_w_discont_pages.oh_buckets[i];
394                 read_cum += r;
395                 write_cum += w;
396                 seq_printf(seq, "%u:\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
397                                  i, r, pct(r, read_tot),
398                                  pct(read_cum, read_tot), w,
399                                  pct(w, write_tot),
400                                  pct(write_cum, write_tot));
401                 if (read_cum == read_tot && write_cum == write_tot)
402                         break;
403         }
404
405         seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
406         seq_printf(seq, "discont blocks        rpcs   %% cum %% |");
407         seq_printf(seq, "       rpcs   %% cum %%\n");
408
409         read_tot = lprocfs_oh_sum(&filter->fo_r_discont_blocks);
410         write_tot = lprocfs_oh_sum(&filter->fo_w_discont_blocks);
411
412         read_cum = 0;
413         write_cum = 0;
414         for (i = 0; i < OBD_HIST_MAX; i++) {
415                 unsigned long r = filter->fo_r_discont_blocks.oh_buckets[i];
416                 unsigned long w = filter->fo_w_discont_blocks.oh_buckets[i];
417                 read_cum += r;
418                 write_cum += w;
419                 seq_printf(seq, "%u:\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
420                                  i, r, pct(r, read_tot),
421                                  pct(read_cum, read_tot), w,
422                                  pct(w, write_tot),
423                                  pct(write_cum, write_tot));
424                 if (read_cum == read_tot && write_cum == write_tot)
425                         break;
426         }
427
428 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
429         seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
430         seq_printf(seq, "disk ios in flight     ios   %% cum %% |");
431         seq_printf(seq, "       rpcs   %% cum %%\n");
432
433         read_tot = lprocfs_oh_sum(&filter->fo_read_rpc_hist);
434         write_tot = lprocfs_oh_sum(&filter->fo_write_rpc_hist);
435
436         read_cum = 0;
437         write_cum = 0;
438         for (i = 0; i < OBD_HIST_MAX; i++) {
439                 unsigned long r = filter->fo_read_rpc_hist.oh_buckets[i];
440                 unsigned long w = filter->fo_write_rpc_hist.oh_buckets[i];
441                 read_cum += r;
442                 write_cum += w;
443                 seq_printf(seq, "%u:\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
444                                  i, r, pct(r, read_tot),
445                                  pct(read_cum, read_tot), w,
446                                  pct(w, write_tot),
447                                  pct(write_cum, write_tot));
448                 if (read_cum == read_tot && write_cum == write_tot)
449                         break;
450         }
451
452         seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
453         seq_printf(seq, "io time (jiffies)     rpcs   %% cum %% |");
454         seq_printf(seq, "       rpcs   %% cum %%\n");
455
456         read_tot = lprocfs_oh_sum(&filter->fo_r_io_time);
457         write_tot = lprocfs_oh_sum(&filter->fo_w_io_time);
458
459         read_cum = 0;
460         write_cum = 0;
461         for (i = 0; i < OBD_HIST_MAX; i++) {
462                 unsigned long r = filter->fo_r_io_time.oh_buckets[i];
463                 unsigned long w = filter->fo_w_io_time.oh_buckets[i];
464                 read_cum += r;
465                 write_cum += w;
466                 seq_printf(seq, "%10u:\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
467                                  1 << i, r, pct(r, read_tot),
468                                  pct(read_cum, read_tot), w,
469                                  pct(w, write_tot),
470                                  pct(write_cum, write_tot));
471                 if (read_cum == read_tot && write_cum == write_tot)
472                         break;
473         }
474
475         seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
476         seq_printf(seq, "disk I/O size         count  %% cum %% |");
477         seq_printf(seq, "       count  %% cum %%\n");
478
479         read_tot = lprocfs_oh_sum(&filter->fo_r_disk_iosize);
480         write_tot = lprocfs_oh_sum(&filter->fo_w_disk_iosize);
481
482         read_cum = 0;
483         write_cum = 0;
484         for (i = 0; i < OBD_HIST_MAX; i++) {
485                 unsigned long r = filter->fo_r_disk_iosize.oh_buckets[i];
486                 unsigned long w = filter->fo_w_disk_iosize.oh_buckets[i];
487
488                 read_cum += r;
489                 write_cum += w;
490                 if (read_cum == 0 && write_cum == 0)
491                         continue;
492
493                 if (i < 10)
494                         seq_printf(seq, "%u", 1<<i);
495                 else if (i < 20)
496                         seq_printf(seq, "%uK", 1<<(i-10));
497                 else
498                         seq_printf(seq, "%uM", 1<<(i-20));
499
500                 seq_printf(seq, ":\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
501                            r, pct(r, read_tot), pct(read_cum, read_tot), 
502                            w, pct(w, write_tot), pct(write_cum, write_tot));
503                 if (read_cum == read_tot && write_cum == write_tot)
504                         break;
505         }
506 #endif
507
508         return 0;
509 }
510 #undef pct
511
512 static void *filter_brw_stats_seq_start(struct seq_file *p, loff_t *pos)
513 {
514         if (*pos == 0)
515                 return (void *)1;
516         return NULL;
517 }
518 static void *filter_brw_stats_seq_next(struct seq_file *p, void *v, loff_t *pos)
519 {
520         ++*pos;
521         return NULL;
522 }
523 static void filter_brw_stats_seq_stop(struct seq_file *p, void *v)
524 {
525 }
526 struct seq_operations filter_brw_stats_seq_sops = {
527         .start = filter_brw_stats_seq_start,
528         .stop = filter_brw_stats_seq_stop,
529         .next = filter_brw_stats_seq_next,
530         .show = filter_brw_stats_seq_show,
531 };
532
533 static int filter_brw_stats_seq_open(struct inode *inode, struct file *file)
534 {
535         struct proc_dir_entry *dp = PDE(inode);
536         struct seq_file *seq;
537         int rc;
538
539         rc = seq_open(file, &filter_brw_stats_seq_sops);
540         if (rc)
541                 return rc;
542         seq = file->private_data;
543         seq->private = dp->data;
544         return 0;
545 }
546
547 static ssize_t filter_brw_stats_seq_write(struct file *file, const char *buf,
548                                        size_t len, loff_t *off)
549 {
550         struct seq_file *seq = file->private_data;
551         struct obd_device *dev = seq->private;
552         struct filter_obd *filter = &dev->u.filter;
553
554         lprocfs_oh_clear(&filter->fo_r_pages);
555         lprocfs_oh_clear(&filter->fo_w_pages);
556         lprocfs_oh_clear(&filter->fo_read_rpc_hist);
557         lprocfs_oh_clear(&filter->fo_write_rpc_hist);
558         lprocfs_oh_clear(&filter->fo_r_io_time);
559         lprocfs_oh_clear(&filter->fo_w_io_time);
560         lprocfs_oh_clear(&filter->fo_r_discont_pages);
561         lprocfs_oh_clear(&filter->fo_w_discont_pages);
562         lprocfs_oh_clear(&filter->fo_r_discont_blocks);
563         lprocfs_oh_clear(&filter->fo_w_discont_blocks);
564         lprocfs_oh_clear(&filter->fo_r_disk_iosize);
565         lprocfs_oh_clear(&filter->fo_w_disk_iosize);
566
567         return len;
568 }
569
570 struct file_operations filter_brw_stats_fops = {
571         .owner   = THIS_MODULE,
572         .open    = filter_brw_stats_seq_open,
573         .read    = seq_read,
574         .write   = filter_brw_stats_seq_write,
575         .llseek  = seq_lseek,
576         .release = seq_release,
577 };
578
579 int lproc_filter_attach_seqstat(struct obd_device *dev)
580 {
581         return lprocfs_obd_seq_create(dev, "brw_stats", 0444,
582                                       &filter_brw_stats_fops, dev);
583 }
584
585
586
587 #endif /* LPROCFS */
588 LPROCFS_INIT_VARS(filter, lprocfs_module_vars, lprocfs_obd_vars)