Whamcloud - gitweb
44c0dee2dea24be909dbb2835687cc9a5a163778
[fs/lustre-release.git] / lustre / obdclass / lprocfs_status.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * GPL HEADER START
5  *
6  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 only,
10  * as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License version 2 for more details (a copy is included
16  * in the LICENSE file that accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License
19  * version 2 along with this program; If not, see
20  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
21  *
22  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
23  * CA 95054 USA or visit www.sun.com if you need additional information or
24  * have any questions.
25  *
26  * GPL HEADER END
27  */
28 /*
29  * Copyright  2008 Sun Microsystems, Inc. All rights reserved
30  * Use is subject to license terms.
31  */
32 /*
33  * This file is part of Lustre, http://www.lustre.org/
34  * Lustre is a trademark of Sun Microsystems, Inc.
35  *
36  * lustre/obdclass/lprocfs_status.c
37  *
38  * Author: Hariharan Thantry <thantry@users.sourceforge.net>
39  */
40
41 #ifndef EXPORT_SYMTAB
42 # define EXPORT_SYMTAB
43 #endif
44 #define DEBUG_SUBSYSTEM S_CLASS
45
46 #ifndef __KERNEL__
47 # include <liblustre.h>
48 #endif
49
50 #include <obd_class.h>
51 #include <lprocfs_status.h>
52 #include <lustre_fsfilt.h>
53
54 #if defined(LPROCFS)
55
56 #define MAX_STRING_SIZE 128
57
58 /* for bug 10866, global variable */
59 DECLARE_RWSEM(_lprocfs_lock);
60 EXPORT_SYMBOL(_lprocfs_lock);
61
62 int lprocfs_seq_release(struct inode *inode, struct file *file)
63 {
64         LPROCFS_EXIT();
65         return seq_release(inode, file);
66 }
67 EXPORT_SYMBOL(lprocfs_seq_release);
68
69 struct proc_dir_entry *lprocfs_srch(struct proc_dir_entry *head,
70                                     const char *name)
71 {
72         struct proc_dir_entry *temp;
73
74         if (head == NULL)
75                 return NULL;
76         LPROCFS_ENTRY();
77
78         temp = head->subdir;
79         while (temp != NULL) {
80                 if (strcmp(temp->name, name) == 0) {
81                         LPROCFS_EXIT();
82                         return temp;
83                 }
84
85                 temp = temp->next;
86         }
87         LPROCFS_EXIT();
88         return NULL;
89 }
90
91 /* lprocfs API calls */
92
93 /* Function that emulates snprintf but also has the side effect of advancing
94    the page pointer for the next write into the buffer, incrementing the total
95    length written to the buffer, and decrementing the size left in the
96    buffer. */
97 static int lprocfs_obd_snprintf(char **page, int end, int *len,
98                                 const char *format, ...)
99 {
100         va_list list;
101         int n;
102
103         if (*len >= end)
104                 return 0;
105
106         va_start(list, format);
107         n = vsnprintf(*page, end - *len, format, list);
108         va_end(list);
109
110         *page += n; *len += n;
111         return n;
112 }
113
114 int lprocfs_add_simple(struct proc_dir_entry *root, char *name,
115                        read_proc_t *read_proc, write_proc_t *write_proc,
116                        void *data)
117 {
118         struct proc_dir_entry *proc;
119         mode_t mode = 0;
120         
121         if (root == NULL || name == NULL)
122                 return -EINVAL;
123         if (read_proc)
124                 mode = 0444;
125         if (write_proc)
126                 mode |= 0200;
127         proc = create_proc_entry(name, mode, root);
128         if (!proc) {
129                 CERROR("LprocFS: No memory to create /proc entry %s", name);
130                 return -ENOMEM;
131         }
132         proc->read_proc = read_proc;
133         proc->write_proc = write_proc;
134         proc->data = data;
135         return 0;
136 }
137
138 struct proc_dir_entry *lprocfs_add_symlink(const char *name,
139                         struct proc_dir_entry *parent, const char *dest)
140 {
141         struct proc_dir_entry *entry;
142
143         if (parent == NULL || dest == NULL)
144                 return NULL;
145
146         entry = proc_symlink(name, parent, dest);
147         if (entry == NULL)
148                 CERROR("LprocFS: Could not create symbolic link from %s to %s",
149                         name, dest);
150         return entry;
151 }
152
153 static ssize_t lprocfs_fops_read(struct file *f, char __user *buf,
154                                  size_t size, loff_t *ppos)
155 {
156         struct proc_dir_entry *dp = PDE(f->f_dentry->d_inode);
157         char *page, *start = NULL;
158         int rc = 0, eof = 1, count;
159
160         if (*ppos >= CFS_PAGE_SIZE)
161                 return 0;
162
163         page = (char *)__get_free_page(GFP_KERNEL);
164         if (page == NULL)
165                 return -ENOMEM;
166
167         LPROCFS_ENTRY();
168         OBD_FAIL_TIMEOUT(OBD_FAIL_LPROC_REMOVE, 10);
169         if (!dp->deleted && dp->read_proc)
170                 rc = dp->read_proc(page, &start, *ppos, CFS_PAGE_SIZE, 
171                         &eof, dp->data);
172         LPROCFS_EXIT();
173         if (rc <= 0)
174                 goto out;
175
176         /* for lustre proc read, the read count must be less than PAGE_SIZE */
177         LASSERT(eof == 1);
178
179         if (start == NULL) {
180                 rc -= *ppos;
181                 if (rc < 0)
182                         rc = 0;
183                 if (rc == 0)
184                         goto out;
185                 start = page + *ppos;
186         } else if (start < page) {
187                 start = page;
188         }
189
190         count = (rc < size) ? rc : size;
191         if (copy_to_user(buf, start, count)) {
192                 rc = -EFAULT;
193                 goto out;
194         }
195         *ppos += count;
196
197 out:
198         free_page((unsigned long)page);
199         return rc;
200 }
201
202 static ssize_t lprocfs_fops_write(struct file *f, const char __user *buf, size_t size, loff_t *ppos)
203 {
204         struct proc_dir_entry *dp = PDE(f->f_dentry->d_inode);
205         int rc = -EIO;
206
207         LPROCFS_ENTRY();
208         if (!dp->deleted && dp->write_proc)
209                 rc = dp->write_proc(f, buf, size, dp->data);
210         LPROCFS_EXIT();
211         return rc;
212 }
213
214 static struct file_operations lprocfs_generic_fops = {
215         .owner = THIS_MODULE,
216         .read = lprocfs_fops_read,
217         .write = lprocfs_fops_write,
218 };
219
220 int lprocfs_evict_client_open(struct inode *inode, struct file *f)
221 {
222         struct proc_dir_entry *dp = PDE(f->f_dentry->d_inode);
223         struct obd_device *obd = dp->data;
224
225         atomic_inc(&obd->obd_evict_inprogress);
226
227         return 0;
228 }
229
230 int lprocfs_evict_client_release(struct inode *inode, struct file *f)
231 {
232         struct proc_dir_entry *dp = PDE(f->f_dentry->d_inode);
233         struct obd_device *obd = dp->data;
234
235         atomic_dec(&obd->obd_evict_inprogress);
236         wake_up(&obd->obd_evict_inprogress_waitq);
237
238         return 0;
239 }
240
241 struct file_operations lprocfs_evict_client_fops = {
242         .owner = THIS_MODULE,
243         .read = lprocfs_fops_read,
244         .write = lprocfs_fops_write,
245         .open = lprocfs_evict_client_open,
246         .release = lprocfs_evict_client_release,
247 };
248 EXPORT_SYMBOL(lprocfs_evict_client_fops);
249
250 int lprocfs_add_vars(struct proc_dir_entry *root, struct lprocfs_vars *list,
251                      void *data)
252 {
253         if (root == NULL || list == NULL)
254                 return -EINVAL;
255
256         while (list->name != NULL) {
257                 struct proc_dir_entry *cur_root, *proc;
258                 char *pathcopy, *cur, *next, pathbuf[64];
259                 int pathsize = strlen(list->name) + 1;
260
261                 proc = NULL;
262                 cur_root = root;
263
264                 /* need copy of path for strsep */
265                 if (strlen(list->name) > sizeof(pathbuf) - 1) {
266                         OBD_ALLOC(pathcopy, pathsize);
267                         if (pathcopy == NULL)
268                                 return -ENOMEM;
269                 } else {
270                         pathcopy = pathbuf;
271                 }
272
273                 next = pathcopy;
274                 strcpy(pathcopy, list->name);
275
276                 while (cur_root != NULL && (cur = strsep(&next, "/"))) {
277                         if (*cur =='\0') /* skip double/trailing "/" */
278                                 continue;
279
280                         proc = lprocfs_srch(cur_root, cur);
281                         CDEBUG(D_OTHER, "cur_root=%s, cur=%s, next=%s, (%s)\n",
282                                cur_root->name, cur, next,
283                                (proc ? "exists" : "new"));
284                         if (next != NULL) {
285                                 cur_root = (proc ? proc :
286                                             proc_mkdir(cur, cur_root));
287                         } else if (proc == NULL) {
288                                 mode_t mode = 0;
289                                 if (list->read_fptr)
290                                         mode = 0444;
291                                 if (list->write_fptr)
292                                         mode |= 0200;
293                                 proc = create_proc_entry(cur, mode, cur_root);
294                         }
295                 }
296
297                 if (pathcopy != pathbuf)
298                         OBD_FREE(pathcopy, pathsize);
299
300                 if (cur_root == NULL || proc == NULL) {
301                         CERROR("LprocFS: No memory to create /proc entry %s",
302                                list->name);
303                         return -ENOMEM;
304                 }
305
306                 if (list->fops)
307                         proc->proc_fops = list->fops;
308                 else
309                         proc->proc_fops = &lprocfs_generic_fops;
310                 proc->read_proc = list->read_fptr;
311                 proc->write_proc = list->write_fptr;
312                 proc->data = (list->data ? list->data : data);
313                 list++;
314         }
315         return 0;
316 }
317
318 void lprocfs_remove(struct proc_dir_entry **rooth)
319 {
320         struct proc_dir_entry *root = *rooth;
321         struct proc_dir_entry *temp = root;
322         struct proc_dir_entry *rm_entry;
323         struct proc_dir_entry *parent;
324
325         if (!root)
326                 return;
327         *rooth = NULL;
328
329         parent = root->parent;
330         LASSERT(parent != NULL);
331         LPROCFS_WRITE_ENTRY(); /* search vs remove race */
332
333         while (1) {
334                 while (temp->subdir != NULL)
335                         temp = temp->subdir;
336
337                 rm_entry = temp;
338                 temp = temp->parent;
339
340                 /* Memory corruption once caused this to fail, and
341                    without this LASSERT we would loop here forever. */
342                 LASSERTF(strlen(rm_entry->name) == rm_entry->namelen,
343                          "0x%p  %s/%s len %d\n", rm_entry, temp->name,
344                          rm_entry->name, (int)strlen(rm_entry->name));
345
346                 /* Now, the rm_entry->deleted flags is protected 
347                  * by _lprocfs_lock. */
348                 rm_entry->data = NULL;
349                 remove_proc_entry(rm_entry->name, temp);
350                 if (temp == parent)
351                         break;
352         }
353         LPROCFS_WRITE_EXIT();
354 }
355
356 void lprocfs_remove_proc_entry(const char *name, struct proc_dir_entry *parent)
357 {
358         LASSERT(parent != NULL);
359         remove_proc_entry(name, parent);
360 }
361
362 struct proc_dir_entry *lprocfs_register(const char *name,
363                                         struct proc_dir_entry *parent,
364                                         struct lprocfs_vars *list, void *data)
365 {
366         struct proc_dir_entry *newchild;
367
368         newchild = lprocfs_srch(parent, name);
369         if (newchild != NULL) {
370                 CERROR(" Lproc: Attempting to register %s more than once \n",
371                        name);
372                 return ERR_PTR(-EALREADY);
373         }
374
375         newchild = proc_mkdir(name, parent);
376         if (newchild != NULL && list != NULL) {
377                 int rc = lprocfs_add_vars(newchild, list, data);
378                 if (rc) {
379                         lprocfs_remove(&newchild);
380                         return ERR_PTR(rc);
381                 }
382         }
383         return newchild;
384 }
385
386 /* Generic callbacks */
387 int lprocfs_rd_uint(char *page, char **start, off_t off,
388                     int count, int *eof, void *data)
389 {
390         unsigned int *temp = (unsigned int *)data;
391         return snprintf(page, count, "%u\n", *temp);
392 }
393
394 int lprocfs_wr_uint(struct file *file, const char *buffer,
395                     unsigned long count, void *data)
396 {
397         unsigned *p = data;
398         char dummy[MAX_STRING_SIZE + 1], *end;
399         unsigned long tmp;
400
401         dummy[MAX_STRING_SIZE] = '\0';
402         if (copy_from_user(dummy, buffer, MAX_STRING_SIZE))
403                 return -EFAULT;
404
405         tmp = simple_strtoul(dummy, &end, 0);
406         if (dummy == end)
407                 return -EINVAL;
408
409         *p = (unsigned int)tmp;
410         return count;
411 }
412
413 int lprocfs_rd_u64(char *page, char **start, off_t off,
414                    int count, int *eof, void *data)
415 {
416         LASSERT(data != NULL);
417         *eof = 1;
418         return snprintf(page, count, LPU64"\n", *(__u64 *)data);
419 }
420
421 int lprocfs_rd_atomic(char *page, char **start, off_t off,
422                    int count, int *eof, void *data)
423 {
424         atomic_t *atom = (atomic_t *)data;
425         LASSERT(atom != NULL);
426         *eof = 1;
427         return snprintf(page, count, "%d\n", atomic_read(atom));
428 }
429
430 int lprocfs_wr_atomic(struct file *file, const char *buffer,
431                       unsigned long count, void *data)
432 {
433         atomic_t *atm = data;
434         int val = 0;
435         int rc;
436         
437         rc = lprocfs_write_helper(buffer, count, &val);
438         if (rc < 0)
439                 return rc;
440
441         if (val <= 0)
442                 return -ERANGE;
443                 
444         atomic_set(atm, val);
445         return count;
446 }
447
448 int lprocfs_rd_uuid(char *page, char **start, off_t off, int count,
449                     int *eof, void *data)
450 {
451         struct obd_device *obd = (struct obd_device*)data;
452
453         LASSERT(obd != NULL);
454         *eof = 1;
455         return snprintf(page, count, "%s\n", obd->obd_uuid.uuid);
456 }
457
458 int lprocfs_rd_name(char *page, char **start, off_t off, int count,
459                     int *eof, void* data)
460 {
461         struct obd_device *dev = (struct obd_device *)data;
462
463         LASSERT(dev != NULL);
464         LASSERT(dev->obd_name != NULL);
465         *eof = 1;
466         return snprintf(page, count, "%s\n", dev->obd_name);
467 }
468
469 int lprocfs_rd_fstype(char *page, char **start, off_t off, int count, int *eof,
470                       void *data)
471 {
472         struct obd_device *obd = (struct obd_device *)data;
473
474         LASSERT(obd != NULL);
475         LASSERT(obd->obd_fsops != NULL);
476         LASSERT(obd->obd_fsops->fs_type != NULL);
477         return snprintf(page, count, "%s\n", obd->obd_fsops->fs_type);
478 }
479
480 int lprocfs_rd_blksize(char *page, char **start, off_t off, int count,
481                        int *eof, void *data)
482 {
483         struct obd_statfs osfs;
484         int rc = obd_statfs(data, &osfs, cfs_time_current_64() - HZ,
485                             OBD_STATFS_NODELAY);
486         if (!rc) {
487                 *eof = 1;
488                 rc = snprintf(page, count, "%u\n", osfs.os_bsize);
489         }
490         return rc;
491 }
492
493 int lprocfs_rd_kbytestotal(char *page, char **start, off_t off, int count,
494                            int *eof, void *data)
495 {
496         struct obd_statfs osfs;
497         int rc = obd_statfs(data, &osfs, cfs_time_current_64() - HZ,
498                             OBD_STATFS_NODELAY);
499         if (!rc) {
500                 __u32 blk_size = osfs.os_bsize >> 10;
501                 __u64 result = osfs.os_blocks;
502
503                 while (blk_size >>= 1)
504                         result <<= 1;
505
506                 *eof = 1;
507                 rc = snprintf(page, count, LPU64"\n", result);
508         }
509         return rc;
510 }
511
512 int lprocfs_rd_kbytesfree(char *page, char **start, off_t off, int count,
513                           int *eof, void *data)
514 {
515         struct obd_statfs osfs;
516         int rc = obd_statfs(data, &osfs, cfs_time_current_64() - HZ,
517                             OBD_STATFS_NODELAY);
518         if (!rc) {
519                 __u32 blk_size = osfs.os_bsize >> 10;
520                 __u64 result = osfs.os_bfree;
521
522                 while (blk_size >>= 1)
523                         result <<= 1;
524
525                 *eof = 1;
526                 rc = snprintf(page, count, LPU64"\n", result);
527         }
528         return rc;
529 }
530
531 int lprocfs_rd_kbytesavail(char *page, char **start, off_t off, int count,
532                            int *eof, void *data)
533 {
534         struct obd_statfs osfs;
535         int rc = obd_statfs(data, &osfs, cfs_time_current_64() - HZ,
536                             OBD_STATFS_NODELAY);
537         if (!rc) {
538                 __u32 blk_size = osfs.os_bsize >> 10;
539                 __u64 result = osfs.os_bavail;
540
541                 while (blk_size >>= 1)
542                         result <<= 1;
543
544                 *eof = 1;
545                 rc = snprintf(page, count, LPU64"\n", result);
546         }
547         return rc;
548 }
549
550 int lprocfs_rd_filestotal(char *page, char **start, off_t off, int count,
551                           int *eof, void *data)
552 {
553         struct obd_statfs osfs;
554         int rc = obd_statfs(data, &osfs, cfs_time_current_64() - HZ,
555                             OBD_STATFS_NODELAY);
556         if (!rc) {
557                 *eof = 1;
558                 rc = snprintf(page, count, LPU64"\n", osfs.os_files);
559         }
560
561         return rc;
562 }
563
564 int lprocfs_rd_filesfree(char *page, char **start, off_t off, int count,
565                          int *eof, void *data)
566 {
567         struct obd_statfs osfs;
568         int rc = obd_statfs(data, &osfs, cfs_time_current_64() - HZ,
569                             OBD_STATFS_NODELAY);
570         if (!rc) {
571                 *eof = 1;
572                 rc = snprintf(page, count, LPU64"\n", osfs.os_ffree);
573         }
574         return rc;
575 }
576
577 int lprocfs_rd_server_uuid(char *page, char **start, off_t off, int count,
578                            int *eof, void *data)
579 {
580         struct obd_device *obd = (struct obd_device *)data;
581         struct obd_import *imp;
582         char *imp_state_name = NULL;
583         int rc = 0;
584
585         LASSERT(obd != NULL);
586         LPROCFS_CLIMP_CHECK(obd);
587         imp = obd->u.cli.cl_import;
588         imp_state_name = ptlrpc_import_state_name(imp->imp_state);
589         *eof = 1;
590         rc = snprintf(page, count, "%s\t%s%s\n",
591                       obd2cli_tgt(obd), imp_state_name,
592                       imp->imp_deactive ? "\tDEACTIVATED" : "");
593
594         LPROCFS_CLIMP_EXIT(obd);
595         return rc;
596 }
597
598 int lprocfs_rd_conn_uuid(char *page, char **start, off_t off, int count,
599                          int *eof,  void *data)
600 {
601         struct obd_device *obd = (struct obd_device*)data;
602         struct ptlrpc_connection *conn;
603         int rc = 0;
604
605         LASSERT(obd != NULL);
606
607         LPROCFS_CLIMP_CHECK(obd);
608         conn = obd->u.cli.cl_import->imp_connection;
609         LASSERT(conn != NULL);
610         *eof = 1;
611         if (obd->u.cli.cl_import) {
612                 rc = snprintf(page, count, "%s\n",
613                               conn->c_remote_uuid.uuid);
614         } else {
615                 rc = snprintf(page, count, "%s\n", "<none>");
616         }
617
618         LPROCFS_CLIMP_EXIT(obd);
619         return rc;
620 }
621
622 int lprocfs_at_hist_helper(char *page, int count, int rc,
623                            struct adaptive_timeout *at)
624 {
625         int i;
626         for (i = 0; i < AT_BINS; i++)
627                 rc += snprintf(page + rc, count - rc, "%3u ", at->at_hist[i]);
628         rc += snprintf(page + rc, count - rc, "\n");
629         return rc;
630 }
631
632 /* See also ptlrpc_lprocfs_rd_timeouts */
633 int lprocfs_rd_timeouts(char *page, char **start, off_t off, int count,
634                         int *eof, void *data)
635 {
636         struct obd_device *obd = (struct obd_device *)data;
637         struct obd_import *imp;
638         unsigned int cur, worst;
639         time_t now, worstt;
640         struct dhms ts;
641         int i, rc = 0;
642
643         LASSERT(obd != NULL);
644         LPROCFS_CLIMP_CHECK(obd);
645         imp = obd->u.cli.cl_import;
646         *eof = 1;
647
648         now = cfs_time_current_sec();
649
650         /* Some network health info for kicks */
651         s2dhms(&ts, now - imp->imp_last_reply_time);
652         rc += snprintf(page + rc, count - rc,
653                        "%-10s : %ld, "DHMS_FMT" ago\n",
654                        "last reply", imp->imp_last_reply_time, DHMS_VARS(&ts));
655
656
657         cur = at_get(&imp->imp_at.iat_net_latency);
658         worst = imp->imp_at.iat_net_latency.at_worst_ever;
659         worstt = imp->imp_at.iat_net_latency.at_worst_time;
660         s2dhms(&ts, now - worstt);
661         rc += snprintf(page + rc, count - rc,
662                        "%-10s : cur %3u  worst %3u (at %ld, "DHMS_FMT" ago) ",
663                        "network", cur, worst, worstt, DHMS_VARS(&ts));
664         rc = lprocfs_at_hist_helper(page, count, rc,
665                                     &imp->imp_at.iat_net_latency);
666
667         for(i = 0; i < IMP_AT_MAX_PORTALS; i++) {
668                 if (imp->imp_at.iat_portal[i] == 0)
669                         break;
670                 cur = at_get(&imp->imp_at.iat_service_estimate[i]);
671                 worst = imp->imp_at.iat_service_estimate[i].at_worst_ever;
672                 worstt = imp->imp_at.iat_service_estimate[i].at_worst_time;
673                 s2dhms(&ts, now - worstt);
674                 rc += snprintf(page + rc, count - rc,
675                                "portal %-2d  : cur %3u  worst %3u (at %ld, "
676                                DHMS_FMT" ago) ", imp->imp_at.iat_portal[i],
677                                cur, worst, worstt, DHMS_VARS(&ts));
678                 rc = lprocfs_at_hist_helper(page, count, rc,
679                                           &imp->imp_at.iat_service_estimate[i]);
680         }
681
682         LPROCFS_CLIMP_EXIT(obd);
683         return rc;
684 }
685
686 static const char *obd_connect_names[] = {
687         "read_only",
688         "lov_index",
689         "unused",
690         "write_grant",
691         "server_lock",
692         "version",
693         "request_portal",
694         "acl",
695         "xattr",
696         "create_on_write",
697         "truncate_lock",
698         "initial_transno",
699         "inode_bit_locks",
700         "join_file",
701         "getattr_by_fid",
702         "no_oh_for_devices",
703         "local_client",
704         "remote_client",
705         "max_byte_per_rpc",
706         "64bit_qdata",
707         "mds_capability",
708         "oss_capability",
709         "early_lock_cancel",
710         "size_on_mds",
711         "adaptive_timeouts",
712         "lru_resize",
713         "mds_mds_connection",
714         "real_conn",
715         "change_qunit_size",
716         "alt_checksum_algorithm",
717         "fid_is_enabled",
718         NULL
719 };
720
721 int lprocfs_rd_connect_flags(char *page, char **start, off_t off,
722                              int count, int *eof, void *data)
723 {
724         struct obd_device *obd = data;
725         __u64 mask = 1, flags;
726         int i, ret = 0;
727
728         LPROCFS_CLIMP_CHECK(obd);
729         flags = obd->u.cli.cl_import->imp_connect_data.ocd_connect_flags;
730         ret = snprintf(page, count, "flags="LPX64"\n", flags);
731         for (i = 0; obd_connect_names[i] != NULL; i++, mask <<= 1) {
732                 if (flags & mask)
733                         ret += snprintf(page + ret, count - ret, "%s\n",
734                                         obd_connect_names[i]);
735         }
736         if (flags & ~(mask - 1))
737                 ret += snprintf(page + ret, count - ret,
738                                 "unknown flags "LPX64"\n", flags & ~(mask - 1));
739
740         LPROCFS_CLIMP_EXIT(obd);
741         return ret;
742 }
743 EXPORT_SYMBOL(lprocfs_rd_connect_flags);
744
745 int lprocfs_rd_num_exports(char *page, char **start, off_t off, int count,
746                            int *eof,  void *data)
747 {
748         struct obd_device *obd = (struct obd_device*)data;
749
750         LASSERT(obd != NULL);
751         *eof = 1;
752         return snprintf(page, count, "%u\n", obd->obd_num_exports);
753 }
754
755 int lprocfs_rd_numrefs(char *page, char **start, off_t off, int count,
756                        int *eof, void *data)
757 {
758         struct obd_type *class = (struct obd_type*) data;
759
760         LASSERT(class != NULL);
761         *eof = 1;
762         return snprintf(page, count, "%d\n", class->typ_refcnt);
763 }
764
765 int lprocfs_obd_setup(struct obd_device *obd, struct lprocfs_vars *list)
766 {
767         int rc = 0;
768
769         LASSERT(obd != NULL);
770         LASSERT(obd->obd_magic == OBD_DEVICE_MAGIC);
771         LASSERT(obd->obd_type->typ_procroot != NULL);
772
773         obd->obd_proc_entry = lprocfs_register(obd->obd_name,
774                                                obd->obd_type->typ_procroot,
775                                                list, obd);
776         if (IS_ERR(obd->obd_proc_entry)) {
777                 rc = PTR_ERR(obd->obd_proc_entry);
778                 CERROR("error %d setting up lprocfs for %s\n",rc,obd->obd_name);
779                 obd->obd_proc_entry = NULL;
780         }
781         return rc;
782 }
783
784 int lprocfs_obd_cleanup(struct obd_device *obd)
785 {
786         if (!obd) 
787                 return -EINVAL;
788         if (obd->obd_proc_exports_entry) {
789                 /* Should be no exports left */
790                 LASSERT(obd->obd_proc_exports_entry->subdir == NULL);
791                 lprocfs_remove(&obd->obd_proc_exports_entry);
792         }
793         lprocfs_remove(&obd->obd_proc_entry);
794         return 0;
795 }
796
797 static void lprocfs_free_client_stats(struct nid_stat *client_stat)
798 {
799         CDEBUG(D_CONFIG, "stat %p - data %p/%p/%p\n", client_stat,
800                client_stat->nid_proc, client_stat->nid_stats,
801                client_stat->nid_brw_stats);
802
803         LASSERTF(client_stat->nid_exp_ref_count == 0, "count %d\n",
804                  client_stat->nid_exp_ref_count);
805
806         hlist_del_init(&client_stat->nid_hash);
807
808         if (client_stat->nid_proc)
809                 lprocfs_remove(&client_stat->nid_proc);
810
811         if (client_stat->nid_stats)
812                 lprocfs_free_stats(&client_stat->nid_stats);
813
814         if (client_stat->nid_brw_stats)
815                 OBD_FREE(client_stat->nid_brw_stats, sizeof(struct brw_stats));
816
817         OBD_FREE(client_stat, sizeof(*client_stat));
818         return;
819
820 }
821
822 void lprocfs_free_per_client_stats(struct obd_device *obd)
823 {
824         struct nid_stat *stat;
825         ENTRY;
826
827         /* we need extra list - because hash_exit called to early */
828         /* not need locking because all clients is died */
829         while(!list_empty(&obd->obd_nid_stats)) {
830                 stat = list_entry(obd->obd_nid_stats.next,
831                                   struct nid_stat, nid_list);
832                 list_del_init(&stat->nid_list);
833                 lprocfs_free_client_stats(stat);
834         }
835
836         EXIT;
837 }
838
839 struct lprocfs_stats *lprocfs_alloc_stats(unsigned int num,
840                                           enum lprocfs_stats_flags flags)
841 {
842         struct lprocfs_stats *stats;
843         unsigned int percpusize;
844         unsigned int i, j;
845         unsigned int num_cpu;
846
847         if (num == 0)
848                 return NULL;
849
850         if (flags & LPROCFS_STATS_FLAG_NOPERCPU)
851                 num_cpu = 1;
852         else
853                 num_cpu = num_possible_cpus();
854
855         OBD_ALLOC(stats, offsetof(typeof(*stats), ls_percpu[num_cpu]));
856         if (stats == NULL)
857                 return NULL;
858
859         if (flags & LPROCFS_STATS_FLAG_NOPERCPU) {
860                 stats->ls_flags = flags;
861                 spin_lock_init(&stats->ls_lock);
862                 /* Use this lock only if there are no percpu areas */
863         } else {
864                 stats->ls_flags = 0;
865         }
866
867         percpusize = offsetof(struct lprocfs_percpu, lp_cntr[num]);
868         if (num_cpu > 1)
869                 percpusize = L1_CACHE_ALIGN(percpusize);
870
871         for (i = 0; i < num_cpu; i++) {
872                 OBD_ALLOC(stats->ls_percpu[i], percpusize);
873                 if (stats->ls_percpu[i] == NULL) {
874                         for (j = 0; j < i; j++) {
875                                 OBD_FREE(stats->ls_percpu[j], percpusize);
876                                 stats->ls_percpu[j] = NULL;
877                         }
878                         break;
879                 }
880         }
881         if (stats->ls_percpu[0] == NULL) {
882                 OBD_FREE(stats, offsetof(typeof(*stats),
883                                          ls_percpu[num_cpu]));
884                 return NULL;
885         }
886
887         stats->ls_num = num;
888         return stats;
889 }
890
891 void lprocfs_free_stats(struct lprocfs_stats **statsh)
892 {
893         struct lprocfs_stats *stats = *statsh;
894         unsigned int num_cpu;
895         unsigned int percpusize;
896         unsigned int i;
897
898         if (stats == NULL || stats->ls_num == 0)
899                 return;
900         *statsh = NULL;
901
902         if (stats->ls_flags & LPROCFS_STATS_FLAG_NOPERCPU)
903                 num_cpu = 1;
904         else
905                 num_cpu = num_possible_cpus();
906
907         percpusize = offsetof(struct lprocfs_percpu, lp_cntr[stats->ls_num]);
908         if (num_cpu > 1)
909                 percpusize = L1_CACHE_ALIGN(percpusize);
910         for (i = 0; i < num_cpu; i++)
911                 OBD_FREE(stats->ls_percpu[i], percpusize);
912         OBD_FREE(stats, offsetof(typeof(*stats), ls_percpu[num_cpu]));
913 }
914
915 void lprocfs_clear_stats(struct lprocfs_stats *stats)
916 {
917         struct lprocfs_counter *percpu_cntr;
918         int i,j;
919         unsigned int num_cpu;
920
921         num_cpu = lprocfs_stats_lock(stats, LPROCFS_GET_NUM_CPU);
922
923         for (i = 0; i < num_cpu; i++) {
924                 for (j = 0; j < stats->ls_num; j++) {
925                         percpu_cntr = &(stats->ls_percpu[i])->lp_cntr[j];
926                         atomic_inc(&percpu_cntr->lc_cntl.la_entry);
927                         percpu_cntr->lc_count = 0;
928                         percpu_cntr->lc_sum = 0;
929                         percpu_cntr->lc_min = LC_MIN_INIT;
930                         percpu_cntr->lc_max = 0;
931                         percpu_cntr->lc_sumsquare = 0;
932                         atomic_inc(&percpu_cntr->lc_cntl.la_exit);
933                 }
934         }
935
936         lprocfs_stats_unlock(stats);
937 }
938
939 static ssize_t lprocfs_stats_seq_write(struct file *file, const char *buf,
940                                        size_t len, loff_t *off)
941 {
942         struct seq_file *seq = file->private_data;
943         struct lprocfs_stats *stats = seq->private;
944
945         lprocfs_clear_stats(stats);
946
947         return len;
948 }
949
950 static void *lprocfs_stats_seq_start(struct seq_file *p, loff_t *pos)
951 {
952         struct lprocfs_stats *stats = p->private;
953         /* return 1st cpu location */
954         return (*pos >= stats->ls_num) ? NULL :
955                 &(stats->ls_percpu[0]->lp_cntr[*pos]);
956 }
957
958 static void lprocfs_stats_seq_stop(struct seq_file *p, void *v)
959 {
960 }
961
962 static void *lprocfs_stats_seq_next(struct seq_file *p, void *v, loff_t *pos)
963 {
964         struct lprocfs_stats *stats = p->private;
965         ++*pos;
966         return (*pos >= stats->ls_num) ? NULL :
967                 &(stats->ls_percpu[0]->lp_cntr[*pos]);
968 }
969
970 /* seq file export of one lprocfs counter */
971 static int lprocfs_stats_seq_show(struct seq_file *p, void *v)
972 {
973        struct lprocfs_stats *stats = p->private;
974        struct lprocfs_counter  *cntr = v;
975        struct lprocfs_counter  t, ret = { .lc_min = LC_MIN_INIT };
976        int i, idx, rc;
977        unsigned int num_cpu;
978
979        if (cntr == &(stats->ls_percpu[0])->lp_cntr[0]) {
980                struct timeval now;
981                do_gettimeofday(&now);
982                rc = seq_printf(p, "%-25s %lu.%lu secs.usecs\n",
983                                "snapshot_time", now.tv_sec, now.tv_usec);
984                if (rc < 0)
985                        return rc;
986        }
987        idx = cntr - &(stats->ls_percpu[0])->lp_cntr[0];
988
989        if (stats->ls_flags & LPROCFS_STATS_FLAG_NOPERCPU)
990                num_cpu = 1;
991        else
992                num_cpu = num_possible_cpus();
993
994        for (i = 0; i < num_cpu; i++) {
995                struct lprocfs_counter *percpu_cntr =
996                        &(stats->ls_percpu[i])->lp_cntr[idx];
997                int centry;
998
999                do {
1000                        centry = atomic_read(&percpu_cntr->lc_cntl.la_entry);
1001                        t.lc_count = percpu_cntr->lc_count;
1002                        t.lc_sum = percpu_cntr->lc_sum;
1003                        t.lc_min = percpu_cntr->lc_min;
1004                        t.lc_max = percpu_cntr->lc_max;
1005                        t.lc_sumsquare = percpu_cntr->lc_sumsquare;
1006                } while (centry != atomic_read(&percpu_cntr->lc_cntl.la_entry) &&
1007                         centry != atomic_read(&percpu_cntr->lc_cntl.la_exit));
1008                ret.lc_count += t.lc_count;
1009                ret.lc_sum += t.lc_sum;
1010                if (t.lc_min < ret.lc_min)
1011                        ret.lc_min = t.lc_min;
1012                if (t.lc_max > ret.lc_max)
1013                        ret.lc_max = t.lc_max;
1014                ret.lc_sumsquare += t.lc_sumsquare;
1015        }
1016
1017        rc = seq_printf(p, "%-25s "LPD64" samples [%s]", cntr->lc_name,
1018                        ret.lc_count, cntr->lc_units);
1019        if (rc < 0)
1020                goto out;
1021
1022        if ((cntr->lc_config & LPROCFS_CNTR_AVGMINMAX) && (ret.lc_count > 0)) {
1023                rc = seq_printf(p, " "LPD64" "LPD64" "LPD64,
1024                                ret.lc_min, ret.lc_max, ret.lc_sum);
1025                if (rc < 0)
1026                        goto out;
1027                if (cntr->lc_config & LPROCFS_CNTR_STDDEV)
1028                        rc = seq_printf(p, " "LPD64, ret.lc_sumsquare);
1029                if (rc < 0)
1030                        goto out;
1031        }
1032        rc = seq_printf(p, "\n");
1033  out:
1034        return (rc < 0) ? rc : 0;
1035 }
1036
1037 struct seq_operations lprocfs_stats_seq_sops = {
1038         start: lprocfs_stats_seq_start,
1039         stop:  lprocfs_stats_seq_stop,
1040         next:  lprocfs_stats_seq_next,
1041         show:  lprocfs_stats_seq_show,
1042 };
1043
1044 static int lprocfs_stats_seq_open(struct inode *inode, struct file *file)
1045 {
1046         struct proc_dir_entry *dp = PDE(inode);
1047         struct seq_file *seq;
1048         int rc;
1049
1050         LPROCFS_ENTRY_AND_CHECK(dp);
1051         rc = seq_open(file, &lprocfs_stats_seq_sops);
1052         if (rc) {
1053                 LPROCFS_EXIT();
1054                 return rc;
1055         }
1056         seq = file->private_data;
1057         seq->private = dp->data;
1058         return 0;
1059 }
1060
1061 struct file_operations lprocfs_stats_seq_fops = {
1062         .owner   = THIS_MODULE,
1063         .open    = lprocfs_stats_seq_open,
1064         .read    = seq_read,
1065         .write   = lprocfs_stats_seq_write,
1066         .llseek  = seq_lseek,
1067         .release = lprocfs_seq_release,
1068 };
1069
1070 int lprocfs_register_stats(struct proc_dir_entry *root, const char *name,
1071                            struct lprocfs_stats *stats)
1072 {
1073         struct proc_dir_entry *entry;
1074         LASSERT(root != NULL);
1075
1076         entry = create_proc_entry(name, 0644, root);
1077         if (entry == NULL)
1078                 return -ENOMEM;
1079         entry->proc_fops = &lprocfs_stats_seq_fops;
1080         entry->data = (void *)stats;
1081         return 0;
1082 }
1083
1084 void lprocfs_counter_init(struct lprocfs_stats *stats, int index,
1085                           unsigned conf, const char *name, const char *units)
1086 {
1087         struct lprocfs_counter *c;
1088         int i;
1089         unsigned int num_cpu;
1090
1091         LASSERT(stats != NULL);
1092
1093         num_cpu = lprocfs_stats_lock(stats, LPROCFS_GET_NUM_CPU);
1094
1095         for (i = 0; i < num_cpu; i++) {
1096                 c = &(stats->ls_percpu[i]->lp_cntr[index]);
1097                 c->lc_config = conf;
1098                 c->lc_count = 0;
1099                 c->lc_sum = 0;
1100                 c->lc_min = LC_MIN_INIT;
1101                 c->lc_max = 0;
1102                 c->lc_name = name;
1103                 c->lc_units = units;
1104         }
1105
1106         lprocfs_stats_unlock(stats);
1107 }
1108 EXPORT_SYMBOL(lprocfs_counter_init);
1109
1110 #define LPROCFS_OBD_OP_INIT(base, stats, op)                               \
1111 do {                                                                       \
1112         unsigned int coffset = base + OBD_COUNTER_OFFSET(op);              \
1113         LASSERT(coffset < stats->ls_num);                                  \
1114         lprocfs_counter_init(stats, coffset, 0, #op, "reqs");              \
1115 } while (0)
1116
1117 void lprocfs_init_ops_stats(int num_private_stats, struct lprocfs_stats *stats)
1118 {
1119         LPROCFS_OBD_OP_INIT(num_private_stats, stats, iocontrol);
1120         LPROCFS_OBD_OP_INIT(num_private_stats, stats, get_info);
1121         LPROCFS_OBD_OP_INIT(num_private_stats, stats, set_info_async);
1122         LPROCFS_OBD_OP_INIT(num_private_stats, stats, attach);
1123         LPROCFS_OBD_OP_INIT(num_private_stats, stats, detach);
1124         LPROCFS_OBD_OP_INIT(num_private_stats, stats, setup);
1125         LPROCFS_OBD_OP_INIT(num_private_stats, stats, precleanup);
1126         LPROCFS_OBD_OP_INIT(num_private_stats, stats, cleanup);
1127         LPROCFS_OBD_OP_INIT(num_private_stats, stats, process_config);
1128         LPROCFS_OBD_OP_INIT(num_private_stats, stats, postrecov);
1129         LPROCFS_OBD_OP_INIT(num_private_stats, stats, add_conn);
1130         LPROCFS_OBD_OP_INIT(num_private_stats, stats, del_conn);
1131         LPROCFS_OBD_OP_INIT(num_private_stats, stats, connect);
1132         LPROCFS_OBD_OP_INIT(num_private_stats, stats, reconnect);
1133         LPROCFS_OBD_OP_INIT(num_private_stats, stats, disconnect);
1134         LPROCFS_OBD_OP_INIT(num_private_stats, stats, fid_init);
1135         LPROCFS_OBD_OP_INIT(num_private_stats, stats, fid_fini);
1136         LPROCFS_OBD_OP_INIT(num_private_stats, stats, fid_alloc);
1137         LPROCFS_OBD_OP_INIT(num_private_stats, stats, fid_delete);
1138         LPROCFS_OBD_OP_INIT(num_private_stats, stats, statfs);
1139         LPROCFS_OBD_OP_INIT(num_private_stats, stats, statfs_async);
1140         LPROCFS_OBD_OP_INIT(num_private_stats, stats, packmd);
1141         LPROCFS_OBD_OP_INIT(num_private_stats, stats, unpackmd);
1142         LPROCFS_OBD_OP_INIT(num_private_stats, stats, checkmd);
1143         LPROCFS_OBD_OP_INIT(num_private_stats, stats, preallocate);
1144         LPROCFS_OBD_OP_INIT(num_private_stats, stats, precreate);
1145         LPROCFS_OBD_OP_INIT(num_private_stats, stats, create);
1146         LPROCFS_OBD_OP_INIT(num_private_stats, stats, destroy);
1147         LPROCFS_OBD_OP_INIT(num_private_stats, stats, setattr);
1148         LPROCFS_OBD_OP_INIT(num_private_stats, stats, setattr_async);
1149         LPROCFS_OBD_OP_INIT(num_private_stats, stats, getattr);
1150         LPROCFS_OBD_OP_INIT(num_private_stats, stats, getattr_async);
1151         LPROCFS_OBD_OP_INIT(num_private_stats, stats, brw);
1152         LPROCFS_OBD_OP_INIT(num_private_stats, stats, brw_async);
1153         LPROCFS_OBD_OP_INIT(num_private_stats, stats, prep_async_page);
1154         LPROCFS_OBD_OP_INIT(num_private_stats, stats, reget_short_lock);
1155         LPROCFS_OBD_OP_INIT(num_private_stats, stats, release_short_lock);
1156         LPROCFS_OBD_OP_INIT(num_private_stats, stats, queue_async_io);
1157         LPROCFS_OBD_OP_INIT(num_private_stats, stats, queue_group_io);
1158         LPROCFS_OBD_OP_INIT(num_private_stats, stats, trigger_group_io);
1159         LPROCFS_OBD_OP_INIT(num_private_stats, stats, set_async_flags);
1160         LPROCFS_OBD_OP_INIT(num_private_stats, stats, teardown_async_page);
1161         LPROCFS_OBD_OP_INIT(num_private_stats, stats, merge_lvb);
1162         LPROCFS_OBD_OP_INIT(num_private_stats, stats, adjust_kms);
1163         LPROCFS_OBD_OP_INIT(num_private_stats, stats, punch);
1164         LPROCFS_OBD_OP_INIT(num_private_stats, stats, sync);
1165         LPROCFS_OBD_OP_INIT(num_private_stats, stats, migrate);
1166         LPROCFS_OBD_OP_INIT(num_private_stats, stats, copy);
1167         LPROCFS_OBD_OP_INIT(num_private_stats, stats, iterate);
1168         LPROCFS_OBD_OP_INIT(num_private_stats, stats, preprw);
1169         LPROCFS_OBD_OP_INIT(num_private_stats, stats, commitrw);
1170         LPROCFS_OBD_OP_INIT(num_private_stats, stats, enqueue);
1171         LPROCFS_OBD_OP_INIT(num_private_stats, stats, match);
1172         LPROCFS_OBD_OP_INIT(num_private_stats, stats, change_cbdata);
1173         LPROCFS_OBD_OP_INIT(num_private_stats, stats, cancel);
1174         LPROCFS_OBD_OP_INIT(num_private_stats, stats, cancel_unused);
1175         LPROCFS_OBD_OP_INIT(num_private_stats, stats, join_lru);
1176         LPROCFS_OBD_OP_INIT(num_private_stats, stats, init_export);
1177         LPROCFS_OBD_OP_INIT(num_private_stats, stats, destroy_export);
1178         LPROCFS_OBD_OP_INIT(num_private_stats, stats, extent_calc);
1179         LPROCFS_OBD_OP_INIT(num_private_stats, stats, llog_init);
1180         LPROCFS_OBD_OP_INIT(num_private_stats, stats, llog_connect);
1181         LPROCFS_OBD_OP_INIT(num_private_stats, stats, llog_finish);
1182         LPROCFS_OBD_OP_INIT(num_private_stats, stats, pin);
1183         LPROCFS_OBD_OP_INIT(num_private_stats, stats, unpin);
1184         LPROCFS_OBD_OP_INIT(num_private_stats, stats, import_event);
1185         LPROCFS_OBD_OP_INIT(num_private_stats, stats, notify);
1186         LPROCFS_OBD_OP_INIT(num_private_stats, stats, health_check);
1187         LPROCFS_OBD_OP_INIT(num_private_stats, stats, get_uuid);
1188         LPROCFS_OBD_OP_INIT(num_private_stats, stats, quotacheck);
1189         LPROCFS_OBD_OP_INIT(num_private_stats, stats, quotactl);
1190         LPROCFS_OBD_OP_INIT(num_private_stats, stats, ping);
1191         LPROCFS_OBD_OP_INIT(num_private_stats, stats, register_page_removal_cb);
1192         LPROCFS_OBD_OP_INIT(num_private_stats,stats,unregister_page_removal_cb);
1193         LPROCFS_OBD_OP_INIT(num_private_stats, stats, register_lock_cancel_cb);
1194         LPROCFS_OBD_OP_INIT(num_private_stats, stats,unregister_lock_cancel_cb);
1195 }
1196
1197 int lprocfs_alloc_obd_stats(struct obd_device *obd, unsigned num_private_stats)
1198 {
1199         struct lprocfs_stats *stats;
1200         unsigned int num_stats;
1201         int rc, i;
1202
1203         LASSERT(obd->obd_stats == NULL);
1204         LASSERT(obd->obd_proc_entry != NULL);
1205         LASSERT(obd->obd_cntr_base == 0);
1206
1207         num_stats = ((int)sizeof(*obd->obd_type->typ_dt_ops) / sizeof(void *)) +
1208                 num_private_stats - 1 /* o_owner */;
1209         stats = lprocfs_alloc_stats(num_stats, 0);
1210         if (stats == NULL)
1211                 return -ENOMEM;
1212
1213         lprocfs_init_ops_stats(num_private_stats, stats);
1214
1215         for (i = num_private_stats; i < num_stats; i++) {
1216                 /* If this LBUGs, it is likely that an obd
1217                  * operation was added to struct obd_ops in
1218                  * <obd.h>, and that the corresponding line item
1219                  * LPROCFS_OBD_OP_INIT(.., .., opname)
1220                  * is missing from the list above. */
1221                 LASSERTF(stats->ls_percpu[0]->lp_cntr[i].lc_name != NULL,
1222                          "Missing obd_stat initializer obd_op "
1223                          "operation at offset %d.\n", i - num_private_stats);
1224         }
1225         rc = lprocfs_register_stats(obd->obd_proc_entry, "stats", stats);
1226         if (rc < 0) {
1227                 lprocfs_free_stats(&stats);
1228         } else {
1229                 obd->obd_stats  = stats;
1230                 obd->obd_cntr_base = num_private_stats;
1231         }
1232         return rc;
1233 }
1234
1235 void lprocfs_free_obd_stats(struct obd_device *obd)
1236 {
1237         if (obd->obd_stats) 
1238                 lprocfs_free_stats(&obd->obd_stats);
1239 }
1240
1241 #define LPROCFS_MD_OP_INIT(base, stats, op)                             \
1242 do {                                                                    \
1243         unsigned int coffset = base + MD_COUNTER_OFFSET(op);            \
1244         LASSERT(coffset < stats->ls_num);                               \
1245         lprocfs_counter_init(stats, coffset, 0, #op, "reqs");           \
1246 } while (0)
1247
1248 int lprocfs_alloc_md_stats(struct obd_device *obd,
1249                            unsigned num_private_stats)
1250 {
1251         struct lprocfs_stats *stats;
1252         unsigned int num_stats;
1253         int rc, i;
1254
1255         LASSERT(obd->md_stats == NULL);
1256         LASSERT(obd->obd_proc_entry != NULL);
1257         LASSERT(obd->md_cntr_base == 0);
1258
1259         num_stats = 1 + MD_COUNTER_OFFSET(get_remote_perm) +
1260                     num_private_stats;
1261         stats = lprocfs_alloc_stats(num_stats, 0);
1262         if (stats == NULL)
1263                 return -ENOMEM;
1264
1265         LPROCFS_MD_OP_INIT(num_private_stats, stats, getstatus);
1266         LPROCFS_MD_OP_INIT(num_private_stats, stats, change_cbdata);
1267         LPROCFS_MD_OP_INIT(num_private_stats, stats, close);
1268         LPROCFS_MD_OP_INIT(num_private_stats, stats, create);
1269         LPROCFS_MD_OP_INIT(num_private_stats, stats, done_writing);
1270         LPROCFS_MD_OP_INIT(num_private_stats, stats, enqueue);
1271         LPROCFS_MD_OP_INIT(num_private_stats, stats, getattr);
1272         LPROCFS_MD_OP_INIT(num_private_stats, stats, getattr_name);
1273         LPROCFS_MD_OP_INIT(num_private_stats, stats, intent_lock);
1274         LPROCFS_MD_OP_INIT(num_private_stats, stats, link);
1275         LPROCFS_MD_OP_INIT(num_private_stats, stats, rename);
1276         LPROCFS_MD_OP_INIT(num_private_stats, stats, is_subdir);
1277         LPROCFS_MD_OP_INIT(num_private_stats, stats, setattr);
1278         LPROCFS_MD_OP_INIT(num_private_stats, stats, sync);
1279         LPROCFS_MD_OP_INIT(num_private_stats, stats, readpage);
1280         LPROCFS_MD_OP_INIT(num_private_stats, stats, unlink);
1281         LPROCFS_MD_OP_INIT(num_private_stats, stats, setxattr);
1282         LPROCFS_MD_OP_INIT(num_private_stats, stats, getxattr);
1283         LPROCFS_MD_OP_INIT(num_private_stats, stats, init_ea_size);
1284         LPROCFS_MD_OP_INIT(num_private_stats, stats, get_lustre_md);
1285         LPROCFS_MD_OP_INIT(num_private_stats, stats, free_lustre_md);
1286         LPROCFS_MD_OP_INIT(num_private_stats, stats, set_open_replay_data);
1287         LPROCFS_MD_OP_INIT(num_private_stats, stats, clear_open_replay_data);
1288         LPROCFS_MD_OP_INIT(num_private_stats, stats, set_lock_data);
1289         LPROCFS_MD_OP_INIT(num_private_stats, stats, lock_match);
1290         LPROCFS_MD_OP_INIT(num_private_stats, stats, cancel_unused);
1291         LPROCFS_MD_OP_INIT(num_private_stats, stats, renew_capa);
1292         LPROCFS_MD_OP_INIT(num_private_stats, stats, get_remote_perm);
1293         LPROCFS_MD_OP_INIT(num_private_stats, stats, intent_getattr_async);
1294         LPROCFS_MD_OP_INIT(num_private_stats, stats, revalidate_lock);
1295
1296         for (i = num_private_stats; i < num_stats; i++) {
1297                 if (stats->ls_percpu[0]->lp_cntr[i].lc_name == NULL) {
1298                         CERROR("Missing md_stat initializer md_op "
1299                                "operation at offset %d. Aborting.\n",
1300                                i - num_private_stats);
1301                         LBUG();
1302                 }
1303         }
1304         rc = lprocfs_register_stats(obd->obd_proc_entry, "stats", stats);
1305         if (rc < 0) {
1306                 lprocfs_free_stats(&stats);
1307         } else {
1308                 obd->md_stats  = stats;
1309                 obd->md_cntr_base = num_private_stats;
1310         }
1311         return rc;
1312 }
1313
1314 void lprocfs_free_md_stats(struct obd_device *obd)
1315 {
1316         struct lprocfs_stats *stats = obd->md_stats;
1317
1318         if (stats != NULL) {
1319                 obd->md_stats = NULL;
1320                 lprocfs_free_stats(&stats);
1321         }
1322 }
1323
1324 int lprocfs_exp_rd_nid(char *page, char **start, off_t off, int count,
1325                          int *eof,  void *data)
1326 {
1327         struct obd_export *exp = (struct obd_export*)data;
1328         LASSERT(exp != NULL);
1329         *eof = 1;
1330         return snprintf(page, count, "%s\n", obd_export_nid2str(exp));
1331 }
1332
1333 struct exp_uuid_cb_data {
1334         char                   *page;
1335         int                     count;
1336         int                    *eof;
1337         int                    *len;
1338 };
1339
1340 void lprocfs_exp_print_uuid(void *obj, void *cb_data)
1341 {
1342         struct obd_export *exp = (struct obd_export *)obj;
1343         struct exp_uuid_cb_data *data = (struct exp_uuid_cb_data *)cb_data;
1344
1345         if (exp->exp_nid_stats)
1346                 *data->len += snprintf((data->page + *data->len),
1347                                        data->count, "%s\n",
1348                                        obd_uuid2str(&exp->exp_client_uuid));
1349 }
1350
1351 int lprocfs_exp_rd_uuid(char *page, char **start, off_t off, int count,
1352                         int *eof,  void *data)
1353 {
1354         struct nid_stat *stats = (struct nid_stat *)data;
1355         struct exp_uuid_cb_data cb_data;
1356         struct obd_device *obd = stats->nid_obd;
1357         int len = 0;
1358
1359         *eof = 1;
1360         page[0] = '\0';
1361         LASSERT(obd != NULL);
1362
1363         cb_data.page = page;
1364         cb_data.count = count;
1365         cb_data.eof = eof;
1366         cb_data.len = &len;
1367         lustre_hash_bucket_iterate(obd->obd_nid_hash_body,
1368                                    &stats->nid, lprocfs_exp_print_uuid,
1369                                    &cb_data);
1370         return (*cb_data.len);
1371 }
1372
1373 int lprocfs_nid_stats_clear_read(char *page, char **start, off_t off,
1374                                         int count, int *eof,  void *data)
1375 {
1376         *eof = 1;
1377         return snprintf(page, count, "%s\n",
1378                         "Write into this file to clear all nid stats and "
1379                         "stale nid entries");
1380 }
1381 EXPORT_SYMBOL(lprocfs_nid_stats_clear_read);
1382
1383 void lprocfs_nid_stats_clear_write_cb(void *obj, void *data)
1384 {
1385         struct nid_stat *stat = obj;
1386         int i;
1387
1388         /* object has only hash + iterate_all references.
1389          * add/delete blocked by hash bucket lock */
1390         CDEBUG(D_INFO,"refcnt %d\n", stat->nid_exp_ref_count);
1391         if(stat->nid_exp_ref_count == 2) {
1392                 hlist_del_init(&stat->nid_hash);
1393                 stat->nid_exp_ref_count--;
1394                 spin_lock(&stat->nid_obd->obd_nid_lock);
1395                 list_del_init(&stat->nid_list);
1396                 spin_unlock(&stat->nid_obd->obd_nid_lock);
1397                 list_add(&stat->nid_list, data);
1398                 EXIT;
1399                 return;
1400         }
1401         /* we has reference to object - only clear data*/
1402         if (stat->nid_stats)
1403                 lprocfs_clear_stats(stat->nid_stats);
1404
1405         if (stat->nid_brw_stats) {
1406                 for (i = 0; i < BRW_LAST; i++)
1407                         lprocfs_oh_clear(&stat->nid_brw_stats->hist[i]);
1408         }
1409         EXIT;
1410         return;
1411 }
1412
1413 int lprocfs_nid_stats_clear_write(struct file *file, const char *buffer,
1414                                          unsigned long count, void *data)
1415 {
1416         struct obd_device *obd = (struct obd_device *)data;
1417         struct nid_stat *client_stat;
1418         CFS_LIST_HEAD(free_list);
1419
1420         lustre_hash_iterate_all(obd->obd_nid_stats_hash_body,
1421                                 lprocfs_nid_stats_clear_write_cb, &free_list);
1422
1423         while (!list_empty(&free_list)) {
1424                 client_stat = list_entry(free_list.next, struct nid_stat, nid_list);
1425                 list_del_init(&client_stat->nid_list);
1426                 lprocfs_free_client_stats(client_stat);
1427         }
1428
1429         return count;
1430 }
1431 EXPORT_SYMBOL(lprocfs_nid_stats_clear_write);
1432
1433 int lprocfs_exp_setup(struct obd_export *exp, lnet_nid_t *nid, int *newnid)
1434 {
1435         int rc = 0;
1436         struct nid_stat *tmp = NULL, *tmp1;
1437         struct obd_device *obd = NULL;
1438         ENTRY;
1439
1440         *newnid = 0;
1441
1442         if (!exp || !exp->exp_obd || !exp->exp_obd->obd_proc_exports_entry ||
1443             !exp->exp_obd->obd_nid_stats_hash_body)
1444                 RETURN(-EINVAL);
1445
1446         /* not test against zero because eric say:
1447          * You may only test nid against another nid, or LNET_NID_ANY.  Anything else is
1448          * nonsense.*/
1449         if (!nid || *nid == LNET_NID_ANY)
1450                 RETURN(0);
1451
1452         obd = exp->exp_obd;
1453
1454         CDEBUG(D_CONFIG, "using hash %p\n", obd->obd_nid_stats_hash_body);
1455
1456         OBD_ALLOC(tmp, sizeof(struct nid_stat));
1457         if (tmp == NULL)
1458                 RETURN(-ENOMEM);
1459
1460         tmp->nid = *nid;
1461         tmp->nid_obd = exp->exp_obd;
1462         tmp->nid_exp_ref_count = 1; /* need live in hash after destroy export */
1463
1464        /* protect competitive add to list, not need locking on destroy */
1465         spin_lock(&obd->obd_nid_lock);
1466         list_add(&tmp->nid_list, &obd->obd_nid_stats);
1467         spin_unlock(&obd->obd_nid_lock);
1468
1469         tmp1= lustre_hash_findadd_unique(obd->obd_nid_stats_hash_body, nid,
1470                                          &tmp->nid_hash);
1471         CDEBUG(D_INFO, "Found stats %p for nid %s - ref %d\n",
1472                tmp1, libcfs_nid2str(*nid), tmp->nid_exp_ref_count);
1473
1474         if (tmp1 != tmp) {
1475                 exp->exp_nid_stats = tmp1;
1476                 GOTO(destroy_new, rc = 0);
1477         }
1478         /* not found - create */
1479         tmp->nid_proc = lprocfs_register(libcfs_nid2str(*nid),
1480                                          obd->obd_proc_exports_entry, NULL, NULL);
1481         if (!tmp->nid_proc) {
1482                 CERROR("Error making export directory for"
1483                        " nid %s\n", libcfs_nid2str(*nid));
1484                 lustre_hash_delitem(obd->obd_nid_stats_hash_body, nid,
1485                                     &tmp->nid_hash);
1486                 GOTO(destroy_new, rc = -ENOMEM);
1487         }
1488
1489         rc = lprocfs_add_simple(tmp->nid_proc, "uuid",
1490                                 lprocfs_exp_rd_uuid, NULL, tmp);
1491         if (rc)
1492                 CWARN("Error adding the uuid file\n");
1493
1494         exp->exp_nid_stats = tmp;
1495         *newnid = 1;
1496         RETURN(rc);
1497
1498 destroy_new:
1499         spin_lock(&obd->obd_nid_lock);
1500         list_del(&tmp->nid_list);
1501         spin_unlock(&obd->obd_nid_lock);
1502         OBD_FREE(tmp, sizeof(struct nid_stat));
1503         RETURN(rc);
1504 }
1505
1506 int lprocfs_exp_cleanup(struct obd_export *exp)
1507 {
1508         struct nid_stat *stat = exp->exp_nid_stats;
1509
1510         if(!stat)
1511                 RETURN(0);
1512
1513         stat->nid_exp_ref_count--;
1514         CDEBUG(D_INFO, "Put stat %p - %d\n", stat, stat->nid_exp_ref_count);
1515
1516         exp->exp_nid_stats = NULL;
1517         return 0;
1518 }
1519
1520 int lprocfs_write_helper(const char *buffer, unsigned long count,
1521                          int *val)
1522 {
1523         return lprocfs_write_frac_helper(buffer, count, val, 1);
1524 }
1525
1526 int lprocfs_write_frac_helper(const char *buffer, unsigned long count,
1527                               int *val, int mult)
1528 {
1529         char kernbuf[20], *end, *pbuf;
1530
1531         if (count > (sizeof(kernbuf) - 1))
1532                 return -EINVAL;
1533
1534         if (copy_from_user(kernbuf, buffer, count))
1535                 return -EFAULT;
1536
1537         kernbuf[count] = '\0';
1538         pbuf = kernbuf;
1539         if (*pbuf == '-') {
1540                 mult = -mult;
1541                 pbuf++;
1542         }
1543
1544         *val = (int)simple_strtoul(pbuf, &end, 10) * mult;
1545         if (pbuf == end)
1546                 return -EINVAL;
1547
1548         if (end != NULL && *end == '.') {
1549                 int temp_val, pow = 1;
1550                 int i;
1551
1552                 pbuf = end + 1;
1553                 if (strlen(pbuf) > 5)
1554                         pbuf[5] = '\0'; /*only allow 5bits fractional*/
1555
1556                 temp_val = (int)simple_strtoul(pbuf, &end, 10) * mult;
1557
1558                 if (pbuf < end) {
1559                         for (i = 0; i < (end - pbuf); i++)
1560                                 pow *= 10;
1561
1562                         *val += temp_val / pow;
1563                 }
1564         }
1565         return 0;
1566 }
1567
1568 int lprocfs_read_frac_helper(char *buffer, unsigned long count, long val, int mult)
1569 {
1570         long decimal_val, frac_val;
1571         int prtn;
1572
1573         if (count < 10)
1574                 return -EINVAL;
1575
1576         decimal_val = val / mult;
1577         prtn = snprintf(buffer, count, "%ld", decimal_val);
1578         frac_val = val % mult;
1579
1580         if (prtn < (count - 4) && frac_val > 0) {
1581                 long temp_frac;
1582                 int i, temp_mult = 1, frac_bits = 0;
1583
1584                 temp_frac = frac_val * 10;
1585                 buffer[prtn++] = '.';
1586                 while (frac_bits < 2 && (temp_frac / mult) < 1 ) { /*only reserved 2bits fraction*/
1587                         buffer[prtn++] ='0';
1588                         temp_frac *= 10;
1589                         frac_bits++;
1590                 }
1591                 /*
1592                   Need to think these cases :
1593                         1. #echo x.00 > /proc/xxx       output result : x
1594                         2. #echo x.0x > /proc/xxx       output result : x.0x
1595                         3. #echo x.x0 > /proc/xxx       output result : x.x
1596                         4. #echo x.xx > /proc/xxx       output result : x.xx
1597                         Only reserved 2bits fraction.       
1598                  */
1599                 for (i = 0; i < (5 - prtn); i++)
1600                         temp_mult *= 10;
1601
1602                 frac_bits = min((int)count - prtn, 3 - frac_bits);
1603                 prtn += snprintf(buffer + prtn, frac_bits, "%ld", frac_val * temp_mult / mult);
1604
1605                 prtn--;
1606                 while(buffer[prtn] < '1' || buffer[prtn] > '9') {
1607                         prtn--;
1608                         if (buffer[prtn] == '.') {
1609                                 prtn--;
1610                                 break;
1611                         }
1612                 }
1613                 prtn++;
1614         }
1615         buffer[prtn++] ='\n';
1616         return prtn;
1617 }
1618
1619 int lprocfs_write_u64_helper(const char *buffer, unsigned long count,__u64 *val)
1620 {
1621         return lprocfs_write_frac_u64_helper(buffer, count, val, 1);
1622 }
1623
1624 int lprocfs_write_frac_u64_helper(const char *buffer, unsigned long count,
1625                               __u64 *val, int mult)
1626 {
1627         char kernbuf[22], *end, *pbuf;
1628         __u64 whole, frac = 0, units;
1629         unsigned frac_d = 1;
1630
1631         if (count > (sizeof(kernbuf) - 1) )
1632                 return -EINVAL;
1633
1634         if (copy_from_user(kernbuf, buffer, count))
1635                 return -EFAULT;
1636
1637         kernbuf[count] = '\0';
1638         pbuf = kernbuf;
1639         if (*pbuf == '-') {
1640                 mult = -mult;
1641                 pbuf++;
1642         }
1643
1644         whole = simple_strtoull(pbuf, &end, 10);
1645         if (pbuf == end)
1646                 return -EINVAL;
1647
1648         if (end != NULL && *end == '.') {
1649                 int i;
1650                 pbuf = end + 1;
1651
1652                 /* need to limit frac_d to a __u32 */
1653                 if (strlen(pbuf) > 10)
1654                         pbuf[10] = '\0';
1655
1656                 frac = simple_strtoull(pbuf, &end, 10);
1657                 /* count decimal places */
1658                 for (i = 0; i < (end - pbuf); i++)
1659                         frac_d *= 10;
1660         }
1661
1662         units = 1;
1663         switch(*end) {
1664         case 'p': case 'P':
1665                 units <<= 10;
1666         case 't': case 'T':
1667                 units <<= 10;
1668         case 'g': case 'G':
1669                 units <<= 10;
1670         case 'm': case 'M':
1671                 units <<= 10;
1672         case 'k': case 'K':
1673                 units <<= 10;
1674         }
1675         /* Specified units override the multiplier */
1676         if (units) 
1677                 mult = mult < 0 ? -units : units;
1678
1679         frac *= mult;
1680         do_div(frac, frac_d);
1681         *val = whole * mult + frac;
1682         return 0;
1683 }
1684
1685 int lprocfs_seq_create(cfs_proc_dir_entry_t *parent, 
1686                        char *name, mode_t mode,
1687                        struct file_operations *seq_fops, void *data)
1688 {
1689         struct proc_dir_entry *entry;
1690         ENTRY;
1691
1692         entry = create_proc_entry(name, mode, parent);
1693         if (entry == NULL)
1694                 RETURN(-ENOMEM);
1695         entry->proc_fops = seq_fops;
1696         entry->data = data;
1697
1698         RETURN(0);
1699 }
1700 EXPORT_SYMBOL(lprocfs_seq_create);
1701
1702 __inline__ int lprocfs_obd_seq_create(struct obd_device *dev, char *name,
1703                                       mode_t mode,
1704                                       struct file_operations *seq_fops,
1705                                       void *data)
1706 {
1707         return (lprocfs_seq_create(dev->obd_proc_entry, name, 
1708                                    mode, seq_fops, data));
1709 }
1710 EXPORT_SYMBOL(lprocfs_obd_seq_create);
1711
1712 void lprocfs_oh_tally(struct obd_histogram *oh, unsigned int value)
1713 {
1714         if (value >= OBD_HIST_MAX)
1715                 value = OBD_HIST_MAX - 1;
1716
1717         spin_lock(&oh->oh_lock);
1718         oh->oh_buckets[value]++;
1719         spin_unlock(&oh->oh_lock);
1720 }
1721 EXPORT_SYMBOL(lprocfs_oh_tally);
1722
1723 void lprocfs_oh_tally_log2(struct obd_histogram *oh, unsigned int value)
1724 {
1725         unsigned int val;
1726
1727         for (val = 0; ((1 << val) < value) && (val <= OBD_HIST_MAX); val++)
1728                 ;
1729
1730         lprocfs_oh_tally(oh, val);
1731 }
1732 EXPORT_SYMBOL(lprocfs_oh_tally_log2);
1733
1734 unsigned long lprocfs_oh_sum(struct obd_histogram *oh)
1735 {
1736         unsigned long ret = 0;
1737         int i;
1738
1739         for (i = 0; i < OBD_HIST_MAX; i++)
1740                 ret +=  oh->oh_buckets[i];
1741         return ret;
1742 }
1743 EXPORT_SYMBOL(lprocfs_oh_sum);
1744
1745 void lprocfs_oh_clear(struct obd_histogram *oh)
1746 {
1747         spin_lock(&oh->oh_lock);
1748         memset(oh->oh_buckets, 0, sizeof(oh->oh_buckets));
1749         spin_unlock(&oh->oh_lock);
1750 }
1751 EXPORT_SYMBOL(lprocfs_oh_clear);
1752
1753 int lprocfs_obd_rd_recovery_status(char *page, char **start, off_t off,
1754                                    int count, int *eof, void *data)
1755 {
1756         struct obd_device *obd = data;
1757         int len = 0, size;
1758
1759         LASSERT(obd != NULL);
1760         LASSERT(count >= 0);
1761
1762         /* Set start of user data returned to
1763            page + off since the user may have
1764            requested to read much smaller than
1765            what we need to read */
1766         *start = page + off;
1767
1768         /* We know we are allocated a page here.
1769            Also we know that this function will
1770            not need to write more than a page
1771            so we can truncate at CFS_PAGE_SIZE.  */
1772         size = min(count + (int)off + 1, (int)CFS_PAGE_SIZE);
1773
1774         /* Initialize the page */
1775         memset(page, 0, size);
1776
1777         if (lprocfs_obd_snprintf(&page, size, &len, "status: ") <= 0)
1778                 goto out;
1779         if (obd->obd_max_recoverable_clients == 0) {
1780                 if (lprocfs_obd_snprintf(&page, size, &len, "INACTIVE\n") <= 0)
1781                         goto out;
1782
1783                 goto fclose;
1784         }
1785
1786         /* sampled unlocked, but really... */
1787         if (obd->obd_recovering == 0) {
1788                 if (lprocfs_obd_snprintf(&page, size, &len, "COMPLETE\n") <= 0)
1789                         goto out;
1790                 if (lprocfs_obd_snprintf(&page, size, &len,
1791                                          "recovery_start: %lu\n",
1792                                          obd->obd_recovery_start) <= 0)
1793                         goto out;
1794                 if (lprocfs_obd_snprintf(&page, size, &len,
1795                                          "recovery_duration: %lu\n",
1796                                          obd->obd_recovery_end -
1797                                          obd->obd_recovery_start) <= 0)
1798                         goto out;
1799                 /* Number of clients that have completed recovery */
1800                 if (lprocfs_obd_snprintf(&page, size, &len,
1801                                          "completed_clients: %d/%d\n",
1802                                          obd->obd_max_recoverable_clients -
1803                                          obd->obd_recoverable_clients,
1804                                          obd->obd_max_recoverable_clients) <= 0)
1805                         goto out;
1806                 if (lprocfs_obd_snprintf(&page, size, &len,
1807                                          "replayed_requests: %d\n",
1808                                          obd->obd_replayed_requests) <= 0)
1809                         goto out;
1810                 if (lprocfs_obd_snprintf(&page, size, &len,
1811                                          "last_transno: "LPD64"\n",
1812                                          obd->obd_next_recovery_transno - 1)<=0)
1813                         goto out;
1814                 goto fclose;
1815         }
1816
1817         if (lprocfs_obd_snprintf(&page, size, &len, "RECOVERING\n") <= 0)
1818                 goto out;
1819         if (lprocfs_obd_snprintf(&page, size, &len, "recovery_start: %lu\n",
1820                                  obd->obd_recovery_start) <= 0)
1821                 goto out;
1822         if (lprocfs_obd_snprintf(&page, size, &len, "time_remaining: %lu\n",
1823                            cfs_time_current_sec() >= obd->obd_recovery_end ? 0 :
1824                            obd->obd_recovery_end - cfs_time_current_sec()) <= 0)
1825                 goto out;
1826         if (lprocfs_obd_snprintf(&page, size, &len,"connected_clients: %d/%d\n",
1827                                  obd->obd_connected_clients,
1828                                  obd->obd_max_recoverable_clients) <= 0)
1829                 goto out;
1830         /* Number of clients that have completed recovery */
1831         if (lprocfs_obd_snprintf(&page, size, &len,"completed_clients: %d/%d\n",
1832                                  obd->obd_max_recoverable_clients -
1833                                  obd->obd_recoverable_clients,
1834                                  obd->obd_max_recoverable_clients) <= 0)
1835                 goto out;
1836         if (lprocfs_obd_snprintf(&page, size, &len,"replayed_requests: %d/??\n",
1837                                  obd->obd_replayed_requests) <= 0)
1838                 goto out;
1839         if (lprocfs_obd_snprintf(&page, size, &len, "queued_requests: %d\n",
1840                                  obd->obd_requests_queued_for_recovery) <= 0)
1841                 goto out;
1842
1843         if (lprocfs_obd_snprintf(&page, size, &len, "next_transno: "LPD64"\n", 
1844                                  obd->obd_next_recovery_transno) <= 0)
1845                 goto out;
1846
1847 fclose:
1848         *eof = 1;
1849 out:
1850         return min(count, len - (int)off);
1851 }
1852 EXPORT_SYMBOL(lprocfs_obd_rd_recovery_status);
1853
1854 int lprocfs_obd_rd_recovery_maxtime(char *page, char **start, off_t off,
1855                                     int count, int *eof, void *data)
1856 {
1857         struct obd_device *obd = (struct obd_device *)data;
1858         LASSERT(obd != NULL);
1859
1860         return snprintf(page, count, "%lu\n", 
1861                         obd->obd_recovery_max_time);
1862 }
1863 EXPORT_SYMBOL(lprocfs_obd_rd_recovery_maxtime);
1864
1865 int lprocfs_obd_wr_recovery_maxtime(struct file *file, const char *buffer,
1866                                     unsigned long count, void *data)
1867 {
1868         struct obd_device *obd = (struct obd_device *)data;
1869         int val, rc;
1870         LASSERT(obd != NULL);
1871
1872         rc = lprocfs_write_helper(buffer, count, &val);
1873         if (rc)
1874                 return rc;
1875
1876         obd->obd_recovery_max_time = val;
1877         return count;
1878 }
1879 EXPORT_SYMBOL(lprocfs_obd_wr_recovery_maxtime);
1880
1881 EXPORT_SYMBOL(lprocfs_register);
1882 EXPORT_SYMBOL(lprocfs_srch);
1883 EXPORT_SYMBOL(lprocfs_remove);
1884 EXPORT_SYMBOL(lprocfs_remove_proc_entry);
1885 EXPORT_SYMBOL(lprocfs_add_vars);
1886 EXPORT_SYMBOL(lprocfs_obd_setup);
1887 EXPORT_SYMBOL(lprocfs_obd_cleanup);
1888 EXPORT_SYMBOL(lprocfs_add_simple);
1889 EXPORT_SYMBOL(lprocfs_add_symlink);
1890 EXPORT_SYMBOL(lprocfs_free_per_client_stats);
1891 EXPORT_SYMBOL(lprocfs_alloc_stats);
1892 EXPORT_SYMBOL(lprocfs_free_stats);
1893 EXPORT_SYMBOL(lprocfs_clear_stats);
1894 EXPORT_SYMBOL(lprocfs_register_stats);
1895 EXPORT_SYMBOL(lprocfs_init_ops_stats);
1896 EXPORT_SYMBOL(lprocfs_alloc_obd_stats);
1897 EXPORT_SYMBOL(lprocfs_free_obd_stats);
1898 EXPORT_SYMBOL(lprocfs_exp_setup);
1899 EXPORT_SYMBOL(lprocfs_exp_cleanup);
1900
1901 EXPORT_SYMBOL(lprocfs_rd_u64);
1902 EXPORT_SYMBOL(lprocfs_rd_atomic);
1903 EXPORT_SYMBOL(lprocfs_wr_atomic);
1904 EXPORT_SYMBOL(lprocfs_rd_uint);
1905 EXPORT_SYMBOL(lprocfs_wr_uint);
1906 EXPORT_SYMBOL(lprocfs_rd_uuid);
1907 EXPORT_SYMBOL(lprocfs_rd_name);
1908 EXPORT_SYMBOL(lprocfs_rd_fstype);
1909 EXPORT_SYMBOL(lprocfs_rd_server_uuid);
1910 EXPORT_SYMBOL(lprocfs_rd_conn_uuid);
1911 EXPORT_SYMBOL(lprocfs_rd_num_exports);
1912 EXPORT_SYMBOL(lprocfs_rd_numrefs);
1913 EXPORT_SYMBOL(lprocfs_at_hist_helper);
1914 EXPORT_SYMBOL(lprocfs_rd_timeouts);
1915 EXPORT_SYMBOL(lprocfs_rd_blksize);
1916 EXPORT_SYMBOL(lprocfs_rd_kbytestotal);
1917 EXPORT_SYMBOL(lprocfs_rd_kbytesfree);
1918 EXPORT_SYMBOL(lprocfs_rd_kbytesavail);
1919 EXPORT_SYMBOL(lprocfs_rd_filestotal);
1920 EXPORT_SYMBOL(lprocfs_rd_filesfree);
1921
1922 EXPORT_SYMBOL(lprocfs_write_helper);
1923 EXPORT_SYMBOL(lprocfs_write_frac_helper);
1924 EXPORT_SYMBOL(lprocfs_read_frac_helper);
1925 EXPORT_SYMBOL(lprocfs_write_u64_helper);
1926 EXPORT_SYMBOL(lprocfs_write_frac_u64_helper);
1927 #endif /* LPROCFS*/