]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
parse-util: add parse_loadavg_fixed_point
authorAnita Zhang <the.anitazha@gmail.com>
Thu, 4 Jun 2020 09:13:29 +0000 (02:13 -0700)
committerAnita Zhang <the.anitazha@gmail.com>
Wed, 7 Oct 2020 23:17:24 +0000 (16:17 -0700)
src/basic/linux/loadavg.h [new file with mode: 0644]
src/basic/meson.build
src/basic/parse-util.c
src/basic/parse-util.h
src/test/test-parse-util.c

diff --git a/src/basic/linux/loadavg.h b/src/basic/linux/loadavg.h
new file mode 100644 (file)
index 0000000..521a787
--- /dev/null
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_SCHED_LOADAVG_H
+#define _LINUX_SCHED_LOADAVG_H
+
+/*
+ * These are the constant used to fake the fixed-point load-average
+ * counting. Some notes:
+ *  - 11 bit fractions expand to 22 bits by the multiplies: this gives
+ *    a load-average precision of 10 bits integer + 11 bits fractional
+ *  - if you want to count load-averages more often, you need more
+ *    precision, or rounding will get you. With 2-second counting freq,
+ *    the EXP_n values would be 1981, 2034 and 2043 if still using only
+ *    11 bit fractions.
+ */
+extern unsigned long avenrun[];                 /* Load averages */
+extern void get_avenrun(unsigned long *loads, unsigned long offset, int shift);
+
+#define FSHIFT          11                      /* nr of bits of precision */
+#define FIXED_1         (1<<FSHIFT)             /* 1.0 as fixed-point */
+#define LOAD_FREQ       (5*HZ+1)                /* 5 sec intervals */
+#define EXP_1           1884                    /* 1/exp(5sec/1min) as fixed-point */
+#define EXP_5           2014                    /* 1/exp(5sec/5min) */
+#define EXP_15          2037                    /* 1/exp(5sec/15min) */
+
+/*
+ * a1 = a0 * e + a * (1 - e)
+ */
+static inline unsigned long
+calc_load(unsigned long load, unsigned long exp, unsigned long active)
+{
+        unsigned long newload;
+
+        newload = load * exp + active * (FIXED_1 - exp);
+        if (active >= load)
+                newload += FIXED_1-1;
+
+        return newload / FIXED_1;
+}
+
+extern unsigned long calc_load_n(unsigned long load, unsigned long exp,
+                                 unsigned long active, unsigned int n);
+
+#define LOAD_INT(x) ((x) >> FSHIFT)
+#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
+
+extern void calc_global_load(unsigned long ticks);
+
+#endif /* _LINUX_SCHED_LOADAVG_H */
index 42d0754d6d1891e29a88f3e943f34e43605586ad..748ad3ab61c095195c77d8f6684a9b67d570c456 100644 (file)
@@ -108,6 +108,7 @@ basic_sources = files('''
         linux/in6.h
         linux/l2tp.h
         linux/libc-compat.h
+        linux/loadavg.h
         linux/netdevice.h
         linux/netlink.h
         linux/rtnetlink.h
index 818c9054d6e4d6d21b44137b98d3fbc1fd2f7d2e..dca2ef9f92adb49a7dcf1393e6b42003d90069a3 100644 (file)
@@ -862,3 +862,45 @@ int parse_oom_score_adjust(const char *s, int *ret) {
         *ret = v;
         return 0;
 }
+
+int store_loadavg_fixed_point(unsigned long i, unsigned long f, loadavg_t *ret) {
+        assert(ret);
+
+        if (i >= (~0UL << FSHIFT))
+                return -ERANGE;
+
+        i = i << FSHIFT;
+        f = DIV_ROUND_UP((f << FSHIFT), 100);
+
+        if (f >= FIXED_1)
+                return -ERANGE;
+
+        *ret = i | f;
+        return 0;
+}
+
+int parse_loadavg_fixed_point(const char *s, loadavg_t *ret) {
+        const char *d, *f_str, *i_str;
+        unsigned long i, f;
+        int r;
+
+        assert(s);
+        assert(ret);
+
+        d = strchr(s, '.');
+        if (!d)
+                return -EINVAL;
+
+        i_str = strndupa(s, d - s);
+        f_str = d + 1;
+
+        r = safe_atolu_full(i_str, 10, &i);
+        if (r < 0)
+                return r;
+
+        r = safe_atolu_full(f_str, 10, &f);
+        if (r < 0)
+                return r;
+
+        return store_loadavg_fixed_point(i, f, ret);
+}
index 2cee65c49ae36b6dafc091b4a57c4253d281c1dd..f22a19c5c6eb1d74124c6bd348212d3434f396d3 100644 (file)
@@ -3,12 +3,15 @@
 
 #include <inttypes.h>
 #include <limits.h>
+#include <linux/loadavg.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <sys/types.h>
 
 #include "macro.h"
 
+typedef unsigned long loadavg_t;
+
 int parse_boolean(const char *v) _pure_;
 int parse_dev(const char *s, dev_t *ret);
 int parse_pid(const char *s, pid_t* ret_pid);
@@ -88,18 +91,18 @@ static inline int safe_atoux64(const char *s, uint64_t *ret) {
 }
 
 #if LONG_MAX == INT_MAX
-static inline int safe_atolu(const char *s, unsigned long *ret_u) {
+static inline int safe_atolu_full(const char *s, unsigned base, long unsigned *ret_u) {
         assert_cc(sizeof(unsigned long) == sizeof(unsigned));
-        return safe_atou(s, (unsigned*) ret_u);
+        return safe_atou_full(s, base, (unsigned*) ret_u);
 }
 static inline int safe_atoli(const char *s, long int *ret_u) {
         assert_cc(sizeof(long int) == sizeof(int));
         return safe_atoi(s, (int*) ret_u);
 }
 #else
-static inline int safe_atolu(const char *s, unsigned long *ret_u) {
+static inline int safe_atolu_full(const char *s, unsigned base, unsigned long *ret_u) {
         assert_cc(sizeof(unsigned long) == sizeof(unsigned long long));
-        return safe_atollu(s, (unsigned long long*) ret_u);
+        return safe_atollu_full(s, base, (unsigned long long*) ret_u);
 }
 static inline int safe_atoli(const char *s, long int *ret_u) {
         assert_cc(sizeof(long int) == sizeof(long long int));
@@ -107,6 +110,10 @@ static inline int safe_atoli(const char *s, long int *ret_u) {
 }
 #endif
 
+static inline int safe_atolu(const char *s, unsigned long *ret_u) {
+        return safe_atolu_full(s, 0, ret_u);
+}
+
 #if SIZE_MAX == UINT_MAX
 static inline int safe_atozu(const char *s, size_t *ret_u) {
         assert_cc(sizeof(size_t) == sizeof(unsigned));
@@ -137,3 +144,8 @@ int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high);
 int parse_ip_prefix_length(const char *s, int *ret);
 
 int parse_oom_score_adjust(const char *s, int *ret);
+
+/* Given a Linux load average (e.g. decimal number 34.89 where 34 is passed as i and 89 is passed as f), convert it
+ * to a loadavg_t. */
+int store_loadavg_fixed_point(unsigned long i, unsigned long f, loadavg_t *ret);
+int parse_loadavg_fixed_point(const char *s, loadavg_t *ret);
index 3806c3f8cf92db7f3490242b86b973e4325a8db5..d4f908f5d46875601965cb93ed2dd436f5181999 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include <errno.h>
+#include <linux/loadavg.h>
 #include <locale.h>
 #include <math.h>
 #include <sys/socket.h>
@@ -929,6 +930,42 @@ static void test_parse_mtu(void) {
         assert_se(parse_mtu(AF_UNSPEC, "", &mtu) == -EINVAL);
 }
 
+static void test_parse_loadavg_fixed_point(void) {
+        loadavg_t fp;
+
+        assert_se(parse_loadavg_fixed_point("1.23", &fp) == 0);
+        assert_se(LOAD_INT(fp) == 1);
+        assert_se(LOAD_FRAC(fp) == 23);
+
+        assert_se(parse_loadavg_fixed_point("1.80", &fp) == 0);
+        assert_se(LOAD_INT(fp) == 1);
+        assert_se(LOAD_FRAC(fp) == 80);
+
+        assert_se(parse_loadavg_fixed_point("0.07", &fp) == 0);
+        assert_se(LOAD_INT(fp) == 0);
+        assert_se(LOAD_FRAC(fp) == 7);
+
+        assert_se(parse_loadavg_fixed_point("0.00", &fp) == 0);
+        assert_se(LOAD_INT(fp) == 0);
+        assert_se(LOAD_FRAC(fp) == 0);
+
+        assert_se(parse_loadavg_fixed_point("4096.57", &fp) == 0);
+        assert_se(LOAD_INT(fp) == 4096);
+        assert_se(LOAD_FRAC(fp) == 57);
+
+        /* Caps out at 2 digit fracs */
+        assert_se(parse_loadavg_fixed_point("1.100", &fp) == -ERANGE);
+
+        assert_se(parse_loadavg_fixed_point("4096.4096", &fp) == -ERANGE);
+        assert_se(parse_loadavg_fixed_point("-4000.5", &fp) == -ERANGE);
+        assert_se(parse_loadavg_fixed_point("18446744073709551615.5", &fp) == -ERANGE);
+        assert_se(parse_loadavg_fixed_point("foobar", &fp) == -EINVAL);
+        assert_se(parse_loadavg_fixed_point("3333", &fp) == -EINVAL);
+        assert_se(parse_loadavg_fixed_point("1.2.3", &fp) == -EINVAL);
+        assert_se(parse_loadavg_fixed_point(".", &fp) == -EINVAL);
+        assert_se(parse_loadavg_fixed_point("", &fp) == -EINVAL);
+}
+
 int main(int argc, char *argv[]) {
         log_parse_environment();
         log_open();
@@ -955,6 +992,7 @@ int main(int argc, char *argv[]) {
         test_parse_errno();
         test_parse_syscall_and_errno();
         test_parse_mtu();
+        test_parse_loadavg_fixed_point();
 
         return 0;
 }