]> git.ipfire.org Git - thirdparty/libcgroup.git/commitdiff
abstraction-map: Add cpu.cfs_{quota,period}_us <-> cpu.max mapping
authorTom Hromatka <tom.hromatka@oracle.com>
Mon, 31 Jan 2022 16:21:54 +0000 (09:21 -0700)
committerTom Hromatka <tom.hromatka@oracle.com>
Thu, 3 Feb 2022 21:42:34 +0000 (14:42 -0700)
Add custom functions to map cpu.cfs_quota_us <-> cpu.max and
cpu.cfs_period_us <-> cpu.max.  These mappings can be 1->N or
N->1 and utilize custom values for limits, "-1" in cgroup v1
and "max" in cgroup v2.

Signed-off-by: Tom Hromatka <tom.hromatka@oracle.com>
Reviewed-by: Kamalesh Babulal <kamalesh.babulal@oracle.com>
src/Makefile.am
src/abstraction-common.c
src/abstraction-common.h
src/abstraction-cpu.c [new file with mode: 0644]
src/abstraction-map.c

index 2008ebd7e04771503dc5671588824a4381323400..80d95a1a3418b39a39fd588333d9ea47eba175aa 100644 (file)
@@ -22,7 +22,8 @@ lib_LTLIBRARIES = libcgroup.la
 libcgroup_la_SOURCES = parse.h parse.y lex.l api.c config.c \
                       libcgroup-internal.h libcgroup.map wrapper.c log.c \
                       abstraction-common.c abstraction-common.h \
-                      abstraction-map.c abstraction-map.h
+                      abstraction-map.c abstraction-map.h \
+                      abstraction-cpu.c
 libcgroup_la_LIBADD = -lpthread $(CODE_COVERAGE_LIBS)
 libcgroup_la_CFLAGS = $(CODE_COVERAGE_CFLAGS) -DSTATIC=static
 libcgroup_la_LDFLAGS = -Wl,--version-script,$(srcdir)/libcgroup.map \
