]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/contrib/openzfs/module/os/freebsd/spl/spl_kstat.c
ZFS: MFV 2.0-rc1-gfd20a8
[FreeBSD/FreeBSD.git] / sys / contrib / openzfs / module / os / freebsd / spl / spl_kstat.c
1 /*
2  * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * Links to Illumos.org for more information on kstat function:
27  * [1] https://illumos.org/man/1M/kstat
28  * [2] https://illumos.org/man/9f/kstat_create
29  */
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/kernel.h>
37 #include <sys/systm.h>
38 #include <sys/malloc.h>
39 #include <sys/sysctl.h>
40 #include <sys/kstat.h>
41 #include <sys/sbuf.h>
42
43 static MALLOC_DEFINE(M_KSTAT, "kstat_data", "Kernel statistics");
44
45 SYSCTL_ROOT_NODE(OID_AUTO, kstat, CTLFLAG_RW, 0, "Kernel statistics");
46
47 void
48 __kstat_set_raw_ops(kstat_t *ksp,
49     int (*headers)(char *buf, size_t size),
50     int (*data)(char *buf, size_t size, void *data),
51     void *(*addr)(kstat_t *ksp, loff_t index))
52 {
53         ksp->ks_raw_ops.headers = headers;
54         ksp->ks_raw_ops.data    = data;
55         ksp->ks_raw_ops.addr    = addr;
56 }
57
58 static int
59 kstat_default_update(kstat_t *ksp, int rw)
60 {
61         ASSERT(ksp != NULL);
62
63         if (rw == KSTAT_WRITE)
64                 return (EACCES);
65
66         return (0);
67 }
68
69 static int
70 kstat_resize_raw(kstat_t *ksp)
71 {
72         if (ksp->ks_raw_bufsize == KSTAT_RAW_MAX)
73                 return (ENOMEM);
74
75         free(ksp->ks_raw_buf, M_TEMP);
76         ksp->ks_raw_bufsize = MIN(ksp->ks_raw_bufsize * 2, KSTAT_RAW_MAX);
77         ksp->ks_raw_buf = malloc(ksp->ks_raw_bufsize, M_TEMP, M_WAITOK);
78
79         return (0);
80 }
81
82 static void *
83 kstat_raw_default_addr(kstat_t *ksp, loff_t n)
84 {
85         if (n == 0)
86                 return (ksp->ks_data);
87         return (NULL);
88 }
89
90 static int
91 kstat_sysctl(SYSCTL_HANDLER_ARGS)
92 {
93         kstat_t *ksp = arg1;
94         kstat_named_t *ksent;
95         uint64_t val;
96
97         ksent = ksp->ks_data;
98         /* Select the correct element */
99         ksent += arg2;
100         /* Update the aggsums before reading */
101         (void) ksp->ks_update(ksp, KSTAT_READ);
102         val = ksent->value.ui64;
103
104         return (sysctl_handle_64(oidp, &val, 0, req));
105 }
106
107 static int
108 kstat_sysctl_string(SYSCTL_HANDLER_ARGS)
109 {
110         kstat_t *ksp = arg1;
111         kstat_named_t *ksent = ksp->ks_data;
112         char *val;
113         uint32_t len = 0;
114
115         /* Select the correct element */
116         ksent += arg2;
117         /* Update the aggsums before reading */
118         (void) ksp->ks_update(ksp, KSTAT_READ);
119         val = KSTAT_NAMED_STR_PTR(ksent);
120         len = KSTAT_NAMED_STR_BUFLEN(ksent);
121         val[len-1] = '\0';
122
123         return (sysctl_handle_string(oidp, val, len, req));
124 }
125
126 static int
127 kstat_sysctl_io(SYSCTL_HANDLER_ARGS)
128 {
129         struct sbuf *sb;
130         kstat_t *ksp = arg1;
131         kstat_io_t *kip = ksp->ks_data;
132         int rc;
133
134         sb = sbuf_new_auto();
135         if (sb == NULL)
136                 return (ENOMEM);
137         /* Update the aggsums before reading */
138         (void) ksp->ks_update(ksp, KSTAT_READ);
139
140         /* though wlentime & friends are signed, they will never be negative */
141         sbuf_printf(sb,
142             "%-8llu %-8llu %-8u %-8u %-8llu %-8llu "
143             "%-8llu %-8llu %-8llu %-8llu %-8u %-8u\n",
144             kip->nread, kip->nwritten,
145             kip->reads, kip->writes,
146             kip->wtime, kip->wlentime, kip->wlastupdate,
147             kip->rtime, kip->rlentime, kip->rlastupdate,
148             kip->wcnt,  kip->rcnt);
149         rc = sbuf_finish(sb);
150         if (rc == 0)
151                 rc = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb));
152         sbuf_delete(sb);
153         return (rc);
154 }
155
156 static int
157 kstat_sysctl_raw(SYSCTL_HANDLER_ARGS)
158 {
159         struct sbuf *sb;
160         void *data;
161         kstat_t *ksp = arg1;
162         void *(*addr_op)(kstat_t *ksp, loff_t index);
163         int n, rc = 0;
164
165         sb = sbuf_new_auto();
166         if (sb == NULL)
167                 return (ENOMEM);
168
169         if (ksp->ks_raw_ops.addr)
170                 addr_op = ksp->ks_raw_ops.addr;
171         else
172                 addr_op = kstat_raw_default_addr;
173
174         mutex_enter(ksp->ks_lock);
175
176         /* Update the aggsums before reading */
177         (void) ksp->ks_update(ksp, KSTAT_READ);
178
179         ksp->ks_raw_bufsize = PAGE_SIZE;
180         ksp->ks_raw_buf = malloc(PAGE_SIZE, M_TEMP, M_WAITOK);
181
182         n = 0;
183 restart_headers:
184         if (ksp->ks_raw_ops.headers) {
185                 rc = ksp->ks_raw_ops.headers(
186                     ksp->ks_raw_buf, ksp->ks_raw_bufsize);
187                 if (rc == ENOMEM && !kstat_resize_raw(ksp))
188                         goto restart_headers;
189                 if (rc == 0)
190                         sbuf_printf(sb, "%s", ksp->ks_raw_buf);
191         }
192
193         while ((data = addr_op(ksp, n)) != NULL) {
194 restart:
195                 if (ksp->ks_raw_ops.data) {
196                         rc = ksp->ks_raw_ops.data(ksp->ks_raw_buf,
197                             ksp->ks_raw_bufsize, data);
198                         if (rc == ENOMEM && !kstat_resize_raw(ksp))
199                                 goto restart;
200                         if (rc == 0)
201                                 sbuf_printf(sb, "%s", ksp->ks_raw_buf);
202
203                 } else {
204                         ASSERT(ksp->ks_ndata == 1);
205                         sbuf_hexdump(sb, ksp->ks_data,
206                             ksp->ks_data_size, NULL, 0);
207                 }
208                 n++;
209         }
210         free(ksp->ks_raw_buf, M_TEMP);
211         mutex_exit(ksp->ks_lock);
212         rc = sbuf_finish(sb);
213         if (rc == 0)
214                 rc = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb));
215         sbuf_delete(sb);
216         return (rc);
217 }
218
219 kstat_t *
220 __kstat_create(const char *module, int instance, const char *name,
221     const char *class, uchar_t ks_type, uint_t ks_ndata, uchar_t flags)
222 {
223         struct sysctl_oid *root;
224         kstat_t *ksp;
225
226         KASSERT(instance == 0, ("instance=%d", instance));
227         if ((ks_type == KSTAT_TYPE_INTR) || (ks_type == KSTAT_TYPE_IO))
228                 ASSERT(ks_ndata == 1);
229
230         /*
231          * Allocate the main structure. We don't need to copy module/class/name
232          * stuff in here, because it is only used for sysctl node creation
233          * done in this function.
234          */
235         ksp = malloc(sizeof (*ksp), M_KSTAT, M_WAITOK|M_ZERO);
236
237         ksp->ks_crtime = gethrtime();
238         ksp->ks_snaptime = ksp->ks_crtime;
239         ksp->ks_instance = instance;
240         strncpy(ksp->ks_name, name, KSTAT_STRLEN);
241         strncpy(ksp->ks_class, class, KSTAT_STRLEN);
242         ksp->ks_type = ks_type;
243         ksp->ks_flags = flags;
244         ksp->ks_update = kstat_default_update;
245
246         mutex_init(&ksp->ks_private_lock, NULL, MUTEX_DEFAULT, NULL);
247         ksp->ks_lock = &ksp->ks_private_lock;
248
249         switch (ksp->ks_type) {
250                 case KSTAT_TYPE_RAW:
251                         ksp->ks_ndata = 1;
252                         ksp->ks_data_size = ks_ndata;
253                         break;
254                 case KSTAT_TYPE_NAMED:
255                         ksp->ks_ndata = ks_ndata;
256                         ksp->ks_data_size = ks_ndata * sizeof (kstat_named_t);
257                         break;
258                 case KSTAT_TYPE_INTR:
259                         ksp->ks_ndata = ks_ndata;
260                         ksp->ks_data_size = ks_ndata * sizeof (kstat_intr_t);
261                         break;
262                 case KSTAT_TYPE_IO:
263                         ksp->ks_ndata = ks_ndata;
264                         ksp->ks_data_size = ks_ndata * sizeof (kstat_io_t);
265                         break;
266                 case KSTAT_TYPE_TIMER:
267                         ksp->ks_ndata = ks_ndata;
268                         ksp->ks_data_size = ks_ndata * sizeof (kstat_timer_t);
269                         break;
270                 default:
271                         panic("Undefined kstat type %d\n", ksp->ks_type);
272         }
273
274         if (ksp->ks_flags & KSTAT_FLAG_VIRTUAL) {
275                 ksp->ks_data = NULL;
276         } else {
277                 ksp->ks_data = kmem_zalloc(ksp->ks_data_size, KM_SLEEP);
278                 if (ksp->ks_data == NULL) {
279                         kmem_free(ksp, sizeof (*ksp));
280                         ksp = NULL;
281                 }
282         }
283         /*
284          * Create sysctl tree for those statistics:
285          *
286          *      kstat.<module>.<class>.<name>.
287          */
288         sysctl_ctx_init(&ksp->ks_sysctl_ctx);
289         root = SYSCTL_ADD_NODE(&ksp->ks_sysctl_ctx,
290             SYSCTL_STATIC_CHILDREN(_kstat), OID_AUTO, module, CTLFLAG_RW, 0,
291             "");
292         if (root == NULL) {
293                 printf("%s: Cannot create kstat.%s tree!\n", __func__, module);
294                 sysctl_ctx_free(&ksp->ks_sysctl_ctx);
295                 free(ksp, M_KSTAT);
296                 return (NULL);
297         }
298         root = SYSCTL_ADD_NODE(&ksp->ks_sysctl_ctx, SYSCTL_CHILDREN(root),
299             OID_AUTO, class, CTLFLAG_RW, 0, "");
300         if (root == NULL) {
301                 printf("%s: Cannot create kstat.%s.%s tree!\n", __func__,
302                     module, class);
303                 sysctl_ctx_free(&ksp->ks_sysctl_ctx);
304                 free(ksp, M_KSTAT);
305                 return (NULL);
306         }
307         if (ksp->ks_type == KSTAT_TYPE_NAMED) {
308                 root = SYSCTL_ADD_NODE(&ksp->ks_sysctl_ctx,
309                     SYSCTL_CHILDREN(root),
310                     OID_AUTO, name, CTLFLAG_RW, 0, "");
311                 if (root == NULL) {
312                         printf("%s: Cannot create kstat.%s.%s.%s tree!\n",
313                             __func__, module, class, name);
314                         sysctl_ctx_free(&ksp->ks_sysctl_ctx);
315                         free(ksp, M_KSTAT);
316                         return (NULL);
317                 }
318
319         }
320         ksp->ks_sysctl_root = root;
321
322         return (ksp);
323 }
324
325 static void
326 kstat_install_named(kstat_t *ksp)
327 {
328         kstat_named_t *ksent;
329         char *namelast;
330         int typelast;
331
332         ksent = ksp->ks_data;
333
334         VERIFY((ksp->ks_flags & KSTAT_FLAG_VIRTUAL) || ksent != NULL);
335
336         typelast = 0;
337         namelast = NULL;
338
339         for (int i = 0; i < ksp->ks_ndata; i++, ksent++) {
340                 if (ksent->data_type != 0) {
341                         typelast = ksent->data_type;
342                         namelast = ksent->name;
343                 }
344                 switch (typelast) {
345                         case KSTAT_DATA_CHAR:
346                                 /* Not Implemented */
347                                 break;
348                         case KSTAT_DATA_INT32:
349                                 SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
350                                     SYSCTL_CHILDREN(ksp->ks_sysctl_root),
351                                     OID_AUTO, namelast,
352                                     CTLTYPE_S32 | CTLFLAG_RD, ksp, i,
353                                     kstat_sysctl, "I", namelast);
354                                 break;
355                         case KSTAT_DATA_UINT32:
356                                 SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
357                                     SYSCTL_CHILDREN(ksp->ks_sysctl_root),
358                                     OID_AUTO, namelast,
359                                     CTLTYPE_U32 | CTLFLAG_RD, ksp, i,
360                                     kstat_sysctl, "IU", namelast);
361                                 break;
362                         case KSTAT_DATA_INT64:
363                                 SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
364                                     SYSCTL_CHILDREN(ksp->ks_sysctl_root),
365                                     OID_AUTO, namelast,
366                                     CTLTYPE_S64 | CTLFLAG_RD, ksp, i,
367                                     kstat_sysctl, "Q", namelast);
368                                 break;
369                         case KSTAT_DATA_UINT64:
370                                 SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
371                                     SYSCTL_CHILDREN(ksp->ks_sysctl_root),
372                                     OID_AUTO, namelast,
373                                     CTLTYPE_U64 | CTLFLAG_RD, ksp, i,
374                                     kstat_sysctl, "QU", namelast);
375                                 break;
376                         case KSTAT_DATA_LONG:
377                                 SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
378                                     SYSCTL_CHILDREN(ksp->ks_sysctl_root),
379                                     OID_AUTO, namelast,
380                                     CTLTYPE_LONG | CTLFLAG_RD, ksp, i,
381                                     kstat_sysctl, "L", namelast);
382                                 break;
383                         case KSTAT_DATA_ULONG:
384                                 SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
385                                     SYSCTL_CHILDREN(ksp->ks_sysctl_root),
386                                     OID_AUTO, namelast,
387                                     CTLTYPE_ULONG | CTLFLAG_RD, ksp, i,
388                                     kstat_sysctl, "LU", namelast);
389                                 break;
390                         case KSTAT_DATA_STRING:
391                                 SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
392                                     SYSCTL_CHILDREN(ksp->ks_sysctl_root),
393                                     OID_AUTO, namelast,
394                                     CTLTYPE_STRING | CTLFLAG_RD, ksp, i,
395                                     kstat_sysctl_string, "A", namelast);
396                                 break;
397                         default:
398                                 panic("unsupported type: %d", typelast);
399                 }
400
401         }
402
403 }
404
405 void
406 kstat_install(kstat_t *ksp)
407 {
408         struct sysctl_oid *root;
409
410         if (ksp->ks_ndata == UINT32_MAX)
411                 VERIFY(ksp->ks_type == KSTAT_TYPE_RAW);
412
413         switch (ksp->ks_type) {
414                 case KSTAT_TYPE_NAMED:
415                         return (kstat_install_named(ksp));
416                         break;
417                 case KSTAT_TYPE_RAW:
418                         if (ksp->ks_raw_ops.data) {
419                                 root = SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
420                                     SYSCTL_CHILDREN(ksp->ks_sysctl_root),
421                                     OID_AUTO, ksp->ks_name,
422                                     CTLTYPE_STRING | CTLFLAG_RD, ksp, 0,
423                                     kstat_sysctl_raw, "A", ksp->ks_name);
424                         } else {
425                                 root = SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
426                                     SYSCTL_CHILDREN(ksp->ks_sysctl_root),
427                                     OID_AUTO, ksp->ks_name,
428                                     CTLTYPE_OPAQUE | CTLFLAG_RD, ksp, 0,
429                                     kstat_sysctl_raw, "", ksp->ks_name);
430                         }
431                         VERIFY(root != NULL);
432                         break;
433                 case KSTAT_TYPE_IO:
434                         root = SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
435                             SYSCTL_CHILDREN(ksp->ks_sysctl_root),
436                             OID_AUTO, ksp->ks_name,
437                             CTLTYPE_STRING | CTLFLAG_RD, ksp, 0,
438                             kstat_sysctl_io, "A", ksp->ks_name);
439                         break;
440                 case KSTAT_TYPE_TIMER:
441                 case KSTAT_TYPE_INTR:
442                 default:
443                         panic("unsupported kstat type %d\n", ksp->ks_type);
444         }
445         ksp->ks_sysctl_root = root;
446
447 }
448
449 void
450 kstat_delete(kstat_t *ksp)
451 {
452
453         sysctl_ctx_free(&ksp->ks_sysctl_ctx);
454         ksp->ks_lock = NULL;
455         mutex_destroy(&ksp->ks_private_lock);
456         free(ksp, M_KSTAT);
457 }
458
459 void
460 kstat_waitq_enter(kstat_io_t *kiop)
461 {
462         hrtime_t new, delta;
463         ulong_t wcnt;
464
465         new = gethrtime();
466         delta = new - kiop->wlastupdate;
467         kiop->wlastupdate = new;
468         wcnt = kiop->wcnt++;
469         if (wcnt != 0) {
470                 kiop->wlentime += delta * wcnt;
471                 kiop->wtime += delta;
472         }
473 }
474
475 void
476 kstat_waitq_exit(kstat_io_t *kiop)
477 {
478         hrtime_t new, delta;
479         ulong_t wcnt;
480
481         new = gethrtime();
482         delta = new - kiop->wlastupdate;
483         kiop->wlastupdate = new;
484         wcnt = kiop->wcnt--;
485         ASSERT((int)wcnt > 0);
486         kiop->wlentime += delta * wcnt;
487         kiop->wtime += delta;
488 }
489
490 void
491 kstat_runq_enter(kstat_io_t *kiop)
492 {
493         hrtime_t new, delta;
494         ulong_t rcnt;
495
496         new = gethrtime();
497         delta = new - kiop->rlastupdate;
498         kiop->rlastupdate = new;
499         rcnt = kiop->rcnt++;
500         if (rcnt != 0) {
501                 kiop->rlentime += delta * rcnt;
502                 kiop->rtime += delta;
503         }
504 }
505
506 void
507 kstat_runq_exit(kstat_io_t *kiop)
508 {
509         hrtime_t new, delta;
510         ulong_t rcnt;
511
512         new = gethrtime();
513         delta = new - kiop->rlastupdate;
514         kiop->rlastupdate = new;
515         rcnt = kiop->rcnt--;
516         ASSERT((int)rcnt > 0);
517         kiop->rlentime += delta * rcnt;
518         kiop->rtime += delta;
519 }