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 Lustre, http://www.lustre.org.
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.
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.
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.
22 #define DEBUG_SUBSYSTEM S_CLASS
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>
30 #include "filter_internal.h"
33 static struct lprocfs_vars lprocfs_obd_vars[] = { {0} };
34 static struct lprocfs_vars lprocfs_module_vars[] = { {0} };
37 static int lprocfs_filter_rd_groups(char *page, char **start, off_t off,
38 int count, int *eof, void *data)
41 return snprintf(page, count, "%u\n", FILTER_GROUPS);
44 static int lprocfs_filter_rd_tot_dirty(char *page, char **start, off_t off,
45 int count, int *eof, void *data)
47 struct obd_device *obd = (struct obd_device *)data;
51 return snprintf(page, count, LPU64"\n", obd->u.filter.fo_tot_dirty);
54 static int lprocfs_filter_rd_tot_granted(char *page, char **start, off_t off,
55 int count, int *eof, void *data)
57 struct obd_device *obd = (struct obd_device *)data;
61 return snprintf(page, count, LPU64"\n", obd->u.filter.fo_tot_granted);
64 static int lprocfs_filter_rd_tot_pending(char *page, char **start, off_t off,
65 int count, int *eof, void *data)
67 struct obd_device *obd = (struct obd_device *)data;
71 return snprintf(page, count, LPU64"\n", obd->u.filter.fo_tot_pending);
74 static int lprocfs_filter_rd_mntdev(char *page, char **start, off_t off,
75 int count, int *eof, void *data)
77 struct obd_device *obd = (struct obd_device *)data;
80 LASSERT(obd->u.filter.fo_vfsmnt->mnt_devname);
82 return snprintf(page, count, "%s\n",
83 obd->u.filter.fo_vfsmnt->mnt_devname);
86 static int lprocfs_filter_rd_last_id(char *page, char **start, off_t off,
87 int count, int *eof, void *data)
89 struct obd_device *obd = data;
94 return snprintf(page, count, LPU64"\n",
95 filter_last_id(&obd->u.filter, 0));
98 int lprocfs_filter_rd_readcache(char *page, char **start, off_t off, int count,
101 struct obd_device *obd = data;
104 rc = snprintf(page, count, LPU64"\n",
105 obd->u.filter.fo_readcache_max_filesize);
109 int lprocfs_filter_wr_readcache(struct file *file, const char *buffer,
110 unsigned long count, void *data)
112 struct obd_device *obd = data;
116 rc = lprocfs_write_u64_helper(buffer, count, &val);
120 obd->u.filter.fo_readcache_max_filesize = val;
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.filter.fo_quota_ctxt.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.filter.fo_quota_ctxt.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.filter.fo_quota_ctxt.lqc_btune_sz)
159 obd->u.filter.fo_quota_ctxt.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.filter.fo_quota_ctxt.lqc_itune_sz)
177 obd->u.filter.fo_quota_ctxt.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.filter.fo_quota_ctxt.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.filter.fo_quota_ctxt.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.filter.fo_quota_ctxt.lqc_bunit_sz)
216 obd->u.filter.fo_quota_ctxt.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.filter.fo_quota_ctxt.lqc_iunit_sz)
235 obd->u.filter.fo_quota_ctxt.lqc_itune_sz = val;
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},
272 static struct lprocfs_vars lprocfs_module_vars[] = {
273 { "num_refs", lprocfs_rd_numrefs, 0, 0 },
277 void filter_tally_write(struct filter_obd *filter, struct page **pages,
278 int nr_pages, unsigned long *blocks, int blocks_per_page)
280 struct page *last_page = NULL;
281 unsigned long *last_block = NULL;
282 unsigned long discont_pages = 0;
283 unsigned long discont_blocks = 0;
289 lprocfs_oh_tally_log2(&filter->fo_w_pages, nr_pages);
291 while (nr_pages-- > 0) {
292 if (last_page && (*pages)->index != (last_page->index + 1))
296 for (i = 0; i < blocks_per_page; i++) {
297 if (last_block && *blocks != (*last_block + 1))
299 last_block = blocks++;
303 lprocfs_oh_tally(&filter->fo_w_discont_pages, discont_pages);
304 lprocfs_oh_tally(&filter->fo_w_discont_blocks, discont_blocks);
307 void filter_tally_read(struct filter_obd *filter, struct niobuf_local *lnb,
310 struct niobuf_local *end;
311 struct page *last_page = NULL;
312 unsigned long discont_pages = 0;
313 unsigned long discont_blocks = 0;
318 for (end = lnb + niocount; lnb < end && lnb->page; lnb++) {
319 struct page *page = lnb->page;
321 if (page->index != (last_page->index + 1))
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)))
330 #warning "port on 2.6 -bzzz"
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);
341 #define pct(a,b) (b ? a * 100 / b : 0)
343 static int filter_brw_stats_seq_show(struct seq_file *seq, void *v)
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;
351 do_gettimeofday(&now);
353 /* this sampling races with updates */
355 seq_printf(seq, "snapshot_time: %lu.%lu (secs.usecs)\n",
356 now.tv_sec, now.tv_usec);
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");
362 read_tot = lprocfs_oh_sum(&filter->fo_r_pages);
363 write_tot = lprocfs_oh_sum(&filter->fo_w_pages);
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];
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,
376 pct(write_cum, write_tot));
377 if (read_cum == read_tot && write_cum == write_tot)
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");
385 read_tot = lprocfs_oh_sum(&filter->fo_r_discont_pages);
386 write_tot = lprocfs_oh_sum(&filter->fo_w_discont_pages);
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];
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,
400 pct(write_cum, write_tot));
401 if (read_cum == read_tot && write_cum == write_tot)
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");
409 read_tot = lprocfs_oh_sum(&filter->fo_r_discont_blocks);
410 write_tot = lprocfs_oh_sum(&filter->fo_w_discont_blocks);
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];
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,
423 pct(write_cum, write_tot));
424 if (read_cum == read_tot && write_cum == write_tot)
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");
433 read_tot = lprocfs_oh_sum(&filter->fo_read_rpc_hist);
434 write_tot = lprocfs_oh_sum(&filter->fo_write_rpc_hist);
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];
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,
447 pct(write_cum, write_tot));
448 if (read_cum == read_tot && write_cum == write_tot)
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");
456 read_tot = lprocfs_oh_sum(&filter->fo_r_io_time);
457 write_tot = lprocfs_oh_sum(&filter->fo_w_io_time);
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];
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,
470 pct(write_cum, write_tot));
471 if (read_cum == read_tot && write_cum == write_tot)
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");
479 read_tot = lprocfs_oh_sum(&filter->fo_r_disk_iosize);
480 write_tot = lprocfs_oh_sum(&filter->fo_w_disk_iosize);
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];
490 if (read_cum == 0 && write_cum == 0)
494 seq_printf(seq, "%u", 1<<i);
496 seq_printf(seq, "%uK", 1<<(i-10));
498 seq_printf(seq, "%uM", 1<<(i-20));
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)
512 static void *filter_brw_stats_seq_start(struct seq_file *p, loff_t *pos)
518 static void *filter_brw_stats_seq_next(struct seq_file *p, void *v, loff_t *pos)
523 static void filter_brw_stats_seq_stop(struct seq_file *p, void *v)
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,
533 static int filter_brw_stats_seq_open(struct inode *inode, struct file *file)
535 struct proc_dir_entry *dp = PDE(inode);
536 struct seq_file *seq;
539 rc = seq_open(file, &filter_brw_stats_seq_sops);
542 seq = file->private_data;
543 seq->private = dp->data;
547 static ssize_t filter_brw_stats_seq_write(struct file *file, const char *buf,
548 size_t len, loff_t *off)
550 struct seq_file *seq = file->private_data;
551 struct obd_device *dev = seq->private;
552 struct filter_obd *filter = &dev->u.filter;
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);
570 struct file_operations filter_brw_stats_fops = {
571 .owner = THIS_MODULE,
572 .open = filter_brw_stats_seq_open,
574 .write = filter_brw_stats_seq_write,
576 .release = seq_release,
579 int lproc_filter_attach_seqstat(struct obd_device *dev)
581 return lprocfs_obd_seq_create(dev, "brw_stats", 0444,
582 &filter_brw_stats_fops, dev);
588 LPROCFS_INIT_VARS(filter, lprocfs_module_vars, lprocfs_obd_vars)