Whamcloud - gitweb
132aeb34cb51c9ee98bd3645af2463143e617a1d
[fs/lustre-release.git] / lustre / obdfilter / lproc_obdfilter.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
19  *
20  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
21  * CA 95054 USA or visit www.sun.com if you need additional information or
22  * have any questions.
23  *
24  * GPL HEADER END
25  */
26 /*
27  * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
28  * Use is subject to license terms.
29  *
30  * Copyright (c) 2011, Whamcloud, Inc.
31  */
32 /*
33  * This file is part of Lustre, http://www.lustre.org/
34  * Lustre is a trademark of Sun Microsystems, Inc.
35  */
36 #define DEBUG_SUBSYSTEM S_CLASS
37
38 #include <linux/version.h>
39 #include <lprocfs_status.h>
40 #include <obd.h>
41 #include <linux/seq_file.h>
42 #include <linux/version.h>
43
44 #include "filter_internal.h"
45
46 #ifdef LPROCFS
47 static int lprocfs_filter_rd_groups(char *page, char **start, off_t off,
48                                     int count, int *eof, void *data)
49 {
50         struct obd_device *obd = (struct obd_device *)data;
51         *eof = 1;
52         return snprintf(page, count, "%u\n", obd->u.filter.fo_group_count);
53 }
54
55 static int lprocfs_filter_rd_tot_dirty(char *page, char **start, off_t off,
56                                        int count, int *eof, void *data)
57 {
58         struct obd_device *obd = (struct obd_device *)data;
59
60         LASSERT(obd != NULL);
61         *eof = 1;
62         return snprintf(page, count, LPU64"\n", obd->u.filter.fo_tot_dirty);
63 }
64
65 static int lprocfs_filter_rd_tot_granted(char *page, char **start, off_t off,
66                                          int count, int *eof, void *data)
67 {
68         struct obd_device *obd = (struct obd_device *)data;
69
70         LASSERT(obd != NULL);
71         *eof = 1;
72         return snprintf(page, count, LPU64"\n", obd->u.filter.fo_tot_granted);
73 }
74
75 static int lprocfs_filter_rd_tot_pending(char *page, char **start, off_t off,
76                                          int count, int *eof, void *data)
77 {
78         struct obd_device *obd = (struct obd_device *)data;
79
80         LASSERT(obd != NULL);
81         *eof = 1;
82         return snprintf(page, count, LPU64"\n", obd->u.filter.fo_tot_pending);
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         struct filter_obd *filter = &obd->u.filter;
90         int retval = 0, rc, i;
91
92         if (obd == NULL || !obd->obd_set_up || obd->obd_stopping)
93                 return 0;
94         rc = snprintf(page, count, LPU64"\n",filter_last_id(filter, 0));
95         if (rc < 0)
96                 return rc;
97         page += rc;
98         count -= rc;
99         retval += rc;
100
101         for (i = FID_SEQ_OST_MDT1; i < filter->fo_group_count; i++) {
102                 rc = snprintf(page, count, LPU64"\n",filter_last_id(filter, i));
103                 if (rc < 0) {
104                         retval = rc;
105                         break;
106                 }
107                 page += rc;
108                 count -= rc;
109                 retval += rc;
110         }
111         return retval;
112 }
113
114 int lprocfs_filter_rd_readcache(char *page, char **start, off_t off, int count,
115                                 int *eof, void *data)
116 {
117         struct obd_device *obd = data;
118         int rc;
119
120         rc = snprintf(page, count, LPU64"\n",
121                       obd->u.filter.fo_readcache_max_filesize);
122         return rc;
123 }
124
125 int lprocfs_filter_wr_readcache(struct file *file, const char *buffer,
126                                 unsigned long count, void *data)
127 {
128         struct obd_device *obd = data;
129         __u64 val;
130         int rc;
131
132         rc = lprocfs_write_u64_helper(buffer, count, &val);
133         if (rc)
134                 return rc;
135
136         obd->u.filter.fo_readcache_max_filesize = val;
137         return count;
138 }
139
140 int lprocfs_filter_rd_fmd_max_num(char *page, char **start, off_t off,
141                                   int count, int *eof, void *data)
142 {
143         struct obd_device *obd = data;
144         int rc;
145
146         rc = snprintf(page, count, "%u\n", obd->u.filter.fo_fmd_max_num);
147         return rc;
148 }
149
150 int lprocfs_filter_wr_fmd_max_num(struct file *file, const char *buffer,
151                                   unsigned long count, void *data)
152 {
153         struct obd_device *obd = data;
154         int val;
155         int rc;
156
157         rc = lprocfs_write_helper(buffer, count, &val);
158         if (rc)
159                 return rc;
160
161         if (val > 65536 || val < 1)
162                 return -EINVAL;
163
164         obd->u.filter.fo_fmd_max_num = val;
165         return count;
166 }
167
168 int lprocfs_filter_rd_fmd_max_age(char *page, char **start, off_t off,
169                                   int count, int *eof, void *data)
170 {
171         struct obd_device *obd = data;
172         int rc;
173
174         rc = snprintf(page, count, "%u\n",
175                       obd->u.filter.fo_fmd_max_age / CFS_HZ);
176         return rc;
177 }
178
179 int lprocfs_filter_wr_fmd_max_age(struct file *file, const char *buffer,
180                                   unsigned long count, void *data)
181 {
182         struct obd_device *obd = data;
183         int val;
184         int rc;
185
186         rc = lprocfs_write_helper(buffer, count, &val);
187         if (rc)
188                 return rc;
189
190         if (val > 65536 || val < 1)
191                 return -EINVAL;
192
193         obd->u.filter.fo_fmd_max_age = val * CFS_HZ;
194         return count;
195 }
196
197 static int lprocfs_filter_rd_capa(char *page, char **start, off_t off,
198                                   int count, int *eof, void *data)
199 {
200         struct obd_device *obd = data;
201         int rc;
202
203         rc = snprintf(page, count, "capability on: %s\n",
204                       obd->u.filter.fo_fl_oss_capa ? "oss" : "");
205         return rc;
206 }
207
208 static int lprocfs_filter_wr_capa(struct file *file, const char *buffer,
209                                   unsigned long count, void *data)
210 {
211         struct obd_device *obd = data;
212         int val, rc;
213
214         rc = lprocfs_write_helper(buffer, count, &val);
215         if (rc)
216                 return rc;
217
218         if (val & ~0x1) {
219                 CERROR("invalid capability mode, only 0/1 are accepted.\n"
220                        " 1: enable oss fid capability\n"
221                        " 0: disable oss fid capability\n");
222                 return -EINVAL;
223         }
224
225         obd->u.filter.fo_fl_oss_capa = val;
226         LCONSOLE_INFO("OSS %s %s fid capability.\n", obd->obd_name,
227                       val ? "enabled" : "disabled");
228         return count;
229 }
230
231 static int lprocfs_filter_rd_capa_count(char *page, char **start, off_t off,
232                                         int count, int *eof, void *data)
233 {
234         return snprintf(page, count, "%d %d\n",
235                         capa_count[CAPA_SITE_CLIENT],
236                         capa_count[CAPA_SITE_SERVER]);
237 }
238
239 static int lprocfs_rd_sec_level(char *page, char **start, off_t off,
240                                 int count, int *eof, void *data)
241 {
242         struct obd_device *obd = data;
243
244         return snprintf(page, count, "%d\n", obd->u.filter.fo_sec_level);
245 }
246
247 static int lprocfs_wr_sec_level(struct file *file, const char *buffer,
248                                 unsigned long count, void *data)
249 {
250         struct obd_device *obd = data;
251         int val, rc;
252
253         rc = lprocfs_write_helper(buffer, count, &val);
254         if (rc)
255                 return rc;
256
257         if (val > LUSTRE_SEC_ALL || val < LUSTRE_SEC_NONE)
258                 return -EINVAL;
259
260         if (val == LUSTRE_SEC_SPECIFY) {
261                 CWARN("security level %d will be supported in future.\n",
262                       LUSTRE_SEC_SPECIFY);
263                 return -EINVAL;
264         }
265
266         obd->u.filter.fo_sec_level = val;
267         return count;
268 }
269
270 static int lprocfs_filter_rd_cache(char *page, char **start, off_t off,
271                                    int count, int *eof, void *data)
272 {
273         struct obd_device *obd = (struct obd_device *)data;
274         LASSERT(obd != NULL);
275
276         return snprintf(page, count, "%u\n", obd->u.filter.fo_read_cache);
277 }
278
279 static int lprocfs_filter_wr_cache(struct file *file, const char *buffer,
280                      unsigned long count, void *data)
281 {
282         struct obd_device *obd = (struct obd_device *)data;
283         int val, rc;
284         LASSERT(obd != NULL);
285
286         rc = lprocfs_write_helper(buffer, count, &val);
287
288         if (rc)
289                 return rc;
290
291         cfs_spin_lock_bh(&obd->u.filter.fo_flags_lock);
292         obd->u.filter.fo_read_cache = !!val;
293         cfs_spin_unlock_bh(&obd->u.filter.fo_flags_lock);
294         return count;
295 }
296
297 static int lprocfs_filter_rd_wcache(char *page, char **start, off_t off,
298                                    int count, int *eof, void *data)
299 {
300         struct obd_device *obd = (struct obd_device *)data;
301         LASSERT(obd != NULL);
302
303         return snprintf(page, count, "%u\n", obd->u.filter.fo_writethrough_cache);
304 }
305
306 static int lprocfs_filter_wr_wcache(struct file *file, const char *buffer,
307                      unsigned long count, void *data)
308 {
309         struct obd_device *obd = (struct obd_device *)data;
310         int val, rc;
311         LASSERT(obd != NULL);
312
313         rc = lprocfs_write_helper(buffer, count, &val);
314
315         if (rc)
316                 return rc;
317
318         cfs_spin_lock(&obd->u.filter.fo_flags_lock);
319         obd->u.filter.fo_writethrough_cache = !!val;
320         cfs_spin_unlock(&obd->u.filter.fo_flags_lock);
321         return count;
322 }
323
324 static int lprocfs_filter_rd_mds_sync(char *page, char **start, off_t off,
325                                       int count, int *eof, void *data)
326 {
327         struct obd_device *obd = (struct obd_device *)data;
328         LASSERT(obd != NULL);
329
330         return snprintf(page, count, "%u\n", obd->u.filter.fo_mds_ost_sync);
331 }
332
333 int lprocfs_filter_rd_degraded(char *page, char **start, off_t off,
334                                int count, int *eof, void *data)
335 {
336         struct obd_device *obd = data;
337
338         return snprintf(page, count, "%u\n", obd->u.filter.fo_raid_degraded);
339 }
340
341 int lprocfs_filter_wr_degraded(struct file *file, const char *buffer,
342                                unsigned long count, void *data)
343 {
344         struct obd_device *obd = data;
345         int val, rc;
346
347         rc = lprocfs_write_helper(buffer, count, &val);
348         if (rc)
349                 return rc;
350
351         cfs_spin_lock(&obd->u.filter.fo_flags_lock);
352         obd->u.filter.fo_raid_degraded = !!val;
353         cfs_spin_unlock(&obd->u.filter.fo_flags_lock);
354         return count;
355 }
356
357 int lprocfs_filter_rd_syncjournal(char *page, char **start, off_t off,
358                                   int count, int *eof, void *data)
359 {
360         struct obd_device *obd = data;
361         int rc;
362
363         rc = snprintf(page, count, "%u\n", obd->u.filter.fo_syncjournal);
364         return rc;
365 }
366
367 int lprocfs_filter_wr_syncjournal(struct file *file, const char *buffer,
368                                   unsigned long count, void *data)
369 {
370         struct obd_device *obd = data;
371         int val;
372         int rc;
373
374         rc = lprocfs_write_helper(buffer, count, &val);
375         if (rc)
376                 return rc;
377
378         if (val < 0)
379                 return -EINVAL;
380
381         obd->u.filter.fo_syncjournal = !!val;
382         filter_slc_set(&obd->u.filter);
383
384         return count;
385 }
386
387 static char *sync_on_cancel_states[] = {"never",
388                                         "blocking",
389                                         "always" };
390
391 int lprocfs_filter_rd_sync_lock_cancel(char *page, char **start, off_t off,
392                                        int count, int *eof, void *data)
393 {
394         struct obd_device *obd = data;
395         int rc;
396
397         rc = snprintf(page, count, "%s\n",
398                       sync_on_cancel_states[obd->u.filter.fo_sync_lock_cancel]);
399         return rc;
400 }
401
402 int lprocfs_filter_wr_sync_lock_cancel(struct file *file, const char *buffer,
403                                           unsigned long count, void *data)
404 {
405         struct obd_device *obd = data;
406         int val = -1;
407         int i;
408
409         for (i = 0 ; i < NUM_SYNC_ON_CANCEL_STATES; i++) {
410                 if (memcmp(buffer, sync_on_cancel_states[i],
411                     strlen(sync_on_cancel_states[i])) == 0) {
412                         val = i;
413                         break;
414                 }
415         }
416         if (val == -1) {
417                 int rc;
418                 rc = lprocfs_write_helper(buffer, count, &val);
419                 if (rc)
420                         return rc;
421         }
422
423         if (val < 0 || val > 2)
424                 return -EINVAL;
425
426         obd->u.filter.fo_sync_lock_cancel = val;
427         return count;
428 }
429
430 static struct lprocfs_vars lprocfs_filter_obd_vars[] = {
431         { "uuid",         lprocfs_rd_uuid,          0, 0 },
432         { "blocksize",    lprocfs_rd_blksize,       0, 0 },
433         { "kbytestotal",  lprocfs_rd_kbytestotal,   0, 0 },
434         { "kbytesfree",   lprocfs_rd_kbytesfree,    0, 0 },
435         { "kbytesavail",  lprocfs_rd_kbytesavail,   0, 0 },
436         { "filestotal",   lprocfs_rd_filestotal,    0, 0 },
437         { "filesfree",    lprocfs_rd_filesfree,     0, 0 },
438         { "filegroups",   lprocfs_filter_rd_groups, 0, 0 },
439         { "fstype",       lprocfs_rd_fstype,        0, 0 },
440         { "mntdev",       lprocfs_obd_rd_mntdev,    0, 0 },
441         { "last_id",      lprocfs_filter_rd_last_id,0, 0 },
442         { "tot_dirty",    lprocfs_filter_rd_tot_dirty,   0, 0 },
443         { "tot_pending",  lprocfs_filter_rd_tot_pending, 0, 0 },
444         { "tot_granted",  lprocfs_filter_rd_tot_granted, 0, 0 },
445         { "hash_stats",   lprocfs_obd_rd_hash,      0, 0 },
446         { "recovery_status",    lprocfs_obd_rd_recovery_status, 0, 0 },
447         { "recovery_time_soft", lprocfs_obd_rd_recovery_time_soft,
448                                 lprocfs_obd_wr_recovery_time_soft, 0},
449         { "recovery_time_hard", lprocfs_obd_rd_recovery_time_hard,
450                                 lprocfs_obd_wr_recovery_time_hard, 0},
451         { "evict_client", 0, lprocfs_wr_evict_client, 0,
452                                 &lprocfs_evict_client_fops},
453         { "num_exports",  lprocfs_rd_num_exports,   0, 0 },
454         { "readcache_max_filesize",
455                           lprocfs_filter_rd_readcache,
456                           lprocfs_filter_wr_readcache, 0 },
457 #ifdef HAVE_QUOTA_SUPPORT
458         { "quota_type",     lprocfs_quota_rd_type,
459                             lprocfs_quota_wr_type, 0},
460 #endif
461         { "client_cache_count", lprocfs_filter_rd_fmd_max_num,
462                           lprocfs_filter_wr_fmd_max_num, 0 },
463         { "client_cache_seconds", lprocfs_filter_rd_fmd_max_age,
464                           lprocfs_filter_wr_fmd_max_age, 0 },
465         { "capa",         lprocfs_filter_rd_capa,
466                           lprocfs_filter_wr_capa, 0 },
467         { "capa_count",   lprocfs_filter_rd_capa_count, 0, 0 },
468         { "sec_level",    lprocfs_rd_sec_level,
469                           lprocfs_wr_sec_level,            0 },
470         { "read_cache_enable", lprocfs_filter_rd_cache, lprocfs_filter_wr_cache, 0},
471         { "writethrough_cache_enable", lprocfs_filter_rd_wcache,
472                           lprocfs_filter_wr_wcache, 0},
473         { "mds_sync",     lprocfs_filter_rd_mds_sync, 0, 0},
474         { "degraded",     lprocfs_filter_rd_degraded,
475                           lprocfs_filter_wr_degraded, 0 },
476         { "sync_journal", lprocfs_filter_rd_syncjournal,
477                           lprocfs_filter_wr_syncjournal, 0 },
478         { "sync_on_lock_cancel", lprocfs_filter_rd_sync_lock_cancel,
479                                  lprocfs_filter_wr_sync_lock_cancel, 0 },
480         { "instance",     lprocfs_target_rd_instance, 0 },
481         { "ir_factor",    lprocfs_obd_rd_ir_factor,
482                           lprocfs_obd_wr_ir_factor, 0},
483         { 0 }
484 };
485
486 static struct lprocfs_vars lprocfs_filter_module_vars[] = {
487         { "num_refs",     lprocfs_rd_numrefs,       0, 0 },
488         { 0 }
489 };
490
491 void filter_tally(struct obd_export *exp, struct page **pages, int nr_pages,
492                   unsigned long *blocks, int blocks_per_page, int wr)
493 {
494         struct filter_obd *filter = &exp->exp_obd->u.filter;
495         struct page *last_page = NULL;
496         unsigned long *last_block = NULL;
497         unsigned long discont_pages = 0;
498         unsigned long discont_blocks = 0;
499         int i;
500
501         if (nr_pages == 0)
502                 return;
503
504         lprocfs_oh_tally_log2(&filter->fo_filter_stats.hist[BRW_R_PAGES + wr],
505                               nr_pages);
506         if (exp->exp_nid_stats && exp->exp_nid_stats->nid_brw_stats)
507                 lprocfs_oh_tally_log2(&exp->exp_nid_stats->nid_brw_stats->
508                                         hist[BRW_R_PAGES + wr], nr_pages);
509
510         while (nr_pages-- > 0) {
511                 if (last_page && (*pages)->index != (last_page->index + 1))
512                         discont_pages++;
513                 last_page = *pages;
514                 pages++;
515                 for (i = 0; i < blocks_per_page; i++) {
516                         if (last_block && *blocks != (*last_block + 1))
517                                 discont_blocks++;
518                         last_block = blocks++;
519                 }
520         }
521
522         lprocfs_oh_tally(&filter->fo_filter_stats.hist[BRW_R_DISCONT_PAGES +wr],
523                          discont_pages);
524         lprocfs_oh_tally(&filter->fo_filter_stats.hist[BRW_R_DISCONT_BLOCKS+wr],
525                          discont_blocks);
526
527         if (exp->exp_nid_stats && exp->exp_nid_stats->nid_brw_stats) {
528                 lprocfs_oh_tally_log2(&exp->exp_nid_stats->nid_brw_stats->
529                                         hist[BRW_R_DISCONT_PAGES + wr],
530                                       discont_pages);
531                 lprocfs_oh_tally_log2(&exp->exp_nid_stats->nid_brw_stats->
532                                         hist[BRW_R_DISCONT_BLOCKS + wr],
533                                       discont_blocks);
534         }
535 }
536
537 #define pct(a,b) (b ? a * 100 / b : 0)
538
539 static void display_brw_stats(struct seq_file *seq, char *name, char *units,
540         struct obd_histogram *read, struct obd_histogram *write, int log2)
541 {
542         unsigned long read_tot, write_tot, r, w, read_cum = 0, write_cum = 0;
543         int i;
544
545         seq_printf(seq, "\n%26s read      |     write\n", " ");
546         seq_printf(seq, "%-22s %-5s %% cum %% |  %-5s %% cum %%\n",
547                    name, units, units);
548
549         read_tot = lprocfs_oh_sum(read);
550         write_tot = lprocfs_oh_sum(write);
551         for (i = 0; i < OBD_HIST_MAX; i++) {
552                 r = read->oh_buckets[i];
553                 w = write->oh_buckets[i];
554                 read_cum += r;
555                 write_cum += w;
556                 if (read_cum == 0 && write_cum == 0)
557                         continue;
558
559                 if (!log2)
560                         seq_printf(seq, "%u", i);
561                 else if (i < 10)
562                         seq_printf(seq, "%u", 1<<i);
563                 else if (i < 20)
564                         seq_printf(seq, "%uK", 1<<(i-10));
565                 else
566                         seq_printf(seq, "%uM", 1<<(i-20));
567
568                 seq_printf(seq, ":\t\t%10lu %3lu %3lu   | %4lu %3lu %3lu\n",
569                            r, pct(r, read_tot), pct(read_cum, read_tot),
570                            w, pct(w, write_tot), pct(write_cum, write_tot));
571
572                 if (read_cum == read_tot && write_cum == write_tot)
573                         break;
574         }
575 }
576
577 static void brw_stats_show(struct seq_file *seq, struct brw_stats *brw_stats)
578 {
579         struct timeval now;
580
581         /* this sampling races with updates */
582         cfs_gettimeofday(&now);
583         seq_printf(seq, "snapshot_time:         %lu.%lu (secs.usecs)\n",
584                    now.tv_sec, now.tv_usec);
585
586         display_brw_stats(seq, "pages per bulk r/w", "rpcs",
587                           &brw_stats->hist[BRW_R_PAGES],
588                           &brw_stats->hist[BRW_W_PAGES], 1);
589
590         display_brw_stats(seq, "discontiguous pages", "rpcs",
591                           &brw_stats->hist[BRW_R_DISCONT_PAGES],
592                           &brw_stats->hist[BRW_W_DISCONT_PAGES], 0);
593
594         display_brw_stats(seq, "discontiguous blocks", "rpcs",
595                           &brw_stats->hist[BRW_R_DISCONT_BLOCKS],
596                           &brw_stats->hist[BRW_W_DISCONT_BLOCKS], 0);
597
598         display_brw_stats(seq, "disk fragmented I/Os", "ios",
599                           &brw_stats->hist[BRW_R_DIO_FRAGS],
600                           &brw_stats->hist[BRW_W_DIO_FRAGS], 0);
601
602         display_brw_stats(seq, "disk I/Os in flight", "ios",
603                           &brw_stats->hist[BRW_R_RPC_HIST],
604                           &brw_stats->hist[BRW_W_RPC_HIST], 0);
605
606         {
607                 char title[24];
608                 sprintf(title, "I/O time (1/%ds)", CFS_HZ);
609                 display_brw_stats(seq, title, "ios",
610                                   &brw_stats->hist[BRW_R_IO_TIME],
611                                   &brw_stats->hist[BRW_W_IO_TIME], 1);
612         }
613
614         display_brw_stats(seq, "disk I/O size", "ios",
615                           &brw_stats->hist[BRW_R_DISK_IOSIZE],
616                           &brw_stats->hist[BRW_W_DISK_IOSIZE], 1);
617 }
618
619 #undef pct
620
621 static int filter_brw_stats_seq_show(struct seq_file *seq, void *v)
622 {
623         struct obd_device *dev = seq->private;
624         struct filter_obd *filter = &dev->u.filter;
625
626         brw_stats_show(seq, &filter->fo_filter_stats);
627
628         return 0;
629 }
630
631 static ssize_t filter_brw_stats_seq_write(struct file *file, const char *buf,
632                                        size_t len, loff_t *off)
633 {
634         struct seq_file *seq = file->private_data;
635         struct obd_device *dev = seq->private;
636         struct filter_obd *filter = &dev->u.filter;
637         int i;
638
639         for (i = 0; i < BRW_LAST; i++)
640                 lprocfs_oh_clear(&filter->fo_filter_stats.hist[i]);
641
642         return len;
643 }
644
645 LPROC_SEQ_FOPS(filter_brw_stats);
646
647 int lproc_filter_attach_seqstat(struct obd_device *dev)
648 {
649         return lprocfs_obd_seq_create(dev, "brw_stats", 0444,
650                                       &filter_brw_stats_fops, dev);
651 }
652
653 void lprocfs_filter_init_vars(struct lprocfs_static_vars *lvars)
654 {
655     lvars->module_vars  = lprocfs_filter_module_vars;
656     lvars->obd_vars     = lprocfs_filter_obd_vars;
657 }
658
659 static int filter_per_nid_stats_seq_show(struct seq_file *seq, void *v)
660 {
661         nid_stat_t * stat = seq->private;
662
663         if (stat->nid_brw_stats)
664                 brw_stats_show(seq, stat->nid_brw_stats);
665
666         return 0;
667 }
668
669 static ssize_t filter_per_nid_stats_seq_write(struct file *file,
670                                               const char *buf, size_t len,
671                                               loff_t *off)
672 {
673         struct seq_file *seq  = file->private_data;
674         nid_stat_t      *stat = seq->private;
675         int i;
676
677         if (stat->nid_brw_stats)
678                 for (i = 0; i < BRW_LAST; i++)
679                         lprocfs_oh_clear(&stat->nid_brw_stats->hist[i]);
680
681         return len;
682 }
683
684 LPROC_SEQ_FOPS(filter_per_nid_stats);
685 #endif /* LPROCFS */