Whamcloud - gitweb
4a7fa3ea6c764ca91b1ac20e912a861f79bbc44b
[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  *  Copyright (C) 2002, 2003 Cluster File Systems, Inc.
5  *   Author: Hariharan Thantry <thantry@users.sourceforge.net>
6  *
7  *   This file is part of the Lustre file system, http://www.lustre.org
8  *   Lustre is a trademark of Cluster File Systems, Inc.
9  *
10  *   You may have signed or agreed to another license before downloading
11  *   this software.  If so, you are bound by the terms and conditions
12  *   of that agreement, and the following does not apply to you.  See the
13  *   LICENSE file included with this distribution for more information.
14  *
15  *   If you did not agree to a different license, then this copy of Lustre
16  *   is open source software; you can redistribute it and/or modify it
17  *   under the terms of version 2 of the GNU General Public License as
18  *   published by the Free Software Foundation.
19  *
20  *   In either case, Lustre is distributed in the hope that it will be
21  *   useful, but WITHOUT ANY WARRANTY; without even the implied warranty
22  *   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  *   license text for more details.
24  */
25
26 #ifndef EXPORT_SYMTAB
27 # define EXPORT_SYMTAB
28 #endif
29 #define DEBUG_SUBSYSTEM S_CLASS
30
31 #ifndef __KERNEL__
32 # include <liblustre.h>
33 #endif
34
35 #include <obd_class.h>
36 #include <lprocfs_status.h>
37 #include <lustre_fsfilt.h>
38
39 #if defined(LPROCFS)
40
41 /* for bug 10866, global variable */
42 DECLARE_RWSEM(_lprocfs_lock);
43 EXPORT_SYMBOL(_lprocfs_lock);
44
45 int lprocfs_seq_release(struct inode *inode, struct file *file)
46 {
47         LPROCFS_EXIT();
48         return seq_release(inode, file);
49 }
50 EXPORT_SYMBOL(lprocfs_seq_release);
51
52 struct proc_dir_entry *lprocfs_srch(struct proc_dir_entry *head,
53                                     const char *name)
54 {
55         struct proc_dir_entry *temp;
56
57         if (head == NULL)
58                 return NULL;
59
60         temp = head->subdir;
61         while (temp != NULL) {
62                 if (strcmp(temp->name, name) == 0)
63                         return temp;
64
65                 temp = temp->next;
66         }
67         return NULL;
68 }
69
70 /* lprocfs API calls */
71
72 /* Function that emulates snprintf but also has the side effect of advancing
73    the page pointer for the next write into the buffer, incrementing the total
74    length written to the buffer, and decrementing the size left in the
75    buffer. */
76 static int lprocfs_obd_snprintf(char **page, int end, int *len,
77                                 const char *format, ...)
78 {
79         va_list list;
80         int n;
81
82         if (*len >= end)
83                 return 0;
84
85         va_start(list, format);
86         n = vsnprintf(*page, end - *len, format, list);
87         va_end(list);
88
89         *page += n; *len += n;
90         return n;
91 }
92
93 int lprocfs_add_simple(struct proc_dir_entry *root, char *name,
94                        read_proc_t *read_proc, write_proc_t *write_proc,
95                        void *data)
96 {
97         struct proc_dir_entry *proc;
98         mode_t mode = 0;
99         
100         if (root == NULL || name == NULL)
101                 return -EINVAL;
102         if (read_proc)
103                 mode = 0444;
104         if (write_proc)
105                 mode |= 0200;
106         proc = create_proc_entry(name, mode, root);
107         if (!proc) {
108                 CERROR("LprocFS: No memory to create /proc entry %s", name);
109                 return -ENOMEM;
110         }
111         proc->read_proc = read_proc;
112         proc->write_proc = write_proc;
113         proc->data = data;
114         return 0;
115 }
116
117 static ssize_t lprocfs_fops_read(struct file *f, char __user *buf,
118                                  size_t size, loff_t *ppos)
119 {
120         struct proc_dir_entry *dp = PDE(f->f_dentry->d_inode);
121         char *page, *start = NULL;
122         int rc = 0, eof = 1, count;
123
124         if (*ppos >= CFS_PAGE_SIZE)
125                 return 0;
126
127         page = (char *)__get_free_page(GFP_KERNEL);
128         if (page == NULL)
129                 return -ENOMEM;
130
131         LPROCFS_ENTRY();
132         OBD_FAIL_TIMEOUT(OBD_FAIL_LPROC_REMOVE, 10);
133         if (!dp->deleted && dp->read_proc)
134                 rc = dp->read_proc(page, &start, *ppos, CFS_PAGE_SIZE, 
135                         &eof, dp->data);
136         LPROCFS_EXIT();
137         if (rc <= 0)
138                 goto out;
139
140         /* for lustre proc read, the read count must be less than PAGE_SIZE */
141         LASSERT(eof == 1);
142
143         if (start == NULL) {
144                 rc -= *ppos;
145                 if (rc < 0)
146                         rc = 0;
147                 if (rc == 0)
148                         goto out;
149                 start = page + *ppos;
150         } else if (start < page) {
151                 start = page;
152         }
153
154         count = (rc < size) ? rc : size;
155         if (copy_to_user(buf, start, count)) {
156                 rc = -EFAULT;
157                 goto out;
158         }
159         *ppos += count;
160
161 out:
162         free_page((unsigned long)page);
163         return rc;
164 }
165
166 static ssize_t lprocfs_fops_write(struct file *f, const char __user *buf, size_t size, loff_t *ppos)
167 {
168         struct proc_dir_entry *dp = PDE(f->f_dentry->d_inode);
169         int rc = 0;
170
171         LPROCFS_ENTRY();
172         if (!dp->deleted && dp->write_proc)
173                 rc = dp->write_proc(f, buf, size, dp->data);
174         LPROCFS_EXIT();
175         return rc;
176 }
177
178 static struct file_operations lprocfs_generic_fops = {
179         .owner = THIS_MODULE,
180         .read = lprocfs_fops_read,
181         .write = lprocfs_fops_write,
182 };
183
184 int lprocfs_add_vars(struct proc_dir_entry *root, struct lprocfs_vars *list,
185                      void *data)
186 {
187         if (root == NULL || list == NULL)
188                 return -EINVAL;
189
190         while (list->name != NULL) {
191                 struct proc_dir_entry *cur_root, *proc;
192                 char *pathcopy, *cur, *next, pathbuf[64];
193                 int pathsize = strlen(list->name) + 1;
194
195                 proc = NULL;
196                 cur_root = root;
197
198                 /* need copy of path for strsep */
199                 if (strlen(list->name) > sizeof(pathbuf) - 1) {
200                         OBD_ALLOC(pathcopy, pathsize);
201                         if (pathcopy == NULL)
202                                 return -ENOMEM;
203                 } else {
204                         pathcopy = pathbuf;
205                 }
206
207                 next = pathcopy;
208                 strcpy(pathcopy, list->name);
209
210                 while (cur_root != NULL && (cur = strsep(&next, "/"))) {
211                         if (*cur =='\0') /* skip double/trailing "/" */
212                                 continue;
213
214                         proc = lprocfs_srch(cur_root, cur);
215                         CDEBUG(D_OTHER, "cur_root=%s, cur=%s, next=%s, (%s)\n",
216                                cur_root->name, cur, next,
217                                (proc ? "exists" : "new"));
218                         if (next != NULL) {
219                                 cur_root = (proc ? proc :
220                                             proc_mkdir(cur, cur_root));
221                         } else if (proc == NULL) {
222                                 mode_t mode = 0;
223                                 if (list->read_fptr)
224                                         mode = 0444;
225                                 if (list->write_fptr)
226                                         mode |= 0200;
227                                 proc = create_proc_entry(cur, mode, cur_root);
228                         }
229                 }
230
231                 if (pathcopy != pathbuf)
232                         OBD_FREE(pathcopy, pathsize);
233
234                 if (cur_root == NULL || proc == NULL) {
235                         CERROR("LprocFS: No memory to create /proc entry %s",
236                                list->name);
237                         return -ENOMEM;
238                 }
239
240                 proc->proc_fops = &lprocfs_generic_fops;
241                 proc->read_proc = list->read_fptr;
242                 proc->write_proc = list->write_fptr;
243                 proc->data = (list->data ? list->data : data);
244                 list++;
245         }
246         return 0;
247 }
248
249 void lprocfs_remove(struct proc_dir_entry **rooth)
250 {
251         struct proc_dir_entry *root = *rooth;
252         struct proc_dir_entry *temp = root;
253         struct proc_dir_entry *rm_entry;
254         struct proc_dir_entry *parent;
255
256         if (!root) 
257                 return;
258         *rooth = NULL;
259
260         parent = root->parent;
261         LASSERT(parent != NULL);
262  
263         while (1) {
264                 while (temp->subdir != NULL)
265                         temp = temp->subdir;
266
267                 rm_entry = temp;
268                 temp = temp->parent;
269
270                 /* Memory corruption once caused this to fail, and
271                    without this LASSERT we would loop here forever. */
272                 LASSERTF(strlen(rm_entry->name) == rm_entry->namelen,
273                          "0x%p  %s/%s len %d\n", rm_entry, temp->name,
274                          rm_entry->name, (int)strlen(rm_entry->name));
275
276                 /* Now, the rm_entry->deleted flags is protected 
277                  * by _lprocfs_lock. */
278                 down_write(&_lprocfs_lock);
279                 rm_entry->data = NULL;
280                 remove_proc_entry(rm_entry->name, rm_entry->parent);
281                 up_write(&_lprocfs_lock);
282                 if (temp == parent)
283                         break;
284         }
285 }
286
287 struct proc_dir_entry *lprocfs_register(const char *name,
288                                         struct proc_dir_entry *parent,
289                                         struct lprocfs_vars *list, void *data)
290 {
291         struct proc_dir_entry *newchild;
292
293         newchild = lprocfs_srch(parent, name);
294         if (newchild != NULL) {
295                 CERROR(" Lproc: Attempting to register %s more than once \n",
296                        name);
297                 return ERR_PTR(-EALREADY);
298         }
299
300         newchild = proc_mkdir(name, parent);
301         if (newchild != NULL && list != NULL) {
302                 int rc = lprocfs_add_vars(newchild, list, data);
303                 if (rc) {
304                         lprocfs_remove(&newchild);
305                         return ERR_PTR(rc);
306                 }
307         }
308         return newchild;
309 }
310
311 /* Generic callbacks */
312
313 int lprocfs_rd_u64(char *page, char **start, off_t off,
314                    int count, int *eof, void *data)
315 {
316         LASSERT(data != NULL);
317         *eof = 1;
318         return snprintf(page, count, LPU64"\n", *(__u64 *)data);
319 }
320
321 int lprocfs_rd_atomic(char *page, char **start, off_t off,
322                    int count, int *eof, void *data)
323 {
324         atomic_t *atom = (atomic_t *)data;
325         LASSERT(atom != NULL);
326         *eof = 1;
327         return snprintf(page, count, "%d\n", atomic_read(atom));
328 }
329
330 int lprocfs_rd_uuid(char *page, char **start, off_t off, int count,
331                     int *eof, void *data)
332 {
333         struct obd_device *obd = (struct obd_device*)data;
334
335         LASSERT(obd != NULL);
336         *eof = 1;
337         return snprintf(page, count, "%s\n", obd->obd_uuid.uuid);
338 }
339
340 int lprocfs_rd_name(char *page, char **start, off_t off, int count,
341                     int *eof, void* data)
342 {
343         struct obd_device *dev = (struct obd_device *)data;
344
345         LASSERT(dev != NULL);
346         LASSERT(dev->obd_name != NULL);
347         *eof = 1;
348         return snprintf(page, count, "%s\n", dev->obd_name);
349 }
350
351 int lprocfs_rd_fstype(char *page, char **start, off_t off, int count, int *eof,
352                       void *data)
353 {
354         struct obd_device *obd = (struct obd_device *)data;
355
356         LASSERT(obd != NULL);
357         LASSERT(obd->obd_fsops != NULL);
358         LASSERT(obd->obd_fsops->fs_type != NULL);
359         return snprintf(page, count, "%s\n", obd->obd_fsops->fs_type);
360 }
361
362 int lprocfs_rd_blksize(char *page, char **start, off_t off, int count,
363                        int *eof, void *data)
364 {
365         struct obd_statfs osfs;
366         int rc = obd_statfs(data, &osfs, cfs_time_current_64() - HZ);
367         if (!rc) {
368                 *eof = 1;
369                 rc = snprintf(page, count, "%u\n", osfs.os_bsize);
370         }
371         return rc;
372 }
373
374 int lprocfs_rd_kbytestotal(char *page, char **start, off_t off, int count,
375                            int *eof, void *data)
376 {
377         struct obd_statfs osfs;
378         int rc = obd_statfs(data, &osfs, cfs_time_current_64() - HZ);
379         if (!rc) {
380                 __u32 blk_size = osfs.os_bsize >> 10;
381                 __u64 result = osfs.os_blocks;
382
383                 while (blk_size >>= 1)
384                         result <<= 1;
385
386                 *eof = 1;
387                 rc = snprintf(page, count, LPU64"\n", result);
388         }
389         return rc;
390 }
391
392 int lprocfs_rd_kbytesfree(char *page, char **start, off_t off, int count,
393                           int *eof, void *data)
394 {
395         struct obd_statfs osfs;
396         int rc = obd_statfs(data, &osfs, cfs_time_current_64() - HZ);
397         if (!rc) {
398                 __u32 blk_size = osfs.os_bsize >> 10;
399                 __u64 result = osfs.os_bfree;
400
401                 while (blk_size >>= 1)
402                         result <<= 1;
403
404                 *eof = 1;
405                 rc = snprintf(page, count, LPU64"\n", result);
406         }
407         return rc;
408 }
409
410 int lprocfs_rd_kbytesavail(char *page, char **start, off_t off, int count,
411                            int *eof, void *data)
412 {
413         struct obd_statfs osfs;
414         int rc = obd_statfs(data, &osfs, cfs_time_current_64() - HZ);
415         if (!rc) {
416                 __u32 blk_size = osfs.os_bsize >> 10;
417                 __u64 result = osfs.os_bavail;
418
419                 while (blk_size >>= 1)
420                         result <<= 1;
421
422                 *eof = 1;
423                 rc = snprintf(page, count, LPU64"\n", result);
424         }
425         return rc;
426 }
427
428 int lprocfs_rd_filestotal(char *page, char **start, off_t off, int count,
429                           int *eof, void *data)
430 {
431         struct obd_statfs osfs;
432         int rc = obd_statfs(data, &osfs, cfs_time_current_64() - HZ);
433         if (!rc) {
434                 *eof = 1;
435                 rc = snprintf(page, count, LPU64"\n", osfs.os_files);
436         }
437
438         return rc;
439 }
440
441 int lprocfs_rd_filesfree(char *page, char **start, off_t off, int count,
442                          int *eof, void *data)
443 {
444         struct obd_statfs osfs;
445         int rc = obd_statfs(data, &osfs, cfs_time_current_64() - HZ);
446         if (!rc) {
447                 *eof = 1;
448                 rc = snprintf(page, count, LPU64"\n", osfs.os_ffree);
449         }
450         return rc;
451 }
452
453 int lprocfs_rd_server_uuid(char *page, char **start, off_t off, int count,
454                            int *eof, void *data)
455 {
456         struct obd_device *obd = (struct obd_device *)data;
457         struct obd_import *imp;
458         char *imp_state_name = NULL;
459         int rc = 0;
460
461         LASSERT(obd != NULL);
462         LPROCFS_CLIMP_CHECK(obd);
463         imp = obd->u.cli.cl_import;
464         imp_state_name = ptlrpc_import_state_name(imp->imp_state);
465         *eof = 1;
466         rc = snprintf(page, count, "%s\t%s%s\n",
467                       obd2cli_tgt(obd), imp_state_name,
468                       imp->imp_deactive ? "\tDEACTIVATED" : "");
469
470         LPROCFS_CLIMP_EXIT(obd);
471         return rc;
472 }
473
474 int lprocfs_rd_conn_uuid(char *page, char **start, off_t off, int count,
475                          int *eof,  void *data)
476 {
477         struct obd_device *obd = (struct obd_device*)data;
478         struct ptlrpc_connection *conn;
479         int rc = 0;
480
481         LASSERT(obd != NULL);
482
483         LPROCFS_CLIMP_CHECK(obd);
484         conn = obd->u.cli.cl_import->imp_connection;
485         LASSERT(conn != NULL);
486         *eof = 1;
487         if (obd->u.cli.cl_import) {
488                 rc = snprintf(page, count, "%s\n",
489                               conn->c_remote_uuid.uuid);
490         } else {
491                 rc = snprintf(page, count, "%s\n", "<none>");
492         }
493
494         LPROCFS_CLIMP_EXIT(obd);
495         return rc;
496 }
497
498 static const char *obd_connect_names[] = {
499         "read_only",
500         "lov_index",
501         "unused",
502         "write_grant",
503         "server_lock",
504         "version",
505         "request_portal",
506         "acl",
507         "xattr",
508         "real_conn",
509         "truncate_lock",
510         "obsoleted",
511         "inode_bit_locks",
512         "join_file",
513         "getattr_by_fid",
514         "no_oh_for_devices",
515         "local_client",
516         "remote_client",
517         "max_byte_per_rpc",
518         "64bit_qdata",
519         "mds_capability",
520         "oss_capability",
521         "mds_mds_connection",
522         "size_on_mds",
523         NULL
524 };
525
526 int lprocfs_rd_connect_flags(char *page, char **start, off_t off,
527                              int count, int *eof, void *data)
528 {
529         struct obd_device *obd = data;
530         __u64 mask = 1, flags;
531         int i, ret = 0;
532
533         LPROCFS_CLIMP_CHECK(obd);
534         flags = obd->u.cli.cl_import->imp_connect_data.ocd_connect_flags;
535         ret = snprintf(page, count, "flags="LPX64"\n", flags);
536         for (i = 0; obd_connect_names[i] != NULL; i++, mask <<= 1) {
537                 if (flags & mask)
538                         ret += snprintf(page + ret, count - ret, "%s\n",
539                                         obd_connect_names[i]);
540         }
541         if (flags & ~(mask - 1))
542                 ret += snprintf(page + ret, count - ret,
543                                 "unknown flags "LPX64"\n", flags & ~(mask - 1));
544
545         LPROCFS_CLIMP_EXIT(obd);
546         return ret;
547 }
548 EXPORT_SYMBOL(lprocfs_rd_connect_flags);
549
550 int lprocfs_rd_num_exports(char *page, char **start, off_t off, int count,
551                            int *eof,  void *data)
552 {
553         struct obd_device *obd = (struct obd_device*)data;
554
555         LASSERT(obd != NULL);
556         *eof = 1;
557         return snprintf(page, count, "%u\n", obd->obd_num_exports);
558 }
559
560 int lprocfs_rd_numrefs(char *page, char **start, off_t off, int count,
561                        int *eof, void *data)
562 {
563         struct obd_type *class = (struct obd_type*) data;
564
565         LASSERT(class != NULL);
566         *eof = 1;
567         return snprintf(page, count, "%d\n", class->typ_refcnt);
568 }
569
570 int lprocfs_obd_setup(struct obd_device *obd, struct lprocfs_vars *list)
571 {
572         int rc = 0;
573
574         LASSERT(obd != NULL);
575         LASSERT(obd->obd_magic == OBD_DEVICE_MAGIC);
576         LASSERT(obd->obd_type->typ_procroot != NULL);
577
578         obd->obd_proc_entry = lprocfs_register(obd->obd_name,
579                                                obd->obd_type->typ_procroot,
580                                                list, obd);
581         if (IS_ERR(obd->obd_proc_entry)) {
582                 rc = PTR_ERR(obd->obd_proc_entry);
583                 CERROR("error %d setting up lprocfs for %s\n",rc,obd->obd_name);
584                 obd->obd_proc_entry = NULL;
585         }
586         return rc;
587 }
588
589 int lprocfs_obd_cleanup(struct obd_device *obd)
590 {
591         if (!obd) 
592                 return -EINVAL;
593         if (obd->obd_proc_exports) {
594                 /* Should be no exports left */
595                 LASSERT(obd->obd_proc_exports->subdir == NULL);
596                 lprocfs_remove(&obd->obd_proc_exports);
597         }
598         lprocfs_remove(&obd->obd_proc_entry);
599         return 0;
600 }
601
602 struct lprocfs_stats *lprocfs_alloc_stats(unsigned int num)
603 {
604         struct lprocfs_stats *stats;
605         struct lprocfs_percpu *percpu;
606         unsigned int percpusize;
607         unsigned int i;
608
609         if (num == 0)
610                 return NULL;
611
612         OBD_ALLOC(stats, offsetof(typeof(*stats), ls_percpu[num_online_cpus()]));
613         if (stats == NULL)
614                 return NULL;
615
616         percpusize = L1_CACHE_ALIGN(offsetof(typeof(*percpu), lp_cntr[num]));
617         stats->ls_percpu_size = num_online_cpus() * percpusize;
618         OBD_ALLOC(stats->ls_percpu[0], stats->ls_percpu_size);
619         if (stats->ls_percpu[0] == NULL) {
620                 OBD_FREE(stats, offsetof(typeof(*stats),
621                                          ls_percpu[num_online_cpus()]));
622                 return NULL;
623         }
624
625         stats->ls_num = num;
626         for (i = 1; i < num_online_cpus(); i++)
627                 stats->ls_percpu[i] = (void *)(stats->ls_percpu[i - 1]) +
628                         percpusize;
629
630         return stats;
631 }
632
633 void lprocfs_free_stats(struct lprocfs_stats **statsh)
634 {
635         struct lprocfs_stats *stats = *statsh;
636
637         if (stats == NULL || stats->ls_num == 0)
638                 return;
639         *statsh = NULL;
640
641         OBD_FREE(stats->ls_percpu[0], stats->ls_percpu_size);
642         OBD_FREE(stats, offsetof(typeof(*stats), ls_percpu[num_online_cpus()]));
643 }
644
645 void lprocfs_clear_stats(struct lprocfs_stats *stats)
646 {
647         struct lprocfs_counter *percpu_cntr;
648         int i,j;
649
650         for (i = 0; i < num_online_cpus(); i++) {
651                 for (j = 0; j < stats->ls_num; j++) {        
652                         percpu_cntr = &(stats->ls_percpu[i])->lp_cntr[j];
653                         atomic_inc(&percpu_cntr->lc_cntl.la_entry);
654                         percpu_cntr->lc_count = 0;
655                         percpu_cntr->lc_sum = 0;
656                         percpu_cntr->lc_min = ~(__u64)0;
657                         percpu_cntr->lc_max = 0;
658                         percpu_cntr->lc_sumsquare = 0;
659                         atomic_inc(&percpu_cntr->lc_cntl.la_exit);
660                 }
661         }
662 }
663
664 static ssize_t lprocfs_stats_seq_write(struct file *file, const char *buf,
665                                        size_t len, loff_t *off)
666 {
667         struct seq_file *seq = file->private_data;
668         struct lprocfs_stats *stats = seq->private;
669
670         lprocfs_clear_stats(stats);
671
672         return len;
673 }
674
675 static void *lprocfs_stats_seq_start(struct seq_file *p, loff_t *pos)
676 {
677         struct lprocfs_stats *stats = p->private;
678         /* return 1st cpu location */
679         return (*pos >= stats->ls_num) ? NULL :
680                 &(stats->ls_percpu[0]->lp_cntr[*pos]);
681 }
682
683 static void lprocfs_stats_seq_stop(struct seq_file *p, void *v)
684 {
685 }
686
687 static void *lprocfs_stats_seq_next(struct seq_file *p, void *v, loff_t *pos)
688 {
689         struct lprocfs_stats *stats = p->private;
690         ++*pos;
691         return (*pos >= stats->ls_num) ? NULL :
692                 &(stats->ls_percpu[0]->lp_cntr[*pos]);
693 }
694
695 /* seq file export of one lprocfs counter */
696 static int lprocfs_stats_seq_show(struct seq_file *p, void *v)
697 {
698        struct lprocfs_stats *stats = p->private;
699        struct lprocfs_counter  *cntr = v;
700        struct lprocfs_counter  t, ret = { .lc_min = ~(__u64)0 };
701        int i, idx, rc;
702
703        if (cntr == &(stats->ls_percpu[0])->lp_cntr[0]) {
704                struct timeval now;
705                do_gettimeofday(&now);
706                rc = seq_printf(p, "%-25s %lu.%lu secs.usecs\n",
707                                "snapshot_time", now.tv_sec, now.tv_usec);
708                if (rc < 0)
709                        return rc;
710        }
711        idx = cntr - &(stats->ls_percpu[0])->lp_cntr[0];
712
713        for (i = 0; i < num_online_cpus(); i++) {
714                struct lprocfs_counter *percpu_cntr =
715                        &(stats->ls_percpu[i])->lp_cntr[idx];
716                int centry;
717
718                do {
719                        centry = atomic_read(&percpu_cntr->lc_cntl.la_entry);
720                        t.lc_count = percpu_cntr->lc_count;
721                        t.lc_sum = percpu_cntr->lc_sum;
722                        t.lc_min = percpu_cntr->lc_min;
723                        t.lc_max = percpu_cntr->lc_max;
724                        t.lc_sumsquare = percpu_cntr->lc_sumsquare;
725                } while (centry != atomic_read(&percpu_cntr->lc_cntl.la_entry) &&
726                         centry != atomic_read(&percpu_cntr->lc_cntl.la_exit));
727                ret.lc_count += t.lc_count;
728                ret.lc_sum += t.lc_sum;
729                if (t.lc_min < ret.lc_min)
730                        ret.lc_min = t.lc_min;
731                if (t.lc_max > ret.lc_max)
732                        ret.lc_max = t.lc_max;
733                ret.lc_sumsquare += t.lc_sumsquare;
734        }
735
736        rc = seq_printf(p, "%-25s "LPU64" samples [%s]", cntr->lc_name,
737                        ret.lc_count, cntr->lc_units);
738        if (rc < 0)
739                goto out;
740
741        if ((cntr->lc_config & LPROCFS_CNTR_AVGMINMAX) && (ret.lc_count > 0)) {
742                rc = seq_printf(p, " "LPU64" "LPU64" "LPU64,
743                                ret.lc_min, ret.lc_max, ret.lc_sum);
744                if (rc < 0)
745                        goto out;
746                if (cntr->lc_config & LPROCFS_CNTR_STDDEV)
747                        rc = seq_printf(p, " "LPU64, ret.lc_sumsquare);
748                if (rc < 0)
749                        goto out;
750        }
751        rc = seq_printf(p, "\n");
752  out:
753        return (rc < 0) ? rc : 0;
754 }
755
756 struct seq_operations lprocfs_stats_seq_sops = {
757         start: lprocfs_stats_seq_start,
758         stop:  lprocfs_stats_seq_stop,
759         next:  lprocfs_stats_seq_next,
760         show:  lprocfs_stats_seq_show,
761 };
762
763 static int lprocfs_stats_seq_open(struct inode *inode, struct file *file)
764 {
765         struct proc_dir_entry *dp = PDE(inode);
766         struct seq_file *seq;
767         int rc;
768
769         LPROCFS_ENTRY_AND_CHECK(dp);
770         rc = seq_open(file, &lprocfs_stats_seq_sops);
771         if (rc) {
772                 LPROCFS_EXIT();
773                 return rc;
774         }
775         seq = file->private_data;
776         seq->private = dp->data;
777         return 0;
778 }
779
780 struct file_operations lprocfs_stats_seq_fops = {
781         .owner   = THIS_MODULE,
782         .open    = lprocfs_stats_seq_open,
783         .read    = seq_read,
784         .write   = lprocfs_stats_seq_write,
785         .llseek  = seq_lseek,
786         .release = lprocfs_seq_release,
787 };
788
789 int lprocfs_register_stats(struct proc_dir_entry *root, const char *name,
790                            struct lprocfs_stats *stats)
791 {
792         struct proc_dir_entry *entry;
793         LASSERT(root != NULL);
794
795         entry = create_proc_entry(name, 0644, root);
796         if (entry == NULL)
797                 return -ENOMEM;
798         entry->proc_fops = &lprocfs_stats_seq_fops;
799         entry->data = (void *)stats;
800         return 0;
801 }
802
803 void lprocfs_counter_init(struct lprocfs_stats *stats, int index,
804                           unsigned conf, const char *name, const char *units)
805 {
806         struct lprocfs_counter *c;
807         int i;
808
809         LASSERT(stats != NULL);
810         for (i = 0; i < num_online_cpus(); i++) {
811                 c = &(stats->ls_percpu[i]->lp_cntr[index]);
812                 c->lc_config = conf;
813                 c->lc_count = 0;
814                 c->lc_sum = 0;
815                 c->lc_min = ~(__u64)0;
816                 c->lc_max = 0;
817                 c->lc_name = name;
818                 c->lc_units = units;
819         }
820 }
821 EXPORT_SYMBOL(lprocfs_counter_init);
822
823 #define LPROCFS_OBD_OP_INIT(base, stats, op)                               \
824 do {                                                                       \
825         unsigned int coffset = base + OBD_COUNTER_OFFSET(op);              \
826         LASSERT(coffset < stats->ls_num);                                  \
827         lprocfs_counter_init(stats, coffset, 0, #op, "reqs");              \
828 } while (0)
829
830 void lprocfs_init_ops_stats(int num_private_stats, struct lprocfs_stats *stats)
831 {
832         LPROCFS_OBD_OP_INIT(num_private_stats, stats, iocontrol);
833         LPROCFS_OBD_OP_INIT(num_private_stats, stats, get_info);
834         LPROCFS_OBD_OP_INIT(num_private_stats, stats, set_info_async);
835         LPROCFS_OBD_OP_INIT(num_private_stats, stats, attach);
836         LPROCFS_OBD_OP_INIT(num_private_stats, stats, detach);
837         LPROCFS_OBD_OP_INIT(num_private_stats, stats, setup);
838         LPROCFS_OBD_OP_INIT(num_private_stats, stats, precleanup);
839         LPROCFS_OBD_OP_INIT(num_private_stats, stats, cleanup);
840         LPROCFS_OBD_OP_INIT(num_private_stats, stats, process_config);
841         LPROCFS_OBD_OP_INIT(num_private_stats, stats, postrecov);
842         LPROCFS_OBD_OP_INIT(num_private_stats, stats, add_conn);
843         LPROCFS_OBD_OP_INIT(num_private_stats, stats, del_conn);
844         LPROCFS_OBD_OP_INIT(num_private_stats, stats, connect);
845         LPROCFS_OBD_OP_INIT(num_private_stats, stats, reconnect);
846         LPROCFS_OBD_OP_INIT(num_private_stats, stats, disconnect);
847         LPROCFS_OBD_OP_INIT(num_private_stats, stats, fid_init);
848         LPROCFS_OBD_OP_INIT(num_private_stats, stats, fid_fini);
849         LPROCFS_OBD_OP_INIT(num_private_stats, stats, fid_alloc);
850         LPROCFS_OBD_OP_INIT(num_private_stats, stats, fid_delete);
851         LPROCFS_OBD_OP_INIT(num_private_stats, stats, statfs);
852         LPROCFS_OBD_OP_INIT(num_private_stats, stats, statfs_async);
853         LPROCFS_OBD_OP_INIT(num_private_stats, stats, packmd);
854         LPROCFS_OBD_OP_INIT(num_private_stats, stats, unpackmd);
855         LPROCFS_OBD_OP_INIT(num_private_stats, stats, checkmd);
856         LPROCFS_OBD_OP_INIT(num_private_stats, stats, preallocate);
857         LPROCFS_OBD_OP_INIT(num_private_stats, stats, precreate);
858         LPROCFS_OBD_OP_INIT(num_private_stats, stats, create);
859         LPROCFS_OBD_OP_INIT(num_private_stats, stats, destroy);
860         LPROCFS_OBD_OP_INIT(num_private_stats, stats, setattr);
861         LPROCFS_OBD_OP_INIT(num_private_stats, stats, setattr_async);
862         LPROCFS_OBD_OP_INIT(num_private_stats, stats, getattr);
863         LPROCFS_OBD_OP_INIT(num_private_stats, stats, getattr_async);
864         LPROCFS_OBD_OP_INIT(num_private_stats, stats, brw);
865         LPROCFS_OBD_OP_INIT(num_private_stats, stats, brw_async);
866         LPROCFS_OBD_OP_INIT(num_private_stats, stats, prep_async_page);
867         LPROCFS_OBD_OP_INIT(num_private_stats, stats, queue_async_io);
868         LPROCFS_OBD_OP_INIT(num_private_stats, stats, queue_group_io);
869         LPROCFS_OBD_OP_INIT(num_private_stats, stats, trigger_group_io);
870         LPROCFS_OBD_OP_INIT(num_private_stats, stats, set_async_flags);
871         LPROCFS_OBD_OP_INIT(num_private_stats, stats, teardown_async_page);
872         LPROCFS_OBD_OP_INIT(num_private_stats, stats, merge_lvb);
873         LPROCFS_OBD_OP_INIT(num_private_stats, stats, adjust_kms);
874         LPROCFS_OBD_OP_INIT(num_private_stats, stats, punch);
875         LPROCFS_OBD_OP_INIT(num_private_stats, stats, sync);
876         LPROCFS_OBD_OP_INIT(num_private_stats, stats, migrate);
877         LPROCFS_OBD_OP_INIT(num_private_stats, stats, copy);
878         LPROCFS_OBD_OP_INIT(num_private_stats, stats, iterate);
879         LPROCFS_OBD_OP_INIT(num_private_stats, stats, preprw);
880         LPROCFS_OBD_OP_INIT(num_private_stats, stats, commitrw);
881         LPROCFS_OBD_OP_INIT(num_private_stats, stats, enqueue);
882         LPROCFS_OBD_OP_INIT(num_private_stats, stats, match);
883         LPROCFS_OBD_OP_INIT(num_private_stats, stats, change_cbdata);
884         LPROCFS_OBD_OP_INIT(num_private_stats, stats, cancel);
885         LPROCFS_OBD_OP_INIT(num_private_stats, stats, cancel_unused);
886         LPROCFS_OBD_OP_INIT(num_private_stats, stats, join_lru);
887         LPROCFS_OBD_OP_INIT(num_private_stats, stats, init_export);
888         LPROCFS_OBD_OP_INIT(num_private_stats, stats, destroy_export);
889         LPROCFS_OBD_OP_INIT(num_private_stats, stats, extent_calc);
890         LPROCFS_OBD_OP_INIT(num_private_stats, stats, llog_init);
891         LPROCFS_OBD_OP_INIT(num_private_stats, stats, llog_connect);
892         LPROCFS_OBD_OP_INIT(num_private_stats, stats, llog_finish);
893         LPROCFS_OBD_OP_INIT(num_private_stats, stats, pin);
894         LPROCFS_OBD_OP_INIT(num_private_stats, stats, unpin);
895         LPROCFS_OBD_OP_INIT(num_private_stats, stats, import_event);
896         LPROCFS_OBD_OP_INIT(num_private_stats, stats, notify);
897         LPROCFS_OBD_OP_INIT(num_private_stats, stats, health_check);
898         LPROCFS_OBD_OP_INIT(num_private_stats, stats, quotacheck);
899         LPROCFS_OBD_OP_INIT(num_private_stats, stats, quotactl);
900         LPROCFS_OBD_OP_INIT(num_private_stats, stats, ping);
901 }
902
903 int lprocfs_alloc_obd_stats(struct obd_device *obd, unsigned num_private_stats)
904 {
905         struct lprocfs_stats *stats;
906         unsigned int num_stats;
907         int rc, i;
908
909         LASSERT(obd->obd_stats == NULL);
910         LASSERT(obd->obd_proc_entry != NULL);
911         LASSERT(obd->obd_cntr_base == 0);
912
913         num_stats = ((int)sizeof(*obd->obd_type->typ_dt_ops) / sizeof(void *)) +
914                 num_private_stats - 1 /* o_owner */;
915         stats = lprocfs_alloc_stats(num_stats);
916         if (stats == NULL)
917                 return -ENOMEM;
918
919         lprocfs_init_ops_stats(num_private_stats, stats);
920
921         for (i = num_private_stats; i < num_stats; i++) {
922                 /* If this LBUGs, it is likely that an obd
923                  * operation was added to struct obd_ops in
924                  * <obd.h>, and that the corresponding line item
925                  * LPROCFS_OBD_OP_INIT(.., .., opname)
926                  * is missing from the list above. */
927                 LASSERTF(stats->ls_percpu[0]->lp_cntr[i].lc_name != NULL,
928                          "Missing obd_stat initializer obd_op "
929                          "operation at offset %d.\n", i - num_private_stats);
930         }
931         rc = lprocfs_register_stats(obd->obd_proc_entry, "stats", stats);
932         if (rc < 0) {
933                 lprocfs_free_stats(&stats);
934         } else {
935                 obd->obd_stats  = stats;
936                 obd->obd_cntr_base = num_private_stats;
937         }
938         return rc;
939 }
940
941 void lprocfs_free_obd_stats(struct obd_device *obd)
942 {
943         if (obd->obd_stats) 
944                 lprocfs_free_stats(&obd->obd_stats);
945 }
946
947 #define LPROCFS_MD_OP_INIT(base, stats, op)                             \
948 do {                                                                    \
949         unsigned int coffset = base + MD_COUNTER_OFFSET(op);            \
950         LASSERT(coffset < stats->ls_num);                               \
951         lprocfs_counter_init(stats, coffset, 0, #op, "reqs");           \
952 } while (0)
953
954 int lprocfs_alloc_md_stats(struct obd_device *obd,
955                            unsigned num_private_stats)
956 {
957         struct lprocfs_stats *stats;
958         unsigned int num_stats;
959         int rc, i;
960
961         LASSERT(obd->md_stats == NULL);
962         LASSERT(obd->obd_proc_entry != NULL);
963         LASSERT(obd->md_cntr_base == 0);
964
965         num_stats = 1 + MD_COUNTER_OFFSET(get_remote_perm) +
966                     num_private_stats;
967         stats = lprocfs_alloc_stats(num_stats);
968         if (stats == NULL)
969                 return -ENOMEM;
970
971         LPROCFS_MD_OP_INIT(num_private_stats, stats, getstatus);
972         LPROCFS_MD_OP_INIT(num_private_stats, stats, change_cbdata);
973         LPROCFS_MD_OP_INIT(num_private_stats, stats, close);
974         LPROCFS_MD_OP_INIT(num_private_stats, stats, create);
975         LPROCFS_MD_OP_INIT(num_private_stats, stats, done_writing);
976         LPROCFS_MD_OP_INIT(num_private_stats, stats, enqueue);
977         LPROCFS_MD_OP_INIT(num_private_stats, stats, getattr);
978         LPROCFS_MD_OP_INIT(num_private_stats, stats, getattr_name);
979         LPROCFS_MD_OP_INIT(num_private_stats, stats, intent_lock);
980         LPROCFS_MD_OP_INIT(num_private_stats, stats, link);
981         LPROCFS_MD_OP_INIT(num_private_stats, stats, rename);
982         LPROCFS_MD_OP_INIT(num_private_stats, stats, is_subdir);
983         LPROCFS_MD_OP_INIT(num_private_stats, stats, setattr);
984         LPROCFS_MD_OP_INIT(num_private_stats, stats, sync);
985         LPROCFS_MD_OP_INIT(num_private_stats, stats, readpage);
986         LPROCFS_MD_OP_INIT(num_private_stats, stats, unlink);
987         LPROCFS_MD_OP_INIT(num_private_stats, stats, setxattr);
988         LPROCFS_MD_OP_INIT(num_private_stats, stats, getxattr);
989         LPROCFS_MD_OP_INIT(num_private_stats, stats, init_ea_size);
990         LPROCFS_MD_OP_INIT(num_private_stats, stats, get_lustre_md);
991         LPROCFS_MD_OP_INIT(num_private_stats, stats, free_lustre_md);
992         LPROCFS_MD_OP_INIT(num_private_stats, stats, set_open_replay_data);
993         LPROCFS_MD_OP_INIT(num_private_stats, stats, clear_open_replay_data);
994         LPROCFS_MD_OP_INIT(num_private_stats, stats, set_lock_data);
995         LPROCFS_MD_OP_INIT(num_private_stats, stats, lock_match);
996         LPROCFS_MD_OP_INIT(num_private_stats, stats, cancel_unused);
997         LPROCFS_MD_OP_INIT(num_private_stats, stats, renew_capa);
998         LPROCFS_MD_OP_INIT(num_private_stats, stats, get_remote_perm);
999
1000         for (i = num_private_stats; i < num_stats; i++) {
1001                 if (stats->ls_percpu[0]->lp_cntr[i].lc_name == NULL) {
1002                         CERROR("Missing md_stat initializer md_op "
1003                                "operation at offset %d. Aborting.\n",
1004                                i - num_private_stats);
1005                         LBUG();
1006                 }
1007         }
1008         rc = lprocfs_register_stats(obd->obd_proc_entry, "stats", stats);
1009         if (rc < 0) {
1010                 lprocfs_free_stats(&stats);
1011         } else {
1012                 obd->md_stats  = stats;
1013                 obd->md_cntr_base = num_private_stats;
1014         }
1015         return rc;
1016 }
1017
1018 void lprocfs_free_md_stats(struct obd_device *obd)
1019 {
1020         struct lprocfs_stats *stats = obd->md_stats;
1021
1022         if (stats != NULL) {
1023                 obd->md_stats = NULL;
1024                 lprocfs_free_stats(&stats);
1025         }
1026 }
1027
1028 int lprocfs_exp_rd_nid(char *page, char **start, off_t off, int count,
1029                          int *eof,  void *data)
1030 {
1031         struct obd_export *exp = (struct obd_export*)data;
1032         LASSERT(exp != NULL);
1033         *eof = 1;
1034         return snprintf(page, count, "%s\n", obd_export_nid2str(exp));
1035 }
1036
1037 int lprocfs_exp_rd_uuid(char *page, char **start, off_t off, int count,
1038                          int *eof,  void *data)
1039 {
1040         struct obd_export *exp = (struct obd_export*)data;
1041         LASSERT(exp != NULL);
1042         *eof = 1;
1043         return snprintf(page, count, "%s\n", 
1044                         obd_uuid2str(&exp->exp_client_uuid));
1045 }
1046         
1047 int lprocfs_exp_setup(struct obd_export *exp)
1048 {
1049         char name[sizeof (exp->exp_client_uuid.uuid) + 3];
1050         int i = 1, rc;
1051         ENTRY;
1052         if (!exp || !exp->exp_obd || !exp->exp_obd->obd_proc_exports)
1053                 RETURN(-EINVAL);
1054         mutex_down(&exp->exp_obd->obd_proc_exp_sem);
1055         sprintf(name, "%s", (char *)exp->exp_client_uuid.uuid);
1056         while (lprocfs_srch(exp->exp_obd->obd_proc_exports, name)) {
1057                 /* We might add a new export before deleting the old one during 
1058                    an eviction (recovery-small 19a). Suckage. We
1059                    could block, or come up with a new name, or just give up. */
1060                 if (++i > 9) 
1061                         GOTO(out, rc = -EEXIST);
1062                 sprintf(name, "%s:%d", (char *)exp->exp_client_uuid.uuid, i);
1063         }
1064
1065         /* Create a proc entry for this export */
1066         exp->exp_proc = proc_mkdir(name, exp->exp_obd->obd_proc_exports);
1067         if (!exp->exp_proc) {
1068                 CERROR("Error making export directory for %s\n", name);
1069                 GOTO(out, rc = -ENOMEM);
1070         }
1071
1072         /* Always add nid and uuid */
1073         rc = lprocfs_add_simple(exp->exp_proc, "nid",
1074                                 lprocfs_exp_rd_nid, NULL, exp);
1075         if (rc)
1076                 GOTO(out, rc);
1077         rc = lprocfs_add_simple(exp->exp_proc, "uuid",
1078                                 lprocfs_exp_rd_uuid, NULL, exp);
1079         if (rc)
1080                 GOTO(out, rc);
1081         /* Always add ldlm stats */
1082         exp->exp_ldlm_stats = lprocfs_alloc_stats(LDLM_LAST_OPC 
1083                                                   - LDLM_FIRST_OPC);
1084         if (exp->exp_ldlm_stats == NULL) {
1085                 lprocfs_remove(&exp->exp_proc);
1086                 GOTO(out, rc = -ENOMEM);
1087         }
1088
1089         lprocfs_counter_init(exp->exp_ldlm_stats, 
1090                              LDLM_ENQUEUE - LDLM_FIRST_OPC,
1091                              0, "ldlm_enqueue", "reqs");
1092         lprocfs_counter_init(exp->exp_ldlm_stats, 
1093                              LDLM_CONVERT - LDLM_FIRST_OPC,
1094                              0, "ldlm_convert", "reqs");
1095         lprocfs_counter_init(exp->exp_ldlm_stats, 
1096                              LDLM_CANCEL - LDLM_FIRST_OPC,
1097                              0, "ldlm_cancel", "reqs");
1098         lprocfs_counter_init(exp->exp_ldlm_stats, 
1099                              LDLM_BL_CALLBACK - LDLM_FIRST_OPC,
1100                              0, "ldlm_bl_callback", "reqs");
1101         lprocfs_counter_init(exp->exp_ldlm_stats, 
1102                              LDLM_CP_CALLBACK - LDLM_FIRST_OPC,
1103                              0, "ldlm_cp_callback", "reqs");
1104         lprocfs_counter_init(exp->exp_ldlm_stats, 
1105                              LDLM_GL_CALLBACK - LDLM_FIRST_OPC,
1106                              0, "ldlm_gl_callback", "reqs");
1107         lprocfs_register_stats(exp->exp_proc, "ldlm_stats",
1108                                exp->exp_ldlm_stats);
1109 out:
1110         mutex_up(&exp->exp_obd->obd_proc_exp_sem);
1111         RETURN(rc);
1112 }
1113
1114 int lprocfs_exp_cleanup(struct obd_export *exp)
1115 {
1116         mutex_down(&exp->exp_obd->obd_proc_exp_sem);
1117         lprocfs_remove(&exp->exp_proc);
1118         lprocfs_free_stats(&exp->exp_ops_stats);
1119         lprocfs_free_stats(&exp->exp_ldlm_stats);
1120         mutex_up(&exp->exp_obd->obd_proc_exp_sem);
1121         return 0;
1122 }
1123
1124 int lprocfs_write_helper(const char *buffer, unsigned long count,
1125                          int *val)
1126 {
1127         return lprocfs_write_frac_helper(buffer, count, val, 1);
1128 }
1129
1130 int lprocfs_write_frac_helper(const char *buffer, unsigned long count,
1131                               int *val, int mult)
1132 {
1133         char kernbuf[20], *end, *pbuf;
1134
1135         if (count > (sizeof(kernbuf) - 1))
1136                 return -EINVAL;
1137
1138         if (copy_from_user(kernbuf, buffer, count))
1139                 return -EFAULT;
1140
1141         kernbuf[count] = '\0';
1142         pbuf = kernbuf;
1143         if (*pbuf == '-') {
1144                 mult = -mult;
1145                 pbuf++;
1146         }
1147
1148         *val = (int)simple_strtoul(pbuf, &end, 10) * mult;
1149         if (pbuf == end)
1150                 return -EINVAL;
1151
1152         if (end != NULL && *end == '.') {
1153                 int temp_val, pow = 1;
1154                 int i;
1155
1156                 pbuf = end + 1;
1157                 if (strlen(pbuf) > 5)
1158                         pbuf[5] = '\0'; /*only allow 5bits fractional*/
1159
1160                 temp_val = (int)simple_strtoul(pbuf, &end, 10) * mult;
1161
1162                 if (pbuf < end) {
1163                         for (i = 0; i < (end - pbuf); i++)
1164                                 pow *= 10;
1165
1166                         *val += temp_val / pow;
1167                 }
1168         }
1169         return 0;
1170 }
1171
1172 int lprocfs_read_frac_helper(char *buffer, unsigned long count, long val, int mult)
1173 {
1174         long decimal_val, frac_val;
1175         int prtn;
1176
1177         if (count < 10)
1178                 return -EINVAL;
1179
1180         decimal_val = val / mult;
1181         prtn = snprintf(buffer, count, "%ld", decimal_val);
1182         frac_val = val % mult;
1183
1184         if (prtn < (count - 4) && frac_val > 0) {
1185                 long temp_frac;
1186                 int i, temp_mult = 1, frac_bits = 0;
1187
1188                 temp_frac = frac_val * 10;
1189                 buffer[prtn++] = '.';
1190                 while (frac_bits < 2 && (temp_frac / mult) < 1 ) { /*only reserved 2bits fraction*/
1191                         buffer[prtn++] ='0';
1192                         temp_frac *= 10;
1193                         frac_bits++;
1194                 }
1195                 /*
1196                   Need to think these cases :
1197                         1. #echo x.00 > /proc/xxx       output result : x
1198                         2. #echo x.0x > /proc/xxx       output result : x.0x
1199                         3. #echo x.x0 > /proc/xxx       output result : x.x
1200                         4. #echo x.xx > /proc/xxx       output result : x.xx
1201                         Only reserved 2bits fraction.       
1202                  */
1203                 for (i = 0; i < (5 - prtn); i++)
1204                         temp_mult *= 10;
1205
1206                 frac_bits = min((int)count - prtn, 3 - frac_bits);
1207                 prtn += snprintf(buffer + prtn, frac_bits, "%ld", frac_val * temp_mult / mult);
1208
1209                 prtn--;
1210                 while(buffer[prtn] < '1' || buffer[prtn] > '9') {
1211                         prtn--;
1212                         if (buffer[prtn] == '.') {
1213                                 prtn--;
1214                                 break;
1215                         }
1216                 }
1217                 prtn++;
1218         }
1219         buffer[prtn++] ='\n';
1220         return prtn;
1221 }
1222
1223 int lprocfs_write_u64_helper(const char *buffer, unsigned long count,__u64 *val)
1224 {
1225         return lprocfs_write_frac_u64_helper(buffer, count, val, 1);
1226 }
1227
1228 int lprocfs_write_frac_u64_helper(const char *buffer, unsigned long count,
1229                               __u64 *val, int mult)
1230 {
1231         char kernbuf[22], *end, *pbuf;
1232         __u64 whole, frac = 0, units;
1233         unsigned frac_d = 1;
1234
1235         if (count > (sizeof(kernbuf) - 1) )
1236                 return -EINVAL;
1237
1238         if (copy_from_user(kernbuf, buffer, count))
1239                 return -EFAULT;
1240
1241         kernbuf[count] = '\0';
1242         pbuf = kernbuf;
1243         if (*pbuf == '-') {
1244                 mult = -mult;
1245                 pbuf++;
1246         }
1247
1248         whole = simple_strtoull(pbuf, &end, 10);
1249         if (pbuf == end)
1250                 return -EINVAL;
1251
1252         if (end != NULL && *end == '.') {
1253                 int i;
1254                 pbuf = end + 1;
1255
1256                 /* need to limit frac_d to a __u32 */
1257                 if (strlen(pbuf) > 10)
1258                         pbuf[10] = '\0';
1259
1260                 frac = simple_strtoull(pbuf, &end, 10);
1261                 /* count decimal places */
1262                 for (i = 0; i < (end - pbuf); i++)
1263                         frac_d *= 10;
1264         }
1265
1266         units = 1;
1267         switch(*end) {
1268         case 'p': case 'P':
1269                 units <<= 10;
1270         case 't': case 'T':
1271                 units <<= 10;
1272         case 'g': case 'G':
1273                 units <<= 10;
1274         case 'm': case 'M':
1275                 units <<= 10;
1276         case 'k': case 'K':
1277                 units <<= 10;
1278         }
1279         /* Specified units override the multiplier */
1280         if (units) 
1281                 mult = mult < 0 ? -units : units;
1282
1283         frac *= mult;
1284         do_div(frac, frac_d);
1285         *val = whole * mult + frac;
1286         return 0;
1287 }
1288
1289 int lprocfs_seq_create(cfs_proc_dir_entry_t *parent, 
1290                        char *name, mode_t mode,
1291                        struct file_operations *seq_fops, void *data)
1292 {
1293         struct proc_dir_entry *entry;
1294         ENTRY;
1295
1296         entry = create_proc_entry(name, mode, parent);
1297         if (entry == NULL)
1298                 RETURN(-ENOMEM);
1299         entry->proc_fops = seq_fops;
1300         entry->data = data;
1301
1302         RETURN(0);
1303 }
1304 EXPORT_SYMBOL(lprocfs_seq_create);
1305
1306 __inline__ int lprocfs_obd_seq_create(struct obd_device *dev, char *name,
1307                                       mode_t mode,
1308                                       struct file_operations *seq_fops,
1309                                       void *data)
1310 {
1311         return (lprocfs_seq_create(dev->obd_proc_entry, name, 
1312                                    mode, seq_fops, data));
1313 }
1314 EXPORT_SYMBOL(lprocfs_obd_seq_create);
1315
1316 void lprocfs_oh_tally(struct obd_histogram *oh, unsigned int value)
1317 {
1318         if (value >= OBD_HIST_MAX)
1319                 value = OBD_HIST_MAX - 1;
1320
1321         spin_lock(&oh->oh_lock);
1322         oh->oh_buckets[value]++;
1323         spin_unlock(&oh->oh_lock);
1324 }
1325 EXPORT_SYMBOL(lprocfs_oh_tally);
1326
1327 void lprocfs_oh_tally_log2(struct obd_histogram *oh, unsigned int value)
1328 {
1329         unsigned int val;
1330
1331         for (val = 0; ((1 << val) < value) && (val <= OBD_HIST_MAX); val++)
1332                 ;
1333
1334         lprocfs_oh_tally(oh, val);
1335 }
1336 EXPORT_SYMBOL(lprocfs_oh_tally_log2);
1337
1338 unsigned long lprocfs_oh_sum(struct obd_histogram *oh)
1339 {
1340         unsigned long ret = 0;
1341         int i;
1342
1343         for (i = 0; i < OBD_HIST_MAX; i++)
1344                 ret +=  oh->oh_buckets[i];
1345         return ret;
1346 }
1347 EXPORT_SYMBOL(lprocfs_oh_sum);
1348
1349 void lprocfs_oh_clear(struct obd_histogram *oh)
1350 {
1351         spin_lock(&oh->oh_lock);
1352         memset(oh->oh_buckets, 0, sizeof(oh->oh_buckets));
1353         spin_unlock(&oh->oh_lock);
1354 }
1355 EXPORT_SYMBOL(lprocfs_oh_clear);
1356
1357 int lprocfs_obd_rd_recovery_status(char *page, char **start, off_t off,
1358                                    int count, int *eof, void *data)
1359 {
1360         struct obd_device *obd = data;
1361         int len = 0, size;
1362
1363         LASSERT(obd != NULL);
1364         LASSERT(count >= 0);
1365
1366         /* Set start of user data returned to
1367            page + off since the user may have
1368            requested to read much smaller than
1369            what we need to read */
1370         *start = page + off;
1371
1372         /* We know we are allocated a page here.
1373            Also we know that this function will
1374            not need to write more than a page
1375            so we can truncate at CFS_PAGE_SIZE.  */
1376         size = min(count + (int)off + 1, (int)CFS_PAGE_SIZE);
1377
1378         /* Initialize the page */
1379         memset(page, 0, size);
1380
1381         if (lprocfs_obd_snprintf(&page, size, &len, "status: ") <= 0)
1382                 goto out;
1383
1384         if (obd->obd_max_recoverable_clients == 0) {
1385                 lprocfs_obd_snprintf(&page, size, &len, "INACTIVE\n");
1386                 goto fclose;
1387         }
1388
1389         /* sampled unlocked, but really... */
1390         if (obd->obd_recovering == 0) {
1391                 if (lprocfs_obd_snprintf(&page, size, &len, "COMPLETE\n") <= 0)
1392                         goto out;
1393
1394                 if (lprocfs_obd_snprintf(&page, size, &len, "recovery_start: %lu\n",
1395                     obd->obd_recovery_start) <= 0)
1396                         goto out;
1397
1398                 if (lprocfs_obd_snprintf(&page, size, &len, "recovery_end: %lu\n",
1399                     obd->obd_recovery_end) <= 0)
1400                         goto out;
1401
1402                 /* Number of clients have have completed recovery */
1403                 if (lprocfs_obd_snprintf(&page, size, &len, "recovered_clients: %d\n",
1404                     obd->obd_max_recoverable_clients - obd->obd_recoverable_clients) <= 0)
1405                         goto out;
1406
1407                 if (lprocfs_obd_snprintf(&page, size, &len, "unrecovered_clients: %d\n",
1408                     obd->obd_recoverable_clients) <= 0)
1409                         goto out;
1410
1411                 if (lprocfs_obd_snprintf(&page, size, &len, "last_transno: "LPD64"\n",
1412                     obd->obd_next_recovery_transno - 1) <= 0)
1413                         goto out;
1414
1415                 lprocfs_obd_snprintf(&page, size, &len, "replayed_requests: %d\n", obd->obd_replayed_requests);
1416                 goto fclose;
1417         }
1418
1419         if (lprocfs_obd_snprintf(&page, size, &len, "RECOVERING\n") <= 0)
1420                 goto out;
1421
1422         if (lprocfs_obd_snprintf(&page, size, &len, "recovery_start: %lu\n",
1423             obd->obd_recovery_start) <= 0)
1424                 goto out;
1425
1426         if (lprocfs_obd_snprintf(&page, size, &len, "time remaining: %lu\n",
1427                                  CURRENT_SECONDS >= obd->obd_recovery_end ? 0 :
1428                                  obd->obd_recovery_end - CURRENT_SECONDS) <= 0)
1429                 goto out;
1430
1431         if(lprocfs_obd_snprintf(&page, size, &len, "connected_clients: %d/%d\n",
1432                                 obd->obd_connected_clients,
1433                                 obd->obd_max_recoverable_clients) <= 0)
1434                 goto out;
1435
1436         /* Number of clients have have completed recovery */
1437         if (lprocfs_obd_snprintf(&page, size, &len, "completed_clients: %d/%d\n",
1438                                  obd->obd_max_recoverable_clients - obd->obd_recoverable_clients,
1439                                  obd->obd_max_recoverable_clients) <= 0)
1440                 goto out;
1441
1442         if (lprocfs_obd_snprintf(&page, size, &len, "replayed_requests: %d/??\n",
1443                                  obd->obd_replayed_requests) <= 0)
1444                 goto out;
1445
1446         if (lprocfs_obd_snprintf(&page, size, &len, "queued_requests: %d\n",
1447                                  obd->obd_requests_queued_for_recovery) <= 0)
1448                 goto out;
1449
1450         lprocfs_obd_snprintf(&page, size, &len, "next_transno: "LPD64"\n", obd->obd_next_recovery_transno);
1451
1452 fclose:
1453         *eof = 1;
1454 out:
1455         return min(count, len - (int)off);
1456 }
1457 EXPORT_SYMBOL(lprocfs_obd_rd_recovery_status);
1458
1459 EXPORT_SYMBOL(lprocfs_register);
1460 EXPORT_SYMBOL(lprocfs_srch);
1461 EXPORT_SYMBOL(lprocfs_remove);
1462 EXPORT_SYMBOL(lprocfs_add_vars);
1463 EXPORT_SYMBOL(lprocfs_obd_setup);
1464 EXPORT_SYMBOL(lprocfs_obd_cleanup);
1465 EXPORT_SYMBOL(lprocfs_alloc_stats);
1466 EXPORT_SYMBOL(lprocfs_free_stats);
1467 EXPORT_SYMBOL(lprocfs_clear_stats);
1468 EXPORT_SYMBOL(lprocfs_register_stats);
1469 EXPORT_SYMBOL(lprocfs_init_ops_stats);
1470 EXPORT_SYMBOL(lprocfs_alloc_obd_stats);
1471 EXPORT_SYMBOL(lprocfs_free_obd_stats);
1472 EXPORT_SYMBOL(lprocfs_exp_setup);
1473 EXPORT_SYMBOL(lprocfs_exp_cleanup);
1474
1475 EXPORT_SYMBOL(lprocfs_rd_u64);
1476 EXPORT_SYMBOL(lprocfs_rd_atomic);
1477 EXPORT_SYMBOL(lprocfs_rd_uuid);
1478 EXPORT_SYMBOL(lprocfs_rd_name);
1479 EXPORT_SYMBOL(lprocfs_rd_fstype);
1480 EXPORT_SYMBOL(lprocfs_rd_server_uuid);
1481 EXPORT_SYMBOL(lprocfs_rd_conn_uuid);
1482 EXPORT_SYMBOL(lprocfs_rd_num_exports);
1483 EXPORT_SYMBOL(lprocfs_rd_numrefs);
1484
1485 EXPORT_SYMBOL(lprocfs_rd_blksize);
1486 EXPORT_SYMBOL(lprocfs_rd_kbytestotal);
1487 EXPORT_SYMBOL(lprocfs_rd_kbytesfree);
1488 EXPORT_SYMBOL(lprocfs_rd_kbytesavail);
1489 EXPORT_SYMBOL(lprocfs_rd_filestotal);
1490 EXPORT_SYMBOL(lprocfs_rd_filesfree);
1491
1492 EXPORT_SYMBOL(lprocfs_write_helper);
1493 EXPORT_SYMBOL(lprocfs_write_frac_helper);
1494 EXPORT_SYMBOL(lprocfs_read_frac_helper);
1495 EXPORT_SYMBOL(lprocfs_write_u64_helper);
1496 EXPORT_SYMBOL(lprocfs_write_frac_u64_helper);
1497 #endif /* LPROCFS*/