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
11 changed files with 201 additions and 41 deletions
+71 -13
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;
}
+84 -2
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);
}