Path: blob/main/sys/contrib/openzfs/module/os/linux/spl/spl-proc.c
48775 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.3* Copyright (C) 2007 The Regents of the University of California.4* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).5* Written by Brian Behlendorf <[email protected]>.6* UCRL-CODE-2351977*8* This file is part of the SPL, Solaris Porting Layer.9*10* The SPL is free software; you can redistribute it and/or modify it11* under the terms of the GNU General Public License as published by the12* Free Software Foundation; either version 2 of the License, or (at your13* option) any later version.14*15* The SPL is distributed in the hope that it will be useful, but WITHOUT16* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or17* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License18* for more details.19*20* You should have received a copy of the GNU General Public License along21* with the SPL. If not, see <http://www.gnu.org/licenses/>.22*23* Solaris Porting Layer (SPL) Proc Implementation.24*/25/*26* Copyright (c) 2024, Rob Norris <[email protected]>27*/2829#include <sys/systeminfo.h>30#include <sys/kstat.h>31#include <sys/kmem.h>32#include <sys/kmem_cache.h>33#include <sys/vmem.h>34#include <sys/proc.h>35#include <linux/ctype.h>36#include <linux/kmod.h>37#include <linux/seq_file.h>38#include <linux/uaccess.h>39#include <linux/version.h>40#include "zfs_gitrev.h"4142#if defined(CONSTIFY_PLUGIN)43typedef struct ctl_table __no_const spl_ctl_table;44#else45typedef struct ctl_table spl_ctl_table;46#endif4748#ifdef HAVE_PROC_HANDLER_CTL_TABLE_CONST49#define CONST_CTL_TABLE const struct ctl_table50#else51#define CONST_CTL_TABLE struct ctl_table52#endif5354static unsigned long table_min = 0;55static unsigned long table_max = ~0;5657static struct ctl_table_header *spl_header = NULL;58#ifndef HAVE_REGISTER_SYSCTL_TABLE59static struct ctl_table_header *spl_kmem = NULL;60static struct ctl_table_header *spl_kstat = NULL;61#endif62static struct proc_dir_entry *proc_spl = NULL;63static struct proc_dir_entry *proc_spl_kmem = NULL;64static struct proc_dir_entry *proc_spl_kmem_slab = NULL;65struct proc_dir_entry *proc_spl_kstat = NULL;6667#ifdef DEBUG_KMEM68static int69proc_domemused(CONST_CTL_TABLE *table, int write,70void __user *buffer, size_t *lenp, loff_t *ppos)71{72int rc = 0;73unsigned long val;74spl_ctl_table dummy = *table;7576dummy.data = &val;77dummy.proc_handler = &proc_dointvec;78dummy.extra1 = &table_min;79dummy.extra2 = &table_max;8081if (write) {82*ppos += *lenp;83} else {84val = atomic64_read((atomic64_t *)table->data);85rc = proc_doulongvec_minmax(&dummy, write, buffer, lenp, ppos);86}8788return (rc);89}90#endif /* DEBUG_KMEM */9192static int93proc_doslab(CONST_CTL_TABLE *table, int write,94void __user *buffer, size_t *lenp, loff_t *ppos)95{96int rc = 0;97unsigned long val = 0, mask;98spl_ctl_table dummy = *table;99spl_kmem_cache_t *skc = NULL;100101dummy.data = &val;102dummy.proc_handler = &proc_dointvec;103dummy.extra1 = &table_min;104dummy.extra2 = &table_max;105106if (write) {107*ppos += *lenp;108} else {109down_read(&spl_kmem_cache_sem);110mask = (unsigned long)table->data;111112list_for_each_entry(skc, &spl_kmem_cache_list, skc_list) {113114/* Only use slabs of the correct kmem/vmem type */115if (!(skc->skc_flags & mask))116continue;117118/* Sum the specified field for selected slabs */119switch (mask & (KMC_TOTAL | KMC_ALLOC | KMC_MAX)) {120case KMC_TOTAL:121val += skc->skc_slab_size * skc->skc_slab_total;122break;123case KMC_ALLOC:124val += skc->skc_obj_size * skc->skc_obj_alloc;125break;126case KMC_MAX:127val += skc->skc_obj_size * skc->skc_obj_max;128break;129}130}131132up_read(&spl_kmem_cache_sem);133rc = proc_doulongvec_minmax(&dummy, write, buffer, lenp, ppos);134}135136return (rc);137}138139static int140proc_dohostid(CONST_CTL_TABLE *table, int write,141void __user *buffer, size_t *lenp, loff_t *ppos)142{143char *end, str[32];144unsigned long hid;145spl_ctl_table dummy = *table;146147dummy.data = str;148dummy.maxlen = sizeof (str) - 1;149150if (!write)151snprintf(str, sizeof (str), "%lx",152(unsigned long) zone_get_hostid(NULL));153154/* always returns 0 */155proc_dostring(&dummy, write, buffer, lenp, ppos);156157if (write) {158/*159* We can't use proc_doulongvec_minmax() in the write160* case here because hostid, while a hex value, has no161* leading 0x, which confuses the helper function.162*/163164hid = simple_strtoul(str, &end, 16);165if (str == end)166return (-EINVAL);167spl_hostid = hid;168}169170return (0);171}172173static void174slab_seq_show_headers(struct seq_file *f)175{176seq_printf(f,177"--------------------- cache ----------"178"--------------------------------------------- "179"----- slab ------ "180"---- object ----- "181"--- emergency ---\n");182seq_printf(f,183"name "184" flags size alloc slabsize objsize "185"total alloc max "186"total alloc max "187"dlock alloc max\n");188}189190static int191slab_seq_show(struct seq_file *f, void *p)192{193spl_kmem_cache_t *skc = p;194195ASSERT(skc->skc_magic == SKC_MAGIC);196197if (skc->skc_flags & KMC_SLAB) {198/*199* This cache is backed by a generic Linux kmem cache which200* has its own accounting. For these caches we only track201* the number of active allocated objects that exist within202* the underlying Linux slabs. For the overall statistics of203* the underlying Linux cache please refer to /proc/slabinfo.204*/205spin_lock(&skc->skc_lock);206uint64_t objs_allocated =207percpu_counter_sum(&skc->skc_linux_alloc);208seq_printf(f, "%-36s ", skc->skc_name);209seq_printf(f, "0x%05lx %9s %9lu %8s %8u "210"%5s %5s %5s %5s %5lu %5s %5s %5s %5s\n",211(long unsigned)skc->skc_flags,212"-",213(long unsigned)(skc->skc_obj_size * objs_allocated),214"-",215(unsigned)skc->skc_obj_size,216"-", "-", "-", "-",217(long unsigned)objs_allocated,218"-", "-", "-", "-");219spin_unlock(&skc->skc_lock);220return (0);221}222223spin_lock(&skc->skc_lock);224seq_printf(f, "%-36s ", skc->skc_name);225seq_printf(f, "0x%05lx %9lu %9lu %8u %8u "226"%5lu %5lu %5lu %5lu %5lu %5lu %5lu %5lu %5lu\n",227(long unsigned)skc->skc_flags,228(long unsigned)(skc->skc_slab_size * skc->skc_slab_total),229(long unsigned)(skc->skc_obj_size * skc->skc_obj_alloc),230(unsigned)skc->skc_slab_size,231(unsigned)skc->skc_obj_size,232(long unsigned)skc->skc_slab_total,233(long unsigned)skc->skc_slab_alloc,234(long unsigned)skc->skc_slab_max,235(long unsigned)skc->skc_obj_total,236(long unsigned)skc->skc_obj_alloc,237(long unsigned)skc->skc_obj_max,238(long unsigned)skc->skc_obj_deadlock,239(long unsigned)skc->skc_obj_emergency,240(long unsigned)skc->skc_obj_emergency_max);241spin_unlock(&skc->skc_lock);242return (0);243}244245static void *246slab_seq_start(struct seq_file *f, loff_t *pos)247{248struct list_head *p;249loff_t n = *pos;250251down_read(&spl_kmem_cache_sem);252if (!n)253slab_seq_show_headers(f);254255p = spl_kmem_cache_list.next;256while (n--) {257p = p->next;258if (p == &spl_kmem_cache_list)259return (NULL);260}261262return (list_entry(p, spl_kmem_cache_t, skc_list));263}264265static void *266slab_seq_next(struct seq_file *f, void *p, loff_t *pos)267{268spl_kmem_cache_t *skc = p;269270++*pos;271return ((skc->skc_list.next == &spl_kmem_cache_list) ?272NULL : list_entry(skc->skc_list.next, spl_kmem_cache_t, skc_list));273}274275static void276slab_seq_stop(struct seq_file *f, void *v)277{278up_read(&spl_kmem_cache_sem);279}280281static const struct seq_operations slab_seq_ops = {282.show = slab_seq_show,283.start = slab_seq_start,284.next = slab_seq_next,285.stop = slab_seq_stop,286};287288static int289proc_slab_open(struct inode *inode, struct file *filp)290{291return (seq_open(filp, &slab_seq_ops));292}293294static const kstat_proc_op_t proc_slab_operations = {295#ifdef HAVE_PROC_OPS_STRUCT296.proc_open = proc_slab_open,297.proc_read = seq_read,298.proc_lseek = seq_lseek,299.proc_release = seq_release,300#else301.open = proc_slab_open,302.read = seq_read,303.llseek = seq_lseek,304.release = seq_release,305#endif306};307308static struct ctl_table spl_kmem_table[] = {309#ifdef DEBUG_KMEM310{311.procname = "kmem_used",312.data = &kmem_alloc_used,313.maxlen = sizeof (atomic64_t),314.mode = 0444,315.proc_handler = &proc_domemused,316},317{318.procname = "kmem_max",319.data = &kmem_alloc_max,320.maxlen = sizeof (uint64_t),321.extra1 = &table_min,322.extra2 = &table_max,323.mode = 0444,324.proc_handler = &proc_doulongvec_minmax,325},326#endif /* DEBUG_KMEM */327{328.procname = "slab_kvmem_total",329.data = (void *)(KMC_KVMEM | KMC_TOTAL),330.maxlen = sizeof (unsigned long),331.extra1 = &table_min,332.extra2 = &table_max,333.mode = 0444,334.proc_handler = &proc_doslab,335},336{337.procname = "slab_kvmem_alloc",338.data = (void *)(KMC_KVMEM | KMC_ALLOC),339.maxlen = sizeof (unsigned long),340.extra1 = &table_min,341.extra2 = &table_max,342.mode = 0444,343.proc_handler = &proc_doslab,344},345{346.procname = "slab_kvmem_max",347.data = (void *)(KMC_KVMEM | KMC_MAX),348.maxlen = sizeof (unsigned long),349.extra1 = &table_min,350.extra2 = &table_max,351.mode = 0444,352.proc_handler = &proc_doslab,353},354{},355};356357static struct ctl_table spl_kstat_table[] = {358{},359};360361static struct ctl_table spl_table[] = {362/*363* NB No .strategy entries have been provided since364* sysctl(8) prefers to go via /proc for portability.365*/366{367.procname = "gitrev",368.data = (char *)ZFS_META_GITREV,369.maxlen = sizeof (ZFS_META_GITREV),370.mode = 0444,371.proc_handler = &proc_dostring,372},373{374.procname = "hostid",375.data = &spl_hostid,376.maxlen = sizeof (unsigned long),377.mode = 0644,378.proc_handler = &proc_dohostid,379},380#ifdef HAVE_REGISTER_SYSCTL_TABLE381{382.procname = "kmem",383.mode = 0555,384.child = spl_kmem_table,385},386{387.procname = "kstat",388.mode = 0555,389.child = spl_kstat_table,390},391#endif392{},393};394395#ifdef HAVE_REGISTER_SYSCTL_TABLE396static struct ctl_table spl_dir[] = {397{398.procname = "spl",399.mode = 0555,400.child = spl_table,401},402{}403};404405static struct ctl_table spl_root[] = {406{407.procname = "kernel",408.mode = 0555,409.child = spl_dir,410},411{}412};413#endif414415static void spl_proc_cleanup(void)416{417remove_proc_entry("kstat", proc_spl);418remove_proc_entry("slab", proc_spl_kmem);419remove_proc_entry("kmem", proc_spl);420remove_proc_entry("spl", NULL);421422#ifndef HAVE_REGISTER_SYSCTL_TABLE423if (spl_kstat) {424unregister_sysctl_table(spl_kstat);425spl_kstat = NULL;426}427if (spl_kmem) {428unregister_sysctl_table(spl_kmem);429spl_kmem = NULL;430}431#endif432if (spl_header) {433unregister_sysctl_table(spl_header);434spl_header = NULL;435}436}437438#ifndef HAVE_REGISTER_SYSCTL_TABLE439440/*441* Traditionally, struct ctl_table arrays have been terminated by an "empty"442* sentinel element (specifically, one with .procname == NULL).443*444* Linux 6.6 began migrating away from this, adding register_sysctl_sz() so445* that callers could provide the size directly, and redefining446* register_sysctl() to just call register_sysctl_sz() with the array size. It447* retained support for the terminating element so that existing callers would448* continue to work.449*450* Linux 6.11 removed support for the terminating element, instead interpreting451* it as a real malformed element, and rejecting it.452*453* In order to continue support older kernels, we retain the terminating454* sentinel element for our sysctl tables, but instead detect availability of455* register_sysctl_sz(). If it exists, we pass it the array size -1, stopping456* the kernel from trying to process the terminator. For pre-6.6 kernels that457* don't have register_sysctl_sz(), we just use register_sysctl(), which can458* handle the terminating element as it always has.459*/460#ifdef HAVE_REGISTER_SYSCTL_SZ461#define spl_proc_register_sysctl(p, t) \462register_sysctl_sz(p, t, ARRAY_SIZE(t)-1)463#else464#define spl_proc_register_sysctl(p, t) \465register_sysctl(p, t)466#endif467#endif468469int470spl_proc_init(void)471{472int rc = 0;473474#ifdef HAVE_REGISTER_SYSCTL_TABLE475spl_header = register_sysctl_table(spl_root);476if (spl_header == NULL)477return (-EUNATCH);478#else479spl_header = spl_proc_register_sysctl("kernel/spl", spl_table);480if (spl_header == NULL)481return (-EUNATCH);482483spl_kmem = spl_proc_register_sysctl("kernel/spl/kmem", spl_kmem_table);484if (spl_kmem == NULL) {485rc = -EUNATCH;486goto out;487}488spl_kstat = spl_proc_register_sysctl("kernel/spl/kstat",489spl_kstat_table);490if (spl_kstat == NULL) {491rc = -EUNATCH;492goto out;493}494#endif495496proc_spl = proc_mkdir("spl", NULL);497if (proc_spl == NULL) {498rc = -EUNATCH;499goto out;500}501502proc_spl_kmem = proc_mkdir("kmem", proc_spl);503if (proc_spl_kmem == NULL) {504rc = -EUNATCH;505goto out;506}507508proc_spl_kmem_slab = proc_create_data("slab", 0444, proc_spl_kmem,509&proc_slab_operations, NULL);510if (proc_spl_kmem_slab == NULL) {511rc = -EUNATCH;512goto out;513}514515proc_spl_kstat = proc_mkdir("kstat", proc_spl);516if (proc_spl_kstat == NULL) {517rc = -EUNATCH;518goto out;519}520out:521if (rc)522spl_proc_cleanup();523524return (rc);525}526527void528spl_proc_fini(void)529{530spl_proc_cleanup();531}532533534