diff options
Diffstat (limited to 'sys/contrib/openzfs/lib/libspl')
-rw-r--r-- | sys/contrib/openzfs/lib/libspl/Makefile.am | 1 | ||||
-rw-r--r-- | sys/contrib/openzfs/lib/libspl/include/Makefile.am | 2 | ||||
-rw-r--r-- | sys/contrib/openzfs/lib/libspl/include/os/linux/sys/stat.h | 5 | ||||
-rw-r--r-- | sys/contrib/openzfs/lib/libspl/include/sys/debug.h | 4 | ||||
-rw-r--r-- | sys/contrib/openzfs/lib/libspl/include/sys/mod.h | 56 | ||||
-rw-r--r-- | sys/contrib/openzfs/lib/libspl/include/sys/tunables.h | 60 | ||||
-rw-r--r-- | sys/contrib/openzfs/lib/libspl/include/umem.h | 2 | ||||
-rw-r--r-- | sys/contrib/openzfs/lib/libspl/os/linux/getmntany.c | 36 | ||||
-rw-r--r-- | sys/contrib/openzfs/lib/libspl/tunables.c | 319 |
9 files changed, 478 insertions, 7 deletions
diff --git a/sys/contrib/openzfs/lib/libspl/Makefile.am b/sys/contrib/openzfs/lib/libspl/Makefile.am index f8943572bf29..6640ecd582a7 100644 --- a/sys/contrib/openzfs/lib/libspl/Makefile.am +++ b/sys/contrib/openzfs/lib/libspl/Makefile.am @@ -20,6 +20,7 @@ libspl_la_SOURCES = \ %D%/strlcat.c \ %D%/strlcpy.c \ %D%/timestamp.c \ + %D%/tunables.c \ %D%/include/sys/list.h \ %D%/include/sys/list_impl.h diff --git a/sys/contrib/openzfs/lib/libspl/include/Makefile.am b/sys/contrib/openzfs/lib/libspl/include/Makefile.am index 8c286142f298..21f0c70db9e7 100644 --- a/sys/contrib/openzfs/lib/libspl/include/Makefile.am +++ b/sys/contrib/openzfs/lib/libspl/include/Makefile.am @@ -45,6 +45,7 @@ libspl_sys_HEADERS = \ %D%/sys/list_impl.h \ %D%/sys/mhd.h \ %D%/sys/mkdev.h \ + %D%/sys/mod.h \ %D%/sys/policy.h \ %D%/sys/poll.h \ %D%/sys/priv.h \ @@ -58,6 +59,7 @@ libspl_sys_HEADERS = \ %D%/sys/time.h \ %D%/sys/trace_spl.h \ %D%/sys/trace_zfs.h \ + %D%/sys/tunables.h \ %D%/sys/types.h \ %D%/sys/types32.h \ %D%/sys/uio.h \ diff --git a/sys/contrib/openzfs/lib/libspl/include/os/linux/sys/stat.h b/sys/contrib/openzfs/lib/libspl/include/os/linux/sys/stat.h index 488554f4e844..a605af962a6d 100644 --- a/sys/contrib/openzfs/lib/libspl/include/os/linux/sys/stat.h +++ b/sys/contrib/openzfs/lib/libspl/include/os/linux/sys/stat.h @@ -31,6 +31,11 @@ #include <sys/mount.h> /* for BLKGETSIZE64 */ +#ifdef HAVE_STATX +#include <fcntl.h> +#include <linux/stat.h> +#endif + /* * Emulate Solaris' behavior of returning the block device size in fstat64(). */ diff --git a/sys/contrib/openzfs/lib/libspl/include/sys/debug.h b/sys/contrib/openzfs/lib/libspl/include/sys/debug.h index cced309bd1bb..02f33a68b75b 100644 --- a/sys/contrib/openzfs/lib/libspl/include/sys/debug.h +++ b/sys/contrib/openzfs/lib/libspl/include/sys/debug.h @@ -38,4 +38,8 @@ #define __maybe_unused __attribute__((unused)) #endif +#ifndef __must_check +#define __must_check __attribute__((warn_unused_result)) +#endif + #endif diff --git a/sys/contrib/openzfs/lib/libspl/include/sys/mod.h b/sys/contrib/openzfs/lib/libspl/include/sys/mod.h new file mode 100644 index 000000000000..ad19b6607a42 --- /dev/null +++ b/sys/contrib/openzfs/lib/libspl/include/sys/mod.h @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: CDDL-1.0 +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or https://opensource.org/licenses/CDDL-1.0. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2011 Nexenta Systems, Inc. All rights reserved. + * Copyright (c) 2012, 2018 by Delphix. All rights reserved. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright (c) 2025, Rob Norris <robn@despairlabs.com> + */ + +#ifndef _SYS_MOD_H +#define _SYS_MOD_H + +#include <sys/tunables.h> + +#define ZFS_MODULE_PARAM(scope, prefix, name, type, perm, desc) \ + static const zfs_tunable_t _zfs_tunable_##prefix##name = { \ + .zt_name = #prefix#name, \ + .zt_varp = &prefix##name, \ + .zt_varsz = sizeof (prefix##name), \ + .zt_type = ZFS_TUNABLE_TYPE_##type, \ + .zt_perm = ZFS_TUNABLE_PERM_##perm, \ + .zt_desc = desc \ + }; \ + static const zfs_tunable_t * \ + __zfs_tunable_##prefix##name \ + __attribute__((__section__("zfs_tunables"))) \ + __attribute__((__used__)) \ + = &_zfs_tunable_##prefix##name; + +#define ZFS_MODULE_PARAM_ARGS void +#define ZFS_MODULE_PARAM_CALL(scope_prefix, name_prefix, name, setfunc, \ + getfunc, perm, desc) + +#define EXPORT_SYMBOL(x) + +#endif diff --git a/sys/contrib/openzfs/lib/libspl/include/sys/tunables.h b/sys/contrib/openzfs/lib/libspl/include/sys/tunables.h new file mode 100644 index 000000000000..5d9bb3d71a4a --- /dev/null +++ b/sys/contrib/openzfs/lib/libspl/include/sys/tunables.h @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: CDDL-1.0 +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or https://opensource.org/licenses/CDDL-1.0. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2025, Rob Norris <robn@despairlabs.com> + */ + +#ifndef _SYS_TUNABLES_H +#define _SYS_TUNABLES_H + +typedef enum { + ZFS_TUNABLE_TYPE_INT, + ZFS_TUNABLE_TYPE_UINT, + ZFS_TUNABLE_TYPE_ULONG, + ZFS_TUNABLE_TYPE_U64, + ZFS_TUNABLE_TYPE_STRING, +} zfs_tunable_type_t; + +typedef enum { + ZFS_TUNABLE_PERM_ZMOD_RW, + ZFS_TUNABLE_PERM_ZMOD_RD, +} zfs_tunable_perm_t; + +typedef struct zfs_tunable { + const char *zt_name; + void *zt_varp; + size_t zt_varsz; + zfs_tunable_type_t zt_type; + zfs_tunable_perm_t zt_perm; + const char *zt_desc; +} zfs_tunable_t; + +int zfs_tunable_set(const zfs_tunable_t *tunable, const char *val); +int zfs_tunable_get(const zfs_tunable_t *tunable, char *val, size_t valsz); + +const zfs_tunable_t *zfs_tunable_lookup(const char *name); + +typedef int (*zfs_tunable_iter_t)(const zfs_tunable_t *tunable, void *arg); +void zfs_tunable_iter(zfs_tunable_iter_t cb, void *arg); + +#endif diff --git a/sys/contrib/openzfs/lib/libspl/include/umem.h b/sys/contrib/openzfs/lib/libspl/include/umem.h index 6945aae9f3ce..3e44610e4e21 100644 --- a/sys/contrib/openzfs/lib/libspl/include/umem.h +++ b/sys/contrib/openzfs/lib/libspl/include/umem.h @@ -102,7 +102,7 @@ static inline void * umem_alloc_aligned(size_t size, size_t align, int flags) { void *ptr = NULL; - int rc = EINVAL; + int rc; do { rc = posix_memalign(&ptr, align, size); diff --git a/sys/contrib/openzfs/lib/libspl/os/linux/getmntany.c b/sys/contrib/openzfs/lib/libspl/os/linux/getmntany.c index dcdf7b3d6fc9..ee1cdf59b9e5 100644 --- a/sys/contrib/openzfs/lib/libspl/os/linux/getmntany.c +++ b/sys/contrib/openzfs/lib/libspl/os/linux/getmntany.c @@ -85,13 +85,21 @@ _sol_getmntent(FILE *fp, struct mnttab *mgetp) } static int -getextmntent_impl(FILE *fp, struct extmnttab *mp) +getextmntent_impl(FILE *fp, struct extmnttab *mp, uint64_t *mnt_id) { int ret; struct stat64 st; + *mnt_id = 0; ret = _sol_getmntent(fp, (struct mnttab *)mp); if (ret == 0) { +#ifdef HAVE_STATX_MNT_ID + struct statx stx; + if (statx(AT_FDCWD, mp->mnt_mountp, + AT_STATX_SYNC_AS_STAT | AT_SYMLINK_NOFOLLOW, + STATX_MNT_ID, &stx) == 0 && (stx.stx_mask & STATX_MNT_ID)) + *mnt_id = stx.stx_mnt_id; +#endif if (stat64(mp->mnt_mountp, &st) != 0) { mp->mnt_major = 0; mp->mnt_minor = 0; @@ -110,6 +118,12 @@ getextmntent(const char *path, struct extmnttab *entry, struct stat64 *statbuf) struct stat64 st; FILE *fp; int match; + boolean_t have_mnt_id = B_FALSE; + uint64_t target_mnt_id = 0; + uint64_t entry_mnt_id; +#ifdef HAVE_STATX_MNT_ID + struct statx stx; +#endif if (strlen(path) >= MAXPATHLEN) { (void) fprintf(stderr, "invalid object; pathname too long\n"); @@ -128,6 +142,13 @@ getextmntent(const char *path, struct extmnttab *entry, struct stat64 *statbuf) return (-1); } +#ifdef HAVE_STATX_MNT_ID + if (statx(AT_FDCWD, path, AT_STATX_SYNC_AS_STAT | AT_SYMLINK_NOFOLLOW, + STATX_MNT_ID, &stx) == 0 && (stx.stx_mask & STATX_MNT_ID)) { + have_mnt_id = B_TRUE; + target_mnt_id = stx.stx_mnt_id; + } +#endif if ((fp = fopen(MNTTAB, "re")) == NULL) { (void) fprintf(stderr, "cannot open %s\n", MNTTAB); @@ -139,12 +160,15 @@ getextmntent(const char *path, struct extmnttab *entry, struct stat64 *statbuf) */ match = 0; - while (getextmntent_impl(fp, entry) == 0) { - if (makedev(entry->mnt_major, entry->mnt_minor) == - statbuf->st_dev) { - match = 1; - break; + while (getextmntent_impl(fp, entry, &entry_mnt_id) == 0) { + if (have_mnt_id) { + match = (entry_mnt_id == target_mnt_id); + } else { + match = makedev(entry->mnt_major, entry->mnt_minor) == + statbuf->st_dev; } + if (match) + break; } (void) fclose(fp); diff --git a/sys/contrib/openzfs/lib/libspl/tunables.c b/sys/contrib/openzfs/lib/libspl/tunables.c new file mode 100644 index 000000000000..67dc9710dee8 --- /dev/null +++ b/sys/contrib/openzfs/lib/libspl/tunables.c @@ -0,0 +1,319 @@ +// SPDX-License-Identifier: CDDL-1.0 +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or https://opensource.org/licenses/CDDL-1.0. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2025, Rob Norris <robn@despairlabs.com> + */ + +#include <stddef.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <limits.h> +#include <inttypes.h> +#include <sys/tunables.h> + +/* + * Userspace tunables. + * + * Tunables are external pointers to global variables that are wired up to the + * host environment in some way that allows the operator to directly change + * their values "under the hood". + * + * In userspace, the "host environment" is the program using libzpool.so. So + * that it can manipulate tunables if it wants, we provide an API to access + * them. + * + * Tunables are declared through the ZFS_MODULE_PARAM* macros, which associate + * a global variable with some metadata we can use to describe and access the + * tunable. This is done by creating a uniquely-named zfs_tunable_t. + * + * At runtime, we need a way to discover these zfs_tunable_t items. Since they + * are declared globally, all over the codebase, there's no central place to + * record or list them. So, we take advantage of the compiler's "linker set" + * feature. + * + * In the ZFS_MODULE_PARAM macro, after we create the zfs_tunable_t, we also + * create a zfs_tunable_t* pointing to it. That pointer is forced into the + * "zfs_tunables" ELF section in compiled object. At link time, the linker will + * collect all these pointers into one single big "zfs_tunable" section, and + * will generate two new symbols in the final object: __start_zfs_tunable and + * __stop_zfs_tunable. These point to the first and last item in that section, + * which allows us to access the pointers in that section like an array, and + * through those pointers access the tunable metadata, and from there the + * actual C variable that the tunable describes. + */ + +extern const zfs_tunable_t *__start_zfs_tunables; +extern const zfs_tunable_t *__stop_zfs_tunables; + +/* + * Because there are no tunables in libspl itself, the above symbols will not + * be generated, which will stop libspl being linked at all. To work around + * that, we force a symbol into that section, and then when iterating, skip + * any NULL pointers. + */ +static void *__zfs_tunable__placeholder + __attribute__((__section__("zfs_tunables"))) + __attribute__((__used__)) = NULL; + +/* + * Find the name tunable by walking through the linker set and comparing names, + * as described above. This is not particularly efficient but it's a fairly + * rare task, so it shouldn't be a big deal. + */ +const zfs_tunable_t * +zfs_tunable_lookup(const char *name) +{ + for (const zfs_tunable_t **ztp = &__start_zfs_tunables; + ztp != &__stop_zfs_tunables; ztp++) { + const zfs_tunable_t *zt = *ztp; + if (zt == NULL) + continue; + if (strcmp(name, zt->zt_name) == 0) + return (zt); + } + + return (NULL); +} + +/* + * Like zfs_tunable_lookup, but call the provided callback for each tunable. + */ +void +zfs_tunable_iter(zfs_tunable_iter_t cb, void *arg) +{ + for (const zfs_tunable_t **ztp = &__start_zfs_tunables; + ztp != &__stop_zfs_tunables; ztp++) { + const zfs_tunable_t *zt = *ztp; + if (zt == NULL) + continue; + if (cb(zt, arg)) + return; + } +} + +/* + * Parse a string into an int or uint. It's easier to have a pair of "generic" + * functions that clamp to a given min and max rather than have multiple + * functions for each width of type. + */ +static int +zfs_tunable_parse_int(const char *val, intmax_t *np, + intmax_t min, intmax_t max) +{ + intmax_t n; + char *end; + errno = 0; + n = strtoimax(val, &end, 0); + if (errno != 0) + return (errno); + if (*end != '\0') + return (EINVAL); + if (n < min || n > max) + return (ERANGE); + *np = n; + return (0); +} + +static int +zfs_tunable_parse_uint(const char *val, uintmax_t *np, + uintmax_t min, uintmax_t max) +{ + uintmax_t n; + char *end; + errno = 0; + n = strtoumax(val, &end, 0); + if (errno != 0) + return (errno); + if (*end != '\0') + return (EINVAL); + if (strchr(val, '-')) + return (ERANGE); + if (n < min || n > max) + return (ERANGE); + *np = n; + return (0); +} + +/* + * Set helpers for each tunable type. Parses the string, and if produces a + * valid value for the tunable, sets it. No effort is made to make sure the + * tunable is of the right type; that's done in zfs_tunable_set() below. + */ +static int +zfs_tunable_set_int(const zfs_tunable_t *zt, const char *val) +{ + intmax_t n; + int err = zfs_tunable_parse_int(val, &n, INT_MIN, INT_MAX); + if (err != 0) + return (err); + *(int *)zt->zt_varp = n; + return (0); +} + +static int +zfs_tunable_set_uint(const zfs_tunable_t *zt, const char *val) +{ + uintmax_t n; + int err = zfs_tunable_parse_uint(val, &n, 0, UINT_MAX); + if (err != 0) + return (err); + *(unsigned int *)zt->zt_varp = n; + return (0); +} + +static int +zfs_tunable_set_ulong(const zfs_tunable_t *zt, const char *val) +{ + uintmax_t n; + int err = zfs_tunable_parse_uint(val, &n, 0, ULONG_MAX); + if (err != 0) + return (err); + *(unsigned long *)zt->zt_varp = n; + return (0); +} + +static int +zfs_tunable_set_u64(const zfs_tunable_t *zt, const char *val) +{ + uintmax_t n; + int err = zfs_tunable_parse_uint(val, &n, 0, UINT64_MAX); + if (err != 0) + return (err); + *(uint64_t *)zt->zt_varp = n; + return (0); +} + +static int +zfs_tunable_set_string(const zfs_tunable_t *zt, const char *val) +{ + (void) zt, (void) val; + /* + * We can't currently handle strings. String tunables are pointers + * into read-only memory, so we can update the pointer, but not the + * contents. That would mean taking an allocation, but we don't have + * an obvious place to free it. + * + * For now, it's no big deal as there's only a couple of string + * tunables anyway. + */ + return (ENOTSUP); +} + +/* + * Get helpers for each tunable type. Converts the value to a string if + * necessary and writes it into the provided buffer. The type is assumed to + * be correct; zfs_tunable_get() below will call the correct function for the + * type. + */ +static int +zfs_tunable_get_int(const zfs_tunable_t *zt, char *val, size_t valsz) +{ + snprintf(val, valsz, "%d", *(int *)zt->zt_varp); + return (0); +} + +static int +zfs_tunable_get_uint(const zfs_tunable_t *zt, char *val, size_t valsz) +{ + snprintf(val, valsz, "%u", *(unsigned int *)zt->zt_varp); + return (0); +} + +static int +zfs_tunable_get_ulong(const zfs_tunable_t *zt, char *val, size_t valsz) +{ + snprintf(val, valsz, "%lu", *(unsigned long *)zt->zt_varp); + return (0); +} + +static int +zfs_tunable_get_u64(const zfs_tunable_t *zt, char *val, size_t valsz) +{ + snprintf(val, valsz, "%"PRIu64, *(uint64_t *)zt->zt_varp); + return (0); +} + +static int +zfs_tunable_get_string(const zfs_tunable_t *zt, char *val, size_t valsz) +{ + strlcpy(val, *(char **)zt->zt_varp, valsz); + return (0); +} + +/* The public set function. Delegates to the type-specific version. */ +int +zfs_tunable_set(const zfs_tunable_t *zt, const char *val) +{ + int err; + switch (zt->zt_type) { + case ZFS_TUNABLE_TYPE_INT: + err = zfs_tunable_set_int(zt, val); + break; + case ZFS_TUNABLE_TYPE_UINT: + err = zfs_tunable_set_uint(zt, val); + break; + case ZFS_TUNABLE_TYPE_ULONG: + err = zfs_tunable_set_ulong(zt, val); + break; + case ZFS_TUNABLE_TYPE_U64: + err = zfs_tunable_set_u64(zt, val); + break; + case ZFS_TUNABLE_TYPE_STRING: + err = zfs_tunable_set_string(zt, val); + break; + default: + err = EOPNOTSUPP; + break; + } + return (err); +} + +/* The public get function. Delegates to the type-specific version. */ +int +zfs_tunable_get(const zfs_tunable_t *zt, char *val, size_t valsz) +{ + int err; + switch (zt->zt_type) { + case ZFS_TUNABLE_TYPE_INT: + err = zfs_tunable_get_int(zt, val, valsz); + break; + case ZFS_TUNABLE_TYPE_UINT: + err = zfs_tunable_get_uint(zt, val, valsz); + break; + case ZFS_TUNABLE_TYPE_ULONG: + err = zfs_tunable_get_ulong(zt, val, valsz); + break; + case ZFS_TUNABLE_TYPE_U64: + err = zfs_tunable_get_u64(zt, val, valsz); + break; + case ZFS_TUNABLE_TYPE_STRING: + err = zfs_tunable_get_string(zt, val, valsz); + break; + default: + err = EOPNOTSUPP; + break; + } + return (err); +} |