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 <allan@klarasystems.com>
Reviewed-by: Ryan Moeller <ryan@ixsystems.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Matt Macy <mmacy@FreeBSD.org>
Closes #10890
This commit is contained in:
Matthew Macy 2020-09-23 16:43:51 -07:00 committed by GitHub
parent 3dad29fb4b
commit 7b8363d7f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 201 additions and 41 deletions

View File

@ -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) \

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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.<module>.<class>.<name>.
* kstat.<module>[.<pool>].<class>.<name>
*/
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;
}

View File

@ -32,12 +32,74 @@ __FBSDID("$FreeBSD$");
#include <sys/mutex.h>
#include <sys/procfs_list.h>
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);
}

View File

@ -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);

View File

@ -94,6 +94,7 @@ void
zfs_dbgmsg_init(void)
{
procfs_list_install("zfs",
NULL,
"dbgmsg",
0600,
&zfs_dbgmsgs,

View File

@ -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,

View File

@ -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