index 6852afcb0a492408f2544cfc1fbfde62e73d3705..e6d51443a85739818db8072933ecb83109711bb2 100644 (file)
@@ -183,7 +183,7 @@ out:
 }
 
 static int convert_controller(struct cgroup_controller * const out_cgc,
-                             const struct cgroup_controller * const in_cgc)
+                             struct cgroup_controller * const in_cgc)
 {
        int ret;
        int i;
@@ -195,6 +195,12 @@ static int convert_controller(struct cgroup_controller * const out_cgc,
                goto out;
        }
 
+       if (strcmp(in_cgc->name, "cpu") == 0) {
+               ret = cgroup_convert_cpu_nto1(out_cgc, in_cgc);
+               if (ret)
+                       goto out;
+       }
+
        for (i = 0; i < in_cgc->index; i++) {
                ret = convert_setting(out_cgc, in_cgc->values[i]);
                if (ret)
index 73eccba6fbf75e1633b7ca73632b11b112a3a810..8e508e258c01ec4b42680bb349e541ecf61be894 100644 (file)
@@ -86,6 +86,34 @@ int cgroup_convert_passthrough(struct cgroup_controller * const dst_cgc,
                               const char * const out_setting,
                               void *in_dflt, void *out_dflt);
 
+/* cpu */
+int cgroup_convert_cpu_nto1(struct cgroup_controller * const out_cgc,
+                           struct cgroup_controller * const in_cgc);
+
+int cgroup_convert_cpu_quota_to_max(
+       struct cgroup_controller * const dst_cgc,
+       const char * const in_value,
+       const char * const out_setting,
+       void *in_dflt, void *out_dflt);
+
+int cgroup_convert_cpu_period_to_max(
+       struct cgroup_controller * const dst_cgc,
+       const char * const in_value,
+       const char * const out_setting,
+       void *in_dflt, void *out_dflt);
+
+int cgroup_convert_cpu_max_to_quota(
+       struct cgroup_controller * const dst_cgc,
+       const char * const in_value,
+       const char * const out_setting,
+       void *in_dflt, void *out_dflt);
+
+int cgroup_convert_cpu_max_to_period(
+       struct cgroup_controller * const dst_cgc,
+       const char * const in_value,
+       const char * const out_setting,
+       void *in_dflt, void *out_dflt);
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
diff --git a/src/abstraction-cpu.c b/src/abstraction-cpu.c
new file mode 100644 (file)
index 0000000..4b5cfe2
--- /dev/null
@@ -0,0 +1,308 @@
+/**
+ * Libcgroup abstraction layer for the cpu controller
+ *
+ * Copyright (c) 2021-2022 Oracle and/or its affiliates.
+ * Author: Tom Hromatka <tom.hromatka@oracle.com>
+ */
+
+/*
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses>.
+ */
+
+#include <libcgroup.h>
+#include <libcgroup-internal.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "abstraction-common.h"
+#include "abstraction-map.h"
+
+#define LL_MAX 8192
+
+static const char * const MAX = "max";
+static const char * const CPU_MAX = "cpu.max";
+static const char * const CFS_QUOTA_US = "cpu.cfs_quota_us";
+static const char * const CFS_PERIOD_US = "cpu.cfs_period_us";
+
+static int read_setting(const char * const cgroup_name,
+                       const char * const controller_name,
+                       const char * const setting_name,
+                       char ** const value)
+{
+       char tmp_line[LL_MAX];
+       void *handle;
+       int ret;
+
+       ret = cgroup_read_value_begin(controller_name, cgroup_name,
+                       setting_name, &handle, tmp_line, LL_MAX);
+       if (ret == ECGEOF)
+               goto read_end;
+       else if (ret != 0)
+               goto end;
+
+       *value = strdup(tmp_line);
+       if ((*value) == NULL) {
+               ret = ECGOTHER;
+               goto end;
+       }
+
+read_end:
+       cgroup_read_value_end(&handle);
+       if (ret == ECGEOF)
+               ret = 0;
+
+end:
+       return ret;
+}
+
+static int get_max(struct cgroup_controller * const cgc,
+                  char ** const max)
+{
+       return read_setting(cgc->cgroup->name, "cpu", "cpu.max", max);
+}
+
+static int get_quota_from_max(struct cgroup_controller * const cgc,
+                             char ** const quota)
+{
+       char *token, *max = NULL, *saveptr = NULL;
+       int ret;
+
+       ret = get_max(cgc, &max);
+       if (ret)
+               goto out;
+
+       token = strtok_r(max, " ", &saveptr);
+
+       *quota = strdup(token);
+       if ((*quota) == NULL)
+               ret = ECGOTHER;
+
+out:
+       if (max)
+               free(max);
+
+       return ret;
+}
+
+static int get_period_from_max(struct cgroup_controller * const cgc,
+                              char ** const period)
+{
+       char *token, *max = NULL, *saveptr = NULL;
+       int ret;
+
+       ret = get_max(cgc, &max);
+       if (ret)
+               goto out;
+
+       token = strtok_r(max, " ", &saveptr);
+       token = strtok_r(NULL, " ", &saveptr);
+
+       *period = strdup(token);
+       if ((*period) == NULL)
+               ret = ECGOTHER;
+
+out:
+       if (max)
+               free(max);
+
+       return ret;
+}
+
+int cgroup_convert_cpu_quota_to_max(
+       struct cgroup_controller * const dst_cgc,
+       const char * const in_value,
+       const char * const out_setting,
+       void *in_dflt, void *out_dflt)
+{
+       char max_line[LL_MAX] = {0};
+       char *period = NULL;
+       int ret;
+
+       if (strlen(in_value) == 0) {
+               /* There's no value to convert.  Populate the setting */
+               ret = cgroup_add_value_string(dst_cgc, out_setting, NULL);
+               if (ret)
+                       goto out;
+       } else {
+               ret = get_period_from_max(dst_cgc, &period);
+               if (ret)
+                       goto out;
+
+               if (strcmp(in_value, "-1") == 0)
+                       snprintf(max_line, LL_MAX, "%s %s", MAX, period);
+               else
+                       snprintf(max_line, LL_MAX, "%s %s", in_value, period);
+
+               ret = cgroup_add_value_string(dst_cgc, out_setting, max_line);
+               if (ret)
+                       goto out;
+       }
+
+       dst_cgc->values[dst_cgc->index - 1]->prev_name =
+               strdup(CFS_QUOTA_US);
+out:
+       if (period)
+               free(period);
+
+       return ret;
+}
+
+int cgroup_convert_cpu_period_to_max(
+       struct cgroup_controller * const dst_cgc,
+       const char * const in_value,
+       const char * const out_setting,
+       void *in_dflt, void *out_dflt)
+{
+       char max_line[LL_MAX] = {0};
+       char *quota = NULL;
+       int ret;
+
+       if (strlen(in_value) == 0) {
+               /* There's no value to convert.  Populate the setting and
+                * return
+                */
+               ret = cgroup_add_value_string(dst_cgc, out_setting, NULL);
+               if (ret)
+                       goto out;
+       } else {
+               ret = get_quota_from_max(dst_cgc, &quota);
+               if (ret)
+                       goto out;
+
+               if (strcmp(in_value, "-1") == 0)
+                       snprintf(max_line, LL_MAX, "%s %s", quota, MAX);
+               else
+                       snprintf(max_line, LL_MAX, "%s %s", quota, in_value);
+               ret = cgroup_add_value_string(dst_cgc, out_setting, max_line);
+               if (ret)
+                       goto out;
+       }
+
+       dst_cgc->values[dst_cgc->index - 1]->prev_name =
+               strdup(CFS_PERIOD_US);
+
+out:
+       if (quota)
+               free(quota);
+
+       return ret;
+}
+
+int cgroup_convert_cpu_max_to_quota(
+       struct cgroup_controller * const dst_cgc,
+       const char * const in_value,
+       const char * const out_setting,
+       void *in_dflt, void *out_dflt)
+{
+       char *token, *copy = NULL, *saveptr = NULL;
+       int ret;
+
+       if (strlen(in_value) == 0) {
+               /* There's no value to convert.  Populate the setting and
+                * return
+                */
+               return cgroup_add_value_string(dst_cgc, out_setting, NULL);
+       }
+
+       copy = strdup(in_value);
+       if (!copy)
+               return ECGOTHER;
+
+       token = strtok_r(copy, " ", &saveptr);
+
+       if (strcmp(token, MAX) == 0)
+               ret = cgroup_add_value_string(dst_cgc, out_setting, "-1");
+       else
+               ret = cgroup_add_value_string(dst_cgc, out_setting, token);
+
+       if (copy)
+               free(copy);
+
+       return ret;
+}
+
+int cgroup_convert_cpu_max_to_period(
+       struct cgroup_controller * const dst_cgc,
+       const char * const in_value,
+       const char * const out_setting,
+       void *in_dflt, void *out_dflt)
+{
+       char *token, *copy = NULL, *saveptr = NULL;
+       int ret;
+
+       if (strlen(in_value) == 0) {
+               /* There's no value to convert.  Populate the setting and
+                * return
+                */
+               return cgroup_add_value_string(dst_cgc, out_setting, NULL);
+       }
+
+       copy = strdup(in_value);
+       if (!copy)
+               return ECGOTHER;
+
+       token = strtok_r(copy, " ", &saveptr);
+       token = strtok_r(NULL, " ", &saveptr);
+
+       ret = cgroup_add_value_string(dst_cgc, out_setting, token);
+
+       if (copy)
+               free(copy);
+
+       return ret;
+}
+
+int cgroup_convert_cpu_nto1(struct cgroup_controller * const out_cgc,
+                           struct cgroup_controller * const in_cgc)
+{
+       char *cfs_quota = NULL, *cfs_period = NULL;
+       char max_line[LL_MAX] = {0};
+       int i, ret = 0;
+
+       for (i = 0; i < in_cgc->index; i++) {
+               if (strcmp(in_cgc->values[i]->name, CFS_QUOTA_US) == 0)
+                       cfs_quota = in_cgc->values[i]->value;
+               else if (strcmp(in_cgc->values[i]->name, CFS_PERIOD_US) == 0)
+                       cfs_period = in_cgc->values[i]->value;
+       }
+
+       if (cfs_quota && cfs_period) {
+               if (strcmp(cfs_quota, "-1") == 0) {
+                       snprintf(max_line, LL_MAX, "%s %s", MAX, cfs_period);
+                       max_line[LL_MAX - 1] = '\0';
+               } else {
+                       snprintf(max_line, LL_MAX, "%s %s", cfs_quota,
+                                cfs_period);
+                       max_line[LL_MAX - 1] = '\0';
+               }
+
+               ret = cgroup_add_value_string(out_cgc, CPU_MAX, max_line);
+               if (ret)
+                       goto out;
+
+               ret = cgroup_remove_value(in_cgc, CFS_QUOTA_US);
+               if (ret)
+                       goto out;
+
+               ret = cgroup_remove_value(in_cgc, CFS_PERIOD_US);
+               if (ret)
+                       goto out;
+       }
+
+out:
+       return ret;
+}
index 0516b0007a5a3945add35edb0e480a3591dabb58..074ece9f72137d982d1bafed6e1cc4224829f479 100644 (file)
 
 const struct cgroup_abstraction_map cgroup_v1_to_v2_map[] = {
        {cgroup_convert_int, "cpu.shares", (void *)1024, "cpu.weight", (void *)100},
+       {cgroup_convert_cpu_quota_to_max, "cpu.cfs_quota_us", NULL, "cpu.max", NULL},
+       {cgroup_convert_cpu_period_to_max, "cpu.cfs_period_us", NULL, "cpu.max", NULL},
 };
 const int cgroup_v1_to_v2_map_sz = sizeof(cgroup_v1_to_v2_map) /
                                   sizeof(cgroup_v1_to_v2_map[0]);
 
 const struct cgroup_abstraction_map cgroup_v2_to_v1_map[] = {
        {cgroup_convert_int, "cpu.weight", (void *)100, "cpu.shares", (void *)1024},
+       {cgroup_convert_cpu_max_to_quota, "cpu.max", NULL, "cpu.cfs_quota_us", NULL},
+       {cgroup_convert_cpu_max_to_period, "cpu.max", NULL, "cpu.cfs_period_us", NULL},
 };
 const int cgroup_v2_to_v1_map_sz = sizeof(cgroup_v2_to_v1_map) /
                                   sizeof(cgroup_v2_to_v1_map[0]);