linux/kstat: allow multi-level module names
Module names are mapped directly to directory names in procfs, but nothing is done to create the intermediate directories, or remove them. This makes it impossible to sensibly present kstats about sub-objects. This commit loops through '/'-separated names in the full module name, creates a separate module for each, and hooks them up with a parent pointer and child counter, and then unrolls this on the other side when deleting a module. Sponsored-by: Klara, Inc. Sponsored-by: Syneto Signed-off-by: Rob Norris <rob.norris@klarasystems.com>
This commit is contained in:
parent
efbef9e6cc
commit
0dd0a2d302
|
@ -20,6 +20,10 @@
|
||||||
* You should have received a copy of the GNU General Public License along
|
* You should have received a copy of the GNU General Public License along
|
||||||
* with the SPL. If not, see <http://www.gnu.org/licenses/>.
|
* with the SPL. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Klara, Inc.
|
||||||
|
* Copyright (c) 2024, Syneto
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef _SPL_KSTAT_H
|
#ifndef _SPL_KSTAT_H
|
||||||
#define _SPL_KSTAT_H
|
#define _SPL_KSTAT_H
|
||||||
|
@ -89,6 +93,8 @@ typedef struct kstat_module {
|
||||||
struct list_head ksm_module_list; /* module linkage */
|
struct list_head ksm_module_list; /* module linkage */
|
||||||
struct list_head ksm_kstat_list; /* list of kstat entries */
|
struct list_head ksm_kstat_list; /* list of kstat entries */
|
||||||
struct proc_dir_entry *ksm_proc; /* proc entry */
|
struct proc_dir_entry *ksm_proc; /* proc entry */
|
||||||
|
struct kstat_module *ksm_parent; /* parent module in hierarchy */
|
||||||
|
uint_t ksm_nchildren; /* number of child modules */
|
||||||
} kstat_module_t;
|
} kstat_module_t;
|
||||||
|
|
||||||
typedef struct kstat_raw_ops {
|
typedef struct kstat_raw_ops {
|
||||||
|
|
|
@ -26,6 +26,10 @@
|
||||||
* [1] https://illumos.org/man/1M/kstat
|
* [1] https://illumos.org/man/1M/kstat
|
||||||
* [2] https://illumos.org/man/9f/kstat_create
|
* [2] https://illumos.org/man/9f/kstat_create
|
||||||
*/
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Klara, Inc.
|
||||||
|
* Copyright (c) 2024, Syneto
|
||||||
|
*/
|
||||||
|
|
||||||
#include <linux/seq_file.h>
|
#include <linux/seq_file.h>
|
||||||
#include <sys/kstat.h>
|
#include <sys/kstat.h>
|
||||||
|
@ -379,33 +383,72 @@ kstat_find_module(char *name)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static kstat_module_t *
|
|
||||||
kstat_create_module(char *name)
|
|
||||||
{
|
|
||||||
kstat_module_t *module;
|
|
||||||
struct proc_dir_entry *pde;
|
|
||||||
|
|
||||||
pde = proc_mkdir(name, proc_spl_kstat);
|
|
||||||
if (pde == NULL)
|
|
||||||
return (NULL);
|
|
||||||
|
|
||||||
module = kmem_alloc(sizeof (kstat_module_t), KM_SLEEP);
|
|
||||||
module->ksm_proc = pde;
|
|
||||||
strlcpy(module->ksm_name, name, KSTAT_STRLEN);
|
|
||||||
INIT_LIST_HEAD(&module->ksm_kstat_list);
|
|
||||||
list_add_tail(&module->ksm_module_list, &kstat_module_list);
|
|
||||||
|
|
||||||
return (module);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
kstat_delete_module(kstat_module_t *module)
|
kstat_delete_module(kstat_module_t *module)
|
||||||
{
|
{
|
||||||
ASSERT(list_empty(&module->ksm_kstat_list));
|
ASSERT(list_empty(&module->ksm_kstat_list));
|
||||||
remove_proc_entry(module->ksm_name, proc_spl_kstat);
|
ASSERT0(module->ksm_nchildren);
|
||||||
|
|
||||||
|
kstat_module_t *parent = module->ksm_parent;
|
||||||
|
|
||||||
|
char *p = module->ksm_name, *frag;
|
||||||
|
while (p != NULL && (frag = strsep(&p, "/"))) {}
|
||||||
|
|
||||||
|
remove_proc_entry(frag, parent ? parent->ksm_proc : proc_spl_kstat);
|
||||||
list_del(&module->ksm_module_list);
|
list_del(&module->ksm_module_list);
|
||||||
kmem_free(module, sizeof (kstat_module_t));
|
kmem_free(module, sizeof (kstat_module_t));
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
parent->ksm_nchildren--;
|
||||||
|
if (parent->ksm_nchildren == 0 &&
|
||||||
|
list_empty(&parent->ksm_kstat_list))
|
||||||
|
kstat_delete_module(parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static kstat_module_t *
|
||||||
|
kstat_create_module(char *name)
|
||||||
|
{
|
||||||
|
char buf[KSTAT_STRLEN];
|
||||||
|
kstat_module_t *module, *parent;
|
||||||
|
|
||||||
|
(void) strlcpy(buf, name, KSTAT_STRLEN);
|
||||||
|
|
||||||
|
parent = NULL;
|
||||||
|
char *p = buf, *frag;
|
||||||
|
while ((frag = strsep(&p, "/")) != NULL) {
|
||||||
|
module = kstat_find_module(buf);
|
||||||
|
if (module == NULL) {
|
||||||
|
struct proc_dir_entry *pde = proc_mkdir(frag,
|
||||||
|
parent ? parent->ksm_proc : proc_spl_kstat);
|
||||||
|
if (pde == NULL) {
|
||||||
|
cmn_err(CE_WARN, "kstat_create('%s'): "
|
||||||
|
"module dir create failed", buf);
|
||||||
|
if (parent)
|
||||||
|
kstat_delete_module(parent);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
module = kmem_alloc(sizeof (kstat_module_t), KM_SLEEP);
|
||||||
|
module->ksm_proc = pde;
|
||||||
|
strlcpy(module->ksm_name, buf, KSTAT_STRLEN);
|
||||||
|
INIT_LIST_HEAD(&module->ksm_kstat_list);
|
||||||
|
list_add_tail(&module->ksm_module_list,
|
||||||
|
&kstat_module_list);
|
||||||
|
|
||||||
|
if (parent != NULL) {
|
||||||
|
module->ksm_parent = parent;
|
||||||
|
parent->ksm_nchildren++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parent = module;
|
||||||
|
if (p != NULL && p > frag)
|
||||||
|
p[-1] = '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (module);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
Loading…
Reference in New Issue