1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2 * vim:expandtab:shiftwidth=8:tabstop=8:
4 * Copyright (C) 2002, 2003 Cluster File Systems, Inc.
6 * This file is part of the Lustre file system, http://www.lustre.org
7 * Lustre is a trademark of Cluster File Systems, Inc.
9 * You may have signed or agreed to another license before downloading
10 * this software. If so, you are bound by the terms and conditions
11 * of that agreement, and the following does not apply to you. See the
12 * LICENSE file included with this distribution for more information.
14 * If you did not agree to a different license, then this copy of Lustre
15 * is open source software; you can redistribute it and/or modify it
16 * under the terms of version 2 of the GNU General Public License as
17 * published by the Free Software Foundation.
19 * In either case, Lustre is distributed in the hope that it will be
20 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
21 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * license text for more details.
25 #define DEBUG_SUBSYSTEM S_CLASS
27 #include <linux/version.h>
28 #include <lprocfs_status.h>
30 #include <linux/seq_file.h>
31 #include <linux/version.h>
33 #include "filter_internal.h"
36 static int lprocfs_filter_rd_groups(char *page, char **start, off_t off,
37 int count, int *eof, void *data)
40 return snprintf(page, count, "%u\n", FILTER_GROUPS);
43 static int lprocfs_filter_rd_tot_dirty(char *page, char **start, off_t off,
44 int count, int *eof, void *data)
46 struct obd_device *obd = (struct obd_device *)data;
50 return snprintf(page, count, LPU64"\n", obd->u.filter.fo_tot_dirty);
53 static int lprocfs_filter_rd_tot_granted(char *page, char **start, off_t off,
54 int count, int *eof, void *data)
56 struct obd_device *obd = (struct obd_device *)data;
60 return snprintf(page, count, LPU64"\n", obd->u.filter.fo_tot_granted);
63 static int lprocfs_filter_rd_tot_pending(char *page, char **start, off_t off,
64 int count, int *eof, void *data)
66 struct obd_device *obd = (struct obd_device *)data;
70 return snprintf(page, count, LPU64"\n", obd->u.filter.fo_tot_pending);
73 static int lprocfs_filter_rd_mntdev(char *page, char **start, off_t off,
74 int count, int *eof, void *data)
76 struct obd_device *obd = (struct obd_device *)data;
79 LASSERT(obd->u.filter.fo_vfsmnt->mnt_devname);
81 return snprintf(page, count, "%s\n",
82 obd->u.filter.fo_vfsmnt->mnt_devname);
85 static int lprocfs_filter_rd_last_id(char *page, char **start, off_t off,
86 int count, int *eof, void *data)
88 struct obd_device *obd = data;
93 return snprintf(page, count, LPU64"\n",
94 filter_last_id(&obd->u.filter, 0));
97 int lprocfs_filter_rd_readcache(char *page, char **start, off_t off, int count,
100 struct obd_device *obd = data;
103 rc = snprintf(page, count, LPU64"\n",
104 obd->u.filter.fo_readcache_max_filesize);
108 int lprocfs_filter_wr_readcache(struct file *file, const char *buffer,
109 unsigned long count, void *data)
111 struct obd_device *obd = data;
115 rc = lprocfs_write_u64_helper(buffer, count, &val);
119 obd->u.filter.fo_readcache_max_filesize = val;
123 #ifdef HAVE_QUOTA_SUPPORT
124 static int lprocfs_filter_rd_bunit(char *page, char **start, off_t off, int count,
125 int *eof, void *data)
127 struct obd_device *obd = (struct obd_device *)data;
128 LASSERT(obd != NULL);
130 return snprintf(page, count, "%lu\n",
131 obd->u.obt.obt_qctxt.lqc_bunit_sz);
134 static int lprocfs_filter_rd_iunit(char *page, char **start, off_t off, int count,
135 int *eof, void *data)
137 struct obd_device *obd = (struct obd_device *)data;
138 LASSERT(obd != NULL);
140 return snprintf(page, count, "%lu\n",
141 obd->u.obt.obt_qctxt.lqc_iunit_sz);
144 static int lprocfs_filter_wr_bunit(struct file *file, const char *buffer,
145 unsigned long count, void *data)
147 struct obd_device *obd = (struct obd_device *)data;
149 LASSERT(obd != NULL);
151 rc = lprocfs_write_helper(buffer, count, &val);
155 if (val % QUOTABLOCK_SIZE ||
156 val <= obd->u.obt.obt_qctxt.lqc_btune_sz)
159 obd->u.obt.obt_qctxt.lqc_bunit_sz = val;
163 static int lprocfs_filter_wr_iunit(struct file *file, const char *buffer,
164 unsigned long count, void *data)
166 struct obd_device *obd = (struct obd_device *)data;
168 LASSERT(obd != NULL);
170 rc = lprocfs_write_helper(buffer, count, &val);
174 if (val <= obd->u.obt.obt_qctxt.lqc_itune_sz)
177 obd->u.obt.obt_qctxt.lqc_iunit_sz = val;
181 static int lprocfs_filter_rd_btune(char *page, char **start, off_t off, int count,
182 int *eof, void *data)
184 struct obd_device *obd = (struct obd_device *)data;
185 LASSERT(obd != NULL);
187 return snprintf(page, count, "%lu\n",
188 obd->u.obt.obt_qctxt.lqc_btune_sz);
191 static int lprocfs_filter_rd_itune(char *page, char **start, off_t off, int count,
192 int *eof, void *data)
194 struct obd_device *obd = (struct obd_device *)data;
195 LASSERT(obd != NULL);
197 return snprintf(page, count, "%lu\n",
198 obd->u.obt.obt_qctxt.lqc_itune_sz);
201 static int lprocfs_filter_wr_btune(struct file *file, const char *buffer,
202 unsigned long count, void *data)
204 struct obd_device *obd = (struct obd_device *)data;
206 LASSERT(obd != NULL);
208 rc = lprocfs_write_helper(buffer, count, &val);
212 if (val <= QUOTABLOCK_SIZE * MIN_QLIMIT || val % QUOTABLOCK_SIZE ||
213 val >= obd->u.obt.obt_qctxt.lqc_bunit_sz)
216 obd->u.obt.obt_qctxt.lqc_btune_sz = val;
220 static int lprocfs_filter_wr_itune(struct file *file, const char *buffer,
221 unsigned long count, void *data)
223 struct obd_device *obd = (struct obd_device *)data;
225 LASSERT(obd != NULL);
227 rc = lprocfs_write_helper(buffer, count, &val);
231 if (val <= MIN_QLIMIT ||
232 val >= obd->u.obt.obt_qctxt.lqc_iunit_sz)
235 obd->u.obt.obt_qctxt.lqc_itune_sz = val;
240 int lprocfs_filter_rd_fmd_max_num(char *page, char **start, off_t off,
241 int count, int *eof, void *data)
243 struct obd_device *obd = data;
246 rc = snprintf(page, count, "%u\n", obd->u.filter.fo_fmd_max_num);
250 int lprocfs_filter_wr_fmd_max_num(struct file *file, const char *buffer,
251 unsigned long count, void *data)
253 struct obd_device *obd = data;
257 rc = lprocfs_write_helper(buffer, count, &val);
261 if (val > 65536 || val < 1)
264 obd->u.filter.fo_fmd_max_num = val;
268 int lprocfs_filter_rd_fmd_max_age(char *page, char **start, off_t off,
269 int count, int *eof, void *data)
271 struct obd_device *obd = data;
274 rc = snprintf(page, count, "%u\n", obd->u.filter.fo_fmd_max_age / HZ);
278 int lprocfs_filter_wr_fmd_max_age(struct file *file, const char *buffer,
279 unsigned long count, void *data)
281 struct obd_device *obd = data;
285 rc = lprocfs_write_helper(buffer, count, &val);
289 if (val > 65536 || val < 1)
292 obd->u.filter.fo_fmd_max_age = val * HZ;
296 static struct lprocfs_vars lprocfs_obd_vars[] = {
297 { "uuid", lprocfs_rd_uuid, 0, 0 },
298 { "blocksize", lprocfs_rd_blksize, 0, 0 },
299 { "kbytestotal", lprocfs_rd_kbytestotal, 0, 0 },
300 { "kbytesfree", lprocfs_rd_kbytesfree, 0, 0 },
301 { "kbytesavail", lprocfs_rd_kbytesavail, 0, 0 },
302 { "filestotal", lprocfs_rd_filestotal, 0, 0 },
303 { "filesfree", lprocfs_rd_filesfree, 0, 0 },
304 { "filegroups", lprocfs_filter_rd_groups, 0, 0 },
305 { "fstype", lprocfs_rd_fstype, 0, 0 },
306 { "mntdev", lprocfs_filter_rd_mntdev, 0, 0 },
307 { "last_id", lprocfs_filter_rd_last_id,0, 0 },
308 { "tot_dirty", lprocfs_filter_rd_tot_dirty, 0, 0 },
309 { "tot_pending", lprocfs_filter_rd_tot_pending, 0, 0 },
310 { "tot_granted", lprocfs_filter_rd_tot_granted, 0, 0 },
311 { "recovery_status", lprocfs_obd_rd_recovery_status, 0, 0 },
312 { "evict_client", 0, lprocfs_wr_evict_client, 0 },
313 { "num_exports", lprocfs_rd_num_exports, 0, 0 },
314 { "readcache_max_filesize",
315 lprocfs_filter_rd_readcache,
316 lprocfs_filter_wr_readcache, 0 },
317 #ifdef HAVE_QUOTA_SUPPORT
318 { "quota_bunit_sz", lprocfs_filter_rd_bunit,
319 lprocfs_filter_wr_bunit, 0},
320 { "quota_btune_sz", lprocfs_filter_rd_btune,
321 lprocfs_filter_wr_btune, 0},
322 { "quota_iunit_sz", lprocfs_filter_rd_iunit,
323 lprocfs_filter_wr_iunit, 0},
324 { "quota_itune_sz", lprocfs_filter_rd_itune,
325 lprocfs_filter_wr_itune, 0},
327 { "client_cache_count", lprocfs_filter_rd_fmd_max_num,
328 lprocfs_filter_wr_fmd_max_num, 0 },
329 { "client_cache_seconds", lprocfs_filter_rd_fmd_max_age,
330 lprocfs_filter_wr_fmd_max_age, 0 },
334 static struct lprocfs_vars lprocfs_module_vars[] = {
335 { "num_refs", lprocfs_rd_numrefs, 0, 0 },
339 void filter_tally_write(struct filter_obd *filter, struct page **pages,
340 int nr_pages, unsigned long *blocks, int blocks_per_page)
342 struct page *last_page = NULL;
343 unsigned long *last_block = NULL;
344 unsigned long discont_pages = 0;
345 unsigned long discont_blocks = 0;
351 lprocfs_oh_tally_log2(&filter->fo_w_pages, nr_pages);
353 while (nr_pages-- > 0) {
354 if (last_page && (*pages)->index != (last_page->index + 1))
358 for (i = 0; i < blocks_per_page; i++) {
359 if (last_block && *blocks != (*last_block + 1))
361 last_block = blocks++;
365 lprocfs_oh_tally(&filter->fo_w_discont_pages, discont_pages);
366 lprocfs_oh_tally(&filter->fo_w_discont_blocks, discont_blocks);
369 void filter_tally_read(struct filter_obd *filter, struct niobuf_local *lnb,
372 struct niobuf_local *end;
373 struct page *last_page = NULL;
374 unsigned long discont_pages = 0;
375 unsigned long discont_blocks = 0;
380 for (end = lnb + niocount; lnb < end && lnb->page; lnb++) {
381 struct page *page = lnb->page;
383 if (page->index != (last_page->index + 1))
385 /* XXX not so smart for now */
386 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
387 if ((page->buffers && last_page->buffers) &&
388 (page->buffers->b_blocknr !=
389 (last_page->buffers->b_blocknr + 1)))
392 #warning "port on 2.6 -bzzz"
398 lprocfs_oh_tally_log2(&filter->fo_r_pages, niocount);
399 lprocfs_oh_tally(&filter->fo_r_discont_pages, discont_pages);
400 lprocfs_oh_tally(&filter->fo_r_discont_blocks, discont_blocks);
403 #define pct(a,b) (b ? a * 100 / b : 0)
405 static int filter_brw_stats_seq_show(struct seq_file *seq, void *v)
408 struct obd_device *dev = seq->private;
409 struct filter_obd *filter = &dev->u.filter;
410 unsigned long read_tot = 0, write_tot = 0, read_cum, write_cum;
413 do_gettimeofday(&now);
415 /* this sampling races with updates */
417 seq_printf(seq, "snapshot_time: %lu.%lu (secs.usecs)\n",
418 now.tv_sec, now.tv_usec);
420 seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
421 seq_printf(seq, "pages per brw brws %% cum %% |");
422 seq_printf(seq, " rpcs %% cum %%\n");
424 read_tot = lprocfs_oh_sum(&filter->fo_r_pages);
425 write_tot = lprocfs_oh_sum(&filter->fo_w_pages);
429 for (i = 0; i < OBD_HIST_MAX; i++) {
430 unsigned long r = filter->fo_r_pages.oh_buckets[i];
431 unsigned long w = filter->fo_w_pages.oh_buckets[i];
434 seq_printf(seq, "%u:\t\t%10lu %3lu %3lu | %10lu %3lu %3lu\n",
435 1 << i, r, pct(r, read_tot),
436 pct(read_cum, read_tot), w,
438 pct(write_cum, write_tot));
439 if (read_cum == read_tot && write_cum == write_tot)
443 seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
444 seq_printf(seq, "discont pages rpcs %% cum %% |");
445 seq_printf(seq, " rpcs %% cum %%\n");
447 read_tot = lprocfs_oh_sum(&filter->fo_r_discont_pages);
448 write_tot = lprocfs_oh_sum(&filter->fo_w_discont_pages);
453 for (i = 0; i < OBD_HIST_MAX; i++) {
454 unsigned long r = filter->fo_r_discont_pages.oh_buckets[i];
455 unsigned long w = filter->fo_w_discont_pages.oh_buckets[i];
458 seq_printf(seq, "%u:\t\t%10lu %3lu %3lu | %10lu %3lu %3lu\n",
459 i, r, pct(r, read_tot),
460 pct(read_cum, read_tot), w,
462 pct(write_cum, write_tot));
463 if (read_cum == read_tot && write_cum == write_tot)
467 seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
468 seq_printf(seq, "discont blocks rpcs %% cum %% |");
469 seq_printf(seq, " rpcs %% cum %%\n");
471 read_tot = lprocfs_oh_sum(&filter->fo_r_discont_blocks);
472 write_tot = lprocfs_oh_sum(&filter->fo_w_discont_blocks);
476 for (i = 0; i < OBD_HIST_MAX; i++) {
477 unsigned long r = filter->fo_r_discont_blocks.oh_buckets[i];
478 unsigned long w = filter->fo_w_discont_blocks.oh_buckets[i];
481 seq_printf(seq, "%u:\t\t%10lu %3lu %3lu | %10lu %3lu %3lu\n",
482 i, r, pct(r, read_tot),
483 pct(read_cum, read_tot), w,
485 pct(write_cum, write_tot));
486 if (read_cum == read_tot && write_cum == write_tot)
490 seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
491 seq_printf(seq, "dio frags rpcs %% cum %% |");
492 seq_printf(seq, " rpcs %% cum %%\n");
494 read_tot = lprocfs_oh_sum(&filter->fo_r_dio_frags);
495 write_tot = lprocfs_oh_sum(&filter->fo_w_dio_frags);
499 for (i = 0; i < OBD_HIST_MAX; i++) {
500 unsigned long r = filter->fo_r_dio_frags.oh_buckets[i];
501 unsigned long w = filter->fo_w_dio_frags.oh_buckets[i];
504 seq_printf(seq, "%u:\t\t%10lu %3lu %3lu | %10lu %3lu %3lu\n",
505 i, r, pct(r, read_tot),
506 pct(read_cum, read_tot), w,
508 pct(write_cum, write_tot));
509 if (read_cum == read_tot && write_cum == write_tot)
513 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
514 seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
515 seq_printf(seq, "disk ios in flight ios %% cum %% |");
516 seq_printf(seq, " rpcs %% cum %%\n");
518 read_tot = lprocfs_oh_sum(&filter->fo_read_rpc_hist);
519 write_tot = lprocfs_oh_sum(&filter->fo_write_rpc_hist);
523 for (i = 0; i < OBD_HIST_MAX; i++) {
524 unsigned long r = filter->fo_read_rpc_hist.oh_buckets[i];
525 unsigned long w = filter->fo_write_rpc_hist.oh_buckets[i];
528 seq_printf(seq, "%u:\t\t%10lu %3lu %3lu | %10lu %3lu %3lu\n",
529 i, r, pct(r, read_tot),
530 pct(read_cum, read_tot), w,
532 pct(write_cum, write_tot));
533 if (read_cum == read_tot && write_cum == write_tot)
537 seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
538 seq_printf(seq, "io time (1/%ds) rpcs %% cum %% |", HZ);
539 seq_printf(seq, " rpcs %% cum %%\n");
541 read_tot = lprocfs_oh_sum(&filter->fo_r_io_time);
542 write_tot = lprocfs_oh_sum(&filter->fo_w_io_time);
546 for (i = 0; i < OBD_HIST_MAX; i++) {
547 unsigned long r = filter->fo_r_io_time.oh_buckets[i];
548 unsigned long w = filter->fo_w_io_time.oh_buckets[i];
551 seq_printf(seq, "%u:\t\t%10lu %3lu %3lu | %10lu %3lu %3lu\n",
552 1 << i, r, pct(r, read_tot),
553 pct(read_cum, read_tot), w,
555 pct(write_cum, write_tot));
556 if (read_cum == read_tot && write_cum == write_tot)
560 seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
561 seq_printf(seq, "disk I/O size count %% cum %% |");
562 seq_printf(seq, " count %% cum %%\n");
564 read_tot = lprocfs_oh_sum(&filter->fo_r_disk_iosize);
565 write_tot = lprocfs_oh_sum(&filter->fo_w_disk_iosize);
569 for (i = 0; i < OBD_HIST_MAX; i++) {
570 unsigned long r = filter->fo_r_disk_iosize.oh_buckets[i];
571 unsigned long w = filter->fo_w_disk_iosize.oh_buckets[i];
575 if (read_cum == 0 && write_cum == 0)
579 seq_printf(seq, "%u", 1<<i);
581 seq_printf(seq, "%uK", 1<<(i-10));
583 seq_printf(seq, "%uM", 1<<(i-20));
585 seq_printf(seq, ":\t\t%10lu %3lu %3lu | %10lu %3lu %3lu\n",
586 r, pct(r, read_tot), pct(read_cum, read_tot),
587 w, pct(w, write_tot), pct(write_cum, write_tot));
588 if (read_cum == read_tot && write_cum == write_tot)
597 static void *filter_brw_stats_seq_start(struct seq_file *p, loff_t *pos)
603 static void *filter_brw_stats_seq_next(struct seq_file *p, void *v, loff_t *pos)
608 static void filter_brw_stats_seq_stop(struct seq_file *p, void *v)
611 struct seq_operations filter_brw_stats_seq_sops = {
612 .start = filter_brw_stats_seq_start,
613 .stop = filter_brw_stats_seq_stop,
614 .next = filter_brw_stats_seq_next,
615 .show = filter_brw_stats_seq_show,
618 static int filter_brw_stats_seq_open(struct inode *inode, struct file *file)
620 struct proc_dir_entry *dp = PDE(inode);
621 struct seq_file *seq;
624 rc = seq_open(file, &filter_brw_stats_seq_sops);
627 seq = file->private_data;
628 seq->private = dp->data;
632 static ssize_t filter_brw_stats_seq_write(struct file *file, const char *buf,
633 size_t len, loff_t *off)
635 struct seq_file *seq = file->private_data;
636 struct obd_device *dev = seq->private;
637 struct filter_obd *filter = &dev->u.filter;
639 lprocfs_oh_clear(&filter->fo_r_pages);
640 lprocfs_oh_clear(&filter->fo_w_pages);
641 lprocfs_oh_clear(&filter->fo_read_rpc_hist);
642 lprocfs_oh_clear(&filter->fo_write_rpc_hist);
643 lprocfs_oh_clear(&filter->fo_r_io_time);
644 lprocfs_oh_clear(&filter->fo_w_io_time);
645 lprocfs_oh_clear(&filter->fo_r_discont_pages);
646 lprocfs_oh_clear(&filter->fo_w_discont_pages);
647 lprocfs_oh_clear(&filter->fo_r_discont_blocks);
648 lprocfs_oh_clear(&filter->fo_w_discont_blocks);
649 lprocfs_oh_clear(&filter->fo_r_disk_iosize);
650 lprocfs_oh_clear(&filter->fo_w_disk_iosize);
651 lprocfs_oh_clear(&filter->fo_r_dio_frags);
652 lprocfs_oh_clear(&filter->fo_w_dio_frags);
657 struct file_operations filter_brw_stats_fops = {
658 .owner = THIS_MODULE,
659 .open = filter_brw_stats_seq_open,
661 .write = filter_brw_stats_seq_write,
663 .release = seq_release,
666 int lproc_filter_attach_seqstat(struct obd_device *dev)
668 return lprocfs_obd_seq_create(dev, "brw_stats", 0444,
669 &filter_brw_stats_fops, dev);
672 LPROCFS_INIT_VARS(filter, lprocfs_module_vars, lprocfs_obd_vars)