FreeBSD: Optimize large kstat outputs

- Use sbuf_new_for_sysctl() to reduce double-buffering on sysctl
output.
- Use much faster sbuf_cat() instead of sbuf_printf("%s").

Together it reduces `sysctl kstat.zfs.misc.dbufs` time from minutes
to seconds, making dbufstat almost usable.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Alexander Motin <mav@FreeBSD.org>
Sponsored by: iXsystems, Inc.
Closes #15495
This commit is contained in:
Alexander Motin 2023-11-07 14:35:40 -05:00 committed by Brian Behlendorf
parent c34fe8dcbc
commit f13593619b

View File

@ -187,19 +187,18 @@ kstat_sysctl_dataset_string(SYSCTL_HANDLER_ARGS)
static int static int
kstat_sysctl_io(SYSCTL_HANDLER_ARGS) kstat_sysctl_io(SYSCTL_HANDLER_ARGS)
{ {
struct sbuf *sb; struct sbuf sb;
kstat_t *ksp = arg1; kstat_t *ksp = arg1;
kstat_io_t *kip = ksp->ks_data; kstat_io_t *kip = ksp->ks_data;
int rc; int rc;
sb = sbuf_new_auto(); sbuf_new_for_sysctl(&sb, NULL, 0, req);
if (sb == NULL)
return (ENOMEM);
/* Update the aggsums before reading */ /* Update the aggsums before reading */
(void) ksp->ks_update(ksp, KSTAT_READ); (void) ksp->ks_update(ksp, KSTAT_READ);
/* though wlentime & friends are signed, they will never be negative */ /* though wlentime & friends are signed, they will never be negative */
sbuf_printf(sb, sbuf_printf(&sb,
"%-8llu %-8llu %-8u %-8u %-8llu %-8llu " "%-8llu %-8llu %-8u %-8u %-8llu %-8llu "
"%-8llu %-8llu %-8llu %-8llu %-8u %-8u\n", "%-8llu %-8llu %-8llu %-8llu %-8u %-8u\n",
kip->nread, kip->nwritten, kip->nread, kip->nwritten,
@ -207,25 +206,21 @@ kstat_sysctl_io(SYSCTL_HANDLER_ARGS)
kip->wtime, kip->wlentime, kip->wlastupdate, kip->wtime, kip->wlentime, kip->wlastupdate,
kip->rtime, kip->rlentime, kip->rlastupdate, kip->rtime, kip->rlentime, kip->rlastupdate,
kip->wcnt, kip->rcnt); kip->wcnt, kip->rcnt);
rc = sbuf_finish(sb); rc = sbuf_finish(&sb);
if (rc == 0) sbuf_delete(&sb);
rc = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb));
sbuf_delete(sb);
return (rc); return (rc);
} }
static int static int
kstat_sysctl_raw(SYSCTL_HANDLER_ARGS) kstat_sysctl_raw(SYSCTL_HANDLER_ARGS)
{ {
struct sbuf *sb; struct sbuf sb;
void *data; void *data;
kstat_t *ksp = arg1; kstat_t *ksp = arg1;
void *(*addr_op)(kstat_t *ksp, loff_t index); void *(*addr_op)(kstat_t *ksp, loff_t index);
int n, has_header, rc = 0; int n, has_header, rc = 0;
sb = sbuf_new_auto(); sbuf_new_for_sysctl(&sb, NULL, PAGE_SIZE, req);
if (sb == NULL)
return (ENOMEM);
if (ksp->ks_raw_ops.addr) if (ksp->ks_raw_ops.addr)
addr_op = ksp->ks_raw_ops.addr; addr_op = ksp->ks_raw_ops.addr;
@ -258,8 +253,10 @@ restart_headers:
if (has_header) { if (has_header) {
if (rc == ENOMEM && !kstat_resize_raw(ksp)) if (rc == ENOMEM && !kstat_resize_raw(ksp))
goto restart_headers; goto restart_headers;
if (rc == 0) if (rc == 0) {
sbuf_printf(sb, "\n%s", ksp->ks_raw_buf); sbuf_cat(&sb, "\n");
sbuf_cat(&sb, ksp->ks_raw_buf);
}
} }
while ((data = addr_op(ksp, n)) != NULL) { while ((data = addr_op(ksp, n)) != NULL) {
@ -270,22 +267,19 @@ restart:
if (rc == ENOMEM && !kstat_resize_raw(ksp)) if (rc == ENOMEM && !kstat_resize_raw(ksp))
goto restart; goto restart;
if (rc == 0) if (rc == 0)
sbuf_printf(sb, "%s", ksp->ks_raw_buf); sbuf_cat(&sb, ksp->ks_raw_buf);
} else { } else {
ASSERT3U(ksp->ks_ndata, ==, 1); ASSERT3U(ksp->ks_ndata, ==, 1);
sbuf_hexdump(sb, ksp->ks_data, sbuf_hexdump(&sb, ksp->ks_data,
ksp->ks_data_size, NULL, 0); ksp->ks_data_size, NULL, 0);
} }
n++; n++;
} }
free(ksp->ks_raw_buf, M_TEMP); free(ksp->ks_raw_buf, M_TEMP);
mutex_exit(ksp->ks_lock); mutex_exit(ksp->ks_lock);
sbuf_trim(sb); rc = sbuf_finish(&sb);
rc = sbuf_finish(sb); sbuf_delete(&sb);
if (rc == 0)
rc = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb));
sbuf_delete(sb);
return (rc); return (rc);
} }