commit: d9e02f319d92d82bac098e56ac7ea2dc33d904a7
Author: Michał Górny <mgorny <AT> gentoo <DOT> org>
AuthorDate: Mon Dec 8 12:07:26 2025 +0000
Commit: Michał Górny <mgorny <AT> gentoo <DOT> org>
CommitDate: Mon Dec 8 12:07:26 2025 +0000
URL: https://gitweb.gentoo.org/proj/steve.git/commit/?id=d9e02f31
Use /proc/loadavg for more accurate load measurement
Signed-off-by: Michał Górny <mgorny <AT> gentoo.org>
steve.cxx | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 57 insertions(+), 7 deletions(-)
diff --git a/steve.cxx b/steve.cxx
index 92615bd..2e69a36 100644
--- a/steve.cxx
+++ b/steve.cxx
@@ -14,6 +14,7 @@
#include <cstddef>
#include <cstdio>
#include <cstdlib>
+#include <cstring>
#include <cerrno>
#include <climits>
#include <csignal>
@@ -93,6 +94,7 @@ struct steve_state {
std::deque<steve_waiter> waiters;
std::unordered_map<uint64_t, steve_process> processes;
struct event_base *evb;
+ int loadavg_fd{-2};
/* to workaround lack of fuse_buf_free(), keep a global buffer */
/* https://github.com/libfuse/libfuse/issues/1373 */
@@ -106,6 +108,57 @@ enum class steve_token_availability {
load_exceeded,
};
+static void steve_get_load(steve_state *state)
+{
+ if (state->loadavg_fd == -2) {
+ state->loadavg_fd = open("/proc/loadavg", O_RDONLY);
+ if (state->loadavg_fd == -1)
+ perror("Unable to open /proc/loadavg, falling back to
getloadavg()");
+ }
+
+ if (state->loadavg_fd != -1) {
+ char buf[64];
+ ssize_t rd;
+
+ if (lseek(state->loadavg_fd, 0, SEEK_SET) == 0 && (rd =
read(state->loadavg_fd, &buf, sizeof(buf) - 1)) > 0) {
+ buf[rd] = 0;
+
+ char *begin = buf;
+ for (int field = 0; *begin && field != 3; ++begin) {
+ if (*begin == ' ')
+ ++field;
+ }
+ char *end = strchr(buf, '/');
+
+ if (*begin && *end) {
+ *end = 0;
+ long load;
+ /* Decrease by one to account for our process.
*/
+ if (arg_to_long(begin, &load)) {
+ state->load_avg = load - 1;
+ return;
+ }
+ }
+
+ std::print(stderr, "Parsing /proc/loadavg failed, value
= {}\n", buf);
+ } else
+ perror("Reading /proc/loadavg failed, falling back to
getloadavg()");
+
+ close(state->loadavg_fd);
+ state->loadavg_fd = -1;
+ }
+
+ if (getloadavg(&state->load_avg, 1) == -1) {
+ static bool warned = false;
+ if (!warned) {
+ perror("getloadavg() failed, will ignore (further
warnings will be suppressed)");
+ warned = true;
+ }
+ /* to make it clear it failed */
+ state->load_avg = -0.0;
+ }
+}
+
static steve_token_availability steve_can_give_token(steve_state *state,
uint64_t pid)
{
/* if there is a token reserved, we give it immediately (even if load
is exceeded now) */
@@ -117,13 +170,8 @@ static steve_token_availability
steve_can_give_token(steve_state *state, uint64_
if (state->max_load_avg > 0) {
if (state->jobs < state->min_jobs + state->tokens)
return steve_token_availability::available;
- if (getloadavg(&state->load_avg, 1) == -1) {
- static bool warned = false;
- if (!warned) {
- perror("getloadavg() failed, will ignore
(further warnings will be suppressed)");
- warned = true;
- }
- } else if (state->load_avg > state->max_load_avg)
+ steve_get_load(state);
+ if (state->load_avg > state->max_load_avg)
return steve_token_availability::load_exceeded;
}
@@ -268,6 +316,8 @@ static void steve_destroy(void *userdata)
state->waiters.clear();
state->processes.clear();
+ if (state->loadavg_fd >= 0)
+ close(state->loadavg_fd);
}
static void steve_open(fuse_req_t req, struct fuse_file_info *fi)