Whamcloud - gitweb
- merge 0.7rc1 from b_devel to HEAD (20030612 merge point)
[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 Lustre, http://www.lustre.org.
8  *
9  *   Lustre is free software; you can redistribute it and/or
10  *   modify it under the terms of version 2 of the GNU General Public
11  *   License as published by the Free Software Foundation.
12  *
13  *   Lustre is distributed in the hope that it will be useful,
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *   GNU General Public License for more details.
17  *
18  *   You should have received a copy of the GNU General Public License
19  *   along with Lustre; if not, write to the Free Software
20  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  */
22
23 #define EXPORT_SYMTAB
24 #define DEBUG_SUBSYSTEM S_CLASS
25 #ifdef __KERNEL__
26 #include <linux/config.h>
27 #include <linux/module.h>
28 #include <linux/version.h>
29 #include <linux/slab.h>
30 #include <linux/types.h>
31 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0))
32 #include <asm/statfs.h>
33 #endif
34 #include <linux/seq_file.h>
35
36 #else
37 #include <liblustre.h>
38 #endif
39
40 #include <linux/obd_class.h>
41 #include <linux/lprocfs_status.h>
42
43 #ifdef LPROCFS
44
45 struct proc_dir_entry *lprocfs_srch(struct proc_dir_entry *head,
46                                     const char *name)
47 {
48         struct proc_dir_entry* temp;
49
50         if (!head)
51                 return NULL;
52
53         temp = head->subdir;
54         while (temp != NULL) {
55                 if (!strcmp(temp->name, name))
56                         return temp;
57
58                 temp = temp->next;
59         }
60         return NULL;
61 }
62
63 /* lprocfs API calls */
64
65 int lprocfs_add_vars(struct proc_dir_entry *root, struct lprocfs_vars *list,
66                      void *data)
67 {
68         if ((root == NULL) || (list == NULL))
69                 return -EINVAL;
70
71         while (list->name) {
72                 struct proc_dir_entry *cur_root, *proc;
73                 char *pathcopy, *cur, *next;
74                 int pathsize = strlen(list->name)+1;
75
76                 proc = NULL;
77                 cur_root = root;
78
79                 /* need copy of path for strsep */
80                 OBD_ALLOC(pathcopy, pathsize);
81                 if (!pathcopy)
82                         return -ENOMEM;
83
84                 next = pathcopy;
85                 strcpy(pathcopy, list->name);
86
87                 while (cur_root && (cur = strsep(&next, "/"))) {
88                         if (*cur =='\0') /* skip double/trailing "/" */
89                                 continue;
90
91                         proc = lprocfs_srch(cur_root, cur);
92                         CDEBUG(D_OTHER, "cur_root=%s, cur=%s, next=%s, (%s)\n",
93                                cur_root->name, cur, next,
94                                (proc ? "exists" : "new"));
95                         if (next)
96                                 cur_root = (proc ? proc :
97                                                    proc_mkdir(cur, cur_root));
98                         else if (!proc)
99                                 proc = create_proc_entry(cur, 0444, cur_root);
100                 }
101
102                 OBD_FREE(pathcopy, pathsize);
103
104                 if ((cur_root == NULL) || (proc == NULL)) {
105                         CERROR("LprocFS: No memory to create /proc entry %s",
106                                list->name);
107                         return -ENOMEM;
108                 }
109
110                 proc->read_proc = list->read_fptr;
111                 proc->write_proc = list->write_fptr;
112                 proc->data = (list->data ? list->data : data);
113                 list++;
114         }
115         return 0;
116 }
117
118 void lprocfs_remove(struct proc_dir_entry* root)
119 {
120         struct proc_dir_entry *temp = root;
121         struct proc_dir_entry *rm_entry;
122         struct proc_dir_entry *parent;
123
124         LASSERT(root != NULL);
125         parent = root->parent;
126         LASSERT(parent != NULL);
127
128         while (1) {
129                 while (temp->subdir)
130                         temp = temp->subdir;
131
132                 rm_entry = temp;
133                 temp = temp->parent;
134                 remove_proc_entry(rm_entry->name, rm_entry->parent);
135                 if (temp == parent)
136                         break;
137         }
138 }
139
140 struct proc_dir_entry *lprocfs_register(const char *name,
141                                         struct proc_dir_entry *parent,
142                                         struct lprocfs_vars *list, void *data)
143 {
144         struct proc_dir_entry *newchild;
145
146         newchild = lprocfs_srch(parent, name);
147         if (newchild) {
148                 CERROR(" Lproc: Attempting to register %s more than once \n",
149                        name);
150                 return ERR_PTR(-EALREADY);
151         }
152
153         newchild = proc_mkdir(name, parent);
154         if (newchild && list) {
155                 int rc = lprocfs_add_vars(newchild, list, data);
156                 if (rc) {
157                         lprocfs_remove(newchild);
158                         return ERR_PTR(rc);
159                 }
160         }
161         return newchild;
162 }
163
164 /* Generic callbacks */
165
166 int lprocfs_rd_u64(char *page, char **start, off_t off,
167                    int count, int *eof, void *data)
168 {
169         LASSERT(data != NULL);
170         *eof = 1;
171         return snprintf(page, count, LPU64"\n", *(__u64 *)data);
172 }
173
174 int lprocfs_rd_uuid(char* page, char **start, off_t off, int count,
175                     int *eof, void *data)
176 {
177         struct obd_device* dev = (struct obd_device*)data;
178
179         LASSERT(dev != NULL);
180         *eof = 1;
181         return snprintf(page, count, "%s\n", dev->obd_uuid.uuid);
182 }
183
184 int lprocfs_rd_name(char *page, char **start, off_t off, int count,
185                     int *eof, void *data)
186 {
187         struct obd_device* dev = (struct obd_device *)data;
188
189         LASSERT(dev != NULL);
190         LASSERT(dev->obd_name != NULL);
191         *eof = 1;
192         return snprintf(page, count, "%s\n", dev->obd_name);
193 }
194
195 int lprocfs_rd_blksize(char* page, char **start, off_t off, int count,
196                        int *eof, struct statfs *sfs)
197 {
198         LASSERT(sfs != NULL);
199         *eof = 1;
200         return snprintf(page, count, "%lu\n", sfs->f_bsize);
201 }
202
203 int lprocfs_rd_kbytestotal(char* page, char **start, off_t off, int count,
204                            int *eof, struct statfs *sfs)
205 {
206         __u32 blk_size;
207         __u64 result;
208
209         LASSERT(sfs != NULL);
210         blk_size = sfs->f_bsize >> 10;
211         result = sfs->f_blocks;
212
213         while (blk_size >>= 1)
214                 result <<= 1;
215
216         *eof = 1;
217         return snprintf(page, count, LPU64"\n", result);
218 }
219
220 int lprocfs_rd_kbytesfree(char* page, char **start, off_t off, int count,
221                           int *eof, struct statfs *sfs)
222 {
223         __u32 blk_size;
224         __u64 result;
225
226         LASSERT(sfs != NULL);
227         blk_size = sfs->f_bsize >> 10;
228         result = sfs->f_bfree;
229
230         while (blk_size >>= 1)
231                 result <<= 1;
232
233         *eof = 1;
234         return snprintf(page, count, LPU64"\n", result);
235 }
236
237 int lprocfs_rd_filestotal(char* page, char **start, off_t off, int count,
238                           int *eof, struct statfs *sfs)
239 {
240         LASSERT(sfs != NULL);
241         *eof = 1;
242         return snprintf(page, count, "%ld\n", sfs->f_files);
243 }
244
245 int lprocfs_rd_filesfree(char* page, char **start, off_t off, int count,
246                          int *eof, struct statfs *sfs)
247 {
248         LASSERT(sfs != NULL);
249         *eof = 1;
250         return snprintf(page, count, "%ld\n", sfs->f_ffree);
251 }
252
253 int lprocfs_rd_filegroups(char* page, char **start, off_t off, int count,
254                           int *eof, struct statfs *sfs)
255 {
256         *eof = 1;
257         return snprintf(page, count, "unimplemented\n");
258 }
259
260 int lprocfs_rd_server_uuid(char* page, char **start, off_t off, int count,
261                            int *eof, void *data)
262 {
263         struct obd_device *obd = (struct obd_device *)data;
264         struct client_obd *cli;
265
266         LASSERT(obd != NULL);
267         cli = &obd->u.cli;
268         *eof = 1;
269         return snprintf(page, count, "%s\n",
270                         cli->cl_import->imp_target_uuid.uuid);
271 }
272
273 int lprocfs_rd_conn_uuid(char *page, char **start, off_t off, int count,
274                          int *eof,  void *data)
275 {
276         struct obd_device *obd = (struct obd_device*)data;
277         struct ptlrpc_connection *conn;
278
279         LASSERT(obd != NULL);
280         conn = obd->u.cli.cl_import->imp_connection;
281         LASSERT(conn != NULL);
282         *eof = 1;
283         return snprintf(page, count, "%s\n", conn->c_remote_uuid.uuid);
284 }
285
286 int lprocfs_rd_numrefs(char *page, char **start, off_t off, int count,
287                        int *eof, void *data)
288 {
289         struct obd_type* class = (struct obd_type*) data;
290
291         LASSERT(class != NULL);
292         *eof = 1;
293         return snprintf(page, count, "%d\n", class->typ_refcnt);
294 }
295
296 int lprocfs_obd_attach(struct obd_device *dev, struct lprocfs_vars *list)
297 {
298         int rc = 0;
299
300         LASSERT(dev != NULL);
301         LASSERT(dev->obd_type != NULL);
302         LASSERT(dev->obd_type->typ_procroot != NULL);
303
304         dev->obd_proc_entry = lprocfs_register(dev->obd_name,
305                                                dev->obd_type->typ_procroot,
306                                                list, dev);
307         if (IS_ERR(dev->obd_proc_entry)) {
308                 rc = PTR_ERR(dev->obd_proc_entry);
309                 dev->obd_proc_entry = NULL;
310         }
311         return rc;
312 }
313
314 int lprocfs_obd_detach(struct obd_device *dev)
315 {
316         if (dev && dev->obd_proc_entry) {
317                 lprocfs_remove(dev->obd_proc_entry);
318                 dev->obd_proc_entry = NULL;
319         }
320         return 0;
321 }
322
323 struct lprocfs_counters* lprocfs_alloc_counters(unsigned int num)
324 {
325         struct lprocfs_counters* cntrs;
326         int csize;
327         if (num == 0)
328                 return NULL;
329
330         csize = offsetof(struct lprocfs_counters, cntr[num]);
331         OBD_ALLOC(cntrs, csize);
332         if (cntrs != NULL) {
333                 cntrs->num = num;
334         }
335         return cntrs;
336 }
337
338 void lprocfs_free_counters(struct lprocfs_counters* cntrs)
339 {
340         if (cntrs != NULL) {
341                 int csize = offsetof(struct lprocfs_counters, cntr[cntrs->num]);                OBD_FREE(cntrs, csize);
342         }
343 }
344
345 /* Reset counter under lock */
346 int lprocfs_counter_write(struct file *file, const char *buffer,
347                           unsigned long count, void *data)
348 {
349         struct lprocfs_counters *cntrs = (struct lprocfs_counters*) data;
350         unsigned int i;
351         LASSERT(cntrs != NULL);
352
353         for (i = 0; i < cntrs->num; i++) {
354                 struct lprocfs_counter *cntr = &(cntrs->cntr[i]);
355                 spinlock_t *lock = (cntr->config & LPROCFS_CNTR_EXTERNALLOCK) ?
356                         cntr->l.external : &cntr->l.internal;
357
358                 spin_lock(lock);
359                 cntr->count     = 0;
360                 cntr->sum       = 0;
361                 cntr->min       = (~(__u64)0);
362                 cntr->max       = 0;
363                 cntr->sumsquare = 0;
364                 spin_unlock(lock);
365         }
366         return 0;
367 }
368
369 static void *lprocfs_counters_seq_start(struct seq_file *p, loff_t *pos)
370 {
371         struct lprocfs_counters *cntrs = p->private;
372         return (*pos >= cntrs->num) ? NULL : (void*) &cntrs->cntr[*pos];
373 }
374
375 static void lprocfs_counters_seq_stop(struct seq_file *p, void *v)
376 {
377 }
378
379 static void *lprocfs_counters_seq_next(struct seq_file *p, void *v,
380                                        loff_t *pos)
381 {
382         struct lprocfs_counters *cntrs = p->private;
383         ++*pos;
384         return (*pos >= cntrs->num) ? NULL : (void*) &(cntrs->cntr[*pos]);
385 }
386
387 /* seq file export of one lprocfs counter */
388 static int lprocfs_counters_seq_show(struct seq_file *p, void *v)
389 {
390        struct lprocfs_counters *cntrs = p->private;
391        struct lprocfs_counter  *cntr = v;
392        spinlock_t              *lock;
393        struct lprocfs_counter  c;
394        int rc = 0;
395
396        if (cntr == &(cntrs->cntr[0])) {
397                struct timeval now;
398                do_gettimeofday(&now);
399                rc = seq_printf(p, "%-25s %lu.%lu secs.usecs\n",
400                                "snapshot_time", now.tv_sec, now.tv_usec);
401                if (rc < 0)
402                        return rc;
403        }
404
405        /* Take a snapshot of the counter under lock */
406        lock = (cntr->config & LPROCFS_CNTR_EXTERNALLOCK) ?
407                cntr->l.external : &cntr->l.internal;
408        spin_lock(lock);
409
410        c.count = cntr->count;
411        c.sum = cntr->sum;
412        c.min = cntr->min;
413        c.max = cntr->max;
414        c.sumsquare = cntr->sumsquare;
415
416        spin_unlock(lock);
417
418        rc = seq_printf(p, "%-25s "LPU64" samples [%s]", cntr->name, c.count,
419                        cntr->units);
420        if (rc < 0)
421                goto out;
422
423        if ((cntr->config & LPROCFS_CNTR_AVGMINMAX) && (c.count > 0)) {
424                rc = seq_printf(p, " "LPU64" "LPU64" "LPU64, c.min,c.max,c.sum);
425                if (rc < 0)
426                        goto out;
427                if (cntr->config & LPROCFS_CNTR_STDDEV)
428                        rc = seq_printf(p, " "LPU64, c.sumsquare);
429                if (rc < 0)
430                        goto out;
431        }
432        rc = seq_printf(p, "\n");
433  out:
434        return (rc < 0) ? rc : 0;
435 }
436
437 struct seq_operations lprocfs_counters_seq_sops = {
438         .start = lprocfs_counters_seq_start,
439         .stop = lprocfs_counters_seq_stop,
440         .next = lprocfs_counters_seq_next,
441         .show = lprocfs_counters_seq_show,
442 };
443
444 static int lprocfs_counters_seq_open(struct inode *inode, struct file *file)
445 {
446         struct proc_dir_entry *dp = inode->u.generic_ip;
447         struct seq_file *seq;
448         int rc;
449
450         rc = seq_open(file, &lprocfs_counters_seq_sops);
451         if (rc)
452                 return rc;
453         seq = file->private_data;
454         seq->private = dp->data;
455         return 0;
456 }
457
458 struct file_operations lprocfs_counters_seq_fops = {
459         .open    = lprocfs_counters_seq_open,
460         .read    = seq_read,
461         .llseek  = seq_lseek,
462         .release = seq_release,
463 };
464
465 int lprocfs_register_counters(struct proc_dir_entry *root, const char* name,
466                               struct lprocfs_counters *cntrs)
467 {
468         struct proc_dir_entry *entry;
469         LASSERT(root != NULL);
470
471         entry = create_proc_entry(name, 0444, root);
472         if (entry == NULL)
473                 return -ENOMEM;
474         entry->proc_fops = &lprocfs_counters_seq_fops;
475         entry->data = (void*) cntrs;
476         entry->write_proc = lprocfs_counter_write;
477         return 0;
478 }
479
480 #define LPROCFS_OBD_OP_INIT(base, cntrs, op)                               \
481 do {                                                                       \
482         unsigned int coffset = base + OBD_COUNTER_OFFSET(op);              \
483         LASSERT(coffset < cntrs->num);                                     \
484         LPROCFS_COUNTER_INIT(&cntrs->cntr[coffset], 0, NULL, #op, "reqs"); \
485 } while (0)
486
487
488 int lprocfs_alloc_obd_counters(struct obd_device *obddev,
489                                unsigned int num_private_counters)
490 {
491         struct lprocfs_counters* obdops_cntrs;
492         unsigned int num_counters;
493         int rc, i;
494
495         LASSERT(obddev->counters == NULL);
496         LASSERT(obddev->obd_proc_entry != NULL);
497         LASSERT(obddev->cntr_base == 0);
498
499         num_counters = 1 + OBD_COUNTER_OFFSET(san_preprw)+num_private_counters;
500         obdops_cntrs = lprocfs_alloc_counters(num_counters);
501         if (!obdops_cntrs)
502                 return -ENOMEM;
503
504         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, iocontrol);
505         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, get_info);
506         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, set_info);
507         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, attach);
508         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, detach);
509         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, setup);
510         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, cleanup);
511         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, connect);
512         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, disconnect);
513         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, statfs);
514         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, syncfs);
515         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, packmd);
516         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, unpackmd);
517         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, preallocate);
518         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, create);
519         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, destroy);
520         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, setattr);
521         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, getattr);
522         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, getattr_async);
523         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, open);
524         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, close);
525         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, brw);
526         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, brw_async);
527         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, punch);
528         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, sync);
529         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, migrate);
530         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, copy);
531         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, iterate);
532         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, preprw);
533         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, commitrw);
534         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, enqueue);
535         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, match);
536         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, cancel);
537         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, cancel_unused);
538         LPROCFS_OBD_OP_INIT(num_private_counters, obdops_cntrs, san_preprw);
539
540         for (i = num_private_counters; i < num_counters; i++) {
541                 /* If this assertion failed, it is likely that an obd
542                  * operation was added to struct obd_ops in
543                  * <linux/obd.h>, and that the corresponding line item
544                  * LPROCFS_OBD_OP_INIT(.., .., opname)
545                  * is missing from the list above. */
546                 LASSERT(obdops_cntrs->cntr[i].name != NULL);
547         }
548         rc = lprocfs_register_counters(obddev->obd_proc_entry, "obd_stats",
549                                        obdops_cntrs);
550         if (rc < 0) {
551                 lprocfs_free_counters(obdops_cntrs);
552         } else {
553                 obddev->counters  = obdops_cntrs;
554                 obddev->cntr_base = num_private_counters;
555         }
556         return rc;
557 }
558
559 void lprocfs_free_obd_counters(struct obd_device *obddev)
560 {
561         struct lprocfs_counters* obdops_cntrs = obddev->counters;
562         if (obdops_cntrs != NULL) {
563                 obddev->counters = NULL;
564                 lprocfs_free_counters(obdops_cntrs);
565         }
566 }
567
568 #endif /* LPROCFS*/
569
570 EXPORT_SYMBOL(lprocfs_register);
571 EXPORT_SYMBOL(lprocfs_remove);
572 EXPORT_SYMBOL(lprocfs_add_vars);
573 EXPORT_SYMBOL(lprocfs_obd_attach);
574 EXPORT_SYMBOL(lprocfs_obd_detach);
575 EXPORT_SYMBOL(lprocfs_alloc_counters);
576 EXPORT_SYMBOL(lprocfs_free_counters);
577 EXPORT_SYMBOL(lprocfs_register_counters);
578 EXPORT_SYMBOL(lprocfs_alloc_obd_counters);
579 EXPORT_SYMBOL(lprocfs_free_obd_counters);
580
581 EXPORT_SYMBOL(lprocfs_rd_u64);
582 EXPORT_SYMBOL(lprocfs_rd_uuid);
583 EXPORT_SYMBOL(lprocfs_rd_name);
584 EXPORT_SYMBOL(lprocfs_rd_server_uuid);
585 EXPORT_SYMBOL(lprocfs_rd_conn_uuid);
586 EXPORT_SYMBOL(lprocfs_rd_numrefs);
587
588 EXPORT_SYMBOL(lprocfs_rd_blksize);
589 EXPORT_SYMBOL(lprocfs_rd_kbytestotal);
590 EXPORT_SYMBOL(lprocfs_rd_kbytesfree);
591 EXPORT_SYMBOL(lprocfs_rd_filestotal);
592 EXPORT_SYMBOL(lprocfs_rd_filesfree);
593 EXPORT_SYMBOL(lprocfs_rd_filegroups);