Whamcloud - gitweb
- use separate create lock for each group on OST, so that, creates from different...
[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 the Lustre file system, http://www.lustre.org
7  *   Lustre is a trademark of Cluster File Systems, Inc.
8  *
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.
13  *
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.
18  *
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.
23  *
24  */
25 #define DEBUG_SUBSYSTEM S_CLASS
26
27 #include <linux/version.h>
28 #include <lprocfs_status.h>
29 #include <obd.h>
30 #include <linux/seq_file.h>
31 #include <linux/version.h>
32
33 #include "filter_internal.h"
34
35 #ifdef LPROCFS
36 static int lprocfs_filter_rd_groups(char *page, char **start, off_t off,
37                                     int count, int *eof, void *data)
38 {
39         struct obd_device *obd = (struct obd_device *)data;
40         *eof = 1;
41         return snprintf(page, count, "%u\n", obd->u.filter.fo_group_count);
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         struct filter_obd *filter = &obd->u.filter;
91         int retval = 0, rc, i;
92
93         if (obd == NULL)
94                 return 0;
95
96         for (i = FILTER_GROUP_MDS0; i < filter->fo_group_count; i++) {
97                 rc = snprintf(page, count, LPU64"\n",filter_last_id(filter, i));
98                 if (rc < 0) {
99                         retval = rc;
100                         break;
101                 }
102                 page += rc;
103                 count -= rc;
104                 retval += rc;
105         }
106         return retval;
107 }
108
109 int lprocfs_filter_rd_readcache(char *page, char **start, off_t off, int count,
110                                 int *eof, void *data)
111 {
112         struct obd_device *obd = data;
113         int rc;
114
115         rc = snprintf(page, count, LPU64"\n",
116                       obd->u.filter.fo_readcache_max_filesize);
117         return rc;
118 }
119
120 int lprocfs_filter_wr_readcache(struct file *file, const char *buffer,
121                                 unsigned long count, void *data)
122 {
123         struct obd_device *obd = data;
124         __u64 val;
125         int rc;
126
127         rc = lprocfs_write_u64_helper(buffer, count, &val);
128         if (rc)
129                 return rc;
130
131         obd->u.filter.fo_readcache_max_filesize = val;
132         return count;
133 }
134
135 #ifdef HAVE_QUOTA_SUPPORT
136 static int lprocfs_filter_rd_bunit(char *page, char **start, off_t off, int count, 
137                                    int *eof, void *data)
138 {
139         struct obd_device *obd = (struct obd_device *)data;
140         LASSERT(obd != NULL);
141
142         return snprintf(page, count, "%lu\n", 
143                         obd->u.obt.obt_qctxt.lqc_bunit_sz);
144 }
145
146 static int lprocfs_filter_rd_iunit(char *page, char **start, off_t off, int count, 
147                                    int *eof, void *data)
148 {
149         struct obd_device *obd = (struct obd_device *)data;
150         LASSERT(obd != NULL);
151
152         return snprintf(page, count, "%lu\n", 
153                         obd->u.obt.obt_qctxt.lqc_iunit_sz);
154 }
155
156 static int lprocfs_filter_wr_bunit(struct file *file, const char *buffer,
157                                    unsigned long count, void *data)
158 {
159         struct obd_device *obd = (struct obd_device *)data;
160         int val, rc;
161         LASSERT(obd != NULL);
162
163         rc = lprocfs_write_helper(buffer, count, &val);
164         if (rc)
165                 return rc;
166
167         if (val % QUOTABLOCK_SIZE ||
168             val <= obd->u.obt.obt_qctxt.lqc_btune_sz)
169                 return -EINVAL;
170
171         obd->u.obt.obt_qctxt.lqc_bunit_sz = val;
172         return count;
173 }
174
175 static int lprocfs_filter_wr_iunit(struct file *file, const char *buffer,
176                                    unsigned long count, void *data)
177 {
178         struct obd_device *obd = (struct obd_device *)data;
179         int val, rc;
180         LASSERT(obd != NULL);
181
182         rc = lprocfs_write_helper(buffer, count, &val);
183         if (rc)
184                 return rc;
185
186         if (val <= obd->u.obt.obt_qctxt.lqc_itune_sz)
187                 return -EINVAL;
188
189         obd->u.obt.obt_qctxt.lqc_iunit_sz = val;
190         return count;
191 }
192
193 static int lprocfs_filter_rd_btune(char *page, char **start, off_t off, int count, 
194                                    int *eof, void *data)
195 {
196         struct obd_device *obd = (struct obd_device *)data;
197         LASSERT(obd != NULL);
198
199         return snprintf(page, count, "%lu\n", 
200                         obd->u.obt.obt_qctxt.lqc_btune_sz);
201 }
202
203 static int lprocfs_filter_rd_itune(char *page, char **start, off_t off, int count, 
204                                    int *eof, void *data)
205 {
206         struct obd_device *obd = (struct obd_device *)data;
207         LASSERT(obd != NULL);
208
209         return snprintf(page, count, "%lu\n", 
210                         obd->u.obt.obt_qctxt.lqc_itune_sz);
211 }
212
213 static int lprocfs_filter_wr_btune(struct file *file, const char *buffer,
214                                    unsigned long count, void *data)
215 {
216         struct obd_device *obd = (struct obd_device *)data;
217         int val, rc;
218         LASSERT(obd != NULL);
219
220         rc = lprocfs_write_helper(buffer, count, &val);
221         if (rc)
222                 return rc;
223         
224         if (val <= QUOTABLOCK_SIZE * MIN_QLIMIT || val % QUOTABLOCK_SIZE || 
225             val >= obd->u.obt.obt_qctxt.lqc_bunit_sz)
226                 return -EINVAL;
227
228         obd->u.obt.obt_qctxt.lqc_btune_sz = val;
229         return count;
230 }
231
232 static int lprocfs_filter_wr_itune(struct file *file, const char *buffer,
233                                    unsigned long count, void *data)
234 {
235         struct obd_device *obd = (struct obd_device *)data;
236         int val, rc;
237         LASSERT(obd != NULL);
238
239         rc = lprocfs_write_helper(buffer, count, &val);
240         if (rc)
241                 return rc;
242         
243         if (val <= MIN_QLIMIT || 
244             val >= obd->u.obt.obt_qctxt.lqc_iunit_sz)
245                 return -EINVAL;
246
247         obd->u.obt.obt_qctxt.lqc_itune_sz = val;
248         return count;
249 }
250 #endif
251
252 int lprocfs_filter_rd_fmd_max_num(char *page, char **start, off_t off,
253                                   int count, int *eof, void *data)
254 {
255         struct obd_device *obd = data;
256         int rc;
257
258         rc = snprintf(page, count, "%u\n", obd->u.filter.fo_fmd_max_num);
259         return rc;
260 }
261
262 int lprocfs_filter_wr_fmd_max_num(struct file *file, const char *buffer,
263                                   unsigned long count, void *data)
264 {
265         struct obd_device *obd = data;
266         int val;
267         int rc;
268
269         rc = lprocfs_write_helper(buffer, count, &val);
270         if (rc)
271                 return rc;
272
273         if (val > 65536 || val < 1)
274                 return -EINVAL;
275
276         obd->u.filter.fo_fmd_max_num = val;
277         return count;
278 }
279
280 int lprocfs_filter_rd_fmd_max_age(char *page, char **start, off_t off,
281                                   int count, int *eof, void *data)
282 {
283         struct obd_device *obd = data;
284         int rc;
285
286         rc = snprintf(page, count, "%u\n", obd->u.filter.fo_fmd_max_age / HZ);
287         return rc;
288 }
289
290 int lprocfs_filter_wr_fmd_max_age(struct file *file, const char *buffer,
291                                   unsigned long count, void *data)
292 {
293         struct obd_device *obd = data;
294         int val;
295         int rc;
296
297         rc = lprocfs_write_helper(buffer, count, &val);
298         if (rc)
299                 return rc;
300
301         if (val > 65536 || val < 1)
302                 return -EINVAL;
303
304         obd->u.filter.fo_fmd_max_age = val * HZ;
305         return count;
306 }
307
308 static int lprocfs_filter_rd_capa(char *page, char **start, off_t off,
309                                   int count, int *eof, void *data)
310 {
311         struct obd_device *obd = data;
312         int rc;
313
314         rc = snprintf(page, count, "capability on: %s\n",
315                       obd->u.filter.fo_fl_oss_capa ? "oss" : "");
316         return rc;
317 }
318
319 static int lprocfs_filter_wr_capa(struct file *file, const char *buffer,
320                                   unsigned long count, void *data)
321 {
322         struct obd_device *obd = data;
323         int val, rc;
324
325         rc = lprocfs_write_helper(buffer, count, &val);
326         if (rc)
327                 return rc;
328
329         if (val & ~0x1) {
330                 CERROR("invalid capability mode, only 0/1 are accepted.\n"
331                        " 1: enable oss fid capability\n"
332                        " 0: disable oss fid capability\n");
333                 return -EINVAL;
334         }
335
336         obd->u.filter.fo_fl_oss_capa = val;
337         return count;
338 }
339
340 static int lprocfs_filter_rd_capa_count(char *page, char **start, off_t off,
341                                         int count, int *eof, void *data)
342 {
343         return snprintf(page, count, "%d %d\n",
344                         capa_count[CAPA_SITE_CLIENT],
345                         capa_count[CAPA_SITE_SERVER]);
346 }
347
348 static struct lprocfs_vars lprocfs_obd_vars[] = {
349         { "uuid",         lprocfs_rd_uuid,          0, 0 },
350         { "blocksize",    lprocfs_rd_blksize,       0, 0 },
351         { "kbytestotal",  lprocfs_rd_kbytestotal,   0, 0 },
352         { "kbytesfree",   lprocfs_rd_kbytesfree,    0, 0 },
353         { "kbytesavail",  lprocfs_rd_kbytesavail,   0, 0 },
354         { "filestotal",   lprocfs_rd_filestotal,    0, 0 },
355         { "filesfree",    lprocfs_rd_filesfree,     0, 0 },
356         { "filegroups",   lprocfs_filter_rd_groups, 0, 0 },
357         { "fstype",       lprocfs_rd_fstype,        0, 0 },
358         { "mntdev",       lprocfs_filter_rd_mntdev, 0, 0 },
359         { "last_id",      lprocfs_filter_rd_last_id,0, 0 },
360         { "tot_dirty",    lprocfs_filter_rd_tot_dirty,   0, 0 },
361         { "tot_pending",  lprocfs_filter_rd_tot_pending, 0, 0 },
362         { "tot_granted",  lprocfs_filter_rd_tot_granted, 0, 0 },
363         { "recovery_status", lprocfs_obd_rd_recovery_status, 0, 0 },
364         { "evict_client", 0, lprocfs_wr_evict_client, 0 },
365         { "num_exports",  lprocfs_rd_num_exports,   0, 0 },
366         { "readcache_max_filesize",
367                           lprocfs_filter_rd_readcache,
368                           lprocfs_filter_wr_readcache, 0 },
369 #ifdef HAVE_QUOTA_SUPPORT
370         { "quota_bunit_sz", lprocfs_filter_rd_bunit,
371                             lprocfs_filter_wr_bunit, 0},
372         { "quota_btune_sz", lprocfs_filter_rd_btune,
373                             lprocfs_filter_wr_btune, 0},
374         { "quota_iunit_sz", lprocfs_filter_rd_iunit,
375                             lprocfs_filter_wr_iunit, 0},
376         { "quota_itune_sz", lprocfs_filter_rd_itune,
377                             lprocfs_filter_wr_itune, 0},
378 #endif
379         { "client_cache_count", lprocfs_filter_rd_fmd_max_num,
380                           lprocfs_filter_wr_fmd_max_num, 0 },
381         { "client_cache_seconds", lprocfs_filter_rd_fmd_max_age,
382                           lprocfs_filter_wr_fmd_max_age, 0 },
383         { "capa",         lprocfs_filter_rd_capa,
384                           lprocfs_filter_wr_capa, 0 },
385         { "capa_count",   lprocfs_filter_rd_capa_count, 0, 0 },
386         { 0 }
387 };
388
389 static struct lprocfs_vars lprocfs_module_vars[] = {
390         { "num_refs",     lprocfs_rd_numrefs,       0, 0 },
391         { 0 }
392 };
393
394 void filter_tally_write(struct filter_obd *filter, struct page **pages,
395                      int nr_pages, unsigned long *blocks, int blocks_per_page)
396 {
397         struct page *last_page = NULL;
398         unsigned long *last_block = NULL;
399         unsigned long discont_pages = 0;
400         unsigned long discont_blocks = 0;
401         int i;
402
403         if (nr_pages == 0)
404                 return;
405
406         lprocfs_oh_tally_log2(&filter->fo_w_pages, nr_pages);
407
408         while (nr_pages-- > 0) {
409                 if (last_page && (*pages)->index != (last_page->index + 1))
410                         discont_pages++;
411                 last_page = *pages;
412                 pages++;
413                 for (i = 0; i < blocks_per_page; i++) {
414                         if (last_block && *blocks != (*last_block + 1))
415                                 discont_blocks++;
416                         last_block = blocks++;
417                 }
418         }
419
420         lprocfs_oh_tally(&filter->fo_w_discont_pages, discont_pages);
421         lprocfs_oh_tally(&filter->fo_w_discont_blocks, discont_blocks);
422 }
423
424 void filter_tally_read(struct filter_obd *filter, struct niobuf_local *lnb,
425                        int niocount)
426 {
427         struct niobuf_local *end;
428         struct page *last_page = NULL;
429         unsigned long discont_pages = 0;
430         unsigned long discont_blocks = 0;
431
432         if (niocount == 0)
433                 return;
434
435         for (end = lnb + niocount; lnb < end && lnb->page; lnb++) {
436                 struct page *page = lnb->page;
437                 if (last_page) {
438                        if (page->index != (last_page->index + 1))
439                                 discont_pages++;
440                         /* XXX not so smart for now */
441 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
442                         if ((page->buffers && last_page->buffers) &&
443                             (page->buffers->b_blocknr !=
444                              (last_page->buffers->b_blocknr + 1)))
445                                 discont_blocks++;
446 #else
447 #warning "port on 2.6 -bzzz"
448 #endif
449                 }
450                 last_page = page;
451         }
452
453         lprocfs_oh_tally_log2(&filter->fo_r_pages, niocount);
454         lprocfs_oh_tally(&filter->fo_r_discont_pages, discont_pages);
455         lprocfs_oh_tally(&filter->fo_r_discont_blocks, discont_blocks);
456 }
457
458 #define pct(a,b) (b ? a * 100 / b : 0)
459
460 static int filter_brw_stats_seq_show(struct seq_file *seq, void *v)
461 {
462         struct timeval now;
463         struct obd_device *dev = seq->private;
464         struct filter_obd *filter = &dev->u.filter;
465         unsigned long read_tot = 0, write_tot = 0, read_cum, write_cum;
466         int i;
467
468         do_gettimeofday(&now);
469
470         /* this sampling races with updates */
471
472         seq_printf(seq, "snapshot_time:         %lu.%lu (secs.usecs)\n",
473                    now.tv_sec, now.tv_usec);
474
475         seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
476         seq_printf(seq, "pages per brw         brws   %% cum %% |");
477         seq_printf(seq, "       rpcs   %% cum %%\n");
478
479         read_tot = lprocfs_oh_sum(&filter->fo_r_pages);
480         write_tot = lprocfs_oh_sum(&filter->fo_w_pages);
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_pages.oh_buckets[i];
486                 unsigned long w = filter->fo_w_pages.oh_buckets[i];
487                 read_cum += r;
488                 write_cum += w;
489                 seq_printf(seq, "%u:\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
490                                  1 << i, r, pct(r, read_tot),
491                                  pct(read_cum, read_tot), w,
492                                  pct(w, write_tot),
493                                  pct(write_cum, write_tot));
494                 if (read_cum == read_tot && write_cum == write_tot)
495                         break;
496         }
497
498         seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
499         seq_printf(seq, "discont pages         rpcs   %% cum %% |");
500         seq_printf(seq, "       rpcs   %% cum %%\n");
501
502         read_tot = lprocfs_oh_sum(&filter->fo_r_discont_pages);
503         write_tot = lprocfs_oh_sum(&filter->fo_w_discont_pages);
504
505         read_cum = 0;
506         write_cum = 0;
507
508         for (i = 0; i < OBD_HIST_MAX; i++) {
509                 unsigned long r = filter->fo_r_discont_pages.oh_buckets[i];
510                 unsigned long w = filter->fo_w_discont_pages.oh_buckets[i];
511                 read_cum += r;
512                 write_cum += w;
513                 seq_printf(seq, "%u:\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
514                                  i, r, pct(r, read_tot),
515                                  pct(read_cum, read_tot), w,
516                                  pct(w, write_tot),
517                                  pct(write_cum, write_tot));
518                 if (read_cum == read_tot && write_cum == write_tot)
519                         break;
520         }
521
522         seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
523         seq_printf(seq, "discont blocks        rpcs   %% cum %% |");
524         seq_printf(seq, "       rpcs   %% cum %%\n");
525
526         read_tot = lprocfs_oh_sum(&filter->fo_r_discont_blocks);
527         write_tot = lprocfs_oh_sum(&filter->fo_w_discont_blocks);
528
529         read_cum = 0;
530         write_cum = 0;
531         for (i = 0; i < OBD_HIST_MAX; i++) {
532                 unsigned long r = filter->fo_r_discont_blocks.oh_buckets[i];
533                 unsigned long w = filter->fo_w_discont_blocks.oh_buckets[i];
534                 read_cum += r;
535                 write_cum += w;
536                 seq_printf(seq, "%u:\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
537                                  i, r, pct(r, read_tot),
538                                  pct(read_cum, read_tot), w,
539                                  pct(w, write_tot),
540                                  pct(write_cum, write_tot));
541                 if (read_cum == read_tot && write_cum == write_tot)
542                         break;
543         }
544
545         seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
546         seq_printf(seq, "dio frags             rpcs   %% cum %% |");
547         seq_printf(seq, "       rpcs   %% cum %%\n");
548
549         read_tot = lprocfs_oh_sum(&filter->fo_r_dio_frags);
550         write_tot = lprocfs_oh_sum(&filter->fo_w_dio_frags);
551
552         read_cum = 0;
553         write_cum = 0;
554         for (i = 0; i < OBD_HIST_MAX; i++) {
555                 unsigned long r = filter->fo_r_dio_frags.oh_buckets[i];
556                 unsigned long w = filter->fo_w_dio_frags.oh_buckets[i];
557                 read_cum += r;
558                 write_cum += w;
559                 seq_printf(seq, "%u:\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
560                                  i, r, pct(r, read_tot),
561                                  pct(read_cum, read_tot), w,
562                                  pct(w, write_tot),
563                                  pct(write_cum, write_tot));
564                 if (read_cum == read_tot && write_cum == write_tot)
565                         break;
566         }
567
568 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
569         seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
570         seq_printf(seq, "disk ios in flight     ios   %% cum %% |");
571         seq_printf(seq, "       rpcs   %% cum %%\n");
572
573         read_tot = lprocfs_oh_sum(&filter->fo_read_rpc_hist);
574         write_tot = lprocfs_oh_sum(&filter->fo_write_rpc_hist);
575
576         read_cum = 0;
577         write_cum = 0;
578         for (i = 0; i < OBD_HIST_MAX; i++) {
579                 unsigned long r = filter->fo_read_rpc_hist.oh_buckets[i];
580                 unsigned long w = filter->fo_write_rpc_hist.oh_buckets[i];
581                 read_cum += r;
582                 write_cum += w;
583                 seq_printf(seq, "%u:\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
584                                  i, r, pct(r, read_tot),
585                                  pct(read_cum, read_tot), w,
586                                  pct(w, write_tot),
587                                  pct(write_cum, write_tot));
588                 if (read_cum == read_tot && write_cum == write_tot)
589                         break;
590         }
591
592         seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
593         seq_printf(seq, "io time (1/%ds)     rpcs   %% cum %% |", HZ);
594         seq_printf(seq, "       rpcs   %% cum %%\n");
595
596         read_tot = lprocfs_oh_sum(&filter->fo_r_io_time);
597         write_tot = lprocfs_oh_sum(&filter->fo_w_io_time);
598
599         read_cum = 0;
600         write_cum = 0;
601         for (i = 0; i < OBD_HIST_MAX; i++) {
602                 unsigned long r = filter->fo_r_io_time.oh_buckets[i];
603                 unsigned long w = filter->fo_w_io_time.oh_buckets[i];
604                 read_cum += r;
605                 write_cum += w;
606                 seq_printf(seq, "%u:\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
607                                  1 << i, r, pct(r, read_tot),
608                                  pct(read_cum, read_tot), w,
609                                  pct(w, write_tot),
610                                  pct(write_cum, write_tot));
611                 if (read_cum == read_tot && write_cum == write_tot)
612                         break;
613         }
614
615         seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
616         seq_printf(seq, "disk I/O size         count  %% cum %% |");
617         seq_printf(seq, "       count  %% cum %%\n");
618
619         read_tot = lprocfs_oh_sum(&filter->fo_r_disk_iosize);
620         write_tot = lprocfs_oh_sum(&filter->fo_w_disk_iosize);
621
622         read_cum = 0;
623         write_cum = 0;
624         for (i = 0; i < OBD_HIST_MAX; i++) {
625                 unsigned long r = filter->fo_r_disk_iosize.oh_buckets[i];
626                 unsigned long w = filter->fo_w_disk_iosize.oh_buckets[i];
627
628                 read_cum += r;
629                 write_cum += w;
630                 if (read_cum == 0 && write_cum == 0)
631                         continue;
632
633                 if (i < 10)
634                         seq_printf(seq, "%u", 1<<i);
635                 else if (i < 20)
636                         seq_printf(seq, "%uK", 1<<(i-10));
637                 else
638                         seq_printf(seq, "%uM", 1<<(i-20));
639
640                 seq_printf(seq, ":\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
641                            r, pct(r, read_tot), pct(read_cum, read_tot),
642                            w, pct(w, write_tot), pct(write_cum, write_tot));
643                 if (read_cum == read_tot && write_cum == write_tot)
644                         break;
645         }
646 #endif
647
648         return 0;
649 }
650 #undef pct
651
652 static void *filter_brw_stats_seq_start(struct seq_file *p, loff_t *pos)
653 {
654         if (*pos == 0)
655                 return (void *)1;
656         return NULL;
657 }
658 static void *filter_brw_stats_seq_next(struct seq_file *p, void *v, loff_t *pos)
659 {
660         ++*pos;
661         return NULL;
662 }
663 static void filter_brw_stats_seq_stop(struct seq_file *p, void *v)
664 {
665 }
666 struct seq_operations filter_brw_stats_seq_sops = {
667         .start = filter_brw_stats_seq_start,
668         .stop = filter_brw_stats_seq_stop,
669         .next = filter_brw_stats_seq_next,
670         .show = filter_brw_stats_seq_show,
671 };
672
673 static int filter_brw_stats_seq_open(struct inode *inode, struct file *file)
674 {
675         struct proc_dir_entry *dp = PDE(inode);
676         struct seq_file *seq;
677         int rc;
678
679         rc = seq_open(file, &filter_brw_stats_seq_sops);
680         if (rc)
681                 return rc;
682         seq = file->private_data;
683         seq->private = dp->data;
684         return 0;
685 }
686
687 static ssize_t filter_brw_stats_seq_write(struct file *file, const char *buf,
688                                        size_t len, loff_t *off)
689 {
690         struct seq_file *seq = file->private_data;
691         struct obd_device *dev = seq->private;
692         struct filter_obd *filter = &dev->u.filter;
693
694         lprocfs_oh_clear(&filter->fo_r_pages);
695         lprocfs_oh_clear(&filter->fo_w_pages);
696         lprocfs_oh_clear(&filter->fo_read_rpc_hist);
697         lprocfs_oh_clear(&filter->fo_write_rpc_hist);
698         lprocfs_oh_clear(&filter->fo_r_io_time);
699         lprocfs_oh_clear(&filter->fo_w_io_time);
700         lprocfs_oh_clear(&filter->fo_r_discont_pages);
701         lprocfs_oh_clear(&filter->fo_w_discont_pages);
702         lprocfs_oh_clear(&filter->fo_r_discont_blocks);
703         lprocfs_oh_clear(&filter->fo_w_discont_blocks);
704         lprocfs_oh_clear(&filter->fo_r_disk_iosize);
705         lprocfs_oh_clear(&filter->fo_w_disk_iosize);
706         lprocfs_oh_clear(&filter->fo_r_dio_frags);
707         lprocfs_oh_clear(&filter->fo_w_dio_frags);
708
709         return len;
710 }
711
712 struct file_operations filter_brw_stats_fops = {
713         .owner   = THIS_MODULE,
714         .open    = filter_brw_stats_seq_open,
715         .read    = seq_read,
716         .write   = filter_brw_stats_seq_write,
717         .llseek  = seq_lseek,
718         .release = seq_release,
719 };
720
721 int lproc_filter_attach_seqstat(struct obd_device *dev)
722 {
723         return lprocfs_obd_seq_create(dev, "brw_stats", 0444,
724                                       &filter_brw_stats_fops, dev);
725 }
726
727 LPROCFS_INIT_VARS(filter, lprocfs_module_vars, lprocfs_obd_vars)
728 #endif /* LPROCFS */