From 510ca79cf2443bba5089f34e1fec2466cf5312ac Mon Sep 17 00:00:00 2001 From: Anita Zhang Date: Thu, 4 Jun 2020 02:13:29 -0700 Subject: [PATCH] parse-util: add parse_loadavg_fixed_point --- src/basic/linux/loadavg.h | 48 ++++++++++++++++++++++++++++++++++++++ src/basic/meson.build | 1 + src/basic/parse-util.c | 42 +++++++++++++++++++++++++++++++++ src/basic/parse-util.h | 20 ++++++++++++---- src/test/test-parse-util.c | 38 ++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 4 deletions(-) create mode 100644 src/basic/linux/loadavg.h diff --git a/src/basic/linux/loadavg.h b/src/basic/linux/loadavg.h new file mode 100644 index 0000000000..521a787e8a --- /dev/null +++ b/src/basic/linux/loadavg.h @@ -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<= 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 */ diff --git a/src/basic/meson.build b/src/basic/meson.build index 42d0754d6d..748ad3ab61 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -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 diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index 818c9054d6..dca2ef9f92 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -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); +} diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h index 2cee65c49a..f22a19c5c6 100644 --- a/src/basic/parse-util.h +++ b/src/basic/parse-util.h @@ -3,12 +3,15 @@ #include #include +#include #include #include #include #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); diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c index 3806c3f8cf..d4f908f5d4 100644 --- a/src/test/test-parse-util.c +++ b/src/test/test-parse-util.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include #include #include @@ -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; }