From c70c6e004e77ab4cf9a74291c546379f6a0d8b43 Mon Sep 17 00:00:00 2001 From: Matthew Macy Date: Wed, 23 Sep 2020 16:43:51 -0700 Subject: [PATCH] FreeBSD: Add support for procfs_list The procfs_list interface is required by several kstats. Implement this functionality for FreeBSD to provide access to these kstats. Reviewed-by: Allan Jude Reviewed-by: Ryan Moeller Reviewed-by: Brian Behlendorf Signed-off-by: Matt Macy Closes #10890 --- include/os/freebsd/spl/sys/kstat.h | 18 +++++ include/os/freebsd/spl/sys/procfs_list.h | 13 ++-- include/os/linux/spl/sys/procfs_list.h | 1 + include/sys/zfs_context.h | 1 + lib/libzpool/kernel.c | 1 + module/os/freebsd/spl/spl_kstat.c | 84 +++++++++++++++++++---- module/os/freebsd/spl/spl_procfs_list.c | 86 +++++++++++++++++++++++- module/os/linux/spl/spl-procfs-list.c | 10 ++- module/os/linux/zfs/zfs_debug.c | 1 + module/zfs/spa_misc.c | 1 + module/zfs/spa_stats.c | 26 ++----- 11 files changed, 201 insertions(+), 41 deletions(-) diff --git a/include/os/freebsd/spl/sys/kstat.h b/include/os/freebsd/spl/sys/kstat.h index 74c3da8ec376..5ceb88b297d9 100644 --- a/include/os/freebsd/spl/sys/kstat.h +++ b/include/os/freebsd/spl/sys/kstat.h @@ -83,6 +83,14 @@ typedef struct kstat_s kstat_t; typedef int kid_t; /* unique kstat id */ typedef int kstat_update_t(struct kstat_s *, int); /* dynamic update cb */ +struct seq_file { + char *sf_buf; + size_t sf_size; +}; + +void seq_printf(struct seq_file *m, const char *fmt, ...); + + typedef struct kstat_module { char ksm_name[KSTAT_STRLEN+1]; /* module name */ struct list_head ksm_module_list; /* module linkage */ @@ -92,6 +100,7 @@ typedef struct kstat_module { typedef struct kstat_raw_ops { int (*headers)(char *buf, size_t size); + int (*seq_headers)(struct seq_file *); int (*data)(char *buf, size_t size, void *data); void *(*addr)(kstat_t *ksp, loff_t index); } kstat_raw_ops_t; @@ -112,6 +121,7 @@ struct kstat_s { size_t ks_data_size; /* size of kstat data section */ kstat_update_t *ks_update; /* dynamic updates */ void *ks_private; /* private data */ + void *ks_private1; /* private data */ kmutex_t ks_private_lock; /* kstat private data lock */ kmutex_t *ks_lock; /* kstat data lock */ struct list_head ks_list; /* kstat linkage */ @@ -185,6 +195,12 @@ extern void __kstat_set_raw_ops(kstat_t *ksp, int (*data)(char *buf, size_t size, void *data), void* (*addr)(kstat_t *ksp, loff_t index)); +extern void __kstat_set_seq_raw_ops(kstat_t *ksp, + int (*headers)(struct seq_file *), + int (*data)(char *buf, size_t size, void *data), + void* (*addr)(kstat_t *ksp, loff_t index)); + + extern kstat_t *__kstat_create(const char *ks_module, int ks_instance, const char *ks_name, const char *ks_class, uchar_t ks_type, uint_t ks_ndata, uchar_t ks_flags); @@ -196,6 +212,8 @@ extern void kstat_waitq_exit(kstat_io_t *); extern void kstat_runq_enter(kstat_io_t *); extern void kstat_runq_exit(kstat_io_t *); +#define kstat_set_seq_raw_ops(k, h, d, a) \ + __kstat_set_seq_raw_ops(k, h, d, a) #define kstat_set_raw_ops(k, h, d, a) \ __kstat_set_raw_ops(k, h, d, a) #define kstat_create(m, i, n, c, t, s, f) \ diff --git a/include/os/freebsd/spl/sys/procfs_list.h b/include/os/freebsd/spl/sys/procfs_list.h index 5d623c369c4c..da13f0387cb5 100644 --- a/include/os/freebsd/spl/sys/procfs_list.h +++ b/include/os/freebsd/spl/sys/procfs_list.h @@ -33,16 +33,18 @@ * procfs list manipulation */ -struct seq_file { }; -void seq_printf(struct seq_file *m, const char *fmt, ...); - -typedef struct procfs_list { +typedef struct procfs_list procfs_list_t; +struct procfs_list { void *pl_private; + void *pl_next_data; kmutex_t pl_lock; list_t pl_list; uint64_t pl_next_id; + int (*pl_show)(struct seq_file *f, void *p); + int (*pl_show_header)(struct seq_file *f); + int (*pl_clear)(procfs_list_t *procfs_list); size_t pl_node_offset; -} procfs_list_t; +}; typedef struct procfs_list_node { list_node_t pln_link; @@ -50,6 +52,7 @@ typedef struct procfs_list_node { } procfs_list_node_t; void procfs_list_install(const char *module, + const char *submodule, const char *name, mode_t mode, procfs_list_t *procfs_list, diff --git a/include/os/linux/spl/sys/procfs_list.h b/include/os/linux/spl/sys/procfs_list.h index eb1519c0ad63..9bb437f55cf7 100644 --- a/include/os/linux/spl/sys/procfs_list.h +++ b/include/os/linux/spl/sys/procfs_list.h @@ -57,6 +57,7 @@ typedef struct procfs_list_node { } procfs_list_node_t; void procfs_list_install(const char *module, + const char *submodule, const char *name, mode_t mode, procfs_list_t *procfs_list, diff --git a/include/sys/zfs_context.h b/include/sys/zfs_context.h index e33f52c176a8..d9f5ed580ef1 100644 --- a/include/sys/zfs_context.h +++ b/include/sys/zfs_context.h @@ -386,6 +386,7 @@ typedef struct procfs_list_node { } procfs_list_node_t; void procfs_list_install(const char *module, + const char *submodule, const char *name, mode_t mode, procfs_list_t *procfs_list, diff --git a/lib/libzpool/kernel.c b/lib/libzpool/kernel.c index 145b21d40f99..ca357899367a 100644 --- a/lib/libzpool/kernel.c +++ b/lib/libzpool/kernel.c @@ -444,6 +444,7 @@ seq_printf(struct seq_file *m, const char *fmt, ...) void procfs_list_install(const char *module, + const char *submodule, const char *name, mode_t mode, procfs_list_t *procfs_list, diff --git a/module/os/freebsd/spl/spl_kstat.c b/module/os/freebsd/spl/spl_kstat.c index f215751420d5..4cc77e20a4eb 100644 --- a/module/os/freebsd/spl/spl_kstat.c +++ b/module/os/freebsd/spl/spl_kstat.c @@ -55,6 +55,17 @@ __kstat_set_raw_ops(kstat_t *ksp, ksp->ks_raw_ops.addr = addr; } +void +__kstat_set_seq_raw_ops(kstat_t *ksp, + int (*headers)(struct seq_file *f), + int (*data)(char *buf, size_t size, void *data), + void *(*addr)(kstat_t *ksp, loff_t index)) +{ + ksp->ks_raw_ops.seq_headers = headers; + ksp->ks_raw_ops.data = data; + ksp->ks_raw_ops.addr = addr; +} + static int kstat_default_update(kstat_t *ksp, int rw) { @@ -160,7 +171,7 @@ kstat_sysctl_raw(SYSCTL_HANDLER_ARGS) void *data; kstat_t *ksp = arg1; void *(*addr_op)(kstat_t *ksp, loff_t index); - int n, rc = 0; + int n, has_header, rc = 0; sb = sbuf_new_auto(); if (sb == NULL) @@ -180,14 +191,25 @@ kstat_sysctl_raw(SYSCTL_HANDLER_ARGS) ksp->ks_raw_buf = malloc(PAGE_SIZE, M_TEMP, M_WAITOK); n = 0; + has_header = (ksp->ks_raw_ops.headers || + ksp->ks_raw_ops.seq_headers); + restart_headers: if (ksp->ks_raw_ops.headers) { rc = ksp->ks_raw_ops.headers( ksp->ks_raw_buf, ksp->ks_raw_bufsize); + } else if (ksp->ks_raw_ops.seq_headers) { + struct seq_file f; + + f.sf_buf = ksp->ks_raw_buf; + f.sf_size = ksp->ks_raw_bufsize; + rc = ksp->ks_raw_ops.seq_headers(&f); + } + if (has_header) { if (rc == ENOMEM && !kstat_resize_raw(ksp)) goto restart_headers; if (rc == 0) - sbuf_printf(sb, "%s", ksp->ks_raw_buf); + sbuf_printf(sb, "\n%s", ksp->ks_raw_buf); } while ((data = addr_op(ksp, n)) != NULL) { @@ -220,16 +242,21 @@ kstat_t * __kstat_create(const char *module, int instance, const char *name, const char *class, uchar_t ks_type, uint_t ks_ndata, uchar_t flags) { + char buf[KSTAT_STRLEN]; struct sysctl_oid *root; kstat_t *ksp; + char *pool; KASSERT(instance == 0, ("instance=%d", instance)); if ((ks_type == KSTAT_TYPE_INTR) || (ks_type == KSTAT_TYPE_IO)) ASSERT(ks_ndata == 1); + if (class == NULL) + class = "misc"; + /* - * Allocate the main structure. We don't need to copy module/class/name - * stuff in here, because it is only used for sysctl node creation + * Allocate the main structure. We don't need to keep a copy of + * module in here, because it is only used for sysctl node creation * done in this function. */ ksp = malloc(sizeof (*ksp), M_KSTAT, M_WAITOK|M_ZERO); @@ -237,8 +264,8 @@ __kstat_create(const char *module, int instance, const char *name, ksp->ks_crtime = gethrtime(); ksp->ks_snaptime = ksp->ks_crtime; ksp->ks_instance = instance; - strncpy(ksp->ks_name, name, KSTAT_STRLEN); - strncpy(ksp->ks_class, class, KSTAT_STRLEN); + (void) strlcpy(ksp->ks_name, name, KSTAT_STRLEN); + (void) strlcpy(ksp->ks_class, class, KSTAT_STRLEN); ksp->ks_type = ks_type; ksp->ks_flags = flags; ksp->ks_update = kstat_default_update; @@ -280,10 +307,22 @@ __kstat_create(const char *module, int instance, const char *name, ksp = NULL; } } + + /* + * Some kstats use a module name like "zfs/poolname" to distinguish a + * set of kstats belonging to a specific pool. Split on '/' to add an + * extra node for the pool name if needed. + */ + (void) strlcpy(buf, module, KSTAT_STRLEN); + module = buf; + pool = strchr(module, '/'); + if (pool != NULL) + *pool++ = '\0'; + /* * Create sysctl tree for those statistics: * - * kstat.... + * kstat.[.].. */ sysctl_ctx_init(&ksp->ks_sysctl_ctx); root = SYSCTL_ADD_NODE(&ksp->ks_sysctl_ctx, @@ -295,11 +334,26 @@ __kstat_create(const char *module, int instance, const char *name, free(ksp, M_KSTAT); return (NULL); } + if (pool != NULL) { + root = SYSCTL_ADD_NODE(&ksp->ks_sysctl_ctx, + SYSCTL_CHILDREN(root), OID_AUTO, pool, CTLFLAG_RW, 0, ""); + if (root == NULL) { + printf("%s: Cannot create kstat.%s.%s tree!\n", + __func__, module, pool); + sysctl_ctx_free(&ksp->ks_sysctl_ctx); + free(ksp, M_KSTAT); + return (NULL); + } + } root = SYSCTL_ADD_NODE(&ksp->ks_sysctl_ctx, SYSCTL_CHILDREN(root), OID_AUTO, class, CTLFLAG_RW, 0, ""); if (root == NULL) { - printf("%s: Cannot create kstat.%s.%s tree!\n", __func__, - module, class); + if (pool != NULL) + printf("%s: Cannot create kstat.%s.%s.%s tree!\n", + __func__, module, pool, class); + else + printf("%s: Cannot create kstat.%s.%s tree!\n", + __func__, module, class); sysctl_ctx_free(&ksp->ks_sysctl_ctx); free(ksp, M_KSTAT); return (NULL); @@ -309,8 +363,13 @@ __kstat_create(const char *module, int instance, const char *name, SYSCTL_CHILDREN(root), OID_AUTO, name, CTLFLAG_RW, 0, ""); if (root == NULL) { - printf("%s: Cannot create kstat.%s.%s.%s tree!\n", - __func__, module, class, name); + if (pool != NULL) + printf("%s: Cannot create kstat.%s.%s.%s.%s " + "tree!\n", __func__, module, pool, class, + name); + else + printf("%s: Cannot create kstat.%s.%s.%s " + "tree!\n", __func__, module, class, name); sysctl_ctx_free(&ksp->ks_sysctl_ctx); free(ksp, M_KSTAT); return (NULL); @@ -411,7 +470,6 @@ kstat_install(kstat_t *ksp) switch (ksp->ks_type) { case KSTAT_TYPE_NAMED: return (kstat_install_named(ksp)); - break; case KSTAT_TYPE_RAW: if (ksp->ks_raw_ops.data) { root = SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, @@ -426,7 +484,6 @@ kstat_install(kstat_t *ksp) CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, ksp, 0, kstat_sysctl_raw, "", ksp->ks_name); } - VERIFY(root != NULL); break; case KSTAT_TYPE_IO: root = SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx, @@ -440,6 +497,7 @@ kstat_install(kstat_t *ksp) default: panic("unsupported kstat type %d\n", ksp->ks_type); } + VERIFY(root != NULL); ksp->ks_sysctl_root = root; } diff --git a/module/os/freebsd/spl/spl_procfs_list.c b/module/os/freebsd/spl/spl_procfs_list.c index 7b4ae9d0e357..e8448ce00686 100644 --- a/module/os/freebsd/spl/spl_procfs_list.c +++ b/module/os/freebsd/spl/spl_procfs_list.c @@ -32,12 +32,74 @@ __FBSDID("$FreeBSD$"); #include #include +typedef struct procfs_list_iter { + procfs_list_t *pli_pl; + void *pli_elt; +} pli_t; + void -seq_printf(struct seq_file *m, const char *fmt, ...) -{} +seq_printf(struct seq_file *f, const char *fmt, ...) +{ + va_list adx; + + va_start(adx, fmt); + (void) vsnprintf(f->sf_buf, f->sf_size, fmt, adx); + va_end(adx); +} + +static int +procfs_list_update(kstat_t *ksp, int rw) +{ + procfs_list_t *pl = ksp->ks_private; + + if (rw == KSTAT_WRITE) + pl->pl_clear(pl); + + return (0); +} + +static int +procfs_list_data(char *buf, size_t size, void *data) +{ + pli_t *p; + void *elt; + procfs_list_t *pl; + struct seq_file f; + + p = data; + pl = p->pli_pl; + elt = p->pli_elt; + free(p, M_TEMP); + f.sf_buf = buf; + f.sf_size = size; + return (pl->pl_show(&f, elt)); +} + +static void * +procfs_list_addr(kstat_t *ksp, loff_t n) +{ + procfs_list_t *pl = ksp->ks_private; + void *elt = ksp->ks_private1; + pli_t *p = NULL; + + + if (n == 0) + ksp->ks_private1 = list_head(&pl->pl_list); + else if (elt) + ksp->ks_private1 = list_next(&pl->pl_list, elt); + + if (ksp->ks_private1) { + p = malloc(sizeof (*p), M_TEMP, M_WAITOK); + p->pli_pl = pl; + p->pli_elt = ksp->ks_private1; + } + + return (p); +} void procfs_list_install(const char *module, + const char *submodule, const char *name, mode_t mode, procfs_list_t *procfs_list, @@ -46,12 +108,31 @@ procfs_list_install(const char *module, int (*clear)(procfs_list_t *procfs_list), size_t procfs_list_node_off) { + kstat_t *procfs_kstat; + mutex_init(&procfs_list->pl_lock, NULL, MUTEX_DEFAULT, NULL); list_create(&procfs_list->pl_list, procfs_list_node_off + sizeof (procfs_list_node_t), procfs_list_node_off + offsetof(procfs_list_node_t, pln_link)); + procfs_list->pl_show = show; + procfs_list->pl_show_header = show_header; + procfs_list->pl_clear = clear; procfs_list->pl_next_id = 1; procfs_list->pl_node_offset = procfs_list_node_off; + + procfs_kstat = kstat_create(module, 0, name, submodule, + KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL); + + if (procfs_kstat) { + procfs_kstat->ks_lock = &procfs_list->pl_lock; + procfs_kstat->ks_ndata = UINT32_MAX; + procfs_kstat->ks_private = procfs_list; + procfs_kstat->ks_update = procfs_list_update; + kstat_set_seq_raw_ops(procfs_kstat, show_header, + procfs_list_data, procfs_list_addr); + kstat_install(procfs_kstat); + procfs_list->pl_private = procfs_kstat; + } } void @@ -62,6 +143,7 @@ void procfs_list_destroy(procfs_list_t *procfs_list) { ASSERT(list_is_empty(&procfs_list->pl_list)); + kstat_delete(procfs_list->pl_private); list_destroy(&procfs_list->pl_list); mutex_destroy(&procfs_list->pl_lock); } diff --git a/module/os/linux/spl/spl-procfs-list.c b/module/os/linux/spl/spl-procfs-list.c index 189d6a7c6082..778da7bc4406 100644 --- a/module/os/linux/spl/spl-procfs-list.c +++ b/module/os/linux/spl/spl-procfs-list.c @@ -207,6 +207,7 @@ static const kstat_proc_op_t procfs_list_operations = { */ void procfs_list_install(const char *module, + const char *submodule, const char *name, mode_t mode, procfs_list_t *procfs_list, @@ -215,6 +216,12 @@ procfs_list_install(const char *module, int (*clear)(procfs_list_t *procfs_list), size_t procfs_list_node_off) { + char *modulestr; + + if (submodule != NULL) + modulestr = kmem_asprintf("%s/%s", module, submodule); + else + modulestr = kmem_asprintf("%s", module); mutex_init(&procfs_list->pl_lock, NULL, MUTEX_DEFAULT, NULL); list_create(&procfs_list->pl_list, procfs_list_node_off + sizeof (procfs_list_node_t), @@ -225,9 +232,10 @@ procfs_list_install(const char *module, procfs_list->pl_clear = clear; procfs_list->pl_node_offset = procfs_list_node_off; - kstat_proc_entry_init(&procfs_list->pl_kstat_entry, module, name); + kstat_proc_entry_init(&procfs_list->pl_kstat_entry, modulestr, name); kstat_proc_entry_install(&procfs_list->pl_kstat_entry, mode, &procfs_list_operations, procfs_list); + kmem_strfree(modulestr); } EXPORT_SYMBOL(procfs_list_install); diff --git a/module/os/linux/zfs/zfs_debug.c b/module/os/linux/zfs/zfs_debug.c index d98463f1b7f7..8d7f04097da8 100644 --- a/module/os/linux/zfs/zfs_debug.c +++ b/module/os/linux/zfs/zfs_debug.c @@ -94,6 +94,7 @@ void zfs_dbgmsg_init(void) { procfs_list_install("zfs", + NULL, "dbgmsg", 0600, &zfs_dbgmsgs, diff --git a/module/zfs/spa_misc.c b/module/zfs/spa_misc.c index 41f0ddbde288..04210472886c 100644 --- a/module/zfs/spa_misc.c +++ b/module/zfs/spa_misc.c @@ -2169,6 +2169,7 @@ spa_import_progress_init(void) spa_import_progress_list; procfs_list_install("zfs", + NULL, "import_progress", 0644, &spa_import_progress_list->procfs_list, diff --git a/module/zfs/spa_stats.c b/module/zfs/spa_stats.c index cf0be3c45dc8..c3eacc14239e 100644 --- a/module/zfs/spa_stats.c +++ b/module/zfs/spa_stats.c @@ -122,14 +122,11 @@ static void spa_read_history_init(spa_t *spa) { spa_history_list_t *shl = &spa->spa_stats.read_history; - char *module; shl->size = 0; - - module = kmem_asprintf("zfs/%s", spa_name(spa)); - shl->procfs_list.pl_private = shl; - procfs_list_install(module, + procfs_list_install("zfs", + spa_name(spa), "reads", 0600, &shl->procfs_list, @@ -137,8 +134,6 @@ spa_read_history_init(spa_t *spa) spa_read_history_show_header, spa_read_history_clear, offsetof(spa_read_history_t, srh_node)); - - kmem_strfree(module); } static void @@ -293,14 +288,11 @@ static void spa_txg_history_init(spa_t *spa) { spa_history_list_t *shl = &spa->spa_stats.txg_history; - char *module; shl->size = 0; - - module = kmem_asprintf("zfs/%s", spa_name(spa)); - shl->procfs_list.pl_private = shl; - procfs_list_install(module, + procfs_list_install("zfs", + spa_name(spa), "txgs", 0644, &shl->procfs_list, @@ -308,8 +300,6 @@ spa_txg_history_init(spa_t *spa) spa_txg_history_show_header, spa_txg_history_clear, offsetof(spa_txg_history_t, sth_node)); - - kmem_strfree(module); } static void @@ -699,14 +689,12 @@ static void spa_mmp_history_init(spa_t *spa) { spa_history_list_t *shl = &spa->spa_stats.mmp_history; - char *module; shl->size = 0; - module = kmem_asprintf("zfs/%s", spa_name(spa)); - shl->procfs_list.pl_private = shl; - procfs_list_install(module, + procfs_list_install("zfs", + spa_name(spa), "multihost", 0644, &shl->procfs_list, @@ -714,8 +702,6 @@ spa_mmp_history_init(spa_t *spa) spa_mmp_history_show_header, spa_mmp_history_clear, offsetof(spa_mmp_history_t, smh_node)); - - kmem_strfree(module); } static void