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