diff --git a/include/os/linux/spl/sys/kstat.h b/include/os/linux/spl/sys/kstat.h index 305c411ddf..7809fda520 100644 --- a/include/os/linux/spl/sys/kstat.h +++ b/include/os/linux/spl/sys/kstat.h @@ -20,6 +20,10 @@ * You should have received a copy of the GNU General Public License along * with the SPL. If not, see . */ +/* + * Copyright (c) 2024, Klara, Inc. + * Copyright (c) 2024, Syneto + */ #ifndef _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_kstat_list; /* list of kstat entries */ 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; typedef struct kstat_raw_ops { diff --git a/module/os/linux/spl/spl-kstat.c b/module/os/linux/spl/spl-kstat.c index ad553a73a6..10732d6090 100644 --- a/module/os/linux/spl/spl-kstat.c +++ b/module/os/linux/spl/spl-kstat.c @@ -26,6 +26,10 @@ * [1] https://illumos.org/man/1M/kstat * [2] https://illumos.org/man/9f/kstat_create */ +/* + * Copyright (c) 2024, Klara, Inc. + * Copyright (c) 2024, Syneto + */ #include #include @@ -379,33 +383,72 @@ kstat_find_module(char *name) 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 kstat_delete_module(kstat_module_t *module) { 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); 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