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/types.h> 32 #include <sys/param.h> 33 #include <sys/kernel.h> 34 #include <sys/systm.h> 35 #include <sys/malloc.h> 36 #include <sys/sysctl.h> 37 #include <sys/kstat.h> 38 #include <sys/sbuf.h> 39 #include <sys/zone.h> 40 41 static MALLOC_DEFINE(M_KSTAT, "kstat_data", "Kernel statistics"); 42 43 SYSCTL_ROOT_NODE(OID_AUTO, kstat, CTLFLAG_RW, 0, "Kernel statistics"); 44 45 void 46 __kstat_set_raw_ops(kstat_t *ksp, 47 int (*headers)(char *buf, size_t size), 48 int (*data)(char *buf, size_t size, void *data), 49 void *(*addr)(kstat_t *ksp, loff_t index)) 50 { 51 ksp->ks_raw_ops.headers = headers; 52 ksp->ks_raw_ops.data = data; 53 ksp->ks_raw_ops.addr = addr; 54 } 55 56 void 57 __kstat_set_seq_raw_ops(kstat_t *ksp, 58 int (*headers)(struct seq_file *f), 59 int (*data)(char *buf, size_t size, void *data), 60 void *(*addr)(kstat_t *ksp, loff_t index)) 61 { 62 ksp->ks_raw_ops.seq_headers = headers; 63 ksp->ks_raw_ops.data = data; 64 ksp->ks_raw_ops.addr = addr; 65 } 66 67 static int 68 kstat_default_update(kstat_t *ksp, int rw) 69 { 70 ASSERT3P(ksp, !=, NULL); 71 72 if (rw == KSTAT_WRITE) 73 return (EACCES); 74 75 return (0); 76 } 77 78 static int 79 kstat_resize_raw(kstat_t *ksp) 80 { 81 if (ksp->ks_raw_bufsize == KSTAT_RAW_MAX) 82 return (ENOMEM); 83 84 free(ksp->ks_raw_buf, M_TEMP); 85 ksp->ks_raw_bufsize = MIN(ksp->ks_raw_bufsize * 2, KSTAT_RAW_MAX); 86 ksp->ks_raw_buf = malloc(ksp->ks_raw_bufsize, M_TEMP, M_WAITOK); 87 88 return (0); 89 } 90 91 static void * 92 kstat_raw_default_addr(kstat_t *ksp, loff_t n) 93 { 94 if (n == 0) 95 return (ksp->ks_data); 96 return (NULL); 97 } 98 99 static int 100 kstat_sysctl(SYSCTL_HANDLER_ARGS) 101 { 102 kstat_t *ksp = arg1; 103 kstat_named_t *ksent; 104 uint64_t val; 105 106 ksent = ksp->ks_data; 107 /* Select the correct element */ 108 ksent += arg2; 109 /* Update the aggsums before reading */ 110 (void) ksp->ks_update(ksp, KSTAT_READ); 111 val = ksent->value.ui64; 112 113 return (sysctl_handle_64(oidp, &val, 0, req)); 114 } 115 116 static int 117 kstat_sysctl_string(SYSCTL_HANDLER_ARGS) 118 { 119 kstat_t *ksp = arg1; 120 kstat_named_t *ksent = ksp->ks_data; 121 char *val; 122 uint32_t len = 0; 123 124 /* Select the correct element */ 125 ksent += arg2; 126 /* Update the aggsums before reading */ 127 (void) ksp->ks_update(ksp, KSTAT_READ); 128 val = KSTAT_NAMED_STR_PTR(ksent); 129 len = KSTAT_NAMED_STR_BUFLEN(ksent); 130 val[len-1] = '\0'; 131 132 return (sysctl_handle_string(oidp, val, len, req)); 133 } 134 135 static int 136 kstat_sysctl_dataset(SYSCTL_HANDLER_ARGS) 137 { 138 kstat_t *ksp = arg1; 139 kstat_named_t *ksent; 140 kstat_named_t *ksent_ds; 141 uint64_t val; 142 char *ds_name; 143 uint32_t ds_len = 0; 144 145 ksent_ds = ksent = ksp->ks_data; 146 ds_name = KSTAT_NAMED_STR_PTR(ksent_ds); 147 ds_len = KSTAT_NAMED_STR_BUFLEN(ksent_ds); 148 ds_name[ds_len-1] = '\0'; 149 150 if (!zone_dataset_visible(ds_name, NULL)) { 151 return (EPERM); 152 } 153 154 /* Select the correct element */ 155 ksent += arg2; 156 /* Update the aggsums before reading */ 157 (void) ksp->ks_update(ksp, KSTAT_READ); 158 val = ksent->value.ui64; 159 160 return (sysctl_handle_64(oidp, &val, 0, req)); 161 } 162 163 static int 164 kstat_sysctl_dataset_string(SYSCTL_HANDLER_ARGS) 165 { 166 kstat_t *ksp = arg1; 167 kstat_named_t *ksent = ksp->ks_data; 168 char *val; 169 uint32_t len = 0; 170 171 /* Select the correct element */ 172 ksent += arg2; 173 val = KSTAT_NAMED_STR_PTR(ksent); 174 len = KSTAT_NAMED_STR_BUFLEN(ksent); 175 val[len-1] = '\0'; 176 177 if (!zone_dataset_visible(val, NULL)) { 178 return (EPERM); 179 } 180 181 return (sysctl_handle_string(oidp, val, len, req)); 182 } 183 184 static int 185 kstat_sysctl_io(SYSCTL_HANDLER_ARGS) 186 { 187 struct sbuf sb; 188 kstat_t *ksp = arg1; 189 kstat_io_t *kip = ksp->ks_data; 190 int rc; 191 192 sbuf_new_for_sysctl(&sb, NULL, 0, req); 193 194 /* Update the aggsums before reading */ 195 (void) ksp->ks_update(ksp, KSTAT_READ); 196 197 /* though wlentime & friends are signed, they will never be negative */ 198 sbuf_printf(&sb, 199 "%-8llu %-8llu %-8u %-8u %-8llu %-8llu " 200 "%-8llu %-8llu %-8llu %-8llu %-8u %-8u\n", 201 kip->nread, kip->nwritten, 202 kip->reads, kip->writes, 203 kip->wtime, kip->wlentime, kip->wlastupdate, 204 kip->rtime, kip->rlentime, kip->rlastupdate, 205 kip->wcnt, kip->rcnt); 206 rc = sbuf_finish(&sb); 207 sbuf_delete(&sb); 208 return (rc); 209 } 210 211 static int 212 kstat_sysctl_raw(SYSCTL_HANDLER_ARGS) 213 { 214 struct sbuf sb; 215 void *data; 216 kstat_t *ksp = arg1; 217 void *(*addr_op)(kstat_t *ksp, loff_t index); 218 int n, has_header, rc = 0; 219 220 sbuf_new_for_sysctl(&sb, NULL, PAGE_SIZE, req); 221 222 if (ksp->ks_raw_ops.addr) 223 addr_op = ksp->ks_raw_ops.addr; 224 else 225 addr_op = kstat_raw_default_addr; 226 227 mutex_enter(ksp->ks_lock); 228 229 /* Update the aggsums before reading */ 230 (void) ksp->ks_update(ksp, KSTAT_READ); 231 232 ksp->ks_raw_bufsize = PAGE_SIZE; 233 ksp->ks_raw_buf = malloc(PAGE_SIZE, M_TEMP, M_WAITOK); 234 235 n = 0; 236 has_header = (ksp->ks_raw_ops.headers || 237 ksp->ks_raw_ops.seq_headers); 238 239 restart_headers: 240 if (ksp->ks_raw_ops.headers) { 241 rc = ksp->ks_raw_ops.headers( 242 ksp->ks_raw_buf, ksp->ks_raw_bufsize); 243 } else if (ksp->ks_raw_ops.seq_headers) { 244 struct seq_file f; 245 246 f.sf_buf = ksp->ks_raw_buf; 247 f.sf_size = ksp->ks_raw_bufsize; 248 rc = ksp->ks_raw_ops.seq_headers(&f); 249 } 250 if (has_header) { 251 if (rc == ENOMEM && !kstat_resize_raw(ksp)) 252 goto restart_headers; 253 if (rc == 0) { 254 sbuf_cat(&sb, "\n"); 255 sbuf_cat(&sb, ksp->ks_raw_buf); 256 } 257 } 258 259 while ((data = addr_op(ksp, n)) != NULL) { 260 restart: 261 if (ksp->ks_raw_ops.data) { 262 rc = ksp->ks_raw_ops.data(ksp->ks_raw_buf, 263 ksp->ks_raw_bufsize, data); 264 if (rc == ENOMEM && !kstat_resize_raw(ksp)) 265 goto restart; 266 if (rc == 0) 267 sbuf_cat(&sb, ksp->ks_raw_buf); 268 269 } else { 270 ASSERT3U(ksp->ks_ndata, ==, 1); 271 sbuf_hexdump(&sb, ksp->ks_data, 272 ksp->ks_data_size, NULL, 0); 273 } 274 n++; 275 } 276 free(ksp->ks_raw_buf, M_TEMP); 277 mutex_exit(ksp->ks_lock); 278 rc = sbuf_finish(&sb); 279 sbuf_delete(&sb); 280 return (rc); 281 } 282 283 kstat_t * 284 __kstat_create(const char *module, int instance, const char *name, 285 const char *class, uchar_t ks_type, uint_t ks_ndata, uchar_t flags) 286 { 287 char buf[KSTAT_STRLEN]; 288 struct sysctl_oid *root; 289 kstat_t *ksp; 290 char *pool; 291 292 KASSERT(instance == 0, ("instance=%d", instance)); 293 if ((ks_type == KSTAT_TYPE_INTR) || (ks_type == KSTAT_TYPE_IO)) 294 ASSERT3U(ks_ndata, ==, 1); 295 296 if (class == NULL) 297 class = "misc"; 298 299 /* 300 * Allocate the main structure. We don't need to keep a copy of 301 * module in here, because it is only used for sysctl node creation 302 * done in this function. 303 */ 304 ksp = malloc(sizeof (*ksp), M_KSTAT, M_WAITOK|M_ZERO); 305 306 ksp->ks_crtime = gethrtime(); 307 ksp->ks_snaptime = ksp->ks_crtime; 308 ksp->ks_instance = instance; 309 (void) strlcpy(ksp->ks_name, name, KSTAT_STRLEN); 310 (void) strlcpy(ksp->ks_class, class, KSTAT_STRLEN); 311 ksp->ks_type = ks_type; 312 ksp->ks_flags = flags; 313 ksp->ks_update = kstat_default_update; 314 315 mutex_init(&ksp->ks_private_lock, NULL, MUTEX_DEFAULT, NULL); 316 ksp->ks_lock = &ksp->ks_private_lock; 317 318 switch (ksp->ks_type) { 319 case KSTAT_TYPE_RAW: 320 ksp->ks_ndata = 1; 321 ksp->ks_data_size = ks_ndata; 322 break; 323 case KSTAT_TYPE_NAMED: 324 ksp->ks_ndata = ks_ndata; 325 ksp->ks_data_size = ks_ndata * sizeof (kstat_named_t); 326 break; 327 case KSTAT_TYPE_INTR: 328 ksp->ks_ndata = ks_ndata; 329 ksp->ks_data_size = ks_ndata * sizeof (kstat_intr_t); 330 break; 331 case KSTAT_TYPE_IO: 332 ksp->ks_ndata = ks_ndata; 333 ksp->ks_data_size = ks_ndata * sizeof (kstat_io_t); 334 break; 335 case KSTAT_TYPE_TIMER: 336 ksp->ks_ndata = ks_ndata; 337 ksp->ks_data_size = ks_ndata * sizeof (kstat_timer_t); 338 break; 339 default: 340 panic("Undefined kstat type %d\n", ksp->ks_type); 341 } 342 343 if (ksp->ks_flags & KSTAT_FLAG_VIRTUAL) 344 ksp->ks_data = NULL; 345 else 346 ksp->ks_data = kmem_zalloc(ksp->ks_data_size, KM_SLEEP); 347 348 /* 349 * Some kstats use a module name like "zfs/poolname" to distinguish a 350 * set of kstats belonging to a specific pool. Split on '/' to add an 351 * extra node for the pool name if needed. 352 */ 353 (void) strlcpy(buf, module, KSTAT_STRLEN); 354 module = buf; 355 pool = strchr(module, '/'); 356 if (pool != NULL) 357 *pool++ = '\0'; 358 359 /* 360 * Create sysctl tree for those statistics: 361 * 362 * kstat.<module>[.<pool>].<class>.<name> 363 */ 364 sysctl_ctx_init(&ksp->ks_sysctl_ctx); 365 root = SYSCTL_ADD_NODE(&ksp->ks_sysctl_ctx, 366 SYSCTL_STATIC_CHILDREN(_kstat), OID_AUTO, module, CTLFLAG_RW, 0, 367 ""); 368 if (root == NULL) { 369 printf("%s: Cannot create kstat.%s tree!\n", __func__, module); 370 sysctl_ctx_free(&ksp->ks_sysctl_ctx); 371 free(ksp, M_KSTAT); 372 return (NULL); 373 } 374 if (pool != NULL) { 375 root = SYSCTL_ADD_NODE(&ksp->ks_sysctl_ctx, 376 SYSCTL_CHILDREN(root), OID_AUTO, pool, CTLFLAG_RW, 0, ""); 377 if (root == NULL) { 378 printf("%s: Cannot create kstat.%s.%s tree!\n", 379 __func__, module, pool); 380 sysctl_ctx_free(&ksp->ks_sysctl_ctx); 381 free(ksp, M_KSTAT); 382 return (NULL); 383 } 384 } 385 root = SYSCTL_ADD_NODE(&ksp->ks_sysctl_ctx, SYSCTL_CHILDREN(root), 386 OID_AUTO, class, CTLFLAG_RW, 0, ""); 387 if (root == NULL) { 388 if (pool != NULL) 389 printf("%s: Cannot create kstat.%s.%s.%s tree!\n", 390 __func__, module, pool, class); 391 else 392 printf("%s: Cannot create kstat.%s.%s tree!\n", 393 __func__, module, class); 394 sysctl_ctx_free(&ksp->ks_sysctl_ctx); 395 free(ksp, M_KSTAT); 396 return (NULL); 397 } 398 if (ksp->ks_type == KSTAT_TYPE_NAMED) { 399 root = SYSCTL_ADD_NODE(&ksp->ks_sysctl_ctx, 400 SYSCTL_CHILDREN(root), 401 OID_AUTO, name, CTLFLAG_RW, 0, ""); 402 if (root == NULL) { 403 if (pool != NULL) 404 printf("%s: Cannot create kstat.%s.%s.%s.%s " 405 "tree!\n", __func__, module, pool, class, 406 name); 407 else 408 printf("%s: Cannot create kstat.%s.%s.%s " 409 "tree!\n", __func__, module, class, name); 410 sysctl_ctx_free(&ksp->ks_sysctl_ctx); 411 free(ksp, M_KSTAT); 412 return (NULL); 413 } 414 415 } 416 ksp->ks_sysctl_root = root; 417 418 return (ksp); 419 } 420 421 static void 422 kstat_install_named(kstat_t *ksp) 423 { 424 kstat_named_t *ksent; 425 char *namelast; 426 int typelast; 427 428 ksent = ksp->ks_data; 429 430 VERIFY((ksp->ks_flags & KSTAT_FLAG_VIRTUAL) || ksent != NULL); 431 432 typelast = 0; 433 namelast = NULL; 434 435 for (int i = 0; i < ksp->ks_ndata; i++, ksent++) { 436 if (ksent->data_type != 0) { 437 typelast = ksent->data_type; 438 namelast = ksent->name; 439 } 440 switch (typelast) { 441 case KSTAT_DATA_CHAR: 442 /* Not Implemented */ 443 break; 444 case KSTAT_DATA_INT32: 445 SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, 446 SYSCTL_CHILDREN(ksp->ks_sysctl_root), 447 OID_AUTO, namelast, 448 CTLTYPE_S32 | CTLFLAG_RD | CTLFLAG_MPSAFE, 449 ksp, i, kstat_sysctl, "I", namelast); 450 break; 451 case KSTAT_DATA_UINT32: 452 SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, 453 SYSCTL_CHILDREN(ksp->ks_sysctl_root), 454 OID_AUTO, namelast, 455 CTLTYPE_U32 | CTLFLAG_RD | CTLFLAG_MPSAFE, 456 ksp, i, kstat_sysctl, "IU", namelast); 457 break; 458 case KSTAT_DATA_INT64: 459 SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, 460 SYSCTL_CHILDREN(ksp->ks_sysctl_root), 461 OID_AUTO, namelast, 462 CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, 463 ksp, i, kstat_sysctl, "Q", namelast); 464 break; 465 case KSTAT_DATA_UINT64: 466 if (strcmp(ksp->ks_class, "dataset") == 0) { 467 SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, 468 SYSCTL_CHILDREN(ksp->ks_sysctl_root), 469 OID_AUTO, namelast, 470 CTLTYPE_U64 | CTLFLAG_RD | CTLFLAG_MPSAFE, 471 ksp, i, kstat_sysctl_dataset, "QU", 472 namelast); 473 } else { 474 SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, 475 SYSCTL_CHILDREN(ksp->ks_sysctl_root), 476 OID_AUTO, namelast, 477 CTLTYPE_U64 | CTLFLAG_RD | CTLFLAG_MPSAFE, 478 ksp, i, kstat_sysctl, "QU", namelast); 479 } 480 break; 481 case KSTAT_DATA_LONG: 482 SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, 483 SYSCTL_CHILDREN(ksp->ks_sysctl_root), 484 OID_AUTO, namelast, 485 CTLTYPE_LONG | CTLFLAG_RD | CTLFLAG_MPSAFE, 486 ksp, i, kstat_sysctl, "L", namelast); 487 break; 488 case KSTAT_DATA_ULONG: 489 SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, 490 SYSCTL_CHILDREN(ksp->ks_sysctl_root), 491 OID_AUTO, namelast, 492 CTLTYPE_ULONG | CTLFLAG_RD | CTLFLAG_MPSAFE, 493 ksp, i, kstat_sysctl, "LU", namelast); 494 break; 495 case KSTAT_DATA_STRING: 496 if (strcmp(ksp->ks_class, "dataset") == 0) { 497 SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, 498 SYSCTL_CHILDREN(ksp->ks_sysctl_root), 499 OID_AUTO, namelast, CTLTYPE_STRING | 500 CTLFLAG_RD | CTLFLAG_MPSAFE, 501 ksp, i, kstat_sysctl_dataset_string, "A", 502 namelast); 503 } else { 504 SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, 505 SYSCTL_CHILDREN(ksp->ks_sysctl_root), 506 OID_AUTO, namelast, CTLTYPE_STRING | 507 CTLFLAG_RD | CTLFLAG_MPSAFE, 508 ksp, i, kstat_sysctl_string, "A", 509 namelast); 510 } 511 break; 512 default: 513 panic("unsupported type: %d", typelast); 514 } 515 } 516 } 517 518 void 519 kstat_install(kstat_t *ksp) 520 { 521 struct sysctl_oid *root; 522 523 if (ksp->ks_ndata == UINT32_MAX) 524 VERIFY3U(ksp->ks_type, ==, KSTAT_TYPE_RAW); 525 526 switch (ksp->ks_type) { 527 case KSTAT_TYPE_NAMED: 528 return (kstat_install_named(ksp)); 529 case KSTAT_TYPE_RAW: 530 if (ksp->ks_raw_ops.data) { 531 root = SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, 532 SYSCTL_CHILDREN(ksp->ks_sysctl_root), 533 OID_AUTO, ksp->ks_name, CTLTYPE_STRING | CTLFLAG_RD 534 | CTLFLAG_MPSAFE | CTLFLAG_SKIP, 535 ksp, 0, kstat_sysctl_raw, "A", ksp->ks_name); 536 } else { 537 root = SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, 538 SYSCTL_CHILDREN(ksp->ks_sysctl_root), 539 OID_AUTO, ksp->ks_name, CTLTYPE_OPAQUE | CTLFLAG_RD 540 | CTLFLAG_MPSAFE | CTLFLAG_SKIP, 541 ksp, 0, kstat_sysctl_raw, "", ksp->ks_name); 542 } 543 break; 544 case KSTAT_TYPE_IO: 545 root = SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, 546 SYSCTL_CHILDREN(ksp->ks_sysctl_root), 547 OID_AUTO, ksp->ks_name, 548 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 549 ksp, 0, kstat_sysctl_io, "A", ksp->ks_name); 550 break; 551 case KSTAT_TYPE_TIMER: 552 case KSTAT_TYPE_INTR: 553 default: 554 panic("unsupported kstat type %d\n", ksp->ks_type); 555 } 556 VERIFY3P(root, !=, NULL); 557 ksp->ks_sysctl_root = root; 558 } 559 560 void 561 kstat_delete(kstat_t *ksp) 562 { 563 564 sysctl_ctx_free(&ksp->ks_sysctl_ctx); 565 ksp->ks_lock = NULL; 566 mutex_destroy(&ksp->ks_private_lock); 567 if (!(ksp->ks_flags & KSTAT_FLAG_VIRTUAL)) 568 kmem_free(ksp->ks_data, ksp->ks_data_size); 569 free(ksp, M_KSTAT); 570 } 571