Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/openzfs/lib/libspl/tunables.c
48378 views
1
// SPDX-License-Identifier: CDDL-1.0
2
/*
3
* CDDL HEADER START
4
*
5
* The contents of this file are subject to the terms of the
6
* Common Development and Distribution License (the "License").
7
* You may not use this file except in compliance with the License.
8
*
9
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10
* or https://opensource.org/licenses/CDDL-1.0.
11
* See the License for the specific language governing permissions
12
* and limitations under the License.
13
*
14
* When distributing Covered Code, include this CDDL HEADER in each
15
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16
* If applicable, add the following below this CDDL HEADER, with the
17
* fields enclosed by brackets "[]" replaced with your own identifying
18
* information: Portions Copyright [yyyy] [name of copyright owner]
19
*
20
* CDDL HEADER END
21
*/
22
23
/*
24
* Copyright (c) 2025, Rob Norris <[email protected]>
25
*/
26
27
#include <stddef.h>
28
#include <string.h>
29
#include <stdlib.h>
30
#include <stdio.h>
31
#include <errno.h>
32
#include <limits.h>
33
#include <inttypes.h>
34
#include <sys/tunables.h>
35
36
/*
37
* Userspace tunables.
38
*
39
* Tunables are external pointers to global variables that are wired up to the
40
* host environment in some way that allows the operator to directly change
41
* their values "under the hood".
42
*
43
* In userspace, the "host environment" is the program using libzpool.so. So
44
* that it can manipulate tunables if it wants, we provide an API to access
45
* them.
46
*
47
* Tunables are declared through the ZFS_MODULE_PARAM* macros, which associate
48
* a global variable with some metadata we can use to describe and access the
49
* tunable. This is done by creating a uniquely-named zfs_tunable_t.
50
*
51
* At runtime, we need a way to discover these zfs_tunable_t items. Since they
52
* are declared globally, all over the codebase, there's no central place to
53
* record or list them. So, we take advantage of the compiler's "linker set"
54
* feature.
55
*
56
* In the ZFS_MODULE_PARAM macro, after we create the zfs_tunable_t, we also
57
* create a zfs_tunable_t* pointing to it. That pointer is forced into the
58
* "zfs_tunables" ELF section in compiled object. At link time, the linker will
59
* collect all these pointers into one single big "zfs_tunable" section, and
60
* will generate two new symbols in the final object: __start_zfs_tunable and
61
* __stop_zfs_tunable. These point to the first and last item in that section,
62
* which allows us to access the pointers in that section like an array, and
63
* through those pointers access the tunable metadata, and from there the
64
* actual C variable that the tunable describes.
65
*/
66
67
extern const zfs_tunable_t *__start_zfs_tunables;
68
extern const zfs_tunable_t *__stop_zfs_tunables;
69
70
/*
71
* Because there are no tunables in libspl itself, the above symbols will not
72
* be generated, which will stop libspl being linked at all. To work around
73
* that, we force a symbol into that section, and then when iterating, skip
74
* any NULL pointers.
75
*/
76
static void *__zfs_tunable__placeholder
77
__attribute__((__section__("zfs_tunables")))
78
__attribute__((__used__)) = NULL;
79
80
/*
81
* Find the name tunable by walking through the linker set and comparing names,
82
* as described above. This is not particularly efficient but it's a fairly
83
* rare task, so it shouldn't be a big deal.
84
*/
85
const zfs_tunable_t *
86
zfs_tunable_lookup(const char *name)
87
{
88
for (const zfs_tunable_t **ztp = &__start_zfs_tunables;
89
ztp != &__stop_zfs_tunables; ztp++) {
90
const zfs_tunable_t *zt = *ztp;
91
if (zt == NULL)
92
continue;
93
if (strcmp(name, zt->zt_name) == 0)
94
return (zt);
95
}
96
97
return (NULL);
98
}
99
100
/*
101
* Like zfs_tunable_lookup, but call the provided callback for each tunable.
102
*/
103
void
104
zfs_tunable_iter(zfs_tunable_iter_t cb, void *arg)
105
{
106
for (const zfs_tunable_t **ztp = &__start_zfs_tunables;
107
ztp != &__stop_zfs_tunables; ztp++) {
108
const zfs_tunable_t *zt = *ztp;
109
if (zt == NULL)
110
continue;
111
if (cb(zt, arg))
112
return;
113
}
114
}
115
116
/*
117
* Parse a string into an int or uint. It's easier to have a pair of "generic"
118
* functions that clamp to a given min and max rather than have multiple
119
* functions for each width of type.
120
*/
121
static int
122
zfs_tunable_parse_int(const char *val, intmax_t *np,
123
intmax_t min, intmax_t max)
124
{
125
intmax_t n;
126
char *end;
127
errno = 0;
128
n = strtoimax(val, &end, 0);
129
if (errno != 0)
130
return (errno);
131
if (*end != '\0')
132
return (EINVAL);
133
if (n < min || n > max)
134
return (ERANGE);
135
*np = n;
136
return (0);
137
}
138
139
static int
140
zfs_tunable_parse_uint(const char *val, uintmax_t *np,
141
uintmax_t min, uintmax_t max)
142
{
143
uintmax_t n;
144
char *end;
145
errno = 0;
146
n = strtoumax(val, &end, 0);
147
if (errno != 0)
148
return (errno);
149
if (*end != '\0')
150
return (EINVAL);
151
if (strchr(val, '-'))
152
return (ERANGE);
153
if (n < min || n > max)
154
return (ERANGE);
155
*np = n;
156
return (0);
157
}
158
159
/*
160
* Set helpers for each tunable type. Parses the string, and if produces a
161
* valid value for the tunable, sets it. No effort is made to make sure the
162
* tunable is of the right type; that's done in zfs_tunable_set() below.
163
*/
164
static int
165
zfs_tunable_set_int(const zfs_tunable_t *zt, const char *val)
166
{
167
intmax_t n;
168
int err = zfs_tunable_parse_int(val, &n, INT_MIN, INT_MAX);
169
if (err != 0)
170
return (err);
171
*(int *)zt->zt_varp = n;
172
return (0);
173
}
174
175
static int
176
zfs_tunable_set_uint(const zfs_tunable_t *zt, const char *val)
177
{
178
uintmax_t n;
179
int err = zfs_tunable_parse_uint(val, &n, 0, UINT_MAX);
180
if (err != 0)
181
return (err);
182
*(unsigned int *)zt->zt_varp = n;
183
return (0);
184
}
185
186
static int
187
zfs_tunable_set_ulong(const zfs_tunable_t *zt, const char *val)
188
{
189
uintmax_t n;
190
int err = zfs_tunable_parse_uint(val, &n, 0, ULONG_MAX);
191
if (err != 0)
192
return (err);
193
*(unsigned long *)zt->zt_varp = n;
194
return (0);
195
}
196
197
static int
198
zfs_tunable_set_u64(const zfs_tunable_t *zt, const char *val)
199
{
200
uintmax_t n;
201
int err = zfs_tunable_parse_uint(val, &n, 0, UINT64_MAX);
202
if (err != 0)
203
return (err);
204
*(uint64_t *)zt->zt_varp = n;
205
return (0);
206
}
207
208
static int
209
zfs_tunable_set_string(const zfs_tunable_t *zt, const char *val)
210
{
211
(void) zt, (void) val;
212
/*
213
* We can't currently handle strings. String tunables are pointers
214
* into read-only memory, so we can update the pointer, but not the
215
* contents. That would mean taking an allocation, but we don't have
216
* an obvious place to free it.
217
*
218
* For now, it's no big deal as there's only a couple of string
219
* tunables anyway.
220
*/
221
return (ENOTSUP);
222
}
223
224
/*
225
* Get helpers for each tunable type. Converts the value to a string if
226
* necessary and writes it into the provided buffer. The type is assumed to
227
* be correct; zfs_tunable_get() below will call the correct function for the
228
* type.
229
*/
230
static int
231
zfs_tunable_get_int(const zfs_tunable_t *zt, char *val, size_t valsz)
232
{
233
snprintf(val, valsz, "%d", *(int *)zt->zt_varp);
234
return (0);
235
}
236
237
static int
238
zfs_tunable_get_uint(const zfs_tunable_t *zt, char *val, size_t valsz)
239
{
240
snprintf(val, valsz, "%u", *(unsigned int *)zt->zt_varp);
241
return (0);
242
}
243
244
static int
245
zfs_tunable_get_ulong(const zfs_tunable_t *zt, char *val, size_t valsz)
246
{
247
snprintf(val, valsz, "%lu", *(unsigned long *)zt->zt_varp);
248
return (0);
249
}
250
251
static int
252
zfs_tunable_get_u64(const zfs_tunable_t *zt, char *val, size_t valsz)
253
{
254
snprintf(val, valsz, "%"PRIu64, *(uint64_t *)zt->zt_varp);
255
return (0);
256
}
257
258
static int
259
zfs_tunable_get_string(const zfs_tunable_t *zt, char *val, size_t valsz)
260
{
261
strlcpy(val, *(char **)zt->zt_varp, valsz);
262
return (0);
263
}
264
265
/* The public set function. Delegates to the type-specific version. */
266
int
267
zfs_tunable_set(const zfs_tunable_t *zt, const char *val)
268
{
269
int err;
270
switch (zt->zt_type) {
271
case ZFS_TUNABLE_TYPE_INT:
272
err = zfs_tunable_set_int(zt, val);
273
break;
274
case ZFS_TUNABLE_TYPE_UINT:
275
err = zfs_tunable_set_uint(zt, val);
276
break;
277
case ZFS_TUNABLE_TYPE_ULONG:
278
err = zfs_tunable_set_ulong(zt, val);
279
break;
280
case ZFS_TUNABLE_TYPE_U64:
281
err = zfs_tunable_set_u64(zt, val);
282
break;
283
case ZFS_TUNABLE_TYPE_STRING:
284
err = zfs_tunable_set_string(zt, val);
285
break;
286
default:
287
err = EOPNOTSUPP;
288
break;
289
}
290
return (err);
291
}
292
293
/* The public get function. Delegates to the type-specific version. */
294
int
295
zfs_tunable_get(const zfs_tunable_t *zt, char *val, size_t valsz)
296
{
297
int err;
298
switch (zt->zt_type) {
299
case ZFS_TUNABLE_TYPE_INT:
300
err = zfs_tunable_get_int(zt, val, valsz);
301
break;
302
case ZFS_TUNABLE_TYPE_UINT:
303
err = zfs_tunable_get_uint(zt, val, valsz);
304
break;
305
case ZFS_TUNABLE_TYPE_ULONG:
306
err = zfs_tunable_get_ulong(zt, val, valsz);
307
break;
308
case ZFS_TUNABLE_TYPE_U64:
309
err = zfs_tunable_get_u64(zt, val, valsz);
310
break;
311
case ZFS_TUNABLE_TYPE_STRING:
312
err = zfs_tunable_get_string(zt, val, valsz);
313
break;
314
default:
315
err = EOPNOTSUPP;
316
break;
317
}
318
return (err);
319
}
320
321