Whamcloud - gitweb
Branch HEAD
[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 #include <lustre_log.h>
54 #include <lustre/lustre_idl.h>
55
56 #if defined(LPROCFS)
57
58 #define MAX_STRING_SIZE 128
59
60 /* for bug 10866, global variable */
61 DECLARE_RWSEM(_lprocfs_lock);
62 EXPORT_SYMBOL(_lprocfs_lock);
63
64 int lprocfs_seq_release(struct inode *inode, struct file *file)
65 {
66         LPROCFS_EXIT();
67         return seq_release(inode, file);
68 }
69 EXPORT_SYMBOL(lprocfs_seq_release);
70
71 struct proc_dir_entry *lprocfs_srch(struct proc_dir_entry *head,
72                                     const char *name)
73 {
74         struct proc_dir_entry *temp;
75
76         if (head == NULL)
77                 return NULL;
78         LPROCFS_ENTRY();
79
80         temp = head->subdir;
81         while (temp != NULL) {
82                 if (strcmp(temp->name, name) == 0) {
83                         LPROCFS_EXIT();
84                         return temp;
85                 }
86
87                 temp = temp->next;
88         }
89         LPROCFS_EXIT();
90         return NULL;
91 }
92
93 /* lprocfs API calls */
94
95 /* Function that emulates snprintf but also has the side effect of advancing
96    the page pointer for the next write into the buffer, incrementing the total
97    length written to the buffer, and decrementing the size left in the
98    buffer. */
99 static int lprocfs_obd_snprintf(char **page, int end, int *len,
100                                 const char *format, ...)
101 {
102         va_list list;
103         int n;
104
105         if (*len >= end)
106                 return 0;
107
108         va_start(list, format);
109         n = vsnprintf(*page, end - *len, format, list);
110         va_end(list);
111
112         *page += n; *len += n;
113         return n;
114 }
115
116 cfs_proc_dir_entry_t *lprocfs_add_simple(struct proc_dir_entry *root,
117                                          char *name,
118                                          read_proc_t *read_proc,
119                                          write_proc_t *write_proc,
120                                          void *data,
121                                          struct file_operations *fops)
122 {
123         cfs_proc_dir_entry_t *proc;
124         mode_t mode = 0;
125
126         if (root == NULL || name == NULL)
127                 return ERR_PTR(-EINVAL);
128         if (read_proc)
129                 mode = 0444;
130         if (write_proc)
131                 mode |= 0200;
132         if (fops)
133                 mode = 0644;
134         proc = create_proc_entry(name, mode, root);
135         if (!proc) {
136                 CERROR("LprocFS: No memory to create /proc entry %s", name);
137                 return ERR_PTR(-ENOMEM);
138         }
139         proc->read_proc = read_proc;
140         proc->write_proc = write_proc;
141         proc->data = data;
142         if (fops)
143                 proc->proc_fops = fops;
144         return proc;
145 }
146
147 struct proc_dir_entry *lprocfs_add_symlink(const char *name,
148                         struct proc_dir_entry *parent, const char *dest)
149 {
150         struct proc_dir_entry *entry;
151
152         if (parent == NULL || dest == NULL)
153                 return NULL;
154
155         entry = proc_symlink(name, parent, dest);
156         if (entry == NULL)
157                 CERROR("LprocFS: Could not create symbolic link from %s to %s",
158                         name, dest);
159         return entry;
160 }
161
162 static ssize_t lprocfs_fops_read(struct file *f, char __user *buf,
163                                  size_t size, loff_t *ppos)
164 {
165         struct proc_dir_entry *dp = PDE(f->f_dentry->d_inode);
166         char *page, *start = NULL;
167         int rc = 0, eof = 1, count;
168
169         if (*ppos >= CFS_PAGE_SIZE)
170                 return 0;
171
172         page = (char *)__get_free_page(GFP_KERNEL);
173         if (page == NULL)
174                 return -ENOMEM;
175
176         LPROCFS_ENTRY();
177         OBD_FAIL_TIMEOUT(OBD_FAIL_LPROC_REMOVE, 10);
178         if (!dp->deleted && dp->read_proc)
179                 rc = dp->read_proc(page, &start, *ppos, CFS_PAGE_SIZE,
180                                    &eof, dp->data);
181         LPROCFS_EXIT();
182         if (rc <= 0)
183                 goto out;
184
185         /* for lustre proc read, the read count must be less than PAGE_SIZE */
186         LASSERT(eof == 1);
187
188         if (start == NULL) {
189                 rc -= *ppos;
190                 if (rc < 0)
191                         rc = 0;
192                 if (rc == 0)
193                         goto out;
194                 start = page + *ppos;
195         } else if (start < page) {
196                 start = page;
197         }
198
199         count = (rc < size) ? rc : size;
200         if (copy_to_user(buf, start, count)) {
201                 rc = -EFAULT;
202                 goto out;
203         }
204         *ppos += count;
205
206 out:
207         free_page((unsigned long)page);
208         return rc;
209 }
210
211 static ssize_t lprocfs_fops_write(struct file *f, const char __user *buf,
212                                   size_t size, loff_t *ppos)
213 {
214         struct proc_dir_entry *dp = PDE(f->f_dentry->d_inode);
215         int rc = -EIO;
216
217         LPROCFS_ENTRY();
218         if (!dp->deleted && dp->write_proc)
219                 rc = dp->write_proc(f, buf, size, dp->data);
220         LPROCFS_EXIT();
221         return rc;
222 }
223
224 static struct file_operations lprocfs_generic_fops = {
225         .owner = THIS_MODULE,
226         .read = lprocfs_fops_read,
227         .write = lprocfs_fops_write,
228 };
229
230 int lprocfs_evict_client_open(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_inc(&obd->obd_evict_inprogress);
236
237         return 0;
238 }
239
240 int lprocfs_evict_client_release(struct inode *inode, struct file *f)
241 {
242         struct proc_dir_entry *dp = PDE(f->f_dentry->d_inode);
243         struct obd_device *obd = dp->data;
244
245         atomic_dec(&obd->obd_evict_inprogress);
246         wake_up(&obd->obd_evict_inprogress_waitq);
247
248         return 0;
249 }
250
251 struct file_operations lprocfs_evict_client_fops = {
252         .owner = THIS_MODULE,
253         .read = lprocfs_fops_read,
254         .write = lprocfs_fops_write,
255         .open = lprocfs_evict_client_open,
256         .release = lprocfs_evict_client_release,
257 };
258 EXPORT_SYMBOL(lprocfs_evict_client_fops);
259
260 /**
261  * Add /proc entries.
262  *
263  * \param root [in]  The parent proc entry on which new entry will be added.
264  * \param list [in]  Array of proc entries to be added.
265  * \param data [in]  The argument to be passed when entries read/write routines
266  *                   are called through /proc file.
267  *
268  * \retval 0   on success
269  *         < 0 on error
270  */
271 int lprocfs_add_vars(struct proc_dir_entry *root, struct lprocfs_vars *list,
272                      void *data)
273 {
274         if (root == NULL || list == NULL)
275                 return -EINVAL;
276
277         while (list->name != NULL) {
278                 struct proc_dir_entry *cur_root, *proc;
279                 char *pathcopy, *cur, *next, pathbuf[64];
280                 int pathsize = strlen(list->name) + 1;
281
282                 proc = NULL;
283                 cur_root = root;
284
285                 /* need copy of path for strsep */
286                 if (strlen(list->name) > sizeof(pathbuf) - 1) {
287                         OBD_ALLOC(pathcopy, pathsize);
288                         if (pathcopy == NULL)
289                                 return -ENOMEM;
290                 } else {
291                         pathcopy = pathbuf;
292                 }
293
294                 next = pathcopy;
295                 strcpy(pathcopy, list->name);
296
297                 while (cur_root != NULL && (cur = strsep(&next, "/"))) {
298                         if (*cur =='\0') /* skip double/trailing "/" */
299                                 continue;
300
301                         proc = lprocfs_srch(cur_root, cur);
302                         CDEBUG(D_OTHER, "cur_root=%s, cur=%s, next=%s, (%s)\n",
303                                cur_root->name, cur, next,
304                                (proc ? "exists" : "new"));
305                         if (next != NULL) {
306                                 cur_root = (proc ? proc :
307                                             proc_mkdir(cur, cur_root));
308                         } else if (proc == NULL) {
309                                 mode_t mode = 0;
310                                 if (list->proc_mode != 0000) {
311                                         mode = list->proc_mode;
312                                 } else {
313                                         if (list->read_fptr)
314                                                 mode = 0444;
315                                         if (list->write_fptr)
316                                                 mode |= 0200;
317                                 }
318                                 proc = create_proc_entry(cur, mode, cur_root);
319                         }
320                 }
321
322                 if (pathcopy != pathbuf)
323                         OBD_FREE(pathcopy, pathsize);
324
325                 if (cur_root == NULL || proc == NULL) {
326                         CERROR("LprocFS: No memory to create /proc entry %s",
327                                list->name);
328                         return -ENOMEM;
329                 }
330
331                 if (list->fops)
332                         proc->proc_fops = list->fops;
333                 else
334                         proc->proc_fops = &lprocfs_generic_fops;
335                 proc->read_proc = list->read_fptr;
336                 proc->write_proc = list->write_fptr;
337                 proc->data = (list->data ? list->data : data);
338                 list++;
339         }
340         return 0;
341 }
342
343 void lprocfs_remove(struct proc_dir_entry **rooth)
344 {
345         struct proc_dir_entry *root = *rooth;
346         struct proc_dir_entry *temp = root;
347         struct proc_dir_entry *rm_entry;
348         struct proc_dir_entry *parent;
349
350         if (!root)
351                 return;
352         *rooth = NULL;
353
354         parent = root->parent;
355         LASSERT(parent != NULL);
356         LPROCFS_WRITE_ENTRY(); /* search vs remove race */
357
358         while (1) {
359                 while (temp->subdir != NULL)
360                         temp = temp->subdir;
361
362                 rm_entry = temp;
363                 temp = temp->parent;
364
365                 /* Memory corruption once caused this to fail, and
366                    without this LASSERT we would loop here forever. */
367                 LASSERTF(strlen(rm_entry->name) == rm_entry->namelen,
368                          "0x%p  %s/%s len %d\n", rm_entry, temp->name,
369                          rm_entry->name, (int)strlen(rm_entry->name));
370
371                 /* Now, the rm_entry->deleted flags is protected
372                  * by _lprocfs_lock. */
373                 rm_entry->data = NULL;
374                 remove_proc_entry(rm_entry->name, temp);
375                 if (temp == parent)
376                         break;
377         }
378         LPROCFS_WRITE_EXIT();
379 }
380
381 void lprocfs_remove_proc_entry(const char *name, struct proc_dir_entry *parent)
382 {
383         LASSERT(parent != NULL);
384         remove_proc_entry(name, parent);
385 }
386
387 struct proc_dir_entry *lprocfs_register(const char *name,
388                                         struct proc_dir_entry *parent,
389                                         struct lprocfs_vars *list, void *data)
390 {
391         struct proc_dir_entry *newchild;
392
393         newchild = lprocfs_srch(parent, name);
394         if (newchild != NULL) {
395                 CERROR(" Lproc: Attempting to register %s more than once \n",
396                        name);
397                 return ERR_PTR(-EALREADY);
398         }
399
400         newchild = proc_mkdir(name, parent);
401         if (newchild != NULL && list != NULL) {
402                 int rc = lprocfs_add_vars(newchild, list, data);
403                 if (rc) {
404                         lprocfs_remove(&newchild);
405                         return ERR_PTR(rc);
406                 }
407         }
408         return newchild;
409 }
410
411 /* Generic callbacks */
412 int lprocfs_rd_uint(char *page, char **start, off_t off,
413                     int count, int *eof, void *data)
414 {
415         unsigned int *temp = data;
416         return snprintf(page, count, "%u\n", *temp);
417 }
418
419 int lprocfs_wr_uint(struct file *file, const char *buffer,
420                     unsigned long count, void *data)
421 {
422         unsigned *p = data;
423         char dummy[MAX_STRING_SIZE + 1], *end;
424         unsigned long tmp;
425
426         dummy[MAX_STRING_SIZE] = '\0';
427         if (copy_from_user(dummy, buffer, MAX_STRING_SIZE))
428                 return -EFAULT;
429
430         tmp = simple_strtoul(dummy, &end, 0);
431         if (dummy == end)
432                 return -EINVAL;
433
434         *p = (unsigned int)tmp;
435         return count;
436 }
437
438 int lprocfs_rd_u64(char *page, char **start, off_t off,
439                    int count, int *eof, void *data)
440 {
441         LASSERT(data != NULL);
442         *eof = 1;
443         return snprintf(page, count, LPU64"\n", *(__u64 *)data);
444 }
445
446 int lprocfs_rd_atomic(char *page, char **start, off_t off,
447                    int count, int *eof, void *data)
448 {
449         atomic_t *atom = data;
450         LASSERT(atom != NULL);
451         *eof = 1;
452         return snprintf(page, count, "%d\n", atomic_read(atom));
453 }
454
455 int lprocfs_wr_atomic(struct file *file, const char *buffer,
456                       unsigned long count, void *data)
457 {
458         atomic_t *atm = data;
459         int val = 0;
460         int rc;
461
462         rc = lprocfs_write_helper(buffer, count, &val);
463         if (rc < 0)
464                 return rc;
465
466         if (val <= 0)
467                 return -ERANGE;
468
469         atomic_set(atm, val);
470         return count;
471 }
472
473 int lprocfs_rd_uuid(char *page, char **start, off_t off, int count,
474                     int *eof, void *data)
475 {
476         struct obd_device *obd = data;
477
478         LASSERT(obd != NULL);
479         *eof = 1;
480         return snprintf(page, count, "%s\n", obd->obd_uuid.uuid);
481 }
482
483 int lprocfs_rd_name(char *page, char **start, off_t off, int count,
484                     int *eof, void *data)
485 {
486         struct obd_device *dev = data;
487
488         LASSERT(dev != NULL);
489         LASSERT(dev->obd_name != NULL);
490         *eof = 1;
491         return snprintf(page, count, "%s\n", dev->obd_name);
492 }
493
494 int lprocfs_rd_fstype(char *page, char **start, off_t off, int count, int *eof,
495                       void *data)
496 {
497         struct obd_device *obd = data;
498
499         LASSERT(obd != NULL);
500         LASSERT(obd->obd_fsops != NULL);
501         LASSERT(obd->obd_fsops->fs_type != NULL);
502         return snprintf(page, count, "%s\n", obd->obd_fsops->fs_type);
503 }
504
505 int lprocfs_rd_blksize(char *page, char **start, off_t off, int count,
506                        int *eof, void *data)
507 {
508         struct obd_statfs osfs;
509         int rc = obd_statfs(data, &osfs, cfs_time_current_64() - HZ,
510                             OBD_STATFS_NODELAY);
511         if (!rc) {
512                 *eof = 1;
513                 rc = snprintf(page, count, "%u\n", osfs.os_bsize);
514         }
515         return rc;
516 }
517
518 int lprocfs_rd_kbytestotal(char *page, char **start, off_t off, int count,
519                            int *eof, void *data)
520 {
521         struct obd_statfs osfs;
522         int rc = obd_statfs(data, &osfs, cfs_time_current_64() - HZ,
523                             OBD_STATFS_NODELAY);
524         if (!rc) {
525                 __u32 blk_size = osfs.os_bsize >> 10;
526                 __u64 result = osfs.os_blocks;
527
528                 while (blk_size >>= 1)
529                         result <<= 1;
530
531                 *eof = 1;
532                 rc = snprintf(page, count, LPU64"\n", result);
533         }
534         return rc;
535 }
536
537 int lprocfs_rd_kbytesfree(char *page, char **start, off_t off, int count,
538                           int *eof, void *data)
539 {
540         struct obd_statfs osfs;
541         int rc = obd_statfs(data, &osfs, cfs_time_current_64() - HZ,
542                             OBD_STATFS_NODELAY);
543         if (!rc) {
544                 __u32 blk_size = osfs.os_bsize >> 10;
545                 __u64 result = osfs.os_bfree;
546
547                 while (blk_size >>= 1)
548                         result <<= 1;
549
550                 *eof = 1;
551                 rc = snprintf(page, count, LPU64"\n", result);
552         }
553         return rc;
554 }
555
556 int lprocfs_rd_kbytesavail(char *page, char **start, off_t off, int count,
557                            int *eof, void *data)
558 {
559         struct obd_statfs osfs;
560         int rc = obd_statfs(data, &osfs, cfs_time_current_64() - HZ,
561                             OBD_STATFS_NODELAY);
562         if (!rc) {
563                 __u32 blk_size = osfs.os_bsize >> 10;
564                 __u64 result = osfs.os_bavail;
565
566                 while (blk_size >>= 1)
567                         result <<= 1;
568
569                 *eof = 1;
570                 rc = snprintf(page, count, LPU64"\n", result);
571         }
572         return rc;
573 }
574
575 int lprocfs_rd_filestotal(char *page, char **start, off_t off, int count,
576                           int *eof, void *data)
577 {
578         struct obd_statfs osfs;
579         int rc = obd_statfs(data, &osfs, cfs_time_current_64() - HZ,
580                             OBD_STATFS_NODELAY);
581         if (!rc) {
582                 *eof = 1;
583                 rc = snprintf(page, count, LPU64"\n", osfs.os_files);
584         }
585
586         return rc;
587 }
588
589 int lprocfs_rd_filesfree(char *page, char **start, off_t off, int count,
590                          int *eof, void *data)
591 {
592         struct obd_statfs osfs;
593         int rc = obd_statfs(data, &osfs, cfs_time_current_64() - HZ,
594                             OBD_STATFS_NODELAY);
595         if (!rc) {
596                 *eof = 1;
597                 rc = snprintf(page, count, LPU64"\n", osfs.os_ffree);
598         }
599         return rc;
600 }
601
602 int lprocfs_rd_server_uuid(char *page, char **start, off_t off, int count,
603                            int *eof, void *data)
604 {
605         struct obd_device *obd = data;
606         struct obd_import *imp;
607         char *imp_state_name = NULL;
608         int rc = 0;
609
610         LASSERT(obd != NULL);
611         LPROCFS_CLIMP_CHECK(obd);
612         imp = obd->u.cli.cl_import;
613         imp_state_name = ptlrpc_import_state_name(imp->imp_state);
614         *eof = 1;
615         rc = snprintf(page, count, "%s\t%s%s\n",
616                       obd2cli_tgt(obd), imp_state_name,
617                       imp->imp_deactive ? "\tDEACTIVATED" : "");
618
619         LPROCFS_CLIMP_EXIT(obd);
620         return rc;
621 }
622
623 int lprocfs_rd_conn_uuid(char *page, char **start, off_t off, int count,
624                          int *eof,  void *data)
625 {
626         struct obd_device *obd = data;
627         struct ptlrpc_connection *conn;
628         int rc = 0;
629
630         LASSERT(obd != NULL);
631
632         LPROCFS_CLIMP_CHECK(obd);
633         conn = obd->u.cli.cl_import->imp_connection;
634         LASSERT(conn != NULL);
635         *eof = 1;
636         if (obd->u.cli.cl_import) {
637                 rc = snprintf(page, count, "%s\n",
638                               conn->c_remote_uuid.uuid);
639         } else {
640                 rc = snprintf(page, count, "%s\n", "<none>");
641         }
642
643         LPROCFS_CLIMP_EXIT(obd);
644         return rc;
645 }
646
647 #define flag2str(flag) \
648         if (imp->imp_##flag && max - len > 0) \
649                 len += snprintf(str + len, max - len, " " #flag);
650
651 /**
652  * Append a space separated list of current set flags to str.
653  */
654 static int obd_import_flags2str(struct obd_import *imp, char *str,
655                                           int max)
656 {
657         int len = 0;
658
659         if (imp->imp_obd->obd_no_recov)
660                 len += snprintf(str, max - len, " no_recov");
661
662         flag2str(invalid);
663         flag2str(deactive);
664         flag2str(replayable);
665         flag2str(pingable);
666         flag2str(recon_bk);
667         flag2str(last_recon);
668         return len;
669 }
670 #undef flags2str
671
672 int lprocfs_rd_import(char *page, char **start, off_t off, int count,
673                       int *eof, void *data)
674 {
675         struct obd_device *obd = (struct obd_device *)data;
676         struct obd_import *imp;
677         char *imp_state_name = NULL;
678         int rc = 0;
679
680         LASSERT(obd != NULL);
681         LPROCFS_CLIMP_CHECK(obd);
682         imp = obd->u.cli.cl_import;
683         imp_state_name = ptlrpc_import_state_name(imp->imp_state);
684         *eof = 1;
685
686         rc = snprintf(page, count,
687                       "import: %s\n"
688                       "    target: %s@%s\n"
689                       "    state: %s\n"
690                       "    inflight: %u\n"
691                       "    unregistering: %u\n"
692                       "    conn_cnt: %u\n"
693                       "    generation: %u\n"
694                       "    inval_cnt: %u\n"
695                       "    last_replay_transno: "LPU64"\n"
696                       "    peer_committed_transno: "LPU64"\n"
697                       "    last_trasno_checked: "LPU64"\n"
698                       "    flags:",
699                       obd->obd_name,
700                       obd2cli_tgt(obd), imp->imp_connection->c_remote_uuid.uuid,
701                       imp_state_name,
702                       atomic_read(&imp->imp_inflight),
703                       atomic_read(&imp->imp_unregistering),
704                       imp->imp_conn_cnt,
705                       imp->imp_generation,
706                       atomic_read(&imp->imp_inval_count),
707                       imp->imp_last_replay_transno,
708                       imp->imp_peer_committed_transno,
709                       imp->imp_last_transno_checked);
710         rc += obd_import_flags2str(imp, page + rc, count - rc);
711         rc += snprintf(page+rc, count - rc, "\n");
712         LPROCFS_CLIMP_EXIT(obd);
713         return rc;
714 }
715
716 int lprocfs_at_hist_helper(char *page, int count, int rc,
717                            struct adaptive_timeout *at)
718 {
719         int i;
720         for (i = 0; i < AT_BINS; i++)
721                 rc += snprintf(page + rc, count - rc, "%3u ", at->at_hist[i]);
722         rc += snprintf(page + rc, count - rc, "\n");
723         return rc;
724 }
725
726 /* See also ptlrpc_lprocfs_rd_timeouts */
727 int lprocfs_rd_timeouts(char *page, char **start, off_t off, int count,
728                         int *eof, void *data)
729 {
730         struct obd_device *obd = (struct obd_device *)data;
731         struct obd_import *imp;
732         unsigned int cur, worst;
733         time_t now, worstt;
734         struct dhms ts;
735         int i, rc = 0;
736
737         LASSERT(obd != NULL);
738         LPROCFS_CLIMP_CHECK(obd);
739         imp = obd->u.cli.cl_import;
740         *eof = 1;
741
742         now = cfs_time_current_sec();
743
744         /* Some network health info for kicks */
745         s2dhms(&ts, now - imp->imp_last_reply_time);
746         rc += snprintf(page + rc, count - rc,
747                        "%-10s : %ld, "DHMS_FMT" ago\n",
748                        "last reply", imp->imp_last_reply_time, DHMS_VARS(&ts));
749
750         cur = at_get(&imp->imp_at.iat_net_latency);
751         worst = imp->imp_at.iat_net_latency.at_worst_ever;
752         worstt = imp->imp_at.iat_net_latency.at_worst_time;
753         s2dhms(&ts, now - worstt);
754         rc += snprintf(page + rc, count - rc,
755                        "%-10s : cur %3u  worst %3u (at %ld, "DHMS_FMT" ago) ",
756                        "network", cur, worst, worstt, DHMS_VARS(&ts));
757         rc = lprocfs_at_hist_helper(page, count, rc,
758                                     &imp->imp_at.iat_net_latency);
759
760         for(i = 0; i < IMP_AT_MAX_PORTALS; i++) {
761                 if (imp->imp_at.iat_portal[i] == 0)
762                         break;
763                 cur = at_get(&imp->imp_at.iat_service_estimate[i]);
764                 worst = imp->imp_at.iat_service_estimate[i].at_worst_ever;
765                 worstt = imp->imp_at.iat_service_estimate[i].at_worst_time;
766                 s2dhms(&ts, now - worstt);
767                 rc += snprintf(page + rc, count - rc,
768                                "portal %-2d  : cur %3u  worst %3u (at %ld, "
769                                DHMS_FMT" ago) ", imp->imp_at.iat_portal[i],
770                                cur, worst, worstt, DHMS_VARS(&ts));
771                 rc = lprocfs_at_hist_helper(page, count, rc,
772                                           &imp->imp_at.iat_service_estimate[i]);
773         }
774
775         LPROCFS_CLIMP_EXIT(obd);
776         return rc;
777 }
778
779 static const char *obd_connect_names[] = {
780         "read_only",
781         "lov_index",
782         "unused",
783         "write_grant",
784         "server_lock",
785         "version",
786         "request_portal",
787         "acl",
788         "xattr",
789         "create_on_write",
790         "truncate_lock",
791         "initial_transno",
792         "inode_bit_locks",
793         "join_file",
794         "getattr_by_fid",
795         "no_oh_for_devices",
796         "local_client",
797         "remote_client",
798         "max_byte_per_rpc",
799         "64bit_qdata",
800         "mds_capability",
801         "oss_capability",
802         "early_lock_cancel",
803         "size_on_mds",
804         "adaptive_timeouts",
805         "lru_resize",
806         "mds_mds_connection",
807         "real_conn",
808         "change_qunit_size",
809         "alt_checksum_algorithm",
810         "fid_is_enabled",
811         "version_recovery",
812         "pools",
813         "", /* reserved for simplified interop */
814         "skip_orphan",
815         NULL
816 };
817
818 int lprocfs_rd_connect_flags(char *page, char **start, off_t off,
819                              int count, int *eof, void *data)
820 {
821         struct obd_device *obd = data;
822         __u64 mask = 1, flags;
823         int i, ret = 0;
824
825         LPROCFS_CLIMP_CHECK(obd);
826         flags = obd->u.cli.cl_import->imp_connect_data.ocd_connect_flags;
827         ret = snprintf(page, count, "flags="LPX64"\n", flags);
828         for (i = 0; obd_connect_names[i] != NULL; i++, mask <<= 1) {
829                 if (flags & mask)
830                         ret += snprintf(page + ret, count - ret, "%s\n",
831                                         obd_connect_names[i]);
832         }
833         if (flags & ~(mask - 1))
834                 ret += snprintf(page + ret, count - ret,
835                                 "unknown flags "LPX64"\n", flags & ~(mask - 1));
836
837         LPROCFS_CLIMP_EXIT(obd);
838         return ret;
839 }
840 EXPORT_SYMBOL(lprocfs_rd_connect_flags);
841
842 int lprocfs_rd_num_exports(char *page, char **start, off_t off, int count,
843                            int *eof,  void *data)
844 {
845         struct obd_device *obd = data;
846
847         LASSERT(obd != NULL);
848         *eof = 1;
849         return snprintf(page, count, "%u\n", obd->obd_num_exports);
850 }
851
852 int lprocfs_rd_numrefs(char *page, char **start, off_t off, int count,
853                        int *eof, void *data)
854 {
855         struct obd_type *class = (struct obd_type*) data;
856
857         LASSERT(class != NULL);
858         *eof = 1;
859         return snprintf(page, count, "%d\n", class->typ_refcnt);
860 }
861
862 int lprocfs_obd_setup(struct obd_device *obd, struct lprocfs_vars *list)
863 {
864         int rc = 0;
865
866         LASSERT(obd != NULL);
867         LASSERT(obd->obd_magic == OBD_DEVICE_MAGIC);
868         LASSERT(obd->obd_type->typ_procroot != NULL);
869
870         obd->obd_proc_entry = lprocfs_register(obd->obd_name,
871                                                obd->obd_type->typ_procroot,
872                                                list, obd);
873         if (IS_ERR(obd->obd_proc_entry)) {
874                 rc = PTR_ERR(obd->obd_proc_entry);
875                 CERROR("error %d setting up lprocfs for %s\n",rc,obd->obd_name);
876                 obd->obd_proc_entry = NULL;
877         }
878         return rc;
879 }
880
881 int lprocfs_obd_cleanup(struct obd_device *obd)
882 {
883         if (!obd)
884                 return -EINVAL;
885         if (obd->obd_proc_exports_entry) {
886                 /* Should be no exports left */
887                 LASSERT(obd->obd_proc_exports_entry->subdir == NULL);
888                 lprocfs_remove(&obd->obd_proc_exports_entry);
889                 obd->obd_proc_exports_entry = NULL;
890         }
891         if (obd->obd_proc_entry) {
892                 lprocfs_remove(&obd->obd_proc_entry);
893                 obd->obd_proc_entry = NULL;
894         }
895         return 0;
896 }
897
898 static void lprocfs_free_client_stats(struct nid_stat *client_stat)
899 {
900         CDEBUG(D_CONFIG, "stat %p - data %p/%p/%p\n", client_stat,
901                client_stat->nid_proc, client_stat->nid_stats,
902                client_stat->nid_brw_stats);
903
904         LASSERTF(client_stat->nid_exp_ref_count == 0, "count %d\n",
905                  client_stat->nid_exp_ref_count);
906
907         hlist_del_init(&client_stat->nid_hash);
908
909         if (client_stat->nid_proc)
910                 lprocfs_remove(&client_stat->nid_proc);
911
912         if (client_stat->nid_stats)
913                 lprocfs_free_stats(&client_stat->nid_stats);
914
915         if (client_stat->nid_brw_stats)
916                 OBD_FREE_PTR(client_stat->nid_brw_stats);
917
918         if (client_stat->nid_ldlm_stats)
919                 lprocfs_free_stats(&client_stat->nid_ldlm_stats);
920
921         OBD_FREE_PTR(client_stat);
922         return;
923
924 }
925
926 void lprocfs_free_per_client_stats(struct obd_device *obd)
927 {
928         struct nid_stat *stat;
929         ENTRY;
930
931         /* we need extra list - because hash_exit called to early */
932         /* not need locking because all clients is died */
933         while(!list_empty(&obd->obd_nid_stats)) {
934                 stat = list_entry(obd->obd_nid_stats.next,
935                                   struct nid_stat, nid_list);
936                 list_del_init(&stat->nid_list);
937                 lprocfs_free_client_stats(stat);
938         }
939
940         EXIT;
941 }
942
943 struct lprocfs_stats *lprocfs_alloc_stats(unsigned int num,
944                                           enum lprocfs_stats_flags flags)
945 {
946         struct lprocfs_stats *stats;
947         unsigned int percpusize;
948         unsigned int i, j;
949         unsigned int num_cpu;
950
951         if (num == 0)
952                 return NULL;
953
954         if (flags & LPROCFS_STATS_FLAG_NOPERCPU)
955                 num_cpu = 1;
956         else
957                 num_cpu = num_possible_cpus();
958
959         OBD_ALLOC(stats, offsetof(typeof(*stats), ls_percpu[num_cpu]));
960         if (stats == NULL)
961                 return NULL;
962
963         if (flags & LPROCFS_STATS_FLAG_NOPERCPU) {
964                 stats->ls_flags = flags;
965                 spin_lock_init(&stats->ls_lock);
966                 /* Use this lock only if there are no percpu areas */
967         } else {
968                 stats->ls_flags = 0;
969         }
970
971         percpusize = offsetof(struct lprocfs_percpu, lp_cntr[num]);
972         if (num_cpu > 1)
973                 percpusize = L1_CACHE_ALIGN(percpusize);
974
975         for (i = 0; i < num_cpu; i++) {
976                 OBD_ALLOC(stats->ls_percpu[i], percpusize);
977                 if (stats->ls_percpu[i] == NULL) {
978                         for (j = 0; j < i; j++) {
979                                 OBD_FREE(stats->ls_percpu[j], percpusize);
980                                 stats->ls_percpu[j] = NULL;
981                         }
982                         break;
983                 }
984         }
985         if (stats->ls_percpu[0] == NULL) {
986                 OBD_FREE(stats, offsetof(typeof(*stats),
987                                          ls_percpu[num_cpu]));
988                 return NULL;
989         }
990
991         stats->ls_num = num;
992         return stats;
993 }
994
995 void lprocfs_free_stats(struct lprocfs_stats **statsh)
996 {
997         struct lprocfs_stats *stats = *statsh;
998         unsigned int num_cpu;
999         unsigned int percpusize;
1000         unsigned int i;
1001
1002         if (stats == NULL || stats->ls_num == 0)
1003                 return;
1004         *statsh = NULL;
1005
1006         if (stats->ls_flags & LPROCFS_STATS_FLAG_NOPERCPU)
1007                 num_cpu = 1;
1008         else
1009                 num_cpu = num_possible_cpus();
1010
1011         percpusize = offsetof(struct lprocfs_percpu, lp_cntr[stats->ls_num]);
1012         if (num_cpu > 1)
1013                 percpusize = L1_CACHE_ALIGN(percpusize);
1014         for (i = 0; i < num_cpu; i++)
1015                 OBD_FREE(stats->ls_percpu[i], percpusize);
1016         OBD_FREE(stats, offsetof(typeof(*stats), ls_percpu[num_cpu]));
1017 }
1018
1019 void lprocfs_clear_stats(struct lprocfs_stats *stats)
1020 {
1021         struct lprocfs_counter *percpu_cntr;
1022         int i,j;
1023         unsigned int num_cpu;
1024
1025         num_cpu = lprocfs_stats_lock(stats, LPROCFS_GET_NUM_CPU);
1026
1027         for (i = 0; i < num_cpu; i++) {
1028                 for (j = 0; j < stats->ls_num; j++) {
1029                         percpu_cntr = &(stats->ls_percpu[i])->lp_cntr[j];
1030                         atomic_inc(&percpu_cntr->lc_cntl.la_entry);
1031                         percpu_cntr->lc_count = 0;
1032                         percpu_cntr->lc_sum = 0;
1033                         percpu_cntr->lc_min = LC_MIN_INIT;
1034                         percpu_cntr->lc_max = 0;
1035                         percpu_cntr->lc_sumsquare = 0;
1036                         atomic_inc(&percpu_cntr->lc_cntl.la_exit);
1037                 }
1038         }
1039
1040         lprocfs_stats_unlock(stats);
1041 }
1042
1043 static ssize_t lprocfs_stats_seq_write(struct file *file, const char *buf,
1044                                        size_t len, loff_t *off)
1045 {
1046         struct seq_file *seq = file->private_data;
1047         struct lprocfs_stats *stats = seq->private;
1048
1049         lprocfs_clear_stats(stats);
1050
1051         return len;
1052 }
1053
1054 static void *lprocfs_stats_seq_start(struct seq_file *p, loff_t *pos)
1055 {
1056         struct lprocfs_stats *stats = p->private;
1057         /* return 1st cpu location */
1058         return (*pos >= stats->ls_num) ? NULL :
1059                 &(stats->ls_percpu[0]->lp_cntr[*pos]);
1060 }
1061
1062 static void lprocfs_stats_seq_stop(struct seq_file *p, void *v)
1063 {
1064 }
1065
1066 static void *lprocfs_stats_seq_next(struct seq_file *p, void *v, loff_t *pos)
1067 {
1068         struct lprocfs_stats *stats = p->private;
1069         ++*pos;
1070         return (*pos >= stats->ls_num) ? NULL :
1071                 &(stats->ls_percpu[0]->lp_cntr[*pos]);
1072 }
1073
1074 /* seq file export of one lprocfs counter */
1075 static int lprocfs_stats_seq_show(struct seq_file *p, void *v)
1076 {
1077        struct lprocfs_stats *stats = p->private;
1078        struct lprocfs_counter  *cntr = v;
1079        struct lprocfs_counter  t, ret = { .lc_min = LC_MIN_INIT };
1080        int i, idx, rc = 0;
1081        unsigned int num_cpu;
1082
1083        if (cntr == &(stats->ls_percpu[0])->lp_cntr[0]) {
1084                struct timeval now;
1085                do_gettimeofday(&now);
1086                rc = seq_printf(p, "%-25s %lu.%lu secs.usecs\n",
1087                                "snapshot_time", now.tv_sec, now.tv_usec);
1088                if (rc < 0)
1089                        return rc;
1090        }
1091        idx = cntr - &(stats->ls_percpu[0])->lp_cntr[0];
1092
1093        if (stats->ls_flags & LPROCFS_STATS_FLAG_NOPERCPU)
1094                num_cpu = 1;
1095        else
1096                num_cpu = num_possible_cpus();
1097
1098        for (i = 0; i < num_cpu; i++) {
1099                struct lprocfs_counter *percpu_cntr =
1100                        &(stats->ls_percpu[i])->lp_cntr[idx];
1101                int centry;
1102
1103                do {
1104                        centry = atomic_read(&percpu_cntr->lc_cntl.la_entry);
1105                        t.lc_count = percpu_cntr->lc_count;
1106                        t.lc_sum = percpu_cntr->lc_sum;
1107                        t.lc_min = percpu_cntr->lc_min;
1108                        t.lc_max = percpu_cntr->lc_max;
1109                        t.lc_sumsquare = percpu_cntr->lc_sumsquare;
1110                } while (centry != atomic_read(&percpu_cntr->lc_cntl.la_entry) &&
1111                         centry != atomic_read(&percpu_cntr->lc_cntl.la_exit));
1112                ret.lc_count += t.lc_count;
1113                ret.lc_sum += t.lc_sum;
1114                if (t.lc_min < ret.lc_min)
1115                        ret.lc_min = t.lc_min;
1116                if (t.lc_max > ret.lc_max)
1117                        ret.lc_max = t.lc_max;
1118                ret.lc_sumsquare += t.lc_sumsquare;
1119        }
1120
1121        if (ret.lc_count == 0)
1122                goto out;
1123
1124        rc = seq_printf(p, "%-25s "LPD64" samples [%s]", cntr->lc_name,
1125                        ret.lc_count, cntr->lc_units);
1126        if (rc < 0)
1127                goto out;
1128
1129        if ((cntr->lc_config & LPROCFS_CNTR_AVGMINMAX) && (ret.lc_count > 0)) {
1130                rc = seq_printf(p, " "LPD64" "LPD64" "LPD64,
1131                                ret.lc_min, ret.lc_max, ret.lc_sum);
1132                if (rc < 0)
1133                        goto out;
1134                if (cntr->lc_config & LPROCFS_CNTR_STDDEV)
1135                        rc = seq_printf(p, " "LPD64, ret.lc_sumsquare);
1136                if (rc < 0)
1137                        goto out;
1138        }
1139        rc = seq_printf(p, "\n");
1140  out:
1141        return (rc < 0) ? rc : 0;
1142 }
1143
1144 struct seq_operations lprocfs_stats_seq_sops = {
1145         start: lprocfs_stats_seq_start,
1146         stop:  lprocfs_stats_seq_stop,
1147         next:  lprocfs_stats_seq_next,
1148         show:  lprocfs_stats_seq_show,
1149 };
1150
1151 static int lprocfs_stats_seq_open(struct inode *inode, struct file *file)
1152 {
1153         struct proc_dir_entry *dp = PDE(inode);
1154         struct seq_file *seq;
1155         int rc;
1156
1157         LPROCFS_ENTRY_AND_CHECK(dp);
1158         rc = seq_open(file, &lprocfs_stats_seq_sops);
1159         if (rc) {
1160                 LPROCFS_EXIT();
1161                 return rc;
1162         }
1163         seq = file->private_data;
1164         seq->private = dp->data;
1165         return 0;
1166 }
1167
1168 struct file_operations lprocfs_stats_seq_fops = {
1169         .owner   = THIS_MODULE,
1170         .open    = lprocfs_stats_seq_open,
1171         .read    = seq_read,
1172         .write   = lprocfs_stats_seq_write,
1173         .llseek  = seq_lseek,
1174         .release = lprocfs_seq_release,
1175 };
1176
1177 int lprocfs_register_stats(struct proc_dir_entry *root, const char *name,
1178                            struct lprocfs_stats *stats)
1179 {
1180         struct proc_dir_entry *entry;
1181         LASSERT(root != NULL);
1182
1183         entry = create_proc_entry(name, 0644, root);
1184         if (entry == NULL)
1185                 return -ENOMEM;
1186         entry->proc_fops = &lprocfs_stats_seq_fops;
1187         entry->data = stats;
1188         return 0;
1189 }
1190
1191 void lprocfs_counter_init(struct lprocfs_stats *stats, int index,
1192                           unsigned conf, const char *name, const char *units)
1193 {
1194         struct lprocfs_counter *c;
1195         int i;
1196         unsigned int num_cpu;
1197
1198         LASSERT(stats != NULL);
1199
1200         num_cpu = lprocfs_stats_lock(stats, LPROCFS_GET_NUM_CPU);
1201
1202         for (i = 0; i < num_cpu; i++) {
1203                 c = &(stats->ls_percpu[i]->lp_cntr[index]);
1204                 c->lc_config = conf;
1205                 c->lc_count = 0;
1206                 c->lc_sum = 0;
1207                 c->lc_min = LC_MIN_INIT;
1208                 c->lc_max = 0;
1209                 c->lc_name = name;
1210                 c->lc_units = units;
1211         }
1212
1213         lprocfs_stats_unlock(stats);
1214 }
1215 EXPORT_SYMBOL(lprocfs_counter_init);
1216
1217 #define LPROCFS_OBD_OP_INIT(base, stats, op)                               \
1218 do {                                                                       \
1219         unsigned int coffset = base + OBD_COUNTER_OFFSET(op);              \
1220         LASSERT(coffset < stats->ls_num);                                  \
1221         lprocfs_counter_init(stats, coffset, 0, #op, "reqs");              \
1222 } while (0)
1223
1224 void lprocfs_init_ops_stats(int num_private_stats, struct lprocfs_stats *stats)
1225 {
1226         LPROCFS_OBD_OP_INIT(num_private_stats, stats, iocontrol);
1227         LPROCFS_OBD_OP_INIT(num_private_stats, stats, get_info);
1228         LPROCFS_OBD_OP_INIT(num_private_stats, stats, set_info_async);
1229         LPROCFS_OBD_OP_INIT(num_private_stats, stats, attach);
1230         LPROCFS_OBD_OP_INIT(num_private_stats, stats, detach);
1231         LPROCFS_OBD_OP_INIT(num_private_stats, stats, setup);
1232         LPROCFS_OBD_OP_INIT(num_private_stats, stats, precleanup);
1233         LPROCFS_OBD_OP_INIT(num_private_stats, stats, cleanup);
1234         LPROCFS_OBD_OP_INIT(num_private_stats, stats, process_config);
1235         LPROCFS_OBD_OP_INIT(num_private_stats, stats, postrecov);
1236         LPROCFS_OBD_OP_INIT(num_private_stats, stats, add_conn);
1237         LPROCFS_OBD_OP_INIT(num_private_stats, stats, del_conn);
1238         LPROCFS_OBD_OP_INIT(num_private_stats, stats, connect);
1239         LPROCFS_OBD_OP_INIT(num_private_stats, stats, reconnect);
1240         LPROCFS_OBD_OP_INIT(num_private_stats, stats, disconnect);
1241         LPROCFS_OBD_OP_INIT(num_private_stats, stats, fid_init);
1242         LPROCFS_OBD_OP_INIT(num_private_stats, stats, fid_fini);
1243         LPROCFS_OBD_OP_INIT(num_private_stats, stats, fid_alloc);
1244         LPROCFS_OBD_OP_INIT(num_private_stats, stats, fid_delete);
1245         LPROCFS_OBD_OP_INIT(num_private_stats, stats, statfs);
1246         LPROCFS_OBD_OP_INIT(num_private_stats, stats, statfs_async);
1247         LPROCFS_OBD_OP_INIT(num_private_stats, stats, packmd);
1248         LPROCFS_OBD_OP_INIT(num_private_stats, stats, unpackmd);
1249         LPROCFS_OBD_OP_INIT(num_private_stats, stats, checkmd);
1250         LPROCFS_OBD_OP_INIT(num_private_stats, stats, preallocate);
1251         LPROCFS_OBD_OP_INIT(num_private_stats, stats, precreate);
1252         LPROCFS_OBD_OP_INIT(num_private_stats, stats, create);
1253         LPROCFS_OBD_OP_INIT(num_private_stats, stats, destroy);
1254         LPROCFS_OBD_OP_INIT(num_private_stats, stats, setattr);
1255         LPROCFS_OBD_OP_INIT(num_private_stats, stats, setattr_async);
1256         LPROCFS_OBD_OP_INIT(num_private_stats, stats, getattr);
1257         LPROCFS_OBD_OP_INIT(num_private_stats, stats, getattr_async);
1258         LPROCFS_OBD_OP_INIT(num_private_stats, stats, brw);
1259         LPROCFS_OBD_OP_INIT(num_private_stats, stats, merge_lvb);
1260         LPROCFS_OBD_OP_INIT(num_private_stats, stats, adjust_kms);
1261         LPROCFS_OBD_OP_INIT(num_private_stats, stats, punch);
1262         LPROCFS_OBD_OP_INIT(num_private_stats, stats, sync);
1263         LPROCFS_OBD_OP_INIT(num_private_stats, stats, migrate);
1264         LPROCFS_OBD_OP_INIT(num_private_stats, stats, copy);
1265         LPROCFS_OBD_OP_INIT(num_private_stats, stats, iterate);
1266         LPROCFS_OBD_OP_INIT(num_private_stats, stats, preprw);
1267         LPROCFS_OBD_OP_INIT(num_private_stats, stats, commitrw);
1268         LPROCFS_OBD_OP_INIT(num_private_stats, stats, enqueue);
1269         LPROCFS_OBD_OP_INIT(num_private_stats, stats, change_cbdata);
1270         LPROCFS_OBD_OP_INIT(num_private_stats, stats, cancel);
1271         LPROCFS_OBD_OP_INIT(num_private_stats, stats, cancel_unused);
1272         LPROCFS_OBD_OP_INIT(num_private_stats, stats, init_export);
1273         LPROCFS_OBD_OP_INIT(num_private_stats, stats, destroy_export);
1274         LPROCFS_OBD_OP_INIT(num_private_stats, stats, extent_calc);
1275         LPROCFS_OBD_OP_INIT(num_private_stats, stats, llog_init);
1276         LPROCFS_OBD_OP_INIT(num_private_stats, stats, llog_connect);
1277         LPROCFS_OBD_OP_INIT(num_private_stats, stats, llog_finish);
1278         LPROCFS_OBD_OP_INIT(num_private_stats, stats, pin);
1279         LPROCFS_OBD_OP_INIT(num_private_stats, stats, unpin);
1280         LPROCFS_OBD_OP_INIT(num_private_stats, stats, import_event);
1281         LPROCFS_OBD_OP_INIT(num_private_stats, stats, notify);
1282         LPROCFS_OBD_OP_INIT(num_private_stats, stats, health_check);
1283         LPROCFS_OBD_OP_INIT(num_private_stats, stats, get_uuid);
1284         LPROCFS_OBD_OP_INIT(num_private_stats, stats, quotacheck);
1285         LPROCFS_OBD_OP_INIT(num_private_stats, stats, quotactl);
1286         LPROCFS_OBD_OP_INIT(num_private_stats, stats, quota_adjust_qunit);
1287         LPROCFS_OBD_OP_INIT(num_private_stats, stats, ping);
1288         LPROCFS_OBD_OP_INIT(num_private_stats, stats, pool_new);
1289         LPROCFS_OBD_OP_INIT(num_private_stats, stats, pool_rem);
1290         LPROCFS_OBD_OP_INIT(num_private_stats, stats, pool_add);
1291         LPROCFS_OBD_OP_INIT(num_private_stats, stats, pool_del);
1292 }
1293
1294 int lprocfs_alloc_obd_stats(struct obd_device *obd, unsigned num_private_stats)
1295 {
1296         struct lprocfs_stats *stats;
1297         unsigned int num_stats;
1298         int rc, i;
1299
1300         LASSERT(obd->obd_stats == NULL);
1301         LASSERT(obd->obd_proc_entry != NULL);
1302         LASSERT(obd->obd_cntr_base == 0);
1303
1304         num_stats = ((int)sizeof(*obd->obd_type->typ_dt_ops) / sizeof(void *)) +
1305                 num_private_stats - 1 /* o_owner */;
1306         stats = lprocfs_alloc_stats(num_stats, 0);
1307         if (stats == NULL)
1308                 return -ENOMEM;
1309
1310         lprocfs_init_ops_stats(num_private_stats, stats);
1311
1312         for (i = num_private_stats; i < num_stats; i++) {
1313                 /* If this LBUGs, it is likely that an obd
1314                  * operation was added to struct obd_ops in
1315                  * <obd.h>, and that the corresponding line item
1316                  * LPROCFS_OBD_OP_INIT(.., .., opname)
1317                  * is missing from the list above. */
1318                 LASSERTF(stats->ls_percpu[0]->lp_cntr[i].lc_name != NULL,
1319                          "Missing obd_stat initializer obd_op "
1320                          "operation at offset %d.\n", i - num_private_stats);
1321         }
1322         rc = lprocfs_register_stats(obd->obd_proc_entry, "stats", stats);
1323         if (rc < 0) {
1324                 lprocfs_free_stats(&stats);
1325         } else {
1326                 obd->obd_stats  = stats;
1327                 obd->obd_cntr_base = num_private_stats;
1328         }
1329         return rc;
1330 }
1331
1332 void lprocfs_free_obd_stats(struct obd_device *obd)
1333 {
1334         if (obd->obd_stats)
1335                 lprocfs_free_stats(&obd->obd_stats);
1336 }
1337
1338 #define LPROCFS_MD_OP_INIT(base, stats, op)                             \
1339 do {                                                                    \
1340         unsigned int coffset = base + MD_COUNTER_OFFSET(op);            \
1341         LASSERT(coffset < stats->ls_num);                               \
1342         lprocfs_counter_init(stats, coffset, 0, #op, "reqs");           \
1343 } while (0)
1344
1345 int lprocfs_alloc_md_stats(struct obd_device *obd,
1346                            unsigned num_private_stats)
1347 {
1348         struct lprocfs_stats *stats;
1349         unsigned int num_stats;
1350         int rc, i;
1351
1352         LASSERT(obd->md_stats == NULL);
1353         LASSERT(obd->obd_proc_entry != NULL);
1354         LASSERT(obd->md_cntr_base == 0);
1355
1356         num_stats = 1 + MD_COUNTER_OFFSET(revalidate_lock) +
1357                     num_private_stats;
1358         stats = lprocfs_alloc_stats(num_stats, 0);
1359         if (stats == NULL)
1360                 return -ENOMEM;
1361
1362         LPROCFS_MD_OP_INIT(num_private_stats, stats, getstatus);
1363         LPROCFS_MD_OP_INIT(num_private_stats, stats, change_cbdata);
1364         LPROCFS_MD_OP_INIT(num_private_stats, stats, close);
1365         LPROCFS_MD_OP_INIT(num_private_stats, stats, create);
1366         LPROCFS_MD_OP_INIT(num_private_stats, stats, done_writing);
1367         LPROCFS_MD_OP_INIT(num_private_stats, stats, enqueue);
1368         LPROCFS_MD_OP_INIT(num_private_stats, stats, getattr);
1369         LPROCFS_MD_OP_INIT(num_private_stats, stats, getattr_name);
1370         LPROCFS_MD_OP_INIT(num_private_stats, stats, intent_lock);
1371         LPROCFS_MD_OP_INIT(num_private_stats, stats, link);
1372         LPROCFS_MD_OP_INIT(num_private_stats, stats, rename);
1373         LPROCFS_MD_OP_INIT(num_private_stats, stats, is_subdir);
1374         LPROCFS_MD_OP_INIT(num_private_stats, stats, setattr);
1375         LPROCFS_MD_OP_INIT(num_private_stats, stats, sync);
1376         LPROCFS_MD_OP_INIT(num_private_stats, stats, readpage);
1377         LPROCFS_MD_OP_INIT(num_private_stats, stats, unlink);
1378         LPROCFS_MD_OP_INIT(num_private_stats, stats, setxattr);
1379         LPROCFS_MD_OP_INIT(num_private_stats, stats, getxattr);
1380         LPROCFS_MD_OP_INIT(num_private_stats, stats, init_ea_size);
1381         LPROCFS_MD_OP_INIT(num_private_stats, stats, get_lustre_md);
1382         LPROCFS_MD_OP_INIT(num_private_stats, stats, free_lustre_md);
1383         LPROCFS_MD_OP_INIT(num_private_stats, stats, set_open_replay_data);
1384         LPROCFS_MD_OP_INIT(num_private_stats, stats, clear_open_replay_data);
1385         LPROCFS_MD_OP_INIT(num_private_stats, stats, set_lock_data);
1386         LPROCFS_MD_OP_INIT(num_private_stats, stats, lock_match);
1387         LPROCFS_MD_OP_INIT(num_private_stats, stats, cancel_unused);
1388         LPROCFS_MD_OP_INIT(num_private_stats, stats, renew_capa);
1389         LPROCFS_MD_OP_INIT(num_private_stats, stats, unpack_capa);
1390         LPROCFS_MD_OP_INIT(num_private_stats, stats, get_remote_perm);
1391         LPROCFS_MD_OP_INIT(num_private_stats, stats, intent_getattr_async);
1392         LPROCFS_MD_OP_INIT(num_private_stats, stats, revalidate_lock);
1393
1394         for (i = num_private_stats; i < num_stats; i++) {
1395                 if (stats->ls_percpu[0]->lp_cntr[i].lc_name == NULL) {
1396                         CERROR("Missing md_stat initializer md_op "
1397                                "operation at offset %d. Aborting.\n",
1398                                i - num_private_stats);
1399                         LBUG();
1400                 }
1401         }
1402         rc = lprocfs_register_stats(obd->obd_proc_entry, "md_stats", stats);
1403         if (rc < 0) {
1404                 lprocfs_free_stats(&stats);
1405         } else {
1406                 obd->md_stats  = stats;
1407                 obd->md_cntr_base = num_private_stats;
1408         }
1409         return rc;
1410 }
1411
1412 void lprocfs_free_md_stats(struct obd_device *obd)
1413 {
1414         struct lprocfs_stats *stats = obd->md_stats;
1415
1416         if (stats != NULL) {
1417                 obd->md_stats = NULL;
1418                 obd->md_cntr_base = 0;
1419                 lprocfs_free_stats(&stats);
1420         }
1421 }
1422
1423 void lprocfs_init_ldlm_stats(struct lprocfs_stats *ldlm_stats)
1424 {
1425         lprocfs_counter_init(ldlm_stats,
1426                              LDLM_ENQUEUE - LDLM_FIRST_OPC,
1427                              0, "ldlm_enqueue", "reqs");
1428         lprocfs_counter_init(ldlm_stats,
1429                              LDLM_CONVERT - LDLM_FIRST_OPC,
1430                              0, "ldlm_convert", "reqs");
1431         lprocfs_counter_init(ldlm_stats,
1432                              LDLM_CANCEL - LDLM_FIRST_OPC,
1433                              0, "ldlm_cancel", "reqs");
1434         lprocfs_counter_init(ldlm_stats,
1435                              LDLM_BL_CALLBACK - LDLM_FIRST_OPC,
1436                              0, "ldlm_bl_callback", "reqs");
1437         lprocfs_counter_init(ldlm_stats,
1438                              LDLM_CP_CALLBACK - LDLM_FIRST_OPC,
1439                              0, "ldlm_cp_callback", "reqs");
1440         lprocfs_counter_init(ldlm_stats,
1441                              LDLM_GL_CALLBACK - LDLM_FIRST_OPC,
1442                              0, "ldlm_gl_callback", "reqs");
1443 }
1444
1445 int lprocfs_exp_rd_nid(char *page, char **start, off_t off, int count,
1446                          int *eof,  void *data)
1447 {
1448         struct obd_export *exp = data;
1449         LASSERT(exp != NULL);
1450         *eof = 1;
1451         return snprintf(page, count, "%s\n", obd_export_nid2str(exp));
1452 }
1453
1454 struct exp_uuid_cb_data {
1455         char                   *page;
1456         int                     count;
1457         int                    *eof;
1458         int                    *len;
1459 };
1460
1461 static void
1462 lprocfs_exp_rd_cb_data_init(struct exp_uuid_cb_data *cb_data, char *page,
1463                             int count, int *eof, int *len)
1464 {
1465         cb_data->page = page;
1466         cb_data->count = count;
1467         cb_data->eof = eof;
1468         cb_data->len = len;
1469 }
1470
1471 void lprocfs_exp_print_uuid(void *obj, void *cb_data)
1472 {
1473         struct obd_export *exp = (struct obd_export *)obj;
1474         struct exp_uuid_cb_data *data = (struct exp_uuid_cb_data *)cb_data;
1475
1476         if (exp->exp_nid_stats)
1477                 *data->len += snprintf((data->page + *data->len),
1478                                        data->count, "%s\n",
1479                                        obd_uuid2str(&exp->exp_client_uuid));
1480 }
1481
1482 int lprocfs_exp_rd_uuid(char *page, char **start, off_t off, int count,
1483                         int *eof,  void *data)
1484 {
1485         struct nid_stat *stats = (struct nid_stat *)data;
1486         struct exp_uuid_cb_data cb_data;
1487         struct obd_device *obd = stats->nid_obd;
1488         int len = 0;
1489
1490         *eof = 1;
1491         page[0] = '\0';
1492         lprocfs_exp_rd_cb_data_init(&cb_data, page, count, eof, &len);
1493         lustre_hash_for_each_key(obd->obd_nid_hash, &stats->nid,
1494                                  lprocfs_exp_print_uuid, &cb_data);
1495         return (*cb_data.len);
1496 }
1497
1498 void lprocfs_exp_print_hash(void *obj, void *cb_data)
1499 {
1500         struct exp_uuid_cb_data *data = cb_data;
1501         struct obd_export       *exp = obj;
1502         lustre_hash_t           *lh;
1503
1504         lh = exp->exp_lock_hash;
1505         if (lh) {
1506                 if (!*data->len)
1507                         *data->len += lustre_hash_debug_header(data->page,
1508                                                                data->count);
1509
1510                 *data->len += lustre_hash_debug_str(lh, data->page + *data->len,
1511                                                     data->count);
1512         }
1513 }
1514
1515 int lprocfs_exp_rd_hash(char *page, char **start, off_t off, int count,
1516                         int *eof,  void *data)
1517 {
1518         struct nid_stat *stats = (struct nid_stat *)data;
1519         struct exp_uuid_cb_data cb_data;
1520         struct obd_device *obd = stats->nid_obd;
1521         int len = 0;
1522
1523         *eof = 1;
1524         page[0] = '\0';
1525         lprocfs_exp_rd_cb_data_init(&cb_data, page, count, eof, &len);
1526
1527         lustre_hash_for_each_key(obd->obd_nid_hash, &stats->nid,
1528                                  lprocfs_exp_print_hash, &cb_data);
1529         return (*cb_data.len);
1530 }
1531
1532 int lprocfs_nid_stats_clear_read(char *page, char **start, off_t off,
1533                                         int count, int *eof,  void *data)
1534 {
1535         *eof = 1;
1536         return snprintf(page, count, "%s\n",
1537                         "Write into this file to clear all nid stats and "
1538                         "stale nid entries");
1539 }
1540 EXPORT_SYMBOL(lprocfs_nid_stats_clear_read);
1541
1542 void lprocfs_nid_stats_clear_write_cb(void *obj, void *data)
1543 {
1544         struct nid_stat *stat = obj;
1545         int i;
1546         ENTRY;
1547         /* object has only hash + iterate_all references.
1548          * add/delete blocked by hash bucket lock */
1549         CDEBUG(D_INFO,"refcnt %d\n", stat->nid_exp_ref_count);
1550         if (stat->nid_exp_ref_count == 2) {
1551                 hlist_del_init(&stat->nid_hash);
1552                 stat->nid_exp_ref_count--;
1553                 spin_lock(&stat->nid_obd->obd_nid_lock);
1554                 list_move(&stat->nid_list, data);
1555                 spin_unlock(&stat->nid_obd->obd_nid_lock);
1556                 EXIT;
1557                 return;
1558         }
1559         /* we has reference to object - only clear data*/
1560         if (stat->nid_stats)
1561                 lprocfs_clear_stats(stat->nid_stats);
1562
1563         if (stat->nid_brw_stats) {
1564                 for (i = 0; i < BRW_LAST; i++)
1565                         lprocfs_oh_clear(&stat->nid_brw_stats->hist[i]);
1566         }
1567         EXIT;
1568         return;
1569 }
1570
1571 int lprocfs_nid_stats_clear_write(struct file *file, const char *buffer,
1572                                          unsigned long count, void *data)
1573 {
1574         struct obd_device *obd = (struct obd_device *)data;
1575         struct nid_stat *client_stat;
1576         CFS_LIST_HEAD(free_list);
1577
1578         lustre_hash_for_each(obd->obd_nid_stats_hash,
1579                              lprocfs_nid_stats_clear_write_cb, &free_list);
1580
1581         while (!list_empty(&free_list)) {
1582                 client_stat = list_entry(free_list.next, struct nid_stat,
1583                                          nid_list);
1584                 list_del_init(&client_stat->nid_list);
1585                 lprocfs_free_client_stats(client_stat);
1586         }
1587
1588         return count;
1589 }
1590 EXPORT_SYMBOL(lprocfs_nid_stats_clear_write);
1591
1592 int lprocfs_exp_setup(struct obd_export *exp, lnet_nid_t *nid, int *newnid)
1593 {
1594         struct nid_stat *new_stat, *old_stat;
1595         struct nid_stat_uuid *new_ns_uuid;
1596         struct obd_device *obd = NULL;
1597         cfs_proc_dir_entry_t *entry;
1598         int rc = 0;
1599         ENTRY;
1600
1601         *newnid = 0;
1602
1603         if (!exp || !exp->exp_obd || !exp->exp_obd->obd_proc_exports_entry ||
1604             !exp->exp_obd->obd_nid_stats_hash)
1605                 RETURN(-EINVAL);
1606
1607         /* not test against zero because eric say:
1608          * You may only test nid against another nid, or LNET_NID_ANY.
1609          * Anything else is nonsense.*/
1610         if (!nid || *nid == LNET_NID_ANY)
1611                 RETURN(0);
1612
1613         obd = exp->exp_obd;
1614
1615         CDEBUG(D_CONFIG, "using hash %p\n", obd->obd_nid_stats_hash);
1616
1617         OBD_ALLOC_PTR(new_stat);
1618         if (new_stat == NULL)
1619                 RETURN(-ENOMEM);
1620
1621         OBD_ALLOC_PTR(new_ns_uuid);
1622         if (new_ns_uuid == NULL) {
1623                 OBD_FREE_PTR(new_stat);
1624                 RETURN(-ENOMEM);
1625         }
1626         CFS_INIT_LIST_HEAD(&new_ns_uuid->ns_uuid_list);
1627         strncpy(new_ns_uuid->ns_uuid.uuid, exp->exp_client_uuid.uuid,
1628                 sizeof(struct obd_uuid));
1629
1630         CFS_INIT_LIST_HEAD(&new_stat->nid_uuid_list);
1631         new_stat->nid               = *nid;
1632         new_stat->nid_obd           = exp->exp_obd;
1633         new_stat->nid_exp_ref_count = 1; /* live in hash after destroy export */
1634
1635         old_stat = lustre_hash_findadd_unique(obd->obd_nid_stats_hash,
1636                                               nid, &new_stat->nid_hash);
1637         CDEBUG(D_INFO, "Found stats %p for nid %s - ref %d\n",
1638                old_stat, libcfs_nid2str(*nid), new_stat->nid_exp_ref_count);
1639
1640         /* Return -EALREADY here so that we know that the /proc
1641          * entry already has been created */
1642         if (old_stat != new_stat) {
1643                 struct nid_stat_uuid *tmp_uuid;
1644                 int found = 0;
1645
1646                 exp->exp_nid_stats = old_stat;
1647                 /* We need to decrement the refcount if the uuid was
1648                  * already in our list */
1649                 spin_lock(&obd->obd_nid_lock);
1650                 list_for_each_entry(tmp_uuid, &old_stat->nid_uuid_list,
1651                                     ns_uuid_list) {
1652                         if (tmp_uuid && obd_uuid_equals(&tmp_uuid->ns_uuid,
1653                                                         &exp->exp_client_uuid)){
1654                                 found = 1;
1655                                 --old_stat->nid_exp_ref_count;
1656                                 break;
1657                         }
1658                 }
1659
1660                 if (!found)
1661                         list_add(&new_ns_uuid->ns_uuid_list,
1662                                  &old_stat->nid_uuid_list);
1663                 else
1664                         OBD_FREE_PTR(new_ns_uuid);
1665
1666                 spin_unlock(&obd->obd_nid_lock);
1667
1668                 GOTO(destroy_new, rc = -EALREADY);
1669         }
1670         /* not found - create */
1671         new_stat->nid_proc = lprocfs_register(libcfs_nid2str(*nid),
1672                                               obd->obd_proc_exports_entry,
1673                                               NULL, NULL);
1674         if (new_stat->nid_proc == NULL) {
1675                 CERROR("Error making export directory for nid %s\n",
1676                        libcfs_nid2str(*nid));
1677                 GOTO(destroy_new_ns, rc = -ENOMEM);
1678         }
1679
1680         /* Add in uuid to our nid_stats list */
1681         spin_lock(&obd->obd_nid_lock);
1682         list_add(&new_ns_uuid->ns_uuid_list, &new_stat->nid_uuid_list);
1683         spin_unlock(&obd->obd_nid_lock);
1684
1685         entry = lprocfs_add_simple(new_stat->nid_proc, "uuid",
1686                                    lprocfs_exp_rd_uuid, NULL, new_stat, NULL);
1687         if (IS_ERR(entry)) {
1688                 CWARN("Error adding the NID stats file\n");
1689                 rc = PTR_ERR(entry);
1690                 GOTO(destroy_new_ns, rc);
1691         }
1692
1693         entry = lprocfs_add_simple(new_stat->nid_proc, "hash",
1694                                    lprocfs_exp_rd_hash, NULL, new_stat, NULL);
1695         if (IS_ERR(entry)) {
1696                 CWARN("Error adding the hash file\n");
1697                 lprocfs_remove(&new_stat->nid_proc);
1698                 rc = PTR_ERR(entry);
1699                 GOTO(destroy_new_ns, rc);
1700         }
1701
1702         exp->exp_nid_stats = new_stat;
1703         *newnid = 1;
1704         /* protect competitive add to list, not need locking on destroy */
1705         spin_lock(&obd->obd_nid_lock);
1706         list_add(&new_stat->nid_list, &obd->obd_nid_stats);
1707         spin_unlock(&obd->obd_nid_lock);
1708
1709         RETURN(rc);
1710
1711 destroy_new_ns:
1712         lustre_hash_del(obd->obd_nid_stats_hash, nid, &new_stat->nid_hash);
1713         OBD_FREE_PTR(new_ns_uuid);
1714
1715 destroy_new:
1716         OBD_FREE_PTR(new_stat);
1717         RETURN(rc);
1718 }
1719
1720 int lprocfs_exp_cleanup(struct obd_export *exp)
1721 {
1722         struct nid_stat *stat = exp->exp_nid_stats;
1723         struct nid_stat_uuid *cursor, *tmp;
1724         int found = 0;
1725
1726         if(!stat || !exp->exp_obd)
1727                 RETURN(0);
1728
1729         spin_lock(&exp->exp_obd->obd_nid_lock);
1730         list_for_each_entry_safe(cursor, tmp,
1731                                  &stat->nid_uuid_list,
1732                                  ns_uuid_list) {
1733                 if (cursor && obd_uuid_equals(&cursor->ns_uuid,
1734                                               &exp->exp_client_uuid)) {
1735                         found = 1;
1736                         list_del(&cursor->ns_uuid_list);
1737                         OBD_FREE_PTR(cursor);
1738                         --stat->nid_exp_ref_count;
1739                         CDEBUG(D_INFO, "Put stat %p - %d\n", stat,
1740                                stat->nid_exp_ref_count);
1741                         break;
1742                 }
1743         }
1744         spin_unlock(&exp->exp_obd->obd_nid_lock);
1745         if (!found)
1746                 CERROR("obd_export's client uuid %s are not found in its "
1747                        "nid_stats list\n", exp->exp_client_uuid.uuid);
1748
1749         exp->exp_nid_stats = NULL;
1750         lprocfs_free_md_stats(exp->exp_obd);
1751
1752         return 0;
1753 }
1754
1755 int lprocfs_write_helper(const char *buffer, unsigned long count,
1756                          int *val)
1757 {
1758         return lprocfs_write_frac_helper(buffer, count, val, 1);
1759 }
1760
1761 int lprocfs_write_frac_helper(const char *buffer, unsigned long count,
1762                               int *val, int mult)
1763 {
1764         char kernbuf[20], *end, *pbuf;
1765
1766         if (count > (sizeof(kernbuf) - 1))
1767                 return -EINVAL;
1768
1769         if (copy_from_user(kernbuf, buffer, count))
1770                 return -EFAULT;
1771
1772         kernbuf[count] = '\0';
1773         pbuf = kernbuf;
1774         if (*pbuf == '-') {
1775                 mult = -mult;
1776                 pbuf++;
1777         }
1778
1779         *val = (int)simple_strtoul(pbuf, &end, 10) * mult;
1780         if (pbuf == end)
1781                 return -EINVAL;
1782
1783         if (end != NULL && *end == '.') {
1784                 int temp_val, pow = 1;
1785                 int i;
1786
1787                 pbuf = end + 1;
1788                 if (strlen(pbuf) > 5)
1789                         pbuf[5] = '\0'; /*only allow 5bits fractional*/
1790
1791                 temp_val = (int)simple_strtoul(pbuf, &end, 10) * mult;
1792
1793                 if (pbuf < end) {
1794                         for (i = 0; i < (end - pbuf); i++)
1795                                 pow *= 10;
1796
1797                         *val += temp_val / pow;
1798                 }
1799         }
1800         return 0;
1801 }
1802
1803 int lprocfs_read_frac_helper(char *buffer, unsigned long count, long val,
1804                              int mult)
1805 {
1806         long decimal_val, frac_val;
1807         int prtn;
1808
1809         if (count < 10)
1810                 return -EINVAL;
1811
1812         decimal_val = val / mult;
1813         prtn = snprintf(buffer, count, "%ld", decimal_val);
1814         frac_val = val % mult;
1815
1816         if (prtn < (count - 4) && frac_val > 0) {
1817                 long temp_frac;
1818                 int i, temp_mult = 1, frac_bits = 0;
1819
1820                 temp_frac = frac_val * 10;
1821                 buffer[prtn++] = '.';
1822                 while (frac_bits < 2 && (temp_frac / mult) < 1 ) {
1823                         /* only reserved 2 bits fraction */
1824                         buffer[prtn++] ='0';
1825                         temp_frac *= 10;
1826                         frac_bits++;
1827                 }
1828                 /*
1829                  * Need to think these cases :
1830                  *      1. #echo x.00 > /proc/xxx       output result : x
1831                  *      2. #echo x.0x > /proc/xxx       output result : x.0x
1832                  *      3. #echo x.x0 > /proc/xxx       output result : x.x
1833                  *      4. #echo x.xx > /proc/xxx       output result : x.xx
1834                  *      Only reserved 2 bits fraction.
1835                  */
1836                 for (i = 0; i < (5 - prtn); i++)
1837                         temp_mult *= 10;
1838
1839                 frac_bits = min((int)count - prtn, 3 - frac_bits);
1840                 prtn += snprintf(buffer + prtn, frac_bits, "%ld",
1841                                  frac_val * temp_mult / mult);
1842
1843                 prtn--;
1844                 while(buffer[prtn] < '1' || buffer[prtn] > '9') {
1845                         prtn--;
1846                         if (buffer[prtn] == '.') {
1847                                 prtn--;
1848                                 break;
1849                         }
1850                 }
1851                 prtn++;
1852         }
1853         buffer[prtn++] ='\n';
1854         return prtn;
1855 }
1856
1857 int lprocfs_write_u64_helper(const char *buffer, unsigned long count,__u64 *val)
1858 {
1859         return lprocfs_write_frac_u64_helper(buffer, count, val, 1);
1860 }
1861
1862 int lprocfs_write_frac_u64_helper(const char *buffer, unsigned long count,
1863                               __u64 *val, int mult)
1864 {
1865         char kernbuf[22], *end, *pbuf;
1866         __u64 whole, frac = 0, units;
1867         unsigned frac_d = 1;
1868
1869         if (count > (sizeof(kernbuf) - 1))
1870                 return -EINVAL;
1871
1872         if (copy_from_user(kernbuf, buffer, count))
1873                 return -EFAULT;
1874
1875         kernbuf[count] = '\0';
1876         pbuf = kernbuf;
1877         if (*pbuf == '-') {
1878                 mult = -mult;
1879                 pbuf++;
1880         }
1881
1882         whole = simple_strtoull(pbuf, &end, 10);
1883         if (pbuf == end)
1884                 return -EINVAL;
1885
1886         if (end != NULL && *end == '.') {
1887                 int i;
1888                 pbuf = end + 1;
1889
1890                 /* need to limit frac_d to a __u32 */
1891                 if (strlen(pbuf) > 10)
1892                         pbuf[10] = '\0';
1893
1894                 frac = simple_strtoull(pbuf, &end, 10);
1895                 /* count decimal places */
1896                 for (i = 0; i < (end - pbuf); i++)
1897                         frac_d *= 10;
1898         }
1899
1900         units = 1;
1901         switch(*end) {
1902         case 'p': case 'P':
1903                 units <<= 10;
1904         case 't': case 'T':
1905                 units <<= 10;
1906         case 'g': case 'G':
1907                 units <<= 10;
1908         case 'm': case 'M':
1909                 units <<= 10;
1910         case 'k': case 'K':
1911                 units <<= 10;
1912         }
1913         /* Specified units override the multiplier */
1914         if (units)
1915                 mult = mult < 0 ? -units : units;
1916
1917         frac *= mult;
1918         do_div(frac, frac_d);
1919         *val = whole * mult + frac;
1920         return 0;
1921 }
1922
1923 int lprocfs_seq_create(cfs_proc_dir_entry_t *parent, char *name, mode_t mode,
1924                        struct file_operations *seq_fops, void *data)
1925 {
1926         struct proc_dir_entry *entry;
1927         ENTRY;
1928
1929         entry = create_proc_entry(name, mode, parent);
1930         if (entry == NULL)
1931                 RETURN(-ENOMEM);
1932         entry->proc_fops = seq_fops;
1933         entry->data = data;
1934
1935         RETURN(0);
1936 }
1937 EXPORT_SYMBOL(lprocfs_seq_create);
1938
1939 __inline__ int lprocfs_obd_seq_create(struct obd_device *dev, char *name,
1940                                       mode_t mode,
1941                                       struct file_operations *seq_fops,
1942                                       void *data)
1943 {
1944         return (lprocfs_seq_create(dev->obd_proc_entry, name,
1945                                    mode, seq_fops, data));
1946 }
1947 EXPORT_SYMBOL(lprocfs_obd_seq_create);
1948
1949 void lprocfs_oh_tally(struct obd_histogram *oh, unsigned int value)
1950 {
1951         if (value >= OBD_HIST_MAX)
1952                 value = OBD_HIST_MAX - 1;
1953
1954         spin_lock(&oh->oh_lock);
1955         oh->oh_buckets[value]++;
1956         spin_unlock(&oh->oh_lock);
1957 }
1958 EXPORT_SYMBOL(lprocfs_oh_tally);
1959
1960 void lprocfs_oh_tally_log2(struct obd_histogram *oh, unsigned int value)
1961 {
1962         unsigned int val;
1963
1964         for (val = 0; ((1 << val) < value) && (val <= OBD_HIST_MAX); val++)
1965                 ;
1966
1967         lprocfs_oh_tally(oh, val);
1968 }
1969 EXPORT_SYMBOL(lprocfs_oh_tally_log2);
1970
1971 unsigned long lprocfs_oh_sum(struct obd_histogram *oh)
1972 {
1973         unsigned long ret = 0;
1974         int i;
1975
1976         for (i = 0; i < OBD_HIST_MAX; i++)
1977                 ret +=  oh->oh_buckets[i];
1978         return ret;
1979 }
1980 EXPORT_SYMBOL(lprocfs_oh_sum);
1981
1982 void lprocfs_oh_clear(struct obd_histogram *oh)
1983 {
1984         spin_lock(&oh->oh_lock);
1985         memset(oh->oh_buckets, 0, sizeof(oh->oh_buckets));
1986         spin_unlock(&oh->oh_lock);
1987 }
1988 EXPORT_SYMBOL(lprocfs_oh_clear);
1989
1990 int lprocfs_obd_rd_hash(char *page, char **start, off_t off,
1991                         int count, int *eof, void *data)
1992 {
1993         struct obd_device *obd = data;
1994         int c = 0;
1995
1996         if (obd == NULL)
1997                 return 0;
1998
1999         c += lustre_hash_debug_header(page, count);
2000         c += lustre_hash_debug_str(obd->obd_uuid_hash, page + c, count - c);
2001         c += lustre_hash_debug_str(obd->obd_nid_hash, page + c, count - c);
2002         c += lustre_hash_debug_str(obd->obd_nid_stats_hash, page+c, count-c);
2003
2004         return c;
2005 }
2006 EXPORT_SYMBOL(lprocfs_obd_rd_hash);
2007
2008 int lprocfs_obd_rd_recovery_status(char *page, char **start, off_t off,
2009                                    int count, int *eof, void *data)
2010 {
2011         struct obd_device *obd = data;
2012         int len = 0, size;
2013
2014         LASSERT(obd != NULL);
2015         LASSERT(count >= 0);
2016
2017         /* Set start of user data returned to
2018            page + off since the user may have
2019            requested to read much smaller than
2020            what we need to read */
2021         *start = page + off;
2022
2023         /* We know we are allocated a page here.
2024            Also we know that this function will
2025            not need to write more than a page
2026            so we can truncate at CFS_PAGE_SIZE.  */
2027         size = min(count + (int)off + 1, (int)CFS_PAGE_SIZE);
2028
2029         /* Initialize the page */
2030         memset(page, 0, size);
2031
2032         if (lprocfs_obd_snprintf(&page, size, &len, "status: ") <= 0)
2033                 goto out;
2034         if (obd->obd_max_recoverable_clients == 0) {
2035                 if (lprocfs_obd_snprintf(&page, size, &len, "INACTIVE\n") <= 0)
2036                         goto out;
2037
2038                 goto fclose;
2039         }
2040
2041         /* sampled unlocked, but really... */
2042         if (obd->obd_recovering == 0) {
2043                 if (lprocfs_obd_snprintf(&page, size, &len, "COMPLETE\n") <= 0)
2044                         goto out;
2045                 if (lprocfs_obd_snprintf(&page, size, &len,
2046                                          "recovery_start: %lu\n",
2047                                          obd->obd_recovery_start) <= 0)
2048                         goto out;
2049                 if (lprocfs_obd_snprintf(&page, size, &len,
2050                                          "recovery_duration: %lu\n",
2051                                          obd->obd_recovery_end -
2052                                          obd->obd_recovery_start) <= 0)
2053                         goto out;
2054                 /* Number of clients that have completed recovery */
2055                 if (lprocfs_obd_snprintf(&page, size, &len,
2056                                          "completed_clients: %d/%d\n",
2057                                          obd->obd_max_recoverable_clients -
2058                                          obd->obd_recoverable_clients,
2059                                          obd->obd_max_recoverable_clients) <= 0)
2060                         goto out;
2061                 if (lprocfs_obd_snprintf(&page, size, &len,
2062                                          "replayed_requests: %d\n",
2063                                          obd->obd_replayed_requests) <= 0)
2064                         goto out;
2065                 if (lprocfs_obd_snprintf(&page, size, &len,
2066                                          "last_transno: "LPD64"\n",
2067                                          obd->obd_next_recovery_transno - 1)<=0)
2068                         goto out;
2069                 goto fclose;
2070         }
2071
2072         if (lprocfs_obd_snprintf(&page, size, &len, "RECOVERING\n") <= 0)
2073                 goto out;
2074         if (lprocfs_obd_snprintf(&page, size, &len, "recovery_start: %lu\n",
2075                                  obd->obd_recovery_start) <= 0)
2076                 goto out;
2077         if (lprocfs_obd_snprintf(&page, size, &len, "time_remaining: %lu\n",
2078                            cfs_time_current_sec() >= obd->obd_recovery_end ? 0 :
2079                            obd->obd_recovery_end - cfs_time_current_sec()) <= 0)
2080                 goto out;
2081         if (lprocfs_obd_snprintf(&page, size, &len,"connected_clients: %d/%d\n",
2082                                  obd->obd_connected_clients,
2083                                  obd->obd_max_recoverable_clients) <= 0)
2084                 goto out;
2085         /* Number of clients that have completed recovery */
2086         if (lprocfs_obd_snprintf(&page, size, &len,"completed_clients: %d/%d\n",
2087                                  obd->obd_max_recoverable_clients -
2088                                  obd->obd_recoverable_clients,
2089                                  obd->obd_max_recoverable_clients) <= 0)
2090                 goto out;
2091         if (lprocfs_obd_snprintf(&page, size, &len,"replayed_requests: %d/??\n",
2092                                  obd->obd_replayed_requests) <= 0)
2093                 goto out;
2094         if (lprocfs_obd_snprintf(&page, size, &len, "queued_requests: %d\n",
2095                                  obd->obd_requests_queued_for_recovery) <= 0)
2096                 goto out;
2097
2098         if (lprocfs_obd_snprintf(&page, size, &len, "next_transno: "LPD64"\n",
2099                                  obd->obd_next_recovery_transno) <= 0)
2100                 goto out;
2101
2102 fclose:
2103         *eof = 1;
2104 out:
2105         return min(count, len - (int)off);
2106 }
2107 EXPORT_SYMBOL(lprocfs_obd_rd_recovery_status);
2108
2109 int lprocfs_obd_rd_recovery_maxtime(char *page, char **start, off_t off,
2110                                     int count, int *eof, void *data)
2111 {
2112         struct obd_device *obd = data;
2113         LASSERT(obd != NULL);
2114
2115         return snprintf(page, count, "%lu\n", obd->obd_recovery_max_time);
2116 }
2117 EXPORT_SYMBOL(lprocfs_obd_rd_recovery_maxtime);
2118
2119 int lprocfs_obd_wr_recovery_maxtime(struct file *file, const char *buffer,
2120                                     unsigned long count, void *data)
2121 {
2122         struct obd_device *obd = data;
2123         int val, rc;
2124         LASSERT(obd != NULL);
2125
2126         rc = lprocfs_write_helper(buffer, count, &val);
2127         if (rc)
2128                 return rc;
2129
2130         obd->obd_recovery_max_time = val;
2131         return count;
2132 }
2133 EXPORT_SYMBOL(lprocfs_obd_wr_recovery_maxtime);
2134
2135
2136 /**** Changelogs *****/
2137 #define D_CHANGELOG 0
2138
2139 DECLARE_CHANGELOG_NAMES;
2140
2141 /* How many records per seq_show.  Too small, we spawn llog_process threads
2142    too often; too large, we run out of buffer space */
2143 #define CHANGELOG_CHUNK_SIZE 100
2144
2145 static int changelog_show_cb(struct llog_handle *llh, struct llog_rec_hdr *hdr,
2146                              void *data)
2147 {
2148         struct seq_file *seq = (struct seq_file *)data;
2149         struct changelog_seq_iter *csi = seq->private;
2150         struct llog_changelog_rec *rec = (struct llog_changelog_rec *)hdr;
2151         int rc;
2152         ENTRY;
2153
2154         if ((rec->cr_hdr.lrh_type != CHANGELOG_REC) ||
2155             (rec->cr_type >= CL_LAST)) {
2156                 CERROR("Not a changelog rec %d/%d\n", rec->cr_hdr.lrh_type,
2157                        rec->cr_type);
2158                 RETURN(-EINVAL);
2159         }
2160
2161         CDEBUG(D_CHANGELOG, "rec="LPU64" start="LPU64" cat=%d:%d start=%d:%d\n",
2162                rec->cr_index, csi->csi_startrec,
2163                llh->lgh_hdr->llh_cat_idx, llh->lgh_cur_idx,
2164                csi->csi_startcat, csi->csi_startidx);
2165
2166         if (rec->cr_index < csi->csi_startrec)
2167                 /* Skip entries earlier than what we are interested in */
2168                 RETURN(0);
2169         if (rec->cr_index == csi->csi_startrec) {
2170                 /* Remember where we started, since seq_read will re-read
2171                  * the data when it reallocs space.  Sigh, if only there was
2172                  * a way to tell seq_file how big the buf should be in the
2173                  * first place...
2174                  */
2175                 csi->csi_startcat = llh->lgh_hdr->llh_cat_idx;
2176                 csi->csi_startidx = rec->cr_hdr.lrh_index - 1;
2177         }
2178         if (csi->csi_wrote > CHANGELOG_CHUNK_SIZE) {
2179                 /* Stop at some point with a reasonable seq_file buffer size.
2180                  * Start from here the next time.
2181                  */
2182                 csi->csi_endrec = rec->cr_index - 1;
2183                 csi->csi_startcat = llh->lgh_hdr->llh_cat_idx;
2184                 csi->csi_startidx = rec->cr_hdr.lrh_index - 1;
2185                 csi->csi_wrote = 0;
2186                 RETURN(LLOG_PROC_BREAK);
2187         }
2188
2189         rc = seq_printf(seq, LPU64" %02d%-5s "LPU64" 0x%x t="DFID,
2190                         rec->cr_index, rec->cr_type,
2191                         changelog_str[rec->cr_type], rec->cr_time,
2192                         rec->cr_flags & CLF_FLAGMASK, PFID(&rec->cr_tfid));
2193
2194         if (rec->cr_namelen)
2195                 /* namespace rec includes parent and filename */
2196                 rc += seq_printf(seq, " p="DFID" %.*s\n", PFID(&rec->cr_pfid),
2197                                  rec->cr_namelen, rec->cr_name);
2198         else
2199                 rc += seq_puts(seq, "\n");
2200
2201         if (rc < 0) {
2202                 /* Ran out of room in the seq buffer. seq_read will dump
2203                  * the whole buffer and re-seq_start with a larger one;
2204                  * no point in continuing the llog_process */
2205                 CDEBUG(D_CHANGELOG, "rec="LPU64" overflow "LPU64"<-"LPU64"\n",
2206                        rec->cr_index, csi->csi_startrec, csi->csi_endrec);
2207                 csi->csi_endrec = csi->csi_startrec - 1;
2208                 csi->csi_wrote = 0;
2209                 RETURN(LLOG_PROC_BREAK);
2210         }
2211
2212         csi->csi_wrote++;
2213         csi->csi_endrec = rec->cr_index;
2214
2215         RETURN(0);
2216 }
2217
2218 static int changelog_seq_show(struct seq_file *seq, void *v)
2219 {
2220         struct changelog_seq_iter *csi = seq->private;
2221         int rc;
2222         ENTRY;
2223
2224         if (csi->csi_fill) {
2225                 /* seq_read wants more data to fill his buffer. But we already
2226                    filled the buf as much as we cared to; force seq_read to
2227                    accept that by padding with 0's */
2228                 while (seq_putc(seq, 0) == 0);
2229                 RETURN(0);
2230         }
2231
2232         /* Since we have to restart the llog_cat_process for each chunk of the
2233            seq_ functions, start from where we left off. */
2234         rc = llog_cat_process(csi->csi_llh, changelog_show_cb, seq,
2235                               csi->csi_startcat, csi->csi_startidx);
2236
2237         CDEBUG(D_CHANGELOG,"seq_show "LPU64"-"LPU64" cat=%d:%d wrote=%d rc=%d\n",
2238                csi->csi_startrec, csi->csi_endrec, csi->csi_startcat,
2239                csi->csi_startidx, csi->csi_wrote, rc);
2240
2241         if (rc == 0)
2242                 csi->csi_done = 1;
2243         if (rc == LLOG_PROC_BREAK)
2244                 /* more records left, but seq_show must return 0 */
2245                 rc = 0;
2246         RETURN(rc);
2247 }
2248
2249 static void *changelog_seq_start(struct seq_file *seq, loff_t *pos)
2250 {
2251         struct changelog_seq_iter *csi = seq->private;
2252         LASSERT(csi);
2253
2254         CDEBUG(D_CHANGELOG, "start "LPU64"-"LPU64" pos="LPU64"\n",
2255                csi->csi_startrec, csi->csi_endrec, *pos);
2256
2257         csi->csi_fill = 0;
2258
2259         if (csi->csi_done)
2260                 /* no more records, seq_read should return 0 if buffer
2261                    is empty */
2262                 return NULL;
2263
2264         if (*pos > csi->csi_pos) {
2265                 /* The seq_read implementation sucks.  It may call start
2266                    multiple times, using pos to indicate advances, if any,
2267                    by arbitrarily increasing it by 1. So ignore the actual
2268                    value of pos, and just register any increase as
2269                    "seq_read wants the next values". */
2270                 csi->csi_startrec = csi->csi_endrec + 1;
2271                 csi->csi_pos = *pos;
2272         }
2273         /* else use old startrec/startidx */
2274
2275         return csi;
2276 }
2277
2278 static void changelog_seq_stop(struct seq_file *seq, void *v)
2279 {
2280         struct changelog_seq_iter *csi = seq->private;
2281
2282         CDEBUG(D_CHANGELOG, "stop "LPU64"-"LPU64"\n",
2283                csi->csi_startrec, csi->csi_endrec);
2284 }
2285
2286 static void *changelog_seq_next(struct seq_file *seq, void *v, loff_t *pos)
2287 {
2288         struct changelog_seq_iter *csi = seq->private;
2289
2290         CDEBUG(D_CHANGELOG, "next "LPU64"-"LPU64" pos="LPU64"\n",
2291                csi->csi_startrec, csi->csi_endrec, *pos);
2292
2293         csi->csi_fill = 1;
2294
2295         return csi;
2296 }
2297
2298 static struct seq_operations changelog_sops = {
2299         .start = changelog_seq_start,
2300         .stop = changelog_seq_stop,
2301         .next = changelog_seq_next,
2302         .show = changelog_seq_show,
2303 };
2304
2305 int changelog_seq_open(struct inode *inode, struct file *file,
2306                        struct changelog_seq_iter **csih)
2307 {
2308         struct changelog_seq_iter *csi;
2309         struct proc_dir_entry *dp = PDE(inode);
2310         struct seq_file *seq;
2311         int rc;
2312
2313         LPROCFS_ENTRY_AND_CHECK(dp);
2314
2315         rc = seq_open(file, &changelog_sops);
2316         if (rc) {
2317                 LPROCFS_EXIT();
2318                 return rc;
2319         }
2320
2321         OBD_ALLOC_PTR(csi);
2322         if (csi == NULL) {
2323                 lprocfs_seq_release(inode, file);
2324                 return -ENOMEM;
2325         }
2326
2327         csi->csi_dev = dp->data;
2328         seq = file->private_data;
2329         seq->private = csi;
2330         *csih = csi;
2331
2332         return rc;
2333 }
2334 EXPORT_SYMBOL(changelog_seq_open);
2335
2336 int changelog_seq_release(struct inode *inode, struct file *file)
2337 {
2338         struct seq_file *seq = file->private_data;
2339         struct changelog_seq_iter *csi = seq->private;
2340
2341         if (csi)
2342                 OBD_FREE_PTR(csi);
2343
2344         return lprocfs_seq_release(inode, file);
2345 }
2346 EXPORT_SYMBOL(changelog_seq_release);
2347
2348 #ifndef SEEK_CUR /* SLES10 needs this */
2349 #define SEEK_CUR        1
2350 #define SEEK_END        2
2351 #endif
2352
2353 loff_t changelog_seq_lseek(struct file *file, loff_t offset, int origin)
2354 {
2355         struct seq_file *seq = (struct seq_file *)file->private_data;
2356         struct changelog_seq_iter *csi = seq->private;
2357
2358         CDEBUG(D_CHANGELOG,"seek "LPU64"-"LPU64" off="LPU64":%d fpos="LPU64"\n",
2359                csi->csi_startrec, csi->csi_endrec, offset, origin, file->f_pos);
2360
2361         LL_SEQ_LOCK(seq);
2362
2363         switch (origin) {
2364                 case SEEK_CUR:
2365                         offset += csi->csi_endrec;
2366                         break;
2367                 case SEEK_END:
2368                         /* we don't know the last rec */
2369                         offset = -1;
2370         }
2371
2372         /* SEEK_SET */
2373
2374         if (offset < 0) {
2375                 LL_SEQ_UNLOCK(seq);
2376                 return -EINVAL;
2377         }
2378
2379         csi->csi_startrec = offset;
2380         csi->csi_endrec = offset ? offset - 1 : 0;
2381
2382         /* drop whatever is left in sucky seq_read's buffer */
2383         seq->count = 0;
2384         seq->from = 0;
2385         seq->index++;
2386         LL_SEQ_UNLOCK(seq);
2387         file->f_pos = csi->csi_startrec;
2388         return csi->csi_startrec;
2389 }
2390 EXPORT_SYMBOL(changelog_seq_lseek);
2391
2392 EXPORT_SYMBOL(lprocfs_register);
2393 EXPORT_SYMBOL(lprocfs_srch);
2394 EXPORT_SYMBOL(lprocfs_remove);
2395 EXPORT_SYMBOL(lprocfs_remove_proc_entry);
2396 EXPORT_SYMBOL(lprocfs_add_vars);
2397 EXPORT_SYMBOL(lprocfs_obd_setup);
2398 EXPORT_SYMBOL(lprocfs_obd_cleanup);
2399 EXPORT_SYMBOL(lprocfs_add_simple);
2400 EXPORT_SYMBOL(lprocfs_add_symlink);
2401 EXPORT_SYMBOL(lprocfs_free_per_client_stats);
2402 EXPORT_SYMBOL(lprocfs_alloc_stats);
2403 EXPORT_SYMBOL(lprocfs_free_stats);
2404 EXPORT_SYMBOL(lprocfs_clear_stats);
2405 EXPORT_SYMBOL(lprocfs_register_stats);
2406 EXPORT_SYMBOL(lprocfs_init_ops_stats);
2407 EXPORT_SYMBOL(lprocfs_init_ldlm_stats);
2408 EXPORT_SYMBOL(lprocfs_alloc_obd_stats);
2409 EXPORT_SYMBOL(lprocfs_alloc_md_stats);
2410 EXPORT_SYMBOL(lprocfs_free_obd_stats);
2411 EXPORT_SYMBOL(lprocfs_exp_setup);
2412 EXPORT_SYMBOL(lprocfs_exp_cleanup);
2413
2414 EXPORT_SYMBOL(lprocfs_rd_u64);
2415 EXPORT_SYMBOL(lprocfs_rd_atomic);
2416 EXPORT_SYMBOL(lprocfs_wr_atomic);
2417 EXPORT_SYMBOL(lprocfs_rd_uint);
2418 EXPORT_SYMBOL(lprocfs_wr_uint);
2419 EXPORT_SYMBOL(lprocfs_rd_uuid);
2420 EXPORT_SYMBOL(lprocfs_rd_name);
2421 EXPORT_SYMBOL(lprocfs_rd_fstype);
2422 EXPORT_SYMBOL(lprocfs_rd_server_uuid);
2423 EXPORT_SYMBOL(lprocfs_rd_conn_uuid);
2424 EXPORT_SYMBOL(lprocfs_rd_num_exports);
2425 EXPORT_SYMBOL(lprocfs_rd_numrefs);
2426 EXPORT_SYMBOL(lprocfs_at_hist_helper);
2427 EXPORT_SYMBOL(lprocfs_rd_import);
2428 EXPORT_SYMBOL(lprocfs_rd_timeouts);
2429 EXPORT_SYMBOL(lprocfs_rd_blksize);
2430 EXPORT_SYMBOL(lprocfs_rd_kbytestotal);
2431 EXPORT_SYMBOL(lprocfs_rd_kbytesfree);
2432 EXPORT_SYMBOL(lprocfs_rd_kbytesavail);
2433 EXPORT_SYMBOL(lprocfs_rd_filestotal);
2434 EXPORT_SYMBOL(lprocfs_rd_filesfree);
2435
2436 EXPORT_SYMBOL(lprocfs_write_helper);
2437 EXPORT_SYMBOL(lprocfs_write_frac_helper);
2438 EXPORT_SYMBOL(lprocfs_read_frac_helper);
2439 EXPORT_SYMBOL(lprocfs_write_u64_helper);
2440 EXPORT_SYMBOL(lprocfs_write_frac_u64_helper);
2441 #endif /* LPROCFS*/