Whamcloud - gitweb
LU-17504 build: fix gcc-13 [-Werror=stringop-overread] error
[fs/lustre-release.git] / lustre / obdclass / lprocfs_status_server.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Copyright (c) 2014, 2017, Intel Corporation.
27  */
28 /*
29  * This file is part of Lustre, http://www.lustre.org/
30  *
31  * lustre/obdclass/lprocfs_status_server.c
32  */
33
34 #define DEBUG_SUBSYSTEM S_CLASS
35
36 #include <linux/kobject.h>
37 #include <linux/sysfs.h>
38
39 #include <obd_class.h>
40 #include <lprocfs_status.h>
41 #include <lustre_nodemap.h>
42
43 #define MAX_STRING_SIZE 128
44
45 struct dentry *ldebugfs_add_symlink(const char *name, const char *target,
46                                     const char *format, ...)
47 {
48         struct dentry *entry = NULL;
49         struct dentry *parent;
50         struct qstr dname;
51         va_list ap;
52         char *dest;
53
54         if (!target || !format)
55                 return NULL;
56
57         dname.name = target;
58         dname.len = strlen(dname.name);
59         dname.hash = ll_full_name_hash(debugfs_lustre_root,
60                                        dname.name, dname.len);
61         parent = d_lookup(debugfs_lustre_root, &dname);
62         if (!parent)
63                 return NULL;
64
65         OBD_ALLOC_WAIT(dest, MAX_STRING_SIZE + 1);
66         if (!dest)
67                 goto no_entry;
68
69         va_start(ap, format);
70         vsnprintf(dest, MAX_STRING_SIZE, format, ap);
71         va_end(ap);
72
73         entry = debugfs_create_symlink(name, parent, dest);
74
75         OBD_FREE(dest, MAX_STRING_SIZE + 1);
76 no_entry:
77         dput(parent);
78         return entry;
79 }
80 EXPORT_SYMBOL(ldebugfs_add_symlink);
81
82 #ifdef CONFIG_PROC_FS
83
84 int lprocfs_evict_client_open(struct inode *inode, struct file *f)
85 {
86         struct obd_device *obd = PDE_DATA(file_inode(f));
87
88         atomic_inc(&obd->obd_evict_inprogress);
89         return 0;
90 }
91
92 int lprocfs_evict_client_release(struct inode *inode, struct file *f)
93 {
94         struct obd_device *obd = PDE_DATA(file_inode(f));
95
96         atomic_dec(&obd->obd_evict_inprogress);
97         wake_up(&obd->obd_evict_inprogress_waitq);
98
99         return 0;
100 }
101
102 #define BUFLEN (UUID_MAX + 5)
103
104 ssize_t
105 lprocfs_evict_client_seq_write(struct file *file, const char __user *buffer,
106                                size_t count, loff_t *off)
107 {
108         struct seq_file *m = file->private_data;
109         struct obd_device *obd = m->private;
110         char *tmpbuf, *kbuf;
111
112         OBD_ALLOC(kbuf, BUFLEN);
113         if (kbuf == NULL)
114                 return -ENOMEM;
115
116         /*
117          * OBD_ALLOC() will zero kbuf, but we only copy BUFLEN - 1
118          * bytes into kbuf, to ensure that the string is NUL-terminated.
119          * UUID_MAX should include a trailing NUL already.
120          */
121         if (copy_from_user(kbuf, buffer,
122                            min_t(unsigned long, BUFLEN - 1, count))) {
123                 count = -EFAULT;
124                 goto out;
125         }
126         tmpbuf = skip_spaces(kbuf);
127         tmpbuf = strsep(&tmpbuf, " \t\n\f\v\r");
128         class_incref(obd, __func__, current);
129
130         if (strncmp(tmpbuf, "nid:", 4) == 0)
131                 obd_export_evict_by_nid(obd, tmpbuf + 4);
132         else if (strncmp(tmpbuf, "uuid:", 5) == 0)
133                 obd_export_evict_by_uuid(obd, tmpbuf + 5);
134         else
135                 obd_export_evict_by_uuid(obd, tmpbuf);
136
137         class_decref(obd, __func__, current);
138
139 out:
140         OBD_FREE(kbuf, BUFLEN);
141         return count;
142 }
143 EXPORT_SYMBOL(lprocfs_evict_client_seq_write);
144
145 #undef BUFLEN
146
147 ssize_t num_exports_show(struct kobject *kobj, struct attribute *attr,
148                          char *buf)
149 {
150         struct obd_device *obd = container_of(kobj, struct obd_device,
151                                               obd_kset.kobj);
152
153         return scnprintf(buf, PAGE_SIZE, "%u\n", obd->obd_num_exports);
154 }
155 EXPORT_SYMBOL(num_exports_show);
156
157 static int obd_export_flags2str(struct obd_export *exp, struct seq_file *m)
158 {
159         bool first = true;
160
161         flag2str(exp, failed);
162         flag2str(exp, in_recovery);
163         flag2str(exp, disconnected);
164         flag2str(exp, connecting);
165         flag2str(exp, no_recovery);
166
167         return 0;
168 }
169
170 static int
171 lprocfs_exp_print_export_seq(struct obd_export *exp, void *cb_data)
172 {
173         struct seq_file         *m = cb_data;
174         struct obd_device       *obd;
175         struct obd_connect_data *ocd;
176
177         LASSERT(exp != NULL);
178         if (exp->exp_nid_stats == NULL)
179                 goto out;
180         obd = exp->exp_obd;
181         ocd = &exp->exp_connect_data;
182
183         seq_printf(m, "%s:\n"
184                    "    name: %s\n"
185                    "    client: %s\n"
186                    "    connect_flags: [ ",
187                    obd_uuid2str(&exp->exp_client_uuid),
188                    obd->obd_name,
189                    obd_export_nid2str(exp));
190         obd_connect_seq_flags2str(m, ocd->ocd_connect_flags,
191                                   ocd->ocd_connect_flags2, ", ");
192         seq_printf(m, " ]\n");
193         obd_connect_data_seqprint(m, ocd);
194         seq_printf(m, "    export_flags: [ ");
195         obd_export_flags2str(exp, m);
196         seq_printf(m, " ]\n");
197
198         if (obd->obd_type &&
199             strcmp(obd->obd_type->typ_name, "obdfilter") == 0) {
200                 struct filter_export_data *fed = &exp->exp_filter_data;
201
202                 seq_printf(m, "    grant:\n");
203                 seq_printf(m, "       granted: %ld\n",
204                         fed->fed_ted.ted_grant);
205                 seq_printf(m, "       dirty: %ld\n",
206                         fed->fed_ted.ted_dirty);
207                 seq_printf(m, "       pending: %ld\n",
208                         fed->fed_ted.ted_pending);
209         }
210
211 out:
212         return 0;
213 }
214
215 /**
216  * RPC connections are composed of an import and an export. Using the
217  * lctl utility we can extract important information about the state.
218  * The lprocfs_exp_export_seq_show routine displays the state information
219  * for the export.
220  *
221  * \param[in] m         seq file
222  * \param[in] data      unused
223  *
224  * \retval              0 on success
225  *
226  * The format of the export state information is like:
227  * a793e354-49c0-aa11-8c4f-a4f2b1a1a92b:
228  *     name: MGS
229  *     client: 10.211.55.10@tcp
230  *     connect_flags: [ version, barrier, adaptive_timeouts, ... ]
231  *     connect_data:
232  *        flags: 0x2000011005002020
233  *        instance: 0
234  *        target_version: 2.10.51.0
235  *        export_flags: [ ... ]
236  *
237  */
238 static int lprocfs_exp_export_seq_show(struct seq_file *m, void *data)
239 {
240         struct nid_stat *stats = m->private;
241
242         return obd_nid_export_for_each(stats->nid_obd, stats->nid,
243                                        lprocfs_exp_print_export_seq, m);
244 }
245 LPROC_SEQ_FOPS_RO(lprocfs_exp_export);
246
247 static void lprocfs_free_client_stats(struct nid_stat *client_stat)
248 {
249         CDEBUG(D_CONFIG, "stat %p - data %p/%p\n", client_stat,
250                client_stat->nid_proc, client_stat->nid_stats);
251
252         LASSERTF(atomic_read(&client_stat->nid_exp_ref_count) == 0,
253                  "nid %s:count %d\n", libcfs_nid2str(client_stat->nid),
254                  atomic_read(&client_stat->nid_exp_ref_count));
255
256         if (client_stat->nid_proc)
257                 lprocfs_remove(&client_stat->nid_proc);
258
259         if (client_stat->nid_stats)
260                 lprocfs_free_stats(&client_stat->nid_stats);
261
262         if (client_stat->nid_ldlm_stats)
263                 lprocfs_free_stats(&client_stat->nid_ldlm_stats);
264
265         OBD_FREE_PTR(client_stat);
266 }
267
268 void lprocfs_free_per_client_stats(struct obd_device *obd)
269 {
270         struct cfs_hash *hash = obd->obd_nid_stats_hash;
271         struct nid_stat *stat;
272         ENTRY;
273
274         /* we need extra list - because hash_exit called to early */
275         /* not need locking because all clients is died */
276         while (!list_empty(&obd->obd_nid_stats)) {
277                 stat = list_entry(obd->obd_nid_stats.next,
278                                   struct nid_stat, nid_list);
279                 list_del_init(&stat->nid_list);
280                 cfs_hash_del(hash, &stat->nid, &stat->nid_hash);
281                 lprocfs_free_client_stats(stat);
282         }
283         EXIT;
284 }
285 EXPORT_SYMBOL(lprocfs_free_per_client_stats);
286
287 static int
288 lprocfs_exp_print_nodemap_seq(struct obd_export *exp, void *cb_data)
289 {
290         struct lu_nodemap *nodemap = exp->exp_target_data.ted_nodemap;
291         struct seq_file *m = cb_data;
292
293         if (nodemap)
294                 seq_printf(m, "%s\n", nodemap->nm_name);
295         return 0;
296 }
297
298 static int
299 lprocfs_exp_nodemap_seq_show(struct seq_file *m, void *data)
300 {
301         struct nid_stat *stats = m->private;
302
303         return obd_nid_export_for_each(stats->nid_obd, stats->nid,
304                                        lprocfs_exp_print_nodemap_seq, m);
305 }
306 LPROC_SEQ_FOPS_RO(lprocfs_exp_nodemap);
307
308 static int
309 lprocfs_exp_print_uuid_seq(struct obd_export *exp, void *cb_data)
310 {
311         struct seq_file *m = cb_data;
312
313         if (exp->exp_nid_stats)
314                 seq_printf(m, "%s\n", obd_uuid2str(&exp->exp_client_uuid));
315         return 0;
316 }
317
318 static int lprocfs_exp_uuid_seq_show(struct seq_file *m, void *data)
319 {
320         struct nid_stat *stats = m->private;
321
322         return obd_nid_export_for_each(stats->nid_obd, stats->nid,
323                                        lprocfs_exp_print_uuid_seq, m);
324 }
325 LPROC_SEQ_FOPS_RO(lprocfs_exp_uuid);
326
327 #define HASH_NAME_LEN   16
328
329 static void ldebugfs_rhash_seq_show(const char *name, struct rhashtable *ht,
330                                     struct seq_file *m)
331 {
332         unsigned int max_size = ht->p.max_size ? ht->p.max_size : UINT_MAX;
333         struct bucket_table *tbl;
334         int dist[8] = { 0, };
335         int maxdep = 0;
336         int i;
337
338         rcu_read_lock();
339         tbl = rht_dereference(ht->tbl, ht);
340         for (i = 0; i < tbl->size; i++) {
341                 struct rhash_head *pos;
342                 int count = 0;
343
344                 rht_for_each(pos, tbl, i)
345                         count++;
346
347                 if (count)
348                         maxdep = max(maxdep, count);
349
350                 dist[min(fls(count), 7)]++;
351         }
352
353         seq_printf(m, "%-*s %5d %5d %10u %d.%03d 0.300 0.750 0x%03x %7d %7d %7d ",
354                    HASH_NAME_LEN, name, tbl->size, ht->p.min_size, max_size,
355                    atomic_read(&ht->nelems) / tbl->size,
356                    atomic_read(&ht->nelems) * 1000 / tbl->size,
357                    ht->p.automatic_shrinking, 0,
358                    atomic_read(&ht->nelems), maxdep);
359         rcu_read_unlock();
360
361         for (i = 0; i < 8; i++)
362                 seq_printf(m, "%d%c",  dist[i], (i == 7) ? '\n' : '/');
363 }
364
365 static int
366 lprocfs_exp_print_hash_seq(struct obd_export *exp, void *cb_data)
367
368 {
369         struct obd_device *obd = exp->exp_obd;
370         struct seq_file *m = cb_data;
371
372         if (exp->exp_lock_hash != NULL) {
373                 seq_printf(m, "%-*s   cur   min        max theta t-min t-max flags rehash   count distribution\n",
374                            HASH_NAME_LEN, "name");
375                 ldebugfs_rhash_seq_show("NID_HASH", &obd->obd_nid_hash.ht, m);
376         }
377         return 0;
378 }
379
380 static int lprocfs_exp_hash_seq_show(struct seq_file *m, void *data)
381 {
382         struct nid_stat *stats = m->private;
383
384         return obd_nid_export_for_each(stats->nid_obd, stats->nid,
385                                        lprocfs_exp_print_hash_seq, m);
386 }
387 LPROC_SEQ_FOPS_RO(lprocfs_exp_hash);
388
389 int lprocfs_exp_print_replydata_seq(struct obd_export *exp, void *cb_data)
390
391 {
392         struct seq_file *m = cb_data;
393         struct tg_export_data *ted = &exp->exp_target_data;
394
395         seq_printf(m, "reply_cnt: %d\n"
396                    "reply_max: %d\n"
397                    "reply_released_by_xid: %d\n"
398                    "reply_released_by_tag: %d\n\n",
399                    ted->ted_reply_cnt,
400                    ted->ted_reply_max,
401                    ted->ted_release_xid,
402                    ted->ted_release_tag);
403         return 0;
404 }
405
406 int lprocfs_exp_replydata_seq_show(struct seq_file *m, void *data)
407 {
408         struct nid_stat *stats = m->private;
409
410         return obd_nid_export_for_each(stats->nid_obd, stats->nid,
411                                        lprocfs_exp_print_replydata_seq, m);
412 }
413 LPROC_SEQ_FOPS_RO(lprocfs_exp_replydata);
414
415 int lprocfs_exp_print_fmd_count_seq(struct obd_export *exp, void *cb_data)
416 {
417         struct seq_file *m = cb_data;
418         struct tg_export_data *ted = &exp->exp_target_data;
419
420         seq_printf(m, "%d\n", ted->ted_fmd_count);
421
422         return 0;
423 }
424
425 int lprocfs_exp_fmd_count_seq_show(struct seq_file *m, void *data)
426 {
427         struct nid_stat *stats = m->private;
428
429         return obd_nid_export_for_each(stats->nid_obd, stats->nid,
430                                        lprocfs_exp_print_fmd_count_seq, m);
431 }
432 LPROC_SEQ_FOPS_RO(lprocfs_exp_fmd_count);
433
434 int lprocfs_nid_stats_clear_seq_show(struct seq_file *m, void *data)
435 {
436         seq_puts(m, "Write into this file to clear all nid stats and stale nid entries\n");
437         return 0;
438 }
439 EXPORT_SYMBOL(lprocfs_nid_stats_clear_seq_show);
440
441 static int lprocfs_nid_stats_clear_write_cb(void *obj, void *data)
442 {
443         struct nid_stat *stat = obj;
444         ENTRY;
445
446         CDEBUG(D_INFO, "refcnt %d\n", atomic_read(&stat->nid_exp_ref_count));
447         if (atomic_read(&stat->nid_exp_ref_count) == 1) {
448                 /* object has only hash references. */
449                 spin_lock(&stat->nid_obd->obd_nid_lock);
450                 list_move(&stat->nid_list, data);
451                 spin_unlock(&stat->nid_obd->obd_nid_lock);
452                 RETURN(1);
453         }
454         /* we has reference to object - only clear data*/
455         if (stat->nid_stats)
456                 lprocfs_clear_stats(stat->nid_stats);
457
458         RETURN(0);
459 }
460
461 ssize_t
462 lprocfs_nid_stats_clear_seq_write(struct file *file, const char __user *buffer,
463                                         size_t count, loff_t *off)
464 {
465         struct seq_file *m = file->private_data;
466         struct obd_device *obd = m->private;
467         struct nid_stat *client_stat;
468         LIST_HEAD(free_list);
469
470         cfs_hash_cond_del(obd->obd_nid_stats_hash,
471                           lprocfs_nid_stats_clear_write_cb, &free_list);
472
473         while (!list_empty(&free_list)) {
474                 client_stat = list_entry(free_list.next, struct nid_stat,
475                                          nid_list);
476                 list_del_init(&client_stat->nid_list);
477                 lprocfs_free_client_stats(client_stat);
478         }
479         return count;
480 }
481 EXPORT_SYMBOL(lprocfs_nid_stats_clear_seq_write);
482
483 int lprocfs_exp_setup(struct obd_export *exp, lnet_nid_t *nid)
484 {
485         struct nid_stat *new_stat, *old_stat;
486         struct obd_device *obd = NULL;
487         struct proc_dir_entry *entry;
488         char nidstr[LNET_NIDSTR_SIZE];
489         int rc = 0;
490         ENTRY;
491
492         if (!exp || !exp->exp_obd || !exp->exp_obd->obd_proc_exports_entry ||
493             !exp->exp_obd->obd_nid_stats_hash)
494                 RETURN(-EINVAL);
495
496         /* not test against zero because eric say:
497          * You may only test nid against another nid, or LNET_NID_ANY.
498          * Anything else is nonsense.*/
499         if (nid == NULL || *nid == LNET_NID_ANY)
500                 RETURN(-EALREADY);
501
502         libcfs_nid2str_r(*nid, nidstr, sizeof(nidstr));
503
504         spin_lock(&exp->exp_lock);
505         if (exp->exp_nid_stats != NULL) {
506                 spin_unlock(&exp->exp_lock);
507                 RETURN(-EALREADY);
508         }
509         spin_unlock(&exp->exp_lock);
510
511         obd = exp->exp_obd;
512
513         CDEBUG(D_CONFIG, "using hash %p\n", obd->obd_nid_stats_hash);
514
515         OBD_ALLOC_PTR(new_stat);
516         if (new_stat == NULL)
517                 RETURN(-ENOMEM);
518
519         new_stat->nid     = *nid;
520         new_stat->nid_obd = exp->exp_obd;
521         /* we need set default refcount to 1 to balance obd_disconnect */
522         atomic_set(&new_stat->nid_exp_ref_count, 1);
523
524         old_stat = cfs_hash_findadd_unique(obd->obd_nid_stats_hash,
525                                            nid, &new_stat->nid_hash);
526         CDEBUG(D_INFO, "Found stats %p for nid %s - ref %d\n",
527                old_stat, nidstr, atomic_read(&old_stat->nid_exp_ref_count));
528
529         /* Return -EALREADY here so that we know that the /proc
530          * entry already has been created */
531         if (old_stat != new_stat) {
532                 spin_lock(&exp->exp_lock);
533                 if (exp->exp_nid_stats) {
534                         LASSERT(exp->exp_nid_stats == old_stat);
535                         nidstat_putref(exp->exp_nid_stats);
536                 }
537                 exp->exp_nid_stats = old_stat;
538                 spin_unlock(&exp->exp_lock);
539                 GOTO(destroy_new, rc = -EALREADY);
540         }
541         /* not found - create */
542         new_stat->nid_proc = lprocfs_register(nidstr,
543                                               obd->obd_proc_exports_entry,
544                                               NULL, NULL);
545
546         if (IS_ERR(new_stat->nid_proc)) {
547                 rc = PTR_ERR(new_stat->nid_proc);
548                 new_stat->nid_proc = NULL;
549                 CERROR("%s: cannot create proc entry for export %s: rc = %d\n",
550                        obd->obd_name, nidstr, rc);
551                 GOTO(destroy_new_ns, rc);
552         }
553
554         entry = lprocfs_add_simple(new_stat->nid_proc, "nodemap", new_stat,
555                                    &lprocfs_exp_nodemap_fops);
556         if (IS_ERR(entry)) {
557                 rc = PTR_ERR(entry);
558                 CWARN("%s: error adding the nodemap file: rc = %d\n",
559                       obd->obd_name, rc);
560                 GOTO(destroy_new_ns, rc);
561         }
562
563         entry = lprocfs_add_simple(new_stat->nid_proc, "uuid", new_stat,
564                                    &lprocfs_exp_uuid_fops);
565         if (IS_ERR(entry)) {
566                 rc = PTR_ERR(entry);
567                 CWARN("%s: error adding the NID stats file: rc = %d\n",
568                       obd->obd_name, rc);
569                 GOTO(destroy_new_ns, rc);
570         }
571
572         entry = lprocfs_add_simple(new_stat->nid_proc, "hash", new_stat,
573                                    &lprocfs_exp_hash_fops);
574         if (IS_ERR(entry)) {
575                 rc = PTR_ERR(entry);
576                 CWARN("%s: error adding the hash file: rc = %d\n",
577                       obd->obd_name, rc);
578                 GOTO(destroy_new_ns, rc);
579         }
580
581         entry = lprocfs_add_simple(new_stat->nid_proc, "export",
582                                    new_stat, &lprocfs_exp_export_fops);
583         if (IS_ERR(entry)) {
584                 rc = PTR_ERR(entry);
585                 CWARN("%s: error adding the export file: rc = %d\n",
586                       obd->obd_name, rc);
587                 GOTO(destroy_new_ns, rc);
588         }
589
590         entry = lprocfs_add_simple(new_stat->nid_proc, "reply_data", new_stat,
591                                    &lprocfs_exp_replydata_fops);
592         if (IS_ERR(entry)) {
593                 rc = PTR_ERR(entry);
594                 CWARN("%s: error adding the reply_data file: rc = %d\n",
595                       obd->obd_name, rc);
596                 GOTO(destroy_new_ns, rc);
597         }
598
599         entry = lprocfs_add_simple(new_stat->nid_proc, "fmd_count", new_stat,
600                                    &lprocfs_exp_fmd_count_fops);
601         if (IS_ERR(entry)) {
602                 rc = PTR_ERR(entry);
603                 CWARN("%s: error adding the fmd_count file: rc = %d\n",
604                       obd->obd_name, rc);
605                 GOTO(destroy_new_ns, rc);
606         }
607
608         spin_lock(&exp->exp_lock);
609         exp->exp_nid_stats = new_stat;
610         spin_unlock(&exp->exp_lock);
611
612         /* protect competitive add to list, not need locking on destroy */
613         spin_lock(&obd->obd_nid_lock);
614         list_add(&new_stat->nid_list, &obd->obd_nid_stats);
615         spin_unlock(&obd->obd_nid_lock);
616
617         RETURN(0);
618
619 destroy_new_ns:
620         if (new_stat->nid_proc != NULL)
621                 lprocfs_remove(&new_stat->nid_proc);
622         cfs_hash_del(obd->obd_nid_stats_hash, nid, &new_stat->nid_hash);
623
624 destroy_new:
625         nidstat_putref(new_stat);
626         OBD_FREE_PTR(new_stat);
627         RETURN(rc);
628 }
629 EXPORT_SYMBOL(lprocfs_exp_setup);
630
631 int lprocfs_exp_cleanup(struct obd_export *exp)
632 {
633         struct nid_stat *stat = exp->exp_nid_stats;
634
635         if (!stat || !exp->exp_obd)
636                 RETURN(0);
637
638         nidstat_putref(exp->exp_nid_stats);
639         exp->exp_nid_stats = NULL;
640
641         return 0;
642 }
643
644 int lprocfs_alloc_obd_stats(struct obd_device *obd, unsigned int num_stats)
645 {
646         struct lprocfs_stats *stats;
647         int rc;
648
649         LASSERT(obd->obd_stats == NULL);
650         LASSERT(obd->obd_proc_entry != NULL);
651
652         stats = lprocfs_alloc_stats(num_stats, 0);
653         if (stats == NULL)
654                 return -ENOMEM;
655
656         rc = lprocfs_register_stats(obd->obd_proc_entry, "stats", stats);
657         if (rc < 0)
658                 lprocfs_free_stats(&stats);
659         else
660                 obd->obd_stats = stats;
661
662         return rc;
663 }
664 EXPORT_SYMBOL(lprocfs_alloc_obd_stats);
665
666 void lprocfs_free_obd_stats(struct obd_device *obd)
667 {
668         if (obd->obd_stats)
669                 lprocfs_free_stats(&obd->obd_stats);
670 }
671 EXPORT_SYMBOL(lprocfs_free_obd_stats);
672
673 static void display_brw_stats(struct seq_file *seq, const char *name,
674                               const char *units, struct obd_histogram *read,
675                               struct obd_histogram *write, bool scale)
676 {
677         unsigned long read_tot, write_tot, r, w, read_cum = 0, write_cum = 0;
678         unsigned int i;
679
680         seq_printf(seq, "\n%26s read      |     write\n", " ");
681         seq_printf(seq, "%-22s %-5s %% cum %% |  %-11s %% cum %%\n",
682                    name, units, units);
683
684         read_tot = lprocfs_oh_sum(read);
685         write_tot = lprocfs_oh_sum(write);
686
687         if (!read_tot && !write_tot)
688                 return;
689
690         for (i = 0; i < OBD_HIST_MAX; i++) {
691                 r = read->oh_buckets[i];
692                 w = write->oh_buckets[i];
693                 read_cum += r;
694                 write_cum += w;
695                 if (read_cum == 0 && write_cum == 0)
696                         continue;
697
698                 if (!scale)
699                         seq_printf(seq, "%u", i);
700                 else if (i < 10)
701                         seq_printf(seq, "%lu", BIT(i));
702                 else if (i < 20)
703                         seq_printf(seq, "%luK", BIT(i - 10));
704                 else
705                         seq_printf(seq, "%luM", BIT(i - 20));
706
707                 seq_printf(seq, ":\t\t%10lu %3u %3u   | %4lu %3u %3u\n",
708                            r, pct(r, read_tot), pct(read_cum, read_tot),
709                            w, pct(w, write_tot), pct(write_cum, write_tot));
710
711                 if (read_cum == read_tot && write_cum == write_tot)
712                         break;
713         }
714 }
715
716 static const struct brw_stats_props brw_props[] = {
717         { .bsp_name     = "pages per bulk r/w",
718           .bsp_units    = "rpcs",
719           .bsp_scale    = true                          },
720         { .bsp_name     = "discontiguous pages",
721           .bsp_units    = "rpcs",
722           .bsp_scale    = false                         },
723         { .bsp_name     = "discontiguous blocks",
724           .bsp_units    = "rpcs",
725           .bsp_scale    = false                         },
726         { .bsp_name     = "disk fragmented I/Os",
727           .bsp_units    = "ios",
728           .bsp_scale    = false                         },
729         { .bsp_name     = "disk I/Os in flight",
730           .bsp_units    = "ios",
731           .bsp_scale    = false                         },
732         { .bsp_name     = "I/O time (1/1000s)",
733           .bsp_units    = "ios",
734           .bsp_scale    = true                          },
735         { .bsp_name     = "disk I/O size",
736           .bsp_units    = "ios",
737           .bsp_scale    = true                          },
738 };
739
740 static int brw_stats_seq_show(struct seq_file *seq, void *v)
741 {
742         struct brw_stats *brw_stats = seq->private;
743         int i;
744
745         /* this sampling races with updates */
746         lprocfs_stats_header(seq, ktime_get(), brw_stats->bs_init, 25, ":", 1);
747
748         for (i = 0; i < ARRAY_SIZE(brw_stats->bs_props); i++) {
749                 if (!brw_stats->bs_props[i].bsp_name)
750                         continue;
751
752                 display_brw_stats(seq, brw_stats->bs_props[i].bsp_name,
753                                   brw_stats->bs_props[i].bsp_units,
754                                   &brw_stats->bs_hist[i * 2],
755                                   &brw_stats->bs_hist[i * 2 + 1],
756                                   brw_stats->bs_props[i].bsp_scale);
757         }
758
759         return 0;
760 }
761
762 static ssize_t brw_stats_seq_write(struct file *file,
763                                    const char __user *buf,
764                                    size_t len, loff_t *off)
765 {
766         struct seq_file *seq = file->private_data;
767         struct brw_stats *brw_stats = seq->private;
768         int i;
769
770         for (i = 0; i < BRW_RW_STATS_NUM; i++)
771                 lprocfs_oh_clear(&brw_stats->bs_hist[i]);
772
773         return len;
774 }
775
776 LDEBUGFS_SEQ_FOPS(brw_stats);
777
778 void ldebugfs_register_osd_stats(struct dentry *parent,
779                                  struct brw_stats *brw_stats,
780                                  struct lprocfs_stats *stats)
781 {
782         int i;
783
784         LASSERT(brw_stats);
785         brw_stats->bs_init = ktime_get();
786         for (i = 0; i < BRW_RW_STATS_NUM; i++) {
787                 struct brw_stats_props *props = brw_stats->bs_props;
788
789                 spin_lock_init(&brw_stats->bs_hist[i].oh_lock);
790                 if (i % 2) {
791                         props[i / 2].bsp_name = brw_props[i / 2].bsp_name;
792                         props[i / 2].bsp_units = brw_props[i / 2].bsp_units;
793                         props[i / 2].bsp_scale = brw_props[i / 2].bsp_scale;
794                 }
795         }
796
797         if (!parent)
798                 return;
799
800         debugfs_create_file("brw_stats", 0644, parent, brw_stats,
801                             &brw_stats_fops);
802
803         if (stats)
804                 debugfs_create_file("stats", 0644, parent, stats,
805                                     &ldebugfs_stats_seq_fops);
806 }
807 EXPORT_SYMBOL(ldebugfs_register_osd_stats);
808
809 int lprocfs_hash_seq_show(struct seq_file *m, void *data)
810 {
811         struct obd_device *obd = m->private;
812
813         if (obd == NULL)
814                 return 0;
815
816         /* header for rhashtable state */
817         seq_printf(m, "%-*s   cur   min        max theta t-min t-max flags  rehash   count  maxdep distribution\n",
818                    HASH_NAME_LEN, "name");
819         ldebugfs_rhash_seq_show("UUID_HASH", &obd->obd_uuid_hash, m);
820         ldebugfs_rhash_seq_show("NID_HASH", &obd->obd_nid_hash.ht, m);
821
822         cfs_hash_debug_header(m);
823         cfs_hash_debug_str(obd->obd_nid_stats_hash, m);
824         return 0;
825 }
826 EXPORT_SYMBOL(lprocfs_hash_seq_show);
827
828 int lprocfs_recovery_status_seq_show(struct seq_file *m, void *data)
829 {
830         struct obd_device *obd = m->private;
831         struct target_distribute_txn_data *tdtd;
832
833         LASSERT(obd != NULL);
834
835         seq_printf(m, "status: ");
836         if (atomic_read(&obd->obd_max_recoverable_clients) == 0) {
837                 seq_printf(m, "INACTIVE\n");
838                 goto out;
839         }
840
841         /* There is gap between client data read from storage and setting
842          * obd_recovering so check obd_recovery_end as well to make sure
843          * recovery is really finished
844          */
845         if (obd->obd_recovery_end > 0 && !obd->obd_recovering) {
846                 seq_printf(m, "COMPLETE\n");
847                 seq_printf(m, "recovery_start: %lld\n",
848                            (s64)ktime_get_real_seconds() -
849                            (ktime_get_seconds() - obd->obd_recovery_start));
850                 seq_printf(m, "recovery_duration: %lld\n",
851                            obd->obd_recovery_end ?
852                            obd->obd_recovery_end - obd->obd_recovery_start :
853                            ktime_get_seconds() - obd->obd_recovery_start);
854                 /* Number of clients that have completed recovery */
855                 seq_printf(m, "completed_clients: %d/%d\n",
856                            atomic_read(&obd->obd_max_recoverable_clients) -
857                            obd->obd_stale_clients,
858                            atomic_read(&obd->obd_max_recoverable_clients));
859                 seq_printf(m, "replayed_requests: %d\n",
860                            obd->obd_replayed_requests);
861                 seq_printf(m, "last_transno: %lld\n",
862                            obd->obd_next_recovery_transno - 1);
863                 seq_printf(m, "VBR: %s\n", obd->obd_version_recov ?
864                            "ENABLED" : "DISABLED");
865                 seq_printf(m, "IR: %s\n", obd->obd_no_ir ?
866                            "DISABLED" : "ENABLED");
867                 goto out;
868         }
869
870         tdtd = obd->u.obt.obt_lut->lut_tdtd;
871         if (tdtd && tdtd->tdtd_show_update_logs_retrievers) {
872                 char *buf;
873                 int size = 0;
874                 int count = 0;
875
876                 buf = tdtd->tdtd_show_update_logs_retrievers(
877                         tdtd->tdtd_show_retrievers_cbdata,
878                         &size, &count);
879                 if (count > 0) {
880                         seq_printf(m, "WAITING\n");
881                         seq_printf(m, "non-ready MDTs: %s\n",
882                                    buf ? buf : "unknown (not enough RAM)");
883                         seq_printf(m, "recovery_start: %lld\n",
884                                    (s64)ktime_get_real_seconds() -
885                                    (ktime_get_seconds() -
886                                     obd->obd_recovery_start));
887                         seq_printf(m, "time_waited: %lld\n",
888                                    (s64)(ktime_get_seconds() -
889                                          obd->obd_recovery_start));
890                 }
891
892                 if (buf != NULL)
893                         OBD_FREE(buf, size);
894
895                 if (likely(count > 0))
896                         goto out;
897         }
898
899         /* recovery won't start until the clients connect */
900         if (obd->obd_recovery_start == 0) {
901                 seq_printf(m, "WAITING_FOR_CLIENTS\n");
902                 goto out;
903         }
904
905         seq_printf(m, "RECOVERING\n");
906         seq_printf(m, "recovery_start: %lld\n", (s64)ktime_get_real_seconds() -
907                    (ktime_get_seconds() - obd->obd_recovery_start));
908         seq_printf(m, "time_remaining: %lld\n",
909                    ktime_get_seconds() >=
910                    obd->obd_recovery_start +
911                    obd->obd_recovery_timeout ? 0 :
912                    (s64)(obd->obd_recovery_start +
913                          obd->obd_recovery_timeout -
914                          ktime_get_seconds()));
915         seq_printf(m, "connected_clients: %d/%d\n",
916                    atomic_read(&obd->obd_connected_clients),
917                    atomic_read(&obd->obd_max_recoverable_clients));
918         /* Number of clients that have completed recovery */
919         seq_printf(m, "req_replay_clients: %d\n",
920                    atomic_read(&obd->obd_req_replay_clients));
921         seq_printf(m, "lock_repay_clients: %d\n",
922                    atomic_read(&obd->obd_lock_replay_clients));
923         seq_printf(m, "completed_clients: %d\n",
924                    atomic_read(&obd->obd_connected_clients) -
925                    atomic_read(&obd->obd_lock_replay_clients));
926         seq_printf(m, "evicted_clients: %d\n", obd->obd_stale_clients);
927         seq_printf(m, "replayed_requests: %d\n", obd->obd_replayed_requests);
928         seq_printf(m, "queued_requests: %d\n",
929                    obd->obd_requests_queued_for_recovery);
930         seq_printf(m, "next_transno: %lld\n",
931                    obd->obd_next_recovery_transno);
932 out:
933         return 0;
934 }
935 EXPORT_SYMBOL(lprocfs_recovery_status_seq_show);
936
937 ssize_t ir_factor_show(struct kobject *kobj, struct attribute *attr,
938                        char *buf)
939 {
940         struct obd_device *obd = container_of(kobj, struct obd_device,
941                                               obd_kset.kobj);
942
943         return scnprintf(buf, PAGE_SIZE, "%d\n", obd->obd_recovery_ir_factor);
944 }
945 EXPORT_SYMBOL(ir_factor_show);
946
947 ssize_t ir_factor_store(struct kobject *kobj, struct attribute *attr,
948                         const char *buffer, size_t count)
949 {
950         struct obd_device *obd = container_of(kobj, struct obd_device,
951                                               obd_kset.kobj);
952         int val;
953         int rc;
954
955         rc = kstrtoint(buffer, 10, &val);
956         if (rc)
957                 return rc;
958
959         if (val < OBD_IR_FACTOR_MIN || val > OBD_IR_FACTOR_MAX)
960                 return -EINVAL;
961
962         obd->obd_recovery_ir_factor = val;
963         return count;
964 }
965 EXPORT_SYMBOL(ir_factor_store);
966
967 int lprocfs_checksum_dump_seq_show(struct seq_file *m, void *data)
968 {
969         struct obd_device *obd = m->private;
970
971         LASSERT(obd != NULL);
972         seq_printf(m, "%d\n", obd->obd_checksum_dump);
973         return 0;
974 }
975 EXPORT_SYMBOL(lprocfs_checksum_dump_seq_show);
976
977 ssize_t
978 lprocfs_checksum_dump_seq_write(struct file *file, const char __user *buffer,
979                             size_t count, loff_t *off)
980 {
981         struct seq_file *m = file->private_data;
982         struct obd_device *obd = m->private;
983         bool val;
984         int rc;
985
986         LASSERT(obd != NULL);
987         rc = kstrtobool_from_user(buffer, count, &val);
988         if (rc)
989                 return rc;
990
991         obd->obd_checksum_dump = val;
992         return count;
993 }
994 EXPORT_SYMBOL(lprocfs_checksum_dump_seq_write);
995
996 ssize_t recovery_time_soft_show(struct kobject *kobj, struct attribute *attr,
997                                 char *buf)
998 {
999         struct obd_device *obd = container_of(kobj, struct obd_device,
1000                                               obd_kset.kobj);
1001
1002         return scnprintf(buf, PAGE_SIZE, "%d\n", obd->obd_recovery_timeout);
1003 }
1004 EXPORT_SYMBOL(recovery_time_soft_show);
1005
1006 ssize_t recovery_time_soft_store(struct kobject *kobj,
1007                                  struct attribute *attr,
1008                                  const char *buffer, size_t count)
1009 {
1010         struct obd_device *obd = container_of(kobj, struct obd_device,
1011                                               obd_kset.kobj);
1012         unsigned int val;
1013         int rc;
1014
1015         rc = kstrtouint(buffer, 0, &val);
1016         if (rc)
1017                 return rc;
1018
1019         obd->obd_recovery_timeout = val;
1020         return count;
1021 }
1022 EXPORT_SYMBOL(recovery_time_soft_store);
1023
1024 ssize_t recovery_time_hard_show(struct kobject *kobj, struct attribute *attr,
1025                                 char *buf)
1026 {
1027         struct obd_device *obd = container_of(kobj, struct obd_device,
1028                                               obd_kset.kobj);
1029
1030         return scnprintf(buf, PAGE_SIZE, "%d\n", obd->obd_recovery_time_hard);
1031 }
1032 EXPORT_SYMBOL(recovery_time_hard_show);
1033
1034 ssize_t recovery_time_hard_store(struct kobject *kobj,
1035                                  struct attribute *attr,
1036                                  const char *buffer, size_t count)
1037 {
1038         struct obd_device *obd = container_of(kobj, struct obd_device,
1039                                               obd_kset.kobj);
1040         unsigned int val;
1041         int rc;
1042
1043         rc = kstrtouint(buffer, 0, &val);
1044         if (rc)
1045                 return rc;
1046
1047         obd->obd_recovery_time_hard = val;
1048         return count;
1049 }
1050 EXPORT_SYMBOL(recovery_time_hard_store);
1051
1052 ssize_t instance_show(struct kobject *kobj, struct attribute *attr,
1053                       char *buf)
1054 {
1055         struct obd_device *obd = container_of(kobj, struct obd_device,
1056                                               obd_kset.kobj);
1057         struct obd_device_target *target = &obd->u.obt;
1058
1059         LASSERT(target->obt_magic == OBT_MAGIC);
1060         return scnprintf(buf, PAGE_SIZE, "%u\n", obd->u.obt.obt_instance);
1061 }
1062 EXPORT_SYMBOL(instance_show);
1063
1064 #endif /* CONFIG_PROC_FS*/