commit: 2c6dff36d4a6d604e803e59db4d3ac6496577274
Author: Michał Górny <mgorny <AT> gentoo <DOT> org>
AuthorDate: Sat Nov 29 20:15:54 2025 +0000
Commit: Michał Górny <mgorny <AT> gentoo <DOT> org>
CommitDate: Sat Nov 29 20:15:54 2025 +0000
URL: https://gitweb.gentoo.org/proj/steve.git/commit/?id=2c6dff36
Implement load average ioctls
Signed-off-by: Michał Górny <mgorny <AT> gentoo.org>
steve.cxx | 56 +++++++++++++++++++++++++++++-------------------------
stevie.cxx | 64 ++++++++++++++++++++++++++++++++++++++++++++++++--------------
2 files changed, 80 insertions(+), 40 deletions(-)
diff --git a/steve.cxx b/steve.cxx
index 37dcd69..ce3b9f8 100644
--- a/steve.cxx
+++ b/steve.cxx
@@ -397,7 +397,7 @@ static void steve_poll(
static void steve_ioctl(
fuse_req_t req, int cmd, void *, fuse_file_info *fi,
- unsigned flags, const void *in_buf, size_t in_buf_sz, size_t out_buf_sz)
+ unsigned flags, const void *in_buf, size_t, size_t)
{
steve_state *state = static_cast<steve_state *>(fuse_req_userdata(req));
/* FUSE uses the wrong type, sigh */
@@ -412,25 +412,21 @@ static void steve_ioctl(
std::print(stderr, "PID {} requested ioctl 0x{:08x}\n",
fi->fh, ioctl_num);
- /* TODO: is this generated by fuse? would assert() be better? */
- if (STEVE_IOC_IS_GET(ioctl_num) && out_buf_sz != sizeof(int64_t)) {
- fuse_reply_err(req, EINVAL);
- return;
- }
- if (STEVE_IOC_IS_SET(ioctl_num) && in_buf_sz != sizeof(int64_t)) {
- fuse_reply_err(req, EINVAL);
- return;
- }
-
int64_t val;
+ double dval;
if (STEVE_IOC_IS_SET(ioctl_num)) {
- const int64_t *in_val = static_cast<const int64_t *>(in_buf);
- if (*in_val < 0 || *in_val >= INT_MAX) {
- fuse_reply_err(req, EINVAL);
- return;
- }
+ if (ioctl_num == STEVE_IOC_SET_LOAD_AVG) {
+ const double *in_val = static_cast<const double
*>(in_buf);
+ dval = *in_val;
+ } else {
+ const int64_t *in_val = static_cast<const int64_t
*>(in_buf);
+ if (*in_val < 0 || *in_val >= INT_MAX) {
+ fuse_reply_err(req, EINVAL);
+ return;
+ }
- val = *in_val;
+ val = *in_val;
+ }
}
switch (ioctl_num) {
@@ -442,6 +438,10 @@ static void steve_ioctl(
val = state->jobs;
fuse_reply_ioctl(req, 0, &val, sizeof(val));
break;
+ case STEVE_IOC_GET_LOAD_AVG:
+ dval = state->max_load_avg;
+ fuse_reply_ioctl(req, 0, &dval, sizeof(dval));
+ break;
case STEVE_IOC_GET_MIN_JOBS:
val = state->min_jobs;
fuse_reply_ioctl(req, 0, &val, sizeof(val));
@@ -472,6 +472,16 @@ static void steve_ioctl(
fuse_reply_ioctl(req, 0, nullptr, 0);
steve_wake_waiters(state);
break;
+ case STEVE_IOC_SET_LOAD_AVG:
+ if (dval < 1) {
+ fuse_reply_err(req, EINVAL);
+ return;
+ }
+ state->max_load_avg = dval;
+ std::print("PID {} set load-average to {}\n", fi->fh,
state->max_load_avg);
+ fuse_reply_ioctl(req, 0, nullptr, 0);
+ steve_wake_waiters(state);
+ break;
default:
fuse_reply_err(req, ENOTTY);
}
@@ -566,15 +576,9 @@ int main(int argc, char **argv)
}
break;
case 'l':
- {
- char *endptr;
- errno = 0;
- double load_avg_arg = strtod(optarg,
&endptr);
- if (*endptr || errno == ERANGE ||
load_avg_arg < 1) {
- std::print(stderr, "invalid
load average value (must be >=1): {}\n", optarg);
- return 1;
- }
- state.max_load_avg = load_avg_arg;
+ if (!arg_to_load_avg(optarg,
&state.max_load_avg)) {
+ std::print(stderr, "invalid load
average value (must be >=1): {}\n", optarg);
+ return 1;
}
break;
case 'v':
diff --git a/stevie.cxx b/stevie.cxx
index 2957825..3fdb95a 100644
--- a/stevie.cxx
+++ b/stevie.cxx
@@ -12,6 +12,7 @@
#include <cstring>
#include <print>
#include <utility>
+#include <variant>
#include <vector>
#include <fcntl.h>
@@ -138,6 +139,9 @@ static constexpr char stevie_usage[] =
" --get-jobs, -j print total job number\n"
" --set-jobs JOBS, -J JOBS\n"
" set total job number\n"
+" --get-load-average, -l print max load-average\n"
+" --set-load-average LOAD_AVG, -L LOAD_AVG\n"
+" set max load-average\n"
" --get-min-jobs, -m print min-job number\n"
" --set-min-jobs JOBS, -M JOBS\n"
" set min-job number\n";
@@ -149,22 +153,25 @@ static const struct option stevie_long_opts[] = {
{"get-tokens", no_argument, 0, 't'},
{"get-jobs", no_argument, 0, 'j'},
{"set-jobs", required_argument, 0, 'J'},
+ {"get-load-average", no_argument, 0, 'l'},
+ {"set-load-average", required_argument, 0, 'L'},
{"get-min-jobs", no_argument, 0, 'm'},
{"set-min-jobs", required_argument, 0, 'M'},
{},
};
-static const char *stevie_short_opts = "+hVtjJ:mM:";
+static const char *stevie_short_opts = "+hVtjJ:lL:mM:";
int main(int argc, char **argv)
{
const char *jobserver_path = "/dev/steve";
int opt;
- std::vector<std::pair<unsigned long, int64_t>> actions;
+ std::vector<std::pair<unsigned long, std::variant<int64_t, double>>>
actions;
while ((opt = getopt_long(argc, argv, stevie_short_opts,
stevie_long_opts, nullptr)) != -1) {
unsigned long ioctl_num = 0;
+ std::variant<int64_t, double> ioctl_val = 0;
switch (opt) {
case 'h':
std::print(stevie_usage, argv[0]);
@@ -184,6 +191,14 @@ int main(int argc, char **argv)
case 'J':
ioctl_num = STEVE_IOC_SET_JOBS;
break;
+ case 'l':
+ ioctl_num = STEVE_IOC_GET_LOAD_AVG;
+ ioctl_val = 0.0;
+ break;
+ case 'L':
+ ioctl_num = STEVE_IOC_SET_LOAD_AVG;
+ ioctl_val = 0.0;
+ break;
case 'm':
ioctl_num = STEVE_IOC_GET_MIN_JOBS;
break;
@@ -197,14 +212,23 @@ int main(int argc, char **argv)
if (ioctl_num != 0) {
if (STEVE_IOC_IS_GET(ioctl_num)) {
- actions.emplace_back(ioctl_num, 0);
+ actions.emplace_back(ioctl_num, ioctl_val);
} else if (STEVE_IOC_IS_SET(ioctl_num)) {
- long long_arg;
- if (!arg_to_long(optarg, &long_arg)) {
- std::print(stderr, "invalid value:
{}\n", optarg);
- return 1;
- }
- actions.emplace_back(ioctl_num, long_arg);
+ if (double *dval =
std::get_if<double>(&ioctl_val)) {
+ if (!arg_to_load_avg(optarg, dval)) {
+ std::print(stderr, "invalid
load average value (must be >=1): {}\n", optarg);
+ return 1;
+ }
+ } else if
(std::holds_alternative<int64_t>(ioctl_val)) {
+ long long_arg;
+ if (!arg_to_long(optarg, &long_arg)) {
+ std::print(stderr, "invalid
value: {}\n", optarg);
+ return 1;
+ }
+ ioctl_val = long_arg;
+ } else
+ assert(0 && "not reached");
+ actions.emplace_back(ioctl_num, ioctl_val);
} else
assert(0 && "not reached");
}
@@ -224,16 +248,28 @@ int main(int argc, char **argv)
for (auto &action : actions) {
unsigned long ioctl_num = action.first;
- int64_t ioctl_val = action.second;
+ int64_t *ioctl_val = std::get_if<int64_t>(&action.second);
+ double *ioctl_dval = std::get_if<double>(&action.second);
+
+ int ret = -1;
+ if (ioctl_val)
+ ret = ioctl(jobserver_fd, ioctl_num, ioctl_val);
+ else if (ioctl_dval)
+ ret = ioctl(jobserver_fd, ioctl_num, ioctl_dval);
+ else
+ assert(0 && "not reached");
- if (ioctl(jobserver_fd, ioctl_num, &ioctl_val) != 0) {
+ if (ret != 0) {
perror("ioctl failed");
return 1;
}
- if (STEVE_IOC_IS_GET(ioctl_num))
- std::print("{}\n", ioctl_val);
- else if (STEVE_IOC_IS_SET(ioctl_num))
+ if (STEVE_IOC_IS_GET(ioctl_num)) {
+ if (ioctl_val)
+ std::print("{}\n", *ioctl_val);
+ else if (ioctl_dval)
+ std::print("{}\n", *ioctl_dval);
+ } else if (STEVE_IOC_IS_SET(ioctl_num))
std::print("ok\n");
else
assert(0 && "not reached");