Files
mirror_zfs/module/os/linux/spl/spl-kstat.c
T

770 lines
18 KiB
C
Raw Normal View History

2025-01-04 12:37:45 +11:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
2010-05-17 15:18:00 -07:00
* Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
* Copyright (C) 2007 The Regents of the University of California.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Brian Behlendorf <behlendorf1@llnl.gov>.
* UCRL-CODE-235197
*
2010-05-17 15:18:00 -07:00
* This file is part of the SPL, Solaris Porting Layer.
*
* The SPL is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
2010-05-17 15:18:00 -07:00
* The SPL is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
2010-05-17 15:18:00 -07:00
* with the SPL. If not, see <http://www.gnu.org/licenses/>.
2018-02-07 11:49:38 -08:00
*
2010-05-17 15:18:00 -07:00
* Solaris Porting Layer (SPL) Kstat Implementation.
2020-09-02 22:12:12 +05:30
*
* Links to Illumos.org for more information on kstat function:
* [1] https://illumos.org/man/1M/kstat
* [2] https://illumos.org/man/9f/kstat_create
*/
2024-04-29 15:26:56 +10:00
/*
* Copyright (c) 2024-2025, Klara, Inc.
* Copyright (c) 2024-2025, Syneto
*/
#include <linux/seq_file.h>
2010-07-19 14:16:05 -07:00
#include <sys/kstat.h>
2014-12-08 13:04:42 -05:00
#include <sys/vmem.h>
2017-07-24 19:52:53 +02:00
#include <sys/cmn_err.h>
2018-02-14 17:01:15 -08:00
#include <sys/sysmacros.h>
#include <sys/string.h>
static kmutex_t kstat_module_lock;
static struct list_head kstat_module_list;
static kid_t kstat_id;
static int
kstat_resize_raw(kstat_t *ksp)
{
if (ksp->ks_raw_bufsize == KSTAT_RAW_MAX)
2018-02-07 11:49:38 -08:00
return (ENOMEM);
vmem_free(ksp->ks_raw_buf, ksp->ks_raw_bufsize);
ksp->ks_raw_bufsize = MIN(ksp->ks_raw_bufsize * 2, KSTAT_RAW_MAX);
ksp->ks_raw_buf = vmem_alloc(ksp->ks_raw_bufsize, KM_SLEEP);
2018-02-07 11:49:38 -08:00
return (0);
}
static int
kstat_seq_show_headers(struct seq_file *f)
{
2018-02-07 11:49:38 -08:00
kstat_t *ksp = (kstat_t *)f->private;
int rc = 0;
2018-02-07 11:49:38 -08:00
ASSERT(ksp->ks_magic == KS_MAGIC);
2018-02-07 11:49:38 -08:00
seq_printf(f, "%d %d 0x%02x %d %d %lld %lld\n",
ksp->ks_kid, ksp->ks_type, ksp->ks_flags,
ksp->ks_ndata, (int)ksp->ks_data_size,
ksp->ks_crtime, ksp->ks_snaptime);
switch (ksp->ks_type) {
2018-02-07 11:49:38 -08:00
case KSTAT_TYPE_RAW:
restart:
2018-02-07 11:49:38 -08:00
if (ksp->ks_raw_ops.headers) {
rc = ksp->ks_raw_ops.headers(
ksp->ks_raw_buf, ksp->ks_raw_bufsize);
if (rc == ENOMEM && !kstat_resize_raw(ksp))
goto restart;
if (!rc)
2018-02-07 11:49:38 -08:00
seq_puts(f, ksp->ks_raw_buf);
} else {
seq_printf(f, "raw data\n");
}
break;
case KSTAT_TYPE_NAMED:
seq_printf(f, "%-31s %-4s %s\n",
"name", "type", "data");
break;
case KSTAT_TYPE_INTR:
seq_printf(f, "%-8s %-8s %-8s %-8s %-8s\n",
"hard", "soft", "watchdog",
"spurious", "multsvc");
break;
case KSTAT_TYPE_IO:
seq_printf(f,
"%-8s %-8s %-8s %-8s %-8s %-8s "
"%-8s %-8s %-8s %-8s %-8s %-8s\n",
"nread", "nwritten", "reads", "writes",
"wtime", "wlentime", "wupdate",
"rtime", "rlentime", "rupdate",
"wcnt", "rcnt");
break;
case KSTAT_TYPE_TIMER:
seq_printf(f,
"%-31s %-8s "
"%-8s %-8s %-8s %-8s %-8s\n",
"name", "events", "elapsed",
"min", "max", "start", "stop");
break;
default:
PANIC("Undefined kstat type %d\n", ksp->ks_type);
}
return (-rc);
}
static int
kstat_seq_show_raw(struct seq_file *f, unsigned char *p, int l)
{
2018-02-07 11:49:38 -08:00
int i, j;
2018-02-07 11:49:38 -08:00
for (i = 0; ; i++) {
seq_printf(f, "%03x:", i);
2018-02-07 11:49:38 -08:00
for (j = 0; j < 16; j++) {
if (i * 16 + j >= l) {
seq_printf(f, "\n");
goto out;
}
2018-02-07 11:49:38 -08:00
seq_printf(f, " %02x", (unsigned char)p[i * 16 + j]);
}
seq_printf(f, "\n");
}
out:
2018-02-07 11:49:38 -08:00
return (0);
}
static int
kstat_seq_show_named(struct seq_file *f, kstat_named_t *knp)
{
2018-02-07 11:49:38 -08:00
seq_printf(f, "%-31s %-4d ", knp->name, knp->data_type);
switch (knp->data_type) {
case KSTAT_DATA_CHAR:
knp->value.c[15] = '\0'; /* NULL terminate */
seq_printf(f, "%-16s", knp->value.c);
break;
/*
* NOTE - We need to be more careful able what tokens are
* used for each arch, for now this is correct for x86_64.
*/
case KSTAT_DATA_INT32:
seq_printf(f, "%d", knp->value.i32);
break;
case KSTAT_DATA_UINT32:
seq_printf(f, "%u", knp->value.ui32);
break;
case KSTAT_DATA_INT64:
seq_printf(f, "%lld", (signed long long)knp->value.i64);
break;
case KSTAT_DATA_UINT64:
seq_printf(f, "%llu",
(unsigned long long)knp->value.ui64);
break;
case KSTAT_DATA_LONG:
seq_printf(f, "%ld", knp->value.l);
break;
case KSTAT_DATA_ULONG:
seq_printf(f, "%lu", knp->value.ul);
break;
case KSTAT_DATA_STRING:
KSTAT_NAMED_STR_PTR(knp)
[KSTAT_NAMED_STR_BUFLEN(knp)-1] = '\0';
seq_printf(f, "%s", KSTAT_NAMED_STR_PTR(knp));
break;
default:
PANIC("Undefined kstat data type %d\n", knp->data_type);
}
seq_printf(f, "\n");
return (0);
}
static int
kstat_seq_show_intr(struct seq_file *f, kstat_intr_t *kip)
{
2018-02-07 11:49:38 -08:00
seq_printf(f, "%-8u %-8u %-8u %-8u %-8u\n",
kip->intrs[KSTAT_INTR_HARD],
kip->intrs[KSTAT_INTR_SOFT],
kip->intrs[KSTAT_INTR_WATCHDOG],
kip->intrs[KSTAT_INTR_SPURIOUS],
kip->intrs[KSTAT_INTR_MULTSVC]);
return (0);
}
static int
kstat_seq_show_io(struct seq_file *f, kstat_io_t *kip)
{
/* though wlentime & friends are signed, they will never be negative */
2018-02-07 11:49:38 -08:00
seq_printf(f,
"%-8llu %-8llu %-8u %-8u %-8llu %-8llu "
"%-8llu %-8llu %-8llu %-8llu %-8u %-8u\n",
2018-02-07 11:49:38 -08:00
kip->nread, kip->nwritten,
kip->reads, kip->writes,
kip->wtime, kip->wlentime, kip->wlastupdate,
kip->rtime, kip->rlentime, kip->rlastupdate,
kip->wcnt, kip->rcnt);
return (0);
}
static int
kstat_seq_show_timer(struct seq_file *f, kstat_timer_t *ktp)
{
2018-02-07 11:49:38 -08:00
seq_printf(f,
"%-31s %-8llu %-8llu %-8llu %-8llu %-8llu %-8llu\n",
2018-02-07 11:49:38 -08:00
ktp->name, ktp->num_events, ktp->elapsed_time,
ktp->min_time, ktp->max_time,
ktp->start_time, ktp->stop_time);
2018-02-07 11:49:38 -08:00
return (0);
}
static int
kstat_seq_show(struct seq_file *f, void *p)
{
2018-02-07 11:49:38 -08:00
kstat_t *ksp = (kstat_t *)f->private;
int rc = 0;
2018-02-07 11:49:38 -08:00
ASSERT(ksp->ks_magic == KS_MAGIC);
switch (ksp->ks_type) {
2018-02-07 11:49:38 -08:00
case KSTAT_TYPE_RAW:
restart:
2018-02-07 11:49:38 -08:00
if (ksp->ks_raw_ops.data) {
rc = ksp->ks_raw_ops.data(
ksp->ks_raw_buf, ksp->ks_raw_bufsize, p);
if (rc == ENOMEM && !kstat_resize_raw(ksp))
goto restart;
if (!rc)
2018-02-07 11:49:38 -08:00
seq_puts(f, ksp->ks_raw_buf);
} else {
ASSERT(ksp->ks_ndata == 1);
rc = kstat_seq_show_raw(f, ksp->ks_data,
2018-02-24 10:05:37 -08:00
ksp->ks_data_size);
2018-02-07 11:49:38 -08:00
}
break;
case KSTAT_TYPE_NAMED:
rc = kstat_seq_show_named(f, (kstat_named_t *)p);
break;
case KSTAT_TYPE_INTR:
rc = kstat_seq_show_intr(f, (kstat_intr_t *)p);
break;
case KSTAT_TYPE_IO:
rc = kstat_seq_show_io(f, (kstat_io_t *)p);
break;
case KSTAT_TYPE_TIMER:
rc = kstat_seq_show_timer(f, (kstat_timer_t *)p);
break;
default:
PANIC("Undefined kstat type %d\n", ksp->ks_type);
}
return (-rc);
}
2018-02-19 19:23:53 +09:00
static int
2012-01-30 12:15:31 -08:00
kstat_default_update(kstat_t *ksp, int rw)
{
ASSERT(ksp != NULL);
if (rw == KSTAT_WRITE)
return (EACCES);
2018-02-07 11:49:38 -08:00
return (0);
2012-01-30 12:15:31 -08:00
}
static void *
kstat_seq_data_addr(kstat_t *ksp, loff_t n)
{
2018-02-07 11:49:38 -08:00
void *rc = NULL;
switch (ksp->ks_type) {
2018-02-07 11:49:38 -08:00
case KSTAT_TYPE_RAW:
if (ksp->ks_raw_ops.addr)
rc = ksp->ks_raw_ops.addr(ksp, n);
else
rc = ksp->ks_data;
break;
case KSTAT_TYPE_NAMED:
rc = ksp->ks_data + n * sizeof (kstat_named_t);
break;
case KSTAT_TYPE_INTR:
rc = ksp->ks_data + n * sizeof (kstat_intr_t);
break;
case KSTAT_TYPE_IO:
rc = ksp->ks_data + n * sizeof (kstat_io_t);
break;
case KSTAT_TYPE_TIMER:
rc = ksp->ks_data + n * sizeof (kstat_timer_t);
break;
default:
PANIC("Undefined kstat type %d\n", ksp->ks_type);
}
return (rc);
}
static void *
kstat_seq_start(struct seq_file *f, loff_t *pos)
{
2018-02-07 11:49:38 -08:00
loff_t n = *pos;
kstat_t *ksp = (kstat_t *)f->private;
ASSERT(ksp->ks_magic == KS_MAGIC);
2013-08-13 15:15:36 +03:00
mutex_enter(ksp->ks_lock);
2012-10-23 09:17:29 -07:00
2018-02-07 11:49:38 -08:00
if (ksp->ks_type == KSTAT_TYPE_RAW) {
ksp->ks_raw_bufsize = PAGE_SIZE;
ksp->ks_raw_buf = vmem_alloc(ksp->ks_raw_bufsize, KM_SLEEP);
}
2018-02-07 11:49:38 -08:00
/* Dynamically update kstat, on error existing kstats are used */
(void) ksp->ks_update(ksp, KSTAT_READ);
2012-01-30 12:15:31 -08:00
ksp->ks_snaptime = gethrtime();
if (!(ksp->ks_flags & KSTAT_FLAG_NO_HEADERS) && !n &&
kstat_seq_show_headers(f))
2014-11-05 17:30:35 -05:00
return (NULL);
2018-02-07 11:49:38 -08:00
if (n >= ksp->ks_ndata)
return (NULL);
2018-02-07 11:49:38 -08:00
return (kstat_seq_data_addr(ksp, n));
}
static void *
kstat_seq_next(struct seq_file *f, void *p, loff_t *pos)
{
2018-02-07 11:49:38 -08:00
kstat_t *ksp = (kstat_t *)f->private;
ASSERT(ksp->ks_magic == KS_MAGIC);
2018-02-07 11:49:38 -08:00
++*pos;
if (*pos >= ksp->ks_ndata)
return (NULL);
2018-02-07 11:49:38 -08:00
return (kstat_seq_data_addr(ksp, *pos));
}
static void
kstat_seq_stop(struct seq_file *f, void *v)
{
2013-08-13 15:15:36 +03:00
kstat_t *ksp = (kstat_t *)f->private;
ASSERT(ksp->ks_magic == KS_MAGIC);
if (ksp->ks_type == KSTAT_TYPE_RAW)
vmem_free(ksp->ks_raw_buf, ksp->ks_raw_bufsize);
2013-08-13 15:15:36 +03:00
mutex_exit(ksp->ks_lock);
}
2022-01-15 00:37:55 +01:00
static const struct seq_operations kstat_seq_ops = {
2018-02-07 11:49:38 -08:00
.show = kstat_seq_show,
.start = kstat_seq_start,
.next = kstat_seq_next,
.stop = kstat_seq_stop,
};
static kstat_module_t *
kstat_find_module(char *name)
{
2024-04-29 15:26:56 +10:00
ASSERT(MUTEX_HELD(&kstat_module_lock));
2019-12-14 00:07:48 +00:00
kstat_module_t *module = NULL;
2018-02-24 10:05:37 -08:00
list_for_each_entry(module, &kstat_module_list, ksm_module_list) {
if (strncmp(name, module->ksm_name, KSTAT_STRLEN) == 0)
return (module);
2018-02-24 10:05:37 -08:00
}
return (NULL);
}
2024-04-29 15:26:56 +10:00
static void
kstat_delete_module(kstat_module_t *module)
{
2024-04-29 15:26:56 +10:00
ASSERT(MUTEX_HELD(&kstat_module_lock));
ASSERT(list_empty(&module->ksm_kstat_list));
ASSERT0(module->ksm_nchildren);
2024-04-29 15:26:56 +10:00
kstat_module_t *parent = module->ksm_parent;
char *p = module->ksm_name, *frag = NULL;
2024-04-29 15:26:56 +10:00
while (p != NULL && (frag = strsep(&p, "/"))) {}
2024-04-29 15:26:56 +10:00
remove_proc_entry(frag, parent ? parent->ksm_proc : proc_spl_kstat);
list_del(&module->ksm_module_list);
kmem_free(module, sizeof (kstat_module_t));
2024-04-29 15:26:56 +10:00
if (parent) {
parent->ksm_nchildren--;
if (parent->ksm_nchildren == 0 &&
list_empty(&parent->ksm_kstat_list))
kstat_delete_module(parent);
}
}
2024-04-29 15:26:56 +10:00
static kstat_module_t *
kstat_create_module(char *name)
{
2024-04-29 15:26:56 +10:00
ASSERT(MUTEX_HELD(&kstat_module_lock));
char buf[KSTAT_STRLEN];
kstat_module_t *module, *parent;
(void) strlcpy(buf, name, KSTAT_STRLEN);
module = parent = NULL;
2024-04-29 15:26:56 +10:00
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_zalloc(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[-1] = '/';
}
return (module);
}
static int
proc_kstat_open(struct inode *inode, struct file *filp)
{
2018-02-07 11:49:38 -08:00
struct seq_file *f;
int rc;
2018-02-07 11:49:38 -08:00
rc = seq_open(filp, &kstat_seq_ops);
if (rc)
return (rc);
2018-02-07 11:49:38 -08:00
f = filp->private_data;
f->private = SPL_PDE_DATA(inode);
2021-01-22 21:26:41 -08:00
return (0);
}
static ssize_t
2018-02-07 11:49:38 -08:00
proc_kstat_write(struct file *filp, const char __user *buf, size_t len,
loff_t *ppos)
{
struct seq_file *f = filp->private_data;
kstat_t *ksp = f->private;
int rc;
ASSERT(ksp->ks_magic == KS_MAGIC);
mutex_enter(ksp->ks_lock);
rc = ksp->ks_update(ksp, KSTAT_WRITE);
mutex_exit(ksp->ks_lock);
if (rc)
return (-rc);
*ppos += len;
return (len);
}
2020-02-06 10:30:41 -08:00
static const kstat_proc_op_t proc_kstat_operations = {
#ifdef HAVE_PROC_OPS_STRUCT
.proc_open = proc_kstat_open,
.proc_write = proc_kstat_write,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = seq_release,
#else
.open = proc_kstat_open,
.write = proc_kstat_write,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
2020-02-06 10:30:41 -08:00
#endif
};
void
__kstat_set_raw_ops(kstat_t *ksp,
2018-02-24 10:05:37 -08:00
int (*headers)(char *buf, size_t size),
int (*data)(char *buf, size_t size, void *data),
void *(*addr)(kstat_t *ksp, loff_t index))
{
ksp->ks_raw_ops.headers = headers;
ksp->ks_raw_ops.data = data;
ksp->ks_raw_ops.addr = addr;
}
EXPORT_SYMBOL(__kstat_set_raw_ops);
void
kstat_proc_entry_init(kstat_proc_entry_t *kpep, const char *module,
const char *name)
{
kpep->kpe_owner = NULL;
kpep->kpe_proc = NULL;
INIT_LIST_HEAD(&kpep->kpe_list);
2022-09-27 19:35:29 -04:00
strlcpy(kpep->kpe_module, module, sizeof (kpep->kpe_module));
strlcpy(kpep->kpe_name, name, sizeof (kpep->kpe_name));
}
kstat_t *
__kstat_create(const char *ks_module, int ks_instance, const char *ks_name,
2018-02-07 11:49:38 -08:00
const char *ks_class, uchar_t ks_type, uint_t ks_ndata,
uchar_t ks_flags)
{
kstat_t *ksp;
ASSERT(ks_module);
2025-08-04 12:07:14 +10:00
ASSERT0(ks_instance);
ASSERT(ks_name);
if ((ks_type == KSTAT_TYPE_INTR) || (ks_type == KSTAT_TYPE_IO))
2018-02-07 11:49:38 -08:00
ASSERT(ks_ndata == 1);
2018-02-07 11:49:38 -08:00
ksp = kmem_zalloc(sizeof (*ksp), KM_SLEEP);
if (ksp == NULL)
2018-02-07 11:49:38 -08:00
return (ksp);
mutex_enter(&kstat_module_lock);
ksp->ks_kid = kstat_id;
2018-02-07 11:49:38 -08:00
kstat_id++;
mutex_exit(&kstat_module_lock);
2018-02-07 11:49:38 -08:00
ksp->ks_magic = KS_MAGIC;
2013-08-13 15:15:36 +03:00
mutex_init(&ksp->ks_private_lock, NULL, MUTEX_DEFAULT, NULL);
ksp->ks_lock = &ksp->ks_private_lock;
ksp->ks_crtime = gethrtime();
2018-02-07 11:49:38 -08:00
ksp->ks_snaptime = ksp->ks_crtime;
ksp->ks_instance = ks_instance;
2022-09-27 19:35:29 -04:00
strlcpy(ksp->ks_class, ks_class, sizeof (ksp->ks_class));
ksp->ks_type = ks_type;
ksp->ks_flags = ks_flags;
2012-01-30 12:15:31 -08:00
ksp->ks_update = kstat_default_update;
ksp->ks_private = NULL;
ksp->ks_raw_ops.headers = NULL;
ksp->ks_raw_ops.data = NULL;
ksp->ks_raw_ops.addr = NULL;
ksp->ks_raw_buf = NULL;
ksp->ks_raw_bufsize = 0;
kstat_proc_entry_init(&ksp->ks_proc, ks_module, ks_name);
switch (ksp->ks_type) {
2018-02-07 11:49:38 -08:00
case KSTAT_TYPE_RAW:
ksp->ks_ndata = 1;
ksp->ks_data_size = ks_ndata;
break;
case KSTAT_TYPE_NAMED:
ksp->ks_ndata = ks_ndata;
ksp->ks_data_size = ks_ndata * sizeof (kstat_named_t);
break;
case KSTAT_TYPE_INTR:
ksp->ks_ndata = ks_ndata;
ksp->ks_data_size = ks_ndata * sizeof (kstat_intr_t);
break;
case KSTAT_TYPE_IO:
ksp->ks_ndata = ks_ndata;
ksp->ks_data_size = ks_ndata * sizeof (kstat_io_t);
break;
case KSTAT_TYPE_TIMER:
ksp->ks_ndata = ks_ndata;
ksp->ks_data_size = ks_ndata * sizeof (kstat_timer_t);
break;
default:
PANIC("Undefined kstat type %d\n", ksp->ks_type);
}
if (ksp->ks_flags & KSTAT_FLAG_VIRTUAL) {
2018-02-07 11:49:38 -08:00
ksp->ks_data = NULL;
} else {
ksp->ks_data = kmem_zalloc(ksp->ks_data_size, KM_SLEEP);
if (ksp->ks_data == NULL) {
kmem_free(ksp, sizeof (*ksp));
ksp = NULL;
}
}
return (ksp);
}
EXPORT_SYMBOL(__kstat_create);
2017-07-24 19:52:53 +02:00
static int
kstat_detect_collision(kstat_proc_entry_t *kpep)
2017-07-24 19:52:53 +02:00
{
kstat_module_t *module;
2019-12-14 00:07:48 +00:00
kstat_proc_entry_t *tmp = NULL;
2017-08-11 08:53:35 -07:00
char *parent;
2017-07-24 19:52:53 +02:00
char *cp;
parent = kmem_asprintf("%s", kpep->kpe_module);
2017-07-24 19:52:53 +02:00
2017-08-11 08:53:35 -07:00
if ((cp = strrchr(parent, '/')) == NULL) {
2019-10-10 09:47:06 -07:00
kmem_strfree(parent);
2017-07-24 19:52:53 +02:00
return (0);
2017-08-11 08:53:35 -07:00
}
2017-07-24 19:52:53 +02:00
cp[0] = '\0';
if ((module = kstat_find_module(parent)) != NULL) {
list_for_each_entry(tmp, &module->ksm_kstat_list, kpe_list) {
if (strncmp(tmp->kpe_name, cp+1, KSTAT_STRLEN) == 0) {
2019-10-10 09:47:06 -07:00
kmem_strfree(parent);
2017-07-24 19:52:53 +02:00
return (EEXIST);
2017-08-11 08:53:35 -07:00
}
2018-02-24 10:05:37 -08:00
}
2017-07-24 19:52:53 +02:00
}
2019-10-10 09:47:06 -07:00
kmem_strfree(parent);
2017-07-24 19:52:53 +02:00
return (0);
}
/*
* Add a file to the proc filesystem under the kstat namespace (i.e.
* /proc/spl/kstat/). The file need not necessarily be implemented as a
* kstat.
*/
void
2019-04-04 18:57:06 -07:00
kstat_proc_entry_install(kstat_proc_entry_t *kpep, mode_t mode,
2020-02-06 10:30:41 -08:00
const kstat_proc_op_t *proc_ops, void *data)
{
kstat_module_t *module;
2019-12-14 00:07:48 +00:00
kstat_proc_entry_t *tmp = NULL;
ASSERT(kpep);
mutex_enter(&kstat_module_lock);
module = kstat_find_module(kpep->kpe_module);
if (module == NULL) {
if (kstat_detect_collision(kpep) != 0) {
2017-07-24 19:52:53 +02:00
cmn_err(CE_WARN, "kstat_create('%s', '%s'): namespace" \
" collision", kpep->kpe_module, kpep->kpe_name);
2017-07-24 19:52:53 +02:00
goto out;
}
module = kstat_create_module(kpep->kpe_module);
if (module == NULL)
goto out;
}
/*
2024-04-29 15:26:56 +10:00
* We can only have one entry of this name per module. If one already
* exists, replace it by first removing the proc entry, then removing
* it from the list. The kstat itself lives on; it just can't be
* inspected through the filesystem.
*/
list_for_each_entry(tmp, &module->ksm_kstat_list, kpe_list) {
2024-04-29 15:26:56 +10:00
if (tmp->kpe_proc != NULL &&
strncmp(tmp->kpe_name, kpep->kpe_name, KSTAT_STRLEN) == 0) {
ASSERT3P(tmp->kpe_owner, ==, module);
remove_proc_entry(tmp->kpe_name, module->ksm_proc);
tmp->kpe_proc = NULL;
list_del_init(&tmp->kpe_list);
break;
}
2018-02-24 10:05:37 -08:00
}
list_add_tail(&kpep->kpe_list, &module->ksm_kstat_list);
kpep->kpe_owner = module;
2019-04-04 18:57:06 -07:00
kpep->kpe_proc = proc_create_data(kpep->kpe_name, mode,
2020-02-06 10:30:41 -08:00
module->ksm_proc, proc_ops, data);
if (kpep->kpe_proc == NULL) {
list_del_init(&kpep->kpe_list);
if (list_empty(&module->ksm_kstat_list))
kstat_delete_module(module);
}
out:
mutex_exit(&kstat_module_lock);
}
void
__kstat_install(kstat_t *ksp)
{
ASSERT(ksp);
2019-04-04 18:57:06 -07:00
mode_t mode;
/* Specify permission modes for different kstats */
if (strncmp(ksp->ks_proc.kpe_name, "dbufs", KSTAT_STRLEN) == 0) {
mode = 0600;
} else {
mode = 0644;
}
kstat_proc_entry_install(
&ksp->ks_proc, mode, &proc_kstat_operations, ksp);
}
EXPORT_SYMBOL(__kstat_install);
void
kstat_proc_entry_delete(kstat_proc_entry_t *kpep)
{
kstat_module_t *module = kpep->kpe_owner;
if (kpep->kpe_proc)
remove_proc_entry(kpep->kpe_name, module->ksm_proc);
mutex_enter(&kstat_module_lock);
list_del_init(&kpep->kpe_list);
/*
* Remove top level module directory if it wasn't empty before, but now
* is.
*/
if (kpep->kpe_proc && list_empty(&module->ksm_kstat_list))
kstat_delete_module(module);
mutex_exit(&kstat_module_lock);
}
void
__kstat_delete(kstat_t *ksp)
{
kstat_proc_entry_delete(&ksp->ks_proc);
if (!(ksp->ks_flags & KSTAT_FLAG_VIRTUAL))
kmem_free(ksp->ks_data, ksp->ks_data_size);
2013-08-13 15:15:36 +03:00
ksp->ks_lock = NULL;
mutex_destroy(&ksp->ks_private_lock);
2018-02-07 11:49:38 -08:00
kmem_free(ksp, sizeof (*ksp));
}
EXPORT_SYMBOL(__kstat_delete);
int
2011-11-11 09:03:31 -08:00
spl_kstat_init(void)
{
mutex_init(&kstat_module_lock, NULL, MUTEX_DEFAULT, NULL);
INIT_LIST_HEAD(&kstat_module_list);
2018-02-07 11:49:38 -08:00
kstat_id = 0;
2014-11-05 17:30:35 -05:00
return (0);
}
void
2011-11-11 09:03:31 -08:00
spl_kstat_fini(void)
{
ASSERT(list_empty(&kstat_module_list));
mutex_destroy(&kstat_module_lock);
}