Whamcloud - gitweb
- merge with 1_5,some fixes.
[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         *eof = 1;
40         return snprintf(page, count, "%u\n", FILTER_GROUPS);
41 }
42
43 static int lprocfs_filter_rd_tot_dirty(char *page, char **start, off_t off,
44                                        int count, int *eof, void *data)
45 {
46         struct obd_device *obd = (struct obd_device *)data;
47
48         LASSERT(obd != NULL);
49         *eof = 1;
50         return snprintf(page, count, LPU64"\n", obd->u.filter.fo_tot_dirty);
51 }
52
53 static int lprocfs_filter_rd_tot_granted(char *page, char **start, off_t off,
54                                          int count, int *eof, void *data)
55 {
56         struct obd_device *obd = (struct obd_device *)data;
57
58         LASSERT(obd != NULL);
59         *eof = 1;
60         return snprintf(page, count, LPU64"\n", obd->u.filter.fo_tot_granted);
61 }
62
63 static int lprocfs_filter_rd_tot_pending(char *page, char **start, off_t off,
64                                          int count, int *eof, void *data)
65 {
66         struct obd_device *obd = (struct obd_device *)data;
67
68         LASSERT(obd != NULL);
69         *eof = 1;
70         return snprintf(page, count, LPU64"\n", obd->u.filter.fo_tot_pending);
71 }
72
73 static int lprocfs_filter_rd_mntdev(char *page, char **start, off_t off,
74                                     int count, int *eof, void *data)
75 {
76         struct obd_device *obd = (struct obd_device *)data;
77
78         LASSERT(obd != NULL);
79         LASSERT(obd->u.filter.fo_vfsmnt->mnt_devname);
80         *eof = 1;
81         return snprintf(page, count, "%s\n",
82                         obd->u.filter.fo_vfsmnt->mnt_devname);
83 }
84
85 static int lprocfs_filter_rd_last_id(char *page, char **start, off_t off,
86                                      int count, int *eof, void *data)
87 {
88         struct obd_device *obd = data;
89
90         if (obd == NULL)
91                 return 0;
92
93         return snprintf(page, count, LPU64"\n",
94                         filter_last_id(&obd->u.filter, 0));
95 }
96
97 int lprocfs_filter_rd_readcache(char *page, char **start, off_t off, int count,
98                                 int *eof, void *data)
99 {
100         struct obd_device *obd = data;
101         int rc;
102
103         rc = snprintf(page, count, LPU64"\n",
104                       obd->u.filter.fo_readcache_max_filesize);
105         return rc;
106 }
107
108 int lprocfs_filter_wr_readcache(struct file *file, const char *buffer,
109                                 unsigned long count, void *data)
110 {
111         struct obd_device *obd = data;
112         __u64 val;
113         int rc;
114
115         rc = lprocfs_write_u64_helper(buffer, count, &val);
116         if (rc)
117                 return rc;
118
119         obd->u.filter.fo_readcache_max_filesize = val;
120         return count;
121 }
122
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)
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.obt.obt_qctxt.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.obt.obt_qctxt.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;
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.obt.obt_qctxt.lqc_btune_sz)
157                 return -EINVAL;
158
159         obd->u.obt.obt_qctxt.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;
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.obt.obt_qctxt.lqc_itune_sz)
175                 return -EINVAL;
176
177         obd->u.obt.obt_qctxt.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.obt.obt_qctxt.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.obt.obt_qctxt.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;
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.obt.obt_qctxt.lqc_bunit_sz)
214                 return -EINVAL;
215
216         obd->u.obt.obt_qctxt.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;
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.obt.obt_qctxt.lqc_iunit_sz)
233                 return -EINVAL;
234
235         obd->u.obt.obt_qctxt.lqc_itune_sz = val;
236         return count;
237 }
238 #endif
239
240 int lprocfs_filter_rd_fmd_max_num(char *page, char **start, off_t off,
241                                   int count, int *eof, void *data)
242 {
243         struct obd_device *obd = data;
244         int rc;
245
246         rc = snprintf(page, count, "%u\n", obd->u.filter.fo_fmd_max_num);
247         return rc;
248 }
249
250 int lprocfs_filter_wr_fmd_max_num(struct file *file, const char *buffer,
251                                   unsigned long count, void *data)
252 {
253         struct obd_device *obd = data;
254         int val;
255         int rc;
256
257         rc = lprocfs_write_helper(buffer, count, &val);
258         if (rc)
259                 return rc;
260
261         if (val > 65536 || val < 1)
262                 return -EINVAL;
263
264         obd->u.filter.fo_fmd_max_num = val;
265         return count;
266 }
267
268 int lprocfs_filter_rd_fmd_max_age(char *page, char **start, off_t off,
269                                   int count, int *eof, void *data)
270 {
271         struct obd_device *obd = data;
272         int rc;
273
274         rc = snprintf(page, count, "%u\n", obd->u.filter.fo_fmd_max_age / HZ);
275         return rc;
276 }
277
278 int lprocfs_filter_wr_fmd_max_age(struct file *file, const char *buffer,
279                                   unsigned long count, void *data)
280 {
281         struct obd_device *obd = data;
282         int val;
283         int rc;
284
285         rc = lprocfs_write_helper(buffer, count, &val);
286         if (rc)
287                 return rc;
288
289         if (val > 65536 || val < 1)
290                 return -EINVAL;
291
292         obd->u.filter.fo_fmd_max_age = val * HZ;
293         return count;
294 }
295
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},
326 #endif
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 },
331         { 0 }
332 };
333
334 static struct lprocfs_vars lprocfs_module_vars[] = {
335         { "num_refs",     lprocfs_rd_numrefs,       0, 0 },
336         { 0 }
337 };
338
339 void filter_tally_write(struct filter_obd *filter, struct page **pages,
340                      int nr_pages, unsigned long *blocks, int blocks_per_page)
341 {
342         struct page *last_page = NULL;
343         unsigned long *last_block = NULL;
344         unsigned long discont_pages = 0;
345         unsigned long discont_blocks = 0;
346         int i;
347
348         if (nr_pages == 0)
349                 return;
350
351         lprocfs_oh_tally_log2(&filter->fo_w_pages, nr_pages);
352
353         while (nr_pages-- > 0) {
354                 if (last_page && (*pages)->index != (last_page->index + 1))
355                         discont_pages++;
356                 last_page = *pages;
357                 pages++;
358                 for (i = 0; i < blocks_per_page; i++) {
359                         if (last_block && *blocks != (*last_block + 1))
360                                 discont_blocks++;
361                         last_block = blocks++;
362                 }
363         }
364
365         lprocfs_oh_tally(&filter->fo_w_discont_pages, discont_pages);
366         lprocfs_oh_tally(&filter->fo_w_discont_blocks, discont_blocks);
367 }
368
369 void filter_tally_read(struct filter_obd *filter, struct niobuf_local *lnb,
370                        int niocount)
371 {
372         struct niobuf_local *end;
373         struct page *last_page = NULL;
374         unsigned long discont_pages = 0;
375         unsigned long discont_blocks = 0;
376
377         if (niocount == 0)
378                 return;
379
380         for (end = lnb + niocount; lnb < end && lnb->page; lnb++) {
381                 struct page *page = lnb->page;
382                 if (last_page) {
383                        if (page->index != (last_page->index + 1))
384                                 discont_pages++;
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)))
390                                 discont_blocks++;
391 #else
392 #warning "port on 2.6 -bzzz"
393 #endif
394                 }
395                 last_page = page;
396         }
397
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);
401 }
402
403 #define pct(a,b) (b ? a * 100 / b : 0)
404
405 static int filter_brw_stats_seq_show(struct seq_file *seq, void *v)
406 {
407         struct timeval now;
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;
411         int i;
412
413         do_gettimeofday(&now);
414
415         /* this sampling races with updates */
416
417         seq_printf(seq, "snapshot_time:         %lu.%lu (secs.usecs)\n",
418                    now.tv_sec, now.tv_usec);
419
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");
423
424         read_tot = lprocfs_oh_sum(&filter->fo_r_pages);
425         write_tot = lprocfs_oh_sum(&filter->fo_w_pages);
426
427         read_cum = 0;
428         write_cum = 0;
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];
432                 read_cum += r;
433                 write_cum += w;
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,
437                                  pct(w, write_tot),
438                                  pct(write_cum, write_tot));
439                 if (read_cum == read_tot && write_cum == write_tot)
440                         break;
441         }
442
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");
446
447         read_tot = lprocfs_oh_sum(&filter->fo_r_discont_pages);
448         write_tot = lprocfs_oh_sum(&filter->fo_w_discont_pages);
449
450         read_cum = 0;
451         write_cum = 0;
452
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];
456                 read_cum += r;
457                 write_cum += w;
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,
461                                  pct(w, write_tot),
462                                  pct(write_cum, write_tot));
463                 if (read_cum == read_tot && write_cum == write_tot)
464                         break;
465         }
466
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");
470
471         read_tot = lprocfs_oh_sum(&filter->fo_r_discont_blocks);
472         write_tot = lprocfs_oh_sum(&filter->fo_w_discont_blocks);
473
474         read_cum = 0;
475         write_cum = 0;
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];
479                 read_cum += r;
480                 write_cum += w;
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,
484                                  pct(w, write_tot),
485                                  pct(write_cum, write_tot));
486                 if (read_cum == read_tot && write_cum == write_tot)
487                         break;
488         }
489
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");
493
494         read_tot = lprocfs_oh_sum(&filter->fo_r_dio_frags);
495         write_tot = lprocfs_oh_sum(&filter->fo_w_dio_frags);
496
497         read_cum = 0;
498         write_cum = 0;
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];
502                 read_cum += r;
503                 write_cum += w;
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,
507                                  pct(w, write_tot),
508                                  pct(write_cum, write_tot));
509                 if (read_cum == read_tot && write_cum == write_tot)
510                         break;
511         }
512
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");
517
518         read_tot = lprocfs_oh_sum(&filter->fo_read_rpc_hist);
519         write_tot = lprocfs_oh_sum(&filter->fo_write_rpc_hist);
520
521         read_cum = 0;
522         write_cum = 0;
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];
526                 read_cum += r;
527                 write_cum += w;
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,
531                                  pct(w, write_tot),
532                                  pct(write_cum, write_tot));
533                 if (read_cum == read_tot && write_cum == write_tot)
534                         break;
535         }
536
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");
540
541         read_tot = lprocfs_oh_sum(&filter->fo_r_io_time);
542         write_tot = lprocfs_oh_sum(&filter->fo_w_io_time);
543
544         read_cum = 0;
545         write_cum = 0;
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];
549                 read_cum += r;
550                 write_cum += w;
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,
554                                  pct(w, write_tot),
555                                  pct(write_cum, write_tot));
556                 if (read_cum == read_tot && write_cum == write_tot)
557                         break;
558         }
559
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");
563
564         read_tot = lprocfs_oh_sum(&filter->fo_r_disk_iosize);
565         write_tot = lprocfs_oh_sum(&filter->fo_w_disk_iosize);
566
567         read_cum = 0;
568         write_cum = 0;
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];
572
573                 read_cum += r;
574                 write_cum += w;
575                 if (read_cum == 0 && write_cum == 0)
576                         continue;
577
578                 if (i < 10)
579                         seq_printf(seq, "%u", 1<<i);
580                 else if (i < 20)
581                         seq_printf(seq, "%uK", 1<<(i-10));
582                 else
583                         seq_printf(seq, "%uM", 1<<(i-20));
584
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)
589                         break;
590         }
591 #endif
592
593         return 0;
594 }
595 #undef pct
596
597 static void *filter_brw_stats_seq_start(struct seq_file *p, loff_t *pos)
598 {
599         if (*pos == 0)
600                 return (void *)1;
601         return NULL;
602 }
603 static void *filter_brw_stats_seq_next(struct seq_file *p, void *v, loff_t *pos)
604 {
605         ++*pos;
606         return NULL;
607 }
608 static void filter_brw_stats_seq_stop(struct seq_file *p, void *v)
609 {
610 }
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,
616 };
617
618 static int filter_brw_stats_seq_open(struct inode *inode, struct file *file)
619 {
620         struct proc_dir_entry *dp = PDE(inode);
621         struct seq_file *seq;
622         int rc;
623
624         rc = seq_open(file, &filter_brw_stats_seq_sops);
625         if (rc)
626                 return rc;
627         seq = file->private_data;
628         seq->private = dp->data;
629         return 0;
630 }
631
632 static ssize_t filter_brw_stats_seq_write(struct file *file, const char *buf,
633                                        size_t len, loff_t *off)
634 {
635         struct seq_file *seq = file->private_data;
636         struct obd_device *dev = seq->private;
637         struct filter_obd *filter = &dev->u.filter;
638
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);
653
654         return len;
655 }
656
657 struct file_operations filter_brw_stats_fops = {
658         .owner   = THIS_MODULE,
659         .open    = filter_brw_stats_seq_open,
660         .read    = seq_read,
661         .write   = filter_brw_stats_seq_write,
662         .llseek  = seq_lseek,
663         .release = seq_release,
664 };
665
666 int lproc_filter_attach_seqstat(struct obd_device *dev)
667 {
668         return lprocfs_obd_seq_create(dev, "brw_stats", 0444,
669                                       &filter_brw_stats_fops, dev);
670 }
671
672 LPROCFS_INIT_VARS(filter, lprocfs_module_vars, lprocfs_obd_vars)
673 #endif /* LPROCFS */