mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 18:40:43 +03:00
libspl: add API for manipulating tunables
Sponsored-by: https://despairlabs.com/sponsor/ Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Alexander Motin <mav@FreeBSD.org> Signed-off-by: Rob Norris <robn@despairlabs.com> Closes #17537
This commit is contained in:
committed by
Brian Behlendorf
parent
967ce75669
commit
cb9742e532
@@ -49,4 +49,12 @@ typedef struct zfs_tunable {
|
||||
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
|
||||
|
||||
@@ -25,6 +25,12 @@
|
||||
*/
|
||||
|
||||
#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>
|
||||
|
||||
/*
|
||||
@@ -70,3 +76,244 @@ extern const zfs_tunable_t *__stop_zfs_tunables;
|
||||
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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user